@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,644 lines (1,643 loc) • 183 kB
JavaScript
import { t as useLoaderContainer } from "./loaderContainer-B9M50bpx.js";
import { PivotGridNavigation, addKPI, addMultipleMeasures, averageAggregate, buildKPIMeasures, cloneDataTree, compareAxes, configuratorReducer, countAggregate, createAxisDescriptors, createDataState, createDataTree, createDiscoverBody, createFlatSchemaDimensions, createLocalDataState, createRequestBody, createTuples, discoverCommands, fetchData, fetchDiscover, headersReducer, maxAggregate, mergeTrees, minAggregate, parseResponse, readData, rootFields, setFilter, setSort, sumAggregate, toColumns, toData, toRows, toTree } from "@progress/kendo-pivotgrid-common";
//#region ../src/kendo.pivotgrid.js
const __meta__ = {
id: "pivotgrid",
name: "PivotGrid",
category: "web",
description: "The PivotGrid widget is a data summarization tool.",
depends: [
"dom",
"data",
"data.xml",
"sortable",
"icons",
"loader",
"html.loadercontainer"
],
features: [
{
id: "pivotgrid-configurator",
name: "Configurator",
description: "The PivotConfigurator widget allows the user to select data slices displayed in PivotGrid",
depends: ["pivot.configurator"]
},
{
id: "pivotgrid-filtering",
name: "Filtering",
description: "Support for filtering",
depends: ["pivot.fieldmenu"]
},
{
id: "pivotgrid-excel-export",
name: "Excel export",
description: "Export pivot grid data as Excel spreadsheet",
depends: ["ooxml"]
},
{
id: "pivotgrid-pdf-export",
name: "PDF export",
description: "Export pivot grid data as PDF",
depends: ["pdf", "drawing"]
},
{
id: "mobile-scroller",
name: "Mobile scroller",
description: "Support for kinetic scrolling in mobile device",
depends: ["mobile.scroller"]
}
]
};
(function($, undefined) {
var kendo = window.kendo, ui = kendo.ui, encode = kendo.htmlEncode, Class = kendo.Class, Comparer = kendo.data.Comparer, Widget = ui.Widget, DataSource = kendo.data.DataSource, outerWidth = kendo._outerWidth, outerHeight = kendo._outerHeight, normalizeFilter = kendo.data.Query.normalizeFilter, normalizeSort = kendo.data.Query.normalizeSort, toString = {}.toString, identity = function(o) {
return o;
}, map = $.map, extend = $.extend, keys = kendo.keys, isFunction = kendo.isFunction, RESIZE = "resize", READ = "read", CHANGE = "change", ERROR = "error", REQUESTSTART = "requestStart", PROGRESS = "progress", REQUESTEND = "requestEnd", MEASURES = "Measures", STATERESET = "stateReset", AUTO = "auto", DIV = "<div></div>", NS = ".kendoPivotGrid", ROW_TOTAL_KEY = "__row_total__", DATABINDING = "dataBinding", DATABOUND = "dataBound", EXPANDMEMBER = "expandMember", HEADERTEMPLATE = ({ id, key, headerClass, colspan, rowspan, expandable, iconClass, role, expanded }) => `<th id="${id}" role="${role}" ${expandable ? `aria-expanded="${expanded}"` : ""} data-key="${encode(key)}" class="${encode(headerClass)}" ${colspan ? "colspan=\"" + encode(colspan) + "\"" : ""} ${rowspan ? "rowspan=\"" + encode(rowspan) + "\"" : ""}>` + `${expandable ? kendo.ui.icon($("<span role=\"presentation\" class=\"k-pivotgrid-toggle\"></span>"), { icon: `chevron-${encode(iconClass)}` }) : ""}` + "</th>", COLLAPSEMEMBER = "collapseMember", STATE_EXPANDED_ICONNAME = "caret-alt-down", STATE_EXPANDED_SELECTOR = `.k-i-${STATE_EXPANDED_ICONNAME},.k-svg-i-${STATE_EXPANDED_ICONNAME}`, STATE_COLLAPSED_ICONNAME = "caret-alt-right", HEADER_TEMPLATE = ({ member }) => `<span>${encode(member.caption || member.name)}</span>`, PIVOTGRID_TREND_ICONS_MAP = {
"kpi-trend-decrease": "caret-alt-down",
"kpi-trend-increase": "caret-alt-up",
"kpi-trend-equal": "minus"
}, KPISTATUS_TEMPLATE = ({ dataItem }) => kendo.ui.icon($(`<span title="${encode(dataItem.value)}"></span>`), { icon: `kpi-status-${dataItem.value > 0 ? "open" : dataItem.value < 0 ? "deny" : "hold"}` }), KPITREND_TEMPLATE = ({ dataItem }) => kendo.ui.icon($(`<span title="${encode(dataItem.value)}"></span>`), { icon: PIVOTGRID_TREND_ICONS_MAP[`kpi-trend-${dataItem.value > 0 ? "increase" : dataItem.value < 0 ? "decrease" : "equal"}`] }), DATACELL_TEMPLATE = ({ dataItem }) => dataItem ? dataItem.fmtValue || dataItem.value ? `<span class="k-pivotgrid-content">${encode(dataItem.fmtValue || dataItem.value)}</span>` : " " : " ", LAYOUT_TABLE = "<table class=\"k-pivot-layout\">" + "<tr>" + "<td>" + "<div class=\"k-pivot-rowheaders\"></div>" + "</td>" + "<td>" + "<div class=\"k-pivot-table\"></div>" + "</td>" + "</tr>" + "</table>";
var AXIS_ROWS = "rows";
var AXIS_COLUMNS = "columns";
const SEPARATOR = "&";
var tableStyles = {
tableRow: "k-table-row",
header: "k-header k-table-th",
headerTable: "k-grid-header-table k-table",
table: "k-table",
contentTable: "k-grid-table k-table",
tbody: "k-table-tbody",
tableCell: "k-table-td"
};
function normalizeMeasures(measure) {
var descriptor = typeof measure === "string" ? [{ name: measure }] : measure;
var descriptors = toString.call(descriptor) === "[object Array]" ? descriptor : descriptor !== undefined ? [descriptor] : [];
return map(descriptors, function(d) {
if (typeof d === "string") {
return { name: d };
}
return $.extend(true, d, {
name: d.name,
type: d.type
});
});
}
function normalizeMembers(member) {
var descriptor = typeof member === "string" ? [{
name: [member],
expand: false
}] : member;
var descriptors = toString.call(descriptor) === "[object Array]" ? descriptor : descriptor !== undefined ? [descriptor] : [];
return map(descriptors, function(d) {
if (typeof d === "string") {
return {
name: [d],
expand: false
};
}
return {
name: toString.call(d.name) === "[object Array]" ? d.name.slice() : [d.name],
expand: d.expand
};
});
}
function normalizeName(name) {
if (name.indexOf(" ") !== -1) {
name = "[\"" + name + "\"]";
}
return name;
}
function accumulateMembers(accumulator, rootTuple, tuple, level) {
var idx, length;
var children;
var member;
if (!tuple) {
tuple = rootTuple;
}
if (!level) {
level = 0;
}
member = tuple.members[level];
if (!member || member.measure) {
return;
}
children = member.children;
length = children.length;
if (tuple === rootTuple) {
accumulator[kendo.stringify([member.name])] = !!length;
} else if (length) {
accumulator[kendo.stringify(buildPath(tuple, level))] = true;
}
if (length) {
for (idx = 0; idx < length; idx++) {
accumulateMembers(accumulator, rootTuple, children[idx], level);
}
}
accumulateMembers(accumulator, rootTuple, tuple, level + 1);
}
function descriptorsForAxes(tuples) {
var result = {};
if (tuples.length) {
accumulateMembers(result, tuples[0]);
}
var descriptors = [];
for (var k in result) {
descriptors.push({
name: JSON.parse(k),
expand: result[k]
});
}
return descriptors;
}
function addMissingPathMembers(members, axis) {
var tuples = axis.tuples || [];
var firstTuple = tuples[0];
if (firstTuple && members.length < firstTuple.members.length) {
var tupleMembers = firstTuple.members;
for (var idx = 0; idx < tupleMembers.length; idx++) {
if (tupleMembers[idx].measure) {
continue;
}
var found = false;
for (var j = 0; j < members.length; j++) {
if (getName(members[j]).indexOf(tupleMembers[idx].hierarchy) === 0) {
found = true;
break;
}
}
if (!found) {
members.push({
name: [tupleMembers[idx].name],
expand: false
});
}
}
}
}
function tupleToDescriptors(tuple) {
var result = [];
var members = tuple.members;
for (var idx = 0; idx < members.length; idx++) {
if (members[idx].measure) {
continue;
}
result.push({
name: [members[idx].name],
expand: members[idx].children.length > 0
});
}
return result;
}
function descriptorsForMembers(axis, members, measures) {
axis = axis || {};
addMissingPathMembers(members, axis);
if (measures.length > 1) {
members.push({
name: MEASURES,
measure: true,
children: normalizeMembers(measures)
});
}
var tupletoSearch = { members };
if (axis.tuples) {
var result = findExistingTuple(axis.tuples, tupletoSearch);
if (result.tuple) {
members = tupleToDescriptors(result.tuple);
}
}
return members;
}
function createAggregateGetter(m) {
var measureGetter = kendo.getter(m.field, true);
return function(aggregatorContext, state) {
return m.aggregate(measureGetter(aggregatorContext.dataItem), state, aggregatorContext);
};
}
function isNumber(val) {
return typeof val === "number" && !isNaN(val);
}
function isDate(val) {
return val && val.getTime;
}
function getScollWidth() {
var scrollbar = 0;
var div;
if (document && document.createElement) {
div = document.createElement("div");
div.style.cssText = "overflow:scroll;overflow-x:hidden;zoom:1;clear:both;display:block";
div.innerHTML = " ";
document.body.appendChild(div);
scrollbar = div.offsetWidth - div.scrollWidth;
document.body.removeChild(div);
}
return scrollbar;
}
function loadLocalData(data, params, deferred) {
const that = this;
const originalData = (that.reader.data(data) || []).slice(0);
if (originalData && !that._pristineData) {
that._pristineData = originalData;
}
const columnSettings = that._createSettings(params.columnAxes);
const rowSettings = that._createSettings(params.rowAxes);
const measures = that.measures();
const dataTree = createDataTree(originalData, rowSettings, columnSettings, measures, {
dataField: "aggregate",
columnsData: "columns"
}, that.filter() || []);
const stateArgs = {
dataTree,
columnSettings,
rowSettings,
columnAxes: params.columnAxes,
rowAxes: params.rowAxes,
measures: measures.map(function(item) {
return item;
}),
fields: {
dataField: "aggregate",
columnsData: "columns"
},
sort: flattenSortDescriptors(params.sort || [])
};
that._saveState(createLocalDataState(stateArgs));
that.trigger(CHANGE);
deferred.resolve();
}
function flattenSortDescriptors(descriptors) {
var result = [];
for (var i = 0; i < descriptors.length; i++) {
result.push({
dir: descriptors[i].dir,
field: descriptors[i].field.split(".").pop()
});
}
return result;
}
function createLocalMeasure(field, key, format, aggregate) {
var formatFunc = function(value) {
return kendo.format(this.format, value);
};
var measureMap = {
"sum": sumAggregate,
"average": averageAggregate,
"min": minAggregate,
"max": maxAggregate,
"count": countAggregate
};
var valueFunc = function(item) {
return item[this.field];
};
var measure = {
value: valueFunc.bind({ field }),
aggregate: measureMap[aggregate],
caption: key,
uniqueName: key,
type: 2,
name: [key]
};
if (format) {
measure.aggregate.format = formatFunc.bind({ format });
}
return measure;
}
function getIcons(sortIcon, options, pivotOptions) {
var sortable = options.sortable, filterable = options.filterable, reorderable = pivotOptions.reorderable, result = "";
if (sortable) {
result += sortIcon ? `<span class="k-chip-action">${kendo.ui.icon(sortIcon + "-sm")}</span>` : "";
}
if (filterable || sortable) {
result += `<span class="k-setting-fieldmenu k-chip-action">${kendo.ui.icon("more-vertical")}</span>`;
}
if (reorderable) {
result += `<span class="k-setting-delete k-chip-action">${kendo.ui.icon("x")}</span>`;
}
return result;
}
var functions = {
sum: function(value, state) {
var accumulator = state.accumulator;
if (!isNumber(accumulator)) {
accumulator = value;
} else if (isNumber(value)) {
accumulator += value;
}
return accumulator;
},
count: function(value, state) {
return (state.accumulator || 0) + 1;
},
average: {
aggregate: function(value, state) {
var accumulator = state.accumulator;
if (state.count === undefined) {
state.count = 0;
}
if (!isNumber(accumulator)) {
accumulator = value;
} else if (isNumber(value)) {
accumulator += value;
}
if (isNumber(value)) {
state.count++;
}
return accumulator;
},
result: function(state) {
var accumulator = state.accumulator;
if (isNumber(accumulator)) {
accumulator = accumulator / state.count;
}
return accumulator;
}
},
max: function(value, state) {
var accumulator = state.accumulator;
if (!isNumber(accumulator) && !isDate(accumulator)) {
accumulator = value;
}
if (accumulator < value && (isNumber(value) || isDate(value))) {
accumulator = value;
}
return accumulator;
},
min: function(value, state) {
var accumulator = state.accumulator;
if (!isNumber(accumulator) && !isDate(accumulator)) {
accumulator = value;
}
if (accumulator > value && (isNumber(value) || isDate(value))) {
accumulator = value;
}
return accumulator;
}
};
var PivotCubeBuilder = Class.extend({
init: function(options) {
this.options = extend({}, this.options, options);
this.dimensions = this._normalizeDescriptors("field", this.options.dimensions);
this.measures = this._normalizeDescriptors("name", this.options.measures);
},
_normalizeDescriptors: function(keyField, descriptors) {
descriptors = descriptors || {};
var fields = {};
var field;
if (toString.call(descriptors) === "[object Array]") {
for (var idx = 0, length = descriptors.length; idx < length; idx++) {
field = descriptors[idx];
if (typeof field === "string") {
fields[field] = {};
} else if (field[keyField]) {
fields[field[keyField]] = field;
}
}
descriptors = fields;
}
return descriptors;
},
_rootTuples: function(rootNames, measureAggregators) {
var aggregatorsLength = measureAggregators.length || 1;
var dimensionsSchema = this.dimensions || [];
var root, name, parts;
var measureIdx = 0;
var idx;
var rootNamesLength = rootNames.length;
var result = [];
var keys = [];
if (rootNamesLength || measureAggregators.length) {
for (measureIdx = 0; measureIdx < aggregatorsLength; measureIdx++) {
root = { members: [] };
for (idx = 0; idx < rootNamesLength; idx++) {
name = rootNames[idx];
parts = name.split("&");
root.members[root.members.length] = {
children: [],
caption: (dimensionsSchema[name] || {}).caption || "All",
name,
levelName: name,
levelNum: "0",
hasChildren: true,
parentName: parts.length > 1 ? parts[0] : undefined,
hierarchy: name
};
}
if (aggregatorsLength > 1) {
root.members[root.members.length] = {
children: [],
caption: measureAggregators[measureIdx].caption,
name: measureAggregators[measureIdx].descriptor.name,
levelName: "MEASURES",
levelNum: "0",
hasChildren: false,
parentName: undefined,
hierarchy: "MEASURES"
};
}
result[result.length] = root;
}
keys.push(ROW_TOTAL_KEY);
}
return {
keys,
tuples: result
};
},
_sortMap: function(map, sortDescriptors) {
var sortedMaps = [];
var sortTree = [];
var flattenTree = [];
var mapItem;
var key;
for (key in map) {
if (!map[key].directParentName) {
sortTree.push($.extend({}, {
name: key,
parentName: map[key].parentName
}));
}
}
if (!sortTree.length) {
for (key in map) {
sortTree.push($.extend({}, {
name: key,
parentName: map[key].parentName
}));
}
}
fillSortTree(sortTree, map);
for (var i = 0; i < sortDescriptors.length; i++) {
sortItemsTree(sortDescriptors[i].field.split(".").pop(), sortTree, Comparer.create({
field: "name",
dir: sortDescriptors[i].dir
}));
}
flattenTree = flatColumns(sortTree);
for (var j = 0; j < flattenTree.length; j++) {
mapItem = map[flattenTree[j].name];
mapItem.index = j;
sortedMaps[j] = mapItem;
}
return sortedMaps;
},
_expandedTuples: function(map, expanded, measureAggregators, sortDescriptors) {
var aggregatorsLength = measureAggregators.length || 1;
var dimensionsSchema = this.dimensions || [];
var measureIdx;
var tuple;
var key;
var mapItem;
var current;
var currentKeys;
var accumulator = [];
var accumulatorKeys = [];
var memberInfo;
var expandedNames;
var parts;
var name;
var idx;
if (sortDescriptors && sortDescriptors.length && !$.isEmptyObject(map)) {
map = this._sortMap(map, sortDescriptors);
}
for (key in map) {
mapItem = map[key];
memberInfo = this._findExpandedMember(expanded, mapItem.uniquePath);
current = accumulator[memberInfo.index] || [];
currentKeys = accumulatorKeys[memberInfo.index] || [];
expandedNames = memberInfo.member.names;
for (measureIdx = 0; measureIdx < aggregatorsLength; measureIdx++) {
tuple = { members: [] };
for (idx = 0; idx < expandedNames.length; idx++) {
if (idx === memberInfo.member.expandedIdx) {
tuple.members[tuple.members.length] = {
children: [],
caption: mapItem.value,
name: mapItem.name,
hasChildren: false,
levelNum: 1,
levelName: mapItem.parentName + mapItem.name,
parentName: mapItem.parentName,
hierarchy: mapItem.parentName + mapItem.name
};
if (measureIdx === 0) {
currentKeys.push(buildPath(tuple, idx).join(""));
}
} else {
name = expandedNames[idx];
parts = name.split("&");
tuple.members[tuple.members.length] = {
children: [],
caption: (dimensionsSchema[name] || {}).caption || "All",
name,
levelName: name,
levelNum: "0",
hasChildren: true,
parentName: parts.length > 1 ? parts[0] : undefined,
hierarchy: name
};
}
}
if (aggregatorsLength > 1) {
tuple.members[tuple.members.length] = {
children: [],
caption: measureAggregators[measureIdx].caption,
name: measureAggregators[measureIdx].descriptor.name,
levelName: "MEASURES",
levelNum: "0",
hasChildren: true,
parentName: undefined,
hierarchy: "MEASURES"
};
}
current[current.length] = tuple;
}
accumulator[memberInfo.index] = current;
accumulatorKeys[memberInfo.index] = currentKeys;
}
return {
keys: accumulatorKeys,
tuples: accumulator
};
},
_findExpandedMember: function(members, parentName) {
for (var idx = 0; idx < members.length; idx++) {
if (members[idx].uniquePath === parentName) {
return {
member: members[idx],
index: idx
};
}
}
},
_asTuples: function(map, descriptor, measureAggregators, sortDescriptors) {
measureAggregators = measureAggregators || [];
var rootInfo = this._rootTuples(descriptor.root, measureAggregators);
var expandedInfo = this._expandedTuples(map, descriptor.expanded, measureAggregators, sortDescriptors);
return {
keys: [].concat.apply(rootInfo.keys, expandedInfo.keys),
tuples: [].concat.apply(rootInfo.tuples, expandedInfo.tuples)
};
},
_measuresInfo: function(measures, rowAxis) {
var idx = 0;
var length = measures && measures.length;
var aggregateNames = [];
var resultFuncs = {};
var formats = {};
var descriptors = this.measures || {};
var measure;
var name;
for (; idx < length; idx++) {
name = measures[idx].descriptor.name;
measure = descriptors[name] || {};
aggregateNames.push(name);
if (measure.result) {
resultFuncs[name] = measure.result;
}
if (measure.format) {
formats[name] = measure.format;
}
}
return {
names: aggregateNames,
formats,
resultFuncs,
rowAxis
};
},
_toDataArray: function(map, measuresInfo, rowKeys, columnKeys) {
var result = [];
var aggregates;
var name, i, j, k, n;
var row, column, columnKey;
var rowMeasureNamesLength = 1;
var rowMeasureNames = [];
var columnMeasureNames;
var rowLength = rowKeys.length || 1;
var columnLength = columnKeys.length || 1;
if (measuresInfo.rowAxis) {
rowMeasureNames = measuresInfo.names;
rowMeasureNamesLength = rowMeasureNames.length;
} else {
columnMeasureNames = measuresInfo.names;
}
for (i = 0; i < rowLength; i++) {
row = map[rowKeys[i] || ROW_TOTAL_KEY];
for (n = 0; n < rowMeasureNamesLength; n++) {
if (measuresInfo.rowAxis) {
columnMeasureNames = [rowMeasureNames[n]];
}
for (j = 0; j < columnLength; j++) {
columnKey = columnKeys[j] || ROW_TOTAL_KEY;
column = row.items[columnKey];
if (columnKey === ROW_TOTAL_KEY) {
aggregates = row.aggregates;
} else {
aggregates = column ? column.aggregates : {};
}
for (k = 0; k < columnMeasureNames.length; k++) {
name = columnMeasureNames[k];
this._addData(result, aggregates[name], measuresInfo.formats[name], measuresInfo.resultFuncs[name]);
}
}
}
}
return result;
},
_addData: function(result, value, format, resultFunc) {
var fmtValue = "";
var ordinal;
if (value) {
value = resultFunc ? resultFunc(value) : value.accumulator;
fmtValue = format ? kendo.format(format, value) : value;
}
ordinal = result.length;
result[ordinal] = {
ordinal,
value: value || "",
fmtValue
};
},
_matchDescriptors: function(dataItem, descriptor, getters) {
var parts;
var parentField;
var expectedValue;
var names = descriptor.names;
var idx = descriptor.expandedIdx;
var value;
while (idx > 0) {
parts = names[--idx].split("&");
if (parts.length > 1) {
parentField = parts[0];
expectedValue = parts[1];
value = getters[parentField](dataItem);
value = value !== undefined && value !== null ? value.toString() : value;
if (value != expectedValue) {
return false;
}
}
}
return true;
},
_calculateAggregate: function(measureAggregators, aggregatorContext, totalItem) {
var result = {};
var state;
var name;
for (var measureIdx = 0; measureIdx < measureAggregators.length; measureIdx++) {
name = measureAggregators[measureIdx].descriptor.name;
state = totalItem.aggregates[name] || {};
state.accumulator = measureAggregators[measureIdx].aggregator(aggregatorContext, state);
result[name] = state;
}
return result;
},
_processColumns: function(measureAggregators, descriptors, getters, columns, aggregatorContext, rowTotal, state, updateColumn) {
var value;
var descriptor;
var column;
var totalItem;
var key, name, parentName, path;
var dataItem = aggregatorContext.dataItem;
var idx = 0;
for (; idx < descriptors.length; idx++) {
descriptor = descriptors[idx];
if (!this._matchDescriptors(dataItem, descriptor, getters)) {
continue;
}
path = descriptor.names.slice(0, descriptor.expandedIdx).join("");
name = descriptor.names[descriptor.expandedIdx];
value = getters[name](dataItem);
value = value !== undefined && value !== null ? value.toString() : value;
parentName = name;
name = name + "&" + value;
key = path + name;
column = columns[key] || {
index: state.columnIndex,
parentName,
name,
directParentName: path.indexOf("&") !== -1 ? path : "",
uniquePath: path + parentName,
childrenMap: {},
value
};
if (path && columns[path] && !columns[path].childrenMap[path + parentName + "&" + value]) {
columns[path].childrenMap[path + parentName + "&" + value] = true;
}
totalItem = rowTotal.items[key] || { aggregates: {} };
rowTotal.items[key] = {
index: column.index,
aggregates: this._calculateAggregate(measureAggregators, aggregatorContext, totalItem)
};
if (updateColumn) {
if (!columns[key]) {
state.columnIndex++;
}
columns[key] = column;
}
}
},
_measureAggregators: function(options) {
var measureDescriptors = options.measures || [];
var measures = this.measures || {};
var aggregators = [];
var descriptor, measure, idx, length;
var defaultAggregate, aggregate;
if (measureDescriptors.length) {
for (idx = 0, length = measureDescriptors.length; idx < length; idx++) {
descriptor = measureDescriptors[idx];
measure = measures[descriptor.name];
defaultAggregate = null;
if (measure) {
aggregate = measure.aggregate;
if (typeof aggregate === "string") {
defaultAggregate = functions[aggregate.toLowerCase()];
if (!defaultAggregate) {
throw new Error("There is no such aggregate function");
}
measure.aggregate = defaultAggregate.aggregate || defaultAggregate;
measure.result = defaultAggregate.result;
}
aggregators.push({
descriptor,
caption: measure.caption,
result: measure.result,
aggregator: createAggregateGetter(measure)
});
}
}
} else {
aggregators.push({
descriptor: { name: "default" },
caption: "default",
aggregator: function() {
return 1;
}
});
}
return aggregators;
},
_buildGetters: function(names) {
var result = {};
var parts;
var name;
for (var idx = 0; idx < names.length; idx++) {
name = names[idx];
parts = name.split("&");
if (parts.length > 1) {
result[parts[0]] = kendo.getter(parts[0], true);
} else {
result[name] = kendo.getter(normalizeName(name), true);
}
}
return result;
},
_parseDescriptors: function(descriptors) {
var parsedDescriptors = parseDescriptors(descriptors);
var rootNames = getRootNames(parsedDescriptors.root);
var expanded = parsedDescriptors.expanded;
var result = [];
for (var idx = 0; idx < expanded.length; idx++) {
result.push(mapNames(expanded[idx].name, rootNames));
}
return {
root: rootNames,
expanded: result
};
},
_filter: function(data, filter) {
if (!filter) {
return data;
}
var expr;
var idx = 0;
var filters = filter.filters;
for (; idx < filters.length; idx++) {
expr = filters[idx];
if (expr.operator === "in") {
filters[idx] = this._normalizeFilter(expr);
}
}
return new kendo.data.Query(data).filter(filter).data;
},
_normalizeFilter: function(filter) {
var value = filter.value.split(",");
var result = [];
if (!value.length) {
return value;
}
for (var idx = 0; idx < value.length; idx++) {
result.push({
field: filter.field,
operator: "eq",
value: value[idx]
});
}
return {
logic: "or",
filters: result
};
},
process: function(data, options) {
data = data || [];
options = options || {};
data = this._filter(data, options.filter);
var measures = options.measures || [];
var measuresRowAxis = options.measuresAxis === "rows";
var columnDescriptors = options.columns || [];
var rowDescriptors = options.rows || [];
if (!columnDescriptors.length && rowDescriptors.length && (!measures.length || measures.length && measuresRowAxis)) {
columnDescriptors = rowDescriptors;
rowDescriptors = [];
measuresRowAxis = false;
}
if (!columnDescriptors.length && !rowDescriptors.length) {
measuresRowAxis = false;
}
if (!columnDescriptors.length && measures.length) {
columnDescriptors = normalizeMembers(options.measures);
}
columnDescriptors = this._parseDescriptors(columnDescriptors);
rowDescriptors = this._parseDescriptors(rowDescriptors);
var aggregatedData = {};
var columns = {};
var rows = {};
var rowValue;
var state = { columnIndex: 0 };
var measureAggregators = this._measureAggregators(options);
var columnGetters = this._buildGetters(columnDescriptors.root);
var rowGetters = this._buildGetters(rowDescriptors.root);
var processed = false;
var expandedColumns = columnDescriptors.expanded;
var expandedRows = rowDescriptors.expanded;
var dataItem;
var aggregatorContext;
var hasExpandedRows = expandedRows.length !== 0;
var rowIdx, rowDescriptor, rowName, rowTotal;
var key, path, parentName, value;
var columnsInfo, rowsInfo;
var length = data.length;
var idx = 0;
if (columnDescriptors.root.length || rowDescriptors.root.length) {
processed = true;
for (idx = 0; idx < length; idx++) {
dataItem = data[idx];
aggregatorContext = {
dataItem,
index: idx
};
rowTotal = aggregatedData[ROW_TOTAL_KEY] || {
items: {},
aggregates: {}
};
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, rowTotal, state, !hasExpandedRows);
rowTotal.aggregates = this._calculateAggregate(measureAggregators, aggregatorContext, rowTotal);
aggregatedData[ROW_TOTAL_KEY] = rowTotal;
for (rowIdx = 0; rowIdx < expandedRows.length; rowIdx++) {
rowDescriptor = expandedRows[rowIdx];
if (!this._matchDescriptors(dataItem, rowDescriptor, rowGetters)) {
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, {
items: {},
aggregates: {}
}, state, true);
continue;
}
path = rowDescriptor.names.slice(0, rowDescriptor.expandedIdx).join("");
rowName = rowDescriptor.names[rowDescriptor.expandedIdx];
parentName = rowName;
rowValue = rowGetters[rowName](dataItem);
rowValue = rowValue !== undefined ? rowValue.toString() : rowValue;
rowName = rowName + "&" + rowValue;
key = path + rowName;
rows[key] = {
uniquePath: path + parentName,
parentName,
name: rowName,
value: rowValue
};
value = aggregatedData[key] || {
items: {},
aggregates: {}
};
this._processColumns(measureAggregators, expandedColumns, columnGetters, columns, aggregatorContext, value, state, true);
value.aggregates = this._calculateAggregate(measureAggregators, aggregatorContext, value);
aggregatedData[key] = value;
}
}
}
if (processed && length) {
if (measureAggregators.length > 1 && (!options.columns || !options.columns.length)) {
columnDescriptors = {
root: [],
expanded: []
};
}
columnsInfo = this._asTuples(columns, columnDescriptors, measuresRowAxis ? [] : measureAggregators, options.sort ? options.sort : []);
rowsInfo = this._asTuples(rows, rowDescriptors, measuresRowAxis ? measureAggregators : [], options.sort ? options.sort : []);
columns = columnsInfo.tuples;
rows = rowsInfo.tuples;
aggregatedData = this._toDataArray(aggregatedData, this._measuresInfo(measureAggregators, measuresRowAxis), rowsInfo.keys, columnsInfo.keys);
} else {
aggregatedData = columns = rows = [];
}
return {
axes: {
columns: { tuples: columns },
rows: { tuples: rows }
},
data: aggregatedData
};
}
});
var PivotTransport = Class.extend({
init: function(options, transport) {
this.transport = transport;
this.options = transport.options || {};
if (!this.transport.discover) {
if (isFunction(options.discover)) {
this.discover = options.discover;
}
}
},
read: function(options) {
return this.transport.read(options);
},
update: function(options) {
return this.transport.update(options);
},
create: function(options) {
return this.transport.create(options);
},
destroy: function(options) {
return this.transport.destroy(options);
},
discover: function(options) {
if (this.transport.discover) {
return this.transport.discover(options);
}
options.success({});
},
catalog: function(val) {
var options = this.options || {};
if (val === undefined) {
return (options.connection || {}).catalog;
}
var connection = options.connection || {};
connection.catalog = val;
this.options.connection = connection;
$.extend(this.transport.options, { connection });
},
cube: function(val) {
var options = this.options || {};
if (val === undefined) {
return (options.connection || {}).cube;
}
var connection = options.connection || {};
connection.cube = val;
this.options.connection = connection;
extend(true, this.transport.options, { connection });
}
});
var PivotDataSourceV2 = DataSource.extend({
init: function(options) {
var cube = ((options || {}).schema || {}).cube;
var schema = {
axes: identity,
cubes: identity,
catalogs: identity,
measures: identity,
dimensions: identity,
hierarchies: identity,
levels: identity,
members: identity
};
if (cube) {
this.cubeSchema = $.extend(schema, this._cubeSchema(cube));
}
DataSource.fn.init.call(this, extend(true, {}, { schema: this.cubeSchema }, options));
var transportOptions = this.options.transport || {};
if ((this.options.type || "xmla").toLowerCase() === "xmla") {
this._online = true;
this.transport = new XmlaTransportV2(transportOptions);
} else {
this.transport = new PivotTransport(this.options.transport || {}, this.transport);
}
this._columns = normalizeMembers(this.options.columns);
this._rows = normalizeMembers(this.options.rows);
var measures = this.cubeSchema ? this.cubeSchema.measures() : this.options.measures || [];
if (toString.call(measures) === "[object Object]") {
this._measuresAxis = measures.axis || "columns";
measures = measures.values || [];
}
this._measures = normalizeMeasures(measures);
},
options: {
serverSorting: true,
serverPaging: true,
serverFiltering: true,
serverGrouping: true,
serverAggregates: true
},
axes: function() {
return {
columns: normalizeAxis(this.columns()),
rows: normalizeAxis(this.rows())
};
},
catalog: function(val) {
if (val === undefined) {
return this.transport.catalog();
}
this.transport.catalog(val);
this._mergeState({});
this.read();
},
cube: function(val) {
if (val === undefined) {
return this.transport.cube();
}
this.transport.cube(val);
this._mergeState({});
this.read();
},
measuresAxis: function() {
return this._measuresAxis || "columns";
},
fetch: function(callback) {
var that = this;
if (this._data === undefined || this._data.length === 0) {
var fn = function() {
if (isFunction(callback)) {
callback.call(that);
}
};
return this._query().done(fn);
}
},
_createSettings: function(axes) {
var settings = [];
var key;
var dimensions = this.cubeSchema.dimensionsSettings();
var displayValueFunc = function(item) {
return item[this.key];
};
var sortValueFunc = function(value) {
return value;
};
for (var i = 0; i < axes.length; i++) {
key = axes[i].name[0];
if (key.indexOf(SEPARATOR) > -1) {
continue;
}
settings.push({
key,
displayValue: displayValueFunc.bind({ key }),
sortValue: sortValueFunc,
caption: (dimensions[key] || {}).caption || key
});
}
return settings;
},
_cubeSchema: function(cube) {
return {
dimensionsSettings: function() {
return cube.dimensions;
},
dimensions: function() {
var result = [];
var dimensions = cube.dimensions;
for (var key in dimensions) {
result.push({
name: key,
caption: dimensions[key].caption || key,
uniqueName: key,
defaultHierarchy: key,
type: 1
});
}
if (cube.measures) {
result.push({
name: MEASURES,
caption: MEASURES,
uniqueName: MEASURES,
type: 2
});
}
return result;
},
restoreMeasure: function(measures, measure) {
for (var i = 0; i < measures.length; i++) {
if (!measures[i].aggregate) {
measures[i].aggregate = measure.aggregate;
measures[i].value = measure.value;
measures[i].caption = measure.caption;
measures[i].uniqueName = measure.uniqueName;
measures[i].type = 2;
}
}
},
measures: function() {
var result = [];
var measures = cube.measures;
for (var key in measures) {
result.push(createLocalMeasure(measures[key].field, key, measures[key].format, measures[key].aggregate));
}
return result;
},
memberType: function(name) {
var getter = kendo.getter(normalizeName(name), true);
var data = this.options.data || this._pristineData || [];
if (!data.length) {
return null;
}
return typeof getter(data[0]);
}.bind(this),
members: function(name) {
var data = this.options.data || this._pristineData || [];
var result = [];
var distinct = {};
var getter;
var value;
var idx = 0;
if (name.indexOf("[(ALL)]") !== -1) {
return [{
caption: cube.dimensions[name.split(".")[0]].caption || name,
levelUniqueName: name,
name,
childrenCardinality: 1,
uniqueName: name
}];
}
getter = kendo.getter(normalizeName(name), true);
for (; idx < data.length; idx++) {
value = getter(data[idx]);
if ((value || value === 0 || value === false) && !distinct[value]) {
distinct[value] = true;
result.push({
caption: value + "",
name: value + "",
childrenCardinality: 0,
uniqueName: value
});
}
}
return result;
}.bind(this)
};
},
read: function(data) {
const that = this;
const isPrevented = that.trigger(REQUESTSTART, { type: READ });
const params = that._params(data);
const deferred = $.Deferred();
if (!isPrevented) {
that.trigger(PROGRESS);
if (that.options.data) {
loadLocalData.call(that, that.options.data, params, deferred);
} else if ((this.options.type || "xmla").toLowerCase() === "xmla") {
that.transport.read({
data: params,
success: function(newDataState) {
that._saveState(newDataState);
that.trigger(REQUESTEND, {
response: newDataState,
type: READ
});
that.trigger(CHANGE);
if (that._preventRefresh) {
that._preventRefresh = false;
}
deferred.resolve();
},
error: function(err) {
that.trigger(ERROR, { error: err });
}
});
} else {
that.transport.read({
success: function(data) {
loadLocalData.call(that, data, params, deferred);
},
error: function(err) {
that.trigger(ERROR, { error: err });
}
});
}
}
return deferred.promise();
},
_params: function(data) {
var that = this;
var options = DataSource.fn._params.call(that, data);
options = extend({
columnAxes: JSON.parse(JSON.stringify(that._columns)),
rowAxes: JSON.parse(JSON.stringify(that._rows)),
measuresAxis: that.measuresAxis(),
measureAxes: that._measures
}, options);
if ((this.options.type || "").toLowerCase() === "xmla") {
options.connection = that.options.transport.connection;
}
if (options.filter) {
options.filter = normalizeFilter(options.filter);
options.filter = (options.filter || {}).filters;
}
if (options.sort) {
options.sort = normalizeSort(options.sort);
}
return options;
},
discover: function(options) {
const that = this;
const transport = that.transport;
const isOdata = that.options.type === "odata";
const converters = {
"schemaMeasures": that.reader.measures,
"schemaKPIs": that.reader.kpis,
"schemaDimensions": that.reader.dimensions,
"schemaHierarchies": that.reader.hierarchies,
"schemaLevels": that.reader.levels,
"schemaCubes": that.reader.cubes,
"schemaCatalogs": that.reader.catalogs,
"schemaMembers": that.reader.members
};
if (transport.discover && !isOdata) {
return transport.discover(options);
}
return $.Deferred(function(deferred) {
transport.discover(extend({
success: function(response) {
response = that.reader.parse(response);
if (that._handleCustomErrors(response)) {
return;
}
if (converters[options.command]) {
response = converters[options.command](response);
}
deferred.resolve(response);
},
error: function(response, status, error) {
deferred.reject(response);
that.error(response, status, error);
}
}, options));
}).promise().done(function() {
that.trigger("schemaChange");
});
},
schemaMeasures: function() {
const that = this;
return that.discover({
command: "schemaMeasures",
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
});
},
schemaKPIs: function() {
const that = this;
return that.discover({
command: "schemaKPIs",
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
});
},
schemaDimensions: function() {
const that = this;
return that.discover({
command: "schemaDimensions",
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
});
},
schemaHierarchies: function(dimensionName) {
const that = this;
return that.discover({
command: "schemaHierarchies",
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube(),
dimensionUniqueName: dimensionName
}
});
},
schemaLevels: function(hierarchyName) {
const that = this;
return that.discover({
command: "schemaLevels",
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube(),
hierarchyUniqueName: hierarchyName
}
});
},
schemaCubes: function() {
const that = this;
return that.discover({
command: "schemaCubes",
restrictions: { catalogName: that.transport.catalog() }
});
},
schemaCatalogs: function() {
const that = this;
return that.discover({ command: "schemaCatalogs" });
},
schemaMembers: function(restrictions) {
const that = this;
return that.discover({
command: "schemaMembers",
restrictions: extend({
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}, restrictions)
});
},
_saveState: function(state) {
var that = this;
that._columnTuples = state.columns;
that._rowTuples = state.rows;
that._view = that._data = state.data;
},
columns: function(val) {
if (val === undefined) {
return this._columns;
}
this._columns = normalizeMembers(val);
this.read();
},
rows: function(val) {
if (val === undefined) {
return this._rows;
}
this._rows = normalizeMembers(val);
this.read();
},
measures: function(val) {
if (val === undefined) {
return this._measures;
}
this._measures = normalizeMeasures(val);
this.read();
},
_mergeState: function(options) {
options = DataSource.fn._mergeState.call(this, options);
return options;
},
_query: function(options) {
var that = this;
var params = extend({}, {
sort: that.sort(),
measuresAxis: that.measuresAxis(),
filter: that.filter()
}, options);
this._mergeState(params);
return this.read();
}
});
var PivotDataSource = DataSource.extend({
init: function(options) {
var cube = ((options || {}).schema || {}).cube;
var measuresAxis = "columns";
var measures;
var schema = {
axes: identity,
cubes: identity,
catalogs: identity,
measures: identity,
dimensions: identity,
hierarchies: identity,
levels: identity,
members: identity
};
if (cube) {
schema = $.extend(schema, this._cubeSchema(cube));
this.cubeBuilder = new PivotCubeBuilder(cube);
}
DataSource.fn.init.call(this, extend(true, {}, { schema }, options));
this.transport = new PivotTransport(this.options.transport || {}, this.transport);
this._columns = normalizeMembers(this.options.columns);
this._rows = normalizeMembers(this.options.rows);
measures = this.options.measures || [];
if (toString.call(measures) === "[object Object]") {
measuresAxis = measures.axis || "columns";
measures = measures.values || [];
}
this._measures = normalizeMeasures(measures);
this._measuresAxis = measuresAxis;
this._skipNormalize = 0;
this._axes = {};
},
_cubeSchema: function(cube) {
return {
dimensions: function() {
var result = [];
var dimensions = cube.dimensions;
for (var key in dimensions) {
result.push({
name: key,
caption: dimensions[key].caption || key,
uniqueName: key,
defaultHierarchy: key,
type: 1
});
}
if (cube.measures) {
result.push({
name: MEASURES,
caption: MEASURES,
uniqueName: MEASURES,
type: 2
});
}
return result;
},
hierarchies: function() {
return [];
},
measures: function() {
var result = [];
var measures = cube.measures;
for (var key in measures) {
result.push({
name: key,
caption: key,
uniqueName: key,
aggregator: key
});
}
return result;
},
members: function(response, restrictions) {
var name = restrictions.levelUniqueName || restrictions.memberUniqueName;
var schemaData = this.options.schema.data;
var dataGetter = isFunction(schemaData) ? schemaData : kendo.getter(schemaData, true);
var data = this.options.data && dataGetter(this.options.data) || this._rawData || [];
var result = [];
var getter;
var value;
var idx = 0;
var distinct = {};
if (name) {
name = name.split(".")[0];
}
if (!restrictions.treeOp) {
result.push({
caption: cube.dimensions[name].caption || name,
childrenCardinality: "1",
dimensionUniqueName: name,
hierarchyUniqueName: name,
levelUniqueName: name,
name,
uniqueName: name
});
return result;
}
getter = kendo.getter(normalizeName(name), true);
for (; idx < data.length; idx++) {
value = getter(data[idx]);
if ((value || value === 0) && !distinct[value]) {
distinct[value] = true;
result.push({
caption: value,
childrenCardinality: "0",
dimensionUniqueName: name,
hierarchyUniqueName: name,
levelUniqueName: name,
name: value,
uniqueName: value
});
}
}
return result;
}.bind(this)
};
},
options: {
serverSorting: true,
serverPaging: true,
serverFiltering: true,
serverGrouping: true,
serverAggregates: true
},
catalog: function(val) {
if (val === undefined) {
return this.transport.catalog();
}
this.transport.catalog(val);
this._mergeState({});
this._axes = {};
this.data([]);
},
cube: function(val) {
if (val === undefined) {
return this.transport.cube();
}
this.transport.cube(val);
this._axes = {};
this._mergeState({});
this.data([]);
},
axes: function() {
return this._axes;
},
columns: function(val) {
if (val === undefined) {
return this._columns;
}
this._skipNormalize += 1;
this._clearAxesData = true;
this._columns = normalizeMembers(val);
this.query({
columns: val,
rows: this.rowsAxisDescriptors(),
measures: this.measures(),
sort: this.sort(),
filter: this.filter()
});
},
rows: function(val) {
if (val === undefined) {
return this._rows;
}
this._skipNormalize += 1;
this._clearAxesData = true;
this._rows = normalizeMembers(val);
this.query({
columns: this.columnsAxisDescriptors(),
rows: val,
measures: this.measures(),
sort: this.sort(),
filter: this.filter()
});
},
measures: function(val) {
if (val === undefined) {
return this._measures;
}
this._skipNormalize += 1;
this._clearAxesData = true;
this.query({
columns: this.columnsAxisDescriptors(),
rows: this.rowsAxisDescriptors(),
measures: normalizeMeasures(val),
sort: this.sort(),
filter: this.filter()
});
},
measuresAxis: function() {
return this._measuresAxis || "columns";
},
_expandPath: function(path, axis) {
var origin = axis === "columns" ? "columns" : "rows";
var other = axis === "columns" ? "rows" : "columns";
var members = normalizeMembers(path);
var memberToExpand = getName(members[members.length - 1]);
this._lastExpanded = origin;
members = descriptorsForMembers(this.axes()[origin], members, this.measures());
for (var idx = 0; idx < members.length; idx++) {
var memberName = getName(members[idx]);
if (memberName === memberToExpand) {
if (members[idx].expand) {
return;
}
members[idx].expand = true;
} else {
members[idx].expand = false;
}
}
var descriptors = {};
descriptors[origin] = members;
descriptors[other] = this._descriptorsForAxis(other);
this._query(descriptors);
},
_descriptorsForAxis: function(axis) {
var axes = this.axes();
var descriptors = this[axis]() || [];
if (axes && axes[axis] && axes[axis].tuples && axes[axis].tuples[0]) {
descriptors = descriptorsForAxes(axes[axis].tuples || []);
}
return descriptors;
},
columnsAxisDescriptors: function() {
return this._descriptorsForAxis("columns");
},
rowsAxisDescriptors: function() {
return this._descriptorsForAxis("rows");
},
_process: function(data, e) {
this._view = data;
e = e || {};
e.items = e.items || this._view;
this.trigger(CHANGE, e);
},
_query: function(options) {
var that = this;
if (!options) {
this._skipNormalize += 1;
this._clearAxesData = true;
}
return that.query(extend({}, {
page: that.page(),