UNPKG

openrosa-xpath-evaluator

Version:

Wrapper for browsers' XPath evaluator with added support for OpenRosa extensions.

84 lines (76 loc) 3.73 kB
const { DATE_STRING, dateToDays, dateStringToDays } = require('./date'); const { toISOLocalString } = require('../date-extensions'); module.exports = { asBoolean, asNumber, asString, }; // cast to boolean, as per https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Boolean-Functions function asBoolean(r) { if (isDomNode(r)) return !!asString(r).trim(); switch (r.t) { case 'arr': return !!r.v.length; case 'date': return !Number.isNaN(Number(r.v)); // TODO should be handled in an extension rather than core code default: return !!r.v; } } // cast to number, as per https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Number-Functions function asNumber(r) { if (r.t === 'num') return r.v; if (r.t === 'bool') return r.v ? 1 : 0; if (r.t === 'date') return dateToDays(r.v); // TODO should be handled in an extension rather than core code const str = asString(r).trim(); if (str === '') return NaN; if (DATE_STRING.test(str)) return dateStringToDays(str); // TODO should be handled in an extension rather than core code return +str; } // cast to string, as per https://www.w3.org/TR/1999/REC-xpath-19991116/#section-String-Functions function asString(r) { if (isDomNode(r)) return nodeToString(r); switch (r.t) { case 'str': return r.v; case 'arr': return r.v.length ? r.v[0].textContent || '' : ''; case 'date': return toISOLocalString(r.v).replace(/T00:00:00.000.*/, ''); // TODO should be handled in an extension rather than core code case 'num': case 'bool': default: return r.v.toString(); } } // Per https://www.w3.org/TR/1999/REC-xpath-19991116/#dt-string-value: // 1. > The string-value of the root node is the concatenation of the string-values of all text node descendants of the root node in document order. // 2. > The string-value of an element node is the concatenation of the string-values of all text node descendants of the element node in document order. // 3. > An attribute node has a string-value. The string-value is the normalized value as specified by the XML Recommendation [XML]. An attribute whose normalized value is a zero-length string is not treated specially: it results in an attribute node whose string-value is a zero-length string. // 4. > The string-value of a namespace node is the namespace URI that is being bound to the namespace prefix; if it is relative, it must be resolved just like a namespace URI in an expanded-name. // 5. > The string-value of a processing instruction node is the part of the processing instruction following the target and any whitespace. // 6. > The string-value of comment is the content of the comment not including the opening <!-- or the closing -->. // 7. > The string-value of a text node is the character data. function nodeToString(node) { switch (node.nodeType) { case Node.DOCUMENT_NODE: return node.documentElement.textContent; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: case Node.PROCESSING_INSTRUCTION_NODE: case Node.COMMENT_NODE: case Node.ELEMENT_NODE: return node.textContent; // potentially not to spec for Element, but much simpler than recursing case Node.ATTRIBUTE_NODE: return node.value; default: throw new Error(`TODO No handling for node type: ${node.nodeType}`); } } /** * Check if an object is a DOM Node, or one of its subtypes (e.g. Element). * @see https://developer.mozilla.org/en-US/docs/Web/API/Node */ function isDomNode(thing) { return thing instanceof Node; }