/** * Encapsulates a grouped collection of records within a {@link Ext.util.Collection} */Ext.define('Ext.util.Group', { extend: 'Ext.util.Collection', isGroup: true, config: { /** * @cfg {Number} level * @readOnly * * The group level in the {@link Ext.util.GroupCollection} hierarchy. */ level: 1, /** * @cfg {String} path * @readOnly * * This is the path to the group in the {@link Ext.util.GroupCollection} hierarchy. * The path is compound of all parent groups keys separated by the `pathSeparator`. */ path: null, /** * @cfg {Object} data * @readOnly * * Custom data to set on this group. Check out `setCustomData` and `getCustomData` methods. */ data: null, /** * @cfg {Ext.util.Grouper} grouper * @readOnly * * That's the grouper object used to generate this group. */ grouper: null, /** * @cfg {String} label * * Group label */ label: null, /** * @cfg {String} groupKey * @readOnly * * That's the group's key */ groupKey: null, // similar to label groupValue: null, /** * @cfg {Ext.util.Group/Ext.util.GroupCollection} * @readOnly * * Reference to the parent group or the {@link Ext.util.GroupCollection} * if it's a top level group. */ parent: null }, /** * @private */ pathSeparator: '<||>', // Group collections must have a higher priority than normal collections. This ensures // that their endupdate handlers for filters and sorters run prior to the endupdate // handler of the store's main collection, and so when the user handles events such // as sort/datachanged, the groups have already been sorted and filtered. $endUpdatePriority: 2001, manageSorters: false, constructor: function(config) { this.callParent([config]); this.on('remove', 'onGroupItemsRemove', this); }, destroy: function() { var parent = this.getParent(), groups; if (parent) { // remove us from the parent group if (parent.isGroup) { groups = parent.getGroups(); } else if (parent.isGroupCollection) { groups = parent; } if (groups) { groups.remove(this); } } this.callParent(); }, clear: function() { var groups = this.getGroups(), length, i, ret; ret = this.callParent(); if (groups) { length = groups.length; for (i = 0; i < length; i++) { groups.items[i].clear(); } groups.clear(); } return ret; }, getData: function() { var data = this._data; if (!data) { data = {}; this.setData(data); } return data; }, /** * Set additional data for this group besides the label. * * @param property * @param value */ setCustomData: function(property, value) { this.getData()[property] = value; }, /** * Fetch custom data set for this group. * * @param property * @return {Object} */ getCustomData: function(property) { return this.getData()[property]; }, updateLabel: function(label) { var me = this, grouper = me.getGrouper(); if (grouper) { me.setCustomData(grouper.getProperty(), label); } else { me.setData({}); } me.setGroupValue(label); }, // we need this for compatibility with the old grouping functionality only getGrouper: function() { return this._grouper; }, // to override the collection fn updateGrouper: Ext.emptyFn, updateSorters: function(sorters, oldSorters) { var children = this.getGroups(), length, i; this.callParent([sorters, oldSorters]); if (!children || this.ejectTime) { return; } length = children.length; for (i = 0; i < length; i++) { children.items[i].setSorters(sorters); } }, updateGroupKey: function() { this.refreshPath(); }, updateParent: function() { this.refreshPath(); }, refreshPath: function() { var me = this, level = 1, parent, path; if (!me.refreshingPath) { me.refreshingPath = true; parent = me.getParent(); path = me.getGroupKey(); if (parent && parent.isGroup) { level += parent.getLevel(); path = parent.getPath() + me.pathSeparator + path; } me.setLevel(level); me.setPath(path); me.refreshingPath = false; } }, /** * Check if this group is the first group in its parent groups. * @return {Boolean} */ isFirst: function() { var parent = this.getParent(), collection = parent ? (parent.isGroup ? parent.getGroups() : parent) : null, first = false; if (collection) { first = (collection.indexOf(this) === 0); } return first; }, /** * Check if this group is the last group in its parent groups. * @return {Boolean} */ isLast: function() { var parent = this.getParent(), collection = parent ? (parent.isGroup ? parent.getGroups() : parent) : null, last = false; if (collection) { last = (collection.indexOf(this) === collection.length - 1); } return last; }, groupItems: function() { if (!this.ejectTime) { return this.callParent(); } }, sortItems: function() { if (!this.ejectTime) { return this.callParent(); } }, eject: function() { var me = this; me.ejectTime = Ext.now(); me.setConfig({ sorters: null, groupers: null, parent: null, grouper: null, groups: null }); }, privates: { /** * Returns true if the specified item can belong to this group. * * @param {Object} item * @return {Boolean} * @private */ canOwnItem: function(item) { // we check the grouper result against the group key since // the group label could be different return this.getGroupKey() === this.getGrouper().getGroupString(item); }, removeItems: function(items) { var groups = this.getGroups(), len = groups ? groups.length : 0, removeGroups = [], i, group; for (i = 0; i < len; ++i) { group = groups.items[i]; group.remove(items); if (!group.length) { removeGroups.push(group); } } if (removeGroups.length) { // empty groups should be removed groups.remove(removeGroups); } }, onGroupItemsRemove: function(collection, info) { this.removeItems(info.items); } } });