Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Container2.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-grid-header-Container'>/**
19 </span> * @class Ext.grid.header.Container
20  * @extends Ext.container.Container
21  *
22  * Container which holds headers and is docked at the top or bottom of a TablePanel.
23  * The HeaderContainer drives resizing/moving/hiding of columns within the TableView.
24  * As headers are hidden, moved or resized the headercontainer is responsible for
25  * triggering changes within the view.
26  */
27 Ext.define('Ext.grid.header.Container', {
28     extend: 'Ext.container.Container',
29     uses: [
30         'Ext.grid.ColumnLayout',
31         'Ext.grid.column.Column',
32         'Ext.menu.Menu',
33         'Ext.menu.CheckItem',
34         'Ext.menu.Separator',
35         'Ext.grid.plugin.HeaderResizer',
36         'Ext.grid.plugin.HeaderReorderer'
37     ],
38     border: true,
39
40     alias: 'widget.headercontainer',
41
42     baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
43     dock: 'top',
44
45 <span id='Ext-grid-header-Container-cfg-weight'>    /**
46 </span>     * @cfg {Number} weight
47      * HeaderContainer overrides the default weight of 0 for all docked items to 100.
48      * This is so that it has more priority over things like toolbars.
49      */
50     weight: 100,
51     defaultType: 'gridcolumn',
52 <span id='Ext-grid-header-Container-cfg-defaultWidth'>    /**
53 </span>     * @cfg {Number} defaultWidth
54      * Width of the header if no width or flex is specified. Defaults to 100.
55      */
56     defaultWidth: 100,
57
58
59     sortAscText: 'Sort Ascending',
60     sortDescText: 'Sort Descending',
61     sortClearText: 'Clear Sort',
62     columnsText: 'Columns',
63
64     lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',
65     firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
66     headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',
67
68     // private; will probably be removed by 4.0
69     triStateSort: false,
70
71     ddLock: false,
72
73     dragging: false,
74
75 <span id='Ext-grid-header-Container-property-isGroupHeader'>    /**
76 </span>     * &lt;code&gt;true&lt;/code&gt; if this HeaderContainer is in fact a group header which contains sub headers.
77      * @type Boolean
78      * @property isGroupHeader
79      */
80
81 <span id='Ext-grid-header-Container-cfg-sortable'>    /**
82 </span>     * @cfg {Boolean} sortable
83      * Provides the default sortable state for all Headers within this HeaderContainer.
84      * Also turns on or off the menus in the HeaderContainer. Note that the menu is
85      * shared across every header and therefore turning it off will remove the menu
86      * items for every header.
87      */
88     sortable: true,
89
90     initComponent: function() {
91         var me = this;
92
93         me.headerCounter = 0;
94         me.plugins = me.plugins || [];
95
96         // TODO: Pass in configurations to turn on/off dynamic
97         //       resizing and disable resizing all together
98
99         // Only set up a Resizer and Reorderer for the topmost HeaderContainer.
100         // Nested Group Headers are themselves HeaderContainers
101         if (!me.isHeader) {
102             me.resizer   = Ext.create('Ext.grid.plugin.HeaderResizer');
103             me.reorderer = Ext.create('Ext.grid.plugin.HeaderReorderer');
104             if (!me.enableColumnResize) {
105                 me.resizer.disable();
106             }
107             if (!me.enableColumnMove) {
108                 me.reorderer.disable();
109             }
110             me.plugins.push(me.reorderer, me.resizer);
111         }
112
113         // Base headers do not need a box layout
114         if (me.isHeader &amp;&amp; !me.items) {
115             me.layout = 'auto';
116         }
117         // HeaderContainer and Group header needs a gridcolumn layout.
118         else {
119             me.layout = {
120                 type: 'gridcolumn',
121                 availableSpaceOffset: me.availableSpaceOffset,
122                 align: 'stretchmax',
123                 resetStretch: true
124             };
125         }
126         me.defaults = me.defaults || {};
127         Ext.applyIf(me.defaults, {
128             width: me.defaultWidth,
129             triStateSort: me.triStateSort,
130             sortable: me.sortable
131         });
132         me.callParent();
133         me.addEvents(
134 <span id='Ext-grid-header-Container-event-columnresize'>            /**
135 </span>             * @event columnresize
136              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
137              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
138              * @param {Number} width
139              */
140             'columnresize',
141
142 <span id='Ext-grid-header-Container-event-headerclick'>            /**
143 </span>             * @event headerclick
144              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
145              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
146              * @param {Ext.EventObject} e
147              * @param {HTMLElement} t
148              */
149             'headerclick',
150
151 <span id='Ext-grid-header-Container-event-headertriggerclick'>            /**
152 </span>             * @event headertriggerclick
153              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
154              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
155              * @param {Ext.EventObject} e
156              * @param {HTMLElement} t
157              */
158             'headertriggerclick',
159
160 <span id='Ext-grid-header-Container-event-columnmove'>            /**
161 </span>             * @event columnmove
162              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
163              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
164              * @param {Number} fromIdx
165              * @param {Number} toIdx
166              */
167             'columnmove',
168 <span id='Ext-grid-header-Container-event-columnhide'>            /**
169 </span>             * @event columnhide
170              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
171              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
172              */
173             'columnhide',
174 <span id='Ext-grid-header-Container-event-columnshow'>            /**
175 </span>             * @event columnshow
176              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
177              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
178              */
179             'columnshow',
180 <span id='Ext-grid-header-Container-event-sortchange'>            /**
181 </span>             * @event sortchange
182              * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
183              * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
184              * @param {String} direction
185              */
186             'sortchange',
187 <span id='Ext-grid-header-Container-event-menucreate'>            /**
188 </span>             * @event menucreate
189              * Fired immediately after the column header menu is created.
190              * @param {Ext.grid.header.Container} ct This instance
191              * @param {Ext.menu.Menu} menu The Menu that was created
192              */
193             'menucreate'
194         );
195     },
196
197     onDestroy: function() {
198         Ext.destroy(this.resizer, this.reorderer);
199         this.callParent();
200     },
201     
202     applyDefaults: function(config){
203         /*
204          * Ensure header.Container defaults don't get applied to a RowNumberer 
205          * if an xtype is supplied. This isn't an ideal solution however it's 
206          * much more likely that a RowNumberer with no options will be created, 
207          * wanting to use the defaults specified on the class as opposed to 
208          * those setup on the Container.
209          */
210         if (config &amp;&amp; !config.isComponent &amp;&amp; config.xtype == 'rownumberer') {
211             return config;
212         }
213         return this.callParent([config]);
214     },
215
216     applyColumnsState: function(columns) {
217         if (!columns || !columns.length) {
218             return;
219         }
220
221         var me = this,
222             i = 0,
223             index,
224             col;
225
226         Ext.each(columns, function (columnState) {
227             col = me.down('gridcolumn[headerId=' + columnState.id + ']');
228             if (col) {
229                 index = me.items.indexOf(col);
230                 if (i !== index) {
231                     me.moveHeader(index, i);
232                 }
233
234                 if (col.applyColumnState) {
235                     col.applyColumnState(columnState);
236                 }
237                 ++i;
238             }
239         });
240     },
241
242     getColumnsState: function () {
243         var me = this,
244             columns = [],
245             state;
246
247         me.items.each(function (col) {
248             state = col.getColumnState &amp;&amp; col.getColumnState();
249             if (state) {
250                 columns.push(state);
251             }
252         });
253
254         return columns;
255     },
256
257     // Invalidate column cache on add
258     // We cannot refresh the View on every add because this method is called
259     // when the HeaderDropZone moves Headers around, that will also refresh the view
260     onAdd: function(c) {
261         var me = this;
262         if (!c.headerId) {
263             c.headerId = c.initialConfig.id || ('h' + (++me.headerCounter));
264         }
265         //&lt;debug warn&gt;
266         if (Ext.global.console &amp;&amp; Ext.global.console.warn) {
267             if (!me._usedIDs) me._usedIDs = {};
268             if (me._usedIDs[c.headerId]) {
269                 Ext.global.console.warn(this.$className, 'attempted to reuse an existing id', c.headerId);
270             }
271             me._usedIDs[c.headerId] = true;
272         }
273         //&lt;/debug&gt;
274         me.callParent(arguments);
275         me.purgeCache();
276     },
277
278     // Invalidate column cache on remove
279     // We cannot refresh the View on every remove because this method is called
280     // when the HeaderDropZone moves Headers around, that will also refresh the view
281     onRemove: function(c) {
282         var me = this;
283         me.callParent(arguments);
284         me.purgeCache();
285     },
286
287     afterRender: function() {
288         this.callParent();
289         var store   = this.up('[store]').store,
290             sorters = store.sorters,
291             first   = sorters.first(),
292             hd;
293
294         if (first) {
295             hd = this.down('gridcolumn[dataIndex=' + first.property  +']');
296             if (hd) {
297                 hd.setSortState(first.direction, false, true);
298             }
299         }
300     },
301
302     afterLayout: function() {
303         if (!this.isHeader) {
304             var me = this,
305                 topHeaders = me.query('&gt;gridcolumn:not([hidden])'),
306                 viewEl,
307                 firstHeaderEl,
308                 lastHeaderEl;
309
310             me.callParent(arguments);
311
312             if (topHeaders.length) {
313                 firstHeaderEl = topHeaders[0].el;
314                 if (firstHeaderEl !== me.pastFirstHeaderEl) {
315                     if (me.pastFirstHeaderEl) {
316                         me.pastFirstHeaderEl.removeCls(me.firstHeaderCls);
317                     }
318                     firstHeaderEl.addCls(me.firstHeaderCls);
319                     me.pastFirstHeaderEl = firstHeaderEl;
320                 }
321
322                 lastHeaderEl = topHeaders[topHeaders.length - 1].el;
323                 if (lastHeaderEl !== me.pastLastHeaderEl) {
324                     if (me.pastLastHeaderEl) {
325                         me.pastLastHeaderEl.removeCls(me.lastHeaderCls);
326                     }
327                     lastHeaderEl.addCls(me.lastHeaderCls);
328                     me.pastLastHeaderEl = lastHeaderEl;
329                 }
330             }
331         }
332
333     },
334
335     onHeaderShow: function(header, preventLayout) {
336         // Pass up to the GridSection
337         var me = this,
338             gridSection = me.ownerCt,
339             menu = me.getMenu(),
340             topItems, topItemsVisible,
341             colCheckItem,
342             itemToEnable,
343             len, i;
344
345         if (menu) {
346
347             colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
348             if (colCheckItem) {
349                 colCheckItem.setChecked(true, true);
350             }
351
352             // There's more than one header visible, and we've disabled some checked items... re-enable them
353             topItems = menu.query('#columnItem&gt;menucheckitem[checked]');
354             topItemsVisible = topItems.length;
355             if ((me.getVisibleGridColumns().length &gt; 1) &amp;&amp; me.disabledMenuItems &amp;&amp; me.disabledMenuItems.length) {
356                 if (topItemsVisible == 1) {
357                     Ext.Array.remove(me.disabledMenuItems, topItems[0]);
358                 }
359                 for (i = 0, len = me.disabledMenuItems.length; i &lt; len; i++) {
360                     itemToEnable = me.disabledMenuItems[i];
361                     if (!itemToEnable.isDestroyed) {
362                         itemToEnable[itemToEnable.menu ? 'enableCheckChange' : 'enable']();
363                     }
364                 }
365                 if (topItemsVisible == 1) {
366                     me.disabledMenuItems = topItems;
367                 } else {
368                     me.disabledMenuItems = [];
369                 }
370             }
371         }
372
373         // Only update the grid UI when we are notified about base level Header shows;
374         // Group header shows just cause a layout of the HeaderContainer
375         if (!header.isGroupHeader) {
376             if (me.view) {
377                 me.view.onHeaderShow(me, header, true);
378             }
379             if (gridSection) {
380                 gridSection.onHeaderShow(me, header);
381             }
382         }
383         me.fireEvent('columnshow', me, header);
384
385         // The header's own hide suppresses cascading layouts, so lay the headers out now
386         if (preventLayout !== true) {
387             me.doLayout();
388         }
389     },
390
391     doComponentLayout: function(){
392         var me = this;
393         if (me.view &amp;&amp; me.view.saveScrollState) {
394             me.view.saveScrollState();
395         }
396         me.callParent(arguments);
397         if (me.view &amp;&amp; me.view.restoreScrollState) {
398             me.view.restoreScrollState();
399         }
400     },
401
402     onHeaderHide: function(header, suppressLayout) {
403         // Pass up to the GridSection
404         var me = this,
405             gridSection = me.ownerCt,
406             menu = me.getMenu(),
407             colCheckItem;
408
409         if (menu) {
410
411             // If the header was hidden programmatically, sync the Menu state
412             colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
413             if (colCheckItem) {
414                 colCheckItem.setChecked(false, true);
415             }
416             me.setDisabledItems();
417         }
418
419         // Only update the UI when we are notified about base level Header hides;
420         if (!header.isGroupHeader) {
421             if (me.view) {
422                 me.view.onHeaderHide(me, header, true);
423             }
424             if (gridSection) {
425                 gridSection.onHeaderHide(me, header);
426             }
427
428             // The header's own hide suppresses cascading layouts, so lay the headers out now
429             if (!suppressLayout) {
430                 me.doLayout();
431             }
432         }
433         me.fireEvent('columnhide', me, header);
434     },
435
436     setDisabledItems: function(){
437         var me = this,
438             menu = me.getMenu(),
439             i = 0,
440             len,
441             itemsToDisable,
442             itemToDisable;
443
444         // Find what to disable. If only one top level item remaining checked, we have to disable stuff.
445         itemsToDisable = menu.query('#columnItem&gt;menucheckitem[checked]');
446         if ((itemsToDisable.length === 1)) {
447             if (!me.disabledMenuItems) {
448                 me.disabledMenuItems = [];
449             }
450
451             // If down to only one column visible, also disable any descendant checkitems
452             if ((me.getVisibleGridColumns().length === 1) &amp;&amp; itemsToDisable[0].menu) {
453                 itemsToDisable = itemsToDisable.concat(itemsToDisable[0].menu.query('menucheckitem[checked]'));
454             }
455
456             len = itemsToDisable.length;
457             // Disable any further unchecking at any level.
458             for (i = 0; i &lt; len; i++) {
459                 itemToDisable = itemsToDisable[i];
460                 if (!Ext.Array.contains(me.disabledMenuItems, itemToDisable)) {
461
462                     // If we only want to disable check change: it might be a disabled item, so enable it prior to
463                     // setting its correct disablement level.
464                     itemToDisable.disabled = false;
465                     itemToDisable[itemToDisable.menu ? 'disableCheckChange' : 'disable']();
466                     me.disabledMenuItems.push(itemToDisable);
467                 }
468             }
469         }
470     },
471
472 <span id='Ext-grid-header-Container-method-tempLock'>    /**
473 </span>     * Temporarily lock the headerCt. This makes it so that clicking on headers
474      * don't trigger actions like sorting or opening of the header menu. This is
475      * done because extraneous events may be fired on the headers after interacting
476      * with a drag drop operation.
477      * @private
478      */
479     tempLock: function() {
480         this.ddLock = true;
481         Ext.Function.defer(function() {
482             this.ddLock = false;
483         }, 200, this);
484     },
485
486     onHeaderResize: function(header, w, suppressFocus) {
487         this.tempLock();
488         if (this.view &amp;&amp; this.view.rendered) {
489             this.view.onHeaderResize(header, w, suppressFocus);
490         }
491     },
492
493     onHeaderClick: function(header, e, t) {
494         this.fireEvent(&quot;headerclick&quot;, this, header, e, t);
495     },
496
497     onHeaderTriggerClick: function(header, e, t) {
498         // generate and cache menu, provide ability to cancel/etc
499         if (this.fireEvent(&quot;headertriggerclick&quot;, this, header, e, t) !== false) {
500             this.showMenuBy(t, header);
501         }
502     },
503
504     showMenuBy: function(t, header) {
505         var menu = this.getMenu(),
506             ascItem  = menu.down('#ascItem'),
507             descItem = menu.down('#descItem'),
508             sortableMth;
509
510         menu.activeHeader = menu.ownerCt = header;
511         menu.setFloatParent(header);
512         // TODO: remove coupling to Header's titleContainer el
513         header.titleContainer.addCls(this.headerOpenCls);
514
515         // enable or disable asc &amp; desc menu items based on header being sortable
516         sortableMth = header.sortable ? 'enable' : 'disable';
517         if (ascItem) {
518             ascItem[sortableMth]();
519         }
520         if (descItem) {
521             descItem[sortableMth]();
522         }
523         menu.showBy(t);
524     },
525
526     // remove the trigger open class when the menu is hidden
527     onMenuDeactivate: function() {
528         var menu = this.getMenu();
529         // TODO: remove coupling to Header's titleContainer el
530         menu.activeHeader.titleContainer.removeCls(this.headerOpenCls);
531     },
532
533     moveHeader: function(fromIdx, toIdx) {
534
535         // An automatically expiring lock
536         this.tempLock();
537         this.onHeaderMoved(this.move(fromIdx, toIdx), fromIdx, toIdx);
538     },
539
540     purgeCache: function() {
541         var me = this;
542         // Delete column cache - column order has changed.
543         delete me.gridDataColumns;
544         delete me.hideableColumns;
545
546         // Menu changes when columns are moved. It will be recreated.
547         if (me.menu) {
548             me.menu.destroy();
549             delete me.menu;
550         }
551     },
552
553     onHeaderMoved: function(header, fromIdx, toIdx) {
554         var me = this,
555             gridSection = me.ownerCt;
556
557         if (gridSection &amp;&amp; gridSection.onHeaderMove) {
558             gridSection.onHeaderMove(me, header, fromIdx, toIdx);
559         }
560         me.fireEvent(&quot;columnmove&quot;, me, header, fromIdx, toIdx);
561     },
562
563 <span id='Ext-grid-header-Container-method-getMenu'>    /**
564 </span>     * Gets the menu (and will create it if it doesn't already exist)
565      * @private
566      */
567     getMenu: function() {
568         var me = this;
569
570         if (!me.menu) {
571             me.menu = Ext.create('Ext.menu.Menu', {
572                 hideOnParentHide: false,  // Persists when owning ColumnHeader is hidden
573                 items: me.getMenuItems(),
574                 listeners: {
575                     deactivate: me.onMenuDeactivate,
576                     scope: me
577                 }
578             });
579             me.setDisabledItems();
580             me.fireEvent('menucreate', me, me.menu);
581         }
582         return me.menu;
583     },
584
585 <span id='Ext-grid-header-Container-method-getMenuItems'>    /**
586 </span>     * Returns an array of menu items to be placed into the shared menu
587      * across all headers in this header container.
588      * @returns {Array} menuItems
589      */
590     getMenuItems: function() {
591         var me = this,
592             menuItems = [],
593             hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;
594
595         if (me.sortable) {
596             menuItems = [{
597                 itemId: 'ascItem',
598                 text: me.sortAscText,
599                 cls: Ext.baseCSSPrefix + 'hmenu-sort-asc',
600                 handler: me.onSortAscClick,
601                 scope: me
602             },{
603                 itemId: 'descItem',
604                 text: me.sortDescText,
605                 cls: Ext.baseCSSPrefix + 'hmenu-sort-desc',
606                 handler: me.onSortDescClick,
607                 scope: me
608             }];
609         }
610         if (hideableColumns &amp;&amp; hideableColumns.length) {
611             menuItems.push('-', {
612                 itemId: 'columnItem',
613                 text: me.columnsText,
614                 cls: Ext.baseCSSPrefix + 'cols-icon',
615                 menu: hideableColumns
616             });
617         }
618         return menuItems;
619     },
620
621     // sort asc when clicking on item in menu
622     onSortAscClick: function() {
623         var menu = this.getMenu(),
624             activeHeader = menu.activeHeader;
625
626         activeHeader.setSortState('ASC');
627     },
628
629     // sort desc when clicking on item in menu
630     onSortDescClick: function() {
631         var menu = this.getMenu(),
632             activeHeader = menu.activeHeader;
633
634         activeHeader.setSortState('DESC');
635     },
636
637 <span id='Ext-grid-header-Container-method-getColumnMenu'>    /**
638 </span>     * Returns an array of menu CheckItems corresponding to all immediate children of the passed Container which have been configured as hideable.
639      */
640     getColumnMenu: function(headerContainer) {
641         var menuItems = [],
642             i = 0,
643             item,
644             items = headerContainer.query('&gt;gridcolumn[hideable]'),
645             itemsLn = items.length,
646             menuItem;
647
648         for (; i &lt; itemsLn; i++) {
649             item = items[i];
650             menuItem = Ext.create('Ext.menu.CheckItem', {
651                 text: item.text,
652                 checked: !item.hidden,
653                 hideOnClick: false,
654                 headerId: item.id,
655                 menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
656                 checkHandler: this.onColumnCheckChange,
657                 scope: this
658             });
659             if (itemsLn === 1) {
660                 menuItem.disabled = true;
661             }
662             menuItems.push(menuItem);
663
664             // If the header is ever destroyed - for instance by dragging out the last remaining sub header,
665             // then the associated menu item must also be destroyed.
666             item.on({
667                 destroy: Ext.Function.bind(menuItem.destroy, menuItem)
668             });
669         }
670         return menuItems;
671     },
672
673     onColumnCheckChange: function(checkItem, checked) {
674         var header = Ext.getCmp(checkItem.headerId);
675         header[checked ? 'show' : 'hide']();
676     },
677
678 <span id='Ext-grid-header-Container-method-getColumnsForTpl'>    /**
679 </span>     * Get the columns used for generating a template via TableChunker.
680      * Returns an array of all columns and their
681      *  - dataIndex
682      *  - align
683      *  - width
684      *  - id
685      *  - columnId - used to create an identifying CSS class
686      *  - cls The tdCls configuration from the Column object
687      *  @private
688      */
689     getColumnsForTpl: function(flushCache) {
690         var cols    = [],
691             headers   = this.getGridColumns(flushCache),
692             headersLn = headers.length,
693             i = 0,
694             header,
695             width;
696
697         for (; i &lt; headersLn; i++) {
698             header = headers[i];
699
700             if (header.hidden || header.up('headercontainer[hidden=true]')) {
701                 width = 0;
702             } else {
703                 width = header.getDesiredWidth();
704                 // IE6 and IE7 bug.
705                 // Setting the width of the first TD does not work - ends up with a 1 pixel discrepancy.
706                 // We need to increment the passed with in this case.
707                 if ((i === 0) &amp;&amp; (Ext.isIE6 || Ext.isIE7)) {
708                     width += 1;
709                 }
710             }
711             cols.push({
712                 dataIndex: header.dataIndex,
713                 align: header.align,
714                 width: width,
715                 id: header.id,
716                 cls: header.tdCls,
717                 columnId: header.getItemId()
718             });
719         }
720         return cols;
721     },
722
723 <span id='Ext-grid-header-Container-method-getColumnCount'>    /**
724 </span>     * Returns the number of &lt;b&gt;grid columns&lt;/b&gt; descended from this HeaderContainer.
725      * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones.
726      */
727     getColumnCount: function() {
728         return this.getGridColumns().length;
729     },
730
731 <span id='Ext-grid-header-Container-method-getFullWidth'>    /**
732 </span>     * Gets the full width of all columns that are visible.
733      */
734     getFullWidth: function(flushCache) {
735         var fullWidth = 0,
736             headers     = this.getVisibleGridColumns(flushCache),
737             headersLn   = headers.length,
738             i         = 0;
739
740         for (; i &lt; headersLn; i++) {
741             if (!isNaN(headers[i].width)) {
742                 // use headers getDesiredWidth if its there
743                 if (headers[i].getDesiredWidth) {
744                     fullWidth += headers[i].getDesiredWidth();
745                 // if injected a diff cmp use getWidth
746                 } else {
747                     fullWidth += headers[i].getWidth();
748                 }
749             }
750         }
751         return fullWidth;
752     },
753
754     // invoked internally by a header when not using triStateSorting
755     clearOtherSortStates: function(activeHeader) {
756         var headers   = this.getGridColumns(),
757             headersLn = headers.length,
758             i         = 0,
759             oldSortState;
760
761         for (; i &lt; headersLn; i++) {
762             if (headers[i] !== activeHeader) {
763                 oldSortState = headers[i].sortState;
764                 // unset the sortstate and dont recurse
765                 headers[i].setSortState(null, true);
766                 //if (!silent &amp;&amp; oldSortState !== null) {
767                 //    this.fireEvent('sortchange', this, headers[i], null);
768                 //}
769             }
770         }
771     },
772
773 <span id='Ext-grid-header-Container-method-getVisibleGridColumns'>    /**
774 </span>     * Returns an array of the &lt;b&gt;visible&lt;/b&gt; columns in the grid. This goes down to the lowest column header
775      * level, and does not return &lt;i&gt;grouped&lt;/i&gt; headers which contain sub headers.
776      * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
777      * @returns {Array}
778      */
779     getVisibleGridColumns: function(refreshCache) {
780         return Ext.ComponentQuery.query(':not([hidden])', this.getGridColumns(refreshCache));
781     },
782
783 <span id='Ext-grid-header-Container-method-getGridColumns'>    /**
784 </span>     * Returns an array of all columns which map to Store fields. This goes down to the lowest column header
785      * level, and does not return &lt;i&gt;grouped&lt;/i&gt; headers which contain sub headers.
786      * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
787      * @returns {Array}
788      */
789     getGridColumns: function(refreshCache) {
790         var me = this,
791             result = refreshCache ? null : me.gridDataColumns;
792
793         // Not already got the column cache, so collect the base columns
794         if (!result) {
795             me.gridDataColumns = result = [];
796             me.cascade(function(c) {
797                 if ((c !== me) &amp;&amp; !c.isGroupHeader) {
798                     result.push(c);
799                 }
800             });
801         }
802
803         return result;
804     },
805
806 <span id='Ext-grid-header-Container-method-getHideableColumns'>    /**
807 </span>     * @private
808      * For use by column headers in determining whether there are any hideable columns when deciding whether or not
809      * the header menu should be disabled.
810      */
811     getHideableColumns: function(refreshCache) {
812         var me = this,
813             result = refreshCache ? null : me.hideableColumns;
814
815         if (!result) {
816             result = me.hideableColumns = me.query('[hideable]');
817         }
818         return result;
819     },
820
821 <span id='Ext-grid-header-Container-method-getHeaderIndex'>    /**
822 </span>     * Get the index of a leaf level header regardless of what the nesting
823      * structure is.
824      */
825     getHeaderIndex: function(header) {
826         var columns = this.getGridColumns();
827         return Ext.Array.indexOf(columns, header);
828     },
829
830 <span id='Ext-grid-header-Container-method-getHeaderAtIndex'>    /**
831 </span>     * Get a leaf level header by index regardless of what the nesting
832      * structure is.
833      */
834     getHeaderAtIndex: function(index) {
835         var columns = this.getGridColumns();
836         return columns[index];
837     },
838
839 <span id='Ext-grid-header-Container-method-prepareData'>    /**
840 </span>     * Maps the record data to base it on the header id's.
841      * This correlates to the markup/template generated by
842      * TableChunker.
843      */
844     prepareData: function(data, rowIdx, record, view, panel) {
845         var obj       = {},
846             headers   = this.gridDataColumns || this.getGridColumns(),
847             headersLn = headers.length,
848             colIdx    = 0,
849             header,
850             headerId,
851             renderer,
852             value,
853             metaData,
854             store = panel.store;
855
856         for (; colIdx &lt; headersLn; colIdx++) {
857             metaData = {
858                 tdCls: '',
859                 style: ''
860             };
861             header = headers[colIdx];
862             headerId = header.id;
863             renderer = header.renderer;
864             value = data[header.dataIndex];
865
866             // When specifying a renderer as a string, it always resolves
867             // to Ext.util.Format
868             if (typeof renderer === &quot;string&quot;) {
869                 header.renderer = renderer = Ext.util.Format[renderer];
870             }
871
872             if (typeof renderer === &quot;function&quot;) {
873                 value = renderer.call(
874                     header.scope || this.ownerCt,
875                     value,
876                     // metadata per cell passing an obj by reference so that
877                     // it can be manipulated inside the renderer
878                     metaData,
879                     record,
880                     rowIdx,
881                     colIdx,
882                     store,
883                     view
884                 );
885             }
886
887             // &lt;debug&gt;
888             if (metaData.css) {
889                 // This warning attribute is used by the compat layer
890                 obj.cssWarning = true;
891                 metaData.tdCls = metaData.css;
892                 delete metaData.css;
893             }
894             // &lt;/debug&gt;
895
896             obj[headerId+'-modified'] = record.isModified(header.dataIndex) ? Ext.baseCSSPrefix + 'grid-dirty-cell' : '';
897             obj[headerId+'-tdCls'] = metaData.tdCls;
898             obj[headerId+'-tdAttr'] = metaData.tdAttr;
899             obj[headerId+'-style'] = metaData.style;
900             if (value === undefined || value === null || value === '') {
901                 value = '&amp;#160;';
902             }
903             obj[headerId] = value;
904         }
905         return obj;
906     },
907
908     expandToFit: function(header) {
909         if (this.view) {
910             this.view.expandToFit(header);
911         }
912     }
913 });
914 </pre>
915 </body>
916 </html>