devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
445 lines (439 loc) • 17.6 kB
JavaScript
/**
* DevExtreme (ui/pivot_grid/ui.pivot_grid.summary_display_modes.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 typeUtils = require("../../core/utils/type"),
extend = require("../../core/utils/extend").extend,
inArray = require("../../core/utils/array").inArray,
isDefined = typeUtils.isDefined,
pivotGridUtils = require("./ui.pivot_grid.utils"),
findField = pivotGridUtils.findField,
foreachTree = pivotGridUtils.foreachTree,
COLUMN = "column",
ROW = "row",
NULL = null,
calculatePercentValue = function(value, totalValue) {
var result = value / totalValue;
if (!isDefined(value) || isNaN(result)) {
result = NULL
}
return result
},
_percentOfGrandTotal = function(e, dimension) {
return calculatePercentValue(e.value(), e.grandTotal(dimension).value())
},
percentOfParent = function(e, dimension) {
var parent = e.parent(dimension),
parentValue = parent ? parent.value() : e.value();
return calculatePercentValue(e.value(), parentValue)
},
createAbsoluteVariationExp = function(allowCrossGroup) {
return function(e) {
var prevCell = e.prev(COLUMN, allowCrossGroup),
prevValue = prevCell && prevCell.value();
if (isDefined(prevValue) && isDefined(e.value())) {
return e.value() - prevValue
}
return NULL
}
},
createPercentVariationExp = function(allowCrossGroup) {
var absoluteExp = createAbsoluteVariationExp(allowCrossGroup);
return function(e) {
var absVar = absoluteExp(e),
prevCell = e.prev(COLUMN, allowCrossGroup),
prevValue = prevCell && prevCell.value();
return absVar !== NULL && prevValue ? absVar / prevValue : NULL
}
},
summaryDictionary = {
percentOfColumnTotal: function(e) {
return percentOfParent(e, ROW)
},
percentOfRowTotal: function(e) {
return percentOfParent(e, COLUMN)
},
percentOfColumnGrandTotal: function(e) {
return _percentOfGrandTotal(e, ROW)
},
percentOfRowGrandTotal: function(e) {
return _percentOfGrandTotal(e, COLUMN)
},
percentOfGrandTotal: function(e) {
return _percentOfGrandTotal(e)
}
},
getPrevCellCrossGroup = function getPrevCellCrossGroup(cell, direction) {
if (!cell || !cell.parent(direction)) {
return
}
var prevCell = cell.prev(direction);
if (!prevCell) {
prevCell = getPrevCellCrossGroup(cell.parent(direction), direction)
}
return prevCell
},
createRunningTotalExpr = function(field) {
if (!field.runningTotal) {
return
}
var direction = field.runningTotal === COLUMN ? ROW : COLUMN;
return function(e) {
var prevCell = field.allowCrossGroupCalculation ? getPrevCellCrossGroup(e, direction) : e.prev(direction, false),
value = e.value(true),
prevValue = prevCell && prevCell.value(true);
if (isDefined(prevValue) && isDefined(value)) {
value = prevValue + value
} else {
if (isDefined(prevValue)) {
value = prevValue
}
}
return value
}
};
function createCache() {
return {
fields: {},
positions: {}
}
}
function getFieldPos(descriptions, field, cache) {
var fieldIndex, allFields, fieldParams = {
index: -1
};
if (!typeUtils.isObject(field)) {
if (cache.fields[field]) {
field = cache[field]
} else {
allFields = descriptions.columns.concat(descriptions.rows).concat(descriptions.values);
fieldIndex = findField(allFields, field);
field = cache[field] = allFields[fieldIndex]
}
}
if (field) {
var area = field.area || "data";
fieldParams = cache.positions[field.index] = cache.positions[field.index] || {
area: area,
index: inArray(field, descriptions["data" === area ? "values" : area + "s"])
}
}
return fieldParams
}
function getPathFieldName(dimension) {
return dimension === ROW ? "_rowPath" : "_columnPath"
}
var SummaryCell = function(columnPath, rowPath, data, descriptions, fieldIndex, fieldsCache) {
this._columnPath = columnPath;
this._rowPath = rowPath;
this._fieldIndex = fieldIndex;
this._fieldsCache = fieldsCache || createCache();
this._data = data;
this._descriptions = descriptions;
var cell = data.values && data.values[rowPath[0].index] && data.values[rowPath[0].index][columnPath[0].index];
if (cell) {
cell.originalCell = cell.originalCell || cell.slice();
this._cell = cell
}
};
SummaryCell.prototype = extend(SummaryCell.prototype, {
_getPath: function(dimension) {
return this[getPathFieldName(dimension)]
},
_getDimension: function(dimension) {
dimension = dimension === ROW ? "rows" : "columns";
return this._descriptions[dimension]
},
_createCell: function(config) {
var that = this;
return new SummaryCell(config._columnPath || that._columnPath, config._rowPath || that._rowPath, that._data, that._descriptions, that._fieldIndex)
},
parent: function(direction) {
var path = this._getPath(direction).slice(),
config = {};
path.shift();
if (path.length) {
config[getPathFieldName(direction)] = path;
return this._createCell(config)
}
return NULL
},
children: function(direction) {
var path = this._getPath(direction).slice(),
item = path[0],
result = [],
cellConfig = {};
if (item.children) {
for (var i = 0; i < item.children.length; i++) {
cellConfig[getPathFieldName(direction)] = [item.children[i]].concat(path.slice());
result.push(this._createCell(cellConfig))
}
}
return result
},
grandTotal: function(direction) {
var config = {},
rowPath = this._rowPath,
columnPath = this._columnPath,
dimensionPath = this._getPath(direction),
pathFieldName = getPathFieldName(direction);
if (!direction) {
config._rowPath = [rowPath[rowPath.length - 1]];
config._columnPath = [columnPath[columnPath.length - 1]]
} else {
config[pathFieldName] = [dimensionPath[dimensionPath.length - 1]]
}
return this._createCell(config)
},
next: function(direction, allowCrossGroup) {
var siblings, index, currentPath = this._getPath(direction),
item = currentPath[0],
parent = this.parent(direction);
if (parent) {
index = inArray(item, currentPath[1].children);
siblings = parent.children(direction);
if (siblings[index + 1]) {
return siblings[index + 1]
}
}
if (allowCrossGroup && parent) {
do {
parent = parent.next(direction, allowCrossGroup);
siblings = parent ? parent.children(direction) : []
} while (parent && !siblings.length);
return siblings[0] || NULL
}
return NULL
},
prev: function(direction, allowCrossGroup) {
var siblings, index, currentPath = this._getPath(direction),
item = currentPath[0],
parent = this.parent(direction);
if (parent) {
index = inArray(item, currentPath[1].children);
siblings = parent.children(direction);
if (siblings[index - 1]) {
return siblings[index - 1]
}
}
if (allowCrossGroup && parent) {
do {
parent = parent.prev(direction, allowCrossGroup);
siblings = parent ? parent.children(direction) : []
} while (parent && !siblings.length);
return siblings[siblings.length - 1] || NULL
}
return NULL
},
cell: function() {
return this._cell
},
field: function field(area) {
var path = this._getPath(area),
descriptions = this._getDimension(area),
field = descriptions[path.length - 2];
return field || NULL
},
child: function(direction, fieldValue) {
var childLevelField, children = this.children(direction);
for (var i = 0; i < children.length; i++) {
childLevelField = childLevelField || children[i].field(direction);
if (children[i].value(childLevelField) === fieldValue) {
return children[i]
}
}
return NULL
},
slice: function(field, value) {
var childItems, path, currentValue, level, that = this,
config = {},
fieldPos = getFieldPos(this._descriptions, field, this._fieldsCache),
area = fieldPos.area,
fieldIndex = fieldPos.index,
sliceCell = NULL,
newPath = [];
if (area === ROW || area === COLUMN) {
path = this._getPath(area).slice();
level = fieldIndex !== -1 && path.length - 2 - fieldIndex;
if (path[level]) {
newPath[path.length - 1] = path[path.length - 1];
for (var i = level; i >= 0; i--) {
if (path[i + 1]) {
childItems = path[i + 1].children || [];
currentValue = i === level ? value : path[i].value;
path[i] = void 0;
for (var childIndex = 0; childIndex < childItems.length; childIndex++) {
if (childItems[childIndex].value === currentValue) {
path[i] = childItems[childIndex];
break
}
}
}
if (void 0 === path[i]) {
return sliceCell
}
}
config[getPathFieldName(area)] = path;
sliceCell = that._createCell(config)
}
}
return sliceCell
},
value: function(arg1, arg2) {
var path, level, cell = this._cell,
fieldIndex = this._fieldIndex,
fistArgIsBoolean = true === arg1 || false === arg1,
field = !fistArgIsBoolean ? arg1 : NULL,
needCalculatedValue = fistArgIsBoolean && arg1 || arg2;
if (isDefined(field)) {
var fieldPos = getFieldPos(this._descriptions, field, this._fieldsCache);
fieldIndex = fieldPos.index;
if ("data" !== fieldPos.area) {
path = this._getPath(fieldPos.area);
level = fieldIndex !== -1 && path.length - 2 - fieldIndex;
return path[level] && path[level].value
}
}
if (cell && cell.originalCell) {
return needCalculatedValue ? cell[fieldIndex] : cell.originalCell[fieldIndex]
}
return NULL
}
});
function getExpression(field) {
var summaryDisplayMode = field.summaryDisplayMode,
crossGroupCalculation = field.allowCrossGroupCalculation,
expression = NULL;
if (typeUtils.isFunction(field.calculateSummaryValue)) {
expression = field.calculateSummaryValue
} else {
if (summaryDisplayMode) {
if ("absoluteVariation" === summaryDisplayMode) {
expression = createAbsoluteVariationExp(crossGroupCalculation)
} else {
if ("percentVariation" === summaryDisplayMode) {
expression = createPercentVariationExp(crossGroupCalculation)
} else {
expression = summaryDictionary[summaryDisplayMode]
}
}
if (expression && !field.format && summaryDisplayMode.indexOf("percent") !== -1) {
pivotGridUtils.setFieldProperty(field, "format", "percent")
}
}
}
return expression
}
function processDataCell(data, rowIndex, columnIndex, isRunningTotalCalculation) {
var values = data.values[rowIndex][columnIndex] = data.values[rowIndex][columnIndex] || [],
originalCell = values.originalCell;
if (!originalCell) {
return
}
if (values.allowResetting || !isRunningTotalCalculation) {
data.values[rowIndex][columnIndex] = originalCell.slice()
}
data.values[rowIndex][columnIndex].allowResetting = isRunningTotalCalculation
}
exports.applyDisplaySummaryMode = function(descriptions, data) {
var expressions = [],
columnElements = [{
index: data.grandTotalColumnIndex,
children: data.columns
}],
rowElements = [{
index: data.grandTotalRowIndex,
children: data.rows
}],
valueFields = descriptions.values,
fieldsCache = createCache();
data.values = data.values || [];
foreachTree(rowElements, function(rowPath) {
var rowItem = rowPath[0];
rowItem.isEmpty = [];
data.values[rowItem.index] = data.values[rowItem.index] || [];
foreachTree(columnElements, function(columnPath) {
var expression, expressionArg, cell, field, isEmptyCell, value, columnItem = columnPath[0];
columnItem.isEmpty = columnItem.isEmpty || [];
processDataCell(data, rowItem.index, columnItem.index, false);
for (var i = 0; i < valueFields.length; i++) {
field = valueFields[i];
expression = expressions[i] = void 0 === expressions[i] ? getExpression(field) : expressions[i];
isEmptyCell = false;
if (expression) {
expressionArg = new SummaryCell(columnPath, rowPath, data, descriptions, i, fieldsCache);
cell = expressionArg.cell();
value = cell[i] = expression(expressionArg);
isEmptyCell = null === value || void 0 === value
}
if (void 0 === columnItem.isEmpty[i]) {
columnItem.isEmpty[i] = true
}
if (void 0 === rowItem.isEmpty[i]) {
rowItem.isEmpty[i] = true
}
if (!isEmptyCell) {
rowItem.isEmpty[i] = columnItem.isEmpty[i] = false
}
}
}, false)
}, false);
data.isEmptyGrandTotalRow = rowElements[0].isEmpty;
data.isEmptyGrandTotalColumn = columnElements[0].isEmpty
};
exports.applyRunningTotal = function(descriptions, data) {
var expressions = [],
columnElements = [{
index: data.grandTotalColumnIndex,
children: data.columns
}],
rowElements = [{
index: data.grandTotalRowIndex,
children: data.rows
}],
valueFields = descriptions.values,
fieldsCache = createCache();
data.values = data.values || [];
foreachTree(rowElements, function(rowPath) {
var rowItem = rowPath[0];
data.values[rowItem.index] = data.values[rowItem.index] || [];
foreachTree(columnElements, function(columnPath) {
var expression, expressionArg, cell, field, value, columnItem = columnPath[0];
processDataCell(data, rowItem.index, columnItem.index, true);
for (var i = 0; i < valueFields.length; i++) {
field = valueFields[i];
expression = expressions[i] = void 0 === expressions[i] ? createRunningTotalExpr(field) : expressions[i];
if (expression) {
expressionArg = new SummaryCell(columnPath, rowPath, data, descriptions, i, fieldsCache);
cell = expressionArg.cell();
value = cell[i] = expression(expressionArg)
}
}
}, false)
}, false)
};
exports.createMockSummaryCell = function(descriptions, fields, indices) {
var summaryCell = new SummaryCell([], [], {}, descriptions, 0);
summaryCell.value = function(fieldId) {
if (isDefined(fieldId)) {
var index = findField(fields, fieldId),
field = fields[index];
if (!indices[index] && field && !isDefined(field.area)) {
descriptions.values.push(field);
indices[index] = true
}
}
};
summaryCell.grandTotal = function() {
return this
};
summaryCell.children = function() {
return []
};
return summaryCell
};