/** * This class remodels the grid store when required. * * @private */Ext.define('Ext.pivot.feature.PivotStore', { config: { store: null, grid: null, matrix: null, clsGrandTotal: '', clsGroupTotal: '', summaryDataCls: '', rowCls: '' }, constructor: function(config) { this.initConfig(config); return this.callParent(arguments); }, destroy: function(){ var me = this; Ext.destroy(me.storeListeners, me.matrixListeners); me.setConfig({ store: null, matrix: null, grid: null }); me.storeInfo = me.storeListeners = null; me.callParent(arguments); }, updateStore: function(store) { var me = this; Ext.destroy(me.storeListeners); if (store) { me.storeListeners = store.on({ // this event is fired by the pivot grid for private use pivotstoreremodel: me.processStore, scope: me, destroyable: true }); } }, updateMatrix: function(matrix){ var me = this; Ext.destroy(me.matrixListeners); if (matrix) { me.matrixListeners = matrix.on({ // this event is fired by the pivot grid for private use groupexpand: me.onGroupExpand, groupcollapse: me.onGroupCollapse, scope: me, destroyable: true }); } }, processStore: function(){ var me = this, store = me.getStore(), matrix = me.getMatrix(), records = [], isClassic = Ext.toolkit == 'classic', items, length, i, group, fields; if(!matrix || !store){ return; } fields = matrix.getColumns(); store.model.replaceFields(fields, true); store.removeAll(true); me.storeInfo = {}; if(matrix.rowGrandTotalsPosition == 'first'){ records.push.apply(records, me.processGrandTotal() || []); } items = matrix.leftAxis.getTree(); length = items.length; for(i = 0; i < length; i++){ group = items[i]; records.push.apply(records, me.processGroup({ group: group, previousExpanded: (i > 0 ? items[i-1].expanded : false) }) || []); } if(matrix.rowGrandTotalsPosition == 'last'){ records.push.apply(records, me.processGrandTotal() || []); } store.loadData(records); if(!isClassic) { store.fireEvent('load', store); } }, processGroup: function(config){ var me = this, fn = me['processGroup' + Ext.String.capitalize(me.getMatrix().viewLayoutType)]; if(!Ext.isFunction(fn)){ // specified view type doesn't exist so let's use the outline view fn = me.processGroupOutline; } return fn.call(me, config); }, processGrandTotal: function(){ var me = this, found = false, matrix = me.getMatrix(), group = { key: matrix.grandTotalKey }, records = [], lenT = matrix.totals.length, dimensions = matrix.leftAxis.dimensions.items, lenD = dimensions.length, i, j, k, total, column, record, key; for(i = 0; i < lenT; i++){ total = matrix.totals[i]; record = total.record; k = lenD; if(record instanceof Ext.data.Model) { me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getClsGrandTotal(), me.getSummaryDataCls()], rendererParams: {} }; for(j = 0; j < lenD; j++){ column = dimensions[j]; if (matrix.viewLayoutType == 'compact' || j === 0) { if (matrix.viewLayoutType == 'compact') { key = matrix.compactViewKey; k = 1; } else { key = column.getId(); } record.data[key] = total.title; me.storeInfo[record.internalId].rendererParams[key] = { fn: 'groupOutlineRenderer', group: group, colspan: k, hidden: false, subtotalRow: true }; found = true; } else { me.storeInfo[record.internalId].rendererParams[column.id] = { fn: 'groupOutlineRenderer', group: group, colspan: 0, hidden: found, subtotalRow: true }; k--; } } // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: 'topAxisRenderer' }; records.push(record); } } return records; }, // Outline view functions processGroupOutline: function(config){ var me = this, group = config['group'], results = []; if(group.record){ me.processRecordOutline({ results: results, group: group }); }else{ me.processGroupOutlineWithChildren({ results: results, group: group, previousExpanded: config.previousExpanded }); } return results; }, processGroupOutlineWithChildren: function(config){ var me = this, matrix = me.getMatrix(), group = config['group'], previousExpanded = config['previousExpanded'], hasSummaryData, record, i, len; hasSummaryData = (!group.expanded || (group.expanded && matrix.rowSubTotalsPosition == 'first')); record = group.expanded ? group.records.expanded : group.records.collapsed; me.processGroupHeaderRecordOutline({ results: config.results, group: group, record: record, previousExpanded: previousExpanded, hasSummaryData: hasSummaryData }); if(group.expanded){ if(group.children){ len = group.children.length; for(i = 0; i < len; i++){ if(group.children[i]['children']){ me.processGroupOutlineWithChildren({ results: config.results, group: group.children[i] }); }else{ me.processRecordOutline({ results: config.results, group: group.children[i] }); } } } if(matrix.rowSubTotalsPosition == 'last'){ record = group.records.footer; me.processGroupHeaderRecordOutline({ results: config.results, group: group, record: record, previousExpanded: previousExpanded, subtotalRow: true, hasSummaryData: true }); } } }, processGroupHeaderRecordOutline: function(config){ var me = this, matrix = me.getMatrix(), group = config['group'], record = config['record'], previousExpanded = config['previousExpanded'], subtotalRow = config['subtotalRow'], hasSummaryData = config['hasSummaryData'], items = matrix.leftAxis.dimensions.items, len = items.length, k = len, found = false, i, column; me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getClsGroupTotal(), hasSummaryData ? me.getSummaryDataCls() : ''], rendererParams: {} }; for(i = 0; i < len; i++){ column = items[i]; if(column.id == group.dimension.id){ me.storeInfo[record.internalId].rendererParams[column.id] = { fn: 'groupOutlineRenderer', group: group, colspan: k, hidden: false, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; found = true; }else{ me.storeInfo[record.internalId].rendererParams[column.id] = { fn: 'groupOutlineRenderer', group: group, colspan: 0, hidden: found, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; k--; } } // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: (hasSummaryData ? 'topAxisRenderer' : 'topAxisNoRenderer'), group: group }; config.results.push(record); }, processRecordOutline: function(config){ var me = this, group = config['group'], found = false, record = group.record, items = me.getMatrix().leftAxis.dimensions.items, len = items.length, i, column; me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getSummaryDataCls()], rendererParams: {} }; for(i = 0; i < len; i++){ column = items[i]; if(column.id == group.dimension.id){ found = true; } me.storeInfo[record.internalId].rendererParams[column.id] = { fn: 'recordOutlineRenderer', group: group, hidden: !found }; } // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: 'topAxisRenderer', group: group }; config.results.push(record); }, // Compact view functions processGroupCompact: function(config){ var me = this, group = config['group'], previousExpanded = config['previousExpanded'], results = []; if(group.record){ me.processRecordCompact({ results: results, group: group }); }else{ me.processGroupCompactWithChildren({ results: results, group: group, previousExpanded: previousExpanded }); } return results; }, processGroupCompactWithChildren: function(config){ var me = this, matrix = me.getMatrix(), group = config['group'], previousExpanded = config['previousExpanded'], hasSummaryData, i, len; hasSummaryData = (!group.expanded || (group.expanded && matrix.rowSubTotalsPosition == 'first')); me.processGroupHeaderRecordCompact({ results: config.results, group: group, record: group.expanded ? group.records.expanded : group.records.collapsed, previousExpanded: previousExpanded, hasSummaryData: hasSummaryData }); if(group.expanded){ if(group.children){ len = group.children.length; for(i = 0; i < len; i++){ if(group.children[i]['children']){ me.processGroupCompactWithChildren({ results: config.results, group: group.children[i] }); }else{ me.processRecordCompact({ results: config.results, group: group.children[i] }); } } } if(matrix.rowSubTotalsPosition == 'last'){ me.processGroupHeaderRecordCompact({ results: config.results, group: group, record: group.records.footer, previousExpanded: previousExpanded, subtotalRow: true, hasSummaryData: true }); } } }, processGroupHeaderRecordCompact: function(config){ var me = this, matrix = me.getMatrix(), group = config['group'], record = config['record'], previousExpanded = config['previousExpanded'], subtotalRow = config['subtotalRow'], hasSummaryData = config['hasSummaryData']; me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getClsGroupTotal(), hasSummaryData ? me.getSummaryDataCls() : ''], rendererParams: {} }; me.storeInfo[record.internalId].rendererParams[matrix.compactViewKey] = { fn: 'groupCompactRenderer', group: group, colspan: 0, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: (hasSummaryData ? 'topAxisRenderer' : 'topAxisNoRenderer'), group: group }; config.results.push(record); }, processRecordCompact: function(config){ var me = this, group = config['group'], record = group.record; me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getSummaryDataCls()], rendererParams: {} }; me.storeInfo[record.internalId].rendererParams[me.getMatrix().compactViewKey] = { fn: 'recordCompactRenderer', group: group }; // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: 'topAxisRenderer', group: group }; config.results.push(record); }, // Tabular view functions processGroupTabular: function(config){ var me = this, group = config['group'], results = []; if(group.record){ me.processRecordTabular({ results: results, group: group }); }else{ me.processGroupTabularWithChildren({ results: results, group: group, previousExpanded: config.previousExpanded }); } return results; }, processGroupTabularWithChildren: function(config, noFirstRecord){ var me = this, matrix = me.getMatrix(), group = config['group'], previousExpanded = config['previousExpanded'], record, i, child, len; if(!noFirstRecord) { me.processGroupHeaderRecordTabular({ results: config.results, group: group, previousExpanded: previousExpanded, hasSummaryData: !group.expanded }); } if(group.expanded){ if(group.children){ len = group.children.length; for(i = 0; i < len; i++){ child = group.children[i]; if(i === 0 && child.children && child.expanded){ me.processGroupTabularWithChildren({ results: config.results, group: child }, true); }else if(i > 0){ if(child.children){ me.processGroupTabularWithChildren({ results: config.results, group: child }); }else{ me.processRecordTabular({ results: config.results, group: child }); } } } } if(matrix.rowSubTotalsPosition !== 'none'){ record = group.records.footer; me.processGroupHeaderRecordTabular({ results: config.results, group: group, record: record, previousExpanded: previousExpanded, subtotalRow: true, hasSummaryData: true }); } } }, // used in PivotEvents getTabularGroupRecord: function(group){ var record = group.record; if(!record) { if (!group.expanded) { record = group.records.collapsed; } else { record = this.getTabularGroupRecord(group.children[0]); } } return record; }, processGroupHeaderRecordTabular: function(config){ var me = this, matrix = me.getMatrix(), group = config['group'], record = config['record'], previousExpanded = config['previousExpanded'], subtotalRow = config['subtotalRow'], hasSummaryData = config['hasSummaryData'], dimensions = matrix.leftAxis.dimensions, len = dimensions.length, k = len, found = false, rendererParams = {}, rowClasses = [], i, dim, item, keys, parentGroup, prevGroup; if(!record) { item = group; record = item.record; while(!record){ rendererParams[item.dimension.id] = { fn: 'groupTabularRenderer', group: item, colspan: 0, hidden: false, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; if(item.children){ if(!item.expanded){ record = item.records.collapsed; rendererParams[item.dimension.id].colspan = len - item.level; }else{ item = item.children[0]; } }else{ record = item.record; } } //Ext.apply(record.data, item.data); found = false; for(i = 0; i < len; i++) { dim = matrix.leftAxis.dimensions.items[i]; if(rendererParams[dim.id]){ record.data[dim.id] = rendererParams[dim.id].group.name; found = true; }else if(found){ rendererParams[dim.id] = { fn: 'groupTabularRenderer', group: group, colspan: 0, hidden: true, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; } } if(group.level > 0){ prevGroup = group; keys = prevGroup.key.split(matrix.keysSeparator); keys.length--; parentGroup = matrix.leftAxis.items.getByKey(keys.join(matrix.keysSeparator)); while(parentGroup && parentGroup.children[0] == prevGroup){ rendererParams[parentGroup.dimension.id] = { fn: 'groupTabularRenderer', group: parentGroup, colspan: 0, hidden: false, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; record.data[parentGroup.dimension.id] = parentGroup.name; prevGroup = parentGroup; keys = prevGroup.key.split(matrix.keysSeparator); keys.length--; parentGroup = matrix.leftAxis.items.getByKey(keys.join(matrix.keysSeparator)); } } }else{ for(i = 0; i < len; i++){ dim = matrix.leftAxis.dimensions.items[i]; rendererParams[dim.id] = { fn: 'groupTabularRenderer', group: group, colspan: 0, hidden: false, previousExpanded: previousExpanded, subtotalRow: subtotalRow }; if(dim.id == group.dimension.id){ rendererParams[dim.id].colspan = k; found = true; }else{ rendererParams[dim.id].hidden = found; k--; } } item = group; } if(hasSummaryData){ rowClasses.push(me.getClsGroupTotal()); } if(!config.record){ rowClasses.push(me.getSummaryDataCls()); } me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: rowClasses, rendererParams: rendererParams }; // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: 'topAxisRenderer', group: item }; config.results.push(record); }, processRecordTabular: function(config){ var me = this, group = config['group'], found = false, record = group.record, items = me.getMatrix().leftAxis.dimensions.items, len = items.length, i, column; me.storeInfo[record.internalId] = { leftKey: group.key, rowStyle: '', rowClasses: [me.getSummaryDataCls()], rendererParams: {} }; for(i = 0; i < len; i++){ column = items[i]; if(column.id == group.dimension.id){ found = true; } me.storeInfo[record.internalId].rendererParams[column.id] = { fn: 'recordTabularRenderer', group: group, hidden: !found }; } // for all top axis columns use a new renderer me.storeInfo[record.internalId].rendererParams['topaxis'] = { fn: 'topAxisRenderer', group: group }; config.results.push(record); }, // various functions doExpandCollapse: function(key, oldRecord){ var me = this, gridMaster = me.getGrid(), group; group = me.getMatrix().leftAxis.findTreeElement('key', key); if(!group){ return; } me.doExpandCollapseInternal(group.node, oldRecord); gridMaster.fireEvent((group.node.expanded ? 'pivotgroupexpand' : 'pivotgroupcollapse'), gridMaster, 'row', group.node); }, doExpandCollapseInternal: function(group, oldRecord){ var me = this, store = me.getStore(), isClassic = Ext.toolkit == 'classic', items, oldItems, startIdx, len; group.expanded = !group.expanded; oldItems = me.processGroup({ group: group, previousExpanded: false }); group.expanded = !group.expanded; items = me.processGroup({ group: group, previousExpanded: false }); if(items.length && oldItems.length && (startIdx = store.indexOf(oldItems[0])) !== -1){ if(isClassic) { store.suspendEvents(); } if(group.expanded){ store.remove(store.getAt(startIdx)); store.insert(startIdx, items); }else{ store.remove(oldItems); store.insert(startIdx, items); } me.removeStoreInfoData(oldItems); if(isClassic) { store.resumeEvents(); // the replace event is better than remove and inserts store.fireEvent('replace', store, startIdx, oldItems, items); } } }, removeStoreInfoData: function(records){ var len = records.length, record, i; for(i = 0; i < len; i++){ record = records[i]; if(this.storeInfo[record.internalId]){ delete this.storeInfo[record.internalId]; } } }, onGroupExpand: function(matrix, type, item){ if(type == 'row') { if(item) { this.doExpandCollapseInternal(item, item.records.collapsed); }else{ this.processStore(); } } }, onGroupCollapse: function(matrix, type, item){ if(type == 'row') { if(item) { this.doExpandCollapseInternal(item, item.records.expanded); }else{ this.processStore(); } } }});