| 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 }; | |
| } | |