Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

openrosa-xpath-evaluator

Package Overview
Dependencies
Maintainers
9
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openrosa-xpath-evaluator - npm Package Compare versions

Comparing version 1.5.0 to 2.0.0-beta.0

.eslintignore

50

package.json
{
"name": "openrosa-xpath-evaluator",
"version": "1.5.0",
"version": "2.0.0-beta.0",
"description": "Wrapper for browsers' XPath evaluator with added support for OpenRosa extensions.",
"main": "src/openrosa-xpath.js",
"main": "src/orxe.js",
"scripts": {
"jshint": "jshint src/*.js test/*.js",
"test": "karma start karma.config.js --single-run",
"travis": "npm run jshint && npm test"
"lint": "eslint --ignore-path .gitignore src",
"lint:watch": "esw --ignore-path .gitignore . -w",
"prebuild": "rimraf dist",
"build": "npm run clear && webpack",
"test": "karma start --single-run",
"travis": "npm run lint && npm run test"
},
"author": "Alex",
"license": "MIT",
"license": "Apache-2.0",
"dependencies": {
"node-forge": "^0.9.0"
},
"devDependencies": {
"chai": "^3.5.0",
"jshint": "^2.9.5",
"karma": "^1.7.0",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.0.1",
"karma-mocha": "^0.2.2",
"karma-requirejs": "^0.2.6",
"mocha": "^2.5.3",
"requirejs": "^2.3.4"
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"babel-loader": "^8.0.6",
"chai": "^4.2.0",
"eslint-watch": "^6.0.0",
"karma": "^4.3.0",
"karma-babel-preprocessor": "^8.0.1",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.1",
"karma-firefox-launcher": "^1.2.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.2",
"lodash": "^4.17.15",
"mocha": "^6.2.0",
"puppeteer": "^1.19.0",
"terser-webpack-plugin": "^1.4.1",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7"
}
}

@@ -14,12 +14,39 @@ Openrosa XForms Evaluator

