source: trip-planner-front/node_modules/parse5/lib/parser/open-element-stack.js@ 76712b2

Last change on this file since 76712b2 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 12.2 KB
Line 
1'use strict';
2
3const HTML = require('../common/html');
4
5//Aliases
6const $ = HTML.TAG_NAMES;
7const 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.
13function 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
34function 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
71function 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
122class 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
482module.exports = OpenElementStack;
Note: See TracBrowser for help on using the repository browser.