/** * 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 + ''; } result += ''; } 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)(); }));