/**
* Kendo UI v2016.1.112 (http://www.telerik.com/kendo-ui)
* Copyright 2016 Telerik AD. All rights reserved.
*
* Kendo UI commercial licenses may be obtained at
* http://www.telerik.com/purchase/license-agreement/kendo-ui-complete
* If you do not own a commercial license, this file shall be governed by the trial license terms.
*/
(function (f, define) {
define('kendo.pivotgrid', [
'kendo.dom',
'kendo.data'
], f);
}(function () {
var __meta__ = {
id: 'pivotgrid',
name: 'PivotGrid',
category: 'web',
description: 'The PivotGrid widget is a data summarization tool.',
depends: [
'dom',
'data',
'data.xml',
'sortable'
],
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, Class = kendo.Class, Widget = ui.Widget, DataSource = kendo.data.DataSource, toString = {}.toString, identity = function (o) {
return o;
}, map = $.map, extend = $.extend, isFunction = kendo.isFunction, CHANGE = 'change', ERROR = 'error', MEASURES = 'Measures', PROGRESS = 'progress', STATERESET = 'stateReset', AUTO = 'auto', DIV = '
', NS = '.kendoPivotGrid', ROW_TOTAL_KEY = '__row_total__', DATABINDING = 'dataBinding', DATABOUND = 'dataBound', EXPANDMEMBER = 'expandMember', COLLAPSEMEMBER = 'collapseMember', STATE_EXPANDED = 'k-i-arrow-s', STATE_COLLAPSED = 'k-i-arrow-e', HEADER_TEMPLATE = '#: data.member.caption || data.member.name #', KPISTATUS_TEMPLATE = '#:data.dataItem.value#', KPITREND_TEMPLATE = '#:data.dataItem.value#', DATACELL_TEMPLATE = '#= data.dataItem ? kendo.htmlEncode(data.dataItem.fmtValue || data.dataItem.value) || " " : " " #', LAYOUT_TABLE = '' + '' + '' + '' + ' | ' + '' + '' + ' | ' + '
' + '
';
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 {
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: $.parseJSON(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: 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;
}
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: 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: keys,
tuples: result
};
},
_expandedTuples: function (map, expanded, measureAggregators) {
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;
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: 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) {
measureAggregators = measureAggregators || [];
var rootInfo = this._rootTuples(descriptor.root, measureAggregators);
var expandedInfo = this._expandedTuples(map, descriptor.expanded, measureAggregators);
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: formats,
resultFuncs: resultFuncs,
rowAxis: 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: ordinal,
value: value || '',
fmtValue: 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: parentName,
name: name,
uniquePath: path + parentName,
value: value
};
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: 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: 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: 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);
rowsInfo = this._asTuples(rows, rowDescriptors, measuresRowAxis ? measureAggregators : []);
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: 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: connection });
}
});
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: 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: $.proxy(function (response, restrictions) {
var name = restrictions.levelUniqueName || restrictions.memberUniqueName;
var data = 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: 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;
}, 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()
});
},
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()
});
},
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)
});
},
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(),
pageSize: that.pageSize(),
sort: that.sort(),
filter: that.filter(),
group: that.group(),
aggregate: that.aggregate(),
columns: this.columnsAxisDescriptors(),
rows: this.rowsAxisDescriptors(),
measures: this.measures()
}, options));
},
query: function (options) {
var state = this._mergeState(options);
if (this._data.length && this.cubeBuilder) {
this._params(state);
this._updateLocalData(this._pristineData);
return $.Deferred().resolve().promise();
}
return this.read(state);
},
_mergeState: function (options) {
options = DataSource.fn._mergeState.call(this, options);
if (options !== undefined) {
this._measures = normalizeMeasures(options.measures);
if (options.columns) {
options.columns = normalizeMembers(options.columns);
} else if (!options.columns) {
this._columns = [];
}
if (options.rows) {
options.rows = normalizeMembers(options.rows);
} else if (!options.rows) {
this._rows = [];
}
}
return options;
},
filter: function (val) {
if (val === undefined) {
return this._filter;
}
this._skipNormalize += 1;
this._clearAxesData = true;
this._query({
filter: val,
page: 1
});
},
expandColumn: function (path) {
this._expandPath(path, 'columns');
},
expandRow: function (path) {
this._expandPath(path, 'rows');
},
success: function (data) {
var originalData;
if (this.cubeBuilder) {
originalData = (this.reader.data(data) || []).slice(0);
}
DataSource.fn.success.call(this, data);
if (originalData) {
this._pristineData = originalData;
}
},
_processResult: function (data, axes) {
if (this.cubeBuilder) {
var processedData = this.cubeBuilder.process(data, this._requestData);
data = processedData.data;
axes = processedData.axes;
}
var columnIndexes, rowIndexes;
var tuples, resultAxis, measures, axisToSkip;
var columnDescriptors = this.columns();
var rowDescriptors = this.rows();
var hasColumnTuples = axes.columns && axes.columns.tuples;
if (!columnDescriptors.length && rowDescriptors.length && hasColumnTuples && (this._rowMeasures().length || !this.measures().length)) {
axes = {
columns: {},
rows: axes.columns
};
}
if (!columnDescriptors.length && !rowDescriptors.length && this.measuresAxis() === 'rows' && hasColumnTuples) {
axes = {
columns: {},
rows: axes.columns
};
}
this._axes = {
columns: normalizeAxis(this._axes.columns),
rows: normalizeAxis(this._axes.rows)
};
axes = {
columns: normalizeAxis(axes.columns),
rows: normalizeAxis(axes.rows)
};
columnIndexes = this._normalizeTuples(axes.columns.tuples, this._axes.columns.tuples, columnDescriptors, this._columnMeasures());
rowIndexes = this._normalizeTuples(axes.rows.tuples, this._axes.rows.tuples, rowDescriptors, this._rowMeasures());
this._skipNormalize -= 1;
if (!this.cubeBuilder) {
data = this._normalizeData({
columnsLength: axes.columns.tuples.length,
rowsLength: axes.rows.tuples.length,
columnIndexes: columnIndexes,
rowIndexes: rowIndexes,
data: data
});
}
if (this._lastExpanded == 'rows') {
tuples = axes.columns.tuples;
measures = this._columnMeasures();
resultAxis = validateAxis(axes.columns, this._axes.columns, measures);
if (resultAxis) {
axisToSkip = 'columns';
axes.columns = resultAxis;
adjustDataByColumn(tuples, resultAxis.tuples, axes.rows.tuples.length, measures, data);
if (!this.cubeBuilder) {
data = this._normalizeData({
columnsLength: membersCount(axes.columns.tuples, measures),
rowsLength: axes.rows.tuples.length,
data: data
});
}
}
} else if (this._lastExpanded == 'columns') {
tuples = axes.rows.tuples;
measures = this._rowMeasures();
resultAxis = validateAxis(axes.rows, this._axes.rows, measures);
if (resultAxis) {
axisToSkip = 'rows';
axes.rows = resultAxis;
adjustDataByRow(tuples, resultAxis.tuples, axes.columns.tuples.length, measures, data);
if (!this.cubeBuilder) {
data = this._normalizeData({
columnsLength: membersCount(axes.rows.tuples, measures),
rowsLength: axes.columns.tuples.length,
data: data
});
}
}
}
this._lastExpanded = null;
var result = this._mergeAxes(axes, data, axisToSkip);
this._axes = result.axes;
return result.data;
},
_readData: function (data) {
var axes = this.reader.axes(data);
var newData = this.reader.data(data);
if (this.cubeBuilder) {
this._rawData = newData;
}
return this._processResult(newData, axes);
},
_createTuple: function (tuple, measure, buildRoot) {
var members = tuple.members;
var length = members.length;
var root = { members: [] };
var levelName, levelNum;
var name, parentName;
var hasChildren;
var hierarchy;
var caption;
var member;
var idx = 0;
if (measure) {
length -= 1;
}
for (; idx < length; idx++) {
member = members[idx];
levelNum = Number(member.levelNum);
name = member.name;
parentName = member.parentName;
caption = member.caption || name;
hasChildren = member.hasChildren;
hierarchy = member.hierarchy;
levelName = member.levelName;
if (buildRoot) {
caption = 'All';
if (levelNum === 0) {
parentName = member.name;
} else {
levelNum -= 1;
}
hasChildren = true;
name = hierarchy = levelName = parentName;
}
root.members.push({
name: name,
children: [],
caption: caption,
levelName: levelName,
levelNum: levelNum.toString(),
hasChildren: hasChildren,
hierarchy: hierarchy,
parentName: !buildRoot ? parentName : ''
});
}
if (measure) {
root.members.push({
name: measure.name,
children: []
});
}
return root;
},
_hasRoot: function (target, source, descriptors) {
if (source.length) {
return findExistingTuple(source, target).tuple;
}
var members = target.members;
var member;
var descriptor;
var isRoot = true;
var levelNum;
for (var idx = 0, length = members.length; idx < length; idx++) {
member = members[idx];
levelNum = Number(member.levelNum) || 0;
descriptor = descriptors[idx];
if (!(levelNum === 0 || descriptor && member.name === getName(descriptor))) {
isRoot = false;
break;
}
}
return isRoot;
},
_mergeAxes: function (sourceAxes, data, axisToSkip) {
var columnMeasures = this._columnMeasures();
var rowMeasures = this._rowMeasures();
var axes = this.axes();
var startIndex, tuples;
var newRowsLength = sourceAxes.rows.tuples.length;
var oldColumnsLength = membersCount(axes.columns.tuples, columnMeasures);
var newColumnsLength = sourceAxes.columns.tuples.length;
if (axisToSkip == 'columns') {
newColumnsLength = oldColumnsLength;
tuples = sourceAxes.columns.tuples;
} else {
tuples = parseSource(sourceAxes.columns.tuples, columnMeasures);
data = prepareDataOnColumns(tuples, data);
}
var mergedColumns = mergeTuples(axes.columns.tuples, tuples, columnMeasures);
if (axisToSkip == 'rows') {
newRowsLength = membersCount(sourceAxes.rows.tuples, rowMeasures);
tuples = sourceAxes.rows.tuples;
} else {
tuples = parseSource(sourceAxes.rows.tuples, rowMeasures);
data = prepareDataOnRows(tuples, data);
}
var mergedRows = mergeTuples(axes.rows.tuples, tuples, rowMeasures);
axes.columns.tuples = mergedColumns.tuples;
axes.rows.tuples = mergedRows.tuples;
if (oldColumnsLength !== membersCount(axes.columns.tuples, columnMeasures)) {
startIndex = mergedColumns.index + findDataIndex(mergedColumns.parsedRoot, mergedColumns.memberIndex, columnMeasures);
var offset = oldColumnsLength + newColumnsLength;
data = this._mergeColumnData(data, startIndex, newRowsLength, newColumnsLength, offset);
} else {
startIndex = mergedRows.index + findDataIndex(mergedRows.parsedRoot, mergedRows.memberIndex, rowMeasures);
data = this._mergeRowData(data, startIndex, newRowsLength, newColumnsLength);
}
return {
axes: axes,
data: data
};
},
_mergeColumnData: function (newData, columnIndex, rowsLength, columnsLength, offset) {
var data = this.data().toJSON();
var rowIndex, index, drop = 0, toAdd;
var columnMeasures = Math.max(this._columnMeasures().length, 1);
rowsLength = Math.max(rowsLength, 1);
if (data.length > 0) {
drop = columnMeasures;
offset -= columnMeasures;
}
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
index = columnIndex + rowIndex * offset;
toAdd = newData.splice(0, columnsLength);
toAdd.splice(0, drop);
[].splice.apply(data, [
index,
0
].concat(toAdd));
}
return data;
},
_mergeRowData: function (newData, rowIndex, rowsLength, columnsLength) {
var data = this.data().toJSON();
var idx, dataIndex, toAdd;
var rowMeasures = Math.max(this._rowMeasures().length, 1);
columnsLength = Math.max(columnsLength, 1);
if (data.length > 0) {
rowsLength -= rowMeasures;
newData.splice(0, columnsLength * rowMeasures);
}
for (idx = 0; idx < rowsLength; idx++) {
toAdd = newData.splice(0, columnsLength);
dataIndex = rowIndex * columnsLength + idx * columnsLength;
[].splice.apply(data, [
dataIndex,
0
].concat(toAdd));
}
return data;
},
_columnMeasures: function () {
var measures = this.measures();
var columnMeasures = [];
if (this.measuresAxis() === 'columns') {
if (this.columns().length === 0) {
columnMeasures = measures;
} else if (measures.length > 1) {
columnMeasures = measures;
}
}
return columnMeasures;
},
_rowMeasures: function () {
var measures = this.measures();
var rowMeasures = [];
if (this.measuresAxis() === 'rows') {
if (this.rows().length === 0) {
rowMeasures = measures;
} else if (measures.length > 1) {
rowMeasures = measures;
}
}
return rowMeasures;
},
_updateLocalData: function (data, state) {
if (this.cubeBuilder) {
if (state) {
this._requestData = state;
}
data = this._processResult(data);
}
this._data = this._observe(data);
this._ranges = [];
this._addRange(this._data);
this._total = this._data.length;
this._pristineTotal = this._total;
this._process(this._data);
},
data: function (value) {
var that = this;
if (value !== undefined) {
this._pristineData = value.slice(0);
this._updateLocalData(value, {
columns: this.columns(),
rows: this.rows(),
measures: this.measures()
});
} else {
return that._data;
}
},
_normalizeTuples: function (tuples, source, descriptors, measures) {
var length = measures.length || 1;
var idx = 0;
var roots = [];
var indexes = {};
var measureIdx = 0;
var tuple, memberIdx, last;
if (!tuples.length) {
return;
}
if (this._skipNormalize <= 0 && !this._hasRoot(tuples[0], source, descriptors)) {
this._skipNormalize = 0;
for (; idx < length; idx++) {
roots.push(this._createTuple(tuples[0], measures[idx], true));
indexes[idx] = idx;
}
tuples.splice.apply(tuples, [
0,
tuples.length
].concat(roots).concat(tuples));
idx = length;
}
if (measures.length) {
last = tuple = tuples[idx];
memberIdx = tuple.members.length - 1;
while (tuple) {
if (measureIdx >= length) {
measureIdx = 0;
}
if (tuple.members[memberIdx].name !== measures[measureIdx].name) {
tuples.splice(idx, 0, this._createTuple(tuple, measures[measureIdx]));
indexes[idx] = idx;
}
idx += 1;
measureIdx += 1;
tuple = tuples[idx];
if (length > measureIdx && (!tuple || tupleName(last, memberIdx - 1) !== tupleName(tuple, memberIdx - 1))) {
for (; measureIdx < length; measureIdx++) {
tuples.splice(idx, 0, this._createTuple(last, measures[measureIdx]));
indexes[idx] = idx;
idx += 1;
}
tuple = tuples[idx];
}
last = tuple;
}
}
return indexes;
},
_addMissingDataItems: function (result, metadata) {
while (metadata.rowIndexes[parseInt(result.length / metadata.columnsLength, 10)] !== undefined) {
for (var idx = 0; idx < metadata.columnsLength; idx++) {
result = addEmptyDataItem(result);
}
}
while (metadata.columnIndexes[result.length % metadata.columnsLength] !== undefined) {
result = addEmptyDataItem(result);
}
return result;
},
_normalizeOrdinals: function (result, dataItem, metadata) {
var lastOrdinal = metadata.lastOrdinal;
if (!dataItem) {
return addEmptyDataItem(result);
}
if (dataItem.ordinal - lastOrdinal > 1) {
lastOrdinal += 1;
while (lastOrdinal < dataItem.ordinal && result.length < metadata.length) {
result = this._addMissingDataItems(addEmptyDataItem(result), metadata);
lastOrdinal += 1;
}
}
dataItem.ordinal = result.length;
result[result.length] = dataItem;
return result;
},
_normalizeData: function (options) {
var data = options.data;
var dataIdx = 0;
var dataItem;
var result = [];
var lastOrdinal;
var length;
options.lastOrdinal = 0;
options.columnIndexes = options.columnIndexes || {};
options.rowIndexes = options.rowIndexes || {};
options.columnsLength = options.columnsLength || 1;
options.rowsLength = options.rowsLength || 1;
options.length = options.columnsLength * options.rowsLength;
length = options.length;
if (data.length === length) {
return data;
}
while (result.length < length) {
dataItem = data[dataIdx++];
if (dataItem) {
lastOrdinal = dataItem.ordinal;
}
result = this._normalizeOrdinals(this._addMissingDataItems(result, options), dataItem, options);
options.lastOrdinal = lastOrdinal;
}
return result;
},
discover: function (options, converter) {
var that = this, transport = that.transport;
return $.Deferred(function (deferred) {
transport.discover(extend({
success: function (response) {
response = that.reader.parse(response);
if (that._handleCustomErrors(response)) {
return;
}
if (converter) {
response = converter(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 () {
var that = this;
return that.discover({
data: {
command: 'schemaMeasures',
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
}
}, function (response) {
return that.reader.measures(response);
});
},
schemaKPIs: function () {
var that = this;
return that.discover({
data: {
command: 'schemaKPIs',
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
}
}, function (response) {
return that.reader.kpis(response);
});
},
schemaDimensions: function () {
var that = this;
return that.discover({
data: {
command: 'schemaDimensions',
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}
}
}, function (response) {
return that.reader.dimensions(response);
});
},
schemaHierarchies: function (dimensionName) {
var that = this;
return that.discover({
data: {
command: 'schemaHierarchies',
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube(),
dimensionUniqueName: dimensionName
}
}
}, function (response) {
return that.reader.hierarchies(response);
});
},
schemaLevels: function (hierarchyName) {
var that = this;
return that.discover({
data: {
command: 'schemaLevels',
restrictions: {
catalogName: that.transport.catalog(),
cubeName: that.transport.cube(),
hierarchyUniqueName: hierarchyName
}
}
}, function (response) {
return that.reader.levels(response);
});
},
schemaCubes: function () {
var that = this;
return that.discover({
data: {
command: 'schemaCubes',
restrictions: { catalogName: that.transport.catalog() }
}
}, function (response) {
return that.reader.cubes(response);
});
},
schemaCatalogs: function () {
var that = this;
return that.discover({ data: { command: 'schemaCatalogs' } }, function (response) {
return that.reader.catalogs(response);
});
},
schemaMembers: function (restrictions) {
var that = this;
var success = function (restrictions) {
return function (response) {
return that.reader.members(response, restrictions);
};
}(restrictions);
return that.discover({
data: {
command: 'schemaMembers',
restrictions: extend({
catalogName: that.transport.catalog(),
cubeName: that.transport.cube()
}, restrictions)
}
}, success);
},
_params: function (data) {
if (this._clearAxesData) {
this._axes = {};
this._data = this._observe([]);
this._clearAxesData = false;
this.trigger(STATERESET);
}
var options = DataSource.fn._params.call(this, data);
options = extend({
measures: this.measures(),
measuresAxis: this.measuresAxis(),
columns: this.columns(),
rows: this.rows()
}, options);
if (this.cubeBuilder) {
this._requestData = options;
}
return options;
}
});
function addEmptyDataItem(result) {
result[result.length] = {
value: '',
fmtValue: '',
ordinal: result.length
};
return result;
}
function validateAxis(newAxis, axis, measures) {
if (newAxis.tuples.length < membersCount(axis.tuples, measures)) {
return axis;
}
return;
}
function adjustDataByColumn(sourceTuples, targetTuples, rowsLength, measures, data) {
var columnIdx, rowIdx, dataIdx;
var columnsLength = sourceTuples.length;
var targetColumnsLength = membersCount(targetTuples, measures);
var measuresLength = measures.length || 1;
for (rowIdx = 0; rowIdx < rowsLength; rowIdx++) {
for (columnIdx = 0; columnIdx < columnsLength; columnIdx++) {
dataIdx = tupleIndex(sourceTuples[columnIdx], targetTuples) * measuresLength;
dataIdx += columnIdx % measuresLength;
data[rowIdx * columnsLength + columnIdx].ordinal = rowIdx * targetColumnsLength + dataIdx;
}
}
}
function adjustDataByRow(sourceTuples, targetTuples, columnsLength, measures, data) {
var columnIdx, rowIdx, dataIdx;
var rowsLength = sourceTuples.length;
var measuresLength = measures.length || 1;
for (rowIdx = 0; rowIdx < rowsLength; rowIdx++) {
dataIdx = tupleIndex(sourceTuples[rowIdx], targetTuples);
dataIdx *= measuresLength;
dataIdx += rowIdx % measuresLength;
for (columnIdx = 0; columnIdx < columnsLength; columnIdx++) {
data[rowIdx * columnsLength + columnIdx].ordinal = dataIdx * columnsLength + columnIdx;
}
}
}
function tupleIndex(tuple, collection) {
return findExistingTuple(collection, tuple).index;
}
function membersCount(tuples, measures) {
if (!tuples.length) {
return 0;
}
var queue = tuples.slice();
var current = queue.shift();
var result = 1;
while (current) {
if (current.members) {
[].push.apply(queue, current.members);
} else if (current.children) {
if (!current.measure) {
result += current.children.length;
}
[].push.apply(queue, current.children);
}
current = queue.shift();
}
if (measures.length) {
result = result * measures.length;
}
return result;
}
function normalizeAxis(axis) {
if (!axis) {
axis = { tuples: [] };
}
if (!axis.tuples) {
axis.tuples = [];
}
return axis;
}
function findDataIndex(tuple, memberIndex, measures) {
if (!tuple) {
return 0;
}
var measuresLength = Math.max(measures.length, 1);
var tuples = tuple.members.slice(0, memberIndex);
var counter = measuresLength;
var current = tuples.shift();
if (measuresLength > 1) {
measuresLength += 1;
}
while (current) {
if (current.name === MEASURES) {
counter += measuresLength;
} else if (current.children) {
[].push.apply(tuples, current.children);
} else {
counter++;
[].push.apply(tuples, current.members);
}
current = tuples.shift();
}
return counter;
}
function mergeTuples(target, source, measures) {
if (!source[0]) {
return {
parsedRoot: null,
tuples: target,
memberIndex: 0,
index: 0
};
}
var result = findExistingTuple(target, source[0]);
if (!result.tuple) {
return {
parsedRoot: null,
tuples: source,
memberIndex: 0,
index: 0
};
}
var targetMembers = result.tuple.members;
var sourceMembers = source[0].members;
var memberIndex = -1;
if (targetMembers.length !== sourceMembers.length) {
return {
parsedRoot: null,
tuples: source,
memberIndex: 0,
index: 0
};
}
for (var idx = 0, length = targetMembers.length; idx < length; idx++) {
if (!targetMembers[idx].measure && sourceMembers[idx].children[0]) {
if (memberIndex == -1 && sourceMembers[idx].children.length) {
memberIndex = idx;
}
targetMembers[idx].children = sourceMembers[idx].children;
}
}
measures = Math.max(measures.length, 1);
return {
parsedRoot: result.tuple,
index: result.index * measures,
memberIndex: memberIndex,
tuples: target
};
}
function equalTuples(first, second) {
var equal = true;
var idx, length;
first = first.members;
second = second.members;
for (idx = 0, length = first.length; idx < length; idx++) {
if (first[idx].measure || second[idx].measure) {
continue;
}
equal = equal && getName(first[idx]) === getName(second[idx]);
}
return equal;
}
function findExistingTuple(tuples, toFind) {
var idx, length, tuple, found, counter = 0;
var memberIndex, membersLength, member;
for (idx = 0, length = tuples.length; idx < length; idx++) {
tuple = tuples[idx];
if (equalTuples(tuple, toFind)) {
return {
tuple: tuple,
index: counter
};
}
counter++;
for (memberIndex = 0, membersLength = tuple.members.length; memberIndex < membersLength; memberIndex++) {
member = tuple.members[memberIndex];
if (member.measure) {
continue;
}
found = findExistingTuple(member.children, toFind);
counter += found.index;
if (found.tuple) {
return {
tuple: found.tuple,
index: counter
};
}
}
}
return { index: counter };
}
function addMembers(members, map) {
var member, i, len, path = '';
for (i = 0, len = members.length; i < len; i++) {
member = members[i];
path += member.name;
if (!map[path]) {
map[path] = member;
}
}
}
function findParentMember(tuple, map) {
var members = tuple.members;
var i, len, member, path = '';
var parentPath = '';
var parentMember;
for (i = 0, len = members.length; i < len; i++) {
member = members[i];
if (parentMember) {
if (map[path + member.name]) {
path += member.name;
parentMember = map[path];
continue;
} else if (map[path + member.parentName]) {
return map[path + member.parentName];
} else if (map[parentPath + member.parentName]) {
return map[parentPath + member.parentName];
} else {
return map[parentPath];
}
}
path += member.name;
parentMember = map[member.parentName];
if (!parentMember) {
parentMember = map[path];
if (!parentMember) {
return null;
}
}
if (parentMember) {
parentPath += parentMember.name;
}
}
return parentMember;
}
function measurePosition(tuple, measures) {
if (measures.length === 0) {
return -1;
}
var measure = measures[0];
var members = tuple.members;
for (var idx = 0, len = members.length; idx < len; idx++) {
if (members[idx].name == measure.name) {
return idx;
}
}
}
function normalizeTupleMeasures(tuple, index) {
if (index < 0) {
return;
}
var member = {
name: MEASURES,
measure: true,
children: [$.extend({
members: [],
dataIndex: tuple.dataIndex
}, tuple.members[index])]
};
tuple.members.splice(index, 1, member);
tuple.dataIndex = undefined;
}
function parseSource(tuples, measures) {
if (tuples.length < 1) {
return [];
}
var result = [];
var map = {};
var measureIndex = measurePosition(tuples[0], measures);
for (var i = 0; i < tuples.length; i++) {
var tuple = tuples[i];
tuple.dataIndex = i;
normalizeTupleMeasures(tuple, measureIndex);
var parentMember = findParentMember(tuple, map);
if (parentMember) {
if (measureIndex < 0 || !parentMember.measure) {
parentMember.children.push(tuple);
} else {
parentMember.children.push(tuple.members[measureIndex].children[0]);
}
} else {
result.push(tuple);
}
addMembers(tuple.members, map);
}
return result;
}
function prepareDataOnRows(tuples, data) {
if (!tuples || !tuples.length) {
return data;
}
var result = [];
var indices = buildDataIndices(tuples);
var rowsLength = indices.length;
var columnsLength = Math.max(data.length / rowsLength, 1);
var rowIndex, columnIndex, targetIndex, sourceIndex;
var calcIndex;
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
targetIndex = columnsLength * rowIndex;
sourceIndex = columnsLength * indices[rowIndex];
for (columnIndex = 0; columnIndex < columnsLength; columnIndex++) {
calcIndex = parseInt(sourceIndex + columnIndex, 10);
result[parseInt(targetIndex + columnIndex, 10)] = data[calcIndex] || {
value: '',
fmtValue: '',
ordinal: calcIndex
};
}
}
return result;
}
function prepareDataOnColumns(tuples, data) {
if (!tuples || !tuples.length) {
return data;
}
var result = [];
var indices = buildDataIndices(tuples);
var columnsLength = indices.length;
var rowsLength = Math.max(data.length / columnsLength, 1);
var columnIndex, rowIndex, dataIndex, calcIndex;
for (rowIndex = 0; rowIndex < rowsLength; rowIndex++) {
dataIndex = columnsLength * rowIndex;
for (columnIndex = 0; columnIndex < columnsLength; columnIndex++) {
calcIndex = indices[columnIndex] + dataIndex;
result[dataIndex + columnIndex] = data[calcIndex] || {
value: '',
fmtValue: '',
ordinal: calcIndex
};
}
}
return result;
}
function buildDataIndices(tuples) {
tuples = tuples.slice();
var result = [];
var tuple = tuples.shift();
var idx, length, spliceIndex, children, member;
while (tuple) {
if (tuple.dataIndex !== undefined) {
result.push(tuple.dataIndex);
}
spliceIndex = 0;
for (idx = 0, length = tuple.members.length; idx < length; idx++) {
member = tuple.members[idx];
children = member.children;
if (member.measure) {
[].splice.apply(tuples, [
0,
0
].concat(children));
} else {
[].splice.apply(tuples, [
spliceIndex,
0
].concat(children));
}
spliceIndex += children.length;
}
tuple = tuples.shift();
}
return result;
}
PivotDataSource.create = function (options) {
options = options && options.push ? { data: options } : options;
var dataSource = options || {}, data = dataSource.data;
dataSource.data = data;
if (!(dataSource instanceof PivotDataSource) && dataSource instanceof kendo.data.DataSource) {
throw new Error('Incorrect DataSource type. Only PivotDataSource instances are supported');
}
return dataSource instanceof PivotDataSource ? dataSource : new PivotDataSource(dataSource);
};
function baseHierarchyPath(memberName) {
var parts = memberName.split('.');
if (parts.length > 2) {
return parts[0] + '.' + parts[1];
}
return memberName;
}
function expandMemberDescriptor(names, sort) {
var idx = names.length - 1;
var name = names[idx];
var sortDescriptor;
sortDescriptor = sortDescriptorForMember(sort, name);
if (sortDescriptor && sortDescriptor.dir) {
name = 'ORDER(' + name + '.Children,' + sortDescriptor.field + '.CurrentMember.MEMBER_CAPTION,' + sortDescriptor.dir + ')';
} else {
name += '.Children';
}
names[idx] = name;
return names;
}
function sortDescriptorForMember(sort, member) {
for (var idx = 0, length = sort.length; idx < length; idx++) {
if (member.indexOf(sort[idx].field) === 0) {
return sort[idx];
}
}
return null;
}
function crossJoin(names) {
var result = 'CROSSJOIN({';
var r;
if (names.length > 2) {
r = names.pop();
result += crossJoin(names);
} else {
result += names.shift();
r = names.pop();
}
result += '},{';
result += r;
result += '})';
return result;
}
function crossJoinCommand(members, measures) {
var tmp = members.slice(0);
if (measures.length > 1) {
tmp.push('{' + measureNames(measures).join(',') + '}');
}
return crossJoin(tmp);
}
function measureNames(measures) {
var idx = 0;
var length = measures.length;
var result = [];
var measure;
for (; idx < length; idx++) {
measure = measures[idx];
result.push(measure.name !== undefined ? measure.name : measure);
}
return result;
}
function getName(name) {
name = name.name || name;
if (toString.call(name) === '[object Array]') {
name = name[name.length - 1];
}
return name;
}
function getRootNames(members) {
var length = members.length;
var names = [];
var idx = 0;
for (; idx < length; idx++) {
names.push(members[idx].name[0]);
}
return names;
}
function mapNames(names, rootNames) {
var name;
var rootName;
var j;
var idx = 0;
var length = names.length;
var rootLength = rootNames.length;
rootNames = rootNames.slice(0);
for (; idx < length; idx++) {
name = names[idx];
for (j = 0; j < rootLength; j++) {
rootName = baseHierarchyPath(rootNames[j]);
if (name.indexOf(rootName) !== -1) {
rootNames[j] = name;
break;
}
}
}
return {
names: rootNames,
expandedIdx: j,
uniquePath: rootNames.slice(0, j + 1).join('')
};
}
function parseDescriptors(members) {
var expanded = [];
var child = [];
var root = [];
var member;
var j, l;
var idx = 0;
var length = members.length;
var name;
var hierarchyName;
var found;
for (; idx < length; idx++) {
member = members[idx];
name = member.name;
found = false;
if (toString.call(name) !== '[object Array]') {
member.name = name = [name];
}
if (name.length > 1) {
child.push(member);
} else {
hierarchyName = baseHierarchyPath(name[0]);
for (j = 0, l = root.length; j < l; j++) {
if (root[j].name[0].indexOf(hierarchyName) === 0) {
found = true;
break;
}
}
if (!found) {
root.push(member);
}
if (member.expand) {
expanded.push(member);
}
}
}
expanded = expanded.concat(child);
return {
root: root,
expanded: expanded
};
}
function serializeMembers(members, measures, sort) {
var command = '';
members = members || [];
var expanded = parseDescriptors(members);
var root = expanded.root;
var rootNames = getRootNames(root);
var crossJoinCommands = [];
expanded = expanded.expanded;
var length = expanded.length;
var idx = 0;
var memberName;
var names = [];
if (rootNames.length > 1 || measures.length > 1) {
crossJoinCommands.push(crossJoinCommand(rootNames, measures));
for (; idx < length; idx++) {
memberName = expandMemberDescriptor(expanded[idx].name, sort);
names = mapNames(memberName, rootNames).names;
crossJoinCommands.push(crossJoinCommand(names, measures));
}
command += crossJoinCommands.join(',');
} else {
for (; idx < length; idx++) {
memberName = expandMemberDescriptor(expanded[idx].name, sort);
names.push(memberName[0]);
}
command += rootNames.concat(names).join(',');
}
return command;
}
var filterFunctionFormats = {
contains: ', InStr({0}.CurrentMember.MEMBER_CAPTION,"{1}") > 0',
doesnotcontain: ', InStr({0}.CurrentMember.MEMBER_CAPTION,"{1}") = 0',
startswith: ', Left({0}.CurrentMember.MEMBER_CAPTION,Len("{1}"))="{1}"',
endswith: ', Right({0}.CurrentMember.MEMBER_CAPTION,Len("{1}"))="{1}"',
eq: ', {0}.CurrentMember.MEMBER_CAPTION = "{1}"',
neq: ', NOT {0}.CurrentMember.MEMBER_CAPTION = "{1}"'
};
function serializeExpression(expression) {
var command = '';
var value = expression.value;
var field = expression.field;
var operator = expression.operator;
if (operator == 'in') {
command += '{';
command += value;
command += '}';
} else {
command += 'Filter(';
command += field + '.MEMBERS';
command += kendo.format(filterFunctionFormats[operator], field, value);
command += ')';
}
return command;
}
function serializeFilters(filter, cube) {
var command = '', current;
var filters = filter.filters;
var length = filters.length;
var idx;
for (idx = length - 1; idx >= 0; idx--) {
current = 'SELECT (';
current += serializeExpression(filters[idx]);
current += ') ON 0';
if (idx == length - 1) {
current += ' FROM [' + cube + ']';
command = current;
} else {
command = current + ' FROM ( ' + command + ' )';
}
}
return command;
}
function serializeOptions(parentTagName, options, capitalize) {
var result = '';
if (options) {
result += '<' + parentTagName + '>';
var value;
for (var key in options) {
value = options[key];
if (capitalize) {
key = key.replace(/([A-Z]+(?=$|[A-Z][a-z])|[A-Z]?[a-z]+)/g, '$1_').toUpperCase().replace(/_$/, '');
}
result += '<' + key + '>' + value + '' + key + '>';
}
result += '' + parentTagName + '>';
} else {
result += '<' + parentTagName + '/>';
}
return result;
}
var xmlaDiscoverCommands = {
schemaCubes: 'MDSCHEMA_CUBES',
schemaCatalogs: 'DBSCHEMA_CATALOGS',
schemaMeasures: 'MDSCHEMA_MEASURES',
schemaDimensions: 'MDSCHEMA_DIMENSIONS',
schemaHierarchies: 'MDSCHEMA_HIERARCHIES',
schemaLevels: 'MDSCHEMA_LEVELS',
schemaMembers: 'MDSCHEMA_MEMBERS',
schemaKPIs: 'MDSCHEMA_KPIS'
};
var convertersMap = {
read: function (options) {
var command = '';
command += 'SELECT NON EMPTY {';
var columns = options.columns || [];
var rows = options.rows || [];
var measures = options.measures || [];
var measuresRowAxis = options.measuresAxis === 'rows';
var sort = options.sort || [];
if (!columns.length && rows.length && (!measures.length || measures.length && measuresRowAxis)) {
columns = rows;
rows = [];
measuresRowAxis = false;
}
if (!columns.length && !rows.length) {
measuresRowAxis = false;
}
if (columns.length) {
command += serializeMembers(columns, !measuresRowAxis ? measures : [], sort);
} else if (measures.length && !measuresRowAxis) {
command += measureNames(measures).join(',');
}
command += '} DIMENSION PROPERTIES CHILDREN_CARDINALITY, PARENT_UNIQUE_NAME ON COLUMNS';
if (rows.length || measuresRowAxis && measures.length > 1) {
command += ', NON EMPTY {';
if (rows.length) {
command += serializeMembers(rows, measuresRowAxis ? measures : [], sort);
} else {
command += measureNames(measures).join(',');
}
command += '} DIMENSION PROPERTIES CHILDREN_CARDINALITY, PARENT_UNIQUE_NAME ON ROWS';
}
if (options.filter) {
command += ' FROM ';
command += '(';
command += serializeFilters(options.filter, options.connection.cube);
command += ')';
} else {
command += ' FROM [' + options.connection.cube + ']';
}
if (measures.length == 1 && columns.length) {
command += ' WHERE (' + measureNames(measures).join(',') + ')';
}
command += '' + options.connection.catalog + 'Multidimensional';
return command.replace(/\&/g, '&');
},
discover: function (options) {
options = options || {};
var command = '';
command += '' + (xmlaDiscoverCommands[options.command] || options.command) + '';
command += '' + serializeOptions('RestrictionList', options.restrictions, true) + '';
if (options.connection && options.connection.catalog) {
options.properties = $.extend({}, { Catalog: options.connection.catalog }, options.properties);
}
command += '' + serializeOptions('PropertyList', options.properties) + '';
command += '';
return command;
}
};
var XmlaTransport = kendo.data.RemoteTransport.extend({
init: function (options) {
var originalOptions = options;
options = this.options = extend(true, {}, this.options, options);
kendo.data.RemoteTransport.call(this, options);
if (isFunction(originalOptions.discover)) {
this.discover = originalOptions.discover;
} else if (typeof originalOptions.discover === 'string') {
this.options.discover = { url: originalOptions.discover };
} else if (!originalOptions.discover) {
this.options.discover = this.options.read;
}
},
setup: function (options, type) {
options.data = options.data || {};
$.extend(true, options.data, { connection: this.options.connection });
return kendo.data.RemoteTransport.fn.setup.call(this, options, type);
},
options: {
read: {
dataType: 'text',
contentType: 'text/xml',
type: 'POST'
},
discover: {
dataType: 'text',
contentType: 'text/xml',
type: 'POST'
},
parameterMap: function (options, type) {
return convertersMap[type](options, type);
}
},
discover: function (options) {
return $.ajax(this.setup(options, 'discover'));
}
});
function asArray(object) {
if (object == null) {
return [];
}
var type = toString.call(object);
if (type !== '[object Array]') {
return [object];
}
return object;
}
function translateAxis(axis) {
var result = { tuples: [] };
var tuples = asArray(kendo.getter('Tuples.Tuple', true)(axis));
var captionGetter = kendo.getter('Caption[\'#text\']');
var unameGetter = kendo.getter('UName[\'#text\']');
var levelNameGetter = kendo.getter('LName[\'#text\']');
var levelNumGetter = kendo.getter('LNum[\'#text\']');
var childrenGetter = kendo.getter('CHILDREN_CARDINALITY[\'#text\']', true);
var hierarchyGetter = kendo.getter('[\'@Hierarchy\']');
var parentNameGetter = kendo.getter('PARENT_UNIQUE_NAME[\'#text\']', true);
for (var idx = 0; idx < tuples.length; idx++) {
var members = [];
var member = asArray(tuples[idx].Member);
for (var memberIdx = 0; memberIdx < member.length; memberIdx++) {
members.push({
children: [],
caption: captionGetter(member[memberIdx]),
name: unameGetter(member[memberIdx]),
levelName: levelNameGetter(member[memberIdx]),
levelNum: levelNumGetter(member[memberIdx]),
hasChildren: parseInt(childrenGetter(member[memberIdx]), 10) > 0,
parentName: parentNameGetter(member[memberIdx]),
hierarchy: hierarchyGetter(member[memberIdx])
});
}
result.tuples.push({ members: members });
}
return result;
}
var schemaDataReaderMap = {
cubes: {
name: kendo.getter('CUBE_NAME[\'#text\']', true),
caption: kendo.getter('CUBE_CAPTION[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true),
type: kendo.getter('CUBE_TYPE[\'#text\']', true)
},
catalogs: {
name: kendo.getter('CATALOG_NAME[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true)
},
measures: {
name: kendo.getter('MEASURE_NAME[\'#text\']', true),
caption: kendo.getter('MEASURE_CAPTION[\'#text\']', true),
uniqueName: kendo.getter('MEASURE_UNIQUE_NAME[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true),
aggregator: kendo.getter('MEASURE_AGGREGATOR[\'#text\']', true),
groupName: kendo.getter('MEASUREGROUP_NAME[\'#text\']', true),
displayFolder: kendo.getter('MEASURE_DISPLAY_FOLDER[\'#text\']', true),
defaultFormat: kendo.getter('DEFAULT_FORMAT_STRING[\'#text\']', true)
},
kpis: {
name: kendo.getter('KPI_NAME[\'#text\']', true),
caption: kendo.getter('KPI_CAPTION[\'#text\']', true),
value: kendo.getter('KPI_VALUE[\'#text\']', true),
goal: kendo.getter('KPI_GOAL[\'#text\']', true),
status: kendo.getter('KPI_STATUS[\'#text\']', true),
trend: kendo.getter('KPI_TREND[\'#text\']', true),
statusGraphic: kendo.getter('KPI_STATUS_GRAPHIC[\'#text\']', true),
trendGraphic: kendo.getter('KPI_TREND_GRAPHIC[\'#text\']', true),
description: kendo.getter('KPI_DESCRIPTION[\'#text\']', true),
groupName: kendo.getter('MEASUREGROUP_NAME[\'#text\']', true)
},
dimensions: {
name: kendo.getter('DIMENSION_NAME[\'#text\']', true),
caption: kendo.getter('DIMENSION_CAPTION[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true),
uniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
defaultHierarchy: kendo.getter('DEFAULT_HIERARCHY[\'#text\']', true),
type: kendo.getter('DIMENSION_TYPE[\'#text\']', true)
},
hierarchies: {
name: kendo.getter('HIERARCHY_NAME[\'#text\']', true),
caption: kendo.getter('HIERARCHY_CAPTION[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true),
uniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true),
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
displayFolder: kendo.getter('HIERARCHY_DISPLAY_FOLDER[\'#text\']', true),
origin: kendo.getter('HIERARCHY_ORIGIN[\'#text\']', true),
defaultMember: kendo.getter('DEFAULT_MEMBER[\'#text\']', true)
},
levels: {
name: kendo.getter('LEVEL_NAME[\'#text\']', true),
caption: kendo.getter('LEVEL_CAPTION[\'#text\']', true),
description: kendo.getter('DESCRIPTION[\'#text\']', true),
uniqueName: kendo.getter('LEVEL_UNIQUE_NAME[\'#text\']', true),
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
displayFolder: kendo.getter('LEVEL_DISPLAY_FOLDER[\'#text\']', true),
orderingProperty: kendo.getter('LEVEL_ORDERING_PROPERTY[\'#text\']', true),
origin: kendo.getter('LEVEL_ORIGIN[\'#text\']', true),
hierarchyUniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true)
},
members: {
name: kendo.getter('MEMBER_NAME[\'#text\']', true),
caption: kendo.getter('MEMBER_CAPTION[\'#text\']', true),
uniqueName: kendo.getter('MEMBER_UNIQUE_NAME[\'#text\']', true),
dimensionUniqueName: kendo.getter('DIMENSION_UNIQUE_NAME[\'#text\']', true),
hierarchyUniqueName: kendo.getter('HIERARCHY_UNIQUE_NAME[\'#text\']', true),
levelUniqueName: kendo.getter('LEVEL_UNIQUE_NAME[\'#text\']', true),
childrenCardinality: kendo.getter('CHILDREN_CARDINALITY[\'#text\']', true)
}
};
var xmlaReaderMethods = [
'axes',
'catalogs',
'cubes',
'dimensions',
'hierarchies',
'levels',
'measures'
];
var XmlaDataReader = kendo.data.XmlDataReader.extend({
init: function (options) {
kendo.data.XmlDataReader.call(this, options);
this._extend(options);
},
_extend: function (options) {
var idx = 0;
var length = xmlaReaderMethods.length;
var methodName;
var option;
for (; idx < length; idx++) {
methodName = xmlaReaderMethods[idx];
option = options[methodName];
if (option && option !== identity) {
this[methodName] = option;
}
}
},
parse: function (xml) {
var result = kendo.data.XmlDataReader.fn.parse(xml.replace(/<(\/?)(\w|-)+:/g, '<$1'));
return kendo.getter('[\'Envelope\'][\'Body\']', true)(result);
},
errors: function (root) {
var fault = kendo.getter('[\'Fault\']', true)(root);
if (fault) {
return [{
faultstring: kendo.getter('faultstring[\'#text\']', true)(fault),
faultcode: kendo.getter('faultcode[\'#text\']', true)(fault)
}];
}
return null;
},
axes: function (root) {
root = kendo.getter('ExecuteResponse["return"].root', true)(root);
var axes = asArray(kendo.getter('Axes.Axis', true)(root));
var axis;
var result = {
columns: {},
rows: {}
};
for (var idx = 0; idx < axes.length; idx++) {
axis = axes[idx];
if (axis['@name'].toLowerCase() !== 'sliceraxis') {
if (!result.columns.tuples) {
result.columns = translateAxis(axis);
} else {
result.rows = translateAxis(axis);
}
}
}
return result;
},
data: function (root) {
root = kendo.getter('ExecuteResponse["return"].root', true)(root);
var cells = asArray(kendo.getter('CellData.Cell', true)(root));
var result = [];
var ordinalGetter = kendo.getter('[\'@CellOrdinal\']');
var valueGetter = kendo.getter('Value[\'#text\']');
var fmtValueGetter = kendo.getter('FmtValue[\'#text\']');
for (var idx = 0; idx < cells.length; idx++) {
result.push({
value: valueGetter(cells[idx]),
fmtValue: fmtValueGetter(cells[idx]),
ordinal: parseInt(ordinalGetter(cells[idx]), 10)
});
}
return result;
},
_mapSchema: function (root, getters) {
root = kendo.getter('DiscoverResponse["return"].root', true)(root);
var rows = asArray(kendo.getter('row', true)(root));
var result = [];
for (var idx = 0; idx < rows.length; idx++) {
var obj = {};
for (var key in getters) {
obj[key] = getters[key](rows[idx]);
}
result.push(obj);
}
return result;
},
measures: function (root) {
return this._mapSchema(root, schemaDataReaderMap.measures);
},
kpis: function (root) {
return this._mapSchema(root, schemaDataReaderMap.kpis);
},
hierarchies: function (root) {
return this._mapSchema(root, schemaDataReaderMap.hierarchies);
},
levels: function (root) {
return this._mapSchema(root, schemaDataReaderMap.levels);
},
dimensions: function (root) {
return this._mapSchema(root, schemaDataReaderMap.dimensions);
},
cubes: function (root) {
return this._mapSchema(root, schemaDataReaderMap.cubes);
},
catalogs: function (root) {
return this._mapSchema(root, schemaDataReaderMap.catalogs);
},
members: function (root) {
return this._mapSchema(root, schemaDataReaderMap.members);
}
});
extend(true, kendo.data, {
PivotDataSource: PivotDataSource,
XmlaTransport: XmlaTransport,
XmlaDataReader: XmlaDataReader,
PivotCubeBuilder: PivotCubeBuilder,
transports: { xmla: XmlaTransport },
readers: { xmla: XmlaDataReader }
});
var sortExpr = function (expressions, name) {
if (!expressions) {
return null;
}
for (var idx = 0, length = expressions.length; idx < length; idx++) {
if (expressions[idx].field === name) {
return expressions[idx];
}
}
return null;
};
var removeExpr = function (expressions, name) {
var result = [];
for (var idx = 0, length = expressions.length; idx < length; idx++) {
if (expressions[idx].field !== name) {
result.push(expressions[idx]);
}
}
return result;
};
kendo.ui.PivotSettingTarget = Widget.extend({
init: function (element, options) {
var that = this;
Widget.fn.init.call(that, element, options);
that.element.addClass('k-pivot-setting');
that.dataSource = kendo.data.PivotDataSource.create(options.dataSource);
that._refreshHandler = $.proxy(that.refresh, that);
that.dataSource.first(CHANGE, that._refreshHandler);
if (!options.template) {
that.options.template = '${data.name || data}' + (that.options.enabled ? '
' : '') + '
';
}
that.template = kendo.template(that.options.template);
that.emptyTemplate = kendo.template(that.options.emptyTemplate);
that._sortable();
that.element.on('click' + NS, '.k-button,.k-item', function (e) {
var target = $(e.target);
var name = target.closest('[' + kendo.attr('name') + ']').attr(kendo.attr('name'));
if (!name) {
return;
}
if (target.hasClass('k-setting-delete')) {
that.remove(name);
} else if (that.options.sortable && target[0] === e.currentTarget) {
that.sort({
field: name,
dir: target.find('.k-i-sort-asc')[0] ? 'desc' : 'asc'
});
}
});
if (options.filterable || options.sortable) {
that.fieldMenu = new ui.PivotFieldMenu(that.element, {
messages: that.options.messages.fieldMenu,
filter: '.k-setting-fieldmenu',
filterable: options.filterable,
sortable: options.sortable,
dataSource: that.dataSource
});
}
that.refresh();
},
options: {
name: 'PivotSettingTarget',
template: null,
filterable: false,
sortable: false,
emptyTemplate: '${data}
',
setting: 'columns',
enabled: true,
messages: { empty: 'Drop Fields Here' }
},
setDataSource: function (dataSource) {
this.dataSource.unbind(CHANGE, this._refreshHandler);
this.dataSource = this.options.dataSource = dataSource;
if (this.fieldMenu) {
this.fieldMenu.setDataSource(dataSource);
}
dataSource.first(CHANGE, this._refreshHandler);
this.refresh();
},
_sortable: function () {
var that = this;
if (that.options.enabled) {
this.sortable = this.element.kendoSortable({
connectWith: this.options.connectWith,
filter: '>:not(.k-empty)',
hint: that.options.hint,
cursor: 'move',
start: function (e) {
e.item.focus().blur();
},
change: function (e) {
var name = e.item.attr(kendo.attr('name'));
if (e.action == 'receive') {
that.add(name);
} else if (e.action == 'remove') {
that.remove(name);
} else if (e.action == 'sort') {
that.move(name, e.newIndex);
}
}
}).data('kendoSortable');
}
},
_indexOf: function (name, items) {
var idx, length, index = -1;
for (idx = 0, length = items.length; idx < length; idx++) {
if (getName(items[idx]) === name) {
index = idx;
break;
}
}
return index;
},
_isKPI: function (data) {
return data.type === 'kpi' || data.measure;
},
validate: function (data) {
var isMeasure = data.type == 2 || 'aggregator' in data || this._isKPI(data);
if (isMeasure) {
return this.options.setting === 'measures';
}
if (this.options.setting === 'measures') {
return isMeasure;
}
var items = this.dataSource[this.options.setting]();
var name = data.defaultHierarchy || data.uniqueName;
if (this._indexOf(name, items) > -1) {
return false;
}
items = this.dataSource[this.options.setting === 'columns' ? 'rows' : 'columns']();
if (this._indexOf(name, items) > -1) {
return false;
}
return true;
},
add: function (name) {
var items = this.dataSource[this.options.setting]();
var i, l;
name = $.isArray(name) ? name.slice(0) : [name];
for (i = 0, l = name.length; i < l; i++) {
if (this._indexOf(name[i], items) !== -1) {
name.splice(i, 1);
i -= 1;
l -= 1;
}
}
if (name.length) {
items = items.concat(name);
this.dataSource[this.options.setting](items);
}
},
move: function (name, index) {
var items = this.dataSource[this.options.setting]();
var idx = this._indexOf(name, items);
if (idx > -1) {
name = items.splice(idx, 1)[0];
items.splice(index, 0, name);
this.dataSource[this.options.setting](items);
}
},
remove: function (name) {
var items = this.dataSource[this.options.setting]();
var idx = this._indexOf(name, items);
if (idx > -1) {
items.splice(idx, 1);
this.dataSource[this.options.setting](items);
}
},
sort: function (expr) {
var sortable = this.options.sortable;
var allowUnsort = sortable === true || sortable.allowUnsort;
var skipExpr = allowUnsort && expr.dir === 'asc';
var expressions = this.dataSource.sort() || [];
var result = removeExpr(expressions, expr.field);
if (skipExpr && expressions.length !== result.length) {
expr = null;
}
if (expr) {
result.push(expr);
}
this.dataSource.sort(result);
},
refresh: function () {
var html = '';
var items = this.dataSource[this.options.setting]();
var length = items.length;
var idx = 0;
var item;
if (length) {
for (; idx < length; idx++) {
item = items[idx];
item = item.name === undefined ? { name: item } : item;
html += this.template(extend({ sortIcon: this._sortIcon(item.name) }, item));
}
} else {
html = this.emptyTemplate(this.options.messages.empty);
}
this.element.html(html);
},
destroy: function () {
Widget.fn.destroy.call(this);
this.dataSource.unbind(CHANGE, this._refreshHandler);
this.element.off(NS);
if (this.sortable) {
this.sortable.destroy();
}
if (this.fieldMenu) {
this.fieldMenu.destroy();
}
this.element = null;
this._refreshHandler = null;
},
_sortIcon: function (name) {
var expressions = this.dataSource.sort();
var expr = sortExpr(expressions, getName(name));
var icon = '';
if (expr) {
icon = 'k-i-sort-' + expr.dir;
}
return icon;
}
});
var PivotGrid = Widget.extend({
init: function (element, options) {
var that = this;
var columnBuilder;
var rowBuilder;
Widget.fn.init.call(that, element, options);
that._dataSource();
that._bindConfigurator();
that._wrapper();
that._createLayout();
that._columnBuilder = columnBuilder = new ColumnBuilder();
that._rowBuilder = rowBuilder = new RowBuilder();
that._contentBuilder = new ContentBuilder();
that._templates();
that.columnsHeader.add(that.rowsHeader).on('click', 'span.k-icon', function () {
var button = $(this);
var builder = columnBuilder;
var action = 'expandColumn';
var eventName;
var path = button.attr(kendo.attr('path'));
var eventArgs = {
axis: 'columns',
path: $.parseJSON(path)
};
if (button.parent().is('td')) {
builder = rowBuilder;
action = 'expandRow';
eventArgs.axis = 'rows';
}
var expanded = button.hasClass(STATE_EXPANDED);
var metadata = builder.metadata[path];
var request = metadata.expanded === undefined;
eventName = expanded ? COLLAPSEMEMBER : EXPANDMEMBER;
eventArgs.childrenLoaded = metadata.maxChildren > metadata.children;
if (that.trigger(eventName, eventArgs)) {
return;
}
builder.metadata[path].expanded = !expanded;
button.toggleClass(STATE_EXPANDED, !expanded).toggleClass(STATE_COLLAPSED, expanded);
if (!expanded && request) {
that.dataSource[action](eventArgs.path);
} else {
that.refresh();
}
});
that._scrollable();
if (that.options.autoBind) {
that.dataSource.fetch();
}
kendo.notify(that);
},
events: [
DATABINDING,
DATABOUND,
EXPANDMEMBER,
COLLAPSEMEMBER
],
options: {
name: 'PivotGrid',
autoBind: true,
reorderable: true,
filterable: false,
sortable: false,
height: null,
columnWidth: 100,
configurator: '',
columnHeaderTemplate: null,
rowHeaderTemplate: null,
dataCellTemplate: null,
kpiStatusTemplate: null,
kpiTrendTemplate: null,
messages: {
measureFields: 'Drop Data Fields Here',
columnFields: 'Drop Column Fields Here',
rowFields: 'Drop Rows Fields Here'
}
},
_templates: function () {
var columnTemplate = this.options.columnHeaderTemplate;
var rowTemplate = this.options.rowHeaderTemplate;
var dataTemplate = this.options.dataCellTemplate;
var kpiStatusTemplate = this.options.kpiStatusTemplate;
var kpiTrendTemplate = this.options.kpiTrendTemplate;
this._columnBuilder.template = kendo.template(columnTemplate || HEADER_TEMPLATE, { useWithBlock: !!columnTemplate });
this._contentBuilder.dataTemplate = kendo.template(dataTemplate || DATACELL_TEMPLATE, { useWithBlock: !!dataTemplate });
this._contentBuilder.kpiStatusTemplate = kendo.template(kpiStatusTemplate || KPISTATUS_TEMPLATE, { useWithBlock: !!kpiStatusTemplate });
this._contentBuilder.kpiTrendTemplate = kendo.template(kpiTrendTemplate || KPITREND_TEMPLATE, { useWithBlock: !!kpiTrendTemplate });
this._rowBuilder.template = kendo.template(rowTemplate || HEADER_TEMPLATE, { useWithBlock: !!rowTemplate });
},
_bindConfigurator: function () {
var configurator = this.options.configurator;
if (configurator) {
$(configurator).kendoPivotConfigurator('setDataSource', this.dataSource);
}
},
cellInfoByElement: function (element) {
element = $(element);
return this.cellInfo(element.index(), element.parent('tr').index());
},
cellInfo: function (columnIndex, rowIndex) {
var contentBuilder = this._contentBuilder;
var columnInfo = contentBuilder.columnIndexes[columnIndex || 0];
var rowInfo = contentBuilder.rowIndexes[rowIndex || 0];
var dataIndex;
if (!columnInfo || !rowInfo) {
return null;
}
dataIndex = rowInfo.index * contentBuilder.rowLength + columnInfo.index;
return {
columnTuple: columnInfo.tuple,
rowTuple: rowInfo.tuple,
measure: columnInfo.measure || rowInfo.measure,
dataItem: this.dataSource.view()[dataIndex]
};
},
setDataSource: function (dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
if (this.measuresTarget) {
this.measuresTarget.setDataSource(dataSource);
}
if (this.rowsTarget) {
this.rowsTarget.setDataSource(dataSource);
}
if (this.columnsTarget) {
this.columnsTarget.setDataSource(dataSource);
}
this._bindConfigurator();
if (this.options.autoBind) {
dataSource.fetch();
}
},
setOptions: function (options) {
Widget.fn.setOptions.call(this, options);
this._templates();
},
destroy: function () {
Widget.fn.destroy.call(this);
clearTimeout(this._headerReflowTimeout);
},
_dataSource: function () {
var that = this;
var dataSource = that.options.dataSource;
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
if (that.dataSource && this._refreshHandler) {
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(STATERESET, that._stateResetHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
} else {
that._refreshHandler = $.proxy(that.refresh, that);
that._progressHandler = $.proxy(that._requestStart, that);
that._stateResetHandler = $.proxy(that._stateReset, that);
that._errorHandler = $.proxy(that._error, that);
}
that.dataSource = kendo.data.PivotDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(STATERESET, that._stateResetHandler).bind(ERROR, that._errorHandler);
},
_error: function () {
this._progress(false);
},
_requestStart: function () {
this._progress(true);
},
_stateReset: function () {
this._columnBuilder.reset();
this._rowBuilder.reset();
},
_wrapper: function () {
var height = this.options.height;
this.wrapper = this.element.addClass('k-widget k-pivot');
if (height) {
this.wrapper.css('height', height);
}
},
_measureFields: function () {
this.measureFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-measures');
this.measuresTarget = this._createSettingTarget(this.measureFields, {
setting: 'measures',
messages: { empty: this.options.messages.measureFields }
});
},
_createSettingTarget: function (element, options) {
var template = '${data.name}';
var sortable = options.sortable;
var icons = '';
if (sortable) {
icons += '#if (data.sortIcon) {#';
icons += '';
icons += '#}#';
}
if (options.filterable || sortable) {
icons += '';
}
if (this.options.reorderable) {
icons += '';
}
if (icons) {
template += '' + icons + '';
}
template += '';
return new kendo.ui.PivotSettingTarget(element, $.extend({
template: template,
emptyTemplate: '${data}',
enabled: this.options.reorderable,
dataSource: this.dataSource
}, options));
},
_initSettingTargets: function () {
this.columnsTarget = this._createSettingTarget(this.columnFields, {
connectWith: this.rowFields,
setting: 'columns',
filterable: this.options.filterable,
sortable: this.options.sortable,
messages: {
empty: this.options.messages.columnFields,
fieldMenu: this.options.messages.fieldMenu
}
});
this.rowsTarget = this._createSettingTarget(this.rowFields, {
connectWith: this.columnFields,
setting: 'rows',
filterable: this.options.filterable,
sortable: this.options.sortable,
messages: {
empty: this.options.messages.rowFields,
fieldMenu: this.options.messages.fieldMenu
}
});
},
_createLayout: function () {
var that = this;
var layoutTable = $(LAYOUT_TABLE);
var leftContainer = layoutTable.find('.k-pivot-rowheaders');
var rightContainer = layoutTable.find('.k-pivot-table');
var gridWrapper = $(DIV).addClass('k-grid k-widget');
that._measureFields();
that.columnFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-columns');
that.rowFields = $(DIV).addClass('k-pivot-toolbar k-header k-settings-rows');
that.columnsHeader = $('').wrap('');
that.columnsHeader.parent().css('padding-right', kendo.support.scrollbar());
that.rowsHeader = $('');
that.content = $('');
leftContainer.append(that.measureFields);
leftContainer.append(that.rowFields);
leftContainer.append(that.rowsHeader);
gridWrapper.append(that.columnsHeader.parent());
gridWrapper.append(that.content);
rightContainer.append(that.columnFields);
rightContainer.append(gridWrapper);
that.wrapper.append(layoutTable);
that.columnsHeaderTree = new kendo.dom.Tree(that.columnsHeader[0]);
that.rowsHeaderTree = new kendo.dom.Tree(that.rowsHeader[0]);
that.contentTree = new kendo.dom.Tree(that.content[0]);
that._initSettingTargets();
},
_progress: function (toggle) {
kendo.ui.progress(this.wrapper, toggle);
},
_resize: function () {
if (this.content[0].firstChild) {
this._setSectionsWidth();
this._setSectionsHeight();
this._setContentWidth();
this._setContentHeight();
this._columnHeaderReflow();
}
},
_columnHeaderReflow: function () {
var columnTable = this.columnsHeader.children('table');
if (!kendo.support.browser.mozilla) {
return;
}
clearTimeout(this._headerReflowTimeout);
columnTable.css('table-layout', 'auto');
this._headerReflowTimeout = setTimeout(function () {
columnTable.css('table-layout', '');
});
},
_setSectionsWidth: function () {
var rowsHeader = this.rowsHeader;
var leftColumn = rowsHeader.parent('.k-pivot-rowheaders').width(AUTO);
var width;
width = Math.max(this.measureFields.outerWidth(), this.rowFields.outerWidth());
width = Math.max(rowsHeader.children('table').width(), width);
leftColumn.width(width);
},
_setSectionsHeight: function () {
var measureFieldsHeight = this.measureFields.height(AUTO).height();
var columnFieldsHeight = this.columnFields.height(AUTO).height();
var rowFieldsHeight = this.rowFields.height(AUTO).innerHeight();
var columnsHeight = this.columnsHeader.height(AUTO).innerHeight();
var padding = rowFieldsHeight - this.rowFields.height();
var firstRowHeight = columnFieldsHeight > measureFieldsHeight ? columnFieldsHeight : measureFieldsHeight;
var secondRowHeight = columnsHeight > rowFieldsHeight ? columnsHeight : rowFieldsHeight;
this.measureFields.height(firstRowHeight);
this.columnFields.height(firstRowHeight);
this.rowFields.height(secondRowHeight - padding);
this.columnsHeader.height(secondRowHeight);
},
_setContentWidth: function () {
var contentTable = this.content.find('table');
var columnTable = this.columnsHeader.children('table');
var rowLength = contentTable.children('colgroup').children().length;
var calculatedWidth = rowLength * this.options.columnWidth;
var minWidth = Math.ceil(calculatedWidth / this.content.width() * 100);
if (minWidth < 100) {
minWidth = 100;
}
contentTable.add(columnTable).css('width', minWidth + '%');
this._resetColspan(columnTable);
},
_setContentHeight: function () {
var that = this;
var content = that.content;
var rowsHeader = that.rowsHeader;
var innerHeight = that.wrapper.innerHeight();
var scrollbar = kendo.support.scrollbar();
var skipScrollbar = content[0].offsetHeight === content[0].clientHeight;
var height = that.options.height;
if (that.wrapper.is(':visible')) {
if (!innerHeight || !height) {
if (skipScrollbar) {
scrollbar = 0;
}
content.height('auto');
rowsHeader.height(content.height() - scrollbar);
return;
}
innerHeight -= that.columnFields.outerHeight();
innerHeight -= that.columnsHeader.outerHeight();
if (innerHeight <= scrollbar * 2) {
innerHeight = scrollbar * 2 + 1;
if (!skipScrollbar) {
innerHeight += scrollbar;
}
}
content.height(innerHeight);
if (skipScrollbar) {
scrollbar = 0;
}
rowsHeader.height(innerHeight - scrollbar);
}
},
_resetColspan: function (columnTable) {
var that = this;
var cell = columnTable.children('tbody').children(':first').children(':first');
if (that._colspan === undefined) {
that._colspan = cell.attr('colspan');
}
cell.attr('colspan', 1);
clearTimeout(that._layoutTimeout);
that._layoutTimeout = setTimeout(function () {
cell.attr('colspan', that._colspan);
that._colspan = undefined;
});
},
_axisMeasures: function (axis) {
var result = [];
var dataSource = this.dataSource;
var measures = dataSource.measures();
var hasMeasure = measures.length > 1 || measures[0] && measures[0].type;
if (dataSource.measuresAxis() === axis) {
if (dataSource[axis]().length === 0 || hasMeasure) {
result = measures;
}
}
return result;
},
items: function () {
return [];
},
refresh: function () {
var that = this;
var dataSource = that.dataSource;
var axes = dataSource.axes();
var columns = (axes.columns || {}).tuples || [];
var rows = (axes.rows || {}).tuples || [];
var columnBuilder = that._columnBuilder;
var rowBuilder = that._rowBuilder;
var columnAxis = {};
var rowAxis = {};
if (that.trigger(DATABINDING, { action: 'rebind' })) {
return;
}
columnBuilder.measures = that._axisMeasures('columns');
that.columnsHeaderTree.render(columnBuilder.build(columns));
that.rowsHeaderTree.render(rowBuilder.build(rows));
columnAxis = {
indexes: columnBuilder._indexes,
measures: columnBuilder.measures,
metadata: columnBuilder.metadata
};
rowAxis = {
indexes: rowBuilder._indexes,
measures: this._axisMeasures('rows'),
metadata: rowBuilder.metadata
};
that.contentTree.render(that._contentBuilder.build(dataSource.view(), columnAxis, rowAxis));
that._resize();
if (that.touchScroller) {
that.touchScroller.contentResized();
} else {
var touchScroller = kendo.touchScroller(that.content);
if (touchScroller && touchScroller.movable) {
that.touchScroller = touchScroller;
touchScroller.movable.bind('change', function (e) {
that.columnsHeader.scrollLeft(-e.sender.x);
that.rowsHeader.scrollTop(-e.sender.y);
});
}
}
that._progress(false);
that.trigger(DATABOUND);
},
_scrollable: function () {
var that = this;
var columnsHeader = that.columnsHeader;
var rowsHeader = that.rowsHeader;
that.content.scroll(function () {
columnsHeader.scrollLeft(this.scrollLeft);
rowsHeader.scrollTop(this.scrollTop);
});
rowsHeader.bind('DOMMouseScroll' + NS + ' mousewheel' + NS, $.proxy(that._wheelScroll, that));
},
_wheelScroll: function (e) {
if (e.ctrlKey) {
return;
}
var delta = kendo.wheelDeltaY(e);
var scrollTop = this.content.scrollTop();
if (delta) {
e.preventDefault();
$(e.currentTarget).one('wheel' + NS, false);
this.rowsHeader.scrollTop(scrollTop + -delta);
this.content.scrollTop(scrollTop + -delta);
}
}
});
var element = kendo.dom.element;
var htmlNode = kendo.dom.html;
var createMetadata = function (levelNum, memberIdx) {
return {
maxChildren: 0,
children: 0,
maxMembers: 0,
members: 0,
measures: 1,
levelNum: levelNum,
parentMember: memberIdx !== 0
};
};
var buildPath = function (tuple, index) {
var path = [];
var idx = 0;
for (; idx <= index; idx++) {
path.push(tuple.members[idx].name);
}
return path;
};
var tupleName = function (tuple, index) {
var name = '';
var idx = 0;
for (; idx <= index; idx++) {
name += tuple.members[idx].name;
}
return name;
};
var ColumnBuilder = Class.extend({
init: function () {
this.measures = 1;
this.metadata = {};
},
build: function (tuples) {
var tbody = this._tbody(tuples);
var colgroup = this._colGroup();
return [element('table', null, [
colgroup,
tbody
])];
},
reset: function () {
this.metadata = {};
},
_colGroup: function () {
var length = this._rowLength();
var children = [];
var idx = 0;
for (; idx < length; idx++) {
children.push(element('col', null));
}
return element('colgroup', null, children);
},
_tbody: function (tuples) {
var root = tuples[0];
this.map = {};
this.rows = [];
this.rootTuple = root;
this._indexes = [];
if (root) {
this._buildRows(root, 0);
this._normalize();
} else {
this.rows.push(element('tr', null, [element('th', null, [htmlNode(' ')])]));
}
return element('tbody', null, this.rows);
},
_normalize: function () {
var rows = this.rows;
var rowsLength = rows.length;
var rowIdx = 0;
var row;
var cellsLength;
var cellIdx;
var cells;
var cell;
for (; rowIdx < rowsLength; rowIdx++) {
row = rows[rowIdx];
if (row.rowSpan === 1) {
continue;
}
cells = row.children;
cellIdx = 0;
cellsLength = cells.length;
for (; cellIdx < cellsLength; cellIdx++) {
cell = cells[cellIdx];
if (cell.tupleAll) {
cell.attr.rowSpan = row.rowSpan;
}
}
}
},
_rowIndex: function (row) {
var rows = this.rows;
var length = rows.length;
var idx = 0;
for (; idx < length; idx++) {
if (rows[idx] === row) {
break;
}
}
return idx;
},
_rowLength: function () {
var cells = this.rows[0] ? this.rows[0].children : [];
var length = cells.length;
var rowLength = 0;
var idx = 0;
if (length) {
for (; idx < length; idx++) {
rowLength += cells[idx].attr.colSpan || 1;
}
}
if (!rowLength) {
rowLength = this.measures;
}
return rowLength;
},
_row: function (tuple, memberIdx, parentMember) {
var rootName = this.rootTuple.members[memberIdx].name;
var levelNum = tuple.members[memberIdx].levelNum;
var rowKey = rootName + levelNum;
var map = this.map;
var parentRow;
var children;
var row = map[rowKey];
if (!row) {
row = element('tr', null, []);
row.parentMember = parentMember;
row.collapsed = 0;
row.colSpan = 0;
row.rowSpan = 1;
map[rowKey] = row;
parentRow = map[rootName + (Number(levelNum) - 1)];
if (parentRow) {
children = parentRow.children;
if (children[1] && children[1].attr.className.indexOf('k-alt') === -1) {
row.notFirst = true;
} else {
row.notFirst = parentRow.notFirst;
}
}
this.rows.splice(this._rowIndex(parentRow) + 1, 0, row);
} else {
row.notFirst = false;
if (!row.parentMember || row.parentMember !== parentMember) {
row.parentMember = parentMember;
row.collapsed = 0;
row.colSpan = 0;
}
}
return row;
},
_measures: function (measures, tuple, className) {
var map = this.map;
var row = map.measureRow;
var measure;
if (!row) {
row = element('tr', null, []);
map.measureRow = row;
this.rows.push(row);
}
for (var idx = 0, length = measures.length; idx < length; idx++) {
measure = measures[idx];
row.children.push(this._cell(className || '', [this._content(measure, tuple)], measure));
}
return length;
},
_content: function (member, tuple) {
return htmlNode(this.template({
member: member,
tuple: tuple
}));
},
_cell: function (className, children, member) {
var cell = element('th', { className: 'k-header' + className }, children);
cell.value = member.caption || member.name;
return cell;
},
_buildRows: function (tuple, memberIdx, parentMember) {
var members = tuple.members;
var member = members[memberIdx];
var nextMember = members[memberIdx + 1];
var row, childRow, children, childrenLength;
var cell, allCell, cellAttr;
var cellChildren = [];
var path;
var idx = 0;
var metadata;
var colSpan;
var collapsed = 0;
var memberCollapsed = 0;
if (member.measure) {
this._measures(member.children, tuple);
return;
}
path = kendo.stringify(buildPath(tuple, memberIdx));
row = this._row(tuple, memberIdx, parentMember);
children = member.children;
childrenLength = children.length;
metadata = this.metadata[path];
if (!metadata) {
this.metadata[path] = metadata = createMetadata(Number(member.levelNum), memberIdx);
metadata.rootLevelNum = Number(this.rootTuple.members[memberIdx].levelNum);
}
this._indexes.push({
path: path,
tuple: tuple
});
if (member.hasChildren) {
if (metadata.expanded === false) {
collapsed = metadata.maxChildren;
row.collapsed += collapsed;
metadata.children = 0;
childrenLength = 0;
}
cellAttr = { className: 'k-icon ' + (childrenLength ? STATE_EXPANDED : STATE_COLLAPSED) };
cellAttr[kendo.attr('path')] = path;
cellChildren.push(element('span', cellAttr));
}
cellChildren.push(this._content(member, tuple));
cell = this._cell(row.notFirst ? ' k-first' : '', cellChildren, member);
row.children.push(cell);
row.colSpan += 1;
if (childrenLength) {
allCell = this._cell(' k-alt', [this._content(member, tuple)], member);
row.children.push(allCell);
for (; idx < childrenLength; idx++) {
childRow = this._buildRows(children[idx], memberIdx, member);
}
colSpan = childRow.colSpan;
collapsed = childRow.collapsed;
cell.attr.colSpan = colSpan;
metadata.children = colSpan;
metadata.members = 1;
row.colSpan += colSpan;
row.collapsed += collapsed;
row.rowSpan = childRow.rowSpan + 1;
if (nextMember) {
if (nextMember.measure) {
colSpan = this._measures(nextMember.children, tuple, ' k-alt');
} else {
childRow = this._buildRows(tuple, memberIdx + 1);
colSpan = childRow.colSpan;
row.collapsed += childRow.collapsed;
memberCollapsed = childRow.collapsed;
}
allCell.attr.colSpan = colSpan;
colSpan -= 1;
metadata.members += colSpan;
row.colSpan += colSpan;
}
} else if (nextMember) {
if (nextMember.measure) {
colSpan = this._measures(nextMember.children, tuple);
} else {
childRow = this._buildRows(tuple, memberIdx + 1);
colSpan = childRow.colSpan;
row.collapsed += childRow.collapsed;
memberCollapsed = childRow.collapsed;
}
metadata.members = colSpan;
if (colSpan > 1) {
cell.attr.colSpan = colSpan;
row.colSpan += colSpan - 1;
}
}
if (metadata.maxMembers < metadata.members + memberCollapsed) {
metadata.maxMembers = metadata.members + memberCollapsed;
}
children = metadata.children + collapsed;
if (metadata.maxChildren < children) {
metadata.maxChildren = children;
}
(allCell || cell).tupleAll = true;
return row;
}
});
var RowBuilder = Class.extend({
init: function () {
this.metadata = {};
},
build: function (tuples) {
var tbody = this._tbody(tuples);
var colgroup = this._colGroup();
return [element('table', null, [
colgroup,
tbody
])];
},
reset: function () {
this.metadata = {};
},
_rowLength: function () {
var children = this.rows[0].children;
var length = 0;
var idx = 0;
var cell = children[idx];
while (cell) {
length += cell.attr.colSpan || 1;
cell = children[++idx];
}
return length;
},
_colGroup: function () {
var length = this._rowLength();
var children = [];
var idx = 0;
for (; idx < length; idx++) {
children.push(element('col', null));
}
return element('colgroup', null, children);
},
_tbody: function (tuples) {
var root = tuples[0];
this.rootTuple = root;
this.rows = [];
this.map = {};
this._indexes = [];
if (root) {
this._buildRows(root, 0);
this._normalize();
} else {
this.rows.push(element('tr', null, [element('td', null, [htmlNode(' ')])]));
}
return element('tbody', null, this.rows);
},
_normalize: function () {
var rows = this.rows;
var rowsLength = rows.length;
var rowIdx = 0;
var members = this.rootTuple.members;
var firstMemberName = members[0].name;
var membersLength = members.length;
var memberIdx = 0;
var row;
var cell;
var maxcolSpan;
var map = this.map;
var allRow;
for (; rowIdx < rowsLength; rowIdx++) {
row = rows[rowIdx];
for (memberIdx = 0; memberIdx < membersLength; memberIdx++) {
maxcolSpan = this[members[memberIdx].name];
cell = row.colSpan['dim' + memberIdx];
if (cell && cell.colSpan < maxcolSpan) {
cell.attr.colSpan = maxcolSpan - cell.colSpan + 1;
}
}
}
row = map[firstMemberName];
allRow = map[firstMemberName + 'all'];
if (row) {
row.children[0].attr.className = 'k-first';
}
if (allRow) {
allRow.children[0].attr.className += ' k-first';
}
},
_row: function (children) {
var row = element('tr', null, children);
row.rowSpan = 1;
row.colSpan = {};
this.rows.push(row);
return row;
},
_content: function (member, tuple) {
return htmlNode(this.template({
member: member,
tuple: tuple
}));
},
_cell: function (className, children, member) {
var cell = element('td', { className: className }, children);
cell.value = member.caption || member.name;
return cell;
},
_buildRows: function (tuple, memberIdx) {
var map = this.map;
var path;
var members = tuple.members;
var member = members[memberIdx];
var nextMember = members[memberIdx + 1];
var children = member.children;
var childrenLength = children.length;
var levelNum = Number(member.levelNum);
var rootName = this.rootTuple.members[memberIdx].name;
var tuplePath = buildPath(tuple, memberIdx - 1).join('');
var rootLevelNum = Number(this.rootTuple.members[memberIdx].levelNum);
var parentName = tuplePath + (rootLevelNum === levelNum ? '' : member.parentName || '');
var row = map[parentName + 'all'] || map[parentName];
var colSpan = levelNum + 1;
var cell, allCell;
var childRow, allRow;
var metadata;
var className;
var cellChildren = [];
var expandIconAttr;
var idx;
if (!row || row.hasChild) {
row = this._row();
} else {
row.hasChild = true;
}
if (member.measure) {
className = row.allCell ? 'k-grid-footer' : '';
row.children.push(this._cell(className, [this._content(children[0], tuple)], children[0]));
row.rowSpan = childrenLength;
for (idx = 1; idx < childrenLength; idx++) {
this._row([this._cell(className, [this._content(children[idx], tuple)], children[idx])]);
}
return row;
}
map[tuplePath + member.name] = row;
path = kendo.stringify(buildPath(tuple, memberIdx));
metadata = this.metadata[path];
if (!metadata) {
this.metadata[path] = metadata = createMetadata(levelNum, memberIdx);
metadata.rootLevelNum = rootLevelNum;
}
this._indexes.push({
path: path,
tuple: tuple
});
if (member.hasChildren) {
if (metadata.expanded === false) {
childrenLength = 0;
metadata.children = 0;
}
expandIconAttr = { className: 'k-icon ' + (childrenLength ? STATE_EXPANDED : STATE_COLLAPSED) };
expandIconAttr[kendo.attr('path')] = path;
cellChildren.push(element('span', expandIconAttr));
}
cellChildren.push(this._content(member, tuple));
className = row.allCell && !childrenLength ? 'k-grid-footer' : '';
cell = this._cell(className, cellChildren, member);
cell.colSpan = colSpan;
row.children.push(cell);
row.colSpan['dim' + memberIdx] = cell;
if (!this[rootName] || this[rootName] < colSpan) {
this[rootName] = colSpan;
}
if (childrenLength) {
row.allCell = false;
row.hasChild = false;
for (idx = 0; idx < childrenLength; idx++) {
childRow = this._buildRows(children[idx], memberIdx);
if (row !== childRow) {
row.rowSpan += childRow.rowSpan;
}
}
if (row.rowSpan > 1) {
cell.attr.rowSpan = row.rowSpan;
}
metadata.children = row.rowSpan;
allCell = this._cell('k-grid-footer', [this._content(member, tuple)], member);
allCell.colSpan = colSpan;
allRow = this._row([allCell]);
allRow.colSpan['dim' + memberIdx] = allCell;
allRow.allCell = true;
map[tuplePath + member.name + 'all'] = allRow;
if (nextMember) {
childRow = this._buildRows(tuple, memberIdx + 1);
allCell.attr.rowSpan = childRow.rowSpan;
}
row.rowSpan += allRow.rowSpan;
metadata.members = allRow.rowSpan;
} else if (nextMember) {
row.hasChild = false;
this._buildRows(tuple, memberIdx + 1);
(allCell || cell).attr.rowSpan = row.rowSpan;
metadata.members = row.rowSpan;
}
if (metadata.maxChildren < metadata.children) {
metadata.maxChildren = metadata.children;
}
if (metadata.maxMembers < metadata.members) {
metadata.maxMembers = metadata.members;
}
return row;
}
});
var ContentBuilder = Class.extend({
init: function () {
this.columnAxis = {};
this.rowAxis = {};
},
build: function (data, columnAxis, rowAxis) {
var index = columnAxis.indexes[0];
var metadata = columnAxis.metadata[index ? index.path : undefined];
this.columnAxis = columnAxis;
this.rowAxis = rowAxis;
this.data = data;
this.rowLength = metadata ? metadata.maxChildren + metadata.maxMembers : columnAxis.measures.length || 1;
if (!this.rowLength) {
this.rowLength = 1;
}
var tbody = this._tbody();
var colgroup = this._colGroup();
return [element('table', null, [
colgroup,
tbody
])];
},
_colGroup: function () {
var length = this.columnAxis.measures.length || 1;
var children = [];
var idx = 0;
if (this.rows[0]) {
length = this.rows[0].children.length;
}
for (; idx < length; idx++) {
children.push(element('col', null));
}
return element('colgroup', null, children);
},
_tbody: function () {
this.rows = [];
if (this.data[0]) {
this.columnIndexes = this._indexes(this.columnAxis, this.rowLength);
this.rowIndexes = this._indexes(this.rowAxis, Math.ceil(this.data.length / this.rowLength));
this._buildRows();
} else {
this.rows.push(element('tr', null, [element('td', null, [htmlNode(' ')])]));
}
return element('tbody', null, this.rows);
},
_indexes: function (axisInfo, total) {
var result = [];
var axisInfoMember;
var indexes = axisInfo.indexes;
var metadata = axisInfo.metadata;
var measures = axisInfo.measures;
var measuresLength = measures.length || 1;
var current;
var dataIdx = 0;
var firstEmpty = 0;
var idx = 0;
var length = indexes.length;
var measureIdx;
var index;
var children;
var skipChildren;
if (!length) {
for (measureIdx = 0; measureIdx < measuresLength; measureIdx++) {
result[measureIdx] = {
index: measureIdx,
measure: measures[measureIdx],
tuple: null
};
}
return result;
}
for (; idx < length; idx++) {
axisInfoMember = indexes[idx];
current = metadata[axisInfoMember.path];
children = current.children + current.members;
skipChildren = 0;
if (children) {
children -= measuresLength;
}
if (current.expanded === false && current.children !== current.maxChildren) {
skipChildren = current.maxChildren;
}
if (current.parentMember && current.levelNum === current.rootLevelNum) {
children = -1;
}
if (children > -1) {
for (measureIdx = 0; measureIdx < measuresLength; measureIdx++) {
index = children + measureIdx;
if (!current.children) {
index += firstEmpty;
}
result[children + firstEmpty + measureIdx] = {
children: children,
index: dataIdx,
measure: measures[measureIdx],
tuple: axisInfoMember.tuple
};
dataIdx += 1;
}
while (result[firstEmpty] !== undefined) {
firstEmpty += 1;
}
}
if (firstEmpty === total) {
break;
}
dataIdx += skipChildren;
}
return result;
},
_buildRows: function () {
var rowIndexes = this.rowIndexes;
var length = rowIndexes.length;
var idx = 0;
for (; idx < length; idx++) {
this.rows.push(this._buildRow(rowIndexes[idx]));
}
},
_buildRow: function (rowInfo) {
var startIdx = rowInfo.index * this.rowLength;
var columnIndexes = this.columnIndexes;
var length = columnIndexes.length;
var columnInfo;
var cells = [];
var idx = 0;
var templateInfo;
var cell, cellContent;
var attr, dataItem, measure;
for (; idx < length; idx++) {
columnInfo = columnIndexes[idx];
attr = {};
if (columnInfo.children) {
attr.className = 'k-alt';
}
cellContent = '';
dataItem = this.data[startIdx + columnInfo.index];
measure = columnInfo.measure || rowInfo.measure;
templateInfo = {
columnTuple: columnInfo.tuple,
rowTuple: rowInfo.tuple,
measure: measure,
dataItem: dataItem
};
if (dataItem.value !== '' && measure && measure.type) {
if (measure.type === 'status') {
cellContent = this.kpiStatusTemplate(templateInfo);
} else if (measure.type === 'trend') {
cellContent = this.kpiTrendTemplate(templateInfo);
}
}
if (!cellContent) {
cellContent = this.dataTemplate(templateInfo);
}
cell = element('td', attr, [htmlNode(cellContent)]);
cell.value = dataItem.value;
cells.push(cell);
}
attr = {};
if (rowInfo.children) {
attr.className = 'k-grid-footer';
}
return element('tr', attr, cells);
}
});
ui.plugin(PivotGrid);
kendo.PivotExcelExporter = kendo.Class.extend({
init: function (options) {
this.options = options;
this.widget = options.widget;
this.dataSource = this.widget.dataSource;
},
_columns: function () {
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
var columnHeaderLength = columnHeaderTable.children[0].children.length;
var rowHeaderLength = rowHeaderTable.children[0].children.length;
var width = this.widget.options.columnWidth;
var result = [];
var idx;
if (rowHeaderLength && this.dataSource.data()[0]) {
for (idx = 0; idx < rowHeaderLength; idx++) {
result.push({ autoWidth: true });
}
}
for (idx = 0; idx < columnHeaderLength; idx++) {
result.push({
autoWidth: false,
width: width
});
}
return result;
},
_cells: function (rows, type, callback) {
var result = [];
var i = 0;
var length = rows.length;
var cellsLength;
var row, cells;
var j, cell;
for (; i < length; i++) {
row = [];
cells = rows[i].children;
cellsLength = cells.length;
for (j = 0; j < cellsLength; j++) {
cell = cells[j];
row.push({
background: '#7a7a7a',
color: '#fff',
value: cell.value,
colSpan: cell.attr.colSpan || 1,
rowSpan: cell.attr.rowSpan || 1
});
}
if (callback) {
callback(row, i);
}
result.push({
cells: row,
type: type
});
}
return result;
},
_rows: function () {
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
var columnHeaderLength = columnHeaderTable.children[0].children.length;
var rowHeaderLength = rowHeaderTable.children[0].children.length;
var columnHeaderRows = columnHeaderTable.children[1].children;
var rowHeaderRows = rowHeaderTable.children[1].children;
var contentRows = this.widget.contentTree.children[0].children[1].children;
var columnRows = this._cells(columnHeaderRows, 'header');
if (rowHeaderLength) {
columnRows[0].cells.splice(0, 0, {
background: '#7a7a7a',
color: '#fff',
value: '',
colSpan: rowHeaderLength,
rowSpan: columnHeaderRows.length
});
}
var dataCallback = function (row, index) {
var j = 0;
var cell, value;
var cells = contentRows[index].children;
for (; j < columnHeaderLength; j++) {
cell = cells[j];
value = Number(cell.value);
if (isNaN(value)) {
value = cell.value;
}
row.push({
background: '#dfdfdf',
color: '#333',
value: value,
colSpan: 1,
rowSpan: 1
});
}
};
var rowRows = this._cells(rowHeaderRows, 'data', dataCallback);
return columnRows.concat(rowRows);
},
_freezePane: function () {
var columnHeaderTable = this.widget.columnsHeaderTree.children[0];
var rowHeaderTable = this.widget.rowsHeaderTree.children[0];
var rowHeaderLength = rowHeaderTable.children[0].children.length;
var columnHeaderRows = columnHeaderTable.children[1].children;
return {
colSplit: rowHeaderLength,
rowSplit: columnHeaderRows.length
};
},
workbook: function () {
var promise;
if (this.dataSource.view()[0]) {
promise = $.Deferred();
promise.resolve();
} else {
promise = this.dataSource.fetch();
}
return promise.then($.proxy(function () {
return {
sheets: [{
columns: this._columns(),
rows: this._rows(),
freezePane: this._freezePane(),
filter: null
}]
};
}, this));
}
});
var PivotExcelMixin = {
extend: function (proto) {
proto.events.push('excelExport');
proto.options.excel = $.extend(proto.options.excel, this.options);
proto.saveAsExcel = this.saveAsExcel;
},
options: {
proxyURL: '',
filterable: false,
fileName: 'Export.xlsx'
},
saveAsExcel: function () {
var excel = this.options.excel || {};
var exporter = new kendo.PivotExcelExporter({ widget: this });
exporter.workbook().then($.proxy(function (book) {
if (!this.trigger('excelExport', { workbook: book })) {
var workbook = new kendo.ooxml.Workbook(book);
kendo.saveAs({
dataURI: workbook.toDataURL(),
fileName: book.fileName || excel.fileName,
proxyURL: excel.proxyURL,
forceProxy: excel.forceProxy
});
}
}, this));
}
};
kendo.PivotExcelMixin = PivotExcelMixin;
if (kendo.ooxml && kendo.ooxml.Workbook) {
PivotExcelMixin.extend(PivotGrid.prototype);
}
if (kendo.PDFMixin) {
kendo.PDFMixin.extend(PivotGrid.prototype);
PivotGrid.fn._drawPDF = function () {
return this._drawPDFShadow({ width: this.wrapper.width() }, { avoidLinks: this.options.pdf.avoidLinks });
};
}
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));