Created
October 28, 2012 07:57
-
-
Save balupton/3968010 to your computer and use it in GitHub Desktop.
QueryEngine Performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.4.0 | |
(function() { | |
var Backbone, Criteria, Hash, Pill, Query, QueryCollection, queryEngine, util, _, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
__slice = [].slice; | |
_ = typeof module !== "undefined" && module !== null ? require('underscore') : this._; | |
Backbone = typeof module !== "undefined" && module !== null ? require('backbone') : this.Backbone; | |
util = { | |
isEqual: function(value1, value2) { | |
return _.isEqual(value1, value2); | |
}, | |
toString: function(value) { | |
return Object.prototype.toString.call(value); | |
}, | |
isPlainObject: function(value) { | |
return util.isObject(value) && value.__proto__ === Object.prototype; | |
}, | |
isObject: function(value) { | |
return value && typeof value === 'object'; | |
}, | |
isError: function(value) { | |
return value instanceof Error; | |
}, | |
isDate: function(value) { | |
return util.toString(value) === '[object Date]'; | |
}, | |
isArguments: function(value) { | |
return util.toString(value) === '[object Arguments]'; | |
}, | |
isFunction: function(value) { | |
return util.toString(value) === '[object Function]'; | |
}, | |
isRegExp: function(value) { | |
return util.toString(value) === '[object RegExp]'; | |
}, | |
isArray: function(value) { | |
if (Array.isArray != null) { | |
return Array.isArray(value); | |
} else { | |
return util.toString(value) === '[object Array]'; | |
} | |
}, | |
isNumber: function(value) { | |
return typeof value === 'number' || util.toString(value) === '[object Number]'; | |
}, | |
isString: function(value) { | |
return typeof value === 'string' || util.toString(value) === '[object String]'; | |
}, | |
isBoolean: function(value) { | |
return value === true || value === false || util.toString(value) === '[object Boolean]'; | |
}, | |
isNull: function(value) { | |
return value === null; | |
}, | |
isUndefined: function(value) { | |
return typeof value === 'undefined'; | |
}, | |
isDefined: function(value) { | |
return typeof value !== 'undefined'; | |
}, | |
isEmpty: function(value) { | |
return value != null; | |
}, | |
isComparable: function(value) { | |
return util.isNumber(value) || util.isDate(value); | |
}, | |
safeRegex: function(str) { | |
if (str === false) { | |
return 'false'; | |
} else if (str === true) { | |
return 'true'; | |
} else if (str === null) { | |
return 'null'; | |
} else { | |
return (str || '').replace('(.)', '\\$1'); | |
} | |
}, | |
createRegex: function(str) { | |
return new RegExp(str, 'ig'); | |
}, | |
createSafeRegex: function(str) { | |
return util.createRegex(util.safeRegex(str)); | |
}, | |
toArray: function(value) { | |
var item, key, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
result.push(item); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
toArrayGroup: function(value) { | |
var item, key, obj, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
obj = {}; | |
obj[key] = item; | |
result.push(obj); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
generateComparator: function(input) { | |
var generateFunction; | |
generateFunction = function(comparator) { | |
if (!comparator) { | |
throw new Error('Cannot sort without a comparator'); | |
} else if (util.isFunction(comparator)) { | |
return comparator; | |
} else if (util.isArray(comparator)) { | |
return function(a, b) { | |
var comparison, key, value, _i, _len; | |
comparison = 0; | |
for (key = _i = 0, _len = comparator.length; _i < _len; key = ++_i) { | |
value = comparator[key]; | |
comparison = generateFunction(value)(a, b); | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else if (util.isObject(comparator)) { | |
return function(a, b) { | |
var aValue, bValue, comparison, key, value, _ref, _ref1; | |
comparison = 0; | |
for (key in comparator) { | |
if (!__hasProp.call(comparator, key)) continue; | |
value = comparator[key]; | |
aValue = (_ref = typeof a.get === "function" ? a.get(key) : void 0) != null ? _ref : a[key]; | |
bValue = (_ref1 = typeof b.get === "function" ? b.get(key) : void 0) != null ? _ref1 : b[key]; | |
if (aValue === bValue) { | |
comparison = 0; | |
} else if (aValue < bValue) { | |
comparison = -1; | |
} else if (aValue > bValue) { | |
comparison = 1; | |
} | |
if (value === -1) { | |
comparison *= -1; | |
} | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else { | |
throw new Error('Unknown comparator type'); | |
} | |
}; | |
return generateFunction(input); | |
} | |
}; | |
Hash = (function(_super) { | |
__extends(Hash, _super); | |
Hash.prototype.arr = []; | |
function Hash(value) { | |
var item, key, _i, _len; | |
value = util.toArray(value); | |
for (key = _i = 0, _len = value.length; _i < _len; key = ++_i) { | |
item = value[key]; | |
this.push(item); | |
} | |
} | |
Hash.prototype.hasIn = function(options) { | |
var value, _i, _len; | |
options = util.toArray(options); | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
if (__indexOf.call(options, value) >= 0) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Hash.prototype.hasAll = function(options) { | |
var empty, pass, value, _i, _len; | |
options = util.toArray(options); | |
empty = true; | |
pass = true; | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
empty = false; | |
if (__indexOf.call(options, value) < 0) { | |
pass = false; | |
} | |
} | |
if (empty) { | |
pass = false; | |
} | |
return pass; | |
}; | |
Hash.prototype.isSame = function(options) { | |
var pass; | |
options = util.toArray(options); | |
pass = this.sort().join() === options.sort().join(); | |
return pass; | |
}; | |
return Hash; | |
})(Array); | |
QueryCollection = (function(_super) { | |
__extends(QueryCollection, _super); | |
function QueryCollection() { | |
this.onParentReset = __bind(this.onParentReset, this); | |
this.onParentAdd = __bind(this.onParentAdd, this); | |
this.onParentRemove = __bind(this.onParentRemove, this); | |
this.onParentChange = __bind(this.onParentChange, this); | |
this.onChange = __bind(this.onChange, this); | |
return QueryCollection.__super__.constructor.apply(this, arguments); | |
} | |
QueryCollection.prototype.model = Backbone.Model; | |
QueryCollection.prototype.initialize = function(models, options) { | |
var key, me, value, _ref, _ref1, _ref2; | |
me = this; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
_ref1 = Criteria.prototype; | |
for (key in _ref1) { | |
if (!__hasProp.call(_ref1, key)) continue; | |
value = _ref1[key]; | |
if ((_ref2 = this[key]) == null) { | |
this[key] = value; | |
} | |
} | |
if (this.comparator != null) { | |
this.setComparator(this.comparator); | |
} | |
this.applyCriteria(options); | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.getComparator = function() { | |
return this.comparator; | |
}; | |
QueryCollection.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.comparator = comparator; | |
return this; | |
}; | |
QueryCollection.prototype.createChildCollection = function(models, options) { | |
var collection, _ref, _ref1; | |
options || (options = {}); | |
options.parentCollection = this; | |
if ((_ref = options.collection) == null) { | |
options.collection = this.collection || QueryCollection; | |
} | |
if ((_ref1 = options.comparator) == null) { | |
options.comparator = options.collection.prototype.comparator || this.comparator; | |
} | |
collection = new options.collection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.createLiveChildCollection = function(models, options) { | |
var collection; | |
options || (options = {}); | |
options.live = true; | |
collection = this.createChildCollection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.hasParentCollection = function() { | |
return this.options.parentCollection != null; | |
}; | |
QueryCollection.prototype.getParentCollection = function() { | |
return this.options.parentCollection; | |
}; | |
QueryCollection.prototype.setParentCollection = function(parentCollection, skipCheck) { | |
if (!skipCheck && this.options.parentCollection === parentCollection) { | |
return this; | |
} | |
this.options.parentCollection = parentCollection; | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.hasModel = function(model) { | |
var exists; | |
model || (model = {}); | |
if ((model.id != null) && this.get(model.id)) { | |
exists = true; | |
} else if ((model.cid != null) && this.getByCid(model.cid)) { | |
exists = true; | |
} else { | |
exists = false; | |
} | |
return exists; | |
}; | |
QueryCollection.prototype.safeRemove = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (exists) { | |
this.remove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.safeAdd = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (!exists) { | |
this.add(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortCollection = function(comparator) { | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
this.models.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
this.models.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortArray = function(comparator) { | |
var arr; | |
arr = this.toJSON(); | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
arr.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
arr.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return arr; | |
}; | |
QueryCollection.prototype.findAll = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findAllLive = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createLiveChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findOne = function() { | |
var args, comparator, criteria, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
passed = this.testModels(this.models, criteria); | |
if ((passed != null ? passed.length : void 0) !== 0) { | |
return passed[0]; | |
} else { | |
return null; | |
} | |
}; | |
QueryCollection.prototype.query = function() { | |
var args, criteria, passed; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = { | |
paging: args[0] | |
}; | |
} | |
} | |
passed = this.queryModels(criteria); | |
this.reset(passed); | |
return this; | |
}; | |
QueryCollection.prototype.queryModels = function() { | |
var args, collection, comparator, criteria, models, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = args[0]; | |
} | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.getParentCollection() || this; | |
models = collection.models; | |
passed = this.testModels(models, criteria); | |
return passed; | |
}; | |
QueryCollection.prototype.queryArray = function() { | |
var args, model, passed, result, _i, _len; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
result = []; | |
passed = this.queryModels.apply(this, args); | |
for (_i = 0, _len = passed.length; _i < _len; _i++) { | |
model = passed[_i]; | |
result.push(model.toJSON()); | |
} | |
return result; | |
}; | |
QueryCollection.prototype.live = function(enabled) { | |
var parentCollection; | |
if (enabled == null) { | |
enabled = this.options.live; | |
} | |
this.options.live = enabled; | |
if (enabled) { | |
this.on('change', this.onChange); | |
} else { | |
this.off('change', this.onChange); | |
} | |
parentCollection = this.getParentCollection(); | |
if (parentCollection != null) { | |
if (enabled) { | |
parentCollection.on('change', this.onParentChange); | |
parentCollection.on('remove', this.onParentRemove); | |
parentCollection.on('add', this.onParentAdd); | |
parentCollection.on('reset', this.onParentReset); | |
} else { | |
parentCollection.off('change', this.onParentChange); | |
parentCollection.off('remove', this.onParentRemove); | |
parentCollection.off('add', this.onParentAdd); | |
parentCollection.off('reset', this.onParentReset); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.add = function(models, options) { | |
var model, passedModels, _i, _len; | |
options = options ? _.clone(options) : {}; | |
models = _.isArray(models) ? models.slice() : [models]; | |
passedModels = []; | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
passedModels.push(model); | |
} | |
} | |
Backbone.Collection.prototype.add.apply(this, [passedModels, options]); | |
return this; | |
}; | |
QueryCollection.prototype.create = function(model, options) { | |
options = options ? _.clone(options) : {}; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
Backbone.Collection.prototype.create.apply(this, [model, options]); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onChange = function(model) { | |
var pass; | |
pass = this.test(model); | |
if (!pass) { | |
this.safeRemove(model); | |
} else { | |
if (this.comparator) { | |
this.sortCollection(); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentChange = function(model) { | |
var pass; | |
pass = this.test(model) && this.getParentCollection().hasModel(model); | |
if (pass) { | |
this.safeAdd(model); | |
} else { | |
this.safeRemove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentRemove = function(model) { | |
this.safeRemove(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentAdd = function(model) { | |
this.safeAdd(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentReset = function(model) { | |
this.reset(this.getParentCollection().models); | |
return this; | |
}; | |
return QueryCollection; | |
})(Backbone.Collection); | |
Criteria = (function() { | |
function Criteria(options) { | |
this.applyCriteria = __bind(this.applyCriteria, this); | |
var _ref; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
this; | |
} | |
Criteria.prototype.applyCriteria = function(options) { | |
var _base; | |
if (options == null) { | |
options = {}; | |
} | |
this.options.filters = _.extend({}, this.options.filters || {}); | |
this.options.queries = _.extend({}, this.options.queries || {}); | |
this.options.pills = _.extend({}, this.options.pills || {}); | |
(_base = this.options).searchString || (_base.searchString = null); | |
this.options.paging = _.extend({}, this.options.paging || {}); | |
this.setFilters(this.options.filters); | |
this.setQueries(this.options.queries); | |
this.setPills(this.options.pills); | |
if (this.options.searchString != null) { | |
this.setSearchString(this.options.searchString); | |
} | |
this.setPaging(this.options.paging); | |
if (this.options.comparator != null) { | |
this.setComparator(this.options.comparator); | |
} | |
return this; | |
}; | |
Criteria.prototype.getPaging = function() { | |
return this.options.paging; | |
}; | |
Criteria.prototype.setPaging = function(paging) { | |
paging = _.extend(this.getPaging(), paging || {}); | |
paging.page || (paging.page = null); | |
paging.limit || (paging.limit = null); | |
paging.offset || (paging.offset = null); | |
this.options.paging = paging; | |
return this; | |
}; | |
Criteria.prototype.getComparator = function() { | |
return this.options.comparator; | |
}; | |
Criteria.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.options.comparator = comparator; | |
return this; | |
}; | |
Criteria.prototype.getFilter = function(key) { | |
return this.options.filters[key]; | |
}; | |
Criteria.prototype.getFilters = function() { | |
return this.options.filters; | |
}; | |
Criteria.prototype.setFilters = function(filters) { | |
var key, value; | |
filters || (filters = {}); | |
for (key in filters) { | |
if (!__hasProp.call(filters, key)) continue; | |
value = filters[key]; | |
this.setFilter(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setFilter = function(name, value) { | |
var filters; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setFilter was called without both arguments'); | |
} | |
filters = this.options.filters; | |
if (value != null) { | |
filters[name] = value; | |
} else if (filters[name] != null) { | |
delete filters[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getQuery = function(key) { | |
return this.options.queries[key]; | |
}; | |
Criteria.prototype.getQueries = function() { | |
return this.options.queries; | |
}; | |
Criteria.prototype.setQueries = function(queries) { | |
var key, value; | |
queries || (queries = {}); | |
for (key in queries) { | |
if (!__hasProp.call(queries, key)) continue; | |
value = queries[key]; | |
this.setQuery(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setQuery = function(name, value) { | |
var queries; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setQuery was called without both arguments'); | |
} | |
queries = this.options.queries; | |
if (value != null) { | |
if (!(value instanceof Query)) { | |
value = new Query(value); | |
} | |
queries[name] = value; | |
} else if (queries[name] != null) { | |
delete queries[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getPill = function(key) { | |
return this.options.pills[key]; | |
}; | |
Criteria.prototype.getPills = function() { | |
return this.options.pills; | |
}; | |
Criteria.prototype.setPills = function(pills) { | |
var key, value; | |
pills || (pills = {}); | |
for (key in pills) { | |
if (!__hasProp.call(pills, key)) continue; | |
value = pills[key]; | |
this.setPill(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setPill = function(name, value) { | |
var pills, searchString; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setPill was called without both arguments'); | |
} | |
pills = this.getPills(); | |
searchString = this.getSearchString(); | |
if (value != null) { | |
if (!(value instanceof Pill)) { | |
value = new Pill(value); | |
} | |
if (searchString) { | |
value.setSearchString(searchString); | |
} | |
pills[name] = value; | |
} else if (pills[name] != null) { | |
delete pills[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getCleanedSearchString = function() { | |
return this.options.cleanedSearchString; | |
}; | |
Criteria.prototype.getSearchString = function() { | |
return this.options.searchString; | |
}; | |
Criteria.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, pill, pillName, pills; | |
pills = this.options.pills; | |
cleanedSearchString = searchString; | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
cleanedSearchString = pill.setSearchString(cleanedSearchString); | |
} | |
this.options.searchString = searchString; | |
this.options.cleanedSearchString = cleanedSearchString; | |
return this; | |
}; | |
Criteria.prototype.test = function() { | |
var args; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
return this.testModel.apply(this, args); | |
}; | |
Criteria.prototype.testModel = function(model, criteria) { | |
var passed; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
passed = this.testFilters(model, criteria.filters) && this.testQueries(model, criteria.queries) && this.testPills(model, criteria.pills); | |
return passed; | |
}; | |
Criteria.prototype.testModels = function(models, criteria) { | |
var comparator, finish, me, model, paging, pass, passed, start, _i, _len, _ref; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
me = this; | |
passed = []; | |
paging = (_ref = criteria.paging) != null ? _ref : this.getPaging(); | |
if (criteria.comparator != null) { | |
comparator = util.generateComparator(criteria.comparator); | |
} else { | |
comparator = this.getComparator(); | |
} | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
pass = me.testModel(model, criteria); | |
if (pass) { | |
passed.push(model); | |
} | |
} | |
if (comparator) { | |
passed.sort(comparator); | |
} | |
start = paging.offset || 0; | |
if ((paging.limit != null) && paging.limit > 0) { | |
start = start + paging.limit * ((paging.page || 1) - 1); | |
finish = start + paging.limit; | |
passed = passed.slice(start, finish); | |
} else { | |
passed = passed.slice(start); | |
} | |
return passed; | |
}; | |
Criteria.prototype.testFilters = function(model, filters) { | |
var cleanedSearchString, filter, filterName, passed; | |
passed = true; | |
cleanedSearchString = this.getCleanedSearchString(); | |
if (filters == null) { | |
filters = this.getFilters(); | |
} | |
for (filterName in filters) { | |
if (!__hasProp.call(filters, filterName)) continue; | |
filter = filters[filterName]; | |
if (filter(model, cleanedSearchString) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testQueries = function(model, queries) { | |
var passed, query, queryName; | |
passed = true; | |
if (queries == null) { | |
queries = this.getQueries(); | |
} | |
for (queryName in queries) { | |
if (!__hasProp.call(queries, queryName)) continue; | |
query = queries[queryName]; | |
if (!(query instanceof Query)) { | |
query = new Query(query); | |
queries[queryName] = query; | |
} | |
if (query.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testPills = function(model, pills) { | |
var passed, pill, pillName, searchString; | |
passed = true; | |
searchString = this.getSearchString(); | |
if (pills == null) { | |
pills = this.getPills(); | |
} | |
if (searchString != null) { | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
if (!(pill instanceof Pill)) { | |
pill = new Pill(query); | |
pill.setSearchString(searchString); | |
pills[pillName] = pill; | |
} | |
if (pill.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
} | |
return passed; | |
}; | |
return Criteria; | |
})(); | |
Pill = (function() { | |
Pill.prototype.callback = null; | |
Pill.prototype.regex = null; | |
Pill.prototype.prefixes = null; | |
Pill.prototype.searchString = null; | |
Pill.prototype.values = null; | |
Pill.prototype.logicalOperator = 'OR'; | |
function Pill(pill) { | |
var prefix, regexString, safePrefixes, safePrefixesStr, _i, _len, _ref; | |
pill || (pill = {}); | |
this.callback = pill.callback; | |
this.prefixes = pill.prefixes; | |
if (pill.logicalOperator != null) { | |
this.logicalOperator = pill.logicalOperator; | |
} | |
safePrefixes = []; | |
_ref = this.prefixes; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
prefix = _ref[_i]; | |
safePrefixes.push(util.safeRegex(prefix)); | |
} | |
safePrefixesStr = safePrefixes.join('|'); | |
regexString = "(" + safePrefixesStr + ")\\s*('[^']+'|\\\"[^\\\"]+\\\"|[^'\\\"\\s]\\S*)"; | |
this.regex = util.createRegex(regexString); | |
this; | |
} | |
Pill.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, match, value, values; | |
cleanedSearchString = searchString; | |
values = []; | |
while (match = this.regex.exec(searchString)) { | |
value = match[2].trim().replace(/(^['"]\s*|\s*['"]$)/g, ''); | |
switch (value) { | |
case 'true': | |
case 'TRUE': | |
value = true; | |
break; | |
case 'false': | |
case 'FALSE': | |
value = false; | |
break; | |
case 'null': | |
case 'NULL': | |
value = null; | |
} | |
values.push(value); | |
cleanedSearchString = cleanedSearchString.replace(match[0], '').trim(); | |
} | |
this.searchString = searchString; | |
this.values = values; | |
return cleanedSearchString; | |
}; | |
Pill.prototype.test = function(model) { | |
var pass, value, _i, _j, _len, _len1, _ref, _ref1, _ref2; | |
if ((_ref = this.values) != null ? _ref.length : void 0) { | |
if (this.logicalOperator === 'OR') { | |
pass = false; | |
_ref1 = this.values; | |
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | |
value = _ref1[_i]; | |
pass = this.callback(model, value); | |
if (pass) { | |
break; | |
} | |
} | |
} else if (this.logicalOperator === 'AND') { | |
pass = false; | |
_ref2 = this.values; | |
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | |
value = _ref2[_j]; | |
pass = this.callback(model, value); | |
if (!pass) { | |
break; | |
} | |
} | |
} else { | |
throw new Error('Unkown logical operator type'); | |
} | |
} else { | |
pass = null; | |
} | |
return pass; | |
}; | |
return Pill; | |
})(); | |
Query = (function() { | |
Query.prototype.query = null; | |
function Query(query) { | |
if (query == null) { | |
query = {}; | |
} | |
this.query = query; | |
} | |
Query.prototype.test = function(model) { | |
var $mod, beginsWithValue, empty, endWithValue, match, matchAll, matchAny, modelId, modelValue, modelValueExists, query, queryGroup, queryType, queryValue, selectorName, selectorValue, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; | |
matchAll = true; | |
matchAny = false; | |
empty = true; | |
_ref = this.query; | |
for (selectorName in _ref) { | |
if (!__hasProp.call(_ref, selectorName)) continue; | |
selectorValue = _ref[selectorName]; | |
match = false; | |
empty = false; | |
modelValue = model.get(selectorName); | |
modelId = model.get('id'); | |
modelValueExists = typeof modelValue !== 'undefined'; | |
if (!modelValueExists) { | |
modelValue = false; | |
} | |
if (selectorName === '$or' || selectorName === '$nor') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_i = 0, _len = queryGroup.length; _i < _len; _i++) { | |
query = queryGroup[_i]; | |
query = new Query(query); | |
if (query.test(model)) { | |
match = true; | |
break; | |
} | |
} | |
if (selectorName === '$nor') { | |
match = !match; | |
} | |
} else if (selectorName === '$and' || selectorName === '$not') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_j = 0, _len1 = queryGroup.length; _j < _len1; _j++) { | |
query = queryGroup[_j]; | |
query = new Query(query); | |
match = query.test(model); | |
if (!match) { | |
break; | |
} | |
} | |
if (selectorName === '$not') { | |
match = !match; | |
} | |
} else if (util.isString(selectorValue) || util.isNumber(selectorValue) || util.isBoolean(selectorValue)) { | |
if (modelValueExists && modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isArray(selectorValue)) { | |
if (modelValueExists && (new Hash(modelValue)).isSame(selectorValue)) { | |
match = true; | |
} | |
} else if (util.isDate(selectorValue)) { | |
if (modelValueExists && modelValue.toString() === selectorValue.toString()) { | |
match = true; | |
} | |
} else if (util.isRegExp(selectorValue)) { | |
if (modelValueExists && selectorValue.test(modelValue)) { | |
match = true; | |
} | |
} else if (util.isNull(selectorValue)) { | |
if (modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isObject(selectorValue)) { | |
for (queryType in selectorValue) { | |
if (!__hasProp.call(selectorValue, queryType)) continue; | |
queryValue = selectorValue[queryType]; | |
switch (queryType) { | |
case '$beginsWith': | |
case '$startsWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_k = 0, _len2 = queryValue.length; _k < _len2; _k++) { | |
beginsWithValue = queryValue[_k]; | |
if (modelValue.substr(0, beginsWithValue.length) === beginsWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$endsWith': | |
case '$finishesWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_l = 0, _len3 = queryValue.length; _l < _len3; _l++) { | |
endWithValue = queryValue[_l]; | |
if (modelValue.substr(endWithValue.length * -1) === endWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$all': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasAll(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$in': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) || (new Hash(queryValue)).hasIn(modelValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$nin': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) === false && (new Hash(queryValue)).hasIn(modelValue) === false) { | |
match = true; | |
} | |
} | |
break; | |
case '$has': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$hasAll': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$size': | |
case '$length': | |
if (modelValue.length != null) { | |
if (modelValue.length === queryValue) { | |
match = true; | |
} | |
} | |
break; | |
case '$type': | |
if (typeof modelValue === queryValue) { | |
match = true; | |
} | |
break; | |
case '$like': | |
if (util.isString(modelValue) && modelValue.toLowerCase().indexOf(queryValue.toLowerCase()) !== -1) { | |
match = true; | |
} | |
break; | |
case '$likeSensitive': | |
if (util.isString(modelValue) && modelValue.indexOf(queryValue) !== -1) { | |
match = true; | |
} | |
break; | |
case '$exists': | |
if (queryValue === modelValueExists) { | |
match = true; | |
} | |
break; | |
case '$mod': | |
if (modelValueExists) { | |
$mod = queryValue; | |
if (!util.isArray($mod)) { | |
$mod = [$mod]; | |
} | |
if ($mod.length === 1) { | |
$mod.push(0); | |
} | |
if ((modelValue % $mod[0]) === $mod[1]) { | |
match = true; | |
} | |
} | |
break; | |
case '$eq': | |
case '$equal': | |
if (util.isEqual(modelValue, queryValue)) { | |
match = true; | |
} | |
break; | |
case '$ne': | |
if (modelValue !== queryValue) { | |
match = true; | |
} | |
break; | |
case '$lt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue < queryValue) { | |
match = true; | |
} | |
break; | |
case '$gt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue > queryValue) { | |
match = true; | |
} | |
break; | |
case '$bt': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] < modelValue && modelValue < queryValue[1]) { | |
match = true; | |
} | |
break; | |
case '$lte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue <= queryValue) { | |
match = true; | |
} | |
break; | |
case '$gte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue >= queryValue) { | |
match = true; | |
} | |
break; | |
case '$bte': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] <= modelValue && modelValue <= queryValue[1]) { | |
match = true; | |
} | |
} | |
} | |
} | |
if (match) { | |
matchAny = true; | |
} else { | |
matchAll = false; | |
} | |
} | |
if (matchAll && !matchAny) { | |
matchAll = false; | |
} | |
return matchAll; | |
}; | |
return Query; | |
})(); | |
queryEngine = { | |
safeRegex: util.safeRegex, | |
createRegex: util.createRegex, | |
createSafeRegex: util.createSafeRegex, | |
generateComparator: util.generateComparator, | |
toArray: util.toArray, | |
Backbone: Backbone, | |
Hash: Hash, | |
QueryCollection: QueryCollection, | |
Criteria: Criteria, | |
Query: Query, | |
Pill: Pill, | |
createCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options); | |
return collection; | |
}, | |
createLiveCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options).live(true); | |
return collection; | |
} | |
}; | |
if (typeof module !== "undefined" && module !== null) { | |
module.exports = queryEngine; | |
} else { | |
this.queryEnginePublished130 = queryEngine; | |
} | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.4.0 | |
(function() { | |
var Backbone, Criteria, Hash, Pill, Query, QueryCollection, queryEngine, util, _, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
__slice = [].slice; | |
_ = typeof module !== "undefined" && module !== null ? require('underscore') : this._; | |
Backbone = typeof module !== "undefined" && module !== null ? require('backbone') : this.Backbone; | |
util = { | |
isEqual: function(value1, value2) { | |
return _.isEqual(value1, value2); | |
}, | |
toString: function(value) { | |
return Object.prototype.toString.call(value); | |
}, | |
isPlainObject: function(value) { | |
return util.isObject(value) && value.__proto__ === Object.prototype; | |
}, | |
isObject: function(value) { | |
return value && typeof value === 'object'; | |
}, | |
isError: function(value) { | |
return value instanceof Error; | |
}, | |
isDate: function(value) { | |
return util.toString(value) === '[object Date]'; | |
}, | |
isArguments: function(value) { | |
return util.toString(value) === '[object Arguments]'; | |
}, | |
isFunction: function(value) { | |
return util.toString(value) === '[object Function]'; | |
}, | |
isRegExp: function(value) { | |
return util.toString(value) === '[object RegExp]'; | |
}, | |
isArray: function(value) { | |
if (Array.isArray != null) { | |
return Array.isArray(value); | |
} else { | |
return util.toString(value) === '[object Array]'; | |
} | |
}, | |
isNumber: function(value) { | |
return typeof value === 'number' || util.toString(value) === '[object Number]'; | |
}, | |
isString: function(value) { | |
return typeof value === 'string' || util.toString(value) === '[object String]'; | |
}, | |
isBoolean: function(value) { | |
return value === true || value === false || util.toString(value) === '[object Boolean]'; | |
}, | |
isNull: function(value) { | |
return value === null; | |
}, | |
isUndefined: function(value) { | |
return typeof value === 'undefined'; | |
}, | |
isDefined: function(value) { | |
return typeof value !== 'undefined'; | |
}, | |
isEmpty: function(value) { | |
return value != null; | |
}, | |
isComparable: function(value) { | |
return util.isNumber(value) || util.isDate(value); | |
}, | |
safeRegex: function(str) { | |
if (str === false) { | |
return 'false'; | |
} else if (str === true) { | |
return 'true'; | |
} else if (str === null) { | |
return 'null'; | |
} else { | |
return (str || '').replace('(.)', '\\$1'); | |
} | |
}, | |
createRegex: function(str) { | |
return new RegExp(str, 'ig'); | |
}, | |
createSafeRegex: function(str) { | |
return util.createRegex(util.safeRegex(str)); | |
}, | |
toArray: function(value) { | |
var item, key, result, valueExists; | |
result = []; | |
valueExists = typeof value !== 'undefined'; | |
if (valueExists) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
result.push(item); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
toArrayGroup: function(value) { | |
var item, key, obj, result, valueExists; | |
result = []; | |
valueExists = typeof value !== 'undefined'; | |
if (valueExists) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
obj = {}; | |
obj[key] = item; | |
result.push(obj); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
generateComparator: function(input) { | |
var generateFunction; | |
generateFunction = function(comparator) { | |
if (!comparator) { | |
throw new Error('Cannot sort without a comparator'); | |
} else if (util.isFunction(comparator)) { | |
return comparator; | |
} else if (util.isArray(comparator)) { | |
return function(a, b) { | |
var comparison, key, value, _i, _len; | |
comparison = 0; | |
for (key = _i = 0, _len = comparator.length; _i < _len; key = ++_i) { | |
value = comparator[key]; | |
comparison = generateFunction(value)(a, b); | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else if (util.isObject(comparator)) { | |
return function(a, b) { | |
var aValue, bValue, comparison, key, value, _ref, _ref1; | |
comparison = 0; | |
for (key in comparator) { | |
if (!__hasProp.call(comparator, key)) continue; | |
value = comparator[key]; | |
aValue = (_ref = typeof a.get === "function" ? a.get(key) : void 0) != null ? _ref : a[key]; | |
bValue = (_ref1 = typeof b.get === "function" ? b.get(key) : void 0) != null ? _ref1 : b[key]; | |
if (aValue === bValue) { | |
comparison = 0; | |
} else if (aValue < bValue) { | |
comparison = -1; | |
} else if (aValue > bValue) { | |
comparison = 1; | |
} | |
if (value === -1) { | |
comparison *= -1; | |
} | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else { | |
throw new Error('Unknown comparator type'); | |
} | |
}; | |
return generateFunction(input); | |
} | |
}; | |
Hash = (function(_super) { | |
__extends(Hash, _super); | |
Hash.prototype.arr = []; | |
function Hash(value) { | |
var item, key, _i, _len; | |
value = util.toArray(value); | |
for (key = _i = 0, _len = value.length; _i < _len; key = ++_i) { | |
item = value[key]; | |
this.push(item); | |
} | |
} | |
Hash.prototype.hasIn = function(options) { | |
var value, _i, _len; | |
options = util.toArray(options); | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
if (__indexOf.call(options, value) >= 0) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Hash.prototype.hasAll = function(options) { | |
var empty, pass, value, _i, _len; | |
options = util.toArray(options); | |
empty = true; | |
pass = true; | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
empty = false; | |
if (__indexOf.call(options, value) < 0) { | |
pass = false; | |
} | |
} | |
if (empty) { | |
pass = false; | |
} | |
return pass; | |
}; | |
Hash.prototype.isSame = function(options) { | |
var pass; | |
options = util.toArray(options); | |
pass = this.sort().join() === options.sort().join(); | |
return pass; | |
}; | |
return Hash; | |
})(Array); | |
QueryCollection = (function(_super) { | |
__extends(QueryCollection, _super); | |
function QueryCollection() { | |
this.onParentReset = __bind(this.onParentReset, this); | |
this.onParentAdd = __bind(this.onParentAdd, this); | |
this.onParentRemove = __bind(this.onParentRemove, this); | |
this.onParentChange = __bind(this.onParentChange, this); | |
this.onChange = __bind(this.onChange, this); | |
return QueryCollection.__super__.constructor.apply(this, arguments); | |
} | |
QueryCollection.prototype.model = Backbone.Model; | |
QueryCollection.prototype.initialize = function(models, options) { | |
var key, me, value, _ref, _ref1, _ref2; | |
me = this; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
_ref1 = Criteria.prototype; | |
for (key in _ref1) { | |
if (!__hasProp.call(_ref1, key)) continue; | |
value = _ref1[key]; | |
if ((_ref2 = this[key]) == null) { | |
this[key] = value; | |
} | |
} | |
if (this.comparator != null) { | |
this.setComparator(this.comparator); | |
} | |
this.applyCriteria(options); | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.getComparator = function() { | |
return this.comparator; | |
}; | |
QueryCollection.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.comparator = comparator; | |
return this; | |
}; | |
QueryCollection.prototype.createChildCollection = function(models, options) { | |
var collection, _ref, _ref1; | |
options || (options = {}); | |
options.parentCollection = this; | |
if ((_ref = options.collection) == null) { | |
options.collection = this.collection || QueryCollection; | |
} | |
if ((_ref1 = options.comparator) == null) { | |
options.comparator = options.collection.prototype.comparator || this.comparator; | |
} | |
collection = new options.collection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.createLiveChildCollection = function(models, options) { | |
var collection; | |
options || (options = {}); | |
options.live = true; | |
collection = this.createChildCollection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.hasParentCollection = function() { | |
return this.options.parentCollection != null; | |
}; | |
QueryCollection.prototype.getParentCollection = function() { | |
return this.options.parentCollection; | |
}; | |
QueryCollection.prototype.setParentCollection = function(parentCollection, skipCheck) { | |
if (!skipCheck && this.options.parentCollection === parentCollection) { | |
return this; | |
} | |
this.options.parentCollection = parentCollection; | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.hasModel = function(model) { | |
var exists; | |
model || (model = {}); | |
if ((model.id != null) && this.get(model.id)) { | |
exists = true; | |
} else if ((model.cid != null) && this.getByCid(model.cid)) { | |
exists = true; | |
} else { | |
exists = false; | |
} | |
return exists; | |
}; | |
QueryCollection.prototype.safeRemove = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (exists) { | |
this.remove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.safeAdd = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (!exists) { | |
this.add(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortCollection = function(comparator) { | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
this.models.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
this.models.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortArray = function(comparator) { | |
var arr; | |
arr = this.toJSON(); | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
arr.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
arr.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return arr; | |
}; | |
QueryCollection.prototype.findAll = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findAllLive = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createLiveChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findOne = function() { | |
var args, comparator, criteria, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
passed = this.testModels(this.models, criteria); | |
if ((passed != null ? passed.length : void 0) !== 0) { | |
return passed[0]; | |
} else { | |
return null; | |
} | |
}; | |
QueryCollection.prototype.query = function() { | |
var args, criteria, passed; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = { | |
paging: args[0] | |
}; | |
} | |
} | |
passed = this.queryModels(criteria); | |
this.reset(passed); | |
return this; | |
}; | |
QueryCollection.prototype.queryModels = function() { | |
var args, collection, comparator, criteria, models, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = args[0]; | |
} | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.getParentCollection() || this; | |
models = collection.models; | |
passed = this.testModels(models, criteria); | |
return passed; | |
}; | |
QueryCollection.prototype.queryArray = function() { | |
var args, model, passed, result, _i, _len; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
result = []; | |
passed = this.queryModels.apply(this, args); | |
for (_i = 0, _len = passed.length; _i < _len; _i++) { | |
model = passed[_i]; | |
result.push(model.toJSON()); | |
} | |
return result; | |
}; | |
QueryCollection.prototype.live = function(enabled) { | |
var parentCollection; | |
if (enabled == null) { | |
enabled = this.options.live; | |
} | |
this.options.live = enabled; | |
if (enabled) { | |
this.on('change', this.onChange); | |
} else { | |
this.off('change', this.onChange); | |
} | |
parentCollection = this.getParentCollection(); | |
if (parentCollection != null) { | |
if (enabled) { | |
parentCollection.on('change', this.onParentChange); | |
parentCollection.on('remove', this.onParentRemove); | |
parentCollection.on('add', this.onParentAdd); | |
parentCollection.on('reset', this.onParentReset); | |
} else { | |
parentCollection.off('change', this.onParentChange); | |
parentCollection.off('remove', this.onParentRemove); | |
parentCollection.off('add', this.onParentAdd); | |
parentCollection.off('reset', this.onParentReset); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.add = function(models, options) { | |
var model, passedModels, _i, _len; | |
options = options ? _.clone(options) : {}; | |
models = _.isArray(models) ? models.slice() : [models]; | |
passedModels = []; | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
passedModels.push(model); | |
} | |
} | |
Backbone.Collection.prototype.add.apply(this, [passedModels, options]); | |
return this; | |
}; | |
QueryCollection.prototype.create = function(model, options) { | |
options = options ? _.clone(options) : {}; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
Backbone.Collection.prototype.create.apply(this, [model, options]); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onChange = function(model) { | |
var pass; | |
pass = this.test(model); | |
if (!pass) { | |
this.safeRemove(model); | |
} else { | |
if (this.comparator) { | |
this.sortCollection(); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentChange = function(model) { | |
var pass; | |
pass = this.test(model) && this.getParentCollection().hasModel(model); | |
if (pass) { | |
this.safeAdd(model); | |
} else { | |
this.safeRemove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentRemove = function(model) { | |
this.safeRemove(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentAdd = function(model) { | |
this.safeAdd(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentReset = function(model) { | |
this.reset(this.getParentCollection().models); | |
return this; | |
}; | |
return QueryCollection; | |
})(Backbone.Collection); | |
Criteria = (function() { | |
function Criteria(options) { | |
this.applyCriteria = __bind(this.applyCriteria, this); | |
var _ref; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
this; | |
} | |
Criteria.prototype.applyCriteria = function(options) { | |
var _base; | |
if (options == null) { | |
options = {}; | |
} | |
this.options.filters = _.extend({}, this.options.filters || {}); | |
this.options.queries = _.extend({}, this.options.queries || {}); | |
this.options.pills = _.extend({}, this.options.pills || {}); | |
(_base = this.options).searchString || (_base.searchString = null); | |
this.options.paging = _.extend({}, this.options.paging || {}); | |
this.setFilters(this.options.filters); | |
this.setQueries(this.options.queries); | |
this.setPills(this.options.pills); | |
if (this.options.searchString != null) { | |
this.setSearchString(this.options.searchString); | |
} | |
this.setPaging(this.options.paging); | |
if (this.options.comparator != null) { | |
this.setComparator(this.options.comparator); | |
} | |
return this; | |
}; | |
Criteria.prototype.getPaging = function() { | |
return this.options.paging; | |
}; | |
Criteria.prototype.setPaging = function(paging) { | |
paging = _.extend(this.getPaging(), paging || {}); | |
paging.page || (paging.page = null); | |
paging.limit || (paging.limit = null); | |
paging.offset || (paging.offset = null); | |
this.options.paging = paging; | |
return this; | |
}; | |
Criteria.prototype.getComparator = function() { | |
return this.options.comparator; | |
}; | |
Criteria.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.options.comparator = comparator; | |
return this; | |
}; | |
Criteria.prototype.getFilter = function(key) { | |
return this.options.filters[key]; | |
}; | |
Criteria.prototype.getFilters = function() { | |
return this.options.filters; | |
}; | |
Criteria.prototype.setFilters = function(filters) { | |
var key, value; | |
filters || (filters = {}); | |
for (key in filters) { | |
if (!__hasProp.call(filters, key)) continue; | |
value = filters[key]; | |
this.setFilter(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setFilter = function(name, value) { | |
var filters; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setFilter was called without both arguments'); | |
} | |
filters = this.options.filters; | |
if (value != null) { | |
filters[name] = value; | |
} else if (filters[name] != null) { | |
delete filters[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getQuery = function(key) { | |
return this.options.queries[key]; | |
}; | |
Criteria.prototype.getQueries = function() { | |
return this.options.queries; | |
}; | |
Criteria.prototype.setQueries = function(queries) { | |
var key, value; | |
queries || (queries = {}); | |
for (key in queries) { | |
if (!__hasProp.call(queries, key)) continue; | |
value = queries[key]; | |
this.setQuery(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setQuery = function(name, value) { | |
var queries; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setQuery was called without both arguments'); | |
} | |
queries = this.options.queries; | |
if (value != null) { | |
if (!(value instanceof Query)) { | |
value = new Query(value); | |
} | |
queries[name] = value; | |
} else if (queries[name] != null) { | |
delete queries[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getPill = function(key) { | |
return this.options.pills[key]; | |
}; | |
Criteria.prototype.getPills = function() { | |
return this.options.pills; | |
}; | |
Criteria.prototype.setPills = function(pills) { | |
var key, value; | |
pills || (pills = {}); | |
for (key in pills) { | |
if (!__hasProp.call(pills, key)) continue; | |
value = pills[key]; | |
this.setPill(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setPill = function(name, value) { | |
var pills, searchString; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setPill was called without both arguments'); | |
} | |
pills = this.getPills(); | |
searchString = this.getSearchString(); | |
if (value != null) { | |
if (!(value instanceof Pill)) { | |
value = new Pill(value); | |
} | |
if (searchString) { | |
value.setSearchString(searchString); | |
} | |
pills[name] = value; | |
} else if (pills[name] != null) { | |
delete pills[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getCleanedSearchString = function() { | |
return this.options.cleanedSearchString; | |
}; | |
Criteria.prototype.getSearchString = function() { | |
return this.options.searchString; | |
}; | |
Criteria.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, pill, pillName, pills; | |
pills = this.options.pills; | |
cleanedSearchString = searchString; | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
cleanedSearchString = pill.setSearchString(cleanedSearchString); | |
} | |
this.options.searchString = searchString; | |
this.options.cleanedSearchString = cleanedSearchString; | |
return this; | |
}; | |
Criteria.prototype.test = function() { | |
var args; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
return this.testModel.apply(this, args); | |
}; | |
Criteria.prototype.testModel = function(model, criteria) { | |
var passed; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
passed = this.testFilters(model, criteria.filters) && this.testQueries(model, criteria.queries) && this.testPills(model, criteria.pills); | |
return passed; | |
}; | |
Criteria.prototype.testModels = function(models, criteria) { | |
var comparator, finish, me, model, paging, pass, passed, start, _i, _len, _ref; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
me = this; | |
passed = []; | |
paging = (_ref = criteria.paging) != null ? _ref : this.getPaging(); | |
if (criteria.comparator != null) { | |
comparator = util.generateComparator(criteria.comparator); | |
} else { | |
comparator = this.getComparator(); | |
} | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
pass = me.testModel(model, criteria); | |
if (pass) { | |
passed.push(model); | |
} | |
} | |
if (comparator) { | |
passed.sort(comparator); | |
} | |
start = paging.offset || 0; | |
if ((paging.limit != null) && paging.limit > 0) { | |
start = start + paging.limit * ((paging.page || 1) - 1); | |
finish = start + paging.limit; | |
passed = passed.slice(start, finish); | |
} else { | |
passed = passed.slice(start); | |
} | |
return passed; | |
}; | |
Criteria.prototype.testFilters = function(model, filters) { | |
var cleanedSearchString, filter, filterName, passed; | |
passed = true; | |
cleanedSearchString = this.getCleanedSearchString(); | |
if (filters == null) { | |
filters = this.getFilters(); | |
} | |
for (filterName in filters) { | |
if (!__hasProp.call(filters, filterName)) continue; | |
filter = filters[filterName]; | |
if (filter(model, cleanedSearchString) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testQueries = function(model, queries) { | |
var passed, query, queryName; | |
passed = true; | |
if (queries == null) { | |
queries = this.getQueries(); | |
} | |
for (queryName in queries) { | |
if (!__hasProp.call(queries, queryName)) continue; | |
query = queries[queryName]; | |
if (!(query instanceof Query)) { | |
query = new Query(query); | |
queries[queryName] = query; | |
} | |
if (query.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testPills = function(model, pills) { | |
var passed, pill, pillName, searchString; | |
passed = true; | |
searchString = this.getSearchString(); | |
if (pills == null) { | |
pills = this.getPills(); | |
} | |
if (searchString != null) { | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
if (!(pill instanceof Pill)) { | |
pill = new Pill(query); | |
pill.setSearchString(searchString); | |
pills[pillName] = pill; | |
} | |
if (pill.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
} | |
return passed; | |
}; | |
return Criteria; | |
})(); | |
Pill = (function() { | |
Pill.prototype.callback = null; | |
Pill.prototype.regex = null; | |
Pill.prototype.prefixes = null; | |
Pill.prototype.searchString = null; | |
Pill.prototype.values = null; | |
Pill.prototype.logicalOperator = 'OR'; | |
function Pill(pill) { | |
var prefix, regexString, safePrefixes, safePrefixesStr, _i, _len, _ref; | |
pill || (pill = {}); | |
this.callback = pill.callback; | |
this.prefixes = pill.prefixes; | |
if (pill.logicalOperator != null) { | |
this.logicalOperator = pill.logicalOperator; | |
} | |
safePrefixes = []; | |
_ref = this.prefixes; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
prefix = _ref[_i]; | |
safePrefixes.push(util.safeRegex(prefix)); | |
} | |
safePrefixesStr = safePrefixes.join('|'); | |
regexString = "(" + safePrefixesStr + ")\\s*('[^']+'|\\\"[^\\\"]+\\\"|[^'\\\"\\s]\\S*)"; | |
this.regex = util.createRegex(regexString); | |
this; | |
} | |
Pill.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, match, value, values; | |
cleanedSearchString = searchString; | |
values = []; | |
while (match = this.regex.exec(searchString)) { | |
value = match[2].trim().replace(/(^['"]\s*|\s*['"]$)/g, ''); | |
switch (value) { | |
case 'true': | |
case 'TRUE': | |
value = true; | |
break; | |
case 'false': | |
case 'FALSE': | |
value = false; | |
break; | |
case 'null': | |
case 'NULL': | |
value = null; | |
} | |
values.push(value); | |
cleanedSearchString = cleanedSearchString.replace(match[0], '').trim(); | |
} | |
this.searchString = searchString; | |
this.values = values; | |
return cleanedSearchString; | |
}; | |
Pill.prototype.test = function(model) { | |
var pass, value, _i, _j, _len, _len1, _ref, _ref1, _ref2; | |
if ((_ref = this.values) != null ? _ref.length : void 0) { | |
if (this.logicalOperator === 'OR') { | |
pass = false; | |
_ref1 = this.values; | |
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | |
value = _ref1[_i]; | |
pass = this.callback(model, value); | |
if (pass) { | |
break; | |
} | |
} | |
} else if (this.logicalOperator === 'AND') { | |
pass = false; | |
_ref2 = this.values; | |
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | |
value = _ref2[_j]; | |
pass = this.callback(model, value); | |
if (!pass) { | |
break; | |
} | |
} | |
} else { | |
throw new Error('Unkown logical operator type'); | |
} | |
} else { | |
pass = null; | |
} | |
return pass; | |
}; | |
return Pill; | |
})(); | |
Query = (function() { | |
Query.prototype.query = null; | |
function Query(query) { | |
if (query == null) { | |
query = {}; | |
} | |
this.query = query; | |
} | |
Query.prototype.test = function(model) { | |
var $mod, beginsWithValue, empty, endWithValue, match, matchAll, matchAny, modelId, modelValue, modelValueExists, query, queryGroup, queryType, queryValue, selectorName, selectorValue, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; | |
matchAll = true; | |
matchAny = false; | |
empty = true; | |
_ref = this.query; | |
for (selectorName in _ref) { | |
if (!__hasProp.call(_ref, selectorName)) continue; | |
selectorValue = _ref[selectorName]; | |
match = false; | |
empty = false; | |
modelValue = model.get(selectorName); | |
modelId = model.get('id'); | |
modelValueExists = typeof modelValue !== 'undefined'; | |
if (!modelValueExists) { | |
modelValue = false; | |
} | |
if (selectorName === '$or' || selectorName === '$nor') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_i = 0, _len = queryGroup.length; _i < _len; _i++) { | |
query = queryGroup[_i]; | |
query = new Query(query); | |
if (query.test(model)) { | |
match = true; | |
break; | |
} | |
} | |
if (selectorName === '$nor') { | |
match = !match; | |
} | |
} else if (selectorName === '$and' || selectorName === '$not') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_j = 0, _len1 = queryGroup.length; _j < _len1; _j++) { | |
query = queryGroup[_j]; | |
query = new Query(query); | |
match = query.test(model); | |
if (!match) { | |
break; | |
} | |
} | |
if (selectorName === '$not') { | |
match = !match; | |
} | |
} else if (util.isString(selectorValue) || util.isNumber(selectorValue) || util.isBoolean(selectorValue)) { | |
if (modelValueExists && modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isArray(selectorValue)) { | |
if (modelValueExists && (new Hash(modelValue)).isSame(selectorValue)) { | |
match = true; | |
} | |
} else if (util.isDate(selectorValue)) { | |
if (modelValueExists && modelValue.toString() === selectorValue.toString()) { | |
match = true; | |
} | |
} else if (util.isRegExp(selectorValue)) { | |
if (modelValueExists && selectorValue.test(modelValue)) { | |
match = true; | |
} | |
} else if (util.isNull(selectorValue)) { | |
if (modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isObject(selectorValue)) { | |
for (queryType in selectorValue) { | |
if (!__hasProp.call(selectorValue, queryType)) continue; | |
queryValue = selectorValue[queryType]; | |
switch (queryType) { | |
case '$beginsWith': | |
case '$startsWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_k = 0, _len2 = queryValue.length; _k < _len2; _k++) { | |
beginsWithValue = queryValue[_k]; | |
if (modelValue.substr(0, beginsWithValue.length) === beginsWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$endsWith': | |
case '$finishesWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_l = 0, _len3 = queryValue.length; _l < _len3; _l++) { | |
endWithValue = queryValue[_l]; | |
if (modelValue.substr(endWithValue.length * -1) === endWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$all': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasAll(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$in': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) || (new Hash(queryValue)).hasIn(modelValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$nin': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) === false && (new Hash(queryValue)).hasIn(modelValue) === false) { | |
match = true; | |
} | |
} | |
break; | |
case '$has': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$hasAll': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$size': | |
case '$length': | |
if (modelValue.length != null) { | |
if (modelValue.length === queryValue) { | |
match = true; | |
} | |
} | |
break; | |
case '$type': | |
if (typeof modelValue === queryValue) { | |
match = true; | |
} | |
break; | |
case '$like': | |
if (util.isString(modelValue) && modelValue.toLowerCase().indexOf(queryValue.toLowerCase()) !== -1) { | |
match = true; | |
} | |
break; | |
case '$likeSensitive': | |
if (util.isString(modelValue) && modelValue.indexOf(queryValue) !== -1) { | |
match = true; | |
} | |
break; | |
case '$exists': | |
if (queryValue === modelValueExists) { | |
match = true; | |
} | |
break; | |
case '$mod': | |
if (modelValueExists) { | |
$mod = queryValue; | |
if (!util.isArray($mod)) { | |
$mod = [$mod]; | |
} | |
if ($mod.length === 1) { | |
$mod.push(0); | |
} | |
if ((modelValue % $mod[0]) === $mod[1]) { | |
match = true; | |
} | |
} | |
break; | |
case '$eq': | |
case '$equal': | |
if (util.isEqual(modelValue, queryValue)) { | |
match = true; | |
} | |
break; | |
case '$ne': | |
if (modelValue !== queryValue) { | |
match = true; | |
} | |
break; | |
case '$lt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue < queryValue) { | |
match = true; | |
} | |
break; | |
case '$gt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue > queryValue) { | |
match = true; | |
} | |
break; | |
case '$bt': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] < modelValue && modelValue < queryValue[1]) { | |
match = true; | |
} | |
break; | |
case '$lte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue <= queryValue) { | |
match = true; | |
} | |
break; | |
case '$gte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue >= queryValue) { | |
match = true; | |
} | |
break; | |
case '$bte': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] <= modelValue && modelValue <= queryValue[1]) { | |
match = true; | |
} | |
} | |
} | |
} | |
if (match) { | |
matchAny = true; | |
} else { | |
matchAll = false; | |
} | |
} | |
if (matchAll && !matchAny) { | |
matchAll = false; | |
} | |
return matchAll; | |
}; | |
return Query; | |
})(); | |
queryEngine = { | |
safeRegex: util.safeRegex, | |
createRegex: util.createRegex, | |
createSafeRegex: util.createSafeRegex, | |
generateComparator: util.generateComparator, | |
toArray: util.toArray, | |
Backbone: Backbone, | |
Hash: Hash, | |
QueryCollection: QueryCollection, | |
Criteria: Criteria, | |
Query: Query, | |
Pill: Pill, | |
createCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options); | |
return collection; | |
}, | |
createLiveCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options).live(true); | |
return collection; | |
} | |
}; | |
if (typeof module !== "undefined" && module !== null) { | |
module.exports = queryEngine; | |
} else { | |
this.queryEnginePublished131 = queryEngine; | |
} | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.3.3 | |
(function() { | |
var Backbone, Criteria, Hash, Pill, Query, QueryCollection, queryEngine, util, _, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
__slice = [].slice; | |
_ = typeof module !== "undefined" && module !== null ? require('underscore') : this._; | |
Backbone = typeof module !== "undefined" && module !== null ? require('backbone') : this.Backbone; | |
util = { | |
isEqual: function(value1, value2) { | |
return _.isEqual(value1, value2); | |
}, | |
toString: function(value) { | |
return Object.prototype.toString.call(value); | |
}, | |
isPlainObject: function(value) { | |
return util.isObject(value) && value.__proto__ === Object.prototype; | |
}, | |
isObject: function(value) { | |
return value && typeof value === 'object'; | |
}, | |
isError: function(value) { | |
return value instanceof Error; | |
}, | |
isDate: function(value) { | |
return util.toString(value) === '[object Date]'; | |
}, | |
isArguments: function(value) { | |
return util.toString(value) === '[object Arguments]'; | |
}, | |
isFunction: function(value) { | |
return util.toString(value) === '[object Function]'; | |
}, | |
isRegExp: function(value) { | |
return util.toString(value) === '[object RegExp]'; | |
}, | |
isArray: function(value) { | |
if (Array.isArray != null) { | |
return Array.isArray(value); | |
} else { | |
return util.toString(value) === '[object Array]'; | |
} | |
}, | |
isNumber: function(value) { | |
return typeof value === 'number' || util.toString(value) === '[object Number]'; | |
}, | |
isString: function(value) { | |
return typeof value === 'string' || util.toString(value) === '[object String]'; | |
}, | |
isBoolean: function(value) { | |
return value === true || value === false || util.toString(value) === '[object Boolean]'; | |
}, | |
isNull: function(value) { | |
return value === null; | |
}, | |
isUndefined: function(value) { | |
return typeof value === 'undefined'; | |
}, | |
isDefined: function(value) { | |
return typeof value !== 'undefined'; | |
}, | |
isEmpty: function(value) { | |
return value != null; | |
}, | |
isComparable: function(value) { | |
return util.isNumber(value) || util.isDate(value); | |
}, | |
safeRegex: function(str) { | |
if (str === false) { | |
return 'false'; | |
} else if (str === true) { | |
return 'true'; | |
} else if (str === null) { | |
return 'null'; | |
} else { | |
return (str || '').replace('(.)', '\\$1'); | |
} | |
}, | |
createRegex: function(str) { | |
return new RegExp(str, 'ig'); | |
}, | |
createSafeRegex: function(str) { | |
return util.createRegex(util.safeRegex(str)); | |
}, | |
toArray: function(value) { | |
var item, key, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value; | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
result.push(item); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
toArrayGroup: function(value) { | |
var item, key, obj, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value; | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
obj = {}; | |
obj[key] = item; | |
result.push(obj); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
generateComparator: function(input) { | |
var generateFunction; | |
generateFunction = function(comparator) { | |
if (!comparator) { | |
throw new Error('Cannot sort without a comparator'); | |
} else if (util.isFunction(comparator)) { | |
return comparator; | |
} else if (util.isArray(comparator)) { | |
return function(a, b) { | |
var comparison, key, value, _i, _len; | |
comparison = 0; | |
for (key = _i = 0, _len = comparator.length; _i < _len; key = ++_i) { | |
value = comparator[key]; | |
comparison = generateFunction(value)(a, b); | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else if (util.isObject(comparator)) { | |
return function(a, b) { | |
var aValue, bValue, comparison, key, value, _ref, _ref1; | |
comparison = 0; | |
for (key in comparator) { | |
if (!__hasProp.call(comparator, key)) continue; | |
value = comparator[key]; | |
aValue = (_ref = typeof a.get === "function" ? a.get(key) : void 0) != null ? _ref : a[key]; | |
bValue = (_ref1 = typeof b.get === "function" ? b.get(key) : void 0) != null ? _ref1 : b[key]; | |
if (aValue === bValue) { | |
comparison = 0; | |
} else if (aValue < bValue) { | |
comparison = -1; | |
} else if (aValue > bValue) { | |
comparison = 1; | |
} | |
if (value === -1) { | |
comparison *= -1; | |
} | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else { | |
throw new Error('Unknown comparator type'); | |
} | |
}; | |
return generateFunction(input); | |
} | |
}; | |
Hash = (function(_super) { | |
__extends(Hash, _super); | |
Hash.prototype.arr = []; | |
function Hash(value) { | |
var item, key, _i, _len; | |
value = util.toArray(value); | |
for (key = _i = 0, _len = value.length; _i < _len; key = ++_i) { | |
item = value[key]; | |
this.push(item); | |
} | |
} | |
Hash.prototype.hasIn = function(options) { | |
var value, _i, _len; | |
options = util.toArray(options); | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
if (__indexOf.call(options, value) >= 0) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Hash.prototype.hasAll = function(options) { | |
var empty, pass, value, _i, _len; | |
options = util.toArray(options); | |
empty = true; | |
pass = true; | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
empty = false; | |
if (__indexOf.call(options, value) < 0) { | |
pass = false; | |
} | |
} | |
if (empty) { | |
pass = false; | |
} | |
return pass; | |
}; | |
Hash.prototype.isSame = function(options) { | |
var pass; | |
options = util.toArray(options); | |
pass = this.sort().join() === options.sort().join(); | |
return pass; | |
}; | |
return Hash; | |
})(Array); | |
QueryCollection = (function(_super) { | |
__extends(QueryCollection, _super); | |
function QueryCollection() { | |
this.onParentReset = __bind(this.onParentReset, this); | |
this.onParentAdd = __bind(this.onParentAdd, this); | |
this.onParentRemove = __bind(this.onParentRemove, this); | |
this.onParentChange = __bind(this.onParentChange, this); | |
this.onChange = __bind(this.onChange, this); | |
return QueryCollection.__super__.constructor.apply(this, arguments); | |
} | |
QueryCollection.prototype.model = Backbone.Model; | |
QueryCollection.prototype.initialize = function(models, options) { | |
var key, me, value, _ref, _ref1, _ref2; | |
me = this; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
_ref1 = Criteria.prototype; | |
for (key in _ref1) { | |
if (!__hasProp.call(_ref1, key)) continue; | |
value = _ref1[key]; | |
if ((_ref2 = this[key]) == null) { | |
this[key] = value; | |
} | |
} | |
if (this.comparator != null) { | |
this.setComparator(this.comparator); | |
} | |
this.applyCriteria(options); | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.getComparator = function() { | |
return this.comparator; | |
}; | |
QueryCollection.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.comparator = comparator; | |
return this; | |
}; | |
QueryCollection.prototype.createChildCollection = function(models, options) { | |
var collection, _ref, _ref1; | |
options || (options = {}); | |
options.parentCollection = this; | |
if ((_ref = options.collection) == null) { | |
options.collection = this.collection || QueryCollection; | |
} | |
if ((_ref1 = options.comparator) == null) { | |
options.comparator = options.collection.prototype.comparator || this.comparator; | |
} | |
collection = new options.collection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.createLiveChildCollection = function(models, options) { | |
var collection; | |
options || (options = {}); | |
options.live = true; | |
collection = this.createChildCollection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.hasParentCollection = function() { | |
return this.options.parentCollection != null; | |
}; | |
QueryCollection.prototype.getParentCollection = function() { | |
return this.options.parentCollection; | |
}; | |
QueryCollection.prototype.setParentCollection = function(parentCollection, skipCheck) { | |
if (!skipCheck && this.options.parentCollection === parentCollection) { | |
return this; | |
} | |
this.options.parentCollection = parentCollection; | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.hasModel = function(model) { | |
var exists; | |
model || (model = {}); | |
if ((model.id != null) && this.get(model.id)) { | |
exists = true; | |
} else if ((model.cid != null) && this.getByCid(model.cid)) { | |
exists = true; | |
} else { | |
exists = false; | |
} | |
return exists; | |
}; | |
QueryCollection.prototype.safeRemove = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (exists) { | |
this.remove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.safeAdd = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (!exists) { | |
this.add(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortCollection = function(comparator) { | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
this.models.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
this.models.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortArray = function(comparator) { | |
var arr; | |
arr = this.toJSON(); | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
arr.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
arr.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return arr; | |
}; | |
QueryCollection.prototype.findAll = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findAllLive = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createLiveChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findOne = function() { | |
var args, comparator, criteria, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
passed = this.testModels(this.models, criteria); | |
if ((passed != null ? passed.length : void 0) !== 0) { | |
return passed[0]; | |
} else { | |
return null; | |
} | |
}; | |
QueryCollection.prototype.query = function() { | |
var args, collection, criteria, models, passed; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length === 1) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = { | |
paging: args[0] | |
}; | |
} | |
} | |
collection = this.getParentCollection() || this; | |
models = collection.models; | |
passed = this.testModels(models, criteria); | |
this.reset(passed); | |
return this; | |
}; | |
QueryCollection.prototype.live = function(enabled) { | |
var parentCollection; | |
if (enabled == null) { | |
enabled = this.options.live; | |
} | |
this.options.live = enabled; | |
if (enabled) { | |
this.on('change', this.onChange); | |
} else { | |
this.off('change', this.onChange); | |
} | |
parentCollection = this.getParentCollection(); | |
if (parentCollection != null) { | |
if (enabled) { | |
parentCollection.on('change', this.onParentChange); | |
parentCollection.on('remove', this.onParentRemove); | |
parentCollection.on('add', this.onParentAdd); | |
parentCollection.on('reset', this.onParentReset); | |
} else { | |
parentCollection.off('change', this.onParentChange); | |
parentCollection.off('remove', this.onParentRemove); | |
parentCollection.off('add', this.onParentAdd); | |
parentCollection.off('reset', this.onParentReset); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.add = function(models, options) { | |
var model, passedModels, _i, _len; | |
options = options ? _.clone(options) : {}; | |
models = _.isArray(models) ? models.slice() : [models]; | |
passedModels = []; | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
passedModels.push(model); | |
} | |
} | |
Backbone.Collection.prototype.add.apply(this, [passedModels, options]); | |
return this; | |
}; | |
QueryCollection.prototype.create = function(model, options) { | |
options = options ? _.clone(options) : {}; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
Backbone.Collection.prototype.create.apply(this, [model, options]); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onChange = function(model) { | |
var pass; | |
pass = this.test(model); | |
if (!pass) { | |
this.safeRemove(model); | |
} else { | |
if (this.comparator) { | |
this.sortCollection(); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentChange = function(model) { | |
var pass; | |
pass = this.test(model) && this.getParentCollection().hasModel(model); | |
if (pass) { | |
this.safeAdd(model); | |
} else { | |
this.safeRemove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentRemove = function(model) { | |
this.safeRemove(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentAdd = function(model) { | |
this.safeAdd(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentReset = function(model) { | |
this.reset(this.getParentCollection().models); | |
return this; | |
}; | |
return QueryCollection; | |
})(Backbone.Collection); | |
Criteria = (function() { | |
function Criteria(options) { | |
this.applyCriteria = __bind(this.applyCriteria, this); | |
var _ref; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
this; | |
} | |
Criteria.prototype.applyCriteria = function(options) { | |
var _base; | |
if (options == null) { | |
options = {}; | |
} | |
this.options.filters = _.extend({}, this.options.filters || {}); | |
this.options.queries = _.extend({}, this.options.queries || {}); | |
this.options.pills = _.extend({}, this.options.pills || {}); | |
(_base = this.options).searchString || (_base.searchString = null); | |
this.options.paging = _.extend({}, this.options.paging || {}); | |
this.setFilters(this.options.filters); | |
this.setQueries(this.options.queries); | |
this.setPills(this.options.pills); | |
if (this.options.searchString != null) { | |
this.setSearchString(this.options.searchString); | |
} | |
this.setPaging(this.options.paging); | |
if (this.options.comparator != null) { | |
this.setComparator(this.options.comparator); | |
} | |
return this; | |
}; | |
Criteria.prototype.getPaging = function() { | |
return this.options.paging; | |
}; | |
Criteria.prototype.setPaging = function(paging) { | |
paging = _.extend(this.getPaging(), paging || {}); | |
paging.page || (paging.page = null); | |
paging.limit || (paging.limit = null); | |
paging.offset || (paging.offset = null); | |
this.options.paging = paging; | |
return this; | |
}; | |
Criteria.prototype.getComparator = function() { | |
return this.options.comparator; | |
}; | |
Criteria.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.options.comparator = comparator; | |
return this; | |
}; | |
Criteria.prototype.getFilter = function(key) { | |
return this.options.filters[key]; | |
}; | |
Criteria.prototype.getFilters = function() { | |
return this.options.filters; | |
}; | |
Criteria.prototype.setFilters = function(filters) { | |
var key, value; | |
filters || (filters = {}); | |
for (key in filters) { | |
if (!__hasProp.call(filters, key)) continue; | |
value = filters[key]; | |
this.setFilter(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setFilter = function(name, value) { | |
var filters; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setFilter was called without both arguments'); | |
} | |
filters = this.options.filters; | |
if (value != null) { | |
filters[name] = value; | |
} else if (filters[name] != null) { | |
delete filters[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getQuery = function(key) { | |
return this.options.queries[key]; | |
}; | |
Criteria.prototype.getQueries = function() { | |
return this.options.queries; | |
}; | |
Criteria.prototype.setQueries = function(queries) { | |
var key, value; | |
queries || (queries = {}); | |
for (key in queries) { | |
if (!__hasProp.call(queries, key)) continue; | |
value = queries[key]; | |
this.setQuery(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setQuery = function(name, value) { | |
var queries; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setQuery was called without both arguments'); | |
} | |
queries = this.options.queries; | |
if (value != null) { | |
if (!(value instanceof Query)) { | |
value = new Query(value); | |
} | |
queries[name] = value; | |
} else if (queries[name] != null) { | |
delete queries[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getPill = function(key) { | |
return this.options.pills[key]; | |
}; | |
Criteria.prototype.getPills = function() { | |
return this.options.pills; | |
}; | |
Criteria.prototype.setPills = function(pills) { | |
var key, value; | |
pills || (pills = {}); | |
for (key in pills) { | |
if (!__hasProp.call(pills, key)) continue; | |
value = pills[key]; | |
this.setPill(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setPill = function(name, value) { | |
var pills, searchString; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setPill was called without both arguments'); | |
} | |
pills = this.getPills(); | |
searchString = this.getSearchString(); | |
if (value != null) { | |
if (!(value instanceof Pill)) { | |
value = new Pill(value); | |
} | |
if (searchString) { | |
value.setSearchString(searchString); | |
} | |
pills[name] = value; | |
} else if (pills[name] != null) { | |
delete pills[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getCleanedSearchString = function() { | |
return this.options.cleanedSearchString; | |
}; | |
Criteria.prototype.getSearchString = function() { | |
return this.options.searchString; | |
}; | |
Criteria.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, pill, pillName, pills; | |
pills = this.options.pills; | |
cleanedSearchString = searchString; | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
cleanedSearchString = pill.setSearchString(cleanedSearchString); | |
} | |
this.options.searchString = searchString; | |
this.options.cleanedSearchString = cleanedSearchString; | |
return this; | |
}; | |
Criteria.prototype.test = function() { | |
var args; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
return this.testModel.apply(this, args); | |
}; | |
Criteria.prototype.testModel = function(model, criteria) { | |
var passed; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
passed = this.testFilters(model, criteria.filters) && this.testQueries(model, criteria.queries) && this.testPills(model, criteria.pills); | |
return passed; | |
}; | |
Criteria.prototype.testModels = function(models, criteria) { | |
var comparator, finish, me, model, paging, pass, passed, start, _i, _len, _ref, _ref1; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
me = this; | |
passed = []; | |
if (models == null) { | |
models = this.models; | |
} | |
paging = (_ref = criteria.paging) != null ? _ref : this.getPaging(); | |
comparator = (_ref1 = criteria.comparator) != null ? _ref1 : this.getComparator(); | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
pass = me.testModel(model, criteria); | |
if (pass) { | |
passed.push(model); | |
} | |
} | |
start = paging.offset || 0; | |
if ((paging.limit != null) && paging.limit > 0) { | |
start = start + paging.limit * ((paging.page || 1) - 1); | |
finish = start + paging.limit; | |
passed = passed.slice(start, finish); | |
} else { | |
passed = passed.slice(start); | |
} | |
if (comparator) { | |
passed.sort(comparator); | |
} | |
return passed; | |
}; | |
Criteria.prototype.testFilters = function(model, filters) { | |
var cleanedSearchString, filter, filterName, passed; | |
passed = true; | |
cleanedSearchString = this.getCleanedSearchString(); | |
if (filters == null) { | |
filters = this.getFilters(); | |
} | |
for (filterName in filters) { | |
if (!__hasProp.call(filters, filterName)) continue; | |
filter = filters[filterName]; | |
if (filter(model, cleanedSearchString) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testQueries = function(model, queries) { | |
var passed, query, queryName; | |
passed = true; | |
if (queries == null) { | |
queries = this.getQueries(); | |
} | |
for (queryName in queries) { | |
if (!__hasProp.call(queries, queryName)) continue; | |
query = queries[queryName]; | |
if (!(query instanceof Query)) { | |
query = new Query(query); | |
queries[queryName] = query; | |
} | |
if (query.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testPills = function(model, pills) { | |
var passed, pill, pillName, searchString; | |
passed = true; | |
searchString = this.getSearchString(); | |
if (pills == null) { | |
pills = this.getPills(); | |
} | |
if (searchString != null) { | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
if (!(pill instanceof Pill)) { | |
pill = new Pill(query); | |
pill.setSearchString(searchString); | |
pills[pillName] = pill; | |
} | |
if (pill.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
} | |
return passed; | |
}; | |
return Criteria; | |
})(); | |
Pill = (function() { | |
Pill.prototype.callback = null; | |
Pill.prototype.regex = null; | |
Pill.prototype.prefixes = null; | |
Pill.prototype.searchString = null; | |
Pill.prototype.values = null; | |
Pill.prototype.logicalOperator = 'OR'; | |
function Pill(pill) { | |
var prefix, regexString, safePrefixes, safePrefixesStr, _i, _len, _ref; | |
pill || (pill = {}); | |
this.callback = pill.callback; | |
this.prefixes = pill.prefixes; | |
if (pill.logicalOperator != null) { | |
this.logicalOperator = pill.logicalOperator; | |
} | |
safePrefixes = []; | |
_ref = this.prefixes; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
prefix = _ref[_i]; | |
safePrefixes.push(util.safeRegex(prefix)); | |
} | |
safePrefixesStr = safePrefixes.join('|'); | |
regexString = "(" + safePrefixesStr + ")\\s*('[^']+'|\\\"[^\\\"]+\\\"|[^'\\\"\\s]\\S*)"; | |
this.regex = util.createRegex(regexString); | |
this; | |
} | |
Pill.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, match, value, values; | |
cleanedSearchString = searchString; | |
values = []; | |
while (match = this.regex.exec(searchString)) { | |
value = match[2].trim().replace(/(^['"]\s*|\s*['"]$)/g, ''); | |
switch (value) { | |
case 'true': | |
case 'TRUE': | |
value = true; | |
break; | |
case 'false': | |
case 'FALSE': | |
value = false; | |
break; | |
case 'null': | |
case 'NULL': | |
value = null; | |
} | |
values.push(value); | |
cleanedSearchString = cleanedSearchString.replace(match[0], '').trim(); | |
} | |
this.searchString = searchString; | |
this.values = values; | |
return cleanedSearchString; | |
}; | |
Pill.prototype.test = function(model) { | |
var pass, value, _i, _j, _len, _len1, _ref, _ref1, _ref2; | |
if ((_ref = this.values) != null ? _ref.length : void 0) { | |
if (this.logicalOperator === 'OR') { | |
pass = false; | |
_ref1 = this.values; | |
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | |
value = _ref1[_i]; | |
pass = this.callback(model, value); | |
if (pass) { | |
break; | |
} | |
} | |
} else if (this.logicalOperator === 'AND') { | |
pass = false; | |
_ref2 = this.values; | |
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | |
value = _ref2[_j]; | |
pass = this.callback(model, value); | |
if (!pass) { | |
break; | |
} | |
} | |
} else { | |
throw new Error('Unkown logical operator type'); | |
} | |
} else { | |
pass = null; | |
} | |
return pass; | |
}; | |
return Pill; | |
})(); | |
Query = (function() { | |
Query.prototype.query = null; | |
function Query(query) { | |
if (query == null) { | |
query = {}; | |
} | |
this.query = query; | |
} | |
Query.prototype.test = function(model) { | |
var $beginsWith, $beginsWithValue, $endWithValue, $endsWith, $mod, $size, empty, match, matchAll, matchAny, modelId, modelValue, modelValueExists, query, queryGroup, selectorName, selectorValue, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; | |
matchAll = true; | |
matchAny = false; | |
empty = true; | |
_ref = this.query; | |
for (selectorName in _ref) { | |
if (!__hasProp.call(_ref, selectorName)) continue; | |
selectorValue = _ref[selectorName]; | |
match = false; | |
empty = false; | |
modelValue = model.get(selectorName); | |
modelId = model.get('id'); | |
modelValueExists = typeof modelValue !== 'undefined'; | |
if (!modelValueExists) { | |
modelValue = false; | |
} | |
if (selectorName === '$or' || selectorName === '$nor') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_i = 0, _len = queryGroup.length; _i < _len; _i++) { | |
query = queryGroup[_i]; | |
query = new Query(query); | |
if (query.test(model)) { | |
match = true; | |
break; | |
} | |
} | |
if (selectorName === '$nor') { | |
match = !match; | |
} | |
} else if (selectorName === '$and' || selectorName === '$not') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_j = 0, _len1 = queryGroup.length; _j < _len1; _j++) { | |
query = queryGroup[_j]; | |
query = new Query(query); | |
match = query.test(model); | |
if (!match) { | |
break; | |
} | |
} | |
if (selectorName === '$not') { | |
match = !match; | |
} | |
} else if (util.isString(selectorValue) || util.isNumber(selectorValue) || util.isBoolean(selectorValue)) { | |
if (modelValueExists && modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isArray(selectorValue)) { | |
if (modelValueExists && (new Hash(modelValue)).isSame(selectorValue)) { | |
match = true; | |
} | |
} else if (util.isDate(selectorValue)) { | |
if (modelValueExists && modelValue.toString() === selectorValue.toString()) { | |
match = true; | |
} | |
} else if (util.isRegExp(selectorValue)) { | |
if (modelValueExists && selectorValue.test(modelValue)) { | |
match = true; | |
} | |
} else if (util.isNull(selectorValue)) { | |
if (modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isObject(selectorValue)) { | |
$beginsWith = selectorValue.$beginsWith || selectorValue.$startsWith || null; | |
if ($beginsWith && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray($beginsWith)) { | |
$beginsWith = [$beginsWith]; | |
} | |
for (_k = 0, _len2 = $beginsWith.length; _k < _len2; _k++) { | |
$beginsWithValue = $beginsWith[_k]; | |
if (modelValue.substr(0, $beginsWithValue.length) === $beginsWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
$endsWith = selectorValue.$endsWith || selectorValue.$finishesWith || null; | |
if ($endsWith && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray($endsWith)) { | |
$endsWith = [$endsWith]; | |
} | |
for (_l = 0, _len3 = $endsWith.length; _l < _len3; _l++) { | |
$endWithValue = $endsWith[_l]; | |
if (modelValue.substr($endWithValue.length * -1) === $endWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
if (selectorValue.$all != null) { | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasAll(selectorValue.$all)) { | |
match = true; | |
} | |
} | |
} | |
if (selectorValue.$in != null) { | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(selectorValue.$in)) { | |
match = true; | |
} else if ((new Hash(selectorValue.$in)).hasIn(modelValue)) { | |
match = true; | |
} | |
} | |
} | |
if (selectorValue.$has != null) { | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(selectorValue.$has)) { | |
match = true; | |
} | |
} | |
} | |
if (selectorValue.$hasAll != null) { | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(selectorValue.$hasAll)) { | |
match = true; | |
} | |
} | |
} | |
if (selectorValue.$nin != null) { | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(selectorValue.$nin) === false && (new Hash(selectorValue.$nin)).hasIn(selectorValue) === false) { | |
match = true; | |
} | |
} | |
} | |
$size = selectorValue.$size || selectorValue.$length; | |
if ($size != null) { | |
if ((modelValue.length != null) && modelValue.length === $size) { | |
match = true; | |
} | |
} | |
if (selectorValue.$type) { | |
if (typeof modelValue === selectorValue.$type) { | |
match = true; | |
} | |
} | |
if (selectorValue.$like != null) { | |
if (util.isString(modelValue) && modelValue.toLowerCase().indexOf(selectorValue.$like.toLowerCase()) !== -1) { | |
match = true; | |
} | |
} | |
if (selectorValue.$likeSensitive != null) { | |
if (util.isString(modelValue) && modelValue.indexOf(selectorValue.$likeSensitive) !== -1) { | |
match = true; | |
} | |
} | |
if (selectorValue.$exists != null) { | |
if (selectorValue.$exists) { | |
if (modelValueExists === true) { | |
match = true; | |
} | |
} else { | |
if (modelValueExists === false) { | |
match = true; | |
} | |
} | |
} | |
if (selectorValue.$mod != null) { | |
if (modelValueExists) { | |
$mod = selectorValue.$mod; | |
if (!util.isArray($mod)) { | |
$mod = [$mod]; | |
} | |
if ($mod.length === 1) { | |
$mod.push(0); | |
} | |
if ((modelValue % $mod[0]) === $mod[1]) { | |
match = true; | |
} | |
} | |
} | |
if (util.isDefined(selectorValue.$eq)) { | |
if (util.isEqual(modelValue, selectorValue.$eq)) { | |
match = true; | |
} | |
} | |
if (util.isDefined(selectorValue.$ne)) { | |
if (modelValue !== selectorValue.$ne) { | |
match = true; | |
} | |
} | |
if (selectorValue.$lt != null) { | |
if (util.isComparable(modelValue) && modelValue < selectorValue.$lt) { | |
match = true; | |
} | |
} | |
if (selectorValue.$gt != null) { | |
if (util.isComparable(modelValue) && modelValue > selectorValue.$gt) { | |
match = true; | |
} | |
} | |
if (selectorValue.$bt != null) { | |
if (util.isComparable(modelValue) && selectorValue.$bt[0] < modelValue && modelValue < selectorValue.$bt[1]) { | |
match = true; | |
} | |
} | |
if (selectorValue.$lte != null) { | |
if (util.isComparable(modelValue) && modelValue <= selectorValue.$lte) { | |
match = true; | |
} | |
} | |
if (selectorValue.$gte != null) { | |
if (util.isComparable(modelValue) && modelValue >= selectorValue.$gte) { | |
match = true; | |
} | |
} | |
if (selectorValue.$bte != null) { | |
if (util.isComparable(modelValue) && selectorValue.$bte[0] <= modelValue && modelValue <= selectorValue.$bte[1]) { | |
match = true; | |
} | |
} | |
} | |
if (match) { | |
matchAny = true; | |
} else { | |
matchAll = false; | |
} | |
} | |
if (matchAll && !matchAny) { | |
matchAll = false; | |
} | |
return matchAll; | |
}; | |
return Query; | |
})(); | |
queryEngine = { | |
safeRegex: util.safeRegex, | |
createRegex: util.createRegex, | |
createSafeRegex: util.createSafeRegex, | |
generateComparator: util.generateComparator, | |
toArray: util.toArray, | |
Backbone: Backbone, | |
Hash: Hash, | |
QueryCollection: QueryCollection, | |
Query: Query, | |
Pill: Pill, | |
createCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options); | |
return collection; | |
}, | |
createLiveCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options).live(true); | |
return collection; | |
} | |
}; | |
if (typeof module !== "undefined" && module !== null) { | |
module.exports = queryEngine; | |
} else { | |
this.queryEngineRewrite1 = queryEngine; | |
} | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.3.3 | |
(function() { | |
var Backbone, Criteria, Hash, Pill, Query, QueryCollection, queryEngine, util, _, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
__slice = [].slice; | |
_ = typeof module !== "undefined" && module !== null ? require('underscore') : this._; | |
Backbone = typeof module !== "undefined" && module !== null ? require('backbone') : this.Backbone; | |
util = { | |
isEqual: function(value1, value2) { | |
return _.isEqual(value1, value2); | |
}, | |
toString: function(value) { | |
return Object.prototype.toString.call(value); | |
}, | |
isPlainObject: function(value) { | |
return util.isObject(value) && value.__proto__ === Object.prototype; | |
}, | |
isObject: function(value) { | |
return value && typeof value === 'object'; | |
}, | |
isError: function(value) { | |
return value instanceof Error; | |
}, | |
isDate: function(value) { | |
return util.toString(value) === '[object Date]'; | |
}, | |
isArguments: function(value) { | |
return util.toString(value) === '[object Arguments]'; | |
}, | |
isFunction: function(value) { | |
return util.toString(value) === '[object Function]'; | |
}, | |
isRegExp: function(value) { | |
return util.toString(value) === '[object RegExp]'; | |
}, | |
isArray: function(value) { | |
if (Array.isArray != null) { | |
return Array.isArray(value); | |
} else { | |
return util.toString(value) === '[object Array]'; | |
} | |
}, | |
isNumber: function(value) { | |
return typeof value === 'number' || util.toString(value) === '[object Number]'; | |
}, | |
isString: function(value) { | |
return typeof value === 'string' || util.toString(value) === '[object String]'; | |
}, | |
isBoolean: function(value) { | |
return value === true || value === false || util.toString(value) === '[object Boolean]'; | |
}, | |
isNull: function(value) { | |
return value === null; | |
}, | |
isUndefined: function(value) { | |
return typeof value === 'undefined'; | |
}, | |
isDefined: function(value) { | |
return typeof value !== 'undefined'; | |
}, | |
isEmpty: function(value) { | |
return value != null; | |
}, | |
isComparable: function(value) { | |
return util.isNumber(value) || util.isDate(value); | |
}, | |
safeRegex: function(str) { | |
if (str === false) { | |
return 'false'; | |
} else if (str === true) { | |
return 'true'; | |
} else if (str === null) { | |
return 'null'; | |
} else { | |
return (str || '').replace('(.)', '\\$1'); | |
} | |
}, | |
createRegex: function(str) { | |
return new RegExp(str, 'ig'); | |
}, | |
createSafeRegex: function(str) { | |
return util.createRegex(util.safeRegex(str)); | |
}, | |
toArray: function(value) { | |
var item, key, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value; | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
result.push(item); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
toArrayGroup: function(value) { | |
var item, key, obj, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value; | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
obj = {}; | |
obj[key] = item; | |
result.push(obj); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
generateComparator: function(input) { | |
var generateFunction; | |
generateFunction = function(comparator) { | |
if (!comparator) { | |
throw new Error('Cannot sort without a comparator'); | |
} else if (util.isFunction(comparator)) { | |
return comparator; | |
} else if (util.isArray(comparator)) { | |
return function(a, b) { | |
var comparison, key, value, _i, _len; | |
comparison = 0; | |
for (key = _i = 0, _len = comparator.length; _i < _len; key = ++_i) { | |
value = comparator[key]; | |
comparison = generateFunction(value)(a, b); | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else if (util.isObject(comparator)) { | |
return function(a, b) { | |
var aValue, bValue, comparison, key, value, _ref, _ref1; | |
comparison = 0; | |
for (key in comparator) { | |
if (!__hasProp.call(comparator, key)) continue; | |
value = comparator[key]; | |
aValue = (_ref = typeof a.get === "function" ? a.get(key) : void 0) != null ? _ref : a[key]; | |
bValue = (_ref1 = typeof b.get === "function" ? b.get(key) : void 0) != null ? _ref1 : b[key]; | |
if (aValue === bValue) { | |
comparison = 0; | |
} else if (aValue < bValue) { | |
comparison = -1; | |
} else if (aValue > bValue) { | |
comparison = 1; | |
} | |
if (value === -1) { | |
comparison *= -1; | |
} | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else { | |
throw new Error('Unknown comparator type'); | |
} | |
}; | |
return generateFunction(input); | |
} | |
}; | |
Hash = (function(_super) { | |
__extends(Hash, _super); | |
Hash.prototype.arr = []; | |
function Hash(value) { | |
var item, key, _i, _len; | |
value = util.toArray(value); | |
for (key = _i = 0, _len = value.length; _i < _len; key = ++_i) { | |
item = value[key]; | |
this.push(item); | |
} | |
} | |
Hash.prototype.hasIn = function(options) { | |
var value, _i, _len; | |
options = util.toArray(options); | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
if (__indexOf.call(options, value) >= 0) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Hash.prototype.hasAll = function(options) { | |
var empty, pass, value, _i, _len; | |
options = util.toArray(options); | |
empty = true; | |
pass = true; | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
empty = false; | |
if (__indexOf.call(options, value) < 0) { | |
pass = false; | |
} | |
} | |
if (empty) { | |
pass = false; | |
} | |
return pass; | |
}; | |
Hash.prototype.isSame = function(options) { | |
var pass; | |
options = util.toArray(options); | |
pass = this.sort().join() === options.sort().join(); | |
return pass; | |
}; | |
return Hash; | |
})(Array); | |
QueryCollection = (function(_super) { | |
__extends(QueryCollection, _super); | |
function QueryCollection() { | |
this.onParentReset = __bind(this.onParentReset, this); | |
this.onParentAdd = __bind(this.onParentAdd, this); | |
this.onParentRemove = __bind(this.onParentRemove, this); | |
this.onParentChange = __bind(this.onParentChange, this); | |
this.onChange = __bind(this.onChange, this); | |
return QueryCollection.__super__.constructor.apply(this, arguments); | |
} | |
QueryCollection.prototype.model = Backbone.Model; | |
QueryCollection.prototype.initialize = function(models, options) { | |
var key, me, value, _ref, _ref1, _ref2; | |
me = this; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
_ref1 = Criteria.prototype; | |
for (key in _ref1) { | |
if (!__hasProp.call(_ref1, key)) continue; | |
value = _ref1[key]; | |
if ((_ref2 = this[key]) == null) { | |
this[key] = value; | |
} | |
} | |
if (this.comparator != null) { | |
this.setComparator(this.comparator); | |
} | |
this.applyCriteria(options); | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.getComparator = function() { | |
return this.comparator; | |
}; | |
QueryCollection.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.comparator = comparator; | |
return this; | |
}; | |
QueryCollection.prototype.createChildCollection = function(models, options) { | |
var collection, _ref, _ref1; | |
options || (options = {}); | |
options.parentCollection = this; | |
if ((_ref = options.collection) == null) { | |
options.collection = this.collection || QueryCollection; | |
} | |
if ((_ref1 = options.comparator) == null) { | |
options.comparator = options.collection.prototype.comparator || this.comparator; | |
} | |
collection = new options.collection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.createLiveChildCollection = function(models, options) { | |
var collection; | |
options || (options = {}); | |
options.live = true; | |
collection = this.createChildCollection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.hasParentCollection = function() { | |
return this.options.parentCollection != null; | |
}; | |
QueryCollection.prototype.getParentCollection = function() { | |
return this.options.parentCollection; | |
}; | |
QueryCollection.prototype.setParentCollection = function(parentCollection, skipCheck) { | |
if (!skipCheck && this.options.parentCollection === parentCollection) { | |
return this; | |
} | |
this.options.parentCollection = parentCollection; | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.hasModel = function(model) { | |
var exists; | |
model || (model = {}); | |
if ((model.id != null) && this.get(model.id)) { | |
exists = true; | |
} else if ((model.cid != null) && this.getByCid(model.cid)) { | |
exists = true; | |
} else { | |
exists = false; | |
} | |
return exists; | |
}; | |
QueryCollection.prototype.safeRemove = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (exists) { | |
this.remove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.safeAdd = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (!exists) { | |
this.add(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortCollection = function(comparator) { | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
this.models.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
this.models.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortArray = function(comparator) { | |
var arr; | |
arr = this.toJSON(); | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
arr.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
arr.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return arr; | |
}; | |
QueryCollection.prototype.findAll = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findAllLive = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createLiveChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findOne = function() { | |
var args, comparator, criteria, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
passed = this.testModels(this.models, criteria); | |
if ((passed != null ? passed.length : void 0) !== 0) { | |
return passed[0]; | |
} else { | |
return null; | |
} | |
}; | |
QueryCollection.prototype.query = function() { | |
var args, collection, criteria, models, passed; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length === 1) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = { | |
paging: args[0] | |
}; | |
} | |
} | |
collection = this.getParentCollection() || this; | |
models = collection.models; | |
passed = this.testModels(models, criteria); | |
this.reset(passed); | |
return this; | |
}; | |
QueryCollection.prototype.live = function(enabled) { | |
var parentCollection; | |
if (enabled == null) { | |
enabled = this.options.live; | |
} | |
this.options.live = enabled; | |
if (enabled) { | |
this.on('change', this.onChange); | |
} else { | |
this.off('change', this.onChange); | |
} | |
parentCollection = this.getParentCollection(); | |
if (parentCollection != null) { | |
if (enabled) { | |
parentCollection.on('change', this.onParentChange); | |
parentCollection.on('remove', this.onParentRemove); | |
parentCollection.on('add', this.onParentAdd); | |
parentCollection.on('reset', this.onParentReset); | |
} else { | |
parentCollection.off('change', this.onParentChange); | |
parentCollection.off('remove', this.onParentRemove); | |
parentCollection.off('add', this.onParentAdd); | |
parentCollection.off('reset', this.onParentReset); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.add = function(models, options) { | |
var model, passedModels, _i, _len; | |
options = options ? _.clone(options) : {}; | |
models = _.isArray(models) ? models.slice() : [models]; | |
passedModels = []; | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
passedModels.push(model); | |
} | |
} | |
Backbone.Collection.prototype.add.apply(this, [passedModels, options]); | |
return this; | |
}; | |
QueryCollection.prototype.create = function(model, options) { | |
options = options ? _.clone(options) : {}; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
Backbone.Collection.prototype.create.apply(this, [model, options]); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onChange = function(model) { | |
var pass; | |
pass = this.test(model); | |
if (!pass) { | |
this.safeRemove(model); | |
} else { | |
if (this.comparator) { | |
this.sortCollection(); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentChange = function(model) { | |
var pass; | |
pass = this.test(model) && this.getParentCollection().hasModel(model); | |
if (pass) { | |
this.safeAdd(model); | |
} else { | |
this.safeRemove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentRemove = function(model) { | |
this.safeRemove(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentAdd = function(model) { | |
this.safeAdd(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentReset = function(model) { | |
this.reset(this.getParentCollection().models); | |
return this; | |
}; | |
return QueryCollection; | |
})(Backbone.Collection); | |
Criteria = (function() { | |
function Criteria(options) { | |
this.applyCriteria = __bind(this.applyCriteria, this); | |
var _ref; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
this; | |
} | |
Criteria.prototype.applyCriteria = function(options) { | |
var _base; | |
if (options == null) { | |
options = {}; | |
} | |
this.options.filters = _.extend({}, this.options.filters || {}); | |
this.options.queries = _.extend({}, this.options.queries || {}); | |
this.options.pills = _.extend({}, this.options.pills || {}); | |
(_base = this.options).searchString || (_base.searchString = null); | |
this.options.paging = _.extend({}, this.options.paging || {}); | |
this.setFilters(this.options.filters); | |
this.setQueries(this.options.queries); | |
this.setPills(this.options.pills); | |
if (this.options.searchString != null) { | |
this.setSearchString(this.options.searchString); | |
} | |
this.setPaging(this.options.paging); | |
if (this.options.comparator != null) { | |
this.setComparator(this.options.comparator); | |
} | |
return this; | |
}; | |
Criteria.prototype.getPaging = function() { | |
return this.options.paging; | |
}; | |
Criteria.prototype.setPaging = function(paging) { | |
paging = _.extend(this.getPaging(), paging || {}); | |
paging.page || (paging.page = null); | |
paging.limit || (paging.limit = null); | |
paging.offset || (paging.offset = null); | |
this.options.paging = paging; | |
return this; | |
}; | |
Criteria.prototype.getComparator = function() { | |
return this.options.comparator; | |
}; | |
Criteria.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.options.comparator = comparator; | |
return this; | |
}; | |
Criteria.prototype.getFilter = function(key) { | |
return this.options.filters[key]; | |
}; | |
Criteria.prototype.getFilters = function() { | |
return this.options.filters; | |
}; | |
Criteria.prototype.setFilters = function(filters) { | |
var key, value; | |
filters || (filters = {}); | |
for (key in filters) { | |
if (!__hasProp.call(filters, key)) continue; | |
value = filters[key]; | |
this.setFilter(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setFilter = function(name, value) { | |
var filters; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setFilter was called without both arguments'); | |
} | |
filters = this.options.filters; | |
if (value != null) { | |
filters[name] = value; | |
} else if (filters[name] != null) { | |
delete filters[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getQuery = function(key) { | |
return this.options.queries[key]; | |
}; | |
Criteria.prototype.getQueries = function() { | |
return this.options.queries; | |
}; | |
Criteria.prototype.setQueries = function(queries) { | |
var key, value; | |
queries || (queries = {}); | |
for (key in queries) { | |
if (!__hasProp.call(queries, key)) continue; | |
value = queries[key]; | |
this.setQuery(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setQuery = function(name, value) { | |
var queries; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setQuery was called without both arguments'); | |
} | |
queries = this.options.queries; | |
if (value != null) { | |
if (!(value instanceof Query)) { | |
value = new Query(value); | |
} | |
queries[name] = value; | |
} else if (queries[name] != null) { | |
delete queries[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getPill = function(key) { | |
return this.options.pills[key]; | |
}; | |
Criteria.prototype.getPills = function() { | |
return this.options.pills; | |
}; | |
Criteria.prototype.setPills = function(pills) { | |
var key, value; | |
pills || (pills = {}); | |
for (key in pills) { | |
if (!__hasProp.call(pills, key)) continue; | |
value = pills[key]; | |
this.setPill(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setPill = function(name, value) { | |
var pills, searchString; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setPill was called without both arguments'); | |
} | |
pills = this.getPills(); | |
searchString = this.getSearchString(); | |
if (value != null) { | |
if (!(value instanceof Pill)) { | |
value = new Pill(value); | |
} | |
if (searchString) { | |
value.setSearchString(searchString); | |
} | |
pills[name] = value; | |
} else if (pills[name] != null) { | |
delete pills[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getCleanedSearchString = function() { | |
return this.options.cleanedSearchString; | |
}; | |
Criteria.prototype.getSearchString = function() { | |
return this.options.searchString; | |
}; | |
Criteria.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, pill, pillName, pills; | |
pills = this.options.pills; | |
cleanedSearchString = searchString; | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
cleanedSearchString = pill.setSearchString(cleanedSearchString); | |
} | |
this.options.searchString = searchString; | |
this.options.cleanedSearchString = cleanedSearchString; | |
return this; | |
}; | |
Criteria.prototype.test = function() { | |
var args; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
return this.testModel.apply(this, args); | |
}; | |
Criteria.prototype.testModel = function(model, criteria) { | |
var passed; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
passed = this.testFilters(model, criteria.filters) && this.testQueries(model, criteria.queries) && this.testPills(model, criteria.pills); | |
return passed; | |
}; | |
Criteria.prototype.testModels = function(models, criteria) { | |
var comparator, finish, me, model, paging, pass, passed, start, _i, _len, _ref, _ref1; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
me = this; | |
passed = []; | |
if (models == null) { | |
models = this.models; | |
} | |
paging = (_ref = criteria.paging) != null ? _ref : this.getPaging(); | |
comparator = (_ref1 = criteria.comparator) != null ? _ref1 : this.getComparator(); | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
pass = me.testModel(model, criteria); | |
if (pass) { | |
passed.push(model); | |
} | |
} | |
start = paging.offset || 0; | |
if ((paging.limit != null) && paging.limit > 0) { | |
start = start + paging.limit * ((paging.page || 1) - 1); | |
finish = start + paging.limit; | |
passed = passed.slice(start, finish); | |
} else { | |
passed = passed.slice(start); | |
} | |
if (comparator) { | |
passed.sort(comparator); | |
} | |
return passed; | |
}; | |
Criteria.prototype.testFilters = function(model, filters) { | |
var cleanedSearchString, filter, filterName, passed; | |
passed = true; | |
cleanedSearchString = this.getCleanedSearchString(); | |
if (filters == null) { | |
filters = this.getFilters(); | |
} | |
for (filterName in filters) { | |
if (!__hasProp.call(filters, filterName)) continue; | |
filter = filters[filterName]; | |
if (filter(model, cleanedSearchString) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testQueries = function(model, queries) { | |
var passed, query, queryName; | |
passed = true; | |
if (queries == null) { | |
queries = this.getQueries(); | |
} | |
for (queryName in queries) { | |
if (!__hasProp.call(queries, queryName)) continue; | |
query = queries[queryName]; | |
if (!(query instanceof Query)) { | |
query = new Query(query); | |
queries[queryName] = query; | |
} | |
if (query.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testPills = function(model, pills) { | |
var passed, pill, pillName, searchString; | |
passed = true; | |
searchString = this.getSearchString(); | |
if (pills == null) { | |
pills = this.getPills(); | |
} | |
if (searchString != null) { | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
if (!(pill instanceof Pill)) { | |
pill = new Pill(query); | |
pill.setSearchString(searchString); | |
pills[pillName] = pill; | |
} | |
if (pill.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
} | |
return passed; | |
}; | |
return Criteria; | |
})(); | |
Pill = (function() { | |
Pill.prototype.callback = null; | |
Pill.prototype.regex = null; | |
Pill.prototype.prefixes = null; | |
Pill.prototype.searchString = null; | |
Pill.prototype.values = null; | |
Pill.prototype.logicalOperator = 'OR'; | |
function Pill(pill) { | |
var prefix, regexString, safePrefixes, safePrefixesStr, _i, _len, _ref; | |
pill || (pill = {}); | |
this.callback = pill.callback; | |
this.prefixes = pill.prefixes; | |
if (pill.logicalOperator != null) { | |
this.logicalOperator = pill.logicalOperator; | |
} | |
safePrefixes = []; | |
_ref = this.prefixes; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
prefix = _ref[_i]; | |
safePrefixes.push(util.safeRegex(prefix)); | |
} | |
safePrefixesStr = safePrefixes.join('|'); | |
regexString = "(" + safePrefixesStr + ")\\s*('[^']+'|\\\"[^\\\"]+\\\"|[^'\\\"\\s]\\S*)"; | |
this.regex = util.createRegex(regexString); | |
this; | |
} | |
Pill.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, match, value, values; | |
cleanedSearchString = searchString; | |
values = []; | |
while (match = this.regex.exec(searchString)) { | |
value = match[2].trim().replace(/(^['"]\s*|\s*['"]$)/g, ''); | |
switch (value) { | |
case 'true': | |
case 'TRUE': | |
value = true; | |
break; | |
case 'false': | |
case 'FALSE': | |
value = false; | |
break; | |
case 'null': | |
case 'NULL': | |
value = null; | |
} | |
values.push(value); | |
cleanedSearchString = cleanedSearchString.replace(match[0], '').trim(); | |
} | |
this.searchString = searchString; | |
this.values = values; | |
return cleanedSearchString; | |
}; | |
Pill.prototype.test = function(model) { | |
var pass, value, _i, _j, _len, _len1, _ref, _ref1, _ref2; | |
if ((_ref = this.values) != null ? _ref.length : void 0) { | |
if (this.logicalOperator === 'OR') { | |
pass = false; | |
_ref1 = this.values; | |
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | |
value = _ref1[_i]; | |
pass = this.callback(model, value); | |
if (pass) { | |
break; | |
} | |
} | |
} else if (this.logicalOperator === 'AND') { | |
pass = false; | |
_ref2 = this.values; | |
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | |
value = _ref2[_j]; | |
pass = this.callback(model, value); | |
if (!pass) { | |
break; | |
} | |
} | |
} else { | |
throw new Error('Unkown logical operator type'); | |
} | |
} else { | |
pass = null; | |
} | |
return pass; | |
}; | |
return Pill; | |
})(); | |
Query = (function() { | |
Query.prototype.query = null; | |
function Query(query) { | |
if (query == null) { | |
query = {}; | |
} | |
this.query = query; | |
} | |
Query.prototype.test = function(model) { | |
var $mod, beginsWithValue, empty, endWithValue, match, matchAll, matchAny, modelId, modelValue, modelValueExists, query, queryGroup, queryType, queryValue, selectorName, selectorValue, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; | |
matchAll = true; | |
matchAny = false; | |
empty = true; | |
_ref = this.query; | |
for (selectorName in _ref) { | |
if (!__hasProp.call(_ref, selectorName)) continue; | |
selectorValue = _ref[selectorName]; | |
match = false; | |
empty = false; | |
modelValue = model.get(selectorName); | |
modelId = model.get('id'); | |
modelValueExists = typeof modelValue !== 'undefined'; | |
if (!modelValueExists) { | |
modelValue = false; | |
} | |
if (selectorName === '$or' || selectorName === '$nor') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_i = 0, _len = queryGroup.length; _i < _len; _i++) { | |
query = queryGroup[_i]; | |
query = new Query(query); | |
if (query.test(model)) { | |
match = true; | |
break; | |
} | |
} | |
if (selectorName === '$nor') { | |
match = !match; | |
} | |
} else if (selectorName === '$and' || selectorName === '$not') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_j = 0, _len1 = queryGroup.length; _j < _len1; _j++) { | |
query = queryGroup[_j]; | |
query = new Query(query); | |
match = query.test(model); | |
if (!match) { | |
break; | |
} | |
} | |
if (selectorName === '$not') { | |
match = !match; | |
} | |
} else if (util.isString(selectorValue) || util.isNumber(selectorValue) || util.isBoolean(selectorValue)) { | |
if (modelValueExists && modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isArray(selectorValue)) { | |
if (modelValueExists && (new Hash(modelValue)).isSame(selectorValue)) { | |
match = true; | |
} | |
} else if (util.isDate(selectorValue)) { | |
if (modelValueExists && modelValue.toString() === selectorValue.toString()) { | |
match = true; | |
} | |
} else if (util.isRegExp(selectorValue)) { | |
if (modelValueExists && selectorValue.test(modelValue)) { | |
match = true; | |
} | |
} else if (util.isNull(selectorValue)) { | |
if (modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isObject(selectorValue)) { | |
for (queryType in selectorValue) { | |
if (!__hasProp.call(selectorValue, queryType)) continue; | |
queryValue = selectorValue[queryType]; | |
switch (queryType) { | |
case '$beginsWith': | |
case '$startsWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_k = 0, _len2 = queryValue.length; _k < _len2; _k++) { | |
beginsWithValue = queryValue[_k]; | |
if (modelValue.substr(0, beginsWithValue.length) === beginsWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$endsWith': | |
case '$finishesWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_l = 0, _len3 = queryValue.length; _l < _len3; _l++) { | |
endWithValue = queryValue[_l]; | |
if (modelValue.substr(endWithValue.length * -1) === endWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$all': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasAll(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$in': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) || (new Hash(queryValue)).hasIn(modelValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$nin': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) === false && (new Hash(queryValue)).hasIn(modelValue) === false) { | |
match = true; | |
} | |
} | |
break; | |
case '$has': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$hasAll': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$size': | |
case '$length': | |
if (modelValue.length != null) { | |
if (modelValue.length === queryValue) { | |
match = true; | |
} | |
} | |
break; | |
case '$type': | |
if (typeof modelValue === queryValue) { | |
match = true; | |
} | |
break; | |
case '$like': | |
if (util.isString(modelValue) && modelValue.toLowerCase().indexOf(queryValue.toLowerCase()) !== -1) { | |
match = true; | |
} | |
break; | |
case '$likeSensitive': | |
if (util.isString(modelValue) && modelValue.indexOf(queryValue) !== -1) { | |
match = true; | |
} | |
break; | |
case '$exists': | |
if (queryValue === modelValueExists) { | |
match = true; | |
} | |
break; | |
case '$mod': | |
if (modelValueExists) { | |
$mod = queryValue; | |
if (!util.isArray($mod)) { | |
$mod = [$mod]; | |
} | |
if ($mod.length === 1) { | |
$mod.push(0); | |
} | |
if ((modelValue % $mod[0]) === $mod[1]) { | |
match = true; | |
} | |
} | |
break; | |
case '$eq': | |
case '$equal': | |
if (util.isEqual(modelValue, queryValue)) { | |
match = true; | |
} | |
break; | |
case '$ne': | |
if (modelValue !== queryValue) { | |
match = true; | |
} | |
break; | |
case '$lt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue < queryValue) { | |
match = true; | |
} | |
break; | |
case '$gt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue > queryValue) { | |
match = true; | |
} | |
break; | |
case '$bt': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] < modelValue && modelValue < queryValue[1]) { | |
match = true; | |
} | |
break; | |
case '$lte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue <= queryValue) { | |
match = true; | |
} | |
break; | |
case '$gte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue >= queryValue) { | |
match = true; | |
} | |
break; | |
case '$bte': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] <= modelValue && modelValue <= queryValue[1]) { | |
match = true; | |
} | |
} | |
} | |
} | |
if (match) { | |
matchAny = true; | |
} else { | |
matchAll = false; | |
} | |
} | |
if (matchAll && !matchAny) { | |
matchAll = false; | |
} | |
return matchAll; | |
}; | |
return Query; | |
})(); | |
queryEngine = { | |
safeRegex: util.safeRegex, | |
createRegex: util.createRegex, | |
createSafeRegex: util.createSafeRegex, | |
generateComparator: util.generateComparator, | |
toArray: util.toArray, | |
Backbone: Backbone, | |
Hash: Hash, | |
QueryCollection: QueryCollection, | |
Query: Query, | |
Pill: Pill, | |
createCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options); | |
return collection; | |
}, | |
createLiveCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options).live(true); | |
return collection; | |
} | |
}; | |
if (typeof module !== "undefined" && module !== null) { | |
module.exports = queryEngine; | |
} else { | |
this.queryEngineRewrite2 = queryEngine; | |
} | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.3.3 | |
(function() { | |
var Backbone, Criteria, Hash, Pill, Query, QueryCollection, queryEngine, util, _, | |
__hasProp = {}.hasOwnProperty, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
__slice = [].slice; | |
_ = typeof module !== "undefined" && module !== null ? require('underscore') : this._; | |
Backbone = typeof module !== "undefined" && module !== null ? require('backbone') : this.Backbone; | |
util = { | |
isEqual: function(value1, value2) { | |
return _.isEqual(value1, value2); | |
}, | |
toString: function(value) { | |
return Object.prototype.toString.call(value); | |
}, | |
isPlainObject: function(value) { | |
return util.isObject(value) && value.__proto__ === Object.prototype; | |
}, | |
isObject: function(value) { | |
return value && typeof value === 'object'; | |
}, | |
isError: function(value) { | |
return value instanceof Error; | |
}, | |
isDate: function(value) { | |
return util.toString(value) === '[object Date]'; | |
}, | |
isArguments: function(value) { | |
return util.toString(value) === '[object Arguments]'; | |
}, | |
isFunction: function(value) { | |
return util.toString(value) === '[object Function]'; | |
}, | |
isRegExp: function(value) { | |
return util.toString(value) === '[object RegExp]'; | |
}, | |
isArray: function(value) { | |
if (Array.isArray != null) { | |
return Array.isArray(value); | |
} else { | |
return util.toString(value) === '[object Array]'; | |
} | |
}, | |
isNumber: function(value) { | |
return typeof value === 'number' || util.toString(value) === '[object Number]'; | |
}, | |
isString: function(value) { | |
return typeof value === 'string' || util.toString(value) === '[object String]'; | |
}, | |
isBoolean: function(value) { | |
return value === true || value === false || util.toString(value) === '[object Boolean]'; | |
}, | |
isNull: function(value) { | |
return value === null; | |
}, | |
isUndefined: function(value) { | |
return typeof value === 'undefined'; | |
}, | |
isDefined: function(value) { | |
return typeof value !== 'undefined'; | |
}, | |
isEmpty: function(value) { | |
return value != null; | |
}, | |
isComparable: function(value) { | |
return util.isNumber(value) || util.isDate(value); | |
}, | |
safeRegex: function(str) { | |
if (str === false) { | |
return 'false'; | |
} else if (str === true) { | |
return 'true'; | |
} else if (str === null) { | |
return 'null'; | |
} else { | |
return (str || '').replace('(.)', '\\$1'); | |
} | |
}, | |
createRegex: function(str) { | |
return new RegExp(str, 'ig'); | |
}, | |
createSafeRegex: function(str) { | |
return util.createRegex(util.safeRegex(str)); | |
}, | |
toArray: function(value) { | |
var item, key, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
result.push(item); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
toArrayGroup: function(value) { | |
var item, key, obj, result; | |
result = []; | |
if (value) { | |
if (util.isArray(value)) { | |
result = value.slice(); | |
} else if (util.isObject(value)) { | |
for (key in value) { | |
if (!__hasProp.call(value, key)) continue; | |
item = value[key]; | |
obj = {}; | |
obj[key] = item; | |
result.push(obj); | |
} | |
} else { | |
result.push(value); | |
} | |
} | |
return result; | |
}, | |
generateComparator: function(input) { | |
var generateFunction; | |
generateFunction = function(comparator) { | |
if (!comparator) { | |
throw new Error('Cannot sort without a comparator'); | |
} else if (util.isFunction(comparator)) { | |
return comparator; | |
} else if (util.isArray(comparator)) { | |
return function(a, b) { | |
var comparison, key, value, _i, _len; | |
comparison = 0; | |
for (key = _i = 0, _len = comparator.length; _i < _len; key = ++_i) { | |
value = comparator[key]; | |
comparison = generateFunction(value)(a, b); | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else if (util.isObject(comparator)) { | |
return function(a, b) { | |
var aValue, bValue, comparison, key, value, _ref, _ref1; | |
comparison = 0; | |
for (key in comparator) { | |
if (!__hasProp.call(comparator, key)) continue; | |
value = comparator[key]; | |
aValue = (_ref = typeof a.get === "function" ? a.get(key) : void 0) != null ? _ref : a[key]; | |
bValue = (_ref1 = typeof b.get === "function" ? b.get(key) : void 0) != null ? _ref1 : b[key]; | |
if (aValue === bValue) { | |
comparison = 0; | |
} else if (aValue < bValue) { | |
comparison = -1; | |
} else if (aValue > bValue) { | |
comparison = 1; | |
} | |
if (value === -1) { | |
comparison *= -1; | |
} | |
if (comparison) { | |
return comparison; | |
} | |
} | |
return comparison; | |
}; | |
} else { | |
throw new Error('Unknown comparator type'); | |
} | |
}; | |
return generateFunction(input); | |
} | |
}; | |
Hash = (function(_super) { | |
__extends(Hash, _super); | |
Hash.prototype.arr = []; | |
function Hash(value) { | |
var item, key, _i, _len; | |
value = util.toArray(value); | |
for (key = _i = 0, _len = value.length; _i < _len; key = ++_i) { | |
item = value[key]; | |
this.push(item); | |
} | |
} | |
Hash.prototype.hasIn = function(options) { | |
var value, _i, _len; | |
options = util.toArray(options); | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
if (__indexOf.call(options, value) >= 0) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Hash.prototype.hasAll = function(options) { | |
var empty, pass, value, _i, _len; | |
options = util.toArray(options); | |
empty = true; | |
pass = true; | |
for (_i = 0, _len = this.length; _i < _len; _i++) { | |
value = this[_i]; | |
empty = false; | |
if (__indexOf.call(options, value) < 0) { | |
pass = false; | |
} | |
} | |
if (empty) { | |
pass = false; | |
} | |
return pass; | |
}; | |
Hash.prototype.isSame = function(options) { | |
var pass; | |
options = util.toArray(options); | |
pass = this.sort().join() === options.sort().join(); | |
return pass; | |
}; | |
return Hash; | |
})(Array); | |
QueryCollection = (function(_super) { | |
__extends(QueryCollection, _super); | |
function QueryCollection() { | |
this.onParentReset = __bind(this.onParentReset, this); | |
this.onParentAdd = __bind(this.onParentAdd, this); | |
this.onParentRemove = __bind(this.onParentRemove, this); | |
this.onParentChange = __bind(this.onParentChange, this); | |
this.onChange = __bind(this.onChange, this); | |
return QueryCollection.__super__.constructor.apply(this, arguments); | |
} | |
QueryCollection.prototype.model = Backbone.Model; | |
QueryCollection.prototype.initialize = function(models, options) { | |
var key, me, value, _ref, _ref1, _ref2; | |
me = this; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
_ref1 = Criteria.prototype; | |
for (key in _ref1) { | |
if (!__hasProp.call(_ref1, key)) continue; | |
value = _ref1[key]; | |
if ((_ref2 = this[key]) == null) { | |
this[key] = value; | |
} | |
} | |
if (this.comparator != null) { | |
this.setComparator(this.comparator); | |
} | |
this.applyCriteria(options); | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.getComparator = function() { | |
return this.comparator; | |
}; | |
QueryCollection.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.comparator = comparator; | |
return this; | |
}; | |
QueryCollection.prototype.createChildCollection = function(models, options) { | |
var collection, _ref, _ref1; | |
options || (options = {}); | |
options.parentCollection = this; | |
if ((_ref = options.collection) == null) { | |
options.collection = this.collection || QueryCollection; | |
} | |
if ((_ref1 = options.comparator) == null) { | |
options.comparator = options.collection.prototype.comparator || this.comparator; | |
} | |
collection = new options.collection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.createLiveChildCollection = function(models, options) { | |
var collection; | |
options || (options = {}); | |
options.live = true; | |
collection = this.createChildCollection(models, options); | |
return collection; | |
}; | |
QueryCollection.prototype.hasParentCollection = function() { | |
return this.options.parentCollection != null; | |
}; | |
QueryCollection.prototype.getParentCollection = function() { | |
return this.options.parentCollection; | |
}; | |
QueryCollection.prototype.setParentCollection = function(parentCollection, skipCheck) { | |
if (!skipCheck && this.options.parentCollection === parentCollection) { | |
return this; | |
} | |
this.options.parentCollection = parentCollection; | |
this.live(); | |
return this; | |
}; | |
QueryCollection.prototype.hasModel = function(model) { | |
var exists; | |
model || (model = {}); | |
if ((model.id != null) && this.get(model.id)) { | |
exists = true; | |
} else if ((model.cid != null) && this.getByCid(model.cid)) { | |
exists = true; | |
} else { | |
exists = false; | |
} | |
return exists; | |
}; | |
QueryCollection.prototype.safeRemove = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (exists) { | |
this.remove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.safeAdd = function(model) { | |
var exists; | |
exists = this.hasModel(model); | |
if (!exists) { | |
this.add(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortCollection = function(comparator) { | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
this.models.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
this.models.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.sortArray = function(comparator) { | |
var arr; | |
arr = this.toJSON(); | |
if (comparator) { | |
comparator = util.generateComparator(comparator); | |
arr.sort(comparator); | |
} else { | |
comparator = this.getComparator(); | |
if (comparator) { | |
arr.sort(comparator); | |
} else { | |
throw new Error('You need a comparator to sort'); | |
} | |
} | |
return arr; | |
}; | |
QueryCollection.prototype.findAll = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findAllLive = function() { | |
var args, collection, comparator, criteria, paging, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.createLiveChildCollection([], criteria).query(); | |
return collection; | |
}; | |
QueryCollection.prototype.findOne = function() { | |
var args, comparator, criteria, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1 && args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
passed = this.testModels(this.models, criteria); | |
if ((passed != null ? passed.length : void 0) !== 0) { | |
return passed[0]; | |
} else { | |
return null; | |
} | |
}; | |
QueryCollection.prototype.query = function() { | |
var args, criteria, passed; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = { | |
paging: args[0] | |
}; | |
} | |
} | |
passed = this.queryModels(criteria); | |
this.reset(passed); | |
return this; | |
}; | |
QueryCollection.prototype.queryModels = function() { | |
var args, collection, comparator, criteria, models, paging, passed, query; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
if (args.length) { | |
if (args.length === 1) { | |
if (args[0] instanceof Criteria) { | |
criteria = args[0].options; | |
} else { | |
criteria = args[0]; | |
} | |
} else { | |
query = args[0], comparator = args[1], paging = args[2]; | |
criteria = { | |
comparator: comparator, | |
paging: paging, | |
queries: { | |
find: query | |
} | |
}; | |
} | |
} | |
collection = this.getParentCollection() || this; | |
models = collection.models; | |
passed = this.testModels(models, criteria); | |
return passed; | |
}; | |
QueryCollection.prototype.queryArray = function() { | |
var args, model, passed, result, _i, _len; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
result = []; | |
passed = this.queryModels.apply(this, args); | |
for (_i = 0, _len = passed.length; _i < _len; _i++) { | |
model = passed[_i]; | |
result.push(model.toJSON()); | |
} | |
return result; | |
}; | |
QueryCollection.prototype.live = function(enabled) { | |
var parentCollection; | |
if (enabled == null) { | |
enabled = this.options.live; | |
} | |
this.options.live = enabled; | |
if (enabled) { | |
this.on('change', this.onChange); | |
} else { | |
this.off('change', this.onChange); | |
} | |
parentCollection = this.getParentCollection(); | |
if (parentCollection != null) { | |
if (enabled) { | |
parentCollection.on('change', this.onParentChange); | |
parentCollection.on('remove', this.onParentRemove); | |
parentCollection.on('add', this.onParentAdd); | |
parentCollection.on('reset', this.onParentReset); | |
} else { | |
parentCollection.off('change', this.onParentChange); | |
parentCollection.off('remove', this.onParentRemove); | |
parentCollection.off('add', this.onParentAdd); | |
parentCollection.off('reset', this.onParentReset); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.add = function(models, options) { | |
var model, passedModels, _i, _len; | |
options = options ? _.clone(options) : {}; | |
models = _.isArray(models) ? models.slice() : [models]; | |
passedModels = []; | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
passedModels.push(model); | |
} | |
} | |
Backbone.Collection.prototype.add.apply(this, [passedModels, options]); | |
return this; | |
}; | |
QueryCollection.prototype.create = function(model, options) { | |
options = options ? _.clone(options) : {}; | |
model = this._prepareModel(model, options); | |
if (model && this.test(model)) { | |
Backbone.Collection.prototype.create.apply(this, [model, options]); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onChange = function(model) { | |
var pass; | |
pass = this.test(model); | |
if (!pass) { | |
this.safeRemove(model); | |
} else { | |
if (this.comparator) { | |
this.sortCollection(); | |
} | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentChange = function(model) { | |
var pass; | |
pass = this.test(model) && this.getParentCollection().hasModel(model); | |
if (pass) { | |
this.safeAdd(model); | |
} else { | |
this.safeRemove(model); | |
} | |
return this; | |
}; | |
QueryCollection.prototype.onParentRemove = function(model) { | |
this.safeRemove(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentAdd = function(model) { | |
this.safeAdd(model); | |
return this; | |
}; | |
QueryCollection.prototype.onParentReset = function(model) { | |
this.reset(this.getParentCollection().models); | |
return this; | |
}; | |
return QueryCollection; | |
})(Backbone.Collection); | |
Criteria = (function() { | |
function Criteria(options) { | |
this.applyCriteria = __bind(this.applyCriteria, this); | |
var _ref; | |
if ((_ref = this.options) == null) { | |
this.options = {}; | |
} | |
_.extend(this.options, options); | |
this; | |
} | |
Criteria.prototype.applyCriteria = function(options) { | |
var _base; | |
if (options == null) { | |
options = {}; | |
} | |
this.options.filters = _.extend({}, this.options.filters || {}); | |
this.options.queries = _.extend({}, this.options.queries || {}); | |
this.options.pills = _.extend({}, this.options.pills || {}); | |
(_base = this.options).searchString || (_base.searchString = null); | |
this.options.paging = _.extend({}, this.options.paging || {}); | |
this.setFilters(this.options.filters); | |
this.setQueries(this.options.queries); | |
this.setPills(this.options.pills); | |
if (this.options.searchString != null) { | |
this.setSearchString(this.options.searchString); | |
} | |
this.setPaging(this.options.paging); | |
if (this.options.comparator != null) { | |
this.setComparator(this.options.comparator); | |
} | |
return this; | |
}; | |
Criteria.prototype.getPaging = function() { | |
return this.options.paging; | |
}; | |
Criteria.prototype.setPaging = function(paging) { | |
paging = _.extend(this.getPaging(), paging || {}); | |
paging.page || (paging.page = null); | |
paging.limit || (paging.limit = null); | |
paging.offset || (paging.offset = null); | |
this.options.paging = paging; | |
return this; | |
}; | |
Criteria.prototype.getComparator = function() { | |
return this.options.comparator; | |
}; | |
Criteria.prototype.setComparator = function(comparator) { | |
comparator = util.generateComparator(comparator); | |
this.options.comparator = comparator; | |
return this; | |
}; | |
Criteria.prototype.getFilter = function(key) { | |
return this.options.filters[key]; | |
}; | |
Criteria.prototype.getFilters = function() { | |
return this.options.filters; | |
}; | |
Criteria.prototype.setFilters = function(filters) { | |
var key, value; | |
filters || (filters = {}); | |
for (key in filters) { | |
if (!__hasProp.call(filters, key)) continue; | |
value = filters[key]; | |
this.setFilter(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setFilter = function(name, value) { | |
var filters; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setFilter was called without both arguments'); | |
} | |
filters = this.options.filters; | |
if (value != null) { | |
filters[name] = value; | |
} else if (filters[name] != null) { | |
delete filters[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getQuery = function(key) { | |
return this.options.queries[key]; | |
}; | |
Criteria.prototype.getQueries = function() { | |
return this.options.queries; | |
}; | |
Criteria.prototype.setQueries = function(queries) { | |
var key, value; | |
queries || (queries = {}); | |
for (key in queries) { | |
if (!__hasProp.call(queries, key)) continue; | |
value = queries[key]; | |
this.setQuery(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setQuery = function(name, value) { | |
var queries; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setQuery was called without both arguments'); | |
} | |
queries = this.options.queries; | |
if (value != null) { | |
if (!(value instanceof Query)) { | |
value = new Query(value); | |
} | |
queries[name] = value; | |
} else if (queries[name] != null) { | |
delete queries[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getPill = function(key) { | |
return this.options.pills[key]; | |
}; | |
Criteria.prototype.getPills = function() { | |
return this.options.pills; | |
}; | |
Criteria.prototype.setPills = function(pills) { | |
var key, value; | |
pills || (pills = {}); | |
for (key in pills) { | |
if (!__hasProp.call(pills, key)) continue; | |
value = pills[key]; | |
this.setPill(key, value); | |
} | |
return this; | |
}; | |
Criteria.prototype.setPill = function(name, value) { | |
var pills, searchString; | |
if (typeof value === 'undefined') { | |
throw new Error('QueryCollection::setPill was called without both arguments'); | |
} | |
pills = this.getPills(); | |
searchString = this.getSearchString(); | |
if (value != null) { | |
if (!(value instanceof Pill)) { | |
value = new Pill(value); | |
} | |
if (searchString) { | |
value.setSearchString(searchString); | |
} | |
pills[name] = value; | |
} else if (pills[name] != null) { | |
delete pills[name]; | |
} | |
return this; | |
}; | |
Criteria.prototype.getCleanedSearchString = function() { | |
return this.options.cleanedSearchString; | |
}; | |
Criteria.prototype.getSearchString = function() { | |
return this.options.searchString; | |
}; | |
Criteria.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, pill, pillName, pills; | |
pills = this.options.pills; | |
cleanedSearchString = searchString; | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
cleanedSearchString = pill.setSearchString(cleanedSearchString); | |
} | |
this.options.searchString = searchString; | |
this.options.cleanedSearchString = cleanedSearchString; | |
return this; | |
}; | |
Criteria.prototype.test = function() { | |
var args; | |
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
return this.testModel.apply(this, args); | |
}; | |
Criteria.prototype.testModel = function(model, criteria) { | |
var passed; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
passed = this.testFilters(model, criteria.filters) && this.testQueries(model, criteria.queries) && this.testPills(model, criteria.pills); | |
return passed; | |
}; | |
Criteria.prototype.testModels = function(models, criteria) { | |
var comparator, finish, me, model, paging, pass, passed, start, _i, _len, _ref; | |
if (criteria == null) { | |
criteria = {}; | |
} | |
me = this; | |
passed = []; | |
paging = (_ref = criteria.paging) != null ? _ref : this.getPaging(); | |
if (criteria.comparator != null) { | |
comparator = util.generateComparator(criteria.comparator); | |
} else { | |
comparator = this.getComparator(); | |
} | |
for (_i = 0, _len = models.length; _i < _len; _i++) { | |
model = models[_i]; | |
pass = me.testModel(model, criteria); | |
if (pass) { | |
passed.push(model); | |
} | |
} | |
if (comparator) { | |
passed.sort(comparator); | |
} | |
start = paging.offset || 0; | |
if ((paging.limit != null) && paging.limit > 0) { | |
start = start + paging.limit * ((paging.page || 1) - 1); | |
finish = start + paging.limit; | |
passed = passed.slice(start, finish); | |
} else { | |
passed = passed.slice(start); | |
} | |
return passed; | |
}; | |
Criteria.prototype.testFilters = function(model, filters) { | |
var cleanedSearchString, filter, filterName, passed; | |
passed = true; | |
cleanedSearchString = this.getCleanedSearchString(); | |
if (filters == null) { | |
filters = this.getFilters(); | |
} | |
for (filterName in filters) { | |
if (!__hasProp.call(filters, filterName)) continue; | |
filter = filters[filterName]; | |
if (filter(model, cleanedSearchString) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testQueries = function(model, queries) { | |
var passed, query, queryName; | |
passed = true; | |
if (queries == null) { | |
queries = this.getQueries(); | |
} | |
for (queryName in queries) { | |
if (!__hasProp.call(queries, queryName)) continue; | |
query = queries[queryName]; | |
if (!(query instanceof Query)) { | |
query = new Query(query); | |
queries[queryName] = query; | |
} | |
if (query.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
return passed; | |
}; | |
Criteria.prototype.testPills = function(model, pills) { | |
var passed, pill, pillName, searchString; | |
passed = true; | |
searchString = this.getSearchString(); | |
if (pills == null) { | |
pills = this.getPills(); | |
} | |
if (searchString != null) { | |
for (pillName in pills) { | |
if (!__hasProp.call(pills, pillName)) continue; | |
pill = pills[pillName]; | |
if (!(pill instanceof Pill)) { | |
pill = new Pill(query); | |
pill.setSearchString(searchString); | |
pills[pillName] = pill; | |
} | |
if (pill.test(model) === false) { | |
passed = false; | |
return false; | |
} | |
} | |
} | |
return passed; | |
}; | |
return Criteria; | |
})(); | |
Pill = (function() { | |
Pill.prototype.callback = null; | |
Pill.prototype.regex = null; | |
Pill.prototype.prefixes = null; | |
Pill.prototype.searchString = null; | |
Pill.prototype.values = null; | |
Pill.prototype.logicalOperator = 'OR'; | |
function Pill(pill) { | |
var prefix, regexString, safePrefixes, safePrefixesStr, _i, _len, _ref; | |
pill || (pill = {}); | |
this.callback = pill.callback; | |
this.prefixes = pill.prefixes; | |
if (pill.logicalOperator != null) { | |
this.logicalOperator = pill.logicalOperator; | |
} | |
safePrefixes = []; | |
_ref = this.prefixes; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
prefix = _ref[_i]; | |
safePrefixes.push(util.safeRegex(prefix)); | |
} | |
safePrefixesStr = safePrefixes.join('|'); | |
regexString = "(" + safePrefixesStr + ")\\s*('[^']+'|\\\"[^\\\"]+\\\"|[^'\\\"\\s]\\S*)"; | |
this.regex = util.createRegex(regexString); | |
this; | |
} | |
Pill.prototype.setSearchString = function(searchString) { | |
var cleanedSearchString, match, value, values; | |
cleanedSearchString = searchString; | |
values = []; | |
while (match = this.regex.exec(searchString)) { | |
value = match[2].trim().replace(/(^['"]\s*|\s*['"]$)/g, ''); | |
switch (value) { | |
case 'true': | |
case 'TRUE': | |
value = true; | |
break; | |
case 'false': | |
case 'FALSE': | |
value = false; | |
break; | |
case 'null': | |
case 'NULL': | |
value = null; | |
} | |
values.push(value); | |
cleanedSearchString = cleanedSearchString.replace(match[0], '').trim(); | |
} | |
this.searchString = searchString; | |
this.values = values; | |
return cleanedSearchString; | |
}; | |
Pill.prototype.test = function(model) { | |
var pass, value, _i, _j, _len, _len1, _ref, _ref1, _ref2; | |
if ((_ref = this.values) != null ? _ref.length : void 0) { | |
if (this.logicalOperator === 'OR') { | |
pass = false; | |
_ref1 = this.values; | |
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | |
value = _ref1[_i]; | |
pass = this.callback(model, value); | |
if (pass) { | |
break; | |
} | |
} | |
} else if (this.logicalOperator === 'AND') { | |
pass = false; | |
_ref2 = this.values; | |
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | |
value = _ref2[_j]; | |
pass = this.callback(model, value); | |
if (!pass) { | |
break; | |
} | |
} | |
} else { | |
throw new Error('Unkown logical operator type'); | |
} | |
} else { | |
pass = null; | |
} | |
return pass; | |
}; | |
return Pill; | |
})(); | |
Query = (function() { | |
Query.prototype.query = null; | |
function Query(query) { | |
if (query == null) { | |
query = {}; | |
} | |
this.query = query; | |
} | |
Query.prototype.test = function(model) { | |
var $mod, beginsWithValue, empty, endWithValue, match, matchAll, matchAny, modelId, modelValue, modelValueExists, query, queryGroup, queryType, queryValue, selectorName, selectorValue, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; | |
matchAll = true; | |
matchAny = false; | |
empty = true; | |
_ref = this.query; | |
for (selectorName in _ref) { | |
if (!__hasProp.call(_ref, selectorName)) continue; | |
selectorValue = _ref[selectorName]; | |
match = false; | |
empty = false; | |
modelValue = model.get(selectorName); | |
modelId = model.get('id'); | |
modelValueExists = typeof modelValue !== 'undefined'; | |
if (!modelValueExists) { | |
modelValue = false; | |
} | |
if (selectorName === '$or' || selectorName === '$nor') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_i = 0, _len = queryGroup.length; _i < _len; _i++) { | |
query = queryGroup[_i]; | |
query = new Query(query); | |
if (query.test(model)) { | |
match = true; | |
break; | |
} | |
} | |
if (selectorName === '$nor') { | |
match = !match; | |
} | |
} else if (selectorName === '$and' || selectorName === '$not') { | |
queryGroup = util.toArrayGroup(selectorValue); | |
if (!queryGroup.length) { | |
throw new Error("Query called with an empty " + selectorName + " statement"); | |
} | |
for (_j = 0, _len1 = queryGroup.length; _j < _len1; _j++) { | |
query = queryGroup[_j]; | |
query = new Query(query); | |
match = query.test(model); | |
if (!match) { | |
break; | |
} | |
} | |
if (selectorName === '$not') { | |
match = !match; | |
} | |
} else if (util.isString(selectorValue) || util.isNumber(selectorValue) || util.isBoolean(selectorValue)) { | |
if (modelValueExists && modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isArray(selectorValue)) { | |
if (modelValueExists && (new Hash(modelValue)).isSame(selectorValue)) { | |
match = true; | |
} | |
} else if (util.isDate(selectorValue)) { | |
if (modelValueExists && modelValue.toString() === selectorValue.toString()) { | |
match = true; | |
} | |
} else if (util.isRegExp(selectorValue)) { | |
if (modelValueExists && selectorValue.test(modelValue)) { | |
match = true; | |
} | |
} else if (util.isNull(selectorValue)) { | |
if (modelValue === selectorValue) { | |
match = true; | |
} | |
} else if (util.isObject(selectorValue)) { | |
for (queryType in selectorValue) { | |
if (!__hasProp.call(selectorValue, queryType)) continue; | |
queryValue = selectorValue[queryType]; | |
switch (queryType) { | |
case '$beginsWith': | |
case '$startsWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_k = 0, _len2 = queryValue.length; _k < _len2; _k++) { | |
beginsWithValue = queryValue[_k]; | |
if (modelValue.substr(0, beginsWithValue.length) === beginsWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$endsWith': | |
case '$finishesWith': | |
if (queryValue && modelValueExists && util.isString(modelValue)) { | |
if (!util.isArray(queryValue)) { | |
queryValue = [queryValue]; | |
} | |
for (_l = 0, _len3 = queryValue.length; _l < _len3; _l++) { | |
endWithValue = queryValue[_l]; | |
if (modelValue.substr(endWithValue.length * -1) === endWithValue) { | |
match = true; | |
break; | |
} | |
} | |
} | |
break; | |
case '$all': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasAll(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$in': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) || (new Hash(queryValue)).hasIn(modelValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$nin': | |
if ((queryValue != null) && modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue) === false && (new Hash(queryValue)).hasIn(modelValue) === false) { | |
match = true; | |
} | |
} | |
break; | |
case '$has': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$hasAll': | |
if (modelValueExists) { | |
if ((new Hash(modelValue)).hasIn(queryValue)) { | |
match = true; | |
} | |
} | |
break; | |
case '$size': | |
case '$length': | |
if (modelValue.length != null) { | |
if (modelValue.length === queryValue) { | |
match = true; | |
} | |
} | |
break; | |
case '$type': | |
if (typeof modelValue === queryValue) { | |
match = true; | |
} | |
break; | |
case '$like': | |
if (util.isString(modelValue) && modelValue.toLowerCase().indexOf(queryValue.toLowerCase()) !== -1) { | |
match = true; | |
} | |
break; | |
case '$likeSensitive': | |
if (util.isString(modelValue) && modelValue.indexOf(queryValue) !== -1) { | |
match = true; | |
} | |
break; | |
case '$exists': | |
if (queryValue === modelValueExists) { | |
match = true; | |
} | |
break; | |
case '$mod': | |
if (modelValueExists) { | |
$mod = queryValue; | |
if (!util.isArray($mod)) { | |
$mod = [$mod]; | |
} | |
if ($mod.length === 1) { | |
$mod.push(0); | |
} | |
if ((modelValue % $mod[0]) === $mod[1]) { | |
match = true; | |
} | |
} | |
break; | |
case '$eq': | |
case '$equal': | |
if (util.isEqual(modelValue, queryValue)) { | |
match = true; | |
} | |
break; | |
case '$ne': | |
if (modelValue !== queryValue) { | |
match = true; | |
} | |
break; | |
case '$lt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue < queryValue) { | |
match = true; | |
} | |
break; | |
case '$gt': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue > queryValue) { | |
match = true; | |
} | |
break; | |
case '$bt': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] < modelValue && modelValue < queryValue[1]) { | |
match = true; | |
} | |
break; | |
case '$lte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue <= queryValue) { | |
match = true; | |
} | |
break; | |
case '$gte': | |
if ((queryValue != null) && util.isComparable(modelValue) && modelValue >= queryValue) { | |
match = true; | |
} | |
break; | |
case '$bte': | |
if ((queryValue != null) && util.isComparable(modelValue) && queryValue[0] <= modelValue && modelValue <= queryValue[1]) { | |
match = true; | |
} | |
} | |
} | |
} | |
if (match) { | |
matchAny = true; | |
} else { | |
matchAll = false; | |
} | |
} | |
if (matchAll && !matchAny) { | |
matchAll = false; | |
} | |
return matchAll; | |
}; | |
return Query; | |
})(); | |
queryEngine = { | |
safeRegex: util.safeRegex, | |
createRegex: util.createRegex, | |
createSafeRegex: util.createSafeRegex, | |
generateComparator: util.generateComparator, | |
toArray: util.toArray, | |
Backbone: Backbone, | |
Hash: Hash, | |
QueryCollection: QueryCollection, | |
Criteria: Criteria, | |
Query: Query, | |
Pill: Pill, | |
createCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options); | |
return collection; | |
}, | |
createLiveCollection: function(models, options) { | |
var collection; | |
models = util.toArray(models); | |
collection = new QueryCollection(models, options).live(true); | |
return collection; | |
} | |
}; | |
if (typeof module !== "undefined" && module !== null) { | |
module.exports = queryEngine; | |
} else { | |
this.queryEngineRewrite3 = queryEngine; | |
} | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment