fontoxpath
Version:
A minimalistic XPath 3.1 engine in JavaScript
60 lines (49 loc) • 2.32 kB
JavaScript
import adaptJavaScriptValueToXPathValue from './selectors/adaptJavaScriptValueToXPathValue';
import isSubtypeOf from './selectors/dataTypes/isSubtypeOf';
import functionRegistry from './selectors/functions/functionRegistry';
function adaptXPathValueToJavascriptValue (valueSequence, sequenceType) {
switch (sequenceType[sequenceType.length - 1]) {
case '?':
if (valueSequence.isEmpty()) {
return null;
}
return valueSequence.first().value;
case '*':
case '+':
return valueSequence.getAllValues().map(function (value) {
if (isSubtypeOf(value.type, 'attribute()')) {
throw new Error('Cannot pass attribute nodes to custom functions');
}
return value.value;
});
default:
return valueSequence.first().value;
}
}
/**
* Add a custom test for use in xpath-serialized selectors.
*
* @param {string} name The name of this test, starts with fonto:
* @param {Array<string>} signature The signature of the test, as array of strings (e.g. ['item()', 'node()?', 'xs:numeric'])
* @param {string} returnType The return type of the test, as sequence type (e.g. 'xs:boolean()')
* @param {function(*):*} callback The test itself, which gets the dynamicContext and arguments passed
* @return {undefined}
*/
export default function registerCustomXPathFunction (name, signature, returnType, callback) {
const callFunction = function (dynamicContext) {
// Make arguments a read array instead of a array-like object
const args = Array.from(arguments);
args.splice(0, 1);
const newArguments = args.map(function (argument, index) {
return adaptXPathValueToJavascriptValue(argument, signature[index]);
});
// Adapt the domFacade into another object to prevent passing everything. The closure compiler might rename some variables otherwise.
// Since the interface for domFacade (IDomFacade) is marked as extern, it will not be changed
const dynamicContextAdapter = {};
dynamicContextAdapter['domFacade'] = dynamicContext.domFacade;
const jsResult = callback.apply(undefined, [dynamicContextAdapter].concat(newArguments));
const xpathResult = adaptJavaScriptValueToXPathValue(jsResult, returnType);
return xpathResult;
};
functionRegistry.registerFunction(name, signature, returnType, callFunction);
}