"use strict"; const parse5 = require("parse5"); const DocumentType = require("../../living/generated/DocumentType"); const DocumentFragment = require("../../living/generated/DocumentFragment"); const Text = require("../../living/generated/Text"); const Comment = require("../../living/generated/Comment"); const attributes = require("../../living/attributes"); const nodeTypes = require("../../living/node-type"); const serializationAdapter = require("../../living/domparsing/parse5-adapter-serialization"); const OpenElementStack = require("parse5/lib/parser/open-element-stack"); const OpenElementStackOriginalPop = OpenElementStack.prototype.pop; const OpenElementStackOriginalPush = OpenElementStack.prototype.push; class JSDOMParse5Adapter { constructor(documentImpl) { this._documentImpl = documentImpl; // Since the createElement hook doesn't provide the parent element, we keep track of this using _currentElement: // https://github.com/inikulin/parse5/issues/285 this._currentElement = undefined; // Horrible monkey-patch to implement https://github.com/inikulin/parse5/issues/237 const adapter = this; OpenElementStack.prototype.push = function (...args) { OpenElementStackOriginalPush.apply(this, args); adapter._currentElement = this.current; const after = this.items[this.stackTop]; if (after._pushedOnStackOfOpenElements) { after._pushedOnStackOfOpenElements(); } }; OpenElementStack.prototype.pop = function (...args) { const before = this.items[this.stackTop]; OpenElementStackOriginalPop.apply(this, args); adapter._currentElement = this.current; if (before._poppedOffStackOfOpenElements) { before._poppedOffStackOfOpenElements(); } }; } _ownerDocument() { // The _currentElement is undefined when parsing elements at the root of the document. In this case we would // fallback to the global _documentImpl. return this._currentElement ? this._currentElement._ownerDocument : this._documentImpl; } createDocument() { // parse5's model assumes that parse(html) will call into here to create the new Document, then return it. However, // jsdom's model assumes we can create a Window (and through that create an empty Document), do some other setup // stuff, and then parse, stuffing nodes into that Document as we go. So to adapt between these two models, we just // return the already-created Document when asked by parse5 to "create" a Document. return this._documentImpl; } createDocumentFragment() { return DocumentFragment.createImpl([], { ownerDocument: this._currentElement._ownerDocument }); } createElement(localName, namespace, attrs) { const ownerDocument = this._ownerDocument(); const element = ownerDocument._createElementWithCorrectElementInterface(localName, namespace); element._namespaceURI = namespace; this.adoptAttributes(element, attrs); if ("_parserInserted" in element) { element._parserInserted = true; } return element; } createCommentNode(data) { const ownerDocument = this._ownerDocument(); return Comment.createImpl([], { data, ownerDocument }); } appendChild(parentNode, newNode) { parentNode._append(newNode); } insertBefore(parentNode, newNode, referenceNode) { parentNode._insert(newNode, referenceNode); } setTemplateContent(templateElement, contentFragment) { // This code makes the glue between jsdom and parse5 HTMLTemplateElement parsing: // // * jsdom during the construction of the HTMLTemplateElement (for example when create via // `document.createElement("template")`), creates a DocumentFragment and set it into _templateContents. // * parse5 when parsing a