@cjser/parse5 0.0.0-stage → 8.1.0-cjser.2

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.
@@ -0,0 +1,3274 @@
1
+ import { Tokenizer, TokenizerMode } from '../tokenizer/index.js';
2
+ import { OpenElementStack } from './open-element-stack.js';
3
+ import { FormattingElementList, EntryType } from './formatting-element-list.js';
4
+ import { defaultTreeAdapter } from '../tree-adapters/default.js';
5
+ import * as doctype from '../common/doctype.js';
6
+ import * as foreignContent from '../common/foreign-content.js';
7
+ import { ERR } from '../common/error-codes.js';
8
+ import * as unicode from '../common/unicode.js';
9
+ import { TAG_ID as $, TAG_NAMES as TN, NS, ATTRS, SPECIAL_ELEMENTS, DOCUMENT_MODE, NUMBERED_HEADERS, getTagID, } from '../common/html.js';
10
+ import { TokenType, getTokenAttr, } from '../common/token.js';
11
+ //Misc constants
12
+ const HIDDEN_INPUT_TYPE = 'hidden';
13
+ //Adoption agency loops iteration count
14
+ const AA_OUTER_LOOP_ITER = 8;
15
+ const AA_INNER_LOOP_ITER = 3;
16
+ //Insertion modes
17
+ export var InsertionMode;
18
+ (function (InsertionMode) {
19
+ InsertionMode[InsertionMode["INITIAL"] = 0] = "INITIAL";
20
+ InsertionMode[InsertionMode["BEFORE_HTML"] = 1] = "BEFORE_HTML";
21
+ InsertionMode[InsertionMode["BEFORE_HEAD"] = 2] = "BEFORE_HEAD";
22
+ InsertionMode[InsertionMode["IN_HEAD"] = 3] = "IN_HEAD";
23
+ InsertionMode[InsertionMode["IN_HEAD_NO_SCRIPT"] = 4] = "IN_HEAD_NO_SCRIPT";
24
+ InsertionMode[InsertionMode["AFTER_HEAD"] = 5] = "AFTER_HEAD";
25
+ InsertionMode[InsertionMode["IN_BODY"] = 6] = "IN_BODY";
26
+ InsertionMode[InsertionMode["TEXT"] = 7] = "TEXT";
27
+ InsertionMode[InsertionMode["IN_TABLE"] = 8] = "IN_TABLE";
28
+ InsertionMode[InsertionMode["IN_TABLE_TEXT"] = 9] = "IN_TABLE_TEXT";
29
+ InsertionMode[InsertionMode["IN_CAPTION"] = 10] = "IN_CAPTION";
30
+ InsertionMode[InsertionMode["IN_COLUMN_GROUP"] = 11] = "IN_COLUMN_GROUP";
31
+ InsertionMode[InsertionMode["IN_TABLE_BODY"] = 12] = "IN_TABLE_BODY";
32
+ InsertionMode[InsertionMode["IN_ROW"] = 13] = "IN_ROW";
33
+ InsertionMode[InsertionMode["IN_CELL"] = 14] = "IN_CELL";
34
+ InsertionMode[InsertionMode["IN_SELECT"] = 15] = "IN_SELECT";
35
+ InsertionMode[InsertionMode["IN_SELECT_IN_TABLE"] = 16] = "IN_SELECT_IN_TABLE";
36
+ InsertionMode[InsertionMode["IN_TEMPLATE"] = 17] = "IN_TEMPLATE";
37
+ InsertionMode[InsertionMode["AFTER_BODY"] = 18] = "AFTER_BODY";
38
+ InsertionMode[InsertionMode["IN_FRAMESET"] = 19] = "IN_FRAMESET";
39
+ InsertionMode[InsertionMode["AFTER_FRAMESET"] = 20] = "AFTER_FRAMESET";
40
+ InsertionMode[InsertionMode["AFTER_AFTER_BODY"] = 21] = "AFTER_AFTER_BODY";
41
+ InsertionMode[InsertionMode["AFTER_AFTER_FRAMESET"] = 22] = "AFTER_AFTER_FRAMESET";
42
+ })(InsertionMode || (InsertionMode = {}));
43
+ const BASE_LOC = {
44
+ startLine: -1,
45
+ startCol: -1,
46
+ startOffset: -1,
47
+ endLine: -1,
48
+ endCol: -1,
49
+ endOffset: -1,
50
+ };
51
+ const TABLE_STRUCTURE_TAGS = new Set([$.TABLE, $.TBODY, $.TFOOT, $.THEAD, $.TR]);
52
+ const defaultParserOptions = {
53
+ scriptingEnabled: true,
54
+ sourceCodeLocationInfo: false,
55
+ treeAdapter: defaultTreeAdapter,
56
+ onParseError: null,
57
+ };
58
+ //Parser
59
+ export class Parser {
60
+ constructor(options, document,
61
+ /** @internal */
62
+ fragmentContext = null,
63
+ /** @internal */
64
+ scriptHandler = null) {
65
+ this.fragmentContext = fragmentContext;
66
+ this.scriptHandler = scriptHandler;
67
+ this.currentToken = null;
68
+ this.stopped = false;
69
+ /** @internal */
70
+ this.insertionMode = InsertionMode.INITIAL;
71
+ /** @internal */
72
+ this.originalInsertionMode = InsertionMode.INITIAL;
73
+ /** @internal */
74
+ this.headElement = null;
75
+ /** @internal */
76
+ this.formElement = null;
77
+ /** Indicates that the current node is not an element in the HTML namespace */
78
+ this.currentNotInHTML = false;
79
+ /**
80
+ * The template insertion mode stack is maintained from the left.
81
+ * Ie. the topmost element will always have index 0.
82
+ *
83
+ * @internal
84
+ */
85
+ this.tmplInsertionModeStack = [];
86
+ /** @internal */
87
+ this.pendingCharacterTokens = [];
88
+ /** @internal */
89
+ this.hasNonWhitespacePendingCharacterToken = false;
90
+ /** @internal */
91
+ this.framesetOk = true;
92
+ /** @internal */
93
+ this.skipNextNewLine = false;
94
+ /** @internal */
95
+ this.fosterParentingEnabled = false;
96
+ this.options = {
97
+ ...defaultParserOptions,
98
+ ...options,
99
+ };
100
+ this.treeAdapter = this.options.treeAdapter;
101
+ this.onParseError = this.options.onParseError;
102
+ // Always enable location info if we report parse errors.
103
+ if (this.onParseError) {
104
+ this.options.sourceCodeLocationInfo = true;
105
+ }
106
+ this.document = document !== null && document !== void 0 ? document : this.treeAdapter.createDocument();
107
+ this.tokenizer = new Tokenizer(this.options, this);
108
+ this.activeFormattingElements = new FormattingElementList(this.treeAdapter);
109
+ this.fragmentContextID = fragmentContext ? getTagID(this.treeAdapter.getTagName(fragmentContext)) : $.UNKNOWN;
110
+ this._setContextModes(fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : this.document, this.fragmentContextID);
111
+ this.openElements = new OpenElementStack(this.document, this.treeAdapter, this);
112
+ }
113
+ // API
114
+ static parse(html, options) {
115
+ const parser = new this(options);
116
+ parser.tokenizer.write(html, true);
117
+ return parser.document;
118
+ }
119
+ static getFragmentParser(fragmentContext, options) {
120
+ const opts = {
121
+ ...defaultParserOptions,
122
+ ...options,
123
+ };
124
+ //NOTE: use a <template> element as the fragment context if no context element was provided,
125
+ //so we will parse in a "forgiving" manner
126
+ fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : (fragmentContext = opts.treeAdapter.createElement(TN.TEMPLATE, NS.HTML, []));
127
+ //NOTE: create a fake element which will be used as the `document` for fragment parsing.
128
+ //This is important for jsdom, where a new `document` cannot be created. This led to
129
+ //fragment parsing messing with the main `document`.
130
+ const documentMock = opts.treeAdapter.createElement('documentmock', NS.HTML, []);
131
+ const parser = new this(opts, documentMock, fragmentContext);
132
+ if (parser.fragmentContextID === $.TEMPLATE) {
133
+ parser.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
134
+ }
135
+ parser._initTokenizerForFragmentParsing();
136
+ parser._insertFakeRootElement();
137
+ parser._resetInsertionMode();
138
+ parser._findFormInFragmentContext();
139
+ return parser;
140
+ }
141
+ getFragment() {
142
+ const rootElement = this.treeAdapter.getFirstChild(this.document);
143
+ const fragment = this.treeAdapter.createDocumentFragment();
144
+ this._adoptNodes(rootElement, fragment);
145
+ return fragment;
146
+ }
147
+ //Errors
148
+ /** @internal */
149
+ _err(token, code, beforeToken) {
150
+ var _a;
151
+ if (!this.onParseError)
152
+ return;
153
+ const loc = (_a = token.location) !== null && _a !== void 0 ? _a : BASE_LOC;
154
+ const err = {
155
+ code,
156
+ startLine: loc.startLine,
157
+ startCol: loc.startCol,
158
+ startOffset: loc.startOffset,
159
+ endLine: beforeToken ? loc.startLine : loc.endLine,
160
+ endCol: beforeToken ? loc.startCol : loc.endCol,
161
+ endOffset: beforeToken ? loc.startOffset : loc.endOffset,
162
+ };
163
+ this.onParseError(err);
164
+ }
165
+ //Stack events
166
+ /** @internal */
167
+ onItemPush(node, tid, isTop) {
168
+ var _a, _b;
169
+ (_b = (_a = this.treeAdapter).onItemPush) === null || _b === void 0 ? void 0 : _b.call(_a, node);
170
+ if (isTop && this.openElements.stackTop > 0)
171
+ this._setContextModes(node, tid);
172
+ }
173
+ /** @internal */
174
+ onItemPop(node, isTop) {
175
+ var _a, _b;
176
+ if (this.options.sourceCodeLocationInfo) {
177
+ this._setEndLocation(node, this.currentToken);
178
+ }
179
+ (_b = (_a = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a, node, this.openElements.current);
180
+ if (isTop) {
181
+ let current;
182
+ let currentTagId;
183
+ if (this.openElements.stackTop === 0 && this.fragmentContext) {
184
+ current = this.fragmentContext;
185
+ currentTagId = this.fragmentContextID;
186
+ }
187
+ else {
188
+ ({ current, currentTagId } = this.openElements);
189
+ }
190
+ this._setContextModes(current, currentTagId);
191
+ }
192
+ }
193
+ _setContextModes(current, tid) {
194
+ const isHTML = current === this.document || (current && this.treeAdapter.getNamespaceURI(current) === NS.HTML);
195
+ this.currentNotInHTML = !isHTML;
196
+ this.tokenizer.inForeignNode =
197
+ !isHTML && current !== undefined && tid !== undefined && !this._isIntegrationPoint(tid, current);
198
+ }
199
+ /** @protected */
200
+ _switchToTextParsing(currentToken, nextTokenizerState) {
201
+ this._insertElement(currentToken, NS.HTML);
202
+ this.tokenizer.state = nextTokenizerState;
203
+ this.originalInsertionMode = this.insertionMode;
204
+ this.insertionMode = InsertionMode.TEXT;
205
+ }
206
+ switchToPlaintextParsing() {
207
+ this.insertionMode = InsertionMode.TEXT;
208
+ this.originalInsertionMode = InsertionMode.IN_BODY;
209
+ this.tokenizer.state = TokenizerMode.PLAINTEXT;
210
+ }
211
+ //Fragment parsing
212
+ /** @protected */
213
+ _getAdjustedCurrentElement() {
214
+ return this.openElements.stackTop === 0 && this.fragmentContext
215
+ ? this.fragmentContext
216
+ : this.openElements.current;
217
+ }
218
+ /** @protected */
219
+ _findFormInFragmentContext() {
220
+ let node = this.fragmentContext;
221
+ while (node) {
222
+ if (this.treeAdapter.getTagName(node) === TN.FORM) {
223
+ this.formElement = node;
224
+ break;
225
+ }
226
+ node = this.treeAdapter.getParentNode(node);
227
+ }
228
+ }
229
+ _initTokenizerForFragmentParsing() {
230
+ if (!this.fragmentContext || this.treeAdapter.getNamespaceURI(this.fragmentContext) !== NS.HTML) {
231
+ return;
232
+ }
233
+ switch (this.fragmentContextID) {
234
+ case $.TITLE:
235
+ case $.TEXTAREA: {
236
+ this.tokenizer.state = TokenizerMode.RCDATA;
237
+ break;
238
+ }
239
+ case $.STYLE:
240
+ case $.XMP:
241
+ case $.IFRAME:
242
+ case $.NOEMBED:
243
+ case $.NOFRAMES:
244
+ case $.NOSCRIPT: {
245
+ this.tokenizer.state = TokenizerMode.RAWTEXT;
246
+ break;
247
+ }
248
+ case $.SCRIPT: {
249
+ this.tokenizer.state = TokenizerMode.SCRIPT_DATA;
250
+ break;
251
+ }
252
+ case $.PLAINTEXT: {
253
+ this.tokenizer.state = TokenizerMode.PLAINTEXT;
254
+ break;
255
+ }
256
+ default:
257
+ // Do nothing
258
+ }
259
+ }
260
+ //Tree mutation
261
+ /** @protected */
262
+ _setDocumentType(token) {
263
+ const name = token.name || '';
264
+ const publicId = token.publicId || '';
265
+ const systemId = token.systemId || '';
266
+ this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
267
+ if (token.location) {
268
+ const documentChildren = this.treeAdapter.getChildNodes(this.document);
269
+ const docTypeNode = documentChildren.find((node) => this.treeAdapter.isDocumentTypeNode(node));
270
+ if (docTypeNode) {
271
+ this.treeAdapter.setNodeSourceCodeLocation(docTypeNode, token.location);
272
+ }
273
+ }
274
+ }
275
+ /** @protected */
276
+ _attachElementToTree(element, location, insertionLocation) {
277
+ if (this.options.sourceCodeLocationInfo) {
278
+ const loc = location && {
279
+ ...location,
280
+ startTag: location,
281
+ };
282
+ this.treeAdapter.setNodeSourceCodeLocation(element, loc);
283
+ }
284
+ if (insertionLocation.beforeElement) {
285
+ this.treeAdapter.insertBefore(insertionLocation.parent, element, insertionLocation.beforeElement);
286
+ }
287
+ else {
288
+ this.treeAdapter.appendChild(insertionLocation.parent, element);
289
+ }
290
+ }
291
+ /** @protected */
292
+ _getInsertionLocation() {
293
+ var _a;
294
+ if (this._shouldFosterParentOnInsertion()) {
295
+ return this._findFosterParentingLocation();
296
+ }
297
+ return {
298
+ parent: (_a = this.openElements.currentTmplContentOrNode) !== null && _a !== void 0 ? _a : this.document,
299
+ beforeElement: null,
300
+ };
301
+ }
302
+ /** @protected */
303
+ _createElement(tagName, namespaceURI, attrs, intendedParent) {
304
+ var _a, _b;
305
+ const element = this.treeAdapter.createElement(tagName, namespaceURI, attrs);
306
+ (_b = (_a = this.treeAdapter).onElementCreated) === null || _b === void 0 ? void 0 : _b.call(_a, element, {
307
+ intendedParent,
308
+ formElement: this.formElement,
309
+ isInTemplate: this.openElements.tmplCount > 0,
310
+ });
311
+ return element;
312
+ }
313
+ /**
314
+ * For self-closing tags. Add an element to the tree, but skip adding it
315
+ * to the stack.
316
+ */
317
+ /** @protected */
318
+ _appendElement(token, namespaceURI) {
319
+ const insertionLocation = this._getInsertionLocation();
320
+ const element = this._createElement(token.tagName, namespaceURI, token.attrs, insertionLocation.parent);
321
+ this._attachElementToTree(element, token.location, insertionLocation);
322
+ }
323
+ /** @protected */
324
+ _insertElement(token, namespaceURI) {
325
+ const insertionLocation = this._getInsertionLocation();
326
+ const element = this._createElement(token.tagName, namespaceURI, token.attrs, insertionLocation.parent);
327
+ this._attachElementToTree(element, token.location, insertionLocation);
328
+ this.openElements.push(element, token.tagID);
329
+ }
330
+ /** @protected */
331
+ _insertFakeElement(tagName, tagID) {
332
+ const insertionLocation = this._getInsertionLocation();
333
+ const element = this._createElement(tagName, NS.HTML, [], insertionLocation.parent);
334
+ this._attachElementToTree(element, null, insertionLocation);
335
+ this.openElements.push(element, tagID);
336
+ }
337
+ /** @protected */
338
+ _insertTemplate(token) {
339
+ const insertionLocation = this._getInsertionLocation();
340
+ const tmpl = this._createElement(token.tagName, NS.HTML, token.attrs, insertionLocation.parent);
341
+ const content = this.treeAdapter.createDocumentFragment();
342
+ this.treeAdapter.setTemplateContent(tmpl, content);
343
+ this._attachElementToTree(tmpl, token.location, insertionLocation);
344
+ this.openElements.push(tmpl, token.tagID);
345
+ if (this.options.sourceCodeLocationInfo)
346
+ this.treeAdapter.setNodeSourceCodeLocation(content, null);
347
+ }
348
+ /** @protected */
349
+ _insertFakeRootElement() {
350
+ const element = this._createElement(TN.HTML, NS.HTML, [], this.document);
351
+ if (this.options.sourceCodeLocationInfo)
352
+ this.treeAdapter.setNodeSourceCodeLocation(element, null);
353
+ this.treeAdapter.appendChild(this.document, element);
354
+ this.openElements.push(element, $.HTML);
355
+ }
356
+ /** @protected */
357
+ _appendCommentNode(token, parent) {
358
+ const commentNode = this.treeAdapter.createCommentNode(token.data);
359
+ this.treeAdapter.appendChild(parent, commentNode);
360
+ if (this.options.sourceCodeLocationInfo) {
361
+ this.treeAdapter.setNodeSourceCodeLocation(commentNode, token.location);
362
+ }
363
+ }
364
+ /** @protected */
365
+ _insertCharacters(token) {
366
+ let parent;
367
+ let beforeElement;
368
+ if (this._shouldFosterParentOnInsertion()) {
369
+ ({ parent, beforeElement } = this._findFosterParentingLocation());
370
+ if (beforeElement) {
371
+ this.treeAdapter.insertTextBefore(parent, token.chars, beforeElement);
372
+ }
373
+ else {
374
+ this.treeAdapter.insertText(parent, token.chars);
375
+ }
376
+ }
377
+ else {
378
+ parent = this.openElements.currentTmplContentOrNode;
379
+ this.treeAdapter.insertText(parent, token.chars);
380
+ }
381
+ if (!token.location)
382
+ return;
383
+ const siblings = this.treeAdapter.getChildNodes(parent);
384
+ const textNodeIdx = beforeElement ? siblings.lastIndexOf(beforeElement) : siblings.length;
385
+ const textNode = siblings[textNodeIdx - 1];
386
+ //NOTE: if we have a location assigned by another token, then just update the end position
387
+ const tnLoc = this.treeAdapter.getNodeSourceCodeLocation(textNode);
388
+ if (tnLoc) {
389
+ const { endLine, endCol, endOffset } = token.location;
390
+ this.treeAdapter.updateNodeSourceCodeLocation(textNode, { endLine, endCol, endOffset });
391
+ }
392
+ else if (this.options.sourceCodeLocationInfo) {
393
+ this.treeAdapter.setNodeSourceCodeLocation(textNode, token.location);
394
+ }
395
+ }
396
+ /** @protected */
397
+ _adoptNodes(donor, recipient) {
398
+ for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
399
+ this.treeAdapter.detachNode(child);
400
+ this.treeAdapter.appendChild(recipient, child);
401
+ }
402
+ }
403
+ /** @protected */
404
+ _setEndLocation(element, closingToken) {
405
+ if (this.treeAdapter.getNodeSourceCodeLocation(element) && closingToken.location) {
406
+ const ctLoc = closingToken.location;
407
+ const tn = this.treeAdapter.getTagName(element);
408
+ const endLoc =
409
+ // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
410
+ // tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
411
+ closingToken.type === TokenType.END_TAG && tn === closingToken.tagName
412
+ ? {
413
+ endTag: { ...ctLoc },
414
+ endLine: ctLoc.endLine,
415
+ endCol: ctLoc.endCol,
416
+ endOffset: ctLoc.endOffset,
417
+ }
418
+ : {
419
+ endLine: ctLoc.startLine,
420
+ endCol: ctLoc.startCol,
421
+ endOffset: ctLoc.startOffset,
422
+ };
423
+ this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
424
+ }
425
+ }
426
+ //Token processing
427
+ shouldProcessStartTagTokenInForeignContent(token) {
428
+ // Check that neither current === document, or ns === NS.HTML
429
+ if (!this.currentNotInHTML)
430
+ return false;
431
+ let current;
432
+ let currentTagId;
433
+ if (this.openElements.stackTop === 0 && this.fragmentContext) {
434
+ current = this.fragmentContext;
435
+ currentTagId = this.fragmentContextID;
436
+ }
437
+ else {
438
+ ({ current, currentTagId } = this.openElements);
439
+ }
440
+ if (token.tagID === $.SVG &&
441
+ this.treeAdapter.getTagName(current) === TN.ANNOTATION_XML &&
442
+ this.treeAdapter.getNamespaceURI(current) === NS.MATHML) {
443
+ return false;
444
+ }
445
+ return (
446
+ // Check that `current` is not an integration point for HTML or MathML elements.
447
+ this.tokenizer.inForeignNode ||
448
+ // If it _is_ an integration point, then we might have to check that it is not an HTML
449
+ // integration point.
450
+ ((token.tagID === $.MGLYPH || token.tagID === $.MALIGNMARK) &&
451
+ currentTagId !== undefined &&
452
+ !this._isIntegrationPoint(currentTagId, current, NS.HTML)));
453
+ }
454
+ /** @protected */
455
+ _processToken(token) {
456
+ switch (token.type) {
457
+ case TokenType.CHARACTER: {
458
+ this.onCharacter(token);
459
+ break;
460
+ }
461
+ case TokenType.NULL_CHARACTER: {
462
+ this.onNullCharacter(token);
463
+ break;
464
+ }
465
+ case TokenType.COMMENT: {
466
+ this.onComment(token);
467
+ break;
468
+ }
469
+ case TokenType.DOCTYPE: {
470
+ this.onDoctype(token);
471
+ break;
472
+ }
473
+ case TokenType.START_TAG: {
474
+ this._processStartTag(token);
475
+ break;
476
+ }
477
+ case TokenType.END_TAG: {
478
+ this.onEndTag(token);
479
+ break;
480
+ }
481
+ case TokenType.EOF: {
482
+ this.onEof(token);
483
+ break;
484
+ }
485
+ case TokenType.WHITESPACE_CHARACTER: {
486
+ this.onWhitespaceCharacter(token);
487
+ break;
488
+ }
489
+ }
490
+ }
491
+ //Integration points
492
+ /** @protected */
493
+ _isIntegrationPoint(tid, element, foreignNS) {
494
+ const ns = this.treeAdapter.getNamespaceURI(element);
495
+ const attrs = this.treeAdapter.getAttrList(element);
496
+ return foreignContent.isIntegrationPoint(tid, ns, attrs, foreignNS);
497
+ }
498
+ //Active formatting elements reconstruction
499
+ /** @protected */
500
+ _reconstructActiveFormattingElements() {
501
+ const listLength = this.activeFormattingElements.entries.length;
502
+ if (listLength) {
503
+ const endIndex = this.activeFormattingElements.entries.findIndex((entry) => entry.type === EntryType.Marker || this.openElements.contains(entry.element));
504
+ const unopenIdx = endIndex === -1 ? listLength - 1 : endIndex - 1;
505
+ for (let i = unopenIdx; i >= 0; i--) {
506
+ const entry = this.activeFormattingElements.entries[i];
507
+ this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
508
+ entry.element = this.openElements.current;
509
+ }
510
+ }
511
+ }
512
+ //Close elements
513
+ /** @protected */
514
+ _closeTableCell() {
515
+ this.openElements.generateImpliedEndTags();
516
+ this.openElements.popUntilTableCellPopped();
517
+ this.activeFormattingElements.clearToLastMarker();
518
+ this.insertionMode = InsertionMode.IN_ROW;
519
+ }
520
+ /** @protected */
521
+ _closePElement() {
522
+ this.openElements.generateImpliedEndTagsWithExclusion($.P);
523
+ this.openElements.popUntilTagNamePopped($.P);
524
+ }
525
+ //Insertion modes
526
+ /** @protected */
527
+ _resetInsertionMode() {
528
+ for (let i = this.openElements.stackTop; i >= 0; i--) {
529
+ //Insertion mode reset map
530
+ switch (i === 0 && this.fragmentContext ? this.fragmentContextID : this.openElements.tagIDs[i]) {
531
+ case $.TR: {
532
+ this.insertionMode = InsertionMode.IN_ROW;
533
+ return;
534
+ }
535
+ case $.TBODY:
536
+ case $.THEAD:
537
+ case $.TFOOT: {
538
+ this.insertionMode = InsertionMode.IN_TABLE_BODY;
539
+ return;
540
+ }
541
+ case $.CAPTION: {
542
+ this.insertionMode = InsertionMode.IN_CAPTION;
543
+ return;
544
+ }
545
+ case $.COLGROUP: {
546
+ this.insertionMode = InsertionMode.IN_COLUMN_GROUP;
547
+ return;
548
+ }
549
+ case $.TABLE: {
550
+ this.insertionMode = InsertionMode.IN_TABLE;
551
+ return;
552
+ }
553
+ case $.BODY: {
554
+ this.insertionMode = InsertionMode.IN_BODY;
555
+ return;
556
+ }
557
+ case $.FRAMESET: {
558
+ this.insertionMode = InsertionMode.IN_FRAMESET;
559
+ return;
560
+ }
561
+ case $.SELECT: {
562
+ this._resetInsertionModeForSelect(i);
563
+ return;
564
+ }
565
+ case $.TEMPLATE: {
566
+ this.insertionMode = this.tmplInsertionModeStack[0];
567
+ return;
568
+ }
569
+ case $.HTML: {
570
+ this.insertionMode = this.headElement ? InsertionMode.AFTER_HEAD : InsertionMode.BEFORE_HEAD;
571
+ return;
572
+ }
573
+ case $.TD:
574
+ case $.TH: {
575
+ if (i > 0) {
576
+ this.insertionMode = InsertionMode.IN_CELL;
577
+ return;
578
+ }
579
+ break;
580
+ }
581
+ case $.HEAD: {
582
+ if (i > 0) {
583
+ this.insertionMode = InsertionMode.IN_HEAD;
584
+ return;
585
+ }
586
+ break;
587
+ }
588
+ }
589
+ }
590
+ this.insertionMode = InsertionMode.IN_BODY;
591
+ }
592
+ /** @protected */
593
+ _resetInsertionModeForSelect(selectIdx) {
594
+ if (selectIdx > 0) {
595
+ for (let i = selectIdx - 1; i > 0; i--) {
596
+ const tn = this.openElements.tagIDs[i];
597
+ if (tn === $.TEMPLATE) {
598
+ break;
599
+ }
600
+ else if (tn === $.TABLE) {
601
+ this.insertionMode = InsertionMode.IN_SELECT_IN_TABLE;
602
+ return;
603
+ }
604
+ }
605
+ }
606
+ this.insertionMode = InsertionMode.IN_SELECT;
607
+ }
608
+ //Foster parenting
609
+ /** @protected */
610
+ _isElementCausesFosterParenting(tn) {
611
+ return TABLE_STRUCTURE_TAGS.has(tn);
612
+ }
613
+ /** @protected */
614
+ _shouldFosterParentOnInsertion() {
615
+ return (this.fosterParentingEnabled &&
616
+ this.openElements.currentTagId !== undefined &&
617
+ this._isElementCausesFosterParenting(this.openElements.currentTagId));
618
+ }
619
+ /** @protected */
620
+ _findFosterParentingLocation() {
621
+ for (let i = this.openElements.stackTop; i >= 0; i--) {
622
+ const openElement = this.openElements.items[i];
623
+ switch (this.openElements.tagIDs[i]) {
624
+ case $.TEMPLATE: {
625
+ if (this.treeAdapter.getNamespaceURI(openElement) === NS.HTML) {
626
+ return { parent: this.treeAdapter.getTemplateContent(openElement), beforeElement: null };
627
+ }
628
+ break;
629
+ }
630
+ case $.TABLE: {
631
+ const parent = this.treeAdapter.getParentNode(openElement);
632
+ if (parent) {
633
+ return { parent, beforeElement: openElement };
634
+ }
635
+ return { parent: this.openElements.items[i - 1], beforeElement: null };
636
+ }
637
+ default:
638
+ // Do nothing
639
+ }
640
+ }
641
+ return { parent: this.openElements.items[0], beforeElement: null };
642
+ }
643
+ /** @protected */
644
+ _fosterParentElement(element) {
645
+ const location = this._findFosterParentingLocation();
646
+ if (location.beforeElement) {
647
+ this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
648
+ }
649
+ else {
650
+ this.treeAdapter.appendChild(location.parent, element);
651
+ }
652
+ }
653
+ //Special elements
654
+ /** @protected */
655
+ _isSpecialElement(element, id) {
656
+ const ns = this.treeAdapter.getNamespaceURI(element);
657
+ return SPECIAL_ELEMENTS[ns].has(id);
658
+ }
659
+ /** @internal */
660
+ onCharacter(token) {
661
+ this.skipNextNewLine = false;
662
+ if (this.tokenizer.inForeignNode) {
663
+ characterInForeignContent(this, token);
664
+ return;
665
+ }
666
+ switch (this.insertionMode) {
667
+ case InsertionMode.INITIAL: {
668
+ tokenInInitialMode(this, token);
669
+ break;
670
+ }
671
+ case InsertionMode.BEFORE_HTML: {
672
+ tokenBeforeHtml(this, token);
673
+ break;
674
+ }
675
+ case InsertionMode.BEFORE_HEAD: {
676
+ tokenBeforeHead(this, token);
677
+ break;
678
+ }
679
+ case InsertionMode.IN_HEAD: {
680
+ tokenInHead(this, token);
681
+ break;
682
+ }
683
+ case InsertionMode.IN_HEAD_NO_SCRIPT: {
684
+ tokenInHeadNoScript(this, token);
685
+ break;
686
+ }
687
+ case InsertionMode.AFTER_HEAD: {
688
+ tokenAfterHead(this, token);
689
+ break;
690
+ }
691
+ case InsertionMode.IN_BODY:
692
+ case InsertionMode.IN_CAPTION:
693
+ case InsertionMode.IN_CELL:
694
+ case InsertionMode.IN_TEMPLATE: {
695
+ characterInBody(this, token);
696
+ break;
697
+ }
698
+ case InsertionMode.TEXT:
699
+ case InsertionMode.IN_SELECT:
700
+ case InsertionMode.IN_SELECT_IN_TABLE: {
701
+ this._insertCharacters(token);
702
+ break;
703
+ }
704
+ case InsertionMode.IN_TABLE:
705
+ case InsertionMode.IN_TABLE_BODY:
706
+ case InsertionMode.IN_ROW: {
707
+ characterInTable(this, token);
708
+ break;
709
+ }
710
+ case InsertionMode.IN_TABLE_TEXT: {
711
+ characterInTableText(this, token);
712
+ break;
713
+ }
714
+ case InsertionMode.IN_COLUMN_GROUP: {
715
+ tokenInColumnGroup(this, token);
716
+ break;
717
+ }
718
+ case InsertionMode.AFTER_BODY: {
719
+ tokenAfterBody(this, token);
720
+ break;
721
+ }
722
+ case InsertionMode.AFTER_AFTER_BODY: {
723
+ tokenAfterAfterBody(this, token);
724
+ break;
725
+ }
726
+ default:
727
+ // Do nothing
728
+ }
729
+ }
730
+ /** @internal */
731
+ onNullCharacter(token) {
732
+ this.skipNextNewLine = false;
733
+ if (this.tokenizer.inForeignNode) {
734
+ nullCharacterInForeignContent(this, token);
735
+ return;
736
+ }
737
+ switch (this.insertionMode) {
738
+ case InsertionMode.INITIAL: {
739
+ tokenInInitialMode(this, token);
740
+ break;
741
+ }
742
+ case InsertionMode.BEFORE_HTML: {
743
+ tokenBeforeHtml(this, token);
744
+ break;
745
+ }
746
+ case InsertionMode.BEFORE_HEAD: {
747
+ tokenBeforeHead(this, token);
748
+ break;
749
+ }
750
+ case InsertionMode.IN_HEAD: {
751
+ tokenInHead(this, token);
752
+ break;
753
+ }
754
+ case InsertionMode.IN_HEAD_NO_SCRIPT: {
755
+ tokenInHeadNoScript(this, token);
756
+ break;
757
+ }
758
+ case InsertionMode.AFTER_HEAD: {
759
+ tokenAfterHead(this, token);
760
+ break;
761
+ }
762
+ case InsertionMode.TEXT: {
763
+ this._insertCharacters(token);
764
+ break;
765
+ }
766
+ case InsertionMode.IN_TABLE:
767
+ case InsertionMode.IN_TABLE_BODY:
768
+ case InsertionMode.IN_ROW: {
769
+ characterInTable(this, token);
770
+ break;
771
+ }
772
+ case InsertionMode.IN_COLUMN_GROUP: {
773
+ tokenInColumnGroup(this, token);
774
+ break;
775
+ }
776
+ case InsertionMode.AFTER_BODY: {
777
+ tokenAfterBody(this, token);
778
+ break;
779
+ }
780
+ case InsertionMode.AFTER_AFTER_BODY: {
781
+ tokenAfterAfterBody(this, token);
782
+ break;
783
+ }
784
+ default:
785
+ // Do nothing
786
+ }
787
+ }
788
+ /** @internal */
789
+ onComment(token) {
790
+ this.skipNextNewLine = false;
791
+ if (this.currentNotInHTML) {
792
+ appendComment(this, token);
793
+ return;
794
+ }
795
+ switch (this.insertionMode) {
796
+ case InsertionMode.INITIAL:
797
+ case InsertionMode.BEFORE_HTML:
798
+ case InsertionMode.BEFORE_HEAD:
799
+ case InsertionMode.IN_HEAD:
800
+ case InsertionMode.IN_HEAD_NO_SCRIPT:
801
+ case InsertionMode.AFTER_HEAD:
802
+ case InsertionMode.IN_BODY:
803
+ case InsertionMode.IN_TABLE:
804
+ case InsertionMode.IN_CAPTION:
805
+ case InsertionMode.IN_COLUMN_GROUP:
806
+ case InsertionMode.IN_TABLE_BODY:
807
+ case InsertionMode.IN_ROW:
808
+ case InsertionMode.IN_CELL:
809
+ case InsertionMode.IN_SELECT:
810
+ case InsertionMode.IN_SELECT_IN_TABLE:
811
+ case InsertionMode.IN_TEMPLATE:
812
+ case InsertionMode.IN_FRAMESET:
813
+ case InsertionMode.AFTER_FRAMESET: {
814
+ appendComment(this, token);
815
+ break;
816
+ }
817
+ case InsertionMode.IN_TABLE_TEXT: {
818
+ tokenInTableText(this, token);
819
+ break;
820
+ }
821
+ case InsertionMode.AFTER_BODY: {
822
+ appendCommentToRootHtmlElement(this, token);
823
+ break;
824
+ }
825
+ case InsertionMode.AFTER_AFTER_BODY:
826
+ case InsertionMode.AFTER_AFTER_FRAMESET: {
827
+ appendCommentToDocument(this, token);
828
+ break;
829
+ }
830
+ default:
831
+ // Do nothing
832
+ }
833
+ }
834
+ /** @internal */
835
+ onDoctype(token) {
836
+ this.skipNextNewLine = false;
837
+ switch (this.insertionMode) {
838
+ case InsertionMode.INITIAL: {
839
+ doctypeInInitialMode(this, token);
840
+ break;
841
+ }
842
+ case InsertionMode.BEFORE_HEAD:
843
+ case InsertionMode.IN_HEAD:
844
+ case InsertionMode.IN_HEAD_NO_SCRIPT:
845
+ case InsertionMode.AFTER_HEAD: {
846
+ this._err(token, ERR.misplacedDoctype);
847
+ break;
848
+ }
849
+ case InsertionMode.IN_TABLE_TEXT: {
850
+ tokenInTableText(this, token);
851
+ break;
852
+ }
853
+ default:
854
+ // Do nothing
855
+ }
856
+ }
857
+ /** @internal */
858
+ onStartTag(token) {
859
+ this.skipNextNewLine = false;
860
+ this.currentToken = token;
861
+ this._processStartTag(token);
862
+ if (token.selfClosing && !token.ackSelfClosing) {
863
+ this._err(token, ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
864
+ }
865
+ }
866
+ /**
867
+ * Processes a given start tag.
868
+ *
869
+ * `onStartTag` checks if a self-closing tag was recognized. When a token
870
+ * is moved inbetween multiple insertion modes, this check for self-closing
871
+ * could lead to false positives. To avoid this, `_processStartTag` is used
872
+ * for nested calls.
873
+ *
874
+ * @param token The token to process.
875
+ * @protected
876
+ */
877
+ _processStartTag(token) {
878
+ if (this.shouldProcessStartTagTokenInForeignContent(token)) {
879
+ startTagInForeignContent(this, token);
880
+ }
881
+ else {
882
+ this._startTagOutsideForeignContent(token);
883
+ }
884
+ }
885
+ /** @protected */
886
+ _startTagOutsideForeignContent(token) {
887
+ switch (this.insertionMode) {
888
+ case InsertionMode.INITIAL: {
889
+ tokenInInitialMode(this, token);
890
+ break;
891
+ }
892
+ case InsertionMode.BEFORE_HTML: {
893
+ startTagBeforeHtml(this, token);
894
+ break;
895
+ }
896
+ case InsertionMode.BEFORE_HEAD: {
897
+ startTagBeforeHead(this, token);
898
+ break;
899
+ }
900
+ case InsertionMode.IN_HEAD: {
901
+ startTagInHead(this, token);
902
+ break;
903
+ }
904
+ case InsertionMode.IN_HEAD_NO_SCRIPT: {
905
+ startTagInHeadNoScript(this, token);
906
+ break;
907
+ }
908
+ case InsertionMode.AFTER_HEAD: {
909
+ startTagAfterHead(this, token);
910
+ break;
911
+ }
912
+ case InsertionMode.IN_BODY: {
913
+ startTagInBody(this, token);
914
+ break;
915
+ }
916
+ case InsertionMode.IN_TABLE: {
917
+ startTagInTable(this, token);
918
+ break;
919
+ }
920
+ case InsertionMode.IN_TABLE_TEXT: {
921
+ tokenInTableText(this, token);
922
+ break;
923
+ }
924
+ case InsertionMode.IN_CAPTION: {
925
+ startTagInCaption(this, token);
926
+ break;
927
+ }
928
+ case InsertionMode.IN_COLUMN_GROUP: {
929
+ startTagInColumnGroup(this, token);
930
+ break;
931
+ }
932
+ case InsertionMode.IN_TABLE_BODY: {
933
+ startTagInTableBody(this, token);
934
+ break;
935
+ }
936
+ case InsertionMode.IN_ROW: {
937
+ startTagInRow(this, token);
938
+ break;
939
+ }
940
+ case InsertionMode.IN_CELL: {
941
+ startTagInCell(this, token);
942
+ break;
943
+ }
944
+ case InsertionMode.IN_SELECT: {
945
+ startTagInSelect(this, token);
946
+ break;
947
+ }
948
+ case InsertionMode.IN_SELECT_IN_TABLE: {
949
+ startTagInSelectInTable(this, token);
950
+ break;
951
+ }
952
+ case InsertionMode.IN_TEMPLATE: {
953
+ startTagInTemplate(this, token);
954
+ break;
955
+ }
956
+ case InsertionMode.AFTER_BODY: {
957
+ startTagAfterBody(this, token);
958
+ break;
959
+ }
960
+ case InsertionMode.IN_FRAMESET: {
961
+ startTagInFrameset(this, token);
962
+ break;
963
+ }
964
+ case InsertionMode.AFTER_FRAMESET: {
965
+ startTagAfterFrameset(this, token);
966
+ break;
967
+ }
968
+ case InsertionMode.AFTER_AFTER_BODY: {
969
+ startTagAfterAfterBody(this, token);
970
+ break;
971
+ }
972
+ case InsertionMode.AFTER_AFTER_FRAMESET: {
973
+ startTagAfterAfterFrameset(this, token);
974
+ break;
975
+ }
976
+ default:
977
+ // Do nothing
978
+ }
979
+ }
980
+ /** @internal */
981
+ onEndTag(token) {
982
+ this.skipNextNewLine = false;
983
+ this.currentToken = token;
984
+ if (this.currentNotInHTML) {
985
+ endTagInForeignContent(this, token);
986
+ }
987
+ else {
988
+ this._endTagOutsideForeignContent(token);
989
+ }
990
+ }
991
+ /** @protected */
992
+ _endTagOutsideForeignContent(token) {
993
+ switch (this.insertionMode) {
994
+ case InsertionMode.INITIAL: {
995
+ tokenInInitialMode(this, token);
996
+ break;
997
+ }
998
+ case InsertionMode.BEFORE_HTML: {
999
+ endTagBeforeHtml(this, token);
1000
+ break;
1001
+ }
1002
+ case InsertionMode.BEFORE_HEAD: {
1003
+ endTagBeforeHead(this, token);
1004
+ break;
1005
+ }
1006
+ case InsertionMode.IN_HEAD: {
1007
+ endTagInHead(this, token);
1008
+ break;
1009
+ }
1010
+ case InsertionMode.IN_HEAD_NO_SCRIPT: {
1011
+ endTagInHeadNoScript(this, token);
1012
+ break;
1013
+ }
1014
+ case InsertionMode.AFTER_HEAD: {
1015
+ endTagAfterHead(this, token);
1016
+ break;
1017
+ }
1018
+ case InsertionMode.IN_BODY: {
1019
+ endTagInBody(this, token);
1020
+ break;
1021
+ }
1022
+ case InsertionMode.TEXT: {
1023
+ endTagInText(this, token);
1024
+ break;
1025
+ }
1026
+ case InsertionMode.IN_TABLE: {
1027
+ endTagInTable(this, token);
1028
+ break;
1029
+ }
1030
+ case InsertionMode.IN_TABLE_TEXT: {
1031
+ tokenInTableText(this, token);
1032
+ break;
1033
+ }
1034
+ case InsertionMode.IN_CAPTION: {
1035
+ endTagInCaption(this, token);
1036
+ break;
1037
+ }
1038
+ case InsertionMode.IN_COLUMN_GROUP: {
1039
+ endTagInColumnGroup(this, token);
1040
+ break;
1041
+ }
1042
+ case InsertionMode.IN_TABLE_BODY: {
1043
+ endTagInTableBody(this, token);
1044
+ break;
1045
+ }
1046
+ case InsertionMode.IN_ROW: {
1047
+ endTagInRow(this, token);
1048
+ break;
1049
+ }
1050
+ case InsertionMode.IN_CELL: {
1051
+ endTagInCell(this, token);
1052
+ break;
1053
+ }
1054
+ case InsertionMode.IN_SELECT: {
1055
+ endTagInSelect(this, token);
1056
+ break;
1057
+ }
1058
+ case InsertionMode.IN_SELECT_IN_TABLE: {
1059
+ endTagInSelectInTable(this, token);
1060
+ break;
1061
+ }
1062
+ case InsertionMode.IN_TEMPLATE: {
1063
+ endTagInTemplate(this, token);
1064
+ break;
1065
+ }
1066
+ case InsertionMode.AFTER_BODY: {
1067
+ endTagAfterBody(this, token);
1068
+ break;
1069
+ }
1070
+ case InsertionMode.IN_FRAMESET: {
1071
+ endTagInFrameset(this, token);
1072
+ break;
1073
+ }
1074
+ case InsertionMode.AFTER_FRAMESET: {
1075
+ endTagAfterFrameset(this, token);
1076
+ break;
1077
+ }
1078
+ case InsertionMode.AFTER_AFTER_BODY: {
1079
+ tokenAfterAfterBody(this, token);
1080
+ break;
1081
+ }
1082
+ default:
1083
+ // Do nothing
1084
+ }
1085
+ }
1086
+ /** @internal */
1087
+ onEof(token) {
1088
+ switch (this.insertionMode) {
1089
+ case InsertionMode.INITIAL: {
1090
+ tokenInInitialMode(this, token);
1091
+ break;
1092
+ }
1093
+ case InsertionMode.BEFORE_HTML: {
1094
+ tokenBeforeHtml(this, token);
1095
+ break;
1096
+ }
1097
+ case InsertionMode.BEFORE_HEAD: {
1098
+ tokenBeforeHead(this, token);
1099
+ break;
1100
+ }
1101
+ case InsertionMode.IN_HEAD: {
1102
+ tokenInHead(this, token);
1103
+ break;
1104
+ }
1105
+ case InsertionMode.IN_HEAD_NO_SCRIPT: {
1106
+ tokenInHeadNoScript(this, token);
1107
+ break;
1108
+ }
1109
+ case InsertionMode.AFTER_HEAD: {
1110
+ tokenAfterHead(this, token);
1111
+ break;
1112
+ }
1113
+ case InsertionMode.IN_BODY:
1114
+ case InsertionMode.IN_TABLE:
1115
+ case InsertionMode.IN_CAPTION:
1116
+ case InsertionMode.IN_COLUMN_GROUP:
1117
+ case InsertionMode.IN_TABLE_BODY:
1118
+ case InsertionMode.IN_ROW:
1119
+ case InsertionMode.IN_CELL:
1120
+ case InsertionMode.IN_SELECT:
1121
+ case InsertionMode.IN_SELECT_IN_TABLE: {
1122
+ eofInBody(this, token);
1123
+ break;
1124
+ }
1125
+ case InsertionMode.TEXT: {
1126
+ eofInText(this, token);
1127
+ break;
1128
+ }
1129
+ case InsertionMode.IN_TABLE_TEXT: {
1130
+ tokenInTableText(this, token);
1131
+ break;
1132
+ }
1133
+ case InsertionMode.IN_TEMPLATE: {
1134
+ eofInTemplate(this, token);
1135
+ break;
1136
+ }
1137
+ case InsertionMode.AFTER_BODY:
1138
+ case InsertionMode.IN_FRAMESET:
1139
+ case InsertionMode.AFTER_FRAMESET:
1140
+ case InsertionMode.AFTER_AFTER_BODY:
1141
+ case InsertionMode.AFTER_AFTER_FRAMESET: {
1142
+ stopParsing(this, token);
1143
+ break;
1144
+ }
1145
+ default:
1146
+ // Do nothing
1147
+ }
1148
+ }
1149
+ /** @internal */
1150
+ onWhitespaceCharacter(token) {
1151
+ if (this.skipNextNewLine) {
1152
+ this.skipNextNewLine = false;
1153
+ if (token.chars.charCodeAt(0) === unicode.CODE_POINTS.LINE_FEED) {
1154
+ if (token.chars.length === 1) {
1155
+ return;
1156
+ }
1157
+ token.chars = token.chars.substr(1);
1158
+ }
1159
+ }
1160
+ if (this.tokenizer.inForeignNode) {
1161
+ this._insertCharacters(token);
1162
+ return;
1163
+ }
1164
+ switch (this.insertionMode) {
1165
+ case InsertionMode.IN_HEAD:
1166
+ case InsertionMode.IN_HEAD_NO_SCRIPT:
1167
+ case InsertionMode.AFTER_HEAD:
1168
+ case InsertionMode.TEXT:
1169
+ case InsertionMode.IN_COLUMN_GROUP:
1170
+ case InsertionMode.IN_SELECT:
1171
+ case InsertionMode.IN_SELECT_IN_TABLE:
1172
+ case InsertionMode.IN_FRAMESET:
1173
+ case InsertionMode.AFTER_FRAMESET: {
1174
+ this._insertCharacters(token);
1175
+ break;
1176
+ }
1177
+ case InsertionMode.IN_BODY:
1178
+ case InsertionMode.IN_CAPTION:
1179
+ case InsertionMode.IN_CELL:
1180
+ case InsertionMode.IN_TEMPLATE:
1181
+ case InsertionMode.AFTER_BODY:
1182
+ case InsertionMode.AFTER_AFTER_BODY:
1183
+ case InsertionMode.AFTER_AFTER_FRAMESET: {
1184
+ whitespaceCharacterInBody(this, token);
1185
+ break;
1186
+ }
1187
+ case InsertionMode.IN_TABLE:
1188
+ case InsertionMode.IN_TABLE_BODY:
1189
+ case InsertionMode.IN_ROW: {
1190
+ characterInTable(this, token);
1191
+ break;
1192
+ }
1193
+ case InsertionMode.IN_TABLE_TEXT: {
1194
+ whitespaceCharacterInTableText(this, token);
1195
+ break;
1196
+ }
1197
+ default:
1198
+ // Do nothing
1199
+ }
1200
+ }
1201
+ }
1202
+ //Adoption agency algorithm
1203
+ //(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency)
1204
+ //------------------------------------------------------------------
1205
+ //Steps 5-8 of the algorithm
1206
+ function aaObtainFormattingElementEntry(p, token) {
1207
+ let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName);
1208
+ if (formattingElementEntry) {
1209
+ if (!p.openElements.contains(formattingElementEntry.element)) {
1210
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
1211
+ formattingElementEntry = null;
1212
+ }
1213
+ else if (!p.openElements.hasInScope(token.tagID)) {
1214
+ formattingElementEntry = null;
1215
+ }
1216
+ }
1217
+ else {
1218
+ genericEndTagInBody(p, token);
1219
+ }
1220
+ return formattingElementEntry;
1221
+ }
1222
+ //Steps 9 and 10 of the algorithm
1223
+ function aaObtainFurthestBlock(p, formattingElementEntry) {
1224
+ let furthestBlock = null;
1225
+ let idx = p.openElements.stackTop;
1226
+ for (; idx >= 0; idx--) {
1227
+ const element = p.openElements.items[idx];
1228
+ if (element === formattingElementEntry.element) {
1229
+ break;
1230
+ }
1231
+ if (p._isSpecialElement(element, p.openElements.tagIDs[idx])) {
1232
+ furthestBlock = element;
1233
+ }
1234
+ }
1235
+ if (!furthestBlock) {
1236
+ p.openElements.shortenToLength(Math.max(idx, 0));
1237
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
1238
+ }
1239
+ return furthestBlock;
1240
+ }
1241
+ //Step 13 of the algorithm
1242
+ function aaInnerLoop(p, furthestBlock, formattingElement) {
1243
+ let lastElement = furthestBlock;
1244
+ let nextElement = p.openElements.getCommonAncestor(furthestBlock);
1245
+ const intendedParent = p.openElements.getCommonAncestor(formattingElement);
1246
+ for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
1247
+ //NOTE: store the next element for the next loop iteration (it may be deleted from the stack by step 9.5)
1248
+ nextElement = p.openElements.getCommonAncestor(element);
1249
+ const elementEntry = p.activeFormattingElements.getElementEntry(element);
1250
+ const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
1251
+ const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
1252
+ if (shouldRemoveFromOpenElements) {
1253
+ if (counterOverflow) {
1254
+ p.activeFormattingElements.removeEntry(elementEntry);
1255
+ }
1256
+ p.openElements.remove(element);
1257
+ }
1258
+ else {
1259
+ element = aaRecreateElementFromEntry(p, elementEntry, intendedParent);
1260
+ if (lastElement === furthestBlock) {
1261
+ p.activeFormattingElements.bookmark = elementEntry;
1262
+ }
1263
+ p.treeAdapter.detachNode(lastElement);
1264
+ p.treeAdapter.appendChild(element, lastElement);
1265
+ lastElement = element;
1266
+ }
1267
+ }
1268
+ return lastElement;
1269
+ }
1270
+ //Step 13.7 of the algorithm
1271
+ function aaRecreateElementFromEntry(p, elementEntry, intendedParent) {
1272
+ const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
1273
+ const newElement = p._createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs, intendedParent);
1274
+ p.openElements.replace(elementEntry.element, newElement);
1275
+ elementEntry.element = newElement;
1276
+ return newElement;
1277
+ }
1278
+ //Step 14 of the algorithm
1279
+ function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
1280
+ const tn = p.treeAdapter.getTagName(commonAncestor);
1281
+ const tid = getTagID(tn);
1282
+ if (p._isElementCausesFosterParenting(tid)) {
1283
+ p._fosterParentElement(lastElement);
1284
+ }
1285
+ else {
1286
+ const ns = p.treeAdapter.getNamespaceURI(commonAncestor);
1287
+ if (tid === $.TEMPLATE && ns === NS.HTML) {
1288
+ commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor);
1289
+ }
1290
+ p.treeAdapter.appendChild(commonAncestor, lastElement);
1291
+ }
1292
+ }
1293
+ //Steps 15-19 of the algorithm
1294
+ function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
1295
+ const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
1296
+ const { token } = formattingElementEntry;
1297
+ const newElement = p._createElement(token.tagName, ns, token.attrs, furthestBlock);
1298
+ p._adoptNodes(furthestBlock, newElement);
1299
+ p.treeAdapter.appendChild(furthestBlock, newElement);
1300
+ p.activeFormattingElements.insertElementAfterBookmark(newElement, token);
1301
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
1302
+ p.openElements.remove(formattingElementEntry.element);
1303
+ p.openElements.insertAfter(furthestBlock, newElement, token.tagID);
1304
+ }
1305
+ //Algorithm entry point
1306
+ function callAdoptionAgency(p, token) {
1307
+ for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
1308
+ const formattingElementEntry = aaObtainFormattingElementEntry(p, token);
1309
+ if (!formattingElementEntry) {
1310
+ break;
1311
+ }
1312
+ const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
1313
+ if (!furthestBlock) {
1314
+ break;
1315
+ }
1316
+ p.activeFormattingElements.bookmark = formattingElementEntry;
1317
+ const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
1318
+ const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
1319
+ p.treeAdapter.detachNode(lastElement);
1320
+ if (commonAncestor)
1321
+ aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement);
1322
+ aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry);
1323
+ }
1324
+ }
1325
+ //Generic token handlers
1326
+ //------------------------------------------------------------------
1327
+ function appendComment(p, token) {
1328
+ p._appendCommentNode(token, p.openElements.currentTmplContentOrNode);
1329
+ }
1330
+ function appendCommentToRootHtmlElement(p, token) {
1331
+ p._appendCommentNode(token, p.openElements.items[0]);
1332
+ }
1333
+ function appendCommentToDocument(p, token) {
1334
+ p._appendCommentNode(token, p.document);
1335
+ }
1336
+ function stopParsing(p, token) {
1337
+ p.stopped = true;
1338
+ // NOTE: Set end locations for elements that remain on the open element stack.
1339
+ if (token.location) {
1340
+ // NOTE: If we are not in a fragment, `html` and `body` will stay on the stack.
1341
+ // This is a problem, as we might overwrite their end position here.
1342
+ const target = p.fragmentContext ? 0 : 2;
1343
+ for (let i = p.openElements.stackTop; i >= target; i--) {
1344
+ p._setEndLocation(p.openElements.items[i], token);
1345
+ }
1346
+ // Handle `html` and `body`
1347
+ if (!p.fragmentContext && p.openElements.stackTop >= 0) {
1348
+ const htmlElement = p.openElements.items[0];
1349
+ const htmlLocation = p.treeAdapter.getNodeSourceCodeLocation(htmlElement);
1350
+ if (htmlLocation && !htmlLocation.endTag) {
1351
+ p._setEndLocation(htmlElement, token);
1352
+ if (p.openElements.stackTop >= 1) {
1353
+ const bodyElement = p.openElements.items[1];
1354
+ const bodyLocation = p.treeAdapter.getNodeSourceCodeLocation(bodyElement);
1355
+ if (bodyLocation && !bodyLocation.endTag) {
1356
+ p._setEndLocation(bodyElement, token);
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+ }
1362
+ }
1363
+ // The "initial" insertion mode
1364
+ //------------------------------------------------------------------
1365
+ function doctypeInInitialMode(p, token) {
1366
+ p._setDocumentType(token);
1367
+ const mode = token.forceQuirks ? DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token);
1368
+ if (!doctype.isConforming(token)) {
1369
+ p._err(token, ERR.nonConformingDoctype);
1370
+ }
1371
+ p.treeAdapter.setDocumentMode(p.document, mode);
1372
+ p.insertionMode = InsertionMode.BEFORE_HTML;
1373
+ }
1374
+ function tokenInInitialMode(p, token) {
1375
+ p._err(token, ERR.missingDoctype, true);
1376
+ p.treeAdapter.setDocumentMode(p.document, DOCUMENT_MODE.QUIRKS);
1377
+ p.insertionMode = InsertionMode.BEFORE_HTML;
1378
+ p._processToken(token);
1379
+ }
1380
+ // The "before html" insertion mode
1381
+ //------------------------------------------------------------------
1382
+ function startTagBeforeHtml(p, token) {
1383
+ if (token.tagID === $.HTML) {
1384
+ p._insertElement(token, NS.HTML);
1385
+ p.insertionMode = InsertionMode.BEFORE_HEAD;
1386
+ }
1387
+ else {
1388
+ tokenBeforeHtml(p, token);
1389
+ }
1390
+ }
1391
+ function endTagBeforeHtml(p, token) {
1392
+ const tn = token.tagID;
1393
+ if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) {
1394
+ tokenBeforeHtml(p, token);
1395
+ }
1396
+ }
1397
+ function tokenBeforeHtml(p, token) {
1398
+ p._insertFakeRootElement();
1399
+ p.insertionMode = InsertionMode.BEFORE_HEAD;
1400
+ p._processToken(token);
1401
+ }
1402
+ // The "before head" insertion mode
1403
+ //------------------------------------------------------------------
1404
+ function startTagBeforeHead(p, token) {
1405
+ switch (token.tagID) {
1406
+ case $.HTML: {
1407
+ startTagInBody(p, token);
1408
+ break;
1409
+ }
1410
+ case $.HEAD: {
1411
+ p._insertElement(token, NS.HTML);
1412
+ p.headElement = p.openElements.current;
1413
+ p.insertionMode = InsertionMode.IN_HEAD;
1414
+ break;
1415
+ }
1416
+ default: {
1417
+ tokenBeforeHead(p, token);
1418
+ }
1419
+ }
1420
+ }
1421
+ function endTagBeforeHead(p, token) {
1422
+ const tn = token.tagID;
1423
+ if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) {
1424
+ tokenBeforeHead(p, token);
1425
+ }
1426
+ else {
1427
+ p._err(token, ERR.endTagWithoutMatchingOpenElement);
1428
+ }
1429
+ }
1430
+ function tokenBeforeHead(p, token) {
1431
+ p._insertFakeElement(TN.HEAD, $.HEAD);
1432
+ p.headElement = p.openElements.current;
1433
+ p.insertionMode = InsertionMode.IN_HEAD;
1434
+ p._processToken(token);
1435
+ }
1436
+ // The "in head" insertion mode
1437
+ //------------------------------------------------------------------
1438
+ function startTagInHead(p, token) {
1439
+ switch (token.tagID) {
1440
+ case $.HTML: {
1441
+ startTagInBody(p, token);
1442
+ break;
1443
+ }
1444
+ case $.BASE:
1445
+ case $.BASEFONT:
1446
+ case $.BGSOUND:
1447
+ case $.LINK:
1448
+ case $.META: {
1449
+ p._appendElement(token, NS.HTML);
1450
+ token.ackSelfClosing = true;
1451
+ break;
1452
+ }
1453
+ case $.TITLE: {
1454
+ p._switchToTextParsing(token, TokenizerMode.RCDATA);
1455
+ break;
1456
+ }
1457
+ case $.NOSCRIPT: {
1458
+ if (p.options.scriptingEnabled) {
1459
+ p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
1460
+ }
1461
+ else {
1462
+ p._insertElement(token, NS.HTML);
1463
+ p.insertionMode = InsertionMode.IN_HEAD_NO_SCRIPT;
1464
+ }
1465
+ break;
1466
+ }
1467
+ case $.NOFRAMES:
1468
+ case $.STYLE: {
1469
+ p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
1470
+ break;
1471
+ }
1472
+ case $.SCRIPT: {
1473
+ p._switchToTextParsing(token, TokenizerMode.SCRIPT_DATA);
1474
+ break;
1475
+ }
1476
+ case $.TEMPLATE: {
1477
+ p._insertTemplate(token);
1478
+ p.activeFormattingElements.insertMarker();
1479
+ p.framesetOk = false;
1480
+ p.insertionMode = InsertionMode.IN_TEMPLATE;
1481
+ p.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
1482
+ break;
1483
+ }
1484
+ case $.HEAD: {
1485
+ p._err(token, ERR.misplacedStartTagForHeadElement);
1486
+ break;
1487
+ }
1488
+ default: {
1489
+ tokenInHead(p, token);
1490
+ }
1491
+ }
1492
+ }
1493
+ function endTagInHead(p, token) {
1494
+ switch (token.tagID) {
1495
+ case $.HEAD: {
1496
+ p.openElements.pop();
1497
+ p.insertionMode = InsertionMode.AFTER_HEAD;
1498
+ break;
1499
+ }
1500
+ case $.BODY:
1501
+ case $.BR:
1502
+ case $.HTML: {
1503
+ tokenInHead(p, token);
1504
+ break;
1505
+ }
1506
+ case $.TEMPLATE: {
1507
+ templateEndTagInHead(p, token);
1508
+ break;
1509
+ }
1510
+ default: {
1511
+ p._err(token, ERR.endTagWithoutMatchingOpenElement);
1512
+ }
1513
+ }
1514
+ }
1515
+ function templateEndTagInHead(p, token) {
1516
+ if (p.openElements.tmplCount > 0) {
1517
+ p.openElements.generateImpliedEndTagsThoroughly();
1518
+ if (p.openElements.currentTagId !== $.TEMPLATE) {
1519
+ p._err(token, ERR.closingOfElementWithOpenChildElements);
1520
+ }
1521
+ p.openElements.popUntilTagNamePopped($.TEMPLATE);
1522
+ p.activeFormattingElements.clearToLastMarker();
1523
+ p.tmplInsertionModeStack.shift();
1524
+ p._resetInsertionMode();
1525
+ }
1526
+ else {
1527
+ p._err(token, ERR.endTagWithoutMatchingOpenElement);
1528
+ }
1529
+ }
1530
+ function tokenInHead(p, token) {
1531
+ p.openElements.pop();
1532
+ p.insertionMode = InsertionMode.AFTER_HEAD;
1533
+ p._processToken(token);
1534
+ }
1535
+ // The "in head no script" insertion mode
1536
+ //------------------------------------------------------------------
1537
+ function startTagInHeadNoScript(p, token) {
1538
+ switch (token.tagID) {
1539
+ case $.HTML: {
1540
+ startTagInBody(p, token);
1541
+ break;
1542
+ }
1543
+ case $.BASEFONT:
1544
+ case $.BGSOUND:
1545
+ case $.HEAD:
1546
+ case $.LINK:
1547
+ case $.META:
1548
+ case $.NOFRAMES:
1549
+ case $.STYLE: {
1550
+ startTagInHead(p, token);
1551
+ break;
1552
+ }
1553
+ case $.NOSCRIPT: {
1554
+ p._err(token, ERR.nestedNoscriptInHead);
1555
+ break;
1556
+ }
1557
+ default: {
1558
+ tokenInHeadNoScript(p, token);
1559
+ }
1560
+ }
1561
+ }
1562
+ function endTagInHeadNoScript(p, token) {
1563
+ switch (token.tagID) {
1564
+ case $.NOSCRIPT: {
1565
+ p.openElements.pop();
1566
+ p.insertionMode = InsertionMode.IN_HEAD;
1567
+ break;
1568
+ }
1569
+ case $.BR: {
1570
+ tokenInHeadNoScript(p, token);
1571
+ break;
1572
+ }
1573
+ default: {
1574
+ p._err(token, ERR.endTagWithoutMatchingOpenElement);
1575
+ }
1576
+ }
1577
+ }
1578
+ function tokenInHeadNoScript(p, token) {
1579
+ const errCode = token.type === TokenType.EOF ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead;
1580
+ p._err(token, errCode);
1581
+ p.openElements.pop();
1582
+ p.insertionMode = InsertionMode.IN_HEAD;
1583
+ p._processToken(token);
1584
+ }
1585
+ // The "after head" insertion mode
1586
+ //------------------------------------------------------------------
1587
+ function startTagAfterHead(p, token) {
1588
+ switch (token.tagID) {
1589
+ case $.HTML: {
1590
+ startTagInBody(p, token);
1591
+ break;
1592
+ }
1593
+ case $.BODY: {
1594
+ p._insertElement(token, NS.HTML);
1595
+ p.framesetOk = false;
1596
+ p.insertionMode = InsertionMode.IN_BODY;
1597
+ break;
1598
+ }
1599
+ case $.FRAMESET: {
1600
+ p._insertElement(token, NS.HTML);
1601
+ p.insertionMode = InsertionMode.IN_FRAMESET;
1602
+ break;
1603
+ }
1604
+ case $.BASE:
1605
+ case $.BASEFONT:
1606
+ case $.BGSOUND:
1607
+ case $.LINK:
1608
+ case $.META:
1609
+ case $.NOFRAMES:
1610
+ case $.SCRIPT:
1611
+ case $.STYLE:
1612
+ case $.TEMPLATE:
1613
+ case $.TITLE: {
1614
+ p._err(token, ERR.abandonedHeadElementChild);
1615
+ p.openElements.push(p.headElement, $.HEAD);
1616
+ startTagInHead(p, token);
1617
+ p.openElements.remove(p.headElement);
1618
+ break;
1619
+ }
1620
+ case $.HEAD: {
1621
+ p._err(token, ERR.misplacedStartTagForHeadElement);
1622
+ break;
1623
+ }
1624
+ default: {
1625
+ tokenAfterHead(p, token);
1626
+ }
1627
+ }
1628
+ }
1629
+ function endTagAfterHead(p, token) {
1630
+ switch (token.tagID) {
1631
+ case $.BODY:
1632
+ case $.HTML:
1633
+ case $.BR: {
1634
+ tokenAfterHead(p, token);
1635
+ break;
1636
+ }
1637
+ case $.TEMPLATE: {
1638
+ templateEndTagInHead(p, token);
1639
+ break;
1640
+ }
1641
+ default: {
1642
+ p._err(token, ERR.endTagWithoutMatchingOpenElement);
1643
+ }
1644
+ }
1645
+ }
1646
+ function tokenAfterHead(p, token) {
1647
+ p._insertFakeElement(TN.BODY, $.BODY);
1648
+ p.insertionMode = InsertionMode.IN_BODY;
1649
+ modeInBody(p, token);
1650
+ }
1651
+ // The "in body" insertion mode
1652
+ //------------------------------------------------------------------
1653
+ function modeInBody(p, token) {
1654
+ switch (token.type) {
1655
+ case TokenType.CHARACTER: {
1656
+ characterInBody(p, token);
1657
+ break;
1658
+ }
1659
+ case TokenType.WHITESPACE_CHARACTER: {
1660
+ whitespaceCharacterInBody(p, token);
1661
+ break;
1662
+ }
1663
+ case TokenType.COMMENT: {
1664
+ appendComment(p, token);
1665
+ break;
1666
+ }
1667
+ case TokenType.START_TAG: {
1668
+ startTagInBody(p, token);
1669
+ break;
1670
+ }
1671
+ case TokenType.END_TAG: {
1672
+ endTagInBody(p, token);
1673
+ break;
1674
+ }
1675
+ case TokenType.EOF: {
1676
+ eofInBody(p, token);
1677
+ break;
1678
+ }
1679
+ default:
1680
+ // Do nothing
1681
+ }
1682
+ }
1683
+ function whitespaceCharacterInBody(p, token) {
1684
+ p._reconstructActiveFormattingElements();
1685
+ p._insertCharacters(token);
1686
+ }
1687
+ function characterInBody(p, token) {
1688
+ p._reconstructActiveFormattingElements();
1689
+ p._insertCharacters(token);
1690
+ p.framesetOk = false;
1691
+ }
1692
+ function htmlStartTagInBody(p, token) {
1693
+ if (p.openElements.tmplCount === 0) {
1694
+ p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs);
1695
+ }
1696
+ }
1697
+ function bodyStartTagInBody(p, token) {
1698
+ const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
1699
+ if (bodyElement && p.openElements.tmplCount === 0) {
1700
+ p.framesetOk = false;
1701
+ p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
1702
+ }
1703
+ }
1704
+ function framesetStartTagInBody(p, token) {
1705
+ const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
1706
+ if (p.framesetOk && bodyElement) {
1707
+ p.treeAdapter.detachNode(bodyElement);
1708
+ p.openElements.popAllUpToHtmlElement();
1709
+ p._insertElement(token, NS.HTML);
1710
+ p.insertionMode = InsertionMode.IN_FRAMESET;
1711
+ }
1712
+ }
1713
+ function addressStartTagInBody(p, token) {
1714
+ if (p.openElements.hasInButtonScope($.P)) {
1715
+ p._closePElement();
1716
+ }
1717
+ p._insertElement(token, NS.HTML);
1718
+ }
1719
+ function numberedHeaderStartTagInBody(p, token) {
1720
+ if (p.openElements.hasInButtonScope($.P)) {
1721
+ p._closePElement();
1722
+ }
1723
+ if (p.openElements.currentTagId !== undefined && NUMBERED_HEADERS.has(p.openElements.currentTagId)) {
1724
+ p.openElements.pop();
1725
+ }
1726
+ p._insertElement(token, NS.HTML);
1727
+ }
1728
+ function preStartTagInBody(p, token) {
1729
+ if (p.openElements.hasInButtonScope($.P)) {
1730
+ p._closePElement();
1731
+ }
1732
+ p._insertElement(token, NS.HTML);
1733
+ //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
1734
+ //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.)
1735
+ p.skipNextNewLine = true;
1736
+ p.framesetOk = false;
1737
+ }
1738
+ function formStartTagInBody(p, token) {
1739
+ const inTemplate = p.openElements.tmplCount > 0;
1740
+ if (!p.formElement || inTemplate) {
1741
+ if (p.openElements.hasInButtonScope($.P)) {
1742
+ p._closePElement();
1743
+ }
1744
+ p._insertElement(token, NS.HTML);
1745
+ if (!inTemplate) {
1746
+ p.formElement = p.openElements.current;
1747
+ }
1748
+ }
1749
+ }
1750
+ function listItemStartTagInBody(p, token) {
1751
+ p.framesetOk = false;
1752
+ const tn = token.tagID;
1753
+ for (let i = p.openElements.stackTop; i >= 0; i--) {
1754
+ const elementId = p.openElements.tagIDs[i];
1755
+ if ((tn === $.LI && elementId === $.LI) ||
1756
+ ((tn === $.DD || tn === $.DT) && (elementId === $.DD || elementId === $.DT))) {
1757
+ p.openElements.generateImpliedEndTagsWithExclusion(elementId);
1758
+ p.openElements.popUntilTagNamePopped(elementId);
1759
+ break;
1760
+ }
1761
+ if (elementId !== $.ADDRESS &&
1762
+ elementId !== $.DIV &&
1763
+ elementId !== $.P &&
1764
+ p._isSpecialElement(p.openElements.items[i], elementId)) {
1765
+ break;
1766
+ }
1767
+ }
1768
+ if (p.openElements.hasInButtonScope($.P)) {
1769
+ p._closePElement();
1770
+ }
1771
+ p._insertElement(token, NS.HTML);
1772
+ }
1773
+ function plaintextStartTagInBody(p, token) {
1774
+ if (p.openElements.hasInButtonScope($.P)) {
1775
+ p._closePElement();
1776
+ }
1777
+ p._insertElement(token, NS.HTML);
1778
+ p.tokenizer.state = TokenizerMode.PLAINTEXT;
1779
+ }
1780
+ function buttonStartTagInBody(p, token) {
1781
+ if (p.openElements.hasInScope($.BUTTON)) {
1782
+ p.openElements.generateImpliedEndTags();
1783
+ p.openElements.popUntilTagNamePopped($.BUTTON);
1784
+ }
1785
+ p._reconstructActiveFormattingElements();
1786
+ p._insertElement(token, NS.HTML);
1787
+ p.framesetOk = false;
1788
+ }
1789
+ function aStartTagInBody(p, token) {
1790
+ const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(TN.A);
1791
+ if (activeElementEntry) {
1792
+ callAdoptionAgency(p, token);
1793
+ p.openElements.remove(activeElementEntry.element);
1794
+ p.activeFormattingElements.removeEntry(activeElementEntry);
1795
+ }
1796
+ p._reconstructActiveFormattingElements();
1797
+ p._insertElement(token, NS.HTML);
1798
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
1799
+ }
1800
+ function bStartTagInBody(p, token) {
1801
+ p._reconstructActiveFormattingElements();
1802
+ p._insertElement(token, NS.HTML);
1803
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
1804
+ }
1805
+ function nobrStartTagInBody(p, token) {
1806
+ p._reconstructActiveFormattingElements();
1807
+ if (p.openElements.hasInScope($.NOBR)) {
1808
+ callAdoptionAgency(p, token);
1809
+ p._reconstructActiveFormattingElements();
1810
+ }
1811
+ p._insertElement(token, NS.HTML);
1812
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
1813
+ }
1814
+ function appletStartTagInBody(p, token) {
1815
+ p._reconstructActiveFormattingElements();
1816
+ p._insertElement(token, NS.HTML);
1817
+ p.activeFormattingElements.insertMarker();
1818
+ p.framesetOk = false;
1819
+ }
1820
+ function tableStartTagInBody(p, token) {
1821
+ if (p.treeAdapter.getDocumentMode(p.document) !== DOCUMENT_MODE.QUIRKS && p.openElements.hasInButtonScope($.P)) {
1822
+ p._closePElement();
1823
+ }
1824
+ p._insertElement(token, NS.HTML);
1825
+ p.framesetOk = false;
1826
+ p.insertionMode = InsertionMode.IN_TABLE;
1827
+ }
1828
+ function areaStartTagInBody(p, token) {
1829
+ p._reconstructActiveFormattingElements();
1830
+ p._appendElement(token, NS.HTML);
1831
+ p.framesetOk = false;
1832
+ token.ackSelfClosing = true;
1833
+ }
1834
+ function isHiddenInput(token) {
1835
+ const inputType = getTokenAttr(token, ATTRS.TYPE);
1836
+ return inputType != null && inputType.toLowerCase() === HIDDEN_INPUT_TYPE;
1837
+ }
1838
+ function inputStartTagInBody(p, token) {
1839
+ p._reconstructActiveFormattingElements();
1840
+ p._appendElement(token, NS.HTML);
1841
+ if (!isHiddenInput(token)) {
1842
+ p.framesetOk = false;
1843
+ }
1844
+ token.ackSelfClosing = true;
1845
+ }
1846
+ function paramStartTagInBody(p, token) {
1847
+ p._appendElement(token, NS.HTML);
1848
+ token.ackSelfClosing = true;
1849
+ }
1850
+ function hrStartTagInBody(p, token) {
1851
+ if (p.openElements.hasInButtonScope($.P)) {
1852
+ p._closePElement();
1853
+ }
1854
+ p._appendElement(token, NS.HTML);
1855
+ p.framesetOk = false;
1856
+ token.ackSelfClosing = true;
1857
+ }
1858
+ function imageStartTagInBody(p, token) {
1859
+ token.tagName = TN.IMG;
1860
+ token.tagID = $.IMG;
1861
+ areaStartTagInBody(p, token);
1862
+ }
1863
+ function textareaStartTagInBody(p, token) {
1864
+ p._insertElement(token, NS.HTML);
1865
+ //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
1866
+ //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.)
1867
+ p.skipNextNewLine = true;
1868
+ p.tokenizer.state = TokenizerMode.RCDATA;
1869
+ p.originalInsertionMode = p.insertionMode;
1870
+ p.framesetOk = false;
1871
+ p.insertionMode = InsertionMode.TEXT;
1872
+ }
1873
+ function xmpStartTagInBody(p, token) {
1874
+ if (p.openElements.hasInButtonScope($.P)) {
1875
+ p._closePElement();
1876
+ }
1877
+ p._reconstructActiveFormattingElements();
1878
+ p.framesetOk = false;
1879
+ p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
1880
+ }
1881
+ function iframeStartTagInBody(p, token) {
1882
+ p.framesetOk = false;
1883
+ p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
1884
+ }
1885
+ //NOTE: here we assume that we always act as a user agent with enabled plugins/frames, so we parse
1886
+ //<noembed>/<noframes> as rawtext.
1887
+ function rawTextStartTagInBody(p, token) {
1888
+ p._switchToTextParsing(token, TokenizerMode.RAWTEXT);
1889
+ }
1890
+ function selectStartTagInBody(p, token) {
1891
+ p._reconstructActiveFormattingElements();
1892
+ p._insertElement(token, NS.HTML);
1893
+ p.framesetOk = false;
1894
+ p.insertionMode =
1895
+ p.insertionMode === InsertionMode.IN_TABLE ||
1896
+ p.insertionMode === InsertionMode.IN_CAPTION ||
1897
+ p.insertionMode === InsertionMode.IN_TABLE_BODY ||
1898
+ p.insertionMode === InsertionMode.IN_ROW ||
1899
+ p.insertionMode === InsertionMode.IN_CELL
1900
+ ? InsertionMode.IN_SELECT_IN_TABLE
1901
+ : InsertionMode.IN_SELECT;
1902
+ }
1903
+ function optgroupStartTagInBody(p, token) {
1904
+ if (p.openElements.currentTagId === $.OPTION) {
1905
+ p.openElements.pop();
1906
+ }
1907
+ p._reconstructActiveFormattingElements();
1908
+ p._insertElement(token, NS.HTML);
1909
+ }
1910
+ function rbStartTagInBody(p, token) {
1911
+ if (p.openElements.hasInScope($.RUBY)) {
1912
+ p.openElements.generateImpliedEndTags();
1913
+ }
1914
+ p._insertElement(token, NS.HTML);
1915
+ }
1916
+ function rtStartTagInBody(p, token) {
1917
+ if (p.openElements.hasInScope($.RUBY)) {
1918
+ p.openElements.generateImpliedEndTagsWithExclusion($.RTC);
1919
+ }
1920
+ p._insertElement(token, NS.HTML);
1921
+ }
1922
+ function mathStartTagInBody(p, token) {
1923
+ p._reconstructActiveFormattingElements();
1924
+ foreignContent.adjustTokenMathMLAttrs(token);
1925
+ foreignContent.adjustTokenXMLAttrs(token);
1926
+ if (token.selfClosing) {
1927
+ p._appendElement(token, NS.MATHML);
1928
+ }
1929
+ else {
1930
+ p._insertElement(token, NS.MATHML);
1931
+ }
1932
+ token.ackSelfClosing = true;
1933
+ }
1934
+ function svgStartTagInBody(p, token) {
1935
+ p._reconstructActiveFormattingElements();
1936
+ foreignContent.adjustTokenSVGAttrs(token);
1937
+ foreignContent.adjustTokenXMLAttrs(token);
1938
+ if (token.selfClosing) {
1939
+ p._appendElement(token, NS.SVG);
1940
+ }
1941
+ else {
1942
+ p._insertElement(token, NS.SVG);
1943
+ }
1944
+ token.ackSelfClosing = true;
1945
+ }
1946
+ function genericStartTagInBody(p, token) {
1947
+ p._reconstructActiveFormattingElements();
1948
+ p._insertElement(token, NS.HTML);
1949
+ }
1950
+ function startTagInBody(p, token) {
1951
+ switch (token.tagID) {
1952
+ case $.I:
1953
+ case $.S:
1954
+ case $.B:
1955
+ case $.U:
1956
+ case $.EM:
1957
+ case $.TT:
1958
+ case $.BIG:
1959
+ case $.CODE:
1960
+ case $.FONT:
1961
+ case $.SMALL:
1962
+ case $.STRIKE:
1963
+ case $.STRONG: {
1964
+ bStartTagInBody(p, token);
1965
+ break;
1966
+ }
1967
+ case $.A: {
1968
+ aStartTagInBody(p, token);
1969
+ break;
1970
+ }
1971
+ case $.H1:
1972
+ case $.H2:
1973
+ case $.H3:
1974
+ case $.H4:
1975
+ case $.H5:
1976
+ case $.H6: {
1977
+ numberedHeaderStartTagInBody(p, token);
1978
+ break;
1979
+ }
1980
+ case $.P:
1981
+ case $.DL:
1982
+ case $.OL:
1983
+ case $.UL:
1984
+ case $.DIV:
1985
+ case $.DIR:
1986
+ case $.NAV:
1987
+ case $.MAIN:
1988
+ case $.MENU:
1989
+ case $.ASIDE:
1990
+ case $.CENTER:
1991
+ case $.FIGURE:
1992
+ case $.FOOTER:
1993
+ case $.HEADER:
1994
+ case $.HGROUP:
1995
+ case $.DIALOG:
1996
+ case $.DETAILS:
1997
+ case $.ADDRESS:
1998
+ case $.ARTICLE:
1999
+ case $.SEARCH:
2000
+ case $.SECTION:
2001
+ case $.SUMMARY:
2002
+ case $.FIELDSET:
2003
+ case $.BLOCKQUOTE:
2004
+ case $.FIGCAPTION: {
2005
+ addressStartTagInBody(p, token);
2006
+ break;
2007
+ }
2008
+ case $.LI:
2009
+ case $.DD:
2010
+ case $.DT: {
2011
+ listItemStartTagInBody(p, token);
2012
+ break;
2013
+ }
2014
+ case $.BR:
2015
+ case $.IMG:
2016
+ case $.WBR:
2017
+ case $.AREA:
2018
+ case $.EMBED:
2019
+ case $.KEYGEN: {
2020
+ areaStartTagInBody(p, token);
2021
+ break;
2022
+ }
2023
+ case $.HR: {
2024
+ hrStartTagInBody(p, token);
2025
+ break;
2026
+ }
2027
+ case $.RB:
2028
+ case $.RTC: {
2029
+ rbStartTagInBody(p, token);
2030
+ break;
2031
+ }
2032
+ case $.RT:
2033
+ case $.RP: {
2034
+ rtStartTagInBody(p, token);
2035
+ break;
2036
+ }
2037
+ case $.PRE:
2038
+ case $.LISTING: {
2039
+ preStartTagInBody(p, token);
2040
+ break;
2041
+ }
2042
+ case $.XMP: {
2043
+ xmpStartTagInBody(p, token);
2044
+ break;
2045
+ }
2046
+ case $.SVG: {
2047
+ svgStartTagInBody(p, token);
2048
+ break;
2049
+ }
2050
+ case $.HTML: {
2051
+ htmlStartTagInBody(p, token);
2052
+ break;
2053
+ }
2054
+ case $.BASE:
2055
+ case $.LINK:
2056
+ case $.META:
2057
+ case $.STYLE:
2058
+ case $.TITLE:
2059
+ case $.SCRIPT:
2060
+ case $.BGSOUND:
2061
+ case $.BASEFONT:
2062
+ case $.TEMPLATE: {
2063
+ startTagInHead(p, token);
2064
+ break;
2065
+ }
2066
+ case $.BODY: {
2067
+ bodyStartTagInBody(p, token);
2068
+ break;
2069
+ }
2070
+ case $.FORM: {
2071
+ formStartTagInBody(p, token);
2072
+ break;
2073
+ }
2074
+ case $.NOBR: {
2075
+ nobrStartTagInBody(p, token);
2076
+ break;
2077
+ }
2078
+ case $.MATH: {
2079
+ mathStartTagInBody(p, token);
2080
+ break;
2081
+ }
2082
+ case $.TABLE: {
2083
+ tableStartTagInBody(p, token);
2084
+ break;
2085
+ }
2086
+ case $.INPUT: {
2087
+ inputStartTagInBody(p, token);
2088
+ break;
2089
+ }
2090
+ case $.PARAM:
2091
+ case $.TRACK:
2092
+ case $.SOURCE: {
2093
+ paramStartTagInBody(p, token);
2094
+ break;
2095
+ }
2096
+ case $.IMAGE: {
2097
+ imageStartTagInBody(p, token);
2098
+ break;
2099
+ }
2100
+ case $.BUTTON: {
2101
+ buttonStartTagInBody(p, token);
2102
+ break;
2103
+ }
2104
+ case $.APPLET:
2105
+ case $.OBJECT:
2106
+ case $.MARQUEE: {
2107
+ appletStartTagInBody(p, token);
2108
+ break;
2109
+ }
2110
+ case $.IFRAME: {
2111
+ iframeStartTagInBody(p, token);
2112
+ break;
2113
+ }
2114
+ case $.SELECT: {
2115
+ selectStartTagInBody(p, token);
2116
+ break;
2117
+ }
2118
+ case $.OPTION:
2119
+ case $.OPTGROUP: {
2120
+ optgroupStartTagInBody(p, token);
2121
+ break;
2122
+ }
2123
+ case $.NOEMBED:
2124
+ case $.NOFRAMES: {
2125
+ rawTextStartTagInBody(p, token);
2126
+ break;
2127
+ }
2128
+ case $.FRAMESET: {
2129
+ framesetStartTagInBody(p, token);
2130
+ break;
2131
+ }
2132
+ case $.TEXTAREA: {
2133
+ textareaStartTagInBody(p, token);
2134
+ break;
2135
+ }
2136
+ case $.NOSCRIPT: {
2137
+ if (p.options.scriptingEnabled) {
2138
+ rawTextStartTagInBody(p, token);
2139
+ }
2140
+ else {
2141
+ genericStartTagInBody(p, token);
2142
+ }
2143
+ break;
2144
+ }
2145
+ case $.PLAINTEXT: {
2146
+ plaintextStartTagInBody(p, token);
2147
+ break;
2148
+ }
2149
+ case $.COL:
2150
+ case $.TH:
2151
+ case $.TD:
2152
+ case $.TR:
2153
+ case $.HEAD:
2154
+ case $.FRAME:
2155
+ case $.TBODY:
2156
+ case $.TFOOT:
2157
+ case $.THEAD:
2158
+ case $.CAPTION:
2159
+ case $.COLGROUP: {
2160
+ // Ignore token
2161
+ break;
2162
+ }
2163
+ default: {
2164
+ genericStartTagInBody(p, token);
2165
+ }
2166
+ }
2167
+ }
2168
+ function bodyEndTagInBody(p, token) {
2169
+ if (p.openElements.hasInScope($.BODY)) {
2170
+ p.insertionMode = InsertionMode.AFTER_BODY;
2171
+ //NOTE: <body> is never popped from the stack, so we need to updated
2172
+ //the end location explicitly.
2173
+ if (p.options.sourceCodeLocationInfo) {
2174
+ const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
2175
+ if (bodyElement) {
2176
+ p._setEndLocation(bodyElement, token);
2177
+ }
2178
+ }
2179
+ }
2180
+ }
2181
+ function htmlEndTagInBody(p, token) {
2182
+ if (p.openElements.hasInScope($.BODY)) {
2183
+ p.insertionMode = InsertionMode.AFTER_BODY;
2184
+ endTagAfterBody(p, token);
2185
+ }
2186
+ }
2187
+ function addressEndTagInBody(p, token) {
2188
+ const tn = token.tagID;
2189
+ if (p.openElements.hasInScope(tn)) {
2190
+ p.openElements.generateImpliedEndTags();
2191
+ p.openElements.popUntilTagNamePopped(tn);
2192
+ }
2193
+ }
2194
+ function formEndTagInBody(p) {
2195
+ const inTemplate = p.openElements.tmplCount > 0;
2196
+ const { formElement } = p;
2197
+ if (!inTemplate) {
2198
+ p.formElement = null;
2199
+ }
2200
+ if ((formElement || inTemplate) && p.openElements.hasInScope($.FORM)) {
2201
+ p.openElements.generateImpliedEndTags();
2202
+ if (inTemplate) {
2203
+ p.openElements.popUntilTagNamePopped($.FORM);
2204
+ }
2205
+ else if (formElement) {
2206
+ p.openElements.remove(formElement);
2207
+ }
2208
+ }
2209
+ }
2210
+ function pEndTagInBody(p) {
2211
+ if (!p.openElements.hasInButtonScope($.P)) {
2212
+ p._insertFakeElement(TN.P, $.P);
2213
+ }
2214
+ p._closePElement();
2215
+ }
2216
+ function liEndTagInBody(p) {
2217
+ if (p.openElements.hasInListItemScope($.LI)) {
2218
+ p.openElements.generateImpliedEndTagsWithExclusion($.LI);
2219
+ p.openElements.popUntilTagNamePopped($.LI);
2220
+ }
2221
+ }
2222
+ function ddEndTagInBody(p, token) {
2223
+ const tn = token.tagID;
2224
+ if (p.openElements.hasInScope(tn)) {
2225
+ p.openElements.generateImpliedEndTagsWithExclusion(tn);
2226
+ p.openElements.popUntilTagNamePopped(tn);
2227
+ }
2228
+ }
2229
+ function numberedHeaderEndTagInBody(p) {
2230
+ if (p.openElements.hasNumberedHeaderInScope()) {
2231
+ p.openElements.generateImpliedEndTags();
2232
+ p.openElements.popUntilNumberedHeaderPopped();
2233
+ }
2234
+ }
2235
+ function appletEndTagInBody(p, token) {
2236
+ const tn = token.tagID;
2237
+ if (p.openElements.hasInScope(tn)) {
2238
+ p.openElements.generateImpliedEndTags();
2239
+ p.openElements.popUntilTagNamePopped(tn);
2240
+ p.activeFormattingElements.clearToLastMarker();
2241
+ }
2242
+ }
2243
+ function brEndTagInBody(p) {
2244
+ p._reconstructActiveFormattingElements();
2245
+ p._insertFakeElement(TN.BR, $.BR);
2246
+ p.openElements.pop();
2247
+ p.framesetOk = false;
2248
+ }
2249
+ function genericEndTagInBody(p, token) {
2250
+ const tn = token.tagName;
2251
+ const tid = token.tagID;
2252
+ for (let i = p.openElements.stackTop; i > 0; i--) {
2253
+ const element = p.openElements.items[i];
2254
+ const elementId = p.openElements.tagIDs[i];
2255
+ // Compare the tag name here, as the tag might not be a known tag with an ID.
2256
+ // Only HTML elements match; a foreign element falls through to the special-element check.
2257
+ if (tid === elementId &&
2258
+ p.treeAdapter.getNamespaceURI(element) === NS.HTML &&
2259
+ (tid !== $.UNKNOWN || p.treeAdapter.getTagName(element) === tn)) {
2260
+ p.openElements.generateImpliedEndTagsWithExclusion(tid);
2261
+ if (p.openElements.stackTop >= i)
2262
+ p.openElements.shortenToLength(i);
2263
+ break;
2264
+ }
2265
+ if (p._isSpecialElement(element, elementId)) {
2266
+ break;
2267
+ }
2268
+ }
2269
+ }
2270
+ function endTagInBody(p, token) {
2271
+ switch (token.tagID) {
2272
+ case $.A:
2273
+ case $.B:
2274
+ case $.I:
2275
+ case $.S:
2276
+ case $.U:
2277
+ case $.EM:
2278
+ case $.TT:
2279
+ case $.BIG:
2280
+ case $.CODE:
2281
+ case $.FONT:
2282
+ case $.NOBR:
2283
+ case $.SMALL:
2284
+ case $.STRIKE:
2285
+ case $.STRONG: {
2286
+ callAdoptionAgency(p, token);
2287
+ break;
2288
+ }
2289
+ case $.P: {
2290
+ pEndTagInBody(p);
2291
+ break;
2292
+ }
2293
+ case $.DL:
2294
+ case $.UL:
2295
+ case $.OL:
2296
+ case $.DIR:
2297
+ case $.DIV:
2298
+ case $.NAV:
2299
+ case $.PRE:
2300
+ case $.MAIN:
2301
+ case $.MENU:
2302
+ case $.ASIDE:
2303
+ case $.BUTTON:
2304
+ case $.CENTER:
2305
+ case $.FIGURE:
2306
+ case $.FOOTER:
2307
+ case $.HEADER:
2308
+ case $.HGROUP:
2309
+ case $.DIALOG:
2310
+ case $.ADDRESS:
2311
+ case $.ARTICLE:
2312
+ case $.DETAILS:
2313
+ case $.SEARCH:
2314
+ case $.SECTION:
2315
+ case $.SUMMARY:
2316
+ case $.LISTING:
2317
+ case $.FIELDSET:
2318
+ case $.BLOCKQUOTE:
2319
+ case $.FIGCAPTION: {
2320
+ addressEndTagInBody(p, token);
2321
+ break;
2322
+ }
2323
+ case $.LI: {
2324
+ liEndTagInBody(p);
2325
+ break;
2326
+ }
2327
+ case $.DD:
2328
+ case $.DT: {
2329
+ ddEndTagInBody(p, token);
2330
+ break;
2331
+ }
2332
+ case $.H1:
2333
+ case $.H2:
2334
+ case $.H3:
2335
+ case $.H4:
2336
+ case $.H5:
2337
+ case $.H6: {
2338
+ numberedHeaderEndTagInBody(p);
2339
+ break;
2340
+ }
2341
+ case $.BR: {
2342
+ brEndTagInBody(p);
2343
+ break;
2344
+ }
2345
+ case $.BODY: {
2346
+ bodyEndTagInBody(p, token);
2347
+ break;
2348
+ }
2349
+ case $.HTML: {
2350
+ htmlEndTagInBody(p, token);
2351
+ break;
2352
+ }
2353
+ case $.FORM: {
2354
+ formEndTagInBody(p);
2355
+ break;
2356
+ }
2357
+ case $.APPLET:
2358
+ case $.OBJECT:
2359
+ case $.MARQUEE: {
2360
+ appletEndTagInBody(p, token);
2361
+ break;
2362
+ }
2363
+ case $.TEMPLATE: {
2364
+ templateEndTagInHead(p, token);
2365
+ break;
2366
+ }
2367
+ default: {
2368
+ genericEndTagInBody(p, token);
2369
+ }
2370
+ }
2371
+ }
2372
+ function eofInBody(p, token) {
2373
+ if (p.tmplInsertionModeStack.length > 0) {
2374
+ eofInTemplate(p, token);
2375
+ }
2376
+ else {
2377
+ stopParsing(p, token);
2378
+ }
2379
+ }
2380
+ // The "text" insertion mode
2381
+ //------------------------------------------------------------------
2382
+ function endTagInText(p, token) {
2383
+ var _a;
2384
+ if (token.tagID === $.SCRIPT) {
2385
+ (_a = p.scriptHandler) === null || _a === void 0 ? void 0 : _a.call(p, p.openElements.current);
2386
+ }
2387
+ p.openElements.pop();
2388
+ p.insertionMode = p.originalInsertionMode;
2389
+ }
2390
+ function eofInText(p, token) {
2391
+ p._err(token, ERR.eofInElementThatCanContainOnlyText);
2392
+ p.openElements.pop();
2393
+ p.insertionMode = p.originalInsertionMode;
2394
+ p.onEof(token);
2395
+ }
2396
+ // The "in table" insertion mode
2397
+ //------------------------------------------------------------------
2398
+ function characterInTable(p, token) {
2399
+ if (p.openElements.currentTagId !== undefined && TABLE_STRUCTURE_TAGS.has(p.openElements.currentTagId)) {
2400
+ p.pendingCharacterTokens.length = 0;
2401
+ p.hasNonWhitespacePendingCharacterToken = false;
2402
+ p.originalInsertionMode = p.insertionMode;
2403
+ p.insertionMode = InsertionMode.IN_TABLE_TEXT;
2404
+ switch (token.type) {
2405
+ case TokenType.CHARACTER: {
2406
+ characterInTableText(p, token);
2407
+ break;
2408
+ }
2409
+ case TokenType.WHITESPACE_CHARACTER: {
2410
+ whitespaceCharacterInTableText(p, token);
2411
+ break;
2412
+ }
2413
+ // Ignore null
2414
+ }
2415
+ }
2416
+ else {
2417
+ tokenInTable(p, token);
2418
+ }
2419
+ }
2420
+ function captionStartTagInTable(p, token) {
2421
+ p.openElements.clearBackToTableContext();
2422
+ p.activeFormattingElements.insertMarker();
2423
+ p._insertElement(token, NS.HTML);
2424
+ p.insertionMode = InsertionMode.IN_CAPTION;
2425
+ }
2426
+ function colgroupStartTagInTable(p, token) {
2427
+ p.openElements.clearBackToTableContext();
2428
+ p._insertElement(token, NS.HTML);
2429
+ p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
2430
+ }
2431
+ function colStartTagInTable(p, token) {
2432
+ p.openElements.clearBackToTableContext();
2433
+ p._insertFakeElement(TN.COLGROUP, $.COLGROUP);
2434
+ p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
2435
+ startTagInColumnGroup(p, token);
2436
+ }
2437
+ function tbodyStartTagInTable(p, token) {
2438
+ p.openElements.clearBackToTableContext();
2439
+ p._insertElement(token, NS.HTML);
2440
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2441
+ }
2442
+ function tdStartTagInTable(p, token) {
2443
+ p.openElements.clearBackToTableContext();
2444
+ p._insertFakeElement(TN.TBODY, $.TBODY);
2445
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2446
+ startTagInTableBody(p, token);
2447
+ }
2448
+ function tableStartTagInTable(p, token) {
2449
+ if (p.openElements.hasInTableScope($.TABLE)) {
2450
+ p.openElements.popUntilTagNamePopped($.TABLE);
2451
+ p._resetInsertionMode();
2452
+ p._processStartTag(token);
2453
+ }
2454
+ }
2455
+ function inputStartTagInTable(p, token) {
2456
+ if (isHiddenInput(token)) {
2457
+ p._appendElement(token, NS.HTML);
2458
+ }
2459
+ else {
2460
+ tokenInTable(p, token);
2461
+ }
2462
+ token.ackSelfClosing = true;
2463
+ }
2464
+ function formStartTagInTable(p, token) {
2465
+ if (!p.formElement && p.openElements.tmplCount === 0) {
2466
+ p._insertElement(token, NS.HTML);
2467
+ p.formElement = p.openElements.current;
2468
+ p.openElements.pop();
2469
+ }
2470
+ }
2471
+ function startTagInTable(p, token) {
2472
+ switch (token.tagID) {
2473
+ case $.TD:
2474
+ case $.TH:
2475
+ case $.TR: {
2476
+ tdStartTagInTable(p, token);
2477
+ break;
2478
+ }
2479
+ case $.STYLE:
2480
+ case $.SCRIPT:
2481
+ case $.TEMPLATE: {
2482
+ startTagInHead(p, token);
2483
+ break;
2484
+ }
2485
+ case $.COL: {
2486
+ colStartTagInTable(p, token);
2487
+ break;
2488
+ }
2489
+ case $.FORM: {
2490
+ formStartTagInTable(p, token);
2491
+ break;
2492
+ }
2493
+ case $.TABLE: {
2494
+ tableStartTagInTable(p, token);
2495
+ break;
2496
+ }
2497
+ case $.TBODY:
2498
+ case $.TFOOT:
2499
+ case $.THEAD: {
2500
+ tbodyStartTagInTable(p, token);
2501
+ break;
2502
+ }
2503
+ case $.INPUT: {
2504
+ inputStartTagInTable(p, token);
2505
+ break;
2506
+ }
2507
+ case $.CAPTION: {
2508
+ captionStartTagInTable(p, token);
2509
+ break;
2510
+ }
2511
+ case $.COLGROUP: {
2512
+ colgroupStartTagInTable(p, token);
2513
+ break;
2514
+ }
2515
+ default: {
2516
+ tokenInTable(p, token);
2517
+ }
2518
+ }
2519
+ }
2520
+ function endTagInTable(p, token) {
2521
+ switch (token.tagID) {
2522
+ case $.TABLE: {
2523
+ if (p.openElements.hasInTableScope($.TABLE)) {
2524
+ p.openElements.popUntilTagNamePopped($.TABLE);
2525
+ p._resetInsertionMode();
2526
+ }
2527
+ break;
2528
+ }
2529
+ case $.TEMPLATE: {
2530
+ templateEndTagInHead(p, token);
2531
+ break;
2532
+ }
2533
+ case $.BODY:
2534
+ case $.CAPTION:
2535
+ case $.COL:
2536
+ case $.COLGROUP:
2537
+ case $.HTML:
2538
+ case $.TBODY:
2539
+ case $.TD:
2540
+ case $.TFOOT:
2541
+ case $.TH:
2542
+ case $.THEAD:
2543
+ case $.TR: {
2544
+ // Ignore token
2545
+ break;
2546
+ }
2547
+ default: {
2548
+ tokenInTable(p, token);
2549
+ }
2550
+ }
2551
+ }
2552
+ function tokenInTable(p, token) {
2553
+ const savedFosterParentingState = p.fosterParentingEnabled;
2554
+ p.fosterParentingEnabled = true;
2555
+ // Process token in `In Body` mode
2556
+ modeInBody(p, token);
2557
+ p.fosterParentingEnabled = savedFosterParentingState;
2558
+ }
2559
+ // The "in table text" insertion mode
2560
+ //------------------------------------------------------------------
2561
+ function whitespaceCharacterInTableText(p, token) {
2562
+ p.pendingCharacterTokens.push(token);
2563
+ }
2564
+ function characterInTableText(p, token) {
2565
+ p.pendingCharacterTokens.push(token);
2566
+ p.hasNonWhitespacePendingCharacterToken = true;
2567
+ }
2568
+ function tokenInTableText(p, token) {
2569
+ let i = 0;
2570
+ if (p.hasNonWhitespacePendingCharacterToken) {
2571
+ for (; i < p.pendingCharacterTokens.length; i++) {
2572
+ tokenInTable(p, p.pendingCharacterTokens[i]);
2573
+ }
2574
+ }
2575
+ else {
2576
+ for (; i < p.pendingCharacterTokens.length; i++) {
2577
+ p._insertCharacters(p.pendingCharacterTokens[i]);
2578
+ }
2579
+ }
2580
+ p.insertionMode = p.originalInsertionMode;
2581
+ p._processToken(token);
2582
+ }
2583
+ // The "in caption" insertion mode
2584
+ //------------------------------------------------------------------
2585
+ const TABLE_VOID_ELEMENTS = new Set([$.CAPTION, $.COL, $.COLGROUP, $.TBODY, $.TD, $.TFOOT, $.TH, $.THEAD, $.TR]);
2586
+ function startTagInCaption(p, token) {
2587
+ const tn = token.tagID;
2588
+ if (TABLE_VOID_ELEMENTS.has(tn)) {
2589
+ if (p.openElements.hasInTableScope($.CAPTION)) {
2590
+ p.openElements.generateImpliedEndTags();
2591
+ p.openElements.popUntilTagNamePopped($.CAPTION);
2592
+ p.activeFormattingElements.clearToLastMarker();
2593
+ p.insertionMode = InsertionMode.IN_TABLE;
2594
+ startTagInTable(p, token);
2595
+ }
2596
+ }
2597
+ else {
2598
+ startTagInBody(p, token);
2599
+ }
2600
+ }
2601
+ function endTagInCaption(p, token) {
2602
+ const tn = token.tagID;
2603
+ switch (tn) {
2604
+ case $.CAPTION:
2605
+ case $.TABLE: {
2606
+ if (p.openElements.hasInTableScope($.CAPTION)) {
2607
+ p.openElements.generateImpliedEndTags();
2608
+ p.openElements.popUntilTagNamePopped($.CAPTION);
2609
+ p.activeFormattingElements.clearToLastMarker();
2610
+ p.insertionMode = InsertionMode.IN_TABLE;
2611
+ if (tn === $.TABLE) {
2612
+ endTagInTable(p, token);
2613
+ }
2614
+ }
2615
+ break;
2616
+ }
2617
+ case $.BODY:
2618
+ case $.COL:
2619
+ case $.COLGROUP:
2620
+ case $.HTML:
2621
+ case $.TBODY:
2622
+ case $.TD:
2623
+ case $.TFOOT:
2624
+ case $.TH:
2625
+ case $.THEAD:
2626
+ case $.TR: {
2627
+ // Ignore token
2628
+ break;
2629
+ }
2630
+ default: {
2631
+ endTagInBody(p, token);
2632
+ }
2633
+ }
2634
+ }
2635
+ // The "in column group" insertion mode
2636
+ //------------------------------------------------------------------
2637
+ function startTagInColumnGroup(p, token) {
2638
+ switch (token.tagID) {
2639
+ case $.HTML: {
2640
+ startTagInBody(p, token);
2641
+ break;
2642
+ }
2643
+ case $.COL: {
2644
+ p._appendElement(token, NS.HTML);
2645
+ token.ackSelfClosing = true;
2646
+ break;
2647
+ }
2648
+ case $.TEMPLATE: {
2649
+ startTagInHead(p, token);
2650
+ break;
2651
+ }
2652
+ default: {
2653
+ tokenInColumnGroup(p, token);
2654
+ }
2655
+ }
2656
+ }
2657
+ function endTagInColumnGroup(p, token) {
2658
+ switch (token.tagID) {
2659
+ case $.COLGROUP: {
2660
+ if (p.openElements.currentTagId === $.COLGROUP) {
2661
+ p.openElements.pop();
2662
+ p.insertionMode = InsertionMode.IN_TABLE;
2663
+ }
2664
+ break;
2665
+ }
2666
+ case $.TEMPLATE: {
2667
+ templateEndTagInHead(p, token);
2668
+ break;
2669
+ }
2670
+ case $.COL: {
2671
+ // Ignore token
2672
+ break;
2673
+ }
2674
+ default: {
2675
+ tokenInColumnGroup(p, token);
2676
+ }
2677
+ }
2678
+ }
2679
+ function tokenInColumnGroup(p, token) {
2680
+ if (p.openElements.currentTagId === $.COLGROUP) {
2681
+ p.openElements.pop();
2682
+ p.insertionMode = InsertionMode.IN_TABLE;
2683
+ p._processToken(token);
2684
+ }
2685
+ }
2686
+ // The "in table body" insertion mode
2687
+ //------------------------------------------------------------------
2688
+ function startTagInTableBody(p, token) {
2689
+ switch (token.tagID) {
2690
+ case $.TR: {
2691
+ p.openElements.clearBackToTableBodyContext();
2692
+ p._insertElement(token, NS.HTML);
2693
+ p.insertionMode = InsertionMode.IN_ROW;
2694
+ break;
2695
+ }
2696
+ case $.TH:
2697
+ case $.TD: {
2698
+ p.openElements.clearBackToTableBodyContext();
2699
+ p._insertFakeElement(TN.TR, $.TR);
2700
+ p.insertionMode = InsertionMode.IN_ROW;
2701
+ startTagInRow(p, token);
2702
+ break;
2703
+ }
2704
+ case $.CAPTION:
2705
+ case $.COL:
2706
+ case $.COLGROUP:
2707
+ case $.TBODY:
2708
+ case $.TFOOT:
2709
+ case $.THEAD: {
2710
+ if (p.openElements.hasTableBodyContextInTableScope()) {
2711
+ p.openElements.clearBackToTableBodyContext();
2712
+ p.openElements.pop();
2713
+ p.insertionMode = InsertionMode.IN_TABLE;
2714
+ startTagInTable(p, token);
2715
+ }
2716
+ break;
2717
+ }
2718
+ default: {
2719
+ startTagInTable(p, token);
2720
+ }
2721
+ }
2722
+ }
2723
+ function endTagInTableBody(p, token) {
2724
+ const tn = token.tagID;
2725
+ switch (token.tagID) {
2726
+ case $.TBODY:
2727
+ case $.TFOOT:
2728
+ case $.THEAD: {
2729
+ if (p.openElements.hasInTableScope(tn)) {
2730
+ p.openElements.clearBackToTableBodyContext();
2731
+ p.openElements.pop();
2732
+ p.insertionMode = InsertionMode.IN_TABLE;
2733
+ }
2734
+ break;
2735
+ }
2736
+ case $.TABLE: {
2737
+ if (p.openElements.hasTableBodyContextInTableScope()) {
2738
+ p.openElements.clearBackToTableBodyContext();
2739
+ p.openElements.pop();
2740
+ p.insertionMode = InsertionMode.IN_TABLE;
2741
+ endTagInTable(p, token);
2742
+ }
2743
+ break;
2744
+ }
2745
+ case $.BODY:
2746
+ case $.CAPTION:
2747
+ case $.COL:
2748
+ case $.COLGROUP:
2749
+ case $.HTML:
2750
+ case $.TD:
2751
+ case $.TH:
2752
+ case $.TR: {
2753
+ // Ignore token
2754
+ break;
2755
+ }
2756
+ default: {
2757
+ endTagInTable(p, token);
2758
+ }
2759
+ }
2760
+ }
2761
+ // The "in row" insertion mode
2762
+ //------------------------------------------------------------------
2763
+ function startTagInRow(p, token) {
2764
+ switch (token.tagID) {
2765
+ case $.TH:
2766
+ case $.TD: {
2767
+ p.openElements.clearBackToTableRowContext();
2768
+ p._insertElement(token, NS.HTML);
2769
+ p.insertionMode = InsertionMode.IN_CELL;
2770
+ p.activeFormattingElements.insertMarker();
2771
+ break;
2772
+ }
2773
+ case $.CAPTION:
2774
+ case $.COL:
2775
+ case $.COLGROUP:
2776
+ case $.TBODY:
2777
+ case $.TFOOT:
2778
+ case $.THEAD:
2779
+ case $.TR: {
2780
+ if (p.openElements.hasInTableScope($.TR)) {
2781
+ p.openElements.clearBackToTableRowContext();
2782
+ p.openElements.pop();
2783
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2784
+ startTagInTableBody(p, token);
2785
+ }
2786
+ break;
2787
+ }
2788
+ default: {
2789
+ startTagInTable(p, token);
2790
+ }
2791
+ }
2792
+ }
2793
+ function endTagInRow(p, token) {
2794
+ switch (token.tagID) {
2795
+ case $.TR: {
2796
+ if (p.openElements.hasInTableScope($.TR)) {
2797
+ p.openElements.clearBackToTableRowContext();
2798
+ p.openElements.pop();
2799
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2800
+ }
2801
+ break;
2802
+ }
2803
+ case $.TABLE: {
2804
+ if (p.openElements.hasInTableScope($.TR)) {
2805
+ p.openElements.clearBackToTableRowContext();
2806
+ p.openElements.pop();
2807
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2808
+ endTagInTableBody(p, token);
2809
+ }
2810
+ break;
2811
+ }
2812
+ case $.TBODY:
2813
+ case $.TFOOT:
2814
+ case $.THEAD: {
2815
+ if (p.openElements.hasInTableScope(token.tagID) || p.openElements.hasInTableScope($.TR)) {
2816
+ p.openElements.clearBackToTableRowContext();
2817
+ p.openElements.pop();
2818
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
2819
+ endTagInTableBody(p, token);
2820
+ }
2821
+ break;
2822
+ }
2823
+ case $.BODY:
2824
+ case $.CAPTION:
2825
+ case $.COL:
2826
+ case $.COLGROUP:
2827
+ case $.HTML:
2828
+ case $.TD:
2829
+ case $.TH: {
2830
+ // Ignore end tag
2831
+ break;
2832
+ }
2833
+ default: {
2834
+ endTagInTable(p, token);
2835
+ }
2836
+ }
2837
+ }
2838
+ // The "in cell" insertion mode
2839
+ //------------------------------------------------------------------
2840
+ function startTagInCell(p, token) {
2841
+ const tn = token.tagID;
2842
+ if (TABLE_VOID_ELEMENTS.has(tn)) {
2843
+ if (p.openElements.hasInTableScope($.TD) || p.openElements.hasInTableScope($.TH)) {
2844
+ p._closeTableCell();
2845
+ startTagInRow(p, token);
2846
+ }
2847
+ }
2848
+ else {
2849
+ startTagInBody(p, token);
2850
+ }
2851
+ }
2852
+ function endTagInCell(p, token) {
2853
+ const tn = token.tagID;
2854
+ switch (tn) {
2855
+ case $.TD:
2856
+ case $.TH: {
2857
+ if (p.openElements.hasInTableScope(tn)) {
2858
+ p.openElements.generateImpliedEndTags();
2859
+ p.openElements.popUntilTagNamePopped(tn);
2860
+ p.activeFormattingElements.clearToLastMarker();
2861
+ p.insertionMode = InsertionMode.IN_ROW;
2862
+ }
2863
+ break;
2864
+ }
2865
+ case $.TABLE:
2866
+ case $.TBODY:
2867
+ case $.TFOOT:
2868
+ case $.THEAD:
2869
+ case $.TR: {
2870
+ if (p.openElements.hasInTableScope(tn)) {
2871
+ p._closeTableCell();
2872
+ endTagInRow(p, token);
2873
+ }
2874
+ break;
2875
+ }
2876
+ case $.BODY:
2877
+ case $.CAPTION:
2878
+ case $.COL:
2879
+ case $.COLGROUP:
2880
+ case $.HTML: {
2881
+ // Ignore token
2882
+ break;
2883
+ }
2884
+ default: {
2885
+ endTagInBody(p, token);
2886
+ }
2887
+ }
2888
+ }
2889
+ // The "in select" insertion mode
2890
+ //------------------------------------------------------------------
2891
+ function startTagInSelect(p, token) {
2892
+ switch (token.tagID) {
2893
+ case $.HTML: {
2894
+ startTagInBody(p, token);
2895
+ break;
2896
+ }
2897
+ case $.OPTION: {
2898
+ if (p.openElements.currentTagId === $.OPTION) {
2899
+ p.openElements.pop();
2900
+ }
2901
+ p._insertElement(token, NS.HTML);
2902
+ break;
2903
+ }
2904
+ case $.OPTGROUP: {
2905
+ if (p.openElements.currentTagId === $.OPTION) {
2906
+ p.openElements.pop();
2907
+ }
2908
+ if (p.openElements.currentTagId === $.OPTGROUP) {
2909
+ p.openElements.pop();
2910
+ }
2911
+ p._insertElement(token, NS.HTML);
2912
+ break;
2913
+ }
2914
+ case $.HR: {
2915
+ if (p.openElements.currentTagId === $.OPTION) {
2916
+ p.openElements.pop();
2917
+ }
2918
+ if (p.openElements.currentTagId === $.OPTGROUP) {
2919
+ p.openElements.pop();
2920
+ }
2921
+ p._appendElement(token, NS.HTML);
2922
+ token.ackSelfClosing = true;
2923
+ break;
2924
+ }
2925
+ case $.INPUT:
2926
+ case $.KEYGEN:
2927
+ case $.TEXTAREA:
2928
+ case $.SELECT: {
2929
+ if (p.openElements.hasInSelectScope($.SELECT)) {
2930
+ p.openElements.popUntilTagNamePopped($.SELECT);
2931
+ p._resetInsertionMode();
2932
+ if (token.tagID !== $.SELECT) {
2933
+ p._processStartTag(token);
2934
+ }
2935
+ }
2936
+ break;
2937
+ }
2938
+ case $.SCRIPT:
2939
+ case $.TEMPLATE: {
2940
+ startTagInHead(p, token);
2941
+ break;
2942
+ }
2943
+ default:
2944
+ // Do nothing
2945
+ }
2946
+ }
2947
+ function endTagInSelect(p, token) {
2948
+ switch (token.tagID) {
2949
+ case $.OPTGROUP: {
2950
+ if (p.openElements.stackTop > 0 &&
2951
+ p.openElements.currentTagId === $.OPTION &&
2952
+ p.openElements.tagIDs[p.openElements.stackTop - 1] === $.OPTGROUP) {
2953
+ p.openElements.pop();
2954
+ }
2955
+ if (p.openElements.currentTagId === $.OPTGROUP) {
2956
+ p.openElements.pop();
2957
+ }
2958
+ break;
2959
+ }
2960
+ case $.OPTION: {
2961
+ if (p.openElements.currentTagId === $.OPTION) {
2962
+ p.openElements.pop();
2963
+ }
2964
+ break;
2965
+ }
2966
+ case $.SELECT: {
2967
+ if (p.openElements.hasInSelectScope($.SELECT)) {
2968
+ p.openElements.popUntilTagNamePopped($.SELECT);
2969
+ p._resetInsertionMode();
2970
+ }
2971
+ break;
2972
+ }
2973
+ case $.TEMPLATE: {
2974
+ templateEndTagInHead(p, token);
2975
+ break;
2976
+ }
2977
+ default:
2978
+ // Do nothing
2979
+ }
2980
+ }
2981
+ // The "in select in table" insertion mode
2982
+ //------------------------------------------------------------------
2983
+ function startTagInSelectInTable(p, token) {
2984
+ const tn = token.tagID;
2985
+ if (tn === $.CAPTION ||
2986
+ tn === $.TABLE ||
2987
+ tn === $.TBODY ||
2988
+ tn === $.TFOOT ||
2989
+ tn === $.THEAD ||
2990
+ tn === $.TR ||
2991
+ tn === $.TD ||
2992
+ tn === $.TH) {
2993
+ p.openElements.popUntilTagNamePopped($.SELECT);
2994
+ p._resetInsertionMode();
2995
+ p._processStartTag(token);
2996
+ }
2997
+ else {
2998
+ startTagInSelect(p, token);
2999
+ }
3000
+ }
3001
+ function endTagInSelectInTable(p, token) {
3002
+ const tn = token.tagID;
3003
+ if (tn === $.CAPTION ||
3004
+ tn === $.TABLE ||
3005
+ tn === $.TBODY ||
3006
+ tn === $.TFOOT ||
3007
+ tn === $.THEAD ||
3008
+ tn === $.TR ||
3009
+ tn === $.TD ||
3010
+ tn === $.TH) {
3011
+ if (p.openElements.hasInTableScope(tn)) {
3012
+ p.openElements.popUntilTagNamePopped($.SELECT);
3013
+ p._resetInsertionMode();
3014
+ p.onEndTag(token);
3015
+ }
3016
+ }
3017
+ else {
3018
+ endTagInSelect(p, token);
3019
+ }
3020
+ }
3021
+ // The "in template" insertion mode
3022
+ //------------------------------------------------------------------
3023
+ function startTagInTemplate(p, token) {
3024
+ switch (token.tagID) {
3025
+ // First, handle tags that can start without a mode change
3026
+ case $.BASE:
3027
+ case $.BASEFONT:
3028
+ case $.BGSOUND:
3029
+ case $.LINK:
3030
+ case $.META:
3031
+ case $.NOFRAMES:
3032
+ case $.SCRIPT:
3033
+ case $.STYLE:
3034
+ case $.TEMPLATE:
3035
+ case $.TITLE: {
3036
+ startTagInHead(p, token);
3037
+ break;
3038
+ }
3039
+ // Re-process the token in the appropriate mode
3040
+ case $.CAPTION:
3041
+ case $.COLGROUP:
3042
+ case $.TBODY:
3043
+ case $.TFOOT:
3044
+ case $.THEAD: {
3045
+ p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE;
3046
+ p.insertionMode = InsertionMode.IN_TABLE;
3047
+ startTagInTable(p, token);
3048
+ break;
3049
+ }
3050
+ case $.COL: {
3051
+ p.tmplInsertionModeStack[0] = InsertionMode.IN_COLUMN_GROUP;
3052
+ p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
3053
+ startTagInColumnGroup(p, token);
3054
+ break;
3055
+ }
3056
+ case $.TR: {
3057
+ p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE_BODY;
3058
+ p.insertionMode = InsertionMode.IN_TABLE_BODY;
3059
+ startTagInTableBody(p, token);
3060
+ break;
3061
+ }
3062
+ case $.TD:
3063
+ case $.TH: {
3064
+ p.tmplInsertionModeStack[0] = InsertionMode.IN_ROW;
3065
+ p.insertionMode = InsertionMode.IN_ROW;
3066
+ startTagInRow(p, token);
3067
+ break;
3068
+ }
3069
+ default: {
3070
+ p.tmplInsertionModeStack[0] = InsertionMode.IN_BODY;
3071
+ p.insertionMode = InsertionMode.IN_BODY;
3072
+ startTagInBody(p, token);
3073
+ }
3074
+ }
3075
+ }
3076
+ function endTagInTemplate(p, token) {
3077
+ if (token.tagID === $.TEMPLATE) {
3078
+ templateEndTagInHead(p, token);
3079
+ }
3080
+ }
3081
+ function eofInTemplate(p, token) {
3082
+ if (p.openElements.tmplCount > 0) {
3083
+ p.openElements.popUntilTagNamePopped($.TEMPLATE);
3084
+ p.activeFormattingElements.clearToLastMarker();
3085
+ p.tmplInsertionModeStack.shift();
3086
+ p._resetInsertionMode();
3087
+ p.onEof(token);
3088
+ }
3089
+ else {
3090
+ stopParsing(p, token);
3091
+ }
3092
+ }
3093
+ // The "after body" insertion mode
3094
+ //------------------------------------------------------------------
3095
+ function startTagAfterBody(p, token) {
3096
+ if (token.tagID === $.HTML) {
3097
+ startTagInBody(p, token);
3098
+ }
3099
+ else {
3100
+ tokenAfterBody(p, token);
3101
+ }
3102
+ }
3103
+ function endTagAfterBody(p, token) {
3104
+ var _a;
3105
+ if (token.tagID === $.HTML) {
3106
+ if (!p.fragmentContext) {
3107
+ p.insertionMode = InsertionMode.AFTER_AFTER_BODY;
3108
+ }
3109
+ //NOTE: <html> is never popped from the stack, so we need to updated
3110
+ //the end location explicitly.
3111
+ if (p.options.sourceCodeLocationInfo && p.openElements.tagIDs[0] === $.HTML) {
3112
+ p._setEndLocation(p.openElements.items[0], token);
3113
+ // Update the body element, if it doesn't have an end tag
3114
+ const bodyElement = p.openElements.items[1];
3115
+ if (bodyElement && !((_a = p.treeAdapter.getNodeSourceCodeLocation(bodyElement)) === null || _a === void 0 ? void 0 : _a.endTag)) {
3116
+ p._setEndLocation(bodyElement, token);
3117
+ }
3118
+ }
3119
+ }
3120
+ else {
3121
+ tokenAfterBody(p, token);
3122
+ }
3123
+ }
3124
+ function tokenAfterBody(p, token) {
3125
+ p.insertionMode = InsertionMode.IN_BODY;
3126
+ modeInBody(p, token);
3127
+ }
3128
+ // The "in frameset" insertion mode
3129
+ //------------------------------------------------------------------
3130
+ function startTagInFrameset(p, token) {
3131
+ switch (token.tagID) {
3132
+ case $.HTML: {
3133
+ startTagInBody(p, token);
3134
+ break;
3135
+ }
3136
+ case $.FRAMESET: {
3137
+ p._insertElement(token, NS.HTML);
3138
+ break;
3139
+ }
3140
+ case $.FRAME: {
3141
+ p._appendElement(token, NS.HTML);
3142
+ token.ackSelfClosing = true;
3143
+ break;
3144
+ }
3145
+ case $.NOFRAMES: {
3146
+ startTagInHead(p, token);
3147
+ break;
3148
+ }
3149
+ default:
3150
+ // Do nothing
3151
+ }
3152
+ }
3153
+ function endTagInFrameset(p, token) {
3154
+ if (token.tagID === $.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) {
3155
+ p.openElements.pop();
3156
+ if (!p.fragmentContext && p.openElements.currentTagId !== $.FRAMESET) {
3157
+ p.insertionMode = InsertionMode.AFTER_FRAMESET;
3158
+ }
3159
+ }
3160
+ }
3161
+ // The "after frameset" insertion mode
3162
+ //------------------------------------------------------------------
3163
+ function startTagAfterFrameset(p, token) {
3164
+ switch (token.tagID) {
3165
+ case $.HTML: {
3166
+ startTagInBody(p, token);
3167
+ break;
3168
+ }
3169
+ case $.NOFRAMES: {
3170
+ startTagInHead(p, token);
3171
+ break;
3172
+ }
3173
+ default:
3174
+ // Do nothing
3175
+ }
3176
+ }
3177
+ function endTagAfterFrameset(p, token) {
3178
+ if (token.tagID === $.HTML) {
3179
+ p.insertionMode = InsertionMode.AFTER_AFTER_FRAMESET;
3180
+ }
3181
+ }
3182
+ // The "after after body" insertion mode
3183
+ //------------------------------------------------------------------
3184
+ function startTagAfterAfterBody(p, token) {
3185
+ if (token.tagID === $.HTML) {
3186
+ startTagInBody(p, token);
3187
+ }
3188
+ else {
3189
+ tokenAfterAfterBody(p, token);
3190
+ }
3191
+ }
3192
+ function tokenAfterAfterBody(p, token) {
3193
+ p.insertionMode = InsertionMode.IN_BODY;
3194
+ modeInBody(p, token);
3195
+ }
3196
+ // The "after after frameset" insertion mode
3197
+ //------------------------------------------------------------------
3198
+ function startTagAfterAfterFrameset(p, token) {
3199
+ switch (token.tagID) {
3200
+ case $.HTML: {
3201
+ startTagInBody(p, token);
3202
+ break;
3203
+ }
3204
+ case $.NOFRAMES: {
3205
+ startTagInHead(p, token);
3206
+ break;
3207
+ }
3208
+ default:
3209
+ // Do nothing
3210
+ }
3211
+ }
3212
+ // The rules for parsing tokens in foreign content
3213
+ //------------------------------------------------------------------
3214
+ function nullCharacterInForeignContent(p, token) {
3215
+ token.chars = unicode.REPLACEMENT_CHARACTER;
3216
+ p._insertCharacters(token);
3217
+ }
3218
+ function characterInForeignContent(p, token) {
3219
+ p._insertCharacters(token);
3220
+ p.framesetOk = false;
3221
+ }
3222
+ function popUntilHtmlOrIntegrationPoint(p) {
3223
+ while (p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML &&
3224
+ p.openElements.currentTagId !== undefined &&
3225
+ !p._isIntegrationPoint(p.openElements.currentTagId, p.openElements.current)) {
3226
+ p.openElements.pop();
3227
+ }
3228
+ }
3229
+ function startTagInForeignContent(p, token) {
3230
+ if (foreignContent.causesExit(token)) {
3231
+ popUntilHtmlOrIntegrationPoint(p);
3232
+ p._startTagOutsideForeignContent(token);
3233
+ }
3234
+ else {
3235
+ const current = p._getAdjustedCurrentElement();
3236
+ const currentNs = p.treeAdapter.getNamespaceURI(current);
3237
+ if (currentNs === NS.MATHML) {
3238
+ foreignContent.adjustTokenMathMLAttrs(token);
3239
+ }
3240
+ else if (currentNs === NS.SVG) {
3241
+ foreignContent.adjustTokenSVGTagName(token);
3242
+ foreignContent.adjustTokenSVGAttrs(token);
3243
+ }
3244
+ foreignContent.adjustTokenXMLAttrs(token);
3245
+ if (token.selfClosing) {
3246
+ p._appendElement(token, currentNs);
3247
+ }
3248
+ else {
3249
+ p._insertElement(token, currentNs);
3250
+ }
3251
+ token.ackSelfClosing = true;
3252
+ }
3253
+ }
3254
+ function endTagInForeignContent(p, token) {
3255
+ if (token.tagID === $.P || token.tagID === $.BR) {
3256
+ popUntilHtmlOrIntegrationPoint(p);
3257
+ p._endTagOutsideForeignContent(token);
3258
+ return;
3259
+ }
3260
+ for (let i = p.openElements.stackTop; i > 0; i--) {
3261
+ const element = p.openElements.items[i];
3262
+ if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) {
3263
+ p._endTagOutsideForeignContent(token);
3264
+ break;
3265
+ }
3266
+ const tagName = p.treeAdapter.getTagName(element);
3267
+ if (tagName.toLowerCase() === token.tagName) {
3268
+ //NOTE: update the token tag name for `_setEndLocation`.
3269
+ token.tagName = tagName;
3270
+ p.openElements.shortenToLength(i);
3271
+ break;
3272
+ }
3273
+ }
3274
+ }