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