(target: string, root: Root)
| 103 | const ORDERED_NODE_SNAPSHOT_TYPE = 7 |
| 104 | |
| 105 | function xpathMany(target: string, root: Root): Element[] { |
| 106 | const doc = (root as Document).documentElement ? (root as Document) : (root as Element).ownerDocument! |
| 107 | if (!doc.documentElement) { |
| 108 | return [] |
| 109 | } |
| 110 | try { |
| 111 | // Namespace prefixes require a colon in the XPath expression. Skip the |
| 112 | // expensive full-DOM scan when the expression contains no colon at all. |
| 113 | let resolver: ((prefix: string | null) => string | null) | null = null |
| 114 | if (target.indexOf(':') !== -1) { |
| 115 | const reversedNs: Record<string, string> = {} |
| 116 | const allNodes = doc.getElementsByTagName('*') |
| 117 | for (let i = 0; i < allNodes.length; i++) { |
| 118 | const n = allNodes[i] |
| 119 | const ns = n.namespaceURI |
| 120 | if (ns && !reversedNs[ns]) { |
| 121 | let prefix = n.lookupPrefix(ns) |
| 122 | if (!prefix) { |
| 123 | const m = ns.match('.*/(\\w+)/?$') |
| 124 | prefix = m ? m[1] : 'xhtml' |
| 125 | } |
| 126 | reversedNs[ns] = prefix! |
| 127 | } |
| 128 | } |
| 129 | const namespaces: Record<string, string> = {} |
| 130 | for (const key in reversedNs) { |
| 131 | namespaces[reversedNs[key]] = key |
| 132 | } |
| 133 | resolver = (prefix: string | null): string | null => namespaces[prefix || ''] || null |
| 134 | } |
| 135 | |
| 136 | let result: XPathResult | null = null |
| 137 | try { |
| 138 | result = doc.evaluate(target, root, resolver, ORDERED_NODE_SNAPSHOT_TYPE, null) |
| 139 | } catch (te) { |
| 140 | if ((te as Error).name === 'TypeError') { |
| 141 | const fallback = doc.createNSResolver ? doc.createNSResolver(doc.documentElement!) : DEFAULT_NS_RESOLVER |
| 142 | result = doc.evaluate(target, root, fallback, ORDERED_NODE_SNAPSHOT_TYPE, null) |
| 143 | } else { |
| 144 | throw te |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | if (!result) { |
| 149 | return [] |
| 150 | } |
| 151 | |
| 152 | const results: Element[] = [] |
| 153 | for (let i = 0; i < result.snapshotLength; i++) { |
| 154 | const node = result.snapshotItem(i) |
| 155 | if (!node || node.nodeType !== Node.ELEMENT_NODE) { |
| 156 | throw botError( |
| 157 | INVALID_SELECTOR, |
| 158 | 'The result of the xpath expression "' + target + '" is: ' + node + '. It should be an element.', |
| 159 | ) |
| 160 | } |
| 161 | results.push(node as Element) |
| 162 | } |
no test coverage detected