parse5 3.0.3 → 5.1.0

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.
Files changed (43) hide show
  1. package/README.md +14 -16
  2. package/lib/common/doctype.js +125 -108
  3. package/lib/common/error-codes.js +65 -0
  4. package/lib/common/foreign-content.js +265 -0
  5. package/lib/common/html.js +102 -102
  6. package/lib/common/unicode.js +81 -19
  7. package/lib/extensions/error-reporting/mixin-base.js +43 -0
  8. package/lib/extensions/error-reporting/parser-mixin.js +52 -0
  9. package/lib/extensions/error-reporting/preprocessor-mixin.js +24 -0
  10. package/lib/extensions/error-reporting/tokenizer-mixin.js +17 -0
  11. package/lib/extensions/location-info/open-element-stack-mixin.js +35 -0
  12. package/lib/extensions/location-info/parser-mixin.js +222 -0
  13. package/lib/extensions/location-info/tokenizer-mixin.js +146 -0
  14. package/lib/extensions/position-tracking/preprocessor-mixin.js +64 -0
  15. package/lib/index.js +6 -21
  16. package/lib/parser/formatting-element-list.js +181 -0
  17. package/lib/parser/index.js +1469 -1332
  18. package/lib/parser/open-element-stack.js +482 -0
  19. package/lib/serializer/index.js +136 -122
  20. package/lib/tokenizer/index.js +1856 -1804
  21. package/lib/tokenizer/{named_entity_data.js → named-entity-data.js} +1 -1
  22. package/lib/tokenizer/preprocessor.js +115 -103
  23. package/lib/tree-adapters/default.js +217 -0
  24. package/lib/utils/{merge_options.js → merge-options.js} +2 -2
  25. package/lib/utils/mixin.js +33 -12
  26. package/package.json +32 -61
  27. package/LICENSE +0 -19
  28. package/lib/common/foreign_content.js +0 -260
  29. package/lib/extensions/location_info/open_element_stack_mixin.js +0 -34
  30. package/lib/extensions/location_info/parser_mixin.js +0 -213
  31. package/lib/extensions/location_info/tokenizer_mixin.js +0 -117
  32. package/lib/extensions/position_tracking/preprocessor_mixin.js +0 -72
  33. package/lib/index.d.ts +0 -1077
  34. package/lib/parser/formatting_element_list.js +0 -167
  35. package/lib/parser/open_element_stack.js +0 -395
  36. package/lib/parser/parser_stream.js +0 -76
  37. package/lib/parser/plain_text_conversion_stream.js +0 -20
  38. package/lib/sax/dev_null_stream.js +0 -14
  39. package/lib/sax/index.js +0 -118
  40. package/lib/sax/parser_feedback_simulator.js +0 -153
  41. package/lib/serializer/serializer_stream.js +0 -28
  42. package/lib/tree_adapters/default.js +0 -209
  43. package/lib/tree_adapters/htmlparser2.js +0 -338
