|
- "use strict";
- var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- const EventTarget_1 = __importDefault(require("../../event/EventTarget"));
- const MutationRecord_1 = __importDefault(require("../../mutation-observer/MutationRecord"));
- const MutationTypeEnum_1 = __importDefault(require("../../mutation-observer/MutationTypeEnum"));
- const DOMException_1 = __importDefault(require("../../exception/DOMException"));
- const NodeListFactory_1 = __importDefault(require("./NodeListFactory"));
- const NodeTypeEnum_1 = __importDefault(require("./NodeTypeEnum"));
- const NodeDocumentPositionEnum_1 = __importDefault(require("./NodeDocumentPositionEnum"));
- const NodeUtility_1 = __importDefault(require("./NodeUtility"));
- /**
- * Node.
- */
- class Node extends EventTarget_1.default {
- /**
- * Constructor.
- */
- constructor() {
- super();
- this.ELEMENT_NODE = NodeTypeEnum_1.default.elementNode;
- this.ATTRIBUTE_NODE = NodeTypeEnum_1.default.attributeNode;
- this.TEXT_NODE = NodeTypeEnum_1.default.textNode;
- this.CDATA_SECTION_NODE = NodeTypeEnum_1.default.cdataSectionNode;
- this.COMMENT_NODE = NodeTypeEnum_1.default.commentNode;
- this.DOCUMENT_NODE = NodeTypeEnum_1.default.documentNode;
- this.DOCUMENT_TYPE_NODE = NodeTypeEnum_1.default.documentTypeNode;
- this.DOCUMENT_FRAGMENT_NODE = NodeTypeEnum_1.default.documentFragmentNode;
- this.PROCESSING_INSTRUCTION_NODE = NodeTypeEnum_1.default.processingInstructionNode;
- this.DOCUMENT_POSITION_CONTAINED_BY = NodeDocumentPositionEnum_1.default.containedBy;
- this.DOCUMENT_POSITION_CONTAINS = NodeDocumentPositionEnum_1.default.contains;
- this.DOCUMENT_POSITION_DISCONNECTED = NodeDocumentPositionEnum_1.default.disconnect;
- this.DOCUMENT_POSITION_FOLLOWING = NodeDocumentPositionEnum_1.default.following;
- this.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = NodeDocumentPositionEnum_1.default.implementationSpecific;
- this.DOCUMENT_POSITION_PRECEDING = NodeDocumentPositionEnum_1.default.preceding;
- this.ownerDocument = null;
- this.parentNode = null;
- this.childNodes = NodeListFactory_1.default.create();
- this.isConnected = false;
- // Custom Properties (not part of HTML standard)
- this._rootNode = null;
- this._observers = [];
- this.ownerDocument = this.constructor._ownerDocument;
- }
- /**
- * Returns `Symbol.toStringTag`.
- *
- * @returns `Symbol.toStringTag`.
- */
- get [Symbol.toStringTag]() {
- return this.constructor.name;
- }
- /**
- * Get text value of children.
- *
- * @returns Text content.
- */
- get textContent() {
- // Sub-classes should implement this method.
- return null;
- }
- /**
- * Sets text content.
- *
- * @param _textContent Text content.
- */
- set textContent(_textContent) {
- // Do nothing.
- // Sub-classes should implement this method.
- }
- /**
- * Node value.
- *
- * @returns Node value.
- */
- get nodeValue() {
- return null;
- }
- /**
- * Sets node value.
- */
- set nodeValue(_nodeValue) {
- // Do nothing
- }
- /**
- * Node name.
- *
- * @returns Node name.
- */
- get nodeName() {
- return '';
- }
- /**
- * Previous sibling.
- *
- * @returns Node.
- */
- get previousSibling() {
- if (this.parentNode) {
- const index = this.parentNode.childNodes.indexOf(this);
- if (index > 0) {
- return this.parentNode.childNodes[index - 1];
- }
- }
- return null;
- }
- /**
- * Next sibling.
- *
- * @returns Node.
- */
- get nextSibling() {
- if (this.parentNode) {
- const index = this.parentNode.childNodes.indexOf(this);
- if (index > -1 && index + 1 < this.parentNode.childNodes.length) {
- return this.parentNode.childNodes[index + 1];
- }
- }
- return null;
- }
- /**
- * First child.
- *
- * @returns Node.
- */
- get firstChild() {
- if (this.childNodes.length > 0) {
- return this.childNodes[0];
- }
- return null;
- }
- /**
- * Last child.
- *
- * @returns Node.
- */
- get lastChild() {
- if (this.childNodes.length > 0) {
- return this.childNodes[this.childNodes.length - 1];
- }
- return null;
- }
- /**
- * Returns parent element.
- *
- * @returns Element.
- */
- get parentElement() {
- let parent = this.parentNode;
- while (parent && parent.nodeType !== Node.ELEMENT_NODE) {
- parent = parent.parentNode;
- }
- return parent;
- }
- /**
- * Returns base URI.
- *
- * @returns Base URI.
- */
- get baseURI() {
- const base = this.ownerDocument.querySelector('base');
- if (base) {
- return base.href;
- }
- return this.ownerDocument.defaultView.location.href;
- }
- /**
- * Returns "true" if the node has child nodes.
- *
- * @returns "true" if the node has child nodes.
- */
- hasChildNodes() {
- return this.childNodes.length > 0;
- }
- /**
- * Returns "true" if this node contains the other node.
- *
- * @param otherNode Node to test with.
- * @returns "true" if this node contains the other node.
- */
- contains(otherNode) {
- if (this === otherNode) {
- return true;
- }
- for (const childNode of this.childNodes) {
- if (childNode === otherNode || childNode.contains(otherNode)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Returns closest root node (Document or ShadowRoot).
- *
- * @param options Options.
- * @param options.composed A Boolean that indicates whether the shadow root should be returned (false, the default), or a root node beyond shadow root (true).
- * @returns Node.
- */
- getRootNode(options) {
- if (!this.isConnected) {
- return this;
- }
- if (this._rootNode && !options?.composed) {
- return this._rootNode;
- }
- return this.ownerDocument;
- }
- /**
- * Clones a node.
- *
- * @param [deep=false] "true" to clone deep.
- * @returns Cloned node.
- */
- cloneNode(deep = false) {
- const clone = new this.constructor();
- // Document has childNodes directly when it is created
- if (clone.childNodes.length) {
- for (const node of clone.childNodes.slice()) {
- node.parentNode.removeChild(node);
- }
- }
- if (deep) {
- for (const childNode of this.childNodes) {
- const childClone = childNode.cloneNode(true);
- childClone.parentNode = clone;
- clone.childNodes.push(childClone);
- }
- }
- clone.ownerDocument = this.ownerDocument;
- return clone;
- }
- /**
- * Append a child node to childNodes.
- *
- * @param node Node to append.
- * @returns Appended node.
- */
- appendChild(node) {
- if (node === this) {
- throw new DOMException_1.default('Not possible to append a node as a child of itself.');
- }
- // If the type is DocumentFragment, then the child nodes of if it should be moved instead of the actual node.
- // See: https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
- if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- for (const child of node.childNodes.slice()) {
- this.appendChild(child);
- }
- return node;
- }
- // Remove the node from its previous parent if it has any.
- if (node.parentNode) {
- const index = node.parentNode.childNodes.indexOf(node);
- if (index !== -1) {
- node.parentNode.childNodes.splice(index, 1);
- }
- }
- if (this.isConnected) {
- (this.ownerDocument || this)['_cacheID']++;
- }
- this.childNodes.push(node);
- node._connectToNode(this);
- // MutationObserver
- if (this._observers.length > 0) {
- const record = new MutationRecord_1.default();
- record.target = this;
- record.type = MutationTypeEnum_1.default.childList;
- record.addedNodes = [node];
- for (const observer of this._observers) {
- if (observer.options.subtree) {
- node._observe(observer);
- }
- if (observer.options.childList) {
- observer.callback([record]);
- }
- }
- }
- return node;
- }
- /**
- * Remove Child element from childNodes array.
- *
- * @param node Node to remove.
- * @returns Removed node.
- */
- removeChild(node) {
- const index = this.childNodes.indexOf(node);
- if (index === -1) {
- throw new DOMException_1.default('Failed to remove node. Node is not child of parent.');
- }
- if (this.isConnected) {
- (this.ownerDocument || this)['_cacheID']++;
- }
- this.childNodes.splice(index, 1);
- node._connectToNode(null);
- // MutationObserver
- if (this._observers.length > 0) {
- const record = new MutationRecord_1.default();
- record.target = this;
- record.type = MutationTypeEnum_1.default.childList;
- record.removedNodes = [node];
- for (const observer of this._observers) {
- node._unobserve(observer);
- if (observer.options.childList) {
- observer.callback([record]);
- }
- }
- }
- return node;
- }
- /**
- * Inserts a node before another.
- *
- * @param newNode Node to insert.
- * @param referenceNode Node to insert before.
- * @returns Inserted node.
- */
- insertBefore(newNode, referenceNode) {
- // If the type is DocumentFragment, then the child nodes of if it should be moved instead of the actual node.
- // See: https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
- if (newNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- for (const child of newNode.childNodes.slice()) {
- this.insertBefore(child, referenceNode);
- }
- return newNode;
- }
- if (referenceNode === null) {
- this.appendChild(newNode);
- return newNode;
- }
- if (referenceNode === undefined) {
- throw new DOMException_1.default("Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.", 'TypeError');
- }
- const index = referenceNode ? this.childNodes.indexOf(referenceNode) : 0;
- if (index === -1) {
- throw new DOMException_1.default("Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.");
- }
- if (this.isConnected) {
- (this.ownerDocument || this)['_cacheID']++;
- }
- if (newNode.parentNode) {
- const index = newNode.parentNode.childNodes.indexOf(newNode);
- if (index !== -1) {
- newNode.parentNode.childNodes.splice(index, 1);
- }
- }
- this.childNodes.splice(index, 0, newNode);
- newNode._connectToNode(this);
- // MutationObserver
- if (this._observers.length > 0) {
- const record = new MutationRecord_1.default();
- record.target = this;
- record.type = MutationTypeEnum_1.default.childList;
- record.addedNodes = [newNode];
- for (const observer of this._observers) {
- if (observer.options.subtree) {
- newNode._observe(observer);
- }
- if (observer.options.childList) {
- observer.callback([record]);
- }
- }
- }
- return newNode;
- }
- /**
- * Replaces a node with another.
- *
- * @param newChild New child.
- * @param oldChild Old child.
- * @returns Replaced node.
- */
- replaceChild(newChild, oldChild) {
- this.insertBefore(newChild, oldChild);
- this.removeChild(oldChild);
- return oldChild;
- }
- /**
- * @override
- */
- dispatchEvent(event) {
- const returnValue = super.dispatchEvent(event);
- if (event.bubbles && !event._propagationStopped) {
- if (this.parentNode) {
- return this.parentNode.dispatchEvent(event);
- }
- // eslint-disable-next-line
- if (event.composed && this.nodeType === NodeTypeEnum_1.default.documentFragmentNode && this.host) {
- // eslint-disable-next-line
- return this.host.dispatchEvent(event);
- }
- }
- return returnValue;
- }
- /**
- * Converts the node to a string.
- *
- * @param listener Listener.
- */
- toString() {
- return `[object ${this.constructor.name}]`;
- }
- /**
- * Observeres the node.
- * Used by MutationObserver, but it is not part of the HTML standard.
- *
- * @param listener Listener.
- */
- _observe(listener) {
- this._observers.push(listener);
- if (listener.options.subtree) {
- for (const node of this.childNodes) {
- node._observe(listener);
- }
- }
- }
- /**
- * Stops observing the node.
- * Used by MutationObserver, but it is not part of the HTML standard.
- *
- * @param listener Listener.
- */
- _unobserve(listener) {
- const index = this._observers.indexOf(listener);
- if (index !== -1) {
- this._observers.splice(index, 1);
- }
- if (listener.options.subtree) {
- for (const node of this.childNodes) {
- node._unobserve(listener);
- }
- }
- }
- /**
- * Connects this element to another element.
- *
- * @param parentNode Parent node.
- */
- _connectToNode(parentNode = null) {
- const isConnected = !!parentNode && parentNode.isConnected;
- if (this.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
- this.parentNode = parentNode;
- this._rootNode = isConnected && parentNode ? parentNode._rootNode : null;
- }
- if (this.isConnected !== isConnected) {
- this.isConnected = isConnected;
- if (isConnected && this.connectedCallback) {
- this.connectedCallback();
- }
- else if (!isConnected && this.disconnectedCallback) {
- if (this.ownerDocument['_activeElement'] === this) {
- this.ownerDocument['_activeElement'] = null;
- }
- this.disconnectedCallback();
- }
- for (const child of this.childNodes) {
- child._connectToNode(this);
- }
- // eslint-disable-next-line
- if (this._shadowRoot) {
- // eslint-disable-next-line
- this._shadowRoot._connectToNode(this);
- }
- }
- }
- /**
- * Reports the position of its argument node relative to the node on which it is called.
- *
- * @see https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
- * @param otherNode Other node.
- */
- compareDocumentPosition(otherNode) {
- /**
- * 1. If this is other, then return zero.
- */
- if (this === otherNode) {
- return 0;
- }
- /**
- * 2. Let node1 be other and node2 be this.
- */
- let node1 = otherNode;
- let node2 = this;
- /**
- * 3. Let attr1 and attr2 be null.
- */
- let attr1 = null;
- let attr2 = null;
- /**
- * 4. If node1 is an attribute, then set attr1 to node1 and node1 to attr1’s element.
- */
- if (node1.nodeType === Node.ATTRIBUTE_NODE) {
- attr1 = node1;
- node1 = attr1.ownerElement;
- }
- /**
- * 5. If node2 is an attribute, then:
- * 5.1. Set attr2 to node2 and node2 to attr2’s element.
- */
- if (node2.nodeType === Node.ATTRIBUTE_NODE) {
- attr2 = node2;
- node2 = attr2.ownerElement;
- /**
- * 5.2. If attr1 and node1 are non-null, and node2 is node1, then:
- */
- if (attr1 !== null && node1 !== null && node2 === node1) {
- /**
- * 5.2.1. For each attr in node2’s attribute list:
- */
- for (const attr of Object.values(node2.attributes)) {
- /**
- * 5.2.1.1. If attr equals attr1, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_PRECEDING.
- */
- if (NodeUtility_1.default.isEqualNode(attr, attr1)) {
- return (Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_PRECEDING);
- }
- /**
- * 5.2.1.2. If attr equals attr2, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_FOLLOWING.
- */
- if (NodeUtility_1.default.isEqualNode(attr, attr2)) {
- return (Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | Node.DOCUMENT_POSITION_FOLLOWING);
- }
- }
- }
- }
- const node2Ancestors = [];
- let node2Ancestor = node2;
- while (node2Ancestor) {
- /**
- * 7. If node1 is an ancestor of node2 […] then return the result of adding DOCUMENT_POSITION_CONTAINS to DOCUMENT_POSITION_PRECEDING.
- */
- if (node2Ancestor === node1) {
- return Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_PRECEDING;
- }
- node2Ancestors.push(node2Ancestor);
- node2Ancestor = node2Ancestor.parentNode;
- }
- const node1Ancestors = [];
- let node1Ancestor = node1;
- while (node1Ancestor) {
- /**
- * 8. If node1 is a descendant of node2 […] then return the result of adding DOCUMENT_POSITION_CONTAINED_BY to DOCUMENT_POSITION_FOLLOWING.
- */
- if (node1Ancestor === node2) {
- return Node.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_FOLLOWING;
- }
- node1Ancestors.push(node1Ancestor);
- node1Ancestor = node1Ancestor.parentNode;
- }
- const reverseArrayIndex = (array, reverseIndex) => {
- return array[array.length - 1 - reverseIndex];
- };
- const root = reverseArrayIndex(node2Ancestors, 0);
- /**
- * 6. If node1 or node2 is null, or node1’s root is not node2’s root, then return the result of adding
- * DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either
- * DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent, together.
- */
- if (!root || root !== reverseArrayIndex(node1Ancestors, 0)) {
- return (Node.DOCUMENT_POSITION_DISCONNECTED |
- Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
- Node.DOCUMENT_POSITION_FOLLOWING);
- }
- // Find the lowest common ancestor
- let commonAncestorIndex = 0;
- const ancestorsMinLength = Math.min(node2Ancestors.length, node1Ancestors.length);
- for (let i = 0; i < ancestorsMinLength; ++i) {
- const node2Ancestor = reverseArrayIndex(node2Ancestors, i);
- const node1Ancestor = reverseArrayIndex(node1Ancestors, i);
- if (node2Ancestor !== node1Ancestor) {
- break;
- }
- commonAncestorIndex = i;
- }
- const commonAncestor = reverseArrayIndex(node2Ancestors, commonAncestorIndex);
- // Indexes within the common ancestor
- let indexes = 0;
- let node2Index = -1;
- let node1Index = -1;
- const node2Node = reverseArrayIndex(node2Ancestors, commonAncestorIndex + 1);
- const node1Node = reverseArrayIndex(node1Ancestors, commonAncestorIndex + 1);
- const computeNodeIndexes = (nodes) => {
- for (const childNode of nodes) {
- computeNodeIndexes(childNode.childNodes);
- if (childNode === node2Node) {
- node2Index = indexes;
- }
- else if (childNode === node1Node) {
- node1Index = indexes;
- }
- if (node2Index !== -1 && node1Index !== -1) {
- break;
- }
- indexes++;
- }
- };
- computeNodeIndexes(commonAncestor.childNodes);
- /**
- * 9. If node1 is preceding node2, then return DOCUMENT_POSITION_PRECEDING.
- * 10. Return DOCUMENT_POSITION_FOLLOWING.
- */
- return node1Index < node2Index
- ? Node.DOCUMENT_POSITION_PRECEDING
- : Node.DOCUMENT_POSITION_FOLLOWING;
- }
- }
- exports.default = Node;
- // Owner document is set when the Node is created by the Document
- Node._ownerDocument = null;
- // Public properties
- Node.ELEMENT_NODE = NodeTypeEnum_1.default.elementNode;
- Node.ATTRIBUTE_NODE = NodeTypeEnum_1.default.attributeNode;
- Node.TEXT_NODE = NodeTypeEnum_1.default.textNode;
- Node.CDATA_SECTION_NODE = NodeTypeEnum_1.default.cdataSectionNode;
- Node.COMMENT_NODE = NodeTypeEnum_1.default.commentNode;
- Node.DOCUMENT_NODE = NodeTypeEnum_1.default.documentNode;
- Node.DOCUMENT_TYPE_NODE = NodeTypeEnum_1.default.documentTypeNode;
- Node.DOCUMENT_FRAGMENT_NODE = NodeTypeEnum_1.default.documentFragmentNode;
- Node.PROCESSING_INSTRUCTION_NODE = NodeTypeEnum_1.default.processingInstructionNode;
- Node.DOCUMENT_POSITION_CONTAINED_BY = NodeDocumentPositionEnum_1.default.containedBy;
- Node.DOCUMENT_POSITION_CONTAINS = NodeDocumentPositionEnum_1.default.contains;
- Node.DOCUMENT_POSITION_DISCONNECTED = NodeDocumentPositionEnum_1.default.disconnect;
- Node.DOCUMENT_POSITION_FOLLOWING = NodeDocumentPositionEnum_1.default.following;
- Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = NodeDocumentPositionEnum_1.default.implementationSpecific;
- Node.DOCUMENT_POSITION_PRECEDING = NodeDocumentPositionEnum_1.default.preceding;
- //# sourceMappingURL=Node.js.map
|