devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
671 lines (540 loc) • 17.1 kB
JavaScript
"use strict";
var Class = require("../core/class"),
typeUtils = require("../core/utils/type"),
iteratorUtils = require("../core/utils/iterator"),
compileGetter = require("../core/utils/data").compileGetter,
toComparable = require("../core/utils/data").toComparable,
Deferred = require("../core/utils/deferred").Deferred,
errorsModule = require("./errors"),
dataUtils = require("./utils");
var Iterator = Class.inherit({
toArray: function toArray() {
var result = [];
this.reset();
while (this.next()) {
result.push(this.current());
}
return result;
},
countable: function countable() {
return false;
}
});
var ArrayIterator = Iterator.inherit({
ctor: function ctor(array) {
this.array = array;
this.index = -1;
},
next: function next() {
if (this.index + 1 < this.array.length) {
this.index++;
return true;
}
return false;
},
current: function current() {
return this.array[this.index];
},
reset: function reset() {
this.index = -1;
},
toArray: function toArray() {
return this.array.slice(0);
},
countable: function countable() {
return true;
},
count: function count() {
return this.array.length;
}
});
var WrappedIterator = Iterator.inherit({
ctor: function ctor(iter) {
this.iter = iter;
},
next: function next() {
return this.iter.next();
},
current: function current() {
return this.iter.current();
},
reset: function reset() {
return this.iter.reset();
}
});
var MapIterator = WrappedIterator.inherit({
ctor: function ctor(iter, mapper) {
this.callBase(iter);
this.index = -1;
this.mapper = mapper;
},
current: function current() {
return this.mapper(this.callBase(), this.index);
},
next: function next() {
var hasNext = this.callBase();
if (hasNext) {
this.index++;
}
return hasNext;
}
});
var defaultCompare = function defaultCompare(xValue, yValue) {
xValue = toComparable(xValue);
yValue = toComparable(yValue);
if (xValue === null && yValue !== null) {
return -1;
}
if (xValue !== null && yValue === null) {
return 1;
}
if (xValue === undefined && yValue !== undefined) {
return 1;
}
if (xValue !== undefined && yValue === undefined) {
return -1;
}
if (xValue < yValue) {
return -1;
}
if (xValue > yValue) {
return 1;
}
return 0;
};
var SortIterator = Iterator.inherit({
ctor: function ctor(iter, getter, desc, compare) {
if (!(iter instanceof MapIterator)) {
iter = new MapIterator(iter, this._wrap);
}
this.iter = iter;
this.rules = [{ getter: getter, desc: desc, compare: compare }];
},
thenBy: function thenBy(getter, desc, compare) {
var result = new SortIterator(this.sortedIter || this.iter, getter, desc, compare);
if (!this.sortedIter) {
result.rules = this.rules.concat(result.rules);
}
return result;
},
next: function next() {
this._ensureSorted();
return this.sortedIter.next();
},
current: function current() {
this._ensureSorted();
return this.sortedIter.current();
},
reset: function reset() {
delete this.sortedIter;
},
countable: function countable() {
return this.sortedIter || this.iter.countable();
},
count: function count() {
if (this.sortedIter) {
return this.sortedIter.count();
}
return this.iter.count();
},
_ensureSorted: function _ensureSorted() {
var that = this;
if (that.sortedIter) {
return;
}
iteratorUtils.each(that.rules, function () {
this.getter = compileGetter(this.getter);
});
that.sortedIter = new MapIterator(new ArrayIterator(this.iter.toArray().sort(function (x, y) {
return that._compare(x, y);
})), that._unwrap);
},
_wrap: function _wrap(record, index) {
return {
index: index,
value: record
};
},
_unwrap: function _unwrap(wrappedItem) {
return wrappedItem.value;
},
_compare: function _compare(x, y) {
var xIndex = x.index,
yIndex = y.index;
x = x.value;
y = y.value;
if (x === y) {
return xIndex - yIndex;
}
for (var i = 0, rulesCount = this.rules.length; i < rulesCount; i++) {
var rule = this.rules[i],
xValue = rule.getter(x),
yValue = rule.getter(y),
compare = rule.compare || defaultCompare,
compareResult = compare(xValue, yValue);
if (compareResult) {
return rule.desc ? -compareResult : compareResult;
}
}
return xIndex - yIndex;
}
});
var compileCriteria = function () {
var compileGroup = function compileGroup(crit) {
var ops = [];
var isConjunctiveOperator = false;
var isConjunctiveNextOperator = false;
iteratorUtils.each(crit, function () {
if (Array.isArray(this) || typeUtils.isFunction(this)) {
if (ops.length > 1 && isConjunctiveOperator !== isConjunctiveNextOperator) {
throw new errorsModule.errors.Error("E4019");
}
ops.push(compileCriteria(this));
isConjunctiveOperator = isConjunctiveNextOperator;
isConjunctiveNextOperator = true;
} else {
isConjunctiveNextOperator = dataUtils.isConjunctiveOperator(this);
}
});
return function (d) {
var result = isConjunctiveOperator;
for (var i = 0; i < ops.length; i++) {
if (ops[i](d) !== isConjunctiveOperator) {
result = !isConjunctiveOperator;
break;
}
}
return result;
};
};
var toString = function toString(value) {
return typeUtils.isDefined(value) ? value.toString() : '';
};
var compileBinary = function compileBinary(crit) {
crit = dataUtils.normalizeBinaryCriterion(crit);
var getter = compileGetter(crit[0]),
op = crit[1],
value = crit[2];
value = toComparable(value);
switch (op.toLowerCase()) {
case "=":
return compileEquals(getter, value);
case "<>":
return compileEquals(getter, value, true);
case ">":
return function (obj) {
return toComparable(getter(obj)) > value;
};
case "<":
return function (obj) {
return toComparable(getter(obj)) < value;
};
case ">=":
return function (obj) {
return toComparable(getter(obj)) >= value;
};
case "<=":
return function (obj) {
return toComparable(getter(obj)) <= value;
};
case "startswith":
return function (obj) {
return toComparable(toString(getter(obj))).indexOf(value) === 0;
};
case "endswith":
return function (obj) {
var getterValue = toComparable(toString(getter(obj))),
searchValue = toString(value);
if (getterValue.length < searchValue.length) {
return false;
}
return getterValue.lastIndexOf(value) === getterValue.length - value.length;
};
case "contains":
return function (obj) {
return toComparable(toString(getter(obj))).indexOf(value) > -1;
};
case "notcontains":
return function (obj) {
return toComparable(toString(getter(obj))).indexOf(value) === -1;
};
}
throw errorsModule.errors.Error("E4003", op);
};
function compileEquals(getter, value, negate) {
return function (obj) {
/* jshint eqeqeq:false */
obj = toComparable(getter(obj));
var result = useStrictComparison(value) ? obj === value : obj == value;
if (negate) {
result = !result;
}
return result;
};
}
function useStrictComparison(value) {
return value === "" || value === 0 || value === false;
}
function compileUnary(crit) {
var op = crit[0],
criteria = compileCriteria(crit[1]);
if (op === "!") {
return function (obj) {
return !criteria(obj);
};
}
throw errorsModule.errors.Error("E4003", op);
}
return function (crit) {
if (typeUtils.isFunction(crit)) {
return crit;
}
if (Array.isArray(crit[0])) {
return compileGroup(crit);
}
if (dataUtils.isUnaryOperation(crit)) {
return compileUnary(crit);
}
return compileBinary(crit);
};
}();
var FilterIterator = WrappedIterator.inherit({
ctor: function ctor(iter, criteria) {
this.callBase(iter);
this.criteria = compileCriteria(criteria);
},
next: function next() {
while (this.iter.next()) {
if (this.criteria(this.current())) {
return true;
}
}
return false;
}
});
var GroupIterator = Iterator.inherit({
ctor: function ctor(iter, getter) {
this.iter = iter;
this.getter = getter;
},
next: function next() {
this._ensureGrouped();
return this.groupedIter.next();
},
current: function current() {
this._ensureGrouped();
return this.groupedIter.current();
},
reset: function reset() {
delete this.groupedIter;
},
countable: function countable() {
return !!this.groupedIter;
},
count: function count() {
return this.groupedIter.count();
},
_ensureGrouped: function _ensureGrouped() {
if (this.groupedIter) {
return;
}
var hash = {},
keys = [],
iter = this.iter,
getter = compileGetter(this.getter);
iter.reset();
while (iter.next()) {
var current = iter.current(),
key = getter(current);
if (key in hash) {
hash[key].push(current);
} else {
hash[key] = [current];
keys.push(key);
}
}
this.groupedIter = new ArrayIterator(iteratorUtils.map(keys, function (key) {
return { key: key, items: hash[key] };
}));
}
});
var SelectIterator = WrappedIterator.inherit({
ctor: function ctor(iter, getter) {
this.callBase(iter);
this.getter = compileGetter(getter);
},
current: function current() {
return this.getter(this.callBase());
},
countable: function countable() {
return this.iter.countable();
},
count: function count() {
return this.iter.count();
}
});
var SliceIterator = WrappedIterator.inherit({
ctor: function ctor(iter, skip, take) {
this.callBase(iter);
this.skip = Math.max(0, skip);
this.take = Math.max(0, take);
this.pos = 0;
},
next: function next() {
if (this.pos >= this.skip + this.take) {
return false;
}
while (this.pos < this.skip && this.iter.next()) {
this.pos++;
}
this.pos++;
return this.iter.next();
},
reset: function reset() {
this.callBase();
this.pos = 0;
},
countable: function countable() {
return this.iter.countable();
},
count: function count() {
return Math.min(this.iter.count() - this.skip, this.take);
}
});
var arrayQueryImpl = function arrayQueryImpl(iter, queryOptions) {
queryOptions = queryOptions || {};
if (!(iter instanceof Iterator)) {
iter = new ArrayIterator(iter);
}
var handleError = function handleError(error) {
var handler = queryOptions.errorHandler;
if (handler) {
handler(error);
}
errorsModule._errorHandler(error);
};
var aggregateCore = function aggregateCore(aggregator) {
var d = new Deferred().fail(handleError),
seed,
step = aggregator.step,
finalize = aggregator.finalize;
try {
iter.reset();
if ("seed" in aggregator) {
seed = aggregator.seed;
} else {
seed = iter.next() ? iter.current() : NaN;
}
var accumulator = seed;
while (iter.next()) {
accumulator = step(accumulator, iter.current());
}
d.resolve(finalize ? finalize(accumulator) : accumulator);
} catch (x) {
d.reject(x);
}
return d.promise();
};
var aggregate = function aggregate(seed, step, finalize) {
if (arguments.length < 2) {
return aggregateCore({ step: arguments[0] });
}
return aggregateCore({
seed: seed,
step: step,
finalize: finalize
});
};
var standardAggregate = function standardAggregate(name) {
return aggregateCore(dataUtils.aggregators[name]);
};
var select = function select(getter) {
if (!typeUtils.isFunction(getter) && !Array.isArray(getter)) {
getter = [].slice.call(arguments);
}
return chainQuery(new SelectIterator(iter, getter));
};
var selectProp = function selectProp(name) {
return select(compileGetter(name));
};
var chainQuery = function chainQuery(iter) {
return arrayQueryImpl(iter, queryOptions);
};
return {
toArray: function toArray() {
return iter.toArray();
},
enumerate: function enumerate() {
var d = new Deferred().fail(handleError);
try {
d.resolve(iter.toArray());
} catch (x) {
d.reject(x);
}
return d.promise();
},
sortBy: function sortBy(getter, desc, compare) {
return chainQuery(new SortIterator(iter, getter, desc, compare));
},
thenBy: function thenBy(getter, desc, compare) {
if (iter instanceof SortIterator) {
return chainQuery(iter.thenBy(getter, desc, compare));
}
throw errorsModule.errors.Error("E4004");
},
filter: function filter(criteria) {
if (!Array.isArray(criteria)) {
criteria = [].slice.call(arguments);
}
return chainQuery(new FilterIterator(iter, criteria));
},
slice: function slice(skip, take) {
if (take === undefined) {
take = Number.MAX_VALUE;
}
return chainQuery(new SliceIterator(iter, skip, take));
},
select: select,
groupBy: function groupBy(getter) {
return chainQuery(new GroupIterator(iter, getter));
},
aggregate: aggregate,
count: function count() {
if (iter.countable()) {
var d = new Deferred().fail(handleError);
try {
d.resolve(iter.count());
} catch (x) {
d.reject(x);
}
return d.promise();
}
return standardAggregate("count");
},
sum: function sum(getter) {
if (getter) {
return selectProp(getter).sum();
}
return standardAggregate("sum");
},
min: function min(getter) {
if (getter) {
return selectProp(getter).min();
}
return standardAggregate("min");
},
max: function max(getter) {
if (getter) {
return selectProp(getter).max();
}
return standardAggregate("max");
},
avg: function avg(getter) {
if (getter) {
return selectProp(getter).avg();
}
return standardAggregate("avg");
}
};
};
module.exports = arrayQueryImpl;