Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Table2.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-panel-Table'>/**
19 </span> * @author Nicolas Ferrero
20  *
21  * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}.
22  *
23  * TablePanel aggregates:
24  *
25  *  - a Selection Model
26  *  - a View
27  *  - a Store
28  *  - Scrollers
29  *  - Ext.grid.header.Container
30  */
31 Ext.define('Ext.panel.Table', {
32     extend: 'Ext.panel.Panel',
33
34     alias: 'widget.tablepanel',
35
36     uses: [
37         'Ext.selection.RowModel',
38         'Ext.grid.Scroller',
39         'Ext.grid.header.Container',
40         'Ext.grid.Lockable'
41     ],
42
43     extraBaseCls: Ext.baseCSSPrefix + 'grid',
44     extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
45
46     layout: 'fit',
47 <span id='Ext-panel-Table-property-hasView'>    /**
48 </span>     * @property {Boolean} hasView
49      * True to indicate that a view has been injected into the panel.
50      */
51     hasView: false,
52
53     // each panel should dictate what viewType and selType to use
54 <span id='Ext-panel-Table-cfg-viewType'>    /**
55 </span>     * @cfg {String} viewType
56      * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid}
57      * and to 'treeview' by {@link Ext.tree.Panel Tree}.
58      */
59     viewType: null,
60
61 <span id='Ext-panel-Table-cfg-viewConfig'>    /**
62 </span>     * @cfg {Object} viewConfig
63      * A config object that will be applied to the grid's UI view. Any of the config options available for
64      * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.
65      */
66
67 <span id='Ext-panel-Table-cfg-view'>    /**
68 </span>     * @cfg {Ext.view.Table} view
69      * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to
70      * view (instead of creating an entire View instance).
71      */
72
73 <span id='Ext-panel-Table-cfg-selType'>    /**
74 </span>     * @cfg {String} selType
75      * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just
76      * a config object or nothing at all given in {@link #selModel} config.
77      */
78     selType: 'rowmodel',
79
80 <span id='Ext-panel-Table-cfg-selModel'>    /**
81 </span>     * @cfg {Ext.selection.Model/Object} selModel
82      * A {@link Ext.selection.Model selection model} instance or config object.  In latter case the {@link #selType}
83      * config option determines to which type of selection model this config is applied.
84      */
85
86 <span id='Ext-panel-Table-cfg-multiSelect'>    /**
87 </span>     * @cfg {Boolean} multiSelect
88      * True to enable 'MULTI' selection mode on selection model. See {@link Ext.selection.Model#mode}.
89      */
90
91 <span id='Ext-panel-Table-cfg-simpleSelect'>    /**
92 </span>     * @cfg {Boolean} simpleSelect
93      * True to enable 'SIMPLE' selection mode on selection model. See {@link Ext.selection.Model#mode}.
94      */
95
96 <span id='Ext-panel-Table-cfg-store'>    /**
97 </span>     * @cfg {Ext.data.Store} store (required)
98      * The {@link Ext.data.Store Store} the grid should use as its data source.
99      */
100
101 <span id='Ext-panel-Table-cfg-scrollDelta'>    /**
102 </span>     * @cfg {Number} scrollDelta
103      * Number of pixels to scroll when scrolling with mousewheel.
104      */
105     scrollDelta: 40,
106
107 <span id='Ext-panel-Table-cfg-scroll'>    /**
108 </span>     * @cfg {String/Boolean} scroll
109      * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.
110      * True implies 'both'. False implies 'none'.
111      */
112     scroll: true,
113
114 <span id='Ext-panel-Table-cfg-columns'>    /**
115 </span>     * @cfg {Ext.grid.column.Column[]} columns
116      * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this
117      * grid. Each column definition provides the header text for the column, and a definition of where the data for that
118      * column comes from.
119      */
120
121 <span id='Ext-panel-Table-cfg-forceFit'>    /**
122 </span>     * @cfg {Boolean} forceFit
123      * Ttrue to force the columns to fit into the available width. Headers are first sized according to configuration,
124      * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire
125      * content width is used.
126      */
127
128 <span id='Ext-panel-Table-cfg-features'>    /**
129 </span>     * @cfg {Ext.grid.feature.Feature[]} features
130      * An array of grid Features to be added to this grid. See {@link Ext.grid.feature.Feature} for usage.
131      */
132
133 <span id='Ext-panel-Table-cfg-hideHeaders'>    /**
134 </span>     * @cfg {Boolean} [hideHeaders=false]
135      * True to hide column headers.
136      */
137
138 <span id='Ext-panel-Table-cfg-deferRowRender'>    /**
139 </span>     * @cfg {Boolean} deferRowRender
140      * Defaults to true to enable deferred row rendering.
141      *
142      * This allows the View to execute a refresh quickly, with the expensive update of the row structure deferred so
143      * that layouts with GridPanels appear, and lay out more quickly.
144      */
145
146      deferRowRender: true,
147      
148 <span id='Ext-panel-Table-cfg-sortableColumns'>    /**
149 </span>     * @cfg {Boolean} sortableColumns
150      * False to disable column sorting via clicking the header and via the Sorting menu items.
151      */
152     sortableColumns: true,
153
154 <span id='Ext-panel-Table-cfg-enableLocking'>    /**
155 </span>     * @cfg {Boolean} [enableLocking=false]
156      * True to enable locking support for this grid. Alternatively, locking will also be automatically
157      * enabled if any of the columns in the column configuration contain the locked config option.
158      */
159     enableLocking: false,
160
161     verticalScrollDock: 'right',
162     verticalScrollerType: 'gridscroller',
163
164     horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
165     verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
166
167     // private property used to determine where to go down to find views
168     // this is here to support locking.
169     scrollerOwner: true,
170
171     invalidateScrollerOnRefresh: true,
172
173 <span id='Ext-panel-Table-cfg-enableColumnMove'>    /**
174 </span>     * @cfg {Boolean} enableColumnMove
175      * False to disable column dragging within this grid.
176      */
177     enableColumnMove: true,
178
179 <span id='Ext-panel-Table-cfg-enableColumnResize'>    /**
180 </span>     * @cfg {Boolean} enableColumnResize
181      * False to disable column resizing within this grid.
182      */
183     enableColumnResize: true,
184
185 <span id='Ext-panel-Table-cfg-enableColumnHide'>    /**
186 </span>     * @cfg {Boolean} enableColumnHide
187      * False to disable column hiding within this grid.
188      */
189     enableColumnHide: true,
190
191     initComponent: function() {
192         //&lt;debug&gt;
193         if (!this.viewType) {
194             Ext.Error.raise(&quot;You must specify a viewType config.&quot;);
195         }
196         if (this.headers) {
197             Ext.Error.raise(&quot;The headers config is not supported. Please specify columns instead.&quot;);
198         }
199         //&lt;/debug&gt;
200
201         var me          = this,
202             scroll      = me.scroll,
203             vertical    = false,
204             horizontal  = false,
205             headerCtCfg = me.columns || me.colModel,
206             i           = 0,
207             view,
208             border = me.border;
209
210         if (me.hideHeaders) {
211             border = false;
212         }
213
214         // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.
215         me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
216
217         // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
218         // Either way, we extract a columns property referencing an array of Column definitions.
219         if (headerCtCfg instanceof Ext.grid.header.Container) {
220             me.headerCt = headerCtCfg;
221             me.headerCt.border = border;
222             me.columns = me.headerCt.items.items;
223         } else {
224             if (Ext.isArray(headerCtCfg)) {
225                 headerCtCfg = {
226                     items: headerCtCfg,
227                     border: border
228                 };
229             }
230             Ext.apply(headerCtCfg, {
231                 forceFit: me.forceFit,
232                 sortable: me.sortableColumns,
233                 enableColumnMove: me.enableColumnMove,
234                 enableColumnResize: me.enableColumnResize,
235                 enableColumnHide: me.enableColumnHide,
236                 border:  border
237             });
238             me.columns = headerCtCfg.items;
239
240              // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
241              // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
242              if (me.enableLocking || Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
243                  me.self.mixin('lockable', Ext.grid.Lockable);
244                  me.injectLockable();
245              }
246         }
247
248         me.addEvents(
249 <span id='Ext-panel-Table-event-reconfigure'>            /**
250 </span>             * @event reconfigure
251              * Fires after a reconfigure.
252              * @param {Ext.panel.Table} this
253              */
254             'reconfigure',
255 <span id='Ext-panel-Table-event-viewready'>            /**
256 </span>             * @event viewready
257              * Fires when the grid view is available (use this for selecting a default row).
258              * @param {Ext.panel.Table} this
259              */
260             'viewready',
261 <span id='Ext-panel-Table-event-scrollerhide'>            /**
262 </span>             * @event scrollerhide
263              * Fires when a scroller is hidden.
264              * @param {Ext.grid.Scroller} scroller
265              * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
266              */
267             'scrollerhide',
268 <span id='Ext-panel-Table-event-scrollershow'>            /**
269 </span>             * @event scrollershow
270              * Fires when a scroller is shown.
271              * @param {Ext.grid.Scroller} scroller
272              * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
273              */
274             'scrollershow'
275         );
276
277         me.bodyCls = me.bodyCls || '';
278         me.bodyCls += (' ' + me.extraBodyCls);
279         
280         me.cls = me.cls || '';
281         me.cls += (' ' + me.extraBaseCls);
282
283         // autoScroll is not a valid configuration
284         delete me.autoScroll;
285
286         // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
287         // 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.
288         if (!me.hasView) {
289
290             // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
291             // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
292             if (!me.headerCt) {
293                 me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
294             }
295
296             // Extract the array of Column objects
297             me.columns = me.headerCt.items.items;
298
299             if (me.hideHeaders) {
300                 me.headerCt.height = 0;
301                 me.headerCt.border = false;
302                 me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
303                 me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
304                 // IE Quirks Mode fix
305                 // If hidden configuration option was used, several layout calculations will be bypassed.
306                 if (Ext.isIEQuirks) {
307                     me.headerCt.style = {
308                         display: 'none'
309                     };
310                 }
311             }
312
313             // turn both on.
314             if (scroll === true || scroll === 'both') {
315                 vertical = horizontal = true;
316             } else if (scroll === 'horizontal') {
317                 horizontal = true;
318             } else if (scroll === 'vertical') {
319                 vertical = true;
320             // All other values become 'none' or false.
321             } else {
322                 me.headerCt.availableSpaceOffset = 0;
323             }
324
325             if (vertical) {
326                 me.verticalScroller = Ext.ComponentManager.create(me.initVerticalScroller());
327                 me.mon(me.verticalScroller, {
328                     bodyscroll: me.onVerticalScroll,
329                     scope: me
330                 });
331             }
332
333             if (horizontal) {
334                 me.horizontalScroller = Ext.ComponentManager.create(me.initHorizontalScroller());
335                 me.mon(me.horizontalScroller, {
336                     bodyscroll: me.onHorizontalScroll,
337                     scope: me
338                 });
339             }
340
341             me.headerCt.on('resize', me.onHeaderResize, me);
342             me.relayHeaderCtEvents(me.headerCt);
343             me.features = me.features || [];
344             if (!Ext.isArray(me.features)) {
345                 me.features = [me.features];
346             }
347             me.dockedItems = me.dockedItems || [];
348             me.dockedItems.unshift(me.headerCt);
349             me.viewConfig = me.viewConfig || {};
350             me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
351
352             // AbstractDataView will look up a Store configured as an object
353             // getView converts viewConfig into a View instance
354             view = me.getView();
355
356             view.on({
357                 afterrender: function () {
358                     // hijack the view el's scroll method
359                     view.el.scroll = Ext.Function.bind(me.elScroll, me);
360                     // We use to listen to document.body wheel events, but that's a
361                     // little much. We scope just to the view now.
362                     me.mon(view.el, {
363                         mousewheel: me.onMouseWheel,
364                         scope: me
365                     });
366                 },
367                 single: true
368             });
369             me.items = [view];
370             me.hasView = true;
371
372             me.mon(view.store, {
373                 load: me.onStoreLoad,
374                 scope: me
375             });
376             me.mon(view, {
377                 viewReady: me.onViewReady,
378                 resize: me.onViewResize,
379                 refresh: {
380                     fn: me.onViewRefresh,
381                     scope: me,
382                     buffer: 50
383                 },
384                 scope: me
385             });
386             this.relayEvents(view, [
387 <span id='Ext-panel-Table-event-beforeitemmousedown'>                /**
388 </span>                 * @event beforeitemmousedown
389                  * @alias Ext.view.View#beforeitemmousedown
390                  */
391                 'beforeitemmousedown',
392 <span id='Ext-panel-Table-event-beforeitemmouseup'>                /**
393 </span>                 * @event beforeitemmouseup
394                  * @alias Ext.view.View#beforeitemmouseup
395                  */
396                 'beforeitemmouseup',
397 <span id='Ext-panel-Table-event-beforeitemmouseenter'>                /**
398 </span>                 * @event beforeitemmouseenter
399                  * @alias Ext.view.View#beforeitemmouseenter
400                  */
401                 'beforeitemmouseenter',
402 <span id='Ext-panel-Table-event-beforeitemmouseleave'>                /**
403 </span>                 * @event beforeitemmouseleave
404                  * @alias Ext.view.View#beforeitemmouseleave
405                  */
406                 'beforeitemmouseleave',
407 <span id='Ext-panel-Table-event-beforeitemclick'>                /**
408 </span>                 * @event beforeitemclick
409                  * @alias Ext.view.View#beforeitemclick
410                  */
411                 'beforeitemclick',
412 <span id='Ext-panel-Table-event-beforeitemdblclick'>                /**
413 </span>                 * @event beforeitemdblclick
414                  * @alias Ext.view.View#beforeitemdblclick
415                  */
416                 'beforeitemdblclick',
417 <span id='Ext-panel-Table-event-beforeitemcontextmenu'>                /**
418 </span>                 * @event beforeitemcontextmenu
419                  * @alias Ext.view.View#beforeitemcontextmenu
420                  */
421                 'beforeitemcontextmenu',
422 <span id='Ext-panel-Table-event-itemmousedown'>                /**
423 </span>                 * @event itemmousedown
424                  * @alias Ext.view.View#itemmousedown
425                  */
426                 'itemmousedown',
427 <span id='Ext-panel-Table-event-itemmouseup'>                /**
428 </span>                 * @event itemmouseup
429                  * @alias Ext.view.View#itemmouseup
430                  */
431                 'itemmouseup',
432 <span id='Ext-panel-Table-event-itemmouseenter'>                /**
433 </span>                 * @event itemmouseenter
434                  * @alias Ext.view.View#itemmouseenter
435                  */
436                 'itemmouseenter',
437 <span id='Ext-panel-Table-event-itemmouseleave'>                /**
438 </span>                 * @event itemmouseleave
439                  * @alias Ext.view.View#itemmouseleave
440                  */
441                 'itemmouseleave',
442 <span id='Ext-panel-Table-event-itemclick'>                /**
443 </span>                 * @event itemclick
444                  * @alias Ext.view.View#itemclick
445                  */
446                 'itemclick',
447 <span id='Ext-panel-Table-event-itemdblclick'>                /**
448 </span>                 * @event itemdblclick
449                  * @alias Ext.view.View#itemdblclick
450                  */
451                 'itemdblclick',
452 <span id='Ext-panel-Table-event-itemcontextmenu'>                /**
453 </span>                 * @event itemcontextmenu
454                  * @alias Ext.view.View#itemcontextmenu
455                  */
456                 'itemcontextmenu',
457 <span id='Ext-panel-Table-event-beforecontainermousedown'>                /**
458 </span>                 * @event beforecontainermousedown
459                  * @alias Ext.view.View#beforecontainermousedown
460                  */
461                 'beforecontainermousedown',
462 <span id='Ext-panel-Table-event-beforecontainermouseup'>                /**
463 </span>                 * @event beforecontainermouseup
464                  * @alias Ext.view.View#beforecontainermouseup
465                  */
466                 'beforecontainermouseup',
467 <span id='Ext-panel-Table-event-beforecontainermouseover'>                /**
468 </span>                 * @event beforecontainermouseover
469                  * @alias Ext.view.View#beforecontainermouseover
470                  */
471                 'beforecontainermouseover',
472 <span id='Ext-panel-Table-event-beforecontainermouseout'>                /**
473 </span>                 * @event beforecontainermouseout
474                  * @alias Ext.view.View#beforecontainermouseout
475                  */
476                 'beforecontainermouseout',
477 <span id='Ext-panel-Table-event-beforecontainerclick'>                /**
478 </span>                 * @event beforecontainerclick
479                  * @alias Ext.view.View#beforecontainerclick
480                  */
481                 'beforecontainerclick',
482 <span id='Ext-panel-Table-event-beforecontainerdblclick'>                /**
483 </span>                 * @event beforecontainerdblclick
484                  * @alias Ext.view.View#beforecontainerdblclick
485                  */
486                 'beforecontainerdblclick',
487 <span id='Ext-panel-Table-event-beforecontainercontextmenu'>                /**
488 </span>                 * @event beforecontainercontextmenu
489                  * @alias Ext.view.View#beforecontainercontextmenu
490                  */
491                 'beforecontainercontextmenu',
492 <span id='Ext-panel-Table-event-containermouseup'>                /**
493 </span>                 * @event containermouseup
494                  * @alias Ext.view.View#containermouseup
495                  */
496                 'containermouseup',
497 <span id='Ext-panel-Table-event-containermouseover'>                /**
498 </span>                 * @event containermouseover
499                  * @alias Ext.view.View#containermouseover
500                  */
501                 'containermouseover',
502 <span id='Ext-panel-Table-event-containermouseout'>                /**
503 </span>                 * @event containermouseout
504                  * @alias Ext.view.View#containermouseout
505                  */
506                 'containermouseout',
507 <span id='Ext-panel-Table-event-containerclick'>                /**
508 </span>                 * @event containerclick
509                  * @alias Ext.view.View#containerclick
510                  */
511                 'containerclick',
512 <span id='Ext-panel-Table-event-containerdblclick'>                /**
513 </span>                 * @event containerdblclick
514                  * @alias Ext.view.View#containerdblclick
515                  */
516                 'containerdblclick',
517 <span id='Ext-panel-Table-event-containercontextmenu'>                /**
518 </span>                 * @event containercontextmenu
519                  * @alias Ext.view.View#containercontextmenu
520                  */
521                 'containercontextmenu',
522 <span id='Ext-panel-Table-event-selectionchange'>                /**
523 </span>                 * @event selectionchange
524                  * @alias Ext.selection.Model#selectionchange
525                  */
526                 'selectionchange',
527 <span id='Ext-panel-Table-event-beforeselect'>                /**
528 </span>                 * @event beforeselect
529                  * @alias Ext.selection.RowModel#beforeselect
530                  */
531                 'beforeselect',
532 <span id='Ext-panel-Table-event-select'>                /**
533 </span>                 * @event select
534                  * @alias Ext.selection.RowModel#select
535                  */
536                 'select',
537 <span id='Ext-panel-Table-event-beforedeselect'>                /**
538 </span>                 * @event beforedeselect
539                  * @alias Ext.selection.RowModel#beforedeselect
540                  */
541                 'beforedeselect',
542 <span id='Ext-panel-Table-event-deselect'>                /**
543 </span>                 * @event deselect
544                  * @alias Ext.selection.RowModel#deselect
545                  */
546                 'deselect'
547             ]);
548         }
549
550         me.callParent(arguments);
551     },
552     
553     onRender: function(){
554         var vScroll = this.verticalScroller,
555             hScroll = this.horizontalScroller;
556
557         if (vScroll) {
558             vScroll.ensureDimension();
559         }
560         if (hScroll) {
561             hScroll.ensureDimension();
562         }
563         this.callParent(arguments);    
564     },
565
566     // state management
567     initStateEvents: function(){
568         var events = this.stateEvents;
569         // push on stateEvents if they don't exist
570         Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
571             if (Ext.Array.indexOf(events, event)) {
572                 events.push(event);
573             }
574         });
575         this.callParent();
576     },
577
578 <span id='Ext-panel-Table-method-initHorizontalScroller'>    /**
579 </span>     * Returns the horizontal scroller config.
580      */
581     initHorizontalScroller: function () {
582         var me = this,
583             ret = {
584                 xtype: 'gridscroller',
585                 dock: 'bottom',
586                 section: me,
587                 store: me.store
588             };
589
590         return ret;
591     },
592
593 <span id='Ext-panel-Table-method-initVerticalScroller'>    /**
594 </span>     * Returns the vertical scroller config.
595      */
596     initVerticalScroller: function () {
597         var me = this,
598             ret = me.verticalScroller || {};
599
600         Ext.applyIf(ret, {
601             xtype: me.verticalScrollerType,
602             dock: me.verticalScrollDock,
603             store: me.store
604         });
605
606         return ret;
607     },
608
609     relayHeaderCtEvents: function (headerCt) {
610         this.relayEvents(headerCt, [
611 <span id='Ext-panel-Table-event-columnresize'>            /**
612 </span>             * @event columnresize
613              * @alias Ext.grid.header.Container#columnresize
614              */
615             'columnresize',
616 <span id='Ext-panel-Table-event-columnmove'>            /**
617 </span>             * @event columnmove
618              * @alias Ext.grid.header.Container#columnmove
619              */
620             'columnmove',
621 <span id='Ext-panel-Table-event-columnhide'>            /**
622 </span>             * @event columnhide
623              * @alias Ext.grid.header.Container#columnhide
624              */
625             'columnhide',
626 <span id='Ext-panel-Table-event-columnshow'>            /**
627 </span>             * @event columnshow
628              * @alias Ext.grid.header.Container#columnshow
629              */
630             'columnshow',
631 <span id='Ext-panel-Table-event-sortchange'>            /**
632 </span>             * @event sortchange
633              * @alias Ext.grid.header.Container#sortchange
634              */
635             'sortchange'
636         ]);
637     },
638
639     getState: function(){
640         var me = this,
641             state = me.callParent(),
642             sorter = me.store.sorters.first();
643
644         state.columns = (me.headerCt || me).getColumnsState();
645
646         if (sorter) {
647             state.sort = {
648                 property: sorter.property,
649                 direction: sorter.direction
650             };
651         }
652
653         return state;
654     },
655
656     applyState: function(state) {
657         var me = this,
658             sorter = state.sort,
659             store = me.store,
660             columns = state.columns;
661
662         delete state.columns;
663
664         // Ensure superclass has applied *its* state.
665         // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
666         me.callParent(arguments);
667
668         if (columns) {
669             (me.headerCt || me).applyColumnsState(columns);
670         }
671
672         if (sorter) {
673             if (store.remoteSort) {
674                 store.sorters.add(Ext.create('Ext.util.Sorter', {
675                     property: sorter.property,
676                     direction: sorter.direction
677                 }));
678             }
679             else {
680                 store.sort(sorter.property, sorter.direction);
681             }
682         }
683     },
684
685 <span id='Ext-panel-Table-method-getStore'>    /**
686 </span>     * Returns the store associated with this Panel.
687      * @return {Ext.data.Store} The store
688      */
689     getStore: function(){
690         return this.store;
691     },
692
693 <span id='Ext-panel-Table-method-getView'>    /**
694 </span>     * Gets the view for this panel.
695      * @return {Ext.view.Table}
696      */
697     getView: function() {
698         var me = this,
699             sm;
700
701         if (!me.view) {
702             sm = me.getSelectionModel();
703             me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
704                 deferInitialRefresh: me.deferRowRender,
705                 xtype: me.viewType,
706                 store: me.store,
707                 headerCt: me.headerCt,
708                 selModel: sm,
709                 features: me.features,
710                 panel: me
711             }));
712             me.mon(me.view, {
713                 uievent: me.processEvent,
714                 scope: me
715             });
716             sm.view = me.view;
717             me.headerCt.view = me.view;
718             me.relayEvents(me.view, ['cellclick', 'celldblclick']);
719         }
720         return me.view;
721     },
722
723 <span id='Ext-panel-Table-property-setAutoScroll'>    /**
724 </span>     * @private
725      * @override
726      * autoScroll is never valid for all classes which extend TablePanel.
727      */
728     setAutoScroll: Ext.emptyFn,
729
730     // This method hijacks Ext.view.Table's el scroll method.
731     // This enables us to keep the virtualized scrollbars in sync
732     // with the view. It currently does NOT support animation.
733     elScroll: function(direction, distance, animate) {
734         var me = this,
735             scroller;
736
737         if (direction === &quot;up&quot; || direction === &quot;left&quot;) {
738             distance = -distance;
739         }
740         
741         if (direction === &quot;down&quot; || direction === &quot;up&quot;) {
742             scroller = me.getVerticalScroller();
743             
744             //if the grid does not currently need a vertical scroller don't try to update it (EXTJSIV-3891)
745             if (scroller) {
746                 scroller.scrollByDeltaY(distance);
747             }
748         } else {
749             scroller = me.getHorizontalScroller();
750             
751             //if the grid does not currently need a horizontal scroller don't try to update it (EXTJSIV-3891)
752             if (scroller) {
753                 scroller.scrollByDeltaX(distance);
754             }
755         }
756     },
757
758 <span id='Ext-panel-Table-method-processEvent'>    /**
759 </span>     * @private
760      * Processes UI events from the view. Propagates them to whatever internal Components need to process them.
761      * @param {String} type Event type, eg 'click'
762      * @param {Ext.view.Table} view TableView Component
763      * @param {HTMLElement} cell Cell HtmlElement the event took place within
764      * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
765      * @param {Number} cellIndex Cell index within the row
766      * @param {Ext.EventObject} e Original event
767      */
768     processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
769         var me = this,
770             header;
771
772         if (cellIndex !== -1) {
773             header = me.headerCt.getGridColumns()[cellIndex];
774             return header.processEvent.apply(header, arguments);
775         }
776     },
777
778 <span id='Ext-panel-Table-method-determineScrollbars'>    /**
779 </span>     * Requests a recalculation of scrollbars and puts them in if they are needed.
780      */
781     determineScrollbars: function() {
782         // Set a flag so that afterComponentLayout does not recurse back into here.
783         if (this.determineScrollbarsRunning) {
784             return;
785         }
786         this.determineScrollbarsRunning = true;
787         var me = this,
788             view = me.view,
789             box,
790             tableEl,
791             scrollWidth,
792             clientWidth,
793             scrollHeight,
794             clientHeight,
795             verticalScroller = me.verticalScroller,
796             horizontalScroller = me.horizontalScroller,
797             curScrollbars = (verticalScroller   &amp;&amp; verticalScroller.ownerCt === me ? 1 : 0) |
798                             (horizontalScroller &amp;&amp; horizontalScroller.ownerCt === me ? 2 : 0),
799             reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both
800
801         // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars
802         if (!me.collapsed &amp;&amp; view &amp;&amp; view.viewReady) {
803
804             // Calculate maximum, *scrollbarless* space which the view has available.
805             // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars
806             box = view.el.getSize();
807
808             clientWidth  = box.width  + ((curScrollbars &amp; 1) ? verticalScroller.width : 0);
809             clientHeight = box.height + ((curScrollbars &amp; 2) ? horizontalScroller.height : 0);
810
811             // Calculate the width of the scrolling block
812             // There will never be a horizontal scrollbar if all columns are flexed.
813
814             scrollWidth = (me.headerCt.query('[flex]').length &amp;&amp; !me.headerCt.layout.tooNarrow) ? 0 : me.headerCt.getFullWidth();
815
816             // Calculate the height of the scrolling block
817             if (verticalScroller &amp;&amp; verticalScroller.el) {
818                 scrollHeight = verticalScroller.getSizeCalculation().height;
819             } else {
820                 tableEl = view.el.child('table', true);
821                 scrollHeight = tableEl ? tableEl.offsetHeight : 0;
822             }
823
824             // View is too high.
825             // Definitely need a vertical scrollbar
826             if (scrollHeight &gt; clientHeight) {
827                 reqScrollbars = 1;
828
829                 // But if scrollable block width goes into the zone required by the vertical scrollbar, we'll also need a horizontal
830                 if (horizontalScroller &amp;&amp; ((clientWidth - scrollWidth) &lt; verticalScroller.width)) {
831                     reqScrollbars = 3;
832                 }
833             }
834
835             // View height fits. But we stil may need a horizontal scrollbar, and this might necessitate a vertical one.
836             else {
837                 // View is too wide.
838                 // Definitely need a horizontal scrollbar
839                 if (scrollWidth &gt; clientWidth) {
840                     reqScrollbars = 2;
841
842                     // But if scrollable block height goes into the zone required by the horizontal scrollbar, we'll also need a vertical
843                     if (verticalScroller &amp;&amp; ((clientHeight - scrollHeight) &lt; horizontalScroller.height)) {
844                         reqScrollbars = 3;
845                     }
846                 }
847             }
848
849             // If scrollbar requirements have changed, change 'em...
850             if (reqScrollbars !== curScrollbars) {
851
852                 // Suspend component layout while we add/remove the docked scrollers
853                 me.suspendLayout = true;
854                 if (reqScrollbars &amp; 1) {
855                     me.showVerticalScroller();
856                 } else {
857                     me.hideVerticalScroller();
858                 }
859                 if (reqScrollbars &amp; 2) {
860                     me.showHorizontalScroller();
861                 } else {
862                     me.hideHorizontalScroller();
863                 }
864                 me.suspendLayout = false;
865
866                 // Lay out the Component.
867                 me.doComponentLayout();
868                 // Lay out me.items
869                 me.getLayout().layout();
870             }
871         }
872         delete me.determineScrollbarsRunning;
873     },
874
875     onViewResize: function() {
876         this.determineScrollbars();
877     },
878
879     afterComponentLayout: function() {
880         this.callParent(arguments);
881         this.determineScrollbars();
882         this.invalidateScroller();
883     },
884
885     onHeaderResize: function() {
886         if (!this.componentLayout.layoutBusy &amp;&amp; this.view &amp;&amp; this.view.rendered) {
887             this.determineScrollbars();
888             this.invalidateScroller();
889         }
890     },
891
892     afterCollapse: function() {
893         var me = this;
894         if (me.verticalScroller) {
895             me.verticalScroller.saveScrollPos();
896         }
897         if (me.horizontalScroller) {
898             me.horizontalScroller.saveScrollPos();
899         }
900         me.callParent(arguments);
901     },
902
903     afterExpand: function() {
904         var me = this;
905         me.callParent(arguments);
906         if (me.verticalScroller) {
907             me.verticalScroller.restoreScrollPos();
908         }
909         if (me.horizontalScroller) {
910             me.horizontalScroller.restoreScrollPos();
911         }
912     },
913
914 <span id='Ext-panel-Table-method-hideHorizontalScroller'>    /**
915 </span>     * Hides the verticalScroller and removes the horizontalScrollerPresentCls.
916      */
917     hideHorizontalScroller: function() {
918         var me = this;
919
920         if (me.horizontalScroller &amp;&amp; me.horizontalScroller.ownerCt === me) {
921             me.verticalScroller.setReservedSpace(0);
922             me.removeDocked(me.horizontalScroller, false);
923             me.removeCls(me.horizontalScrollerPresentCls);
924             me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
925         }
926
927     },
928
929 <span id='Ext-panel-Table-method-showHorizontalScroller'>    /**
930 </span>     * Shows the horizontalScroller and add the horizontalScrollerPresentCls.
931      */
932     showHorizontalScroller: function() {
933         var me = this;
934
935         if (me.verticalScroller) {
936             me.verticalScroller.setReservedSpace(Ext.getScrollbarSize().height - 1);
937         }
938         if (me.horizontalScroller &amp;&amp; me.horizontalScroller.ownerCt !== me) {
939             me.addDocked(me.horizontalScroller);
940             me.addCls(me.horizontalScrollerPresentCls);
941             me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
942         }
943     },
944
945 <span id='Ext-panel-Table-method-hideVerticalScroller'>    /**
946 </span>     * Hides the verticalScroller and removes the verticalScrollerPresentCls.
947      */
948     hideVerticalScroller: function() {
949         var me = this;
950
951         me.setHeaderReserveOffset(false);
952         if (me.verticalScroller &amp;&amp; me.verticalScroller.ownerCt === me) {
953             me.removeDocked(me.verticalScroller, false);
954             me.removeCls(me.verticalScrollerPresentCls);
955             me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
956         }
957     },
958
959 <span id='Ext-panel-Table-method-showVerticalScroller'>    /**
960 </span>     * Shows the verticalScroller and adds the verticalScrollerPresentCls.
961      */
962     showVerticalScroller: function() {
963         var me = this;
964
965         me.setHeaderReserveOffset(true);
966         if (me.verticalScroller &amp;&amp; me.verticalScroller.ownerCt !== me) {
967             me.addDocked(me.verticalScroller);
968             me.addCls(me.verticalScrollerPresentCls);
969             me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
970         }
971     },
972
973     setHeaderReserveOffset: function (reserveOffset) {
974         var headerCt = this.headerCt,
975             layout = headerCt.layout;
976
977         // only trigger a layout when reserveOffset is changing
978         if (layout &amp;&amp; layout.reserveOffset !== reserveOffset) {
979             layout.reserveOffset = reserveOffset;
980             if (!this.suspendLayout) {
981                 headerCt.doLayout();
982             }
983         }
984     },
985
986 <span id='Ext-panel-Table-method-invalidateScroller'>    /**
987 </span>     * Invalides scrollers that are present and forces a recalculation. (Not related to showing/hiding the scrollers)
988      */
989     invalidateScroller: function() {
990         var me = this,
991             vScroll = me.verticalScroller,
992             hScroll = me.horizontalScroller;
993
994         if (vScroll) {
995             vScroll.invalidate();
996         }
997         if (hScroll) {
998             hScroll.invalidate();
999         }
1000     },
1001
1002     // refresh the view when a header moves
1003     onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
1004         this.view.refresh();
1005     },
1006
1007     // Section onHeaderHide is invoked after view.
1008     onHeaderHide: function(headerCt, header) {
1009         this.invalidateScroller();
1010     },
1011
1012     onHeaderShow: function(headerCt, header) {
1013         this.invalidateScroller();
1014     },
1015
1016     getVerticalScroller: function() {
1017         return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
1018     },
1019
1020     getHorizontalScroller: function() {
1021         return this.getScrollerOwner().down('gridscroller[dock=bottom]');
1022     },
1023
1024     onMouseWheel: function(e) {
1025         var me = this,
1026             vertScroller = me.getVerticalScroller(),
1027             horizScroller = me.getHorizontalScroller(),
1028             scrollDelta = -me.scrollDelta,
1029             deltas = e.getWheelDeltas(),
1030             deltaX = scrollDelta * deltas.x,
1031             deltaY = scrollDelta * deltas.y,
1032             vertScrollerEl, horizScrollerEl,
1033             vertScrollerElDom, horizScrollerElDom,
1034             horizontalCanScrollLeft, horizontalCanScrollRight,
1035             verticalCanScrollDown, verticalCanScrollUp;
1036
1037         // calculate whether or not both scrollbars can scroll right/left and up/down
1038         if (horizScroller) {
1039             horizScrollerEl = horizScroller.scrollEl;
1040             if (horizScrollerEl) {
1041                 horizScrollerElDom = horizScrollerEl.dom;
1042                 horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
1043                 horizontalCanScrollLeft  = horizScrollerElDom.scrollLeft !== 0;
1044             }
1045         }
1046         if (vertScroller) {
1047             vertScrollerEl = vertScroller.scrollEl;
1048             if (vertScrollerEl) {
1049                 vertScrollerElDom = vertScrollerEl.dom;
1050                 verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
1051                 verticalCanScrollUp   = vertScrollerElDom.scrollTop !== 0;
1052             }
1053         }
1054
1055         if (horizScroller) {
1056             if ((deltaX &lt; 0 &amp;&amp; horizontalCanScrollLeft) || (deltaX &gt; 0 &amp;&amp; horizontalCanScrollRight)) {
1057                 e.stopEvent();
1058                 horizScroller.scrollByDeltaX(deltaX);
1059             }
1060         }
1061         if (vertScroller) {
1062             if ((deltaY &lt; 0 &amp;&amp; verticalCanScrollUp) || (deltaY &gt; 0 &amp;&amp; verticalCanScrollDown)) {
1063                 e.stopEvent();
1064                 vertScroller.scrollByDeltaY(deltaY);
1065             }
1066         }
1067     },
1068
1069 <span id='Ext-panel-Table-method-onViewReady'>    /**
1070 </span>     * @private
1071      * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready
1072      */
1073     onViewReady: function() {
1074         var me = this;
1075         me.fireEvent('viewready', me);
1076         if (me.deferRowRender) {
1077             me.determineScrollbars();
1078             me.invalidateScroller();
1079         }
1080     },
1081
1082 <span id='Ext-panel-Table-method-onViewRefresh'>    /**
1083 </span>     * @private
1084      * Determines and invalidates scrollers on view refresh
1085      */
1086     onViewRefresh: function() {
1087         var me = this;
1088
1089         // Refresh *during* render must be ignored.
1090         if (!me.rendering) {
1091             this.determineScrollbars();
1092             if (this.invalidateScrollerOnRefresh) {
1093                 this.invalidateScroller();
1094             }
1095         }
1096     },
1097
1098 <span id='Ext-panel-Table-method-setScrollTop'>    /**
1099 </span>     * Sets the scrollTop of the TablePanel.
1100      * @param {Number} top
1101      */
1102     setScrollTop: function(top) {
1103         var me               = this,
1104             rootCmp          = me.getScrollerOwner(),
1105             verticalScroller = me.getVerticalScroller();
1106
1107         rootCmp.virtualScrollTop = top;
1108         if (verticalScroller) {
1109             verticalScroller.setScrollTop(top);
1110         }
1111     },
1112
1113     getScrollerOwner: function() {
1114         var rootCmp = this;
1115         if (!this.scrollerOwner) {
1116             rootCmp = this.up('[scrollerOwner]');
1117         }
1118         return rootCmp;
1119     },
1120
1121 <span id='Ext-panel-Table-method-scrollByDeltaY'>    /**
1122 </span>     * Scrolls the TablePanel by deltaY
1123      * @param {Number} deltaY
1124      */
1125     scrollByDeltaY: function(deltaY) {
1126         var verticalScroller = this.getVerticalScroller();
1127
1128         if (verticalScroller) {
1129             verticalScroller.scrollByDeltaY(deltaY);
1130         }
1131     },
1132
1133 <span id='Ext-panel-Table-method-scrollByDeltaX'>    /**
1134 </span>     * Scrolls the TablePanel by deltaX
1135      * @param {Number} deltaX
1136      */
1137     scrollByDeltaX: function(deltaX) {
1138         var horizontalScroller = this.getHorizontalScroller();
1139
1140         if (horizontalScroller) {
1141             horizontalScroller.scrollByDeltaX(deltaX);
1142         }
1143     },
1144
1145 <span id='Ext-panel-Table-method-getLhsMarker'>    /**
1146 </span>     * Gets left hand side marker for header resizing.
1147      * @private
1148      */
1149     getLhsMarker: function() {
1150         var me = this;
1151
1152         if (!me.lhsMarker) {
1153             me.lhsMarker = Ext.DomHelper.append(me.el, {
1154                 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1155             }, true);
1156         }
1157         return me.lhsMarker;
1158     },
1159
1160 <span id='Ext-panel-Table-method-getRhsMarker'>    /**
1161 </span>     * Gets right hand side marker for header resizing.
1162      * @private
1163      */
1164     getRhsMarker: function() {
1165         var me = this;
1166
1167         if (!me.rhsMarker) {
1168             me.rhsMarker = Ext.DomHelper.append(me.el, {
1169                 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1170             }, true);
1171         }
1172         return me.rhsMarker;
1173     },
1174
1175 <span id='Ext-panel-Table-method-getSelectionModel'>    /**
1176 </span>     * Returns the selection model being used and creates it via the configuration if it has not been created already.
1177      * @return {Ext.selection.Model} selModel
1178      */
1179     getSelectionModel: function(){
1180         if (!this.selModel) {
1181             this.selModel = {};
1182         }
1183
1184         var mode = 'SINGLE',
1185             type;
1186         if (this.simpleSelect) {
1187             mode = 'SIMPLE';
1188         } else if (this.multiSelect) {
1189             mode = 'MULTI';
1190         }
1191
1192         Ext.applyIf(this.selModel, {
1193             allowDeselect: this.allowDeselect,
1194             mode: mode
1195         });
1196
1197         if (!this.selModel.events) {
1198             type = this.selModel.selType || this.selType;
1199             this.selModel = Ext.create('selection.' + type, this.selModel);
1200         }
1201
1202         if (!this.selModel.hasRelaySetup) {
1203             this.relayEvents(this.selModel, [
1204                 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
1205             ]);
1206             this.selModel.hasRelaySetup = true;
1207         }
1208
1209         // lock the selection model if user
1210         // has disabled selection
1211         if (this.disableSelection) {
1212             this.selModel.locked = true;
1213         }
1214         return this.selModel;
1215     },
1216
1217     onVerticalScroll: function(event, target) {
1218         var owner = this.getScrollerOwner(),
1219             items = owner.query('tableview'),
1220             i = 0,
1221             len = items.length;
1222
1223         for (; i &lt; len; i++) {
1224             items[i].el.dom.scrollTop = target.scrollTop;
1225         }
1226     },
1227
1228     onHorizontalScroll: function(event, target) {
1229         var owner = this.getScrollerOwner(),
1230             items = owner.query('tableview'),
1231             center = items[1] || items[0];
1232
1233         center.el.dom.scrollLeft = target.scrollLeft;
1234         this.headerCt.el.dom.scrollLeft = target.scrollLeft;
1235     },
1236
1237     // template method meant to be overriden
1238     onStoreLoad: Ext.emptyFn,
1239
1240     getEditorParent: function() {
1241         return this.body;
1242     },
1243
1244     bindStore: function(store) {
1245         var me = this;
1246         me.store = store;
1247         me.getView().bindStore(store);
1248     },
1249     
1250     beforeDestroy: function(){
1251         // may be some duplication here since the horizontal and vertical
1252         // scroller may be part of the docked items, but we need to clean
1253         // them up in case they aren't visible.
1254         Ext.destroy(this.horizontalScroller, this.verticalScroller);
1255         this.callParent();
1256     },
1257
1258 <span id='Ext-panel-Table-method-reconfigure'>    /**
1259 </span>     * Reconfigures the table with a new store/columns. Either the store or the columns can be ommitted if you don't wish
1260      * to change them.
1261      * @param {Ext.data.Store} store (Optional) The new store.
1262      * @param {Object[]} columns (Optional) An array of column configs
1263      */
1264     reconfigure: function(store, columns) {
1265         var me = this,
1266             headerCt = me.headerCt;
1267
1268         if (me.lockable) {
1269             me.reconfigureLockable(store, columns);
1270         } else {
1271             if (columns) {
1272                 headerCt.suspendLayout = true;
1273                 headerCt.removeAll();
1274                 headerCt.add(columns);
1275             }
1276             if (store) {
1277                 store = Ext.StoreManager.lookup(store);
1278                 me.bindStore(store);
1279             } else {
1280                 me.getView().refresh();
1281             }
1282             if (columns) {
1283                 headerCt.suspendLayout = false;
1284                 me.forceComponentLayout();
1285             }
1286         }
1287         me.fireEvent('reconfigure', me);
1288     }
1289 });</pre>
1290 </body>
1291 </html>