ff-dom 2.0.9 → 3.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.cjs +1 -1
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cdn.js +1 -1
- package/dist/types/browser/browser/xpath.d.ts +43 -3
- package/dist/types/browser/utils/iosSelector.d.ts +7 -0
- package/dist/types/browser/utils/referenceXpath.d.ts +14 -2
- package/dist/types/browser/utils/xpath.d.ts +10 -2
- package/dist/types/browser/utils/xpathHelpers.d.ts +65 -3
- package/dist/types/node/utils/xpath.d.ts +10 -2
- package/dist/types/node/utils/xpathHelpers.d.ts +65 -3
- package/dist/xpath.mjs +1 -1
- package/dist/xpath.mjs.map +1 -1
- package/package.json +71 -71
package/dist/xpath.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xpath.mjs","sources":["../src/utils/xpathHelpers.ts","../src/utils/xpath.ts","../src/utils/cssSelector.ts","../src/utils/getElementsFromHTML.ts","../src/node/xpath.ts"],"sourcesContent":["export const reWhiteSpace = /^[\\S]+( [\\S]+)*$/i;\r\nexport let cspEnabled: boolean = false;\r\nconst xpathCache: { [x: string]: number } = {};\r\nlet relativeXPathCache = new Map();\r\nexport let modifiedElementAttributes: {\r\n url: string | null;\r\n attributeName: string | null;\r\n element: HTMLElement | Element;\r\n doc: Document;\r\n}[] = [];\r\nlet mutationObserver: MutationObserver;\r\n\r\nexport const createObserver = (addedNodeCallBack: Function) => {\r\n mutationObserver = new MutationObserver((mutations) => {\r\n mutations.forEach((mutation) => {\r\n if (mutation?.addedNodes?.length) {\r\n addedNodeCallBack(mutation?.addedNodes);\r\n }\r\n\r\n if (mutation.target instanceof HTMLElement) {\r\n if (\r\n mutation?.type === \"attributes\" &&\r\n mutation.attributeName === \"class\" &&\r\n ((isSvg(mutation.target) &&\r\n mutation.oldValue?.trim() ===\r\n mutation.target.classList.value?.trim()) ||\r\n mutation.target.classList?.value\r\n ?.replace(mutation.oldValue!, \"\")\r\n .trim() === \"marked-element-temp\" ||\r\n mutation?.oldValue\r\n ?.replace(mutation.target.classList?.value, \"\")\r\n .trim() === \"marked-element-temp\")\r\n ) {\r\n } else if (\r\n mutation?.type === \"attributes\" &&\r\n ![\"flndisabled\"].includes(mutation.attributeName!)\r\n ) {\r\n modifiedElementAttributes.push({\r\n url: mutation.target.baseURI,\r\n attributeName: mutation.attributeName,\r\n element: mutation.target,\r\n doc: mutation.target.ownerDocument,\r\n });\r\n }\r\n }\r\n });\r\n });\r\n};\r\nexport const startObserver = (\r\n target: Node,\r\n options: MutationObserverInit | undefined,\r\n) => {\r\n mutationObserver?.observe(target, options);\r\n};\r\n\r\nexport const stopObserver = () => {\r\n mutationObserver?.disconnect();\r\n};\r\n\r\nexport const isNumberExist = (str: string): boolean => {\r\n const hasNumber = /\\d/;\r\n return hasNumber.test(str);\r\n};\r\n\r\nexport const buildPattern = (\r\n pattern: string,\r\n isSvg: boolean,\r\n tagName: string,\r\n): string => {\r\n return isSvg ?\r\n `//*[local-name()='${tagName}' and ${pattern}]`\r\n : `//${tagName}[${pattern}]`;\r\n};\r\n\r\nexport const getTextContent = (\r\n targetElement: HTMLElement | Element,\r\n): string => {\r\n const textContent = targetElement?.textContent;\r\n if (cspEnabled) {\r\n if (textContent) {\r\n const tooltip = document.querySelector(\".flntooltip\") as HTMLElement;\r\n if (tooltip) {\r\n const lastIndex = textContent.lastIndexOf(tooltip.innerText);\r\n if (\r\n lastIndex &&\r\n textContent.length === lastIndex + tooltip.innerText.length\r\n ) {\r\n return textContent.substring(0, lastIndex);\r\n }\r\n } else {\r\n return textContent;\r\n }\r\n }\r\n } else {\r\n return textContent;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nexport const getFilteredText = (element: Node): string => {\r\n return element?.childNodes[0]?.nodeValue || \"\";\r\n};\r\n\r\nexport const getCountOfXPath = (\r\n xpath: string,\r\n element: HTMLElement | Element,\r\n docmt: Node,\r\n multiElementReferenceMode: boolean = false,\r\n): number => {\r\n try {\r\n let count;\r\n // Check if result is cached\r\n if (xpathCache[xpath] !== undefined) {\r\n count = xpathCache[xpath];\r\n } else {\r\n const owner =\r\n (\r\n docmt.nodeType === 9 // DOCUMENT_NODE\r\n ) ?\r\n (docmt as Document)\r\n : docmt.ownerDocument!;\r\n\r\n count = owner.evaluate(\r\n `count(${xpath})`,\r\n docmt,\r\n null,\r\n XPathResult.NUMBER_TYPE,\r\n null,\r\n ).numberValue;\r\n xpathCache[xpath] = count;\r\n }\r\n\r\n if (multiElementReferenceMode && Array.isArray(element)) {\r\n if (count > 1) {\r\n const elementsFromXpath = getElementFromXpath(xpath, docmt, true);\r\n let nodex,\r\n matchedCount = 0;\r\n if (elementsFromXpath instanceof XPathResult) {\r\n while ((nodex = elementsFromXpath?.iterateNext())) {\r\n if (element.includes(nodex)) {\r\n matchedCount += 1;\r\n\r\n if (matchedCount === 2) break;\r\n }\r\n }\r\n }\r\n\r\n if (matchedCount !== 2) return 0;\r\n }\r\n } else {\r\n // Short-circuit if we only need to match one element\r\n if (count === 1) {\r\n const elementFromXpath = getElementFromXpath(xpath, docmt);\r\n return elementFromXpath === element ? count : 0; // Return 0 if no match\r\n }\r\n }\r\n return count; // Return the count\r\n } catch (error) {\r\n console.error(`Error evaluating XPath: ${xpath}`, error);\r\n return 0; // Return 0 on error to avoid re-processing\r\n }\r\n};\r\n\r\nexport const escapeCharacters = (text: string): string => {\r\n if (text) {\r\n if (!(text.indexOf('\"') === -1)) {\r\n return `'${text}'`;\r\n }\r\n if (!(text.indexOf(\"'\") === -1)) {\r\n return `\"${text}\"`;\r\n }\r\n }\r\n return `'${text}'`;\r\n};\r\n\r\nexport const removeParenthesis = (xpath: string): string => {\r\n const charArr = xpath.split(\"\");\r\n\r\n let count = charArr.length;\r\n const indexArray = [];\r\n\r\n while (charArr[count - 2] !== \"[\") {\r\n indexArray.push(charArr[count - 2]);\r\n count--;\r\n }\r\n\r\n indexArray.reverse();\r\n let finalStr = \"\";\r\n for (let i = 0; i < indexArray.length; i++) {\r\n finalStr += indexArray[i];\r\n }\r\n\r\n const endBracketLength = finalStr.length + 2;\r\n let firstpart = xpath.slice(0, xpath.length - endBracketLength);\r\n\r\n if (firstpart.startsWith(\"(\") && firstpart.endsWith(\")\")) {\r\n firstpart = firstpart.slice(1, -1);\r\n } else {\r\n firstpart = xpath;\r\n }\r\n\r\n return firstpart;\r\n};\r\n\r\nexport const findXpathWithIndex = (\r\n val: string,\r\n node: HTMLElement | Element,\r\n docmt: Node,\r\n count: number,\r\n) => {\r\n try {\r\n const owner =\r\n (\r\n docmt.nodeType === 9 // DOCUMENT_NODE\r\n ) ?\r\n (docmt as Document)\r\n : docmt.ownerDocument!;\r\n\r\n let index = 0;\r\n if (count) {\r\n if (getCountOfXPath(`${val}[${count}]`, node, docmt) === 1) {\r\n return `${val}[${count}]`;\r\n }\r\n\r\n if (getCountOfXPath(`(${val})[${count}]`, node, docmt) === 1) {\r\n return `(${val})[${count}]`;\r\n }\r\n }\r\n\r\n const nodes = owner.evaluate(val, docmt, null, XPathResult.ANY_TYPE, null);\r\n let nodex;\r\n while ((nodex = nodes.iterateNext())) {\r\n index++;\r\n if (nodex.isSameNode(node)) {\r\n if (getCountOfXPath(`${val}[${index}]`, node, docmt) === 1) {\r\n return `${val}[${index}]`;\r\n }\r\n if (getCountOfXPath(`(${val})[${index}]`, node, docmt) === 1) {\r\n return `(${val})[${index}]`;\r\n }\r\n return;\r\n }\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nconst deleteLineGap = (a: string): string => {\r\n a &&= a.split(\"\\n\")[0].length > 0 ? a.split(\"\\n\")[0] : a.split(\"\\n\")[1];\r\n return a;\r\n};\r\n\r\nconst deleteGarbageFromInnerText = (a: string): string => {\r\n a = deleteLineGap(a);\r\n a = a\r\n .split(/[^\\u0000-\\u00ff]/)\r\n .reduce((b, c) => {\r\n return b.length > c.length ? b : c;\r\n }, \"\")\r\n .trim();\r\n return (a = a.split(\"/\")[0].trim());\r\n};\r\n\r\nexport const replaceWhiteSpaces = (str: string): string => {\r\n if (str) {\r\n return str.replace(/\\s\\s+/g, \" \").trim();\r\n }\r\n\r\n return str;\r\n};\r\n\r\nexport const getShadowRoot = (el: HTMLElement | Element): Element | null => {\r\n if (!el || !el?.getRootNode) return null;\r\n\r\n const root = el.getRootNode() as ShadowRoot;\r\n return root && root?.host ? (root as unknown as Element) : null;\r\n};\r\n\r\nexport const checkBlockedAttributes = (\r\n attribute: {\r\n name: string;\r\n value: string;\r\n },\r\n targetElement: HTMLElement | Element,\r\n isTarget: boolean,\r\n): boolean => {\r\n if (!attribute?.value || typeof attribute?.value === \"boolean\") {\r\n return false;\r\n }\r\n const blockedValues = [\r\n \"true\",\r\n \"false\",\r\n \"on\",\r\n \"off\",\r\n \"flntooltip\",\r\n \"flutter-highlight-overlay\",\r\n ];\r\n if (blockedValues.some((x) => x === attribute.value)) {\r\n return false;\r\n }\r\n const blockedNames = [\"type\", \"style\", \"locator-data-tooltip\", \"value\"];\r\n if (blockedNames.some((x) => x === attribute.name)) {\r\n return false;\r\n }\r\n\r\n const isModified = modifiedElementAttributes?.find(\r\n (x) =>\r\n x.doc === targetElement.ownerDocument &&\r\n x.element === targetElement &&\r\n x.attributeName === attribute.name,\r\n );\r\n if (isModified) {\r\n return false;\r\n }\r\n\r\n if (attribute?.name?.indexOf(\"on\") === 0 && attribute?.name?.length > 3) {\r\n return false;\r\n }\r\n\r\n if (typeof attribute.value === \"function\") {\r\n return false;\r\n }\r\n\r\n if (isTarget && isNumberExist(attribute.value)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n};\r\n\r\nexport const getRelationship = (a: HTMLElement, b: HTMLElement): string => {\r\n let pos = a.compareDocumentPosition(b);\r\n return (\r\n pos === 2 ? \"preceding\"\r\n : pos === 4 ? \"following\"\r\n : pos === 8 ? \"ancestor\"\r\n : pos === 16 ? \"descendant\"\r\n : pos === 32 ? \"self\"\r\n : \"\"\r\n );\r\n};\r\n\r\nexport const isSvg = (element: HTMLElement | Element) => {\r\n return element instanceof SVGElement;\r\n};\r\n\r\nexport const replaceTempAttributes = (str: string): string => {\r\n if (!str) return str;\r\n\r\n return str.replace(/\\b[a-zA-Z_]*disabled\\b/gi, \"disabled\");\r\n};\r\n\r\nconst getElementFromXpath = (xpath: string, docmt: Node, multi = false) => {\r\n let contextNode: Node = docmt;\r\n\r\n // XPath does NOT support DocumentFragment / ShadowRoot\r\n if (docmt.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\r\n contextNode = (docmt as ShadowRoot).host ?? docmt;\r\n }\r\n\r\n const owner =\r\n contextNode.nodeType === Node.DOCUMENT_NODE ?\r\n (contextNode as Document)\r\n : contextNode.ownerDocument!;\r\n\r\n if (multi) {\r\n return owner.evaluate(xpath, contextNode, null, XPathResult.ANY_TYPE, null);\r\n }\r\n\r\n return owner.evaluate(\r\n xpath,\r\n contextNode,\r\n null,\r\n XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null,\r\n ).singleNodeValue;\r\n};\r\n\r\nexport const getPropertyXPath = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n prop: string,\r\n value: string,\r\n isIndex: boolean,\r\n isTarget: boolean,\r\n) => {\r\n if (value) {\r\n const { tagName } = element;\r\n let count;\r\n let combinePattern = \"\";\r\n const mergePattern = [];\r\n let pattern;\r\n\r\n if (value && (!isTarget || !isNumberExist(value))) {\r\n if (!reWhiteSpace.test(value)) {\r\n pattern = buildPattern(\r\n `normalize-space(${prop})=${escapeCharacters(\r\n replaceWhiteSpaces(value),\r\n ).trim()}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase(),\r\n );\r\n } else {\r\n pattern = `//${tagName}[${prop}=${escapeCharacters(value)}]`;\r\n }\r\n\r\n count = getCountOfXPath(pattern, element, docmt);\r\n\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n\r\n if (value && isTarget) {\r\n const splitText = value.split(\" \");\r\n if (splitText?.length) {\r\n if (splitText.length === 1) {\r\n const contentRes = [...new Set(splitText[0].match(/([^0-9]+)/g))];\r\n if (contentRes?.length >= 1) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length > 1\r\n ) {\r\n if (value.startsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0]),\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n contentRes[0],\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (contentRes?.length > 1) {\r\n if (\r\n contentRes[contentRes.length - 1] &&\r\n replaceWhiteSpaces(contentRes[contentRes.length - 1].trim())\r\n ?.length > 1\r\n ) {\r\n if (value.endsWith(contentRes[contentRes.length - 1])) {\r\n if (!reWhiteSpace.test(contentRes[contentRes.length - 1])) {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[contentRes.length - 1]),\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n contentRes[contentRes.length - 1],\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n } else {\r\n const endIndex =\r\n splitText.length % 2 === 0 ?\r\n splitText.length / 2\r\n : splitText.length % 2;\r\n const startIndexString = splitText.slice(0, endIndex).join(\" \");\r\n let contentRes = [...new Set(startIndexString.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length\r\n ) {\r\n if (value.startsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0]),\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n contentRes[0],\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n\r\n const endIndexString = splitText\r\n .slice(endIndex, splitText.length - 1)\r\n .join(\" \");\r\n contentRes = [...new Set(endIndexString.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length > 3\r\n ) {\r\n if (value.endsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0]),\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n contentRes[0],\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (value && isTarget && isNumberExist(value)) {\r\n const contentRes = [...new Set(value.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces(contentRes[i].trim())?.length > 1\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n mergePattern.push(\r\n `contains(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i]),\r\n ).trim()})`,\r\n );\r\n } else {\r\n mergePattern.push(\r\n `contains(${prop},${escapeCharacters(\r\n contentRes[i].trim(),\r\n ).trim()})`,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (mergePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${mergePattern.join(\r\n \" and \",\r\n )}]`;\r\n } else {\r\n pattern = `//${tagName}[${mergePattern.join(\" and \")}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n }\r\n\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and text()]`;\r\n } else {\r\n pattern = `//${tagName}[text()]`;\r\n }\r\n\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n};\r\n\r\nexport const getAbsoluteXPath = (\r\n domNode: HTMLElement | Element | null,\r\n docmt: Document,\r\n): string => {\r\n try {\r\n if (!domNode) {\r\n return \"\";\r\n }\r\n\r\n let xpathe =\r\n isSvg(domNode) ?\r\n `/*[local-name()='${domNode.tagName}']`\r\n : `/${domNode.tagName}`;\r\n\r\n // // If this node has siblings of the same tagName, get the index of this node\r\n if (domNode.parentElement) {\r\n // Get the siblings\r\n const childNodes = Array.prototype.slice\r\n .call(domNode.parentElement.children, 0)\r\n .filter(\r\n (childNode: HTMLElement) => childNode.tagName === domNode.tagName,\r\n );\r\n\r\n // // If there's more than one sibling, append the index\r\n if (childNodes.length > 1) {\r\n const index = childNodes.indexOf(domNode);\r\n xpathe += `[${index + 1}]`;\r\n }\r\n } else if (domNode instanceof HTMLElement) {\r\n if (domNode.offsetParent) {\r\n const childNodes = Array.prototype.slice\r\n .call(domNode.offsetParent.children, 0)\r\n .filter(\r\n (childNode: HTMLElement) => childNode.tagName === domNode.tagName,\r\n );\r\n\r\n // // If there's more than one sibling, append the index\r\n if (childNodes.length > 1) {\r\n const index = childNodes.indexOf(domNode);\r\n xpathe += `[${index + 1}]`;\r\n }\r\n }\r\n }\r\n\r\n // // Make a recursive call to this nodes parents and prepend it to this xpath\r\n return getAbsoluteXPath(domNode?.parentElement, docmt) + xpathe;\r\n } catch (error) {\r\n // If there's an unexpected exception, abort and don't get an XPath\r\n console.log(\"xpath\", error);\r\n\r\n return \"\";\r\n }\r\n};\r\n\r\nexport const getRelativeXPath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean = false,\r\n attributesArray: Attr[],\r\n) => {\r\n try {\r\n // Generate a cache key based on the node's identifier, index, and target flag\r\n // Check if the result for this node is already cached\r\n if (relativeXPathCache.has(domNode)) {\r\n return relativeXPathCache.get(domNode);\r\n }\r\n\r\n // Initialize an array to hold parts of the XPath\r\n const xpathParts = [];\r\n let currentNode = domNode;\r\n\r\n // Traverse up the DOM tree iteratively instead of using recursion\r\n while (currentNode) {\r\n let xpathe: string | undefined = \"\";\r\n let hasUniqueAttr = false;\r\n let attributes =\r\n domNode === currentNode ?\r\n (attributesArray ?? currentNode.attributes)\r\n : currentNode.attributes;\r\n\r\n // Loop through attributes to check for unique identifiers\r\n for (const attrName of Array.from(attributes)) {\r\n if (checkBlockedAttributes(attrName, currentNode, isTarget)) {\r\n let attrValue = attrName.nodeValue;\r\n\r\n // Clean up attribute value\r\n attrValue = attrValue?.replace(\"removePointers\", \"\") ?? \"\";\r\n\r\n const elementName = attrName.name;\r\n\r\n // Construct an XPath based on attribute\r\n xpathe = getXpathString(currentNode, elementName, attrValue);\r\n\r\n let othersWithAttr: number = 0;\r\n if (xpathe) {\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n xpathParts.unshift(replaceTempAttributes(xpathe));\r\n hasUniqueAttr = true;\r\n break;\r\n }\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr,\r\n );\r\n if (xpathe) {\r\n xpathParts.unshift(replaceTempAttributes(xpathe));\r\n hasUniqueAttr = true;\r\n break;\r\n }\r\n // return replaceTempAttributes(xpathe);\r\n }\r\n }\r\n }\r\n\r\n if (currentNode.textContent) {\r\n if (\r\n !isTarget ||\r\n (isTarget && !isNumberExist(currentNode.textContent))\r\n ) {\r\n let reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n\r\n if (!reWhiteSpace.test(currentNode.textContent)) {\r\n xpathe =\r\n isSvg(currentNode) ?\r\n `//*[local-name()='${\r\n currentNode.tagName\r\n }' and normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode),\r\n )}]`\r\n : `//${\r\n currentNode.tagName || \"*\"\r\n }[normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode),\r\n )}]`;\r\n } else {\r\n xpathe =\r\n isSvg(currentNode) ?\r\n `//*[local-name()='${\r\n currentNode.tagName\r\n }' and .=${escapeCharacters(getFilteredText(currentNode))}]`\r\n : `//${currentNode.tagName || \"*\"}[.=${escapeCharacters(\r\n getFilteredText(currentNode),\r\n )}]`;\r\n }\r\n\r\n let othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n return xpathe;\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr,\r\n );\r\n return xpathe;\r\n }\r\n } else {\r\n let combinePattern: string[] = [];\r\n const contentRes = [\r\n ...new Set(getFilteredText(currentNode).match(/([^0-9]+)/g)),\r\n ];\r\n let reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces((contentRes[i] as string).trim())\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i] as string)) {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i]),\r\n ).trim()})`,\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n (contentRes[i] as string).trim(),\r\n ).trim()})`,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n xpathe =\r\n isSvg(currentNode) ?\r\n `//*[local-name()='${\r\n currentNode.tagName\r\n }' and ${combinePattern.join(\" and \")}]`\r\n : `//${currentNode.tagName || \"*\"}[${combinePattern.join(\r\n \" and \",\r\n )}]`;\r\n let othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n return xpathe;\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr,\r\n );\r\n return xpathe;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // If no unique attribute was found, construct XPath by tag name\r\n if (!hasUniqueAttr) {\r\n let tagBasedXPath =\r\n isSvg(currentNode) ?\r\n `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Handle sibling nodes\r\n if (currentNode.parentElement) {\r\n const siblings = Array.from(\r\n currentNode.parentElement.children,\r\n ).filter((childNode) => childNode.tagName === currentNode.tagName);\r\n\r\n // Append index to distinguish between siblings\r\n if (siblings.length > 1) {\r\n const index = siblings.indexOf(currentNode);\r\n tagBasedXPath += `[${index + 1}]`;\r\n }\r\n }\r\n\r\n // Add the constructed tag-based XPath to the parts array\r\n xpathParts.unshift(tagBasedXPath);\r\n } else {\r\n break;\r\n }\r\n\r\n // Move up to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Combine all parts into the final XPath\r\n const finalXPath = `${xpathParts.join(\"\")}`;\r\n\r\n // Cache the final XPath for this node\r\n relativeXPathCache.set(domNode, finalXPath);\r\n return finalXPath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nexport const getCombinationXpath = (\r\n attribute: Attr,\r\n domNode: HTMLElement | Element,\r\n) => {\r\n const combinePattern = [];\r\n let pattern: string = \"\";\r\n\r\n if (\r\n attribute &&\r\n !isNumberExist(attribute.value) &&\r\n typeof attribute.nodeValue !== \"function\" // &&\r\n // !modifiedElementAttributes?.find(\r\n // (x) => x.element === domNode && x.attributeName === attribute.name\r\n // )\r\n ) {\r\n const contentRes = [...new Set(attribute.value.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces(contentRes[i].trim())?.length > 2\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n combinePattern.push(\r\n `contains(@${attribute.name},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i]),\r\n ).trim()})`,\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(@${attribute.name},${escapeCharacters(\r\n contentRes[i].trim(),\r\n )})`,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n pattern =\r\n isSvg(domNode) ?\r\n `//*[local-name()='${domNode.tagName}' and ${combinePattern.join(\r\n \" and \",\r\n )}]`\r\n : `//${domNode.tagName}[${combinePattern.join(\" and \")}]`;\r\n return pattern;\r\n }\r\n }\r\n};\r\n\r\nexport const getAttributeCombinationXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n uniqueAttributes: Attr[],\r\n isTarget: boolean,\r\n): string | undefined => {\r\n try {\r\n const xpathAttributes = [];\r\n\r\n if (uniqueAttributes.length > 1) {\r\n for (const attrName of uniqueAttributes) {\r\n if (checkBlockedAttributes(attrName, domNode, isTarget)) {\r\n const attrValue = attrName.nodeValue;\r\n\r\n if (!reWhiteSpace.test(attrValue!)) {\r\n xpathAttributes.push(\r\n `normalize-space(@${attrName.nodeName})=\"${attrValue}\"`,\r\n );\r\n } else if (attrName.nodeName === \"class\") {\r\n xpathAttributes.push(\r\n `contains(@${attrName.nodeName},\"${attrValue}\")`,\r\n );\r\n } else {\r\n xpathAttributes.push(`@${attrName.nodeName}=\"${attrValue}\"`);\r\n }\r\n\r\n const xpathe =\r\n isSvg(domNode) ?\r\n `//*[local-name()='${domNode.tagName}' and ${xpathAttributes.join(\r\n \" and \",\r\n )}]`\r\n : `//${domNode.tagName}[${xpathAttributes.join(\" and \")}]`;\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = getCountOfXPath(xpathe, domNode, docmt);\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n if (othersWithAttr === 1 && !xpathe.includes(\"and\")) {\r\n return \"\";\r\n }\r\n // If the attribute isn't actually unique, get it's index too\r\n if (othersWithAttr === 1 && xpathe.includes(\"and\")) {\r\n return xpathe;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n // If there's an unexpected exception, abort and don't get an XPath\r\n console.log(`'${JSON.stringify(error, null, 2)}'`);\r\n }\r\n};\r\n\r\nexport const intermediateXpathStep = (\r\n targetElemt: HTMLElement | Element,\r\n attr: { name: string; value: string },\r\n isTarget: boolean,\r\n): string => {\r\n let isSvgElement = isSvg(targetElemt);\r\n let expression: string = \"\";\r\n\r\n if (checkBlockedAttributes(attr, targetElemt, isTarget)) {\r\n let attrValue = attr.value;\r\n attrValue = attrValue.replace(\"removePointers\", \"\");\r\n const elementName = attr.name;\r\n\r\n if (!reWhiteSpace.test(attrValue)) {\r\n expression =\r\n isSvgElement ?\r\n `*[local-name()='${\r\n targetElemt.tagName\r\n }' and normalize-space(@${elementName})=${escapeCharacters(\r\n attrValue,\r\n )}]`\r\n : `${\r\n targetElemt.tagName || \"*\"\r\n }[normalize-space(@${elementName})=${escapeCharacters(attrValue)}]`;\r\n } else {\r\n expression =\r\n isSvgElement ?\r\n `*[local-name()='${\r\n targetElemt.tagName\r\n }' and @${elementName}=${escapeCharacters(attrValue)}]`\r\n : `${targetElemt.tagName || \"*\"}[@${elementName}=${escapeCharacters(\r\n attrValue,\r\n )}]`;\r\n }\r\n }\r\n\r\n return expression;\r\n};\r\n\r\nexport const getFilteredTextXPath = (\r\n node: HTMLElement | Element,\r\n docmt: Document,\r\n): string => {\r\n if (!node.textContent) return \"\";\r\n\r\n const filteredText = getFilteredText(node);\r\n\r\n let xpathe;\r\n\r\n if (!reWhiteSpace.test(filteredText)) {\r\n xpathe =\r\n isSvg(node) ?\r\n `//*[local-name()='${\r\n node.tagName\r\n }' and normalize-space(.)=${escapeCharacters(filteredText)}]`\r\n : `//${node.tagName || \"*\"}[normalize-space(.)=${escapeCharacters(\r\n filteredText,\r\n )}]`;\r\n } else {\r\n xpathe =\r\n isSvg(node) ?\r\n `//*[local-name()='${node.tagName}' and .=${escapeCharacters(\r\n filteredText,\r\n )}]`\r\n : `//${node.tagName || \"*\"}[.=${escapeCharacters(filteredText)}]`;\r\n }\r\n\r\n return xpathe;\r\n};\r\n\r\nexport const getTextXpathFunction = (\r\n domNode: HTMLElement | Element,\r\n): string | undefined => {\r\n const trimmedText = getTextContent(domNode)?.trim();\r\n const filteredText =\r\n trimmedText ?\r\n escapeCharacters(deleteGarbageFromInnerText(trimmedText))\r\n : trimmedText;\r\n if (filteredText) {\r\n if (filteredText !== `'${trimmedText}'`) {\r\n return `contains(.,${filteredText})`;\r\n }\r\n return `normalize-space(.)='${trimmedText}'`;\r\n }\r\n};\r\n\r\nexport const getXpathString = (\r\n node: HTMLElement | Element,\r\n attrName: string,\r\n attrValue: string,\r\n): string => {\r\n const reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n let xpathe: string = \"\";\r\n\r\n if (attrValue) {\r\n if (!reWhiteSpace.test(attrValue)) {\r\n xpathe =\r\n isSvg(node) ?\r\n `//*[local-name()='${\r\n node.tagName\r\n }' and contains(@${attrName},${escapeCharacters(attrValue)})]`\r\n : `//${node.tagName || \"*\"}[contains(@${attrName},${escapeCharacters(\r\n attrValue,\r\n )})]`;\r\n } else if (attrName === \"class\") {\r\n xpathe =\r\n isSvg(node) ?\r\n `//*[local-name()='${\r\n node.tagName\r\n }' and contains(@${attrName},${escapeCharacters(attrValue)})]`\r\n : `//${node.tagName || \"*\"}[contains(@${attrName},${escapeCharacters(\r\n attrValue,\r\n )})]`;\r\n } else {\r\n xpathe =\r\n isSvg(node) ?\r\n `//*[local-name()='${\r\n node.tagName\r\n }' and @${attrName}=${escapeCharacters(attrValue)}]`\r\n : `//${node.tagName || \"*\"}[@${attrName}=${escapeCharacters(\r\n attrValue,\r\n )}]`;\r\n }\r\n }\r\n\r\n return xpathe;\r\n};\r\n\r\nexport const replaceActualAttributes = (\r\n str: string,\r\n element: { attributes: any },\r\n): string => {\r\n if (str) {\r\n return str.replace(/\\bdisabled\\b/gi, \"flndisabled\");\r\n }\r\n return str;\r\n};\r\n\r\nconst addAttributeSplitCombineXpaths = (\r\n attributes: NamedNodeMap,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n): { key: string; value: string }[] => {\r\n const attributesArray = Array.prototype.slice.call(attributes);\r\n const xpaths: { key: string; value: string }[] = [];\r\n try {\r\n attributesArray.map((element) => {\r\n if (checkBlockedAttributes(element, targetElemt, isTarget)) {\r\n const xpth = getCombinationXpath(element, targetElemt);\r\n if (xpth) {\r\n xpaths.push({\r\n key: `split xpath by ${element.name}`,\r\n value: xpth,\r\n });\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n\r\n return xpaths;\r\n};\r\n\r\nexport const getReferenceElementsXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n): { key: string; value: string }[] => {\r\n let nodeXpath1;\r\n const xpaths1 = [];\r\n if (\r\n domNode.textContent &&\r\n (!isTarget || (isTarget && !isNumberExist(domNode.textContent)))\r\n ) {\r\n if (!reWhiteSpace.test(domNode.textContent)) {\r\n nodeXpath1 =\r\n isSvg(domNode) ?\r\n `*[local-name()='${domNode.tagName}' and ${getTextXpathFunction(\r\n domNode,\r\n )})]`\r\n : `${domNode.tagName}[${getTextXpathFunction(domNode)}]`;\r\n if (nodeXpath1) {\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: nodeXpath1 });\r\n }\r\n } else {\r\n nodeXpath1 =\r\n isSvg(domNode) ?\r\n `*[local-name()='${domNode.tagName}' and .=${escapeCharacters(\r\n getTextContent(domNode),\r\n )}]`\r\n : `${domNode.tagName}[.=${escapeCharacters(getTextContent(domNode))}]`;\r\n if (nodeXpath1) {\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: nodeXpath1 });\r\n }\r\n }\r\n }\r\n\r\n if (domNode.attributes) {\r\n for (const attrName of Array.from(domNode.attributes)) {\r\n if (checkBlockedAttributes(attrName, domNode, isTarget)) {\r\n let attrValue = attrName.nodeValue;\r\n if (attrValue) {\r\n attrValue = attrValue.replace(\"removePointers\", \"\");\r\n const elementName = attrName.name;\r\n\r\n if (elementName === \"class\") {\r\n nodeXpath1 =\r\n isSvg(domNode) ?\r\n `*[local-name()='${\r\n domNode.tagName\r\n }' and contains(@${elementName},${escapeCharacters(\r\n attrValue,\r\n )})]`\r\n : `${domNode.tagName}[contains(@${elementName},${escapeCharacters(\r\n attrValue,\r\n )})]`;\r\n } else {\r\n nodeXpath1 =\r\n isSvg(domNode) ?\r\n `*[local-name()='${\r\n domNode.tagName\r\n }' and @${elementName}=${escapeCharacters(attrValue)}]`\r\n : `${domNode.tagName}[@${elementName}=${escapeCharacters(\r\n attrValue,\r\n )}]`;\r\n }\r\n\r\n if (nodeXpath1) {\r\n xpaths1.push({\r\n key: \"getReferenceElementsXpath\",\r\n value: nodeXpath1,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const attributesArray = Array.prototype.slice.call(domNode.attributes);\r\n if (attributesArray?.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n domNode,\r\n docmt,\r\n Array.prototype.slice.call(domNode.attributes),\r\n isTarget,\r\n );\r\n if (combinationXpath) {\r\n xpaths1.push({\r\n key: \"getReferenceElementsXpath\",\r\n value: combinationXpath,\r\n });\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const combinePattern = [];\r\n let pattern;\r\n const tag = isSvg(domNode);\r\n if (domNode.textContent && isTarget && isNumberExist(domNode.textContent)) {\r\n const contentRes = [...new Set(domNode.textContent.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces(contentRes[i].trim())?.length > 1\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i]),\r\n ).trim()})`,\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(contentRes[i]).trim()})`,\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (tag) {\r\n pattern = `//*[local-name()='${tag}' and ${combinePattern.join(\r\n \" and \",\r\n )}]`;\r\n } else {\r\n pattern = `//${tag}[${combinePattern.join(\" and \")}]`;\r\n }\r\n\r\n if (pattern)\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: pattern });\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const xpaths = addAttributeSplitCombineXpaths(\r\n domNode.attributes,\r\n domNode,\r\n docmt,\r\n isTarget,\r\n );\r\n if (xpaths?.length) {\r\n xpaths1.concat(xpaths);\r\n }\r\n }\r\n\r\n return xpaths1;\r\n};\r\n\r\nexport const parseXml = (\r\n xmlStr: string,\r\n type: DOMParserSupportedType,\r\n): Document | null => {\r\n if (window.DOMParser) {\r\n return new window.DOMParser().parseFromString(xmlStr, type);\r\n }\r\n\r\n return null;\r\n};\r\n\r\nexport const normalizeXPath = (xpath: string): string => {\r\n // Replace text() = \"value\" or text()='value'\r\n xpath = xpath.replace(\r\n /text\\(\\)\\s*=\\s*(['\"])(.*?)\\1/g,\r\n \"normalize-space(.)=$1$2$1\",\r\n );\r\n\r\n // Replace . = \"value\" or .='value'\r\n xpath = xpath.replace(/\\.\\s*=\\s*(['\"])(.*?)\\1/g, \"normalize-space(.)=$1$2$1\");\r\n\r\n return xpath;\r\n};\r\n\r\nexport const findMatchingParenthesis = (\r\n text: string,\r\n openPos: number,\r\n): number => {\r\n let closePos = openPos;\r\n let counter = 1;\r\n while (counter > 0) {\r\n const c = text[++closePos];\r\n if (c == \"(\") {\r\n counter++;\r\n } else if (c == \")\") {\r\n counter--;\r\n }\r\n }\r\n return closePos;\r\n};\r\n\r\nexport function canonicalizeXPath(xpath: string): string {\r\n return (\r\n xpath\r\n .toLowerCase()\r\n // replace quoted values\r\n .replace(/'[^']*'/g, \"?\")\r\n .replace(/\"[^\"]*\"/g, \"?\")\r\n // replace numbers in predicates\r\n .replace(/\\[\\d+\\]/g, \"[?]\")\r\n // normalize node names\r\n .replace(/\\/\\/[a-z0-9_-]+/g, \"//node\")\r\n .replace(/\\/[a-z0-9_-]+/g, \"/node\")\r\n );\r\n}\r\n\r\nexport function extractXPathSignatureParts(xpath: string) {\r\n const xp = xpath.toLowerCase();\r\n\r\n const axisMatch = xp.match(\r\n /(ancestor-or-self|ancestor|descendant-or-self|descendant|following-sibling|preceding-sibling|following|preceding|parent|child|self)::/,\r\n );\r\n const axis = axisMatch?.[1] ?? \"none\";\r\n\r\n const attrMatch = xp.match(/@([a-z0-9:-]+)/);\r\n const attribute = attrMatch?.[1] ?? \"none\";\r\n\r\n const usesNormalize = xp.includes(\"normalize-space\");\r\n\r\n return { axis, attribute, usesNormalize };\r\n}\r\n\r\nexport function getXPathPattern(xpath: string): string {\r\n const canonical = canonicalizeXPath(xpath);\r\n const parts = extractXPathSignatureParts(xpath);\r\n\r\n return [\r\n \"XPATH\",\r\n `axis:${parts.axis}`,\r\n `attr:${parts.attribute}`,\r\n `normalize:${parts.usesNormalize}`,\r\n `shape:${canonical}`,\r\n ].join(\"|\");\r\n}\r\n\r\nexport function escapeAttrValue(value: string): string {\r\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/ /g, \"\\\\ \");\r\n}\r\n\r\nexport function shouldUseSnapshot(xpath: string): boolean {\r\n return /\\[(?:\\s*\\.|\\s*contains\\s*\\(\\s*\\.|\\s*normalize-space\\s*\\(\\s*\\.)/.test(\r\n xpath,\r\n );\r\n}\r\n\r\nexport function isUniqueInDOM(\r\n docmt: Document,\r\n name: string,\r\n value: string,\r\n element?: Element | null,\r\n): boolean {\r\n const root = (element?.getRootNode?.() as ParentNode | null) ?? docmt;\r\n const queryAll = (selector: string): Element[] => {\r\n try {\r\n return Array.from(root.querySelectorAll(selector));\r\n } catch {\r\n return [];\r\n }\r\n };\r\n\r\n try {\r\n switch (name) {\r\n case \"id\":\r\n return queryAll(`#${escapeAttrValue(value)}`).length === 1;\r\n\r\n case \"name\":\r\n return queryAll(`[name=\"${escapeAttrValue(value)}\"]`).length === 1;\r\n\r\n case \"className\":\r\n return queryAll(`.${value}`).length === 1;\r\n\r\n case \"tagName\":\r\n return queryAll(value).length === 1;\r\n\r\n case \"linkText\":\r\n return (\r\n queryAll(\"a\").filter((a) => a.textContent?.trim() === value)\r\n .length === 1\r\n );\r\n case \"partialLinkText\":\r\n return (\r\n queryAll(\"a\").filter((a) => a.textContent?.includes(value)).length ===\r\n 1\r\n );\r\n case \"cssSelector\":\r\n return queryAll(value).length === 1;\r\n default:\r\n return false;\r\n }\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport const xpathUtils = {\r\n parseXml,\r\n getReferenceElementsXpath,\r\n getAbsoluteXPath,\r\n getRelativeXPath,\r\n getCombinationXpath,\r\n getAttributeCombinationXpath,\r\n getElementFromXpath,\r\n isSvg,\r\n findXpathWithIndex,\r\n isNumberExist,\r\n getTextContent,\r\n getCountOfXPath,\r\n normalizeXPath,\r\n getShadowRoot,\r\n escapeCharacters,\r\n removeParenthesis,\r\n checkBlockedAttributes,\r\n getRelationship,\r\n findMatchingParenthesis,\r\n deleteGarbageFromInnerText,\r\n replaceTempAttributes,\r\n createObserver,\r\n startObserver,\r\n stopObserver,\r\n modifiedElementAttributes,\r\n cspEnabled,\r\n getXPathPattern,\r\n escapeAttrValue,\r\n isUniqueInDOM,\r\n};\r\n","import {\r\n isNumberExist,\r\n getCountOfXPath,\r\n escapeCharacters,\r\n checkBlockedAttributes,\r\n replaceWhiteSpaces,\r\n getTextContent,\r\n getPropertyXPath,\r\n findXpathWithIndex,\r\n getFilteredText,\r\n intermediateXpathStep,\r\n getAttributeCombinationXpath,\r\n getFilteredTextXPath,\r\n isSvg,\r\n getXpathString,\r\n reWhiteSpace,\r\n} from \"./xpathHelpers.ts\";\r\n\r\nlet xpathData: { key: string; value: string }[] = [];\r\nlet xpathDataWithIndex: { key: string; value: string; count: number }[] = [];\r\nlet referenceElementMode: boolean = false;\r\nlet xpathCache = new Map();\r\nlet cache = new Map();\r\n\r\nconst parentXpathCache = new Map(); // Cache for parent XPaths\r\n\r\nconst checkRelativeXpathRelation = (\r\n nodeXpath1: string,\r\n nodeXpath2: string,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n relationType: string\r\n) => {\r\n if (nodeXpath1 && !referenceElementMode) {\r\n let xpaths;\r\n\r\n if (relationType === \"parent\") {\r\n xpaths = [\r\n // `${nodeXpath1}/descendant::${nodeXpath2}`,\r\n `${nodeXpath1}/descendant-or-self::${nodeXpath2}`,\r\n `${nodeXpath1}/following::${nodeXpath2}`,\r\n ];\r\n } else {\r\n xpaths = [\r\n // `${nodeXpath1}/descendant::${nodeXpath2}`,\r\n `${nodeXpath1}/ancestor-or-self::${nodeXpath2}`,\r\n `${nodeXpath1}/preceding::${nodeXpath2}`,\r\n ];\r\n }\r\n\r\n // Iterate through XPath patterns\r\n for (const xpath of xpaths) {\r\n // Check if result is already cached to avoid recomputation\r\n if (!xpathCache?.get(xpath)) {\r\n // Compute and store result in cache\r\n xpathCache.set(xpath, getCountOfXPath(xpath, targetElemt, docmt));\r\n }\r\n\r\n const count = xpathCache?.get(xpath);\r\n\r\n // Short-circuit: Return the first valid XPath result found\r\n if (count === 1) {\r\n return xpath;\r\n }\r\n if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: xpath,\r\n count,\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: xpath,\r\n count,\r\n });\r\n }\r\n }\r\n\r\n if (count > 1 && isIndex && !xpathData.length) {\r\n // Try finding XPath with index if count is greater than 1\r\n const indexedXpath = findXpathWithIndex(\r\n xpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n\r\n // Cache the indexed XPath result\r\n if (\r\n indexedXpath &&\r\n getCountOfXPath(indexedXpath, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: indexedXpath,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n return null;\r\n};\r\n\r\nconst getUniqueParentXpath = (\r\n domNode: HTMLElement,\r\n docmt: Document,\r\n node: HTMLElement | Element,\r\n isTarget: boolean,\r\n nodeXpath: string,\r\n isIndex: boolean\r\n) => {\r\n try {\r\n if (parentXpathCache.has(domNode)) {\r\n return parentXpathCache.get(domNode);\r\n }\r\n\r\n // Direct XPath construction without loops\r\n const xpathParts = [];\r\n let currentNode = domNode;\r\n\r\n while (currentNode && currentNode.nodeType === 1) {\r\n const hasUniqueAttr = false;\r\n for (const attrName of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attrName, currentNode, isTarget)) {\r\n let attrValue = attrName.nodeValue;\r\n\r\n attrValue = attrValue?.replace(\"removePointers\", \"\") ?? \"\";\r\n const elementName = attrName.name;\r\n const xpathe = getXpathString(currentNode, elementName, attrValue);\r\n\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute isn't actually unique, get it's index too\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n }\r\n }\r\n\r\n if (currentNode.textContent && !currentNode.textContent) {\r\n if (\r\n !isTarget ||\r\n (isTarget && !isNumberExist(currentNode.textContent))\r\n ) {\r\n let xpathe;\r\n\r\n if (!reWhiteSpace.test(currentNode.textContent)) {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${currentNode.tagName\r\n }' and normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`\r\n : `//${currentNode.tagName || \"*\"\r\n }[normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`;\r\n } else {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${currentNode.tagName\r\n }' and .=${escapeCharacters(getFilteredText(currentNode))}]`\r\n : `//${currentNode.tagName || \"*\"}[.=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`;\r\n }\r\n\r\n const othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n } else {\r\n const combinePattern = [];\r\n const contentRes = [\r\n ...new Set(getFilteredText(currentNode).match(/([^0-9]+)/g)),\r\n ];\r\n const reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (contentRes[i] && replaceWhiteSpaces(contentRes[i].trim())) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i])\r\n ).trim()})`\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n contentRes[i].trim()\r\n ).trim()})`\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n const xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${currentNode.tagName\r\n }' and ${combinePattern.join(\" and \")}]`\r\n : `//${currentNode.tagName || \"*\"}[${combinePattern.join(\r\n \" and \"\r\n )}]`;\r\n const othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Construct the XPath based on the tag name\r\n if (!hasUniqueAttr) {\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n xpathParts.unshift(xpathe);\r\n } else {\r\n break;\r\n }\r\n\r\n // Move to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Final constructed XPath\r\n const finalXPath = xpathParts.join(\"\") + nodeXpath;\r\n let count = getCountOfXPath(finalXPath, domNode, docmt);\r\n if (count === 1) {\r\n parentXpathCache.set(domNode, finalXPath); // Cache final result\r\n return finalXPath;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getParentRelativeXpath = (\r\n domNode: HTMLElement,\r\n docmt: Document,\r\n node: HTMLElement | Element,\r\n isTarget: boolean\r\n) => {\r\n const cache = new Map(); // Cache to store computed results\r\n\r\n if (cache.has(domNode)) {\r\n return cache.get(domNode); // Return cached result if available\r\n }\r\n\r\n const xpathParts = []; // Initialize an array to hold parts of the XPath\r\n let currentNode = domNode; // Start with the provided DOM node\r\n\r\n try {\r\n while (currentNode && currentNode.nodeType === 1) {\r\n // BASE CASE #1: If this isn't an element, we're above the root, return empty string\r\n if (!currentNode.tagName) {\r\n return \"\";\r\n }\r\n\r\n // BASE CASE #2: Check for unique attributes\r\n let uniqueAttrFound = false;\r\n for (const attr of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attr, currentNode, isTarget)) {\r\n let attrValue = attr.nodeValue;\r\n\r\n attrValue = attrValue?.replace(\"removePointers\", \"\") ?? \"\";\r\n const elementName = attr.name;\r\n\r\n const xpathe = getXpathString(currentNode, elementName, attrValue);\r\n\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute is unique, return its XPath\r\n if (othersWithAttr === 1) {\r\n xpathParts.unshift(xpathe);\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // If no unique attributes, check for text content\r\n if (!uniqueAttrFound && currentNode.textContent && !node.textContent) {\r\n const textXPath = getFilteredTextXPath(currentNode, docmt);\r\n if (textXPath) {\r\n const othersWithAttr = getCountOfXPath(textXPath, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.unshift(textXPath);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!uniqueAttrFound) {\r\n // Construct the XPath based on the tag name\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Prepend the current XPath part to the array\r\n xpathParts.unshift(xpathe);\r\n } else {\r\n break;\r\n }\r\n // Move to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Combine all parts into the final XPath\r\n const finalXpath = xpathParts.join(\"\");\r\n cache.set(domNode, finalXpath); // Store result in cache\r\n return finalXpath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getChildRelativeXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n node: HTMLElement | Element\r\n) => {\r\n const xpathParts = []; // Initialize an array to hold parts of the XPath.\r\n let currentNode: HTMLElement | Element | null;\r\n\r\n const st = [];\r\n if (\r\n domNode.firstElementChild != null &&\r\n domNode.firstElementChild.classList.contains(\"flntooltip\")\r\n ) {\r\n st.unshift(domNode.firstElementChild);\r\n } else if (domNode.nextElementSibling != null)\r\n st.unshift(domNode.nextElementSibling);\r\n\r\n for (\r\n let m = domNode.parentElement;\r\n m != null && m.nodeType === 1;\r\n m = m.parentElement\r\n ) {\r\n if (m.nextElementSibling) st.unshift(m.nextElementSibling);\r\n }\r\n\r\n try {\r\n do {\r\n let uniqueAttrFound = false;\r\n for (currentNode = st.pop()!; currentNode !== null;) {\r\n for (const attr of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attr, currentNode, true)) {\r\n let attrValue = attr.nodeValue;\r\n\r\n attrValue = attrValue?.replace(\"removePointers\", \"\") ?? \"\";\r\n const elementName = attr.name;\r\n\r\n const xpathe = getXpathString(currentNode, elementName, attrValue);\r\n\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute is unique, return its XPath\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.push(xpathe);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // If no unique attributes, check for text content\r\n if (!uniqueAttrFound && currentNode.textContent && !node.textContent) {\r\n const textXPath = getFilteredTextXPath(currentNode, docmt);\r\n if (textXPath) {\r\n const othersWithAttr = getCountOfXPath(\r\n textXPath,\r\n currentNode,\r\n docmt\r\n );\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.push(textXPath);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!uniqueAttrFound) {\r\n // Construct the XPath based on the tag name\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Prepend the current XPath part to the array\r\n xpathParts.push(xpathe);\r\n\r\n if (currentNode.firstElementChild != null) {\r\n st.push(currentNode.nextElementSibling);\r\n currentNode = currentNode.firstElementChild;\r\n } else {\r\n currentNode = currentNode.nextElementSibling;\r\n }\r\n } else {\r\n break;\r\n }\r\n }\r\n } while (st.length > 0);\r\n\r\n // Combine all parts into the final XPath\r\n const finalXpath = xpathParts.join(\"\");\r\n cache.set(domNode, finalXpath); // Store result in cache\r\n return finalXpath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getSiblingRelativeXPath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n nodeXpath: string\r\n) => {\r\n try {\r\n const markedSpan = document.querySelector(\".flntooltip\");\r\n\r\n for (\r\n let m = domNode.nextElementSibling;\r\n m !== null && m !== markedSpan;\r\n m = m.nextElementSibling\r\n ) {\r\n processSibling(\r\n m,\r\n domNode,\r\n docmt,\r\n nodeXpath,\r\n \"preceding-sibling\",\r\n isTarget\r\n );\r\n }\r\n\r\n for (\r\n let n = domNode.previousElementSibling;\r\n n !== null && n !== markedSpan;\r\n n = n.previousElementSibling\r\n ) {\r\n processSibling(\r\n n,\r\n domNode,\r\n docmt,\r\n nodeXpath,\r\n \"following-sibling\",\r\n isTarget\r\n );\r\n }\r\n } catch (error) {\r\n console.error(\"sibling error\", error);\r\n return null;\r\n }\r\n};\r\n\r\nconst processSibling = (\r\n sibling: Element,\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n nodeXpath: string,\r\n axis: string,\r\n isTarget: boolean\r\n) => {\r\n try {\r\n if (sibling.hasAttributes()) {\r\n for (const attr of Array.from(sibling.attributes)) {\r\n let xpathe = intermediateXpathStep(\r\n sibling,\r\n {\r\n name: attr.name,\r\n value: attr.value,\r\n },\r\n isTarget\r\n );\r\n if (xpathe) {\r\n xpathe += `/${axis}::${nodeXpath}`;\r\n\r\n const count = getCountOfXPath(xpathe, sibling, docmt);\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `xpath by ${axis}`,\r\n value: xpathe,\r\n });\r\n return;\r\n } else if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count,\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!isTarget) {\r\n let xpathe;\r\n xpathe = intermediateXpathStep(\r\n sibling,\r\n {\r\n name: \"text\",\r\n value: sibling.textContent,\r\n },\r\n isTarget\r\n );\r\n\r\n if (xpathe) {\r\n const count = getCountOfXPath(xpathe, sibling, docmt);\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `xpath by ${axis}`,\r\n value: xpathe,\r\n });\r\n return;\r\n } else if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count,\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.log(`${axis} xpath-error`, error);\r\n }\r\n};\r\n\r\nfunction getXPathUsingAttributeAndText(\r\n attributes: Attr[],\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean\r\n) {\r\n const { tagName } = targetElemt;\r\n const textContent = targetElemt.textContent.trim();\r\n for (const attrName of attributes) {\r\n if (checkBlockedAttributes(attrName, targetElemt, isTarget)) {\r\n let attrValue = attrName.nodeValue;\r\n\r\n attrValue = attrValue?.replace(\"removePointers\", \"\") ?? \"\";\r\n const elementName = attrName.name;\r\n const xpath = `//${tagName}[@${elementName}='${attrValue}' and text()='${textContent}']`;\r\n if (xpath) {\r\n const count = getCountOfXPath(xpath, targetElemt, docmt);\r\n if (count == 1) {\r\n return xpath;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nconst addRelativeXpaths = (\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean,\r\n attribute: Attr[]\r\n) => {\r\n try {\r\n let nodeXpath: string[] = [];\r\n let relativeXpath, relativeChildXpath;\r\n xpathData = [];\r\n\r\n console.log(attribute);\r\n if (attribute) {\r\n for (const attrName of attribute) {\r\n let expression = intermediateXpathStep(\r\n targetElemt,\r\n {\r\n name: attrName.name,\r\n value: attrName.value,\r\n },\r\n isTarget\r\n );\r\n\r\n console.log(expression);\r\n if (expression) {\r\n nodeXpath.push(expression);\r\n }\r\n }\r\n }\r\n\r\n if (targetElemt.textContent) {\r\n let expression = intermediateXpathStep(\r\n targetElemt,\r\n {\r\n name: \"text\",\r\n value: targetElemt.textContent,\r\n },\r\n isTarget\r\n );\r\n\r\n console.log(expression);\r\n if (expression) {\r\n nodeXpath.push(expression);\r\n }\r\n }\r\n\r\n nodeXpath.push(targetElemt.tagName);\r\n\r\n if (nodeXpath?.length) {\r\n for (let i = 0; i < nodeXpath.length; i++) {\r\n if (!xpathData.length) {\r\n getSiblingRelativeXPath(targetElemt, docmt, isTarget, nodeXpath[i]);\r\n\r\n if (!xpathData.length) {\r\n if (!relativeXpath) {\r\n relativeXpath = getParentRelativeXpath(\r\n targetElemt.parentElement!,\r\n docmt,\r\n targetElemt,\r\n isTarget\r\n );\r\n }\r\n\r\n console.log(relativeXpath);\r\n\r\n if (\r\n relativeXpath &&\r\n (relativeXpath.includes(\"@\") ||\r\n relativeXpath.includes(\"text()\") ||\r\n relativeXpath.includes(\".=\")) &&\r\n relativeXpath.match(/\\//g)?.length - 2 < 5\r\n ) {\r\n const fullRelativeXpath = relativeXpath + `/${nodeXpath[i]}`;\r\n const count = getCountOfXPath(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt\r\n );\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: \"relative xpath by relative parent\",\r\n value: fullRelativeXpath,\r\n });\r\n } else if (count > 1 && isIndex) {\r\n const relativeXpathIndex = findXpathWithIndex(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n if (\r\n relativeXpathIndex &&\r\n getCountOfXPath(relativeXpathIndex, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by relative parent ${isIndex ? \"index\" : \"\"\r\n }`,\r\n value: relativeXpathIndex,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n if (!relativeChildXpath) {\r\n relativeChildXpath = getChildRelativeXpath(\r\n targetElemt,\r\n docmt,\r\n targetElemt\r\n );\r\n }\r\n\r\n if (\r\n relativeChildXpath &&\r\n (relativeChildXpath.includes(\"@\") ||\r\n relativeChildXpath.includes(\"text()\") ||\r\n relativeChildXpath.includes(\".=\"))\r\n ) {\r\n const fullRelativeXpath = `/${nodeXpath[i] + relativeChildXpath.substring(1)\r\n }`;\r\n const count = getCountOfXPath(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt\r\n );\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: \"relative xpath by relative child\",\r\n value: fullRelativeXpath,\r\n });\r\n } else if (count > 1 && isIndex) {\r\n const relativeXpathIndex = findXpathWithIndex(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n if (\r\n relativeXpathIndex &&\r\n getCountOfXPath(relativeXpathIndex, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by relative parent ${isIndex ? \"index\" : \"\"\r\n }`,\r\n value: relativeXpathIndex,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (\r\n xpathData?.length === 1 &&\r\n xpathData?.[0]?.value?.match(/\\[([0-9]+)\\]/gm)?.length! > 3 &&\r\n !referenceElementMode\r\n ) {\r\n if (targetElemt.textContent) {\r\n const txtXpath = getTextXPath(targetElemt, docmt, isIndex, false);\r\n if (txtXpath) {\r\n xpathData.unshift({\r\n key: `xpath by text${isIndex ? \"index\" : \"\"}`,\r\n value: txtXpath,\r\n });\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n let tempRelativeXpath = getUniqueParentXpath(\r\n targetElemt.parentElement!,\r\n docmt,\r\n targetElemt,\r\n isTarget,\r\n nodeXpath[i],\r\n isIndex\r\n );\r\n\r\n if (tempRelativeXpath) {\r\n xpathData.push({\r\n key: \"xpath by unique parent\",\r\n value: tempRelativeXpath,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return xpathData;\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nexport const attributesBasedXPath = (\r\n attr: Attr,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n let attrName;\r\n\r\n attrName = attr.name;\r\n let xpath = getPropertyXPath(\r\n targetElemt,\r\n docmt,\r\n `@${attrName}`,\r\n attr.value,\r\n isIndex,\r\n isTarget\r\n );\r\n\r\n return xpath;\r\n};\r\n\r\nexport const getUniqueClassName = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n let value = element.className;\r\n if (typeof value !== \"string\") {\r\n value = \"\";\r\n }\r\n value = value?.replace(\"flndisabled\", \"disabled\");\r\n value = value?.replace(\"removePointers\", \"\");\r\n value = value?.trim();\r\n\r\n if (value) {\r\n return getPropertyXPath(element, docmt, `@class`, value, isIndex, isTarget);\r\n }\r\n};\r\n\r\nexport const getTextXPath = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n if (((element.textContent ?? \"\").trim() != \"\")) {\r\n const text = getTextContent(element);\r\n\r\n if (text) {\r\n return getPropertyXPath(element, docmt, \".\", text, isIndex, isTarget);\r\n }\r\n }\r\n};\r\n\r\nconst addAllXPathAttributes = (\r\n attributes: Attr[],\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n const attributesArray = attributes;\r\n try {\r\n attributesArray.map((attr) => {\r\n if (!(attr.name === \"className\" || attr.name === \"class\")) {\r\n if (checkBlockedAttributes(attr, targetElemt, isTarget)) {\r\n const xpth = attributesBasedXPath(\r\n attr,\r\n targetElemt,\r\n docmt,\r\n isIndex,\r\n isTarget\r\n );\r\n if (xpth) {\r\n xpathData.push({\r\n key: `xpath by ${attr.name}${isIndex ? \" index\" : \"\"}`,\r\n value: xpth,\r\n });\r\n }\r\n }\r\n }\r\n });\r\n\r\n const txtXpath = getTextXPath(targetElemt, docmt, isIndex, isTarget);\r\n if (txtXpath) {\r\n xpathData.push({\r\n key: `xpath by text${isIndex ? \" index\" : \"\"}`,\r\n value: txtXpath,\r\n });\r\n }\r\n\r\n if (\r\n attributesArray.find((element) => element.name === \"className\") &&\r\n checkBlockedAttributes(\r\n attributesArray?.find((element) => element.name === \"className\")!,\r\n targetElemt,\r\n isTarget\r\n )\r\n ) {\r\n let xpath = getUniqueClassName(targetElemt, docmt, isIndex, isTarget);\r\n if (xpath) {\r\n xpathData.push({\r\n key: \"xpath by class\",\r\n value: xpath,\r\n });\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n const textAttribute = getXPathUsingAttributeAndText(\r\n attributes,\r\n targetElemt,\r\n docmt,\r\n isTarget\r\n );\r\n if (textAttribute)\r\n xpathData.push({\r\n key: \"xpath by textAttribute\",\r\n value: textAttribute,\r\n });\r\n }\r\n\r\n if (!xpathData.length && attributesArray.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n targetElemt,\r\n docmt,\r\n attributesArray,\r\n isTarget\r\n );\r\n if (combinationXpath)\r\n xpathData.push({\r\n key: \"xpath by combination\",\r\n value: combinationXpath,\r\n });\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nexport const parseDOM = (\r\n element: HTMLElement | Element,\r\n doc: Document,\r\n isIndex: boolean,\r\n isTarget: boolean,\r\n includedAttributes: Attr[] = []\r\n) => {\r\n xpathData = [];\r\n console.log(element);\r\n const targetElemt = element;\r\n const docmt = targetElemt?.ownerDocument || doc;\r\n const tag = targetElemt.tagName;\r\n const { attributes } = targetElemt;\r\n const attributesToUse =\r\n includedAttributes.length > 0 ? includedAttributes : Array.from(attributes);\r\n addAllXPathAttributes(\r\n attributesToUse,\r\n targetElemt,\r\n docmt,\r\n isIndex,\r\n isTarget\r\n );\r\n if (!referenceElementMode) {\r\n if (xpathData.length) {\r\n const len = xpathData.length;\r\n for (let i = 0; i < len; i++) {\r\n let xpth = xpathData[i].value;\r\n xpth = \"//*\" + xpth.substring(xpth.indexOf(\"//\") + 2 + tag.length);\r\n const count = getCountOfXPath(xpth, element, docmt);\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `${xpathData[i].key} regex`,\r\n value: xpth,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n addRelativeXpaths(\r\n targetElemt,\r\n docmt,\r\n isIndex,\r\n isTarget,\r\n Array.from(targetElemt.attributes)\r\n );\r\n }\r\n\r\n return xpathData;\r\n};\r\n\r\nconst xpath = {\r\n parseDOM,\r\n getTextXPath,\r\n getUniqueClassName,\r\n attributesBasedXPath,\r\n addAllXPathAttributes,\r\n addRelativeXpaths,\r\n getXPathUsingAttributeAndText,\r\n getSiblingRelativeXPath,\r\n getChildRelativeXpath,\r\n getParentRelativeXpath,\r\n getUniqueParentXpath,\r\n checkRelativeXpathRelation,\r\n};\r\n\r\nexport default xpath;\r\n","import { SelectorMode } from \"../types/locator.ts\";\r\nimport { isNumberExist } from \"./xpathHelpers.ts\";\r\n\r\nlet modifiedElementAttributes: [] = [];\r\n\r\nconst escapeCssAttributeValue = (value: string): string => {\r\n return String(value).replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\r\n};\r\n\r\nconst isCssSelectorUnique = (\r\n selector: string,\r\n el: Element,\r\n root: Document | ShadowRoot | ParentNode\r\n): boolean => {\r\n try {\r\n const matches = root.querySelectorAll(selector);\r\n return matches.length === 1 && matches[0] === el;\r\n } catch {\r\n return false;\r\n }\r\n};\r\n\r\n\r\nexport const parseCssSelectors = (el: Element, mode: SelectorMode='single') => {\r\n const selectors: { key: string; value: string }[] = [];\r\n const root = el.getRootNode() as Document | ShadowRoot;\r\n\r\n try {\r\n const idPath = getIdCssPath(el);\r\n if (idPath && isCssSelectorUnique(idPath, el, root)) {\r\n selectors.push({ key: 'cssSelector by id', value: idPath });\r\n if (mode === 'single') return selectors;\r\n }\r\n const classPath = getClassCssPath(el);\r\n if (classPath && isCssSelectorUnique(classPath, el, root)) {\r\n selectors.push({ key: 'cssSelector by class', value: classPath });\r\n }\r\n const namePath = getAttributeCssPath(el);\r\n if (namePath) {\r\n selectors.push({ key: 'cssSelector by name', value: namePath });\r\n }\r\n const absPath = getAbsoluteCssPath(el);\r\n if (absPath && isCssSelectorUnique(absPath, el, root)) {\r\n selectors.push({ key: 'Absolute cssSelector', value: absPath });\r\n }\r\n } catch (e) {\r\n console.error(e);\r\n }\r\n return selectors;\r\n};\r\n\r\n\r\nexport const getIdCssPath = (el: HTMLElement | Element,) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n const tagName = el.tagName.toLowerCase();\r\n if (tagName.includes('style') || tagName.includes('script')) return;\r\n\r\n const path = [];\r\n while (el?.nodeType === Node.ELEMENT_NODE) {\r\n let selector = el.nodeName?.toLowerCase();\r\n if (el.id && !isNumberExist(el.id)) {\r\n selector += `#${CSS.escape(el.id)}`;\r\n path.unshift(selector);\r\n break;\r\n } else {\r\n let sib = el;\r\n let nth = 1;\r\n if (sib.previousElementSibling) {\r\n while ((sib = sib.previousElementSibling)) {\r\n if (sib.nodeName?.toLowerCase() === selector) nth++;\r\n }\r\n }\r\n\r\n if (nth !== 1) {\r\n selector += `:nth-of-type(${nth})`;\r\n }\r\n\r\n if (nth === 1 && sib?.parentElement?.childElementCount! > 1) {\r\n selector += `:nth-child(${nth})`;\r\n }\r\n }\r\n path.unshift(selector);\r\n el = el.parentElement!;\r\n }\r\n return path.join(' > ');\r\n};\r\n\r\nconst EXCLUDED_ATTRS = new Set([\r\n 'id',\r\n 'class',\r\n 'style'\r\n]);\r\nfunction getAttributeSelectors(el: Element): string[] {\r\n return Array.from(el.attributes)\r\n .filter(attr =>\r\n !EXCLUDED_ATTRS.has(attr?.name?.toLowerCase()) &&\r\n attr.value &&\r\n !isNumberExist(attr.value)\r\n )\r\n .map(attr =>\r\n `[${attr.name}=\"${escapeCssAttributeValue(attr.value)}\"]`\r\n );\r\n}\r\n\r\n\r\nexport const getAttributeCssPath = (\r\n el: Element\r\n): string | undefined => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n\r\n const root = el.getRootNode() as ParentNode;\r\n const tag = el.tagName.toLowerCase();\r\n\r\n if (tag === 'style' || tag === 'script') return;\r\n\r\n const attrSelectors = getAttributeSelectors(el);\r\n\r\n for (const attrSelector of attrSelectors) {\r\n const candidate = `${tag}${attrSelector}`;\r\n\r\n if (isCssSelectorUnique(candidate, el, root)) {\r\n return candidate;\r\n }\r\n }\r\n return;\r\n};\r\n\r\n\r\n\r\nexport const getClassCssPath = (el: HTMLElement | Element,) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n const tagName = el.tagName.toLowerCase();;\r\n if (tagName.includes('style') || tagName.includes('script')) return;\r\n\r\n const path = [];\r\n while (el?.nodeType === Node.ELEMENT_NODE) {\r\n let selector = el.nodeName?.toLowerCase();\r\n\r\n if (\r\n typeof el.className === 'string' &&\r\n el.className &&\r\n !isNumberExist(el.className) &&\r\n !modifiedElementAttributes?.find(\r\n (x: { element: HTMLElement | Element; attributeName: string; }) => x.element === el && x.attributeName === 'class'\r\n )\r\n ) {\r\n el.classList.remove('marked-element-temp');\r\n el.classList.remove('removePointers');\r\n if (el.className) {\r\n selector += `.${el.className.trim().replace(/\\s+/g, '.')}`;\r\n path.unshift(selector);\r\n break;\r\n }\r\n } else {\r\n let sib = el;\r\n let nth = 1;\r\n if (sib.previousElementSibling) {\r\n while ((sib = sib.previousElementSibling)) {\r\n if (sib.nodeName?.toLowerCase() === selector) nth++;\r\n }\r\n }\r\n\r\n if (nth !== 1) {\r\n selector += `:nth-of-type(${nth})`;\r\n }\r\n\r\n if (nth === 1 && sib?.parentElement?.childElementCount! > 1) {\r\n selector += `:nth-child(${nth})`;\r\n }\r\n }\r\n path.unshift(selector);\r\n el = el.parentElement!;\r\n }\r\n return path.join(' > ');\r\n};\r\n\r\nexport const getAbsoluteCssPath = (el: Element) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n\r\n const path: string[] = [];\r\n\r\n while (el && el.nodeType === Node.ELEMENT_NODE) {\r\n const tagName = el.tagName.toLowerCase();\r\n\r\n if (tagName === 'style' || tagName === 'script') return;\r\n\r\n let selector = tagName;\r\n\r\n const parent = el.parentNode;\r\n\r\n if (parent) {\r\n const siblings = Array.from(parent.children)\r\n .filter(c => c.tagName === el.tagName);\r\n\r\n if (siblings.length > 1) {\r\n selector += `:nth-of-type(${siblings.indexOf(el) + 1})`;\r\n }\r\n }\r\n\r\n path.unshift(selector);\r\n\r\n el = el.parentElement!;\r\n }\r\n\r\n return path.join(' > ');\r\n};\r\n\r\nexport const cssSelectors = {\r\n parseCssSelectors,\r\n getIdCssPath,\r\n getAttributeCssPath,\r\n getClassCssPath,\r\n getAbsoluteCssPath\r\n}","import { ElementRecord, Locator } from \"../types/locator.ts\";\r\nimport { parseDOM } from \"./xpath.ts\";\r\nimport { parseCssSelectors } from \"./cssSelector.ts\";\r\nimport {\r\n isNumberExist,\r\n normalizeXPath,\r\n getXPathPattern,\r\n escapeAttrValue,\r\n isUniqueInDOM,\r\n shouldUseSnapshot\r\n} from \"./xpathHelpers.ts\";\r\n\r\nconst isSameXPathStillValid = (\r\n xpath: string,\r\n docmt: Document,\r\n target: Element\r\n): boolean => {\r\n const normalized = normalizeXPath(xpath);\r\n const found = getElementFromXPath(docmt, normalized);\r\n if (found === target) return true;\r\n\r\n if (shouldUseSnapshot(normalized)) {\r\n const result = docmt.evaluate(\r\n normalized,\r\n docmt,\r\n null,\r\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\r\n null\r\n );\r\n\r\n for (let i = 0; i < result.snapshotLength; i++) {\r\n if (result.snapshotItem(i) === target) return true;\r\n }\r\n }\r\n\r\n return false;\r\n};\r\n\r\nconst getElementFromCssSelector = (\r\n docmt: Document,\r\n selector: string\r\n): Element | null => {\r\n try {\r\n const found = docmt.querySelector(selector);\r\n if (found) return found;\r\n } catch (error) {\r\n console.error(\"Invalid CSS selector:\", selector, error);\r\n return null;\r\n }\r\n\r\n return getElementFromShadowRoot(docmt.body, selector);\r\n};\r\n\r\nconst isSameCssSelectorStillValid = (\r\n selector: string,\r\n docmt: Document,\r\n target: Element\r\n): boolean => {\r\n return getElementFromCssSelector(docmt, selector) === target;\r\n};\r\n\r\nconst resolveIsSelfHealed = (\r\n locName: string,\r\n oldValue: string | null | undefined,\r\n newValue: string | null | undefined\r\n): \"Y\" | null => {\r\n if (!oldValue || !newValue) return \"Y\";\r\n return oldValue === newValue ? null : \"Y\";\r\n};\r\n\r\nconst isCssSelectorLocator = (locator: { name?: string | null }): boolean => {\r\n return locator.name?.toLowerCase().includes(\"cssselector\") ?? false;\r\n};\r\n\r\ntype ElementDetails = {\r\n id?: string | null;\r\n className?: string | null;\r\n xpathByText?: string | null;\r\n xpathById?: string | null;\r\n xpathByClass?: string | null;\r\n xpathAbsolute?: string | null;\r\n xpathByName?: string | null;\r\n xpathByPlaceholder?: string | null;\r\n xpathByType?: string | null;\r\n visibleText?: string | null;\r\n relativeXpath?: string | null;\r\n [key: string]: any;\r\n};\r\n\r\nconst getElementFromShadowRoot = (\r\n el: Element | ShadowRoot,\r\n selector: string\r\n): Element | null => {\r\n // const shadowRoot = (element as HTMLElement).shadowRoot;\r\n // if (shadowRoot && !selector.includes(\"dynamic\")) {\r\n // return shadowRoot.querySelector(selector);\r\n // }\r\n\r\n const elements = Array.from(el.querySelectorAll(\"*\"));\r\n\r\n try {\r\n for (let i = 0; i < elements.length; i++) {\r\n if (elements[i].shadowRoot) {\r\n const { shadowRoot } = elements[i];\r\n if (shadowRoot) {\r\n const nestedElement = getElementFromShadowRoot(shadowRoot, selector);\r\n if (nestedElement) {\r\n return nestedElement;\r\n }\r\n if (shadowRoot && !selector.includes(\"dynamic\")) {\r\n return shadowRoot.querySelector(selector);\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n return null;\r\n};\r\n\r\nconst getId = (element: Element | null): string | null => {\r\n return element?.id || null;\r\n};\r\n\r\nconst getClassName = (element: Element): string | null => {\r\n return (element as HTMLElement).className || null;\r\n};\r\n\r\nconst getVisibleText = (element: Element): string | null => {\r\n return element.textContent?.trim() || null;\r\n};\r\n\r\nconst getName = (element: Element): string | null => {\r\n const elementEl = element as HTMLElement;\r\n\r\n if (elementEl.hasAttribute(\"name\")) {\r\n const attrValue = elementEl.getAttribute(\"name\");\r\n const name = `${attrValue}`;\r\n return name || null;\r\n }\r\n return null;\r\n};\r\n\r\nconst relations: string[] = [\r\n \"/preceding-sibling\",\r\n \"/following-sibling\",\r\n \"/parent\",\r\n \"/descendant\",\r\n \"/ancestor\",\r\n \"/self\",\r\n \"/ancestor-or-self\",\r\n \"/child\",\r\n \"/preceding\",\r\n \"/following\"\r\n];\r\n\r\nfunction getElementFromXPath(docmt: Document, xpath: string): Element | null {\r\n const window = docmt.defaultView;\r\n if (!window) return null;\r\n\r\n const xpathEvaluator = new window.XPathEvaluator();\r\n const xpathResult = xpathEvaluator.evaluate(\r\n xpath,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n return xpathResult.singleNodeValue as Element | null;\r\n}\r\n\r\nfunction checkReferenceElementIsValid(\r\n locator: string,\r\n relation: string,\r\n docmt: Document\r\n): string | null {\r\n if (locator.includes(relation)) {\r\n const locatotSplitArray: string[] = locator.split(relation);\r\n const sourceLoc = locatotSplitArray[0].trim();\r\n const window = docmt.defaultView;\r\n if (!window) return null;\r\n if (!locator.includes(\"dynamic\")) {\r\n const xpathEvaluator = new window.XPathEvaluator();\r\n const xpathResult = xpathEvaluator.evaluate(\r\n sourceLoc,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n\r\n const sourceElement = xpathResult.singleNodeValue;\r\n if (sourceElement) {\r\n const xpathResultComplete = xpathEvaluator.evaluate(\r\n locator,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n const completeElement = xpathResultComplete.singleNodeValue;\r\n let relativeXpath: string;\r\n if (completeElement) {\r\n relativeXpath = locator;\r\n return relativeXpath;\r\n } else {\r\n console.error(\"Complete Locator is Invalid:\", locator);\r\n relativeXpath = locator;\r\n return relativeXpath;\r\n }\r\n } else {\r\n console.error(\"Source Locator Not Found:\", sourceLoc);\r\n }\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nconst getElementsFromHTML = (\r\n record: ElementRecord,\r\n docmt: Document\r\n): ElementDetails | null => {\r\n const elementsToRemove = docmt.querySelectorAll(\r\n \"script, style, link[rel='stylesheet'], meta, noscript, embed, object, param, source, svg\"\r\n );\r\n\r\n if (elementsToRemove) {\r\n elementsToRemove.forEach((tag) => {\r\n (tag as Element).remove();\r\n });\r\n }\r\n\r\n const finalLocatorsSet: Set<string> = new Set();\r\n let finalLocators: any[] = [];\r\n\r\n function createLocator(base: any, overrides: Partial<any> = {}) {\r\n const oldValue = base?.value;\r\n const newValue = overrides.value ?? base?.value;\r\n const newLocator: any = {\r\n name: overrides.name ?? base?.name,\r\n type: overrides.type ?? base?.type,\r\n value: overrides.value ?? base?.value,\r\n reference: overrides.reference ?? base?.reference,\r\n status: overrides.status ?? base?.status,\r\n isRecorded: overrides.isRecorded ?? base?.isRecorded\r\n };\r\n\r\n const previousSelfHealed = base?.isSelfHealed === \"Y\";\r\n\r\n newLocator.isSelfHealed = previousSelfHealed\r\n ? \"Y\"\r\n : overrides.hasOwnProperty(\"isSelfHealed\")\r\n ? overrides.isSelfHealed\r\n : resolveIsSelfHealed(newLocator.name, oldValue, newValue);\r\n\r\n pushUniqueLocator(newLocator);\r\n }\r\n\r\n function resolveElement(\r\n ctx: Document,\r\n locator: any,\r\n selector: string\r\n ): Element | null {\r\n if (isCssSelectorLocator(locator)) {\r\n return getElementFromCssSelector(ctx, selector);\r\n } else if (locator.name.includes(\"id\") || selector.startsWith(\"#\")) {\r\n return ctx.querySelector(\"#\" + escapeAttrValue(selector));\r\n } else if (locator.name.includes(\"className\") || selector.startsWith(\".\")) {\r\n return ctx.querySelector(\".\" + selector);\r\n } else if (locator.name === \"name\") {\r\n const safeName = escapeAttrValue(selector);\r\n return ctx.querySelector(`[name=\"${safeName}\"]`);\r\n } else if (locator.name === \"tagName\") {\r\n return ctx.querySelector(selector);\r\n } else if (locator.name === \"linkText\") {\r\n return (\r\n Array.from(ctx.querySelectorAll(\"a\")).find(\r\n (a) => a.textContent?.trim() === selector\r\n ) || null\r\n );\r\n } else if (locator.name === \"partialLinkText\") {\r\n return (\r\n Array.from(ctx.querySelectorAll(\"a\")).find((a) =>\r\n a.textContent?.includes(selector)\r\n ) || null\r\n );\r\n } else if (\r\n (locator.name.includes(\"xpath\") || selector.startsWith(\"//\")) &&\r\n !locator.type.match(\"dynamic\")\r\n ) {\r\n const normalizedXPath = normalizeXPath(selector);\r\n const el = getElementFromXPath(ctx, normalizedXPath);\r\n\r\n if (el) {\r\n createLocator(locator, {\r\n value: selector,\r\n isRecorded: String(locator.isRecorded).includes(\"N\") ? \"N\" : \"Y\"\r\n });\r\n }\r\n\r\n return el;\r\n } else {\r\n return ctx.querySelector(selector);\r\n }\r\n }\r\n\r\n function findInIframes(\r\n docmt: Document,\r\n locator: any,\r\n selector: string\r\n ): Element | null {\r\n const iframes = docmt.querySelectorAll(\"iframe\");\r\n\r\n for (const iframe of iframes) {\r\n try {\r\n const iframeDoc =\r\n iframe.contentDocument || iframe.contentWindow?.document;\r\n\r\n if (!iframeDoc) continue;\r\n\r\n const el = resolveElement(iframeDoc, locator, selector);\r\n if (el) return el;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n function pushUniqueLocator(obj: any) {\r\n const key = `${obj.name}:${obj.value}`;\r\n if (!finalLocatorsSet.has(key)) {\r\n finalLocatorsSet.add(key);\r\n finalLocators.push(obj);\r\n }\r\n }\r\n\r\n /** Locator Value Cleaner (Handles Special Scenarios) **/\r\n const cleanLocatorValue = (\r\n val: string | null | undefined,\r\n type?: string,\r\n isRecorded?: string\r\n ): string | null => {\r\n if (!val) return null;\r\n\r\n let cleaned = val.trim();\r\n\r\n // Return null for empty or literal \"null\"\r\n if (!cleaned || cleaned.toLowerCase() === \"null\") return null;\r\n\r\n // Unescape any escaped quotes\r\n cleaned = cleaned.replace(/\\\\\"/g, '\"').replace(/\\\\'/g, \"'\");\r\n\r\n // Remove surrounding single or double quotes\r\n cleaned = cleaned.replace(/^['\"](.+?)['\"]$/, \"$1\");\r\n\r\n // Replace double single quotes with a single quote inside XPath\r\n cleaned = cleaned.replace(/''/g, \"'\");\r\n\r\n // Normalize double quotes in XPath attribute selectors [@id=\"\" -> [@id='']\r\n cleaned = cleaned.replace(\r\n /\\[@(id|name)=['\"]{2}(.+?)['\"]{2}\\]/g,\r\n \"[@$1='$2']\"\r\n );\r\n\r\n // For DOM selectors (id or name), remove ALL quotes\r\n if (type === \"id\" || type === \"name\") {\r\n cleaned = cleaned.replace(/['\"]/g, \"\").trim();\r\n }\r\n\r\n if (type === \"xpath\" && isRecorded === \"Y\" && !val.startsWith(\"//\"))\r\n return null;\r\n\r\n // Final check for empty strings\r\n if (!cleaned || /^['\"]{2}$/.test(cleaned)) return null;\r\n\r\n return cleaned;\r\n };\r\n\r\n locators: for (const locator of record.locators) {\r\n try {\r\n const isRecorded = String(locator.isRecorded || \"\");\r\n const recordedNLocators = record.locators.filter(\r\n (l) => l.isRecorded === \"N\"\r\n );\r\n\r\n if (recordedNLocators.length > 0) {\r\n for (const locator of recordedNLocators) {\r\n createLocator(locator);\r\n }\r\n }\r\n\r\n const isDynamic = String(locator.value || locator.type || \"\");\r\n if (\r\n isDynamic.includes(\"dynamic\") ||\r\n isDynamic.match(\"dynamic\") ||\r\n isDynamic.includes(\"{\") ||\r\n isDynamic.includes(\"}\")\r\n ) {\r\n createLocator(locator);\r\n continue;\r\n }\r\n\r\n if (record.isShared.includes(\"Y\")) {\r\n break locators;\r\n }\r\n\r\n try {\r\n let targetElement: Element | null = null;\r\n const selectors = locator.value.split(\">>>\");\r\n\r\n for (const selector of selectors) {\r\n if (!docmt) {\r\n console.error(\"Element not found at:\", selector);\r\n break;\r\n }\r\n\r\n const trimmedSelector = selector.trim();\r\n\r\n //normal DOM\r\n targetElement = resolveElement(docmt, locator, trimmedSelector);\r\n\r\n //iframe (if not found)\r\n if (!targetElement) {\r\n targetElement = findInIframes(docmt, locator, trimmedSelector);\r\n }\r\n\r\n //shadow DOM (if still not found)\r\n if (!targetElement) {\r\n targetElement = getElementFromShadowRoot(\r\n docmt.body,\r\n trimmedSelector\r\n );\r\n }\r\n\r\n if (!targetElement) {\r\n console.error(\"Element not found at:\", trimmedSelector);\r\n break;\r\n }\r\n }\r\n\r\n const locatorExists = (name: string, value: string): boolean => {\r\n const key = `${name}:${value}`;\r\n return finalLocatorsSet.has(key);\r\n };\r\n\r\n if (targetElement) {\r\n const payloadXPaths = record.locators.filter(\r\n (l) => l.name === \"xpath\" && l.value\r\n );\r\n const payloadCssSelectors = record.locators.filter(\r\n (l) => isCssSelectorLocator(l) && l.value\r\n );\r\n\r\n for (const px of payloadXPaths) {\r\n if (isSameXPathStillValid(px.value, docmt, targetElement))\r\n createLocator(px, {\r\n isSelfHealed: null\r\n });\r\n }\r\n\r\n for (const cssLocator of payloadCssSelectors) {\r\n if (\r\n isSameCssSelectorStillValid(\r\n cssLocator.value,\r\n docmt,\r\n targetElement\r\n )\r\n )\r\n createLocator(cssLocator, {\r\n isSelfHealed: null\r\n });\r\n }\r\n\r\n const existingXPaths = finalLocators.filter(\r\n (l) => l.name === \"xpath\" && l.value\r\n );\r\n\r\n // Track XPath patterns already used\r\n const usedXPathPatterns = new Set<string>(\r\n existingXPaths.map((x) => getXPathPattern(x.value))\r\n );\r\n\r\n const excludedAttributes: string[] = [];\r\n const idValue = getId(targetElement);\r\n if (\r\n idValue &&\r\n !locatorExists(\"id\", idValue) &&\r\n !isNumberExist(idValue)\r\n ) {\r\n const prevId = record.locators.find((l) => l.name === \"id\");\r\n if (isUniqueInDOM(docmt, \"id\", idValue, targetElement)) {\r\n excludedAttributes.push(\"id\");\r\n createLocator(prevId, {\r\n name: \"id\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: idValue\r\n });\r\n }\r\n }\r\n\r\n const tagName = targetElement.tagName;\r\n if (tagName && !locatorExists(\"tagName\", tagName)) {\r\n const prevTag = record.locators.find((l) => l.name === \"tagName\");\r\n if (isUniqueInDOM(docmt, \"tagName\", tagName, targetElement)) {\r\n excludedAttributes.push(\"tagName\");\r\n createLocator(prevTag, {\r\n name: \"tagName\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: tagName\r\n });\r\n }\r\n }\r\n\r\n const textValue = getVisibleText(targetElement);\r\n if (textValue && !isNumberExist(textValue)) {\r\n const prevLinkText = record.locators.find(\r\n (l) => l.name === \"linkText\"\r\n );\r\n if (isUniqueInDOM(docmt, \"linkText\", textValue, targetElement)) {\r\n excludedAttributes.push(\"linkText\");\r\n createLocator(prevLinkText, {\r\n name: \"linkText\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: textValue\r\n });\r\n }\r\n }\r\n\r\n const nameLocator = getName(targetElement);\r\n if (\r\n nameLocator &&\r\n !locatorExists(\"name\", nameLocator) &&\r\n !isNumberExist(nameLocator)\r\n ) {\r\n const prevName = record.locators.find((l) => l.name === \"name\");\r\n if (isUniqueInDOM(docmt, \"name\", nameLocator, targetElement)) {\r\n excludedAttributes.push(\"name\");\r\n createLocator(prevName, {\r\n name: \"name\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: nameLocator\r\n });\r\n }\r\n }\r\n\r\n const classValue = getClassName(targetElement);\r\n if (\r\n classValue &&\r\n classValue.trim() !== \"\" &&\r\n !classValue.includes(\" \") &&\r\n !locatorExists(\"className\", classValue) &&\r\n !isNumberExist(classValue)\r\n ) {\r\n const prevClassLocator = record.locators.find(\r\n (l) => l.name === \"className\"\r\n );\r\n if (isUniqueInDOM(docmt, \"className\", classValue, targetElement)) {\r\n excludedAttributes.push(\"className\");\r\n createLocator(prevClassLocator, {\r\n name: \"className\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: classValue\r\n });\r\n }\r\n }\r\n parseCssSelectors(targetElement, \"single\").forEach(\r\n (cssSelector) => {\r\n if (\r\n cssSelector.value &&\r\n !locatorExists(\"cssSelector\", cssSelector.value)\r\n ) {\r\n createLocator(undefined, {\r\n name: \"cssSelector\",\r\n value: cssSelector.value,\r\n type: \"static\",\r\n isRecorded: \"Y\"\r\n });\r\n }\r\n }\r\n );\r\n const allAttributes = Array.from(targetElement.attributes);\r\n const includedAttributes = allAttributes.filter(\r\n (attr) => !excludedAttributes.includes(attr.name)\r\n );\r\n\r\n //If any direct locator is broken then we consider it as broken xpath\r\n\r\n let xpathResults: any[] = [];\r\n try {\r\n xpathResults =\r\n parseDOM(targetElement, docmt, false, true, includedAttributes) ??\r\n [];\r\n } catch (error) {\r\n console.error(\"Error generating XPath candidates:\", error);\r\n }\r\n\r\n if (xpathResults?.length !== 0) {\r\n const brokenPayloadXPaths = payloadXPaths.filter(\r\n (px) => !isSameXPathStillValid(px.value, docmt, targetElement)\r\n );\r\n\r\n let xpathAdded = 0;\r\n\r\n for (const brokenPx of brokenPayloadXPaths) {\r\n if (xpathAdded >= brokenPayloadXPaths.length) break;\r\n\r\n const originalPattern = getXPathPattern(brokenPx.value);\r\n if (usedXPathPatterns.has(originalPattern)) continue;\r\n\r\n const match = xpathResults.find(\r\n (r) => r.value && getXPathPattern(r.value) === originalPattern\r\n );\r\n\r\n if (match?.value) {\r\n createLocator(brokenPx, {\r\n name: \"xpath\",\r\n value: match.value,\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n\r\n usedXPathPatterns.add(originalPattern);\r\n xpathAdded++;\r\n }\r\n }\r\n if (xpathAdded < brokenPayloadXPaths.length) {\r\n for (const result of xpathResults) {\r\n if (xpathAdded >= brokenPayloadXPaths.length) break;\r\n if (!result.value) continue;\r\n\r\n const pattern = getXPathPattern(result.value);\r\n if (usedXPathPatterns.has(pattern)) continue;\r\n\r\n createLocator(result, {\r\n name: \"xpath\",\r\n value: result.value,\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n\r\n usedXPathPatterns.add(pattern);\r\n xpathAdded++;\r\n }\r\n }\r\n }\r\n for (const locator of record.locators) {\r\n try {\r\n for (const loc of record.locators) {\r\n if (!loc.value) continue;\r\n\r\n for (const relation of relations) {\r\n if (loc.value.includes(relation)) {\r\n const relativeXpath = checkReferenceElementIsValid(\r\n loc.value,\r\n relation,\r\n docmt\r\n );\r\n if (relativeXpath) {\r\n createLocator(loc, {\r\n name: \"xpath\",\r\n value: relativeXpath,\r\n isRecorded:\r\n locator.isRecorded !== \"\" &&\r\n locator.isRecorded !== null\r\n ? locator.isRecorded\r\n : \"Y\"\r\n });\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n }\r\n }\r\n if (finalLocators.length < 5) {\r\n const fallbackCandidates = [\r\n { name: \"id\", value: getId(targetElement) },\r\n { name: \"name\", value: getName(targetElement) },\r\n { name: \"className\", value: getClassName(targetElement) },\r\n { name: \"tagName\", value: targetElement.tagName },\r\n { name: \"linkText\", value: getVisibleText(targetElement) }\r\n ];\r\n\r\n for (const candidate of fallbackCandidates) {\r\n if (finalLocators.length > 4) break;\r\n\r\n const { name, value } = candidate;\r\n if (!value) continue;\r\n if (isNumberExist(value)) continue;\r\n if (locatorExists(name, value)) continue;\r\n if (name === \"className\" && value.includes(\" \")) continue;\r\n if (isUniqueInDOM(docmt, name, value, targetElement)) {\r\n createLocator(undefined, {\r\n name,\r\n type: \"static\",\r\n value,\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n }\r\n }\r\n\r\n if (finalLocators.length < 5) {\r\n parseCssSelectors(targetElement, \"multiple\").forEach(\r\n (cssSelector) => {\r\n if (finalLocators.length > 4) return;\r\n if (!cssSelector.value) return;\r\n if (locatorExists(\"cssSelector\", cssSelector.value)) return;\r\n if (\r\n !isUniqueInDOM(\r\n docmt,\r\n \"cssSelector\",\r\n cssSelector.value,\r\n targetElement\r\n )\r\n ) {\r\n return;\r\n }\r\n\r\n createLocator(undefined, {\r\n name: \"cssSelector\",\r\n type: \"static\",\r\n value: cssSelector.value,\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n }\r\n );\r\n }\r\n }\r\n\r\n const finalAutoHealedLocators = finalLocators.map((obj) => ({\r\n ...obj,\r\n value: cleanLocatorValue(obj.value, obj.name, obj.isRecorded)\r\n }));\r\n\r\n const jsonResult = [\r\n {\r\n name: `${record.name}`,\r\n desc: `${record.desc}`,\r\n type: `${record.type}`,\r\n locators: finalAutoHealedLocators.filter(\r\n (locator) => locator?.value != null && locator.value !== \"\"\r\n ),\r\n isShared: `${record.isShared}`,\r\n projectId: `${record.projectId}`,\r\n projectType: `${record.projectType}`,\r\n isRecorded: `${record.isRecorded}`,\r\n folder: `${record.folder}`,\r\n parentId: `${record.parentId}`,\r\n parentName: `${record.parentName}`,\r\n platform: `${record.platform}`,\r\n licenseId: `${record.licenseId}`,\r\n licenseType: `${record.licenseType}`,\r\n userId: `${record.userId}`\r\n }\r\n ];\r\n\r\n return jsonResult;\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n continue;\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n continue;\r\n }\r\n }\r\n return null;\r\n};\r\n\r\nexport { getElementsFromHTML };\r\n","import { JSDOM, VirtualConsole } from \"jsdom\";\r\nimport { ElementRecord } from \"../types/locator\";\r\nimport { getElementsFromHTML } from \"../utils/getElementsFromHTML\";\r\n\r\nasync function createXPathAPI() {\r\n const fromHTML = async (html: string) => {\r\n const virtualConsole = new VirtualConsole();\r\n\r\n const dom = new JSDOM(\r\n html\r\n .replace(/\\\\\"/g, '\"')\r\n .replace(/\\\\r/g, \"\\r\")\r\n .replace(/\\\\n/g, \"\\n\")\r\n .replace(/\\\\t/g, \"\\t\"),\r\n {\r\n pretendToBeVisual: true,\r\n runScripts: \"dangerously\",\r\n resources: \"usable\",\r\n virtualConsole\r\n }\r\n );\r\n\r\n const { window } = dom;\r\n\r\n // ✅ Browser globals parity (REQUIRED)\r\n // @ts-ignore\r\n global.document = window.document;\r\n // @ts-ignore\r\n global.Node = window.Node;\r\n // @ts-ignore\r\n global.Element = window.Element;\r\n // @ts-ignore\r\n global.HTMLElement = window.HTMLElement;\r\n // @ts-ignore\r\n global.SVGElement = window.SVGElement;\r\n\r\n const cssApi = window.CSS ?? {};\r\n if (!cssApi.escape) {\r\n cssApi.escape = (value: string) =>\r\n String(value).replace(/[^a-zA-Z0-9_-]/g, \"\\\\$&\");\r\n }\r\n // @ts-ignore\r\n global.CSS = cssApi;\r\n\r\n // @ts-ignore\r\n global.XPathResult = window.XPathResult;\r\n // @ts-ignore\r\n global.XPathEvaluator = window.XPathEvaluator;\r\n\r\n return {\r\n autoHeal: async (data: ElementRecord) => {\r\n return getElementsFromHTML(data, window.document);\r\n }\r\n };\r\n };\r\n\r\n return { fromHTML };\r\n}\r\n\r\nexport default createXPathAPI;\r\n"],"names":["reWhiteSpace","xpathCache","modifiedElementAttributes","isNumberExist","str","test","getFilteredText","element","childNodes","nodeValue","getCountOfXPath","xpath","docmt","multiElementReferenceMode","count","undefined","nodeType","ownerDocument","evaluate","XPathResult","NUMBER_TYPE","numberValue","Array","isArray","elementsFromXpath","getElementFromXpath","nodex","matchedCount","iterateNext","includes","error","console","escapeCharacters","text","indexOf","findXpathWithIndex","val","node","owner","index","nodes","ANY_TYPE","isSameNode","log","replaceWhiteSpaces","replace","trim","checkBlockedAttributes","attribute","targetElement","isTarget","value","some","x","name","isModified","find","doc","attributeName","length","isSvg","SVGElement","multi","contextNode","Node","DOCUMENT_FRAGMENT_NODE","host","DOCUMENT_NODE","FIRST_ORDERED_NODE_TYPE","singleNodeValue","getPropertyXPath","prop","isIndex","tagName","combinePattern","mergePattern","pattern","buildPattern","toLowerCase","splitText","split","contentRes","Set","match","startsWith","endsWith","endIndex","startIndexString","slice","join","endIndexString","i","push","intermediateXpathStep","targetElemt","attr","isSvgElement","expression","attrValue","elementName","getFilteredTextXPath","textContent","filteredText","xpathe","getXpathString","attrName","RegExp","normalizeXPath","getXPathPattern","canonical","canonicalizeXPath","parts","xp","axisMatch","axis","attrMatch","usesNormalize","extractXPathSignatureParts","escapeAttrValue","isUniqueInDOM","root","getRootNode","queryAll","selector","from","querySelectorAll","filter","a","xpathData","xpathDataWithIndex","Map","cache","parentXpathCache","checkRelativeXpathRelation","nodeXpath1","nodeXpath2","relationType","xpaths","get","set","pop","key","indexedXpath","getUniqueParentXpath","domNode","nodeXpath","has","xpathParts","currentNode","hasUniqueAttr","attributes","othersWithAttr","ign","unshift","parentElement","finalXPath","getParentRelativeXpath","uniqueAttrFound","textXPath","finalXpath","getChildRelativeXpath","st","firstElementChild","classList","contains","nextElementSibling","m","getSiblingRelativeXPath","markedSpan","document","querySelector","processSibling","n","previousElementSibling","sibling","hasAttributes","getTextXPath","getTextContent","addAllXPathAttributes","attributesArray","map","xpth","attributesBasedXPath","txtXpath","className","getUniqueClassName","textAttribute","getXPathUsingAttributeAndText","combinationXpath","uniqueAttributes","xpathAttributes","nodeName","JSON","stringify","getAttributeCombinationXpath","parseDOM","includedAttributes","tag","attributesToUse","len","substring","relativeXpath","relativeChildXpath","fullRelativeXpath","relativeXpathIndex","tempRelativeXpath","addRelativeXpaths","isCssSelectorUnique","el","matches","parseCssSelectors","mode","selectors","idPath","getIdCssPath","classPath","getClassCssPath","namePath","getAttributeCssPath","absPath","getAbsoluteCssPath","e","view","defaultView","Element","path","ELEMENT_NODE","id","CSS","escape","sib","nth","childElementCount","EXCLUDED_ATTRS","getAttributeSelectors","String","attrSelectors","attrSelector","candidate","remove","parent","parentNode","siblings","children","c","isSameXPathStillValid","target","normalized","getElementFromXPath","shouldUseSnapshot","result","ORDERED_NODE_SNAPSHOT_TYPE","snapshotLength","snapshotItem","getElementFromCssSelector","found","getElementFromShadowRoot","body","isSameCssSelectorStillValid","isCssSelectorLocator","locator","elements","shadowRoot","nestedElement","getId","getClassName","getVisibleText","getName","elementEl","hasAttribute","getAttribute","relations","window","XPathEvaluator","checkReferenceElementIsValid","relation","sourceLoc","xpathEvaluator","getElementsFromHTML","record","elementsToRemove","forEach","finalLocatorsSet","finalLocators","createLocator","base","overrides","oldValue","newValue","newLocator","type","reference","status","isRecorded","previousSelfHealed","isSelfHealed","hasOwnProperty","locName","resolveIsSelfHealed","obj","add","pushUniqueLocator","resolveElement","ctx","safeName","findInIframes","iframes","iframe","iframeDoc","contentDocument","contentWindow","cleanLocatorValue","cleaned","locators","recordedNLocators","l","isDynamic","isShared","trimmedSelector","locatorExists","payloadXPaths","payloadCssSelectors","px","cssLocator","existingXPaths","usedXPathPatterns","excludedAttributes","idValue","prevId","prevTag","textValue","prevLinkText","nameLocator","prevName","classValue","prevClassLocator","cssSelector","xpathResults","brokenPayloadXPaths","xpathAdded","brokenPx","originalPattern","r","loc","fallbackCandidates","finalAutoHealedLocators","jsonResult","desc","projectId","projectType","folder","parentId","parentName","platform","licenseId","licenseType","userId","async","createXPathAPI","fromHTML","html","virtualConsole","VirtualConsole","dom","JSDOM","pretendToBeVisual","runScripts","resources","global","HTMLElement","cssApi","autoHeal","data"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"xpath.mjs","sources":["../src/utils/xpathHelpers.ts","../src/utils/xpath.ts","../src/utils/cssSelector.ts","../src/utils/getElementsFromHTML.ts","../src/node/xpath.ts"],"sourcesContent":["export const reWhiteSpace = /^[\\S]+( [\\S]+)*$/i;\r\nexport let cspEnabled: boolean = false;\r\nconst xpathCache: { [x: string]: number } = {};\r\n\r\nexport const timeLog = (label: string, start: number) => {\r\n const duration = performance.now() - start;\r\n console.log(`⏱️ ${label}: ${duration.toFixed(2)}ms time`);\r\n};\r\n\r\ntype EvalResult = {\r\n first: Node | null;\r\n second: Node | null;\r\n};\r\n\r\n// cache per context node → avoids cross-context pollution\r\nlet xpathEvalCache = new WeakMap<Node, Map<string, EvalResult>>();\r\n\r\nconst getXPathContext = (docmt: Node) => {\r\n const owner: Document =\r\n docmt.nodeType === 9\r\n ? (docmt as Document)\r\n : docmt.ownerDocument || document;\r\n\r\n // valid XPath context nodes: Element(1), Document(9), DocumentFragment/ShadowRoot(11)\r\n const contextNode: Node =\r\n docmt.nodeType === 1 || docmt.nodeType === 9 || docmt.nodeType === 11\r\n ? docmt\r\n : owner;\r\n\r\n return { owner, contextNode };\r\n};\r\n\r\nconst getMutationCacheContexts = (node: Node): Node[] => {\r\n const contexts: Node[] = [];\r\n const addContext = (candidate: Node | null | undefined) => {\r\n if (!candidate || contexts.includes(candidate)) return;\r\n contexts.push(candidate);\r\n };\r\n\r\n addContext(node);\r\n\r\n const rootNode = node.getRootNode?.();\r\n if (rootNode instanceof Document || rootNode instanceof ShadowRoot) {\r\n addContext(rootNode);\r\n }\r\n\r\n const ownerDocument =\r\n node.nodeType === Node.DOCUMENT_NODE\r\n ? (node as Document)\r\n : node.ownerDocument;\r\n addContext(ownerDocument);\r\n\r\n return contexts;\r\n};\r\n\r\nexport const clearXPathEvalCache = (node?: Node | null) => {\r\n if (!node) {\r\n xpathEvalCache = new WeakMap<Node, Map<string, EvalResult>>();\r\n return;\r\n }\r\n\r\n getMutationCacheContexts(node).forEach((contextNode) => {\r\n xpathEvalCache.delete(contextNode);\r\n });\r\n};\r\n\r\nexport const evaluateXPathOnce = (xpath: string, docmt: Node): EvalResult => {\r\n const { owner, contextNode } = getXPathContext(docmt);\r\n\r\n // get or create cache for this context node\r\n let nodeCache = xpathEvalCache.get(contextNode);\r\n if (!nodeCache) {\r\n nodeCache = new Map<string, EvalResult>();\r\n xpathEvalCache.set(contextNode, nodeCache);\r\n }\r\n\r\n // cache hit\r\n const cached = nodeCache.get(xpath);\r\n if (cached) return cached;\r\n\r\n // evaluate once, stop after 2 nodes\r\n const result = owner.evaluate(\r\n xpath,\r\n contextNode,\r\n null,\r\n XPathResult.ORDERED_NODE_ITERATOR_TYPE,\r\n null\r\n );\r\n\r\n const first = result.iterateNext();\r\n const second = result.iterateNext();\r\n\r\n const evalResult: EvalResult = { first, second };\r\n nodeCache.set(xpath, evalResult);\r\n\r\n return evalResult;\r\n};\r\nlet relativeXPathCache = new Map();\r\nexport let modifiedElementAttributes: {\r\n url: string | null;\r\n attributeName: string | null;\r\n element: HTMLElement | Element;\r\n doc: Document;\r\n}[] = [];\r\nlet mutationObserver: MutationObserver;\r\nconst INTERNAL_CLASS_TOKENS = new Set([\r\n \"marked-element-temp\",\r\n \"removePointers\"\r\n]);\r\n\r\nconst normalizeClassTokens = (\r\n classValue: string | null | undefined\r\n): string[] =>\r\n (classValue || \"\")\r\n .split(/\\s+/)\r\n .map((token) => token.trim())\r\n .filter((token) => token && !INTERNAL_CLASS_TOKENS.has(token))\r\n .sort();\r\n\r\nexport const hasNumericAttributeValue = (value: string | null | undefined) =>\r\n /\\d/.test(value || \"\");\r\n\r\nexport const getClassTokenConditions = (\r\n element: HTMLElement | Element,\r\n classValue: string | null | undefined,\r\n docmt: Document | ShadowRoot,\r\n allowNumericToken = false\r\n): string[] => {\r\n // Class is tokenized: numeric-looking tokens are unstable, but stable\r\n // non-numeric tokens in the same class attribute should all be tried.\r\n const allClassTokens = normalizeClassTokens(classValue);\r\n const classTokens = allClassTokens.filter((token) =>\r\n allowNumericToken ? true : !hasNumericAttributeValue(token)\r\n );\r\n\r\n if (!classTokens.length) return [];\r\n\r\n if (allClassTokens.length === 1 && classTokens.length === 1) {\r\n // Single class value: exact class is more precise and not affected by\r\n // class-token ordering because there is no competing token.\r\n return [`@class=${escapeCharacters(classTokens[0])}`];\r\n }\r\n\r\n return classTokens\r\n .map((token) => {\r\n const xpath = buildPattern(\r\n `contains(@class,${escapeCharacters(token)})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n\r\n return {\r\n token,\r\n count: getTagOnlyXpathCandidateCount(xpath, element, docmt)\r\n };\r\n })\r\n .sort((left, right) => {\r\n // Multi-token class value: prefer the shortest stable token; uniqueness\r\n // is still validated by the caller/final XPath count.\r\n if (left.token.length !== right.token.length) {\r\n return left.token.length - right.token.length;\r\n }\r\n\r\n return left.count - right.count;\r\n })\r\n .map(({ token }) => `contains(@class,${escapeCharacters(token)})`);\r\n};\r\n\r\nexport const getClassTokenCondition = (\r\n element: HTMLElement | Element,\r\n classValue: string | null | undefined,\r\n docmt: Document | ShadowRoot,\r\n allowNumericToken = false\r\n): string | null => {\r\n return (\r\n getClassTokenConditions(element, classValue, docmt, allowNumericToken)[0] ||\r\n null\r\n );\r\n};\r\n\r\nconst TEST_ID_ATTRIBUTE_NAMES = new Set([\r\n \"data-testid\",\r\n \"data-test-id\",\r\n \"data-test\",\r\n \"data-cy\",\r\n \"testid\"\r\n]);\r\n\r\nconst hasGeneratedNumericSegment = (attributeValue: string): boolean =>\r\n /(?:^|[-_:])\\d{4,}(?:$|[-_:])/.test(attributeValue);\r\n\r\nconst isGeneratedTestIdAttribute = (\r\n attributeName: string,\r\n attributeValue: string\r\n): boolean =>\r\n TEST_ID_ATTRIBUTE_NAMES.has(attributeName.toLowerCase()) &&\r\n hasGeneratedNumericSegment(attributeValue);\r\n\r\nexport const sanitizeAttributeValue = (\r\n attributeName: string,\r\n attributeValue: string | null | undefined\r\n): string => {\r\n if (!attributeValue) {\r\n return \"\";\r\n }\r\n\r\n if (attributeName === \"class\" || attributeName === \"className\") {\r\n return normalizeClassTokens(attributeValue).join(\" \");\r\n }\r\n\r\n return attributeValue.replace(\"removePointers\", \"\").trim();\r\n};\r\n\r\nconst isInternalClassOnlyMutation = (mutation: MutationRecord): boolean => {\r\n if (\r\n mutation.type !== \"attributes\" ||\r\n mutation.attributeName !== \"class\" ||\r\n !(mutation.target instanceof Element)\r\n ) {\r\n return false;\r\n }\r\n\r\n if (\r\n isSvg(mutation.target) &&\r\n mutation.oldValue?.trim() === mutation.target.classList.value?.trim()\r\n ) {\r\n return true;\r\n }\r\n\r\n const previousTokens = normalizeClassTokens(mutation.oldValue);\r\n const currentTokens = normalizeClassTokens(\r\n mutation.target.getAttribute(\"class\")\r\n );\r\n\r\n return previousTokens.join(\" \") === currentTokens.join(\" \");\r\n};\r\n\r\nexport const createObserver = (addedNodeCallBack: Function) => {\r\n mutationObserver = new MutationObserver((mutations) => {\r\n mutations.forEach((mutation) => {\r\n const shouldInvalidateXPathCache =\r\n mutation.type === \"childList\" ||\r\n mutation.type === \"characterData\" ||\r\n (mutation.type === \"attributes\" &&\r\n mutation.attributeName !== \"flndisabled\" &&\r\n !isInternalClassOnlyMutation(mutation));\r\n\r\n if (shouldInvalidateXPathCache) {\r\n clearXPathEvalCache(mutation.target);\r\n }\r\n\r\n if (mutation?.addedNodes?.length) {\r\n addedNodeCallBack(mutation?.addedNodes);\r\n }\r\n\r\n if (mutation.target instanceof HTMLElement) {\r\n if (isInternalClassOnlyMutation(mutation)) {\r\n } else if (\r\n mutation?.type === \"attributes\" &&\r\n ![\"flndisabled\"].includes(mutation.attributeName!)\r\n ) {\r\n modifiedElementAttributes.push({\r\n url: mutation.target.baseURI,\r\n attributeName: mutation.attributeName,\r\n element: mutation.target,\r\n doc: mutation.target.ownerDocument\r\n });\r\n }\r\n }\r\n });\r\n });\r\n};\r\nexport const startObserver = (\r\n target: Node,\r\n options: MutationObserverInit | undefined\r\n) => {\r\n mutationObserver?.observe(target, options);\r\n};\r\n\r\nexport const stopObserver = () => {\r\n mutationObserver?.disconnect();\r\n};\r\n\r\nexport const isNumberExist = (str: string): boolean => {\r\n const hasNumber = /\\d/;\r\n return hasNumber.test(str);\r\n};\r\n\r\nexport const buildPattern = (\r\n pattern: string,\r\n isSvg: boolean,\r\n tagName: string\r\n): string => {\r\n return isSvg\r\n ? `//*[local-name()='${tagName}' and ${pattern}]`\r\n : `//${tagName}[${pattern}]`;\r\n};\r\n\r\nexport const getTextContent = (\r\n targetElement: HTMLElement | Element\r\n): string => {\r\n const textContent = targetElement?.textContent;\r\n if (cspEnabled) {\r\n if (textContent) {\r\n const tooltip = document.querySelector(\".flntooltip\") as HTMLElement;\r\n if (tooltip) {\r\n const lastIndex = textContent.lastIndexOf(tooltip.innerText);\r\n if (\r\n lastIndex &&\r\n textContent.length === lastIndex + tooltip.innerText.length\r\n ) {\r\n return textContent.substring(0, lastIndex);\r\n }\r\n } else {\r\n return textContent;\r\n }\r\n }\r\n } else {\r\n return textContent;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nexport const getFilteredText = (element: Node): string => {\r\n return element?.childNodes[0]?.nodeValue || \"\";\r\n};\r\n\r\nexport const getCountOfXPath = (\r\n xpath: string,\r\n element: HTMLElement | Element,\r\n docmt: Node,\r\n multiElementReferenceMode: boolean = false\r\n): number => {\r\n try {\r\n const { first, second } = evaluateXPathOnce(xpath, docmt);\r\n\r\n if (!first) return 0;\r\n\r\n // exactly one match\r\n if (!second) {\r\n return first === element ? 1 : 0;\r\n }\r\n\r\n // multiple matches\r\n if (multiElementReferenceMode && Array.isArray(element)) {\r\n let matchCount = 0;\r\n\r\n if (element.includes(first)) matchCount++;\r\n if (element.includes(second)) matchCount++;\r\n\r\n if (matchCount === 2) return 2;\r\n const owner =\r\n docmt.nodeType === 9 ? (docmt as Document) : docmt.ownerDocument!;\r\n\r\n const result = owner.evaluate(\r\n xpath,\r\n docmt,\r\n null,\r\n XPathResult.ORDERED_NODE_ITERATOR_TYPE,\r\n null\r\n );\r\n\r\n let node: Node | null;\r\n while ((node = result.iterateNext())) {\r\n if (element.includes(node)) {\r\n matchCount++;\r\n if (matchCount === 2) return 2;\r\n }\r\n }\r\n\r\n return 0;\r\n }\r\n\r\n return 2;\r\n } catch (error) {\r\n console.error(`Error evaluating XPath: ${xpath}`, error);\r\n return 0;\r\n }\r\n};\r\n\r\nexport const escapeCharacters = (text: string): string => {\r\n if (text) {\r\n if (!(text.indexOf('\"') === -1)) {\r\n return `'${text}'`;\r\n }\r\n if (!(text.indexOf(\"'\") === -1)) {\r\n return `\"${text}\"`;\r\n }\r\n }\r\n return `'${text}'`;\r\n};\r\n\r\nexport const removeParenthesis = (xpath: string): string => {\r\n const charArr = xpath.split(\"\");\r\n\r\n let count = charArr.length;\r\n const indexArray = [];\r\n\r\n while (charArr[count - 2] !== \"[\") {\r\n indexArray.push(charArr[count - 2]);\r\n count--;\r\n }\r\n\r\n indexArray.reverse();\r\n let finalStr = \"\";\r\n for (let i = 0; i < indexArray.length; i++) {\r\n finalStr += indexArray[i];\r\n }\r\n\r\n const endBracketLength = finalStr.length + 2;\r\n let firstpart = xpath.slice(0, xpath.length - endBracketLength);\r\n\r\n if (firstpart.startsWith(\"(\") && firstpart.endsWith(\")\")) {\r\n firstpart = firstpart.slice(1, -1);\r\n } else {\r\n firstpart = xpath;\r\n }\r\n\r\n return firstpart;\r\n};\r\n\r\nexport const findXpathWithIndex = (\r\n val: string,\r\n node: HTMLElement | Element,\r\n docmt: Node,\r\n count: number\r\n) => {\r\n try {\r\n const owner =\r\n docmt.nodeType === 9 // DOCUMENT_NODE\r\n ? (docmt as Document)\r\n : docmt.ownerDocument!;\r\n\r\n let index = 0;\r\n if (count) {\r\n if (getCountOfXPath(`${val}[${count}]`, node, docmt) === 1) {\r\n return `${val}[${count}]`;\r\n }\r\n\r\n if (getCountOfXPath(`(${val})[${count}]`, node, docmt) === 1) {\r\n return `(${val})[${count}]`;\r\n }\r\n }\r\n\r\n const nodes = owner.evaluate(val, docmt, null, XPathResult.ANY_TYPE, null);\r\n let nodex: Node | null = null;\r\n while ((nodex = nodes.iterateNext())) {\r\n index++;\r\n if (nodex.isSameNode(node)) {\r\n if (getCountOfXPath(`${val}[${index}]`, node, docmt) === 1) {\r\n return `${val}[${index}]`;\r\n }\r\n if (getCountOfXPath(`(${val})[${index}]`, node, docmt) === 1) {\r\n return `(${val})[${index}]`;\r\n }\r\n return;\r\n }\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nconst deleteLineGap = (a: string): string => {\r\n a &&= a.split(\"\\n\")[0].length > 0 ? a.split(\"\\n\")[0] : a.split(\"\\n\")[1];\r\n return a;\r\n};\r\n\r\nconst deleteGarbageFromInnerText = (a: string): string => {\r\n a = deleteLineGap(a);\r\n a = a\r\n .split(/[^\\u0000-\\u00ff]/)\r\n .reduce((b, c) => {\r\n return b.length > c.length ? b : c;\r\n }, \"\")\r\n .trim();\r\n return (a = a.split(\"/\")[0].trim());\r\n};\r\n\r\nexport const replaceWhiteSpaces = (str: string): string => {\r\n if (str) {\r\n return str.replace(/\\s\\s+/g, \" \").trim();\r\n }\r\n\r\n return str;\r\n};\r\n\r\nexport const getContainerTextCondition = (\r\n element: HTMLElement | Element,\r\n text: string | null | undefined\r\n): string => {\r\n const normalizedText = replaceWhiteSpaces((text || \"\").trim());\r\n\r\n if (!element.children.length || normalizedText.length <= 50) {\r\n return \"\";\r\n }\r\n\r\n const fragment = normalizedText.split(/\\s+/).slice(0, 3).join(\" \");\r\n\r\n // Long container text is usually flattened descendant text. Use a short\r\n // visible fragment instead of exact matching the whole menu/header content.\r\n return fragment.length > 2\r\n ? `contains(normalize-space(.),${escapeCharacters(fragment)})`\r\n : \"\";\r\n};\r\n\r\nexport const getStableTargetTextCandidates = (\r\n text: string | null | undefined\r\n): { prefix: string; fragment: string } => {\r\n const normalizedText = replaceWhiteSpaces((text || \"\").trim());\r\n\r\n if (!normalizedText || !hasNumericAttributeValue(normalizedText)) {\r\n return { prefix: normalizedText, fragment: normalizedText };\r\n }\r\n\r\n const prefix = normalizedText\r\n .split(/[0-9]/)[0]\r\n .replace(/[₹$€£¥₫₽₩₦₱₲₴₵₡₭₮₺฿៛]+/g, \"\")\r\n .trim();\r\n const fragment = prefix\r\n .split(/[^a-zA-Z]+/)\r\n .map((part) => replaceWhiteSpaces(part).trim())\r\n .filter((part) => part.length > 2 && /[a-zA-Z]/.test(part))\r\n .join(\" \")\r\n .trim();\r\n\r\n // Target text containing numbers is dynamic. Keep only a meaningful\r\n // non-numeric prefix/fragment, so price/count text never becomes exact XPath.\r\n return {\r\n prefix: prefix.length > 2 && /[a-zA-Z]/.test(prefix) ? prefix : \"\",\r\n fragment: fragment.length > 2 ? fragment : \"\"\r\n };\r\n};\r\n\r\nexport const getStableTargetText = (\r\n text: string | null | undefined\r\n): string => getStableTargetTextCandidates(text).fragment;\r\n\r\nexport const getShadowRoot = (el: HTMLElement | Element): Element | null => {\r\n if (!el || !el?.getRootNode) return null;\r\n\r\n const root = el.getRootNode() as ShadowRoot;\r\n return root && root?.host ? (root as unknown as Element) : null;\r\n};\r\n\r\nconst isShadowRootNode = (node: Node): node is ShadowRoot => {\r\n return node?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && \"host\" in node;\r\n};\r\n\r\nconst getElementPathFromRoot = (\r\n root: ShadowRoot,\r\n element: Element\r\n): number[] | null => {\r\n const path: number[] = [];\r\n let current: Element | null = element;\r\n\r\n while (current && current.parentElement) {\r\n const parent: Element = current.parentElement as Element;\r\n path.unshift(Array.from(parent.children).indexOf(current));\r\n current = parent;\r\n }\r\n\r\n if (!current || current.parentNode !== root) {\r\n return null;\r\n }\r\n\r\n path.unshift(Array.from(root.children).indexOf(current));\r\n return path;\r\n};\r\n\r\nconst getElementAtPath = (\r\n container: ParentNode & { children: HTMLCollection },\r\n path: number[]\r\n): Element | null => {\r\n let current: (ParentNode & { children: HTMLCollection }) | Element =\r\n container;\r\n\r\n for (const index of path) {\r\n const next: Element | null = current.children.item(index) as Element | null;\r\n if (!next) {\r\n return null;\r\n }\r\n\r\n current = next;\r\n }\r\n\r\n return current as Element | null;\r\n};\r\n\r\nexport const createShadowEvaluationContext = (\r\n root: ShadowRoot,\r\n element?: HTMLElement | Element | (HTMLElement | Element)[]\r\n) => {\r\n const tempDoc =\r\n root.ownerDocument.implementation.createHTMLDocument(\"shadow-xpath\");\r\n const wrapper = tempDoc.createElement(\"div\");\r\n wrapper.innerHTML = root.innerHTML;\r\n tempDoc.body.appendChild(wrapper);\r\n\r\n const cloneForElement = (candidate?: HTMLElement | Element | null) => {\r\n if (\r\n !candidate ||\r\n !(candidate instanceof root.ownerDocument.defaultView!.Element)\r\n ) {\r\n return null;\r\n }\r\n\r\n const path = getElementPathFromRoot(root, candidate);\r\n return path ? getElementAtPath(wrapper, path) : null;\r\n };\r\n\r\n const cloneElements = Array.isArray(element)\r\n ? element.map((candidate) => cloneForElement(candidate)).filter(Boolean)\r\n : [];\r\n\r\n const cloneElement = Array.isArray(element)\r\n ? null\r\n : cloneForElement(element ?? null);\r\n\r\n return {\r\n owner: tempDoc,\r\n contextNode: wrapper,\r\n cloneElement,\r\n cloneElements\r\n };\r\n};\r\n\r\nexport const checkBlockedAttributes = (\r\n attribute: {\r\n name: string;\r\n value: string;\r\n },\r\n targetElement: HTMLElement | Element,\r\n isTarget: boolean\r\n): boolean => {\r\n const sanitizedValue = sanitizeAttributeValue(\r\n attribute.name,\r\n attribute.value\r\n );\r\n\r\n if (!sanitizedValue || typeof attribute?.value === \"boolean\") {\r\n return false;\r\n }\r\n const blockedValues = [\r\n \"true\",\r\n \"false\",\r\n \"on\",\r\n \"off\",\r\n \"flntooltip\",\r\n \"flutter-highlight-overlay\"\r\n ];\r\n if (blockedValues.some((x) => x === sanitizedValue)) {\r\n return false;\r\n }\r\n const blockedNames = [\"style\", \"locator-data-tooltip\", \"value\"];\r\n if (blockedNames.some((x) => x === attribute.name)) {\r\n return false;\r\n }\r\n\r\n const isModified = modifiedElementAttributes?.find(\r\n (x) =>\r\n x.doc === targetElement.ownerDocument &&\r\n x.element === targetElement &&\r\n x.attributeName === attribute.name\r\n );\r\n if (isModified) {\r\n return false;\r\n }\r\n\r\n if (attribute?.name?.indexOf(\"on\") === 0 && attribute?.name?.length > 3) {\r\n return false;\r\n }\r\n\r\n if (typeof attribute.value === \"function\") {\r\n return false;\r\n }\r\n\r\n if (isGeneratedTestIdAttribute(attribute.name, sanitizedValue)) {\r\n return false;\r\n }\r\n\r\n // Strict numeric-attribute validation: numeric attribute values are treated\r\n // as unstable before candidate creation. Class is handled token-by-token by\r\n // getClassTokenCondition, so only numeric class tokens are rejected there.\r\n if (attribute.name !== \"class\" && hasNumericAttributeValue(sanitizedValue)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n};\r\n\r\nexport const getRelationship = (a: HTMLElement, b: HTMLElement): string => {\r\n let pos = a.compareDocumentPosition(b);\r\n return pos === 2\r\n ? \"preceding\"\r\n : pos === 4\r\n ? \"following\"\r\n : pos === 8\r\n ? \"ancestor\"\r\n : pos === 16\r\n ? \"descendant\"\r\n : pos === 32\r\n ? \"self\"\r\n : \"\";\r\n};\r\n\r\nexport const isSvg = (element: HTMLElement | Element) => {\r\n return element instanceof SVGElement;\r\n};\r\n\r\nexport const replaceTempAttributes = (str: string): string => {\r\n if (!str) return str;\r\n\r\n return str.replace(/\\b[a-zA-Z_]*disabled\\b/gi, \"disabled\");\r\n};\r\n\r\nconst getElementFromXpath = (xpath: string, docmt: Node, multi = false) => {\r\n let contextNode: Node = docmt;\r\n\r\n // XPath does NOT support DocumentFragment / ShadowRoot\r\n if (docmt.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\r\n contextNode = (docmt as ShadowRoot).host ?? docmt;\r\n }\r\n\r\n const owner =\r\n contextNode.nodeType === Node.DOCUMENT_NODE\r\n ? (contextNode as Document)\r\n : contextNode.ownerDocument!;\r\n\r\n if (multi) {\r\n return owner.evaluate(xpath, contextNode, null, XPathResult.ANY_TYPE, null);\r\n }\r\n\r\n return owner.evaluate(\r\n xpath,\r\n contextNode,\r\n null,\r\n XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n ).singleNodeValue;\r\n};\r\n\r\nexport const getPropertyXPath = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n prop: string,\r\n value: string,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n if (value) {\r\n const { tagName } = element;\r\n let count;\r\n let combinePattern = \"\";\r\n const mergePattern = [];\r\n let pattern;\r\n\r\n const isAttributeProp = prop.startsWith(\"@\");\r\n const isTextProp = prop === \".\" || prop === \"text()\";\r\n const normalizedTextProp = isTextProp ? \".\" : prop;\r\n const hasTextSpacing = isTextProp && /\\s/.test(value.trim());\r\n const stableTargetText = isTextProp ? getStableTargetText(value) : value;\r\n const textValueForPredicate =\r\n isTextProp && hasNumericAttributeValue(value) ? stableTargetText : value;\r\n const containerTextCondition = isTextProp\r\n ? getContainerTextCondition(element, textValueForPredicate)\r\n : \"\";\r\n\r\n if (prop === \"@class\") {\r\n for (const classCondition of getClassTokenConditions(\r\n element,\r\n value,\r\n docmt\r\n )) {\r\n // Single-mode class generation tries every stable token and lets the\r\n // normal uniqueness check choose the first usable XPath.\r\n pattern = buildPattern(\r\n classCondition,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n\r\n return;\r\n }\r\n\r\n if (isTextProp && hasNumericAttributeValue(value) && !stableTargetText) {\r\n // Target text with only dynamic numeric content should not produce a\r\n // text XPath. Other attribute/relative fallbacks can still run.\r\n return;\r\n }\r\n\r\n if (value && (!isAttributeProp || !isNumberExist(value))) {\r\n if (containerTextCondition) {\r\n pattern = buildPattern(\r\n containerTextCondition,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n } else if (hasTextSpacing) {\r\n pattern = buildPattern(\r\n `normalize-space(${normalizedTextProp})=${escapeCharacters(\r\n replaceWhiteSpaces(textValueForPredicate)\r\n ).trim()}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n } else if (!reWhiteSpace.test(value)) {\r\n pattern = buildPattern(\r\n `normalize-space(${prop})=${escapeCharacters(\r\n replaceWhiteSpaces(textValueForPredicate)\r\n ).trim()}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n } else {\r\n pattern = `//${tagName}[${prop}=${escapeCharacters(\r\n textValueForPredicate\r\n )}]`;\r\n }\r\n\r\n count = getCountOfXPath(pattern, element, docmt);\r\n\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n\r\n if (isAttributeProp && hasNumericAttributeValue(value)) {\r\n // Strict numeric-attribute validation: do not split numeric attribute\r\n // values into starts-with/contains fallbacks.\r\n return;\r\n }\r\n\r\n if (value && isTarget) {\r\n const splitText = value.split(\" \");\r\n if (splitText?.length) {\r\n if (splitText.length === 1) {\r\n const contentRes = [...new Set(splitText[0].match(/([^0-9]+)/g))];\r\n if (contentRes?.length >= 1) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length > 1\r\n ) {\r\n if (value.startsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0])\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n contentRes[0]\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (contentRes?.length > 1) {\r\n if (\r\n contentRes[contentRes.length - 1] &&\r\n replaceWhiteSpaces(contentRes[contentRes.length - 1].trim())\r\n ?.length > 1\r\n ) {\r\n if (value.endsWith(contentRes[contentRes.length - 1])) {\r\n if (!reWhiteSpace.test(contentRes[contentRes.length - 1])) {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[contentRes.length - 1])\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n contentRes[contentRes.length - 1]\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n } else {\r\n const endIndex =\r\n splitText.length % 2 === 0\r\n ? splitText.length / 2\r\n : splitText.length % 2;\r\n const startIndexString = splitText.slice(0, endIndex).join(\" \");\r\n let contentRes = [...new Set(startIndexString.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length\r\n ) {\r\n if (value.startsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0])\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `starts-with(${prop},${escapeCharacters(\r\n contentRes[0]\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n\r\n const endIndexString = splitText\r\n .slice(endIndex, splitText.length - 1)\r\n .join(\" \");\r\n contentRes = [...new Set(endIndexString.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n if (\r\n contentRes[0] &&\r\n replaceWhiteSpaces(contentRes[0].trim())?.length > 3\r\n ) {\r\n if (value.endsWith(contentRes[0])) {\r\n if (!reWhiteSpace.test(contentRes[0])) {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[0])\r\n ).trim()})`;\r\n } else {\r\n combinePattern = `ends-with(${prop},${escapeCharacters(\r\n contentRes[0]\r\n ).trim()})`;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${combinePattern}]`;\r\n } else {\r\n pattern = `//${tagName}[${combinePattern}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (value && isTarget && isNumberExist(value)) {\r\n const contentRes = [...new Set(value.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces(contentRes[i].trim())?.length > 1\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n mergePattern.push(\r\n `contains(${prop},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i])\r\n ).trim()})`\r\n );\r\n } else {\r\n mergePattern.push(\r\n `contains(${prop},${escapeCharacters(\r\n contentRes[i].trim()\r\n ).trim()})`\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (mergePattern?.length) {\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and ${mergePattern.join(\r\n \" and \"\r\n )}]`;\r\n } else {\r\n pattern = `//${tagName}[${mergePattern.join(\" and \")}]`;\r\n }\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n }\r\n\r\n if (isSvg(element)) {\r\n pattern = `//*[local-name()='${tagName}' and text()]`;\r\n } else {\r\n pattern = `//${tagName}[text()]`;\r\n }\r\n\r\n count = getCountOfXPath(pattern, element, docmt);\r\n if (count === 1 && !isIndex) {\r\n return pattern;\r\n }\r\n }\r\n};\r\n\r\nexport const getAbsoluteXPath = (\r\n domNode: HTMLElement | Element | null,\r\n docmt: Document\r\n): string => {\r\n try {\r\n if (!domNode) {\r\n return \"\";\r\n }\r\n\r\n let xpathe = isSvg(domNode)\r\n ? `/*[local-name()='${domNode.tagName}']`\r\n : `/${domNode.tagName}`;\r\n\r\n // // If this node has siblings of the same tagName, get the index of this node\r\n if (domNode.parentElement) {\r\n // Get the siblings\r\n const childNodes = Array.prototype.slice\r\n .call(domNode.parentElement.children, 0)\r\n .filter(\r\n (childNode: HTMLElement) => childNode.tagName === domNode.tagName\r\n );\r\n\r\n // // If there's more than one sibling, append the index\r\n if (childNodes.length > 1) {\r\n const index = childNodes.indexOf(domNode);\r\n xpathe += `[${index + 1}]`;\r\n }\r\n } else if (domNode instanceof HTMLElement) {\r\n if (domNode.offsetParent) {\r\n const childNodes = Array.prototype.slice\r\n .call(domNode.offsetParent.children, 0)\r\n .filter(\r\n (childNode: HTMLElement) => childNode.tagName === domNode.tagName\r\n );\r\n\r\n // // If there's more than one sibling, append the index\r\n if (childNodes.length > 1) {\r\n const index = childNodes.indexOf(domNode);\r\n xpathe += `[${index + 1}]`;\r\n }\r\n }\r\n }\r\n\r\n // // Make a recursive call to this nodes parents and prepend it to this xpath\r\n return getAbsoluteXPath(domNode?.parentElement, docmt) + xpathe;\r\n } catch (error) {\r\n // If there's an unexpected exception, abort and don't get an XPath\r\n console.log(\"xpath\", error);\r\n\r\n return \"\";\r\n }\r\n};\r\n\r\nexport const getRelativeXPath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean = false,\r\n attributesArray: Attr[]\r\n) => {\r\n try {\r\n // Generate a cache key based on the node's identifier, index, and target flag\r\n // Check if the result for this node is already cached\r\n if (relativeXPathCache.has(domNode)) {\r\n return relativeXPathCache.get(domNode);\r\n }\r\n\r\n // Initialize an array to hold parts of the XPath\r\n const xpathParts = [];\r\n let currentNode = domNode;\r\n\r\n // Traverse up the DOM tree iteratively instead of using recursion\r\n while (currentNode) {\r\n let xpathe: string | undefined = \"\";\r\n let hasUniqueAttr = false;\r\n let attributes =\r\n domNode === currentNode\r\n ? (attributesArray ?? currentNode.attributes)\r\n : currentNode.attributes;\r\n\r\n // Loop through attributes to check for unique identifiers\r\n for (const attrName of Array.from(attributes)) {\r\n if (checkBlockedAttributes(attrName, currentNode, isTarget)) {\r\n const attrValue = sanitizeAttributeValue(\r\n attrName.name,\r\n attrName.nodeValue\r\n );\r\n\r\n const elementName = attrName.name;\r\n\r\n // Class can produce multiple stable token candidates; try each\r\n // before moving to text/index fallbacks.\r\n for (const xpathCandidate of getXpathStrings(\r\n currentNode,\r\n elementName,\r\n attrValue\r\n )) {\r\n xpathe = xpathCandidate;\r\n let othersWithAttr: number = 0;\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n xpathParts.unshift(replaceTempAttributes(xpathe));\r\n hasUniqueAttr = true;\r\n break;\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr\r\n );\r\n if (xpathe) {\r\n xpathParts.unshift(replaceTempAttributes(xpathe));\r\n hasUniqueAttr = true;\r\n break;\r\n }\r\n // return replaceTempAttributes(xpathe);\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (currentNode.textContent) {\r\n if (\r\n !isTarget ||\r\n (isTarget && !isNumberExist(currentNode.textContent))\r\n ) {\r\n let reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n const filteredText = getFilteredText(currentNode);\r\n const containerTextCondition = getContainerTextCondition(\r\n currentNode,\r\n filteredText\r\n );\r\n\r\n if (containerTextCondition) {\r\n xpathe = buildPattern(\r\n containerTextCondition,\r\n isSvg(currentNode),\r\n currentNode.tagName || \"*\"\r\n );\r\n } else if (!reWhiteSpace.test(currentNode.textContent)) {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and normalize-space(.)=${escapeCharacters(\r\n filteredText\r\n )}]`\r\n : `//${\r\n currentNode.tagName || \"*\"\r\n }[normalize-space(.)=${escapeCharacters(\r\n filteredText\r\n )}]`;\r\n } else {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and .=${escapeCharacters(filteredText)}]`\r\n : `//${currentNode.tagName || \"*\"}[.=${escapeCharacters(\r\n filteredText\r\n )}]`;\r\n }\r\n\r\n let othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n return xpathe;\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr\r\n );\r\n return xpathe;\r\n }\r\n } else {\r\n let combinePattern: string[] = [];\r\n const contentRes = [\r\n ...new Set(getFilteredText(currentNode).match(/([^0-9]+)/g))\r\n ];\r\n let reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces((contentRes[i] as string).trim())\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i] as string)) {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i])\r\n ).trim()})`\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n (contentRes[i] as string).trim()\r\n ).trim()})`\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and ${combinePattern.join(\" and \")}]`\r\n : `//${currentNode.tagName || \"*\"}[${combinePattern.join(\r\n \" and \"\r\n )}]`;\r\n let othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n return xpathe;\r\n }\r\n\r\n if (othersWithAttr > 1 && isIndex) {\r\n xpathe = findXpathWithIndex(\r\n xpathe,\r\n currentNode,\r\n docmt,\r\n othersWithAttr\r\n );\r\n return xpathe;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // If no unique attribute was found, construct XPath by tag name\r\n if (!hasUniqueAttr) {\r\n let tagBasedXPath = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Handle sibling nodes\r\n if (currentNode.parentElement) {\r\n const siblings = Array.from(\r\n currentNode.parentElement.children\r\n ).filter((childNode) => childNode.tagName === currentNode.tagName);\r\n\r\n // Append index to distinguish between siblings\r\n if (siblings.length > 1) {\r\n const index = siblings.indexOf(currentNode);\r\n tagBasedXPath += `[${index + 1}]`;\r\n }\r\n }\r\n\r\n // Add the constructed tag-based XPath to the parts array\r\n xpathParts.unshift(tagBasedXPath);\r\n } else {\r\n break;\r\n }\r\n\r\n // Move up to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Combine all parts into the final XPath\r\n const finalXPath = `${xpathParts.join(\"\")}`;\r\n\r\n // Cache the final XPath for this node\r\n relativeXPathCache.set(domNode, finalXPath);\r\n return finalXPath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nexport const getCombinationXpath = (\r\n attribute: Attr,\r\n domNode: HTMLElement | Element\r\n) => {\r\n const combinePattern = [];\r\n let pattern: string = \"\";\r\n\r\n if (\r\n attribute &&\r\n typeof attribute.nodeValue !== \"function\" // &&\r\n // !modifiedElementAttributes?.find(\r\n // (x) => x.element === domNode && x.attributeName === attribute.name\r\n // )\r\n ) {\r\n if (attribute.name === \"class\") {\r\n const docmt =\r\n (domNode.getRootNode?.() as Document | ShadowRoot | null) ??\r\n domNode.ownerDocument;\r\n const classConditions = getClassTokenConditions(\r\n domNode,\r\n attribute.value,\r\n docmt\r\n );\r\n\r\n // Class values are handled token-by-token so numeric tokens are blocked\r\n // without throwing away the other stable class tokens.\r\n for (const classCondition of classConditions) {\r\n pattern = isSvg(domNode)\r\n ? `//*[local-name()='${domNode.tagName}' and ${classCondition}]`\r\n : `//${domNode.tagName}[${classCondition}]`;\r\n\r\n if (pattern) {\r\n return pattern;\r\n }\r\n }\r\n\r\n return;\r\n }\r\n\r\n if (isNumberExist(attribute.value)) {\r\n return;\r\n }\r\n\r\n const contentRes = [...new Set(attribute.value.match(/([^0-9]+)/g))];\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (\r\n contentRes[i] &&\r\n replaceWhiteSpaces(contentRes[i].trim())?.length > 2\r\n ) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n combinePattern.push(\r\n `contains(@${attribute.name},${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i])\r\n ).trim()})`\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(@${attribute.name},${escapeCharacters(\r\n contentRes[i].trim()\r\n )})`\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n pattern = isSvg(domNode)\r\n ? `//*[local-name()='${domNode.tagName}' and ${combinePattern.join(\r\n \" and \"\r\n )}]`\r\n : `//${domNode.tagName}[${combinePattern.join(\" and \")}]`;\r\n return pattern;\r\n }\r\n }\r\n};\r\n\r\nexport const getAttributeCombinationXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document | ShadowRoot,\r\n uniqueAttributes: Attr[],\r\n isTarget: boolean\r\n): string | undefined => {\r\n try {\r\n const candidateAttributes = uniqueAttributes.filter((attr) =>\r\n checkBlockedAttributes(attr, domNode, isTarget)\r\n );\r\n\r\n const buildAttributeConditions = (attribute: Attr): string[] => {\r\n const attrValue = sanitizeAttributeValue(\r\n attribute.name,\r\n attribute.nodeValue\r\n );\r\n\r\n if (!attrValue) return [];\r\n\r\n if (attribute.name === \"class\") {\r\n return getClassTokenConditions(\r\n domNode,\r\n attrValue,\r\n docmt\r\n );\r\n }\r\n\r\n // Strict numeric-attribute validation for direct callers.\r\n if (hasNumericAttributeValue(attrValue)) return [];\r\n\r\n if (!reWhiteSpace.test(attrValue)) {\r\n return [`normalize-space(@${attribute.name})=\"${attrValue}\"`];\r\n }\r\n\r\n return [`@${attribute.name}=\"${attrValue}\"`];\r\n };\r\n\r\n const getTextConditions = (): string[] => {\r\n const rawText = replaceWhiteSpaces(getTextContent(domNode)?.trim() || \"\");\r\n const stableTargetText = getStableTargetText(rawText);\r\n if (hasNumericAttributeValue(rawText)) {\r\n return stableTargetText\r\n ? [\r\n `contains(normalize-space(.),${escapeCharacters(\r\n stableTargetText\r\n )})`\r\n ]\r\n : [];\r\n }\r\n\r\n if (!rawText || rawText.length > 80 || /^\\d+$/.test(rawText)) {\r\n const containerTextCondition = getContainerTextCondition(\r\n domNode,\r\n rawText\r\n );\r\n return containerTextCondition ? [containerTextCondition] : [];\r\n }\r\n\r\n const conditions = new Set<string>();\r\n const containerTextCondition = getContainerTextCondition(\r\n domNode,\r\n rawText\r\n );\r\n\r\n if (containerTextCondition) {\r\n conditions.add(containerTextCondition);\r\n return Array.from(conditions);\r\n }\r\n\r\n if (reWhiteSpace.test(rawText) && rawText.length <= 40 && !rawText.includes(\" \")) {\r\n conditions.add(`text()=${escapeCharacters(rawText)}`);\r\n }\r\n\r\n if (rawText.includes(\" \")) {\r\n conditions.add(\r\n `normalize-space(.)=${escapeCharacters(replaceWhiteSpaces(rawText))}`\r\n );\r\n }\r\n\r\n conditions.add(`contains(text(),${escapeCharacters(rawText)})`);\r\n\r\n return Array.from(conditions);\r\n };\r\n\r\n const generateAttributeCombinations = (\r\n attributes: Attr[],\r\n combinationSize: number\r\n ): Attr[][] => {\r\n const results: Attr[][] = [];\r\n\r\n const build = (startIndex: number, currentGroup: Attr[]) => {\r\n if (currentGroup.length === combinationSize) {\r\n results.push([...currentGroup]);\r\n return;\r\n }\r\n\r\n for (let i = startIndex; i < attributes.length; i++) {\r\n currentGroup.push(attributes[i]);\r\n build(i + 1, currentGroup);\r\n currentGroup.pop();\r\n }\r\n };\r\n\r\n build(0, []);\r\n return results;\r\n };\r\n\r\n const buildXPath = (conditions: string[]) =>\r\n isSvg(domNode)\r\n ? `//*[local-name()='${domNode.tagName}' and ${conditions.join(\" and \")}]`\r\n : `//${domNode.tagName}[${conditions.join(\" and \")}]`;\r\n\r\n const tryAttributeOnlyCombinations = () => {\r\n if (candidateAttributes.length < 2) {\r\n return undefined;\r\n }\r\n\r\n for (\r\n let combinationSize = 2;\r\n combinationSize <= candidateAttributes.length;\r\n combinationSize++\r\n ) {\r\n const attributeGroups = generateAttributeCombinations(\r\n candidateAttributes,\r\n combinationSize\r\n );\r\n\r\n for (const attributeGroup of attributeGroups) {\r\n let xpathConditions: string[] = [];\r\n\r\n for (const attribute of attributeGroup) {\r\n const attributeConditions = buildAttributeConditions(attribute);\r\n\r\n if (!attributeConditions.length) continue;\r\n\r\n xpathConditions.push(...attributeConditions);\r\n }\r\n\r\n if (xpathConditions.length < 2) continue;\r\n\r\n const xpath = buildXPath(xpathConditions);\r\n\r\n let matchCount: number;\r\n\r\n try {\r\n matchCount = getCountOfXPath(xpath, domNode, docmt);\r\n } catch {\r\n continue;\r\n }\r\n\r\n if (matchCount === 1) {\r\n return xpath;\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n };\r\n\r\n const attributeOnlyXpath = tryAttributeOnlyCombinations();\r\n if (attributeOnlyXpath) {\r\n return attributeOnlyXpath;\r\n }\r\n\r\n const textConditions = getTextConditions();\r\n if (!textConditions.length || !candidateAttributes.length) {\r\n return;\r\n }\r\n\r\n for (\r\n let combinationSize = 1;\r\n combinationSize <= candidateAttributes.length;\r\n combinationSize++\r\n ) {\r\n const attributeGroups = generateAttributeCombinations(\r\n candidateAttributes,\r\n combinationSize\r\n );\r\n\r\n for (const attributeGroup of attributeGroups) {\r\n let attributeConditions: string[] = [];\r\n\r\n for (const attribute of attributeGroup) {\r\n attributeConditions.push(...buildAttributeConditions(attribute));\r\n }\r\n\r\n if (!attributeConditions.length) {\r\n continue;\r\n }\r\n\r\n for (const textCondition of textConditions) {\r\n const xpathConditions = [...attributeConditions, textCondition];\r\n const xpath = buildXPath(xpathConditions);\r\n\r\n try {\r\n if (getCountOfXPath(xpath, domNode, docmt) === 1) {\r\n return xpath;\r\n }\r\n } catch {\r\n continue;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.log(`XPath generation error: ${JSON.stringify(error, null, 2)}`);\r\n }\r\n};\r\n\r\nexport const intermediateXpathStep = (\r\n targetElemt: HTMLElement | Element,\r\n attr: { name: string; value: string },\r\n isTarget: boolean\r\n): string => {\r\n return intermediateXpathSteps(targetElemt, attr, isTarget)[0] || \"\";\r\n};\r\n\r\nexport const intermediateXpathSteps = (\r\n targetElemt: HTMLElement | Element,\r\n attr: { name: string; value: string },\r\n isTarget: boolean\r\n): string[] => {\r\n let isSvgElement = isSvg(targetElemt);\r\n let expression: string = \"\";\r\n\r\n if (checkBlockedAttributes(attr, targetElemt, isTarget)) {\r\n let attrValue = sanitizeAttributeValue(attr.name, attr.value);\r\n const elementName = attr.name;\r\n\r\n if (elementName === \"class\") {\r\n const docmt =\r\n (targetElemt.getRootNode?.() as Document | ShadowRoot | null) ??\r\n targetElemt.ownerDocument;\r\n\r\n // Relative child/parent steps follow the same class-token rule as direct\r\n // XPath candidates: try every stable token, never numeric tokens.\r\n return getClassTokenConditions(targetElemt, attrValue, docmt).map(\r\n (classCondition) =>\r\n isSvgElement\r\n ? `*[local-name()='${targetElemt.tagName}' and ${classCondition}]`\r\n : `${targetElemt.tagName || \"*\"}[${classCondition}]`\r\n );\r\n }\r\n\r\n // Strict numeric-attribute validation for direct callers.\r\n if (hasNumericAttributeValue(attrValue)) {\r\n return [];\r\n }\r\n\r\n if (!reWhiteSpace.test(attrValue)) {\r\n expression = isSvgElement\r\n ? `*[local-name()='${\r\n targetElemt.tagName\r\n }' and normalize-space(@${elementName})=${escapeCharacters(\r\n attrValue\r\n )}]`\r\n : `${\r\n targetElemt.tagName || \"*\"\r\n }[normalize-space(@${elementName})=${escapeCharacters(attrValue)}]`;\r\n } else {\r\n expression = isSvgElement\r\n ? `*[local-name()='${\r\n targetElemt.tagName\r\n }' and @${elementName}=${escapeCharacters(attrValue)}]`\r\n : `${targetElemt.tagName || \"*\"}[@${elementName}=${escapeCharacters(\r\n attrValue\r\n )}]`;\r\n }\r\n }\r\n\r\n return expression ? [expression] : [];\r\n};\r\n\r\nexport const getFilteredTextXPath = (\r\n node: HTMLElement | Element,\r\n docmt: Document | ShadowRoot\r\n): string => {\r\n if (!node.textContent) return \"\";\r\n\r\n const filteredText = getFilteredText(node);\r\n const stableTargetText = getStableTargetText(filteredText);\r\n if (hasNumericAttributeValue(filteredText) && !stableTargetText) {\r\n // Do not build text XPath from purely numeric/dynamic target text.\r\n return \"\";\r\n }\r\n\r\n const textForPredicate =\r\n hasNumericAttributeValue(filteredText) ? stableTargetText : filteredText;\r\n const containerTextCondition = getContainerTextCondition(\r\n node,\r\n textForPredicate\r\n );\r\n\r\n let xpathe;\r\n\r\n if (containerTextCondition) {\r\n xpathe = buildPattern(\r\n containerTextCondition,\r\n isSvg(node),\r\n node.tagName || \"*\"\r\n );\r\n } else if (hasNumericAttributeValue(filteredText)) {\r\n xpathe = buildPattern(\r\n `contains(normalize-space(.),${escapeCharacters(textForPredicate)})`,\r\n isSvg(node),\r\n node.tagName || \"*\"\r\n );\r\n } else if (!reWhiteSpace.test(filteredText) || /\\s/.test(filteredText.trim())) {\r\n xpathe = buildPattern(\r\n `normalize-space(.)=${escapeCharacters(textForPredicate)}`,\r\n isSvg(node),\r\n node.tagName || \"*\"\r\n );\r\n } else {\r\n xpathe = isSvg(node)\r\n ? `//*[local-name()='${node.tagName}' and .=${escapeCharacters(\r\n textForPredicate\r\n )}]`\r\n : `//${node.tagName || \"*\"}[.=${escapeCharacters(textForPredicate)}]`;\r\n }\r\n\r\n return xpathe;\r\n};\r\n\r\nexport const getNormalizedPropertyXPath = (\r\n element: HTMLElement | Element,\r\n prop: string,\r\n value: string\r\n): string => {\r\n if ((prop === \".\" || prop === \"text()\") && hasNumericAttributeValue(value)) {\r\n const { fragment } = getStableTargetTextCandidates(value);\r\n\r\n return fragment\r\n ? buildPattern(\r\n `contains(normalize-space(.),${escapeCharacters(fragment)})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n )\r\n : \"\";\r\n }\r\n\r\n return buildPattern(\r\n `normalize-space(${prop})=${escapeCharacters(replaceWhiteSpaces(value)).trim()}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n};\r\n\r\nexport const getStartsWithPropertyXPath = (\r\n element: HTMLElement | Element,\r\n prop: string,\r\n value: string\r\n): string => {\r\n if ((prop === \".\" || prop === \"text()\") && hasNumericAttributeValue(value)) {\r\n const { prefix, fragment } = getStableTargetTextCandidates(value);\r\n const stableText = prefix || fragment;\r\n\r\n return stableText\r\n ? buildPattern(\r\n `starts-with(normalize-space(.),${escapeCharacters(stableText)})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n )\r\n : \"\";\r\n }\r\n\r\n return buildPattern(\r\n `starts-with(${prop},${escapeCharacters(replaceWhiteSpaces(value)).trim()})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n};\r\n\r\nexport const getContainsPropertyXPath = (\r\n element: HTMLElement | Element,\r\n prop: string,\r\n value: string\r\n): string => {\r\n if ((prop === \".\" || prop === \"text()\") && hasNumericAttributeValue(value)) {\r\n const { fragment } = getStableTargetTextCandidates(value);\r\n\r\n return fragment\r\n ? buildPattern(\r\n `contains(normalize-space(.),${escapeCharacters(fragment)})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n )\r\n : \"\";\r\n }\r\n\r\n return buildPattern(\r\n `contains(${prop},${escapeCharacters(replaceWhiteSpaces(value)).trim()})`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n};\r\n\r\nexport const getOrAttributesXPath = (\r\n element: HTMLElement | Element,\r\n attributes: Attr[]\r\n): string => {\r\n const buildAttributeConditions = (attribute: Attr): string[] => {\r\n const attrValue = sanitizeAttributeValue(attribute.name, attribute.value);\r\n\r\n if (!attrValue) {\r\n return [];\r\n }\r\n\r\n if (attribute.name === \"class\") {\r\n const docmt =\r\n (element.getRootNode?.() as Document | ShadowRoot | null) ??\r\n element.ownerDocument;\r\n\r\n return getClassTokenConditions(element, attrValue, docmt);\r\n }\r\n\r\n // Strict numeric-attribute validation for direct callers.\r\n if (hasNumericAttributeValue(attrValue)) return [];\r\n\r\n if (!reWhiteSpace.test(attrValue)) {\r\n return [\r\n `normalize-space(@${attribute.name})=${escapeCharacters(attrValue)}`\r\n ];\r\n }\r\n\r\n return [`@${attribute.name}=${escapeCharacters(attrValue)}`];\r\n };\r\n\r\n const buildTextCondition = (): string | null => {\r\n const rawText = replaceWhiteSpaces(getTextContent(element)?.trim() || \"\");\r\n const stableTargetText = getStableTargetText(rawText);\r\n if (hasNumericAttributeValue(rawText)) {\r\n // OR conditions must not reintroduce full dynamic numeric target text.\r\n return stableTargetText\r\n ? `contains(normalize-space(.),${escapeCharacters(stableTargetText)})`\r\n : null;\r\n }\r\n\r\n const containerTextCondition = getContainerTextCondition(element, rawText);\r\n\r\n if (containerTextCondition) {\r\n return containerTextCondition;\r\n }\r\n\r\n if (!rawText || rawText.length > 80 || /^\\d+$/.test(rawText)) {\r\n return null;\r\n }\r\n\r\n if (reWhiteSpace.test(rawText) && rawText.length <= 40 && !rawText.includes(\" \")) {\r\n return `text()=${escapeCharacters(rawText)}`;\r\n }\r\n\r\n if (rawText.includes(\" \")) {\r\n return `normalize-space(.)=${escapeCharacters(rawText)}`;\r\n }\r\n\r\n return `contains(text(),${escapeCharacters(rawText)})`;\r\n };\r\n\r\n const attributeConditions = attributes\r\n .flatMap((attribute) => buildAttributeConditions(attribute))\r\n .filter(Boolean);\r\n\r\n if (attributeConditions.length >= 2) {\r\n return buildPattern(\r\n `${attributeConditions[0]} or ${attributeConditions[1]}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n }\r\n\r\n const textCondition = buildTextCondition();\r\n if (attributeConditions.length === 1 && textCondition) {\r\n return buildPattern(\r\n `${attributeConditions[0]} or ${textCondition}`,\r\n isSvg(element),\r\n element.tagName.toLowerCase()\r\n );\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nconst getTagOnlyXpathCandidateCount = (\r\n xpath: string,\r\n element: HTMLElement | Element,\r\n docmt: Document | ShadowRoot\r\n) => {\r\n try {\r\n return getCountOfXPath(xpath, element, docmt);\r\n } catch (_error) {\r\n return 0;\r\n }\r\n};\r\n\r\nconst getAncestorAnchorCandidates = (\r\n node: HTMLElement | Element,\r\n docmt: Document | ShadowRoot\r\n): string[] => {\r\n const anchors: string[] = [];\r\n const seen = new Set<string>();\r\n const attributes = Array.from(node.attributes || []);\r\n const priorityAttrs = [\r\n \"id\",\r\n \"data-testid\",\r\n \"data-test\",\r\n \"data-qa\",\r\n \"name\",\r\n \"aria-label\",\r\n \"role\"\r\n ];\r\n const orderedAttributes = [\r\n ...priorityAttrs\r\n .map((attrName) => attributes.find((attr) => attr.name === attrName))\r\n .filter(Boolean),\r\n ...attributes.filter((attr) => !priorityAttrs.includes(attr.name))\r\n ] as Attr[];\r\n\r\n const pushAnchor = (xpath: string) => {\r\n if (!xpath || seen.has(xpath)) return;\r\n if (isExactUniqueXpath(xpath, node, docmt)) {\r\n seen.add(xpath);\r\n anchors.push(xpath);\r\n }\r\n };\r\n\r\n orderedAttributes.forEach((attr) => {\r\n if (!checkBlockedAttributes(attr, node, false)) return;\r\n\r\n for (const xpath of getXpathStrings(node, attr.name, attr.value)) {\r\n pushAnchor(xpath);\r\n }\r\n });\r\n\r\n const text = node.textContent?.trim();\r\n if (text && text.length < 40 && node.children.length === 0) {\r\n const textXpath = getFilteredTextXPath(node, docmt);\r\n if (textXpath) {\r\n pushAnchor(textXpath);\r\n }\r\n }\r\n\r\n if (attributes.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n node,\r\n docmt,\r\n attributes,\r\n false\r\n );\r\n if (combinationXpath) {\r\n pushAnchor(combinationXpath);\r\n }\r\n }\r\n\r\n return anchors;\r\n};\r\n\r\nconst getStructuralPathFromAncestor = (\r\n ancestor: HTMLElement | Element,\r\n element: HTMLElement | Element\r\n): string => {\r\n const steps: string[] = [];\r\n let current: Element | null = element;\r\n\r\n while (current && current !== ancestor) {\r\n steps.unshift(getAxisNodeTest(current));\r\n current = current.parentElement;\r\n }\r\n\r\n return current === ancestor ? steps.join(\"/\") : \"\";\r\n};\r\n\r\nexport const getTagOnlyXPath = (\r\n element: HTMLElement | Element,\r\n docmt?: Document | ShadowRoot\r\n): string => {\r\n const root =\r\n docmt ??\r\n ((element.getRootNode?.() as Document | ShadowRoot) ||\r\n element.ownerDocument);\r\n const tagName = getAxisNodeTest(element);\r\n const fallbackXpath = isSvg(element)\r\n ? `//*[local-name()='${element.tagName.toLowerCase()}']`\r\n : `//${element.tagName.toLowerCase()}`;\r\n\r\n for (\r\n let ancestor = element.parentElement;\r\n ancestor;\r\n ancestor = ancestor.parentElement\r\n ) {\r\n const ancestorAnchors = getAncestorAnchorCandidates(ancestor, root);\r\n\r\n for (const ancestorXpath of ancestorAnchors) {\r\n console.log(`Trying ancestor XPath: ${ancestorXpath}`);\r\n\r\n const descendantXpath = `${ancestorXpath}/descendant::${tagName}`;\r\n console.log(`Trying descendant XPath: ${descendantXpath}`);\r\n\r\n const count = getTagOnlyXpathCandidateCount(\r\n descendantXpath,\r\n element,\r\n root\r\n );\r\n console.log(`Match count: ${count}`);\r\n\r\n if (\r\n count === 1 &&\r\n getFirstMatchedNode(descendantXpath, root) === element\r\n ) {\r\n console.log(`Selected XPath: ${descendantXpath}`);\r\n return descendantXpath;\r\n }\r\n\r\n const structuralPath = getStructuralPathFromAncestor(ancestor, element);\r\n if (structuralPath) {\r\n const parentChainXpath = `${ancestorXpath}/${structuralPath}`;\r\n console.log(`Trying descendant XPath: ${parentChainXpath}`);\r\n\r\n const parentChainCount = getTagOnlyXpathCandidateCount(\r\n parentChainXpath,\r\n element,\r\n root\r\n );\r\n console.log(`Match count: ${parentChainCount}`);\r\n\r\n if (\r\n parentChainCount === 1 &&\r\n getFirstMatchedNode(parentChainXpath, root) === element\r\n ) {\r\n console.log(`Selected XPath: ${parentChainXpath}`);\r\n return parentChainXpath;\r\n }\r\n }\r\n }\r\n }\r\n\r\n console.log(`Trying descendant XPath: ${fallbackXpath}`);\r\n const fallbackCount = getTagOnlyXpathCandidateCount(\r\n fallbackXpath,\r\n element,\r\n root\r\n );\r\n console.log(`Match count: ${fallbackCount}`);\r\n\r\n if (\r\n fallbackCount === 1 &&\r\n getFirstMatchedNode(fallbackXpath, root) === element\r\n ) {\r\n console.log(`Selected XPath: ${fallbackXpath}`);\r\n return fallbackXpath;\r\n }\r\n\r\n console.log(\"Selected XPath: \");\r\n return \"\";\r\n};\r\n\r\nexport const getFirstMatchedNode = (\r\n xpath: string,\r\n docmt: Document | ShadowRoot\r\n): HTMLElement | Element | null => {\r\n try {\r\n if (isShadowRootNode(docmt)) {\r\n const { owner, contextNode, cloneElement } =\r\n createShadowEvaluationContext(docmt);\r\n const result = owner.evaluate(\r\n xpath,\r\n contextNode,\r\n null,\r\n XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n\r\n return (result.singleNodeValue || cloneElement) as\r\n | HTMLElement\r\n | Element\r\n | null;\r\n }\r\n\r\n const result = docmt.evaluate(\r\n xpath,\r\n docmt,\r\n null,\r\n docmt.defaultView!.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n\r\n return result.singleNodeValue as HTMLElement | Element | null;\r\n } catch (_error) {\r\n return null;\r\n }\r\n};\r\n\r\nexport const isExactUniqueXpath = (\r\n xpath: string,\r\n element: HTMLElement | Element,\r\n docmt: Document | ShadowRoot\r\n): boolean => {\r\n try {\r\n const { first, second } = evaluateXPathOnce(xpath, docmt);\r\n\r\n return !!first && !second && first === element;\r\n } catch {\r\n return false;\r\n }\r\n};\r\n\r\nexport const getAxisNodeTest = (element: HTMLElement | Element): string => {\r\n return isSvg(element)\r\n ? `*[local-name()='${element.tagName.toLowerCase()}']`\r\n : element.tagName.toLowerCase();\r\n};\r\n\r\nconst hasAnyNumber = (value: string) => /\\d/.test(value);\r\n\r\nexport const getUniqueNodeAnchorXpaths = (\r\n node: HTMLElement | Element,\r\n docmt: Document | ShadowRoot,\r\n isTarget: boolean\r\n): { key: string; value: string }[] => {\r\n if (!(node instanceof Element)) return [];\r\n const seen = new Set<string>();\r\n const anchors: { key: string; value: string }[] = [];\r\n\r\n const attributes = Array.from(node.attributes || []).filter(\r\n (attribute) =>\r\n attribute?.value &&\r\n !hasAnyNumber(attribute.value) &&\r\n checkBlockedAttributes(attribute, node, isTarget)\r\n );\r\n\r\n const pushAnchor = (key: string, value: string) => {\r\n if (!value || seen.has(value)) return;\r\n seen.add(value);\r\n anchors.push({ key, value });\r\n };\r\n\r\n // Priority attributes\r\n const priorityAttrs = [\r\n \"id\",\r\n \"data-testid\",\r\n \"data-test\",\r\n \"data-qa\",\r\n \"name\",\r\n \"aria-label\",\r\n \"role\"\r\n ];\r\n\r\n priorityAttrs.forEach((attrName) => {\r\n const attr = attributes.find((a) => a.name === attrName);\r\n if (!attr) return;\r\n\r\n for (const xpath of getXpathStrings(node, attr.name, attr.value)) {\r\n if (xpath && isExactUniqueXpath(xpath, node, docmt)) {\r\n pushAnchor(`anchor by ${attr.name}`, xpath);\r\n }\r\n }\r\n });\r\n\r\n // Other attributes\r\n attributes.forEach((attribute) => {\r\n if (priorityAttrs.includes(attribute.name)) return;\r\n\r\n for (const xpath of getXpathStrings(node, attribute.name, attribute.value)) {\r\n if (xpath && isExactUniqueXpath(xpath, node, docmt)) {\r\n pushAnchor(`anchor by ${attribute.name}`, xpath);\r\n }\r\n }\r\n });\r\n\r\n // Text (controlled)\r\n const text = node.textContent?.trim();\r\n if (text && text.length < 40 && node.children.length === 0) {\r\n const textXpath = getFilteredTextXPath(node, docmt);\r\n if (textXpath && isExactUniqueXpath(textXpath, node, docmt)) {\r\n pushAnchor(\"anchor by text\", textXpath);\r\n }\r\n }\r\n\r\n // Combination\r\n if (attributes.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n node,\r\n docmt,\r\n attributes,\r\n isTarget\r\n );\r\n\r\n if (combinationXpath && isExactUniqueXpath(combinationXpath, node, docmt)) {\r\n pushAnchor(\"anchor by combination\", combinationXpath);\r\n }\r\n }\r\n\r\n // Tag (last fallback)\r\n const tagXpath = getTagOnlyXPath(node, docmt);\r\n if (tagXpath && isExactUniqueXpath(tagXpath, node, docmt)) {\r\n pushAnchor(\"anchor by tag\", tagXpath);\r\n }\r\n\r\n return anchors;\r\n};\r\n\r\nexport const getTextXpathFunction = (\r\n domNode: HTMLElement | Element\r\n): string | undefined => {\r\n const trimmedText = getTextContent(domNode)?.trim();\r\n const stableTargetText = getStableTargetText(trimmedText);\r\n if (trimmedText && hasNumericAttributeValue(trimmedText)) {\r\n return stableTargetText\r\n ? `contains(normalize-space(.),${escapeCharacters(stableTargetText)})`\r\n : undefined;\r\n }\r\n\r\n const containerTextCondition = getContainerTextCondition(\r\n domNode,\r\n trimmedText\r\n );\r\n\r\n if (containerTextCondition) {\r\n return containerTextCondition;\r\n }\r\n\r\n const filteredText = trimmedText\r\n ? escapeCharacters(deleteGarbageFromInnerText(trimmedText))\r\n : trimmedText;\r\n if (filteredText) {\r\n if (filteredText !== `'${trimmedText}'`) {\r\n return `contains(.,${filteredText})`;\r\n }\r\n if (/\\s/.test(trimmedText)) {\r\n return `normalize-space(.)='${replaceWhiteSpaces(trimmedText)}'`;\r\n }\r\n return `normalize-space(.)='${trimmedText}'`;\r\n }\r\n};\r\n\r\nexport const getXpathString = (\r\n node: HTMLElement | Element,\r\n attrName: string,\r\n attrValue: string\r\n): string => {\r\n return getXpathStrings(node, attrName, attrValue)[0] || \"\";\r\n};\r\n\r\nexport const getXpathStrings = (\r\n node: HTMLElement | Element,\r\n attrName: string,\r\n attrValue: string\r\n): string[] => {\r\n const reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n let xpathe: string = \"\";\r\n attrValue = sanitizeAttributeValue(attrName, attrValue);\r\n\r\n if (attrValue) {\r\n if (attrName === \"class\") {\r\n const docmt =\r\n (node.getRootNode?.() as Document | ShadowRoot | null) ??\r\n node.ownerDocument;\r\n // Try every stable class token; numeric tokens are filtered by the\r\n // shared class helper before any XPath candidate is created.\r\n return getClassTokenConditions(node, attrValue, docmt).map((condition) =>\r\n isSvg(node)\r\n ? `//*[local-name()='${node.tagName}' and ${condition}]`\r\n : `//${node.tagName || \"*\"}[${condition}]`\r\n );\r\n }\r\n\r\n // Strict numeric-attribute validation: do not derive contains/starts-with\r\n // predicates from numeric attributes.\r\n if (hasNumericAttributeValue(attrValue)) return [];\r\n\r\n if (!reWhiteSpace.test(attrValue)) {\r\n xpathe = isSvg(node)\r\n ? `//*[local-name()='${\r\n node.tagName\r\n }' and contains(@${attrName},${escapeCharacters(attrValue)})]`\r\n : `//${node.tagName || \"*\"}[contains(@${attrName},${escapeCharacters(\r\n attrValue\r\n )})]`;\r\n } else {\r\n xpathe = isSvg(node)\r\n ? `//*[local-name()='${\r\n node.tagName\r\n }' and @${attrName}=${escapeCharacters(attrValue)}]`\r\n : `//${node.tagName || \"*\"}[@${attrName}=${escapeCharacters(\r\n attrValue\r\n )}]`;\r\n }\r\n }\r\n\r\n return xpathe ? [xpathe] : [];\r\n};\r\n\r\nexport const replaceActualAttributes = (\r\n str: string,\r\n element: { attributes: any }\r\n): string => {\r\n if (str) {\r\n return str.replace(/\\bdisabled\\b/gi, \"flndisabled\");\r\n }\r\n return str;\r\n};\r\n\r\nconst addAttributeSplitCombineXpaths = (\r\n attributes: NamedNodeMap,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean\r\n): { key: string; value: string }[] => {\r\n const attributesArray = Array.prototype.slice.call(attributes);\r\n const xpaths: { key: string; value: string }[] = [];\r\n try {\r\n attributesArray.map((element) => {\r\n if (checkBlockedAttributes(element, targetElemt, isTarget)) {\r\n const xpth = getCombinationXpath(element, targetElemt);\r\n if (xpth) {\r\n xpaths.push({\r\n key: `split xpath by ${element.name}`,\r\n value: xpth\r\n });\r\n }\r\n }\r\n });\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n\r\n return xpaths;\r\n};\r\n\r\nconst trimAttributePart = (value: string): string =>\r\n value.replace(/^[-_:\\s.]+|[-_:\\s.]+$/g, \"\").trim();\r\n\r\nconst getStableAttributePart = (attributeValue: string): string => {\r\n const value = attributeValue.trim();\r\n const prefix = trimAttributePart(value.split(/\\d/)[0] || \"\");\r\n const prefixParts = prefix\r\n .split(/[-_:\\s.]+/)\r\n .filter(Boolean);\r\n\r\n if (prefixParts.length > 1 && prefixParts[prefixParts.length - 1].length <= 2) {\r\n prefixParts.pop();\r\n }\r\n\r\n const stablePrefix = prefixParts.join(\"-\");\r\n if (stablePrefix.length > 2 && /[a-zA-Z]/.test(stablePrefix)) {\r\n return stablePrefix;\r\n }\r\n\r\n const parts = value\r\n .split(/[\\d\\s\\-_:./\\\\]+/)\r\n .map((part) => trimAttributePart(part))\r\n .filter((part) => part.length > 2 && /[a-zA-Z]/.test(part));\r\n\r\n return parts?.sort((left, right) => right.length - left.length)[0] || \"\";\r\n};\r\n\r\nconst getNumericSafeAttributeCondition = (\r\n attributeName: string,\r\n attributeValue: string\r\n): string => {\r\n // Strict numeric-attribute validation: numeric attributes must not be\r\n // converted into \"safe\" attribute predicates.\r\n return \"\";\r\n};\r\n\r\nconst getNumericSafeAttributeXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n attributeName: string,\r\n attributeValue: string\r\n): string => {\r\n // Strict numeric-attribute validation: all numeric attribute XPath fallbacks\r\n // are disabled; callers should continue to text/context strategies.\r\n return \"\";\r\n};\r\n\r\nexport const getReferenceElementsXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean\r\n): { key: string; value: string }[] => {\r\n let nodeXpath1;\r\n const xpaths1 = [];\r\n if (\r\n domNode.textContent &&\r\n (!isTarget || (isTarget && !isNumberExist(domNode.textContent)))\r\n ) {\r\n if (!reWhiteSpace.test(domNode.textContent)) {\r\n const textCondition = getTextXpathFunction(domNode);\r\n if (textCondition) {\r\n nodeXpath1 = isSvg(domNode)\r\n ? `*[local-name()='${domNode.tagName}' and ${textCondition}]`\r\n : `${domNode.tagName}[${textCondition}]`;\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: nodeXpath1 });\r\n }\r\n } else {\r\n const textContent = getTextContent(domNode);\r\n const containerTextCondition = getContainerTextCondition(\r\n domNode,\r\n textContent\r\n );\r\n const textCondition = containerTextCondition\r\n ? containerTextCondition\r\n : /\\s/.test(textContent.trim())\r\n ? `normalize-space(.)=${escapeCharacters(\r\n replaceWhiteSpaces(textContent)\r\n )}`\r\n : `.=${escapeCharacters(textContent)}`;\r\n nodeXpath1 = isSvg(domNode)\r\n ? `*[local-name()='${domNode.tagName}' and ${textCondition}]`\r\n : `${domNode.tagName}[${textCondition}]`;\r\n if (nodeXpath1) {\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: nodeXpath1 });\r\n }\r\n }\r\n }\r\n\r\n if (domNode.attributes) {\r\n const attributes =\r\n domNode.tagName === \"IMG\"\r\n ? Array.from(domNode.attributes).sort((left, right) => {\r\n if (left.name === \"alt\") return -1;\r\n if (right.name === \"alt\") return 1;\r\n return 0;\r\n })\r\n : Array.from(domNode.attributes);\r\n\r\n for (const attrName of attributes) {\r\n if (checkBlockedAttributes(attrName, domNode, isTarget)) {\r\n let attrValue = attrName.nodeValue;\r\n if (attrValue) {\r\n attrValue = sanitizeAttributeValue(attrName.name, attrValue);\r\n const elementName = attrName.name;\r\n // Strict numeric-attribute validation for direct callers that bypass\r\n // checkBlockedAttributes. Class is tokenized below, so only numeric\r\n // class tokens are skipped by getClassTokenCondition.\r\n if (elementName !== \"class\" && hasNumericAttributeValue(attrValue)) {\r\n continue;\r\n }\r\n\r\n if (elementName === \"class\") {\r\n const classConditions = getClassTokenConditions(\r\n domNode,\r\n attrValue,\r\n docmt\r\n );\r\n\r\n if (!classConditions.length) {\r\n continue;\r\n }\r\n\r\n for (const classCondition of classConditions) {\r\n nodeXpath1 = isSvg(domNode)\r\n ? `*[local-name()='${domNode.tagName}' and ${classCondition}]`\r\n : `${domNode.tagName}[${classCondition}]`;\r\n\r\n xpaths1.push({\r\n key: \"getReferenceElementsXpath\",\r\n value: nodeXpath1\r\n });\r\n }\r\n\r\n continue;\r\n } else {\r\n nodeXpath1 = isSvg(domNode)\r\n ? `*[local-name()='${\r\n domNode.tagName\r\n }' and @${elementName}=${escapeCharacters(attrValue)}]`\r\n : `${domNode.tagName}[@${elementName}=${escapeCharacters(\r\n attrValue\r\n )}]`;\r\n }\r\n\r\n if (nodeXpath1) {\r\n xpaths1.push({\r\n key: \"getReferenceElementsXpath\",\r\n value: nodeXpath1\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const attributesArray = Array.prototype.slice.call(domNode.attributes);\r\n if (attributesArray?.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n domNode,\r\n docmt,\r\n Array.prototype.slice.call(domNode.attributes),\r\n isTarget\r\n );\r\n if (combinationXpath) {\r\n xpaths1.push({\r\n key: \"getReferenceElementsXpath\",\r\n value: combinationXpath\r\n });\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const combinePattern = [];\r\n let pattern;\r\n const tag = domNode.tagName;\r\n if (domNode.textContent && isTarget && isNumberExist(domNode.textContent)) {\r\n const targetText = replaceWhiteSpaces(getTextContent(domNode)).trim();\r\n if (targetText?.length > 1) {\r\n combinePattern.push(`contains(text(),${escapeCharacters(targetText)})`);\r\n }\r\n\r\n if (combinePattern?.length) {\r\n if (isSvg(domNode)) {\r\n pattern = `*[local-name()='${tag}' and ${combinePattern.join(\r\n \" and \"\r\n )}]`;\r\n } else {\r\n pattern = `${tag}[${combinePattern.join(\" and \")}]`;\r\n }\r\n\r\n if (pattern)\r\n xpaths1.push({ key: \"getReferenceElementsXpath\", value: pattern });\r\n }\r\n }\r\n }\r\n\r\n if (!xpaths1?.length) {\r\n const xpaths = addAttributeSplitCombineXpaths(\r\n domNode.attributes,\r\n domNode,\r\n docmt,\r\n isTarget\r\n );\r\n if (xpaths?.length) {\r\n xpaths1.concat(xpaths);\r\n }\r\n }\r\n\r\n return xpaths1;\r\n};\r\n\r\nexport const parseXml = (\r\n xmlStr: string,\r\n type: DOMParserSupportedType\r\n): Document | null => {\r\n if (window.DOMParser) {\r\n return new window.DOMParser().parseFromString(xmlStr, type);\r\n }\r\n\r\n return null;\r\n};\r\n\r\nexport const normalizeXPath = (xpath: string): string => {\r\n // Replace text() = \"value\" or text()='value'\r\n xpath = xpath.replace(\r\n /text\\(\\)\\s*=\\s*(['\"])(.*?)\\1/g,\r\n \"normalize-space(.)=$1$2$1\"\r\n );\r\n\r\n // Replace . = \"value\" or .='value'\r\n xpath = xpath.replace(/\\.\\s*=\\s*(['\"])(.*?)\\1/g, \"normalize-space(.)=$1$2$1\");\r\n\r\n return xpath;\r\n};\r\n\r\nexport const findMatchingParenthesis = (\r\n text: string,\r\n openPos: number\r\n): number => {\r\n let closePos = openPos;\r\n let counter = 1;\r\n while (counter > 0) {\r\n const c = text[++closePos];\r\n if (c == \"(\") {\r\n counter++;\r\n } else if (c == \")\") {\r\n counter--;\r\n }\r\n }\r\n return closePos;\r\n};\r\n\r\nexport function canonicalizeXPath(xpath: string): string {\r\n return (\r\n xpath\r\n .toLowerCase()\r\n // replace quoted values\r\n .replace(/'[^']*'/g, \"?\")\r\n .replace(/\"[^\"]*\"/g, \"?\")\r\n // replace numbers in predicates\r\n .replace(/\\[\\d+\\]/g, \"[?]\")\r\n // normalize node names\r\n .replace(/\\/\\/[a-z0-9_-]+/g, \"//node\")\r\n .replace(/\\/[a-z0-9_-]+/g, \"/node\")\r\n );\r\n}\r\n\r\nexport function extractXPathSignatureParts(xpath: string) {\r\n const xp = xpath.toLowerCase();\r\n\r\n const axisMatch = xp.match(\r\n /(ancestor-or-self|ancestor|descendant-or-self|descendant|following-sibling|preceding-sibling|following|preceding|parent|child|self)::/\r\n );\r\n const axis = axisMatch?.[1] ?? \"none\";\r\n\r\n const attrMatch = xp.match(/@([a-z0-9:-]+)/);\r\n const attribute = attrMatch?.[1] ?? \"none\";\r\n\r\n const usesNormalize = xp.includes(\"normalize-space\");\r\n\r\n return { axis, attribute, usesNormalize };\r\n}\r\n\r\nexport function getXPathPattern(xpath: string): string {\r\n const canonical = canonicalizeXPath(xpath);\r\n const parts = extractXPathSignatureParts(xpath);\r\n\r\n return [\r\n \"XPATH\",\r\n `axis:${parts.axis}`,\r\n `attr:${parts.attribute}`,\r\n `normalize:${parts.usesNormalize}`,\r\n `shape:${canonical}`\r\n ].join(\"|\");\r\n}\r\n\r\nexport function escapeAttrValue(value: string): string {\r\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/ /g, \"\\\\ \");\r\n}\r\n\r\nexport function shouldUseSnapshot(xpath: string): boolean {\r\n return /\\[(?:\\s*\\.|\\s*contains\\s*\\(\\s*\\.|\\s*normalize-space\\s*\\(\\s*\\.)/.test(\r\n xpath\r\n );\r\n}\r\n\r\nexport function isUniqueInDOM(\r\n docmt: Document,\r\n name: string,\r\n value: string,\r\n element?: Element | null\r\n): boolean {\r\n const root = (element?.getRootNode?.() as ParentNode | null) ?? docmt;\r\n const queryAll = (selector: string): Element[] => {\r\n try {\r\n return Array.from(root.querySelectorAll(selector));\r\n } catch {\r\n return [];\r\n }\r\n };\r\n\r\n try {\r\n switch (name) {\r\n case \"id\":\r\n return queryAll(`#${escapeAttrValue(value)}`).length === 1;\r\n\r\n case \"name\":\r\n return queryAll(`[name=\"${escapeAttrValue(value)}\"]`).length === 1;\r\n\r\n case \"className\":\r\n return queryAll(`.${value}`).length === 1;\r\n\r\n case \"tagName\":\r\n return queryAll(value).length === 1;\r\n\r\n case \"linkText\":\r\n return (\r\n queryAll(\"a\").filter((a) => a.textContent?.trim() === value)\r\n .length === 1\r\n );\r\n case \"partialLinkText\":\r\n return (\r\n queryAll(\"a\").filter((a) => a.textContent?.includes(value)).length ===\r\n 1\r\n );\r\n case \"cssSelector\":\r\n return queryAll(value).length === 1;\r\n default:\r\n return false;\r\n }\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport const xpathUtils = {\r\n parseXml,\r\n getReferenceElementsXpath,\r\n getAbsoluteXPath,\r\n getRelativeXPath,\r\n getCombinationXpath,\r\n getAttributeCombinationXpath,\r\n getElementFromXpath,\r\n isSvg,\r\n findXpathWithIndex,\r\n isNumberExist,\r\n getTextContent,\r\n getCountOfXPath,\r\n normalizeXPath,\r\n getShadowRoot,\r\n escapeCharacters,\r\n removeParenthesis,\r\n checkBlockedAttributes,\r\n getRelationship,\r\n findMatchingParenthesis,\r\n deleteGarbageFromInnerText,\r\n replaceTempAttributes,\r\n createObserver,\r\n startObserver,\r\n stopObserver,\r\n modifiedElementAttributes,\r\n cspEnabled,\r\n getXPathPattern,\r\n getNormalizedPropertyXPath,\r\n getStartsWithPropertyXPath,\r\n getContainsPropertyXPath,\r\n getOrAttributesXPath,\r\n getTagOnlyXPath,\r\n getFirstMatchedNode,\r\n isExactUniqueXpath,\r\n getAxisNodeTest,\r\n getUniqueNodeAnchorXpaths,\r\n escapeAttrValue,\r\n isUniqueInDOM,\r\n sanitizeAttributeValue,\r\n getClassTokenCondition,\r\n getClassTokenConditions,\r\n getXpathStrings,\r\n intermediateXpathSteps,\r\n getContainerTextCondition\r\n};\r\n","import {\r\n isNumberExist,\r\n getCountOfXPath,\r\n escapeCharacters,\r\n checkBlockedAttributes,\r\n replaceWhiteSpaces,\r\n getTextContent,\r\n getPropertyXPath,\r\n findXpathWithIndex,\r\n getFilteredText,\r\n intermediateXpathStep,\r\n intermediateXpathSteps,\r\n getAttributeCombinationXpath,\r\n getFilteredTextXPath,\r\n getNormalizedPropertyXPath,\r\n getStartsWithPropertyXPath,\r\n getContainsPropertyXPath,\r\n getOrAttributesXPath,\r\n getTagOnlyXPath,\r\n isExactUniqueXpath,\r\n getAxisNodeTest,\r\n getUniqueNodeAnchorXpaths,\r\n isSvg,\r\n getXpathStrings,\r\n reWhiteSpace,\r\n sanitizeAttributeValue,\r\n getContainerTextCondition,\r\n getStableTargetText,\r\n hasNumericAttributeValue\r\n // timeLog\r\n} from \"./xpathHelpers.ts\";\r\n\r\nlet xpathData: { key: string; value: string }[] = [];\r\nlet xpathDataWithIndex: { key: string; value: string; count: number }[] = [];\r\nlet referenceElementMode: boolean = false;\r\nlet xpathCache = new Map();\r\nlet cache = new Map();\r\n\r\nconst parentXpathCache = new Map(); // Cache for parent XPaths\r\n\r\nconst STRATEGY_MAP = {\r\n andConditions: \"and\",\r\n orConditions: \"or\",\r\n contains: \"contains\",\r\n startsWith: \"startsWith\",\r\n normalizeSpace: \"normalizeSpace\",\r\n axes: \"axes\",\r\n index: \"index\",\r\n hardCodedText: \"text\",\r\n tillTag: \"tag\"\r\n} as const;\r\n\r\nconst getStrategyName = (strategy: string) => {\r\n return STRATEGY_MAP[strategy as keyof typeof STRATEGY_MAP] || strategy;\r\n};\r\n\r\nconst withStrategyKey = (\r\n strategy: string,\r\n entries: { key: string; value: string }[]\r\n) => {\r\n return entries.map((entry) => ({\r\n ...entry,\r\n key: entry?.key?.includes(strategy)\r\n ? entry.key\r\n : `${strategy} ${entry.key}`.trim()\r\n }));\r\n};\r\n\r\nconst getUniqueXpathEntries = (entries: { key: string; value: string }[]) => {\r\n const seen = new Set<string>();\r\n\r\n return entries.filter((entry) => {\r\n if (!entry?.value || seen.has(entry.value)) {\r\n return false;\r\n }\r\n\r\n seen.add(entry.value);\r\n return true;\r\n });\r\n};\r\n\r\nconst hasPositionalIndex = (xpath: string) =>\r\n /(?:^|\\/)[^/\\[]+\\[\\d+\\](?=\\/|$)/.test(xpath) ||\r\n /^\\(.+\\)\\[\\d+\\]$/.test(xpath);\r\n\r\nconst removePositionalIndexXpaths = (\r\n entries: { key: string; value: string }[],\r\n isIndex: boolean\r\n) =>\r\n isIndex ? entries : entries.filter((entry) => !hasPositionalIndex(entry.value));\r\n\r\nconst getNormalizedText = (element: HTMLElement | Element) => {\r\n return element?.textContent?.replace(/\\s+/g, \" \")?.trim() || \"\";\r\n};\r\n\r\nconst buildTextStrategyXpaths = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n strategy: string\r\n) => {\r\n const text = getNormalizedText(element);\r\n\r\n if (!text) {\r\n return [];\r\n }\r\n\r\n if (strategy === \"text\") {\r\n const textXpath =\r\n getFilteredTextXPath(element, docmt) ||\r\n getTextXPath(element, docmt, false, false);\r\n\r\n return textXpath ? [{ key: \"xpath by text\", value: textXpath }] : [];\r\n }\r\n\r\n return [];\r\n};\r\n\r\nconst buildPropertyStrategyXpaths = (\r\n element: HTMLElement | Element,\r\n isTarget: boolean,\r\n strategy: string\r\n) => {\r\n const xpaths: { key: string; value: string }[] = [];\r\n const attributes = Array.from(element.attributes || []).filter(\r\n (attribute) =>\r\n attribute?.value && checkBlockedAttributes(attribute, element, isTarget)\r\n );\r\n const text = getNormalizedText(element);\r\n const getStrategyXPath =\r\n strategy === \"contains\"\r\n ? getContainsPropertyXPath\r\n : strategy === \"startsWith\"\r\n ? getStartsWithPropertyXPath\r\n : strategy === \"normalizeSpace\"\r\n ? getNormalizedPropertyXPath\r\n : null;\r\n\r\n if (!getStrategyXPath) {\r\n return xpaths;\r\n }\r\n\r\n attributes.forEach((attribute) => {\r\n const xpath = getStrategyXPath(\r\n element,\r\n `@${attribute.name}`,\r\n attribute.value\r\n );\r\n\r\n if (xpath) {\r\n xpaths.push({\r\n key: `xpath by ${strategy} ${attribute.name}`,\r\n value: xpath\r\n });\r\n }\r\n });\r\n\r\n if (text) {\r\n const xpath = getStrategyXPath(element, \".\", text);\r\n\r\n if (xpath) {\r\n xpaths.push({\r\n key: `xpath by ${strategy} text`,\r\n value: xpath\r\n });\r\n }\r\n }\r\n\r\n return xpaths;\r\n};\r\n\r\nconst buildConditionStrategyXpaths = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n strategy: string\r\n) => {\r\n const attributes = Array.from(element.attributes || []).filter(\r\n (attribute) =>\r\n attribute?.value && checkBlockedAttributes(attribute, element, isTarget)\r\n );\r\n\r\n if (strategy === \"and\") {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n element,\r\n docmt,\r\n attributes,\r\n isTarget\r\n );\r\n\r\n return combinationXpath\r\n ? [{ key: \"xpath by combination\", value: combinationXpath }]\r\n : [];\r\n }\r\n\r\n if (strategy === \"or\" && attributes.length >= 1) {\r\n const xpath = getOrAttributesXPath(element, attributes);\r\n return xpath ? [{ key: \"xpath by or\", value: xpath }] : [];\r\n }\r\n\r\n return [];\r\n};\r\n\r\nconst collectSubtreeElements = (root: HTMLElement | Element) => {\r\n if (!root || !(root instanceof Element)) return [];\r\n\r\n const nodes: (HTMLElement | Element)[] = [];\r\n const queue = Array.from(root.children || []);\r\n\r\n while (queue.length) {\r\n const currentNode = queue.shift()!;\r\n\r\n if (currentNode.classList?.contains(\"flntooltip\")) {\r\n continue;\r\n }\r\n\r\n nodes.push(currentNode);\r\n queue.push(...Array.from(currentNode.children || []));\r\n }\r\n\r\n return nodes;\r\n};\r\n\r\nconst getSiblingNodes = (\r\n element: HTMLElement | Element,\r\n direction: \"previous\" | \"next\"\r\n) => {\r\n const nodes: (HTMLElement | Element)[] = [];\r\n let currentNode =\r\n direction === \"previous\"\r\n ? element.previousElementSibling\r\n : element.nextElementSibling;\r\n\r\n while (currentNode) {\r\n if (!currentNode.classList?.contains(\"flntooltip\")) {\r\n nodes.push(currentNode);\r\n }\r\n\r\n currentNode =\r\n direction === \"previous\"\r\n ? currentNode.previousElementSibling\r\n : currentNode.nextElementSibling;\r\n }\r\n\r\n return nodes;\r\n};\r\n\r\ntype AxisSeedNode =\r\n | Element\r\n | {\r\n node: Element;\r\n expand: () => Element[];\r\n };\r\n\r\nconst getDirectionalAxisSeedNodes = (\r\n element: HTMLElement | Element,\r\n direction: \"previous\" | \"next\"\r\n): AxisSeedNode[] => {\r\n const nodes: AxisSeedNode[] = [];\r\n\r\n for (\r\n let currentNode: HTMLElement | Element | null = element;\r\n currentNode?.parentElement;\r\n currentNode = currentNode.parentElement\r\n ) {\r\n let sibling =\r\n direction === \"previous\"\r\n ? currentNode.previousElementSibling\r\n : currentNode.nextElementSibling;\r\n\r\n while (sibling) {\r\n if (!sibling.classList?.contains(\"flntooltip\")) {\r\n nodes.push(sibling);\r\n\r\n nodes.push({\r\n node: sibling,\r\n expand: () => collectSubtreeElements(sibling as HTMLElement)\r\n });\r\n }\r\n\r\n sibling =\r\n direction === \"previous\"\r\n ? sibling.previousElementSibling\r\n : sibling.nextElementSibling;\r\n }\r\n }\r\n\r\n return nodes;\r\n};\r\n\r\nconst isLowQualityNode = (node: Element) => {\r\n if (!(node instanceof Element)) return true;\r\n const text = node.textContent?.trim();\r\n\r\n return (\r\n !node.id &&\r\n !node.className &&\r\n node.attributes.length === 0 &&\r\n (!text || text.length > 60) &&\r\n node.children.length > 2\r\n );\r\n};\r\n\r\nconst buildAxisXpathsForNodes = (\r\n nodes: any[],\r\n axis: string,\r\n targetElement: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n key: string,\r\n isIndex: boolean\r\n) => {\r\n const targetNodeTest = getAxisNodeTest(targetElement);\r\n const MAX_INDEX_TRY = 3;\r\n const MAX_CANDIDATES = 5;\r\n\r\n const candidates: { key: string; value: string }[] = [];\r\n\r\n let processed = 0;\r\n const MAX_PROCESS = axis.includes(\"descendant\") ? 10 : 20;\r\n\r\n for (const nodeItem of nodes) {\r\n if (processed++ > MAX_PROCESS) break;\r\n if (candidates.length >= MAX_CANDIDATES) break;\r\n\r\n const node = \"node\" in nodeItem ? nodeItem.node : nodeItem;\r\n\r\n if (!(node instanceof Element)) continue;\r\n if (isLowQualityNode(node)) continue;\r\n\r\n const anchorStages = [\r\n () => getUniqueNodeAnchorXpaths(node, docmt, isTarget),\r\n () => {\r\n const anchors: any[] = [];\r\n const attributes = Array.from(node.attributes || []).filter(\r\n (attr) =>\r\n attr?.value &&\r\n !/\\d/.test(attr.value) &&\r\n checkBlockedAttributes(attr, node, isTarget)\r\n );\r\n\r\n for (const attr of attributes) {\r\n const value = attr.value;\r\n if (!value) continue;\r\n\r\n const containsXpath = getContainsPropertyXPath(\r\n node,\r\n `@${attr.name}`,\r\n value\r\n );\r\n if (containsXpath) {\r\n anchors.push({ key: \"contains\", value: containsXpath });\r\n }\r\n\r\n const startsWithXpath = getStartsWithPropertyXPath(\r\n node,\r\n `@${attr.name}`,\r\n value\r\n );\r\n if (startsWithXpath) {\r\n anchors.push({ key: \"starts-with\", value: startsWithXpath });\r\n }\r\n }\r\n\r\n return anchors;\r\n },\r\n () => [{ key: \"tag\", value: getTagOnlyXPath(node, docmt) }]\r\n ];\r\n\r\n for (let stageIndex = 0; stageIndex < anchorStages.length; stageIndex++) {\r\n let anchors = anchorStages[stageIndex]();\r\n if (!anchors.length) continue;\r\n\r\n if (!isIndex) {\r\n anchors = anchors.filter((a) => !/\\[\\d+\\]/.test(a.value));\r\n }\r\n\r\n const seen = new Set<string>();\r\n anchors = anchors.filter((a) => {\r\n if (seen.has(a.value)) return false;\r\n seen.add(a.value);\r\n return true;\r\n });\r\n\r\n anchors = anchors.slice(0, 5);\r\n\r\n for (const anchor of anchors) {\r\n const xpath = `${anchor.value}/${axis}::${targetNodeTest}`;\r\n\r\n if (isExactUniqueXpath(xpath, targetElement, docmt)) {\r\n candidates.push({ key, value: xpath });\r\n\r\n if (candidates.length >= 2) {\r\n return candidates;\r\n }\r\n\r\n if (candidates.length >= MAX_CANDIDATES) {\r\n return candidates;\r\n }\r\n }\r\n }\r\n\r\n if (isIndex && stageIndex === anchorStages.length - 1) {\r\n for (const anchor of anchors) {\r\n const count = getCountOfXPath(anchor.value, node, docmt);\r\n\r\n for (let i = 1; i <= Math.min(count, MAX_INDEX_TRY); i++) {\r\n const xpath = `(${anchor.value})[${i}]/${axis}::${targetNodeTest}`;\r\n\r\n if (isExactUniqueXpath(xpath, targetElement, docmt)) {\r\n candidates.push({ key, value: xpath });\r\n\r\n if (candidates.length >= 2) {\r\n return candidates;\r\n }\r\n\r\n if (candidates.length >= MAX_CANDIDATES) {\r\n return candidates;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (\"expand\" in nodeItem && nodeItem.expand) {\r\n const expanded = nodeItem.expand();\r\n\r\n let childCount = 0;\r\n for (const child of expanded) {\r\n if (childCount++ > 15) break;\r\n if (candidates.length >= MAX_CANDIDATES) break;\r\n\r\n if (!(child instanceof Element)) continue;\r\n if (isLowQualityNode(child)) continue;\r\n\r\n const anchors = getUniqueNodeAnchorXpaths(child, docmt, isTarget);\r\n\r\n for (const anchor of anchors.slice(0, 5)) {\r\n const xpath = `${anchor.value}/${axis}::${targetNodeTest}`;\r\n\r\n if (isExactUniqueXpath(xpath, targetElement, docmt)) {\r\n candidates.push({ key, value: xpath });\r\n\r\n if (candidates.length >= 2) {\r\n return candidates;\r\n }\r\n\r\n if (candidates.length >= MAX_CANDIDATES) {\r\n return candidates;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return candidates;\r\n};\r\n\r\nconst buildAxesStrategyXpaths = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n isIndex: boolean\r\n) => {\r\n // const totalStart = performance.now();\r\n\r\n // let t = performance.now();\r\n\r\n const ancestors: (HTMLElement | Element)[] = [];\r\n\r\n for (\r\n let currentNode = element.parentElement;\r\n currentNode;\r\n currentNode = currentNode.parentElement\r\n ) {\r\n ancestors.push(currentNode);\r\n }\r\n // timeLog(\"ancestors collection\", t);\r\n // t = performance.now();\r\n const descendants = collectSubtreeElements(element);\r\n // timeLog(\"descendants collection\", t);\r\n // t = performance.now();\r\n const previousSiblings = getSiblingNodes(element, \"previous\");\r\n // timeLog(\"previous siblings collection\", t);\r\n\r\n const nextSiblings = getSiblingNodes(element, \"next\");\r\n // timeLog(\"next siblings collection\", t);\r\n\r\n const precedingSeeds = getDirectionalAxisSeedNodes(element, \"previous\");\r\n // timeLog(\"preceding seeds collection\", t);\r\n // t = performance.now();\r\n const followingSeeds = getDirectionalAxisSeedNodes(element, \"next\");\r\n // timeLog(\"following seeds collection\", t);\r\n // t = performance.now();\r\n const scoreNode = (node: Element) => {\r\n let score = 0;\r\n if (node.id) score += 100;\r\n if (node.getAttribute(\"data-testid\")) score += 90;\r\n if (node.className) score += 50;\r\n if (node.children.length === 0) score += 30;\r\n\r\n const text = node.textContent?.trim();\r\n if (text && text.length < 40) score += 40;\r\n\r\n return score;\r\n };\r\n\r\n const limitNodes = (nodes: any[], limit = 20) => {\r\n return nodes.length > limit ? nodes.slice(0, limit) : nodes;\r\n };\r\n\r\n const tiers = [\r\n [\r\n {\r\n nodes: limitNodes(nextSiblings),\r\n axis: \"preceding-sibling\",\r\n key: \"preceding-sibling\"\r\n },\r\n {\r\n nodes: limitNodes(previousSiblings),\r\n axis: \"following-sibling\",\r\n key: \"following-sibling\"\r\n }\r\n ],\r\n [\r\n { nodes: limitNodes(ancestors), axis: \"child\", key: \"child\" },\r\n {\r\n nodes: limitNodes(descendants, 15),\r\n axis: \"parent\",\r\n key: \"parent\"\r\n }\r\n ],\r\n [\r\n { nodes: limitNodes(ancestors), axis: \"descendant\", key: \"descendant\" },\r\n {\r\n nodes: limitNodes(ancestors),\r\n axis: \"descendant-or-self\",\r\n key: \"descendant-or-self\"\r\n },\r\n {\r\n nodes: limitNodes(descendants, 10),\r\n axis: \"ancestor\",\r\n key: \"ancestor\"\r\n },\r\n {\r\n nodes: limitNodes(descendants, 10),\r\n axis: \"ancestor-or-self\",\r\n key: \"ancestor-or-self\"\r\n }\r\n ],\r\n [\r\n {\r\n nodes: limitNodes(precedingSeeds),\r\n axis: \"following\",\r\n key: \"following\"\r\n },\r\n { nodes: limitNodes(followingSeeds), axis: \"preceding\", key: \"preceding\" }\r\n ]\r\n ];\r\n // timeLog(\"nodes sorting\", t);\r\n for (const tier of tiers) {\r\n for (const strategy of tier) {\r\n // const t = performance.now();\r\n\r\n const results = buildAxisXpathsForNodes(\r\n strategy.nodes,\r\n strategy.axis,\r\n element,\r\n docmt,\r\n isTarget,\r\n strategy.key,\r\n isIndex\r\n );\r\n\r\n // timeLog(`strategy ${strategy.key}`, t);\r\n if (results.length > 0) {\r\n // timeLog(\"TOTAL buildAxesStrategyXpaths\", totalStart);\r\n return results;\r\n }\r\n }\r\n }\r\n\r\n return [];\r\n};\r\n\r\nexport const generateIndexedXpaths = (element: Element, docmt: Document) => {\r\n const results = [];\r\n const tag = element.tagName.toLowerCase();\r\n\r\n const globalList = Array.from(docmt.querySelectorAll(tag));\r\n const idx = globalList.indexOf(element) + 1;\r\n\r\n if (idx <= 0) return [];\r\n\r\n // global must always exist)\r\n results.push({\r\n key: \"xpath by index\",\r\n value: `(//${tag})[${idx}]`\r\n });\r\n\r\n // Add at most one scoped/indexed XPath anchored by the nearest stable ancestor.\r\n let current: Element | null = element.parentElement;\r\n\r\n while (current && current !== docmt.body) {\r\n if (current.id || current.className) {\r\n const scopeXpaths = current.id\r\n ? getXpathStrings(current, \"id\", current.id)\r\n : getXpathStrings(current, \"class\", current.className.toString());\r\n\r\n // Scoped index anchors should also try every stable class token before\r\n // walking to a higher ancestor.\r\n for (const scopeXpath of scopeXpaths) {\r\n const scopedDescendants = Array.from(current.querySelectorAll(tag));\r\n const scopedIdx = scopedDescendants.indexOf(element) + 1;\r\n\r\n if (scopedIdx > 0) {\r\n results.push({\r\n key: \"xpath by index\",\r\n value: `(${scopeXpath}//${tag})[${scopedIdx}]`\r\n });\r\n break;\r\n }\r\n }\r\n\r\n if (results.length > 1) {\r\n break;\r\n }\r\n }\r\n\r\n current = current.parentElement;\r\n }\r\n\r\n return results;\r\n};\r\n\r\nconst buildStrategyXpaths = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n strategy: string,\r\n fallbackXpaths: { key: string; value: string }[]\r\n) => {\r\n const strategyName = getStrategyName(strategy);\r\n\r\n if (strategyName === \"text\") {\r\n return withStrategyKey(\r\n strategy,\r\n buildTextStrategyXpaths(element, docmt, strategyName)\r\n );\r\n }\r\n\r\n if ([\"contains\", \"startsWith\", \"normalizeSpace\"].includes(strategyName)) {\r\n return withStrategyKey(\r\n strategy,\r\n buildPropertyStrategyXpaths(element, isTarget, strategyName)\r\n );\r\n }\r\n\r\n if ([\"and\", \"or\"].includes(strategyName)) {\r\n return withStrategyKey(\r\n strategy,\r\n buildConditionStrategyXpaths(element, docmt, isTarget, strategyName)\r\n );\r\n }\r\n\r\n if (strategyName === \"axes\") {\r\n return withStrategyKey(\r\n strategy,\r\n buildAxesStrategyXpaths(element, docmt, isTarget, false)\r\n );\r\n }\r\n\r\n if (strategyName === \"index\") {\r\n return withStrategyKey(strategy, generateIndexedXpaths(element, docmt));\r\n }\r\n\r\n if (strategyName === \"tag\") {\r\n return withStrategyKey(strategy, [\r\n { key: \"xpath till tag\", value: getTagOnlyXPath(element, docmt) }\r\n ]);\r\n }\r\n\r\n return [];\r\n};\r\n\r\nconst checkRelativeXpathRelation = (\r\n nodeXpath1: string,\r\n nodeXpath2: string,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n relationType: string\r\n) => {\r\n if (nodeXpath1 && !referenceElementMode) {\r\n let xpaths;\r\n\r\n if (relationType === \"parent\") {\r\n xpaths = [\r\n // `${nodeXpath1}/descendant::${nodeXpath2}`,\r\n `${nodeXpath1}/descendant-or-self::${nodeXpath2}`,\r\n `${nodeXpath1}/following::${nodeXpath2}`\r\n ];\r\n } else {\r\n xpaths = [\r\n // `${nodeXpath1}/descendant::${nodeXpath2}`,\r\n `${nodeXpath1}/ancestor-or-self::${nodeXpath2}`,\r\n `${nodeXpath1}/preceding::${nodeXpath2}`\r\n ];\r\n }\r\n\r\n // Iterate through XPath patterns\r\n for (const xpath of xpaths) {\r\n // Check if result is already cached to avoid recomputation\r\n if (!xpathCache?.get(xpath)) {\r\n // Compute and store result in cache\r\n xpathCache.set(xpath, getCountOfXPath(xpath, targetElemt, docmt));\r\n }\r\n\r\n const count = xpathCache?.get(xpath);\r\n\r\n // Short-circuit: Return the first valid XPath result found\r\n if (count === 1) {\r\n return xpath;\r\n }\r\n if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: xpath,\r\n count\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: xpath,\r\n count\r\n });\r\n }\r\n }\r\n\r\n if (count > 1 && isIndex && !xpathData.length) {\r\n // Try finding XPath with index if count is greater than 1\r\n const indexedXpath = findXpathWithIndex(\r\n xpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n\r\n // Cache the indexed XPath result\r\n if (\r\n indexedXpath &&\r\n getCountOfXPath(indexedXpath, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by unique parent ${isIndex ? \"index\" : \"\"}`,\r\n value: indexedXpath\r\n });\r\n }\r\n }\r\n }\r\n }\r\n return null;\r\n};\r\n\r\nconst getUniqueParentXpath = (\r\n domNode: HTMLElement,\r\n docmt: Document,\r\n node: HTMLElement | Element,\r\n isTarget: boolean,\r\n nodeXpath: string,\r\n isIndex: boolean\r\n) => {\r\n try {\r\n if (parentXpathCache.has(domNode)) {\r\n return parentXpathCache.get(domNode);\r\n }\r\n\r\n // Direct XPath construction without loops\r\n const xpathParts = [];\r\n let currentNode = domNode;\r\n\r\n while (currentNode && currentNode.nodeType === 1) {\r\n const hasUniqueAttr = false;\r\n for (const attrName of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attrName, currentNode, isTarget)) {\r\n const attrValue = sanitizeAttributeValue(\r\n attrName.name,\r\n attrName.nodeValue\r\n );\r\n const elementName = attrName.name;\r\n\r\n for (const xpathe of getXpathStrings(currentNode, elementName, attrValue)) {\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute isn't actually unique, get it's index too\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (currentNode.textContent && !currentNode.textContent) {\r\n if (\r\n !isTarget ||\r\n (isTarget && !isNumberExist(currentNode.textContent))\r\n ) {\r\n let xpathe;\r\n\r\n if (!reWhiteSpace.test(currentNode.textContent)) {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`\r\n : `//${\r\n currentNode.tagName || \"*\"\r\n }[normalize-space(.)=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`;\r\n } else {\r\n xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and .=${escapeCharacters(getFilteredText(currentNode))}]`\r\n : `//${currentNode.tagName || \"*\"}[.=${escapeCharacters(\r\n getFilteredText(currentNode)\r\n )}]`;\r\n }\r\n\r\n const othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n } else {\r\n const combinePattern = [];\r\n const contentRes = [\r\n ...new Set(getFilteredText(currentNode).match(/([^0-9]+)/g))\r\n ];\r\n const reWhiteSpace = new RegExp(/^[\\S]+( [\\S]+)*$/gi);\r\n if (contentRes?.length) {\r\n for (let i = 0; i < contentRes?.length; i++) {\r\n if (contentRes[i] && replaceWhiteSpaces(contentRes[i].trim())) {\r\n if (!reWhiteSpace.test(contentRes[i])) {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n replaceWhiteSpaces(contentRes[i])\r\n ).trim()})`\r\n );\r\n } else {\r\n combinePattern.push(\r\n `contains(.,${escapeCharacters(\r\n contentRes[i].trim()\r\n ).trim()})`\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (combinePattern?.length) {\r\n const xpathe = isSvg(currentNode)\r\n ? `//*[local-name()='${\r\n currentNode.tagName\r\n }' and ${combinePattern.join(\" and \")}]`\r\n : `//${currentNode.tagName || \"*\"}[${combinePattern.join(\r\n \" and \"\r\n )}]`;\r\n const othersWithAttr = checkRelativeXpathRelation(\r\n xpathe,\r\n nodeXpath,\r\n node,\r\n docmt,\r\n isIndex,\r\n \"parent\"\r\n );\r\n if (othersWithAttr) {\r\n return othersWithAttr;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Construct the XPath based on the tag name\r\n if (!hasUniqueAttr) {\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n xpathParts.unshift(xpathe);\r\n } else {\r\n break;\r\n }\r\n\r\n // Move to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Final constructed XPath\r\n const finalXPath = xpathParts.join(\"\") + nodeXpath;\r\n let count = getCountOfXPath(finalXPath, domNode, docmt);\r\n if (count === 1) {\r\n parentXpathCache.set(domNode, finalXPath); // Cache final result\r\n return finalXPath;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getParentRelativeXpath = (\r\n domNode: HTMLElement,\r\n docmt: Document,\r\n node: HTMLElement | Element,\r\n isTarget: boolean\r\n) => {\r\n const cache = new Map(); // Cache to store computed results\r\n\r\n if (cache.has(domNode)) {\r\n return cache.get(domNode); // Return cached result if available\r\n }\r\n\r\n const xpathParts = []; // Initialize an array to hold parts of the XPath\r\n let currentNode = domNode; // Start with the provided DOM node\r\n\r\n try {\r\n while (currentNode && currentNode.nodeType === 1) {\r\n // BASE CASE #1: If this isn't an element, we're above the root, return empty string\r\n if (!currentNode.tagName) {\r\n return \"\";\r\n }\r\n\r\n // BASE CASE #2: Check for unique attributes\r\n let uniqueAttrFound = false;\r\n for (const attr of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attr, currentNode, isTarget)) {\r\n const attrValue = sanitizeAttributeValue(attr.name, attr.nodeValue);\r\n const elementName = attr.name;\r\n\r\n for (const xpathe of getXpathStrings(currentNode, elementName, attrValue)) {\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute is unique, return its XPath\r\n if (othersWithAttr === 1) {\r\n xpathParts.unshift(xpathe);\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (uniqueAttrFound) break;\r\n }\r\n\r\n // If no unique attributes, check for text content\r\n if (!uniqueAttrFound && currentNode.textContent && !node.textContent) {\r\n const textXPath = getFilteredTextXPath(currentNode, docmt);\r\n if (textXPath) {\r\n const othersWithAttr = getCountOfXPath(textXPath, currentNode, docmt);\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.unshift(textXPath);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!uniqueAttrFound) {\r\n // Construct the XPath based on the tag name\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Prepend the current XPath part to the array\r\n xpathParts.unshift(xpathe);\r\n } else {\r\n break;\r\n }\r\n // Move to the parent node for the next iteration\r\n currentNode = currentNode.parentElement!;\r\n }\r\n\r\n // Combine all parts into the final XPath\r\n const finalXpath = xpathParts.join(\"\");\r\n cache.set(domNode, finalXpath); // Store result in cache\r\n return finalXpath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getChildRelativeXpath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n node: HTMLElement | Element\r\n) => {\r\n const xpathParts = []; // Initialize an array to hold parts of the XPath.\r\n let currentNode: HTMLElement | Element | null;\r\n\r\n const st = [];\r\n if (\r\n domNode.firstElementChild != null &&\r\n domNode.firstElementChild.classList.contains(\"flntooltip\")\r\n ) {\r\n st.unshift(domNode.firstElementChild);\r\n } else if (domNode.nextElementSibling != null)\r\n st.unshift(domNode.nextElementSibling);\r\n\r\n for (\r\n let m = domNode.parentElement;\r\n m != null && m.nodeType === 1;\r\n m = m.parentElement\r\n ) {\r\n if (m.nextElementSibling) st.unshift(m.nextElementSibling);\r\n }\r\n\r\n try {\r\n do {\r\n let uniqueAttrFound = false;\r\n for (currentNode = st.pop()!; currentNode !== null; ) {\r\n for (const attr of Array.from(currentNode.attributes)) {\r\n if (checkBlockedAttributes(attr, currentNode, true)) {\r\n const attrValue = sanitizeAttributeValue(attr.name, attr.nodeValue);\r\n const elementName = attr.name;\r\n\r\n for (const xpathe of getXpathStrings(currentNode, elementName, attrValue)) {\r\n let othersWithAttr;\r\n\r\n // If the XPath does not parse, move to the next unique attribute\r\n try {\r\n othersWithAttr = getCountOfXPath(xpathe, currentNode, docmt);\r\n } catch (ign) {\r\n continue;\r\n }\r\n\r\n // If the attribute is unique, return its XPath\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.push(xpathe);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (uniqueAttrFound) break;\r\n }\r\n\r\n // If no unique attributes, check for text content\r\n if (!uniqueAttrFound && currentNode.textContent && !node.textContent) {\r\n const textXPath = getFilteredTextXPath(currentNode, docmt);\r\n if (textXPath) {\r\n const othersWithAttr = getCountOfXPath(\r\n textXPath,\r\n currentNode,\r\n docmt\r\n );\r\n if (othersWithAttr === 1) {\r\n uniqueAttrFound = true; // Mark that we found at least one unique attribute\r\n xpathParts.push(textXPath);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!uniqueAttrFound) {\r\n // Construct the XPath based on the tag name\r\n const xpathe = isSvg(currentNode)\r\n ? `/*[local-name()='${currentNode.tagName}']`\r\n : `/${currentNode.tagName}`;\r\n\r\n // Prepend the current XPath part to the array\r\n xpathParts.push(xpathe);\r\n\r\n if (currentNode.firstElementChild != null) {\r\n st.push(currentNode.nextElementSibling);\r\n currentNode = currentNode.firstElementChild;\r\n } else {\r\n currentNode = currentNode.nextElementSibling;\r\n }\r\n } else {\r\n break;\r\n }\r\n }\r\n } while (st.length > 0);\r\n\r\n // Combine all parts into the final XPath\r\n const finalXpath = xpathParts.join(\"\");\r\n cache.set(domNode, finalXpath); // Store result in cache\r\n return finalXpath;\r\n } catch (error) {\r\n console.log(error);\r\n return null;\r\n }\r\n};\r\n\r\nconst getSiblingRelativeXPath = (\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean,\r\n nodeXpath: string\r\n) => {\r\n try {\r\n const markedSpan = document.querySelector(\".flntooltip\");\r\n\r\n for (\r\n let m = domNode.nextElementSibling;\r\n m !== null && m !== markedSpan;\r\n m = m.nextElementSibling\r\n ) {\r\n processSibling(\r\n m,\r\n domNode,\r\n docmt,\r\n nodeXpath,\r\n \"preceding-sibling\",\r\n isTarget\r\n );\r\n }\r\n\r\n for (\r\n let n = domNode.previousElementSibling;\r\n n !== null && n !== markedSpan;\r\n n = n.previousElementSibling\r\n ) {\r\n processSibling(\r\n n,\r\n domNode,\r\n docmt,\r\n nodeXpath,\r\n \"following-sibling\",\r\n isTarget\r\n );\r\n }\r\n } catch (error) {\r\n console.error(\"sibling error\", error);\r\n return null;\r\n }\r\n};\r\n\r\nconst processSibling = (\r\n sibling: Element,\r\n domNode: HTMLElement | Element,\r\n docmt: Document,\r\n nodeXpath: string,\r\n axis: string,\r\n isTarget: boolean\r\n) => {\r\n try {\r\n if (sibling.hasAttributes()) {\r\n for (const attr of Array.from(sibling.attributes)) {\r\n const xpaths = intermediateXpathSteps(\r\n sibling,\r\n {\r\n name: attr.name,\r\n value: attr.value\r\n },\r\n isTarget\r\n );\r\n\r\n for (let xpathe of xpaths) {\r\n xpathe += `/${axis}::${nodeXpath}`;\r\n\r\n const count = getCountOfXPath(xpathe, sibling, docmt);\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `xpath by ${axis}`,\r\n value: xpathe\r\n });\r\n return;\r\n } else if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!isTarget) {\r\n let xpathe;\r\n xpathe = intermediateXpathStep(\r\n sibling,\r\n {\r\n name: \"text\",\r\n value: sibling.textContent\r\n },\r\n isTarget\r\n );\r\n\r\n if (xpathe) {\r\n const count = getCountOfXPath(xpathe, sibling, docmt);\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `xpath by ${axis}`,\r\n value: xpathe\r\n });\r\n return;\r\n } else if (count > 1) {\r\n if (xpathDataWithIndex.length) {\r\n if (count < xpathDataWithIndex[0].count) {\r\n xpathDataWithIndex.pop();\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count\r\n });\r\n }\r\n } else {\r\n xpathDataWithIndex.push({\r\n key: `relative xpath by ${axis}`,\r\n value: xpathe,\r\n count\r\n });\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.log(`${axis} xpath-error`, error);\r\n }\r\n};\r\n\r\nfunction getXPathUsingAttributeAndText(\r\n attributes: Attr[],\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isTarget: boolean\r\n) {\r\n const { tagName } = targetElemt;\r\n const textContent = targetElemt.textContent.trim();\r\n if (!textContent) {\r\n return;\r\n }\r\n const normalizedTextContent = replaceWhiteSpaces(textContent);\r\n const stableTargetText = getStableTargetText(normalizedTextContent);\r\n if (hasNumericAttributeValue(normalizedTextContent) && !stableTargetText) {\r\n return;\r\n }\r\n\r\n const textForPredicate = hasNumericAttributeValue(normalizedTextContent)\r\n ? stableTargetText\r\n : normalizedTextContent;\r\n const containerTextCondition = getContainerTextCondition(\r\n targetElemt,\r\n textForPredicate\r\n );\r\n for (const attrName of attributes) {\r\n if (checkBlockedAttributes(attrName, targetElemt, isTarget)) {\r\n let attrValue = sanitizeAttributeValue(attrName.name, attrName.nodeValue);\r\n const elementName = attrName.name;\r\n const textCondition = containerTextCondition\r\n ? containerTextCondition\r\n : hasNumericAttributeValue(normalizedTextContent)\r\n ? `contains(normalize-space(.),${escapeCharacters(textForPredicate)})`\r\n : /\\s/.test(textContent)\r\n ? `normalize-space(.)=${escapeCharacters(textForPredicate)}`\r\n : `text()=${escapeCharacters(textForPredicate)}`;\r\n const xpath = `//${tagName}[@${elementName}='${attrValue}' and ${textCondition}]`;\r\n if (xpath) {\r\n const count = getCountOfXPath(xpath, targetElemt, docmt);\r\n if (count == 1) {\r\n return xpath;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nconst addRelativeXpaths = (\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean,\r\n attribute: Attr[]\r\n) => {\r\n try {\r\n let nodeXpath: string[] = [];\r\n let relativeXpath, relativeChildXpath;\r\n xpathData = [];\r\n\r\n console.log(attribute);\r\n if (attribute) {\r\n for (const attrName of attribute) {\r\n const expressions = intermediateXpathSteps(\r\n targetElemt,\r\n {\r\n name: attrName.name,\r\n value: attrName.value\r\n },\r\n isTarget\r\n );\r\n\r\n console.log(expressions[0] || \"\");\r\n for (const expression of expressions) {\r\n if (expression) {\r\n nodeXpath.push(expression);\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (targetElemt.textContent) {\r\n let expression = intermediateXpathStep(\r\n targetElemt,\r\n {\r\n name: \"text\",\r\n value: targetElemt.textContent\r\n },\r\n isTarget\r\n );\r\n\r\n console.log(expression);\r\n if (expression) {\r\n nodeXpath.push(expression);\r\n }\r\n }\r\n\r\n nodeXpath.push(targetElemt.tagName);\r\n\r\n if (nodeXpath?.length) {\r\n for (let i = 0; i < nodeXpath.length; i++) {\r\n if (!xpathData.length) {\r\n getSiblingRelativeXPath(targetElemt, docmt, isTarget, nodeXpath[i]);\r\n\r\n if (!xpathData.length) {\r\n if (!relativeXpath) {\r\n relativeXpath = getParentRelativeXpath(\r\n targetElemt.parentElement!,\r\n docmt,\r\n targetElemt,\r\n isTarget\r\n );\r\n }\r\n\r\n console.log(relativeXpath);\r\n\r\n if (\r\n relativeXpath &&\r\n (relativeXpath.includes(\"@\") ||\r\n relativeXpath.includes(\"text()\") ||\r\n relativeXpath.includes(\".=\")) &&\r\n relativeXpath.match(/\\//g)?.length - 2 < 5\r\n ) {\r\n const fullRelativeXpath = relativeXpath + `/${nodeXpath[i]}`;\r\n const count = getCountOfXPath(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt\r\n );\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: \"relative xpath by relative parent\",\r\n value: fullRelativeXpath\r\n });\r\n } else if (count > 1 && isIndex) {\r\n const relativeXpathIndex = findXpathWithIndex(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n if (\r\n relativeXpathIndex &&\r\n getCountOfXPath(relativeXpathIndex, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by relative parent ${\r\n isIndex ? \"index\" : \"\"\r\n }`,\r\n value: relativeXpathIndex\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n if (!relativeChildXpath) {\r\n relativeChildXpath = getChildRelativeXpath(\r\n targetElemt,\r\n docmt,\r\n targetElemt\r\n );\r\n }\r\n\r\n if (\r\n relativeChildXpath &&\r\n (relativeChildXpath.includes(\"@\") ||\r\n relativeChildXpath.includes(\"text()\") ||\r\n relativeChildXpath.includes(\".=\"))\r\n ) {\r\n const fullRelativeXpath = `/${\r\n nodeXpath[i] + relativeChildXpath.substring(1)\r\n }`;\r\n const count = getCountOfXPath(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt\r\n );\r\n\r\n if (count === 1) {\r\n xpathData.push({\r\n key: \"relative xpath by relative child\",\r\n value: fullRelativeXpath\r\n });\r\n } else if (count > 1 && isIndex) {\r\n const relativeXpathIndex = findXpathWithIndex(\r\n fullRelativeXpath,\r\n targetElemt,\r\n docmt,\r\n count\r\n );\r\n if (\r\n relativeXpathIndex &&\r\n getCountOfXPath(relativeXpathIndex, targetElemt, docmt) === 1\r\n ) {\r\n xpathData.push({\r\n key: `relative xpath by relative parent ${\r\n isIndex ? \"index\" : \"\"\r\n }`,\r\n value: relativeXpathIndex\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (\r\n xpathData?.length === 1 &&\r\n xpathData?.[0]?.value?.match(/\\[([0-9]+)\\]/gm)?.length! > 3 &&\r\n !referenceElementMode\r\n ) {\r\n if (targetElemt.textContent) {\r\n const txtXpath = getTextXPath(targetElemt, docmt, isIndex, false);\r\n if (txtXpath) {\r\n xpathData.unshift({\r\n key: `xpath by text${isIndex ? \"index\" : \"\"}`,\r\n value: txtXpath\r\n });\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length) {\r\n let tempRelativeXpath = getUniqueParentXpath(\r\n targetElemt.parentElement!,\r\n docmt,\r\n targetElemt,\r\n isTarget,\r\n nodeXpath[i],\r\n isIndex\r\n );\r\n\r\n if (tempRelativeXpath) {\r\n xpathData.push({\r\n key: \"xpath by unique parent\",\r\n value: tempRelativeXpath\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return xpathData;\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nexport const attributesBasedXPath = (\r\n attr: Attr,\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n let attrName;\r\n\r\n attrName = attr.name;\r\n // Strict numeric-attribute validation for direct callers.\r\n if (!checkBlockedAttributes(attr, targetElemt, isTarget)) {\r\n return;\r\n }\r\n\r\n let xpath = getPropertyXPath(\r\n targetElemt,\r\n docmt,\r\n `@${attrName}`,\r\n attr.value,\r\n isIndex,\r\n isTarget\r\n );\r\n\r\n return xpath;\r\n};\r\n\r\nexport const getUniqueClassName = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n let value = element.className;\r\n if (typeof value !== \"string\") {\r\n value = \"\";\r\n }\r\n value = value?.replace(\"flndisabled\", \"disabled\");\r\n value = sanitizeAttributeValue(\"class\", value);\r\n value = value?.trim();\r\n\r\n if (value && checkBlockedAttributes({ name: \"class\", value }, element, isTarget)) {\r\n return getPropertyXPath(element, docmt, `@class`, value, isIndex, isTarget);\r\n }\r\n};\r\n\r\nexport const getTextXPath = (\r\n element: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n if ((element.textContent ?? \"\").trim() != \"\") {\r\n const text = getTextContent(element);\r\n\r\n if (text) {\r\n return getPropertyXPath(element, docmt, \".\", text, isIndex, isTarget);\r\n }\r\n }\r\n};\r\n\r\nconst addAllXPathAttributes = (\r\n attributes: Attr[],\r\n targetElemt: HTMLElement | Element,\r\n docmt: Document,\r\n isIndex: boolean,\r\n isTarget: boolean\r\n) => {\r\n const attributesArray = attributes;\r\n try {\r\n attributesArray.map((attr) => {\r\n if (!(attr.name === \"className\" || attr.name === \"class\")) {\r\n // Strict numeric-attribute validation is enforced inside\r\n // checkBlockedAttributes before candidate XPath creation.\r\n if (checkBlockedAttributes(attr, targetElemt, isTarget)) {\r\n const xpth = attributesBasedXPath(\r\n attr,\r\n targetElemt,\r\n docmt,\r\n isIndex,\r\n isTarget\r\n );\r\n if (xpth) {\r\n xpathData.push({\r\n key: `xpath by ${attr.name}${isIndex ? \" index\" : \"\"}`,\r\n value: xpth\r\n });\r\n }\r\n }\r\n }\r\n });\r\n\r\n const txtXpath = getTextXPath(targetElemt, docmt, isIndex, isTarget);\r\n if (txtXpath) {\r\n xpathData.push({\r\n key: `xpath by text${isIndex ? \" index\" : \"\"}`,\r\n value: txtXpath\r\n });\r\n }\r\n\r\n if (\r\n attributesArray.find((element) => element.name === \"className\") &&\r\n checkBlockedAttributes(\r\n attributesArray?.find((element) => element.name === \"className\")!,\r\n targetElemt,\r\n isTarget\r\n )\r\n ) {\r\n let xpath = getUniqueClassName(targetElemt, docmt, isIndex, isTarget);\r\n if (xpath) {\r\n xpathData.push({\r\n key: \"xpath by class\",\r\n value: xpath\r\n });\r\n }\r\n }\r\n\r\n if (!xpathData.length && attributesArray.length > 1) {\r\n const combinationXpath = getAttributeCombinationXpath(\r\n targetElemt,\r\n docmt,\r\n attributesArray,\r\n isTarget\r\n );\r\n if (combinationXpath)\r\n xpathData.push({\r\n key: \"xpath by combination\",\r\n value: combinationXpath\r\n });\r\n }\r\n\r\n if (!xpathData.length) {\r\n const textAttribute = getXPathUsingAttributeAndText(\r\n attributes,\r\n targetElemt,\r\n docmt,\r\n isTarget\r\n );\r\n if (textAttribute)\r\n xpathData.push({\r\n key: \"xpath by textAttribute\",\r\n value: textAttribute\r\n });\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n};\r\n\r\nexport const parseDOM = (\r\n element: HTMLElement | Element,\r\n doc: Document,\r\n isIndex: boolean,\r\n isTarget: boolean,\r\n includedAttributes: Attr[] = [],\r\n strategies: string[] = []\r\n) => {\r\n xpathData = [];\r\n console.log(element);\r\n const targetElemt = element;\r\n const rootNode = targetElemt?.getRootNode?.();\r\n const isShadowScoped = rootNode?.nodeType === Node.DOCUMENT_FRAGMENT_NODE;\r\n const docmt = (\r\n isShadowScoped ? rootNode : targetElemt?.ownerDocument || doc\r\n ) as Document;\r\n const tag = targetElemt.tagName;\r\n const { attributes } = targetElemt;\r\n const attributesToUse =\r\n includedAttributes.length > 0 ? includedAttributes : Array.from(attributes);\r\n addAllXPathAttributes(attributesToUse, targetElemt, docmt, isIndex, isTarget);\r\n\r\n if (strategies.length) {\r\n const strategyXpaths = strategies.flatMap((strategy) =>\r\n buildStrategyXpaths(targetElemt, docmt, isTarget, strategy, xpathData)\r\n );\r\n\r\n xpathData = getUniqueXpathEntries(\r\n strategyXpaths.filter(\r\n (xpath) => getCountOfXPath(xpath.value, targetElemt, docmt) === 1\r\n )\r\n );\r\n\r\n return removePositionalIndexXpaths(xpathData, isIndex);\r\n }\r\n\r\n if (!referenceElementMode) {\r\n if (xpathData.length) {\r\n const len = xpathData.length;\r\n for (let i = 0; i < len; i++) {\r\n let xpth = xpathData[i].value;\r\n xpth = \"//*\" + xpth.substring(xpth.indexOf(\"//\") + 2 + tag.length);\r\n const count = getCountOfXPath(xpth, element, docmt);\r\n if (count === 1) {\r\n xpathData.push({\r\n key: `${xpathData[i].key} regex`,\r\n value: xpth\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!xpathData.length && !isShadowScoped) {\r\n xpathData = buildAxesStrategyXpaths(targetElemt, docmt, isTarget, isIndex);\r\n }\r\n\r\n return removePositionalIndexXpaths(xpathData, isIndex);\r\n};\r\n\r\nconst xpath = {\r\n parseDOM,\r\n getTextXPath,\r\n getUniqueClassName,\r\n attributesBasedXPath,\r\n addAllXPathAttributes,\r\n addRelativeXpaths,\r\n getXPathUsingAttributeAndText,\r\n getSiblingRelativeXPath,\r\n getChildRelativeXpath,\r\n getParentRelativeXpath,\r\n getUniqueParentXpath,\r\n checkRelativeXpathRelation,\r\n buildAxesStrategyXpaths\r\n};\r\n\r\nexport default xpath;\r\n","import { SelectorMode } from \"../types/locator.ts\";\r\nimport { isNumberExist } from \"./xpathHelpers.ts\";\r\n\r\nlet modifiedElementAttributes: [] = [];\r\n\r\nconst escapeCssAttributeValue = (value: string): string => {\r\n return String(value).replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\r\n};\r\n\r\nconst isCssSelectorUnique = (\r\n selector: string,\r\n el: Element,\r\n root: Document | ShadowRoot | ParentNode\r\n): boolean => {\r\n try {\r\n const matches = root.querySelectorAll(selector);\r\n return matches.length === 1 && matches[0] === el;\r\n } catch {\r\n return false;\r\n }\r\n};\r\n\r\nexport const parseCssSelectors = (\r\n el: Element,\r\n mode: SelectorMode = \"single\"\r\n) => {\r\n const selectors: { key: string; value: string }[] = [];\r\n const root = el.getRootNode() as Document | ShadowRoot;\r\n\r\n try {\r\n const idPath = getIdCssPath(el);\r\n if (idPath && isCssSelectorUnique(idPath, el, root)) {\r\n selectors.push({ key: \"cssSelector by id\", value: idPath });\r\n if (mode === \"single\") return selectors;\r\n }\r\n const classPath = getClassCssPath(el);\r\n if (classPath && isCssSelectorUnique(classPath, el, root)) {\r\n selectors.push({ key: \"cssSelector by class\", value: classPath });\r\n }\r\n const namePath = getAttributeCssPath(el);\r\n if (namePath) {\r\n selectors.push({ key: \"cssSelector by name\", value: namePath });\r\n }\r\n // commented out absolute path strategy as it is not performing well and causing performance issues in large DOMs\r\n // const absPath = getAbsoluteCssPath(el);\r\n // if (absPath && isCssSelectorUnique(absPath, el, root)) {\r\n // selectors.push({ key: 'Absolute cssSelector', value: absPath });\r\n // }\r\n } catch (e) {\r\n console.error(e);\r\n }\r\n return selectors;\r\n};\r\n\r\nexport const getIdCssPath = (el: HTMLElement | Element) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n const tagName = el.tagName.toLowerCase();\r\n if (tagName.includes(\"style\") || tagName.includes(\"script\")) return;\r\n\r\n const path = [];\r\n while (el?.nodeType === Node.ELEMENT_NODE) {\r\n let selector = el.nodeName?.toLowerCase();\r\n if (el.id && !isNumberExist(el.id)) {\r\n selector += `#${CSS.escape(el.id)}`;\r\n path.unshift(selector);\r\n break;\r\n } else {\r\n let sib = el;\r\n let nth = 1;\r\n if (sib.previousElementSibling) {\r\n while ((sib = sib.previousElementSibling)) {\r\n if (sib.nodeName?.toLowerCase() === selector) nth++;\r\n }\r\n }\r\n\r\n if (nth !== 1) {\r\n selector += `:nth-of-type(${nth})`;\r\n }\r\n\r\n if (nth === 1 && sib?.parentElement?.childElementCount! > 1) {\r\n selector += `:nth-child(${nth})`;\r\n }\r\n }\r\n path.unshift(selector);\r\n el = el.parentElement!;\r\n }\r\n return path.join(\" > \");\r\n};\r\n\r\nconst EXCLUDED_ATTRS = new Set([\"id\", \"class\", \"style\"]);\r\nfunction getAttributeSelectors(el: Element): string[] {\r\n return Array.from(el.attributes)\r\n .filter(\r\n (attr) =>\r\n !EXCLUDED_ATTRS.has(attr?.name?.toLowerCase()) &&\r\n attr.value &&\r\n !isNumberExist(attr.value)\r\n )\r\n .map((attr) => `[${attr.name}=\"${escapeCssAttributeValue(attr.value)}\"]`);\r\n}\r\n\r\nexport const getAttributeCssPath = (el: Element): string | undefined => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n\r\n const root = el.getRootNode() as ParentNode;\r\n const tag = el.tagName.toLowerCase();\r\n\r\n if (tag === \"style\" || tag === \"script\") return;\r\n\r\n const attrSelectors = getAttributeSelectors(el);\r\n\r\n for (const attrSelector of attrSelectors) {\r\n const candidate = `${tag}${attrSelector}`;\r\n\r\n if (isCssSelectorUnique(candidate, el, root)) {\r\n return candidate;\r\n }\r\n }\r\n return;\r\n};\r\n\r\nexport const getClassCssPath = (el: HTMLElement | Element) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n const tagName = el.tagName.toLowerCase();\r\n if (tagName.includes(\"style\") || tagName.includes(\"script\")) return;\r\n\r\n const path = [];\r\n while (el?.nodeType === Node.ELEMENT_NODE) {\r\n let selector = el.nodeName?.toLowerCase();\r\n\r\n if (\r\n typeof el.className === \"string\" &&\r\n el.className &&\r\n !isNumberExist(el.className) &&\r\n !modifiedElementAttributes?.find(\r\n (x: { element: HTMLElement | Element; attributeName: string }) =>\r\n x.element === el && x.attributeName === \"class\"\r\n )\r\n ) {\r\n el.classList.remove(\"marked-element-temp\");\r\n el.classList.remove(\"removePointers\");\r\n if (el.className) {\r\n selector += `.${el.className.trim().replace(/\\s+/g, \".\")}`;\r\n path.unshift(selector);\r\n break;\r\n }\r\n } else {\r\n let sib = el;\r\n let nth = 1;\r\n if (sib.previousElementSibling) {\r\n while ((sib = sib.previousElementSibling)) {\r\n if (sib.nodeName?.toLowerCase() === selector) nth++;\r\n }\r\n }\r\n\r\n if (nth !== 1) {\r\n selector += `:nth-of-type(${nth})`;\r\n }\r\n\r\n if (nth === 1 && sib?.parentElement?.childElementCount! > 1) {\r\n selector += `:nth-child(${nth})`;\r\n }\r\n }\r\n path.unshift(selector);\r\n el = el.parentElement!;\r\n }\r\n return path.join(\" > \");\r\n};\r\n\r\nexport const getAbsoluteCssPath = (el: Element) => {\r\n const view = el.ownerDocument?.defaultView;\r\n if (!view || !(el instanceof view.Element)) return;\r\n\r\n const path: string[] = [];\r\n\r\n while (el && el.nodeType === Node.ELEMENT_NODE) {\r\n const tagName = el.tagName.toLowerCase();\r\n\r\n if (tagName === \"style\" || tagName === \"script\") return;\r\n\r\n let selector = tagName;\r\n\r\n const parent = el.parentNode;\r\n\r\n if (parent) {\r\n const siblings = Array.from(parent.children).filter(\r\n (c) => c.tagName === el.tagName\r\n );\r\n\r\n if (siblings.length > 1) {\r\n selector += `:nth-of-type(${siblings.indexOf(el) + 1})`;\r\n }\r\n }\r\n\r\n path.unshift(selector);\r\n\r\n el = el.parentElement!;\r\n }\r\n\r\n return path.join(\" > \");\r\n};\r\n\r\nexport const cssSelectors = {\r\n parseCssSelectors,\r\n getIdCssPath,\r\n getAttributeCssPath,\r\n getClassCssPath,\r\n getAbsoluteCssPath\r\n};\r\n","import { ElementRecord, Locator } from \"../types/locator.ts\";\r\nimport { parseDOM } from \"./xpath.ts\";\r\nimport { parseCssSelectors } from \"./cssSelector.ts\";\r\nimport {\r\n isNumberExist,\r\n normalizeXPath,\r\n getXPathPattern,\r\n escapeAttrValue,\r\n isUniqueInDOM,\r\n shouldUseSnapshot\r\n} from \"./xpathHelpers.ts\";\r\n\r\nconst isSameXPathStillValid = (\r\n xpath: string,\r\n docmt: Document,\r\n target: Element\r\n): boolean => {\r\n const normalized = normalizeXPath(xpath);\r\n const found = getElementFromXPath(docmt, normalized);\r\n if (found === target) return true;\r\n\r\n if (shouldUseSnapshot(normalized)) {\r\n const result = docmt.evaluate(\r\n normalized,\r\n docmt,\r\n null,\r\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\r\n null\r\n );\r\n\r\n for (let i = 0; i < result.snapshotLength; i++) {\r\n if (result.snapshotItem(i) === target) return true;\r\n }\r\n }\r\n\r\n return false;\r\n};\r\n\r\nconst getElementFromCssSelector = (\r\n docmt: Document,\r\n selector: string\r\n): Element | null => {\r\n try {\r\n const found = docmt.querySelector(selector);\r\n if (found) return found;\r\n } catch (error) {\r\n console.error(\"Invalid CSS selector:\", selector, error);\r\n return null;\r\n }\r\n\r\n return getElementFromShadowRoot(docmt.body, selector);\r\n};\r\n\r\nconst isSameCssSelectorStillValid = (\r\n selector: string,\r\n docmt: Document,\r\n target: Element\r\n): boolean => {\r\n return getElementFromCssSelector(docmt, selector) === target;\r\n};\r\n\r\nconst resolveIsSelfHealed = (\r\n locName: string,\r\n oldValue: string | null | undefined,\r\n newValue: string | null | undefined\r\n): \"Y\" | null => {\r\n if (!oldValue || !newValue) return \"Y\";\r\n return oldValue === newValue ? null : \"Y\";\r\n};\r\n\r\nconst isCssSelectorLocator = (locator: { name?: string | null }): boolean => {\r\n return locator.name?.toLowerCase().includes(\"cssselector\") ?? false;\r\n};\r\n\r\ntype ElementDetails = {\r\n id?: string | null;\r\n className?: string | null;\r\n xpathByText?: string | null;\r\n xpathById?: string | null;\r\n xpathByClass?: string | null;\r\n xpathAbsolute?: string | null;\r\n xpathByName?: string | null;\r\n xpathByPlaceholder?: string | null;\r\n xpathByType?: string | null;\r\n visibleText?: string | null;\r\n relativeXpath?: string | null;\r\n [key: string]: any;\r\n};\r\n\r\nconst getElementFromShadowRoot = (\r\n el: Element | ShadowRoot,\r\n selector: string\r\n): Element | null => {\r\n // const shadowRoot = (element as HTMLElement).shadowRoot;\r\n // if (shadowRoot && !selector.includes(\"dynamic\")) {\r\n // return shadowRoot.querySelector(selector);\r\n // }\r\n\r\n const elements = Array.from(el.querySelectorAll(\"*\"));\r\n\r\n try {\r\n for (let i = 0; i < elements.length; i++) {\r\n if (elements[i].shadowRoot) {\r\n const { shadowRoot } = elements[i];\r\n if (shadowRoot) {\r\n const nestedElement = getElementFromShadowRoot(shadowRoot, selector);\r\n if (nestedElement) {\r\n return nestedElement;\r\n }\r\n if (shadowRoot && !selector.includes(\"dynamic\")) {\r\n return shadowRoot.querySelector(selector);\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.log(error);\r\n }\r\n return null;\r\n};\r\n\r\nconst getId = (element: Element | null): string | null => {\r\n return element?.id || null;\r\n};\r\n\r\nconst getClassName = (element: Element): string | null => {\r\n return (element as HTMLElement).className || null;\r\n};\r\n\r\nconst getVisibleText = (element: Element): string | null => {\r\n return element.textContent?.trim() || null;\r\n};\r\n\r\nconst getName = (element: Element): string | null => {\r\n const elementEl = element as HTMLElement;\r\n\r\n if (elementEl.hasAttribute(\"name\")) {\r\n const attrValue = elementEl.getAttribute(\"name\");\r\n const name = `${attrValue}`;\r\n return name || null;\r\n }\r\n return null;\r\n};\r\n\r\nconst relations: string[] = [\r\n \"/preceding-sibling\",\r\n \"/following-sibling\",\r\n \"/parent\",\r\n \"/descendant\",\r\n \"/ancestor\",\r\n \"/self\",\r\n \"/ancestor-or-self\",\r\n \"/child\",\r\n \"/preceding\",\r\n \"/following\"\r\n];\r\n\r\nfunction getElementFromXPath(docmt: Document, xpath: string): Element | null {\r\n const window = docmt.defaultView;\r\n if (!window) return null;\r\n\r\n const xpathEvaluator = new window.XPathEvaluator();\r\n const xpathResult = xpathEvaluator.evaluate(\r\n xpath,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n return xpathResult.singleNodeValue as Element | null;\r\n}\r\n\r\nfunction checkReferenceElementIsValid(\r\n locator: string,\r\n relation: string,\r\n docmt: Document\r\n): string | null {\r\n if (locator.includes(relation)) {\r\n const locatotSplitArray: string[] = locator.split(relation);\r\n const sourceLoc = locatotSplitArray[0].trim();\r\n const window = docmt.defaultView;\r\n if (!window) return null;\r\n if (!locator.includes(\"dynamic\")) {\r\n const xpathEvaluator = new window.XPathEvaluator();\r\n const xpathResult = xpathEvaluator.evaluate(\r\n sourceLoc,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n\r\n const sourceElement = xpathResult.singleNodeValue;\r\n if (sourceElement) {\r\n const xpathResultComplete = xpathEvaluator.evaluate(\r\n locator,\r\n docmt,\r\n null,\r\n window.XPathResult.FIRST_ORDERED_NODE_TYPE,\r\n null\r\n );\r\n const completeElement = xpathResultComplete.singleNodeValue;\r\n let relativeXpath: string;\r\n if (completeElement) {\r\n relativeXpath = locator;\r\n return relativeXpath;\r\n } else {\r\n console.error(\"Complete Locator is Invalid:\", locator);\r\n relativeXpath = locator;\r\n return relativeXpath;\r\n }\r\n } else {\r\n console.error(\"Source Locator Not Found:\", sourceLoc);\r\n }\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nconst getElementsFromHTML = (\r\n record: ElementRecord,\r\n docmt: Document\r\n): ElementDetails | null => {\r\n const elementsToRemove = docmt.querySelectorAll(\r\n \"script, style, link[rel='stylesheet'], meta, noscript, embed, object, param, source, svg\"\r\n );\r\n\r\n if (elementsToRemove) {\r\n elementsToRemove.forEach((tag) => {\r\n (tag as Element).remove();\r\n });\r\n }\r\n\r\n const finalLocatorsSet: Set<string> = new Set();\r\n let finalLocators: any[] = [];\r\n\r\n function createLocator(base: any, overrides: Partial<any> = {}) {\r\n const oldValue = base?.value;\r\n const newValue = overrides.value ?? base?.value;\r\n const newLocator: any = {\r\n name: overrides.name ?? base?.name,\r\n type: overrides.type ?? base?.type,\r\n value: overrides.value ?? base?.value,\r\n reference: overrides.reference ?? base?.reference,\r\n status: overrides.status ?? base?.status,\r\n isRecorded: overrides.isRecorded ?? base?.isRecorded\r\n };\r\n\r\n const previousSelfHealed = base?.isSelfHealed === \"Y\";\r\n\r\n newLocator.isSelfHealed = previousSelfHealed\r\n ? \"Y\"\r\n : overrides.hasOwnProperty(\"isSelfHealed\")\r\n ? overrides.isSelfHealed\r\n : resolveIsSelfHealed(newLocator.name, oldValue, newValue);\r\n\r\n pushUniqueLocator(newLocator);\r\n }\r\n\r\n function resolveElement(\r\n ctx: Document,\r\n locator: any,\r\n selector: string\r\n ): Element | null {\r\n if (isCssSelectorLocator(locator)) {\r\n return getElementFromCssSelector(ctx, selector);\r\n } else if (locator.name.includes(\"id\") || selector.startsWith(\"#\")) {\r\n return ctx.querySelector(\"#\" + escapeAttrValue(selector));\r\n } else if (locator.name.includes(\"className\") || selector.startsWith(\".\")) {\r\n return ctx.querySelector(\".\" + selector);\r\n } else if (locator.name === \"name\") {\r\n const safeName = escapeAttrValue(selector);\r\n return ctx.querySelector(`[name=\"${safeName}\"]`);\r\n } else if (locator.name === \"tagName\") {\r\n return ctx.querySelector(selector);\r\n } else if (locator.name === \"linkText\") {\r\n return (\r\n Array.from(ctx.querySelectorAll(\"a\")).find(\r\n (a) => a.textContent?.trim() === selector\r\n ) || null\r\n );\r\n } else if (locator.name === \"partialLinkText\") {\r\n return (\r\n Array.from(ctx.querySelectorAll(\"a\")).find((a) =>\r\n a.textContent?.includes(selector)\r\n ) || null\r\n );\r\n } else if (\r\n (locator.name.includes(\"xpath\") || selector.startsWith(\"//\")) &&\r\n !locator.type.match(\"dynamic\")\r\n ) {\r\n const normalizedXPath = normalizeXPath(selector);\r\n const el = getElementFromXPath(ctx, normalizedXPath);\r\n\r\n if (el) {\r\n createLocator(locator, {\r\n value: selector,\r\n isRecorded: String(locator.isRecorded).includes(\"N\") ? \"N\" : \"Y\"\r\n });\r\n }\r\n\r\n return el;\r\n } else {\r\n return ctx.querySelector(selector);\r\n }\r\n }\r\n\r\n function findInIframes(\r\n docmt: Document,\r\n locator: any,\r\n selector: string\r\n ): Element | null {\r\n const iframes = docmt.querySelectorAll(\"iframe\");\r\n\r\n for (const iframe of iframes) {\r\n try {\r\n const iframeDoc =\r\n iframe.contentDocument || iframe.contentWindow?.document;\r\n\r\n if (!iframeDoc) continue;\r\n\r\n const el = resolveElement(iframeDoc, locator, selector);\r\n if (el) return el;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n function pushUniqueLocator(obj: any) {\r\n const key = `${obj.name}:${obj.value}`;\r\n if (!finalLocatorsSet.has(key)) {\r\n finalLocatorsSet.add(key);\r\n finalLocators.push(obj);\r\n }\r\n }\r\n\r\n /** Locator Value Cleaner (Handles Special Scenarios) **/\r\n const cleanLocatorValue = (\r\n val: string | null | undefined,\r\n type?: string,\r\n isRecorded?: string\r\n ): string | null => {\r\n if (!val) return null;\r\n\r\n let cleaned = val.trim();\r\n\r\n // Return null for empty or literal \"null\"\r\n if (!cleaned || cleaned.toLowerCase() === \"null\") return null;\r\n\r\n // Unescape any escaped quotes\r\n cleaned = cleaned.replace(/\\\\\"/g, '\"').replace(/\\\\'/g, \"'\");\r\n\r\n // Remove surrounding single or double quotes\r\n cleaned = cleaned.replace(/^['\"](.+?)['\"]$/, \"$1\");\r\n\r\n // Replace double single quotes with a single quote inside XPath\r\n cleaned = cleaned.replace(/''/g, \"'\");\r\n\r\n // Normalize double quotes in XPath attribute selectors [@id=\"\" -> [@id='']\r\n cleaned = cleaned.replace(\r\n /\\[@(id|name)=['\"]{2}(.+?)['\"]{2}\\]/g,\r\n \"[@$1='$2']\"\r\n );\r\n\r\n // For DOM selectors (id or name), remove ALL quotes\r\n if (type === \"id\" || type === \"name\") {\r\n cleaned = cleaned.replace(/['\"]/g, \"\").trim();\r\n }\r\n\r\n if (type === \"xpath\" && isRecorded === \"Y\" && !val.startsWith(\"//\"))\r\n return null;\r\n\r\n // Final check for empty strings\r\n if (!cleaned || /^['\"]{2}$/.test(cleaned)) return null;\r\n\r\n return cleaned;\r\n };\r\n\r\n locators: for (const locator of record.locators) {\r\n try {\r\n const isRecorded = String(locator.isRecorded || \"\");\r\n const recordedNLocators = record.locators.filter(\r\n (l) => l.isRecorded === \"N\"\r\n );\r\n\r\n if (recordedNLocators.length > 0) {\r\n for (const locator of recordedNLocators) {\r\n createLocator(locator);\r\n }\r\n }\r\n\r\n const isDynamic = String(locator.value || locator.type || \"\");\r\n if (\r\n isDynamic.includes(\"dynamic\") ||\r\n isDynamic.match(\"dynamic\") ||\r\n isDynamic.includes(\"{\") ||\r\n isDynamic.includes(\"}\")\r\n ) {\r\n createLocator(locator);\r\n continue;\r\n }\r\n\r\n if (record.isShared.includes(\"Y\")) {\r\n break locators;\r\n }\r\n\r\n try {\r\n let targetElement: Element | null = null;\r\n const selectors = locator.value.split(\">>>\");\r\n\r\n for (const selector of selectors) {\r\n if (!docmt) {\r\n console.error(\"Element not found at:\", selector);\r\n break;\r\n }\r\n\r\n const trimmedSelector = selector.trim();\r\n\r\n //normal DOM\r\n targetElement = resolveElement(docmt, locator, trimmedSelector);\r\n\r\n //iframe (if not found)\r\n if (!targetElement) {\r\n targetElement = findInIframes(docmt, locator, trimmedSelector);\r\n }\r\n\r\n //shadow DOM (if still not found)\r\n if (!targetElement) {\r\n targetElement = getElementFromShadowRoot(\r\n docmt.body,\r\n trimmedSelector\r\n );\r\n }\r\n\r\n if (!targetElement) {\r\n console.error(\"Element not found at:\", trimmedSelector);\r\n break;\r\n }\r\n }\r\n\r\n const locatorExists = (name: string, value: string): boolean => {\r\n const key = `${name}:${value}`;\r\n return finalLocatorsSet.has(key);\r\n };\r\n\r\n if (targetElement) {\r\n const payloadXPaths = record.locators.filter(\r\n (l) => l.name === \"xpath\" && l.value\r\n );\r\n const payloadCssSelectors = record.locators.filter(\r\n (l) => isCssSelectorLocator(l) && l.value\r\n );\r\n\r\n for (const px of payloadXPaths) {\r\n if (isSameXPathStillValid(px.value, docmt, targetElement))\r\n createLocator(px, {\r\n isSelfHealed: null\r\n });\r\n }\r\n\r\n for (const cssLocator of payloadCssSelectors) {\r\n if (\r\n isSameCssSelectorStillValid(\r\n cssLocator.value,\r\n docmt,\r\n targetElement\r\n )\r\n )\r\n createLocator(cssLocator, {\r\n isSelfHealed: null\r\n });\r\n }\r\n\r\n const existingXPaths = finalLocators.filter(\r\n (l) => l.name === \"xpath\" && l.value\r\n );\r\n\r\n // Track XPath patterns already used\r\n const usedXPathPatterns = new Set<string>(\r\n existingXPaths.map((x) => getXPathPattern(x.value))\r\n );\r\n\r\n const excludedAttributes: string[] = [];\r\n const idValue = getId(targetElement);\r\n if (\r\n idValue &&\r\n !locatorExists(\"id\", idValue) &&\r\n !isNumberExist(idValue)\r\n ) {\r\n const prevId = record.locators.find((l) => l.name === \"id\");\r\n if (isUniqueInDOM(docmt, \"id\", idValue, targetElement)) {\r\n excludedAttributes.push(\"id\");\r\n createLocator(prevId, {\r\n name: \"id\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: idValue\r\n });\r\n }\r\n }\r\n\r\n const tagName = targetElement.tagName;\r\n if (tagName && !locatorExists(\"tagName\", tagName)) {\r\n const prevTag = record.locators.find((l) => l.name === \"tagName\");\r\n if (isUniqueInDOM(docmt, \"tagName\", tagName, targetElement)) {\r\n excludedAttributes.push(\"tagName\");\r\n createLocator(prevTag, {\r\n name: \"tagName\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: tagName\r\n });\r\n }\r\n }\r\n\r\n const textValue = getVisibleText(targetElement);\r\n if (textValue && !isNumberExist(textValue)) {\r\n const prevLinkText = record.locators.find(\r\n (l) => l.name === \"linkText\"\r\n );\r\n if (isUniqueInDOM(docmt, \"linkText\", textValue, targetElement)) {\r\n excludedAttributes.push(\"linkText\");\r\n createLocator(prevLinkText, {\r\n name: \"linkText\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: textValue\r\n });\r\n }\r\n }\r\n\r\n const nameLocator = getName(targetElement);\r\n if (\r\n nameLocator &&\r\n !locatorExists(\"name\", nameLocator) &&\r\n !isNumberExist(nameLocator)\r\n ) {\r\n const prevName = record.locators.find((l) => l.name === \"name\");\r\n if (isUniqueInDOM(docmt, \"name\", nameLocator, targetElement)) {\r\n excludedAttributes.push(\"name\");\r\n createLocator(prevName, {\r\n name: \"name\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: nameLocator\r\n });\r\n }\r\n }\r\n\r\n const classValue = getClassName(targetElement);\r\n if (\r\n classValue &&\r\n classValue.trim() !== \"\" &&\r\n !classValue.includes(\" \") &&\r\n !locatorExists(\"className\", classValue) &&\r\n !isNumberExist(classValue)\r\n ) {\r\n const prevClassLocator = record.locators.find(\r\n (l) => l.name === \"className\"\r\n );\r\n if (isUniqueInDOM(docmt, \"className\", classValue, targetElement)) {\r\n excludedAttributes.push(\"className\");\r\n createLocator(prevClassLocator, {\r\n name: \"className\",\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n value: classValue\r\n });\r\n }\r\n }\r\n parseCssSelectors(targetElement, \"single\").forEach(\r\n (cssSelector) => {\r\n if (\r\n cssSelector.value &&\r\n !locatorExists(\"cssSelector\", cssSelector.value)\r\n ) {\r\n createLocator(undefined, {\r\n name: \"cssSelector\",\r\n value: cssSelector.value,\r\n type: \"static\",\r\n isRecorded: \"Y\"\r\n });\r\n }\r\n }\r\n );\r\n const allAttributes = Array.from(targetElement.attributes);\r\n const includedAttributes = allAttributes.filter(\r\n (attr) => !excludedAttributes.includes(attr.name)\r\n );\r\n\r\n //If any direct locator is broken then we consider it as broken xpath\r\n\r\n let xpathResults: any[] = [];\r\n try {\r\n xpathResults =\r\n parseDOM(targetElement, docmt, false, true, includedAttributes) ??\r\n [];\r\n } catch (error) {\r\n console.error(\"Error generating XPath candidates:\", error);\r\n }\r\n\r\n if (xpathResults?.length !== 0) {\r\n const brokenPayloadXPaths = payloadXPaths.filter(\r\n (px) => !isSameXPathStillValid(px.value, docmt, targetElement)\r\n );\r\n\r\n let xpathAdded = 0;\r\n\r\n for (const brokenPx of brokenPayloadXPaths) {\r\n if (xpathAdded >= brokenPayloadXPaths.length) break;\r\n\r\n const originalPattern = getXPathPattern(brokenPx.value);\r\n if (usedXPathPatterns.has(originalPattern)) continue;\r\n\r\n const match = xpathResults.find(\r\n (r) => r.value && getXPathPattern(r.value) === originalPattern\r\n );\r\n\r\n if (match?.value) {\r\n createLocator(brokenPx, {\r\n name: \"xpath\",\r\n value: match.value,\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n\r\n usedXPathPatterns.add(originalPattern);\r\n xpathAdded++;\r\n }\r\n }\r\n if (xpathAdded < brokenPayloadXPaths.length) {\r\n for (const result of xpathResults) {\r\n if (xpathAdded >= brokenPayloadXPaths.length) break;\r\n if (!result.value) continue;\r\n\r\n const pattern = getXPathPattern(result.value);\r\n if (usedXPathPatterns.has(pattern)) continue;\r\n\r\n createLocator(result, {\r\n name: \"xpath\",\r\n value: result.value,\r\n type: \"static\",\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n\r\n usedXPathPatterns.add(pattern);\r\n xpathAdded++;\r\n }\r\n }\r\n }\r\n for (const locator of record.locators) {\r\n try {\r\n for (const loc of record.locators) {\r\n if (!loc.value) continue;\r\n\r\n for (const relation of relations) {\r\n if (loc.value.includes(relation)) {\r\n const relativeXpath = checkReferenceElementIsValid(\r\n loc.value,\r\n relation,\r\n docmt\r\n );\r\n if (relativeXpath) {\r\n createLocator(loc, {\r\n name: \"xpath\",\r\n value: relativeXpath,\r\n isRecorded:\r\n locator.isRecorded !== \"\" &&\r\n locator.isRecorded !== null\r\n ? locator.isRecorded\r\n : \"Y\"\r\n });\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n }\r\n }\r\n if (finalLocators.length < 5) {\r\n const fallbackCandidates = [\r\n { name: \"id\", value: getId(targetElement) },\r\n { name: \"name\", value: getName(targetElement) },\r\n { name: \"className\", value: getClassName(targetElement) },\r\n { name: \"tagName\", value: targetElement.tagName },\r\n { name: \"linkText\", value: getVisibleText(targetElement) }\r\n ];\r\n\r\n for (const candidate of fallbackCandidates) {\r\n if (finalLocators.length > 4) break;\r\n\r\n const { name, value } = candidate;\r\n if (!value) continue;\r\n if (isNumberExist(value)) continue;\r\n if (locatorExists(name, value)) continue;\r\n if (name === \"className\" && value.includes(\" \")) continue;\r\n if (isUniqueInDOM(docmt, name, value, targetElement)) {\r\n createLocator(undefined, {\r\n name,\r\n type: \"static\",\r\n value,\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n }\r\n }\r\n\r\n if (finalLocators.length < 5) {\r\n parseCssSelectors(targetElement, \"multiple\").forEach(\r\n (cssSelector) => {\r\n if (finalLocators.length > 4) return;\r\n if (!cssSelector.value) return;\r\n if (locatorExists(\"cssSelector\", cssSelector.value)) return;\r\n if (\r\n !isUniqueInDOM(\r\n docmt,\r\n \"cssSelector\",\r\n cssSelector.value,\r\n targetElement\r\n )\r\n ) {\r\n return;\r\n }\r\n\r\n createLocator(undefined, {\r\n name: \"cssSelector\",\r\n type: \"static\",\r\n value: cssSelector.value,\r\n isRecorded: \"Y\",\r\n isSelfHealed: \"Y\"\r\n });\r\n }\r\n );\r\n }\r\n }\r\n\r\n const finalAutoHealedLocators = finalLocators.map((obj) => ({\r\n ...obj,\r\n value: cleanLocatorValue(obj.value, obj.name, obj.isRecorded)\r\n }));\r\n\r\n const jsonResult = [\r\n {\r\n name: `${record.name}`,\r\n desc: `${record.desc}`,\r\n type: `${record.type}`,\r\n locators: finalAutoHealedLocators.filter(\r\n (locator) => locator?.value != null && locator.value !== \"\"\r\n ),\r\n isShared: `${record.isShared}`,\r\n projectId: `${record.projectId}`,\r\n projectType: `${record.projectType}`,\r\n isRecorded: `${record.isRecorded}`,\r\n folder: `${record.folder}`,\r\n parentId: `${record.parentId}`,\r\n parentName: `${record.parentName}`,\r\n platform: `${record.platform}`,\r\n licenseId: `${record.licenseId}`,\r\n licenseType: `${record.licenseType}`,\r\n userId: `${record.userId}`\r\n }\r\n ];\r\n\r\n return jsonResult;\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n continue;\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing locator:\", locator, error);\r\n continue;\r\n }\r\n }\r\n return null;\r\n};\r\n\r\nexport { getElementsFromHTML };\r\n","import { JSDOM, VirtualConsole } from \"jsdom\";\r\nimport { ElementRecord } from \"../types/locator\";\r\nimport { getElementsFromHTML } from \"../utils/getElementsFromHTML\";\r\n\r\nasync function createXPathAPI() {\r\n const fromHTML = async (html: string) => {\r\n const virtualConsole = new VirtualConsole();\r\n\r\n const dom = new JSDOM(\r\n html\r\n .replace(/\\\\\"/g, '\"')\r\n .replace(/\\\\r/g, \"\\r\")\r\n .replace(/\\\\n/g, \"\\n\")\r\n .replace(/\\\\t/g, \"\\t\"),\r\n {\r\n pretendToBeVisual: true,\r\n runScripts: \"dangerously\",\r\n resources: \"usable\",\r\n virtualConsole\r\n }\r\n );\r\n\r\n const { window } = dom;\r\n\r\n // ✅ Browser globals parity (REQUIRED)\r\n // @ts-ignore\r\n global.document = window.document;\r\n // @ts-ignore\r\n global.Node = window.Node;\r\n // @ts-ignore\r\n global.Element = window.Element;\r\n // @ts-ignore\r\n global.HTMLElement = window.HTMLElement;\r\n // @ts-ignore\r\n global.SVGElement = window.SVGElement;\r\n\r\n const cssApi = window.CSS ?? {};\r\n if (!cssApi.escape) {\r\n cssApi.escape = (value: string) =>\r\n String(value).replace(/[^a-zA-Z0-9_-]/g, \"\\\\$&\");\r\n }\r\n // @ts-ignore\r\n global.CSS = cssApi;\r\n\r\n // @ts-ignore\r\n global.XPathResult = window.XPathResult;\r\n // @ts-ignore\r\n global.XPathEvaluator = window.XPathEvaluator;\r\n\r\n return {\r\n autoHeal: async (data: ElementRecord) => {\r\n return getElementsFromHTML(data, window.document);\r\n }\r\n };\r\n };\r\n\r\n return { fromHTML };\r\n}\r\n\r\nexport default createXPathAPI;\r\n"],"names":["reWhiteSpace","xpathEvalCache","WeakMap","evaluateXPathOnce","xpath","docmt","owner","contextNode","nodeType","ownerDocument","document","getXPathContext","nodeCache","get","Map","set","cached","result","evaluate","XPathResult","ORDERED_NODE_ITERATOR_TYPE","evalResult","first","iterateNext","second","modifiedElementAttributes","INTERNAL_CLASS_TOKENS","Set","normalizeClassTokens","classValue","split","map","token","trim","filter","has","sort","hasNumericAttributeValue","value","test","getClassTokenConditions","element","allowNumericToken","allClassTokens","classTokens","length","escapeCharacters","buildPattern","isSvg","tagName","toLowerCase","count","getTagOnlyXpathCandidateCount","left","right","TEST_ID_ATTRIBUTE_NAMES","sanitizeAttributeValue","attributeName","attributeValue","join","replace","isNumberExist","str","pattern","getTextContent","targetElement","textContent","getCountOfXPath","multiElementReferenceMode","Array","isArray","matchCount","includes","node","error","console","text","indexOf","replaceWhiteSpaces","getContainerTextCondition","normalizedText","children","fragment","slice","getStableTargetTextCandidates","prefix","part","getStableTargetText","createShadowEvaluationContext","root","tempDoc","implementation","createHTMLDocument","wrapper","createElement","innerHTML","body","appendChild","cloneForElement","candidate","defaultView","Element","path","current","parentElement","parent","unshift","from","parentNode","getElementPathFromRoot","container","index","next","item","getElementAtPath","cloneElements","Boolean","cloneElement","checkBlockedAttributes","attribute","isTarget","sanitizedValue","name","some","x","isModified","find","doc","hasGeneratedNumericSegment","SVGElement","getPropertyXPath","prop","isIndex","combinePattern","mergePattern","isAttributeProp","startsWith","isTextProp","normalizedTextProp","hasTextSpacing","stableTargetText","textValueForPredicate","containerTextCondition","classCondition","splitText","contentRes","match","endsWith","endIndex","startIndexString","endIndexString","i","push","getAttributeCombinationXpath","domNode","uniqueAttributes","candidateAttributes","attr","buildAttributeConditions","attrValue","nodeValue","getTextConditions","rawText","conditions","add","generateAttributeCombinations","attributes","combinationSize","results","build","startIndex","currentGroup","pop","buildXPath","attributeOnlyXpath","attributeGroups","attributeGroup","xpathConditions","attributeConditions","tryAttributeOnlyCombinations","textConditions","textCondition","log","JSON","stringify","getFilteredTextXPath","filteredText","childNodes","textForPredicate","xpathe","getNormalizedPropertyXPath","getStartsWithPropertyXPath","stableText","getContainsPropertyXPath","getOrAttributesXPath","flatMap","getRootNode","buildTextCondition","_error","getAncestorAnchorCandidates","anchors","seen","priorityAttrs","orderedAttributes","attrName","pushAnchor","isExactUniqueXpath","forEach","getXpathStrings","textXpath","combinationXpath","getStructuralPathFromAncestor","ancestor","steps","getAxisNodeTest","getTagOnlyXPath","fallbackXpath","ancestorAnchors","ancestorXpath","descendantXpath","getFirstMatchedNode","structuralPath","parentChainXpath","parentChainCount","fallbackCount","Node","DOCUMENT_FRAGMENT_NODE","FIRST_ORDERED_NODE_TYPE","singleNodeValue","getUniqueNodeAnchorXpaths","key","a","tagXpath","RegExp","condition","normalizeXPath","getXPathPattern","canonical","canonicalizeXPath","parts","xp","axisMatch","axis","attrMatch","usesNormalize","extractXPathSignatureParts","escapeAttrValue","isUniqueInDOM","queryAll","selector","querySelectorAll","xpathData","STRATEGY_MAP","andConditions","orConditions","contains","normalizeSpace","axes","hardCodedText","tillTag","withStrategyKey","strategy","entries","entry","removePositionalIndexXpaths","getNormalizedText","collectSubtreeElements","nodes","queue","currentNode","shift","classList","getSiblingNodes","direction","previousElementSibling","nextElementSibling","getDirectionalAxisSeedNodes","sibling","expand","isLowQualityNode","id","className","buildAxisXpathsForNodes","targetNodeTest","candidates","processed","MAX_PROCESS","nodeItem","anchorStages","containsXpath","startsWithXpath","stageIndex","anchor","Math","min","expanded","childCount","child","buildAxesStrategyXpaths","ancestors","descendants","previousSiblings","nextSiblings","precedingSeeds","followingSeeds","limitNodes","limit","tiers","tier","buildStrategyXpaths","fallbackXpaths","strategyName","getStrategyName","getTextXPath","buildTextStrategyXpaths","xpaths","getStrategyXPath","buildPropertyStrategyXpaths","buildConditionStrategyXpaths","tag","idx","scopeXpaths","toString","scopeXpath","scopedIdx","generateIndexedXpaths","addAllXPathAttributes","targetElemt","attributesArray","xpth","attributesBasedXPath","txtXpath","getUniqueClassName","textAttribute","normalizedTextContent","getXPathUsingAttributeAndText","parseDOM","includedAttributes","strategies","rootNode","isShadowScoped","attributesToUse","strategyXpaths","getUniqueXpathEntries","len","substring","isCssSelectorUnique","el","matches","parseCssSelectors","mode","selectors","idPath","getIdCssPath","classPath","getClassCssPath","namePath","getAttributeCssPath","e","view","ELEMENT_NODE","nodeName","CSS","escape","sib","nth","childElementCount","EXCLUDED_ATTRS","getAttributeSelectors","String","attrSelectors","attrSelector","remove","isSameXPathStillValid","target","normalized","getElementFromXPath","shouldUseSnapshot","ORDERED_NODE_SNAPSHOT_TYPE","snapshotLength","snapshotItem","getElementFromCssSelector","found","querySelector","getElementFromShadowRoot","isSameCssSelectorStillValid","isCssSelectorLocator","locator","elements","shadowRoot","nestedElement","getId","getClassName","getVisibleText","getName","elementEl","hasAttribute","getAttribute","relations","window","XPathEvaluator","checkReferenceElementIsValid","relation","sourceLoc","xpathEvaluator","relativeXpath","getElementsFromHTML","record","elementsToRemove","finalLocatorsSet","finalLocators","createLocator","base","overrides","oldValue","newValue","newLocator","type","reference","status","isRecorded","previousSelfHealed","isSelfHealed","hasOwnProperty","locName","resolveIsSelfHealed","obj","pushUniqueLocator","resolveElement","ctx","safeName","findInIframes","iframes","iframe","iframeDoc","contentDocument","contentWindow","cleanLocatorValue","val","cleaned","locators","recordedNLocators","l","isDynamic","isShared","trimmedSelector","locatorExists","payloadXPaths","payloadCssSelectors","px","cssLocator","existingXPaths","usedXPathPatterns","excludedAttributes","idValue","prevId","prevTag","textValue","prevLinkText","nameLocator","prevName","prevClassLocator","cssSelector","undefined","xpathResults","brokenPayloadXPaths","xpathAdded","brokenPx","originalPattern","r","loc","fallbackCandidates","finalAutoHealedLocators","jsonResult","desc","projectId","projectType","folder","parentId","parentName","platform","licenseId","licenseType","userId","async","createXPathAPI","fromHTML","html","virtualConsole","VirtualConsole","dom","JSDOM","pretendToBeVisual","runScripts","resources","global","HTMLElement","cssApi","autoHeal","data"],"mappings":"kDAAO,MAAMA,EAAe,oBAe5B,IAAIC,EAAiB,IAAIC,QAEzB,MAiDaC,EAAoB,CAACC,EAAeC,KAC/C,MAAMC,MAAEA,EAAKC,YAAEA,GAlDO,CAACF,IACvB,MAAMC,EACe,IAAnBD,EAAMG,SACDH,EACDA,EAAMI,eAAiBC,SAQ7B,MAAO,CAAEJ,QAAOC,YAJK,IAAnBF,EAAMG,UAAqC,IAAnBH,EAAMG,UAAqC,KAAnBH,EAAMG,SAClDH,EACAC,IAwCyBK,CAAgBN,GAG/C,IAAIO,EAAYX,EAAeY,IAAIN,GAC9BK,IACHA,EAAY,IAAIE,IAChBb,EAAec,IAAIR,EAAaK,IAIlC,MAAMI,EAASJ,EAAUC,IAAIT,GAC7B,GAAIY,EAAQ,OAAOA,EAGnB,MAAMC,EAASX,EAAMY,SACnBd,EACAG,EACA,KACAY,YAAYC,2BACZ,MAMIC,EAAyB,CAAEC,MAHnBL,EAAOM,cAGmBC,OAFzBP,EAAOM,eAKtB,OAFAX,EAAUG,IAAIX,EAAOiB,GAEdA,GAGF,IAAII,EAKL,GAEN,MAAMC,EAAwB,IAAIC,IAAI,CACpC,sBACA,mBAGIC,EACJC,IAECA,GAAc,IACZC,MAAM,OACNC,IAAKC,GAAUA,EAAMC,QACrBC,OAAQF,GAAUA,IAAUN,EAAsBS,IAAIH,IACtDI,OAEQC,EAA4BC,GACvC,KAAKC,KAAKD,GAAS,IAERE,EAA0B,CACrCC,EACAZ,EACAxB,EACAqC,GAAoB,KAIpB,MAAMC,EAAiBf,EAAqBC,GACtCe,EAAcD,EAAeT,OAAQF,KACzCU,IAA4BL,EAAyBL,IAGvD,OAAKY,EAAYC,OAEa,IAA1BF,EAAeE,QAAuC,IAAvBD,EAAYC,OAGtC,CAAC,UAAUC,EAAiBF,EAAY,OAG1CA,EACJb,IAAKC,IACJ,MAAM5B,EAAQ2C,EACZ,mBAAmBD,EAAiBd,MACpCgB,EAAMP,GACNA,EAAQQ,QAAQC,eAGlB,MAAO,CACLlB,QACAmB,MAAOC,EAA8BhD,EAAOqC,EAASpC,MAGxD+B,KAAK,CAACiB,EAAMC,IAGPD,EAAKrB,MAAMa,SAAWS,EAAMtB,MAAMa,OAC7BQ,EAAKrB,MAAMa,OAASS,EAAMtB,MAAMa,OAGlCQ,EAAKF,MAAQG,EAAMH,OAE3BpB,IAAI,EAAGC,WAAY,mBAAmBc,EAAiBd,OA9B1B,IA6C5BuB,EAA0B,IAAI5B,IAAI,CACtC,cACA,eACA,YACA,UACA,WAaW6B,EAAyB,CACpCC,EACAC,IAEKA,EAIiB,UAAlBD,GAA+C,cAAlBA,EACxB7B,EAAqB8B,GAAgBC,KAAK,KAG5CD,EAAeE,QAAQ,iBAAkB,IAAI3B,OAP3C,GAgFE4B,EAAiBC,GACV,KACDvB,KAAKuB,GAGXf,EAAe,CAC1BgB,EACAf,EACAC,IAEOD,EACH,qBAAqBC,UAAgBc,KACrC,KAAKd,KAAWc,KAGTC,EACXC,IAEA,MAAMC,EAAcD,GAAeC,YAiBjC,OAAOA,GAUEC,EAAkB,CAC7B/D,EACAqC,EACApC,EACA+D,GAAqC,KAErC,IACE,MAAM9C,MAAEA,EAAKE,OAAEA,GAAWrB,EAAkBC,EAAOC,GAEnD,IAAKiB,EAAO,OAAO,EAGnB,IAAKE,EACH,OAAOF,IAAUmB,EAAU,EAAI,EAIjC,GAAI2B,GAA6BC,MAAMC,QAAQ7B,GAAU,CACvD,IAAI8B,EAAa,EAKjB,GAHI9B,EAAQ+B,SAASlD,IAAQiD,IACzB9B,EAAQ+B,SAAShD,IAAS+C,IAEX,IAAfA,EAAkB,OAAO,EAC7B,MAGMtD,GAFe,IAAnBZ,EAAMG,SAAkBH,EAAqBA,EAAMI,eAEhCS,SACnBd,EACAC,EACA,KACAc,YAAYC,2BACZ,MAGF,IAAIqD,EACJ,KAAQA,EAAOxD,EAAOM,eACpB,GAAIkB,EAAQ+B,SAASC,KACnBF,IACmB,IAAfA,GAAkB,OAAO,EAIjC,OAAO,CACT,CAEA,OAAO,CACT,CAAE,MAAOG,GAEP,OADAC,QAAQD,MAAM,2BAA2BtE,IAASsE,GAC3C,CACT,GAGW5B,EAAoB8B,IAC/B,GAAIA,EAAM,CACR,IAA4B,IAAtBA,EAAKC,QAAQ,KACjB,MAAO,IAAID,KAEb,IAA4B,IAAtBA,EAAKC,QAAQ,KACjB,MAAO,IAAID,IAEf,CACA,MAAO,IAAIA,MA0FAE,EAAsBhB,GAC7BA,EACKA,EAAIF,QAAQ,SAAU,KAAK3B,OAG7B6B,EAGIiB,EAA4B,CACvCtC,EACAmC,KAEA,MAAMI,EAAiBF,GAAoBF,GAAQ,IAAI3C,QAEvD,IAAKQ,EAAQwC,SAASpC,QAAUmC,EAAenC,QAAU,GACvD,MAAO,GAGT,MAAMqC,EAAWF,EAAelD,MAAM,OAAOqD,MAAM,EAAG,GAAGxB,KAAK,KAI9D,OAAOuB,EAASrC,OAAS,EACrB,+BAA+BC,EAAiBoC,MAChD,IAGOE,EACXR,IAEA,MAAMI,EAAiBF,GAAoBF,GAAQ,IAAI3C,QAEvD,IAAK+C,IAAmB3C,EAAyB2C,GAC/C,MAAO,CAAEK,OAAQL,EAAgBE,SAAUF,GAG7C,MAAMK,EAASL,EACZlD,MAAM,SAAS,GACf8B,QAAQ,0BAA2B,IACnC3B,OACGiD,EAAWG,EACdvD,MAAM,cACNC,IAAKuD,GAASR,EAAmBQ,GAAMrD,QACvCC,OAAQoD,GAASA,EAAKzC,OAAS,GAAK,WAAWN,KAAK+C,IACpD3B,KAAK,KACL1B,OAIH,MAAO,CACLoD,OAAQA,EAAOxC,OAAS,GAAK,WAAWN,KAAK8C,GAAUA,EAAS,GAChEH,SAAUA,EAASrC,OAAS,EAAIqC,EAAW,KAIlCK,EACXX,GACWQ,EAA8BR,GAAMM,SAqDpCM,EAAgC,CAC3CC,EACAhD,KAEA,MAAMiD,EACJD,EAAKhF,cAAckF,eAAeC,mBAAmB,gBACjDC,EAAUH,EAAQI,cAAc,OACtCD,EAAQE,UAAYN,EAAKM,UACzBL,EAAQM,KAAKC,YAAYJ,GAEzB,MAAMK,EAAmBC,IACvB,KACGA,GACCA,aAAqBV,EAAKhF,cAAc2F,YAAaC,SAEvD,OAAO,KAGT,MAAMC,EA1DqB,EAC7Bb,EACAhD,KAEA,MAAM6D,EAAiB,GACvB,IAAIC,EAA0B9D,EAE9B,KAAO8D,GAAWA,EAAQC,eAAe,CACvC,MAAMC,EAAkBF,EAAQC,cAChCF,EAAKI,QAAQrC,MAAMsC,KAAKF,EAAOxB,UAAUJ,QAAQ0B,IACjDA,EAAUE,CACZ,CAEA,OAAKF,GAAWA,EAAQK,aAAenB,GAIvCa,EAAKI,QAAQrC,MAAMsC,KAAKlB,EAAKR,UAAUJ,QAAQ0B,IACxCD,GAJE,MA4CMO,CAAuBpB,EAAMU,GAC1C,OAAOG,EAtCc,EACvBQ,EACAR,KAEA,IAAIC,EACFO,EAEF,IAAK,MAAMC,KAAST,EAAM,CACxB,MAAMU,EAAuBT,EAAQtB,SAASgC,KAAKF,GACnD,IAAKC,EACH,OAAO,KAGTT,EAAUS,CACZ,CAEA,OAAOT,GAsBSW,CAAiBrB,EAASS,GAAQ,MAG5Ca,EAAgB9C,MAAMC,QAAQ7B,GAChCA,EAAQV,IAAKoE,GAAcD,EAAgBC,IAAYjE,OAAOkF,SAC9D,GAEEC,EAAehD,MAAMC,QAAQ7B,GAC/B,KACAyD,EAA2B,MAE/B,MAAO,CACL5F,MAAOoF,EACPnF,YAAasF,EACbwB,eACAF,kBAISG,EAAyB,CACpCC,EAIAtD,EACAuD,KAEA,MAAMC,EAAiBjE,EACrB+D,EAAUG,KACVH,EAAUjF,OAGZ,IAAKmF,GAA8C,kBAArBF,GAAWjF,MACvC,OAAO,EAUT,GARsB,CACpB,OACA,QACA,KACA,MACA,aACA,6BAEgBqF,KAAMC,GAAMA,IAAMH,GAClC,OAAO,EAGT,GADqB,CAAC,QAAS,uBAAwB,SACtCE,KAAMC,GAAMA,IAAML,EAAUG,MAC3C,OAAO,EAGT,MAAMG,EAAapG,GAA2BqG,KAC3CF,GACCA,EAAEG,MAAQ9D,EAAcxD,eACxBmH,EAAEnF,UAAYwB,GACd2D,EAAEnE,gBAAkB8D,EAAUG,MAElC,OAAIG,MAImC,IAAnCN,GAAWG,MAAM7C,QAAQ,OAAe0C,GAAWG,MAAM7E,OAAS,KAIvC,mBAApB0E,EAAUjF,QAlerBmB,EAse+B8D,EAAUG,KArezChE,EAqe+C+D,IAne/ClE,EAAwBpB,IAAIsB,EAAcP,iBAPT,CAACQ,GAClC,+BAA+BnB,KAAKmB,GAOpCsE,CAA2BtE,MAyeJ,UAAnB6D,EAAUG,OAAoBrF,EAAyBoF,OA9e1B,IACjChE,EACAC,GAkgBWV,EAASP,GACbA,aAAmBwF,WAmCfC,EAAmB,CAC9BzF,EACApC,EACA8H,EACA7F,EACA8F,EACAZ,KAEA,GAAIlF,EAAO,CACT,MAAMW,QAAEA,GAAYR,EACpB,IAAIU,EACAkF,EAAiB,GACrB,MAAMC,EAAe,GACrB,IAAIvE,EAEJ,MAAMwE,EAAkBJ,EAAKK,WAAW,KAClCC,EAAsB,MAATN,GAAyB,WAATA,EAC7BO,EAAqBD,EAAa,IAAMN,EACxCQ,EAAiBF,GAAc,KAAKlG,KAAKD,EAAML,QAC/C2G,EAAmBH,EAAalD,EAAoBjD,GAASA,EAC7DuG,EACJJ,GAAcpG,EAAyBC,GAASsG,EAAmBtG,EAC/DwG,EAAyBL,EAC3B1D,EAA0BtC,EAASoG,GACnC,GAEJ,GAAa,WAATV,EAAmB,CACrB,IAAK,MAAMY,KAAkBvG,EAC3BC,EACAH,EACAjC,GAWA,GAPA0D,EAAUhB,EACRgG,EACA/F,EAAMP,GACNA,EAAQQ,QAAQC,eAGlBC,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,EAClB,OAAOrE,EAIX,MACF,CAEA,GAAI0E,GAAcpG,EAAyBC,KAAWsG,EAGpD,OAGF,GAAItG,KAAWiG,IAAoB1E,EAAcvB,MAE7CyB,EADE+E,EACQ/F,EACR+F,EACA9F,EAAMP,GACNA,EAAQQ,QAAQC,eAETyF,EACC5F,EACR,mBAAmB2F,MAAuB5F,EACxCgC,EAAmB+D,IACnB5G,SACFe,EAAMP,GACNA,EAAQQ,QAAQC,eAERlD,EAAauC,KAAKD,GASlB,KAAKW,KAAWkF,KAAQrF,EAChC+F,MATQ9F,EACR,mBAAmBoF,MAASrF,EAC1BgC,EAAmB+D,IACnB5G,SACFe,EAAMP,GACNA,EAAQQ,QAAQC,eAQpBC,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAE5B,IAAV8C,IAAgBiF,GAClB,OAAOrE,EAIX,GAAIwE,GAAmBlG,EAAyBC,GAG9C,OAGF,GAAIA,GAASkF,EAAU,CACrB,MAAMwB,EAAY1G,EAAMR,MAAM,KAC9B,GAAIkH,GAAWnG,OACb,GAAyB,IAArBmG,EAAUnG,OAAc,CAC1B,MAAMoG,EAAa,IAAI,IAAItH,IAAIqH,EAAU,GAAGE,MAAM,gBAwClD,GAvCID,GAAYpG,QAAU,GAEtBoG,EAAW,IACXnE,EAAmBmE,EAAW,GAAGhH,SAASY,OAAS,GAE/CP,EAAMkG,WAAWS,EAAW,MAM5BZ,EALGrI,EAAauC,KAAK0G,EAAW,IAKf,eAAed,KAAQrF,EACtCmG,EAAW,IACXhH,UANe,eAAekG,KAAQrF,EACtCgC,EAAmBmE,EAAW,KAC9BhH,WAUNgH,GAAYpG,OAAS,GAErBoG,EAAWA,EAAWpG,OAAS,IAC/BiC,EAAmBmE,EAAWA,EAAWpG,OAAS,GAAGZ,SACjDY,OAAS,GAETP,EAAM6G,SAASF,EAAWA,EAAWpG,OAAS,MAM9CwF,EALGrI,EAAauC,KAAK0G,EAAWA,EAAWpG,OAAS,IAKnC,aAAasF,KAAQrF,EACpCmG,EAAWA,EAAWpG,OAAS,IAC/BZ,UANe,aAAakG,KAAQrF,EACpCgC,EAAmBmE,EAAWA,EAAWpG,OAAS,KAClDZ,WAUNoG,GAAgBxF,SAEhBkB,EADEf,EAAMP,GACE,qBAAqBQ,UAAgBoF,KAErC,KAAKpF,KAAWoF,KAE5BlF,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,GAClB,OAAOrE,CAGb,KAAO,CACL,MAAMqF,EACJJ,EAAUnG,OAAS,GAAM,EACrBmG,EAAUnG,OAAS,EACnBmG,EAAUnG,OAAS,EACnBwG,EAAmBL,EAAU7D,MAAM,EAAGiE,GAAUzF,KAAK,KAC3D,IAAIsF,EAAa,IAAI,IAAItH,IAAI0H,EAAiBH,MAAM,gBAoBpD,GAnBID,GAAYpG,QAEZoG,EAAW,IACXnE,EAAmBmE,EAAW,GAAGhH,SAASY,QAEtCP,EAAMkG,WAAWS,EAAW,MAM5BZ,EALGrI,EAAauC,KAAK0G,EAAW,IAKf,eAAed,KAAQrF,EACtCmG,EAAW,IACXhH,UANe,eAAekG,KAAQrF,EACtCgC,EAAmBmE,EAAW,KAC9BhH,WAUNoG,GAAgBxF,SAEhBkB,EADEf,EAAMP,GACE,qBAAqBQ,UAAgBoF,KAErC,KAAKpF,KAAWoF,KAE5BlF,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,GAClB,OAAOrE,EAIX,MAAMuF,EAAiBN,EACpB7D,MAAMiE,EAAUJ,EAAUnG,OAAS,GACnCc,KAAK,KAqBR,GApBAsF,EAAa,IAAI,IAAItH,IAAI2H,EAAeJ,MAAM,gBAC1CD,GAAYpG,QAEZoG,EAAW,IACXnE,EAAmBmE,EAAW,GAAGhH,SAASY,OAAS,GAE/CP,EAAM6G,SAASF,EAAW,MAM1BZ,EALGrI,EAAauC,KAAK0G,EAAW,IAKf,aAAad,KAAQrF,EACpCmG,EAAW,IACXhH,UANe,aAAakG,KAAQrF,EACpCgC,EAAmBmE,EAAW,KAC9BhH,WAUNoG,GAAgBxF,SAEhBkB,EADEf,EAAMP,GACE,qBAAqBQ,UAAgBoF,KAErC,KAAKpF,KAAWoF,KAE5BlF,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,GAClB,OAAOrE,CAGb,CAEJ,CAEA,GAAIzB,GAASkF,GAAY3D,EAAcvB,GAAQ,CAC7C,MAAM2G,EAAa,IAAI,IAAItH,IAAIW,EAAM4G,MAAM,gBAC3C,GAAID,GAAYpG,OACd,IAAK,IAAI0G,EAAI,EAAGA,EAAIN,GAAYpG,OAAQ0G,IAEpCN,EAAWM,IACXzE,EAAmBmE,EAAWM,GAAGtH,SAASY,OAAS,IAE9C7C,EAAauC,KAAK0G,EAAWM,IAOhCjB,EAAakB,KACX,YAAYrB,KAAQrF,EAClBmG,EAAWM,GAAGtH,QACdA,WATJqG,EAAakB,KACX,YAAYrB,KAAQrF,EAClBgC,EAAmBmE,EAAWM,KAC9BtH,YAaZ,GAAIqG,GAAczF,SAEdkB,EADEf,EAAMP,GACE,qBAAqBQ,UAAgBqF,EAAa3E,KAC1D,YAGQ,KAAKV,KAAWqF,EAAa3E,KAAK,YAE9CR,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,GAClB,OAAOrE,CAGb,CASA,GANEA,EADEf,EAAMP,GACE,qBAAqBQ,iBAErB,KAAKA,YAGjBE,EAAQgB,EAAgBJ,EAAStB,EAASpC,GAC5B,IAAV8C,IAAgBiF,EAClB,OAAOrE,CAEX,GAmWW0F,EAA+B,CAC1CC,EACArJ,EACAsJ,EACAnC,KAEA,IACE,MAAMoC,EAAsBD,EAAiBzH,OAAQ2H,GACnDvC,EAAuBuC,EAAMH,IAGzBI,EAA4BvC,IAChC,MAAMwC,EAAYvG,EAChB+D,EAAUG,KACVH,EAAUyC,WAGZ,OAAKD,EAEkB,UAAnBxC,EAAUG,KACLlF,EACLkH,EACAK,EACA1J,GAKAgC,EAAyB0H,GAAmB,GAE3C/J,EAAauC,KAAKwH,GAIhB,CAAC,IAAIxC,EAAUG,SAASqC,MAHtB,CAAC,oBAAoBxC,EAAUG,UAAUqC,MAd3B,IAoBnBE,EAAoB,KACxB,MAAMC,EAAUpF,EAAmBd,EAAe0F,IAAUzH,QAAU,IAChE2G,EAAmBrD,EAAoB2E,GAC7C,GAAI7H,EAAyB6H,GAC3B,OAAOtB,EACH,CACE,+BAA+B9F,EAC7B8F,OAGJ,GAGN,IAAKsB,GAAWA,EAAQrH,OAAS,IAAM,QAAQN,KAAK2H,GAAU,CAC5D,MAAMpB,EAAyB/D,EAC7B2E,EACAQ,GAEF,OAAOpB,EAAyB,CAACA,GAA0B,EAC7D,CAEA,MAAMqB,EAAa,IAAIxI,IACjBmH,EAAyB/D,EAC7B2E,EACAQ,GAGF,OAAIpB,GACFqB,EAAWC,IAAItB,GACRzE,MAAMsC,KAAKwD,KAGhBnK,EAAauC,KAAK2H,IAAYA,EAAQrH,QAAU,KAAOqH,EAAQ1F,SAAS,MAC1E2F,EAAWC,IAAI,UAAUtH,EAAiBoH,MAGxCA,EAAQ1F,SAAS,MACnB2F,EAAWC,IACT,sBAAsBtH,EAAiBgC,EAAmBoF,OAI9DC,EAAWC,IAAI,mBAAmBtH,EAAiBoH,OAE5C7F,MAAMsC,KAAKwD,KAGdE,EAAgC,CACpCC,EACAC,KAEA,MAAMC,EAAoB,GAEpBC,EAAQ,CAACC,EAAoBC,KACjC,GAAIA,EAAa9H,SAAW0H,EAK5B,IAAK,IAAIhB,EAAImB,EAAYnB,EAAIe,EAAWzH,OAAQ0G,IAC9CoB,EAAanB,KAAKc,EAAWf,IAC7BkB,EAAMlB,EAAI,EAAGoB,GACbA,EAAaC,WAPbJ,EAAQhB,KAAK,IAAImB,KAYrB,OADAF,EAAM,EAAG,IACFD,GAGHK,EAAcV,GAClBnH,EAAM0G,GACF,qBAAqBA,EAAQzG,gBAAgBkH,EAAWxG,KAAK,YAC7D,KAAK+F,EAAQzG,WAAWkH,EAAWxG,KAAK,YAiDxCmH,EA/C+B,MACnC,KAAIlB,EAAoB/G,OAAS,GAIjC,IACE,IAAI0H,EAAkB,EACtBA,GAAmBX,EAAoB/G,OACvC0H,IACA,CACA,MAAMQ,EAAkBV,EACtBT,EACAW,GAGF,IAAK,MAAMS,KAAkBD,EAAiB,CAC5C,IAAIE,EAA4B,GAEhC,IAAK,MAAM1D,KAAayD,EAAgB,CACtC,MAAME,EAAsBpB,EAAyBvC,GAEhD2D,EAAoBrI,QAEzBoI,EAAgBzB,QAAQ0B,EAC1B,CAEA,GAAID,EAAgBpI,OAAS,EAAG,SAEhC,MAAMzC,EAAQyK,EAAWI,GAEzB,IAAI1G,EAEJ,IACEA,EAAaJ,EAAgB/D,EAAOsJ,EAASrJ,EAC/C,CAAE,MACA,QACF,CAEA,GAAmB,IAAfkE,EACF,OAAOnE,CAEX,CACF,GAKyB+K,GAC3B,GAAIL,EACF,OAAOA,EAGT,MAAMM,EAAiBnB,IACvB,IAAKmB,EAAevI,SAAW+G,EAAoB/G,OACjD,OAGF,IACE,IAAI0H,EAAkB,EACtBA,GAAmBX,EAAoB/G,OACvC0H,IACA,CACA,MAAMQ,EAAkBV,EACtBT,EACAW,GAGF,IAAK,MAAMS,KAAkBD,EAAiB,CAC5C,IAAIG,EAAgC,GAEpC,IAAK,MAAM3D,KAAayD,EACtBE,EAAoB1B,QAAQM,EAAyBvC,IAGvD,GAAK2D,EAAoBrI,OAIzB,IAAK,MAAMwI,KAAiBD,EAAgB,CAC1C,MAAMH,EAAkB,IAAIC,EAAqBG,GAC3CjL,EAAQyK,EAAWI,GAEzB,IACE,GAA+C,IAA3C9G,EAAgB/D,EAAOsJ,EAASrJ,GAClC,OAAOD,CAEX,CAAE,MACA,QACF,CACF,CACF,CACF,CACF,CAAE,MAAOsE,GACPC,QAAQ2G,IAAI,2BAA2BC,KAAKC,UAAU9G,EAAO,KAAM,KACrE,GAmEW+G,EAAuB,CAClChH,EACApE,KAEA,IAAKoE,EAAKP,YAAa,MAAO,GAE9B,MAAMwH,GAnzCwBjJ,EAmzCOgC,EAlzC9BhC,GAASkJ,WAAW,IAAI3B,WAAa,IADf,IAACvH,EAozC9B,MAAMmG,EAAmBrD,EAAoBmG,GAC7C,GAAIrJ,EAAyBqJ,KAAkB9C,EAE7C,MAAO,GAGT,MAAMgD,EACJvJ,EAAyBqJ,GAAgB9C,EAAmB8C,EACxD5C,EAAyB/D,EAC7BN,EACAmH,GAGF,IAAIC,EA4BJ,OAzBEA,EADE/C,EACO/F,EACP+F,EACA9F,EAAMyB,GACNA,EAAKxB,SAAW,KAETZ,EAAyBqJ,GACzB3I,EACP,+BAA+BD,EAAiB8I,MAChD5I,EAAMyB,GACNA,EAAKxB,SAAW,MAERjD,EAAauC,KAAKmJ,IAAiB,KAAKnJ,KAAKmJ,EAAazJ,QAC3Dc,EACP,sBAAsBD,EAAiB8I,KACvC5I,EAAMyB,GACNA,EAAKxB,SAAW,KAGTD,EAAMyB,GACX,qBAAqBA,EAAKxB,kBAAkBH,EAC1C8I,MAEF,KAAKnH,EAAKxB,SAAW,SAASH,EAAiB8I,MAG9CC,GAGIC,EAA6B,CACxCrJ,EACA0F,EACA7F,KAEA,IAAc,MAAT6F,GAAyB,WAATA,IAAsB9F,EAAyBC,GAAQ,CAC1E,MAAM4C,SAAEA,GAAaE,EAA8B9C,GAEnD,OAAO4C,EACHnC,EACE,+BAA+BD,EAAiBoC,MAChDlC,EAAMP,GACNA,EAAQQ,QAAQC,eAElB,EACN,CAEA,OAAOH,EACL,mBAAmBoF,MAASrF,EAAiBgC,EAAmBxC,IAAQL,SACxEe,EAAMP,GACNA,EAAQQ,QAAQC,gBAIP6I,EAA6B,CACxCtJ,EACA0F,EACA7F,KAEA,IAAc,MAAT6F,GAAyB,WAATA,IAAsB9F,EAAyBC,GAAQ,CAC1E,MAAM+C,OAAEA,EAAMH,SAAEA,GAAaE,EAA8B9C,GACrD0J,EAAa3G,GAAUH,EAE7B,OAAO8G,EACHjJ,EACE,kCAAkCD,EAAiBkJ,MACnDhJ,EAAMP,GACNA,EAAQQ,QAAQC,eAElB,EACN,CAEA,OAAOH,EACL,eAAeoF,KAAQrF,EAAiBgC,EAAmBxC,IAAQL,UACnEe,EAAMP,GACNA,EAAQQ,QAAQC,gBAIP+I,EAA2B,CACtCxJ,EACA0F,EACA7F,KAEA,IAAc,MAAT6F,GAAyB,WAATA,IAAsB9F,EAAyBC,GAAQ,CAC1E,MAAM4C,SAAEA,GAAaE,EAA8B9C,GAEnD,OAAO4C,EACHnC,EACE,+BAA+BD,EAAiBoC,MAChDlC,EAAMP,GACNA,EAAQQ,QAAQC,eAElB,EACN,CAEA,OAAOH,EACL,YAAYoF,KAAQrF,EAAiBgC,EAAmBxC,IAAQL,UAChEe,EAAMP,GACNA,EAAQQ,QAAQC,gBAIPgJ,EAAuB,CAClCzJ,EACA6H,KAEA,MA0DMY,EAAsBZ,EACzB6B,QAAS5E,GA3DqB,CAACA,IAChC,MAAMwC,EAAYvG,EAAuB+D,EAAUG,KAAMH,EAAUjF,OAEnE,IAAKyH,EACH,MAAO,GAGT,GAAuB,UAAnBxC,EAAUG,KAAkB,CAC9B,MAAMrH,EACHoC,EAAQ2J,iBACT3J,EAAQhC,cAEV,OAAO+B,EAAwBC,EAASsH,EAAW1J,EACrD,CAGA,OAAIgC,EAAyB0H,GAAmB,GAE3C/J,EAAauC,KAAKwH,GAMhB,CAAC,IAAIxC,EAAUG,QAAQ5E,EAAiBiH,MALtC,CACL,oBAAoBxC,EAAUG,SAAS5E,EAAiBiH,OAuCpCD,CAAyBvC,IAChDrF,OAAOkF,SAEV,GAAI8D,EAAoBrI,QAAU,EAChC,OAAOE,EACL,GAAGmI,EAAoB,SAASA,EAAoB,KACpDlI,EAAMP,GACNA,EAAQQ,QAAQC,eAIpB,MAAMmI,EA3CqB,MACzB,MAAMnB,EAAUpF,EAAmBd,EAAevB,IAAUR,QAAU,IAChE2G,EAAmBrD,EAAoB2E,GAC7C,GAAI7H,EAAyB6H,GAE3B,OAAOtB,EACH,+BAA+B9F,EAAiB8F,MAChD,KAGN,MAAME,EAAyB/D,EAA0BtC,EAASyH,GAElE,OAAIpB,KAICoB,GAAWA,EAAQrH,OAAS,IAAM,QAAQN,KAAK2H,GAC3C,KAGLlK,EAAauC,KAAK2H,IAAYA,EAAQrH,QAAU,KAAOqH,EAAQ1F,SAAS,KACnE,UAAU1B,EAAiBoH,KAGhCA,EAAQ1F,SAAS,KACZ,sBAAsB1B,EAAiBoH,KAGzC,mBAAmBpH,EAAiBoH,QAevBmC,GACtB,OAAmC,IAA/BnB,EAAoBrI,QAAgBwI,EAC/BtI,EACL,GAAGmI,EAAoB,SAASG,IAChCrI,EAAMP,GACNA,EAAQQ,QAAQC,eAIb,IAGHE,EAAgC,CACpChD,EACAqC,EACApC,KAEA,IACE,OAAO8D,EAAgB/D,EAAOqC,EAASpC,EACzC,CAAE,MAAOiM,GACP,OAAO,CACT,GAGIC,EAA8B,CAClC9H,EACApE,KAEA,MAAMmM,EAAoB,GACpBC,EAAO,IAAI9K,IACX2I,EAAajG,MAAMsC,KAAKlC,EAAK6F,YAAc,IAC3CoC,EAAgB,CACpB,KACA,cACA,YACA,UACA,OACA,aACA,QAEIC,EAAoB,IACrBD,EACA3K,IAAK6K,GAAatC,EAAWxC,KAAM+B,GAASA,EAAKnC,OAASkF,IAC1D1K,OAAOkF,YACPkD,EAAWpI,OAAQ2H,IAAU6C,EAAclI,SAASqF,EAAKnC,QAGxDmF,EAAczM,IACbA,IAASqM,EAAKtK,IAAI/B,IACnB0M,EAAmB1M,EAAOqE,EAAMpE,KAClCoM,EAAKrC,IAAIhK,GACToM,EAAQhD,KAAKpJ,KAIjBuM,EAAkBI,QAASlD,IACzB,GAAKvC,EAAuBuC,EAAMpF,GAElC,IAAK,MAAMrE,KAAS4M,EAAgBvI,EAAMoF,EAAKnC,KAAMmC,EAAKvH,OACxDuK,EAAWzM,KAIf,MAAMwE,EAAOH,EAAKP,aAAajC,OAC/B,GAAI2C,GAAQA,EAAK/B,OAAS,IAA+B,IAAzB4B,EAAKQ,SAASpC,OAAc,CAC1D,MAAMoK,EAAYxB,EAAqBhH,GACnCwI,GACFJ,EAAWI,EAEf,CAEA,GAAI3C,EAAWzH,OAAS,EAAG,CACzB,MAAMqK,EAAmBzD,EACvBhF,EACApE,EACAiK,GAGE4C,GACFL,EAAWK,EAEf,CAEA,OAAOV,GAGHW,EAAgC,CACpCC,EACA3K,KAEA,MAAM4K,EAAkB,GACxB,IAAI9G,EAA0B9D,EAE9B,KAAO8D,GAAWA,IAAY6G,GAC5BC,EAAM3G,QAAQ4G,EAAgB/G,IAC9BA,EAAUA,EAAQC,cAGpB,OAAOD,IAAY6G,EAAWC,EAAM1J,KAAK,KAAO,IAGrC4J,EAAkB,CAC7B9K,EACApC,KAEA,MAAMoF,EACJpF,IACEoC,EAAQ2J,iBACR3J,EAAQhC,eACNwC,EAAUqK,EAAgB7K,GAC1B+K,EAAgBxK,EAAMP,GACxB,qBAAqBA,EAAQQ,QAAQC,kBACrC,KAAKT,EAAQQ,QAAQC,gBAEzB,IACE,IAAIkK,EAAW3K,EAAQ+D,cACvB4G,EACAA,EAAWA,EAAS5G,cACpB,CACA,MAAMiH,EAAkBlB,EAA4Ba,EAAU3H,GAE9D,IAAK,MAAMiI,KAAiBD,EAAiB,CAC3C9I,QAAQ2G,IAAI,0BAA0BoC,KAEtC,MAAMC,EAAkB,GAAGD,iBAA6BzK,IACxD0B,QAAQ2G,IAAI,4BAA4BqC,KAExC,MAAMxK,EAAQC,EACZuK,EACAlL,EACAgD,GAIF,GAFAd,QAAQ2G,IAAI,gBAAgBnI,KAGhB,IAAVA,GACAyK,EAAoBD,EAAiBlI,KAAUhD,EAG/C,OADAkC,QAAQ2G,IAAI,mBAAmBqC,KACxBA,EAGT,MAAME,EAAiBV,EAA8BC,EAAU3K,GAC/D,GAAIoL,EAAgB,CAClB,MAAMC,EAAmB,GAAGJ,KAAiBG,IAC7ClJ,QAAQ2G,IAAI,4BAA4BwC,KAExC,MAAMC,EAAmB3K,EACvB0K,EACArL,EACAgD,GAIF,GAFAd,QAAQ2G,IAAI,gBAAgByC,KAGL,IAArBA,GACAH,EAAoBE,EAAkBrI,KAAUhD,EAGhD,OADAkC,QAAQ2G,IAAI,mBAAmBwC,KACxBA,CAEX,CACF,CACF,CAEAnJ,QAAQ2G,IAAI,4BAA4BkC,KACxC,MAAMQ,EAAgB5K,EACpBoK,EACA/K,EACAgD,GAIF,OAFAd,QAAQ2G,IAAI,gBAAgB0C,KAGR,IAAlBA,GACAJ,EAAoBJ,EAAe/H,KAAUhD,GAE7CkC,QAAQ2G,IAAI,mBAAmBkC,KACxBA,IAGT7I,QAAQ2G,IAAI,oBACL,KAGIsC,EAAsB,CACjCxN,EACAC,KAEA,IACE,GAn9CsBoE,EAm9CDpE,EAl9ChBoE,GAAMjE,WAAayN,KAAKC,wBAA0B,SAAUzJ,EAk9CpC,CAC3B,MAAMnE,MAAEA,EAAKC,YAAEA,EAAW8G,aAAEA,GAC1B7B,EAA8BnF,GAShC,OAReC,EAAMY,SACnBd,EACAG,EACA,KACAY,YAAYgN,wBACZ,MAGaC,iBAAmB/G,CAIpC,CAUA,OARehH,EAAMa,SACnBd,EACAC,EACA,KACAA,EAAM+F,YAAajF,YAAYgN,wBAC/B,MAGYC,eAChB,CAAE,MAAO9B,GACP,OAAO,IACT,CA/+CuB,IAAC7H,GAk/CbqI,EAAqB,CAChC1M,EACAqC,EACApC,KAEA,IACE,MAAMiB,MAAEA,EAAKE,OAAEA,GAAWrB,EAAkBC,EAAOC,GAEnD,QAASiB,IAAUE,GAAUF,IAAUmB,CACzC,CAAE,MACA,OAAO,CACT,GAGW6K,EAAmB7K,GACvBO,EAAMP,GACT,mBAAmBA,EAAQQ,QAAQC,kBACnCT,EAAQQ,QAAQC,cAKTmL,EAA4B,CACvC5J,EACApE,EACAmH,KAEA,KAAM/C,aAAgB4B,SAAU,MAAO,GACvC,MAAMoG,EAAO,IAAI9K,IACX6K,EAA4C,GAE5ClC,EAAajG,MAAMsC,KAAKlC,EAAK6F,YAAc,IAAIpI,OAClDqF,IACCA,UAAWjF,QAbKA,EAcFiF,EAAUjF,OAdU,KAAKC,KAAKD,KAe5CgF,EAAuBC,EAAW9C,GAfnB,IAACnC,IAkBduK,EAAa,CAACyB,EAAahM,KAC1BA,IAASmK,EAAKtK,IAAIG,KACvBmK,EAAKrC,IAAI9H,GACTkK,EAAQhD,KAAK,CAAE8E,MAAKhM,YAIhBoK,EAAgB,CACpB,KACA,cACA,YACA,UACA,OACA,aACA,QAGFA,EAAcK,QAASH,IACrB,MAAM/C,EAAOS,EAAWxC,KAAMyG,GAAMA,EAAE7G,OAASkF,GAC/C,GAAK/C,EAEL,IAAK,MAAMzJ,KAAS4M,EAAgBvI,EAAMoF,EAAKnC,KAAMmC,EAAKvH,OACpDlC,GAAS0M,EAAmB1M,EAAOqE,EAAMpE,IAC3CwM,EAAW,aAAahD,EAAKnC,OAAQtH,KAM3CkK,EAAWyC,QAASxF,IAClB,IAAImF,EAAclI,SAAS+C,EAAUG,MAErC,IAAK,MAAMtH,KAAS4M,EAAgBvI,EAAM8C,EAAUG,KAAMH,EAAUjF,OAC9DlC,GAAS0M,EAAmB1M,EAAOqE,EAAMpE,IAC3CwM,EAAW,aAAatF,EAAUG,OAAQtH,KAMhD,MAAMwE,EAAOH,EAAKP,aAAajC,OAC/B,GAAI2C,GAAQA,EAAK/B,OAAS,IAA+B,IAAzB4B,EAAKQ,SAASpC,OAAc,CAC1D,MAAMoK,EAAYxB,EAAqBhH,GACnCwI,GAAaH,EAAmBG,EAAWxI,EAAMpE,IACnDwM,EAAW,iBAAkBI,EAEjC,CAGA,GAAI3C,EAAWzH,OAAS,EAAG,CACzB,MAAMqK,EAAmBzD,EACvBhF,EACApE,EACAiK,GAIE4C,GAAoBJ,EAAmBI,EAAkBzI,EAAMpE,IACjEwM,EAAW,wBAAyBK,EAExC,CAGA,MAAMsB,EAAWjB,EAAgB9I,EAAMpE,GAKvC,OAJImO,GAAY1B,EAAmB0B,EAAU/J,EAAMpE,IACjDwM,EAAW,gBAAiB2B,GAGvBhC,GA6CIQ,EAAkB,CAC7BvI,EACAmI,EACA7C,KAEA,MAAM/J,EAAe,IAAIyO,OAAO,sBAChC,IAAI5C,EAAiB,GAGrB,GAFA9B,EAAYvG,EAAuBoJ,EAAU7C,GAE9B,CACb,GAAiB,UAAb6C,EAAsB,CACxB,MAAMvM,EACHoE,EAAK2H,iBACN3H,EAAKhE,cAGP,OAAO+B,EAAwBiC,EAAMsF,EAAW1J,GAAO0B,IAAK2M,GAC1D1L,EAAMyB,GACF,qBAAqBA,EAAKxB,gBAAgByL,KAC1C,KAAKjK,EAAKxB,SAAW,OAAOyL,KAEpC,CAIA,GAAIrM,EAAyB0H,GAAY,MAAO,GAW9C8B,EATG7L,EAAauC,KAAKwH,GASZ/G,EAAMyB,GACX,qBACEA,EAAKxB,iBACG2J,KAAY9J,EAAiBiH,MACvC,KAAKtF,EAAKxB,SAAW,QAAQ2J,KAAY9J,EACvCiH,MAbG/G,EAAMyB,GACX,qBACEA,EAAKxB,0BACY2J,KAAY9J,EAAiBiH,OAChD,KAAKtF,EAAKxB,SAAW,iBAAiB2J,KAAY9J,EAChDiH,MAWV,CAEA,OAAO8B,EAAS,CAACA,GAAU,IAwQhB8C,EAAkBvO,GAQ7BA,GANAA,EAAQA,EAAMwD,QACZ,gCACA,8BAIYA,QAAQ,0BAA2B,6BAqD7C,SAAUgL,EAAgBxO,GAC9B,MAAMyO,EAhCF,SAA4BzO,GAChC,OACEA,EACG8C,cAEAU,QAAQ,WAAY,KACpBA,QAAQ,WAAY,KAEpBA,QAAQ,WAAY,OAEpBA,QAAQ,mBAAoB,UAC5BA,QAAQ,iBAAkB,QAEjC,CAmBoBkL,CAAkB1O,GAC9B2O,EAlBF,SAAqC3O,GACzC,MAAM4O,EAAK5O,EAAM8C,cAEX+L,EAAYD,EAAG9F,MACnB,yIAEIgG,EAAOD,IAAY,IAAM,OAEzBE,EAAYH,EAAG9F,MAAM,kBAK3B,MAAO,CAAEgG,OAAM3H,UAJG4H,IAAY,IAAM,OAIVC,cAFJJ,EAAGxK,SAAS,mBAGpC,CAIgB6K,CAA2BjP,GAEzC,MAAO,CACL,QACA,QAAQ2O,EAAMG,OACd,QAAQH,EAAMxH,YACd,aAAawH,EAAMK,gBACnB,SAASP,KACTlL,KAAK,IACT,CAEM,SAAU2L,EAAgBhN,GAC9B,OAAOA,EAAMsB,QAAQ,MAAO,QAAQA,QAAQ,KAAM,OAAOA,QAAQ,KAAM,MACzE,CAQM,SAAU2L,EACdlP,EACAqH,EACApF,EACAG,GAEA,MAAMgD,EAAQhD,GAAS2J,iBAAyC/L,EAC1DmP,EAAYC,IAChB,IACE,OAAOpL,MAAMsC,KAAKlB,EAAKiK,iBAAiBD,GAC1C,CAAE,MACA,MAAO,EACT,GAGF,IACE,OAAQ/H,GACN,IAAK,KACH,OAAyD,IAAlD8H,EAAS,IAAIF,EAAgBhN,MAAUO,OAEhD,IAAK,OACH,OAAiE,IAA1D2M,EAAS,UAAUF,EAAgBhN,QAAYO,OAExD,IAAK,YACH,OAAwC,IAAjC2M,EAAS,IAAIlN,KAASO,OAE/B,IAAK,UAaL,IAAK,cACH,OAAkC,IAA3B2M,EAASlN,GAAOO,OAXzB,IAAK,WACH,OAEgB,IADd2M,EAAS,KAAKtN,OAAQqM,GAAMA,EAAErK,aAAajC,SAAWK,GACnDO,OAEP,IAAK,kBACH,OAEE,IADA2M,EAAS,KAAKtN,OAAQqM,GAAMA,EAAErK,aAAaM,SAASlC,IAAQO,OAKhE,QACE,OAAO,EAEb,CAAE,MACA,OAAO,CACT,CACF,CCpkFA,IAAI8M,EAA8C,GAQlD,MAAMC,EAAe,CACnBC,cAAe,MACfC,aAAc,KACdC,SAAU,WACVvH,WAAY,aACZwH,eAAgB,iBAChBC,KAAM,OACNlJ,MAAO,QACPmJ,cAAe,OACfC,QAAS,OAOLC,EAAkB,CACtBC,EACAC,IAEOA,EAAQvO,IAAKwO,IAAK,IACpBA,EACHjC,IAAKiC,GAAOjC,KAAK9J,SAAS6L,GACtBE,EAAMjC,IACN,GAAG+B,KAAYE,EAAMjC,MAAMrM,UAqB7BuO,EAA8B,CAClCF,EACAlI,IAEAA,EAAUkI,EAAUA,EAAQpO,OAAQqO,IAAU,OARpBnQ,EAQwCmQ,EAAMjO,QAPxE,iCAAiCC,KAAKnC,IACtC,kBAAkBmC,KAAKnC,IAFE,IAACA,IAUtBqQ,EAAqBhO,GAClBA,GAASyB,aAAaN,QAAQ,OAAQ,MAAM3B,QAAU,GA8GzDyO,EAA0BjL,IAC9B,KAAKA,GAAUA,aAAgBY,SAAU,MAAO,GAEhD,MAAMsK,EAAmC,GACnCC,EAAQvM,MAAMsC,KAAKlB,EAAKR,UAAY,IAE1C,KAAO2L,EAAM/N,QAAQ,CACnB,MAAMgO,EAAcD,EAAME,QAEtBD,EAAYE,WAAWhB,SAAS,gBAIpCY,EAAMnH,KAAKqH,GACXD,EAAMpH,QAAQnF,MAAMsC,KAAKkK,EAAY5L,UAAY,KACnD,CAEA,OAAO0L,GAGHK,EAAkB,CACtBvO,EACAwO,KAEA,MAAMN,EAAmC,GACzC,IAAIE,EACY,aAAdI,EACIxO,EAAQyO,uBACRzO,EAAQ0O,mBAEd,KAAON,GACAA,EAAYE,WAAWhB,SAAS,eACnCY,EAAMnH,KAAKqH,GAGbA,EACgB,aAAdI,EACIJ,EAAYK,uBACZL,EAAYM,mBAGpB,OAAOR,GAUHS,EAA8B,CAClC3O,EACAwO,KAEA,MAAMN,EAAwB,GAE9B,IACE,IAAIE,EAA4CpO,EAChDoO,GAAarK,cACbqK,EAAcA,EAAYrK,cAC1B,CACA,IAAI6K,EACY,aAAdJ,EACIJ,EAAYK,uBACZL,EAAYM,mBAElB,KAAOE,GACAA,EAAQN,WAAWhB,SAAS,gBAC/BY,EAAMnH,KAAK6H,GAEXV,EAAMnH,KAAK,CACT/E,KAAM4M,EACNC,OAAQ,IAAMZ,EAAuBW,MAIzCA,EACgB,aAAdJ,EACII,EAAQH,uBACRG,EAAQF,kBAElB,CAEA,OAAOR,GAGHY,EAAoB9M,IACxB,KAAMA,aAAgB4B,SAAU,OAAO,EACvC,MAAMzB,EAAOH,EAAKP,aAAajC,OAE/B,OACGwC,EAAK+M,KACL/M,EAAKgN,WACqB,IAA3BhN,EAAK6F,WAAWzH,UACd+B,GAAQA,EAAK/B,OAAS,KACxB4B,EAAKQ,SAASpC,OAAS,GAIrB6O,EAA0B,CAC9Bf,EACAzB,EACAjL,EACA5D,EACAmH,EACA8G,EACAlG,KAEA,MAAMuJ,EAAiBrE,EAAgBrJ,GAIjC2N,EAA+C,GAErD,IAAIC,EAAY,EAChB,MAAMC,EAAc5C,EAAK1K,SAAS,cAAgB,GAAK,GAEvD,IAAK,MAAMuN,KAAYpB,EAAO,CAC5B,GAAIkB,IAAcC,EAAa,MAC/B,GAAIF,EAAW/O,QATM,EASoB,MAEzC,MAAM4B,EAAO,SAAUsN,EAAWA,EAAStN,KAAOsN,EAElD,KAAMtN,aAAgB4B,SAAU,SAChC,GAAIkL,EAAiB9M,GAAO,SAE5B,MAAMuN,EAAe,CACnB,IAAM3D,EAA0B5J,EAAMpE,GACtC,KACE,MAAMmM,EAAiB,GACjBlC,EAAajG,MAAMsC,KAAKlC,EAAK6F,YAAc,IAAIpI,OAClD2H,GACCA,GAAMvH,QACL,KAAKC,KAAKsH,EAAKvH,QAChBgF,EAAuBuC,EAAMpF,IAGjC,IAAK,MAAMoF,KAAQS,EAAY,CAC7B,MAAMhI,EAAQuH,EAAKvH,MACnB,IAAKA,EAAO,SAEZ,MAAM2P,EAAgBhG,EACpBxH,EACA,IAAIoF,EAAKnC,OACTpF,GAEE2P,GACFzF,EAAQhD,KAAK,CAAE8E,IAAK,WAAYhM,MAAO2P,IAGzC,MAAMC,EAAkBnG,EACtBtH,EACA,IAAIoF,EAAKnC,OACTpF,GAEE4P,GACF1F,EAAQhD,KAAK,CAAE8E,IAAK,cAAehM,MAAO4P,GAE9C,CAEA,OAAO1F,GAET,IAAM,CAAC,CAAE8B,IAAK,MAAOhM,MAAOiL,EAAgB9I,EAAMpE,MAGpD,IAAK,IAAI8R,EAAa,EAAGA,EAAaH,EAAanP,OAAQsP,IAAc,CACvE,IAAI3F,EAAUwF,EAAaG,KAC3B,IAAK3F,EAAQ3J,OAAQ,SAEhBuF,IACHoE,EAAUA,EAAQtK,OAAQqM,IAAO,UAAUhM,KAAKgM,EAAEjM,SAGpD,MAAMmK,EAAO,IAAI9K,IACjB6K,EAAUA,EAAQtK,OAAQqM,IACpB9B,EAAKtK,IAAIoM,EAAEjM,SACfmK,EAAKrC,IAAImE,EAAEjM,QACJ,IAGTkK,EAAUA,EAAQrH,MAAM,EAAG,GAE3B,IAAK,MAAMiN,KAAU5F,EAAS,CAC5B,MAAMpM,EAAQ,GAAGgS,EAAO9P,SAAS4M,MAASyC,IAE1C,GAAI7E,EAAmB1M,EAAO6D,EAAe5D,GAAQ,CAGnD,GAFAuR,EAAWpI,KAAK,CAAE8E,MAAKhM,MAAOlC,IAE1BwR,EAAW/O,QAAU,EACvB,OAAO+O,EAGT,GAAIA,EAAW/O,QAlFA,EAmFb,OAAO+O,CAEX,CACF,CAEA,GAAIxJ,GAAW+J,IAAeH,EAAanP,OAAS,EAClD,IAAK,MAAMuP,KAAU5F,EAAS,CAC5B,MAAMrJ,EAAQgB,EAAgBiO,EAAO9P,MAAOmC,EAAMpE,GAElD,IAAK,IAAIkJ,EAAI,EAAGA,GAAK8I,KAAKC,IAAInP,EA7FhB,GA6FuCoG,IAAK,CACxD,MAAMnJ,EAAQ,IAAIgS,EAAO9P,UAAUiH,MAAM2F,MAASyC,IAElD,GAAI7E,EAAmB1M,EAAO6D,EAAe5D,GAAQ,CAGnD,GAFAuR,EAAWpI,KAAK,CAAE8E,MAAKhM,MAAOlC,IAE1BwR,EAAW/O,QAAU,EACvB,OAAO+O,EAGT,GAAIA,EAAW/O,QAtGJ,EAuGT,OAAO+O,CAEX,CACF,CACF,CAEJ,CAEA,GAAI,WAAYG,GAAYA,EAAST,OAAQ,CAC3C,MAAMiB,EAAWR,EAAST,SAE1B,IAAIkB,EAAa,EACjB,IAAK,MAAMC,KAASF,EAAU,CAC5B,GAAIC,IAAe,GAAI,MACvB,GAAIZ,EAAW/O,QArHE,EAqHwB,MAEzC,KAAM4P,aAAiBpM,SAAU,SACjC,GAAIkL,EAAiBkB,GAAQ,SAE7B,MAAMjG,EAAU6B,EAA0BoE,EAAOpS,GAEjD,IAAK,MAAM+R,KAAU5F,EAAQrH,MAAM,EAAG,GAAI,CACxC,MAAM/E,EAAQ,GAAGgS,EAAO9P,SAAS4M,MAASyC,IAE1C,GAAI7E,EAAmB1M,EAAO6D,EAAe5D,GAAQ,CAGnD,GAFAuR,EAAWpI,KAAK,CAAE8E,MAAKhM,MAAOlC,IAE1BwR,EAAW/O,QAAU,EACvB,OAAO+O,EAGT,GAAIA,EAAW/O,QAtIF,EAuIX,OAAO+O,CAEX,CACF,CACF,CACF,CACF,CAEA,OAAOA,GAGHc,GAA0B,CAC9BjQ,EACApC,EACAmH,EACAY,KAMA,MAAMuK,EAAuC,GAE7C,IACE,IAAI9B,EAAcpO,EAAQ+D,cAC1BqK,EACAA,EAAcA,EAAYrK,cAE1BmM,EAAUnJ,KAAKqH,GAIjB,MAAM+B,EAAclC,EAAuBjO,GAGrCoQ,EAAmB7B,EAAgBvO,EAAS,YAG5CqQ,EAAe9B,EAAgBvO,EAAS,QAGxCsQ,EAAiB3B,EAA4B3O,EAAS,YAGtDuQ,EAAiB5B,EAA4B3O,EAAS,QAgBtDwQ,EAAa,CAACtC,EAAcuC,EAAQ,KACjCvC,EAAM9N,OAASqQ,EAAQvC,EAAMxL,MAAM,EAAG+N,GAASvC,EAGlDwC,EAAQ,CACZ,CACE,CACExC,MAAOsC,EAAWH,GAClB5D,KAAM,oBACNZ,IAAK,qBAEP,CACEqC,MAAOsC,EAAWJ,GAClB3D,KAAM,oBACNZ,IAAK,sBAGT,CACE,CAAEqC,MAAOsC,EAAWN,GAAYzD,KAAM,QAASZ,IAAK,SACpD,CACEqC,MAAOsC,EAAWL,EAAa,IAC/B1D,KAAM,SACNZ,IAAK,WAGT,CACE,CAAEqC,MAAOsC,EAAWN,GAAYzD,KAAM,aAAcZ,IAAK,cACzD,CACEqC,MAAOsC,EAAWN,GAClBzD,KAAM,qBACNZ,IAAK,sBAEP,CACEqC,MAAOsC,EAAWL,EAAa,IAC/B1D,KAAM,WACNZ,IAAK,YAEP,CACEqC,MAAOsC,EAAWL,EAAa,IAC/B1D,KAAM,mBACNZ,IAAK,qBAGT,CACE,CACEqC,MAAOsC,EAAWF,GAClB7D,KAAM,YACNZ,IAAK,aAEP,CAAEqC,MAAOsC,EAAWD,GAAiB9D,KAAM,YAAaZ,IAAK,eAIjE,IAAK,MAAM8E,KAAQD,EACjB,IAAK,MAAM9C,KAAY+C,EAAM,CAG3B,MAAM5I,EAAUkH,EACdrB,EAASM,MACTN,EAASnB,KACTzM,EACApC,EACAmH,EACA6I,EAAS/B,IACTlG,GAIF,GAAIoC,EAAQ3H,OAAS,EAEnB,OAAO2H,CAEX,CAGF,MAAO,IAqDH6I,GAAsB,CAC1B5Q,EACApC,EACAmH,EACA6I,EACAiD,KAEA,MAAMC,EA/kBgB,CAAClD,GAChBT,EAAaS,IAA0CA,EA8kBzCmD,CAAgBnD,GAErC,MAAqB,SAAjBkD,EACKnD,EACLC,EAxiB0B,EAC9B5N,EACApC,EACAgQ,KAIA,IAFaI,EAAkBhO,GAG7B,MAAO,GAGT,GAAiB,SAAb4N,EAAqB,CACvB,MAAMpD,EACJxB,EAAqBhJ,IACrBgR,GAAahR,EAASpC,GAAO,GAAO,GAEtC,OAAO4M,EAAY,CAAC,CAAEqB,IAAK,gBAAiBhM,MAAO2K,IAAe,EACpE,CAEA,MAAO,IAshBHyG,CAAwBjR,EAASpC,EAAOkT,IAIxC,CAAC,WAAY,aAAc,kBAAkB/O,SAAS+O,GACjDnD,EACLC,EAzhB8B,EAClC5N,EACA+E,EACA6I,KAEA,MAAMsD,EAA2C,GAC3CrJ,EAAajG,MAAMsC,KAAKlE,EAAQ6H,YAAc,IAAIpI,OACrDqF,GACCA,GAAWjF,OAASgF,EAAuBC,EAAW9E,IAEpDmC,EAAO6L,EAAkBhO,GACzBmR,EACS,aAAbvD,EACIpE,EACa,eAAboE,EACEtE,EACa,mBAAbsE,EACEvE,EACA,KAEV,IAAK8H,EACH,OAAOD,EAkBT,GAfArJ,EAAWyC,QAASxF,IAClB,MAAMnH,EAAQwT,EACZnR,EACA,IAAI8E,EAAUG,OACdH,EAAUjF,OAGRlC,GACFuT,EAAOnK,KAAK,CACV8E,IAAK,YAAY+B,KAAY9I,EAAUG,OACvCpF,MAAOlC,MAKTwE,EAAM,CACR,MAAMxE,EAAQwT,EAAiBnR,EAAS,IAAKmC,GAEzCxE,GACFuT,EAAOnK,KAAK,CACV8E,IAAK,YAAY+B,SACjB/N,MAAOlC,GAGb,CAEA,OAAOuT,GAweHE,CAA4BpR,EAAS+E,EAAU+L,IAI/C,CAAC,MAAO,MAAM/O,SAAS+O,GAClBnD,EACLC,EA3e+B,EACnC5N,EACApC,EACAmH,EACA6I,KAEA,MAAM/F,EAAajG,MAAMsC,KAAKlE,EAAQ6H,YAAc,IAAIpI,OACrDqF,GACCA,GAAWjF,OAASgF,EAAuBC,EAAW9E,IAG1D,GAAiB,QAAb4N,EAAoB,CACtB,MAAMnD,EAAmBzD,EACvBhH,EACApC,EACAiK,GAIF,OAAO4C,EACH,CAAC,CAAEoB,IAAK,uBAAwBhM,MAAO4K,IACvC,EACN,CAEA,GAAiB,OAAbmD,GAAqB/F,EAAWzH,QAAU,EAAG,CAC/C,MAAMzC,EAAQ8L,EAAqBzJ,EAAS6H,GAC5C,OAAOlK,EAAQ,CAAC,CAAEkO,IAAK,cAAehM,MAAOlC,IAAW,EAC1D,CAEA,MAAO,IA+cH0T,CAA6BrR,EAASpC,EAAOmH,EAAU+L,IAItC,SAAjBA,EACKnD,EACLC,EACAqC,GAAwBjQ,EAASpC,EAAOmH,GAAU,IAIjC,UAAjB+L,EACKnD,EAAgBC,EAxFU,EAAC5N,EAAkBpC,KACtD,MAAMmK,EAAU,GACVuJ,EAAMtR,EAAQQ,QAAQC,cAGtB8Q,EADa3P,MAAMsC,KAAKtG,EAAMqP,iBAAiBqE,IAC9BlP,QAAQpC,GAAW,EAE1C,GAAIuR,GAAO,EAAG,MAAO,GAGrBxJ,EAAQhB,KAAK,CACX8E,IAAK,iBACLhM,MAAO,MAAMyR,MAAQC,OAIvB,IAAIzN,EAA0B9D,EAAQ+D,cAEtC,KAAOD,GAAWA,IAAYlG,EAAM2F,MAAM,CACxC,GAAIO,EAAQiL,IAAMjL,EAAQkL,UAAW,CACnC,MAAMwC,EAAc1N,EAAQiL,GACxBxE,EAAgBzG,EAAS,KAAMA,EAAQiL,IACvCxE,EAAgBzG,EAAS,QAASA,EAAQkL,UAAUyC,YAIxD,IAAK,MAAMC,KAAcF,EAAa,CACpC,MACMG,EADoB/P,MAAMsC,KAAKJ,EAAQmJ,iBAAiBqE,IAC1BlP,QAAQpC,GAAW,EAEvD,GAAI2R,EAAY,EAAG,CACjB5J,EAAQhB,KAAK,CACX8E,IAAK,iBACLhM,MAAO,IAAI6R,MAAeJ,MAAQK,OAEpC,KACF,CACF,CAEA,GAAI5J,EAAQ3H,OAAS,EACnB,KAEJ,CAEA0D,EAAUA,EAAQC,aACpB,CAEA,OAAOgE,GAyC4B6J,CAAsB5R,EAASpC,IAG7C,QAAjBkT,EACKnD,EAAgBC,EAAU,CAC/B,CAAE/B,IAAK,iBAAkBhM,MAAOiL,EAAgB9K,EAASpC,MAItD,IAu0BF,MA8CMoT,GAAe,CAC1BhR,EACApC,EACA+H,EACAZ,KAEA,GAA0C,KAArC/E,EAAQyB,aAAe,IAAIjC,OAAc,CAC5C,MAAM2C,EAAOZ,EAAevB,GAE5B,GAAImC,EACF,OAAOsD,EAAiBzF,EAASpC,EAAO,IAAKuE,EAAMwD,EAASZ,EAEhE,GAGI8M,GAAwB,CAC5BhK,EACAiK,EACAlU,EACA+H,EACAZ,KAEA,MAAMgN,EAAkBlK,EACxB,IACEkK,EAAgBzS,IAAK8H,IACnB,GAAoB,cAAdA,EAAKnC,MAAsC,UAAdmC,EAAKnC,MAGlCJ,EAAuBuC,EAAM0K,GAAwB,CACvD,MAAME,EA3EoB,EAClC5K,EACA0K,EACAlU,EACA+H,EACAZ,KAEA,IAAIoF,EAIJ,GAFAA,EAAW/C,EAAKnC,KAEXJ,EAAuBuC,EAAM0K,GAalC,OATYrM,EACVqM,EACAlU,EACA,IAAIuM,IACJ/C,EAAKvH,MACL8F,EACAZ,IAsDmBkN,CACX7K,EACA0K,EACAlU,EACA+H,EACAZ,GAEEiN,GACF9E,EAAUnG,KAAK,CACb8E,IAAK,YAAYzE,EAAKnC,OAAOU,EAAU,SAAW,KAClD9F,MAAOmS,GAGb,IAIJ,MAAME,EAAWlB,GAAac,EAAalU,EAAO+H,EAASZ,GAQ3D,GAPImN,GACFhF,EAAUnG,KAAK,CACb8E,IAAK,iBAAgBlG,EAAU,SAAW,IAC1C9F,MAAOqS,IAKTH,EAAgB1M,KAAMrF,GAA6B,cAAjBA,EAAQiF,OAC1CJ,EACEkN,GAAiB1M,KAAMrF,GAA6B,cAAjBA,EAAQiF,MAC3C6M,GAGF,CACA,IAAInU,EAjFwB,EAChCqC,EACApC,EACA+H,EACAZ,KAEA,IAAIlF,EAAQG,EAAQgP,UAQpB,GAPqB,iBAAVnP,IACTA,EAAQ,IAEVA,EAAQA,GAAOsB,QAAQ,cAAe,YACtCtB,EAAQkB,EAAuB,QAASlB,GACxCA,EAAQA,GAAOL,OAEXK,GAASgF,EAAuB,CAAEI,KAAM,QAASpF,SAASG,GAC5D,OAAOyF,EAAiBzF,EAASpC,EAAO,SAAUiC,EAAO8F,EAASZ,IAkEpDoN,CAAmBL,EAAalU,EAAO+H,EAASZ,GACxDpH,GACFuP,EAAUnG,KAAK,CACb8E,IAAK,iBACLhM,MAAOlC,GAGb,CAEA,IAAKuP,EAAU9M,QAAU2R,EAAgB3R,OAAS,EAAG,CACnD,MAAMqK,EAAmBzD,EACvB8K,EACAlU,EACAmU,GAGEtH,GACFyC,EAAUnG,KAAK,CACb8E,IAAK,uBACLhM,MAAO4K,GAEb,CAEA,IAAKyC,EAAU9M,OAAQ,CACrB,MAAMgS,EA7XZ,SACEvK,EACAiK,EACAlU,GAGA,MAAM4C,QAAEA,GAAYsR,EACdrQ,EAAcqQ,EAAYrQ,YAAYjC,OAC5C,IAAKiC,EACH,OAEF,MAAM4Q,EAAwBhQ,EAAmBZ,GAC3C0E,EAAmBrD,EAAoBuP,GAC7C,GAAIzS,EAAyByS,KAA2BlM,EACtD,OAGF,MAAMgD,EAAmBvJ,EAAyByS,GAC9ClM,EACAkM,EACEhM,EAAyB/D,EAC7BwP,EACA3I,GAEF,IAAK,MAAMgB,KAAYtC,EACrB,GAAIhD,EAAuBsF,EAAU2H,GAAwB,CAC3D,IAAIxK,EAAYvG,EAAuBoJ,EAASlF,KAAMkF,EAAS5C,WAC/D,MAQM5J,EAAQ,KAAK6C,MARC2J,EAASlF,SAQkBqC,UAPzBjB,IAElBzG,EAAyByS,GACvB,+BAA+BhS,EAAiB8I,MAChD,KAAKrJ,KAAK2B,GACR,sBAAsBpB,EAAiB8I,KACvC,UAAU9I,EAAiB8I,SAEnC,GAAIxL,GAEW,GADC+D,EAAgB/D,EAAOmU,EAAalU,GAEhD,OAAOD,CAGb,CAEJ,CAiV4B2U,CACpBzK,EACAiK,EACAlU,GAGEwU,GACFlF,EAAUnG,KAAK,CACb8E,IAAK,yBACLhM,MAAOuS,GAEb,CACF,CAAE,MAAOnQ,GACPC,QAAQ2G,IAAI5G,EACd,GAGWsQ,GAAW,CACtBvS,EACAsF,EACAK,EACAZ,EACAyN,EAA6B,GAC7BC,EAAuB,MAEvBvF,EAAY,GACZhL,QAAQ2G,IAAI7I,GACZ,MAAM8R,EAAc9R,EACd0S,EAAWZ,GAAanI,gBACxBgJ,EAAiBD,GAAU3U,WAAayN,KAAKC,uBAC7C7N,EACJ+U,EAAiBD,EAAWZ,GAAa9T,eAAiBsH,EAEtDgM,EAAMQ,EAAYtR,SAClBqH,WAAEA,GAAeiK,EACjBc,EACJJ,EAAmBpS,OAAS,EAAIoS,EAAqB5Q,MAAMsC,KAAK2D,GAGlE,GAFAgK,GAAsBe,EAAiBd,EAAalU,EAAO+H,EAASZ,GAEhE0N,EAAWrS,OAAQ,CACrB,MAAMyS,EAAiBJ,EAAW/I,QAASkE,GACzCgD,GAAoBkB,EAAalU,EAAOmH,EAAU6I,IASpD,OANAV,EA9lD0B,CAACW,IAC7B,MAAM7D,EAAO,IAAI9K,IAEjB,OAAO2O,EAAQpO,OAAQqO,MAChBA,GAAOjO,OAASmK,EAAKtK,IAAIoO,EAAMjO,SAIpCmK,EAAKrC,IAAImG,EAAMjO,OACR,MAqlDKiT,CACVD,EAAepT,OACZ9B,GAA+D,IAArD+D,EAAgB/D,EAAMkC,MAAOiS,EAAalU,KAIlDmQ,EAA4Bb,EAAWvH,EAChD,CAGE,GAAIuH,EAAU9M,OAAQ,CACpB,MAAM2S,EAAM7F,EAAU9M,OACtB,IAAK,IAAI0G,EAAI,EAAGA,EAAIiM,EAAKjM,IAAK,CAC5B,IAAIkL,EAAO9E,EAAUpG,GAAGjH,MACxBmS,EAAO,MAAQA,EAAKgB,UAAUhB,EAAK5P,QAAQ,MAAQ,EAAIkP,EAAIlR,QAE7C,IADAsB,EAAgBsQ,EAAMhS,EAASpC,IAE3CsP,EAAUnG,KAAK,CACb8E,IAAK,GAAGqB,EAAUpG,GAAG+E,YACrBhM,MAAOmS,GAGb,CACF,CAOF,OAJK9E,EAAU9M,QAAWuS,IACxBzF,EAAY+C,GAAwB6B,EAAalU,EAAOmH,EAAUY,IAG7DoI,EAA4Bb,EAAWvH,IC7rDhD,IAAI3G,GAAgC,GAEpC,MAIMiU,GAAsB,CAC1BjG,EACAkG,EACAlQ,KAEA,IACE,MAAMmQ,EAAUnQ,EAAKiK,iBAAiBD,GACtC,OAA0B,IAAnBmG,EAAQ/S,QAAgB+S,EAAQ,KAAOD,CAChD,CAAE,MACA,OAAO,CACT,GAGWE,GAAoB,CAC/BF,EACAG,EAAqB,YAErB,MAAMC,EAA8C,GAC9CtQ,EAAOkQ,EAAGvJ,cAEhB,IACE,MAAM4J,EAASC,GAAaN,GAC5B,GAAIK,GAAUN,GAAoBM,EAAQL,EAAIlQ,KAC5CsQ,EAAUvM,KAAK,CAAE8E,IAAK,oBAAqBhM,MAAO0T,IACrC,WAATF,GAAmB,OAAOC,EAEhC,MAAMG,EAAYC,GAAgBR,GAC9BO,GAAaR,GAAoBQ,EAAWP,EAAIlQ,IAClDsQ,EAAUvM,KAAK,CAAE8E,IAAK,uBAAwBhM,MAAO4T,IAEvD,MAAME,EAAWC,GAAoBV,GACjCS,GACFL,EAAUvM,KAAK,CAAE8E,IAAK,sBAAuBhM,MAAO8T,GAOxD,CAAE,MAAOE,GACP3R,QAAQD,MAAM4R,EAChB,CACA,OAAOP,GAGIE,GAAgBN,IAC3B,MAAMY,EAAOZ,EAAGlV,eAAe2F,YAC/B,KAAKmQ,GAAUZ,aAAcY,EAAKlQ,SAAU,OAC5C,MAAMpD,EAAU0S,EAAG1S,QAAQC,cAC3B,GAAID,EAAQuB,SAAS,UAAYvB,EAAQuB,SAAS,UAAW,OAE7D,MAAM8B,EAAO,GACb,KAAOqP,GAAInV,WAAayN,KAAKuI,cAAc,CACzC,IAAI/G,EAAWkG,EAAGc,UAAUvT,cAC5B,GAAIyS,EAAGnE,KAAO3N,EAAc8R,EAAGnE,IAAK,CAClC/B,GAAY,IAAIiH,IAAIC,OAAOhB,EAAGnE,MAC9BlL,EAAKI,QAAQ+I,GACb,KACF,CAAO,CACL,IAAImH,EAAMjB,EACNkB,EAAM,EACV,GAAID,EAAI1F,uBACN,KAAQ0F,EAAMA,EAAI1F,wBACZ0F,EAAIH,UAAUvT,gBAAkBuM,GAAUoH,IAItC,IAARA,IACFpH,GAAY,gBAAgBoH,MAGlB,IAARA,GAAaD,GAAKpQ,eAAesQ,kBAAqB,IACxDrH,GAAY,cAAcoH,KAE9B,CACAvQ,EAAKI,QAAQ+I,GACbkG,EAAKA,EAAGnP,aACV,CACA,OAAOF,EAAK3C,KAAK,QAGboT,GAAiB,IAAIpV,IAAI,CAAC,KAAM,QAAS,UAC/C,SAASqV,GAAsBrB,GAC7B,OAAOtR,MAAMsC,KAAKgP,EAAGrL,YAClBpI,OACE2H,IACEkN,GAAe5U,IAAI0H,GAAMnC,MAAMxE,gBAChC2G,EAAKvH,QACJuB,EAAcgG,EAAKvH,QAEvBP,IAAK8H,IAAS,UAAIA,EAAKnC,SA9FKpF,EA8F4BuH,EAAKvH,MA7FzD2U,OAAO3U,GAAOsB,QAAQ,MAAO,QAAQA,QAAQ,KAAM,WAD5B,IAACtB,GA+FjC,CAEO,MAAM+T,GAAuBV,IAClC,MAAMY,EAAOZ,EAAGlV,eAAe2F,YAC/B,KAAKmQ,GAAUZ,aAAcY,EAAKlQ,SAAU,OAE5C,MAAMZ,EAAOkQ,EAAGvJ,cACV2H,EAAM4B,EAAG1S,QAAQC,cAEvB,GAAY,UAAR6Q,GAA2B,WAARA,EAAkB,OAEzC,MAAMmD,EAAgBF,GAAsBrB,GAE5C,IAAK,MAAMwB,KAAgBD,EAAe,CACxC,MAAM/Q,EAAY,GAAG4N,IAAMoD,IAE3B,GAAIzB,GAAoBvP,EAAWwP,EAAIlQ,GACrC,OAAOU,CAEX,GAIWgQ,GAAmBR,IAC9B,MAAMY,EAAOZ,EAAGlV,eAAe2F,YAC/B,KAAKmQ,GAAUZ,aAAcY,EAAKlQ,SAAU,OAC5C,MAAMpD,EAAU0S,EAAG1S,QAAQC,cAC3B,GAAID,EAAQuB,SAAS,UAAYvB,EAAQuB,SAAS,UAAW,OAE7D,MAAM8B,EAAO,GACb,KAAOqP,GAAInV,WAAayN,KAAKuI,cAAc,CACzC,IAAI/G,EAAWkG,EAAGc,UAAUvT,cAE5B,GAC0B,iBAAjByS,EAAGlE,YACVkE,EAAGlE,WACF5N,EAAc8R,EAAGlE,YACjBhQ,IAA2BqG,KACzBF,GACCA,EAAEnF,UAAYkT,GAA0B,UAApB/N,EAAEnE,eAUrB,CACL,IAAImT,EAAMjB,EACNkB,EAAM,EACV,GAAID,EAAI1F,uBACN,KAAQ0F,EAAMA,EAAI1F,wBACZ0F,EAAIH,UAAUvT,gBAAkBuM,GAAUoH,IAItC,IAARA,IACFpH,GAAY,gBAAgBoH,MAGlB,IAARA,GAAaD,GAAKpQ,eAAesQ,kBAAqB,IACxDrH,GAAY,cAAcoH,KAE9B,MArBE,GAFAlB,EAAG5E,UAAUqG,OAAO,uBACpBzB,EAAG5E,UAAUqG,OAAO,kBAChBzB,EAAGlE,UAAW,CAChBhC,GAAY,IAAIkG,EAAGlE,UAAUxP,OAAO2B,QAAQ,OAAQ,OACpD0C,EAAKI,QAAQ+I,GACb,KACF,CAkBFnJ,EAAKI,QAAQ+I,GACbkG,EAAKA,EAAGnP,aACV,CACA,OAAOF,EAAK3C,KAAK,QC7Jb0T,GAAwB,CAC5BjX,EACAC,EACAiX,KAEA,MAAMC,EAAa5I,EAAevO,GAElC,GADcoX,GAAoBnX,EAAOkX,KAC3BD,EAAQ,OAAO,EAE7B,GH0hFI,SAA4BlX,GAChC,MAAO,iEAAiEmC,KACtEnC,EAEJ,CG9hFMqX,CAAkBF,GAAa,CACjC,MAAMtW,EAASZ,EAAMa,SACnBqW,EACAlX,EACA,KACAc,YAAYuW,2BACZ,MAGF,IAAK,IAAInO,EAAI,EAAGA,EAAItI,EAAO0W,eAAgBpO,IACzC,GAAItI,EAAO2W,aAAarO,KAAO+N,EAAQ,OAAO,CAElD,CAEA,OAAO,GAGHO,GAA4B,CAChCxX,EACAoP,KAEA,IACE,MAAMqI,EAAQzX,EAAM0X,cAActI,GAClC,GAAIqI,EAAO,OAAOA,CACpB,CAAE,MAAOpT,GAEP,OADAC,QAAQD,MAAM,wBAAyB+K,EAAU/K,GAC1C,IACT,CAEA,OAAOsT,GAAyB3X,EAAM2F,KAAMyJ,IAGxCwI,GAA8B,CAClCxI,EACApP,EACAiX,IAEOO,GAA0BxX,EAAOoP,KAAc6H,EAYlDY,GAAwBC,GACrBA,EAAQzQ,MAAMxE,cAAcsB,SAAS,iBAAkB,EAkB1DwT,GAA2B,CAC/BrC,EACAlG,KAOA,MAAM2I,EAAW/T,MAAMsC,KAAKgP,EAAGjG,iBAAiB,MAEhD,IACE,IAAK,IAAInG,EAAI,EAAGA,EAAI6O,EAASvV,OAAQ0G,IACnC,GAAI6O,EAAS7O,GAAG8O,WAAY,CAC1B,MAAMA,WAAEA,GAAeD,EAAS7O,GAChC,GAAI8O,EAAY,CACd,MAAMC,EAAgBN,GAAyBK,EAAY5I,GAC3D,GAAI6I,EACF,OAAOA,EAET,GAAID,IAAe5I,EAASjL,SAAS,WACnC,OAAO6T,EAAWN,cAActI,EAEpC,CACF,CAEJ,CAAE,MAAO/K,GACPC,QAAQ2G,IAAI5G,EACd,CACA,OAAO,MAGH6T,GAAS9V,GACNA,GAAS+O,IAAM,KAGlBgH,GAAgB/V,GACZA,EAAwBgP,WAAa,KAGzCgH,GAAkBhW,GACfA,EAAQyB,aAAajC,QAAU,KAGlCyW,GAAWjW,IACf,MAAMkW,EAAYlW,EAElB,GAAIkW,EAAUC,aAAa,QAAS,CAGlC,MADa,GADKD,EAAUE,aAAa,WAE1B,IACjB,CACA,OAAO,MAGHC,GAAsB,CAC1B,qBACA,qBACA,UACA,cACA,YACA,QACA,oBACA,SACA,aACA,cAGF,SAAStB,GAAoBnX,EAAiBD,GAC5C,MAAM2Y,EAAS1Y,EAAM+F,YACrB,IAAK2S,EAAQ,OAAO,KAUpB,OARuB,IAAIA,EAAOC,gBACC9X,SACjCd,EACAC,EACA,KACA0Y,EAAO5X,YAAYgN,wBACnB,MAEiBC,eACrB,CAEA,SAAS6K,GACPd,EACAe,EACA7Y,GAEA,GAAI8X,EAAQ3T,SAAS0U,GAAW,CAC9B,MACMC,EAD8BhB,EAAQrW,MAAMoX,GACd,GAAGjX,OACjC8W,EAAS1Y,EAAM+F,YACrB,IAAK2S,EAAQ,OAAO,KACpB,IAAKZ,EAAQ3T,SAAS,WAAY,CAChC,MAAM4U,EAAiB,IAAIL,EAAOC,eAUlC,GAToBI,EAAelY,SACjCiY,EACA9Y,EACA,KACA0Y,EAAO5X,YAAYgN,wBACnB,MAGgCC,gBACf,CASjB,IAAIiL,EACJ,OAT4BD,EAAelY,SACzCiX,EACA9X,EACA,KACA0Y,EAAO5X,YAAYgN,wBACnB,MAE0CC,iBAG1CiL,EAAgBlB,EACTkB,IAEP1U,QAAQD,MAAM,+BAAgCyT,GAC9CkB,EAAgBlB,EACTkB,EAEX,CACE1U,QAAQD,MAAM,4BAA6ByU,EAE/C,CACF,CACA,OAAO,IACT,CAEA,MAAMG,GAAsB,CAC1BC,EACAlZ,KAEA,MAAMmZ,EAAmBnZ,EAAMqP,iBAC7B,4FAGE8J,GACFA,EAAiBzM,QAASgH,IACvBA,EAAgBqD,WAIrB,MAAMqC,EAAgC,IAAI9X,IAC1C,IAAI+X,EAAuB,GAE3B,SAASC,EAAcC,EAAWC,EAA0B,IAC1D,MAAMC,EAAWF,GAAMtX,MACjByX,EAAWF,EAAUvX,OAASsX,GAAMtX,MACpC0X,EAAkB,CACtBtS,KAAMmS,EAAUnS,MAAQkS,GAAMlS,KAC9BuS,KAAMJ,EAAUI,MAAQL,GAAMK,KAC9B3X,MAAOuX,EAAUvX,OAASsX,GAAMtX,MAChC4X,UAAWL,EAAUK,WAAaN,GAAMM,UACxCC,OAAQN,EAAUM,QAAUP,GAAMO,OAClCC,WAAYP,EAAUO,YAAcR,GAAMQ,YAGtCC,EAA4C,MAAvBT,GAAMU,aAEjCN,EAAWM,aAAeD,EACtB,IACAR,EAAUU,eAAe,gBACvBV,EAAUS,aAhMQ,EAC1BE,EACAV,EACAC,IAEKD,GAAaC,GACXD,IAAaC,EAAW,KADI,IA4L3BU,CAAoBT,EAAWtS,KAAMoS,EAAUC,GA6EvD,SAA2BW,GACzB,MAAMpM,EAAM,GAAGoM,EAAIhT,QAAQgT,EAAIpY,QAC1BmX,EAAiBtX,IAAImM,KACxBmL,EAAiBrP,IAAIkE,GACrBoL,EAAclQ,KAAKkR,GAEvB,CAjFEC,CAAkBX,EACpB,CAEA,SAASY,EACPC,EACA1C,EACA1I,GAEA,GAAIyI,GAAqBC,GACvB,OAAON,GAA0BgD,EAAKpL,GACjC,GAAI0I,EAAQzQ,KAAKlD,SAAS,OAASiL,EAASjH,WAAW,KAC5D,OAAOqS,EAAI9C,cAAc,IAAMzI,EAAgBG,IAC1C,GAAI0I,EAAQzQ,KAAKlD,SAAS,cAAgBiL,EAASjH,WAAW,KACnE,OAAOqS,EAAI9C,cAAc,IAAMtI,GAC1B,GAAqB,SAAjB0I,EAAQzQ,KAAiB,CAClC,MAAMoT,EAAWxL,EAAgBG,GACjC,OAAOoL,EAAI9C,cAAc,UAAU+C,MACrC,CAAO,GAAqB,YAAjB3C,EAAQzQ,KACjB,OAAOmT,EAAI9C,cAActI,GACpB,GAAqB,aAAjB0I,EAAQzQ,KACjB,OACErD,MAAMsC,KAAKkU,EAAInL,iBAAiB,MAAM5H,KACnCyG,GAAMA,EAAErK,aAAajC,SAAWwN,IAC9B,KAEF,GAAqB,oBAAjB0I,EAAQzQ,KACjB,OACErD,MAAMsC,KAAKkU,EAAInL,iBAAiB,MAAM5H,KAAMyG,GAC1CA,EAAErK,aAAaM,SAASiL,KACrB,KAEF,IACJ0I,EAAQzQ,KAAKlD,SAAS,WAAYiL,EAASjH,WAAW,OACtD2P,EAAQ8B,KAAK/Q,MAAM,WAcpB,OAAO2R,EAAI9C,cAActI,GAbzB,CACA,MACMkG,EAAK6B,GAAoBqD,EADPlM,EAAec,IAUvC,OAPIkG,GACFgE,EAAcxB,EAAS,CACrB7V,MAAOmN,EACP2K,WAAYnD,OAAOkB,EAAQiC,YAAY5V,SAAS,KAAO,IAAM,MAI1DmR,CACT,CAGF,CAEA,SAASoF,EACP1a,EACA8X,EACA1I,GAEA,MAAMuL,EAAU3a,EAAMqP,iBAAiB,UAEvC,IAAK,MAAMuL,KAAUD,EACnB,IACE,MAAME,EACJD,EAAOE,iBAAmBF,EAAOG,eAAe1a,SAElD,IAAKwa,EAAW,SAEhB,MAAMvF,EAAKiF,EAAeM,EAAW/C,EAAS1I,GAC9C,GAAIkG,EAAI,OAAOA,CACjB,CAAE,MACA,QACF,CAGF,OAAO,IACT,CAWA,MAAM0F,EAAoB,CACxBC,EACArB,EACAG,KAEA,IAAKkB,EAAK,OAAO,KAEjB,IAAIC,EAAUD,EAAIrZ,OAGlB,OAAKsZ,GAAqC,SAA1BA,EAAQrY,eAGxBqY,EAAUA,EAAQ3X,QAAQ,OAAQ,KAAKA,QAAQ,OAAQ,KAGvD2X,EAAUA,EAAQ3X,QAAQ,kBAAmB,MAG7C2X,EAAUA,EAAQ3X,QAAQ,MAAO,KAGjC2X,EAAUA,EAAQ3X,QAChB,sCACA,cAIW,OAATqW,GAA0B,SAATA,IACnBsB,EAAUA,EAAQ3X,QAAQ,QAAS,IAAI3B,QAG5B,UAATgY,GAAmC,MAAfG,GAAuBkB,EAAI9S,WAAW,OAIzD+S,GAAW,YAAYhZ,KAAKgZ,GAAiB,KAE3CA,EALE,MAvBgD,MA+B3DC,EAAU,IAAK,MAAMrD,KAAWoB,EAAOiC,SACrC,IACqBvE,OAAOkB,EAAQiC,YAAc,IAAhD,MACMqB,EAAoBlC,EAAOiC,SAAStZ,OACvCwZ,GAAuB,MAAjBA,EAAEtB,YAGX,GAAIqB,EAAkB5Y,OAAS,EAC7B,IAAK,MAAMsV,KAAWsD,EACpB9B,EAAcxB,GAIlB,MAAMwD,EAAY1E,OAAOkB,EAAQ7V,OAAS6V,EAAQ8B,MAAQ,IAC1D,GACE0B,EAAUnX,SAAS,YACnBmX,EAAUzS,MAAM,YAChByS,EAAUnX,SAAS,MACnBmX,EAAUnX,SAAS,KACnB,CACAmV,EAAcxB,GACd,QACF,CAEA,GAAIoB,EAAOqC,SAASpX,SAAS,KAC3B,MAAMgX,EAGR,IACE,IAAIvX,EAAgC,KACpC,MAAM8R,EAAYoC,EAAQ7V,MAAMR,MAAM,OAEtC,IAAK,MAAM2N,KAAYsG,EAAW,CAChC,IAAK1V,EAAO,CACVsE,QAAQD,MAAM,wBAAyB+K,GACvC,KACF,CAEA,MAAMoM,EAAkBpM,EAASxN,OAkBjC,GAfAgC,EAAgB2W,EAAeva,EAAO8X,EAAS0D,GAG1C5X,IACHA,EAAgB8W,EAAc1a,EAAO8X,EAAS0D,IAI3C5X,IACHA,EAAgB+T,GACd3X,EAAM2F,KACN6V,KAIC5X,EAAe,CAClBU,QAAQD,MAAM,wBAAyBmX,GACvC,KACF,CACF,CAEA,MAAMC,EAAgB,CAACpU,EAAcpF,KACnC,MAAMgM,EAAM,GAAG5G,KAAQpF,IACvB,OAAOmX,EAAiBtX,IAAImM,IAG9B,GAAIrK,EAAe,CACjB,MAAM8X,EAAgBxC,EAAOiC,SAAStZ,OACnCwZ,GAAiB,UAAXA,EAAEhU,MAAoBgU,EAAEpZ,OAE3B0Z,EAAsBzC,EAAOiC,SAAStZ,OACzCwZ,GAAMxD,GAAqBwD,IAAMA,EAAEpZ,OAGtC,IAAK,MAAM2Z,KAAMF,EACX1E,GAAsB4E,EAAG3Z,MAAOjC,EAAO4D,IACzC0V,EAAcsC,EAAI,CAChB3B,aAAc,OAIpB,IAAK,MAAM4B,KAAcF,EAErB/D,GACEiE,EAAW5Z,MACXjC,EACA4D,IAGF0V,EAAcuC,EAAY,CACxB5B,aAAc,OAIpB,MAAM6B,EAAiBzC,EAAcxX,OAClCwZ,GAAiB,UAAXA,EAAEhU,MAAoBgU,EAAEpZ,OAI3B8Z,EAAoB,IAAIza,IAC5Bwa,EAAepa,IAAK6F,GAAMgH,EAAgBhH,EAAEtF,SAGxC+Z,EAA+B,GAC/BC,EAAU/D,GAAMtU,GACtB,GACEqY,IACCR,EAAc,KAAMQ,KACpBzY,EAAcyY,GACf,CACA,MAAMC,EAAShD,EAAOiC,SAAS1T,KAAM4T,GAAiB,OAAXA,EAAEhU,MACzC6H,EAAclP,EAAO,KAAMic,EAASrY,KACtCoY,EAAmB7S,KAAK,MACxBmQ,EAAc4C,EAAQ,CACpB7U,KAAM,KACNuS,KAAM,SACNG,WAAY,IACZ9X,MAAOga,IAGb,CAEA,MAAMrZ,EAAUgB,EAAchB,QAC9B,GAAIA,IAAY6Y,EAAc,UAAW7Y,GAAU,CACjD,MAAMuZ,EAAUjD,EAAOiC,SAAS1T,KAAM4T,GAAiB,YAAXA,EAAEhU,MAC1C6H,EAAclP,EAAO,UAAW4C,EAASgB,KAC3CoY,EAAmB7S,KAAK,WACxBmQ,EAAc6C,EAAS,CACrB9U,KAAM,UACNuS,KAAM,SACNG,WAAY,IACZ9X,MAAOW,IAGb,CAEA,MAAMwZ,EAAYhE,GAAexU,GACjC,GAAIwY,IAAc5Y,EAAc4Y,GAAY,CAC1C,MAAMC,EAAenD,EAAOiC,SAAS1T,KAClC4T,GAAiB,aAAXA,EAAEhU,MAEP6H,EAAclP,EAAO,WAAYoc,EAAWxY,KAC9CoY,EAAmB7S,KAAK,YACxBmQ,EAAc+C,EAAc,CAC1BhV,KAAM,WACNuS,KAAM,SACNG,WAAY,IACZ9X,MAAOma,IAGb,CAEA,MAAME,EAAcjE,GAAQzU,GAC5B,GACE0Y,IACCb,EAAc,OAAQa,KACtB9Y,EAAc8Y,GACf,CACA,MAAMC,EAAWrD,EAAOiC,SAAS1T,KAAM4T,GAAiB,SAAXA,EAAEhU,MAC3C6H,EAAclP,EAAO,OAAQsc,EAAa1Y,KAC5CoY,EAAmB7S,KAAK,QACxBmQ,EAAciD,EAAU,CACtBlV,KAAM,OACNuS,KAAM,SACNG,WAAY,IACZ9X,MAAOqa,IAGb,CAEA,MAAM9a,EAAa2W,GAAavU,GAChC,GACEpC,GACsB,KAAtBA,EAAWI,SACVJ,EAAW2C,SAAS,OACpBsX,EAAc,YAAaja,KAC3BgC,EAAchC,GACf,CACA,MAAMgb,EAAmBtD,EAAOiC,SAAS1T,KACtC4T,GAAiB,cAAXA,EAAEhU,MAEP6H,EAAclP,EAAO,YAAawB,EAAYoC,KAChDoY,EAAmB7S,KAAK,aACxBmQ,EAAckD,EAAkB,CAC9BnV,KAAM,YACNuS,KAAM,SACNG,WAAY,IACZ9X,MAAOT,IAGb,CACAgU,GAAkB5R,EAAe,UAAU8I,QACxC+P,IAEGA,EAAYxa,QACXwZ,EAAc,cAAegB,EAAYxa,QAE1CqX,OAAcoD,EAAW,CACvBrV,KAAM,cACNpF,MAAOwa,EAAYxa,MACnB2X,KAAM,SACNG,WAAY,QAKpB,MACMnF,EADgB5Q,MAAMsC,KAAK1C,EAAcqG,YACNpI,OACtC2H,IAAUwS,EAAmB7X,SAASqF,EAAKnC,OAK9C,IAAIsV,EAAsB,GAC1B,IACEA,EACEhI,GAAS/Q,EAAe5D,GAAO,GAAO,EAAM4U,IAC5C,EACJ,CAAE,MAAOvQ,GACPC,QAAQD,MAAM,qCAAsCA,EACtD,CAEA,GAA6B,IAAzBsY,GAAcna,OAAc,CAC9B,MAAMoa,EAAsBlB,EAAc7Z,OACvC+Z,IAAQ5E,GAAsB4E,EAAG3Z,MAAOjC,EAAO4D,IAGlD,IAAIiZ,EAAa,EAEjB,IAAK,MAAMC,KAAYF,EAAqB,CAC1C,GAAIC,GAAcD,EAAoBpa,OAAQ,MAE9C,MAAMua,EAAkBxO,EAAgBuO,EAAS7a,OACjD,GAAI8Z,EAAkBja,IAAIib,GAAkB,SAE5C,MAAMlU,EAAQ8T,EAAalV,KACxBuV,GAAMA,EAAE/a,OAASsM,EAAgByO,EAAE/a,SAAW8a,GAG7ClU,GAAO5G,QACTqX,EAAcwD,EAAU,CACtBzV,KAAM,QACNpF,MAAO4G,EAAM5G,MACb2X,KAAM,SACNG,WAAY,IACZE,aAAc,MAGhB8B,EAAkBhS,IAAIgT,GACtBF,IAEJ,CACA,GAAIA,EAAaD,EAAoBpa,OACnC,IAAK,MAAM5B,KAAU+b,EAAc,CACjC,GAAIE,GAAcD,EAAoBpa,OAAQ,MAC9C,IAAK5B,EAAOqB,MAAO,SAEnB,MAAMyB,EAAU6K,EAAgB3N,EAAOqB,OACnC8Z,EAAkBja,IAAI4B,KAE1B4V,EAAc1Y,EAAQ,CACpByG,KAAM,QACNpF,MAAOrB,EAAOqB,MACd2X,KAAM,SACNG,WAAY,IACZE,aAAc,MAGhB8B,EAAkBhS,IAAIrG,GACtBmZ,IACF,CAEJ,CACA,IAAK,MAAM/E,KAAWoB,EAAOiC,SAC3B,IACE,IAAK,MAAM8B,KAAO/D,EAAOiC,SACvB,GAAK8B,EAAIhb,MAET,IAAK,MAAM4W,KAAYJ,GACrB,GAAIwE,EAAIhb,MAAMkC,SAAS0U,GAAW,CAChC,MAAMG,EAAgBJ,GACpBqE,EAAIhb,MACJ4W,EACA7Y,GAEF,GAAIgZ,EAAe,CACjBM,EAAc2D,EAAK,CACjB5V,KAAM,QACNpF,MAAO+W,EACPe,WACyB,KAAvBjC,EAAQiC,YACe,OAAvBjC,EAAQiC,WACJjC,EAAQiC,WACR,MAER,KACF,CACF,CAGN,CAAE,MAAO1V,GACPC,QAAQD,MAAM,4BAA6ByT,EAASzT,EACtD,CAEF,GAAIgV,EAAc7W,OAAS,EAAG,CAC5B,MAAM0a,EAAqB,CACzB,CAAE7V,KAAM,KAAMpF,MAAOiW,GAAMtU,IAC3B,CAAEyD,KAAM,OAAQpF,MAAOoW,GAAQzU,IAC/B,CAAEyD,KAAM,YAAapF,MAAOkW,GAAavU,IACzC,CAAEyD,KAAM,UAAWpF,MAAO2B,EAAchB,SACxC,CAAEyE,KAAM,WAAYpF,MAAOmW,GAAexU,KAG5C,IAAK,MAAMkC,KAAaoX,EAAoB,CAC1C,GAAI7D,EAAc7W,OAAS,EAAG,MAE9B,MAAM6E,KAAEA,EAAIpF,MAAEA,GAAU6D,EACnB7D,IACDuB,EAAcvB,IACdwZ,EAAcpU,EAAMpF,IACX,cAAToF,GAAwBpF,EAAMkC,SAAS,MACvC+K,EAAclP,EAAOqH,EAAMpF,EAAO2B,IACpC0V,OAAcoD,EAAW,CACvBrV,OACAuS,KAAM,SACN3X,QACA8X,WAAY,IACZE,aAAc,MAGpB,CAEIZ,EAAc7W,OAAS,GACzBgT,GAAkB5R,EAAe,YAAY8I,QAC1C+P,IACKpD,EAAc7W,OAAS,GACtBia,EAAYxa,QACbwZ,EAAc,cAAegB,EAAYxa,QAE1CiN,EACClP,EACA,cACAyc,EAAYxa,MACZ2B,IAMJ0V,OAAcoD,EAAW,CACvBrV,KAAM,cACNuS,KAAM,SACN3X,MAAOwa,EAAYxa,MACnB8X,WAAY,IACZE,aAAc,QAKxB,CAEA,MAAMkD,EAA0B9D,EAAc3X,IAAK2Y,IAAG,IACjDA,EACHpY,MAAO+Y,EAAkBX,EAAIpY,MAAOoY,EAAIhT,KAAMgT,EAAIN,eAG9CqD,EAAa,CACjB,CACE/V,KAAM,GAAG6R,EAAO7R,OAChBgW,KAAM,GAAGnE,EAAOmE,OAChBzD,KAAM,GAAGV,EAAOU,OAChBuB,SAAUgC,EAAwBtb,OAC/BiW,GAA8B,MAAlBA,GAAS7V,OAAmC,KAAlB6V,EAAQ7V,OAEjDsZ,SAAU,GAAGrC,EAAOqC,WACpB+B,UAAW,GAAGpE,EAAOoE,YACrBC,YAAa,GAAGrE,EAAOqE,cACvBxD,WAAY,GAAGb,EAAOa,aACtByD,OAAQ,GAAGtE,EAAOsE,SAClBC,SAAU,GAAGvE,EAAOuE,WACpBC,WAAY,GAAGxE,EAAOwE,aACtBC,SAAU,GAAGzE,EAAOyE,WACpBC,UAAW,GAAG1E,EAAO0E,YACrBC,YAAa,GAAG3E,EAAO2E,cACvBC,OAAQ,GAAG5E,EAAO4E,WAItB,OAAOV,CACT,CACF,CAAE,MAAO/Y,GACPC,QAAQD,MAAM,4BAA6ByT,EAASzT,GACpD,QACF,CACF,CAAE,MAAOA,GACPC,QAAQD,MAAM,4BAA6ByT,EAASzT,GACpD,QACF,CAEF,OAAO,MCzwBT0Z,eAAeC,KAoDb,MAAO,CAAEC,SAnDQF,MAAOG,IACtB,MAAMC,EAAiB,IAAIC,EAErBC,EAAM,IAAIC,EACdJ,EACG3a,QAAQ,OAAQ,KAChBA,QAAQ,OAAQ,MAChBA,QAAQ,OAAQ,MAChBA,QAAQ,OAAQ,MACnB,CACEgb,mBAAmB,EACnBC,WAAY,cACZC,UAAW,SACXN,oBAIEzF,OAAEA,GAAW2F,EAInBK,OAAOre,SAAWqY,EAAOrY,SAEzBqe,OAAO9Q,KAAO8K,EAAO9K,KAErB8Q,OAAO1Y,QAAU0S,EAAO1S,QAExB0Y,OAAOC,YAAcjG,EAAOiG,YAE5BD,OAAO9W,WAAa8Q,EAAO9Q,WAE3B,MAAMgX,EAASlG,EAAOrC,KAAO,CAAA,EAa7B,OAZKuI,EAAOtI,SACVsI,EAAOtI,OAAUrU,GACf2U,OAAO3U,GAAOsB,QAAQ,kBAAmB,SAG7Cmb,OAAOrI,IAAMuI,EAGbF,OAAO5d,YAAc4X,EAAO5X,YAE5B4d,OAAO/F,eAAiBD,EAAOC,eAExB,CACLkG,SAAUd,MAAOe,GACR7F,GAAoB6F,EAAMpG,EAAOrY,YAMhD"}
|