X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6e39d509471fe9b4e2660e0d1631b350d0c66f40..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/Table2.html diff --git a/docs/source/Table2.html b/docs/source/Table2.html new file mode 100644 index 00000000..58eb857e --- /dev/null +++ b/docs/source/Table2.html @@ -0,0 +1,1202 @@ +Sencha Documentation Project
/**
+ * @class Ext.panel.Table
+ * @extends Ext.panel.Panel
+ * @xtype tablepanel
+ * @private
+ * @author Nicolas Ferrero
+ * TablePanel is a private class and the basis of both TreePanel and GridPanel.
+ *
+ * TablePanel aggregates:
+ *
+ *  - a Selection Model
+ *  - a View
+ *  - a Store
+ *  - Scrollers
+ *  - Ext.grid.header.Container
+ *
+ */
+Ext.define('Ext.panel.Table', {
+    extend: 'Ext.panel.Panel',
+
+    alias: 'widget.tablepanel',
+
+    uses: [
+        'Ext.selection.RowModel',
+        'Ext.grid.Scroller',
+        'Ext.grid.header.Container',
+        'Ext.grid.Lockable'
+    ],
+
+    cls: Ext.baseCSSPrefix + 'grid',
+    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
+
+    layout: 'fit',
+    /**
+     * Boolean to indicate that a view has been injected into the panel.
+     * @property hasView
+     */
+    hasView: false,
+
+    // each panel should dictate what viewType and selType to use
+    viewType: null,
+    selType: 'rowmodel',
+
+    /**
+     * @cfg {Number} scrollDelta
+     * Number of pixels to scroll when scrolling with mousewheel.
+     * Defaults to 40.
+     */
+    scrollDelta: 40,
+
+    /**
+     * @cfg {String/Boolean} scroll
+     * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
+     * Defaults to true.
+     */
+    scroll: true,
+
+    /**
+     * @cfg {Array} columns
+     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
+     * column definition provides the header text for the column, and a definition of where the data for that column comes from.
+     */
+
+    /**
+     * @cfg {Boolean} forceFit
+     * Specify as <code>true</code> to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be
+     * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
+     */
+
+    /**
+     * @cfg {Boolean} hideHeaders
+     * Specify as <code>true</code> to hide the headers.
+     */
+
+    /**
+     * @cfg {Boolean} sortableColumns
+     * Defaults to true. Set to false to disable column sorting via clicking the
+     * header and via the Sorting menu items.
+     */
+    sortableColumns: true,
+
+    verticalScrollDock: 'right',
+    verticalScrollerType: 'gridscroller',
+
+    horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
+    verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
+
+    // private property used to determine where to go down to find views
+    // this is here to support locking.
+    scrollerOwner: true,
+
+    invalidateScrollerOnRefresh: true,
+    
+    enableColumnMove: true,
+    enableColumnResize: true,
+
+
+    initComponent: function() {
+        //<debug>
+        if (!this.viewType) {
+            Ext.Error.raise("You must specify a viewType config.");
+        }
+        if (!this.store) {
+            Ext.Error.raise("You must specify a store config");
+        }
+        if (this.headers) {
+            Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
+        }
+        //</debug>
+
+        var me          = this,
+            scroll      = me.scroll,
+            vertical    = false,
+            horizontal  = false,
+            headerCtCfg = me.columns || me.colModel,
+            i           = 0,
+            view,
+            border = me.border;
+
+        // Set our determinScrollbars method to reference a buffered call to determinScrollbars which fires on a 30ms buffer.
+        me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
+        me.injectView = Ext.Function.createBuffered(me.injectView, 30);
+
+        if (me.hideHeaders) {
+            border = false;
+        }
+
+        // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
+        // Either way, we extract a columns property referencing an array of Column definitions.
+        if (headerCtCfg instanceof Ext.grid.header.Container) {
+            me.headerCt = headerCtCfg;
+            me.headerCt.border = border;
+            me.columns = me.headerCt.items.items;
+        } else {
+            if (Ext.isArray(headerCtCfg)) {
+                headerCtCfg = {
+                    items: headerCtCfg,
+                    border: border
+                };
+            }
+            Ext.apply(headerCtCfg, {
+                forceFit: me.forceFit,
+                sortable: me.sortableColumns,
+                enableColumnMove: me.enableColumnMove,
+                enableColumnResize: me.enableColumnResize,
+                border:  border
+            });
+            me.columns = headerCtCfg.items;
+
+             // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
+             // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
+             if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
+                 me.self.mixin('lockable', Ext.grid.Lockable);
+                 me.injectLockable();
+             }
+        }
+
+        me.store = Ext.data.StoreManager.lookup(me.store);
+        me.addEvents(
+            /**
+             * @event scrollerhide
+             * Fires when a scroller is hidden
+             * @param {Ext.grid.Scroller} scroller
+             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
+             */
+            'scrollerhide',
+            /**
+             * @event scrollershow
+             * Fires when a scroller is shown
+             * @param {Ext.grid.Scroller} scroller
+             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
+             */
+            'scrollershow'
+        );
+
+        me.bodyCls = me.bodyCls || '';
+        me.bodyCls += (' ' + me.extraBodyCls);
+
+        // autoScroll is not a valid configuration
+        delete me.autoScroll;
+
+        // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
+        // than a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.
+        if (!me.hasView) {
+
+            // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
+            // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
+            if (!me.headerCt) {
+                me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
+            }
+
+            // Extract the array of Column objects
+            me.columns = me.headerCt.items.items;
+
+            if (me.hideHeaders) {
+                me.headerCt.height = 0;
+                me.headerCt.border = false;
+                me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
+                me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
+                // IE Quirks Mode fix
+                // If hidden configuration option was used, several layout calculations will be bypassed.
+                if (Ext.isIEQuirks) {
+                    me.headerCt.style = {
+                        display: 'none'
+                    };
+                }
+            }
+
+            // turn both on.
+            if (scroll === true || scroll === 'both') {
+                vertical = horizontal = true;
+            } else if (scroll === 'horizontal') {
+                horizontal = true;
+            } else if (scroll === 'vertical') {
+                vertical = true;
+            // All other values become 'none' or false.
+            } else {
+                me.headerCt.availableSpaceOffset = 0;
+            }
+
+            if (vertical) {
+                me.verticalScroller = me.verticalScroller || {};
+                Ext.applyIf(me.verticalScroller, {
+                    dock: me.verticalScrollDock,
+                    xtype: me.verticalScrollerType,
+                    store: me.store
+                });
+                me.verticalScroller = Ext.ComponentManager.create(me.verticalScroller);
+                me.mon(me.verticalScroller, {
+                    bodyscroll: me.onVerticalScroll,
+                    scope: me
+                });
+            }
+
+            if (horizontal) {
+                me.horizontalScroller = Ext.ComponentManager.create({
+                    xtype: 'gridscroller',
+                    section: me,
+                    dock: 'bottom',
+                    store: me.store
+                });
+                me.mon(me.horizontalScroller, {
+                    bodyscroll: me.onHorizontalScroll,
+                    scope: me
+                });
+            }
+
+            me.headerCt.on('columnresize', me.onHeaderResize, me);
+            me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
+            me.features = me.features || [];
+            me.dockedItems = me.dockedItems || [];
+            me.dockedItems.unshift(me.headerCt);
+            me.viewConfig = me.viewConfig || {};
+            me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
+
+            // AbstractDataView will look up a Store configured as an object
+            // getView converts viewConfig into a View instance
+            view = me.getView();
+
+            if (view) {
+                me.mon(view.store, {
+                    load: me.onStoreLoad,
+                    scope: me
+                });
+                me.mon(view, {
+                    refresh: {
+                        fn: this.onViewRefresh,
+                        scope: me,
+                        buffer: 50
+                    },
+                    itemupdate: me.onViewItemUpdate,
+                    scope: me
+                });
+                this.relayEvents(view, [
+                    /**
+                     * @event beforeitemmousedown
+                     * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmousedown',
+                    /**
+                     * @event beforeitemmouseup
+                     * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseup',
+                    /**
+                     * @event beforeitemmouseenter
+                     * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseenter',
+                    /**
+                     * @event beforeitemmouseleave
+                     * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseleave',
+                    /**
+                     * @event beforeitemclick
+                     * Fires before the click event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemclick',
+                    /**
+                     * @event beforeitemdblclick
+                     * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemdblclick',
+                    /**
+                     * @event beforeitemcontextmenu
+                     * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemcontextmenu',
+                    /**
+                     * @event itemmousedown
+                     * Fires when there is a mouse down on an item
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmousedown',
+                    /**
+                     * @event itemmouseup
+                     * Fires when there is a mouse up on an item
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseup',
+                    /**
+                     * @event itemmouseenter
+                     * Fires when the mouse enters an item.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseenter',
+                    /**
+                     * @event itemmouseleave
+                     * Fires when the mouse leaves an item.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseleave',
+                    /**
+                     * @event itemclick
+                     * Fires when an item is clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemclick',
+                    /**
+                     * @event itemdblclick
+                     * Fires when an item is double clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemdblclick',
+                    /**
+                     * @event itemcontextmenu
+                     * Fires when an item is right clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemcontextmenu',
+                    /**
+                     * @event beforecontainermousedown
+                     * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermousedown',
+                    /**
+                     * @event beforecontainermouseup
+                     * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseup',
+                    /**
+                     * @event beforecontainermouseover
+                     * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseover',
+                    /**
+                     * @event beforecontainermouseout
+                     * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseout',
+                    /**
+                     * @event beforecontainerclick
+                     * Fires before the click event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainerclick',
+                    /**
+                     * @event beforecontainerdblclick
+                     * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainerdblclick',
+                    /**
+                     * @event beforecontainercontextmenu
+                     * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainercontextmenu',
+                    /**
+                     * @event containermouseup
+                     * Fires when there is a mouse up on the container
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseup',
+                    /**
+                     * @event containermouseover
+                     * Fires when you move the mouse over the container.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseover',
+                    /**
+                     * @event containermouseout
+                     * Fires when you move the mouse out of the container.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseout',
+                    /**
+                     * @event containerclick
+                     * Fires when the container is clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containerclick',
+                    /**
+                     * @event containerdblclick
+                     * Fires when the container is double clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containerdblclick',
+                    /**
+                     * @event containercontextmenu
+                     * Fires when the container is right clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containercontextmenu',
+
+                    /**
+                     * @event selectionchange
+                     * Fires when the selected nodes change. Relayed event from the underlying selection model.
+                     * @param {Ext.view.View} this
+                     * @param {Array} selections Array of the selected nodes
+                     */
+                    'selectionchange',
+                    /**
+                     * @event beforeselect
+                     * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+                     * @param {Ext.view.View} this
+                     * @param {HTMLElement} node The node to be selected
+                     * @param {Array} selections Array of currently selected nodes
+                     */
+                    'beforeselect'
+                ]);
+            }
+        }
+        me.callParent(arguments);
+    },
+
+    // state management
+    initStateEvents: function(){
+        var events = this.stateEvents;
+        // push on stateEvents if they don't exist
+        Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
+            if (Ext.Array.indexOf(events, event)) {
+                events.push(event);
+            }
+        });
+        this.callParent();
+    },
+
+    getState: function(){
+        var state = {
+            columns: []
+        },
+        sorter = this.store.sorters.first();
+
+        this.headerCt.items.each(function(header){
+            state.columns.push({
+                id: header.headerId,
+                width: header.flex ? undefined : header.width,
+                hidden: header.hidden,
+                sortable: header.sortable
+            });
+        });
+
+        if (sorter) {
+            state.sort = {
+                property: sorter.property,
+                direction: sorter.direction
+            };
+        }
+        return state;
+    },
+
+    applyState: function(state) {
+        var headers = state.columns,
+            length = headers ? headers.length : 0,
+            headerCt = this.headerCt,
+            items = headerCt.items,
+            sorter = state.sort,
+            store = this.store,
+            i = 0,
+            index,
+            headerState,
+            header;
+
+        for (; i < length; ++i) {
+            headerState = headers[i];
+            header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
+            index = items.indexOf(header);
+            if (i !== index) {
+                headerCt.moveHeader(index, i);
+            }
+            header.sortable = headerState.sortable;
+            if (Ext.isDefined(headerState.width)) {
+                delete header.flex;
+                if (header.rendered) {
+                    header.setWidth(headerState.width);
+                } else {
+                    header.minWidth = header.width = headerState.width;
+                }
+            }
+            header.hidden = headerState.hidden;
+        }
+
+        if (sorter) {
+            if (store.remoteSort) {
+                store.sorters.add(Ext.create('Ext.util.Sorter', {
+                    property: sorter.property,
+                    direction: sorter.direction
+                }));
+            }
+            else {
+                store.sort(sorter.property, sorter.direction);
+            }
+        }
+    },
+
+    /**
+     * Returns the store associated with this Panel.
+     * @return {Ext.data.Store} The store
+     */
+    getStore: function(){
+        return this.store;
+    },
+
+    /**
+     * Gets the view for this panel.
+     * @return {Ext.view.Table}
+     */
+    getView: function() {
+        var me = this,
+            sm;
+
+        if (!me.view) {
+            sm = me.getSelectionModel();
+            me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
+                xtype: me.viewType,
+                store: me.store,
+                headerCt: me.headerCt,
+                selModel: sm,
+                features: me.features,
+                panel: me
+            }));
+            me.mon(me.view, {
+                uievent: me.processEvent,
+                scope: me
+            });
+            sm.view = me.view;
+            me.headerCt.view = me.view;
+            me.relayEvents(me.view, ['cellclick', 'celldblclick']);
+        }
+        return me.view;
+    },
+
+    /**
+     * @private
+     * @override
+     * autoScroll is never valid for all classes which extend TablePanel.
+     */
+    setAutoScroll: Ext.emptyFn,
+
+    // This method hijacks Ext.view.Table's el scroll method.
+    // This enables us to keep the virtualized scrollbars in sync
+    // with the view. It currently does NOT support animation.
+    elScroll: function(direction, distance, animate) {
+        var me = this,
+            scroller;
+
+        if (direction === "up" || direction === "left") {
+            distance = -distance;
+        }
+
+        if (direction === "down" || direction === "up") {
+            scroller = me.getVerticalScroller();
+            scroller.scrollByDeltaY(distance);
+        } else {
+            scroller = me.getHorizontalScroller();
+            scroller.scrollByDeltaX(distance);
+        }
+    },
+    
+    afterLayout: function() {
+        this.callParent(arguments);
+        this.injectView();
+    },
+    
+
+    /**
+     * @private
+     * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
+     * This is so we can add the View only after the initial size is known. This method is buffered 30ms.
+     */
+    injectView: function() {
+        if (!this.hasView && !this.collapsed) {
+            var me   = this,
+                view = me.getView();
+
+            me.hasView = true;
+            me.add(view);
+
+            // hijack the view el's scroll method
+            view.el.scroll = Ext.Function.bind(me.elScroll, me);
+            // We use to listen to document.body wheel events, but that's a
+            // little much. We scope just to the view now.
+            me.mon(view.el, {
+                mousewheel: me.onMouseWheel,
+                scope: me
+            });
+        }
+    },
+
+    afterExpand: function() {
+        this.callParent(arguments);
+        if (!this.hasView) {
+            this.injectView();
+        }
+    },
+
+    /**
+     * @private
+     * Process UI events from the view. Propagate them to whatever internal Components need to process them
+     * @param {String} type Event type, eg 'click'
+     * @param {TableView} view TableView Component
+     * @param {HtmlElement} cell Cell HtmlElement the event took place within
+     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
+     * @param {Number} cellIndex Cell index within the row
+     * @param {EventObject} e Original event
+     */
+    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
+        var me = this,
+            header;
+
+        if (cellIndex !== -1) {
+            header = me.headerCt.getGridColumns()[cellIndex];
+            return header.processEvent.apply(header, arguments);
+        }
+    },
+
+    /**
+     * Request a recalculation of scrollbars and put them in if they are needed.
+     */
+    determineScrollbars: function() {
+        var me = this,
+            viewElDom,
+            centerScrollWidth,
+            centerClientWidth,
+            scrollHeight,
+            clientHeight;
+
+        if (!me.collapsed && me.view && me.view.el) {
+            viewElDom = me.view.el.dom;
+            //centerScrollWidth = viewElDom.scrollWidth;
+            centerScrollWidth = me.headerCt.getFullWidth();
+            /**
+             * clientWidth often returns 0 in IE resulting in an
+             * infinity result, here we use offsetWidth bc there are
+             * no possible scrollbars and we don't care about margins
+             */
+            centerClientWidth = viewElDom.offsetWidth;
+            if (me.verticalScroller && me.verticalScroller.el) {
+                scrollHeight = me.verticalScroller.getSizeCalculation().height;
+            } else {
+                scrollHeight = viewElDom.scrollHeight;
+            }
+
+            clientHeight = viewElDom.clientHeight;
+
+            if (!me.collapsed && scrollHeight > clientHeight) {
+                me.showVerticalScroller();
+            } else {
+                me.hideVerticalScroller();
+            }
+
+            if (!me.collapsed && centerScrollWidth > (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
+                me.showHorizontalScroller();
+            } else {
+                me.hideHorizontalScroller();
+            }
+        }
+    },
+
+    onHeaderResize: function() {
+        if (this.view && this.view.rendered) {
+            this.determineScrollbars();
+            this.invalidateScroller();
+        }
+    },
+
+    /**
+     * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
+     */
+    hideHorizontalScroller: function() {
+        var me = this;
+
+        if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
+            me.verticalScroller.offsets.bottom = 0;
+            me.removeDocked(me.horizontalScroller, false);
+            me.removeCls(me.horizontalScrollerPresentCls);
+            me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
+        }
+
+    },
+
+    /**
+     * Show the horizontalScroller and add the horizontalScrollerPresentCls.
+     */
+    showHorizontalScroller: function() {
+        var me = this;
+
+        if (me.verticalScroller) {
+            me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
+        }
+        if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
+            me.addDocked(me.horizontalScroller);
+            me.addCls(me.horizontalScrollerPresentCls);
+            me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
+        }
+    },
+
+    /**
+     * Hide the verticalScroller and remove the verticalScrollerPresentCls.
+     */
+    hideVerticalScroller: function() {
+        var me = this,
+            headerCt = me.headerCt;
+
+        // only trigger a layout when reserveOffset is changing
+        if (headerCt && headerCt.layout.reserveOffset) {
+            headerCt.layout.reserveOffset = false;
+            headerCt.doLayout();
+        }
+        if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
+            me.removeDocked(me.verticalScroller, false);
+            me.removeCls(me.verticalScrollerPresentCls);
+            me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
+        }
+    },
+
+    /**
+     * Show the verticalScroller and add the verticalScrollerPresentCls.
+     */
+    showVerticalScroller: function() {
+        var me = this,
+            headerCt = me.headerCt;
+
+        // only trigger a layout when reserveOffset is changing
+        if (headerCt && !headerCt.layout.reserveOffset) {
+            headerCt.layout.reserveOffset = true;
+            headerCt.doLayout();
+        }
+        if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
+            me.addDocked(me.verticalScroller);
+            me.addCls(me.verticalScrollerPresentCls);
+            me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
+        }
+    },
+
+    /**
+     * Invalides scrollers that are present and forces a recalculation.
+     * (Not related to showing/hiding the scrollers)
+     */
+    invalidateScroller: function() {
+        var me = this,
+            vScroll = me.verticalScroller,
+            hScroll = me.horizontalScroller;
+
+        if (vScroll) {
+            vScroll.invalidate();
+        }
+        if (hScroll) {
+            hScroll.invalidate();
+        }
+    },
+
+    // refresh the view when a header moves
+    onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
+        this.view.refresh();
+    },
+
+    // Section onHeaderHide is invoked after view.
+    onHeaderHide: function(headerCt, header) {
+        this.invalidateScroller();
+    },
+
+    onHeaderShow: function(headerCt, header) {
+        this.invalidateScroller();
+    },
+
+    getVerticalScroller: function() {
+        return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
+    },
+
+    getHorizontalScroller: function() {
+        return this.getScrollerOwner().down('gridscroller[dock=bottom]');
+    },
+
+    onMouseWheel: function(e) {
+        var me = this,
+            browserEvent = e.browserEvent,
+            vertScroller = me.getVerticalScroller(),
+            horizScroller = me.getHorizontalScroller(),
+            scrollDelta = me.scrollDelta,
+            deltaY, deltaX,
+            vertScrollerEl, horizScrollerEl,
+            origScrollLeft, origScrollTop,
+            newScrollLeft, newScrollTop;
+
+        // Track original scroll values, so we can see if we've
+        // reached the end of our scroll height/width.
+        if (horizScroller) {
+            horizScrollerEl = horizScroller.el;
+            if (horizScrollerEl) {
+                origScrollLeft = horizScrollerEl.dom.scrollLeft;
+            }
+        }
+        if (vertScroller) {
+            vertScrollerEl = vertScroller.el;
+            if (vertScrollerEl) {
+                origScrollTop = vertScrollerEl.dom.scrollTop;
+            }
+        }
+
+        // Webkit Horizontal Axis
+        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {
+            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
+            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
+            if (horizScroller) {
+                newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
+            }
+            if (vertScroller) {
+                newScrollTop = vertScroller.scrollByDeltaY(deltaY);
+            }
+        } else {
+            // Gecko Horizontal Axis
+            if (browserEvent.axis && browserEvent.axis === 1) {
+                if (horizScroller) {
+                    deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
+                    newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
+                }
+            } else {
+                if (vertScroller) {
+
+                    deltaY = -(scrollDelta * e.getWheelDelta() / 3);
+                    newScrollTop = vertScroller.scrollByDeltaY(deltaY);
+                }
+            }
+        }
+
+        // If after given our delta, the scroller has not progressed, then we're
+        // at the end of our scroll range and shouldn't stop the browser event.
+        if ((deltaX !== 0 && newScrollLeft !== origScrollLeft) ||
+            (deltaY !== 0 && newScrollTop !== origScrollTop)) {
+            e.stopEvent();
+        }
+    },
+
+    /**
+     * @private
+     * Determine and invalidate scrollers on view refresh
+     */
+    onViewRefresh: function() {
+        if (Ext.isIE) {
+            this.syncCellHeight();
+        }
+        this.determineScrollbars();
+        if (this.invalidateScrollerOnRefresh) {
+            this.invalidateScroller();
+        }
+    },
+
+    onViewItemUpdate: function(record, index, tr) {
+        if (Ext.isIE) {
+            this.syncCellHeight([tr]);
+        }
+    },
+
+    // BrowserBug: IE will not stretch the td to fit the height of the entire
+    // tr, so manually sync cellheights on refresh and when an item has been
+    // updated.
+    syncCellHeight: function(trs) {
+        var me    = this,
+            i     = 0,
+            tds,
+            j, tdsLn,
+            tr, td,
+            trsLn,
+            rowHeights = [],
+            cellHeights,
+            cellClsSelector = ('.' + Ext.baseCSSPrefix + 'grid-cell');
+
+        trs   = trs || me.view.getNodes();
+        
+        trsLn = trs.length;
+        // Reading loop
+        for (; i < trsLn; i++) {
+            tr = trs[i];
+            tds = Ext.fly(tr).query(cellClsSelector);
+            tdsLn = tds.length;
+            cellHeights = [];
+            for (j = 0; j < tdsLn; j++) {
+                td = tds[j];
+                cellHeights.push(td.clientHeight);
+            }
+            rowHeights.push(Ext.Array.max(cellHeights));
+        }
+
+        // Setting loop
+        for (i = 0; i < trsLn; i++) {
+            tr = trs[i];
+            tdsLn = tr.childNodes.length;
+            for (j = 0; j < tdsLn; j++) {
+                td = Ext.fly(tr.childNodes[j]);
+                if (rowHeights[i]) {
+                    if (td.is(cellClsSelector)) {
+                        td.setHeight(rowHeights[i]);
+                    } else {
+                        td.down(cellClsSelector).setHeight(rowHeights[i]);
+                    }
+                }
+                
+            }
+        }
+    },
+
+    /**
+     * Sets the scrollTop of the TablePanel.
+     * @param {Number} deltaY
+     */
+    setScrollTop: function(top) {
+        var me               = this,
+            rootCmp          = me.getScrollerOwner(),
+            verticalScroller = me.getVerticalScroller();
+
+        rootCmp.virtualScrollTop = top;
+        if (verticalScroller) {
+            verticalScroller.setScrollTop(top);
+        }
+
+    },
+
+    getScrollerOwner: function() {
+        var rootCmp = this;
+        if (!this.scrollerOwner) {
+            rootCmp = this.up('[scrollerOwner]');
+        }
+        return rootCmp;
+    },
+
+    /**
+     * Scrolls the TablePanel by deltaY
+     * @param {Number} deltaY
+     */
+    scrollByDeltaY: function(deltaY) {
+        var rootCmp = this.getScrollerOwner(),
+            scrollerRight;
+        scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
+        if (scrollerRight) {
+            scrollerRight.scrollByDeltaY(deltaY);
+        }
+    },
+
+
+    /**
+     * Scrolls the TablePanel by deltaX
+     * @param {Number} deltaY
+     */
+    scrollByDeltaX: function(deltaX) {
+        this.horizontalScroller.scrollByDeltaX(deltaX);
+    },
+
+    /**
+     * Get left hand side marker for header resizing.
+     * @private
+     */
+    getLhsMarker: function() {
+        var me = this;
+
+        if (!me.lhsMarker) {
+            me.lhsMarker = Ext.core.DomHelper.append(me.el, {
+                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
+            }, true);
+        }
+        return me.lhsMarker;
+    },
+
+    /**
+     * Get right hand side marker for header resizing.
+     * @private
+     */
+    getRhsMarker: function() {
+        var me = this;
+
+        if (!me.rhsMarker) {
+            me.rhsMarker = Ext.core.DomHelper.append(me.el, {
+                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
+            }, true);
+        }
+        return me.rhsMarker;
+    },
+
+    /**
+     * Returns the selection model being used and creates it via the configuration
+     * if it has not been created already.
+     * @return {Ext.selection.Model} selModel
+     */
+    getSelectionModel: function(){
+        if (!this.selModel) {
+            this.selModel = {};
+        }
+
+        var mode = 'SINGLE',
+            type;
+        if (this.simpleSelect) {
+            mode = 'SIMPLE';
+        } else if (this.multiSelect) {
+            mode = 'MULTI';
+        }
+
+        Ext.applyIf(this.selModel, {
+            allowDeselect: this.allowDeselect,
+            mode: mode
+        });
+
+        if (!this.selModel.events) {
+            type = this.selModel.selType || this.selType;
+            this.selModel = Ext.create('selection.' + type, this.selModel);
+        }
+
+        if (!this.selModel.hasRelaySetup) {
+            this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
+            this.selModel.hasRelaySetup = true;
+        }
+
+        // lock the selection model if user
+        // has disabled selection
+        if (this.disableSelection) {
+            this.selModel.locked = true;
+        }
+        return this.selModel;
+    },
+
+    onVerticalScroll: function(event, target) {
+        var owner = this.getScrollerOwner(),
+            items = owner.query('tableview'),
+            i = 0,
+            len = items.length;
+
+        for (; i < len; i++) {
+            items[i].el.dom.scrollTop = target.scrollTop;
+        }
+    },
+
+    onHorizontalScroll: function(event, target) {
+        var owner = this.getScrollerOwner(),
+            items = owner.query('tableview'),
+            i = 0,
+            len = items.length,
+            center,
+            centerEl,
+            centerScrollWidth,
+            centerClientWidth,
+            width;
+
+        center = items[1] || items[0];
+        centerEl = center.el.dom;
+        centerScrollWidth = centerEl.scrollWidth;
+        centerClientWidth = centerEl.offsetWidth;
+        width = this.horizontalScroller.getWidth();
+
+        centerEl.scrollLeft = target.scrollLeft;
+        this.headerCt.el.dom.scrollLeft = target.scrollLeft;
+    },
+
+    // template method meant to be overriden
+    onStoreLoad: Ext.emptyFn,
+
+    getEditorParent: function() {
+        return this.body;
+    },
+
+    bindStore: function(store) {
+        var me = this;
+        me.store = store;
+        me.getView().bindStore(store);
+    },
+
+    reconfigure: function(store, columns) {
+        var me = this;
+
+        if (me.lockable) {
+            me.reconfigureLockable(store, columns);
+            return;
+        }
+
+        if (columns) {
+            me.headerCt.removeAll();
+            me.headerCt.add(columns);
+        }
+        if (store) {
+            store = Ext.StoreManager.lookup(store);
+            me.bindStore(store);
+        } else {
+            me.getView().refresh();
+        }
+    },
+
+    afterComponentLayout: function() {
+        this.callParent(arguments);
+        this.determineScrollbars();
+        this.invalidateScroller();
+    }
+});
\ No newline at end of file