static/ext/src/grid/locking/Lockable.js
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,
            scrollbarHeight = Ext.getScrollbarSize().width,
            store = me.store = Ext.StoreManager.lookup(me.store),

            // share the selection model
            selModel = me.getSelectionModel(),

            // Hash of {lockedFeatures:[],normalFeatures:[]}
            allFeatures,

            // Hash of {topPlugins:[],lockedPlugins:[],normalPlugins:[]}
            allPlugins,

            lockedGrid,
            normalGrid,
            i,
            columns,
            lockedHeaderCt,
            normalHeaderCt,
            lockedView,
            normalView,
            listeners;

        allFeatures = me.constructLockableFeatures();

        // This is just a "shell" Panel which acts as a Container for the two grids and must not use the features
        if (me.features) {
            me.features = null;
        }

        // Distribute plugins to whichever Component needs them
        allPlugins = me.constructLockablePlugins();
        me.plugins = allPlugins.topPlugins;

        lockedGrid = Ext.apply({
            id: me.id + '-locked',
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(true),
            store: store,
            scrollerOwner: false,
            // Lockable does NOT support animations for Tree
            // Because the right side is just a grid, and the grid view doen't animate bulk insertions/removals
            animate: false,
            // If the OS does not show a space-taking scrollbar, the locked view can be overflow-y:auto
            scroll: scrollbarHeight ? false : 'vertical',
            selModel: selModel,
            border: false,
            cls: me.lockedGridCls,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.lockedFeatures,
            plugins: allPlugins.lockedPlugins
        }, me.lockedGridConfig);

        normalGrid = Ext.apply({
            id: me.id + '-normal',
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(),
            store: store,
            scrollerOwner: false,
            selModel: selModel,
            border: false,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.normalFeatures,
            plugins: allPlugins.normalPlugins
        }, me.normalGridConfig);

        me.addCls(Ext.baseCSSPrefix + 'grid-locked');

        // Copy appropriate configurations to the respective aggregated tablepanel instances.
        // Pass 4th param true to NOT exclude those settings on our prototype.
        // Delete them from the master tablepanel.
        Ext.copyTo(normalGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(normalGrid, me, me.normalCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true);
        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(
            /**
             * @event processcolumns
             * Fires when the configured (or **reconfigured**) column set is split into two depending on the {@link Ext.grid.column.Column#locked locked} flag.
             * @param {Ext.grid.column.Column[]} lockedColumns The locked columns.
             * @param {Ext.grid.column.Column[]} normalColumns The normal columns.
             */
            'processcolumns',

            /**
             * @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',

            /**
             * @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']);

        columns = me.processColumns(me.columns);

        // Locked grid has a right border. Locked grid must shrinkwrap columns with no horiontal scroll, so
        // we must increment by the border width: 1px. TODO: Use shrinkWrapDock on the locked grid when it works.
        lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0) + (columns.locked.items.length ? 1 : 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;
        lockedGrid.viewConfig.loadMask = false;

        // Padding below locked grid body only if there are scrollbars on this system
        // This takes the place of any spacer element.
        // Important: The height has to be postprocessed after render to override
        // the inline border styles injected by automatic component border settings.
        if (scrollbarHeight) {
            lockedGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight +
                'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || '');
        }

        normalGrid.viewConfig = me.normalViewConfig || {};
        normalGrid.viewConfig.loadMask = false;

        //<debug>
        if (me.viewConfig && me.viewConfig.id) {
            Ext.log.warn('id specified on Lockable viewConfig, it will be shared between both views: "' + me.viewConfig.id + '"');
        }
        //</debug>

        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);

        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);

        if (me.isTree) {
            // Tree must not animate because the partner grid is unable to animate
            me.lockedGrid.getView().animate = false;

            // When this is a locked tree, the normal side is just a gridpanel, so needs the flat NodeStore
            normalGrid.store = me.lockedGrid.view.store;

            // To stay in sync with the tree side; trees don't use deferInitialRefresh by default. Overrideable by user config.
            normalGrid.deferRowRender = false;

            // Match configs between sides
            normalGrid.viewConfig.stripeRows = me.lockedGrid.view.stripeRows;
            normalGrid.rowLines = me.lockedGrid.rowLines;
        }

        // Set up a bidirectional relationship between the two sides of the locked view
        lockedView = me.lockedGrid.getView();
        normalGrid.viewConfig.lockingPartner = lockedView;
        me.normalGrid = Ext.ComponentManager.create(normalGrid);
        lockedView.lockingPartner = normalView = me.normalGrid.getView();

        me.view = new Ext.grid.locking.View({
            loadingText: normalView.loadingText,
            loadingCls: normalView.loadingCls,
            loadingUseMsg: normalView.loadingUseMsg,
            loadMask: me.loadMask !== false,
            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
        // Also we need to postprocess the border width because of inline border setting styles.
        // The locked grid needs a bottom border to match with any scrollbar present in the normal grid .
        // Keep locked section's bottom border width synched
        if (scrollbarHeight) {
            me.lockedGrid.on({
                afterlayout: me.afterLockedViewLayout,
                scope: me
            });

            // Ensure the overflow flags have been calculated from the various overflow configs
            lockedView.getOverflowStyle();

            // If the locked view is configured to scoll vertically, force the columns to fit initially.
            if (lockedView.scrollFlags.y) {
                me.lockedGrid.headerCt.forceFit = true;
            }
            // If the locked view is not configured to scoll vertically, we must listen for mousewheel events to scroll it.
            else {
                listeners.mousewheel = {
                    fn: me.onLockedViewMouseWheel,
                    element: 'el',
                    scope: me
                };
            }
        }
        lockedView.on(listeners);

        // Set up listeners for the normal view
        listeners = {
            scroll: {
                fn: me.onNormalViewScroll,
                element: 'el',
                scope: me
            },
            scope: me
        };
        normalView.on(listeners);

        lockedHeaderCt = me.lockedGrid.headerCt;
        normalHeaderCt = me.normalGrid.headerCt;

        // The top grid, and the LockingView both need to have a headerCt which is usable.
        // It is part of their private API that framework code uses when dealing with a grid or grid view
        me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(this);

        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;

        lockedHeaderCt.on({
            // buffer to wait until multiple adds have fired
            add: {
                buffer: 1,
                scope: me,
                fn: me.onLockedHeaderAdd
            },
            columnshow: me.onLockedHeaderShow,
            columnhide: me.onLockedHeaderHide,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.onLockedHeaderResize,
            scope: me
        });

        normalHeaderCt.on({
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });

        me.modifyHeaderCt();
        me.items = [me.lockedGrid, me.normalGrid];

        me.relayHeaderCtEvents(lockedHeaderCt);
        me.relayHeaderCtEvents(normalHeaderCt);

        // The top level Lockable container does not get bound to the store, so we need to programatically add the relayer so that
        // The filterchange state event is fired.
        me.storeRelayers = me.relayEvents(store, [
            /**
             * @event filterchange
             * @inheritdoc Ext.data.Store#filterchange
             */
            'filterchange'
        ]);

        me.layout = {
            type: 'hbox',
            align: 'stretch'
        };
    }
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,
            scrollbarHeight = Ext.getScrollbarSize().width,
            store = me.store = Ext.StoreManager.lookup(me.store),

            // share the selection model
            selModel = me.getSelectionModel(),

            // Hash of {lockedFeatures:[],normalFeatures:[]}
            allFeatures,

            // Hash of {topPlugins:[],lockedPlugins:[],normalPlugins:[]}
            allPlugins,

            lockedGrid,
            normalGrid,
            i,
            columns,
            lockedHeaderCt,
            normalHeaderCt,
            lockedView,
            normalView,
            listeners;

        allFeatures = me.constructLockableFeatures();

        // This is just a "shell" Panel which acts as a Container for the two grids and must not use the features
        if (me.features) {
            me.features = null;
        }

        // Distribute plugins to whichever Component needs them
        allPlugins = me.constructLockablePlugins();
        me.plugins = allPlugins.topPlugins;

        lockedGrid = Ext.apply({
            id: me.id + '-locked',
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(true),
            store: store,
            scrollerOwner: false,
            // Lockable does NOT support animations for Tree
            // Because the right side is just a grid, and the grid view doen't animate bulk insertions/removals
            animate: false,
            // If the OS does not show a space-taking scrollbar, the locked view can be overflow-y:auto
            scroll: scrollbarHeight ? false : 'vertical',
            selModel: selModel,
            border: false,
            cls: me.lockedGridCls,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.lockedFeatures,
            plugins: allPlugins.lockedPlugins
        }, me.lockedGridConfig);

        normalGrid = Ext.apply({
            id: me.id + '-normal',
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(),
            store: store,
            scrollerOwner: false,
            selModel: selModel,
            border: false,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.normalFeatures,
            plugins: allPlugins.normalPlugins
        }, me.normalGridConfig);

        me.addCls(Ext.baseCSSPrefix + 'grid-locked');

        // Copy appropriate configurations to the respective aggregated tablepanel instances.
        // Pass 4th param true to NOT exclude those settings on our prototype.
        // Delete them from the master tablepanel.
        Ext.copyTo(normalGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(normalGrid, me, me.normalCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true);
        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(
            /**
             * @event processcolumns
             * Fires when the configured (or **reconfigured**) column set is split into two depending on the {@link Ext.grid.column.Column#locked locked} flag.
             * @param {Ext.grid.column.Column[]} lockedColumns The locked columns.
             * @param {Ext.grid.column.Column[]} normalColumns The normal columns.
             */
            'processcolumns',

            /**
             * @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',

            /**
             * @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']);

        columns = me.processColumns(me.columns);

        // Locked grid has a right border. Locked grid must shrinkwrap columns with no horiontal scroll, so
        // we must increment by the border width: 1px. TODO: Use shrinkWrapDock on the locked grid when it works.
        lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0) + (columns.locked.items.length ? 1 : 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;
        lockedGrid.viewConfig.loadMask = false;

        // Padding below locked grid body only if there are scrollbars on this system
        // This takes the place of any spacer element.
        // Important: The height has to be postprocessed after render to override
        // the inline border styles injected by automatic component border settings.
        if (scrollbarHeight) {
            lockedGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight +
                'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || '');
        }

        normalGrid.viewConfig = me.normalViewConfig || {};
        normalGrid.viewConfig.loadMask = false;

        //<debug>
        if (me.viewConfig && me.viewConfig.id) {
            Ext.log.warn('id specified on Lockable viewConfig, it will be shared between both views: "' + me.viewConfig.id + '"');
        }
        //</debug>

        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);

        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);

        if (me.isTree) {
            // Tree must not animate because the partner grid is unable to animate
            me.lockedGrid.getView().animate = false;

            // When this is a locked tree, the normal side is just a gridpanel, so needs the flat NodeStore
            normalGrid.store = me.lockedGrid.view.store;

            // To stay in sync with the tree side; trees don't use deferInitialRefresh by default. Overrideable by user config.
            normalGrid.deferRowRender = false;

            // Match configs between sides
            normalGrid.viewConfig.stripeRows = me.lockedGrid.view.stripeRows;
            normalGrid.rowLines = me.lockedGrid.rowLines;
        }

        // Set up a bidirectional relationship between the two sides of the locked view
        lockedView = me.lockedGrid.getView();
        normalGrid.viewConfig.lockingPartner = lockedView;
        me.normalGrid = Ext.ComponentManager.create(normalGrid);
        lockedView.lockingPartner = normalView = me.normalGrid.getView();

        me.view = new Ext.grid.locking.View({
            loadingText: normalView.loadingText,
            loadingCls: normalView.loadingCls,
            loadingUseMsg: normalView.loadingUseMsg,
            loadMask: me.loadMask !== false,
            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
        // Also we need to postprocess the border width because of inline border setting styles.
        // The locked grid needs a bottom border to match with any scrollbar present in the normal grid .
        // Keep locked section's bottom border width synched
        if (scrollbarHeight) {
            me.lockedGrid.on({
                afterlayout: me.afterLockedViewLayout,
                scope: me
            });

            // Ensure the overflow flags have been calculated from the various overflow configs
            lockedView.getOverflowStyle();

            // If the locked view is configured to scoll vertically, force the columns to fit initially.
            if (lockedView.scrollFlags.y) {
                me.lockedGrid.headerCt.forceFit = true;
            }
            // If the locked view is not configured to scoll vertically, we must listen for mousewheel events to scroll it.
            else {
                listeners.mousewheel = {
                    fn: me.onLockedViewMouseWheel,
                    element: 'el',
                    scope: me
                };
            }
        }
        lockedView.on(listeners);

        // Set up listeners for the normal view
        listeners = {
            scroll: {
                fn: me.onNormalViewScroll,
                element: 'el',
                scope: me
            },
            scope: me
        };
        normalView.on(listeners);

        lockedHeaderCt = me.lockedGrid.headerCt;
        normalHeaderCt = me.normalGrid.headerCt;

        // The top grid, and the LockingView both need to have a headerCt which is usable.
        // It is part of their private API that framework code uses when dealing with a grid or grid view
        me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(this);

        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;

        lockedHeaderCt.on({
            // buffer to wait until multiple adds have fired
            add: {
                buffer: 1,
                scope: me,
                fn: me.onLockedHeaderAdd
            },
            columnshow: me.onLockedHeaderShow,
            columnhide: me.onLockedHeaderHide,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.onLockedHeaderResize,
            scope: me
        });

        normalHeaderCt.on({
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });

        me.modifyHeaderCt();
        me.items = [me.lockedGrid, me.normalGrid];

        me.relayHeaderCtEvents(lockedHeaderCt);
        me.relayHeaderCtEvents(normalHeaderCt);

        // The top level Lockable container does not get bound to the store, so we need to programatically add the relayer so that
        // The filterchange state event is fired.
        me.storeRelayers = me.relayEvents(store, [
            /**
             * @event filterchange
             * @inheritdoc Ext.data.Store#filterchange
             */
            'filterchange'
        ]);

        me.layout = {
            type: 'hbox',
            align: 'stretch'
        };
    }
injectLockable will be invoked before initComponent's parent class implementation is called, so throughout this method this. are configurations