summaryrefslogtreecommitdiff
path: root/school/node_modules/parse5/lib/parser
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2023-01-10 14:54:04 +0100
committerMinteck <contact@minteck.org>2023-01-10 14:54:04 +0100
commit99c1d9af689e5325f3cf535c4007b3aeb8325229 (patch)
treee663b3c2ebdbd67c818ac0c5147f0ce1d2463cda /school/node_modules/parse5/lib/parser
parent9871b03912fc28ad38b4037ebf26a78aa937baba (diff)
downloadpluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.gz
pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.bz2
pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.zip
Update - This is an automated commit
Diffstat (limited to 'school/node_modules/parse5/lib/parser')
-rw-r--r--school/node_modules/parse5/lib/parser/formatting-element-list.js181
-rw-r--r--school/node_modules/parse5/lib/parser/index.js2956
-rw-r--r--school/node_modules/parse5/lib/parser/open-element-stack.js482
3 files changed, 3619 insertions, 0 deletions
diff --git a/school/node_modules/parse5/lib/parser/formatting-element-list.js b/school/node_modules/parse5/lib/parser/formatting-element-list.js
new file mode 100644
index 0000000..0e241db
--- /dev/null
+++ b/school/node_modules/parse5/lib/parser/formatting-element-list.js
@@ -0,0 +1,181 @@
+'use strict';
+
+//Const
+const NOAH_ARK_CAPACITY = 3;
+
+//List of formatting elements
+class FormattingElementList {
+ constructor(treeAdapter) {
+ this.length = 0;
+ this.entries = [];
+ this.treeAdapter = treeAdapter;
+ this.bookmark = null;
+ }
+
+ //Noah Ark's condition
+ //OPTIMIZATION: at first we try to find possible candidates for exclusion using
+ //lightweight heuristics without thorough attributes check.
+ _getNoahArkConditionCandidates(newElement) {
+ const candidates = [];
+
+ if (this.length >= NOAH_ARK_CAPACITY) {
+ const neAttrsLength = this.treeAdapter.getAttrList(newElement).length;
+ const neTagName = this.treeAdapter.getTagName(newElement);
+ const neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
+
+ for (let i = this.length - 1; i >= 0; i--) {
+ const entry = this.entries[i];
+
+ if (entry.type === FormattingElementList.MARKER_ENTRY) {
+ break;
+ }
+
+ const element = entry.element;
+ const elementAttrs = this.treeAdapter.getAttrList(element);
+
+ const isCandidate =
+ this.treeAdapter.getTagName(element) === neTagName &&
+ this.treeAdapter.getNamespaceURI(element) === neNamespaceURI &&
+ elementAttrs.length === neAttrsLength;
+
+ if (isCandidate) {
+ candidates.push({ idx: i, attrs: elementAttrs });
+ }
+ }
+ }
+
+ return candidates.length < NOAH_ARK_CAPACITY ? [] : candidates;
+ }
+
+ _ensureNoahArkCondition(newElement) {
+ const candidates = this._getNoahArkConditionCandidates(newElement);
+ let cLength = candidates.length;
+
+ if (cLength) {
+ const neAttrs = this.treeAdapter.getAttrList(newElement);
+ const neAttrsLength = neAttrs.length;
+ const neAttrsMap = Object.create(null);
+
+ //NOTE: build attrs map for the new element so we can perform fast lookups
+ for (let i = 0; i < neAttrsLength; i++) {
+ const neAttr = neAttrs[i];
+
+ neAttrsMap[neAttr.name] = neAttr.value;
+ }
+
+ for (let i = 0; i < neAttrsLength; i++) {
+ for (let j = 0; j < cLength; j++) {
+ const cAttr = candidates[j].attrs[i];
+
+ if (neAttrsMap[cAttr.name] !== cAttr.value) {
+ candidates.splice(j, 1);
+ cLength--;
+ }
+
+ if (candidates.length < NOAH_ARK_CAPACITY) {
+ return;
+ }
+ }
+ }
+
+ //NOTE: remove bottommost candidates until Noah's Ark condition will not be met
+ for (let i = cLength - 1; i >= NOAH_ARK_CAPACITY - 1; i--) {
+ this.entries.splice(candidates[i].idx, 1);
+ this.length--;
+ }
+ }
+ }
+
+ //Mutations
+ insertMarker() {
+ this.entries.push({ type: FormattingElementList.MARKER_ENTRY });
+ this.length++;
+ }
+
+ pushElement(element, token) {
+ this._ensureNoahArkCondition(element);
+
+ this.entries.push({
+ type: FormattingElementList.ELEMENT_ENTRY,
+ element: element,
+ token: token
+ });
+
+ this.length++;
+ }
+
+ insertElementAfterBookmark(element, token) {
+ let bookmarkIdx = this.length - 1;
+
+ for (; bookmarkIdx >= 0; bookmarkIdx--) {
+ if (this.entries[bookmarkIdx] === this.bookmark) {
+ break;
+ }
+ }
+
+ this.entries.splice(bookmarkIdx + 1, 0, {
+ type: FormattingElementList.ELEMENT_ENTRY,
+ element: element,
+ token: token
+ });
+
+ this.length++;
+ }
+
+ removeEntry(entry) {
+ for (let i = this.length - 1; i >= 0; i--) {
+ if (this.entries[i] === entry) {
+ this.entries.splice(i, 1);
+ this.length--;
+ break;
+ }
+ }
+ }
+
+ clearToLastMarker() {
+ while (this.length) {
+ const entry = this.entries.pop();
+
+ this.length--;
+
+ if (entry.type === FormattingElementList.MARKER_ENTRY) {
+ break;
+ }
+ }
+ }
+
+ //Search
+ getElementEntryInScopeWithTagName(tagName) {
+ for (let i = this.length - 1; i >= 0; i--) {
+ const entry = this.entries[i];
+
+ if (entry.type === FormattingElementList.MARKER_ENTRY) {
+ return null;
+ }
+
+ if (this.treeAdapter.getTagName(entry.element) === tagName) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ getElementEntry(element) {
+ for (let i = this.length - 1; i >= 0; i--) {
+ const entry = this.entries[i];
+
+ if (entry.type === FormattingElementList.ELEMENT_ENTRY && entry.element === element) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+}
+
+//Entry types
+FormattingElementList.MARKER_ENTRY = 'MARKER_ENTRY';
+FormattingElementList.ELEMENT_ENTRY = 'ELEMENT_ENTRY';
+
+module.exports = FormattingElementList;
diff --git a/school/node_modules/parse5/lib/parser/index.js b/school/node_modules/parse5/lib/parser/index.js
new file mode 100644
index 0000000..45d3e83
--- /dev/null
+++ b/school/node_modules/parse5/lib/parser/index.js
@@ -0,0 +1,2956 @@
+'use strict';
+
+const Tokenizer = require('../tokenizer');
+const OpenElementStack = require('./open-element-stack');
+const FormattingElementList = require('./formatting-element-list');
+const LocationInfoParserMixin = require('../extensions/location-info/parser-mixin');
+const ErrorReportingParserMixin = require('../extensions/error-reporting/parser-mixin');
+const Mixin = require('../utils/mixin');
+const defaultTreeAdapter = require('../tree-adapters/default');
+const mergeOptions = require('../utils/merge-options');
+const doctype = require('../common/doctype');
+const foreignContent = require('../common/foreign-content');
+const ERR = require('../common/error-codes');
+const unicode = require('../common/unicode');
+const HTML = require('../common/html');
+
+//Aliases
+const $ = HTML.TAG_NAMES;
+const NS = HTML.NAMESPACES;
+const ATTRS = HTML.ATTRS;
+
+const DEFAULT_OPTIONS = {
+ scriptingEnabled: true,
+ sourceCodeLocationInfo: false,
+ onParseError: null,
+ treeAdapter: defaultTreeAdapter
+};
+
+//Misc constants
+const HIDDEN_INPUT_TYPE = 'hidden';
+
+//Adoption agency loops iteration count
+const AA_OUTER_LOOP_ITER = 8;
+const AA_INNER_LOOP_ITER = 3;
+
+//Insertion modes
+const INITIAL_MODE = 'INITIAL_MODE';
+const BEFORE_HTML_MODE = 'BEFORE_HTML_MODE';
+const BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE';
+const IN_HEAD_MODE = 'IN_HEAD_MODE';
+const IN_HEAD_NO_SCRIPT_MODE = 'IN_HEAD_NO_SCRIPT_MODE';
+const AFTER_HEAD_MODE = 'AFTER_HEAD_MODE';
+const IN_BODY_MODE = 'IN_BODY_MODE';
+const TEXT_MODE = 'TEXT_MODE';
+const IN_TABLE_MODE = 'IN_TABLE_MODE';
+const IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE';
+const IN_CAPTION_MODE = 'IN_CAPTION_MODE';
+const IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE';
+const IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE';
+const IN_ROW_MODE = 'IN_ROW_MODE';
+const IN_CELL_MODE = 'IN_CELL_MODE';
+const IN_SELECT_MODE = 'IN_SELECT_MODE';
+const IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE';
+const IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE';
+const AFTER_BODY_MODE = 'AFTER_BODY_MODE';
+const IN_FRAMESET_MODE = 'IN_FRAMESET_MODE';
+const AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE';
+const AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE';
+const AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE';
+
+//Insertion mode reset map
+const INSERTION_MODE_RESET_MAP = {
+ [$.TR]: IN_ROW_MODE,
+ [$.TBODY]: IN_TABLE_BODY_MODE,
+ [$.THEAD]: IN_TABLE_BODY_MODE,
+ [$.TFOOT]: IN_TABLE_BODY_MODE,
+ [$.CAPTION]: IN_CAPTION_MODE,
+ [$.COLGROUP]: IN_COLUMN_GROUP_MODE,
+ [$.TABLE]: IN_TABLE_MODE,
+ [$.BODY]: IN_BODY_MODE,
+ [$.FRAMESET]: IN_FRAMESET_MODE
+};
+
+//Template insertion mode switch map
+const TEMPLATE_INSERTION_MODE_SWITCH_MAP = {
+ [$.CAPTION]: IN_TABLE_MODE,
+ [$.COLGROUP]: IN_TABLE_MODE,
+ [$.TBODY]: IN_TABLE_MODE,
+ [$.TFOOT]: IN_TABLE_MODE,
+ [$.THEAD]: IN_TABLE_MODE,
+ [$.COL]: IN_COLUMN_GROUP_MODE,
+ [$.TR]: IN_TABLE_BODY_MODE,
+ [$.TD]: IN_ROW_MODE,
+ [$.TH]: IN_ROW_MODE
+};
+
+//Token handlers map for insertion modes
+const TOKEN_HANDLERS = {
+ [INITIAL_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenInInitialMode,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInInitialMode,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: doctypeInInitialMode,
+ [Tokenizer.START_TAG_TOKEN]: tokenInInitialMode,
+ [Tokenizer.END_TAG_TOKEN]: tokenInInitialMode,
+ [Tokenizer.EOF_TOKEN]: tokenInInitialMode
+ },
+ [BEFORE_HTML_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHtml,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHtml,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagBeforeHtml,
+ [Tokenizer.END_TAG_TOKEN]: endTagBeforeHtml,
+ [Tokenizer.EOF_TOKEN]: tokenBeforeHtml
+ },
+ [BEFORE_HEAD_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHead,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHead,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
+ [Tokenizer.START_TAG_TOKEN]: startTagBeforeHead,
+ [Tokenizer.END_TAG_TOKEN]: endTagBeforeHead,
+ [Tokenizer.EOF_TOKEN]: tokenBeforeHead
+ },
+ [IN_HEAD_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenInHead,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHead,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
+ [Tokenizer.START_TAG_TOKEN]: startTagInHead,
+ [Tokenizer.END_TAG_TOKEN]: endTagInHead,
+ [Tokenizer.EOF_TOKEN]: tokenInHead
+ },
+ [IN_HEAD_NO_SCRIPT_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenInHeadNoScript,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHeadNoScript,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
+ [Tokenizer.START_TAG_TOKEN]: startTagInHeadNoScript,
+ [Tokenizer.END_TAG_TOKEN]: endTagInHeadNoScript,
+ [Tokenizer.EOF_TOKEN]: tokenInHeadNoScript
+ },
+ [AFTER_HEAD_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenAfterHead,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterHead,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
+ [Tokenizer.START_TAG_TOKEN]: startTagAfterHead,
+ [Tokenizer.END_TAG_TOKEN]: endTagAfterHead,
+ [Tokenizer.EOF_TOKEN]: tokenAfterHead
+ },
+ [IN_BODY_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInBody,
+ [Tokenizer.END_TAG_TOKEN]: endTagInBody,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [TEXT_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: ignoreToken,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: ignoreToken,
+ [Tokenizer.END_TAG_TOKEN]: endTagInText,
+ [Tokenizer.EOF_TOKEN]: eofInText
+ },
+ [IN_TABLE_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInTable,
+ [Tokenizer.END_TAG_TOKEN]: endTagInTable,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_TABLE_TEXT_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInTableText,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInTableText,
+ [Tokenizer.COMMENT_TOKEN]: tokenInTableText,
+ [Tokenizer.DOCTYPE_TOKEN]: tokenInTableText,
+ [Tokenizer.START_TAG_TOKEN]: tokenInTableText,
+ [Tokenizer.END_TAG_TOKEN]: tokenInTableText,
+ [Tokenizer.EOF_TOKEN]: tokenInTableText
+ },
+ [IN_CAPTION_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInCaption,
+ [Tokenizer.END_TAG_TOKEN]: endTagInCaption,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_COLUMN_GROUP_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenInColumnGroup,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInColumnGroup,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInColumnGroup,
+ [Tokenizer.END_TAG_TOKEN]: endTagInColumnGroup,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_TABLE_BODY_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInTableBody,
+ [Tokenizer.END_TAG_TOKEN]: endTagInTableBody,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_ROW_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInRow,
+ [Tokenizer.END_TAG_TOKEN]: endTagInRow,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_CELL_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInCell,
+ [Tokenizer.END_TAG_TOKEN]: endTagInCell,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_SELECT_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInSelect,
+ [Tokenizer.END_TAG_TOKEN]: endTagInSelect,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_SELECT_IN_TABLE_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInSelectInTable,
+ [Tokenizer.END_TAG_TOKEN]: endTagInSelectInTable,
+ [Tokenizer.EOF_TOKEN]: eofInBody
+ },
+ [IN_TEMPLATE_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: characterInBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInTemplate,
+ [Tokenizer.END_TAG_TOKEN]: endTagInTemplate,
+ [Tokenizer.EOF_TOKEN]: eofInTemplate
+ },
+ [AFTER_BODY_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenAfterBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterBody,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendCommentToRootHtmlElement,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagAfterBody,
+ [Tokenizer.END_TAG_TOKEN]: endTagAfterBody,
+ [Tokenizer.EOF_TOKEN]: stopParsing
+ },
+ [IN_FRAMESET_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagInFrameset,
+ [Tokenizer.END_TAG_TOKEN]: endTagInFrameset,
+ [Tokenizer.EOF_TOKEN]: stopParsing
+ },
+ [AFTER_FRAMESET_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
+ [Tokenizer.COMMENT_TOKEN]: appendComment,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagAfterFrameset,
+ [Tokenizer.END_TAG_TOKEN]: endTagAfterFrameset,
+ [Tokenizer.EOF_TOKEN]: stopParsing
+ },
+ [AFTER_AFTER_BODY_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: tokenAfterAfterBody,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterAfterBody,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterBody,
+ [Tokenizer.END_TAG_TOKEN]: tokenAfterAfterBody,
+ [Tokenizer.EOF_TOKEN]: stopParsing
+ },
+ [AFTER_AFTER_FRAMESET_MODE]: {
+ [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
+ [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
+ [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
+ [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
+ [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterFrameset,
+ [Tokenizer.END_TAG_TOKEN]: ignoreToken,
+ [Tokenizer.EOF_TOKEN]: stopParsing
+ }
+};
+
+//Parser
+class Parser {
+ constructor(options) {
+ this.options = mergeOptions(DEFAULT_OPTIONS, options);
+
+ this.treeAdapter = this.options.treeAdapter;
+ this.pendingScript = null;
+
+ if (this.options.sourceCodeLocationInfo) {
+ Mixin.install(this, LocationInfoParserMixin);
+ }
+
+ if (this.options.onParseError) {
+ Mixin.install(this, ErrorReportingParserMixin, { onParseError: this.options.onParseError });
+ }
+ }
+
+ // API
+ parse(html) {
+ const document = this.treeAdapter.createDocument();
+
+ this._bootstrap(document, null);
+ this.tokenizer.write(html, true);
+ this._runParsingLoop(null);
+
+ return document;
+ }
+
+ parseFragment(html, fragmentContext) {
+ //NOTE: use <template> element as a fragment context if context element was not provided,
+ //so we will parse in "forgiving" manner
+ if (!fragmentContext) {
+ fragmentContext = this.treeAdapter.createElement($.TEMPLATE, NS.HTML, []);
+ }
+
+ //NOTE: create fake element which will be used as 'document' for fragment parsing.
+ //This is important for jsdom there 'document' can't be recreated, therefore
+ //fragment parsing causes messing of the main `document`.
+ const documentMock = this.treeAdapter.createElement('documentmock', NS.HTML, []);
+
+ this._bootstrap(documentMock, fragmentContext);
+
+ if (this.treeAdapter.getTagName(fragmentContext) === $.TEMPLATE) {
+ this._pushTmplInsertionMode(IN_TEMPLATE_MODE);
+ }
+
+ this._initTokenizerForFragmentParsing();
+ this._insertFakeRootElement();
+ this._resetInsertionMode();
+ this._findFormInFragmentContext();
+ this.tokenizer.write(html, true);
+ this._runParsingLoop(null);
+
+ const rootElement = this.treeAdapter.getFirstChild(documentMock);
+ const fragment = this.treeAdapter.createDocumentFragment();
+
+ this._adoptNodes(rootElement, fragment);
+
+ return fragment;
+ }
+
+ //Bootstrap parser
+ _bootstrap(document, fragmentContext) {
+ this.tokenizer = new Tokenizer(this.options);
+
+ this.stopped = false;
+
+ this.insertionMode = INITIAL_MODE;
+ this.originalInsertionMode = '';
+
+ this.document = document;
+ this.fragmentContext = fragmentContext;
+
+ this.headElement = null;
+ this.formElement = null;
+
+ this.openElements = new OpenElementStack(this.document, this.treeAdapter);
+ this.activeFormattingElements = new FormattingElementList(this.treeAdapter);
+
+ this.tmplInsertionModeStack = [];
+ this.tmplInsertionModeStackTop = -1;
+ this.currentTmplInsertionMode = null;
+
+ this.pendingCharacterTokens = [];
+ this.hasNonWhitespacePendingCharacterToken = false;
+
+ this.framesetOk = true;
+ this.skipNextNewLine = false;
+ this.fosterParentingEnabled = false;
+ }
+
+ //Errors
+ _err() {
+ // NOTE: err reporting is noop by default. Enabled by mixin.
+ }
+
+ //Parsing loop
+ _runParsingLoop(scriptHandler) {
+ while (!this.stopped) {
+ this._setupTokenizerCDATAMode();
+
+ const token = this.tokenizer.getNextToken();
+
+ if (token.type === Tokenizer.HIBERNATION_TOKEN) {
+ break;
+ }
+
+ if (this.skipNextNewLine) {
+ this.skipNextNewLine = false;
+
+ if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') {
+ if (token.chars.length === 1) {
+ continue;
+ }
+
+ token.chars = token.chars.substr(1);
+ }
+ }
+
+ this._processInputToken(token);
+
+ if (scriptHandler && this.pendingScript) {
+ break;
+ }
+ }
+ }
+
+ runParsingLoopForCurrentChunk(writeCallback, scriptHandler) {
+ this._runParsingLoop(scriptHandler);
+
+ if (scriptHandler && this.pendingScript) {
+ const script = this.pendingScript;
+
+ this.pendingScript = null;
+
+ scriptHandler(script);
+
+ return;
+ }
+
+ if (writeCallback) {
+ writeCallback();
+ }
+ }
+
+ //Text parsing
+ _setupTokenizerCDATAMode() {
+ const current = this._getAdjustedCurrentElement();
+
+ this.tokenizer.allowCDATA =
+ current &&
+ current !== this.document &&
+ this.treeAdapter.getNamespaceURI(current) !== NS.HTML &&
+ !this._isIntegrationPoint(current);
+ }
+
+ _switchToTextParsing(currentToken, nextTokenizerState) {
+ this._insertElement(currentToken, NS.HTML);
+ this.tokenizer.state = nextTokenizerState;
+ this.originalInsertionMode = this.insertionMode;
+ this.insertionMode = TEXT_MODE;
+ }
+
+ switchToPlaintextParsing() {
+ this.insertionMode = TEXT_MODE;
+ this.originalInsertionMode = IN_BODY_MODE;
+ this.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
+ }
+
+ //Fragment parsing
+ _getAdjustedCurrentElement() {
+ return this.openElements.stackTop === 0 && this.fragmentContext
+ ? this.fragmentContext
+ : this.openElements.current;
+ }
+
+ _findFormInFragmentContext() {
+ let node = this.fragmentContext;
+
+ do {
+ if (this.treeAdapter.getTagName(node) === $.FORM) {
+ this.formElement = node;
+ break;
+ }
+
+ node = this.treeAdapter.getParentNode(node);
+ } while (node);
+ }
+
+ _initTokenizerForFragmentParsing() {
+ if (this.treeAdapter.getNamespaceURI(this.fragmentContext) === NS.HTML) {
+ const tn = this.treeAdapter.getTagName(this.fragmentContext);
+
+ if (tn === $.TITLE || tn === $.TEXTAREA) {
+ this.tokenizer.state = Tokenizer.MODE.RCDATA;
+ } else if (
+ tn === $.STYLE ||
+ tn === $.XMP ||
+ tn === $.IFRAME ||
+ tn === $.NOEMBED ||
+ tn === $.NOFRAMES ||
+ tn === $.NOSCRIPT
+ ) {
+ this.tokenizer.state = Tokenizer.MODE.RAWTEXT;
+ } else if (tn === $.SCRIPT) {
+ this.tokenizer.state = Tokenizer.MODE.SCRIPT_DATA;
+ } else if (tn === $.PLAINTEXT) {
+ this.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
+ }
+ }
+ }
+
+ //Tree mutation
+ _setDocumentType(token) {
+ const name = token.name || '';
+ const publicId = token.publicId || '';
+ const systemId = token.systemId || '';
+
+ this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
+ }
+
+ _attachElementToTree(element) {
+ if (this._shouldFosterParentOnInsertion()) {
+ this._fosterParentElement(element);
+ } else {
+ const parent = this.openElements.currentTmplContent || this.openElements.current;
+
+ this.treeAdapter.appendChild(parent, element);
+ }
+ }
+
+ _appendElement(token, namespaceURI) {
+ const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
+
+ this._attachElementToTree(element);
+ }
+
+ _insertElement(token, namespaceURI) {
+ const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
+
+ this._attachElementToTree(element);
+ this.openElements.push(element);
+ }
+
+ _insertFakeElement(tagName) {
+ const element = this.treeAdapter.createElement(tagName, NS.HTML, []);
+
+ this._attachElementToTree(element);
+ this.openElements.push(element);
+ }
+
+ _insertTemplate(token) {
+ const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs);
+ const content = this.treeAdapter.createDocumentFragment();
+
+ this.treeAdapter.setTemplateContent(tmpl, content);
+ this._attachElementToTree(tmpl);
+ this.openElements.push(tmpl);
+ }
+
+ _insertFakeRootElement() {
+ const element = this.treeAdapter.createElement($.HTML, NS.HTML, []);
+
+ this.treeAdapter.appendChild(this.openElements.current, element);
+ this.openElements.push(element);
+ }
+
+ _appendCommentNode(token, parent) {
+ const commentNode = this.treeAdapter.createCommentNode(token.data);
+
+ this.treeAdapter.appendChild(parent, commentNode);
+ }
+
+ _insertCharacters(token) {
+ if (this._shouldFosterParentOnInsertion()) {
+ this._fosterParentText(token.chars);
+ } else {
+ const parent = this.openElements.currentTmplContent || this.openElements.current;
+
+ this.treeAdapter.insertText(parent, token.chars);
+ }
+ }
+
+ _adoptNodes(donor, recipient) {
+ for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
+ this.treeAdapter.detachNode(child);
+ this.treeAdapter.appendChild(recipient, child);
+ }
+ }
+
+ //Token processing
+ _shouldProcessTokenInForeignContent(token) {
+ const current = this._getAdjustedCurrentElement();
+
+ if (!current || current === this.document) {
+ return false;
+ }
+
+ const ns = this.treeAdapter.getNamespaceURI(current);
+
+ if (ns === NS.HTML) {
+ return false;
+ }
+
+ if (
+ this.treeAdapter.getTagName(current) === $.ANNOTATION_XML &&
+ ns === NS.MATHML &&
+ token.type === Tokenizer.START_TAG_TOKEN &&
+ token.tagName === $.SVG
+ ) {
+ return false;
+ }
+
+ const isCharacterToken =
+ token.type === Tokenizer.CHARACTER_TOKEN ||
+ token.type === Tokenizer.NULL_CHARACTER_TOKEN ||
+ token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN;
+
+ const isMathMLTextStartTag =
+ token.type === Tokenizer.START_TAG_TOKEN && token.tagName !== $.MGLYPH && token.tagName !== $.MALIGNMARK;
+
+ if ((isMathMLTextStartTag || isCharacterToken) && this._isIntegrationPoint(current, NS.MATHML)) {
+ return false;
+ }
+
+ if (
+ (token.type === Tokenizer.START_TAG_TOKEN || isCharacterToken) &&
+ this._isIntegrationPoint(current, NS.HTML)
+ ) {
+ return false;
+ }
+
+ return token.type !== Tokenizer.EOF_TOKEN;
+ }
+
+ _processToken(token) {
+ TOKEN_HANDLERS[this.insertionMode][token.type](this, token);
+ }
+
+ _processTokenInBodyMode(token) {
+ TOKEN_HANDLERS[IN_BODY_MODE][token.type](this, token);
+ }
+
+ _processTokenInForeignContent(token) {
+ if (token.type === Tokenizer.CHARACTER_TOKEN) {
+ characterInForeignContent(this, token);
+ } else if (token.type === Tokenizer.NULL_CHARACTER_TOKEN) {
+ nullCharacterInForeignContent(this, token);
+ } else if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN) {
+ insertCharacters(this, token);
+ } else if (token.type === Tokenizer.COMMENT_TOKEN) {
+ appendComment(this, token);
+ } else if (token.type === Tokenizer.START_TAG_TOKEN) {
+ startTagInForeignContent(this, token);
+ } else if (token.type === Tokenizer.END_TAG_TOKEN) {
+ endTagInForeignContent(this, token);
+ }
+ }
+
+ _processInputToken(token) {
+ if (this._shouldProcessTokenInForeignContent(token)) {
+ this._processTokenInForeignContent(token);
+ } else {
+ this._processToken(token);
+ }
+
+ if (token.type === Tokenizer.START_TAG_TOKEN && token.selfClosing && !token.ackSelfClosing) {
+ this._err(ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
+ }
+ }
+
+ //Integration points
+ _isIntegrationPoint(element, foreignNS) {
+ const tn = this.treeAdapter.getTagName(element);
+ const ns = this.treeAdapter.getNamespaceURI(element);
+ const attrs = this.treeAdapter.getAttrList(element);
+
+ return foreignContent.isIntegrationPoint(tn, ns, attrs, foreignNS);
+ }
+
+ //Active formatting elements reconstruction
+ _reconstructActiveFormattingElements() {
+ const listLength = this.activeFormattingElements.length;
+
+ if (listLength) {
+ let unopenIdx = listLength;
+ let entry = null;
+
+ do {
+ unopenIdx--;
+ entry = this.activeFormattingElements.entries[unopenIdx];
+
+ if (entry.type === FormattingElementList.MARKER_ENTRY || this.openElements.contains(entry.element)) {
+ unopenIdx++;
+ break;
+ }
+ } while (unopenIdx > 0);
+
+ for (let i = unopenIdx; i < listLength; i++) {
+ entry = this.activeFormattingElements.entries[i];
+ this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
+ entry.element = this.openElements.current;
+ }
+ }
+ }
+
+ //Close elements
+ _closeTableCell() {
+ this.openElements.generateImpliedEndTags();
+ this.openElements.popUntilTableCellPopped();
+ this.activeFormattingElements.clearToLastMarker();
+ this.insertionMode = IN_ROW_MODE;
+ }
+
+ _closePElement() {
+ this.openElements.generateImpliedEndTagsWithExclusion($.P);
+ this.openElements.popUntilTagNamePopped($.P);
+ }
+
+ //Insertion modes
+ _resetInsertionMode() {
+ for (let i = this.openElements.stackTop, last = false; i >= 0; i--) {
+ let element = this.openElements.items[i];
+
+ if (i === 0) {
+ last = true;
+
+ if (this.fragmentContext) {
+ element = this.fragmentContext;
+ }
+ }
+
+ const tn = this.treeAdapter.getTagName(element);
+ const newInsertionMode = INSERTION_MODE_RESET_MAP[tn];
+
+ if (newInsertionMode) {
+ this.insertionMode = newInsertionMode;
+ break;
+ } else if (!last && (tn === $.TD || tn === $.TH)) {
+ this.insertionMode = IN_CELL_MODE;
+ break;
+ } else if (!last && tn === $.HEAD) {
+ this.insertionMode = IN_HEAD_MODE;
+ break;
+ } else if (tn === $.SELECT) {
+ this._resetInsertionModeForSelect(i);
+ break;
+ } else if (tn === $.TEMPLATE) {
+ this.insertionMode = this.currentTmplInsertionMode;
+ break;
+ } else if (tn === $.HTML) {
+ this.insertionMode = this.headElement ? AFTER_HEAD_MODE : BEFORE_HEAD_MODE;
+ break;
+ } else if (last) {
+ this.insertionMode = IN_BODY_MODE;
+ break;
+ }
+ }
+ }
+
+ _resetInsertionModeForSelect(selectIdx) {
+ if (selectIdx > 0) {
+ for (let i = selectIdx - 1; i > 0; i--) {
+ const ancestor = this.openElements.items[i];
+ const tn = this.treeAdapter.getTagName(ancestor);
+
+ if (tn === $.TEMPLATE) {
+ break;
+ } else if (tn === $.TABLE) {
+ this.insertionMode = IN_SELECT_IN_TABLE_MODE;
+ return;
+ }
+ }
+ }
+
+ this.insertionMode = IN_SELECT_MODE;
+ }
+
+ _pushTmplInsertionMode(mode) {
+ this.tmplInsertionModeStack.push(mode);
+ this.tmplInsertionModeStackTop++;
+ this.currentTmplInsertionMode = mode;
+ }
+
+ _popTmplInsertionMode() {
+ this.tmplInsertionModeStack.pop();
+ this.tmplInsertionModeStackTop--;
+ this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop];
+ }
+
+ //Foster parenting
+ _isElementCausesFosterParenting(element) {
+ const tn = this.treeAdapter.getTagName(element);
+
+ return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR;
+ }
+
+ _shouldFosterParentOnInsertion() {
+ return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current);
+ }
+
+ _findFosterParentingLocation() {
+ const location = {
+ parent: null,
+ beforeElement: null
+ };
+
+ for (let i = this.openElements.stackTop; i >= 0; i--) {
+ const openElement = this.openElements.items[i];
+ const tn = this.treeAdapter.getTagName(openElement);
+ const ns = this.treeAdapter.getNamespaceURI(openElement);
+
+ if (tn === $.TEMPLATE && ns === NS.HTML) {
+ location.parent = this.treeAdapter.getTemplateContent(openElement);
+ break;
+ } else if (tn === $.TABLE) {
+ location.parent = this.treeAdapter.getParentNode(openElement);
+
+ if (location.parent) {
+ location.beforeElement = openElement;
+ } else {
+ location.parent = this.openElements.items[i - 1];
+ }
+
+ break;
+ }
+ }
+
+ if (!location.parent) {
+ location.parent = this.openElements.items[0];
+ }
+
+ return location;
+ }
+
+ _fosterParentElement(element) {
+ const location = this._findFosterParentingLocation();
+
+ if (location.beforeElement) {
+ this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
+ } else {
+ this.treeAdapter.appendChild(location.parent, element);
+ }
+ }
+
+ _fosterParentText(chars) {
+ const location = this._findFosterParentingLocation();
+
+ if (location.beforeElement) {
+ this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement);
+ } else {
+ this.treeAdapter.insertText(location.parent, chars);
+ }
+ }
+
+ //Special elements
+ _isSpecialElement(element) {
+ const tn = this.treeAdapter.getTagName(element);
+ const ns = this.treeAdapter.getNamespaceURI(element);
+
+ return HTML.SPECIAL_ELEMENTS[ns][tn];
+ }
+}
+
+module.exports = Parser;
+
+//Adoption agency algorithm
+//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency)
+//------------------------------------------------------------------
+
+//Steps 5-8 of the algorithm
+function aaObtainFormattingElementEntry(p, token) {
+ let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName);
+
+ if (formattingElementEntry) {
+ if (!p.openElements.contains(formattingElementEntry.element)) {
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
+ formattingElementEntry = null;
+ } else if (!p.openElements.hasInScope(token.tagName)) {
+ formattingElementEntry = null;
+ }
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ return formattingElementEntry;
+}
+
+//Steps 9 and 10 of the algorithm
+function aaObtainFurthestBlock(p, formattingElementEntry) {
+ let furthestBlock = null;
+
+ for (let i = p.openElements.stackTop; i >= 0; i--) {
+ const element = p.openElements.items[i];
+
+ if (element === formattingElementEntry.element) {
+ break;
+ }
+
+ if (p._isSpecialElement(element)) {
+ furthestBlock = element;
+ }
+ }
+
+ if (!furthestBlock) {
+ p.openElements.popUntilElementPopped(formattingElementEntry.element);
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
+ }
+
+ return furthestBlock;
+}
+
+//Step 13 of the algorithm
+function aaInnerLoop(p, furthestBlock, formattingElement) {
+ let lastElement = furthestBlock;
+ let nextElement = p.openElements.getCommonAncestor(furthestBlock);
+
+ for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
+ //NOTE: store next element for the next loop iteration (it may be deleted from the stack by step 9.5)
+ nextElement = p.openElements.getCommonAncestor(element);
+
+ const elementEntry = p.activeFormattingElements.getElementEntry(element);
+ const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
+ const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
+
+ if (shouldRemoveFromOpenElements) {
+ if (counterOverflow) {
+ p.activeFormattingElements.removeEntry(elementEntry);
+ }
+
+ p.openElements.remove(element);
+ } else {
+ element = aaRecreateElementFromEntry(p, elementEntry);
+
+ if (lastElement === furthestBlock) {
+ p.activeFormattingElements.bookmark = elementEntry;
+ }
+
+ p.treeAdapter.detachNode(lastElement);
+ p.treeAdapter.appendChild(element, lastElement);
+ lastElement = element;
+ }
+ }
+
+ return lastElement;
+}
+
+//Step 13.7 of the algorithm
+function aaRecreateElementFromEntry(p, elementEntry) {
+ const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
+ const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs);
+
+ p.openElements.replace(elementEntry.element, newElement);
+ elementEntry.element = newElement;
+
+ return newElement;
+}
+
+//Step 14 of the algorithm
+function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
+ if (p._isElementCausesFosterParenting(commonAncestor)) {
+ p._fosterParentElement(lastElement);
+ } else {
+ const tn = p.treeAdapter.getTagName(commonAncestor);
+ const ns = p.treeAdapter.getNamespaceURI(commonAncestor);
+
+ if (tn === $.TEMPLATE && ns === NS.HTML) {
+ commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor);
+ }
+
+ p.treeAdapter.appendChild(commonAncestor, lastElement);
+ }
+}
+
+//Steps 15-19 of the algorithm
+function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
+ const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
+ const token = formattingElementEntry.token;
+ const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs);
+
+ p._adoptNodes(furthestBlock, newElement);
+ p.treeAdapter.appendChild(furthestBlock, newElement);
+
+ p.activeFormattingElements.insertElementAfterBookmark(newElement, formattingElementEntry.token);
+ p.activeFormattingElements.removeEntry(formattingElementEntry);
+
+ p.openElements.remove(formattingElementEntry.element);
+ p.openElements.insertAfter(furthestBlock, newElement);
+}
+
+//Algorithm entry point
+function callAdoptionAgency(p, token) {
+ let formattingElementEntry;
+
+ for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
+ formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry);
+
+ if (!formattingElementEntry) {
+ break;
+ }
+
+ const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
+
+ if (!furthestBlock) {
+ break;
+ }
+
+ p.activeFormattingElements.bookmark = formattingElementEntry;
+
+ const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
+ const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
+
+ p.treeAdapter.detachNode(lastElement);
+ aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement);
+ aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry);
+ }
+}
+
+//Generic token handlers
+//------------------------------------------------------------------
+function ignoreToken() {
+ //NOTE: do nothing =)
+}
+
+function misplacedDoctype(p) {
+ p._err(ERR.misplacedDoctype);
+}
+
+function appendComment(p, token) {
+ p._appendCommentNode(token, p.openElements.currentTmplContent || p.openElements.current);
+}
+
+function appendCommentToRootHtmlElement(p, token) {
+ p._appendCommentNode(token, p.openElements.items[0]);
+}
+
+function appendCommentToDocument(p, token) {
+ p._appendCommentNode(token, p.document);
+}
+
+function insertCharacters(p, token) {
+ p._insertCharacters(token);
+}
+
+function stopParsing(p) {
+ p.stopped = true;
+}
+
+// The "initial" insertion mode
+//------------------------------------------------------------------
+function doctypeInInitialMode(p, token) {
+ p._setDocumentType(token);
+
+ const mode = token.forceQuirks ? HTML.DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token);
+
+ if (!doctype.isConforming(token)) {
+ p._err(ERR.nonConformingDoctype);
+ }
+
+ p.treeAdapter.setDocumentMode(p.document, mode);
+
+ p.insertionMode = BEFORE_HTML_MODE;
+}
+
+function tokenInInitialMode(p, token) {
+ p._err(ERR.missingDoctype, { beforeToken: true });
+ p.treeAdapter.setDocumentMode(p.document, HTML.DOCUMENT_MODE.QUIRKS);
+ p.insertionMode = BEFORE_HTML_MODE;
+ p._processToken(token);
+}
+
+// The "before html" insertion mode
+//------------------------------------------------------------------
+function startTagBeforeHtml(p, token) {
+ if (token.tagName === $.HTML) {
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = BEFORE_HEAD_MODE;
+ } else {
+ tokenBeforeHtml(p, token);
+ }
+}
+
+function endTagBeforeHtml(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) {
+ tokenBeforeHtml(p, token);
+ }
+}
+
+function tokenBeforeHtml(p, token) {
+ p._insertFakeRootElement();
+ p.insertionMode = BEFORE_HEAD_MODE;
+ p._processToken(token);
+}
+
+// The "before head" insertion mode
+//------------------------------------------------------------------
+function startTagBeforeHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.HEAD) {
+ p._insertElement(token, NS.HTML);
+ p.headElement = p.openElements.current;
+ p.insertionMode = IN_HEAD_MODE;
+ } else {
+ tokenBeforeHead(p, token);
+ }
+}
+
+function endTagBeforeHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) {
+ tokenBeforeHead(p, token);
+ } else {
+ p._err(ERR.endTagWithoutMatchingOpenElement);
+ }
+}
+
+function tokenBeforeHead(p, token) {
+ p._insertFakeElement($.HEAD);
+ p.headElement = p.openElements.current;
+ p.insertionMode = IN_HEAD_MODE;
+ p._processToken(token);
+}
+
+// The "in head" insertion mode
+//------------------------------------------------------------------
+function startTagInHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META) {
+ p._appendElement(token, NS.HTML);
+ token.ackSelfClosing = true;
+ } else if (tn === $.TITLE) {
+ p._switchToTextParsing(token, Tokenizer.MODE.RCDATA);
+ } else if (tn === $.NOSCRIPT) {
+ if (p.options.scriptingEnabled) {
+ p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
+ } else {
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_HEAD_NO_SCRIPT_MODE;
+ }
+ } else if (tn === $.NOFRAMES || tn === $.STYLE) {
+ p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
+ } else if (tn === $.SCRIPT) {
+ p._switchToTextParsing(token, Tokenizer.MODE.SCRIPT_DATA);
+ } else if (tn === $.TEMPLATE) {
+ p._insertTemplate(token, NS.HTML);
+ p.activeFormattingElements.insertMarker();
+ p.framesetOk = false;
+ p.insertionMode = IN_TEMPLATE_MODE;
+ p._pushTmplInsertionMode(IN_TEMPLATE_MODE);
+ } else if (tn === $.HEAD) {
+ p._err(ERR.misplacedStartTagForHeadElement);
+ } else {
+ tokenInHead(p, token);
+ }
+}
+
+function endTagInHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HEAD) {
+ p.openElements.pop();
+ p.insertionMode = AFTER_HEAD_MODE;
+ } else if (tn === $.BODY || tn === $.BR || tn === $.HTML) {
+ tokenInHead(p, token);
+ } else if (tn === $.TEMPLATE) {
+ if (p.openElements.tmplCount > 0) {
+ p.openElements.generateImpliedEndTagsThoroughly();
+
+ if (p.openElements.currentTagName !== $.TEMPLATE) {
+ p._err(ERR.closingOfElementWithOpenChildElements);
+ }
+
+ p.openElements.popUntilTagNamePopped($.TEMPLATE);
+ p.activeFormattingElements.clearToLastMarker();
+ p._popTmplInsertionMode();
+ p._resetInsertionMode();
+ } else {
+ p._err(ERR.endTagWithoutMatchingOpenElement);
+ }
+ } else {
+ p._err(ERR.endTagWithoutMatchingOpenElement);
+ }
+}
+
+function tokenInHead(p, token) {
+ p.openElements.pop();
+ p.insertionMode = AFTER_HEAD_MODE;
+ p._processToken(token);
+}
+
+// The "in head no script" insertion mode
+//------------------------------------------------------------------
+function startTagInHeadNoScript(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (
+ tn === $.BASEFONT ||
+ tn === $.BGSOUND ||
+ tn === $.HEAD ||
+ tn === $.LINK ||
+ tn === $.META ||
+ tn === $.NOFRAMES ||
+ tn === $.STYLE
+ ) {
+ startTagInHead(p, token);
+ } else if (tn === $.NOSCRIPT) {
+ p._err(ERR.nestedNoscriptInHead);
+ } else {
+ tokenInHeadNoScript(p, token);
+ }
+}
+
+function endTagInHeadNoScript(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.NOSCRIPT) {
+ p.openElements.pop();
+ p.insertionMode = IN_HEAD_MODE;
+ } else if (tn === $.BR) {
+ tokenInHeadNoScript(p, token);
+ } else {
+ p._err(ERR.endTagWithoutMatchingOpenElement);
+ }
+}
+
+function tokenInHeadNoScript(p, token) {
+ const errCode =
+ token.type === Tokenizer.EOF_TOKEN ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead;
+
+ p._err(errCode);
+ p.openElements.pop();
+ p.insertionMode = IN_HEAD_MODE;
+ p._processToken(token);
+}
+
+// The "after head" insertion mode
+//------------------------------------------------------------------
+function startTagAfterHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.BODY) {
+ p._insertElement(token, NS.HTML);
+ p.framesetOk = false;
+ p.insertionMode = IN_BODY_MODE;
+ } else if (tn === $.FRAMESET) {
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_FRAMESET_MODE;
+ } else if (
+ tn === $.BASE ||
+ tn === $.BASEFONT ||
+ tn === $.BGSOUND ||
+ tn === $.LINK ||
+ tn === $.META ||
+ tn === $.NOFRAMES ||
+ tn === $.SCRIPT ||
+ tn === $.STYLE ||
+ tn === $.TEMPLATE ||
+ tn === $.TITLE
+ ) {
+ p._err(ERR.abandonedHeadElementChild);
+ p.openElements.push(p.headElement);
+ startTagInHead(p, token);
+ p.openElements.remove(p.headElement);
+ } else if (tn === $.HEAD) {
+ p._err(ERR.misplacedStartTagForHeadElement);
+ } else {
+ tokenAfterHead(p, token);
+ }
+}
+
+function endTagAfterHead(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.BODY || tn === $.HTML || tn === $.BR) {
+ tokenAfterHead(p, token);
+ } else if (tn === $.TEMPLATE) {
+ endTagInHead(p, token);
+ } else {
+ p._err(ERR.endTagWithoutMatchingOpenElement);
+ }
+}
+
+function tokenAfterHead(p, token) {
+ p._insertFakeElement($.BODY);
+ p.insertionMode = IN_BODY_MODE;
+ p._processToken(token);
+}
+
+// The "in body" insertion mode
+//------------------------------------------------------------------
+function whitespaceCharacterInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertCharacters(token);
+}
+
+function characterInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertCharacters(token);
+ p.framesetOk = false;
+}
+
+function htmlStartTagInBody(p, token) {
+ if (p.openElements.tmplCount === 0) {
+ p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs);
+ }
+}
+
+function bodyStartTagInBody(p, token) {
+ const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
+
+ if (bodyElement && p.openElements.tmplCount === 0) {
+ p.framesetOk = false;
+ p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
+ }
+}
+
+function framesetStartTagInBody(p, token) {
+ const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
+
+ if (p.framesetOk && bodyElement) {
+ p.treeAdapter.detachNode(bodyElement);
+ p.openElements.popAllUpToHtmlElement();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_FRAMESET_MODE;
+ }
+}
+
+function addressStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function numberedHeaderStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ const tn = p.openElements.currentTagName;
+
+ if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
+ p.openElements.pop();
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function preStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+ //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
+ //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.)
+ p.skipNextNewLine = true;
+ p.framesetOk = false;
+}
+
+function formStartTagInBody(p, token) {
+ const inTemplate = p.openElements.tmplCount > 0;
+
+ if (!p.formElement || inTemplate) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+
+ if (!inTemplate) {
+ p.formElement = p.openElements.current;
+ }
+ }
+}
+
+function listItemStartTagInBody(p, token) {
+ p.framesetOk = false;
+
+ const tn = token.tagName;
+
+ for (let i = p.openElements.stackTop; i >= 0; i--) {
+ const element = p.openElements.items[i];
+ const elementTn = p.treeAdapter.getTagName(element);
+ let closeTn = null;
+
+ if (tn === $.LI && elementTn === $.LI) {
+ closeTn = $.LI;
+ } else if ((tn === $.DD || tn === $.DT) && (elementTn === $.DD || elementTn === $.DT)) {
+ closeTn = elementTn;
+ }
+
+ if (closeTn) {
+ p.openElements.generateImpliedEndTagsWithExclusion(closeTn);
+ p.openElements.popUntilTagNamePopped(closeTn);
+ break;
+ }
+
+ if (elementTn !== $.ADDRESS && elementTn !== $.DIV && elementTn !== $.P && p._isSpecialElement(element)) {
+ break;
+ }
+ }
+
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function plaintextStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+ p.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
+}
+
+function buttonStartTagInBody(p, token) {
+ if (p.openElements.hasInScope($.BUTTON)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped($.BUTTON);
+ }
+
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+ p.framesetOk = false;
+}
+
+function aStartTagInBody(p, token) {
+ const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName($.A);
+
+ if (activeElementEntry) {
+ callAdoptionAgency(p, token);
+ p.openElements.remove(activeElementEntry.element);
+ p.activeFormattingElements.removeEntry(activeElementEntry);
+ }
+
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
+}
+
+function bStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
+}
+
+function nobrStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+
+ if (p.openElements.hasInScope($.NOBR)) {
+ callAdoptionAgency(p, token);
+ p._reconstructActiveFormattingElements();
+ }
+
+ p._insertElement(token, NS.HTML);
+ p.activeFormattingElements.pushElement(p.openElements.current, token);
+}
+
+function appletStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+ p.activeFormattingElements.insertMarker();
+ p.framesetOk = false;
+}
+
+function tableStartTagInBody(p, token) {
+ if (
+ p.treeAdapter.getDocumentMode(p.document) !== HTML.DOCUMENT_MODE.QUIRKS &&
+ p.openElements.hasInButtonScope($.P)
+ ) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+ p.framesetOk = false;
+ p.insertionMode = IN_TABLE_MODE;
+}
+
+function areaStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._appendElement(token, NS.HTML);
+ p.framesetOk = false;
+ token.ackSelfClosing = true;
+}
+
+function inputStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._appendElement(token, NS.HTML);
+
+ const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
+
+ if (!inputType || inputType.toLowerCase() !== HIDDEN_INPUT_TYPE) {
+ p.framesetOk = false;
+ }
+
+ token.ackSelfClosing = true;
+}
+
+function paramStartTagInBody(p, token) {
+ p._appendElement(token, NS.HTML);
+ token.ackSelfClosing = true;
+}
+
+function hrStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._appendElement(token, NS.HTML);
+ p.framesetOk = false;
+ token.ackSelfClosing = true;
+}
+
+function imageStartTagInBody(p, token) {
+ token.tagName = $.IMG;
+ areaStartTagInBody(p, token);
+}
+
+function textareaStartTagInBody(p, token) {
+ p._insertElement(token, NS.HTML);
+ //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
+ //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.)
+ p.skipNextNewLine = true;
+ p.tokenizer.state = Tokenizer.MODE.RCDATA;
+ p.originalInsertionMode = p.insertionMode;
+ p.framesetOk = false;
+ p.insertionMode = TEXT_MODE;
+}
+
+function xmpStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._reconstructActiveFormattingElements();
+ p.framesetOk = false;
+ p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
+}
+
+function iframeStartTagInBody(p, token) {
+ p.framesetOk = false;
+ p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
+}
+
+//NOTE: here we assume that we always act as an user agent with enabled plugins, so we parse
+//<noembed> as a rawtext.
+function noembedStartTagInBody(p, token) {
+ p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
+}
+
+function selectStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+ p.framesetOk = false;
+
+ if (
+ p.insertionMode === IN_TABLE_MODE ||
+ p.insertionMode === IN_CAPTION_MODE ||
+ p.insertionMode === IN_TABLE_BODY_MODE ||
+ p.insertionMode === IN_ROW_MODE ||
+ p.insertionMode === IN_CELL_MODE
+ ) {
+ p.insertionMode = IN_SELECT_IN_TABLE_MODE;
+ } else {
+ p.insertionMode = IN_SELECT_MODE;
+ }
+}
+
+function optgroupStartTagInBody(p, token) {
+ if (p.openElements.currentTagName === $.OPTION) {
+ p.openElements.pop();
+ }
+
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+}
+
+function rbStartTagInBody(p, token) {
+ if (p.openElements.hasInScope($.RUBY)) {
+ p.openElements.generateImpliedEndTags();
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function rtStartTagInBody(p, token) {
+ if (p.openElements.hasInScope($.RUBY)) {
+ p.openElements.generateImpliedEndTagsWithExclusion($.RTC);
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function menuStartTagInBody(p, token) {
+ if (p.openElements.hasInButtonScope($.P)) {
+ p._closePElement();
+ }
+
+ p._insertElement(token, NS.HTML);
+}
+
+function mathStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+
+ foreignContent.adjustTokenMathMLAttrs(token);
+ foreignContent.adjustTokenXMLAttrs(token);
+
+ if (token.selfClosing) {
+ p._appendElement(token, NS.MATHML);
+ } else {
+ p._insertElement(token, NS.MATHML);
+ }
+
+ token.ackSelfClosing = true;
+}
+
+function svgStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+
+ foreignContent.adjustTokenSVGAttrs(token);
+ foreignContent.adjustTokenXMLAttrs(token);
+
+ if (token.selfClosing) {
+ p._appendElement(token, NS.SVG);
+ } else {
+ p._insertElement(token, NS.SVG);
+ }
+
+ token.ackSelfClosing = true;
+}
+
+function genericStartTagInBody(p, token) {
+ p._reconstructActiveFormattingElements();
+ p._insertElement(token, NS.HTML);
+}
+
+//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
+//It's faster than using dictionary.
+function startTagInBody(p, token) {
+ const tn = token.tagName;
+
+ switch (tn.length) {
+ case 1:
+ if (tn === $.I || tn === $.S || tn === $.B || tn === $.U) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.P) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.A) {
+ aStartTagInBody(p, token);
+ } else {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 2:
+ if (tn === $.DL || tn === $.OL || tn === $.UL) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
+ numberedHeaderStartTagInBody(p, token);
+ } else if (tn === $.LI || tn === $.DD || tn === $.DT) {
+ listItemStartTagInBody(p, token);
+ } else if (tn === $.EM || tn === $.TT) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.BR) {
+ areaStartTagInBody(p, token);
+ } else if (tn === $.HR) {
+ hrStartTagInBody(p, token);
+ } else if (tn === $.RB) {
+ rbStartTagInBody(p, token);
+ } else if (tn === $.RT || tn === $.RP) {
+ rtStartTagInBody(p, token);
+ } else if (tn !== $.TH && tn !== $.TD && tn !== $.TR) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 3:
+ if (tn === $.DIV || tn === $.DIR || tn === $.NAV) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.PRE) {
+ preStartTagInBody(p, token);
+ } else if (tn === $.BIG) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.IMG || tn === $.WBR) {
+ areaStartTagInBody(p, token);
+ } else if (tn === $.XMP) {
+ xmpStartTagInBody(p, token);
+ } else if (tn === $.SVG) {
+ svgStartTagInBody(p, token);
+ } else if (tn === $.RTC) {
+ rbStartTagInBody(p, token);
+ } else if (tn !== $.COL) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 4:
+ if (tn === $.HTML) {
+ htmlStartTagInBody(p, token);
+ } else if (tn === $.BASE || tn === $.LINK || tn === $.META) {
+ startTagInHead(p, token);
+ } else if (tn === $.BODY) {
+ bodyStartTagInBody(p, token);
+ } else if (tn === $.MAIN || tn === $.MENU) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.FORM) {
+ formStartTagInBody(p, token);
+ } else if (tn === $.CODE || tn === $.FONT) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.NOBR) {
+ nobrStartTagInBody(p, token);
+ } else if (tn === $.AREA) {
+ areaStartTagInBody(p, token);
+ } else if (tn === $.MATH) {
+ mathStartTagInBody(p, token);
+ } else if (tn === $.MENU) {
+ menuStartTagInBody(p, token);
+ } else if (tn !== $.HEAD) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 5:
+ if (tn === $.STYLE || tn === $.TITLE) {
+ startTagInHead(p, token);
+ } else if (tn === $.ASIDE) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.SMALL) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.TABLE) {
+ tableStartTagInBody(p, token);
+ } else if (tn === $.EMBED) {
+ areaStartTagInBody(p, token);
+ } else if (tn === $.INPUT) {
+ inputStartTagInBody(p, token);
+ } else if (tn === $.PARAM || tn === $.TRACK) {
+ paramStartTagInBody(p, token);
+ } else if (tn === $.IMAGE) {
+ imageStartTagInBody(p, token);
+ } else if (tn !== $.FRAME && tn !== $.TBODY && tn !== $.TFOOT && tn !== $.THEAD) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 6:
+ if (tn === $.SCRIPT) {
+ startTagInHead(p, token);
+ } else if (
+ tn === $.CENTER ||
+ tn === $.FIGURE ||
+ tn === $.FOOTER ||
+ tn === $.HEADER ||
+ tn === $.HGROUP ||
+ tn === $.DIALOG
+ ) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.BUTTON) {
+ buttonStartTagInBody(p, token);
+ } else if (tn === $.STRIKE || tn === $.STRONG) {
+ bStartTagInBody(p, token);
+ } else if (tn === $.APPLET || tn === $.OBJECT) {
+ appletStartTagInBody(p, token);
+ } else if (tn === $.KEYGEN) {
+ areaStartTagInBody(p, token);
+ } else if (tn === $.SOURCE) {
+ paramStartTagInBody(p, token);
+ } else if (tn === $.IFRAME) {
+ iframeStartTagInBody(p, token);
+ } else if (tn === $.SELECT) {
+ selectStartTagInBody(p, token);
+ } else if (tn === $.OPTION) {
+ optgroupStartTagInBody(p, token);
+ } else {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 7:
+ if (tn === $.BGSOUND) {
+ startTagInHead(p, token);
+ } else if (
+ tn === $.DETAILS ||
+ tn === $.ADDRESS ||
+ tn === $.ARTICLE ||
+ tn === $.SECTION ||
+ tn === $.SUMMARY
+ ) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.LISTING) {
+ preStartTagInBody(p, token);
+ } else if (tn === $.MARQUEE) {
+ appletStartTagInBody(p, token);
+ } else if (tn === $.NOEMBED) {
+ noembedStartTagInBody(p, token);
+ } else if (tn !== $.CAPTION) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 8:
+ if (tn === $.BASEFONT) {
+ startTagInHead(p, token);
+ } else if (tn === $.FRAMESET) {
+ framesetStartTagInBody(p, token);
+ } else if (tn === $.FIELDSET) {
+ addressStartTagInBody(p, token);
+ } else if (tn === $.TEXTAREA) {
+ textareaStartTagInBody(p, token);
+ } else if (tn === $.TEMPLATE) {
+ startTagInHead(p, token);
+ } else if (tn === $.NOSCRIPT) {
+ if (p.options.scriptingEnabled) {
+ noembedStartTagInBody(p, token);
+ } else {
+ genericStartTagInBody(p, token);
+ }
+ } else if (tn === $.OPTGROUP) {
+ optgroupStartTagInBody(p, token);
+ } else if (tn !== $.COLGROUP) {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 9:
+ if (tn === $.PLAINTEXT) {
+ plaintextStartTagInBody(p, token);
+ } else {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ case 10:
+ if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) {
+ addressStartTagInBody(p, token);
+ } else {
+ genericStartTagInBody(p, token);
+ }
+
+ break;
+
+ default:
+ genericStartTagInBody(p, token);
+ }
+}
+
+function bodyEndTagInBody(p) {
+ if (p.openElements.hasInScope($.BODY)) {
+ p.insertionMode = AFTER_BODY_MODE;
+ }
+}
+
+function htmlEndTagInBody(p, token) {
+ if (p.openElements.hasInScope($.BODY)) {
+ p.insertionMode = AFTER_BODY_MODE;
+ p._processToken(token);
+ }
+}
+
+function addressEndTagInBody(p, token) {
+ const tn = token.tagName;
+
+ if (p.openElements.hasInScope(tn)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped(tn);
+ }
+}
+
+function formEndTagInBody(p) {
+ const inTemplate = p.openElements.tmplCount > 0;
+ const formElement = p.formElement;
+
+ if (!inTemplate) {
+ p.formElement = null;
+ }
+
+ if ((formElement || inTemplate) && p.openElements.hasInScope($.FORM)) {
+ p.openElements.generateImpliedEndTags();
+
+ if (inTemplate) {
+ p.openElements.popUntilTagNamePopped($.FORM);
+ } else {
+ p.openElements.remove(formElement);
+ }
+ }
+}
+
+function pEndTagInBody(p) {
+ if (!p.openElements.hasInButtonScope($.P)) {
+ p._insertFakeElement($.P);
+ }
+
+ p._closePElement();
+}
+
+function liEndTagInBody(p) {
+ if (p.openElements.hasInListItemScope($.LI)) {
+ p.openElements.generateImpliedEndTagsWithExclusion($.LI);
+ p.openElements.popUntilTagNamePopped($.LI);
+ }
+}
+
+function ddEndTagInBody(p, token) {
+ const tn = token.tagName;
+
+ if (p.openElements.hasInScope(tn)) {
+ p.openElements.generateImpliedEndTagsWithExclusion(tn);
+ p.openElements.popUntilTagNamePopped(tn);
+ }
+}
+
+function numberedHeaderEndTagInBody(p) {
+ if (p.openElements.hasNumberedHeaderInScope()) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilNumberedHeaderPopped();
+ }
+}
+
+function appletEndTagInBody(p, token) {
+ const tn = token.tagName;
+
+ if (p.openElements.hasInScope(tn)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped(tn);
+ p.activeFormattingElements.clearToLastMarker();
+ }
+}
+
+function brEndTagInBody(p) {
+ p._reconstructActiveFormattingElements();
+ p._insertFakeElement($.BR);
+ p.openElements.pop();
+ p.framesetOk = false;
+}
+
+function genericEndTagInBody(p, token) {
+ const tn = token.tagName;
+
+ for (let i = p.openElements.stackTop; i > 0; i--) {
+ const element = p.openElements.items[i];
+
+ if (p.treeAdapter.getTagName(element) === tn) {
+ p.openElements.generateImpliedEndTagsWithExclusion(tn);
+ p.openElements.popUntilElementPopped(element);
+ break;
+ }
+
+ if (p._isSpecialElement(element)) {
+ break;
+ }
+ }
+}
+
+//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
+//It's faster than using dictionary.
+function endTagInBody(p, token) {
+ const tn = token.tagName;
+
+ switch (tn.length) {
+ case 1:
+ if (tn === $.A || tn === $.B || tn === $.I || tn === $.S || tn === $.U) {
+ callAdoptionAgency(p, token);
+ } else if (tn === $.P) {
+ pEndTagInBody(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 2:
+ if (tn === $.DL || tn === $.UL || tn === $.OL) {
+ addressEndTagInBody(p, token);
+ } else if (tn === $.LI) {
+ liEndTagInBody(p, token);
+ } else if (tn === $.DD || tn === $.DT) {
+ ddEndTagInBody(p, token);
+ } else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
+ numberedHeaderEndTagInBody(p, token);
+ } else if (tn === $.BR) {
+ brEndTagInBody(p, token);
+ } else if (tn === $.EM || tn === $.TT) {
+ callAdoptionAgency(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 3:
+ if (tn === $.BIG) {
+ callAdoptionAgency(p, token);
+ } else if (tn === $.DIR || tn === $.DIV || tn === $.NAV || tn === $.PRE) {
+ addressEndTagInBody(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 4:
+ if (tn === $.BODY) {
+ bodyEndTagInBody(p, token);
+ } else if (tn === $.HTML) {
+ htmlEndTagInBody(p, token);
+ } else if (tn === $.FORM) {
+ formEndTagInBody(p, token);
+ } else if (tn === $.CODE || tn === $.FONT || tn === $.NOBR) {
+ callAdoptionAgency(p, token);
+ } else if (tn === $.MAIN || tn === $.MENU) {
+ addressEndTagInBody(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 5:
+ if (tn === $.ASIDE) {
+ addressEndTagInBody(p, token);
+ } else if (tn === $.SMALL) {
+ callAdoptionAgency(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 6:
+ if (
+ tn === $.CENTER ||
+ tn === $.FIGURE ||
+ tn === $.FOOTER ||
+ tn === $.HEADER ||
+ tn === $.HGROUP ||
+ tn === $.DIALOG
+ ) {
+ addressEndTagInBody(p, token);
+ } else if (tn === $.APPLET || tn === $.OBJECT) {
+ appletEndTagInBody(p, token);
+ } else if (tn === $.STRIKE || tn === $.STRONG) {
+ callAdoptionAgency(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 7:
+ if (
+ tn === $.ADDRESS ||
+ tn === $.ARTICLE ||
+ tn === $.DETAILS ||
+ tn === $.SECTION ||
+ tn === $.SUMMARY ||
+ tn === $.LISTING
+ ) {
+ addressEndTagInBody(p, token);
+ } else if (tn === $.MARQUEE) {
+ appletEndTagInBody(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 8:
+ if (tn === $.FIELDSET) {
+ addressEndTagInBody(p, token);
+ } else if (tn === $.TEMPLATE) {
+ endTagInHead(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ case 10:
+ if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) {
+ addressEndTagInBody(p, token);
+ } else {
+ genericEndTagInBody(p, token);
+ }
+
+ break;
+
+ default:
+ genericEndTagInBody(p, token);
+ }
+}
+
+function eofInBody(p, token) {
+ if (p.tmplInsertionModeStackTop > -1) {
+ eofInTemplate(p, token);
+ } else {
+ p.stopped = true;
+ }
+}
+
+// The "text" insertion mode
+//------------------------------------------------------------------
+function endTagInText(p, token) {
+ if (token.tagName === $.SCRIPT) {
+ p.pendingScript = p.openElements.current;
+ }
+
+ p.openElements.pop();
+ p.insertionMode = p.originalInsertionMode;
+}
+
+function eofInText(p, token) {
+ p._err(ERR.eofInElementThatCanContainOnlyText);
+ p.openElements.pop();
+ p.insertionMode = p.originalInsertionMode;
+ p._processToken(token);
+}
+
+// The "in table" insertion mode
+//------------------------------------------------------------------
+function characterInTable(p, token) {
+ const curTn = p.openElements.currentTagName;
+
+ if (curTn === $.TABLE || curTn === $.TBODY || curTn === $.TFOOT || curTn === $.THEAD || curTn === $.TR) {
+ p.pendingCharacterTokens = [];
+ p.hasNonWhitespacePendingCharacterToken = false;
+ p.originalInsertionMode = p.insertionMode;
+ p.insertionMode = IN_TABLE_TEXT_MODE;
+ p._processToken(token);
+ } else {
+ tokenInTable(p, token);
+ }
+}
+
+function captionStartTagInTable(p, token) {
+ p.openElements.clearBackToTableContext();
+ p.activeFormattingElements.insertMarker();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_CAPTION_MODE;
+}
+
+function colgroupStartTagInTable(p, token) {
+ p.openElements.clearBackToTableContext();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_COLUMN_GROUP_MODE;
+}
+
+function colStartTagInTable(p, token) {
+ p.openElements.clearBackToTableContext();
+ p._insertFakeElement($.COLGROUP);
+ p.insertionMode = IN_COLUMN_GROUP_MODE;
+ p._processToken(token);
+}
+
+function tbodyStartTagInTable(p, token) {
+ p.openElements.clearBackToTableContext();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_TABLE_BODY_MODE;
+}
+
+function tdStartTagInTable(p, token) {
+ p.openElements.clearBackToTableContext();
+ p._insertFakeElement($.TBODY);
+ p.insertionMode = IN_TABLE_BODY_MODE;
+ p._processToken(token);
+}
+
+function tableStartTagInTable(p, token) {
+ if (p.openElements.hasInTableScope($.TABLE)) {
+ p.openElements.popUntilTagNamePopped($.TABLE);
+ p._resetInsertionMode();
+ p._processToken(token);
+ }
+}
+
+function inputStartTagInTable(p, token) {
+ const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
+
+ if (inputType && inputType.toLowerCase() === HIDDEN_INPUT_TYPE) {
+ p._appendElement(token, NS.HTML);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ token.ackSelfClosing = true;
+}
+
+function formStartTagInTable(p, token) {
+ if (!p.formElement && p.openElements.tmplCount === 0) {
+ p._insertElement(token, NS.HTML);
+ p.formElement = p.openElements.current;
+ p.openElements.pop();
+ }
+}
+
+function startTagInTable(p, token) {
+ const tn = token.tagName;
+
+ switch (tn.length) {
+ case 2:
+ if (tn === $.TD || tn === $.TH || tn === $.TR) {
+ tdStartTagInTable(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 3:
+ if (tn === $.COL) {
+ colStartTagInTable(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 4:
+ if (tn === $.FORM) {
+ formStartTagInTable(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 5:
+ if (tn === $.TABLE) {
+ tableStartTagInTable(p, token);
+ } else if (tn === $.STYLE) {
+ startTagInHead(p, token);
+ } else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
+ tbodyStartTagInTable(p, token);
+ } else if (tn === $.INPUT) {
+ inputStartTagInTable(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 6:
+ if (tn === $.SCRIPT) {
+ startTagInHead(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 7:
+ if (tn === $.CAPTION) {
+ captionStartTagInTable(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ case 8:
+ if (tn === $.COLGROUP) {
+ colgroupStartTagInTable(p, token);
+ } else if (tn === $.TEMPLATE) {
+ startTagInHead(p, token);
+ } else {
+ tokenInTable(p, token);
+ }
+
+ break;
+
+ default:
+ tokenInTable(p, token);
+ }
+}
+
+function endTagInTable(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TABLE) {
+ if (p.openElements.hasInTableScope($.TABLE)) {
+ p.openElements.popUntilTagNamePopped($.TABLE);
+ p._resetInsertionMode();
+ }
+ } else if (tn === $.TEMPLATE) {
+ endTagInHead(p, token);
+ } else if (
+ tn !== $.BODY &&
+ tn !== $.CAPTION &&
+ tn !== $.COL &&
+ tn !== $.COLGROUP &&
+ tn !== $.HTML &&
+ tn !== $.TBODY &&
+ tn !== $.TD &&
+ tn !== $.TFOOT &&
+ tn !== $.TH &&
+ tn !== $.THEAD &&
+ tn !== $.TR
+ ) {
+ tokenInTable(p, token);
+ }
+}
+
+function tokenInTable(p, token) {
+ const savedFosterParentingState = p.fosterParentingEnabled;
+
+ p.fosterParentingEnabled = true;
+ p._processTokenInBodyMode(token);
+ p.fosterParentingEnabled = savedFosterParentingState;
+}
+
+// The "in table text" insertion mode
+//------------------------------------------------------------------
+function whitespaceCharacterInTableText(p, token) {
+ p.pendingCharacterTokens.push(token);
+}
+
+function characterInTableText(p, token) {
+ p.pendingCharacterTokens.push(token);
+ p.hasNonWhitespacePendingCharacterToken = true;
+}
+
+function tokenInTableText(p, token) {
+ let i = 0;
+
+ if (p.hasNonWhitespacePendingCharacterToken) {
+ for (; i < p.pendingCharacterTokens.length; i++) {
+ tokenInTable(p, p.pendingCharacterTokens[i]);
+ }
+ } else {
+ for (; i < p.pendingCharacterTokens.length; i++) {
+ p._insertCharacters(p.pendingCharacterTokens[i]);
+ }
+ }
+
+ p.insertionMode = p.originalInsertionMode;
+ p._processToken(token);
+}
+
+// The "in caption" insertion mode
+//------------------------------------------------------------------
+function startTagInCaption(p, token) {
+ const tn = token.tagName;
+
+ if (
+ tn === $.CAPTION ||
+ tn === $.COL ||
+ tn === $.COLGROUP ||
+ tn === $.TBODY ||
+ tn === $.TD ||
+ tn === $.TFOOT ||
+ tn === $.TH ||
+ tn === $.THEAD ||
+ tn === $.TR
+ ) {
+ if (p.openElements.hasInTableScope($.CAPTION)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped($.CAPTION);
+ p.activeFormattingElements.clearToLastMarker();
+ p.insertionMode = IN_TABLE_MODE;
+ p._processToken(token);
+ }
+ } else {
+ startTagInBody(p, token);
+ }
+}
+
+function endTagInCaption(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.CAPTION || tn === $.TABLE) {
+ if (p.openElements.hasInTableScope($.CAPTION)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped($.CAPTION);
+ p.activeFormattingElements.clearToLastMarker();
+ p.insertionMode = IN_TABLE_MODE;
+
+ if (tn === $.TABLE) {
+ p._processToken(token);
+ }
+ }
+ } else if (
+ tn !== $.BODY &&
+ tn !== $.COL &&
+ tn !== $.COLGROUP &&
+ tn !== $.HTML &&
+ tn !== $.TBODY &&
+ tn !== $.TD &&
+ tn !== $.TFOOT &&
+ tn !== $.TH &&
+ tn !== $.THEAD &&
+ tn !== $.TR
+ ) {
+ endTagInBody(p, token);
+ }
+}
+
+// The "in column group" insertion mode
+//------------------------------------------------------------------
+function startTagInColumnGroup(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.COL) {
+ p._appendElement(token, NS.HTML);
+ token.ackSelfClosing = true;
+ } else if (tn === $.TEMPLATE) {
+ startTagInHead(p, token);
+ } else {
+ tokenInColumnGroup(p, token);
+ }
+}
+
+function endTagInColumnGroup(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.COLGROUP) {
+ if (p.openElements.currentTagName === $.COLGROUP) {
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_MODE;
+ }
+ } else if (tn === $.TEMPLATE) {
+ endTagInHead(p, token);
+ } else if (tn !== $.COL) {
+ tokenInColumnGroup(p, token);
+ }
+}
+
+function tokenInColumnGroup(p, token) {
+ if (p.openElements.currentTagName === $.COLGROUP) {
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_MODE;
+ p._processToken(token);
+ }
+}
+
+// The "in table body" insertion mode
+//------------------------------------------------------------------
+function startTagInTableBody(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TR) {
+ p.openElements.clearBackToTableBodyContext();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_ROW_MODE;
+ } else if (tn === $.TH || tn === $.TD) {
+ p.openElements.clearBackToTableBodyContext();
+ p._insertFakeElement($.TR);
+ p.insertionMode = IN_ROW_MODE;
+ p._processToken(token);
+ } else if (
+ tn === $.CAPTION ||
+ tn === $.COL ||
+ tn === $.COLGROUP ||
+ tn === $.TBODY ||
+ tn === $.TFOOT ||
+ tn === $.THEAD
+ ) {
+ if (p.openElements.hasTableBodyContextInTableScope()) {
+ p.openElements.clearBackToTableBodyContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_MODE;
+ p._processToken(token);
+ }
+ } else {
+ startTagInTable(p, token);
+ }
+}
+
+function endTagInTableBody(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
+ if (p.openElements.hasInTableScope(tn)) {
+ p.openElements.clearBackToTableBodyContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_MODE;
+ }
+ } else if (tn === $.TABLE) {
+ if (p.openElements.hasTableBodyContextInTableScope()) {
+ p.openElements.clearBackToTableBodyContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_MODE;
+ p._processToken(token);
+ }
+ } else if (
+ (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP) ||
+ (tn !== $.HTML && tn !== $.TD && tn !== $.TH && tn !== $.TR)
+ ) {
+ endTagInTable(p, token);
+ }
+}
+
+// The "in row" insertion mode
+//------------------------------------------------------------------
+function startTagInRow(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TH || tn === $.TD) {
+ p.openElements.clearBackToTableRowContext();
+ p._insertElement(token, NS.HTML);
+ p.insertionMode = IN_CELL_MODE;
+ p.activeFormattingElements.insertMarker();
+ } else if (
+ tn === $.CAPTION ||
+ tn === $.COL ||
+ tn === $.COLGROUP ||
+ tn === $.TBODY ||
+ tn === $.TFOOT ||
+ tn === $.THEAD ||
+ tn === $.TR
+ ) {
+ if (p.openElements.hasInTableScope($.TR)) {
+ p.openElements.clearBackToTableRowContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_BODY_MODE;
+ p._processToken(token);
+ }
+ } else {
+ startTagInTable(p, token);
+ }
+}
+
+function endTagInRow(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TR) {
+ if (p.openElements.hasInTableScope($.TR)) {
+ p.openElements.clearBackToTableRowContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_BODY_MODE;
+ }
+ } else if (tn === $.TABLE) {
+ if (p.openElements.hasInTableScope($.TR)) {
+ p.openElements.clearBackToTableRowContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_BODY_MODE;
+ p._processToken(token);
+ }
+ } else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
+ if (p.openElements.hasInTableScope(tn) || p.openElements.hasInTableScope($.TR)) {
+ p.openElements.clearBackToTableRowContext();
+ p.openElements.pop();
+ p.insertionMode = IN_TABLE_BODY_MODE;
+ p._processToken(token);
+ }
+ } else if (
+ (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP) ||
+ (tn !== $.HTML && tn !== $.TD && tn !== $.TH)
+ ) {
+ endTagInTable(p, token);
+ }
+}
+
+// The "in cell" insertion mode
+//------------------------------------------------------------------
+function startTagInCell(p, token) {
+ const tn = token.tagName;
+
+ if (
+ tn === $.CAPTION ||
+ tn === $.COL ||
+ tn === $.COLGROUP ||
+ tn === $.TBODY ||
+ tn === $.TD ||
+ tn === $.TFOOT ||
+ tn === $.TH ||
+ tn === $.THEAD ||
+ tn === $.TR
+ ) {
+ if (p.openElements.hasInTableScope($.TD) || p.openElements.hasInTableScope($.TH)) {
+ p._closeTableCell();
+ p._processToken(token);
+ }
+ } else {
+ startTagInBody(p, token);
+ }
+}
+
+function endTagInCell(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.TD || tn === $.TH) {
+ if (p.openElements.hasInTableScope(tn)) {
+ p.openElements.generateImpliedEndTags();
+ p.openElements.popUntilTagNamePopped(tn);
+ p.activeFormattingElements.clearToLastMarker();
+ p.insertionMode = IN_ROW_MODE;
+ }
+ } else if (tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR) {
+ if (p.openElements.hasInTableScope(tn)) {
+ p._closeTableCell();
+ p._processToken(token);
+ }
+ } else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML) {
+ endTagInBody(p, token);
+ }
+}
+
+// The "in select" insertion mode
+//------------------------------------------------------------------
+function startTagInSelect(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.OPTION) {
+ if (p.openElements.currentTagName === $.OPTION) {
+ p.openElements.pop();
+ }
+
+ p._insertElement(token, NS.HTML);
+ } else if (tn === $.OPTGROUP) {
+ if (p.openElements.currentTagName === $.OPTION) {
+ p.openElements.pop();
+ }
+
+ if (p.openElements.currentTagName === $.OPTGROUP) {
+ p.openElements.pop();
+ }
+
+ p._insertElement(token, NS.HTML);
+ } else if (tn === $.INPUT || tn === $.KEYGEN || tn === $.TEXTAREA || tn === $.SELECT) {
+ if (p.openElements.hasInSelectScope($.SELECT)) {
+ p.openElements.popUntilTagNamePopped($.SELECT);
+ p._resetInsertionMode();
+
+ if (tn !== $.SELECT) {
+ p._processToken(token);
+ }
+ }
+ } else if (tn === $.SCRIPT || tn === $.TEMPLATE) {
+ startTagInHead(p, token);
+ }
+}
+
+function endTagInSelect(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.OPTGROUP) {
+ const prevOpenElement = p.openElements.items[p.openElements.stackTop - 1];
+ const prevOpenElementTn = prevOpenElement && p.treeAdapter.getTagName(prevOpenElement);
+
+ if (p.openElements.currentTagName === $.OPTION && prevOpenElementTn === $.OPTGROUP) {
+ p.openElements.pop();
+ }
+
+ if (p.openElements.currentTagName === $.OPTGROUP) {
+ p.openElements.pop();
+ }
+ } else if (tn === $.OPTION) {
+ if (p.openElements.currentTagName === $.OPTION) {
+ p.openElements.pop();
+ }
+ } else if (tn === $.SELECT && p.openElements.hasInSelectScope($.SELECT)) {
+ p.openElements.popUntilTagNamePopped($.SELECT);
+ p._resetInsertionMode();
+ } else if (tn === $.TEMPLATE) {
+ endTagInHead(p, token);
+ }
+}
+
+//12.2.5.4.17 The "in select in table" insertion mode
+//------------------------------------------------------------------
+function startTagInSelectInTable(p, token) {
+ const tn = token.tagName;
+
+ if (
+ tn === $.CAPTION ||
+ tn === $.TABLE ||
+ tn === $.TBODY ||
+ tn === $.TFOOT ||
+ tn === $.THEAD ||
+ tn === $.TR ||
+ tn === $.TD ||
+ tn === $.TH
+ ) {
+ p.openElements.popUntilTagNamePopped($.SELECT);
+ p._resetInsertionMode();
+ p._processToken(token);
+ } else {
+ startTagInSelect(p, token);
+ }
+}
+
+function endTagInSelectInTable(p, token) {
+ const tn = token.tagName;
+
+ if (
+ tn === $.CAPTION ||
+ tn === $.TABLE ||
+ tn === $.TBODY ||
+ tn === $.TFOOT ||
+ tn === $.THEAD ||
+ tn === $.TR ||
+ tn === $.TD ||
+ tn === $.TH
+ ) {
+ if (p.openElements.hasInTableScope(tn)) {
+ p.openElements.popUntilTagNamePopped($.SELECT);
+ p._resetInsertionMode();
+ p._processToken(token);
+ }
+ } else {
+ endTagInSelect(p, token);
+ }
+}
+
+// The "in template" insertion mode
+//------------------------------------------------------------------
+function startTagInTemplate(p, token) {
+ const tn = token.tagName;
+
+ if (
+ tn === $.BASE ||
+ tn === $.BASEFONT ||
+ tn === $.BGSOUND ||
+ tn === $.LINK ||
+ tn === $.META ||
+ tn === $.NOFRAMES ||
+ tn === $.SCRIPT ||
+ tn === $.STYLE ||
+ tn === $.TEMPLATE ||
+ tn === $.TITLE
+ ) {
+ startTagInHead(p, token);
+ } else {
+ const newInsertionMode = TEMPLATE_INSERTION_MODE_SWITCH_MAP[tn] || IN_BODY_MODE;
+
+ p._popTmplInsertionMode();
+ p._pushTmplInsertionMode(newInsertionMode);
+ p.insertionMode = newInsertionMode;
+ p._processToken(token);
+ }
+}
+
+function endTagInTemplate(p, token) {
+ if (token.tagName === $.TEMPLATE) {
+ endTagInHead(p, token);
+ }
+}
+
+function eofInTemplate(p, token) {
+ if (p.openElements.tmplCount > 0) {
+ p.openElements.popUntilTagNamePopped($.TEMPLATE);
+ p.activeFormattingElements.clearToLastMarker();
+ p._popTmplInsertionMode();
+ p._resetInsertionMode();
+ p._processToken(token);
+ } else {
+ p.stopped = true;
+ }
+}
+
+// The "after body" insertion mode
+//------------------------------------------------------------------
+function startTagAfterBody(p, token) {
+ if (token.tagName === $.HTML) {
+ startTagInBody(p, token);
+ } else {
+ tokenAfterBody(p, token);
+ }
+}
+
+function endTagAfterBody(p, token) {
+ if (token.tagName === $.HTML) {
+ if (!p.fragmentContext) {
+ p.insertionMode = AFTER_AFTER_BODY_MODE;
+ }
+ } else {
+ tokenAfterBody(p, token);
+ }
+}
+
+function tokenAfterBody(p, token) {
+ p.insertionMode = IN_BODY_MODE;
+ p._processToken(token);
+}
+
+// The "in frameset" insertion mode
+//------------------------------------------------------------------
+function startTagInFrameset(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.FRAMESET) {
+ p._insertElement(token, NS.HTML);
+ } else if (tn === $.FRAME) {
+ p._appendElement(token, NS.HTML);
+ token.ackSelfClosing = true;
+ } else if (tn === $.NOFRAMES) {
+ startTagInHead(p, token);
+ }
+}
+
+function endTagInFrameset(p, token) {
+ if (token.tagName === $.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) {
+ p.openElements.pop();
+
+ if (!p.fragmentContext && p.openElements.currentTagName !== $.FRAMESET) {
+ p.insertionMode = AFTER_FRAMESET_MODE;
+ }
+ }
+}
+
+// The "after frameset" insertion mode
+//------------------------------------------------------------------
+function startTagAfterFrameset(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.NOFRAMES) {
+ startTagInHead(p, token);
+ }
+}
+
+function endTagAfterFrameset(p, token) {
+ if (token.tagName === $.HTML) {
+ p.insertionMode = AFTER_AFTER_FRAMESET_MODE;
+ }
+}
+
+// The "after after body" insertion mode
+//------------------------------------------------------------------
+function startTagAfterAfterBody(p, token) {
+ if (token.tagName === $.HTML) {
+ startTagInBody(p, token);
+ } else {
+ tokenAfterAfterBody(p, token);
+ }
+}
+
+function tokenAfterAfterBody(p, token) {
+ p.insertionMode = IN_BODY_MODE;
+ p._processToken(token);
+}
+
+// The "after after frameset" insertion mode
+//------------------------------------------------------------------
+function startTagAfterAfterFrameset(p, token) {
+ const tn = token.tagName;
+
+ if (tn === $.HTML) {
+ startTagInBody(p, token);
+ } else if (tn === $.NOFRAMES) {
+ startTagInHead(p, token);
+ }
+}
+
+// The rules for parsing tokens in foreign content
+//------------------------------------------------------------------
+function nullCharacterInForeignContent(p, token) {
+ token.chars = unicode.REPLACEMENT_CHARACTER;
+ p._insertCharacters(token);
+}
+
+function characterInForeignContent(p, token) {
+ p._insertCharacters(token);
+ p.framesetOk = false;
+}
+
+function startTagInForeignContent(p, token) {
+ if (foreignContent.causesExit(token) && !p.fragmentContext) {
+ while (
+ p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML &&
+ !p._isIntegrationPoint(p.openElements.current)
+ ) {
+ p.openElements.pop();
+ }
+
+ p._processToken(token);
+ } else {
+ const current = p._getAdjustedCurrentElement();
+ const currentNs = p.treeAdapter.getNamespaceURI(current);
+
+ if (currentNs === NS.MATHML) {
+ foreignContent.adjustTokenMathMLAttrs(token);
+ } else if (currentNs === NS.SVG) {
+ foreignContent.adjustTokenSVGTagName(token);
+ foreignContent.adjustTokenSVGAttrs(token);
+ }
+
+ foreignContent.adjustTokenXMLAttrs(token);
+
+ if (token.selfClosing) {
+ p._appendElement(token, currentNs);
+ } else {
+ p._insertElement(token, currentNs);
+ }
+
+ token.ackSelfClosing = true;
+ }
+}
+
+function endTagInForeignContent(p, token) {
+ for (let i = p.openElements.stackTop; i > 0; i--) {
+ const element = p.openElements.items[i];
+
+ if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) {
+ p._processToken(token);
+ break;
+ }
+
+ if (p.treeAdapter.getTagName(element).toLowerCase() === token.tagName) {
+ p.openElements.popUntilElementPopped(element);
+ break;
+ }
+ }
+}
diff --git a/school/node_modules/parse5/lib/parser/open-element-stack.js b/school/node_modules/parse5/lib/parser/open-element-stack.js
new file mode 100644
index 0000000..c10880a
--- /dev/null
+++ b/school/node_modules/parse5/lib/parser/open-element-stack.js
@@ -0,0 +1,482 @@
+'use strict';
+
+const HTML = require('../common/html');
+
+//Aliases
+const $ = HTML.TAG_NAMES;
+const NS = HTML.NAMESPACES;
+
+//Element utils
+
+//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
+//It's faster than using dictionary.
+function isImpliedEndTagRequired(tn) {
+ switch (tn.length) {
+ case 1:
+ return tn === $.P;
+
+ case 2:
+ return tn === $.RB || tn === $.RP || tn === $.RT || tn === $.DD || tn === $.DT || tn === $.LI;
+
+ case 3:
+ return tn === $.RTC;
+
+ case 6:
+ return tn === $.OPTION;
+
+ case 8:
+ return tn === $.OPTGROUP;
+ }
+
+ return false;
+}
+
+function isImpliedEndTagRequiredThoroughly(tn) {
+ switch (tn.length) {
+ case 1:
+ return tn === $.P;
+
+ case 2:
+ return (
+ tn === $.RB ||
+ tn === $.RP ||
+ tn === $.RT ||
+ tn === $.DD ||
+ tn === $.DT ||
+ tn === $.LI ||
+ tn === $.TD ||
+ tn === $.TH ||
+ tn === $.TR
+ );
+
+ case 3:
+ return tn === $.RTC;
+
+ case 5:
+ return tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD;
+
+ case 6:
+ return tn === $.OPTION;
+
+ case 7:
+ return tn === $.CAPTION;
+
+ case 8:
+ return tn === $.OPTGROUP || tn === $.COLGROUP;
+ }
+
+ return false;
+}
+
+function isScopingElement(tn, ns) {
+ switch (tn.length) {
+ case 2:
+ if (tn === $.TD || tn === $.TH) {
+ return ns === NS.HTML;
+ } else if (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS) {
+ return ns === NS.MATHML;
+ }
+
+ break;
+
+ case 4:
+ if (tn === $.HTML) {
+ return ns === NS.HTML;
+ } else if (tn === $.DESC) {
+ return ns === NS.SVG;
+ }
+
+ break;
+
+ case 5:
+ if (tn === $.TABLE) {
+ return ns === NS.HTML;
+ } else if (tn === $.MTEXT) {
+ return ns === NS.MATHML;
+ } else if (tn === $.TITLE) {
+ return ns === NS.SVG;
+ }
+
+ break;
+
+ case 6:
+ return (tn === $.APPLET || tn === $.OBJECT) && ns === NS.HTML;
+
+ case 7:
+ return (tn === $.CAPTION || tn === $.MARQUEE) && ns === NS.HTML;
+
+ case 8:
+ return tn === $.TEMPLATE && ns === NS.HTML;
+
+ case 13:
+ return tn === $.FOREIGN_OBJECT && ns === NS.SVG;
+
+ case 14:
+ return tn === $.ANNOTATION_XML && ns === NS.MATHML;
+ }
+
+ return false;
+}
+
+//Stack of open elements
+class OpenElementStack {
+ constructor(document, treeAdapter) {
+ this.stackTop = -1;
+ this.items = [];
+ this.current = document;
+ this.currentTagName = null;
+ this.currentTmplContent = null;
+ this.tmplCount = 0;
+ this.treeAdapter = treeAdapter;
+ }
+
+ //Index of element
+ _indexOf(element) {
+ let idx = -1;
+
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (this.items[i] === element) {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+ }
+
+ //Update current element
+ _isInTemplate() {
+ return this.currentTagName === $.TEMPLATE && this.treeAdapter.getNamespaceURI(this.current) === NS.HTML;
+ }
+
+ _updateCurrentElement() {
+ this.current = this.items[this.stackTop];
+ this.currentTagName = this.current && this.treeAdapter.getTagName(this.current);
+
+ this.currentTmplContent = this._isInTemplate() ? this.treeAdapter.getTemplateContent(this.current) : null;
+ }
+
+ //Mutations
+ push(element) {
+ this.items[++this.stackTop] = element;
+ this._updateCurrentElement();
+
+ if (this._isInTemplate()) {
+ this.tmplCount++;
+ }
+ }
+
+ pop() {
+ this.stackTop--;
+
+ if (this.tmplCount > 0 && this._isInTemplate()) {
+ this.tmplCount--;
+ }
+
+ this._updateCurrentElement();
+ }
+
+ replace(oldElement, newElement) {
+ const idx = this._indexOf(oldElement);
+
+ this.items[idx] = newElement;
+
+ if (idx === this.stackTop) {
+ this._updateCurrentElement();
+ }
+ }
+
+ insertAfter(referenceElement, newElement) {
+ const insertionIdx = this._indexOf(referenceElement) + 1;
+
+ this.items.splice(insertionIdx, 0, newElement);
+
+ if (insertionIdx === ++this.stackTop) {
+ this._updateCurrentElement();
+ }
+ }
+
+ popUntilTagNamePopped(tagName) {
+ while (this.stackTop > -1) {
+ const tn = this.currentTagName;
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
+
+ this.pop();
+
+ if (tn === tagName && ns === NS.HTML) {
+ break;
+ }
+ }
+ }
+
+ popUntilElementPopped(element) {
+ while (this.stackTop > -1) {
+ const poppedElement = this.current;
+
+ this.pop();
+
+ if (poppedElement === element) {
+ break;
+ }
+ }
+ }
+
+ popUntilNumberedHeaderPopped() {
+ while (this.stackTop > -1) {
+ const tn = this.currentTagName;
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
+
+ this.pop();
+
+ if (
+ tn === $.H1 ||
+ tn === $.H2 ||
+ tn === $.H3 ||
+ tn === $.H4 ||
+ tn === $.H5 ||
+ (tn === $.H6 && ns === NS.HTML)
+ ) {
+ break;
+ }
+ }
+ }
+
+ popUntilTableCellPopped() {
+ while (this.stackTop > -1) {
+ const tn = this.currentTagName;
+ const ns = this.treeAdapter.getNamespaceURI(this.current);
+
+ this.pop();
+
+ if (tn === $.TD || (tn === $.TH && ns === NS.HTML)) {
+ break;
+ }
+ }
+ }
+
+ popAllUpToHtmlElement() {
+ //NOTE: here we assume that root <html> element is always first in the open element stack, so
+ //we perform this fast stack clean up.
+ this.stackTop = 0;
+ this._updateCurrentElement();
+ }
+
+ clearBackToTableContext() {
+ while (
+ (this.currentTagName !== $.TABLE && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
+ ) {
+ this.pop();
+ }
+ }
+
+ clearBackToTableBodyContext() {
+ while (
+ (this.currentTagName !== $.TBODY &&
+ this.currentTagName !== $.TFOOT &&
+ this.currentTagName !== $.THEAD &&
+ this.currentTagName !== $.TEMPLATE &&
+ this.currentTagName !== $.HTML) ||
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
+ ) {
+ this.pop();
+ }
+ }
+
+ clearBackToTableRowContext() {
+ while (
+ (this.currentTagName !== $.TR && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
+ this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
+ ) {
+ this.pop();
+ }
+ }
+
+ remove(element) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (this.items[i] === element) {
+ this.items.splice(i, 1);
+ this.stackTop--;
+ this._updateCurrentElement();
+ break;
+ }
+ }
+ }
+
+ //Search
+ tryPeekProperlyNestedBodyElement() {
+ //Properly nested <body> element (should be second element in stack).
+ const element = this.items[1];
+
+ return element && this.treeAdapter.getTagName(element) === $.BODY ? element : null;
+ }
+
+ contains(element) {
+ return this._indexOf(element) > -1;
+ }
+
+ getCommonAncestor(element) {
+ let elementIdx = this._indexOf(element);
+
+ return --elementIdx >= 0 ? this.items[elementIdx] : null;
+ }
+
+ isRootHtmlElementCurrent() {
+ return this.stackTop === 0 && this.currentTagName === $.HTML;
+ }
+
+ //Element in scope
+ hasInScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (tn === tagName && ns === NS.HTML) {
+ return true;
+ }
+
+ if (isScopingElement(tn, ns)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasNumberedHeaderInScope() {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (
+ (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) &&
+ ns === NS.HTML
+ ) {
+ return true;
+ }
+
+ if (isScopingElement(tn, ns)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasInListItemScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (tn === tagName && ns === NS.HTML) {
+ return true;
+ }
+
+ if (((tn === $.UL || tn === $.OL) && ns === NS.HTML) || isScopingElement(tn, ns)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasInButtonScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (tn === tagName && ns === NS.HTML) {
+ return true;
+ }
+
+ if ((tn === $.BUTTON && ns === NS.HTML) || isScopingElement(tn, ns)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasInTableScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (ns !== NS.HTML) {
+ continue;
+ }
+
+ if (tn === tagName) {
+ return true;
+ }
+
+ if (tn === $.TABLE || tn === $.TEMPLATE || tn === $.HTML) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasTableBodyContextInTableScope() {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (ns !== NS.HTML) {
+ continue;
+ }
+
+ if (tn === $.TBODY || tn === $.THEAD || tn === $.TFOOT) {
+ return true;
+ }
+
+ if (tn === $.TABLE || tn === $.HTML) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ hasInSelectScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.treeAdapter.getTagName(this.items[i]);
+ const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
+
+ if (ns !== NS.HTML) {
+ continue;
+ }
+
+ if (tn === tagName) {
+ return true;
+ }
+
+ if (tn !== $.OPTION && tn !== $.OPTGROUP) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //Implied end tags
+ generateImpliedEndTags() {
+ while (isImpliedEndTagRequired(this.currentTagName)) {
+ this.pop();
+ }
+ }
+
+ generateImpliedEndTagsThoroughly() {
+ while (isImpliedEndTagRequiredThoroughly(this.currentTagName)) {
+ this.pop();
+ }
+ }
+
+ generateImpliedEndTagsWithExclusion(exclusionTagName) {
+ while (isImpliedEndTagRequired(this.currentTagName) && this.currentTagName !== exclusionTagName) {
+ this.pop();
+ }
+ }
+}
+
+module.exports = OpenElementStack;