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.
- package/README.md +14 -16
- package/lib/common/doctype.js +125 -108
- package/lib/common/error-codes.js +65 -0
- package/lib/common/foreign-content.js +265 -0
- package/lib/common/html.js +102 -102
- package/lib/common/unicode.js +81 -19
- package/lib/extensions/error-reporting/mixin-base.js +43 -0
- package/lib/extensions/error-reporting/parser-mixin.js +52 -0
- package/lib/extensions/error-reporting/preprocessor-mixin.js +24 -0
- package/lib/extensions/error-reporting/tokenizer-mixin.js +17 -0
- package/lib/extensions/location-info/open-element-stack-mixin.js +35 -0
- package/lib/extensions/location-info/parser-mixin.js +222 -0
- package/lib/extensions/location-info/tokenizer-mixin.js +146 -0
- package/lib/extensions/position-tracking/preprocessor-mixin.js +64 -0
- package/lib/index.js +6 -21
- package/lib/parser/formatting-element-list.js +181 -0
- package/lib/parser/index.js +1469 -1332
- package/lib/parser/open-element-stack.js +482 -0
- package/lib/serializer/index.js +136 -122
- package/lib/tokenizer/index.js +1856 -1804
- package/lib/tokenizer/{named_entity_data.js → named-entity-data.js} +1 -1
- package/lib/tokenizer/preprocessor.js +115 -103
- package/lib/tree-adapters/default.js +217 -0
- package/lib/utils/{merge_options.js → merge-options.js} +2 -2
- package/lib/utils/mixin.js +33 -12
- package/package.json +32 -61
- package/LICENSE +0 -19
- package/lib/common/foreign_content.js +0 -260
- package/lib/extensions/location_info/open_element_stack_mixin.js +0 -34
- package/lib/extensions/location_info/parser_mixin.js +0 -213
- package/lib/extensions/location_info/tokenizer_mixin.js +0 -117
- package/lib/extensions/position_tracking/preprocessor_mixin.js +0 -72
- package/lib/index.d.ts +0 -1077
- package/lib/parser/formatting_element_list.js +0 -167
- package/lib/parser/open_element_stack.js +0 -395
- package/lib/parser/parser_stream.js +0 -76
- package/lib/parser/plain_text_conversion_stream.js +0 -20
- package/lib/sax/dev_null_stream.js +0 -14
- package/lib/sax/index.js +0 -118
- package/lib/sax/parser_feedback_simulator.js +0 -153
- package/lib/serializer/serializer_stream.js +0 -28
- package/lib/tree_adapters/default.js +0 -209
- 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;
|