export interface DecodedHtmlEntity {
text: string;
length: number;
}
function decodeCodePoint(codePoint: number): string | undefined {
if (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {
return undefined;
}
return String.fromCodePoint(codePoint);
}
export function decodeHtmlEntity(entity: string): string | undefined {
switch (entity) {
case "amp":
return "&";
case "lt":
return "<";
case "gt":
return ">";
case "quot":
return '"';
case "apos":
return "'";
}
if (entity.startsWith("#x") || entity.startsWith("#X")) {
return decodeCodePoint(Number.parseInt(entity.slice(2), 16));
}
if (entity.startsWith("#")) {
return decodeCodePoint(Number.parseInt(entity.slice(1), 10));
}
return undefined;
}
export function decodeHtmlEntityAt(html: string, index: number): DecodedHtmlEntity | undefined {
const semicolonIndex = html.indexOf(";", index + 1);
if (semicolonIndex === -1 || semicolonIndex - index > 16) {
return undefined;
}
const entity = html.slice(index + 1, semicolonIndex);
const decoded = decodeHtmlEntity(entity);
if (decoded === undefined) {
return undefined;
}
return { text: decoded, length: semicolonIndex - index + 1 };
}