devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
179 lines (146 loc) • 5.44 kB
JavaScript
"use strict";
var Class = require("../../core/class"),
compileGetter = require("../../core/utils/data").compileGetter,
isFunction = require("../../core/utils/type").isFunction,
errors = require("../../data/errors").errors,
dataUtils = require("../../data/utils");
function depthFirstSearch(i, depth, root, callback) {
var j = 0;
if (i < depth) {
for (; j < root.items.length; j++) {
depthFirstSearch(i + 1, depth, root.items[j], callback);
}
}
if (i === depth) {
callback(root);
}
}
// NOTE: https://github.com/jquery/jquery/blame/master/src/core.js#L392
function map(array, callback) {
var i, result;
if ("map" in array) {
return array.map(callback);
}
result = new Array(array.length);
for (i in array) {
result[i] = callback(array[i], i);
}
return result;
}
function isEmpty(x) {
return x !== x || x === "" || x === null || x === undefined;
}
function isCount(aggregator) {
return aggregator === dataUtils.aggregators.count;
}
function normalizeAggregate(aggregate) {
var selector = compileGetter(aggregate.selector),
skipEmptyValues = "skipEmptyValues" in aggregate ? aggregate.skipEmptyValues : true,
aggregator = aggregate.aggregator;
if (typeof aggregator === "string") {
aggregator = dataUtils.aggregators[aggregator];
if (!aggregator) {
throw errors.Error("E4001", aggregate.aggregator);
}
}
return {
selector: selector,
aggregator: aggregator,
skipEmptyValues: skipEmptyValues
};
}
module.exports = Class.inherit({
ctor: function ctor(options) {
this._data = options.data;
this._groupLevel = options.groupLevel || 0;
this._totalAggregates = map(options.totalAggregates || [], normalizeAggregate);
this._groupAggregates = map(options.groupAggregates || [], normalizeAggregate);
this._totals = [];
},
calculate: function calculate() {
if (this._totalAggregates.length) {
this._calculateTotals(0, { items: this._data });
}
if (this._groupAggregates.length && this._groupLevel > 0) {
this._calculateGroups({ items: this._data });
}
},
totalAggregates: function totalAggregates() {
return this._totals;
},
_aggregate: function _aggregate(aggregates, data, container) {
var i, j;
for (i = 0; i < aggregates.length; i++) {
if (isCount(aggregates[i].aggregator)) {
container[i] = (container[i] || 0) + data.items.length;
continue;
}
for (j = 0; j < data.items.length; j++) {
this._accumulate(i, aggregates[i], container, data.items[j]);
}
}
},
_calculateTotals: function _calculateTotals(level, data) {
var i;
if (level === 0) {
this._totals = this._seed(this._totalAggregates);
}
if (level === this._groupLevel) {
this._aggregate(this._totalAggregates, data, this._totals);
} else {
for (i = 0; i < data.items.length; i++) {
this._calculateTotals(level + 1, data.items[i]);
}
}
if (level === 0) {
this._totals = this._finalize(this._totalAggregates, this._totals);
}
},
_calculateGroups: function _calculateGroups(root) {
var maxLevel = this._groupLevel,
currentLevel = maxLevel + 1,
seedFn = this._seed.bind(this, this._groupAggregates),
stepFn = this._aggregate.bind(this, this._groupAggregates),
finalizeFn = this._finalize.bind(this, this._groupAggregates);
function aggregator(node) {
node.aggregates = seedFn();
if (currentLevel === maxLevel) {
stepFn(node, node.aggregates);
} else {
depthFirstSearch(currentLevel, maxLevel, node, function (innerNode) {
stepFn(innerNode, node.aggregates);
});
}
node.aggregates = finalizeFn(node.aggregates);
}
while (--currentLevel > 0) {
depthFirstSearch(0, currentLevel, root, aggregator);
}
},
_seed: function _seed(aggregates) {
return map(aggregates, function (aggregate) {
var aggregator = aggregate.aggregator,
seed = "seed" in aggregator ? isFunction(aggregator.seed) ? aggregator.seed() : aggregator.seed : NaN;
return seed;
});
},
_accumulate: function _accumulate(aggregateIndex, aggregate, results, item) {
var value = aggregate.selector(item),
aggregator = aggregate.aggregator,
skipEmptyValues = aggregate.skipEmptyValues;
if (skipEmptyValues && isEmpty(value)) {
return;
}
if (results[aggregateIndex] !== results[aggregateIndex]) {
results[aggregateIndex] = value;
} else {
results[aggregateIndex] = aggregator.step(results[aggregateIndex], value);
}
},
_finalize: function _finalize(aggregates, results) {
return map(aggregates, function (aggregate, index) {
var fin = aggregate.aggregator.finalize;
return fin ? fin(results[index]) : results[index];
});
}
});