UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

559 lines (555 loc) • 17.1 kB
/** * DevExtreme (data/array_query.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "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() { var result = []; this.reset(); while (this.next()) { result.push(this.current()) } return result }, countable: function() { return false } }); var ArrayIterator = Iterator.inherit({ ctor: function(array) { this.array = array; this.index = -1 }, next: function() { if (this.index + 1 < this.array.length) { this.index++; return true } return false }, current: function() { return this.array[this.index] }, reset: function() { this.index = -1 }, toArray: function() { return this.array.slice(0) }, countable: function() { return true }, count: function() { return this.array.length } }); var WrappedIterator = Iterator.inherit({ ctor: function(iter) { this.iter = iter }, next: function() { return this.iter.next() }, current: function() { return this.iter.current() }, reset: function() { return this.iter.reset() } }); var MapIterator = WrappedIterator.inherit({ ctor: function(iter, mapper) { this.callBase(iter); this.index = -1; this.mapper = mapper }, current: function() { return this.mapper(this.callBase(), this.index) }, next: function() { var hasNext = this.callBase(); if (hasNext) { this.index++ } return hasNext } }); var defaultCompare = function(xValue, yValue) { xValue = toComparable(xValue); yValue = toComparable(yValue); if (null === xValue && null !== yValue) { return -1 } if (null !== xValue && null === yValue) { return 1 } if (void 0 === xValue && void 0 !== yValue) { return 1 } if (void 0 !== xValue && void 0 === yValue) { return -1 } if (xValue < yValue) { return -1 } if (xValue > yValue) { return 1 } return 0 }; var SortIterator = Iterator.inherit({ ctor: function(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(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() { this._ensureSorted(); return this.sortedIter.next() }, current: function() { this._ensureSorted(); return this.sortedIter.current() }, reset: function() { delete this.sortedIter }, countable: function() { return this.sortedIter || this.iter.countable() }, count: function() { if (this.sortedIter) { return this.sortedIter.count() } return this.iter.count() }, _ensureSorted: function() { 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(record, index) { return { index: index, value: record } }, _unwrap: function(wrappedItem) { return wrappedItem.value }, _compare: function(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(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(value) { return typeUtils.isDefined(value) ? value.toString() : "" }; var compileBinary = function(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 0 === toComparable(toString(getter(obj))).indexOf(value) }; 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) { obj = toComparable(getter(obj)); var result = useStrictComparison(value) ? obj === value : obj == value; if (negate) { result = !result } return result } } function useStrictComparison(value) { return "" === value || 0 === value || false === value } 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(iter, criteria) { this.callBase(iter); this.criteria = compileCriteria(criteria) }, next: function() { while (this.iter.next()) { if (this.criteria(this.current())) { return true } } return false } }); var GroupIterator = Iterator.inherit({ ctor: function(iter, getter) { this.iter = iter; this.getter = getter }, next: function() { this._ensureGrouped(); return this.groupedIter.next() }, current: function() { this._ensureGrouped(); return this.groupedIter.current() }, reset: function() { delete this.groupedIter }, countable: function() { return !!this.groupedIter }, count: function() { return this.groupedIter.count() }, _ensureGrouped: function() { 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(iter, getter) { this.callBase(iter); this.getter = compileGetter(getter) }, current: function() { return this.getter(this.callBase()) }, countable: function() { return this.iter.countable() }, count: function() { return this.iter.count() } }); var SliceIterator = WrappedIterator.inherit({ ctor: function(iter, skip, take) { this.callBase(iter); this.skip = Math.max(0, skip); this.take = Math.max(0, take); this.pos = 0 }, next: function() { 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() { this.callBase(); this.pos = 0 }, countable: function() { return this.iter.countable() }, count: function() { 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(error) { var handler = queryOptions.errorHandler; if (handler) { handler(error) } errorsModule._errorHandler(error) }; var aggregateCore = function(aggregator) { var seed, d = (new Deferred).fail(handleError), 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(seed, step, finalize) { if (arguments.length < 2) { return aggregateCore({ step: arguments[0] }) } return aggregateCore({ seed: seed, step: step, finalize: finalize }) }; var standardAggregate = function(name) { return aggregateCore(dataUtils.aggregators[name]) }; var select = function(getter) { if (!typeUtils.isFunction(getter) && !Array.isArray(getter)) { getter = [].slice.call(arguments) } return chainQuery(new SelectIterator(iter, getter)) }; var selectProp = function(name) { return select(compileGetter(name)) }; var chainQuery = function(iter) { return arrayQueryImpl(iter, queryOptions) }; return { toArray: function() { return iter.toArray() }, enumerate: function() { var d = (new Deferred).fail(handleError); try { d.resolve(iter.toArray()) } catch (x) { d.reject(x) } return d.promise() }, sortBy: function(getter, desc, compare) { return chainQuery(new SortIterator(iter, getter, desc, compare)) }, thenBy: function(getter, desc, compare) { if (iter instanceof SortIterator) { return chainQuery(iter.thenBy(getter, desc, compare)) } throw errorsModule.errors.Error("E4004") }, filter: function(criteria) { if (!Array.isArray(criteria)) { criteria = [].slice.call(arguments) } return chainQuery(new FilterIterator(iter, criteria)) }, slice: function(skip, take) { if (void 0 === take) { take = Number.MAX_VALUE } return chainQuery(new SliceIterator(iter, skip, take)) }, select: select, groupBy: function(getter) { return chainQuery(new GroupIterator(iter, getter)) }, aggregate: aggregate, count: function() { 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(getter) { if (getter) { return selectProp(getter).sum() } return standardAggregate("sum") }, min: function(getter) { if (getter) { return selectProp(getter).min() } return standardAggregate("min") }, max: function(getter) { if (getter) { return selectProp(getter).max() } return standardAggregate("max") }, avg: function(getter) { if (getter) { return selectProp(getter).avg() } return standardAggregate("avg") } } }; module.exports = arrayQueryImpl;