UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

98 lines (95 loc) 4 kB
import { DOMTreeConstruction, ConcreteBounds, NewElementBuilder } from '../runtime/index.js'; import createHTMLDocument from '../../@simple-dom/document/index.js'; class NodeDOMTreeConstruction extends DOMTreeConstruction { // Hides property on base class constructor(doc) { super(doc || createHTMLDocument()); } // override to prevent usage of `this.document` until after the constructor setupUselessElement() {} insertHTMLBefore(parent, reference, html) { let raw = this.document.createRawHTMLSection(html); return parent.insertBefore(raw, reference), new ConcreteBounds(parent, raw, raw); } // override to avoid SVG detection/work when in node (this is not needed in SSR) createElement(tag) { return this.document.createElement(tag); } // override to avoid namespace shenanigans when in node (this is not needed in SSR) setAttribute(element, name, value) { element.setAttribute(name, value); } } const NEEDS_EXTRA_CLOSE = new WeakMap(); class SerializeBuilder extends NewElementBuilder { serializeBlockDepth = 0; __openBlock() { let { tagName: tagName } = this.element; if ("TITLE" !== tagName && "SCRIPT" !== tagName && "STYLE" !== tagName) { let depth = this.serializeBlockDepth++; this.__appendComment(`%+b:${depth}%`); } super.__openBlock(); } __closeBlock() { let { tagName: tagName } = this.element; if (super.__closeBlock(), "TITLE" !== tagName && "SCRIPT" !== tagName && "STYLE" !== tagName) { let depth = --this.serializeBlockDepth; this.__appendComment(`%-b:${depth}%`); } } __appendHTML(html) { let { tagName: tagName } = this.element; if ("TITLE" === tagName || "SCRIPT" === tagName || "STYLE" === tagName) return super.__appendHTML(html); // Do we need to run the html tokenizer here? let first = this.__appendComment("%glmr%"); if ("TABLE" === tagName) { let openIndex = html.indexOf("<"); openIndex > -1 && "tr" === html.slice(openIndex + 1, openIndex + 3) && (html = `<tbody>${html}</tbody>`); } "" === html ? this.__appendComment("% %") : super.__appendHTML(html); let last = this.__appendComment("%glmr%"); return new ConcreteBounds(this.element, first, last); } __appendText(string) { let { tagName: tagName } = this.element, current = function (cursor) { let { element: element, nextSibling: nextSibling } = cursor; return null === nextSibling ? element.lastChild : nextSibling.previousSibling; }(this); return "TITLE" === tagName || "SCRIPT" === tagName || "STYLE" === tagName ? super.__appendText(string) : "" === string ? this.__appendComment("% %") : (current && 3 === current.nodeType && this.__appendComment("%|%"), super.__appendText(string)); } closeElement() { return NEEDS_EXTRA_CLOSE.has(this.element) && (NEEDS_EXTRA_CLOSE.delete(this.element), super.closeElement()), super.closeElement(); } openElement(tag) { return "tr" === tag && "TBODY" !== this.element.tagName && "THEAD" !== this.element.tagName && "TFOOT" !== this.element.tagName && (this.openElement("tbody"), // This prevents the closeBlock comment from being re-parented // under the auto inserted tbody. Rehydration builder needs to // account for the insertion since it is injected here and not // really in the template. NEEDS_EXTRA_CLOSE.set(this.constructing, !0), this.flushElement(null)), super.openElement(tag); } pushRemoteElement(element, cursorId, insertBefore = null) { let { dom: dom } = this, script = dom.createElement("script"); return script.setAttribute("glmr", cursorId), dom.insertBefore(element, script, insertBefore), super.pushRemoteElement(element, cursorId, insertBefore); } } function serializeBuilder(env, cursor) { return SerializeBuilder.forInitialRender(env, cursor); } export { NodeDOMTreeConstruction, serializeBuilder };