Commit 5dfad22f authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Migrate xpath module to TS

parent 5e58b586
/** /**
* Get the node name for use in generating an xpath expression. * Get the node name for use in generating an xpath expression.
*
* @param {Node} node
*/ */
function getNodeName(node) { function getNodeName(node: Node): string {
const nodeName = node.nodeName.toLowerCase(); const nodeName = node.nodeName.toLowerCase();
let result = nodeName; return nodeName === '#text' ? 'text()' : nodeName;
if (nodeName === '#text') {
result = 'text()';
}
return result;
} }
/** /**
* Get the index of the node as it appears in its parent's child list * Get the index of the node as it appears in its parent's child list
*
* @param {Node} node
*/ */
function getNodePosition(node) { function getNodePosition(node: Node): number {
let pos = 0; let pos = 0;
/** @type {Node|null} */ let tmp: Node | null = node;
let tmp = node;
while (tmp) { while (tmp) {
if (tmp.nodeName === node.nodeName) { if (tmp.nodeName === node.nodeName) {
pos += 1; pos += 1;
...@@ -30,8 +21,7 @@ function getNodePosition(node) { ...@@ -30,8 +21,7 @@ function getNodePosition(node) {
return pos; return pos;
} }
/** @param {Node} node */ function getPathSegment(node: Node): string {
function getPathSegment(node) {
const name = getNodeName(node); const name = getNodeName(node);
const pos = getNodePosition(node); const pos = getNodePosition(node);
return `${name}[${pos}]`; return `${name}[${pos}]`;
...@@ -41,14 +31,13 @@ function getPathSegment(node) { ...@@ -41,14 +31,13 @@ function getPathSegment(node) {
* A simple XPath generator which can generate XPaths of the form * A simple XPath generator which can generate XPaths of the form
* /tag[index]/tag[index]. * /tag[index]/tag[index].
* *
* @param {Node} node - The node to generate a path to * @param node - The node to generate a path to
* @param {Node} root - Root node to which the returned path is relative * @param root - Root node to which the returned path is relative
*/ */
export function xpathFromNode(node, root) { export function xpathFromNode(node: Node, root: Node) {
let xpath = ''; let xpath = '';
/** @type {Node|null} */ let elem: Node | null = node;
let elem = node;
while (elem !== root) { while (elem !== root) {
if (!elem) { if (!elem) {
throw new Error('Node is not a descendant of root'); throw new Error('Node is not a descendant of root');
...@@ -65,12 +54,12 @@ export function xpathFromNode(node, root) { ...@@ -65,12 +54,12 @@ export function xpathFromNode(node, root) {
/** /**
* Return the `index`'th immediate child of `element` whose tag name is * Return the `index`'th immediate child of `element` whose tag name is
* `nodeName` (case insensitive). * `nodeName` (case insensitive).
*
* @param {Element} element
* @param {string} nodeName
* @param {number} index
*/ */
function nthChildOfType(element, nodeName, index) { function nthChildOfType(
element: Element,
nodeName: string,
index: number
): Element | null {
nodeName = nodeName.toUpperCase(); nodeName = nodeName.toUpperCase();
let matchIndex = -1; let matchIndex = -1;
...@@ -99,16 +88,12 @@ function nthChildOfType(element, nodeName, index) { ...@@ -99,16 +88,12 @@ function nthChildOfType(element, nodeName, index) {
* - Is not affected by the document's _type_ (HTML or XML/XHTML) * - Is not affected by the document's _type_ (HTML or XML/XHTML)
* - Ignores element namespaces when matching element names in the XPath against * - Ignores element namespaces when matching element names in the XPath against
* elements in the DOM tree * elements in the DOM tree
* - Is case insensitive for all elements, not just HTML elements * - Is case-insensitive for all elements, not just HTML elements
* *
* The matching element is returned or `null` if no such element is found. * The matching element is returned or `null` if no such element is found.
* An error is thrown if `xpath` is not a simple XPath. * An error is thrown if `xpath` is not a simple XPath.
*
* @param {string} xpath
* @param {Element} root
* @return {Element|null}
*/ */
function evaluateSimpleXPath(xpath, root) { function evaluateSimpleXPath(xpath: string, root: Element): Element | null {
const isSimpleXPath = const isSimpleXPath =
xpath.match(/^(\/[A-Za-z0-9-]+(\[[0-9]+\])?)+$/) !== null; xpath.match(/^(\/[A-Za-z0-9-]+(\[[0-9]+\])?)+$/) !== null;
if (!isSimpleXPath) { if (!isSimpleXPath) {
...@@ -122,7 +107,7 @@ function evaluateSimpleXPath(xpath, root) { ...@@ -122,7 +107,7 @@ function evaluateSimpleXPath(xpath, root) {
// has at least two segments, with the first being empty and the others non-empty. // has at least two segments, with the first being empty and the others non-empty.
segments.shift(); segments.shift();
for (let segment of segments) { for (const segment of segments) {
let elementName; let elementName;
let elementIndex; let elementIndex;
...@@ -156,12 +141,12 @@ function evaluateSimpleXPath(xpath, root) { ...@@ -156,12 +141,12 @@ function evaluateSimpleXPath(xpath, root) {
* *
* Example: * Example:
* node = nodeFromXPath('/main/article[1]/p[3]', document.body) * node = nodeFromXPath('/main/article[1]/p[3]', document.body)
*
* @param {string} xpath
* @param {Element} [root]
* @return {Node|null}
*/ */
export function nodeFromXPath(xpath, root = document.body) { export function nodeFromXPath(
xpath: string,
/* istanbul ignore next */
root: Element = document.body
): Node | null {
try { try {
return evaluateSimpleXPath(xpath, root); return evaluateSimpleXPath(xpath, root);
} catch (err) { } catch (err) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment