| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947 | <!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-Lockable'>/**</span> * @private * * Lockable is a private mixin which injects lockable behavior into any * TablePanel subclass such as GridPanel or TreePanel. TablePanel will * automatically inject the Ext.grid.Lockable mixin in when one of the * these conditions are met: * *  - The TablePanel has the lockable configuration set to true *  - One of the columns in the TablePanel has locked set to true/false * * Each TablePanel subclass must register an alias. It should have an array * of configurations to copy to the 2 separate tablepanel's that will be generated * to note what configurations should be copied. These are named normalCfgCopy and * lockedCfgCopy respectively. * * Columns which are locked must specify a fixed width. They do NOT support a * flex width. * * Configurations which are specified in this class will be available on any grid or * tree which is using the lockable functionality. */Ext.define('Ext.grid.Lockable', {    requires: [        'Ext.grid.LockingView',        'Ext.view.Table'    ],<span id='Ext-grid-Lockable-cfg-syncRowHeight'>    /**</span>     * @cfg {Boolean} syncRowHeight Synchronize rowHeight between the normal and     * locked grid view. This is turned on by default. If your grid is guaranteed     * to have rows of all the same height, you should set this to false to     * optimize performance.     */    syncRowHeight: true,<span id='Ext-grid-Lockable-cfg-subGridXType'>    /**</span>     * @cfg {String} subGridXType The xtype of the subgrid to specify. If this is     * not specified lockable will determine the subgrid xtype to create by the     * following rule. Use the superclasses xtype if the superclass is NOT     * tablepanel, otherwise use the xtype itself.     */<span id='Ext-grid-Lockable-cfg-lockedViewConfig'>    /**</span>     * @cfg {Object} lockedViewConfig A view configuration to be applied to the     * locked side of the grid. Any conflicting configurations between lockedViewConfig     * and viewConfig will be overwritten by the lockedViewConfig.     */<span id='Ext-grid-Lockable-cfg-normalViewConfig'>    /**</span>     * @cfg {Object} normalViewConfig A view configuration to be applied to the     * normal/unlocked side of the grid. Any conflicting configurations between normalViewConfig     * and viewConfig will be overwritten by the normalViewConfig.     */    headerCounter: 0,<span id='Ext-grid-Lockable-cfg-scrollDelta'>    /**</span>     * @cfg {Number} scrollDelta     * Number of pixels to scroll when scrolling the locked section with mousewheel.     */    scrollDelta: 40,    <span id='Ext-grid-Lockable-cfg-lockedGridConfig'>    /**</span>     * @cfg {Object} lockedGridConfig     * Any special configuration options for the locked part of the grid     */    <span id='Ext-grid-Lockable-cfg-normalGridConfig'>    /**</span>     * @cfg {Object} normalGridConfig     * Any special configuration options for the normal part of the grid     */    // i8n text    //<locale>    unlockText: 'Unlock',    //</locale>    //<locale>    lockText: 'Lock',    //</locale>    determineXTypeToCreate: function() {        var me = this,            typeToCreate,            xtypes, xtypesLn, xtype, superxtype;        if (me.subGridXType) {            typeToCreate = me.subGridXType;        } else {            xtypes     = this.getXTypes().split('/');            xtypesLn   = xtypes.length;            xtype      = xtypes[xtypesLn - 1];            superxtype = xtypes[xtypesLn - 2];            if (superxtype !== 'tablepanel') {                typeToCreate = superxtype;            } else {                typeToCreate = xtype;            }        }        return typeToCreate;    },    // injectLockable will be invoked before initComponent's parent class implementation    // is called, so throughout this method this. are configurations    injectLockable: function() {        // ensure lockable is set to true in the TablePanel        this.lockable = true;        // Instruct the TablePanel it already has a view and not to create one.        // We are going to aggregate 2 copies of whatever TablePanel we are using        this.hasView = true;        var me = this,            // If the OS does not show a space-taking scrollbar, the locked view can be overflow:auto            scrollLocked = Ext.getScrollbarSize().width === 0,            store = me.store = Ext.StoreManager.lookup(me.store),            // xtype of this class, 'treepanel' or 'gridpanel'            // (Note: this makes it a requirement that any subclass that wants to use lockable functionality needs to register an            // alias.)            xtype = me.determineXTypeToCreate(),            // share the selection model            selModel = me.getSelectionModel(),            lockedFeatures,            normalFeatures,            lockedPlugins,            normalPlugins,            lockedGrid,            normalGrid,            i, len,            columns,            lockedHeaderCt,            normalHeaderCt,            lockedView,            normalView,            listeners;        lockedFeatures = me.constructFeatures();        // Clone any Features in the Array which are already instantiated        me.cloneFeatures();        normalFeatures = me.constructFeatures();        lockedPlugins = me.constructPlugins();        // Clone any Plugins in the Array which are already instantiated        me.clonePlugins();        normalPlugins = me.constructPlugins();        // The "shell" Panel which just acts as a Container for the two grids must not use the features and plugins        delete me.features;        delete me.plugins;        // Each Feature must have a reference to its counterpart on the opposite side of the locking view        for (i = 0, len = (lockedFeatures ? lockedFeatures.length : 0); i < len; i++) {            lockedFeatures[i].lockingPartner = normalFeatures[i];            normalFeatures[i].lockingPartner = lockedFeatures[i];        }        lockedGrid = Ext.apply({            xtype: xtype,            store: store,            scrollerOwner: false,            // Lockable does NOT support animations for Tree            enableAnimations: false,            scroll: scrollLocked ? 'vertical' : false,            selModel: selModel,            border: false,            cls: Ext.baseCSSPrefix + 'grid-inner-locked',            isLayoutRoot: function() {                return false;            },            features: lockedFeatures,            plugins: lockedPlugins        }, me.lockedGridConfig);        normalGrid = Ext.apply({            xtype: xtype,            store: store,            scrollerOwner: false,            enableAnimations: false,            selModel: selModel,            border: false,            isLayoutRoot: function() {                return false;            },            features: normalFeatures,            plugins: normalPlugins        }, me.normalGridConfig);        me.addCls(Ext.baseCSSPrefix + 'grid-locked');        // copy appropriate configurations to the respective        // aggregated tablepanel instances and then delete them        // from the master tablepanel.        Ext.copyTo(normalGrid, me, me.bothCfgCopy);        Ext.copyTo(lockedGrid, me, me.bothCfgCopy);        Ext.copyTo(normalGrid, me, me.normalCfgCopy);        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy);        for (i = 0; i < me.normalCfgCopy.length; i++) {            delete me[me.normalCfgCopy[i]];        }        for (i = 0; i < me.lockedCfgCopy.length; i++) {            delete me[me.lockedCfgCopy[i]];        }        me.addEvents(<span id='Ext-grid-Lockable-event-lockcolumn'>            /**</span>             * @event lockcolumn             * Fires when a column is locked.             * @param {Ext.grid.Panel} this The gridpanel.             * @param {Ext.grid.column.Column} column The column being locked.             */            'lockcolumn',<span id='Ext-grid-Lockable-event-unlockcolumn'>            /**</span>             * @event unlockcolumn             * Fires when a column is unlocked.             * @param {Ext.grid.Panel} this The gridpanel.             * @param {Ext.grid.column.Column} column The column being unlocked.             */            'unlockcolumn'        );        me.addStateEvents(['lockcolumn', 'unlockcolumn']);        me.lockedHeights = [];        me.normalHeights = [];        columns = me.processColumns(me.columns);        lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0);        lockedGrid.columns = columns.locked;        normalGrid.columns = columns.normal;        // normal grid should flex the rest of the width        normalGrid.flex = 1;        lockedGrid.viewConfig = me.lockedViewConfig || {};        lockedGrid.viewConfig.loadingUseMsg = false;        normalGrid.viewConfig = me.normalViewConfig || {};        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);        lockedView = me.lockedGrid.getView();                normalGrid.viewConfig.lockingPartner = lockedView;        me.normalGrid = Ext.ComponentManager.create(normalGrid);        normalView = me.normalGrid.getView();        me.view = new Ext.grid.LockingView({            locked: me.lockedGrid,            normal: me.normalGrid,            panel: me        });        // Set up listeners for the locked view. If its SelModel ever scrolls it, the normal view must sync        listeners = {            scroll: {                fn: me.onLockedViewScroll,                element: 'el',                scope: me            }        };        // If there are system scrollbars, we have to monitor the mousewheel and fake a scroll        if (!scrollLocked) {            listeners.mousewheel = {                fn: me.onLockedViewMouseWheel,                element: 'el',                scope: me            };        }        if (me.syncRowHeight) {            listeners.refresh = me.onLockedViewRefresh;            listeners.itemupdate = me.onLockedViewItemUpdate;            listeners.scope = me;        }        lockedView.on(listeners);        // Set up listeners for the normal view        listeners = {            scroll: {                fn: me.onNormalViewScroll,                element: 'el',                scope: me            },            refresh: me.syncRowHeight ? me.onNormalViewRefresh : me.updateSpacer,            scope: me        };        normalView.on(listeners);        lockedHeaderCt = me.lockedGrid.headerCt;        normalHeaderCt = me.normalGrid.headerCt;        lockedHeaderCt.lockedCt = true;        lockedHeaderCt.lockableInjected = true;        normalHeaderCt.lockableInjected = true;        lockedHeaderCt.on({            columnshow: me.onLockedHeaderShow,            columnhide: me.onLockedHeaderHide,            columnmove: me.onLockedHeaderMove,            sortchange: me.onLockedHeaderSortChange,            columnresize: me.onLockedHeaderResize,            scope: me        });        normalHeaderCt.on({            columnmove: me.onNormalHeaderMove,            sortchange: me.onNormalHeaderSortChange,            scope: me        });        me.modifyHeaderCt();        me.items = [me.lockedGrid, me.normalGrid];        me.relayHeaderCtEvents(lockedHeaderCt);        me.relayHeaderCtEvents(normalHeaderCt);        me.layout = {            type: 'hbox',            align: 'stretch'        };    },    processColumns: function(columns){        // split apart normal and lockedWidths        var i = 0,            len = columns.length,            lockedWidth = 0,            lockedHeaders = [],            normalHeaders = [],            column;        for (; i < len; ++i) {            column = columns[i];            // MUST clone the column config because we mutate it, and we must not mutate passed in config objects in case they are re-used            // eg, in an extend-to-configure scenario.            if (!column.isComponent) {                column = Ext.apply({}, columns[i]);            }            // mark the column as processed so that the locked attribute does not            // trigger trying to aggregate the columns again.            column.processed = true;            if (column.locked) {                // <debug>                if (column.flex) {                    Ext.Error.raise("Columns which are locked do NOT support a flex width. You must set a width on the " + columns[i].text + "column.");                }                // </debug>                if (!column.hidden) {                    lockedWidth += column.width || Ext.grid.header.Container.prototype.defaultWidth;                }                lockedHeaders.push(column);            } else {                normalHeaders.push(column);            }            if (!column.headerId) {                column.headerId = (column.initialConfig || column).id || ('L' + (++this.headerCounter));            }        }        return {            lockedWidth: lockedWidth,            locked: {                items: lockedHeaders,                itemId: 'lockedHeaderCt',                stretchMaxPartner: '^^>>#normalHeaderCt'            },            normal: {                items: normalHeaders,                itemId: 'normalHeaderCt',                stretchMaxPartner: '^^>>#lockedHeaderCt'            }        };    },<span id='Ext-grid-Lockable-method-onLockedViewMouseWheel'>    /**</span>     * @private     * Listen for mousewheel events on the locked section which does not scroll.     * Scroll it in response, and the other section will automatically sync.     */    onLockedViewMouseWheel: function(e) {        var me = this,            scrollDelta = -me.scrollDelta,            deltaY = scrollDelta * e.getWheelDeltas().y,            vertScrollerEl = me.lockedGrid.getView().el.dom,            verticalCanScrollDown, verticalCanScrollUp;        if (vertScrollerEl) {            verticalCanScrollDown = vertScrollerEl.scrollTop !== vertScrollerEl.scrollHeight - vertScrollerEl.clientHeight;            verticalCanScrollUp   = vertScrollerEl.scrollTop !== 0;        }        if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {            e.stopEvent();            // Inhibit processing of any scroll events we *may* cause here.            // Some OSs do not fire a scroll event when we set the scrollTop of an overflow:hidden element,            // so we invoke the scroll handler programatically below.            me.scrolling = true;            vertScrollerEl.scrollTop += deltaY;            me.normalGrid.getView().el.dom.scrollTop = vertScrollerEl.scrollTop;            me.scrolling = false;            // Invoke the scroll event handler programatically to sync everything.            me.onNormalViewScroll();        }    },    onLockedViewScroll: function() {        var me = this,            lockedView = me.lockedGrid.getView(),            normalView = me.normalGrid.getView(),            normalTable,            lockedTable;        // Set a flag so that the scroll even doesn't bounce back when we set the normal view's scroll position        if (!me.scrolling) {            me.scrolling = true;            normalView.el.dom.scrollTop = lockedView.el.dom.scrollTop;                // For buffered views, the absolute position is important as well as scrollTop            if (me.store.buffered) {                lockedTable = lockedView.el.child('table', true);                normalTable = normalView.el.child('table', true);                lockedTable.style.position = 'absolute';            }            me.scrolling = false;        }    },        onNormalViewScroll: function() {        var me = this,            lockedView = me.lockedGrid.getView(),            normalView = me.normalGrid.getView(),            normalTable,            lockedTable;        // Set a flag so that the scroll even doesn't bounce back when we set the locked view's scroll position        if (!me.scrolling) {            me.scrolling = true;            lockedView.el.dom.scrollTop = normalView.el.dom.scrollTop;                // For buffered views, the absolute position is important as well as scrollTop            if (me.store.buffered) {                lockedTable = lockedView.el.child('table', true);                normalTable = normalView.el.child('table', true);                lockedTable.style.position = 'absolute';                lockedTable.style.top = normalTable.style.top;            }            me.scrolling = false;        }    },    // trigger a pseudo refresh on the normal side    onLockedHeaderMove: function() {        if (this.syncRowHeight) {            this.onNormalViewRefresh();        }    },    // trigger a pseudo refresh on the locked side    onNormalHeaderMove: function() {        if (this.syncRowHeight) {            this.onLockedViewRefresh();        }    },        // Create a spacer in lockedsection and store a reference.    // This is to allow the locked section to scroll past the bottom to    // take the mormal section's horizontal scrollbar into account    // TODO: Should destroy before refreshing content    updateSpacer: function() {        var me   = this,            // This affects scrolling all the way to the bottom of a locked grid            // additional test, sort a column and make sure it synchronizes            lockedViewEl   = me.lockedGrid.getView().el,            normalViewEl = me.normalGrid.getView().el.dom,            spacerId = lockedViewEl.dom.id + '-spacer',            spacerHeight = (normalViewEl.offsetHeight - normalViewEl.clientHeight) + 'px';        me.spacerEl = Ext.getDom(spacerId);        if (me.spacerEl) {            me.spacerEl.style.height = spacerHeight;        } else {            Ext.core.DomHelper.append(lockedViewEl, {                id: spacerId,                style: 'height: ' + spacerHeight            });        }    },    // cache the heights of all locked rows and sync rowheights    onLockedViewRefresh: function() {        // Only bother if there are some columns in the normal grid to sync        if (this.normalGrid.headerCt.getGridColumns().length) {            var me     = this,                view   = me.lockedGrid.getView(),                el     = view.el,                rowEls = el.query(view.getItemSelector()),                ln     = rowEls.length,                i = 0;                // reset heights each time.            me.lockedHeights = [];            for (; i < ln; i++) {                me.lockedHeights[i] = rowEls[i].offsetHeight;            }            me.syncRowHeights();            me.updateSpacer();        }    },    // cache the heights of all normal rows and sync rowheights    onNormalViewRefresh: function() {        // Only bother if there are some columns in the locked grid to sync        if (this.lockedGrid.headerCt.getGridColumns().length) {            var me     = this,                view   = me.normalGrid.getView(),                el     = view.el,                rowEls = el.query(view.getItemSelector()),                ln     = rowEls.length,                i = 0;                // reset heights each time.            me.normalHeights = [];                for (; i < ln; i++) {                me.normalHeights[i] = rowEls[i].offsetHeight;            }            me.syncRowHeights();            me.updateSpacer();        }    },    // rows can get bigger/smaller    onLockedViewItemUpdate: function(record, index, node) {        // Only bother if there are some columns in the normal grid to sync        if (this.normalGrid.headerCt.getGridColumns().length) {            this.lockedHeights[index] = node.offsetHeight;            this.syncRowHeights();        }    },    // rows can get bigger/smaller    onNormalViewItemUpdate: function(record, index, node) {            // Only bother if there are some columns in the locked grid to sync        if (this.lockedGrid.headerCt.getGridColumns().length) {            this.normalHeights[index] = node.offsetHeight;            this.syncRowHeights();        }    },<span id='Ext-grid-Lockable-method-syncRowHeights'>    /**</span>     * Synchronizes the row heights between the locked and non locked portion of the grid for each     * row. If one row is smaller than the other, the height will be increased to match the larger one.     */    syncRowHeights: function() {        var me = this,            lockedHeights = me.lockedHeights,            normalHeights = me.normalHeights,            ln = lockedHeights.length,            i  = 0,            lockedView, normalView,            lockedRowEls, normalRowEls,            scrollTop;        // ensure there are an equal num of locked and normal        // rows before synchronization        if (lockedHeights.length && normalHeights.length) {            lockedView = me.lockedGrid.getView();            normalView = me.normalGrid.getView();            lockedRowEls = lockedView.el.query(lockedView.getItemSelector());            normalRowEls = normalView.el.query(normalView.getItemSelector());            // loop thru all of the heights and sync to the other side            for (; i < ln; i++) {                // ensure both are numbers                if (!isNaN(lockedHeights[i]) && !isNaN(normalHeights[i])) {                    if (lockedHeights[i] > normalHeights[i]) {                        Ext.fly(normalRowEls[i]).setHeight(lockedHeights[i]);                    } else if (lockedHeights[i] < normalHeights[i]) {                        Ext.fly(lockedRowEls[i]).setHeight(normalHeights[i]);                    }                }            }            // Synchronize the scrollTop positions of the two views            scrollTop = normalView.el.dom.scrollTop;            normalView.el.dom.scrollTop = scrollTop;            lockedView.el.dom.scrollTop = scrollTop;                        // reset the heights            me.lockedHeights = [];            me.normalHeights = [];        }    },    // inject Lock and Unlock text    modifyHeaderCt: function() {        var me = this;        me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(me.lockedGrid.headerCt.getMenuItems, true);        me.normalGrid.headerCt.getMenuItems = me.getMenuItems(me.normalGrid.headerCt.getMenuItems, false);    },    onUnlockMenuClick: function() {        this.unlock();    },    onLockMenuClick: function() {        this.lock();    },    getMenuItems: function(getMenuItems, locked) {        var me            = this,            unlockText    = me.unlockText,            lockText      = me.lockText,            unlockCls     = Ext.baseCSSPrefix + 'hmenu-unlock',            lockCls       = Ext.baseCSSPrefix + 'hmenu-lock',            unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),            lockHandler   = Ext.Function.bind(me.onLockMenuClick, me);        // 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('-', {                cls: unlockCls,                text: unlockText,                handler: unlockHandler,                disabled: !locked            });            o.push({                cls: lockCls,                text: lockText,                handler: lockHandler,                disabled: locked            });            return o;        };    },    // going from unlocked section to locked<span id='Ext-grid-Lockable-method-lock'>    /**</span>     * Locks the activeHeader as determined by which menu is open OR a header     * as specified.     * @param {Ext.grid.column.Column} [header] Header to unlock from the locked section.     * Defaults to the header which has the menu open currently.     * @param {Number} [toIdx] The index to move the unlocked header to.     * Defaults to appending as the last item.     * @private     */    lock: function(activeHd, toIdx) {        var me         = this,            normalGrid = me.normalGrid,            lockedGrid = me.lockedGrid,            normalHCt  = normalGrid.headerCt,            lockedHCt  = lockedGrid.headerCt;        activeHd = activeHd || normalHCt.getMenu().activeHeader;        // if column was previously flexed, get/set current width        // and remove the flex        if (activeHd.flex) {            activeHd.width = activeHd.getWidth();            delete activeHd.flex;        }        Ext.suspendLayouts();        activeHd.ownerCt.remove(activeHd, false);        activeHd.locked = true;        if (Ext.isDefined(toIdx)) {            lockedHCt.insert(toIdx, activeHd);        } else {            lockedHCt.add(activeHd);        }        me.syncLockedSection();        Ext.resumeLayouts(true);        me.updateSpacer();        me.fireEvent('lockcolumn', me, activeHd);    },    syncLockedSection: function() {        var me = this;        me.syncLockedWidth();        me.lockedGrid.getView().refresh();        me.normalGrid.getView().refresh();    },    // adjust the locked section to the width of its respective    // headerCt    syncLockedWidth: function() {        var me = this,            locked = me.lockedGrid,            width = locked.headerCt.getFullWidth(true);                    Ext.suspendLayouts();        if (width > 0) {            locked.setWidth(width);            locked.show();        } else {            locked.hide();        }        Ext.resumeLayouts(true);                return width > 0;    },    onLockedHeaderResize: function() {        this.syncLockedWidth();    },    onLockedHeaderHide: function() {        this.syncLockedWidth();    },    onLockedHeaderShow: function() {        this.syncLockedWidth();    },    onLockedHeaderSortChange: function(headerCt, header, sortState) {        if (sortState) {            // no real header, and silence the event so we dont get into an            // infinite loop            this.normalGrid.headerCt.clearOtherSortStates(null, true);        }    },    onNormalHeaderSortChange: function(headerCt, header, sortState) {        if (sortState) {            // no real header, and silence the event so we dont get into an            // infinite loop            this.lockedGrid.headerCt.clearOtherSortStates(null, true);        }    },    // going from locked section to unlocked<span id='Ext-grid-Lockable-method-unlock'>    /**</span>     * Unlocks the activeHeader as determined by which menu is open OR a header     * as specified.     * @param {Ext.grid.column.Column} [header] Header to unlock from the locked section.     * Defaults to the header which has the menu open currently.     * @param {Number} [toIdx=0] The index to move the unlocked header to.     * @private     */    unlock: function(activeHd, toIdx) {        var me         = this,            normalGrid = me.normalGrid,            lockedGrid = me.lockedGrid,            normalHCt  = normalGrid.headerCt,            lockedHCt  = lockedGrid.headerCt,            refreshLocked = false;        if (!Ext.isDefined(toIdx)) {            toIdx = 0;        }        activeHd = activeHd || lockedHCt.getMenu().activeHeader;        Ext.suspendLayouts();        activeHd.ownerCt.remove(activeHd, false);        if (me.syncLockedWidth()) {            refreshLocked = true;        }        activeHd.locked = false;        // Refresh the locked section first in case it was empty        normalHCt.insert(toIdx, activeHd);        me.normalGrid.getView().refresh();        if (refreshLocked) {            me.lockedGrid.getView().refresh();        }        Ext.resumeLayouts(true);        me.fireEvent('unlockcolumn', me, activeHd);    },    applyColumnsState: function (columns) {        var me             = this,            lockedGrid     = me.lockedGrid,            lockedHeaderCt = lockedGrid.headerCt,            normalHeaderCt = me.normalGrid.headerCt,            lockedCols     = Ext.Array.toMap(lockedHeaderCt.items, 'headerId'),            normalCols     = Ext.Array.toMap(normalHeaderCt.items, 'headerId'),            locked         = [],            normal         = [],            lockedWidth    = 1,            length         = columns.length,            i, existing,            lockedDefault,            col;        for (i = 0; i < length; i++) {            col = columns[i];            lockedDefault = lockedCols[col.id];            existing = lockedDefault || normalCols[col.id];            if (existing) {                if (existing.applyColumnState) {                    existing.applyColumnState(col);                }                if (existing.locked === undefined) {                    existing.locked = !!lockedDefault;                }                if (existing.locked) {                    locked.push(existing);                    if (!existing.hidden && typeof existing.width == 'number') {                        lockedWidth += existing.width;                    }                } else {                    normal.push(existing);                }            }        }        // state and config must have the same columns (compare counts for now):        if (locked.length + normal.length == lockedHeaderCt.items.getCount() + normalHeaderCt.items.getCount()) {            lockedHeaderCt.removeAll(false);            normalHeaderCt.removeAll(false);            lockedHeaderCt.add(locked);            normalHeaderCt.add(normal);            lockedGrid.setWidth(lockedWidth);        }    },    getColumnsState: function () {        var me = this,            locked = me.lockedGrid.headerCt.getColumnsState(),            normal = me.normalGrid.headerCt.getColumnsState();        return locked.concat(normal);    },    // we want to totally override the reconfigure behaviour here, since we're creating 2 sub-grids    reconfigureLockable: function(store, columns) {        var me = this,            lockedGrid = me.lockedGrid,            normalGrid = me.normalGrid;        if (columns) {            Ext.suspendLayouts();            lockedGrid.headerCt.removeAll();            normalGrid.headerCt.removeAll();            columns = me.processColumns(columns);            lockedGrid.setWidth(columns.lockedWidth);            lockedGrid.headerCt.add(columns.locked.items);            normalGrid.headerCt.add(columns.normal.items);            Ext.resumeLayouts(true);        }        if (store) {            store = Ext.data.StoreManager.lookup(store);            me.store = store;            lockedGrid.bindStore(store);            normalGrid.bindStore(store);        } else {            lockedGrid.getView().refresh();            normalGrid.getView().refresh();        }    },<span id='Ext-grid-Lockable-method-cloneFeatures'>    /**</span>     * Clones items in the features array if they are instantiated Features. If an item     * is just a feature config, it leaves it alone.     *     * This is so that features can be replicated on both sides of the LockingView     *     */    cloneFeatures: function() {        var me = this,            features = me.features,            feature,            i = 0, len;                if (features) {            len = features.length;            for (; i < len; i++) {                feature = features[i];                if (feature.isFeature) {                    features[i] = feature.clone();                }            }        }    },<span id='Ext-grid-Lockable-method-clonePlugins'>    /**</span>     * Clones items in the plugins array if they are instantiated Plugins. If an item     * is just a plugin config, it leaves it alone.     *     * This is so that plugins can be replicated on both sides of the LockingView     *     */    clonePlugins: function() {        var me = this,            plugins = me.plugins,            plugin,            i = 0, len;                if (plugins) {            len = plugins.length;            for (; i < len; i++) {                plugin = plugins[i];                if (typeof plugin.init === 'function') {                    plugins[i] = plugin.clone();                }            }        }    }}, function() {    this.borrow(Ext.view.Table, ['constructFeatures']);    this.borrow(Ext.AbstractComponent, ['constructPlugins', 'constructPlugin']);});</pre></body></html>
 |