# TODO
Check that UUID generation is correct (random? UUID vX?)
## Getting Started
# Limitations
1. Include with `npm install openrosa-xpath-evaluator --save` or manually download and add [dist/orxe.min.js](https://raw.github.com/medic/openrosa-xpath-evaluator/master/dist/orxe.min.js) file.
Any expression made requesting a node-type result will be delegated to the underlying xpath evaluator.
2. Include orxe.min.js in the \<head> of your HTML document.
NOTE: Make sure HTML document is in strict mode i.e. it has a !DOCTYPE declaration at the top!
Also, the expression parser is currently very basic and will fail for some xpath expressions. Some examples of expressions that are and are not supported follow.
2. Initialize orxe:
```js
// bind XPath methods to document and window objects
// NOTE: This will overwrite native XPath implementation if it exists
orxe.bindDomLevel3XPath();
```
3. You can now use XPath expressions to query the DOM:
```js
var result = document.evaluate(
'//ul/li/text()', // XPath expression
document, // context node
null, // namespace resolver
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE
);
// loop through results
for (var i = 0; i < result.snapshotLength; i++) {
var node = result.snapshotItem(i);
alert(node.nodeValue);
}
```
# External Libraries
This library does not depend on any external libraries.
But the odk digest function can be supported by installing the node-forge library.
## Supported XPath expressions:

@@ -90,6 +117,214 @@

* `(x/y)[1]`
* `today() < (today() + 1)
* `today() < '1970-06-03'
* `today() + 1 < '1970-06-03'
* `today() + 1 > '1970-06-03'
* `../some-path='some-value'`
* `'some-value'=../some-path`
* `/simple/xpath/to/node < today() + 1`
* `"aardvark" < "aligator"`
* `self::node()`
* `namespace::node()`
* `child::node()`
* `descendant::node()`
* `descendant-or-self::node()`
* `parent::node()`
* `following-sibling::node()`
* `preceding-sibling::node()`
* `namespace::node()`
* `preceding-sibling::node()`
* `following::node()`
* `preceding::node()`
* `attribute::node()`
* `boolean('a')`
* `boolean('')`
* `boolean(true())`
* `boolean(false())`
* `boolean(1)`
* `boolean(-1)`
* `boolean(1 div 0)`
* `boolean(0.1)`
* `boolean('0.0001')`
* `boolean(0)`
* `boolean(0.0)`
* `boolean(number(''))`
* `boolean(/xhtml:html)`
* `boolean(/asdf)`
* `boolean(//xhtml:article)`
* `boolean(self::node())`
* `ceiling(-1.55)`
* `ceiling(2.44)`
* `ceiling(0.001)`
* `ceiling(1.5)`
* `id('ComparisonOperatorCaseNodesetNegative5to5')/* < * `
* `lang('en')`
* `lang('EN-us')`
* `attribute::*`
* `namespace::*`
* `child::*`
* `ancestor-or-self::*`
* `namespace::ns2:*`
* `namespace::ns2:ns2`
* `attribute::attrib3`
* `child::node()`
* `child::text()`
* `child::comment()`
* `child::processing-instruction()`
* `child::processing-instruction('custom-process-instruct')`
* `id('FunctionNodesetIdCaseSimple')`
* `last()`
* `xhtml:p[last()]`
* `last(1)`
* `*[position()=last()]`
* `count(xhtml:p)`
* `local-name(namespace::node())`
* `local-name(1, 2)`
* `local-name(1)`
* `namespace-uri(1, 2)`
* `namespace-uri(1)`
* `number(-1.0)`
* `number(1)`
* `number(0.199999)`
* `sum(1, 2)`
* `ceiling(-1.55)`
* `round(-1.55)`
* `sum(self::*)`
* `sum(*)`
* `sum(node())`
* `string('As Df')`
* `string(attribute::node()[1])`
* `string(namespace-uri(/*))`
* `string(namespace::node())`
* `starts-with('a', '')`
* `contains('asdf', 'sd')`
* `substring-before('ab', 'a')`
* `substring-after('aab', 'a')`
* `substring('12345', 2)`
* `string-length('a')`
* `normalize-space(' a')`
* `translate('aabb', 'ab', 'ba')`
* `id('eee40') | id('eee20') | id('eee25') | id('eee10') | id('eee30') | id('eee50')`
* `id('eee40')/attribute::*[1] | id('eee30')`
* `id('nss25')/namespace::*`
* `id('nss40')/namespace::* | id('nss40')/namespace::*`
* `abs(10.5)`
* `area("7.9377 -11.5845 0 0;7.9324 -11.5902 0 0;7.927 -11.5857 0 0;7.925 -11.578 0 0;7.9267 -11.5722 0 0;7.9325 -11.5708 0 0;7.9372 -11.5737 0 0;7.9393 -11.579 0 0;7.9377 -11.5845 0 0")`
* `distance("7.9377 -11.5845 0 0;7.9324 -11.5902 0 0;7.927 -11.5857 0 0;7.925 -11.578 0 0;7.9267 -11.5722 0 0;7.9325 -11.5708 0 0;7.9372 -11.5737 0 0;7.9393 -11.579 0 0;7.9377 -11.5845 0 0")`
* `boolean-from-string('whatever')`
* `checklist(-1, 2, 2>1)`
* `coalesce(/simple/xpath/to/node, "whatever")`
* `coalesce("FIRST", "whatever")`
* `count-non-empty(//xhtml:div[@id="FunctionCountNonEmpty"]/xhtml:div)`
* `count-selected(self::node())`
* `date-time('1970-01-01')`
* `number(date('1970-01-01'))`
* `date('2100-01-02') > now()`
* `today() > ('2012-01-01' + 10)`
* `decimal-date-time("1969-12-31T00:00:00Z")`
* `decimal-date("1969-12-31")`
* `decimal-time("06:60:00.000-07:00")`
* `digest("abc", "MD5", "hex")`
* `ends-with("ba", "a")`
* `false("a")`
* `floor(-1.005)`
* `format-date-time("2001-12-31", "%b %e, %Y")`
* `if(true(), 5, "abc")`
* `if(self::node(), "exists", "does not exist")`
* `int(/simple/xpath/to/node)`
* `int(7.922021953507237e-12)`
* `join(" :: ", //item)`
* `join(" ", "This", "is", "a", "sentence.")`
* `max(/simple/xpath/to/node)`
* `max(self::*)`
* `max(*)`
* `min(/simple/xpath/to/node)`
* `not(true())`
* `not(false())`
* `once("aa")`
* `once(. * 10)`
* `position(..)`
* `position(.)`
* `position(../..)`
* `pow(/simple/xpath/to/node, 0)`
* `pow(2.5, 2)`
* `random()`
* `randomize(//xhtml:div[@id="FunctionRandomize"]/xhtml:div, 'a')`
* `regex(/simple/xpath/to/node, "[0-9]{3}")`
* `round("-50.55", "-2")`
* `selected-at('zero one two three', '4')`
* `selected("apple baby crimson", " baby ")`
* `substr(/simple/xpath/to/node, 5)`
* `sum(*)`
* `sum(self::*)`
* `sum(node())`
* `sum(*)`
* `sum(/root/item)`
* `sin(2)`
* `cos(2)`
* `tan(2)`
* `acos(0.5)`
* `asin(0.5)`
* `atan(0.5)`
* `log(2)`
* `log10("a")`
* `pi()`
* `exp(2)`
* `exp10(2)`
* `sqrt(4)`
* `uuid()`
* `uuid(6)`
* `weighted-checklist(-1, 2, 2>1, 2)`
## Unsupported XPath expressions:
(Add any examples of known-unsupported expressions here and to `test/extended-xpath.spec.js`.)
## Support for custom functions:
To support custom functions, this library can be extended with the following.
```
orxe.customXPathFunction.add('comment-status', function(a) {
if(arguments.length !== 1) throw new Error('Invalid args');
const curValue = a.v[0]; // {t: 'arr', v: [{'status': 'good'}]}
const status = JSON.parse(curValue).status;
return new orxe.customXPathFunction.type.StringType(status);
});
```
The arguments passed to the custom function (string, number, xpath) will determine the
arguments passed by the library to the function implementation.
The argument format will be any of these:
```
{t: 'arr', v:[]}
{t: 'num', v:123}
{t: 'str', v:'123'}
```
The return types currently supported are these:
```
orxe.customXPathFunction.type.StringType
orxe.customXPathFunction.type.NumberType
orxe.customXPathFunction.type.BooleanType
orxe.customXPathFunction.type.DateType
```
## Configuration support
The library can be configured with:
```
orxe.config = {
allowStringComparison: false,
includeTimeForTodayString: false,
returnCurrentTimeForToday: false
};
```
#### allowStringComparison (default: false)
This flag allows comparing expressions like this: 'bcd' > 'abc'.
#### includeTimeForTodayString (default: false)
This flag allows the inclusion of time for today() expressions that expect XPathResult.STRING_TYPE.
#### returnCurrentTimeForToday (default: false)
This flag allows time to be considered for today() expressions that expect XPathResult.ANY_TYPE, XPathResult.NUMBER_TYPE, etc.

@@ -0,1 +1,10 @@

var config = require('./config');
var shuffle = require('./utils/shuffle');
var {isNamespaceExpr, handleNamespaceExpr} = require('./utils/ns');
var {handleOperation} = require('./utils/operation');
var {isNativeFunction, preprocessNativeArgs} = require('./utils/native');
var {DATE_STRING, dateToDays} = require('./utils/date');
var {toNodes, toSnapshotResult} = require('./utils/result');
var {inputArgs, preprocessInput} = require('./utils/input');
var xpr = require('./xpr');
/*

@@ -16,5 +25,13 @@ * From http://www.w3.org/TR/xpath/#section-Expressions XPath infix

var FUNCTION_NAME = /^[a-z]/;
var NUMERIC_COMPARATOR = /(>|<)/;
var BOOLEAN_COMPARATOR = /(=)/;
var BOOLEAN_FN_COMPARATOR = /(true\(\)|false\(\))/;
var COMPARATOR = /(=|<|>)/;
var INVALID_ARGS = new Error('invalid args');
var TOO_MANY_ARGS = new Error('too many args');
var TOO_FEW_ARGS = new Error('too few args');
// TODO remove all the checks for cur.t==='?' - what else woudl it be?
var ExtendedXpathEvaluator = function(wrapped, extensions) {
var ExtendedXPathEvaluator = function(wrapped, extensions) {
var

@@ -34,3 +51,3 @@ extendedFuncs = extensions.func || {},

},
toExternalResult = function(r) {
toExternalResult = function(r, rt) {
if(extendedProcessors.toExternalResult) {

@@ -40,11 +57,65 @@ var res = extendedProcessors.toExternalResult(r);

}
// returns promise
if(r.v && typeof r.v.then === 'function' && rt === XPathResult.STRING_TYPE) {
return {resultType: XPathResult.STRING_TYPE, stringValue: r.v};
}
if((r.t === 'arr' && rt === XPathResult.NUMBER_TYPE && DATE_STRING.test(r.v[0])) ||
(r.t === 'str' && rt === XPathResult.NUMBER_TYPE && DATE_STRING.test(r.v))) {
var val = r.t === 'arr' ? r.v[0] : r.v;
var days = dateToDays(val);
return {
resultType:XPathResult.NUMBER_TYPE,
numberValue:days,
stringValue:days
};
}
if(r.t === 'num') return { resultType:XPathResult.NUMBER_TYPE, numberValue:r.v, stringValue:r.v.toString() };
if(r.t === 'bool') return { resultType:XPathResult.BOOLEAN_TYPE, booleanValue:r.v, stringValue:r.v.toString() };
if(r.t === 'bool')return { resultType:XPathResult.BOOLEAN_TYPE, booleanValue:r.v, stringValue:r.v.toString() };
if(rt > 3) {
r = shuffle(r[0], r[1]);
return toSnapshotResult(r, XPathResult.UNORDERED_SNAPSHOT_TYPE);
}
if(!r.t && Array.isArray(r)) {
if(rt === XPathResult.NUMBER_TYPE) {
var v = parseInt(r[0].textContent);
return { resultType:XPathResult.NUMBER_TYPE, numberValue:v, stringValue:v.toString() };
} else if(rt === XPathResult.STRING_TYPE) {
return { resultType:XPathResult.STRING_TYPE, stringValue: r.length ? r[0] : '' };
}
}
return { resultType:XPathResult.STRING_TYPE, stringValue: r.v===null ? '' : r.v.toString() };
},
callFn = function(name, args) {
callFn = function(name, args, rt) {
if(extendedFuncs.hasOwnProperty(name)) {
// if(rt && (/^(date|true|false|now$|today$|randomize$)/.test(name))) args.push(rt);
if(rt && (/^(date|now$|today$|randomize$)/.test(name))) args.push(rt);
if(/^(true$|false$)/.test(name)) args.push(rt || XPathResult.BOOLEAN_TYPE);
return callExtended(name, args);
}
return callNative(name, args);
if(name === 'normalize-space' && args.length) {
var res = args[0].v;
res = res.replace(/\f/g, '\\f');
res = res.replace(/\r\v/g, '\v');
res = res.replace(/\v/g, '\\v');
res = res.replace(/\s+/g, ' ');
res = res.replace(/^\s+|\s+$/g, '');
res = res.replace(/\\v/g, '\v');
res = res.replace(/\\f/g, '\f');
return {t: 'str', v: res};
}
if(name === 'string' && args.length > 0 && (
args[0].v === Number.POSITIVE_INFINITY ||
args[0].v === Number.NEGATIVE_INFINITY ||
args[0].v !== args[0].v )) {//NaN
return { t:'str', v: args[0].v };
}
return callNative(name, preprocessNativeArgs(name, args));
},

@@ -85,5 +156,55 @@ callExtended = function(name, args) {

*/
this.evaluate = function(input, cN, nR, rT, r) {
if(rT > 3) return wrapped(input, cN, nR, rT, r); // we don't try to handle node expressions
this.evaluate = function(input, cN, nR, rT) {
input = preprocessInput(input, rT);
if(isNamespaceExpr(input)) return handleNamespaceExpr(input, cN);
if(isNativeFunction(input)) {
var args = inputArgs(input);
if(args.length && args[0].length && !isNaN(args[0])) { throw INVALID_ARGS; }
if(input === 'lang()') throw TOO_FEW_ARGS;
if(/^lang\(/.test(input) && cN.nodeType === 2) cN = cN.ownerElement;
const res = wrapped(input, cN);
if(rT === XPathResult.NUMBER_TYPE &&
(res.resultType === XPathResult.UNORDERED_NODE_ITERATOR_TYPE ||
res.resultType === XPathResult.UNORDERED_NODE_ITERATOR_TYPE)) {
var val = parseInt(res.iterateNext().textContent);
return {
resultType: XPathResult.NUMBER_TYPE,
numberValue: val,
stringValue: val
};
}
return res;
}
if((rT > 3 && !input.startsWith('randomize')) ||
/^count\(|boolean\(/.test(input)) {
if(input.startsWith('count(')) {
if(input.indexOf(',') > 0) throw TOO_MANY_ARGS;
if(input === 'count()') throw TOO_FEW_ARGS;
if(!isNaN(/\((.*)\)/.exec(input)[1])) throw INVALID_ARGS;//firefox
}
if(input.startsWith('boolean(')) { //firefox
if(input === 'boolean()') throw TOO_FEW_ARGS;
var bargs = input.substring(8, input.indexOf(')')).split(',');
if(bargs.length > 1) throw TOO_MANY_ARGS;
}
if(input === '/') cN = cN.ownerDocument || cN;
return wrapped(input, cN);
}
if(rT === XPathResult.BOOLEAN_TYPE && input.indexOf('(') < 0 &&
input.indexOf('/') < 0 && input.indexOf('=') < 0 &&
input.indexOf('!=') < 0) {
input = input.replace(/(\n|\r|\t)/g, '');
input = input.replace(/"(\d)"/g, '$1');
input = input.replace(/'(\d)'/g, '$1');
input = "boolean-from-string("+input+")";
}
if(rT === XPathResult.NUMBER_TYPE && input.indexOf('string-length') < 0) {
input = input.replace(/(\n|\r|\t)/g, '');
}
var i, cur, stack = [{ t:'root', tokens:[] }],

@@ -106,18 +227,3 @@ peek = function() { return stack[stack.length-1]; },

}
switch(op.v) {
case '+': return lhs.v + rhs.v;
case '-': return lhs.v - rhs.v;
case '*': return lhs.v * rhs.v;
case '/': return lhs.v / rhs.v;
case '%': return lhs.v % rhs.v;
case '=': return lhs.v == rhs.v;
case '<': return lhs.v < rhs.v;
case '>': return lhs.v > rhs.v;
case '<=': return lhs.v <= rhs.v;
case '>=': return lhs.v >= rhs.v;
case '!=': return lhs.v != rhs.v;
case '&': return lhs.v && rhs.v;
case '|': return lhs.v || rhs.v;
}
return handleOperation(lhs, op, rhs, config);
},

@@ -139,3 +245,2 @@ evalOpAt = function(tokens, opIndex) {

tokens = peek().tokens;
for(j=OP_PRECEDENCE.length-1; j>=0; --j) {

@@ -152,3 +257,18 @@ ops = OP_PRECEDENCE[j];

handleXpathExpr = function() {
var evaluated = toInternalResult(wrapped(cur.v));
var expr = cur.v;
var evaluated;
if(['position'].includes(peek().v)) {
evaluated = wrapped(expr);
} else {
if(rT > 3 || (cur.v.indexOf('position()=') >= 0 &&
stack.length === 1 && !/^[a-z]*[(|[]{1}/.test(cur.v))) {
evaluated = toNodes(wrapped(expr));
} else {
if(expr.startsWith('$')) {
evaluated = expr;
} else {
evaluated = toInternalResult(wrapped(expr, cN));
}
}
}
peek().tokens.push(evaluated);

@@ -190,6 +310,9 @@ newCurrent();

}
if (cur.t === 'num') {
if(DIGIT.test(c)) {
if(cur.t === 'num') {
if(DIGIT.test(c) || ['e', '"', "'"].includes(c) ||
(c === '-' && input[i-1] === 'e')) {
cur.string += c;
continue;
} else if(c === ' ' && cur.string === '-') {
continue;
} else if(c === '.' && !cur.decimal) {

@@ -201,5 +324,5 @@ cur.decimal = 1;

if(isNum(c)) {
if(cur.v === '') {
cur = { t:'num', string:c };
} else cur.v += c;
if(cur.v === '') {
cur = { t:'num', string:c };
} else cur.v += c;
} else switch(c) {

@@ -216,3 +339,9 @@ case "'":

stack.push(cur);
if(cur.v === 'once') {
newCurrent();
cur.v = '.';
handleXpathExpr();
}
newCurrent();
break;

@@ -228,2 +357,3 @@ case ')':

}
if(cur.v !== '') handleXpathExpr();

@@ -234,3 +364,14 @@ backtrack();

if(cur.v) {
peek().tokens.push(callFn(cur.v, cur.tokens));
var expectedReturnType = rT;
if(rT === XPathResult.BOOLEAN_TYPE) {
if(NUMERIC_COMPARATOR.test(input) && !BOOLEAN_FN_COMPARATOR.test(input)) expectedReturnType = XPathResult.NUMBER_TYPE;
if(BOOLEAN_COMPARATOR.test(input)) expectedReturnType = XPathResult.BOOLEAN_TYPE;
if(COMPARATOR.test(input) && cur.t === 'fn' && /^(date|date-time)$/.test(cur.v)) {
expectedReturnType = XPathResult.STRING_TYPE;
}
}
var res = callFn(cur.v, cur.tokens, expectedReturnType);
if(cur.v === 'node' && res.t === 'arr' && res.v.length > 0)
res.v = [res.v[0]]; // only interested in first element
peek().tokens.push(res);
} else {

@@ -249,2 +390,8 @@ if(cur.tokens.length !== 1) err();

cur.v += c;
if(cur.v === './*') handleXpathExpr();
} else if(cur.v === '' &&
([')', ''].includes(nextChar()) ||
input.substring(i+1).trim() === ')')) {
cur.v = c;
handleXpathExpr();
} else {

@@ -255,9 +402,19 @@ pushOp(c);

case '-':
if(cur.v !== '') {
var prev = prevToken();
if(cur.v !== '' && nextChar() !== ' ' && input.charAt(i-1) !== ' ') {
// function name expr
cur.v += c;
} else if(peek().tokens.length === 0 || prevToken().t === 'op') {
} else if((peek().tokens.length === 0 && cur.v === '') ||
(prev && prev.t === 'op') ||
// two argument function
(prev && prev.t === 'num' && stack.length > 1 && stack[1].t === 'fn') ||
// negative argument
(prev && prev.t !== 'num' && isNum(nextChar()))) {
// -ve number
cur = { t:'num', string:'-' };
} else {
if(cur.v !== '') {
if(!DIGIT.test(cur.v) && input[i-1] !== ' ') throw INVALID_ARGS;
peek().tokens.push(cur);
}
pushOp(c);

@@ -304,3 +461,7 @@ }

case 'or': pushOp('|'); break;
default: if(!FUNCTION_NAME.test(cur.v)) handleXpathExpr();
default: {
var op = cur.v.toLowerCase();
if(/^(mod|div|and|or)$/.test(op)) throw INVALID_ARGS;
if(!FUNCTION_NAME.test(cur.v)) handleXpathExpr();
}
}

@@ -311,2 +472,13 @@ break;

/* falls through */
case '.':
if(cur.v === '' && nextChar() === ')') {
cur.v = c;
handleXpathExpr();
break;
}
if(cur.v === '' && isNum(nextChar())) {
cur = { t:'num', string:c };
break;
}
/* falls through */
default:

@@ -316,7 +488,4 @@ cur.v += c;

}
if(cur.t === 'num') finaliseNum();
if(cur.t === '?' && cur.v !== '') handleXpathExpr();
if(cur.t !== '?' || cur.v !== '' || (cur.tokens && cur.tokens.length)) err('Current item not evaluated!');

@@ -328,11 +497,25 @@ if(stack.length > 1) err('Stuff left on stack.');

if(stack[0].tokens.length > 1) err('Too many tokens.');
return toExternalResult(stack[0].tokens[0], rT);
};
return toExternalResult(stack[0].tokens[0]);
this.customXPathFunction = {
type: {
StringType: xpr.string,
NumberType: xpr.number,
BooleanType: xpr.boolean,
DateType: xpr.date
},
add: function(name, fnObj) {
extendedFuncs[name] = fnObj;
},
remove: function(name) {
delete extendedFuncs[name];
},
all: function() {
return extendedFuncs;
}
};
};
if(typeof define === 'function') {
define(function() { return ExtendedXpathEvaluator; });
} else if(typeof module === 'object' && typeof module.exports === 'object') {
module.exports = ExtendedXpathEvaluator;
}
module.exports = ExtendedXPathEvaluator;

@@ -1,320 +0,294 @@

define(['src/extended-xpath', 'chai', 'lodash'], function(ExtendedXpathEvaluator, chai, _) {
var docs = '',
DATE_MATCH = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d\\d 20\\d\\d \\d\\d:\\d\\d:\\d\\d GMT([+-]\\d\\d\\d\\d \(.+\))?',
examples = {
'false':
/false/,
'true':
/true/,
'"double-string"':
/^double-string$/,
"'single-string'":
/^single-string$/,
'"string(shhh)"':
/^string\(shhh\)$/,
'date()':
new RegExp('^' + DATE_MATCH + '$'),
'upcase("spOtTy")':
/^SPOTTY$/,
'concat("single")':
/^single$/,
'concat("a","b")':
/^ab$/,
'concat("a","b","c")':
/^abc$/,
'concat("a", "b", "c")':
/^abc$/,
'"plus" + "one"':
/^plusone$/,
'"plus" + "one" + "plus" + "two"':
/^plusoneplustwo$/,
'upcase("what") + upcase("ever")':
/^WHATEVER$/,
'downcase("Fox"+"Trot")':
/^foxtrot$/,
'downcase("Fox" + "Trot")':
/^foxtrot$/,
'concat(date())':
new RegExp('^' + DATE_MATCH + '$'),
'concat(date(), "X")':
new RegExp('^' + DATE_MATCH + 'X$'),
'concat("X", date())':
new RegExp('^X' + DATE_MATCH + '$'),
'"Today\'s date: " + date()':
new RegExp('^Today\'s date: ' + DATE_MATCH + '$'),
'concat("Report::", "Today\'s date: " + date())':
new RegExp('^Report::Today\'s date: ' + DATE_MATCH + '$'),
'concat(concat(upcase("Big") + downcase("Little")) + "Average", " by ", concat("Some", " ", "Author"))':
/^BIGlittleAverage by Some Author$/,
'/xpath/expression':
/^<xpath:\/xpath\/expression>$/,
'"string-prefix-" + /xpath/expression':
/^string-prefix-<xpath:\/xpath\/expression>$/,
'/xpath/expression + "-string-suffix"':
/^<xpath:\/xpath\/expression>-string-suffix$/,
'concat("Evaluates to: ", /xpath/expression)':
/^Evaluates to: <xpath:\/xpath\/expression>$/,
'3':
/^3$/,
'3.1416':
/^3.1416$/,
'-3':
/^-3$/,
'-3.1416':
/^-3.1416$/,
'1 + 1':
/^2$/,
'1 - 1':
/^0$/,
'10 div 100':
/^0.1$/,
'random()':
/^(0\.\d+)|(\d\.\d+e-\d)$/,
'random() div 10':
/^(0\.0\d+)|(\d\.\d+e-\d)$/,
'12 mod 5':
/^2$/,
'reverse("hello " + "friend")':
/^dneirf olleh$/,
'reverse("hello ") + reverse("friend")':
/^ ollehdneirf$/,
'native_function()':
/^<xpath:native_function\(\)>$/,
'native_function(3)':
/^<xpath:native_function\(3\)>$/,
'native_function("string-arg")':
/^<xpath:native_function\("string-arg"\)>$/,
'native_function(\'string-with-escaped-"-arg\')':
/^<xpath:native_function\('string-with-escaped-"-arg'\)>$/,
'native_function(1, 2, 3, "a", \'b\', "c")':
/^<xpath:native_function\(1, 2, 3, "a", "b", "c"\)>$/,
'native-function()':
/^<xpath:native-function\(\)>$/,
'native-function(3)':
/^<xpath:native-function\(3\)>$/,
'native-function("string-arg")':
/^<xpath:native-function\("string-arg"\)>$/,
'native-function(1, 2, 3, "a", \'b\', "c")':
/^<xpath:native-function\(1, 2, 3, "a", "b", "c"\)>$/,
'native-function1(native-function2() + native-function3()) + native-function4(native-function5() + native-function6())':
/^<xpath:native-function1\("<xpath:native-function2\(\)><xpath:native-function3\(\)>"\)><xpath:native-function4\("<xpath:native-function5\(\)><xpath:native-function6\(\)>"\)>$/,
'native-function-with-space-before-bracket ()':
/^<xpath:native-function-with-space-before-bracket\(\)>$/,
'3 * 2 + 1':
/^7$/,
'1 + 2 * 3':
/^7$/,
'1 > 0':
/^true$/,
'1 > 1':
/^false$/,
'1 < 1':
/^false$/,
'1 > -1':
/^true$/,
'1 < -1':
/^false$/,
'-1 > 1':
/^false$/,
'-1 < 1':
/^true$/,
'-1 > -1':
/^false$/,
'-1 < -1':
/^false$/,
'-1 < -2':
/^false$/,
const ExtendedXPathEvaluator = require('../src/extended-xpath');
const assert = chai.assert;
var docs = '',
DATE_MATCH = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d\\d 20\\d\\d \\d\\d:\\d\\d:\\d\\d GMT([+-]\\d\\d\\d\\d \(.+\))?',
examples = {
'false':
/false/,
'true':
/true/,
'"double-string"':
/^double-string$/,
"'single-string'":
/^single-string$/,
'"string(shhh)"':
/^string\(shhh\)$/,
'date()':
new RegExp('^' + DATE_MATCH + '$'),
'upcase("spOtTy")':
/^SPOTTY$/,
'concat("single")':
/^single$/,
'concat("a","b")':
/^ab$/,
'concat("a","b","c")':
/^abc$/,
'concat("a", "b", "c")':
/^abc$/,
'"plus" + "one"':
/^plusone$/,
'"plus" + "one" + "plus" + "two"':
/^plusoneplustwo$/,
'upcase("what") + upcase("ever")':
/^WHATEVER$/,
'downcase("Fox"+"Trot")':
/^foxtrot$/,
'downcase("Fox" + "Trot")':
/^foxtrot$/,
'concat(date())':
new RegExp('^' + DATE_MATCH + '$'),
'concat(date(), "X")':
new RegExp('^' + DATE_MATCH + 'X$'),
'concat("X", date())':
new RegExp('^X' + DATE_MATCH + '$'),
'"Today\'s date: " + date()':
new RegExp('^Today\'s date: ' + DATE_MATCH + '$'),
'concat("Report::", "Today\'s date: " + date())':
new RegExp('^Report::Today\'s date: ' + DATE_MATCH + '$'),
'concat(concat(upcase("Big") + downcase("Little")) + "Average", " by ", concat("Some", " ", "Author"))':
/^BIGlittleAverage by Some Author$/,
'/xpath/expression':
/^<xpath:\/xpath\/expression>$/,
'"string-prefix-" + /xpath/expression':
/^string-prefix-<xpath:\/xpath\/expression>$/,
'/xpath/expression + "-string-suffix"':
/^<xpath:\/xpath\/expression>-string-suffix$/,
'concat("Evaluates to: ", /xpath/expression)':
/^Evaluates to: <xpath:\/xpath\/expression>$/,
'3':
/^3$/,
'3.1416':
/^3.1416$/,
'-3':
/^-3$/,
'-3.1416':
/^-3.1416$/,
'1 + 1':
/^2$/,
'1 - 1':
/^0$/,
'10 div 100':
/^0.1$/,
'random()':
/^(0\.\d+)|(\d\.\d+e-\d)$/,
'random() div 10':
/^(0\.0\d+)|(\d\.\d+e-\d)$/,
'12 mod 5':
/^2$/,
'reverse("hello " + "friend")':
/^dneirf olleh$/,
'reverse("hello ") + reverse("friend")':
/^ ollehdneirf$/,
'native_function()':
/^<xpath:native_function\(\)>$/,
'native_function(3)':
/^<xpath:native_function\(3\)>$/,
'native_function("string-arg")':
/^<xpath:native_function\("string-arg"\)>$/,
'native_function(\'string-with-escaped-"-arg\')':
/^<xpath:native_function\('string-with-escaped-"-arg'\)>$/,
'native_function(1, 2, 3, "a", \'b\', "c")':
/^<xpath:native_function\(1, 2, 3, "a", "b", "c"\)>$/,
'native-function()':
/^<xpath:native-function\(\)>$/,
'native-function(3)':
/^<xpath:native-function\(3\)>$/,
'native-function("string-arg")':
/^<xpath:native-function\("string-arg"\)>$/,
'native-function(1, 2, 3, "a", \'b\', "c")':
/^<xpath:native-function\(1, 2, 3, "a", "b", "c"\)>$/,
'native-function1(native-function2() + native-function3()) + native-function4(native-function5() + native-function6())':
/^<xpath:native-function1\("<xpath:native-function2\(\)><xpath:native-function3\(\)>"\)><xpath:native-function4\("<xpath:native-function5\(\)><xpath:native-function6\(\)>"\)>$/,
'native-function-with-space-before-bracket ()':
/^<xpath:native-function-with-space-before-bracket\(\)>$/,
'3 * 2 + 1':
/^7$/,
'1 + 2 * 3':
/^7$/,
'1 > 0':
/^true$/,
'1 > 1':
/^false$/,
'1 < 1':
/^false$/,
'1 > -1':
/^true$/,
'1 < -1':
/^false$/,
'-1 > 1':
/^false$/,
'-1 < 1':
/^true$/,
'-1 > -1':
/^false$/,
'-1 < -1':
/^false$/,
'-1 < -2':
/^false$/,
},
trickyStandardXpath_supported = [
'/model/instance[1]//*',
'/model/instance[1]/*/meta/*',
'./author',
'author',
'first.name',
'/bookstore',
'//author',
'author/first-name',
'bookstore//title',
'bookstore/*/title',
'bookstore//book/excerpt//emph',
'.//title',
'author/*',
'book/*/last-name',
'@style',
'price/@exchange',
'price/@exchange/total',
'book[@style]',
'book/@style',
'./first-name',
'first-name',
'author[1]',
'author[first-name][3]',
'my:book',
'x/y[1]',
'x[1]/y[2]',
'book[excerpt]',
'book[excerpt]/title',
'book[excerpt]/author[degree]',
'book[author/degree]',
'author[degree][award]',
'ancestor::book[1]',
'ancestor::book[author][1]',
'ancestor::author[parent::book][1]',
'../../some-path',
'*/*',
'*[@specialty]',
'@*',
'@my:*',
'my:*',
'author[degree and award]',
'author[(degree or award) and publication]',
'author[degree and not(publication)]',
'author[not(degree or award) and publication]',
'author[. = "Matthew Bob"]',
'author[last-name = "Bob" and ../price &gt; 50]',
'author[not(last-name = "Bob")]',
'author[first-name = "Bob"]',
'author[last-name = "Bob" and first-name = "Joe"]',
'author[* = "Bob"]',
'author[last-name = "Bob"]',
'author[last-name[1] = "Bob"]',
'author[last-name [position()=1]= "Bob"]',
'book[last()]',
'book/author[last()]',
'book[position() &lt;= 3]',
'book[/bookstore/@specialty=@style]',
'degree[position() &lt; 3]',
'degree[@from != "Harvard"]',
'p/text()[2]',
'price[@intl = "Canada"]',
'x/y[position() = 1]',
'(book/author)[last()]',
'(x/y)[1]',
],
trickyStandardXpath_unsupported = [
],
xp = {
str: function(v) { return { t:'str', v:v }; },
num: function(v) { return { t:'num', v:v }; },
},
_document = function(line) {
docs += line + '\n';
},
extendedXPathEvaluator = new ExtendedXPathEvaluator(
function wrappedXpathEvaluator(xpath) {
return { resultType:XPathResult.STRING_TYPE, stringValue:'<xpath:' + xpath + '>' };
},
trickyStandardXpath_supported = [
'/model/instance[1]//*',
'/model/instance[1]/*/meta/*',
'./author',
'author',
'first.name',
'/bookstore',
'//author',
'author/first-name',
'bookstore//title',
'bookstore/*/title',
'bookstore//book/excerpt//emph',
'.//title',
'author/*',
'book/*/last-name',
'@style',
'price/@exchange',
'price/@exchange/total',
'book[@style]',
'book/@style',
'./first-name',
'first-name',
'author[1]',
'author[first-name][3]',
'my:book',
'x/y[1]',
'x[1]/y[2]',
'book[excerpt]',
'book[excerpt]/title',
'book[excerpt]/author[degree]',
'book[author/degree]',
'author[degree][award]',
'ancestor::book[1]',
'ancestor::book[author][1]',
'ancestor::author[parent::book][1]',
'../../some-path',
'*/*',
'*[@specialty]',
'@*',
'@my:*',
'my:*',
'author[degree and award]',
'author[(degree or award) and publication]',
'author[degree and not(publication)]',
'author[not(degree or award) and publication]',
'author[. = "Matthew Bob"]',
'author[last-name = "Bob" and ../price &gt; 50]',
'author[not(last-name = "Bob")]',
'author[first-name = "Bob"]',
'author[last-name = "Bob" and first-name = "Joe"]',
'author[* = "Bob"]',
'author[last-name = "Bob"]',
'author[last-name[1] = "Bob"]',
'author[last-name [position()=1]= "Bob"]',
'book[last()]',
'book/author[last()]',
'book[position() &lt;= 3]',
'book[/bookstore/@specialty=@style]',
'degree[position() &lt; 3]',
'degree[@from != "Harvard"]',
'p/text()[2]',
'price[@intl = "Canada"]',
'x/y[position() = 1]',
'(book/author)[last()]',
'(x/y)[1]',
],
trickyStandardXpath_unsupported = [
],
xp = {
str: function(v) { return { t:'str', v:v }; },
num: function(v) { return { t:'num', v:v }; },
},
_document = function(line) {
docs += line + '\n';
},
extendedXpathEvaluator = new ExtendedXpathEvaluator(
function wrappedXpathEvaluator(xpath) {
return { resultType:XPathResult.STRING_TYPE, stringValue:'<xpath:' + xpath + '>' };
{
func: {
upcase: function(it) { return xp.str(it.v.toUpperCase()); },
downcase: function(it) { return xp.str(it.v.toLowerCase()); },
date: function() { return xp.str(new Date().toString()); },
concat: function() {
var i, acc = '';
for(i=0; i<arguments.length; ++i) acc += arguments[i].v;
return xp.str(acc);
},
random: function() { return xp.num(Math.random()); },
reverse: function(it) { return xp.str(it.v.split('').reverse().join('')); },
},
{
func: {
upcase: function(it) { return xp.str(it.v.toUpperCase()); },
downcase: function(it) { return xp.str(it.v.toLowerCase()); },
date: function() { return xp.str(new Date().toString()); },
concat: function() {
var i, acc = '';
for(i=0; i<arguments.length; ++i) acc += arguments[i].v;
return xp.str(acc);
},
random: function() { return xp.num(Math.random()); },
reverse: function(it) { return xp.str(it.v.split('').reverse().join('')); },
},
}
);
}
);
describe('ExtendedXpathEvaluator', function() {
describe('ExtendedXpathEvaluator', function() {
describe('should delegate to the wrpaped evaluator when', function() {
_.forIn({
UNORDERED_NODE_ITERATOR_TYPE: 4,
RDERED_NODE_ITERATOR_TYPE: 5,
UNORDERED_NODE_SNAPSHOT_TYPE: 6,
ORDERED_NODE_SNAPSHOT_TYPE: 7,
ANY_UNORDERED_NODE_TYPE: 8,
FIRST_ORDERED_NODE_TYPE: 9,
}, function(typeVal, typeName) {
it(typeName + ' is requested', function() {
// given
var wrappedCalls = [],
evaluator = new ExtendedXpathEvaluator(function() {
wrappedCalls.push(Array.prototype.slice.call(arguments));
},
{ func:{} }),
someExpr = '/a/b/c', someContextNode = {}, someNamespaceResolver = {}, someResult = {};
// when
evaluator.evaluate(someExpr, someContextNode, someNamespaceResolver, typeVal, someResult);
// given
assert.deepEqual(wrappedCalls, [[someExpr, someContextNode, someNamespaceResolver, typeVal, someResult]]);
});
});
_.map(examples, function(expected, expr) {
it(expr + ' should be evaluated', function() {
if(typeof expected === 'string') {
assert.equal(
extendedXPathEvaluator.evaluate(expr).stringValue,
expected);
} else {
assert.match(
extendedXPathEvaluator.evaluate(expr).stringValue,
expected);
}
});
});
_.map(examples, function(expected, expr) {
it(expr + ' should be evaluated', function() {
if(typeof expected === 'string') {
assert.equal(
extendedXpathEvaluator.evaluate(expr).stringValue,
expected);
} else {
assert.match(
extendedXpathEvaluator.evaluate(expr).stringValue,
expected);
}
});
describe('Supported XPath expressions', function() {
it('has a documentation header', function() {
_document('## Supported XPath expressions:');
_document('');
});
describe('Supported XPath expressions', function() {
it('has a documentation header', function() {
_document('## Supported XPath expressions:');
_document('');
});
_.each(trickyStandardXpath_supported, function(expr) {
it(expr + ' should be delegated to the regular XPath evaluator', function() {
_document('* `' + expr + '`');
_.each(trickyStandardXpath_supported, function(expr) {
it(expr + ' should be delegated to the regular XPath evaluator', function() {
_document('* `' + expr + '`');
assert.equal(
extendedXpathEvaluator.evaluate(expr).stringValue,
'<xpath:' + expr + '>');
});
assert.equal(
extendedXPathEvaluator.evaluate(expr).stringValue,
'<xpath:' + expr + '>');
});
});
it('has a documentation footer', function() {
_document('');
_document('');
});
it('has a documentation footer', function() {
_document('');
_document('');
});
});
// Documents standard XPath expressions which are not currently supported
describe('Unsupported XPath expressions', function() {
it('has a documentation header', function() {
_document('## Unsupported XPath expressions:');
_document('');
});
// Documents standard XPath expressions which are not currently supported
describe('Unsupported XPath expressions', function() {
it('has a documentation header', function() {
_document('## Unsupported XPath expressions:');
_document('');
});
_.each(trickyStandardXpath_unsupported, function(expr) {
it(expr + ' should not be parsed correctly', function() {
_document('* `' + expr + '`');
_.each(trickyStandardXpath_unsupported, function(expr) {
it(expr + ' should not be parsed correctly', function() {
_document('* `' + expr + '`');
// expect
try {
extendedXpathEvaluator.evaluate(expr);
assert.notEqual(
extendedXpathEvaluator.evaluate(expr).stringValue,
'<xpath:' + expr + '>');
} catch(e) {
if(e.message.indexOf('Too many tokens.') === 0) {
// expected
} else throw e;
}
});
// expect
try {
extendedXPathEvaluator.evaluate(expr);
assert.notEqual(
extendedXPathEvaluator.evaluate(expr).stringValue,
'<xpath:' + expr + '>');
} catch(e) {
if(e.message.indexOf('Too many tokens.') === 0) {
// expected
} else throw e;
}
});
});
});
describe('DOCUMENTATION', function() {
it('supports the following:', function() {
console.log('\n' + docs);
});
describe('DOCUMENTATION', function() {
it('supports the following:', function() {
console.log('\n' + docs);
});
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc