source: frontend/node_modules/html-entities/src/index.ts

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.1 KB
Line 
1import {bodyRegExps, namedReferences} from './named-references.js';
2import {numericUnicodeMap} from './numeric-unicode-map.js';
3import {fromCodePoint, getCodePoint} from './surrogate-pairs.js';
4
5const allNamedReferences = {
6 ...namedReferences,
7 all: namedReferences.html5
8};
9
10export type Level = 'xml' | 'html4' | 'html5' | 'all';
11
12interface CommonOptions {
13 level?: Level;
14}
15
16export type EncodeMode = 'specialChars' | 'nonAscii' | 'nonAsciiPrintable' | 'nonAsciiPrintableOnly' | 'extensive';
17
18export interface EncodeOptions extends CommonOptions {
19 mode?: EncodeMode;
20 numeric?: 'decimal' | 'hexadecimal';
21}
22
23export type DecodeScope = 'strict' | 'body' | 'attribute';
24
25export interface DecodeOptions extends CommonOptions {
26 scope?: DecodeScope;
27}
28
29const encodeRegExps: Record<EncodeMode, RegExp> = {
30 specialChars: /[<>'"&]/g,
31 nonAscii: /[<>'"&\u0080-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g,
32 nonAsciiPrintable: /[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g,
33 nonAsciiPrintableOnly: /[\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g,
34 extensive: /[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g
35};
36
37const defaultEncodeOptions: EncodeOptions = {
38 mode: 'specialChars',
39 level: 'all',
40 numeric: 'decimal'
41};
42
43/** Encodes all the necessary (specified by `level`) characters in the text */
44export function encode(
45 text: string | undefined | null,
46 {mode = 'specialChars', numeric = 'decimal', level = 'all'}: EncodeOptions = defaultEncodeOptions
47) {
48 if (!text) {
49 return '';
50 }
51
52 const encodeRegExp = encodeRegExps[mode];
53 const references = allNamedReferences[level].characters;
54 const isHex = numeric === 'hexadecimal';
55
56 return String.prototype.replace.call(text, encodeRegExp, (input) => {
57 let result = references[input];
58 if (!result) {
59 const code = input.length > 1 ? getCodePoint(input, 0)! : input.charCodeAt(0);
60 result = (isHex ? '&#x' + code.toString(16) : '&#' + code) + ';';
61 }
62 return result;
63 });
64}
65
66const defaultDecodeOptions: DecodeOptions = {
67 scope: 'body',
68 level: 'all'
69};
70
71const strict = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g;
72const attribute = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g;
73
74const baseDecodeRegExps: Record<Exclude<Level, 'all'>, Record<DecodeScope, RegExp>> = {
75 xml: {
76 strict,
77 attribute,
78 body: bodyRegExps.xml
79 },
80 html4: {
81 strict,
82 attribute,
83 body: bodyRegExps.html4
84 },
85 html5: {
86 strict,
87 attribute,
88 body: bodyRegExps.html5
89 }
90};
91
92const decodeRegExps: Record<Level, Record<DecodeScope, RegExp>> = {
93 ...baseDecodeRegExps,
94 all: baseDecodeRegExps.html5
95};
96
97const fromCharCode = String.fromCharCode;
98const outOfBoundsChar = fromCharCode(65533);
99
100const defaultDecodeEntityOptions: CommonOptions = {
101 level: 'all'
102};
103
104function getDecodedEntity(
105 entity: string,
106 references: Record<string, string>,
107 isAttribute: boolean,
108 isStrict: boolean
109): string {
110 let decodeResult = entity;
111 const decodeEntityLastChar = entity[entity.length - 1];
112 if (isAttribute && decodeEntityLastChar === '=') {
113 decodeResult = entity;
114 } else if (isStrict && decodeEntityLastChar !== ';') {
115 decodeResult = entity;
116 } else {
117 const decodeResultByReference = references[entity];
118 if (decodeResultByReference) {
119 decodeResult = decodeResultByReference;
120 } else if (entity[0] === '&' && entity[1] === '#') {
121 const decodeSecondChar = entity[2];
122 const decodeCode =
123 decodeSecondChar == 'x' || decodeSecondChar == 'X'
124 ? parseInt(entity.substr(3), 16)
125 : parseInt(entity.substr(2));
126
127 decodeResult =
128 decodeCode >= 0x10ffff
129 ? outOfBoundsChar
130 : decodeCode > 65535
131 ? fromCodePoint(decodeCode)
132 : fromCharCode(numericUnicodeMap[decodeCode] || decodeCode);
133 }
134 }
135 return decodeResult;
136}
137
138/** Decodes a single entity */
139export function decodeEntity(
140 entity: string | undefined | null,
141 {level = 'all'}: CommonOptions = defaultDecodeEntityOptions
142): string {
143 if (!entity) {
144 return '';
145 }
146 return getDecodedEntity(entity, allNamedReferences[level].entities, false, false);
147}
148
149/** Decodes all entities in the text */
150export function decode(
151 text: string | undefined | null,
152 {level = 'all', scope = level === 'xml' ? 'strict' : 'body'}: DecodeOptions = defaultDecodeOptions
153) {
154 if (!text) {
155 return '';
156 }
157
158 const decodeRegExp = decodeRegExps[level][scope];
159 const references = allNamedReferences[level].entities;
160 const isAttribute = scope === 'attribute';
161 const isStrict = scope === 'strict';
162
163 return text.replace(decodeRegExp, (entity) => getDecodedEntity(entity, references, isAttribute, isStrict));
164}
Note: See TracBrowser for help on using the repository browser.