"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const PropertySymbol = __importStar(require("../../PropertySymbol.cjs")); const NodeTypeEnum_js_1 = __importDefault(require("./NodeTypeEnum.cjs")); /** * Node utility. */ class NodeUtility { /** * Returns whether the passed node is a text node, and narrows its type. * * @param node The node to be tested. * @returns "true" if the node is a text node. */ static isTextNode(node) { return node?.[PropertySymbol.nodeType] === NodeTypeEnum_js_1.default.textNode; } /** * Returns boolean indicating if "ancestorNode" is an inclusive ancestor of "referenceNode". * * Based on: * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js * * @see https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor * @param ancestorNode Ancestor node. * @param referenceNode Reference node. * @param [includeShadowRoots = false] Include shadow roots. * @returns "true" if inclusive ancestor. */ static isInclusiveAncestor(ancestorNode, referenceNode, includeShadowRoots = false) { if (ancestorNode === null || referenceNode === null) { return false; } if (ancestorNode === referenceNode) { return true; } if (!ancestorNode[PropertySymbol.nodeArray].length) { return false; } if (includeShadowRoots && referenceNode[PropertySymbol.isConnected] !== ancestorNode[PropertySymbol.isConnected]) { return false; } if (includeShadowRoots && ancestorNode === referenceNode[PropertySymbol.ownerDocument] && referenceNode[PropertySymbol.isConnected]) { return true; } let parent = referenceNode[PropertySymbol.parentNode]; while (parent) { if (ancestorNode === parent) { return true; } parent = parent[PropertySymbol.parentNode] ? parent[PropertySymbol.parentNode] : includeShadowRoots && parent.host ? parent.host : null; } return false; } /** * Returns boolean indicating if nodeB is following nodeA in the document tree. * * Based on: * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js * * @see https://dom.spec.whatwg.org/#concept-tree-following * @param nodeA Node A. * @param nodeB Node B. * @returns "true" if following. */ static isFollowing(nodeA, nodeB) { if (nodeA === nodeB) { return false; } let current = nodeB; while (current) { current = this.following(current); if (current === nodeA) { return true; } } return false; } /** * Node length. * * Based on: * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/helpers/node.js * * @see https://dom.spec.whatwg.org/#concept-node-length * @param node Node. * @returns Node length. */ static getNodeLength(node) { switch (node[PropertySymbol.nodeType]) { case NodeTypeEnum_js_1.default.documentTypeNode: return 0; case NodeTypeEnum_js_1.default.textNode: case NodeTypeEnum_js_1.default.processingInstructionNode: case NodeTypeEnum_js_1.default.commentNode: return node.data.length; default: return node[PropertySymbol.nodeArray].length; } } /** * Returns boolean indicating if nodeB is following nodeA in the document tree. * * Based on: * https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L220 * * @param node Node. * @param [root] Root. * @returns Following node. */ static following(node, root) { const firstChild = node.firstChild; if (firstChild) { return firstChild; } let current = node; while (current) { if (current === root) { return null; } const nextSibling = current.nextSibling; if (nextSibling) { return nextSibling; } current = current[PropertySymbol.parentNode]; } return null; } /** * Returns the next sibling or parents sibling. * * @param node Node. * @returns Next descendant node. */ static nextDescendantNode(node) { while (node && !node.nextSibling) { node = node[PropertySymbol.parentNode]; } if (!node) { return null; } return node.nextSibling; } /** * Needed by https://dom.spec.whatwg.org/#concept-node-equals * * @param elementA * @param elementB */ static attributeListsEqual(elementA, elementB) { const attributesA = Array.from(elementA[PropertySymbol.attributes][PropertySymbol.namedItems].values()); const attributesB = Array.from(elementB[PropertySymbol.attributes][PropertySymbol.namedItems].values()); for (const attributeA of attributesA) { let found = false; for (const attributeB of attributesB) { if (attributeA[PropertySymbol.namespaceURI] === attributeB[PropertySymbol.namespaceURI] && attributeA.localName === attributeB.localName && attributeA[PropertySymbol.value] === attributeB[PropertySymbol.value]) { found = true; break; } } if (!found) { return false; } } return true; } /** * Check if node nodeA equals node nodeB. * Reference: https://dom.spec.whatwg.org/#concept-node-equals * * @param nodeA Node A. * @param nodeB Node B. */ static isEqualNode(nodeA, nodeB) { if (nodeA[PropertySymbol.nodeType] !== nodeB[PropertySymbol.nodeType]) { return false; } switch (nodeA[PropertySymbol.nodeType]) { case NodeTypeEnum_js_1.default.documentTypeNode: const documentTypeA = nodeA; const documentTypeB = nodeB; if (documentTypeA.name !== documentTypeB.name || documentTypeA.publicId !== documentTypeB.publicId || documentTypeA.systemId !== documentTypeB.systemId) { return false; } break; case NodeTypeEnum_js_1.default.elementNode: const elementA = nodeA; const elementB = nodeB; if (elementA[PropertySymbol.namespaceURI] !== elementB[PropertySymbol.namespaceURI] || elementA[PropertySymbol.prefix] !== elementB[PropertySymbol.prefix] || elementA[PropertySymbol.localName] !== elementB[PropertySymbol.localName] || elementA[PropertySymbol.attributes][PropertySymbol.namespaceItems].size !== elementB[PropertySymbol.attributes][PropertySymbol.namespaceItems].size) { return false; } break; case NodeTypeEnum_js_1.default.attributeNode: const attributeA = nodeA; const attributeB = nodeB; if (attributeA[PropertySymbol.namespaceURI] !== attributeB[PropertySymbol.namespaceURI] || attributeA.localName !== attributeB.localName || attributeA[PropertySymbol.value] !== attributeB[PropertySymbol.value]) { return false; } break; case NodeTypeEnum_js_1.default.processingInstructionNode: const processingInstructionA = nodeA; const processingInstructionB = nodeB; if (processingInstructionA.target !== processingInstructionB.target || processingInstructionA.data !== processingInstructionB.data) { return false; } break; case NodeTypeEnum_js_1.default.textNode: case NodeTypeEnum_js_1.default.commentNode: const textOrCommentA = nodeA; const textOrCommentB = nodeB; if (textOrCommentA.data !== textOrCommentB.data) { return false; } break; } if (nodeA[PropertySymbol.nodeType] === NodeTypeEnum_js_1.default.elementNode && !NodeUtility.attributeListsEqual(nodeA, nodeB)) { return false; } if (nodeA[PropertySymbol.nodeArray].length !== nodeB[PropertySymbol.nodeArray].length) { return false; } for (let i = 0; i < nodeA[PropertySymbol.nodeArray].length; i++) { const childNodeA = nodeA[PropertySymbol.nodeArray][i]; const childNodeB = nodeB[PropertySymbol.nodeArray][i]; if (!NodeUtility.isEqualNode(childNodeA, childNodeB)) { return false; } } return true; } } exports.default = NodeUtility; //# sourceMappingURL=NodeUtility.cjs.map