template.js.plugins.grid.pivot.js Maven / Gradle / Ivy
/**
* jqGrid pivot functions
* Copyright (c) 2008-2014, Tony Tomov, [email protected], http://trirand.com/blog/
* Copyright (c) 2014-2018, Oleg Kiriljuk, [email protected]
* The modul is created initially by Tony Tomov and it's full rewritten
* for free jqGrid: https://github.com/free-jqgrid/jqGrid by Oleg Kiriljuk
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
*/
/*jshint eqeqeq:false */
/*global jQuery, define, exports, module, require */
/*jslint eqeq: true, plusplus: true, continue: true, white: true */
(function (factory) {
"use strict";
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define([
"jquery",
"./jquery.fmatter",
"./grid.grouping"
], factory);
} else if (typeof module === "object" && module.exports) {
// Node/CommonJS
module.exports = function (root, $) {
if (!root) {
root = window;
}
if ($ === undefined) {
// require("jquery") returns a factory that requires window to
// build a jQuery instance, we normalize how we use modules
// that require this pattern but the window provided is a noop
// if it's defined (how jquery works)
$ = typeof window !== "undefined" ?
require("jquery") :
require("jquery")(root);
}
require("./jquery.fmatter");
require("./grid.grouping");
factory($);
return $;
};
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
"use strict";
var jgrid = $.jgrid;
// begin module grid.pivot
function Aggregation(aggregator, context, pivotOptions) {
if (!(this instanceof Aggregation)) {
return new Aggregation(aggregator);
}
//this.result = undefined;
//this.count = undefined;
this.aggregator = aggregator;
this.finilized = false;
this.context = context;
this.pivotOptions = pivotOptions;
}
Aggregation.prototype.calc = function (v, fieldName, row, iRow, rows) {
var self = this;
if (v !== undefined) {
self.result = self.result || 0; // change undefined to 0
v = parseFloat(v);
switch (self.aggregator) {
case "sum":
self.result += v;
break;
case "count":
self.result++;
break;
case "avg":
if (self.finilized) {
self.count = self.count || 0; // change undefined to 0
self.result = (self.result * self.count + v) / (self.count + 1);
self.count++;
} else {
self.result += v;
self.count = self.count || 0; // change undefined to 0
self.count++;
}
break;
case "min":
self.result = Math.min(self.result, v);
break;
case "max":
self.result = Math.max(self.result, v);
break;
default:
if ($.isFunction(self.aggregator)) {
self.result = self.aggregator.call(self.context, {
previousResult: self.result,
value: v,
fieldName: fieldName,
item: row,
iItem: iRow,
items: rows
});
}
break;
}
}
};
Aggregation.prototype.getResult = function (obj, propName, forceSaving) {
var self = this;
if (self.result !== undefined || forceSaving) {
if (forceSaving) {
if (self.result !== undefined) {
self.result = 0;
self.count = 0;
}
}
if (self.result !== undefined && !self.finilized && self.aggregator === "avg") {
self.result = self.result / self.count;
self.finilized = true;
}
obj[propName] = self.result;
}
};
function ArrayOfFieldsets(trimByCollect, caseSensitive, skipSort, dimension, fieldName) {
var iField, dimensionLength = dimension.length, dimensionItem, self = this,
stringCompare = function (a, b) {
var a1 = a, b1 = b;
if (a1 == null) { a1 = ""; } // we will place undefined and null values as the lowest TOGETHER with ""
if (b1 == null) { b1 = ""; }
// be sure that we have no other input data (Number, Date and so on)
a1 = String(a1);
b1 = String(b1);
if (!this.caseSensitive) {
a1 = a1.toUpperCase();
b1 = b1.toUpperCase();
}
if (a1 === b1) {
if (a === b) {//typeof a === typeof b) {
return 0;
}
// either a or b is undefined or null
if (a === undefined) { return -1; } // make undefined less as all other
if (b === undefined) { return 1; }
if (a === null) { return -1; } // make null less as all other with the exception undefined
if (b === null) { return 1; }
}
if (a1 < b1) {
return -1;
}
return 1;
},
numberCompare = function (a, b) {
a = Number(a);
b = Number(b);
if (a === b) {
return 0;
}
if (a < b) {
return -1;
}
return 1;
},
integerCompare = function (a, b) {
a = Math.floor(Number(a));
b = Math.floor(Number(b));
if (a === b) {
return 0;
}
if (a < b) {
return -1;
}
return 1;
};
self.items = [];
self.indexesOfSourceData = [];
self.trimByCollect = trimByCollect;
self.caseSensitive = caseSensitive;
self.skipSort = skipSort;
self.fieldLength = dimensionLength;
self.fieldNames = new Array(dimensionLength);
self.fieldSortDirection = new Array(dimensionLength);
self.fieldCompare = new Array(dimensionLength); // 0 - number, 1 - integer, 2 - string, one can extend for Date and other
for (iField = 0; iField < dimensionLength; iField++) {
dimensionItem = dimension[iField];
self.fieldNames[iField] = dimensionItem[fieldName || "dataName"];
switch (dimensionItem.sorttype) {
case "integer":
case "int":
self.fieldCompare[iField] = integerCompare;
break;
case "number":
case "currency":
case "float":
self.fieldCompare[iField] = numberCompare;
break;
default:
self.fieldCompare[iField] = $.isFunction(dimensionItem.compare) ? dimensionItem.compare : stringCompare;
break;
}
self.fieldSortDirection[iField] = dimensionItem.sortorder === "desc" ? -1 : 1;
}
}
ArrayOfFieldsets.prototype.compareVectorsEx = function (vector1, vector2) {
var self = this, fieldLength = self.fieldLength, iField, compareResult;
for (iField = 0; iField < fieldLength; iField++) {
compareResult = self.fieldCompare[iField](vector1[iField], vector2[iField]);
if (compareResult !== 0) {
return {
index: iField,
result: compareResult
};
}
}
return {
index: -1,
result: 0
};
};
ArrayOfFieldsets.prototype.getIndexOfDifferences = function (vector1, vector2) {
if (vector2 === null || vector1 === null) {
return 0;
}
return this.compareVectorsEx(vector1, vector2).index;
};
ArrayOfFieldsets.prototype.compareVectors = function (vector1, vector2) {
var compareRestlts = this.compareVectorsEx(vector1, vector2),
sortDirection = compareRestlts.index >= 0 ? this.fieldSortDirection[compareRestlts.index] : 1;
return sortDirection > 0 ? compareRestlts.result : -compareRestlts.result;
};
ArrayOfFieldsets.prototype.getItem = function (index) {
return this.items[index];
};
ArrayOfFieldsets.prototype.getIndexLength = function () {
return this.items.length;
};
ArrayOfFieldsets.prototype.getIndexesOfSourceData = function (index) {
return this.indexesOfSourceData[index];
};
ArrayOfFieldsets.prototype.createDataIndex = function (data) {
var self = this, iRow, nRows = data.length, fieldLength = self.fieldLength, values, v,
fieldNames = self.fieldNames, indexesOfSourceData = self.indexesOfSourceData, iField, compareResult, i, item,
items = self.items, iMin, iMax;
for (iRow = 0; iRow < nRows; iRow++) {
item = data[iRow];
// build the set of fields with data of the current item
values = new Array(fieldLength);
for (iField = 0; iField < fieldLength; iField++) {
v = item[fieldNames[iField]];
if (v !== undefined) {
if (typeof v === "string" && self.trimByCollect) {
v = $.trim(v);
}
values[iField] = v;
}
}
// compare values with items having index iMax and iMin
// If we use skipSort:true option then we compare always
// with iMax item only.
iMin = 0;
iMax = items.length - 1;
if (iMax < 0) {
items.push(values);
indexesOfSourceData.push([iRow]);
continue;
}
compareResult = self.compareVectors(values, items[iMax]);
if (compareResult === 0) {
indexesOfSourceData[iMax].push(iRow);
continue;
}
if (compareResult === 1 || self.skipSort) {
// in case of the empty array this.items or if the values is larger as the
// the max (last) element of this.items: append values to the array this.items
items.push(values);
indexesOfSourceData.push([iRow]);
continue;
}
compareResult = self.compareVectors(items[0], values);
if (compareResult === 1) {
// if the min (first) element values is larger as the values:
// insert the values as the first element of the array this.items
items.unshift(values);
indexesOfSourceData.unshift([iRow]);
continue;
}
if (compareResult === 0) {
indexesOfSourceData[0].push(iRow);
continue;
}
// we are sure that items[iMin] < values < items[iMax]
while (true) {
if (iMax - iMin < 2) {
// no identical items are found we need to insert the item at i index
items.splice(iMax, 0, values); // insert after iMin
indexesOfSourceData.splice(iMax, 0, [iRow]);
break;
}
i = Math.floor((iMin + iMax) / 2); // | 0 means Math.floor, but it's faster sometimes.
compareResult = self.compareVectors(items[i], values);
if (compareResult === 0) {
indexesOfSourceData[i].push(iRow);
break;
}
if (compareResult === 1) {
iMax = i;
} else {
iMin = i;
}
}
}
};
jgrid.extend({
pivotSetup: function (data, options) {
// data should come in json format
// The function return the new colModel and the transformed data
// again with group setup options which then will be passed to the grid
var self = this[0], isArray = $.isArray, summaries = {},
groupingView = {
groupField: [],
groupSummary: [],
groupSummaryPos: []
},
groupOptions = {
grouping: true,
groupingView: groupingView
},
o = $.extend({
totals: false, // replacement for rowTotals. totalText and totalHeader can be used additionally
useColSpanStyle: false,
trimByCollect: true,
skipSortByX: false,
skipSortByY: false,
caseSensitive: false,
footerTotals: false, // replacement colTotals. footerAggregator option and totalText properties of xDimension[i] can be used additionally
groupSummary: true,
groupSummaryPos: "header",
frozenStaticCols: false,
defaultFormatting: true,
data: data
}, options || {}),
row, i, k, nRows = data.length, x, y, cm, iRow, cmName, iXData, itemXData, pivotInfos, rows,
xDimension = o.xDimension, yDimension = o.yDimension, aggregates = o.aggregates, aggrContext,
isRowTotal = o.totalText || o.totals || o.rowTotals || o.totalHeader, aggrTotal, gi,
xlen = isArray(xDimension) ? xDimension.length : 0,
ylen = isArray(yDimension) ? yDimension.length : 0,
aggrlen = isArray(aggregates) ? aggregates.length : 0,
headerLevels = ylen - (aggrlen === 1 ? 1 : 0),
colHeaders = [], hasGroupTotal = [], colModel = [], outputItems = [],
additionalProperties = ["pivotInfos"],
aggrContextTotalRows = new Array(aggrlen), aggrContextGroupTotalRows = new Array(ylen),
xIndexLength, indexesOfDataWithTheSameXValues, iYData, itemYData, indexesOfDataWithTheSameYValues,
iRows, agr, outputItem, previousY, groupHeaders, iRowsY, xIndex, yIndex, yIndexLength,
indexDataBy = function (dimension, skipSort, compareVectors) {
var index = new ArrayOfFieldsets(o.trimByCollect, o.caseSensitive, skipSort, dimension);
if ($.isFunction(compareVectors)) {
index.compareVectorsEx = compareVectors;
}
index.createDataIndex(data);
return index;
},
buildColModelItem = function (colType, agr1, iAggr, level, iyData) {
var label, name, cmItem;
switch (colType) {
case 1: // total group
label = yDimension[level].totalText || "{0} {1} {2}";
name = "y" + iyData + "t" + level;
break;
case 2: // grand total
label = o.totalText || "{0}";
name = "t";
break;
//case 0: // standard column
default:
label = aggrlen > 1 ?
agr1.label || "{0}" :
($.isFunction(yDimension[level].label) ?
yDimension[level].label :
yIndex.getItem(iyData)[level]);
name = "y" + iyData;
break;
}
cmItem = $.extend({}, agr1, {
name: name + (aggrlen > 1 ? "a" + iAggr : ""),
label: $.isFunction(label) ?
(label.call(self, colType === 2 ?
{ aggregate: agr1, iAggregate: iAggr, pivotOptions: o } :
(colType === 1 ?
{ yIndex: yIndex.getItem(iyData), aggregate: agr1, iAggregate: iAggr, yLevel: level, pivotOptions: o } :
{ yData: yIndex.getItem(iyData)[level], yIndex: yIndex.getItem(iyData), yLevel: level, pivotOptions: o }))) :
(jgrid.template.apply(self, colType === 2 ?
[String(label), agr1.aggregator, agr1.member, iAggr] :
[String(label), agr1.aggregator, agr1.member, yIndex.getItem(iyData)[level], level]))
});
delete cmItem.member;
delete cmItem.aggregator;
return cmItem;
},
addColumnToColModel = function (colType, level, iyData) {
var iAggr, aggregate;
for (iAggr = 0; iAggr < aggrlen; iAggr++) {
aggregate = aggregates[iAggr];
if (aggregate.template === undefined && aggregate.formatter === undefined && o.defaultFormatting) {
aggregate.template = aggregate.aggregator === "count" ? "integer" : "number";
}
colModel.push(buildColModelItem(colType, aggregate, iAggr, level, iyData));
}
},
addGroupTotalHeaders = function (iyData, level, previousY1) {
var iLevel, j, totalHeader, headerOnTop;
for (iLevel = headerLevels - 1; iLevel >= level; iLevel--) {
if (hasGroupTotal[iLevel]) {
for (j = 0; j <= iLevel; j++) {
groupHeaders = colHeaders[j].groupHeaders;
groupHeaders[groupHeaders.length - 1].numberOfColumns += aggrlen;
}
y = yDimension[iLevel];
totalHeader = y.totalHeader;
headerOnTop = y.headerOnTop;
for (j = iLevel + 1; j <= headerLevels - 1; j++) {
colHeaders[j].groupHeaders.push({
titleText: ((headerOnTop && j === iLevel + 1) || (!headerOnTop && j === headerLevels - 1)) ?
($.isFunction(totalHeader) ?
totalHeader.call(self, previousY1, iLevel) :
jgrid.template.call(self, String(totalHeader || ""), previousY1[iLevel], iLevel)) :
"",
startColumnName: "y" + (iyData - 1) + "t" + iLevel + (aggrlen === 1 ? "" : "a0"),
numberOfColumns: aggrlen
});
}
}
}
},
createTotalAggregation = function (iAggr) {
var aggrGroup = new Aggregation(aggregates[iAggr].aggregator === "count" ? "sum" : aggregates[iAggr].aggregator, self, options);
aggrGroup.groupInfo = { iRows: [], rows: [], ys: [], iYs: [] };
return aggrGroup;
},
initializeGroupTotals = function () {
var iLevel, iAggr;
for (iLevel = headerLevels - 1; iLevel >= 0; iLevel--) {
if (hasGroupTotal[iLevel]) {
if (aggrContextGroupTotalRows[iLevel] == null) {// first call
aggrContextGroupTotalRows[iLevel] = new Array(aggrlen);
}
for (iAggr = 0; iAggr < aggrlen; iAggr++) {
aggrContextGroupTotalRows[iLevel][iAggr] = createTotalAggregation(iAggr);
}
}
}
},
finalizeGroupTotals = function (iyData, itemYData1, previousY1, iAggr) {
var iLevel, level = yIndex.getIndexOfDifferences(itemYData1, previousY1), fieldName, aggrGroup;
if (previousY1 !== null) {
// test whether the group is finished and one need to get results
level = Math.max(level, 0); // change -1 to 0 for the last call (itemYData === previousY)
for (iLevel = headerLevels - 1; iLevel >= level; iLevel--) {
fieldName = "y" + iyData + "t" + iLevel + (aggrlen > 1 ? "a" + iAggr : "");
if (hasGroupTotal[iLevel] && outputItem[fieldName] === undefined) {
aggrGroup = aggrContextGroupTotalRows[iLevel][iAggr];
aggrGroup.getResult(outputItem, fieldName);
outputItem.pivotInfos[fieldName] = {
colType: 1,
iA: iAggr,
a: aggregates[iAggr],
level: iLevel,
iRows: aggrGroup.groupInfo.iRows,
rows: aggrGroup.groupInfo.rows,
ys: aggrGroup.groupInfo.ys,
iYs: aggrGroup.groupInfo.iYs
};
if (itemYData1 !== previousY1) {
aggrContextGroupTotalRows[iLevel][iAggr] = createTotalAggregation(iAggr);
}
}
}
}
},
calculateGroupTotals = function (itemYData1, previousY1, aggregate, iAggr, row1, iRow1, iyData) {
// the method will be called at the first time with previousY === null in every output row
// and finally with itemYData === previousY for getting results of all aggregation contexts
var iLevel, aggrGroup, groupInfo;
if (itemYData1 !== previousY1) { // not the last call in the row
for (iLevel = headerLevels - 1; iLevel >= 0; iLevel--) {
if (hasGroupTotal[iLevel]) {
aggrGroup = aggrContextGroupTotalRows[iLevel][iAggr];
aggrGroup.calc(row1[aggregate.member], aggregate.member, row1, iRow1, data);
groupInfo = aggrGroup.groupInfo;
if ($.inArray(iyData, groupInfo.iYs) < 0) {
groupInfo.iYs.push(iyData);
groupInfo.ys.push(itemYData1);
}
if ($.inArray(iRow1, groupInfo.iRows) < 0) {
groupInfo.iRows.push(iRow1);
groupInfo.rows.push(row1);
}
}
}
}
};
if (xlen === 0 || aggrlen === 0) {
throw ("xDimension or aggregates options are not set!");
}
// ****************************************************************
// The step 1: scan input data and build the list of unique indexes
// ****************************************************************
xIndex = indexDataBy(xDimension, o.skipSortByX, o.compareVectorsByX);
yIndex = indexDataBy(yDimension, o.skipSortByY, o.compareVectorsByY);
// save to be used probably later
options.xIndex = xIndex;
options.yIndex = yIndex;
// *******************************************
// The step 2: build colModel and groupOptions
// *******************************************
// fill the first xlen columns of colModel and fill the groupOptions
// the names of the first columns will be "x"+i. The first column have the name "x0".
for (i = 0; i < xlen; i++) {
x = xDimension[i];
cm = {
name: "x" + i,
label: x.label != null ?
($.isFunction(x.label) ? x.label.call(self, x, i, o) : x.label) :
x.dataName,
frozen: o.frozenStaticCols
};
if (i < xlen - 1 && !x.skipGrouping && !x.additionalProperty) {
// based on xDimension levels build grouping
groupingView.groupField.push(cm.name);
groupingView.groupSummary.push(o.groupSummary);
groupingView.groupSummaryPos.push(o.groupSummaryPos);
}
cm = $.extend(cm, x);
delete cm.dataName;
delete cm.footerText;
if (!x.additionalProperty) {
colModel.push(cm);
groupOptions.sortname = cm.name;
} else {
additionalProperties.push(cm.name);
}
}
if (xlen < 2) {
groupOptions.grouping = false; // no grouping is needed
}
groupingView.hideFirstGroupCol = true;
// Fill hasGroupTotal and groupColumnsPerLevel arrays
// The hasGroupTotal just shows whether one need create additional totals for every group.
for (i = 0; i < ylen; i++) {
y = yDimension[i];
hasGroupTotal.push(y.totals || y.rowTotals || y.totalText || y.totalHeader ? true : false);
}
// fill other columns of colModel based on collected uniqueYData and aggregates options
// the names of the first columns will be "y"+i in case of one aggregate and
// "y"+i+"a"+k in case of multiple aggregates. The name of the first "y"-column is "y0" or "y0a0"
// The next function build and insert item in colModel
// colType: 0 - means standard column, 1 - total group, 2 - grand total
previousY = yIndex.getItem(0);
addColumnToColModel(0, ylen - 1, 0); // add standard column
yIndexLength = yIndex.getIndexLength();
for (iYData = 1; iYData < yIndexLength; iYData++) {
itemYData = yIndex.getItem(iYData);
/*
* find where (on which level) the itemYData have the differences to
* the previous y (previousY). If the level has (totals:true/rowTotals:true) in yDimension
* then one should insert new total columns for all levels starting with the highest one
* (yDimension[yDimension.length-1]) and till the current one.
*/
i = yIndex.getIndexOfDifferences(itemYData, previousY);
for (k = headerLevels - 1; k >= i; k--) {
if (hasGroupTotal[k]) {
addColumnToColModel(1, k, iYData - 1); // add group total columns
}
}
previousY = itemYData;
addColumnToColModel(0, ylen - 1, iYData); // add standard column
}
// finalize of all totals
for (i = headerLevels - 1; i >= 0; i--) {
if (hasGroupTotal[i]) {
addColumnToColModel(1, i, yIndexLength - 1); // add the last group total columns
}
}
// add total columns calculated over all data of the row
if (isRowTotal) {
addColumnToColModel(2);
}
// ********************************
// The step 3: build column headers
// ********************************
// initialize colHeaders
previousY = yIndex.getItem(0);
for (k = 0; k < headerLevels; k++) {
colHeaders.push({
useColSpanStyle: o.useColSpanStyle,
groupHeaders: [{
titleText: ($.isFunction(yDimension[k].label) ?
yDimension[k].label.call(self, { yData: previousY[k], yIndex: previousY, yLevel: k, pivotOptions: o }) :
previousY[k]),
startColumnName: aggrlen === 1 ? "y0" : "y0a0",
numberOfColumns: aggrlen
}]
});
}
for (iYData = 1; iYData < yIndexLength; iYData++) {
itemYData = yIndex.getItem(iYData);
i = yIndex.getIndexOfDifferences(itemYData, previousY);
// We placed QNIQUE data in uniqueYData array.
// So we always find a difference on one level
addGroupTotalHeaders(iYData, i, previousY);
// add column headers which corresponds the main data
for (k = headerLevels - 1; k >= i; k--) {
colHeaders[k].groupHeaders.push({
titleText: ($.isFunction(yDimension[k].label) ?
yDimension[k].label.call(self, { yData: itemYData[k], yIndex: itemYData, yLevel: k, pivotOptions: o }) :
itemYData[k]),
startColumnName: "y" + iYData + (aggrlen === 1 ? "" : "a0"),
numberOfColumns: aggrlen
});
}
for (k = 0; k < i; k++) {
groupHeaders = colHeaders[k].groupHeaders;
groupHeaders[groupHeaders.length - 1].numberOfColumns += aggrlen;
}
previousY = itemYData;
}
addGroupTotalHeaders(yIndexLength, 0, previousY);
// fill groupHeaders without taking in consideration group total columns
if (isRowTotal) {
for (i = 0; i < headerLevels; i++) {
colHeaders[i].groupHeaders.push({
titleText: (i < headerLevels - 1 ? "" : o.totalHeader || ""),
startColumnName: "t" + (aggrlen === 1 ? "" : "a0"),
numberOfColumns: aggrlen
});
}
}
// *****************************
// The step 4: fill data of grid
// *****************************
xIndexLength = xIndex.getIndexLength();
for (iXData = 0; iXData < xIndexLength; iXData++) {
itemXData = xIndex.getItem(iXData);
pivotInfos = { iX: iXData, x: itemXData };
outputItem = { pivotInfos: pivotInfos }; // item of output data
// itemXData corresponds to the row of output data
for (i = 0; i < xlen; i++) {
// fill first columns of data
outputItem["x" + i] = itemXData[i];
}
indexesOfDataWithTheSameXValues = xIndex.getIndexesOfSourceData(iXData);
// The rows of input data with indexes from indexesOfDataWithTheSameXValues contains itemXData
// Now we build columns of itemXData row
if (isRowTotal) {
for (k = 0; k < aggrlen; k++) {
aggrContextTotalRows[k] = createTotalAggregation(k);
}
}
previousY = null;
initializeGroupTotals();
for (iYData = 0; iYData < yIndexLength; iYData++) {
itemYData = yIndex.getItem(iYData);
indexesOfDataWithTheSameYValues = yIndex.getIndexesOfSourceData(iYData);
// we calculate aggregate in every itemYData
for (k = 0; k < aggrlen; k++) {
if (previousY !== null) { // empty input data
finalizeGroupTotals(iYData - 1, itemYData, previousY, k);
}
iRows = [];
for (i = 0; i < indexesOfDataWithTheSameYValues.length; i++) {
iRowsY = indexesOfDataWithTheSameYValues[i];
if ($.inArray(iRowsY, indexesOfDataWithTheSameXValues) >= 0) {
iRows.push(iRowsY);
}
}
if (iRows.length > 0) {
// iRows array have all indexes of input data which have both itemXData and itemYData
// We need calculate aggregate agr over all the items
rows = new Array(iRows.length);
agr = aggregates[k];
aggrContext = new Aggregation(agr.aggregator, self, options);
for (iRow = 0; iRow < iRows.length; iRow++) {
i = iRows[iRow];
row = data[i];
rows[iRow] = row;
aggrContext.calc(row[agr.member], agr.member, row, i, data);
if (isRowTotal) {
aggrTotal = aggrContextTotalRows[k];
aggrTotal.calc(row[agr.member], agr.member, row, i, data);
gi = aggrTotal.groupInfo;
if ($.inArray(i, gi.iYs) < 0) {
gi.iYs.push(iYData);
gi.ys.push(itemYData);
}
if ($.inArray(i, gi.iRows) < 0) {
gi.iRows.push(i);
gi.rows.push(row);
}
}
calculateGroupTotals(itemYData, previousY, agr, k, row, i, iYData);
}
cmName = "y" + iYData + (aggrlen === 1 ? "" : "a" + k);
aggrContext.getResult(outputItem, cmName);
pivotInfos[cmName] = {
colType: 0, // standard row
iY: iYData,
y: itemYData,
iA: k,
a: agr,
iRows: iRows,
rows: rows
};
}
}
previousY = itemYData;
}
if (previousY !== null) { // if non-empty input data
for (k = 0; k < aggrlen; k++) {
finalizeGroupTotals(yIndexLength - 1, previousY, previousY, k);
}
}
if (isRowTotal) {
for (k = 0; k < aggrlen; k++) {
cmName = "t" + (aggrlen === 1 ? "" : "a" + k);
aggrTotal = aggrContextTotalRows[k];
aggrTotal.getResult(outputItem, cmName);
gi = aggrTotal.groupInfo;
pivotInfos[cmName] = {
colType: 2, // row total
iA: k,
a: aggregates[k],
iRows: gi.iRows,
rows: gi.rows,
iYs: gi.iYs,
ys: gi.ys
};
}
}
outputItems.push(outputItem);
}
// *****************************
// The step 5: fill total footer
// *****************************
if (o.footerTotals || o.colTotals) {
nRows = outputItems.length;
for (i = 0; i < xlen; i++) {
summaries["x" + i] = xDimension[i].footerText || "";
}
for (i = xlen; i < colModel.length; i++) {
cmName = colModel[i].name;
aggrContext = new Aggregation(o.footerAggregator || "sum", self, options);
for (iRow = 0; iRow < nRows; iRow++) {
outputItem = outputItems[iRow];
aggrContext.calc(outputItem[cmName], cmName, outputItem, iRow, outputItems);
}
aggrContext.getResult(summaries, cmName);
}
}
// return the final result.
options.colHeaders = colHeaders;
return {
colModel: colModel,
additionalProperties: additionalProperties,
options: options,
rows: outputItems,
groupOptions: groupOptions,
groupHeaders: colHeaders,
summary: summaries
};
},
jqPivot: function (data, pivotOpt, gridOpt, ajaxOpt) {
return this.each(function () {
var $t = this, $self = $($t), $j = $.fn.jqGrid;
function pivot() {
var pivotGrid = $j.pivotSetup.call($self, data, pivotOpt),
gHead = pivotGrid.groupHeaders,
assocArraySize = function (obj) {
// http://stackoverflow.com/a/6700/11236
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
size++;
}
}
return size;
},
footerrow = assocArraySize(pivotGrid.summary) > 0 ? true : false,
groupingView = pivotGrid.groupOptions.groupingView,
query = jgrid.from.call($t, pivotGrid.rows), i;
if (!pivotOpt.skipSortByX) {
for (i = 0; i < groupingView.groupField.length; i++) {
query.orderBy(groupingView.groupField[i],
gridOpt != null && gridOpt.groupingView && gridOpt.groupingView.groupOrder != null && gridOpt.groupingView.groupOrder[i] === "desc" ? "d" : "a",
"text",
"");
}
}
pivotOpt.data = data;
$j.call($self, $.extend(true, {
datastr: $.extend(query.select(), footerrow ? { userdata: pivotGrid.summary } : {}),
datatype: "jsonstring",
footerrow: footerrow,
userDataOnFooter: footerrow,
colModel: pivotGrid.colModel,
additionalProperties: pivotGrid.additionalProperties,
pivotOptions: pivotGrid.options,
viewrecords: true,
sortname: pivotOpt.xDimension[0].dataName // ?????
}, pivotGrid.groupOptions, gridOpt || {}));
if (gHead.length) {
for (i = 0; i < gHead.length; i++) {
// Multiple calls of setGroupHeaders for one grid are wrong,
// but there are produces good results in case of usage
// useColSpanStyle: false option. The rowspan values
// needed be increased in case of usage useColSpanStyle: true
if (gHead[i] && gHead[i].groupHeaders.length) {
$j.setGroupHeaders.call($self, gHead[i]);
}
}
}
if (pivotOpt.frozenStaticCols) {
$j.setFrozenColumns.call($self);
}
}
if (typeof data === "string") {
$.ajax($.extend({
url: data,
dataType: "json",
success: function (data1) {
data = jgrid.getAccessor(data1, ajaxOpt && ajaxOpt.reader ? ajaxOpt.reader : "rows");
pivot();
}
}, ajaxOpt || {}));
} else {
pivot();
}
});
}
});
// end module grid.pivot
}));
© 2015 - 2025 Weber Informatics LLC | Privacy Policy