@@ -0,0 +1,482 @@
1
+ 'use strict';
2
+
3
+ const HTML = require('../common/html');
4
+
5
+ //Aliases
6
+ const $ = HTML.TAG_NAMES;
7
+ const NS = HTML.NAMESPACES;
8
+
9
+ //Element utils
10
+
11
+ //OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
12
+ //It's faster than using dictionary.
13
+ function isImpliedEndTagRequired(tn) {
14
+ switch (tn.length) {
15
+ case 1:
16
+ return tn === $.P;
17
+
18
+ case 2:
19
+ return tn === $.RB || tn === $.RP || tn === $.RT || tn === $.DD || tn === $.DT || tn === $.LI;
20
+
21
+ case 3:
22
+ return tn === $.RTC;
23
+
24
+ case 6:
25
+ return tn === $.OPTION;
26
+
27
+ case 8:
28
+ return tn === $.OPTGROUP;
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ function isImpliedEndTagRequiredThoroughly(tn) {
35
+ switch (tn.length) {
36
+ case 1:
37
+ return tn === $.P;
38
+
39
+ case 2:
40
+ return (
41
+ tn === $.RB ||
42
+ tn === $.RP ||
43
+ tn === $.RT ||
44
+ tn === $.DD ||
45
+ tn === $.DT ||
46
+ tn === $.LI ||
47
+ tn === $.TD ||
48
+ tn === $.TH ||
49
+ tn === $.TR
50
+ );
51
+
52
+ case 3:
53
+ return tn === $.RTC;
54
+
55
+ case 5:
56
+ return tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD;
57
+
58
+ case 6:
59
+ return tn === $.OPTION;
60
+
61
+ case 7:
62
+ return tn === $.CAPTION;
63
+
64
+ case 8:
65
+ return tn === $.OPTGROUP || tn === $.COLGROUP;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ function isScopingElement(tn, ns) {
72
+ switch (tn.length) {
73
+ case 2:
74
+ if (tn === $.TD || tn === $.TH) {
75
+ return ns === NS.HTML;
76
+ } else if (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS) {
77
+ return ns === NS.MATHML;
78
+ }
79
+
80
+ break;
81
+
82
+ case 4:
83
+ if (tn === $.HTML) {
84
+ return ns === NS.HTML;
85
+ } else if (tn === $.DESC) {
86
+ return ns === NS.SVG;
87
+ }
88
+
89
+ break;
90
+
91
+ case 5:
92
+ if (tn === $.TABLE) {
93
+ return ns === NS.HTML;
94
+ } else if (tn === $.MTEXT) {
95
+ return ns === NS.MATHML;
96
+ } else if (tn === $.TITLE) {
97
+ return ns === NS.SVG;
98
+ }
99
+
100
+ break;
101
+
102
+ case 6:
103
+ return (tn === $.APPLET || tn === $.OBJECT) && ns === NS.HTML;
104
+
105
+ case 7:
106
+ return (tn === $.CAPTION || tn === $.MARQUEE) && ns === NS.HTML;
107
+
108
+ case 8:
109
+ return tn === $.TEMPLATE && ns === NS.HTML;
110
+
111
+ case 13:
112
+ return tn === $.FOREIGN_OBJECT && ns === NS.SVG;
113
+
114
+ case 14:
115
+ return tn === $.ANNOTATION_XML && ns === NS.MATHML;
116
+ }
117
+
118
+ return false;
119
+ }
120
+
121
+ //Stack of open elements
122
+ class OpenElementStack {
123
+ constructor(document, treeAdapter) {
124
+ this.stackTop = -1;
125
+ this.items = [];
126
+ this.current = document;
127
+ this.currentTagName = null;
128
+ this.currentTmplContent = null;
129
+ this.tmplCount = 0;
130
+ this.treeAdapter = treeAdapter;
131
+ }
132
+
133
+ //Index of element
134
+ _indexOf(element) {
135
+ let idx = -1;
136
+
137
+ for (let i = this.stackTop; i >= 0; i--) {
138
+ if (this.items[i] === element) {
139
+ idx = i;
140
+ break;
141
+ }
142
+ }
143
+ return idx;
144
+ }
145
+
146
+ //Update current element
147
+ _isInTemplate() {
148
+ return this.currentTagName === $.TEMPLATE && this.treeAdapter.getNamespaceURI(this.current) === NS.HTML;
149
+ }
150
+
151
+ _updateCurrentElement() {
152
+ this.current = this.items[this.stackTop];
153
+ this.currentTagName = this.current && this.treeAdapter.getTagName(this.current);
154
+
155
+ this.currentTmplContent = this._isInTemplate() ? this.treeAdapter.getTemplateContent(this.current) : null;
156
+ }
157
+
158
+ //Mutations
159
+ push(element) {
160
+ this.items[++this.stackTop] = element;
161
+ this._updateCurrentElement();
162
+
163
+ if (this._isInTemplate()) {
164
+ this.tmplCount++;
165
+ }
166
+ }
167
+
168
+ pop() {
169
+ this.stackTop--;
170
+
171
+ if (this.tmplCount > 0 && this._isInTemplate()) {
172
+ this.tmplCount--;
173
+ }
174
+
175
+ this._updateCurrentElement();
176
+ }
177
+
178
+ replace(oldElement, newElement) {
179
+ const idx = this._indexOf(oldElement);
180
+
181
+ this.items[idx] = newElement;
182
+
183
+ if (idx === this.stackTop) {
184
+ this._updateCurrentElement();
185
+ }
186
+ }
187
+
188
+ insertAfter(referenceElement, newElement) {
189
+ const insertionIdx = this._indexOf(referenceElement) + 1;
190
+
191
+ this.items.splice(insertionIdx, 0, newElement);
192
+
193
+ if (insertionIdx === ++this.stackTop) {
194
+ this._updateCurrentElement();
195
+ }
196
+ }
197
+
198
+ popUntilTagNamePopped(tagName) {
199
+ while (this.stackTop > -1) {
200
+ const tn = this.currentTagName;
201
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
202
+
203
+ this.pop();
204
+
205
+ if (tn === tagName && ns === NS.HTML) {
206
+ break;
207
+ }
208
+ }
209
+ }
210
+
211
+ popUntilElementPopped(element) {
212
+ while (this.stackTop > -1) {
213
+ const poppedElement = this.current;
214
+
215
+ this.pop();
216
+
217
+ if (poppedElement === element) {
218
+ break;
219
+ }
220
+ }
221
+ }
222
+
223
+ popUntilNumberedHeaderPopped() {
224
+ while (this.stackTop > -1) {
225
+ const tn = this.currentTagName;
226
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
227
+
228
+ this.pop();
229
+
230
+ if (
231
+ tn === $.H1 ||
232
+ tn === $.H2 ||
233
+ tn === $.H3 ||
234
+ tn === $.H4 ||
235
+ tn === $.H5 ||
236
+ (tn === $.H6 && ns === NS.HTML)
237
+ ) {
238
+ break;
239
+ }
240
+ }
241
+ }
242
+
243
+ popUntilTableCellPopped() {
244
+ while (this.stackTop > -1) {
245
+ const tn = this.currentTagName;
246
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
247
+
248
+ this.pop();
249
+
250
+ if (tn === $.TD || (tn === $.TH && ns === NS.HTML)) {
251
+ break;
252
+ }
253
+ }
254
+ }
255
+
256
+ popAllUpToHtmlElement() {
257
+ //NOTE: here we assume that root <html> element is always first in the open element stack, so
258
+ //we perform this fast stack clean up.
259
+ this.stackTop = 0;
260
+ this._updateCurrentElement();
261
+ }
262
+
263
+ clearBackToTableContext() {
264
+ while (
265
+ (this.currentTagName !== $.TABLE && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
266
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
267
+ ) {
268
+ this.pop();
269
+ }
270
+ }
271
+
272
+ clearBackToTableBodyContext() {
273
+ while (
274
+ (this.currentTagName !== $.TBODY &&
275
+ this.currentTagName !== $.TFOOT &&
276
+ this.currentTagName !== $.THEAD &&
277
+ this.currentTagName !== $.TEMPLATE &&
278
+ this.currentTagName !== $.HTML) ||
279
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
280
+ ) {
281
+ this.pop();
282
+ }
283
+ }
284
+
285
+ clearBackToTableRowContext() {
286
+ while (
287
+ (this.currentTagName !== $.TR && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
288
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
289
+ ) {
290
+ this.pop();
291
+ }
292
+ }
293
+
294
+ remove(element) {
295
+ for (let i = this.stackTop; i >= 0; i--) {
296
+ if (this.items[i] === element) {
297
+ this.items.splice(i, 1);
298
+ this.stackTop--;
299
+ this._updateCurrentElement();
300
+ break;
301
+ }
302
+ }
303
+ }
304
+
305
+ //Search
306
+ tryPeekProperlyNestedBodyElement() {
307
+ //Properly nested <body> element (should be second element in stack).
308
+ const element = this.items[1];
309
+
310
+ return element && this.treeAdapter.getTagName(element) === $.BODY ? element : null;
311
+ }
312
+
313
+ contains(element) {
314
+ return this._indexOf(element) > -1;
315
+ }
316
+
317
+ getCommonAncestor(element) {
318
+ let elementIdx = this._indexOf(element);
319
+
320
+ return --elementIdx >= 0 ? this.items[elementIdx] : null;
321
+ }
322
+
323
+ isRootHtmlElementCurrent() {
324
+ return this.stackTop === 0 && this.currentTagName === $.HTML;
325
+ }
326
+
327
+ //Element in scope
328
+ hasInScope(tagName) {
329
+ for (let i = this.stackTop; i >= 0; i--) {
330
+ const tn = this.treeAdapter.getTagName(this.items[i]);
331
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
332
+
333
+ if (tn === tagName && ns === NS.HTML) {
334
+ return true;
335
+ }
336
+
337
+ if (isScopingElement(tn, ns)) {
338
+ return false;
339
+ }
340
+ }
341
+
342
+ return true;
343
+ }
344
+
345
+ hasNumberedHeaderInScope() {
346
+ for (let i = this.stackTop; i >= 0; i--) {
347
+ const tn = this.treeAdapter.getTagName(this.items[i]);
348
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
349
+
350
+ if (
351
+ (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) &&
352
+ ns === NS.HTML
353
+ ) {
354
+ return true;
355
+ }
356
+
357
+ if (isScopingElement(tn, ns)) {
358
+ return false;
359
+ }
360
+ }
361
+
362
+ return true;
363
+ }
364
+
365
+ hasInListItemScope(tagName) {
366
+ for (let i = this.stackTop; i >= 0; i--) {
367
+ const tn = this.treeAdapter.getTagName(this.items[i]);
368
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
369
+
370
+ if (tn === tagName && ns === NS.HTML) {
371
+ return true;
372
+ }
373
+
374
+ if (((tn === $.UL || tn === $.OL) && ns === NS.HTML) || isScopingElement(tn, ns)) {
375
+ return false;
376
+ }
377
+ }
378
+
379
+ return true;
380
+ }
381
+
382
+ hasInButtonScope(tagName) {
383
+ for (let i = this.stackTop; i >= 0; i--) {
384
+ const tn = this.treeAdapter.getTagName(this.items[i]);
385
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
386
+
387
+ if (tn === tagName && ns === NS.HTML) {
388
+ return true;
389
+ }
390
+
391
+ if ((tn === $.BUTTON && ns === NS.HTML) || isScopingElement(tn, ns)) {
392
+ return false;
393
+ }
394
+ }
395
+
396
+ return true;
397
+ }
398
+
399
+ hasInTableScope(tagName) {
400
+ for (let i = this.stackTop; i >= 0; i--) {
401
+ const tn = this.treeAdapter.getTagName(this.items[i]);
402
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
403
+
404
+ if (ns !== NS.HTML) {
405
+ continue;
406
+ }
407
+
408
+ if (tn === tagName) {
409
+ return true;
410
+ }
411
+
412
+ if (tn === $.TABLE || tn === $.TEMPLATE || tn === $.HTML) {
413
+ return false;
414
+ }
415
+ }
416
+
417
+ return true;
418
+ }
419
+
420
+ hasTableBodyContextInTableScope() {
421
+ for (let i = this.stackTop; i >= 0; i--) {
422
+ const tn = this.treeAdapter.getTagName(this.items[i]);
423
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
424
+
425
+ if (ns !== NS.HTML) {
426
+ continue;
427
+ }
428
+
429
+ if (tn === $.TBODY || tn === $.THEAD || tn === $.TFOOT) {
430
+ return true;
431
+ }
432
+
433
+ if (tn === $.TABLE || tn === $.HTML) {
434
+ return false;
435
+ }
436
+ }
437
+
438
+ return true;
439
+ }
440
+
441
+ hasInSelectScope(tagName) {
442
+ for (let i = this.stackTop; i >= 0; i--) {
443
+ const tn = this.treeAdapter.getTagName(this.items[i]);
444
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
445
+
446
+ if (ns !== NS.HTML) {
447
+ continue;
448
+ }
449
+
450
+ if (tn === tagName) {
451
+ return true;
452
+ }
453
+
454
+ if (tn !== $.OPTION && tn !== $.OPTGROUP) {
455
+ return false;
456
+ }
457
+ }
458
+
459
+ return true;
460
+ }
461
+
462
+ //Implied end tags
463
+ generateImpliedEndTags() {
464
+ while (isImpliedEndTagRequired(this.currentTagName)) {
465
+ this.pop();
466
+ }
467
+ }
468
+
469
+ generateImpliedEndTagsThoroughly() {
470
+ while (isImpliedEndTagRequiredThoroughly(this.currentTagName)) {
471
+ this.pop();
472
+ }
473
+ }
474
+
475
+ generateImpliedEndTagsWithExclusion(exclusionTagName) {
476
+ while (isImpliedEndTagRequired(this.currentTagName) && this.currentTagName !== exclusionTagName) {
477
+ this.pop();
478
+ }
479
+ }
480
+ }
481
+
482
+ module.exports = OpenElementStack;