UNPKG

html-document

Version:

Partial implementation of document, HTMLElement for node

248 lines (213 loc) 6.35 kB
import Node from './Node'; import parse from './parse'; /** * The ParentNode interface contains methods that are particular to Node objects that can have children. * * @see https://developer.mozilla.org/en/docs/Web/API/ParentNode * @class ParentNode * @extends Node */ export default class ParentNode extends Node { constructor() { super(); this._childNodes = []; } /** * TODO return {HTMLCollection} * @member {String} ParentNode#childNodes * @readonly */ get childNodes() { return this._childNodes; } _childNodesRecursiveForEach(callback) { this._childNodes.forEach(function(node) { callback(node); if (node instanceof ParentNode) { node._childNodesRecursiveForEach(callback); } }); } _childNodesRecursiveFind(callback) { let result; this._childNodes.some(function(node) { if (callback(node)) { result = node; return true; } if (node instanceof ParentNode) { result = node._childNodesRecursiveFind(callback); if (result !== undefined) { return true; } } }); return result; } /** * @member {Node|null} ParentNode#parentNode * @readonly */ get parentNode() { return this._parentNode || null; } /** * @member {Node|null} ParentNode#firstChild * @readonly */ get firstChild() { return this._childNodes[0] || null; } /** * @member {Node|null} ParentNode#lastChild * @readonly */ get lastChild() { return this._childNodes[this._childNodes.length - 1] || null; } /** * @member {Node|null} ParentNode#previousSibling * @readonly */ get previousSibling() { const indexInParent = this.parentNode._childNodes.indexOf(this); if (indexInParent === -1) { throw new Error('Unexpected state: this node is not in the parent'); } return indexInParent !== 0 && this.parentNode._childNodes[indexInParent - 1] || null; } /** * @member {Node|null} ParentNode#nextSibling * @readonly */ get nextSibling() { const indexInParent = this.parentNode._childNodes.indexOf(this); if (indexInParent === -1) { throw new Error('Unexpected state: this node is not in the parent'); } return this.parentNode._childNodes[indexInParent + 1] || null; } /** * @method ParentNode#appendChild * @param {Node} child * @return {Node} */ appendChild(child) { if (!(child instanceof Node)) { throw new Error('Trying to add non node element'); } if (child._parentNode) { child._parentNode.removeChild(child); } if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { let childNode; while (childNode = child.firstChild) { child.removeChild(childNode); this.appendChild(childNode); } return child; } child._parentNode = this; this._childNodes.push(child); return child; } /** * @method ParentNode#replaceChild * @param {Node} newChild * @param {Node} oldChild * @return {Node} */ replaceChild(newChild, oldChild) { const index = this._childNodes.indexOf(oldChild); if (index === -1) { throw new Error('Node was not found'); } if (newChild._parentNode) { newChild._parentNode.removeChild(newChild); } if (newChild.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { const newChildren = []; let childNode; while (childNode = newChild.firstChild) { newChild.removeChild(childNode); childNode._parentNode = this; newChildren.push(childNode); } this._childNodes.splice.bind(this._childNodes, index, 1).apply(null, newChildren); } else { newChild._parentNode = this; this._childNodes[index] = newChild; } delete oldChild._parentNode; return oldChild; } /** * @method ParentNode#removeChild * @param {Node} toRemoveChild * @return {Node} */ removeChild(toRemoveChild) { const index = this._childNodes.indexOf(toRemoveChild); if (index === -1) { throw new Error('Node was not found'); } this._childNodes.splice(index, 1); delete toRemoveChild._parentNode; return toRemoveChild; } /** * @method ParentNode#insertBefore * @param {Node} child * @param {Node} existingChild * @return {Node} */ insertBefore(child, existingChild) { const index = this._childNodes.indexOf(existingChild); if (index === -1) { throw new Error('Node was not found'); } if (child._parentNode) { child._parentNode.removeChild(child); } if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { const children = []; let childNode; while (childNode = child.firstChild) { child.removeChild(childNode); childNode._parentNode = this; children.push(childNode); } this._childNodes.splice.bind(this._childNodes, index, 0).apply(null, children); } else { child._parentNode = this; this._childNodes.splice(index, 0, child); } return child; } /** * @ignore * @return {String} */ get innerHTML() { return this._childNodes.reduce(function(value, node) { return value + node._toHTML(); }, ''); } /** * @ignore * @param {String} html */ set innerHTML(html) { this._childNodes = []; parse(html, this); } /** * @ignore * @return {String} */ get textContent() { return this._childNodes.reduce(function(value, node) { return value + node.textContent; }, ''); } }