@m2c2kit/build-helpers 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -7
- package/dist/index.js +701 -363
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
22
22
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
23
23
|
PERFORMANCE OF THIS SOFTWARE.
|
|
24
24
|
***************************************************************************** */
|
|
25
|
+
/* global Reflect, Promise */
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
27
29
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -51,6 +53,7 @@ var xmlDecodeTree = new Uint16Array(
|
|
|
51
53
|
var _a;
|
|
52
54
|
const decodeMap = new Map([
|
|
53
55
|
[0, 65533],
|
|
56
|
+
// C1 Unicode control character reference replacements
|
|
54
57
|
[128, 8364],
|
|
55
58
|
[130, 8218],
|
|
56
59
|
[131, 402],
|
|
@@ -79,6 +82,9 @@ const decodeMap = new Map([
|
|
|
79
82
|
[158, 382],
|
|
80
83
|
[159, 376],
|
|
81
84
|
]);
|
|
85
|
+
/**
|
|
86
|
+
* Polyfill for `String.fromCodePoint`. It is used to create a string from a Unicode code point.
|
|
87
|
+
*/
|
|
82
88
|
const fromCodePoint =
|
|
83
89
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, node/no-unsupported-features/es-builtins
|
|
84
90
|
(_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : function (codePoint) {
|
|
@@ -91,6 +97,11 @@ const fromCodePoint =
|
|
|
91
97
|
output += String.fromCharCode(codePoint);
|
|
92
98
|
return output;
|
|
93
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* Replace the given code point with a replacement character if it is a
|
|
102
|
+
* surrogate or is outside the valid range. Otherwise return the code
|
|
103
|
+
* point unchanged.
|
|
104
|
+
*/
|
|
94
105
|
function replaceCodePoint(codePoint) {
|
|
95
106
|
var _a;
|
|
96
107
|
if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) {
|
|
@@ -103,20 +114,419 @@ var CharCodes$1;
|
|
|
103
114
|
(function (CharCodes) {
|
|
104
115
|
CharCodes[CharCodes["NUM"] = 35] = "NUM";
|
|
105
116
|
CharCodes[CharCodes["SEMI"] = 59] = "SEMI";
|
|
117
|
+
CharCodes[CharCodes["EQUALS"] = 61] = "EQUALS";
|
|
106
118
|
CharCodes[CharCodes["ZERO"] = 48] = "ZERO";
|
|
107
119
|
CharCodes[CharCodes["NINE"] = 57] = "NINE";
|
|
108
120
|
CharCodes[CharCodes["LOWER_A"] = 97] = "LOWER_A";
|
|
109
121
|
CharCodes[CharCodes["LOWER_F"] = 102] = "LOWER_F";
|
|
110
122
|
CharCodes[CharCodes["LOWER_X"] = 120] = "LOWER_X";
|
|
111
|
-
|
|
112
|
-
CharCodes[CharCodes["
|
|
123
|
+
CharCodes[CharCodes["LOWER_Z"] = 122] = "LOWER_Z";
|
|
124
|
+
CharCodes[CharCodes["UPPER_A"] = 65] = "UPPER_A";
|
|
125
|
+
CharCodes[CharCodes["UPPER_F"] = 70] = "UPPER_F";
|
|
126
|
+
CharCodes[CharCodes["UPPER_Z"] = 90] = "UPPER_Z";
|
|
113
127
|
})(CharCodes$1 || (CharCodes$1 = {}));
|
|
128
|
+
/** Bit that needs to be set to convert an upper case ASCII character to lower case */
|
|
129
|
+
const TO_LOWER_BIT = 0b100000;
|
|
114
130
|
var BinTrieFlags;
|
|
115
131
|
(function (BinTrieFlags) {
|
|
116
132
|
BinTrieFlags[BinTrieFlags["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH";
|
|
117
133
|
BinTrieFlags[BinTrieFlags["BRANCH_LENGTH"] = 16256] = "BRANCH_LENGTH";
|
|
118
134
|
BinTrieFlags[BinTrieFlags["JUMP_TABLE"] = 127] = "JUMP_TABLE";
|
|
119
135
|
})(BinTrieFlags || (BinTrieFlags = {}));
|
|
136
|
+
function isNumber(code) {
|
|
137
|
+
return code >= CharCodes$1.ZERO && code <= CharCodes$1.NINE;
|
|
138
|
+
}
|
|
139
|
+
function isHexadecimalCharacter(code) {
|
|
140
|
+
return ((code >= CharCodes$1.UPPER_A && code <= CharCodes$1.UPPER_F) ||
|
|
141
|
+
(code >= CharCodes$1.LOWER_A && code <= CharCodes$1.LOWER_F));
|
|
142
|
+
}
|
|
143
|
+
function isAsciiAlphaNumeric(code) {
|
|
144
|
+
return ((code >= CharCodes$1.UPPER_A && code <= CharCodes$1.UPPER_Z) ||
|
|
145
|
+
(code >= CharCodes$1.LOWER_A && code <= CharCodes$1.LOWER_Z) ||
|
|
146
|
+
isNumber(code));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Checks if the given character is a valid end character for an entity in an attribute.
|
|
150
|
+
*
|
|
151
|
+
* Attribute values that aren't terminated properly aren't parsed, and shouldn't lead to a parser error.
|
|
152
|
+
* See the example in https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state
|
|
153
|
+
*/
|
|
154
|
+
function isEntityInAttributeInvalidEnd(code) {
|
|
155
|
+
return code === CharCodes$1.EQUALS || isAsciiAlphaNumeric(code);
|
|
156
|
+
}
|
|
157
|
+
var EntityDecoderState;
|
|
158
|
+
(function (EntityDecoderState) {
|
|
159
|
+
EntityDecoderState[EntityDecoderState["EntityStart"] = 0] = "EntityStart";
|
|
160
|
+
EntityDecoderState[EntityDecoderState["NumericStart"] = 1] = "NumericStart";
|
|
161
|
+
EntityDecoderState[EntityDecoderState["NumericDecimal"] = 2] = "NumericDecimal";
|
|
162
|
+
EntityDecoderState[EntityDecoderState["NumericHex"] = 3] = "NumericHex";
|
|
163
|
+
EntityDecoderState[EntityDecoderState["NamedEntity"] = 4] = "NamedEntity";
|
|
164
|
+
})(EntityDecoderState || (EntityDecoderState = {}));
|
|
165
|
+
var DecodingMode;
|
|
166
|
+
(function (DecodingMode) {
|
|
167
|
+
/** Entities in text nodes that can end with any character. */
|
|
168
|
+
DecodingMode[DecodingMode["Legacy"] = 0] = "Legacy";
|
|
169
|
+
/** Only allow entities terminated with a semicolon. */
|
|
170
|
+
DecodingMode[DecodingMode["Strict"] = 1] = "Strict";
|
|
171
|
+
/** Entities in attributes have limitations on ending characters. */
|
|
172
|
+
DecodingMode[DecodingMode["Attribute"] = 2] = "Attribute";
|
|
173
|
+
})(DecodingMode || (DecodingMode = {}));
|
|
174
|
+
/**
|
|
175
|
+
* Token decoder with support of writing partial entities.
|
|
176
|
+
*/
|
|
177
|
+
class EntityDecoder {
|
|
178
|
+
constructor(
|
|
179
|
+
/** The tree used to decode entities. */
|
|
180
|
+
decodeTree,
|
|
181
|
+
/**
|
|
182
|
+
* The function that is called when a codepoint is decoded.
|
|
183
|
+
*
|
|
184
|
+
* For multi-byte named entities, this will be called multiple times,
|
|
185
|
+
* with the second codepoint, and the same `consumed` value.
|
|
186
|
+
*
|
|
187
|
+
* @param codepoint The decoded codepoint.
|
|
188
|
+
* @param consumed The number of bytes consumed by the decoder.
|
|
189
|
+
*/
|
|
190
|
+
emitCodePoint,
|
|
191
|
+
/** An object that is used to produce errors. */
|
|
192
|
+
errors) {
|
|
193
|
+
this.decodeTree = decodeTree;
|
|
194
|
+
this.emitCodePoint = emitCodePoint;
|
|
195
|
+
this.errors = errors;
|
|
196
|
+
/** The current state of the decoder. */
|
|
197
|
+
this.state = EntityDecoderState.EntityStart;
|
|
198
|
+
/** Characters that were consumed while parsing an entity. */
|
|
199
|
+
this.consumed = 1;
|
|
200
|
+
/**
|
|
201
|
+
* The result of the entity.
|
|
202
|
+
*
|
|
203
|
+
* Either the result index of a numeric entity, or the codepoint of a
|
|
204
|
+
* numeric entity.
|
|
205
|
+
*/
|
|
206
|
+
this.result = 0;
|
|
207
|
+
/** The current index in the decode tree. */
|
|
208
|
+
this.treeIndex = 0;
|
|
209
|
+
/** The number of characters that were consumed in excess. */
|
|
210
|
+
this.excess = 1;
|
|
211
|
+
/** The mode in which the decoder is operating. */
|
|
212
|
+
this.decodeMode = DecodingMode.Strict;
|
|
213
|
+
}
|
|
214
|
+
/** Resets the instance to make it reusable. */
|
|
215
|
+
startEntity(decodeMode) {
|
|
216
|
+
this.decodeMode = decodeMode;
|
|
217
|
+
this.state = EntityDecoderState.EntityStart;
|
|
218
|
+
this.result = 0;
|
|
219
|
+
this.treeIndex = 0;
|
|
220
|
+
this.excess = 1;
|
|
221
|
+
this.consumed = 1;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Write an entity to the decoder. This can be called multiple times with partial entities.
|
|
225
|
+
* If the entity is incomplete, the decoder will return -1.
|
|
226
|
+
*
|
|
227
|
+
* Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the
|
|
228
|
+
* entity is incomplete, and resume when the next string is written.
|
|
229
|
+
*
|
|
230
|
+
* @param string The string containing the entity (or a continuation of the entity).
|
|
231
|
+
* @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
|
|
232
|
+
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
|
233
|
+
*/
|
|
234
|
+
write(str, offset) {
|
|
235
|
+
switch (this.state) {
|
|
236
|
+
case EntityDecoderState.EntityStart: {
|
|
237
|
+
if (str.charCodeAt(offset) === CharCodes$1.NUM) {
|
|
238
|
+
this.state = EntityDecoderState.NumericStart;
|
|
239
|
+
this.consumed += 1;
|
|
240
|
+
return this.stateNumericStart(str, offset + 1);
|
|
241
|
+
}
|
|
242
|
+
this.state = EntityDecoderState.NamedEntity;
|
|
243
|
+
return this.stateNamedEntity(str, offset);
|
|
244
|
+
}
|
|
245
|
+
case EntityDecoderState.NumericStart: {
|
|
246
|
+
return this.stateNumericStart(str, offset);
|
|
247
|
+
}
|
|
248
|
+
case EntityDecoderState.NumericDecimal: {
|
|
249
|
+
return this.stateNumericDecimal(str, offset);
|
|
250
|
+
}
|
|
251
|
+
case EntityDecoderState.NumericHex: {
|
|
252
|
+
return this.stateNumericHex(str, offset);
|
|
253
|
+
}
|
|
254
|
+
case EntityDecoderState.NamedEntity: {
|
|
255
|
+
return this.stateNamedEntity(str, offset);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Switches between the numeric decimal and hexadecimal states.
|
|
261
|
+
*
|
|
262
|
+
* Equivalent to the `Numeric character reference state` in the HTML spec.
|
|
263
|
+
*
|
|
264
|
+
* @param str The string containing the entity (or a continuation of the entity).
|
|
265
|
+
* @param offset The current offset.
|
|
266
|
+
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
|
267
|
+
*/
|
|
268
|
+
stateNumericStart(str, offset) {
|
|
269
|
+
if (offset >= str.length) {
|
|
270
|
+
return -1;
|
|
271
|
+
}
|
|
272
|
+
if ((str.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes$1.LOWER_X) {
|
|
273
|
+
this.state = EntityDecoderState.NumericHex;
|
|
274
|
+
this.consumed += 1;
|
|
275
|
+
return this.stateNumericHex(str, offset + 1);
|
|
276
|
+
}
|
|
277
|
+
this.state = EntityDecoderState.NumericDecimal;
|
|
278
|
+
return this.stateNumericDecimal(str, offset);
|
|
279
|
+
}
|
|
280
|
+
addToNumericResult(str, start, end, base) {
|
|
281
|
+
if (start !== end) {
|
|
282
|
+
const digitCount = end - start;
|
|
283
|
+
this.result =
|
|
284
|
+
this.result * Math.pow(base, digitCount) +
|
|
285
|
+
parseInt(str.substr(start, digitCount), base);
|
|
286
|
+
this.consumed += digitCount;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Parses a hexadecimal numeric entity.
|
|
291
|
+
*
|
|
292
|
+
* Equivalent to the `Hexademical character reference state` in the HTML spec.
|
|
293
|
+
*
|
|
294
|
+
* @param str The string containing the entity (or a continuation of the entity).
|
|
295
|
+
* @param offset The current offset.
|
|
296
|
+
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
|
297
|
+
*/
|
|
298
|
+
stateNumericHex(str, offset) {
|
|
299
|
+
const startIdx = offset;
|
|
300
|
+
while (offset < str.length) {
|
|
301
|
+
const char = str.charCodeAt(offset);
|
|
302
|
+
if (isNumber(char) || isHexadecimalCharacter(char)) {
|
|
303
|
+
offset += 1;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.addToNumericResult(str, startIdx, offset, 16);
|
|
307
|
+
return this.emitNumericEntity(char, 3);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
this.addToNumericResult(str, startIdx, offset, 16);
|
|
311
|
+
return -1;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Parses a decimal numeric entity.
|
|
315
|
+
*
|
|
316
|
+
* Equivalent to the `Decimal character reference state` in the HTML spec.
|
|
317
|
+
*
|
|
318
|
+
* @param str The string containing the entity (or a continuation of the entity).
|
|
319
|
+
* @param offset The current offset.
|
|
320
|
+
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
|
321
|
+
*/
|
|
322
|
+
stateNumericDecimal(str, offset) {
|
|
323
|
+
const startIdx = offset;
|
|
324
|
+
while (offset < str.length) {
|
|
325
|
+
const char = str.charCodeAt(offset);
|
|
326
|
+
if (isNumber(char)) {
|
|
327
|
+
offset += 1;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
this.addToNumericResult(str, startIdx, offset, 10);
|
|
331
|
+
return this.emitNumericEntity(char, 2);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
this.addToNumericResult(str, startIdx, offset, 10);
|
|
335
|
+
return -1;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Validate and emit a numeric entity.
|
|
339
|
+
*
|
|
340
|
+
* Implements the logic from the `Hexademical character reference start
|
|
341
|
+
* state` and `Numeric character reference end state` in the HTML spec.
|
|
342
|
+
*
|
|
343
|
+
* @param lastCp The last code point of the entity. Used to see if the
|
|
344
|
+
* entity was terminated with a semicolon.
|
|
345
|
+
* @param expectedLength The minimum number of characters that should be
|
|
346
|
+
* consumed. Used to validate that at least one digit
|
|
347
|
+
* was consumed.
|
|
348
|
+
* @returns The number of characters that were consumed.
|
|
349
|
+
*/
|
|
350
|
+
emitNumericEntity(lastCp, expectedLength) {
|
|
351
|
+
var _a;
|
|
352
|
+
// Ensure we consumed at least one digit.
|
|
353
|
+
if (this.consumed <= expectedLength) {
|
|
354
|
+
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
// Figure out if this is a legit end of the entity
|
|
358
|
+
if (lastCp === CharCodes$1.SEMI) {
|
|
359
|
+
this.consumed += 1;
|
|
360
|
+
}
|
|
361
|
+
else if (this.decodeMode === DecodingMode.Strict) {
|
|
362
|
+
return 0;
|
|
363
|
+
}
|
|
364
|
+
this.emitCodePoint(replaceCodePoint(this.result), this.consumed);
|
|
365
|
+
if (this.errors) {
|
|
366
|
+
if (lastCp !== CharCodes$1.SEMI) {
|
|
367
|
+
this.errors.missingSemicolonAfterCharacterReference();
|
|
368
|
+
}
|
|
369
|
+
this.errors.validateNumericCharacterReference(this.result);
|
|
370
|
+
}
|
|
371
|
+
return this.consumed;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Parses a named entity.
|
|
375
|
+
*
|
|
376
|
+
* Equivalent to the `Named character reference state` in the HTML spec.
|
|
377
|
+
*
|
|
378
|
+
* @param str The string containing the entity (or a continuation of the entity).
|
|
379
|
+
* @param offset The current offset.
|
|
380
|
+
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
|
381
|
+
*/
|
|
382
|
+
stateNamedEntity(str, offset) {
|
|
383
|
+
const { decodeTree } = this;
|
|
384
|
+
let current = decodeTree[this.treeIndex];
|
|
385
|
+
// The mask is the number of bytes of the value, including the current byte.
|
|
386
|
+
let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
|
|
387
|
+
for (; offset < str.length; offset++, this.excess++) {
|
|
388
|
+
const char = str.charCodeAt(offset);
|
|
389
|
+
this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
|
|
390
|
+
if (this.treeIndex < 0) {
|
|
391
|
+
return this.result === 0 ||
|
|
392
|
+
// If we are parsing an attribute
|
|
393
|
+
(this.decodeMode === DecodingMode.Attribute &&
|
|
394
|
+
// We shouldn't have consumed any characters after the entity,
|
|
395
|
+
(valueLength === 0 ||
|
|
396
|
+
// And there should be no invalid characters.
|
|
397
|
+
isEntityInAttributeInvalidEnd(char)))
|
|
398
|
+
? 0
|
|
399
|
+
: this.emitNotTerminatedNamedEntity();
|
|
400
|
+
}
|
|
401
|
+
current = decodeTree[this.treeIndex];
|
|
402
|
+
valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
|
|
403
|
+
// If the branch is a value, store it and continue
|
|
404
|
+
if (valueLength !== 0) {
|
|
405
|
+
// If the entity is terminated by a semicolon, we are done.
|
|
406
|
+
if (char === CharCodes$1.SEMI) {
|
|
407
|
+
return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
|
|
408
|
+
}
|
|
409
|
+
// If we encounter a non-terminated (legacy) entity while parsing strictly, then ignore it.
|
|
410
|
+
if (this.decodeMode !== DecodingMode.Strict) {
|
|
411
|
+
this.result = this.treeIndex;
|
|
412
|
+
this.consumed += this.excess;
|
|
413
|
+
this.excess = 0;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return -1;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Emit a named entity that was not terminated with a semicolon.
|
|
421
|
+
*
|
|
422
|
+
* @returns The number of characters consumed.
|
|
423
|
+
*/
|
|
424
|
+
emitNotTerminatedNamedEntity() {
|
|
425
|
+
var _a;
|
|
426
|
+
const { result, decodeTree } = this;
|
|
427
|
+
const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14;
|
|
428
|
+
this.emitNamedEntityData(result, valueLength, this.consumed);
|
|
429
|
+
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference();
|
|
430
|
+
return this.consumed;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Emit a named entity.
|
|
434
|
+
*
|
|
435
|
+
* @param result The index of the entity in the decode tree.
|
|
436
|
+
* @param valueLength The number of bytes in the entity.
|
|
437
|
+
* @param consumed The number of characters consumed.
|
|
438
|
+
*
|
|
439
|
+
* @returns The number of characters consumed.
|
|
440
|
+
*/
|
|
441
|
+
emitNamedEntityData(result, valueLength, consumed) {
|
|
442
|
+
const { decodeTree } = this;
|
|
443
|
+
this.emitCodePoint(valueLength === 1
|
|
444
|
+
? decodeTree[result] & ~BinTrieFlags.VALUE_LENGTH
|
|
445
|
+
: decodeTree[result + 1], consumed);
|
|
446
|
+
if (valueLength === 3) {
|
|
447
|
+
// For multi-byte values, we need to emit the second byte.
|
|
448
|
+
this.emitCodePoint(decodeTree[result + 2], consumed);
|
|
449
|
+
}
|
|
450
|
+
return consumed;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Signal to the parser that the end of the input was reached.
|
|
454
|
+
*
|
|
455
|
+
* Remaining data will be emitted and relevant errors will be produced.
|
|
456
|
+
*
|
|
457
|
+
* @returns The number of characters consumed.
|
|
458
|
+
*/
|
|
459
|
+
end() {
|
|
460
|
+
var _a;
|
|
461
|
+
switch (this.state) {
|
|
462
|
+
case EntityDecoderState.NamedEntity: {
|
|
463
|
+
// Emit a named entity if we have one.
|
|
464
|
+
return this.result !== 0 &&
|
|
465
|
+
(this.decodeMode !== DecodingMode.Attribute ||
|
|
466
|
+
this.result === this.treeIndex)
|
|
467
|
+
? this.emitNotTerminatedNamedEntity()
|
|
468
|
+
: 0;
|
|
469
|
+
}
|
|
470
|
+
// Otherwise, emit a numeric entity if we have one.
|
|
471
|
+
case EntityDecoderState.NumericDecimal: {
|
|
472
|
+
return this.emitNumericEntity(0, 2);
|
|
473
|
+
}
|
|
474
|
+
case EntityDecoderState.NumericHex: {
|
|
475
|
+
return this.emitNumericEntity(0, 3);
|
|
476
|
+
}
|
|
477
|
+
case EntityDecoderState.NumericStart: {
|
|
478
|
+
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
|
|
479
|
+
return 0;
|
|
480
|
+
}
|
|
481
|
+
case EntityDecoderState.EntityStart: {
|
|
482
|
+
// Return 0 if we have no entity.
|
|
483
|
+
return 0;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Creates a function that decodes entities in a string.
|
|
490
|
+
*
|
|
491
|
+
* @param decodeTree The decode tree.
|
|
492
|
+
* @returns A function that decodes entities in a string.
|
|
493
|
+
*/
|
|
494
|
+
function getDecoder(decodeTree) {
|
|
495
|
+
let ret = "";
|
|
496
|
+
const decoder = new EntityDecoder(decodeTree, (str) => (ret += fromCodePoint(str)));
|
|
497
|
+
return function decodeWithTrie(str, decodeMode) {
|
|
498
|
+
let lastIndex = 0;
|
|
499
|
+
let offset = 0;
|
|
500
|
+
while ((offset = str.indexOf("&", offset)) >= 0) {
|
|
501
|
+
ret += str.slice(lastIndex, offset);
|
|
502
|
+
decoder.startEntity(decodeMode);
|
|
503
|
+
const len = decoder.write(str,
|
|
504
|
+
// Skip the "&"
|
|
505
|
+
offset + 1);
|
|
506
|
+
if (len < 0) {
|
|
507
|
+
lastIndex = offset + decoder.end();
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
lastIndex = offset + len;
|
|
511
|
+
// If `len` is 0, skip the current `&` and continue.
|
|
512
|
+
offset = len === 0 ? lastIndex + 1 : lastIndex;
|
|
513
|
+
}
|
|
514
|
+
const result = ret + str.slice(lastIndex);
|
|
515
|
+
// Make sure we don't keep a reference to the final string.
|
|
516
|
+
ret = "";
|
|
517
|
+
return result;
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Determines the branch of the current node that is taken given the current
|
|
522
|
+
* character. This function is used to traverse the trie.
|
|
523
|
+
*
|
|
524
|
+
* @param decodeTree The trie.
|
|
525
|
+
* @param current The current node.
|
|
526
|
+
* @param nodeIdx The index right after the current node and its value.
|
|
527
|
+
* @param char The current character.
|
|
528
|
+
* @returns The index of the next node, or -1 if no branch is taken.
|
|
529
|
+
*/
|
|
120
530
|
function determineBranch(decodeTree, current, nodeIdx, char) {
|
|
121
531
|
const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
|
|
122
532
|
const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
|
|
@@ -150,6 +560,8 @@ function determineBranch(decodeTree, current, nodeIdx, char) {
|
|
|
150
560
|
}
|
|
151
561
|
return -1;
|
|
152
562
|
}
|
|
563
|
+
getDecoder(htmlDecodeTree);
|
|
564
|
+
getDecoder(xmlDecodeTree);
|
|
153
565
|
|
|
154
566
|
var CharCodes;
|
|
155
567
|
(function (CharCodes) {
|
|
@@ -213,11 +625,7 @@ var State$1;
|
|
|
213
625
|
State[State["BeforeSpecialS"] = 22] = "BeforeSpecialS";
|
|
214
626
|
State[State["SpecialStartSequence"] = 23] = "SpecialStartSequence";
|
|
215
627
|
State[State["InSpecialTag"] = 24] = "InSpecialTag";
|
|
216
|
-
State[State["
|
|
217
|
-
State[State["BeforeNumericEntity"] = 26] = "BeforeNumericEntity";
|
|
218
|
-
State[State["InNamedEntity"] = 27] = "InNamedEntity";
|
|
219
|
-
State[State["InNumericEntity"] = 28] = "InNumericEntity";
|
|
220
|
-
State[State["InHexEntity"] = 29] = "InHexEntity";
|
|
628
|
+
State[State["InEntity"] = 25] = "InEntity";
|
|
221
629
|
})(State$1 || (State$1 = {}));
|
|
222
630
|
function isWhitespace$1(c) {
|
|
223
631
|
return (c === CharCodes.Space ||
|
|
@@ -229,17 +637,10 @@ function isWhitespace$1(c) {
|
|
|
229
637
|
function isEndOfTagSection(c) {
|
|
230
638
|
return c === CharCodes.Slash || c === CharCodes.Gt || isWhitespace$1(c);
|
|
231
639
|
}
|
|
232
|
-
function isNumber(c) {
|
|
233
|
-
return c >= CharCodes.Zero && c <= CharCodes.Nine;
|
|
234
|
-
}
|
|
235
640
|
function isASCIIAlpha(c) {
|
|
236
641
|
return ((c >= CharCodes.LowerA && c <= CharCodes.LowerZ) ||
|
|
237
642
|
(c >= CharCodes.UpperA && c <= CharCodes.UpperZ));
|
|
238
643
|
}
|
|
239
|
-
function isHexDigit$1(c) {
|
|
240
|
-
return ((c >= CharCodes.UpperA && c <= CharCodes.UpperF) ||
|
|
241
|
-
(c >= CharCodes.LowerA && c <= CharCodes.LowerF));
|
|
242
|
-
}
|
|
243
644
|
var QuoteType;
|
|
244
645
|
(function (QuoteType) {
|
|
245
646
|
QuoteType[QuoteType["NoValue"] = 0] = "NoValue";
|
|
@@ -272,6 +673,8 @@ class Tokenizer {
|
|
|
272
673
|
this.sectionStart = 0;
|
|
273
674
|
/** The index within the buffer that we are currently looking at. */
|
|
274
675
|
this.index = 0;
|
|
676
|
+
/** The start of the last entity. */
|
|
677
|
+
this.entityStart = 0;
|
|
275
678
|
/** Some behavior, eg. when decoding entities, is done while we are in another state. This keeps track of the other state type. */
|
|
276
679
|
this.baseState = State$1.Text;
|
|
277
680
|
/** For special parsing behavior inside of script and style tags. */
|
|
@@ -282,14 +685,9 @@ class Tokenizer {
|
|
|
282
685
|
this.offset = 0;
|
|
283
686
|
this.currentSequence = undefined;
|
|
284
687
|
this.sequenceIndex = 0;
|
|
285
|
-
this.trieIndex = 0;
|
|
286
|
-
this.trieCurrent = 0;
|
|
287
|
-
/** For named entities, the index of the value. For numeric entities, the code point. */
|
|
288
|
-
this.entityResult = 0;
|
|
289
|
-
this.entityExcess = 0;
|
|
290
688
|
this.xmlMode = xmlMode;
|
|
291
689
|
this.decodeEntities = decodeEntities;
|
|
292
|
-
this.
|
|
690
|
+
this.entityDecoder = new EntityDecoder(xmlMode ? xmlDecodeTree : htmlDecodeTree, (cp, consumed) => this.emitCodePoint(cp, consumed));
|
|
293
691
|
}
|
|
294
692
|
reset() {
|
|
295
693
|
this.state = State$1.Text;
|
|
@@ -319,18 +717,6 @@ class Tokenizer {
|
|
|
319
717
|
this.parse();
|
|
320
718
|
}
|
|
321
719
|
}
|
|
322
|
-
/**
|
|
323
|
-
* The current index within all of the written data.
|
|
324
|
-
*/
|
|
325
|
-
getIndex() {
|
|
326
|
-
return this.index;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* The start of the current section.
|
|
330
|
-
*/
|
|
331
|
-
getSectionStart() {
|
|
332
|
-
return this.sectionStart;
|
|
333
|
-
}
|
|
334
720
|
stateText(c) {
|
|
335
721
|
if (c === CharCodes.Lt ||
|
|
336
722
|
(!this.decodeEntities && this.fastForwardTo(CharCodes.Lt))) {
|
|
@@ -341,7 +727,7 @@ class Tokenizer {
|
|
|
341
727
|
this.sectionStart = this.index;
|
|
342
728
|
}
|
|
343
729
|
else if (this.decodeEntities && c === CharCodes.Amp) {
|
|
344
|
-
this.
|
|
730
|
+
this.startEntity();
|
|
345
731
|
}
|
|
346
732
|
}
|
|
347
733
|
stateSpecialStartSequence(c) {
|
|
@@ -388,7 +774,7 @@ class Tokenizer {
|
|
|
388
774
|
if (this.currentSequence === Sequences.TitleEnd) {
|
|
389
775
|
// We have to parse entities in <title> tags.
|
|
390
776
|
if (this.decodeEntities && c === CharCodes.Amp) {
|
|
391
|
-
this.
|
|
777
|
+
this.startEntity();
|
|
392
778
|
}
|
|
393
779
|
}
|
|
394
780
|
else if (this.fastForwardTo(CharCodes.Lt)) {
|
|
@@ -547,7 +933,6 @@ class Tokenizer {
|
|
|
547
933
|
// Skip everything until ">"
|
|
548
934
|
if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {
|
|
549
935
|
this.state = State$1.Text;
|
|
550
|
-
this.baseState = State$1.Text;
|
|
551
936
|
this.sectionStart = this.index + 1;
|
|
552
937
|
}
|
|
553
938
|
}
|
|
@@ -561,7 +946,6 @@ class Tokenizer {
|
|
|
561
946
|
else {
|
|
562
947
|
this.state = State$1.Text;
|
|
563
948
|
}
|
|
564
|
-
this.baseState = this.state;
|
|
565
949
|
this.sectionStart = this.index + 1;
|
|
566
950
|
}
|
|
567
951
|
else if (c === CharCodes.Slash) {
|
|
@@ -576,7 +960,6 @@ class Tokenizer {
|
|
|
576
960
|
if (c === CharCodes.Gt) {
|
|
577
961
|
this.cbs.onselfclosingtag(this.index);
|
|
578
962
|
this.state = State$1.Text;
|
|
579
|
-
this.baseState = State$1.Text;
|
|
580
963
|
this.sectionStart = this.index + 1;
|
|
581
964
|
this.isSpecial = false; // Reset special state, in case of self-closing special tags
|
|
582
965
|
}
|
|
@@ -634,8 +1017,7 @@ class Tokenizer {
|
|
|
634
1017
|
this.state = State$1.BeforeAttributeName;
|
|
635
1018
|
}
|
|
636
1019
|
else if (this.decodeEntities && c === CharCodes.Amp) {
|
|
637
|
-
this.
|
|
638
|
-
this.state = State$1.BeforeEntity;
|
|
1020
|
+
this.startEntity();
|
|
639
1021
|
}
|
|
640
1022
|
}
|
|
641
1023
|
stateInAttributeValueDoubleQuotes(c) {
|
|
@@ -653,8 +1035,7 @@ class Tokenizer {
|
|
|
653
1035
|
this.stateBeforeAttributeName(c);
|
|
654
1036
|
}
|
|
655
1037
|
else if (this.decodeEntities && c === CharCodes.Amp) {
|
|
656
|
-
this.
|
|
657
|
-
this.state = State$1.BeforeEntity;
|
|
1038
|
+
this.startEntity();
|
|
658
1039
|
}
|
|
659
1040
|
}
|
|
660
1041
|
stateBeforeDeclaration(c) {
|
|
@@ -715,147 +1096,30 @@ class Tokenizer {
|
|
|
715
1096
|
this.stateInTagName(c); // Consume the token again
|
|
716
1097
|
}
|
|
717
1098
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
this.
|
|
721
|
-
this.
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
if (this.trieIndex < 0) {
|
|
737
|
-
this.emitNamedEntity();
|
|
738
|
-
this.index--;
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
this.trieCurrent = this.entityTrie[this.trieIndex];
|
|
742
|
-
const masked = this.trieCurrent & BinTrieFlags.VALUE_LENGTH;
|
|
743
|
-
// If the branch is a value, store it and continue
|
|
744
|
-
if (masked) {
|
|
745
|
-
// The mask is the number of bytes of the value, including the current byte.
|
|
746
|
-
const valueLength = (masked >> 14) - 1;
|
|
747
|
-
// If we have a legacy entity while parsing strictly, just skip the number of bytes
|
|
748
|
-
if (!this.allowLegacyEntity() && c !== CharCodes.Semi) {
|
|
749
|
-
this.trieIndex += valueLength;
|
|
750
|
-
}
|
|
751
|
-
else {
|
|
752
|
-
// Add 1 as we have already incremented the excess
|
|
753
|
-
const entityStart = this.index - this.entityExcess + 1;
|
|
754
|
-
if (entityStart > this.sectionStart) {
|
|
755
|
-
this.emitPartial(this.sectionStart, entityStart);
|
|
756
|
-
}
|
|
757
|
-
// If this is a surrogate pair, consume the next two bytes
|
|
758
|
-
this.entityResult = this.trieIndex;
|
|
759
|
-
this.trieIndex += valueLength;
|
|
760
|
-
this.entityExcess = 0;
|
|
761
|
-
this.sectionStart = this.index + 1;
|
|
762
|
-
if (valueLength === 0) {
|
|
763
|
-
this.emitNamedEntity();
|
|
764
|
-
}
|
|
1099
|
+
startEntity() {
|
|
1100
|
+
this.baseState = this.state;
|
|
1101
|
+
this.state = State$1.InEntity;
|
|
1102
|
+
this.entityStart = this.index;
|
|
1103
|
+
this.entityDecoder.startEntity(this.xmlMode
|
|
1104
|
+
? DecodingMode.Strict
|
|
1105
|
+
: this.baseState === State$1.Text ||
|
|
1106
|
+
this.baseState === State$1.InSpecialTag
|
|
1107
|
+
? DecodingMode.Legacy
|
|
1108
|
+
: DecodingMode.Attribute);
|
|
1109
|
+
}
|
|
1110
|
+
stateInEntity() {
|
|
1111
|
+
const length = this.entityDecoder.write(this.buffer, this.index - this.offset);
|
|
1112
|
+
// If `length` is positive, we are done with the entity.
|
|
1113
|
+
if (length >= 0) {
|
|
1114
|
+
this.state = this.baseState;
|
|
1115
|
+
if (length === 0) {
|
|
1116
|
+
this.index = this.entityStart;
|
|
765
1117
|
}
|
|
766
1118
|
}
|
|
767
|
-
}
|
|
768
|
-
emitNamedEntity() {
|
|
769
|
-
this.state = this.baseState;
|
|
770
|
-
if (this.entityResult === 0) {
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
const valueLength = (this.entityTrie[this.entityResult] & BinTrieFlags.VALUE_LENGTH) >>
|
|
774
|
-
14;
|
|
775
|
-
switch (valueLength) {
|
|
776
|
-
case 1: {
|
|
777
|
-
this.emitCodePoint(this.entityTrie[this.entityResult] &
|
|
778
|
-
~BinTrieFlags.VALUE_LENGTH);
|
|
779
|
-
break;
|
|
780
|
-
}
|
|
781
|
-
case 2: {
|
|
782
|
-
this.emitCodePoint(this.entityTrie[this.entityResult + 1]);
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
case 3: {
|
|
786
|
-
this.emitCodePoint(this.entityTrie[this.entityResult + 1]);
|
|
787
|
-
this.emitCodePoint(this.entityTrie[this.entityResult + 2]);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
stateBeforeNumericEntity(c) {
|
|
792
|
-
if ((c | 0x20) === CharCodes.LowerX) {
|
|
793
|
-
this.entityExcess++;
|
|
794
|
-
this.state = State$1.InHexEntity;
|
|
795
|
-
}
|
|
796
1119
|
else {
|
|
797
|
-
|
|
798
|
-
this.
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
emitNumericEntity(strict) {
|
|
802
|
-
const entityStart = this.index - this.entityExcess - 1;
|
|
803
|
-
const numberStart = entityStart + 2 + Number(this.state === State$1.InHexEntity);
|
|
804
|
-
if (numberStart !== this.index) {
|
|
805
|
-
// Emit leading data if any
|
|
806
|
-
if (entityStart > this.sectionStart) {
|
|
807
|
-
this.emitPartial(this.sectionStart, entityStart);
|
|
808
|
-
}
|
|
809
|
-
this.sectionStart = this.index + Number(strict);
|
|
810
|
-
this.emitCodePoint(replaceCodePoint(this.entityResult));
|
|
1120
|
+
// Mark buffer as consumed.
|
|
1121
|
+
this.index = this.offset + this.buffer.length - 1;
|
|
811
1122
|
}
|
|
812
|
-
this.state = this.baseState;
|
|
813
|
-
}
|
|
814
|
-
stateInNumericEntity(c) {
|
|
815
|
-
if (c === CharCodes.Semi) {
|
|
816
|
-
this.emitNumericEntity(true);
|
|
817
|
-
}
|
|
818
|
-
else if (isNumber(c)) {
|
|
819
|
-
this.entityResult = this.entityResult * 10 + (c - CharCodes.Zero);
|
|
820
|
-
this.entityExcess++;
|
|
821
|
-
}
|
|
822
|
-
else {
|
|
823
|
-
if (this.allowLegacyEntity()) {
|
|
824
|
-
this.emitNumericEntity(false);
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
this.state = this.baseState;
|
|
828
|
-
}
|
|
829
|
-
this.index--;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
stateInHexEntity(c) {
|
|
833
|
-
if (c === CharCodes.Semi) {
|
|
834
|
-
this.emitNumericEntity(true);
|
|
835
|
-
}
|
|
836
|
-
else if (isNumber(c)) {
|
|
837
|
-
this.entityResult = this.entityResult * 16 + (c - CharCodes.Zero);
|
|
838
|
-
this.entityExcess++;
|
|
839
|
-
}
|
|
840
|
-
else if (isHexDigit$1(c)) {
|
|
841
|
-
this.entityResult =
|
|
842
|
-
this.entityResult * 16 + ((c | 0x20) - CharCodes.LowerA + 10);
|
|
843
|
-
this.entityExcess++;
|
|
844
|
-
}
|
|
845
|
-
else {
|
|
846
|
-
if (this.allowLegacyEntity()) {
|
|
847
|
-
this.emitNumericEntity(false);
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
this.state = this.baseState;
|
|
851
|
-
}
|
|
852
|
-
this.index--;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
allowLegacyEntity() {
|
|
856
|
-
return (!this.xmlMode &&
|
|
857
|
-
(this.baseState === State$1.Text ||
|
|
858
|
-
this.baseState === State$1.InSpecialTag));
|
|
859
1123
|
}
|
|
860
1124
|
/**
|
|
861
1125
|
* Remove data that has already been consumed from the buffer.
|
|
@@ -984,44 +1248,30 @@ class Tokenizer {
|
|
|
984
1248
|
this.stateInProcessingInstruction(c);
|
|
985
1249
|
break;
|
|
986
1250
|
}
|
|
987
|
-
case State$1.
|
|
988
|
-
this.
|
|
989
|
-
break;
|
|
990
|
-
}
|
|
991
|
-
case State$1.BeforeEntity: {
|
|
992
|
-
this.stateBeforeEntity(c);
|
|
1251
|
+
case State$1.InEntity: {
|
|
1252
|
+
this.stateInEntity();
|
|
993
1253
|
break;
|
|
994
1254
|
}
|
|
995
|
-
case State$1.InHexEntity: {
|
|
996
|
-
this.stateInHexEntity(c);
|
|
997
|
-
break;
|
|
998
|
-
}
|
|
999
|
-
case State$1.InNumericEntity: {
|
|
1000
|
-
this.stateInNumericEntity(c);
|
|
1001
|
-
break;
|
|
1002
|
-
}
|
|
1003
|
-
default: {
|
|
1004
|
-
// `this._state === State.BeforeNumericEntity`
|
|
1005
|
-
this.stateBeforeNumericEntity(c);
|
|
1006
|
-
}
|
|
1007
1255
|
}
|
|
1008
1256
|
this.index++;
|
|
1009
1257
|
}
|
|
1010
1258
|
this.cleanup();
|
|
1011
1259
|
}
|
|
1012
1260
|
finish() {
|
|
1013
|
-
if (this.state === State$1.
|
|
1014
|
-
this.
|
|
1015
|
-
|
|
1016
|
-
// If there is remaining data, emit it in a reasonable way
|
|
1017
|
-
if (this.sectionStart < this.index) {
|
|
1018
|
-
this.handleTrailingData();
|
|
1261
|
+
if (this.state === State$1.InEntity) {
|
|
1262
|
+
this.entityDecoder.end();
|
|
1263
|
+
this.state = this.baseState;
|
|
1019
1264
|
}
|
|
1265
|
+
this.handleTrailingData();
|
|
1020
1266
|
this.cbs.onend();
|
|
1021
1267
|
}
|
|
1022
1268
|
/** Handle any trailing data. */
|
|
1023
1269
|
handleTrailingData() {
|
|
1024
1270
|
const endIndex = this.buffer.length + this.offset;
|
|
1271
|
+
// If there is no remaining data, we are done.
|
|
1272
|
+
if (this.sectionStart >= endIndex) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1025
1275
|
if (this.state === State$1.InCommentLike) {
|
|
1026
1276
|
if (this.currentSequence === Sequences.CdataEnd) {
|
|
1027
1277
|
this.cbs.oncdata(this.sectionStart, endIndex, 0);
|
|
@@ -1030,16 +1280,6 @@ class Tokenizer {
|
|
|
1030
1280
|
this.cbs.oncomment(this.sectionStart, endIndex, 0);
|
|
1031
1281
|
}
|
|
1032
1282
|
}
|
|
1033
|
-
else if (this.state === State$1.InNumericEntity &&
|
|
1034
|
-
this.allowLegacyEntity()) {
|
|
1035
|
-
this.emitNumericEntity(false);
|
|
1036
|
-
// All trailing data will have been consumed
|
|
1037
|
-
}
|
|
1038
|
-
else if (this.state === State$1.InHexEntity &&
|
|
1039
|
-
this.allowLegacyEntity()) {
|
|
1040
|
-
this.emitNumericEntity(false);
|
|
1041
|
-
// All trailing data will have been consumed
|
|
1042
|
-
}
|
|
1043
1283
|
else if (this.state === State$1.InTagName ||
|
|
1044
1284
|
this.state === State$1.BeforeAttributeName ||
|
|
1045
1285
|
this.state === State$1.BeforeAttributeValue ||
|
|
@@ -1053,22 +1293,23 @@ class Tokenizer {
|
|
|
1053
1293
|
this.cbs.ontext(this.sectionStart, endIndex);
|
|
1054
1294
|
}
|
|
1055
1295
|
}
|
|
1056
|
-
|
|
1057
|
-
if (this.baseState !== State$1.Text &&
|
|
1058
|
-
this.baseState !== State$1.InSpecialTag) {
|
|
1059
|
-
this.cbs.onattribdata(start, endIndex);
|
|
1060
|
-
}
|
|
1061
|
-
else {
|
|
1062
|
-
this.cbs.ontext(start, endIndex);
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
emitCodePoint(cp) {
|
|
1296
|
+
emitCodePoint(cp, consumed) {
|
|
1066
1297
|
if (this.baseState !== State$1.Text &&
|
|
1067
1298
|
this.baseState !== State$1.InSpecialTag) {
|
|
1299
|
+
if (this.sectionStart < this.entityStart) {
|
|
1300
|
+
this.cbs.onattribdata(this.sectionStart, this.entityStart);
|
|
1301
|
+
}
|
|
1302
|
+
this.sectionStart = this.entityStart + consumed;
|
|
1303
|
+
this.index = this.sectionStart - 1;
|
|
1068
1304
|
this.cbs.onattribentity(cp);
|
|
1069
1305
|
}
|
|
1070
1306
|
else {
|
|
1071
|
-
this.
|
|
1307
|
+
if (this.sectionStart < this.entityStart) {
|
|
1308
|
+
this.cbs.ontext(this.sectionStart, this.entityStart);
|
|
1309
|
+
}
|
|
1310
|
+
this.sectionStart = this.entityStart + consumed;
|
|
1311
|
+
this.index = this.sectionStart - 1;
|
|
1312
|
+
this.cbs.ontextentity(cp, this.sectionStart);
|
|
1072
1313
|
}
|
|
1073
1314
|
}
|
|
1074
1315
|
}
|
|
@@ -1187,7 +1428,6 @@ let Parser$1 = class Parser {
|
|
|
1187
1428
|
this.attribvalue = "";
|
|
1188
1429
|
this.attribs = null;
|
|
1189
1430
|
this.stack = [];
|
|
1190
|
-
this.foreignContext = [];
|
|
1191
1431
|
this.buffers = [];
|
|
1192
1432
|
this.bufferOffset = 0;
|
|
1193
1433
|
/** The index of the last written buffer. Used when resuming after a `pause()`. */
|
|
@@ -1195,10 +1435,12 @@ let Parser$1 = class Parser {
|
|
|
1195
1435
|
/** Indicates whether the parser has finished running / `.end` has been called. */
|
|
1196
1436
|
this.ended = false;
|
|
1197
1437
|
this.cbs = cbs !== null && cbs !== void 0 ? cbs : {};
|
|
1198
|
-
this.
|
|
1438
|
+
this.htmlMode = !this.options.xmlMode;
|
|
1439
|
+
this.lowerCaseTagNames = (_a = options.lowerCaseTags) !== null && _a !== void 0 ? _a : this.htmlMode;
|
|
1199
1440
|
this.lowerCaseAttributeNames =
|
|
1200
|
-
(_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b :
|
|
1441
|
+
(_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b : this.htmlMode;
|
|
1201
1442
|
this.tokenizer = new ((_c = options.Tokenizer) !== null && _c !== void 0 ? _c : Tokenizer)(this.options, this);
|
|
1443
|
+
this.foreignContext = [!this.htmlMode];
|
|
1202
1444
|
(_e = (_d = this.cbs).onparserinit) === null || _e === void 0 ? void 0 : _e.call(_d, this);
|
|
1203
1445
|
}
|
|
1204
1446
|
// Tokenizer event handlers
|
|
@@ -1211,19 +1453,18 @@ let Parser$1 = class Parser {
|
|
|
1211
1453
|
this.startIndex = endIndex;
|
|
1212
1454
|
}
|
|
1213
1455
|
/** @internal */
|
|
1214
|
-
ontextentity(cp) {
|
|
1456
|
+
ontextentity(cp, endIndex) {
|
|
1215
1457
|
var _a, _b;
|
|
1216
|
-
|
|
1217
|
-
* Entities can be emitted on the character, or directly after.
|
|
1218
|
-
* We use the section start here to get accurate indices.
|
|
1219
|
-
*/
|
|
1220
|
-
const index = this.tokenizer.getSectionStart();
|
|
1221
|
-
this.endIndex = index - 1;
|
|
1458
|
+
this.endIndex = endIndex - 1;
|
|
1222
1459
|
(_b = (_a = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a, fromCodePoint(cp));
|
|
1223
|
-
this.startIndex =
|
|
1460
|
+
this.startIndex = endIndex;
|
|
1224
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Checks if the current tag is a void element. Override this if you want
|
|
1464
|
+
* to specify your own additional void elements.
|
|
1465
|
+
*/
|
|
1225
1466
|
isVoidElement(name) {
|
|
1226
|
-
return
|
|
1467
|
+
return this.htmlMode && voidElements.has(name);
|
|
1227
1468
|
}
|
|
1228
1469
|
/** @internal */
|
|
1229
1470
|
onopentagname(start, endIndex) {
|
|
@@ -1238,21 +1479,22 @@ let Parser$1 = class Parser {
|
|
|
1238
1479
|
var _a, _b, _c, _d;
|
|
1239
1480
|
this.openTagStart = this.startIndex;
|
|
1240
1481
|
this.tagname = name;
|
|
1241
|
-
const impliesClose =
|
|
1482
|
+
const impliesClose = this.htmlMode && openImpliesClose.get(name);
|
|
1242
1483
|
if (impliesClose) {
|
|
1243
|
-
while (this.stack.length > 0 &&
|
|
1244
|
-
|
|
1245
|
-
const element = this.stack.pop();
|
|
1484
|
+
while (this.stack.length > 0 && impliesClose.has(this.stack[0])) {
|
|
1485
|
+
const element = this.stack.shift();
|
|
1246
1486
|
(_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, element, true);
|
|
1247
1487
|
}
|
|
1248
1488
|
}
|
|
1249
1489
|
if (!this.isVoidElement(name)) {
|
|
1250
|
-
this.stack.
|
|
1251
|
-
if (
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1490
|
+
this.stack.unshift(name);
|
|
1491
|
+
if (this.htmlMode) {
|
|
1492
|
+
if (foreignContextElements.has(name)) {
|
|
1493
|
+
this.foreignContext.unshift(true);
|
|
1494
|
+
}
|
|
1495
|
+
else if (htmlIntegrationElements.has(name)) {
|
|
1496
|
+
this.foreignContext.unshift(false);
|
|
1497
|
+
}
|
|
1256
1498
|
}
|
|
1257
1499
|
}
|
|
1258
1500
|
(_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, name);
|
|
@@ -1280,40 +1522,37 @@ let Parser$1 = class Parser {
|
|
|
1280
1522
|
}
|
|
1281
1523
|
/** @internal */
|
|
1282
1524
|
onclosetag(start, endIndex) {
|
|
1283
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1525
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1284
1526
|
this.endIndex = endIndex;
|
|
1285
1527
|
let name = this.getSlice(start, endIndex);
|
|
1286
1528
|
if (this.lowerCaseTagNames) {
|
|
1287
1529
|
name = name.toLowerCase();
|
|
1288
1530
|
}
|
|
1289
|
-
if (
|
|
1290
|
-
|
|
1291
|
-
|
|
1531
|
+
if (this.htmlMode &&
|
|
1532
|
+
(foreignContextElements.has(name) ||
|
|
1533
|
+
htmlIntegrationElements.has(name))) {
|
|
1534
|
+
this.foreignContext.shift();
|
|
1292
1535
|
}
|
|
1293
1536
|
if (!this.isVoidElement(name)) {
|
|
1294
|
-
const pos = this.stack.
|
|
1537
|
+
const pos = this.stack.indexOf(name);
|
|
1295
1538
|
if (pos !== -1) {
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
this.cbs.onclosetag(this.stack.pop(), count !== 0);
|
|
1301
|
-
}
|
|
1539
|
+
for (let index = 0; index <= pos; index++) {
|
|
1540
|
+
const element = this.stack.shift();
|
|
1541
|
+
// We know the stack has sufficient elements.
|
|
1542
|
+
(_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, element, index !== pos);
|
|
1302
1543
|
}
|
|
1303
|
-
else
|
|
1304
|
-
this.stack.length = pos;
|
|
1305
1544
|
}
|
|
1306
|
-
else if (
|
|
1545
|
+
else if (this.htmlMode && name === "p") {
|
|
1307
1546
|
// Implicit open before close
|
|
1308
1547
|
this.emitOpenTag("p");
|
|
1309
1548
|
this.closeCurrentTag(true);
|
|
1310
1549
|
}
|
|
1311
1550
|
}
|
|
1312
|
-
else if (
|
|
1551
|
+
else if (this.htmlMode && name === "br") {
|
|
1313
1552
|
// We can't use `emitOpenTag` for implicit open, as `br` would be implicitly closed.
|
|
1314
|
-
(
|
|
1315
|
-
(
|
|
1316
|
-
(
|
|
1553
|
+
(_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, "br");
|
|
1554
|
+
(_f = (_e = this.cbs).onopentag) === null || _f === void 0 ? void 0 : _f.call(_e, "br", {}, true);
|
|
1555
|
+
(_h = (_g = this.cbs).onclosetag) === null || _h === void 0 ? void 0 : _h.call(_g, "br", false);
|
|
1317
1556
|
}
|
|
1318
1557
|
// Set `startIndex` for next node
|
|
1319
1558
|
this.startIndex = endIndex + 1;
|
|
@@ -1321,9 +1560,7 @@ let Parser$1 = class Parser {
|
|
|
1321
1560
|
/** @internal */
|
|
1322
1561
|
onselfclosingtag(endIndex) {
|
|
1323
1562
|
this.endIndex = endIndex;
|
|
1324
|
-
if (this.options.
|
|
1325
|
-
this.options.recognizeSelfClosing ||
|
|
1326
|
-
this.foreignContext[this.foreignContext.length - 1]) {
|
|
1563
|
+
if (this.options.recognizeSelfClosing || this.foreignContext[0]) {
|
|
1327
1564
|
this.closeCurrentTag(false);
|
|
1328
1565
|
// Set `startIndex` for next node
|
|
1329
1566
|
this.startIndex = endIndex + 1;
|
|
@@ -1338,10 +1575,10 @@ let Parser$1 = class Parser {
|
|
|
1338
1575
|
const name = this.tagname;
|
|
1339
1576
|
this.endOpenTag(isOpenImplied);
|
|
1340
1577
|
// Self-closing tags will be on the top of the stack
|
|
1341
|
-
if (this.stack[
|
|
1578
|
+
if (this.stack[0] === name) {
|
|
1342
1579
|
// If the opening tag isn't implied, the closing tag has to be implied.
|
|
1343
1580
|
(_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, name, !isOpenImplied);
|
|
1344
|
-
this.stack.
|
|
1581
|
+
this.stack.shift();
|
|
1345
1582
|
}
|
|
1346
1583
|
}
|
|
1347
1584
|
/** @internal */
|
|
@@ -1421,7 +1658,7 @@ let Parser$1 = class Parser {
|
|
|
1421
1658
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
1422
1659
|
this.endIndex = endIndex;
|
|
1423
1660
|
const value = this.getSlice(start, endIndex - offset);
|
|
1424
|
-
if (this.
|
|
1661
|
+
if (!this.htmlMode || this.options.recognizeCDATA) {
|
|
1425
1662
|
(_b = (_a = this.cbs).oncdatastart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1426
1663
|
(_d = (_c = this.cbs).ontext) === null || _d === void 0 ? void 0 : _d.call(_c, value);
|
|
1427
1664
|
(_f = (_e = this.cbs).oncdataend) === null || _f === void 0 ? void 0 : _f.call(_e);
|
|
@@ -1439,8 +1676,9 @@ let Parser$1 = class Parser {
|
|
|
1439
1676
|
if (this.cbs.onclosetag) {
|
|
1440
1677
|
// Set the end index for all remaining tags
|
|
1441
1678
|
this.endIndex = this.startIndex;
|
|
1442
|
-
for (let index =
|
|
1443
|
-
;
|
|
1679
|
+
for (let index = 0; index < this.stack.length; index++) {
|
|
1680
|
+
this.cbs.onclosetag(this.stack[index], true);
|
|
1681
|
+
}
|
|
1444
1682
|
}
|
|
1445
1683
|
(_b = (_a = this.cbs).onend) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1446
1684
|
}
|
|
@@ -1459,6 +1697,8 @@ let Parser$1 = class Parser {
|
|
|
1459
1697
|
this.endIndex = 0;
|
|
1460
1698
|
(_d = (_c = this.cbs).onparserinit) === null || _d === void 0 ? void 0 : _d.call(_c, this);
|
|
1461
1699
|
this.buffers.length = 0;
|
|
1700
|
+
this.foreignContext.length = 0;
|
|
1701
|
+
this.foreignContext.unshift(!this.htmlMode);
|
|
1462
1702
|
this.bufferOffset = 0;
|
|
1463
1703
|
this.writeIndex = 0;
|
|
1464
1704
|
this.ended = false;
|
|
@@ -2140,6 +2380,16 @@ function encodeXML(str) {
|
|
|
2140
2380
|
}
|
|
2141
2381
|
return ret + str.substr(lastIdx);
|
|
2142
2382
|
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Creates a function that escapes all characters matched by the given regular
|
|
2385
|
+
* expression using the given map of characters to escape to their entities.
|
|
2386
|
+
*
|
|
2387
|
+
* @param regex Regular expression to match characters to escape.
|
|
2388
|
+
* @param map Map of characters to escape to their entities.
|
|
2389
|
+
*
|
|
2390
|
+
* @returns Function that escapes all characters matched by the given regular
|
|
2391
|
+
* expression using the given map of characters to escape to their entities.
|
|
2392
|
+
*/
|
|
2143
2393
|
function getEscaper(regex, map) {
|
|
2144
2394
|
return function escape(data) {
|
|
2145
2395
|
let match;
|
|
@@ -2149,7 +2399,7 @@ function getEscaper(regex, map) {
|
|
|
2149
2399
|
if (lastIdx !== match.index) {
|
|
2150
2400
|
result += data.substring(lastIdx, match.index);
|
|
2151
2401
|
}
|
|
2152
|
-
// We know that this
|
|
2402
|
+
// We know that this character will be in the map.
|
|
2153
2403
|
result += map.get(match[0].charCodeAt(0));
|
|
2154
2404
|
// Every match will be of length 1
|
|
2155
2405
|
lastIdx = match.index + 1;
|
|
@@ -2486,7 +2736,7 @@ function getInnerHTML(node, options) {
|
|
|
2486
2736
|
: "";
|
|
2487
2737
|
}
|
|
2488
2738
|
/**
|
|
2489
|
-
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags.
|
|
2739
|
+
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments.
|
|
2490
2740
|
*
|
|
2491
2741
|
* @category Stringify
|
|
2492
2742
|
* @deprecated Use `textContent` instead.
|
|
@@ -2505,7 +2755,7 @@ function getText(node) {
|
|
|
2505
2755
|
return "";
|
|
2506
2756
|
}
|
|
2507
2757
|
/**
|
|
2508
|
-
* Get a node's text content.
|
|
2758
|
+
* Get a node's text content. Ignores comments.
|
|
2509
2759
|
*
|
|
2510
2760
|
* @category Stringify
|
|
2511
2761
|
* @param node Node to get the text content of.
|
|
@@ -2523,7 +2773,7 @@ function textContent(node) {
|
|
|
2523
2773
|
return "";
|
|
2524
2774
|
}
|
|
2525
2775
|
/**
|
|
2526
|
-
* Get a node's inner text.
|
|
2776
|
+
* Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments.
|
|
2527
2777
|
*
|
|
2528
2778
|
* @category Stringify
|
|
2529
2779
|
* @param node Node to get the inner text of.
|
|
@@ -2556,7 +2806,7 @@ function getChildren(elem) {
|
|
|
2556
2806
|
*
|
|
2557
2807
|
* @category Traversal
|
|
2558
2808
|
* @param elem Node to get the parent of.
|
|
2559
|
-
* @returns `elem`'s parent node.
|
|
2809
|
+
* @returns `elem`'s parent node, or `null` if `elem` is a root node.
|
|
2560
2810
|
*/
|
|
2561
2811
|
function getParent(elem) {
|
|
2562
2812
|
return elem.parent || null;
|
|
@@ -2570,7 +2820,7 @@ function getParent(elem) {
|
|
|
2570
2820
|
*
|
|
2571
2821
|
* @category Traversal
|
|
2572
2822
|
* @param elem Element to get the siblings of.
|
|
2573
|
-
* @returns `elem`'s siblings
|
|
2823
|
+
* @returns `elem`'s siblings, including `elem`.
|
|
2574
2824
|
*/
|
|
2575
2825
|
function getSiblings(elem) {
|
|
2576
2826
|
const parent = getParent(elem);
|
|
@@ -2628,7 +2878,8 @@ function getName(elem) {
|
|
|
2628
2878
|
*
|
|
2629
2879
|
* @category Traversal
|
|
2630
2880
|
* @param elem The element to get the next sibling of.
|
|
2631
|
-
* @returns `elem`'s next sibling that is a tag
|
|
2881
|
+
* @returns `elem`'s next sibling that is a tag, or `null` if there is no next
|
|
2882
|
+
* sibling.
|
|
2632
2883
|
*/
|
|
2633
2884
|
function nextElementSibling(elem) {
|
|
2634
2885
|
let { next } = elem;
|
|
@@ -2641,7 +2892,8 @@ function nextElementSibling(elem) {
|
|
|
2641
2892
|
*
|
|
2642
2893
|
* @category Traversal
|
|
2643
2894
|
* @param elem The element to get the previous sibling of.
|
|
2644
|
-
* @returns `elem`'s previous sibling that is a tag
|
|
2895
|
+
* @returns `elem`'s previous sibling that is a tag, or `null` if there is no
|
|
2896
|
+
* previous sibling.
|
|
2645
2897
|
*/
|
|
2646
2898
|
function prevElementSibling(elem) {
|
|
2647
2899
|
let { prev } = elem;
|
|
@@ -2663,8 +2915,14 @@ function removeElement(elem) {
|
|
|
2663
2915
|
elem.next.prev = elem.prev;
|
|
2664
2916
|
if (elem.parent) {
|
|
2665
2917
|
const childs = elem.parent.children;
|
|
2666
|
-
childs.
|
|
2918
|
+
const childsIndex = childs.lastIndexOf(elem);
|
|
2919
|
+
if (childsIndex >= 0) {
|
|
2920
|
+
childs.splice(childsIndex, 1);
|
|
2921
|
+
}
|
|
2667
2922
|
}
|
|
2923
|
+
elem.next = null;
|
|
2924
|
+
elem.prev = null;
|
|
2925
|
+
elem.parent = null;
|
|
2668
2926
|
}
|
|
2669
2927
|
/**
|
|
2670
2928
|
* Replace an element in the dom
|
|
@@ -2693,15 +2951,15 @@ function replaceElement(elem, replacement) {
|
|
|
2693
2951
|
* Append a child to an element.
|
|
2694
2952
|
*
|
|
2695
2953
|
* @category Manipulation
|
|
2696
|
-
* @param
|
|
2954
|
+
* @param parent The element to append to.
|
|
2697
2955
|
* @param child The element to be added as a child.
|
|
2698
2956
|
*/
|
|
2699
|
-
function appendChild(
|
|
2957
|
+
function appendChild(parent, child) {
|
|
2700
2958
|
removeElement(child);
|
|
2701
2959
|
child.next = null;
|
|
2702
|
-
child.parent =
|
|
2703
|
-
if (
|
|
2704
|
-
const sibling =
|
|
2960
|
+
child.parent = parent;
|
|
2961
|
+
if (parent.children.push(child) > 1) {
|
|
2962
|
+
const sibling = parent.children[parent.children.length - 2];
|
|
2705
2963
|
sibling.next = child;
|
|
2706
2964
|
child.prev = sibling;
|
|
2707
2965
|
}
|
|
@@ -2739,15 +2997,15 @@ function append(elem, next) {
|
|
|
2739
2997
|
* Prepend a child to an element.
|
|
2740
2998
|
*
|
|
2741
2999
|
* @category Manipulation
|
|
2742
|
-
* @param
|
|
3000
|
+
* @param parent The element to prepend before.
|
|
2743
3001
|
* @param child The element to be added as a child.
|
|
2744
3002
|
*/
|
|
2745
|
-
function prependChild(
|
|
3003
|
+
function prependChild(parent, child) {
|
|
2746
3004
|
removeElement(child);
|
|
2747
|
-
child.parent =
|
|
3005
|
+
child.parent = parent;
|
|
2748
3006
|
child.prev = null;
|
|
2749
|
-
if (
|
|
2750
|
-
const sibling =
|
|
3007
|
+
if (parent.children.unshift(child) !== 1) {
|
|
3008
|
+
const sibling = parent.children[1];
|
|
2751
3009
|
sibling.prev = child;
|
|
2752
3010
|
child.next = sibling;
|
|
2753
3011
|
}
|
|
@@ -2779,7 +3037,7 @@ function prepend(elem, prev) {
|
|
|
2779
3037
|
}
|
|
2780
3038
|
|
|
2781
3039
|
/**
|
|
2782
|
-
* Search a node and its children for nodes passing a test function.
|
|
3040
|
+
* Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one.
|
|
2783
3041
|
*
|
|
2784
3042
|
* @category Querying
|
|
2785
3043
|
* @param test Function to test nodes on.
|
|
@@ -2789,12 +3047,10 @@ function prepend(elem, prev) {
|
|
|
2789
3047
|
* @returns All nodes passing `test`.
|
|
2790
3048
|
*/
|
|
2791
3049
|
function filter(test, node, recurse = true, limit = Infinity) {
|
|
2792
|
-
|
|
2793
|
-
node = [node];
|
|
2794
|
-
return find(test, node, recurse, limit);
|
|
3050
|
+
return find(test, Array.isArray(node) ? node : [node], recurse, limit);
|
|
2795
3051
|
}
|
|
2796
3052
|
/**
|
|
2797
|
-
* Search an array of
|
|
3053
|
+
* Search an array of nodes and their children for nodes passing a test function.
|
|
2798
3054
|
*
|
|
2799
3055
|
* @category Querying
|
|
2800
3056
|
* @param test Function to test nodes on.
|
|
@@ -2805,24 +3061,41 @@ function filter(test, node, recurse = true, limit = Infinity) {
|
|
|
2805
3061
|
*/
|
|
2806
3062
|
function find(test, nodes, recurse, limit) {
|
|
2807
3063
|
const result = [];
|
|
2808
|
-
|
|
3064
|
+
/** Stack of the arrays we are looking at. */
|
|
3065
|
+
const nodeStack = [nodes];
|
|
3066
|
+
/** Stack of the indices within the arrays. */
|
|
3067
|
+
const indexStack = [0];
|
|
3068
|
+
for (;;) {
|
|
3069
|
+
// First, check if the current array has any more elements to look at.
|
|
3070
|
+
if (indexStack[0] >= nodeStack[0].length) {
|
|
3071
|
+
// If we have no more arrays to look at, we are done.
|
|
3072
|
+
if (indexStack.length === 1) {
|
|
3073
|
+
return result;
|
|
3074
|
+
}
|
|
3075
|
+
// Otherwise, remove the current array from the stack.
|
|
3076
|
+
nodeStack.shift();
|
|
3077
|
+
indexStack.shift();
|
|
3078
|
+
// Loop back to the start to continue with the next array.
|
|
3079
|
+
continue;
|
|
3080
|
+
}
|
|
3081
|
+
const elem = nodeStack[0][indexStack[0]++];
|
|
2809
3082
|
if (test(elem)) {
|
|
2810
3083
|
result.push(elem);
|
|
2811
3084
|
if (--limit <= 0)
|
|
2812
|
-
|
|
3085
|
+
return result;
|
|
2813
3086
|
}
|
|
2814
3087
|
if (recurse && hasChildren(elem) && elem.children.length > 0) {
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
3088
|
+
/*
|
|
3089
|
+
* Add the children to the stack. We are depth-first, so this is
|
|
3090
|
+
* the next array we look at.
|
|
3091
|
+
*/
|
|
3092
|
+
indexStack.unshift(0);
|
|
3093
|
+
nodeStack.unshift(elem.children);
|
|
2820
3094
|
}
|
|
2821
3095
|
}
|
|
2822
|
-
return result;
|
|
2823
3096
|
}
|
|
2824
3097
|
/**
|
|
2825
|
-
* Finds the first element inside of an array that matches a test function.
|
|
3098
|
+
* Finds the first element inside of an array that matches a test function. This is an alias for `Array.prototype.find`.
|
|
2826
3099
|
*
|
|
2827
3100
|
* @category Querying
|
|
2828
3101
|
* @param test Function to test nodes on.
|
|
@@ -2838,27 +3111,29 @@ function findOneChild(test, nodes) {
|
|
|
2838
3111
|
*
|
|
2839
3112
|
* @category Querying
|
|
2840
3113
|
* @param test Function to test nodes on.
|
|
2841
|
-
* @param nodes
|
|
3114
|
+
* @param nodes Node or array of nodes to search.
|
|
2842
3115
|
* @param recurse Also consider child nodes.
|
|
2843
|
-
* @returns The first
|
|
3116
|
+
* @returns The first node that passes `test`.
|
|
2844
3117
|
*/
|
|
2845
3118
|
function findOne(test, nodes, recurse = true) {
|
|
2846
3119
|
let elem = null;
|
|
2847
3120
|
for (let i = 0; i < nodes.length && !elem; i++) {
|
|
2848
|
-
const
|
|
2849
|
-
if (!isTag(
|
|
3121
|
+
const node = nodes[i];
|
|
3122
|
+
if (!isTag(node)) {
|
|
2850
3123
|
continue;
|
|
2851
3124
|
}
|
|
2852
|
-
else if (test(
|
|
2853
|
-
elem =
|
|
3125
|
+
else if (test(node)) {
|
|
3126
|
+
elem = node;
|
|
2854
3127
|
}
|
|
2855
|
-
else if (recurse &&
|
|
2856
|
-
elem = findOne(test,
|
|
3128
|
+
else if (recurse && node.children.length > 0) {
|
|
3129
|
+
elem = findOne(test, node.children, true);
|
|
2857
3130
|
}
|
|
2858
3131
|
}
|
|
2859
3132
|
return elem;
|
|
2860
3133
|
}
|
|
2861
3134
|
/**
|
|
3135
|
+
* Checks if a tree of nodes contains at least one node passing a test.
|
|
3136
|
+
*
|
|
2862
3137
|
* @category Querying
|
|
2863
3138
|
* @param test Function to test nodes on.
|
|
2864
3139
|
* @param nodes Array of nodes to search.
|
|
@@ -2866,12 +3141,10 @@ function findOne(test, nodes, recurse = true) {
|
|
|
2866
3141
|
*/
|
|
2867
3142
|
function existsOne(test, nodes) {
|
|
2868
3143
|
return nodes.some((checked) => isTag(checked) &&
|
|
2869
|
-
(test(checked) ||
|
|
2870
|
-
(checked.children.length > 0 &&
|
|
2871
|
-
existsOne(test, checked.children))));
|
|
3144
|
+
(test(checked) || existsOne(test, checked.children)));
|
|
2872
3145
|
}
|
|
2873
3146
|
/**
|
|
2874
|
-
* Search
|
|
3147
|
+
* Search an array of nodes and their children for elements passing a test function.
|
|
2875
3148
|
*
|
|
2876
3149
|
* Same as `find`, but limited to elements and with less options, leading to reduced complexity.
|
|
2877
3150
|
*
|
|
@@ -2881,21 +3154,35 @@ function existsOne(test, nodes) {
|
|
|
2881
3154
|
* @returns All nodes passing `test`.
|
|
2882
3155
|
*/
|
|
2883
3156
|
function findAll(test, nodes) {
|
|
2884
|
-
var _a;
|
|
2885
3157
|
const result = [];
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
3158
|
+
const nodeStack = [nodes];
|
|
3159
|
+
const indexStack = [0];
|
|
3160
|
+
for (;;) {
|
|
3161
|
+
if (indexStack[0] >= nodeStack[0].length) {
|
|
3162
|
+
if (nodeStack.length === 1) {
|
|
3163
|
+
return result;
|
|
3164
|
+
}
|
|
3165
|
+
// Otherwise, remove the current array from the stack.
|
|
3166
|
+
nodeStack.shift();
|
|
3167
|
+
indexStack.shift();
|
|
3168
|
+
// Loop back to the start to continue with the next array.
|
|
3169
|
+
continue;
|
|
2892
3170
|
}
|
|
3171
|
+
const elem = nodeStack[0][indexStack[0]++];
|
|
3172
|
+
if (!isTag(elem))
|
|
3173
|
+
continue;
|
|
2893
3174
|
if (test(elem))
|
|
2894
3175
|
result.push(elem);
|
|
3176
|
+
if (elem.children.length > 0) {
|
|
3177
|
+
indexStack.unshift(0);
|
|
3178
|
+
nodeStack.unshift(elem.children);
|
|
3179
|
+
}
|
|
2895
3180
|
}
|
|
2896
|
-
return result;
|
|
2897
3181
|
}
|
|
2898
3182
|
|
|
3183
|
+
/**
|
|
3184
|
+
* A map of functions to check nodes against.
|
|
3185
|
+
*/
|
|
2899
3186
|
const Checks = {
|
|
2900
3187
|
tag_name(name) {
|
|
2901
3188
|
if (typeof name === "function") {
|
|
@@ -2920,6 +3207,9 @@ const Checks = {
|
|
|
2920
3207
|
},
|
|
2921
3208
|
};
|
|
2922
3209
|
/**
|
|
3210
|
+
* Returns a function to check whether a node has an attribute with a particular
|
|
3211
|
+
* value.
|
|
3212
|
+
*
|
|
2923
3213
|
* @param attrib Attribute to check.
|
|
2924
3214
|
* @param value Attribute value to look for.
|
|
2925
3215
|
* @returns A function to check whether the a node has an attribute with a
|
|
@@ -2932,6 +3222,9 @@ function getAttribCheck(attrib, value) {
|
|
|
2932
3222
|
return (elem) => isTag(elem) && elem.attribs[attrib] === value;
|
|
2933
3223
|
}
|
|
2934
3224
|
/**
|
|
3225
|
+
* Returns a function that returns `true` if either of the input functions
|
|
3226
|
+
* returns `true` for a node.
|
|
3227
|
+
*
|
|
2935
3228
|
* @param a First function to combine.
|
|
2936
3229
|
* @param b Second function to combine.
|
|
2937
3230
|
* @returns A function taking a node and returning `true` if either of the input
|
|
@@ -2941,9 +3234,12 @@ function combineFuncs(a, b) {
|
|
|
2941
3234
|
return (elem) => a(elem) || b(elem);
|
|
2942
3235
|
}
|
|
2943
3236
|
/**
|
|
3237
|
+
* Returns a function that executes all checks in `options` and returns `true`
|
|
3238
|
+
* if any of them match a node.
|
|
3239
|
+
*
|
|
2944
3240
|
* @param options An object describing nodes to look for.
|
|
2945
|
-
* @returns A function
|
|
2946
|
-
* any of them match a node.
|
|
3241
|
+
* @returns A function that executes all checks in `options` and returns `true`
|
|
3242
|
+
* if any of them match a node.
|
|
2947
3243
|
*/
|
|
2948
3244
|
function compileTest(options) {
|
|
2949
3245
|
const funcs = Object.keys(options).map((key) => {
|
|
@@ -2955,6 +3251,8 @@ function compileTest(options) {
|
|
|
2955
3251
|
return funcs.length === 0 ? null : funcs.reduce(combineFuncs);
|
|
2956
3252
|
}
|
|
2957
3253
|
/**
|
|
3254
|
+
* Checks whether a node matches the description in `options`.
|
|
3255
|
+
*
|
|
2958
3256
|
* @category Legacy Query Functions
|
|
2959
3257
|
* @param options An object describing nodes to look for.
|
|
2960
3258
|
* @param node The element to test.
|
|
@@ -2965,6 +3263,8 @@ function testElement(options, node) {
|
|
|
2965
3263
|
return test ? test(node) : true;
|
|
2966
3264
|
}
|
|
2967
3265
|
/**
|
|
3266
|
+
* Returns all nodes that match `options`.
|
|
3267
|
+
*
|
|
2968
3268
|
* @category Legacy Query Functions
|
|
2969
3269
|
* @param options An object describing nodes to look for.
|
|
2970
3270
|
* @param nodes Nodes to search through.
|
|
@@ -2977,6 +3277,8 @@ function getElements(options, nodes, recurse, limit = Infinity) {
|
|
|
2977
3277
|
return test ? filter(test, nodes, recurse, limit) : [];
|
|
2978
3278
|
}
|
|
2979
3279
|
/**
|
|
3280
|
+
* Returns the node with the supplied ID.
|
|
3281
|
+
*
|
|
2980
3282
|
* @category Legacy Query Functions
|
|
2981
3283
|
* @param id The unique ID attribute value to look for.
|
|
2982
3284
|
* @param nodes Nodes to search through.
|
|
@@ -2989,6 +3291,8 @@ function getElementById(id, nodes, recurse = true) {
|
|
|
2989
3291
|
return findOne(getAttribCheck("id", id), nodes, recurse);
|
|
2990
3292
|
}
|
|
2991
3293
|
/**
|
|
3294
|
+
* Returns all nodes with the supplied `tagName`.
|
|
3295
|
+
*
|
|
2992
3296
|
* @category Legacy Query Functions
|
|
2993
3297
|
* @param tagName Tag name to search for.
|
|
2994
3298
|
* @param nodes Nodes to search through.
|
|
@@ -3000,6 +3304,8 @@ function getElementsByTagName(tagName, nodes, recurse = true, limit = Infinity)
|
|
|
3000
3304
|
return filter(Checks["tag_name"](tagName), nodes, recurse, limit);
|
|
3001
3305
|
}
|
|
3002
3306
|
/**
|
|
3307
|
+
* Returns all nodes with the supplied `type`.
|
|
3308
|
+
*
|
|
3003
3309
|
* @category Legacy Query Functions
|
|
3004
3310
|
* @param type Element type to look for.
|
|
3005
3311
|
* @param nodes Nodes to search through.
|
|
@@ -3012,11 +3318,12 @@ function getElementsByTagType(type, nodes, recurse = true, limit = Infinity) {
|
|
|
3012
3318
|
}
|
|
3013
3319
|
|
|
3014
3320
|
/**
|
|
3015
|
-
* Given an array of nodes, remove any member that is contained by another
|
|
3321
|
+
* Given an array of nodes, remove any member that is contained by another
|
|
3322
|
+
* member.
|
|
3016
3323
|
*
|
|
3017
3324
|
* @category Helpers
|
|
3018
3325
|
* @param nodes Nodes to filter.
|
|
3019
|
-
* @returns Remaining nodes that aren't
|
|
3326
|
+
* @returns Remaining nodes that aren't contained by other nodes.
|
|
3020
3327
|
*/
|
|
3021
3328
|
function removeSubsets(nodes) {
|
|
3022
3329
|
let idx = nodes.length;
|
|
@@ -3057,8 +3364,8 @@ var DocumentPosition;
|
|
|
3057
3364
|
DocumentPosition[DocumentPosition["CONTAINED_BY"] = 16] = "CONTAINED_BY";
|
|
3058
3365
|
})(DocumentPosition || (DocumentPosition = {}));
|
|
3059
3366
|
/**
|
|
3060
|
-
* Compare the position of one node against another node in any other document
|
|
3061
|
-
*
|
|
3367
|
+
* Compare the position of one node against another node in any other document,
|
|
3368
|
+
* returning a bitmask with the values from {@link DocumentPosition}.
|
|
3062
3369
|
*
|
|
3063
3370
|
* Document order:
|
|
3064
3371
|
* > There is an ordering, document order, defined on all the nodes in the
|
|
@@ -3122,9 +3429,9 @@ function compareDocumentPosition(nodeA, nodeB) {
|
|
|
3122
3429
|
return DocumentPosition.PRECEDING;
|
|
3123
3430
|
}
|
|
3124
3431
|
/**
|
|
3125
|
-
* Sort an array of nodes based on their relative position in the document
|
|
3126
|
-
*
|
|
3127
|
-
* the same document, sort order is unspecified.
|
|
3432
|
+
* Sort an array of nodes based on their relative position in the document,
|
|
3433
|
+
* removing any duplicate nodes. If the array contains nodes that do not belong
|
|
3434
|
+
* to the same document, sort order is unspecified.
|
|
3128
3435
|
*
|
|
3129
3436
|
* @category Helpers
|
|
3130
3437
|
* @param nodes Array of DOM nodes.
|
|
@@ -3225,7 +3532,7 @@ function getRssFeed(feedRoot) {
|
|
|
3225
3532
|
addConditionally(entry, "title", "title", children);
|
|
3226
3533
|
addConditionally(entry, "link", "link", children);
|
|
3227
3534
|
addConditionally(entry, "description", "description", children);
|
|
3228
|
-
const pubDate = fetch("pubDate", children);
|
|
3535
|
+
const pubDate = fetch("pubDate", children) || fetch("dc:date", children);
|
|
3229
3536
|
if (pubDate)
|
|
3230
3537
|
entry.pubDate = new Date(pubDate);
|
|
3231
3538
|
return entry;
|
|
@@ -3376,7 +3683,7 @@ var DomUtils = /*#__PURE__*/Object.freeze({
|
|
|
3376
3683
|
* Parses the data, returns the resulting document.
|
|
3377
3684
|
*
|
|
3378
3685
|
* @param data The data that should be parsed.
|
|
3379
|
-
* @param options Optional options for the parser and DOM
|
|
3686
|
+
* @param options Optional options for the parser and DOM handler.
|
|
3380
3687
|
*/
|
|
3381
3688
|
function parseDocument(data, options) {
|
|
3382
3689
|
const handler = new DomHandler(undefined, options);
|
|
@@ -10545,6 +10852,7 @@ function parse(input, options) {
|
|
|
10545
10852
|
|
|
10546
10853
|
// AST walker module for Mozilla Parser API compatible trees
|
|
10547
10854
|
|
|
10855
|
+
|
|
10548
10856
|
// An ancestor walk keeps an array of ancestor nodes (including the
|
|
10549
10857
|
// current node) and passes them to the callback as third parameter
|
|
10550
10858
|
// (and also as state parameter when no other state is present).
|
|
@@ -12020,13 +12328,14 @@ const HASH_CHARACTER_LENGTH = 16;
|
|
|
12020
12328
|
* finding the asset urls in the AST assumes that there will be no
|
|
12021
12329
|
* user code with the same structure as the asset urls. In other words:
|
|
12022
12330
|
* Don't use a define a property named "canvasKitWasmUrl" that refers to
|
|
12023
|
-
* a string literal, don't define a property named "
|
|
12024
|
-
* to an array expression of
|
|
12025
|
-
*
|
|
12026
|
-
*
|
|
12027
|
-
*
|
|
12028
|
-
*
|
|
12029
|
-
*
|
|
12331
|
+
* a string literal, don't define a property named "fonts" that that refers
|
|
12332
|
+
* to an array expression of object expressions with properties named
|
|
12333
|
+
* "fontName" and "url", and don't define a property called "images" that
|
|
12334
|
+
* refers to an array expression of object expressions with properties named
|
|
12335
|
+
* "imageName", "height", "width", and "url". Otherwise, this plugin may alter
|
|
12336
|
+
* your code in unexpected ways (although most likely it will simply give
|
|
12337
|
+
* warnings, because it's unlikely there will be valid file assets that will
|
|
12338
|
+
* be found and hashed).
|
|
12030
12339
|
*
|
|
12031
12340
|
* @param rootDir - root directory of build, usually "dist" because you
|
|
12032
12341
|
* usually hash only production builds
|
|
@@ -12071,10 +12380,16 @@ function hashM2c2kitAssets(rootDir) {
|
|
|
12071
12380
|
const literal = node;
|
|
12072
12381
|
const originalUrlValue = literal.value;
|
|
12073
12382
|
try {
|
|
12074
|
-
const hashedUrlValue = addHashToUrl(originalUrlValue,
|
|
12383
|
+
const hashedUrlValue = addHashToUrl(originalUrlValue,
|
|
12384
|
+
/**
|
|
12385
|
+
* by our convention, the wasm file will be served from
|
|
12386
|
+
* the assets directory, so the location is
|
|
12387
|
+
* `assets/${canvasKitWasmUrl}` not `${canvasKitWasmUrl}`
|
|
12388
|
+
*/
|
|
12389
|
+
`${rootDir}/assets`);
|
|
12075
12390
|
literal.value = literal.value.replace(originalUrlValue, hashedUrlValue);
|
|
12076
12391
|
literal.raw = literal.raw.replace(originalUrlValue, hashedUrlValue);
|
|
12077
|
-
addFileToFilesToBeRenamed(rootDir
|
|
12392
|
+
addFileToFilesToBeRenamed(`${rootDir}/assets`, originalUrlValue, hashedUrlValue, fileRenames);
|
|
12078
12393
|
}
|
|
12079
12394
|
catch (_a) {
|
|
12080
12395
|
console.log(`warning: could not hash canvaskit.wasm resource because it was not found at ${originalUrlValue}`);
|
|
@@ -12082,25 +12397,48 @@ function hashM2c2kitAssets(rootDir) {
|
|
|
12082
12397
|
}
|
|
12083
12398
|
}
|
|
12084
12399
|
}
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
const
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12400
|
+
// we'll be looking back 5 levels
|
|
12401
|
+
if (ancestors.length >= 5) {
|
|
12402
|
+
const maybeProperty = ancestors.slice(-2)[0];
|
|
12403
|
+
if (maybeProperty.type === "Property") {
|
|
12404
|
+
const property = maybeProperty;
|
|
12405
|
+
if (property.key.type === "Identifier" &&
|
|
12406
|
+
property.key.name == "url") {
|
|
12407
|
+
// property is url
|
|
12408
|
+
const maybeObjExpression = ancestors.slice(-3)[0];
|
|
12409
|
+
if (maybeObjExpression.type === "ObjectExpression") {
|
|
12410
|
+
const objExpression = maybeObjExpression;
|
|
12411
|
+
const properties = objExpression.properties
|
|
12412
|
+
.filter((p) => p.type === "Property")
|
|
12413
|
+
.map((p) => p);
|
|
12414
|
+
const identifiers = properties
|
|
12415
|
+
.filter((p) => p.key.type === "Identifier")
|
|
12416
|
+
.map((p) => p.key.name);
|
|
12417
|
+
const urlFontAssetProperties = ["fontName", "url"];
|
|
12418
|
+
const propCount = identifiers.filter((i) => urlFontAssetProperties.indexOf(i) !== -1).length;
|
|
12419
|
+
if (propCount === 2) {
|
|
12420
|
+
// the object expression has the 2 properties
|
|
12421
|
+
const maybeArrayExpression = ancestors.slice(-4)[0];
|
|
12422
|
+
if (maybeArrayExpression.type === "ArrayExpression") {
|
|
12423
|
+
const maybeProperty = ancestors.slice(-5)[0];
|
|
12424
|
+
if (maybeProperty.type === "Property") {
|
|
12425
|
+
const property = maybeProperty;
|
|
12426
|
+
if (property.key.type === "Identifier" &&
|
|
12427
|
+
property.key.name == "fonts") {
|
|
12428
|
+
// property is fonts
|
|
12429
|
+
const literal = node;
|
|
12430
|
+
const originalUrlValue = literal.value;
|
|
12431
|
+
try {
|
|
12432
|
+
const hashedUrlValue = addHashToUrl(originalUrlValue, rootDir);
|
|
12433
|
+
literal.value = literal.value.replace(originalUrlValue, hashedUrlValue);
|
|
12434
|
+
literal.raw = literal.raw.replace(originalUrlValue, hashedUrlValue);
|
|
12435
|
+
addFileToFilesToBeRenamed(rootDir, originalUrlValue, hashedUrlValue, fileRenames);
|
|
12436
|
+
}
|
|
12437
|
+
catch (_b) {
|
|
12438
|
+
}
|
|
12439
|
+
}
|
|
12440
|
+
}
|
|
12441
|
+
}
|
|
12104
12442
|
}
|
|
12105
12443
|
}
|
|
12106
12444
|
}
|