123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- var aesprim = require('./aesprim');
- var slice = require('./slice');
- var _evaluate = require('static-eval');
- var _uniq = require('underscore').uniq;
- var Handlers = function() {
- return this.initialize.apply(this, arguments);
- }
- Handlers.prototype.initialize = function() {
- this.traverse = traverser(true);
- this.descend = traverser();
- }
- Handlers.prototype.keys = Object.keys;
- Handlers.prototype.resolve = function(component) {
- var key = [ component.operation, component.scope, component.expression.type ].join('-');
- var method = this._fns[key];
- if (!method) throw new Error("couldn't resolve key: " + key);
- return method.bind(this);
- };
- Handlers.prototype.register = function(key, handler) {
- if (!handler instanceof Function) {
- throw new Error("handler must be a function");
- }
- this._fns[key] = handler;
- };
- Handlers.prototype._fns = {
- 'member-child-identifier': function(component, partial) {
- var key = component.expression.value;
- var value = partial.value;
- if (value instanceof Object && key in value) {
- return [ { value: value[key], path: partial.path.concat(key) } ]
- }
- },
- 'member-descendant-identifier':
- _traverse(function(key, value, ref) { return key == ref }),
- 'subscript-child-numeric_literal':
- _descend(function(key, value, ref) { return key === ref }),
- 'member-child-numeric_literal':
- _descend(function(key, value, ref) { return String(key) === String(ref) }),
- 'subscript-descendant-numeric_literal':
- _traverse(function(key, value, ref) { return key === ref }),
- 'member-child-wildcard':
- _descend(function() { return true }),
- 'member-descendant-wildcard':
- _traverse(function() { return true }),
- 'subscript-descendant-wildcard':
- _traverse(function() { return true }),
- 'subscript-child-wildcard':
- _descend(function() { return true }),
- 'subscript-child-slice': function(component, partial) {
- if (is_array(partial.value)) {
- var args = component.expression.value.split(':').map(_parse_nullable_int);
- var values = partial.value.map(function(v, i) { return { value: v, path: partial.path.concat(i) } });
- return slice.apply(null, [values].concat(args));
- }
- },
- 'subscript-child-union': function(component, partial) {
- var results = [];
- component.expression.value.forEach(function(component) {
- var _component = { operation: 'subscript', scope: 'child', expression: component.expression };
- var handler = this.resolve(_component);
- var _results = handler(_component, partial);
- if (_results) {
- results = results.concat(_results);
- }
- }, this);
- return unique(results);
- },
- 'subscript-descendant-union': function(component, partial, count) {
- var jp = require('..');
- var self = this;
- var results = [];
- var nodes = jp.nodes(partial, '$..*').slice(1);
- nodes.forEach(function(node) {
- if (results.length >= count) return;
- component.expression.value.forEach(function(component) {
- var _component = { operation: 'subscript', scope: 'child', expression: component.expression };
- var handler = self.resolve(_component);
- var _results = handler(_component, node);
- results = results.concat(_results);
- });
- });
- return unique(results);
- },
- 'subscript-child-filter_expression': function(component, partial, count) {
- // slice out the expression from ?(expression)
- var src = component.expression.value.slice(2, -1);
- var ast = aesprim.parse(src).body[0].expression;
- var passable = function(key, value) {
- return evaluate(ast, { '@': value });
- }
- return this.descend(partial, null, passable, count);
- },
- 'subscript-descendant-filter_expression': function(component, partial, count) {
- // slice out the expression from ?(expression)
- var src = component.expression.value.slice(2, -1);
- var ast = aesprim.parse(src).body[0].expression;
- var passable = function(key, value) {
- return evaluate(ast, { '@': value });
- }
- return this.traverse(partial, null, passable, count);
- },
- 'subscript-child-script_expression': function(component, partial) {
- var exp = component.expression.value.slice(1, -1);
- return eval_recurse(partial, exp, '$[{{value}}]');
- },
- 'member-child-script_expression': function(component, partial) {
- var exp = component.expression.value.slice(1, -1);
- return eval_recurse(partial, exp, '$.{{value}}');
- },
- 'member-descendant-script_expression': function(component, partial) {
- var exp = component.expression.value.slice(1, -1);
- return eval_recurse(partial, exp, '$..value');
- }
- };
- Handlers.prototype._fns['subscript-child-string_literal'] =
- Handlers.prototype._fns['member-child-identifier'];
- Handlers.prototype._fns['member-descendant-numeric_literal'] =
- Handlers.prototype._fns['subscript-descendant-string_literal'] =
- Handlers.prototype._fns['member-descendant-identifier'];
- function eval_recurse(partial, src, template) {
- var jp = require('./index');
- var ast = aesprim.parse(src).body[0].expression;
- var value = evaluate(ast, { '@': partial.value });
- var path = template.replace(/\{\{\s*value\s*\}\}/g, value);
- var results = jp.nodes(partial.value, path);
- results.forEach(function(r) {
- r.path = partial.path.concat(r.path.slice(1));
- });
- return results;
- }
- function is_array(val) {
- return Array.isArray(val);
- }
- function is_object(val) {
- // is this a non-array, non-null object?
- return val && !(val instanceof Array) && val instanceof Object;
- }
- function traverser(recurse) {
- return function(partial, ref, passable, count) {
- var value = partial.value;
- var path = partial.path;
- var results = [];
- var descend = function(value, path) {
- if (is_array(value)) {
- value.forEach(function(element, index) {
- if (results.length >= count) { return }
- if (passable(index, element, ref)) {
- results.push({ path: path.concat(index), value: element });
- }
- });
- value.forEach(function(element, index) {
- if (results.length >= count) { return }
- if (recurse) {
- descend(element, path.concat(index));
- }
- });
- } else if (is_object(value)) {
- this.keys(value).forEach(function(k) {
- if (results.length >= count) { return }
- if (passable(k, value[k], ref)) {
- results.push({ path: path.concat(k), value: value[k] });
- }
- })
- this.keys(value).forEach(function(k) {
- if (results.length >= count) { return }
- if (recurse) {
- descend(value[k], path.concat(k));
- }
- });
- }
- }.bind(this);
- descend(value, path);
- return results;
- }
- }
- function _descend(passable) {
- return function(component, partial, count) {
- return this.descend(partial, component.expression.value, passable, count);
- }
- }
- function _traverse(passable) {
- return function(component, partial, count) {
- return this.traverse(partial, component.expression.value, passable, count);
- }
- }
- function evaluate() {
- try { return _evaluate.apply(this, arguments) }
- catch (e) { }
- }
- function unique(results) {
- results = results.filter(function(d) { return d })
- return _uniq(
- results,
- function(r) { return r.path.map(function(c) { return String(c).replace('-', '--') }).join('-') }
- );
- }
- function _parse_nullable_int(val) {
- var sval = String(val);
- return sval.match(/^-?[0-9]+$/) ? parseInt(sval) : null;
- }
- module.exports = Handlers;
|