aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/parse5/dist/parser/open-element-stack.js
diff options
context:
space:
mode:
Diffstat (limited to 'vanilla/node_modules/parse5/dist/parser/open-element-stack.js')
-rw-r--r--vanilla/node_modules/parse5/dist/parser/open-element-stack.js324
1 files changed, 324 insertions, 0 deletions
diff --git a/vanilla/node_modules/parse5/dist/parser/open-element-stack.js b/vanilla/node_modules/parse5/dist/parser/open-element-stack.js
new file mode 100644
index 0000000..b3f98e4
--- /dev/null
+++ b/vanilla/node_modules/parse5/dist/parser/open-element-stack.js
@@ -0,0 +1,324 @@
+import { TAG_ID as $, NS, NUMBERED_HEADERS } from '../common/html.js';
+//Element utils
+const IMPLICIT_END_TAG_REQUIRED = new Set([$.DD, $.DT, $.LI, $.OPTGROUP, $.OPTION, $.P, $.RB, $.RP, $.RT, $.RTC]);
+const IMPLICIT_END_TAG_REQUIRED_THOROUGHLY = new Set([
+ ...IMPLICIT_END_TAG_REQUIRED,
+ $.CAPTION,
+ $.COLGROUP,
+ $.TBODY,
+ $.TD,
+ $.TFOOT,
+ $.TH,
+ $.THEAD,
+ $.TR,
+]);
+const SCOPING_ELEMENTS_HTML = new Set([
+ $.APPLET,
+ $.CAPTION,
+ $.HTML,
+ $.MARQUEE,
+ $.OBJECT,
+ $.TABLE,
+ $.TD,
+ $.TEMPLATE,
+ $.TH,
+]);
+const SCOPING_ELEMENTS_HTML_LIST = new Set([...SCOPING_ELEMENTS_HTML, $.OL, $.UL]);
+const SCOPING_ELEMENTS_HTML_BUTTON = new Set([...SCOPING_ELEMENTS_HTML, $.BUTTON]);
+const SCOPING_ELEMENTS_MATHML = new Set([$.ANNOTATION_XML, $.MI, $.MN, $.MO, $.MS, $.MTEXT]);
+const SCOPING_ELEMENTS_SVG = new Set([$.DESC, $.FOREIGN_OBJECT, $.TITLE]);
+const TABLE_ROW_CONTEXT = new Set([$.TR, $.TEMPLATE, $.HTML]);
+const TABLE_BODY_CONTEXT = new Set([$.TBODY, $.TFOOT, $.THEAD, $.TEMPLATE, $.HTML]);
+const TABLE_CONTEXT = new Set([$.TABLE, $.TEMPLATE, $.HTML]);
+const TABLE_CELLS = new Set([$.TD, $.TH]);
+//Stack of open elements
+export class OpenElementStack {
+ get currentTmplContentOrNode() {
+ return this._isInTemplate() ? this.treeAdapter.getTemplateContent(this.current) : this.current;
+ }
+ constructor(document, treeAdapter, handler) {
+ this.treeAdapter = treeAdapter;
+ this.handler = handler;
+ this.items = [];
+ this.tagIDs = [];
+ this.stackTop = -1;
+ this.tmplCount = 0;
+ this.currentTagId = $.UNKNOWN;
+ this.current = document;
+ }
+ //Index of element
+ _indexOf(element) {
+ return this.items.lastIndexOf(element, this.stackTop);
+ }
+ //Update current element
+ _isInTemplate() {
+ return this.currentTagId === $.TEMPLATE && this.treeAdapter.getNamespaceURI(this.current) === NS.HTML;
+ }
+ _updateCurrentElement() {
+ this.current = this.items[this.stackTop];
+ this.currentTagId = this.tagIDs[this.stackTop];
+ }
+ //Mutations
+ push(element, tagID) {
+ this.stackTop++;
+ this.items[this.stackTop] = element;
+ this.current = element;
+ this.tagIDs[this.stackTop] = tagID;
+ this.currentTagId = tagID;
+ if (this._isInTemplate()) {
+ this.tmplCount++;
+ }
+ this.handler.onItemPush(element, tagID, true);
+ }
+ pop() {
+ const popped = this.current;
+ if (this.tmplCount > 0 && this._isInTemplate()) {
+ this.tmplCount--;
+ }
+ this.stackTop--;
+ this._updateCurrentElement();
+ this.handler.onItemPop(popped, true);
+ }
+ replace(oldElement, newElement) {
+ const idx = this._indexOf(oldElement);
+ this.items[idx] = newElement;
+ if (idx === this.stackTop) {
+ this.current = newElement;
+ }
+ }
+ insertAfter(referenceElement, newElement, newElementID) {
+ const insertionIdx = this._indexOf(referenceElement) + 1;
+ this.items.splice(insertionIdx, 0, newElement);
+ this.tagIDs.splice(insertionIdx, 0, newElementID);
+ this.stackTop++;
+ if (insertionIdx === this.stackTop) {
+ this._updateCurrentElement();
+ }
+ if (this.current && this.currentTagId !== undefined) {
+ this.handler.onItemPush(this.current, this.currentTagId, insertionIdx === this.stackTop);
+ }
+ }
+ popUntilTagNamePopped(tagName) {
+ let targetIdx = this.stackTop + 1;
+ do {
+ targetIdx = this.tagIDs.lastIndexOf(tagName, targetIdx - 1);
+ } while (targetIdx > 0 && this.treeAdapter.getNamespaceURI(this.items[targetIdx]) !== NS.HTML);
+ this.shortenToLength(Math.max(targetIdx, 0));
+ }
+ shortenToLength(idx) {
+ while (this.stackTop >= idx) {
+ const popped = this.current;
+ if (this.tmplCount > 0 && this._isInTemplate()) {
+ this.tmplCount -= 1;
+ }
+ this.stackTop--;
+ this._updateCurrentElement();
+ this.handler.onItemPop(popped, this.stackTop < idx);
+ }
+ }
+ popUntilElementPopped(element) {
+ const idx = this._indexOf(element);
+ this.shortenToLength(Math.max(idx, 0));
+ }
+ popUntilPopped(tagNames, targetNS) {
+ const idx = this._indexOfTagNames(tagNames, targetNS);
+ this.shortenToLength(Math.max(idx, 0));
+ }
+ popUntilNumberedHeaderPopped() {
+ this.popUntilPopped(NUMBERED_HEADERS, NS.HTML);
+ }
+ popUntilTableCellPopped() {
+ this.popUntilPopped(TABLE_CELLS, NS.HTML);
+ }
+ popAllUpToHtmlElement() {
+ //NOTE: here we assume that the root <html> element is always first in the open element stack, so
+ //we perform this fast stack clean up.
+ this.tmplCount = 0;
+ this.shortenToLength(1);
+ }
+ _indexOfTagNames(tagNames, namespace) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (tagNames.has(this.tagIDs[i]) && this.treeAdapter.getNamespaceURI(this.items[i]) === namespace) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ clearBackTo(tagNames, targetNS) {
+ const idx = this._indexOfTagNames(tagNames, targetNS);
+ this.shortenToLength(idx + 1);
+ }
+ clearBackToTableContext() {
+ this.clearBackTo(TABLE_CONTEXT, NS.HTML);
+ }
+ clearBackToTableBodyContext() {
+ this.clearBackTo(TABLE_BODY_CONTEXT, NS.HTML);
+ }
+ clearBackToTableRowContext() {
+ this.clearBackTo(TABLE_ROW_CONTEXT, NS.HTML);
+ }
+ remove(element) {
+ const idx = this._indexOf(element);
+ if (idx >= 0) {
+ if (idx === this.stackTop) {
+ this.pop();
+ }
+ else {
+ this.items.splice(idx, 1);
+ this.tagIDs.splice(idx, 1);
+ this.stackTop--;
+ this._updateCurrentElement();
+ this.handler.onItemPop(element, false);
+ }
+ }
+ }
+ //Search
+ tryPeekProperlyNestedBodyElement() {
+ //Properly nested <body> element (should be second element in stack).
+ return this.stackTop >= 1 && this.tagIDs[1] === $.BODY ? this.items[1] : null;
+ }
+ contains(element) {
+ return this._indexOf(element) > -1;
+ }
+ getCommonAncestor(element) {
+ const elementIdx = this._indexOf(element) - 1;
+ return elementIdx >= 0 ? this.items[elementIdx] : null;
+ }
+ isRootHtmlElementCurrent() {
+ return this.stackTop === 0 && this.tagIDs[0] === $.HTML;
+ }
+ //Element in scope
+ hasInDynamicScope(tagName, htmlScope) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.tagIDs[i];
+ switch (this.treeAdapter.getNamespaceURI(this.items[i])) {
+ case NS.HTML: {
+ if (tn === tagName)
+ return true;
+ if (htmlScope.has(tn))
+ return false;
+ break;
+ }
+ case NS.SVG: {
+ if (SCOPING_ELEMENTS_SVG.has(tn))
+ return false;
+ break;
+ }
+ case NS.MATHML: {
+ if (SCOPING_ELEMENTS_MATHML.has(tn))
+ return false;
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ hasInScope(tagName) {
+ return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML);
+ }
+ hasInListItemScope(tagName) {
+ return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML_LIST);
+ }
+ hasInButtonScope(tagName) {
+ return this.hasInDynamicScope(tagName, SCOPING_ELEMENTS_HTML_BUTTON);
+ }
+ hasNumberedHeaderInScope() {
+ for (let i = this.stackTop; i >= 0; i--) {
+ const tn = this.tagIDs[i];
+ switch (this.treeAdapter.getNamespaceURI(this.items[i])) {
+ case NS.HTML: {
+ if (NUMBERED_HEADERS.has(tn))
+ return true;
+ if (SCOPING_ELEMENTS_HTML.has(tn))
+ return false;
+ break;
+ }
+ case NS.SVG: {
+ if (SCOPING_ELEMENTS_SVG.has(tn))
+ return false;
+ break;
+ }
+ case NS.MATHML: {
+ if (SCOPING_ELEMENTS_MATHML.has(tn))
+ return false;
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ hasInTableScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
+ continue;
+ }
+ switch (this.tagIDs[i]) {
+ case tagName: {
+ return true;
+ }
+ case $.TABLE:
+ case $.HTML: {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ hasTableBodyContextInTableScope() {
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
+ continue;
+ }
+ switch (this.tagIDs[i]) {
+ case $.TBODY:
+ case $.THEAD:
+ case $.TFOOT: {
+ return true;
+ }
+ case $.TABLE:
+ case $.HTML: {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ hasInSelectScope(tagName) {
+ for (let i = this.stackTop; i >= 0; i--) {
+ if (this.treeAdapter.getNamespaceURI(this.items[i]) !== NS.HTML) {
+ continue;
+ }
+ switch (this.tagIDs[i]) {
+ case tagName: {
+ return true;
+ }
+ case $.OPTION:
+ case $.OPTGROUP: {
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ //Implied end tags
+ generateImpliedEndTags() {
+ while (this.currentTagId !== undefined && IMPLICIT_END_TAG_REQUIRED.has(this.currentTagId)) {
+ this.pop();
+ }
+ }
+ generateImpliedEndTagsThoroughly() {
+ while (this.currentTagId !== undefined && IMPLICIT_END_TAG_REQUIRED_THOROUGHLY.has(this.currentTagId)) {
+ this.pop();
+ }
+ }
+ generateImpliedEndTagsWithExclusion(exclusionId) {
+ while (this.currentTagId !== undefined &&
+ this.currentTagId !== exclusionId &&
+ IMPLICIT_END_TAG_REQUIRED_THOROUGHLY.has(this.currentTagId)) {
+ this.pop();
+ }
+ }
+}