| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-grid-feature-Grouping'>/**</span> * This feature allows to display the grid rows aggregated into groups as specified by the {@link Ext.data.Store#groupers} * specified on the Store. The group will show the title for the group name and then the appropriate records for the group * underneath. The groups can also be expanded and collapsed. *  * ## Extra Events * * This feature adds several extra events that will be fired on the grid to interact with the groups: * *  - {@link #groupclick} *  - {@link #groupdblclick} *  - {@link #groupcontextmenu} *  - {@link #groupexpand} *  - {@link #groupcollapse} *  * ## Menu Augmentation * * This feature adds extra options to the grid column menu to provide the user with functionality to modify the grouping. * This can be disabled by setting the {@link #enableGroupingMenu} option. The option to disallow grouping from being turned off * by the user is {@link #enableNoGroups}. *  * ## Controlling Group Text * * The {@link #groupHeaderTpl} is used to control the rendered title for each group. It can modified to customized * the default display. *  * ## Example Usage *  *     @example *     var store = Ext.create('Ext.data.Store', { *         storeId:'employeeStore', *         fields:['name', 'seniority', 'department'], *         groupField: 'department', *         data: {'employees':[ *             { "name": "Michael Scott",  "seniority": 7, "department": "Management" }, *             { "name": "Dwight Schrute", "seniority": 2, "department": "Sales" }, *             { "name": "Jim Halpert",    "seniority": 3, "department": "Sales" }, *             { "name": "Kevin Malone",   "seniority": 4, "department": "Accounting" }, *             { "name": "Angela Martin",  "seniority": 5, "department": "Accounting" } *         ]}, *         proxy: { *             type: 'memory', *             reader: { *                 type: 'json', *                 root: 'employees' *             } *         } *     }); * *     Ext.create('Ext.grid.Panel', { *         title: 'Employees', *         store: Ext.data.StoreManager.lookup('employeeStore'), *         columns: [ *             { text: 'Name',     dataIndex: 'name' }, *             { text: 'Seniority', dataIndex: 'seniority' } *         ], *         features: [{ftype:'grouping'}], *         width: 200, *         height: 275, *         renderTo: Ext.getBody() *     }); * * **Note:** To use grouping with a grid that has {@link Ext.grid.column.Column#locked locked columns}, you need to supply * the grouping feature as a config object - so the grid can create two instances of the grouping feature. * * @author Nicolas Ferrero */Ext.define('Ext.grid.feature.Grouping', {    extend: 'Ext.grid.feature.Feature',    alias: 'feature.grouping',    eventPrefix: 'group',    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',    bodySelector: '.' + Ext.baseCSSPrefix + 'grid-group-body',    constructor: function() {        var me = this;        me.collapsedState = {};        me.callParent(arguments);    },    <span id='Ext-grid-feature-Grouping-event-groupclick'>    /**</span>     * @event groupclick     * @param {Ext.view.Table} view     * @param {HTMLElement} node     * @param {String} group The name of the group     * @param {Ext.EventObject} e     */<span id='Ext-grid-feature-Grouping-event-groupdblclick'>    /**</span>     * @event groupdblclick     * @param {Ext.view.Table} view     * @param {HTMLElement} node     * @param {String} group The name of the group     * @param {Ext.EventObject} e     */<span id='Ext-grid-feature-Grouping-event-groupcontextmenu'>    /**</span>     * @event groupcontextmenu     * @param {Ext.view.Table} view     * @param {HTMLElement} node     * @param {String} group The name of the group     * @param {Ext.EventObject} e     */<span id='Ext-grid-feature-Grouping-event-groupcollapse'>    /**</span>     * @event groupcollapse     * @param {Ext.view.Table} view     * @param {HTMLElement} node     * @param {String} group The name of the group     */<span id='Ext-grid-feature-Grouping-event-groupexpand'>    /**</span>     * @event groupexpand     * @param {Ext.view.Table} view     * @param {HTMLElement} node     * @param {String} group The name of the group     */<span id='Ext-grid-feature-Grouping-cfg-groupHeaderTpl'>    /**</span>     * @cfg {String/Array/Ext.Template} groupHeaderTpl     * A string Template snippet, an array of strings (optionally followed by an object containing Template methods) to be used to construct a Template, or a Template instance.     *      * - Example 1 (Template snippet):     *      *       groupHeaderTpl: 'Group: {name}'     *          * - Example 2 (Array):     *      *       groupHeaderTpl: [     *           'Group: ',     *           '<div>{name:this.formatName}</div>',     *           {     *               formatName: function(name) {     *                   return Ext.String.trim(name);     *               }     *           }     *       ]     *          * - Example 3 (Template Instance):     *      *       groupHeaderTpl: Ext.create('Ext.XTemplate',     *           'Group: ',     *           '<div>{name:this.formatName}</div>',     *           {     *               formatName: function(name) {     *                   return Ext.String.trim(name);     *               }     *           }     *       )     *     * @cfg {String}           groupHeaderTpl.groupField         The field name being grouped by.     * @cfg {String}           groupHeaderTpl.columnName         The column header associated with the field being grouped by *if there is a column for the field*, falls back to the groupField name.     * @cfg {Mixed}            groupHeaderTpl.groupValue         The value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered.     * @cfg {String}           groupHeaderTpl.renderedGroupValue The rendered value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered, as produced by the column renderer.     * @cfg {String}           groupHeaderTpl.name               An alias for renderedGroupValue     * @cfg {Object[]}         groupHeaderTpl.rows               An array of child row data objects as returned by the View's {@link Ext.view.AbstractView#prepareData prepareData} method.     * @cfg {Ext.data.Model[]} groupHeaderTpl.children           An array containing the child records for the group being rendered.     */    groupHeaderTpl: '{columnName}: {name}',    <span id='Ext-grid-feature-Grouping-cfg-depthToIndent'>    /**</span>     * @cfg {Number} [depthToIndent=17]     * Number of pixels to indent per grouping level     */    depthToIndent: 17,    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',    hdCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible',    //<locale><span id='Ext-grid-feature-Grouping-cfg-groupByText'>    /**</span>     * @cfg {String} [groupByText="Group by this field"]     * Text displayed in the grid header menu for grouping by header.     */    groupByText : 'Group by this field',    //</locale>    //<locale><span id='Ext-grid-feature-Grouping-cfg-showGroupsText'>    /**</span>     * @cfg {String} [showGroupsText="Show in groups"]     * Text displayed in the grid header for enabling/disabling grouping.     */    showGroupsText : 'Show in groups',    //</locale><span id='Ext-grid-feature-Grouping-cfg-hideGroupedHeader'>    /**</span>     * @cfg {Boolean} [hideGroupedHeader=false]     * True to hide the header that is currently grouped.     */    hideGroupedHeader : false,<span id='Ext-grid-feature-Grouping-cfg-startCollapsed'>    /**</span>     * @cfg {Boolean} [startCollapsed=false]     * True to start all groups collapsed.     */    startCollapsed : false,<span id='Ext-grid-feature-Grouping-cfg-enableGroupingMenu'>    /**</span>     * @cfg {Boolean} [enableGroupingMenu=true]     * True to enable the grouping control in the header menu.     */    enableGroupingMenu : true,<span id='Ext-grid-feature-Grouping-cfg-enableNoGroups'>    /**</span>     * @cfg {Boolean} [enableNoGroups=true]     * True to allow the user to turn off grouping.     */    enableNoGroups : true,<span id='Ext-grid-feature-Grouping-cfg-collapsible'>    /**</span>     * @cfg {Boolean} [collapsible=true]     * Set to `falsee` to disable collapsing groups from the UI.     *      * This is set to `false` when the associated {@link Ext.data.Store store} is      * {@link Ext.data.Store#buffered buffered}.     */    collapsible: true,    enable: function() {        var me    = this,            view  = me.view,            store = view.store,            groupToggleMenuItem;        me.lastGroupField = me.getGroupField();        if (me.lastGroupIndex) {            me.block();            store.group(me.lastGroupIndex);            me.unblock();        }        me.callParent();        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');        groupToggleMenuItem.setChecked(true, true);        me.refreshIf();    },    disable: function() {        var me    = this,            view  = me.view,            store = view.store,            remote = store.remoteGroup,            groupToggleMenuItem,            lastGroup;        lastGroup = store.groupers.first();        if (lastGroup) {            me.lastGroupIndex = lastGroup.property;            me.block();            store.clearGrouping();            me.unblock();        }        me.callParent();        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');        groupToggleMenuItem.setChecked(true, true);        groupToggleMenuItem.setChecked(false, true);        me.refreshIf();    },    refreshIf: function() {        var ownerCt = this.grid.ownerCt,            view = this.view;                    if (!view.store.remoteGroup && !this.blockRefresh) {            // We are one side of a lockable grid, so refresh the locking view            if (ownerCt && ownerCt.lockable) {                ownerCt.view.refresh();            } else {                view.refresh();            }        }    },    getFeatureTpl: function(values, parent, x, xcount) {        return [            '<tpl if="typeof rows !== \'undefined\'">',                // group row tpl                '<tr id="{groupHeaderId}" class="' + Ext.baseCSSPrefix + 'grid-group-hd {hdCollapsedCls} {collapsibleClass}"><td class="' + Ext.baseCSSPrefix + 'grid-cell" colspan="' + parent.columns.length + '" {[this.indentByDepth(values)]}><div class="' + Ext.baseCSSPrefix + 'grid-cell-inner"><div class="' + Ext.baseCSSPrefix + 'grid-group-title">{collapsed}{[this.renderGroupHeaderTpl(values, parent)]}</div></div></td></tr>',                // this is the rowbody                '<tr id="{groupBodyId}" class="' + Ext.baseCSSPrefix + 'grid-group-body {collapsedCls}"><td colspan="' + parent.columns.length + '">{[this.recurse(values)]}</td></tr>',            '</tpl>'        ].join('');    },    getFragmentTpl: function() {        var me = this;        return {            indentByDepth: me.indentByDepth,            depthToIndent: me.depthToIndent,            renderGroupHeaderTpl: function(values, parent) {                return Ext.XTemplate.getTpl(me, 'groupHeaderTpl').apply(values, parent);            }        };    },    indentByDepth: function(values) {        return 'style="padding-left:'+ ((values.depth || 0) * this.depthToIndent) + 'px;"';    },    // Containers holding these components are responsible for    // destroying them, we are just deleting references.    destroy: function() {        delete this.view;        delete this.prunedHeader;    },    // perhaps rename to afterViewRender    attachEvents: function() {        var me = this,            view = me.view;        view.on({            scope: me,            groupclick: me.onGroupClick,            rowfocus: me.onRowFocus        });        view.mon(view.store, {            scope: me,            groupchange: me.onGroupChange,            remove: me.onRemove,            add: me.onAdd,            update: me.onUpdate        });        if (me.enableGroupingMenu) {            me.injectGroupingMenu();        }        me.pruneGroupedHeader();        me.lastGroupField = me.getGroupField();        me.block();        me.onGroupChange();        me.unblock();    },    // If we add a new item that doesn't belong to a rendered group, refresh the view    onAdd: function(store, records){        var me = this,            view = me.view,            groupField = me.getGroupField(),            i = 0,            len = records.length,            activeGroups,            addedGroups,            groups,            needsRefresh,            group;        if (view.rendered) {            addedGroups = {};            activeGroups = {};            for (; i < len; ++i) {                group = records[i].get(groupField);                if (addedGroups[group] === undefined) {                    addedGroups[group] = 0;                }                addedGroups[group] += 1;            }            groups = store.getGroups();            for (i = 0, len = groups.length; i < len; ++i) {                group = groups[i];                activeGroups[group.name] = group.children.length;            }            for (group in addedGroups) {                if (addedGroups[group] === activeGroups[group]) {                    needsRefresh = true;                    break;                }            }                        if (needsRefresh) {                view.refresh();            }        }    },    onUpdate: function(store, record, type, changedFields){        var view = this.view;        if (view.rendered && !changedFields || Ext.Array.contains(changedFields, this.getGroupField())) {            view.refresh();        }    },    onRemove: function(store, record) {        var me = this,            groupField = me.getGroupField(),            removedGroup = record.get(groupField),            view = me.view;        if (view.rendered) {            // If that was the last one in the group, force a refresh            if (store.findExact(groupField, removedGroup) === -1) {                me.view.refresh();             }        }    },    injectGroupingMenu: function() {        var me       = this,            headerCt = me.view.headerCt;        headerCt.showMenuBy = me.showMenuBy;        headerCt.getMenuItems = me.getMenuItems();    },    showMenuBy: function(t, header) {        var menu = this.getMenu(),            groupMenuItem  = menu.down('#groupMenuItem'),            groupableMth = header.groupable === false ?  'disable' : 'enable';                    groupMenuItem[groupableMth]();        Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);    },    getMenuItems: function() {        var me                 = this,            groupByText        = me.groupByText,            disabled           = me.disabled || !me.getGroupField(),            showGroupsText     = me.showGroupsText,            enableNoGroups     = me.enableNoGroups,            getMenuItems       = me.view.headerCt.getMenuItems;        // runs in the scope of headerCt        return function() {            // We cannot use the method from HeaderContainer's prototype here            // because other plugins or features may already have injected an implementation            var o = getMenuItems.call(this);            o.push('-', {                iconCls: Ext.baseCSSPrefix + 'group-by-icon',                itemId: 'groupMenuItem',                text: groupByText,                handler: me.onGroupMenuItemClick,                scope: me            });            if (enableNoGroups) {                o.push({                    itemId: 'groupToggleMenuItem',                    text: showGroupsText,                    checked: !disabled,                    checkHandler: me.onGroupToggleMenuItemClick,                    scope: me                });            }            return o;        };    },<span id='Ext-grid-feature-Grouping-method-onGroupMenuItemClick'>    /**</span>     * Group by the header the user has clicked on.     * @private     */    onGroupMenuItemClick: function(menuItem, e) {        var me = this,            menu = menuItem.parentMenu,            hdr  = menu.activeHeader,            view = me.view,            store = view.store;        delete me.lastGroupIndex;        me.block();        me.enable();        store.group(hdr.dataIndex);        me.pruneGroupedHeader();        me.unblock();        me.refreshIf();    },    block: function(){        this.blockRefresh = this.view.blockRefresh = true;    },    unblock: function(){        this.blockRefresh = this.view.blockRefresh = false;    },<span id='Ext-grid-feature-Grouping-method-onGroupToggleMenuItemClick'>    /**</span>     * Turn on and off grouping via the menu     * @private     */    onGroupToggleMenuItemClick: function(menuItem, checked) {        this[checked ? 'enable' : 'disable']();    },<span id='Ext-grid-feature-Grouping-method-pruneGroupedHeader'>    /**</span>     * Prunes the grouped header from the header container     * @private     */    pruneGroupedHeader: function() {        var me = this,            header = me.getGroupedHeader();        if (me.hideGroupedHeader && header) {            if (me.prunedHeader) {                me.prunedHeader.show();            }            me.prunedHeader = header;            header.hide();        }    },    getGroupedHeader: function(){        var groupField = this.getGroupField(),            headerCt = this.view.headerCt;        return groupField ? headerCt.down('[dataIndex=' + groupField + ']') : null;    },    getGroupField: function(){        var group = this.view.store.groupers.first();        if (group) {            return group.property;        }        return '';     },<span id='Ext-grid-feature-Grouping-method-onRowFocus'>    /**</span>     * When a row gains focus, expand the groups above it     * @private     */    onRowFocus: function(rowIdx) {        var node    = this.view.getNode(rowIdx),            groupBd = Ext.fly(node).up('.' + this.collapsedCls);        if (groupBd) {            // for multiple level groups, should expand every groupBd            // above            this.expand(groupBd);        }    },<span id='Ext-grid-feature-Grouping-method-isExpanded'>    /**</span>     * Returns `true` if the named group is expanded.     * @param {String} groupName The group name as returned from {@link Ext.data.Store#getGroupString getGroupString}. This is usually the value of     * the {@link Ext.data.Store#groupField groupField}.     * @return {Boolean} `true` if the group defined by that value is expanded.     */    isExpanded: function(groupName) {        return (this.collapsedState[groupName] === false);    },<span id='Ext-grid-feature-Grouping-method-expand'>    /**</span>     * Expand a group     * @param {String/Ext.Element} groupName The group name, or the element that contains the group body     * @param {Boolean} focus Pass `true` to focus the group after expand.     */    expand: function(groupName, focus, /*private*/ preventSizeCalculation) {        var me = this,            view = me.view,            groupHeader,            groupBody,            lockingPartner = me.lockingPartner;        // We've been passed the group name        if (Ext.isString(groupName)) {            groupBody = Ext.fly(me.getGroupBodyId(groupName), '_grouping');        }        // We've been passed an element        else {            groupBody = Ext.fly(groupName, '_grouping')            groupName = me.getGroupName(groupBody);        }        groupHeader = Ext.get(me.getGroupHeaderId(groupName));        // If we are collapsed...        if (me.collapsedState[groupName]) {            groupBody.removeCls(me.collapsedCls);            groupBody.prev().removeCls(me.hdCollapsedCls);            if (preventSizeCalculation !== true) {                view.refreshSize();            }            view.fireEvent('groupexpand', view, groupHeader, groupName);            me.collapsedState[groupName] = false;            // If we are one side of a locking view, the other side has to stay in sync            if (lockingPartner) {                lockingPartner.expand(groupName, focus, preventSizeCalculation);            }            if (focus) {                groupBody.scrollIntoView(view.el, null, true);            }        }    },<span id='Ext-grid-feature-Grouping-method-expandAll'>    /**</span>     * Expand all groups     */    expandAll: function(){        var me   = this,            view = me.view,            els  = view.el.select(me.eventSelector).elements,            e,            eLen = els.length;        for (e = 0; e < eLen; e++) {            me.expand(Ext.fly(els[e]).next(), false, true);        }        view.refreshSize();    },<span id='Ext-grid-feature-Grouping-method-collapse'>    /**</span>     * Collapse a group     * @param {String/Ext.Element} groupName The group name, or the element that contains group body     * @param {Boolean} focus Pass `true` to focus the group after expand.     */    collapse: function(groupName, focus, /*private*/ preventSizeCalculation) {        var me = this,            view = me.view,            groupHeader,            groupBody,            lockingPartner = me.lockingPartner;        // We've been passed the group name        if (Ext.isString(groupName)) {            groupBody = Ext.fly(me.getGroupBodyId(groupName), '_grouping');        }        // We've been passed an element        else {            groupBody = Ext.fly(groupName, '_grouping')            groupName = me.getGroupName(groupBody);        }        groupHeader = Ext.get(me.getGroupHeaderId(groupName));         // If we are not collapsed...        if (!me.collapsedState[groupName]) {            groupBody.addCls(me.collapsedCls);            groupBody.prev().addCls(me.hdCollapsedCls);            if (preventSizeCalculation !== true) {                view.refreshSize();            }            view.fireEvent('groupcollapse', view, groupHeader, groupName);            me.collapsedState[groupName] = true;            // If we are one side of a locking view, the other side has to stay in sync            if (lockingPartner) {                lockingPartner.collapse(groupName, focus, preventSizeCalculation);            }            if (focus) {                groupHeader.scrollIntoView(view.el, null, true);            }        }    },<span id='Ext-grid-feature-Grouping-method-collapseAll'>    /**</span>     * Collapse all groups     */    collapseAll: function() {        var me     = this,            view   = me.view,            els    = view.el.select(me.eventSelector).elements,            e,            eLen   = els.length;        for (e = 0; e < eLen; e++) {            me.collapse(Ext.fly(els[e]).next(), false, true);        }        view.refreshSize();    },    onGroupChange: function(){        var me = this,            field = me.getGroupField(),            menuItem,            visibleGridColumns,            groupingByLastVisibleColumn;        if (me.hideGroupedHeader) {            if (me.lastGroupField) {                menuItem = me.getMenuItem(me.lastGroupField);                if (menuItem) {                    menuItem.setChecked(true);                }            }            if (field) {                visibleGridColumns = me.view.headerCt.getVisibleGridColumns();                // See if we are being asked to group by the sole remaining visible column.                // If so, then do not hide that column.                groupingByLastVisibleColumn = ((visibleGridColumns.length === 1) && (visibleGridColumns[0].dataIndex == field));                menuItem = me.getMenuItem(field);                if (menuItem && !groupingByLastVisibleColumn) {                    menuItem.setChecked(false);                }            }        }        me.refreshIf();        me.lastGroupField = field;    },<span id='Ext-grid-feature-Grouping-method-getMenuItem'>    /**</span>     * Gets the related menu item for a dataIndex     * @private     * @return {Ext.grid.header.Container} The header     */    getMenuItem: function(dataIndex){        var view = this.view,            header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),            menu = view.headerCt.getMenu();        return header ? menu.down('menuitem[headerId='+ header.id +']') : null;    },<span id='Ext-grid-feature-Grouping-method-onGroupClick'>    /**</span>     * Toggle between expanded/collapsed state when clicking on     * the group.     * @private     */    onGroupClick: function(view, rowElement, groupName, e) {        var me = this;        if (me.collapsible) {            if (me.collapsedState[groupName]) {                me.expand(groupName);            } else {                me.collapse(groupName);            }        }    },    // Injects isRow and closeRow into the metaRowTpl.    getMetaRowTplFragments: function() {        return {            isRow: this.isRow,            closeRow: this.closeRow        };    },    // injected into rowtpl and wrapped around metaRowTpl    // becomes part of the standard tpl    isRow: function() {        return '<tpl if="typeof rows === \'undefined\'">';    },    // injected into rowtpl and wrapped around metaRowTpl    // becomes part of the standard tpl    closeRow: function() {        return '</tpl>';    },    // isRow and closeRow are injected via getMetaRowTplFragments    mutateMetaRowTpl: function(metaRowTpl) {        metaRowTpl.unshift('{[this.isRow()]}');        metaRowTpl.push('{[this.closeRow()]}');    },    // injects an additional style attribute via tdAttrKey with the proper    // amount of padding    getAdditionalData: function(data, idx, record, orig) {        var view = this.view,            hCt  = view.headerCt,            col  = hCt.items.getAt(0),            o = {},            tdAttrKey;        // If there *are* any columne in this grid (possible empty side of a locking grid)...        // Add the padding-left style to indent the row according to grouping depth.        // Preserve any current tdAttr that a user may have set.        if (col) {            tdAttrKey = col.id + '-tdAttr';            o[tdAttrKey] = this.indentByDepth(data) + " " + (orig[tdAttrKey] ? orig[tdAttrKey] : '');            o.collapsed = 'true';            o.data = record.getData();        }        return o;    },    // return matching preppedRecords    getGroupRows: function(group, records, preppedRecords, fullWidth) {        var me = this,            children = group.children,            rows = group.rows = [],            view = me.view,            header = me.getGroupedHeader(),            groupField = me.getGroupField(),            index = -1,            r,            rLen = records.length,            record;                    // Buffered rendering implies that user collapsing is disabled.        if (view.store.buffered) {            me.collapsible = false;        }                    group.viewId = view.id;        for (r = 0; r < rLen; r++) {            record = records[r];            if (record.get(groupField) == group.name) {                index = r;            }            if (Ext.Array.indexOf(children, record) != -1) {                rows.push(Ext.apply(preppedRecords[r], {                    depth : 1                }));            }        }        group.groupField = groupField,        group.groupHeaderId = me.getGroupHeaderId(group.name);        group.groupBodyId = me.getGroupBodyId(group.name);        group.fullWidth = fullWidth;        group.columnName = header ? header.text : groupField;        group.groupValue = group.name;        // Here we attempt to overwrite the group name value from the Store with        // the get the rendered value of the column from the *prepped* record        if (header && index > -1) {            group.name = group.renderedValue = preppedRecords[index][header.id];        }        if (me.collapsedState[group.name]) {            group.collapsedCls = me.collapsedCls;            group.hdCollapsedCls = me.hdCollapsedCls;        } else {            group.collapsedCls = group.hdCollapsedCls = '';        }        // Collapsibility of groups may be disabled.        if (me.collapsible) {            group.collapsibleClass = me.hdCollapsibleCls;        } else {            group.collapsibleClass = '';        }        return group;    },    // Create an associated DOM id for the group's header element given the group name    getGroupHeaderId: function(groupName) {        return this.view.id + '-hd-' + groupName;    },    // Create an associated DOM id for the group's body element given the group name    getGroupBodyId: function(groupName) {        return this.view.id + '-bd-' + groupName;    },    // Get the group name from an associated element whether it's within a header or a body    getGroupName: function(element) {        var me = this,            targetEl;                        // See if element is, or is within a group header. If so, we can extract its name        targetEl = Ext.fly(element).findParent(me.eventSelector);        if (targetEl) {            return targetEl.id.split(this.view.id + '-hd-')[1];        }        // See if element is, or is within a group body. If so, we can extract its name        targetEl = Ext.fly(element).findParent(me.bodySelector);        if (targetEl) {            return targetEl.id.split(this.view.id + '-bd-')[1];        }    },    // return the data in a grouped format.    collectData: function(records, preppedRecords, startIndex, fullWidth, o) {        var me    = this,            store = me.view.store,            collapsedState = me.collapsedState,            collapseGroups,            g,            groups, gLen, group;        if (me.startCollapsed) {            // If we start collapse, we'll set the state of the groups here            // and unset the flag so any subsequent expand/collapse is            // managed by the feature            me.startCollapsed = false;            collapseGroups = true;        }        if (!me.disabled && store.isGrouped()) {            o.rows = groups = store.getGroups();            gLen   = groups.length;            for (g = 0; g < gLen; g++) {                group = groups[g];                                if (collapseGroups) {                    collapsedState[group.name] = true;                }                me.getGroupRows(group, records, preppedRecords, fullWidth);            }        }        return o;    },    // adds the groupName to the groupclick, groupdblclick, groupcontextmenu    // events that are fired on the view. Chose not to return the actual    // group itself because of its expense and because developers can simply    // grab the group via store.getGroups(groupName)    getFireEventArgs: function(type, view, targetEl, e) {        return [type, view, targetEl, this.getGroupName(targetEl), e];    }});</pre></body></html>
 |