UNPKG

nehan

Version:

Html layout engine for paged-media written in Typescript

363 lines 10.9 kB
import { CssStyleDeclaration, DomTokenList, SelectorLexer, SelectorParser, WhiteSpace, ReplacedElement, PhysicalSize, CssLoader, } from "./public-api"; export class NehanElement { constructor(node, root) { this.$node = node; this.$dom = undefined; this.tagName = this.getTagName(); this.childNodes = []; this.parent = null; this.root = root; this.style = new CssStyleDeclaration(); this.computedStyle = new CssStyleDeclaration(); this.id = this.$node instanceof Element ? this.$node.id : ""; this.classList = this.createClassList(); this.setupChildren(this.$node, root); } acceptChildFilter(visitor) { this.childNodes = this.childNodes.filter(node => visitor.visit(node)); return this; } acceptEffector(visitor) { visitor.visit(this); return this; } acceptEffectorAll(visitor) { visitor.visit(this); this.childNodes.forEach(node => node.acceptEffectorAll(visitor)); return this; } get nextSibling() { if (!this.parent) { return null; } const index = this.parent.childNodes.indexOf(this); return (index < 0 || index + 1 >= this.parent.childNodes.length) ? null : this.parent.childNodes[index + 1]; } get previousSibling() { if (!this.parent) { return null; } const index = this.parent.childNodes.indexOf(this); return (index > 0) ? this.parent.childNodes[index - 1] : null; } clone(deep = false) { const element = this.ownerDocument.createNehanElement(this.$node.cloneNode(deep)); element.style = this.style; element.computedStyle = this.computedStyle; return element; } createClassList() { if (this.$node instanceof Element) { let items = []; for (let i = 0; i < this.$node.classList.length; i++) { let item = this.$node.classList.item(i); if (item !== null) { items.push(item); } } return new DomTokenList(items); } return new DomTokenList([]); } setupChildren($node, root) { if ($node instanceof Element) { for (let i = 0; i < $node.childNodes.length; i++) { let child = $node.childNodes.item(i); let child_element = root.createNehanElement(child); this.appendChild(child_element); } } } set innerHTML(html) { this.$node.innerHTML = html; this.childNodes = []; this.setupChildren(this.$node, this.root); CssLoader.loadAll(this); } get innerHTML() { if (this.isTextElement()) { return this.textContent; } return this.$node.innerHTML; } get className() { return this.classList.values().join(" "); } set className(class_names) { let tokens = class_names.split(" ").filter(cls => cls !== ""); this.classList = new DomTokenList(tokens); } get pureTagName() { return this.tagName.replace("::", ""); } get attributes() { if (this.$node instanceof Element) { return this.$node.attributes; } return null; } get dataset() { if (this.$node instanceof Element) { return this.$node.dataset; } throw new Error("dataset is not defined(not HTMLElement)"); } get textContent() { return this.$node.textContent || ""; } getTagName() { if (this.$node instanceof Text) { return "(text)"; } if (this.$node instanceof Element) { return this.$node.tagName.toLowerCase(); } console.info("unsupported node type:%o", this); return "???"; } getNodeName() { let str = this.tagName; if (this.id) { str += "#" + this.id; } for (let i = 0; i < this.classList.length; i++) { str += "." + this.classList.item(i); } return str; } getPath(with_parent = false) { let str = this.getNodeName(); if (!with_parent) { return str; } let parent = this.parent; while (parent) { str = parent.getNodeName() + ">" + str; parent = parent.parent; } return str; } toString(with_index = false) { let str = this.getPath(true); if (!with_index) { return str; } let index = this.indexOfType; str += "(" + index + ")"; return str; } querySelectorAll(query) { const lexer = new SelectorLexer(query); const selector = new SelectorParser(lexer).parse(); const elements = selector.querySelectorAll(this); return elements; } querySelector(query) { const lexer = new SelectorLexer(query); const selector = new SelectorParser(lexer).parse(); const element = selector.querySelector(this); return element; } queryLeafs(selector) { return this.root.getSelectorCache(selector).filter(leaf => { let parent = leaf.parent; while (parent) { if (parent === this) { return true; } parent = parent.parent; } return false; }); } appendChild(element) { element.parent = this; this.childNodes.push(element); return this; } replaceChild(new_child, old_child) { if (old_child) { const index = this.childNodes.indexOf(old_child); if (index >= 0) { new_child.parent = this; this.childNodes[index] = new_child; } } return new_child; } removeChild(target_child) { const index = this.childNodes.indexOf(target_child); if (index >= 0) { this.childNodes.splice(index, 1); } return target_child; } insertBefore(new_node, ref_node) { if (!ref_node) { this.appendChild(new_node); return null; } if (ref_node.parent !== this) { throw new Error("reference node is not included in this element"); } new_node.parent = this; const index = this.childNodes.indexOf(ref_node); if (index >= 0) { this.childNodes.splice(index, 0, new_node); } return ref_node.nextSibling ? new_node : null; } hasAttribute(name) { if (this.$node instanceof Element) { return this.$node.hasAttribute(name); } return false; } isOnlyChild() { return this.siblings.length === 1; } isOnlyOfType() { const siblings = this.siblings.filter(sib => sib.tagName === this.tagName); return siblings.length === 1; } isFirstChild() { if (!this.parent) { return true; } return this.parent.childNodes.indexOf(this) === 0; } isLastChild() { if (!this.parent) { return true; } const children = this.parent.childNodes; return children[children.length - 1] === this; } isFirstElementChild() { if (!this.parent) { return true; } return this.parent.children[0] === this; } isLastElementChild() { if (!this.parent) { return true; } const children = this.parent.children; return children[children.length - 1] === this; } isNthChild(nth) { return this.index === Math.max(nth - 1, 0); } isElement() { return this.$node instanceof HTMLElement; } isTextElement() { return this.$node instanceof Text; } setAttribute(name, value) { if (this.$node instanceof Element) { this.$node.setAttribute(name, value); } } get ownerDocument() { return this.root; } get firstChild() { return this.childNodes[0] || null; } get firstTextElement() { const firstChild = this.firstChild; if (!firstChild) { return null; } if (firstChild.isTextElement()) { return firstChild; } const nextChild = firstChild.nextSibling; if (!nextChild) { return null; } return nextChild.firstTextElement; } get lastChild() { return this.childNodes[this.childNodes.length - 1] || null; } get lastElementChild() { const children = this.children; return children[children.length - 1] || null; } get firstElementChild() { return this.children[0] || null; } get nextElementSibling() { let next = this.nextSibling; while (next) { if (next.isElement()) { break; } next = next.nextSibling; } return next; } get previousElementSibling() { let prev = this.previousSibling; while (prev) { if (prev.isElement()) { break; } prev = prev.previousSibling; } return prev; } get firstAtomChild() { for (let i = 0; i < this.childNodes.length; i++) { const child = this.childNodes[i]; if (child.isTextElement() && !WhiteSpace.isWhiteSpaceElement(child)) { return child; } if (ReplacedElement.isReplacedElement(child) && !PhysicalSize.load(child).hasZero()) { return child; } const firstAtomChild = child.firstAtomChild; if (firstAtomChild) { return firstAtomChild; } } return null; } get siblings() { if (!this.parent) { return []; } return this.parent.childNodes; } get index() { if (!this.parent) { return 0; } return this.parent.childNodes.indexOf(this); } get indexOfType() { if (!this.parent) { return 0; } return this.parent.childNodes.filter(child => child.tagName === this.tagName).indexOf(this); } get indexOfElement() { if (!this.parent) { return 0; } return this.parent.children.indexOf(this); } get children() { return this.childNodes.filter(element => !element.isTextElement()); } getAttribute(name) { if (this.$node instanceof Element) { return this.$node.getAttribute(name); } return null; } } //# sourceMappingURL=nehan-element.js.map