Upgrade to ExtJS 3.2.2 - Released 06/02/2010
[extjs.git] / pkgs / pkg-grid-foundation-debug.js
1 /*!
2  * Ext JS Library 3.2.2
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.grid.GridPanel
9  * @extends Ext.Panel
10  * <p>This class represents the primary interface of a component based grid control to represent data
11  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>
12  * <div class="mdetail-params"><ul>
13  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)
14  * <div class="sub-desc"></div></li>
15  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup
16  * <div class="sub-desc"></div></li>
17  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface
18  * <div class="sub-desc"></div></li>
19  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior
20  * <div class="sub-desc"></div></li>
21  * </ul></div>
22  * <p>Example usage:</p>
23  * <pre><code>
24 var grid = new Ext.grid.GridPanel({
25     {@link #store}: new {@link Ext.data.Store}({
26         {@link Ext.data.Store#autoDestroy autoDestroy}: true,
27         {@link Ext.data.Store#reader reader}: reader,
28         {@link Ext.data.Store#data data}: xg.dummyData
29     }),
30     {@link #colModel}: new {@link Ext.grid.ColumnModel}({
31         {@link Ext.grid.ColumnModel#defaults defaults}: {
32             width: 120,
33             sortable: true
34         },
35         {@link Ext.grid.ColumnModel#columns columns}: [
36             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},
37             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
38             {header: 'Change', dataIndex: 'change'},
39             {header: '% Change', dataIndex: 'pctChange'},
40             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype
41             {
42                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',
43                 xtype: 'datecolumn', format: 'M d, Y'
44             }
45         ],
46     }),
47     {@link #viewConfig}: {
48         {@link Ext.grid.GridView#forceFit forceFit}: true,
49
50 //      Return CSS class to apply to rows depending upon data values
51         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {
52             var c = record.{@link Ext.data.Record#get get}('change');
53             if (c < 0) {
54                 return 'price-fall';
55             } else if (c > 0) {
56                 return 'price-rise';
57             }
58         }
59     },
60     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),
61     width: 600,
62     height: 300,
63     frame: true,
64     title: 'Framed with Row Selection and Horizontal Scrolling',
65     iconCls: 'icon-grid'
66 });
67  * </code></pre>
68  * <p><b><u>Notes:</u></b></p>
69  * <div class="mdetail-params"><ul>
70  * <li>Although this class inherits many configuration options from base classes, some of them
71  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will
72  * have no effect.</li>
73  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to
74  * scroll its rows. These dimensions can either be set explicitly through the
75  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>
76  * configuration options or implicitly set by using the grid as a child item of a
77  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}
78  * provide the sizing of its child items (for example the Container of the Grid may specify
79  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>
80  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated
81  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>
82  * </ul></div>
83  * @constructor
84  * @param {Object} config The config object
85  * @xtype grid
86  */
87 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {
88     /**
89      * @cfg {String} autoExpandColumn
90      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in
91      * this grid that should expand to fill unused space. This value specified here can not
92      * be <tt>0</tt>.</p>
93      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with
94      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>
95      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>
96      * for additional details.</p>
97      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>
98      */
99     autoExpandColumn : false,
100     /**
101      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>
102      * can have (if enabled). Defaults to <tt>1000</tt>.
103      */
104     autoExpandMax : 1000,
105     /**
106      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>
107      * can have (if enabled). Defaults to <tt>50</tt>.
108      */
109     autoExpandMin : 50,
110     /**
111      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.
112      * Default is <tt>false</tt>.
113      */
114     columnLines : false,
115     /**
116      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.
117      */
118     /**
119      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).
120      */
121     /**
122      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a
123      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the
124      * <tt>{@link #colModel}</tt> configuration property.
125      */
126     /**
127      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.
128      */
129     /**
130      * @cfg {String} ddText
131      * Configures the text in the drag proxy.  Defaults to:
132      * <pre><code>
133      * ddText : '{0} selected row{1}'
134      * </code></pre>
135      * <tt>{0}</tt> is replaced with the number of selected rows.
136      */
137     ddText : '{0} selected row{1}',
138     /**
139      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>
140      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row
141      * structure deferred so that layouts with GridPanels appear more quickly.</p>
142      */
143     deferRowRender : true,
144     /**
145      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>
146      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>
147      */
148     /**
149      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.
150      */
151     /**
152      * @cfg {Boolean} enableColumnHide
153      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}
154      * with the {@link #enableHdMenu header menu}.
155      */
156     enableColumnHide : true,
157     /**
158      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>
159      * to turn off column reordering via drag drop.
160      */
161     enableColumnMove : true,
162     /**
163      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>
164      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to
165      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>
166      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>
167      * property.</p>
168      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of
169      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
170      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able
171      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>
172      */
173     enableDragDrop : false,
174     /**
175      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.
176      */
177     enableHdMenu : true,
178     /**
179      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.
180      */
181     /**
182      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while
183      * loading. Defaults to <code>false</code>.
184      */
185     loadMask : false,
186     /**
187      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.
188      */
189     /**
190      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.
191      */
192     minColumnWidth : 25,
193     /**
194      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.
195      */
196     /**
197      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide
198      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).
199      */
200     /**
201      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).
202      */
203     /**
204      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
205      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of
206      * the grid. A default CSS rule is provided which sets a background colour, but you can override this
207      * with a rule which either overrides the <b>background-color</b> style using the '!important'
208      * modifier, or which uses a CSS selector of higher specificity.</p>
209      */
210     stripeRows : false,
211     /**
212      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>
213      * for GridPanel, but <tt>false</tt> for EditorGridPanel.
214      */
215     trackMouseOver : true,
216     /**
217      * @cfg {Array} stateEvents
218      * An array of events that, when fired, should trigger this component to save its state.
219      * Defaults to:<pre><code>
220      * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']
221      * </code></pre>
222      * <p>These can be any types of events supported by this component, including browser or
223      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>
224      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring
225      * Component state.</p>
226      */
227     stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
228     /**
229      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set
230      * before a call to {@link Ext.Component#render render()}.
231      */
232     view : null,
233
234     /**
235      * @cfg {Array} bubbleEvents
236      * <p>An array of events that, when fired, should be bubbled to any parent container.
237      * See {@link Ext.util.Observable#enableBubble}.
238      * Defaults to <tt>[]</tt>.
239      */
240     bubbleEvents: [],
241
242     /**
243      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of
244      * the config options available for {@link Ext.grid.GridView} can be specified here. This option
245      * is ignored if <tt>{@link #view}</tt> is specified.
246      */
247
248     // private
249     rendered : false,
250     // private
251     viewReady : false,
252
253     // private
254     initComponent : function(){
255         Ext.grid.GridPanel.superclass.initComponent.call(this);
256
257         if(this.columnLines){
258             this.cls = (this.cls || '') + ' x-grid-with-col-lines';
259         }
260         // override any provided value since it isn't valid
261         // and is causing too many bug reports ;)
262         this.autoScroll = false;
263         this.autoWidth = false;
264
265         if(Ext.isArray(this.columns)){
266             this.colModel = new Ext.grid.ColumnModel(this.columns);
267             delete this.columns;
268         }
269
270         // check and correct shorthanded configs
271         if(this.ds){
272             this.store = this.ds;
273             delete this.ds;
274         }
275         if(this.cm){
276             this.colModel = this.cm;
277             delete this.cm;
278         }
279         if(this.sm){
280             this.selModel = this.sm;
281             delete this.sm;
282         }
283         this.store = Ext.StoreMgr.lookup(this.store);
284
285         this.addEvents(
286             // raw events
287             /**
288              * @event click
289              * The raw click event for the entire grid.
290              * @param {Ext.EventObject} e
291              */
292             'click',
293             /**
294              * @event dblclick
295              * The raw dblclick event for the entire grid.
296              * @param {Ext.EventObject} e
297              */
298             'dblclick',
299             /**
300              * @event contextmenu
301              * The raw contextmenu event for the entire grid.
302              * @param {Ext.EventObject} e
303              */
304             'contextmenu',
305             /**
306              * @event mousedown
307              * The raw mousedown event for the entire grid.
308              * @param {Ext.EventObject} e
309              */
310             'mousedown',
311             /**
312              * @event mouseup
313              * The raw mouseup event for the entire grid.
314              * @param {Ext.EventObject} e
315              */
316             'mouseup',
317             /**
318              * @event mouseover
319              * The raw mouseover event for the entire grid.
320              * @param {Ext.EventObject} e
321              */
322             'mouseover',
323             /**
324              * @event mouseout
325              * The raw mouseout event for the entire grid.
326              * @param {Ext.EventObject} e
327              */
328             'mouseout',
329             /**
330              * @event keypress
331              * The raw keypress event for the entire grid.
332              * @param {Ext.EventObject} e
333              */
334             'keypress',
335             /**
336              * @event keydown
337              * The raw keydown event for the entire grid.
338              * @param {Ext.EventObject} e
339              */
340             'keydown',
341
342             // custom events
343             /**
344              * @event cellmousedown
345              * Fires before a cell is clicked
346              * @param {Grid} this
347              * @param {Number} rowIndex
348              * @param {Number} columnIndex
349              * @param {Ext.EventObject} e
350              */
351             'cellmousedown',
352             /**
353              * @event rowmousedown
354              * Fires before a row is clicked
355              * @param {Grid} this
356              * @param {Number} rowIndex
357              * @param {Ext.EventObject} e
358              */
359             'rowmousedown',
360             /**
361              * @event headermousedown
362              * Fires before a header is clicked
363              * @param {Grid} this
364              * @param {Number} columnIndex
365              * @param {Ext.EventObject} e
366              */
367             'headermousedown',
368
369             /**
370              * @event groupmousedown
371              * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
372              * @param {Grid} this
373              * @param {String} groupField
374              * @param {String} groupValue
375              * @param {Ext.EventObject} e
376              */
377             'groupmousedown',
378
379             /**
380              * @event rowbodymousedown
381              * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
382              * @param {Grid} this
383              * @param {Number} rowIndex
384              * @param {Ext.EventObject} e
385              */
386             'rowbodymousedown',
387
388             /**
389              * @event containermousedown
390              * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.
391              * @param {Grid} this
392              * @param {Ext.EventObject} e
393              */
394             'containermousedown',
395
396             /**
397              * @event cellclick
398              * Fires when a cell is clicked.
399              * The data for the cell is drawn from the {@link Ext.data.Record Record}
400              * for this row. To access the data in the listener function use the
401              * following technique:
402              * <pre><code>
403 function(grid, rowIndex, columnIndex, e) {
404     var record = grid.getStore().getAt(rowIndex);  // Get the Record
405     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
406     var data = record.get(fieldName);
407 }
408 </code></pre>
409              * @param {Grid} this
410              * @param {Number} rowIndex
411              * @param {Number} columnIndex
412              * @param {Ext.EventObject} e
413              */
414             'cellclick',
415             /**
416              * @event celldblclick
417              * Fires when a cell is double clicked
418              * @param {Grid} this
419              * @param {Number} rowIndex
420              * @param {Number} columnIndex
421              * @param {Ext.EventObject} e
422              */
423             'celldblclick',
424             /**
425              * @event rowclick
426              * Fires when a row is clicked
427              * @param {Grid} this
428              * @param {Number} rowIndex
429              * @param {Ext.EventObject} e
430              */
431             'rowclick',
432             /**
433              * @event rowdblclick
434              * Fires when a row is double clicked
435              * @param {Grid} this
436              * @param {Number} rowIndex
437              * @param {Ext.EventObject} e
438              */
439             'rowdblclick',
440             /**
441              * @event headerclick
442              * Fires when a header is clicked
443              * @param {Grid} this
444              * @param {Number} columnIndex
445              * @param {Ext.EventObject} e
446              */
447             'headerclick',
448             /**
449              * @event headerdblclick
450              * Fires when a header cell is double clicked
451              * @param {Grid} this
452              * @param {Number} columnIndex
453              * @param {Ext.EventObject} e
454              */
455             'headerdblclick',
456             /**
457              * @event groupclick
458              * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
459              * @param {Grid} this
460              * @param {String} groupField
461              * @param {String} groupValue
462              * @param {Ext.EventObject} e
463              */
464             'groupclick',
465             /**
466              * @event groupdblclick
467              * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
468              * @param {Grid} this
469              * @param {String} groupField
470              * @param {String} groupValue
471              * @param {Ext.EventObject} e
472              */
473             'groupdblclick',
474             /**
475              * @event containerclick
476              * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.
477              * @param {Grid} this
478              * @param {Ext.EventObject} e
479              */
480             'containerclick',
481             /**
482              * @event containerdblclick
483              * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.
484              * @param {Grid} this
485              * @param {Ext.EventObject} e
486              */
487             'containerdblclick',
488
489             /**
490              * @event rowbodyclick
491              * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
492              * @param {Grid} this
493              * @param {Number} rowIndex
494              * @param {Ext.EventObject} e
495              */
496             'rowbodyclick',
497             /**
498              * @event rowbodydblclick
499              * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
500              * @param {Grid} this
501              * @param {Number} rowIndex
502              * @param {Ext.EventObject} e
503              */
504             'rowbodydblclick',
505
506             /**
507              * @event rowcontextmenu
508              * Fires when a row is right clicked
509              * @param {Grid} this
510              * @param {Number} rowIndex
511              * @param {Ext.EventObject} e
512              */
513             'rowcontextmenu',
514             /**
515              * @event cellcontextmenu
516              * Fires when a cell is right clicked
517              * @param {Grid} this
518              * @param {Number} rowIndex
519              * @param {Number} cellIndex
520              * @param {Ext.EventObject} e
521              */
522             'cellcontextmenu',
523             /**
524              * @event headercontextmenu
525              * Fires when a header is right clicked
526              * @param {Grid} this
527              * @param {Number} columnIndex
528              * @param {Ext.EventObject} e
529              */
530             'headercontextmenu',
531             /**
532              * @event groupcontextmenu
533              * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
534              * @param {Grid} this
535              * @param {String} groupField
536              * @param {String} groupValue
537              * @param {Ext.EventObject} e
538              */
539             'groupcontextmenu',
540             /**
541              * @event containercontextmenu
542              * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.
543              * @param {Grid} this
544              * @param {Ext.EventObject} e
545              */
546             'containercontextmenu',
547             /**
548              * @event rowbodycontextmenu
549              * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
550              * @param {Grid} this
551              * @param {Number} rowIndex
552              * @param {Ext.EventObject} e
553              */
554             'rowbodycontextmenu',
555             /**
556              * @event bodyscroll
557              * Fires when the body element is scrolled
558              * @param {Number} scrollLeft
559              * @param {Number} scrollTop
560              */
561             'bodyscroll',
562             /**
563              * @event columnresize
564              * Fires when the user resizes a column
565              * @param {Number} columnIndex
566              * @param {Number} newSize
567              */
568             'columnresize',
569             /**
570              * @event columnmove
571              * Fires when the user moves a column
572              * @param {Number} oldIndex
573              * @param {Number} newIndex
574              */
575             'columnmove',
576             /**
577              * @event sortchange
578              * Fires when the grid's store sort changes
579              * @param {Grid} this
580              * @param {Object} sortInfo An object with the keys field and direction
581              */
582             'sortchange',
583             /**
584              * @event groupchange
585              * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})
586              * @param {Grid} this
587              * @param {String} groupField A string with the grouping field, null if the store is not grouped.
588              */
589             'groupchange',
590             /**
591              * @event reconfigure
592              * Fires when the grid is reconfigured with a new store and/or column model.
593              * @param {Grid} this
594              * @param {Ext.data.Store} store The new store
595              * @param {Ext.grid.ColumnModel} colModel The new column model
596              */
597             'reconfigure',
598             /**
599              * @event viewready
600              * Fires when the grid view is available (use this for selecting a default row).
601              * @param {Grid} this
602              */
603             'viewready'
604         );
605     },
606
607     // private
608     onRender : function(ct, position){
609         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);
610
611         var c = this.getGridEl();
612
613         this.el.addClass('x-grid-panel');
614
615         this.mon(c, {
616             scope: this,
617             mousedown: this.onMouseDown,
618             click: this.onClick,
619             dblclick: this.onDblClick,
620             contextmenu: this.onContextMenu
621         });
622
623         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);
624
625         var view = this.getView();
626         view.init(this);
627         view.render();
628         this.getSelectionModel().init(this);
629     },
630
631     // private
632     initEvents : function(){
633         Ext.grid.GridPanel.superclass.initEvents.call(this);
634
635         if(this.loadMask){
636             this.loadMask = new Ext.LoadMask(this.bwrap,
637                     Ext.apply({store:this.store}, this.loadMask));
638         }
639     },
640
641     initStateEvents : function(){
642         Ext.grid.GridPanel.superclass.initStateEvents.call(this);
643         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});
644     },
645
646     applyState : function(state){
647         var cm = this.colModel,
648             cs = state.columns,
649             store = this.store,
650             s,
651             c,
652             oldIndex;
653
654         if(cs){
655             for(var i = 0, len = cs.length; i < len; i++){
656                 s = cs[i];
657                 c = cm.getColumnById(s.id);
658                 if(c){
659                     cm.setState(i, {
660                         hidden: s.hidden,
661                         width: s.width    
662                     });
663                     oldIndex = cm.getIndexById(s.id);
664                     if(oldIndex != i){
665                         cm.moveColumn(oldIndex, i);
666                     }
667                 }
668             }
669         }
670         if(store){
671             s = state.sort;
672             if(s){
673                 store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
674             }
675             s = state.group;
676             if(store.groupBy){
677                 if(s){
678                     store.groupBy(s);
679                 }else{
680                     store.clearGrouping();
681                 }
682             }
683
684         }
685         var o = Ext.apply({}, state);
686         delete o.columns;
687         delete o.sort;
688         Ext.grid.GridPanel.superclass.applyState.call(this, o);
689     },
690
691     getState : function(){
692         var o = {columns: []},
693             store = this.store,
694             ss,
695             gs;
696
697         for(var i = 0, c; (c = this.colModel.config[i]); i++){
698             o.columns[i] = {
699                 id: c.id,
700                 width: c.width
701             };
702             if(c.hidden){
703                 o.columns[i].hidden = true;
704             }
705         }
706         if(store){
707             ss = store.getSortState();
708             if(ss){
709                 o.sort = ss;
710             }
711             if(store.getGroupState){
712                 gs = store.getGroupState();
713                 if(gs){
714                     o.group = gs;
715                 }
716             }
717         }
718         return o;
719     },
720
721     // private
722     afterRender : function(){
723         Ext.grid.GridPanel.superclass.afterRender.call(this);
724         var v = this.view;
725         this.on('bodyresize', v.layout, v);
726         v.layout();
727         if(this.deferRowRender){
728             if (!this.deferRowRenderTask){
729                 this.deferRowRenderTask = new Ext.util.DelayedTask(v.afterRender, this.view);
730             }
731             this.deferRowRenderTask.delay(10);
732         }else{
733             v.afterRender();
734         }
735         this.viewReady = true;
736     },
737
738     /**
739      * <p>Reconfigures the grid to use a different Store and Column Model
740      * and fires the 'reconfigure' event. The View will be bound to the new
741      * objects and refreshed.</p>
742      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
743      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
744      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
745      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
746      * with the new data.</p>
747      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
748      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
749      */
750     reconfigure : function(store, colModel){
751         var rendered = this.rendered;
752         if(rendered){
753             if(this.loadMask){
754                 this.loadMask.destroy();
755                 this.loadMask = new Ext.LoadMask(this.bwrap,
756                         Ext.apply({}, {store:store}, this.initialConfig.loadMask));
757             }
758         }
759         if(this.view){
760             this.view.initData(store, colModel);
761         }
762         this.store = store;
763         this.colModel = colModel;
764         if(rendered){
765             this.view.refresh(true);
766         }
767         this.fireEvent('reconfigure', this, store, colModel);
768     },
769
770     // private
771     onDestroy : function(){
772         if (this.deferRowRenderTask && this.deferRowRenderTask.cancel){
773             this.deferRowRenderTask.cancel();
774         }
775         if(this.rendered){
776             Ext.destroy(this.view, this.loadMask);
777         }else if(this.store && this.store.autoDestroy){
778             this.store.destroy();
779         }
780         Ext.destroy(this.colModel, this.selModel);
781         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
782         Ext.grid.GridPanel.superclass.onDestroy.call(this);
783     },
784
785     // private
786     processEvent : function(name, e){
787         this.view.processEvent(name, e);
788     },
789
790     // private
791     onClick : function(e){
792         this.processEvent('click', e);
793     },
794
795     // private
796     onMouseDown : function(e){
797         this.processEvent('mousedown', e);
798     },
799
800     // private
801     onContextMenu : function(e, t){
802         this.processEvent('contextmenu', e);
803     },
804
805     // private
806     onDblClick : function(e){
807         this.processEvent('dblclick', e);
808     },
809
810     // private
811     walkCells : function(row, col, step, fn, scope){
812         var cm    = this.colModel,
813             clen  = cm.getColumnCount(),
814             ds    = this.store,
815             rlen  = ds.getCount(),
816             first = true;
817
818         if(step < 0){
819             if(col < 0){
820                 row--;
821                 first = false;
822             }
823             while(row >= 0){
824                 if(!first){
825                     col = clen-1;
826                 }
827                 first = false;
828                 while(col >= 0){
829                     if(fn.call(scope || this, row, col, cm) === true){
830                         return [row, col];
831                     }
832                     col--;
833                 }
834                 row--;
835             }
836         } else {
837             if(col >= clen){
838                 row++;
839                 first = false;
840             }
841             while(row < rlen){
842                 if(!first){
843                     col = 0;
844                 }
845                 first = false;
846                 while(col < clen){
847                     if(fn.call(scope || this, row, col, cm) === true){
848                         return [row, col];
849                     }
850                     col++;
851                 }
852                 row++;
853             }
854         }
855         return null;
856     },
857
858     /**
859      * Returns the grid's underlying element.
860      * @return {Element} The element
861      */
862     getGridEl : function(){
863         return this.body;
864     },
865
866     // private for compatibility, overridden by editor grid
867     stopEditing : Ext.emptyFn,
868
869     /**
870      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
871      * configuration option. If no selection model was configured, this will create
872      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
873      * @return {SelectionModel}
874      */
875     getSelectionModel : function(){
876         if(!this.selModel){
877             this.selModel = new Ext.grid.RowSelectionModel(
878                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);
879         }
880         return this.selModel;
881     },
882
883     /**
884      * Returns the grid's data store.
885      * @return {Ext.data.Store} The store
886      */
887     getStore : function(){
888         return this.store;
889     },
890
891     /**
892      * Returns the grid's ColumnModel.
893      * @return {Ext.grid.ColumnModel} The column model
894      */
895     getColumnModel : function(){
896         return this.colModel;
897     },
898
899     /**
900      * Returns the grid's GridView object.
901      * @return {Ext.grid.GridView} The grid view
902      */
903     getView : function(){
904         if(!this.view){
905             this.view = new Ext.grid.GridView(this.viewConfig);
906         }
907         return this.view;
908     },
909     /**
910      * Called to get grid's drag proxy text, by default returns this.ddText.
911      * @return {String} The text
912      */
913     getDragDropText : function(){
914         var count = this.selModel.getCount();
915         return String.format(this.ddText, count, count == 1 ? '' : 's');
916     }
917
918     /**
919      * @cfg {String/Number} activeItem
920      * @hide
921      */
922     /**
923      * @cfg {Boolean} autoDestroy
924      * @hide
925      */
926     /**
927      * @cfg {Object/String/Function} autoLoad
928      * @hide
929      */
930     /**
931      * @cfg {Boolean} autoWidth
932      * @hide
933      */
934     /**
935      * @cfg {Boolean/Number} bufferResize
936      * @hide
937      */
938     /**
939      * @cfg {String} defaultType
940      * @hide
941      */
942     /**
943      * @cfg {Object} defaults
944      * @hide
945      */
946     /**
947      * @cfg {Boolean} hideBorders
948      * @hide
949      */
950     /**
951      * @cfg {Mixed} items
952      * @hide
953      */
954     /**
955      * @cfg {String} layout
956      * @hide
957      */
958     /**
959      * @cfg {Object} layoutConfig
960      * @hide
961      */
962     /**
963      * @cfg {Boolean} monitorResize
964      * @hide
965      */
966     /**
967      * @property items
968      * @hide
969      */
970     /**
971      * @method add
972      * @hide
973      */
974     /**
975      * @method cascade
976      * @hide
977      */
978     /**
979      * @method doLayout
980      * @hide
981      */
982     /**
983      * @method find
984      * @hide
985      */
986     /**
987      * @method findBy
988      * @hide
989      */
990     /**
991      * @method findById
992      * @hide
993      */
994     /**
995      * @method findByType
996      * @hide
997      */
998     /**
999      * @method getComponent
1000      * @hide
1001      */
1002     /**
1003      * @method getLayout
1004      * @hide
1005      */
1006     /**
1007      * @method getUpdater
1008      * @hide
1009      */
1010     /**
1011      * @method insert
1012      * @hide
1013      */
1014     /**
1015      * @method load
1016      * @hide
1017      */
1018     /**
1019      * @method remove
1020      * @hide
1021      */
1022     /**
1023      * @event add
1024      * @hide
1025      */
1026     /**
1027      * @event afterlayout
1028      * @hide
1029      */
1030     /**
1031      * @event beforeadd
1032      * @hide
1033      */
1034     /**
1035      * @event beforeremove
1036      * @hide
1037      */
1038     /**
1039      * @event remove
1040      * @hide
1041      */
1042
1043
1044
1045     /**
1046      * @cfg {String} allowDomMove  @hide
1047      */
1048     /**
1049      * @cfg {String} autoEl @hide
1050      */
1051     /**
1052      * @cfg {String} applyTo  @hide
1053      */
1054     /**
1055      * @cfg {String} autoScroll  @hide
1056      */
1057     /**
1058      * @cfg {String} bodyBorder  @hide
1059      */
1060     /**
1061      * @cfg {String} bodyStyle  @hide
1062      */
1063     /**
1064      * @cfg {String} contentEl  @hide
1065      */
1066     /**
1067      * @cfg {String} disabledClass  @hide
1068      */
1069     /**
1070      * @cfg {String} elements  @hide
1071      */
1072     /**
1073      * @cfg {String} html  @hide
1074      */
1075     /**
1076      * @cfg {Boolean} preventBodyReset
1077      * @hide
1078      */
1079     /**
1080      * @property disabled
1081      * @hide
1082      */
1083     /**
1084      * @method applyToMarkup
1085      * @hide
1086      */
1087     /**
1088      * @method enable
1089      * @hide
1090      */
1091     /**
1092      * @method disable
1093      * @hide
1094      */
1095     /**
1096      * @method setDisabled
1097      * @hide
1098      */
1099 });
1100 Ext.reg('grid', Ext.grid.GridPanel);/**
1101  * @class Ext.grid.GridView
1102  * @extends Ext.util.Observable
1103  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
1104  * Methods of this class may be used to access user interface elements to enable
1105  * special display effects. Do not change the DOM structure of the user interface.</p>
1106  * <p>This class does not provide ways to manipulate the underlying data. The data
1107  * model of a Grid is held in an {@link Ext.data.Store}.</p>
1108  * @constructor
1109  * @param {Object} config
1110  */
1111 Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
1112     /**
1113      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
1114      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
1115      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
1116      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
1117      * (e.g., 'my-class another-class'). Example usage:
1118     <pre><code>
1119 viewConfig: {
1120     forceFit: true,
1121     showPreview: true, // custom property
1122     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
1123     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
1124         if(this.showPreview){
1125             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
1126             return 'x-grid3-row-expanded';
1127         }
1128         return 'x-grid3-row-collapsed';
1129     }
1130 },
1131     </code></pre>
1132      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
1133      * @param {Number} index The row index.
1134      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
1135      * customization of various aspects of a grid row.
1136      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
1137      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
1138      * <ul>
1139      * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
1140      * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
1141      * </ul>
1142      * The following property will be passed in, and may be appended to:
1143      * <ul>
1144      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
1145      * both the standard grid row, and any expansion row.</div></li>
1146      * </ul>
1147      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
1148      * @method getRowClass
1149      * @return {String} a CSS class name to add to the row.
1150      */
1151
1152     /**
1153      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
1154      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
1155      */
1156
1157     /**
1158      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
1159      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
1160     <pre><code>
1161     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
1162     </code></pre>
1163      */
1164
1165     /**
1166      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).
1167      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
1168      * config to disable the <i>menu</i> for individual columns.  While this config is true the
1169      * following will be disabled:<div class="mdetail-params"><ul>
1170      * <li>clicking on header to sort</li>
1171      * <li>the trigger to reveal the menu.</li>
1172      * </ul></div>
1173      */
1174
1175     /**
1176      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
1177      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
1178      * See {@link Ext.grid.GridDragZone} for details.</p>
1179      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
1180      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
1181      * <li><i>after</i> the owning GridPanel has been rendered.</li>
1182      * </ul></div>
1183      * @property dragZone
1184      * @type {Ext.grid.GridDragZone}
1185      */
1186
1187     /**
1188      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
1189      * first load (defaults to <tt>true</tt>).
1190      */
1191     deferEmptyText : true,
1192
1193     /**
1194      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
1195      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
1196      * calculated.
1197      */
1198     scrollOffset : undefined,
1199
1200     /**
1201      * @cfg {Boolean} autoFill
1202      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
1203      * when the grid is <b>initially rendered</b>.  The
1204      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
1205      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
1206      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
1207      * See <tt>{@link #forceFit}</tt> also.
1208      */
1209     autoFill : false,
1210
1211     /**
1212      * @cfg {Boolean} forceFit
1213      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
1214      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
1215      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
1216      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
1217      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
1218      */
1219     forceFit : false,
1220
1221     /**
1222      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
1223      */
1224     sortClasses : ['sort-asc', 'sort-desc'],
1225
1226     /**
1227      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
1228      */
1229     sortAscText : 'Sort Ascending',
1230
1231     /**
1232      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
1233      */
1234     sortDescText : 'Sort Descending',
1235
1236     /**
1237      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
1238      */
1239     columnsText : 'Columns',
1240
1241     /**
1242      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
1243      * example overriding the default styling:
1244     <pre><code>
1245     .x-grid3-row-selected {background-color: yellow;}
1246     </code></pre>
1247      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
1248      * facets (like text) use something like:
1249     <pre><code>
1250     .x-grid3-row-selected .x-grid3-cell-inner {
1251         color: #FFCC00;
1252     }
1253     </code></pre>
1254      * @type String
1255      */
1256     selectedRowClass : 'x-grid3-row-selected',
1257
1258     // private
1259     borderWidth : 2,
1260     tdClass : 'x-grid3-cell',
1261     hdCls : 'x-grid3-hd',
1262     markDirty : true,
1263
1264     /**
1265      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
1266      */
1267     cellSelectorDepth : 4,
1268     /**
1269      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
1270      */
1271     rowSelectorDepth : 10,
1272
1273     /**
1274      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
1275      */
1276     rowBodySelectorDepth : 10,
1277
1278     /**
1279      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
1280      */
1281     cellSelector : 'td.x-grid3-cell',
1282     /**
1283      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
1284      */
1285     rowSelector : 'div.x-grid3-row',
1286
1287     /**
1288      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
1289      */
1290     rowBodySelector : 'div.x-grid3-row-body',
1291
1292     // private
1293     firstRowCls: 'x-grid3-row-first',
1294     lastRowCls: 'x-grid3-row-last',
1295     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
1296
1297     constructor : function(config){
1298         Ext.apply(this, config);
1299         // These events are only used internally by the grid components
1300         this.addEvents(
1301             /**
1302              * @event beforerowremoved
1303              * Internal UI Event. Fired before a row is removed.
1304              * @param {Ext.grid.GridView} view
1305              * @param {Number} rowIndex The index of the row to be removed.
1306              * @param {Ext.data.Record} record The Record to be removed
1307              */
1308             'beforerowremoved',
1309             /**
1310              * @event beforerowsinserted
1311              * Internal UI Event. Fired before rows are inserted.
1312              * @param {Ext.grid.GridView} view
1313              * @param {Number} firstRow The index of the first row to be inserted.
1314              * @param {Number} lastRow The index of the last row to be inserted.
1315              */
1316             'beforerowsinserted',
1317             /**
1318              * @event beforerefresh
1319              * Internal UI Event. Fired before the view is refreshed.
1320              * @param {Ext.grid.GridView} view
1321              */
1322             'beforerefresh',
1323             /**
1324              * @event rowremoved
1325              * Internal UI Event. Fired after a row is removed.
1326              * @param {Ext.grid.GridView} view
1327              * @param {Number} rowIndex The index of the row that was removed.
1328              * @param {Ext.data.Record} record The Record that was removed
1329              */
1330             'rowremoved',
1331             /**
1332              * @event rowsinserted
1333              * Internal UI Event. Fired after rows are inserted.
1334              * @param {Ext.grid.GridView} view
1335              * @param {Number} firstRow The index of the first inserted.
1336              * @param {Number} lastRow The index of the last row inserted.
1337              */
1338             'rowsinserted',
1339             /**
1340              * @event rowupdated
1341              * Internal UI Event. Fired after a row has been updated.
1342              * @param {Ext.grid.GridView} view
1343              * @param {Number} firstRow The index of the row updated.
1344              * @param {Ext.data.record} record The Record backing the row updated.
1345              */
1346             'rowupdated',
1347             /**
1348              * @event refresh
1349              * Internal UI Event. Fired after the GridView's body has been refreshed.
1350              * @param {Ext.grid.GridView} view
1351              */
1352             'refresh'
1353         );
1354         Ext.grid.GridView.superclass.constructor.call(this);
1355     },
1356
1357     /* -------------------------------- UI Specific ----------------------------- */
1358
1359     // private
1360     initTemplates : function(){
1361         var ts = this.templates || {};
1362         if(!ts.master){
1363             ts.master = new Ext.Template(
1364                 '<div class="x-grid3" hidefocus="true">',
1365                     '<div class="x-grid3-viewport">',
1366                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
1367                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
1368                     '</div>',
1369                     '<div class="x-grid3-resize-marker">&#160;</div>',
1370                     '<div class="x-grid3-resize-proxy">&#160;</div>',
1371                 '</div>'
1372             );
1373         }
1374
1375         if(!ts.header){
1376             ts.header = new Ext.Template(
1377                 '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
1378                 '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
1379                 '</table>'
1380             );
1381         }
1382
1383         if(!ts.hcell){
1384             ts.hcell = new Ext.Template(
1385                 '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
1386                 '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
1387                 '</div></td>'
1388             );
1389         }
1390
1391         if(!ts.body){
1392             ts.body = new Ext.Template('{rows}');
1393         }
1394
1395         if(!ts.row){
1396             ts.row = new Ext.Template(
1397                 '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
1398                 '<tbody><tr>{cells}</tr>',
1399                 (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
1400                 '</tbody></table></div>'
1401             );
1402         }
1403
1404         if(!ts.cell){
1405             ts.cell = new Ext.Template(
1406                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
1407                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
1408                     '</td>'
1409                     );
1410         }
1411
1412         for(var k in ts){
1413             var t = ts[k];
1414             if(t && Ext.isFunction(t.compile) && !t.compiled){
1415                 t.disableFormats = true;
1416                 t.compile();
1417             }
1418         }
1419
1420         this.templates = ts;
1421         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
1422     },
1423
1424     // private
1425     fly : function(el){
1426         if(!this._flyweight){
1427             this._flyweight = new Ext.Element.Flyweight(document.body);
1428         }
1429         this._flyweight.dom = el;
1430         return this._flyweight;
1431     },
1432
1433     // private
1434     getEditorParent : function(){
1435         return this.scroller.dom;
1436     },
1437
1438     // private
1439     initElements : function(){
1440         var E = Ext.Element;
1441
1442         var el = this.grid.getGridEl().dom.firstChild;
1443         var cs = el.childNodes;
1444
1445         this.el = new E(el);
1446
1447         this.mainWrap = new E(cs[0]);
1448         this.mainHd = new E(this.mainWrap.dom.firstChild);
1449
1450         if(this.grid.hideHeaders){
1451             this.mainHd.setDisplayed(false);
1452         }
1453
1454         this.innerHd = this.mainHd.dom.firstChild;
1455         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
1456         if(this.forceFit){
1457             this.scroller.setStyle('overflow-x', 'hidden');
1458         }
1459         /**
1460          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
1461          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
1462          * @type Ext.Element
1463          * @property mainBody
1464          */
1465         this.mainBody = new E(this.scroller.dom.firstChild);
1466
1467         this.focusEl = new E(this.scroller.dom.childNodes[1]);
1468         this.focusEl.swallowEvent('click', true);
1469
1470         this.resizeMarker = new E(cs[1]);
1471         this.resizeProxy = new E(cs[2]);
1472     },
1473
1474     // private
1475     getRows : function(){
1476         return this.hasRows() ? this.mainBody.dom.childNodes : [];
1477     },
1478
1479     // finder methods, used with delegation
1480
1481     // private
1482     findCell : function(el){
1483         if(!el){
1484             return false;
1485         }
1486         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
1487     },
1488
1489     /**
1490      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
1491      * See also {@link #findRowIndex}
1492      * @param {HTMLElement} el The target element
1493      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
1494      */
1495     findCellIndex : function(el, requiredCls){
1496         var cell = this.findCell(el);
1497         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
1498             return this.getCellIndex(cell);
1499         }
1500         return false;
1501     },
1502
1503     // private
1504     getCellIndex : function(el){
1505         if(el){
1506             var m = el.className.match(this.colRe);
1507             if(m && m[1]){
1508                 return this.cm.getIndexById(m[1]);
1509             }
1510         }
1511         return false;
1512     },
1513
1514     // private
1515     findHeaderCell : function(el){
1516         var cell = this.findCell(el);
1517         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
1518     },
1519
1520     // private
1521     findHeaderIndex : function(el){
1522         return this.findCellIndex(el, this.hdCls);
1523     },
1524
1525     /**
1526      * Return the HtmlElement representing the grid row which contains the passed element.
1527      * @param {HTMLElement} el The target HTMLElement
1528      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
1529      */
1530     findRow : function(el){
1531         if(!el){
1532             return false;
1533         }
1534         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
1535     },
1536
1537     /**
1538      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
1539      * See also {@link #findCellIndex}
1540      * @param {HTMLElement} el The target HTMLElement
1541      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
1542      */
1543     findRowIndex : function(el){
1544         var r = this.findRow(el);
1545         return r ? r.rowIndex : false;
1546     },
1547
1548     /**
1549      * Return the HtmlElement representing the grid row body which contains the passed element.
1550      * @param {HTMLElement} el The target HTMLElement
1551      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
1552      */
1553     findRowBody : function(el){
1554         if(!el){
1555             return false;
1556         }
1557         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
1558     },
1559
1560     // getter methods for fetching elements dynamically in the grid
1561
1562     /**
1563      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
1564      * @param {Number} index The row index
1565      * @return {HtmlElement} The div element.
1566      */
1567     getRow : function(row){
1568         return this.getRows()[row];
1569     },
1570
1571     /**
1572      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
1573      * @param {Number} row The row index in which to find the cell.
1574      * @param {Number} col The column index of the cell.
1575      * @return {HtmlElement} The td at the specified coordinates.
1576      */
1577     getCell : function(row, col){
1578         return this.getRow(row).getElementsByTagName('td')[col];
1579     },
1580
1581     /**
1582      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
1583      * @param {Number} index The column index
1584      * @return {HtmlElement} The td element.
1585      */
1586     getHeaderCell : function(index){
1587         return this.mainHd.dom.getElementsByTagName('td')[index];
1588     },
1589
1590     // manipulating elements
1591
1592     // private - use getRowClass to apply custom row classes
1593     addRowClass : function(row, cls){
1594         var r = this.getRow(row);
1595         if(r){
1596             this.fly(r).addClass(cls);
1597         }
1598     },
1599
1600     // private
1601     removeRowClass : function(row, cls){
1602         var r = this.getRow(row);
1603         if(r){
1604             this.fly(r).removeClass(cls);
1605         }
1606     },
1607
1608     // private
1609     removeRow : function(row){
1610         Ext.removeNode(this.getRow(row));
1611         this.syncFocusEl(row);
1612     },
1613
1614     // private
1615     removeRows : function(firstRow, lastRow){
1616         var bd = this.mainBody.dom;
1617         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
1618             Ext.removeNode(bd.childNodes[firstRow]);
1619         }
1620         this.syncFocusEl(firstRow);
1621     },
1622
1623     // scrolling stuff
1624
1625     // private
1626     getScrollState : function(){
1627         var sb = this.scroller.dom;
1628         return {left: sb.scrollLeft, top: sb.scrollTop};
1629     },
1630
1631     // private
1632     restoreScroll : function(state){
1633         var sb = this.scroller.dom;
1634         sb.scrollLeft = state.left;
1635         sb.scrollTop = state.top;
1636     },
1637
1638     /**
1639      * Scrolls the grid to the top
1640      */
1641     scrollToTop : function(){
1642         this.scroller.dom.scrollTop = 0;
1643         this.scroller.dom.scrollLeft = 0;
1644     },
1645
1646     // private
1647     syncScroll : function(){
1648         this.syncHeaderScroll();
1649         var mb = this.scroller.dom;
1650         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
1651     },
1652
1653     // private
1654     syncHeaderScroll : function(){
1655         var mb = this.scroller.dom;
1656         this.innerHd.scrollLeft = mb.scrollLeft;
1657         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
1658     },
1659
1660     // private
1661     updateSortIcon : function(col, dir){
1662         var sc = this.sortClasses;
1663         var hds = this.mainHd.select('td').removeClass(sc);
1664         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
1665     },
1666
1667     // private
1668     updateAllColumnWidths : function(){
1669         var tw   = this.getTotalWidth(),
1670             clen = this.cm.getColumnCount(),
1671             ws   = [],
1672             len,
1673             i;
1674
1675         for(i = 0; i < clen; i++){
1676             ws[i] = this.getColumnWidth(i);
1677         }
1678
1679         this.innerHd.firstChild.style.width = this.getOffsetWidth();
1680         this.innerHd.firstChild.firstChild.style.width = tw;
1681         this.mainBody.dom.style.width = tw;
1682
1683         for(i = 0; i < clen; i++){
1684             var hd = this.getHeaderCell(i);
1685             hd.style.width = ws[i];
1686         }
1687
1688         var ns = this.getRows(), row, trow;
1689         for(i = 0, len = ns.length; i < len; i++){
1690             row = ns[i];
1691             row.style.width = tw;
1692             if(row.firstChild){
1693                 row.firstChild.style.width = tw;
1694                 trow = row.firstChild.rows[0];
1695                 for (var j = 0; j < clen; j++) {
1696                    trow.childNodes[j].style.width = ws[j];
1697                 }
1698             }
1699         }
1700
1701         this.onAllColumnWidthsUpdated(ws, tw);
1702     },
1703
1704     // private
1705     updateColumnWidth : function(col, width){
1706         var w = this.getColumnWidth(col);
1707         var tw = this.getTotalWidth();
1708         this.innerHd.firstChild.style.width = this.getOffsetWidth();
1709         this.innerHd.firstChild.firstChild.style.width = tw;
1710         this.mainBody.dom.style.width = tw;
1711         var hd = this.getHeaderCell(col);
1712         hd.style.width = w;
1713
1714         var ns = this.getRows(), row;
1715         for(var i = 0, len = ns.length; i < len; i++){
1716             row = ns[i];
1717             row.style.width = tw;
1718             if(row.firstChild){
1719                 row.firstChild.style.width = tw;
1720                 row.firstChild.rows[0].childNodes[col].style.width = w;
1721             }
1722         }
1723
1724         this.onColumnWidthUpdated(col, w, tw);
1725     },
1726
1727     // private
1728     updateColumnHidden : function(col, hidden){
1729         var tw = this.getTotalWidth();
1730         this.innerHd.firstChild.style.width = this.getOffsetWidth();
1731         this.innerHd.firstChild.firstChild.style.width = tw;
1732         this.mainBody.dom.style.width = tw;
1733         var display = hidden ? 'none' : '';
1734
1735         var hd = this.getHeaderCell(col);
1736         hd.style.display = display;
1737
1738         var ns = this.getRows(), row;
1739         for(var i = 0, len = ns.length; i < len; i++){
1740             row = ns[i];
1741             row.style.width = tw;
1742             if(row.firstChild){
1743                 row.firstChild.style.width = tw;
1744                 row.firstChild.rows[0].childNodes[col].style.display = display;
1745             }
1746         }
1747
1748         this.onColumnHiddenUpdated(col, hidden, tw);
1749         delete this.lastViewWidth; // force recalc
1750         this.layout();
1751     },
1752
1753     /**
1754      * @private
1755      * Renders all of the rows to a string buffer and returns the string. This is called internally
1756      * by renderRows and performs the actual string building for the rows - it does not inject HTML into the DOM.
1757      * @param {Array} columns The column data acquired from getColumnData.
1758      * @param {Array} records The array of records to render
1759      * @param {Ext.data.Store} store The store to render the rows from
1760      * @param {Number} startRow The index of the first row being rendered. Sometimes we only render a subset of
1761      * the rows so this is used to maintain logic for striping etc
1762      * @param {Number} colCount The total number of columns in the column model
1763      * @param {Boolean} stripe True to stripe the rows
1764      * @return {String} A string containing the HTML for the rendered rows
1765      */
1766     doRender : function(columns, records, store, startRow, colCount, stripe) {
1767         var templates    = this.templates,
1768             cellTemplate = templates.cell,
1769             rowTemplate  = templates.row,
1770             last         = colCount - 1;
1771
1772         var tstyle = 'width:' + this.getTotalWidth() + ';';
1773
1774         // buffers
1775         var rowBuffer = [],
1776             colBuffer = [],
1777             rowParams = {tstyle: tstyle},
1778             meta      = {},
1779             column,
1780             record;
1781
1782         //build up each row's HTML
1783         for (var j = 0, len = records.length; j < len; j++) {
1784             record    = records[j];
1785             colBuffer = [];
1786
1787             var rowIndex = j + startRow;
1788
1789             //build up each column's HTML
1790             for (var i = 0; i < colCount; i++) {
1791                 column = columns[i];
1792
1793                 meta.id    = column.id;
1794                 meta.css   = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
1795                 meta.attr  = meta.cellAttr = '';
1796                 meta.style = column.style;
1797                 meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
1798
1799                 if (Ext.isEmpty(meta.value)) {
1800                     meta.value = '&#160;';
1801                 }
1802
1803                 if (this.markDirty && record.dirty && Ext.isDefined(record.modified[column.name])) {
1804                     meta.css += ' x-grid3-dirty-cell';
1805                 }
1806
1807                 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
1808             }
1809
1810             //set up row striping and row dirtiness CSS classes
1811             var alt = [];
1812
1813             if (stripe && ((rowIndex + 1) % 2 === 0)) {
1814                 alt[0] = 'x-grid3-row-alt';
1815             }
1816
1817             if (record.dirty) {
1818                 alt[1] = ' x-grid3-dirty-row';
1819             }
1820
1821             rowParams.cols = colCount;
1822
1823             if (this.getRowClass) {
1824                 alt[2] = this.getRowClass(record, rowIndex, rowParams, store);
1825             }
1826
1827             rowParams.alt   = alt.join(' ');
1828             rowParams.cells = colBuffer.join('');
1829
1830             rowBuffer[rowBuffer.length] = rowTemplate.apply(rowParams);
1831         }
1832
1833         return rowBuffer.join('');
1834     },
1835
1836     // private
1837     processRows : function(startRow, skipStripe) {
1838         if (!this.ds || this.ds.getCount() < 1) {
1839             return;
1840         }
1841
1842         var rows = this.getRows(),
1843             len  = rows.length,
1844             i, r;
1845
1846         skipStripe = skipStripe || !this.grid.stripeRows;
1847         startRow   = startRow   || 0;
1848
1849         for (i = 0; i<len; i++) {
1850             r = rows[i];
1851             if (r) {
1852                 r.rowIndex = i;
1853                 if (!skipStripe) {
1854                     r.className = r.className.replace(this.rowClsRe, ' ');
1855                     if ((i + 1) % 2 === 0){
1856                         r.className += ' x-grid3-row-alt';
1857                     }
1858                 }
1859             }
1860         }
1861
1862         // add first/last-row classes
1863         if (startRow === 0) {
1864             Ext.fly(rows[0]).addClass(this.firstRowCls);
1865         }
1866
1867         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
1868     },
1869
1870     afterRender : function(){
1871         if(!this.ds || !this.cm){
1872             return;
1873         }
1874         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
1875         this.processRows(0, true);
1876
1877         if(this.deferEmptyText !== true){
1878             this.applyEmptyText();
1879         }
1880         this.grid.fireEvent('viewready', this.grid);
1881     },
1882
1883     /**
1884      * @private
1885      * Renders each of the UI elements in turn. This is called internally, once, by this.render. It does not
1886      * render rows from the store, just the surrounding UI elements. It also sets up listeners on the UI elements
1887      * and sets up options like column menus, moving and resizing.
1888      */
1889     renderUI : function() {
1890         var templates = this.templates,
1891             header    = this.renderHeaders(),
1892             body      = templates.body.apply({rows:'&#160;'});
1893
1894         var html = templates.master.apply({
1895             body  : body,
1896             header: header,
1897             ostyle: 'width:' + this.getOffsetWidth() + ';',
1898             bstyle: 'width:' + this.getTotalWidth()  + ';'
1899         });
1900
1901         var g = this.grid;
1902
1903         g.getGridEl().dom.innerHTML = html;
1904
1905         this.initElements();
1906
1907         // get mousedowns early
1908         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
1909
1910         this.mainHd.on({
1911             scope    : this,
1912             mouseover: this.handleHdOver,
1913             mouseout : this.handleHdOut,
1914             mousemove: this.handleHdMove
1915         });
1916
1917         this.scroller.on('scroll', this.syncScroll,  this);
1918         if (g.enableColumnResize !== false) {
1919             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
1920         }
1921
1922         if (g.enableColumnMove) {
1923             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
1924             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
1925         }
1926
1927         if (g.enableHdMenu !== false) {
1928             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
1929             this.hmenu.add(
1930                 {itemId:'asc',  text: this.sortAscText,  cls: 'xg-hmenu-sort-asc'},
1931                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
1932             );
1933
1934             if (g.enableColumnHide !== false) {
1935                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
1936                 this.colMenu.on({
1937                     scope     : this,
1938                     beforeshow: this.beforeColMenuShow,
1939                     itemclick : this.handleHdMenuClick
1940                 });
1941                 this.hmenu.add('-', {
1942                     itemId:'columns',
1943                     hideOnClick: false,
1944                     text: this.columnsText,
1945                     menu: this.colMenu,
1946                     iconCls: 'x-cols-icon'
1947                 });
1948             }
1949
1950             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
1951         }
1952
1953         if (g.trackMouseOver) {
1954             this.mainBody.on({
1955                 scope    : this,
1956                 mouseover: this.onRowOver,
1957                 mouseout : this.onRowOut
1958             });
1959         }
1960
1961         if (g.enableDragDrop || g.enableDrag) {
1962             this.dragZone = new Ext.grid.GridDragZone(g, {
1963                 ddGroup : g.ddGroup || 'GridDD'
1964             });
1965         }
1966
1967         this.updateHeaderSortState();
1968     },
1969
1970     // private
1971     processEvent : function(name, e) {
1972         var t = e.getTarget(),
1973             g = this.grid,
1974             header = this.findHeaderIndex(t);
1975         g.fireEvent(name, e);
1976         if (header !== false) {
1977             g.fireEvent('header' + name, g, header, e);
1978         } else {
1979             var row = this.findRowIndex(t),
1980                 cell,
1981                 body;
1982             if (row !== false) {
1983                 g.fireEvent('row' + name, g, row, e);
1984                 cell = this.findCellIndex(t);
1985                 if (cell !== false) {
1986                     g.fireEvent('cell' + name, g, row, cell, e);
1987                 } else {
1988                     body = this.findRowBody(t);
1989                     if (body) {
1990                         g.fireEvent('rowbody' + name, g, row, e);
1991                     }
1992                 }
1993             } else {
1994                 g.fireEvent('container' + name, g, e);
1995             }
1996         }
1997     },
1998
1999     // private
2000     layout : function() {
2001         if(!this.mainBody){
2002             return; // not rendered
2003         }
2004         var g = this.grid;
2005         var c = g.getGridEl();
2006         var csize = c.getSize(true);
2007         var vw = csize.width;
2008
2009         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
2010             return;
2011         }
2012
2013         if(g.autoHeight){
2014             this.scroller.dom.style.overflow = 'visible';
2015             if(Ext.isWebKit){
2016                 this.scroller.dom.style.position = 'static';
2017             }
2018         }else{
2019             this.el.setSize(csize.width, csize.height);
2020
2021             var hdHeight = this.mainHd.getHeight();
2022             var vh = csize.height - (hdHeight);
2023
2024             this.scroller.setSize(vw, vh);
2025             if(this.innerHd){
2026                 this.innerHd.style.width = (vw)+'px';
2027             }
2028         }
2029         if(this.forceFit){
2030             if(this.lastViewWidth != vw){
2031                 this.fitColumns(false, false);
2032                 this.lastViewWidth = vw;
2033             }
2034         }else {
2035             this.autoExpand();
2036             this.syncHeaderScroll();
2037         }
2038         this.onLayout(vw, vh);
2039     },
2040
2041     // template functions for subclasses and plugins
2042     // these functions include precalculated values
2043     onLayout : function(vw, vh){
2044         // do nothing
2045     },
2046
2047     onColumnWidthUpdated : function(col, w, tw){
2048         //template method
2049     },
2050
2051     onAllColumnWidthsUpdated : function(ws, tw){
2052         //template method
2053     },
2054
2055     onColumnHiddenUpdated : function(col, hidden, tw){
2056         // template method
2057     },
2058
2059     updateColumnText : function(col, text){
2060         // template method
2061     },
2062
2063     afterMove : function(colIndex){
2064         // template method
2065     },
2066
2067     /* ----------------------------------- Core Specific -------------------------------------------*/
2068     // private
2069     init : function(grid){
2070         this.grid = grid;
2071
2072         this.initTemplates();
2073         this.initData(grid.store, grid.colModel);
2074         this.initUI(grid);
2075     },
2076
2077     // private
2078     getColumnId : function(index){
2079       return this.cm.getColumnId(index);
2080     },
2081
2082     // private
2083     getOffsetWidth : function() {
2084         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
2085     },
2086
2087     getScrollOffset: function(){
2088         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
2089     },
2090
2091     /**
2092      * @private
2093      * Renders the header row using the 'header' template. Does not inject the HTML into the DOM, just
2094      * returns a string.
2095      * @return {String} Rendered header row
2096      */
2097     renderHeaders : function() {
2098         var cm   = this.cm,
2099             ts   = this.templates,
2100             ct   = ts.hcell,
2101             cb   = [],
2102             p    = {},
2103             len  = cm.getColumnCount(),
2104             last = len - 1;
2105
2106         for (var i = 0; i < len; i++) {
2107             p.id = cm.getColumnId(i);
2108             p.value = cm.getColumnHeader(i) || '';
2109             p.style = this.getColumnStyle(i, true);
2110             p.tooltip = this.getColumnTooltip(i);
2111             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
2112
2113             if (cm.config[i].align == 'right') {
2114                 p.istyle = 'padding-right:16px';
2115             } else {
2116                 delete p.istyle;
2117             }
2118             cb[cb.length] = ct.apply(p);
2119         }
2120         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
2121     },
2122
2123     // private
2124     getColumnTooltip : function(i){
2125         var tt = this.cm.getColumnTooltip(i);
2126         if(tt){
2127             if(Ext.QuickTips.isEnabled()){
2128                 return 'ext:qtip="'+tt+'"';
2129             }else{
2130                 return 'title="'+tt+'"';
2131             }
2132         }
2133         return '';
2134     },
2135
2136     // private
2137     beforeUpdate : function(){
2138         this.grid.stopEditing(true);
2139     },
2140
2141     // private
2142     updateHeaders : function(){
2143         this.innerHd.firstChild.innerHTML = this.renderHeaders();
2144         this.innerHd.firstChild.style.width = this.getOffsetWidth();
2145         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
2146     },
2147
2148     /**
2149      * Focuses the specified row.
2150      * @param {Number} row The row index
2151      */
2152     focusRow : function(row){
2153         this.focusCell(row, 0, false);
2154     },
2155
2156     /**
2157      * Focuses the specified cell.
2158      * @param {Number} row The row index
2159      * @param {Number} col The column index
2160      */
2161     focusCell : function(row, col, hscroll){
2162         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
2163         if(Ext.isGecko){
2164             this.focusEl.focus();
2165         }else{
2166             this.focusEl.focus.defer(1, this.focusEl);
2167         }
2168     },
2169
2170     resolveCell : function(row, col, hscroll){
2171         if(!Ext.isNumber(row)){
2172             row = row.rowIndex;
2173         }
2174         if(!this.ds){
2175             return null;
2176         }
2177         if(row < 0 || row >= this.ds.getCount()){
2178             return null;
2179         }
2180         col = (col !== undefined ? col : 0);
2181
2182         var rowEl = this.getRow(row),
2183             cm = this.cm,
2184             colCount = cm.getColumnCount(),
2185             cellEl;
2186         if(!(hscroll === false && col === 0)){
2187             while(col < colCount && cm.isHidden(col)){
2188                 col++;
2189             }
2190             cellEl = this.getCell(row, col);
2191         }
2192
2193         return {row: rowEl, cell: cellEl};
2194     },
2195
2196     getResolvedXY : function(resolved){
2197         if(!resolved){
2198             return null;
2199         }
2200         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
2201         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
2202     },
2203
2204     syncFocusEl : function(row, col, hscroll){
2205         var xy = row;
2206         if(!Ext.isArray(xy)){
2207             row = Math.min(row, Math.max(0, this.getRows().length-1));
2208             if (isNaN(row)) {
2209                 return;
2210             }
2211             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
2212         }
2213         this.focusEl.setXY(xy||this.scroller.getXY());
2214     },
2215
2216     ensureVisible : function(row, col, hscroll){
2217         var resolved = this.resolveCell(row, col, hscroll);
2218         if(!resolved || !resolved.row){
2219             return;
2220         }
2221
2222         var rowEl = resolved.row,
2223             cellEl = resolved.cell,
2224             c = this.scroller.dom,
2225             ctop = 0,
2226             p = rowEl,
2227             stop = this.el.dom;
2228
2229         while(p && p != stop){
2230             ctop += p.offsetTop;
2231             p = p.offsetParent;
2232         }
2233
2234         ctop -= this.mainHd.dom.offsetHeight;
2235         stop = parseInt(c.scrollTop, 10);
2236
2237         var cbot = ctop + rowEl.offsetHeight,
2238             ch = c.clientHeight,
2239             sbot = stop + ch;
2240
2241
2242         if(ctop < stop){
2243           c.scrollTop = ctop;
2244         }else if(cbot > sbot){
2245             c.scrollTop = cbot-ch;
2246         }
2247
2248         if(hscroll !== false){
2249             var cleft = parseInt(cellEl.offsetLeft, 10);
2250             var cright = cleft + cellEl.offsetWidth;
2251
2252             var sleft = parseInt(c.scrollLeft, 10);
2253             var sright = sleft + c.clientWidth;
2254             if(cleft < sleft){
2255                 c.scrollLeft = cleft;
2256             }else if(cright > sright){
2257                 c.scrollLeft = cright-c.clientWidth;
2258             }
2259         }
2260         return this.getResolvedXY(resolved);
2261     },
2262
2263     // private
2264     insertRows : function(dm, firstRow, lastRow, isUpdate) {
2265         var last = dm.getCount() - 1;
2266         if( !isUpdate && firstRow === 0 && lastRow >= last) {
2267             this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
2268                 this.refresh();
2269             this.fireEvent('rowsinserted', this, firstRow, lastRow);
2270         } else {
2271             if (!isUpdate) {
2272                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
2273             }
2274             var html = this.renderRows(firstRow, lastRow),
2275                 before = this.getRow(firstRow);
2276             if (before) {
2277                 if(firstRow === 0){
2278                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
2279                 }
2280                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
2281             } else {
2282                 var r = this.getRow(last - 1);
2283                 if(r){
2284                     Ext.fly(r).removeClass(this.lastRowCls);
2285                 }
2286                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
2287             }
2288             if (!isUpdate) {
2289                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
2290                 this.processRows(firstRow);
2291             } else if (firstRow === 0 || firstRow >= last) {
2292                 //ensure first/last row is kept after an update.
2293                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
2294             }
2295         }
2296         this.syncFocusEl(firstRow);
2297     },
2298
2299     // private
2300     deleteRows : function(dm, firstRow, lastRow){
2301         if(dm.getRowCount()<1){
2302             this.refresh();
2303         }else{
2304             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
2305
2306             this.removeRows(firstRow, lastRow);
2307
2308             this.processRows(firstRow);
2309             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
2310         }
2311     },
2312
2313     // private
2314     getColumnStyle : function(col, isHeader){
2315         var style = !isHeader ? (this.cm.config[col].css || '') : '';
2316         style += 'width:'+this.getColumnWidth(col)+';';
2317         if(this.cm.isHidden(col)){
2318             style += 'display:none;';
2319         }
2320         var align = this.cm.config[col].align;
2321         if(align){
2322             style += 'text-align:'+align+';';
2323         }
2324         return style;
2325     },
2326
2327     // private
2328     getColumnWidth : function(col){
2329         var w = this.cm.getColumnWidth(col);
2330         if(Ext.isNumber(w)){
2331             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
2332         }
2333         return w;
2334     },
2335
2336     // private
2337     getTotalWidth : function(){
2338         return this.cm.getTotalWidth()+'px';
2339     },
2340
2341     // private
2342     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
2343         var cm = this.cm, i;
2344         var tw = cm.getTotalWidth(false);
2345         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
2346
2347         if(aw < 20){ // not initialized, so don't screw up the default widths
2348             return;
2349         }
2350         var extra = aw - tw;
2351
2352         if(extra === 0){
2353             return false;
2354         }
2355
2356         var vc = cm.getColumnCount(true);
2357         var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
2358         if(ac === 0){
2359             ac = 1;
2360             omitColumn = undefined;
2361         }
2362         var colCount = cm.getColumnCount();
2363         var cols = [];
2364         var extraCol = 0;
2365         var width = 0;
2366         var w;
2367         for (i = 0; i < colCount; i++){
2368             if(!cm.isFixed(i) && i !== omitColumn){
2369                 w = cm.getColumnWidth(i);
2370                 cols.push(i, w);
2371                 if(!cm.isHidden(i)){
2372                     extraCol = i;
2373                     width += w;
2374                 }
2375             }
2376         }
2377         var frac = (aw - cm.getTotalWidth())/width;
2378         while (cols.length){
2379             w = cols.pop();
2380             i = cols.pop();
2381             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
2382         }
2383
2384         if((tw = cm.getTotalWidth(false)) > aw){
2385             var adjustCol = ac != vc ? omitColumn : extraCol;
2386              cm.setColumnWidth(adjustCol, Math.max(1,
2387                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
2388         }
2389
2390         if(preventRefresh !== true){
2391             this.updateAllColumnWidths();
2392         }
2393
2394
2395         return true;
2396     },
2397
2398     // private
2399     autoExpand : function(preventUpdate){
2400         var g = this.grid, cm = this.cm;
2401         if(!this.userResized && g.autoExpandColumn){
2402             var tw = cm.getTotalWidth(false);
2403             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
2404             if(tw != aw){
2405                 var ci = cm.getIndexById(g.autoExpandColumn);
2406                 var currentWidth = cm.getColumnWidth(ci);
2407                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
2408                 if(cw != currentWidth){
2409                     cm.setColumnWidth(ci, cw, true);
2410                     if(preventUpdate !== true){
2411                         this.updateColumnWidth(ci, cw);
2412                     }
2413                 }
2414             }
2415         }
2416     },
2417
2418     /**
2419      * @private
2420      * Returns an array of column configurations - one for each column
2421      * @return {Array} Array of column config objects. This includes the column name, renderer, id style and renderer
2422      */
2423     getColumnData : function(){
2424         // build a map for all the columns
2425         var cs       = [],
2426             cm       = this.cm,
2427             colCount = cm.getColumnCount();
2428
2429         for (var i = 0; i < colCount; i++) {
2430             var name = cm.getDataIndex(i);
2431
2432             cs[i] = {
2433                 name    : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
2434                 renderer: cm.getRenderer(i),
2435                 scope   : cm.getRendererScope(i),
2436                 id      : cm.getColumnId(i),
2437                 style   : this.getColumnStyle(i)
2438             };
2439         }
2440
2441         return cs;
2442     },
2443
2444     // private
2445     renderRows : function(startRow, endRow){
2446         // pull in all the crap needed to render rows
2447         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
2448         var colCount = cm.getColumnCount();
2449
2450         if(ds.getCount() < 1){
2451             return '';
2452         }
2453
2454         var cs = this.getColumnData();
2455
2456         startRow = startRow || 0;
2457         endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
2458
2459         // records to render
2460         var rs = ds.getRange(startRow, endRow);
2461
2462         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
2463     },
2464
2465     // private
2466     renderBody : function(){
2467         var markup = this.renderRows() || '&#160;';
2468         return this.templates.body.apply({rows: markup});
2469     },
2470
2471     // private
2472     refreshRow : function(record){
2473         var ds = this.ds, index;
2474         if(Ext.isNumber(record)){
2475             index = record;
2476             record = ds.getAt(index);
2477             if(!record){
2478                 return;
2479             }
2480         }else{
2481             index = ds.indexOf(record);
2482             if(index < 0){
2483                 return;
2484             }
2485         }
2486         this.insertRows(ds, index, index, true);
2487         this.getRow(index).rowIndex = index;
2488         this.onRemove(ds, record, index+1, true);
2489         this.fireEvent('rowupdated', this, index, record);
2490     },
2491
2492     /**
2493      * Refreshs the grid UI
2494      * @param {Boolean} headersToo (optional) True to also refresh the headers
2495      */
2496     refresh : function(headersToo){
2497         this.fireEvent('beforerefresh', this);
2498         this.grid.stopEditing(true);
2499
2500         var result = this.renderBody();
2501         this.mainBody.update(result).setWidth(this.getTotalWidth());
2502         if(headersToo === true){
2503             this.updateHeaders();
2504             this.updateHeaderSortState();
2505         }
2506         this.processRows(0, true);
2507         this.layout();
2508         this.applyEmptyText();
2509         this.fireEvent('refresh', this);
2510     },
2511
2512     /**
2513      * @private
2514      * Displays the configured emptyText if there are currently no rows to display
2515      */
2516     applyEmptyText : function(){
2517         if(this.emptyText && !this.hasRows()){
2518             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
2519         }
2520     },
2521
2522     /**
2523      * @private
2524      * Adds sorting classes to the column headers based on the bound store's sortInfo. Fires the 'sortchange' event
2525      * if the sorting has changed since this function was last run.
2526      */
2527     updateHeaderSortState : function(){
2528         var state = this.ds.getSortState();
2529         if (!state) {
2530             return;
2531         }
2532
2533         if (!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)) {
2534             this.grid.fireEvent('sortchange', this.grid, state);
2535         }
2536
2537         this.sortState = state;
2538
2539         var sortColumn = this.cm.findColumnIndex(state.field);
2540         if (sortColumn != -1){
2541             var sortDir = state.direction;
2542             this.updateSortIcon(sortColumn, sortDir);
2543         }
2544     },
2545
2546     /**
2547      * @private
2548      * Removes any sorting indicator classes from the column headers
2549      */
2550     clearHeaderSortState : function(){
2551         if (!this.sortState) {
2552             return;
2553         }
2554         this.grid.fireEvent('sortchange', this.grid, null);
2555         this.mainHd.select('td').removeClass(this.sortClasses);
2556         delete this.sortState;
2557     },
2558
2559     // private
2560     destroy : function(){
2561         if (this.scrollToTopTask && this.scrollToTopTask.cancel){
2562             this.scrollToTopTask.cancel();
2563         }
2564         if(this.colMenu){
2565             Ext.menu.MenuMgr.unregister(this.colMenu);
2566             this.colMenu.destroy();
2567             delete this.colMenu;
2568         }
2569         if(this.hmenu){
2570             Ext.menu.MenuMgr.unregister(this.hmenu);
2571             this.hmenu.destroy();
2572             delete this.hmenu;
2573         }
2574
2575         this.initData(null, null);
2576         this.purgeListeners();
2577         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
2578
2579         if(this.grid.enableColumnMove){
2580             Ext.destroy(
2581                 this.columnDrag.el,
2582                 this.columnDrag.proxy.ghost,
2583                 this.columnDrag.proxy.el,
2584                 this.columnDrop.el,
2585                 this.columnDrop.proxyTop,
2586                 this.columnDrop.proxyBottom,
2587                 this.columnDrag.dragData.ddel,
2588                 this.columnDrag.dragData.header
2589             );
2590             if (this.columnDrag.proxy.anim) {
2591                 Ext.destroy(this.columnDrag.proxy.anim);
2592             }
2593             delete this.columnDrag.proxy.ghost;
2594             delete this.columnDrag.dragData.ddel;
2595             delete this.columnDrag.dragData.header;
2596             this.columnDrag.destroy();
2597             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
2598             delete this.columnDrag._domRef;
2599
2600             delete this.columnDrop.proxyTop;
2601             delete this.columnDrop.proxyBottom;
2602             this.columnDrop.destroy();
2603             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
2604             delete this.columnDrop._domRef;
2605             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
2606         }
2607
2608         if (this.splitZone){ // enableColumnResize
2609             this.splitZone.destroy();
2610             delete this.splitZone._domRef;
2611             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
2612         }
2613
2614         Ext.fly(this.innerHd).removeAllListeners();
2615         Ext.removeNode(this.innerHd);
2616         delete this.innerHd;
2617
2618         Ext.destroy(
2619             this.el,
2620             this.mainWrap,
2621             this.mainHd,
2622             this.scroller,
2623             this.mainBody,
2624             this.focusEl,
2625             this.resizeMarker,
2626             this.resizeProxy,
2627             this.activeHdBtn,
2628             this.dragZone,
2629             this.splitZone,
2630             this._flyweight
2631         );
2632
2633         delete this.grid.container;
2634
2635         if(this.dragZone){
2636             this.dragZone.destroy();
2637         }
2638
2639         Ext.dd.DDM.currentTarget = null;
2640         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
2641
2642         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
2643     },
2644
2645     // private
2646     onDenyColumnHide : function(){
2647
2648     },
2649
2650     // private
2651     render : function(){
2652         if(this.autoFill){
2653             var ct = this.grid.ownerCt;
2654             if (ct && ct.getLayout()){
2655                 ct.on('afterlayout', function(){
2656                     this.fitColumns(true, true);
2657                     this.updateHeaders();
2658                 }, this, {single: true});
2659             }else{
2660                 this.fitColumns(true, true);
2661             }
2662         }else if(this.forceFit){
2663             this.fitColumns(true, false);
2664         }else if(this.grid.autoExpandColumn){
2665             this.autoExpand(true);
2666         }
2667
2668         this.renderUI();
2669     },
2670
2671     /* --------------------------------- Model Events and Handlers --------------------------------*/
2672     // private
2673     initData : function(ds, cm){
2674         if(this.ds){
2675             this.ds.un('load', this.onLoad, this);
2676             this.ds.un('datachanged', this.onDataChange, this);
2677             this.ds.un('add', this.onAdd, this);
2678             this.ds.un('remove', this.onRemove, this);
2679             this.ds.un('update', this.onUpdate, this);
2680             this.ds.un('clear', this.onClear, this);
2681             if(this.ds !== ds && this.ds.autoDestroy){
2682                 this.ds.destroy();
2683             }
2684         }
2685         if(ds){
2686             ds.on({
2687                 scope: this,
2688                 load: this.onLoad,
2689                 datachanged: this.onDataChange,
2690                 add: this.onAdd,
2691                 remove: this.onRemove,
2692                 update: this.onUpdate,
2693                 clear: this.onClear
2694             });
2695         }
2696         this.ds = ds;
2697
2698         if(this.cm){
2699             this.cm.un('configchange', this.onColConfigChange, this);
2700             this.cm.un('widthchange', this.onColWidthChange, this);
2701             this.cm.un('headerchange', this.onHeaderChange, this);
2702             this.cm.un('hiddenchange', this.onHiddenChange, this);
2703             this.cm.un('columnmoved', this.onColumnMove, this);
2704         }
2705         if(cm){
2706             delete this.lastViewWidth;
2707             cm.on({
2708                 scope: this,
2709                 configchange: this.onColConfigChange,
2710                 widthchange: this.onColWidthChange,
2711                 headerchange: this.onHeaderChange,
2712                 hiddenchange: this.onHiddenChange,
2713                 columnmoved: this.onColumnMove
2714             });
2715         }
2716         this.cm = cm;
2717     },
2718
2719     // private
2720     onDataChange : function(){
2721         this.refresh();
2722         this.updateHeaderSortState();
2723         this.syncFocusEl(0);
2724     },
2725
2726     // private
2727     onClear : function(){
2728         this.refresh();
2729         this.syncFocusEl(0);
2730     },
2731
2732     // private
2733     onUpdate : function(ds, record){
2734         this.refreshRow(record);
2735     },
2736
2737     // private
2738     onAdd : function(ds, records, index){
2739         this.insertRows(ds, index, index + (records.length-1));
2740     },
2741
2742     // private
2743     onRemove : function(ds, record, index, isUpdate){
2744         if(isUpdate !== true){
2745             this.fireEvent('beforerowremoved', this, index, record);
2746         }
2747         this.removeRow(index);
2748         if(isUpdate !== true){
2749             this.processRows(index);
2750             this.applyEmptyText();
2751             this.fireEvent('rowremoved', this, index, record);
2752         }
2753     },
2754
2755     // private
2756     onLoad : function(){
2757         if (Ext.isGecko){
2758             if (!this.scrollToTopTask) {
2759                 this.scrollToTopTask = new Ext.util.DelayedTask(this.scrollToTop, this);
2760             }
2761             this.scrollToTopTask.delay(1);
2762         }else{
2763             this.scrollToTop();
2764         }
2765     },
2766
2767     // private
2768     onColWidthChange : function(cm, col, width){
2769         this.updateColumnWidth(col, width);
2770     },
2771
2772     // private
2773     onHeaderChange : function(cm, col, text){
2774         this.updateHeaders();
2775     },
2776
2777     // private
2778     onHiddenChange : function(cm, col, hidden){
2779         this.updateColumnHidden(col, hidden);
2780     },
2781
2782     // private
2783     onColumnMove : function(cm, oldIndex, newIndex){
2784         this.indexMap = null;
2785         var s = this.getScrollState();
2786         this.refresh(true);
2787         this.restoreScroll(s);
2788         this.afterMove(newIndex);
2789         this.grid.fireEvent('columnmove', oldIndex, newIndex);
2790     },
2791
2792     // private
2793     onColConfigChange : function(){
2794         delete this.lastViewWidth;
2795         this.indexMap = null;
2796         this.refresh(true);
2797     },
2798
2799     /* -------------------- UI Events and Handlers ------------------------------ */
2800     // private
2801     initUI : function(grid){
2802         grid.on('headerclick', this.onHeaderClick, this);
2803     },
2804
2805     // private
2806     initEvents : function(){
2807     },
2808
2809     // private
2810     onHeaderClick : function(g, index){
2811         if(this.headersDisabled || !this.cm.isSortable(index)){
2812             return;
2813         }
2814         g.stopEditing(true);
2815         g.store.sort(this.cm.getDataIndex(index));
2816     },
2817
2818     // private
2819     onRowOver : function(e, t){
2820         var row;
2821         if((row = this.findRowIndex(t)) !== false){
2822             this.addRowClass(row, 'x-grid3-row-over');
2823         }
2824     },
2825
2826     // private
2827     onRowOut : function(e, t){
2828         var row;
2829         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
2830             this.removeRowClass(row, 'x-grid3-row-over');
2831         }
2832     },
2833
2834     // private
2835     handleWheel : function(e){
2836         e.stopPropagation();
2837     },
2838
2839     // private
2840     onRowSelect : function(row){
2841         this.addRowClass(row, this.selectedRowClass);
2842     },
2843
2844     // private
2845     onRowDeselect : function(row){
2846         this.removeRowClass(row, this.selectedRowClass);
2847     },
2848
2849     // private
2850     onCellSelect : function(row, col){
2851         var cell = this.getCell(row, col);
2852         if(cell){
2853             this.fly(cell).addClass('x-grid3-cell-selected');
2854         }
2855     },
2856
2857     // private
2858     onCellDeselect : function(row, col){
2859         var cell = this.getCell(row, col);
2860         if(cell){
2861             this.fly(cell).removeClass('x-grid3-cell-selected');
2862         }
2863     },
2864
2865     // private
2866     onColumnSplitterMoved : function(i, w){
2867         this.userResized = true;
2868         var cm = this.grid.colModel;
2869         cm.setColumnWidth(i, w, true);
2870
2871         if(this.forceFit){
2872             this.fitColumns(true, false, i);
2873             this.updateAllColumnWidths();
2874         }else{
2875             this.updateColumnWidth(i, w);
2876             this.syncHeaderScroll();
2877         }
2878
2879         this.grid.fireEvent('columnresize', i, w);
2880     },
2881
2882     // private
2883     handleHdMenuClick : function(item){
2884         var index = this.hdCtxIndex,
2885             cm = this.cm,
2886             ds = this.ds,
2887             id = item.getItemId();
2888         switch(id){
2889             case 'asc':
2890                 ds.sort(cm.getDataIndex(index), 'ASC');
2891                 break;
2892             case 'desc':
2893                 ds.sort(cm.getDataIndex(index), 'DESC');
2894                 break;
2895             default:
2896                 index = cm.getIndexById(id.substr(4));
2897                 if(index != -1){
2898                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
2899                         this.onDenyColumnHide();
2900                         return false;
2901                     }
2902                     cm.setHidden(index, item.checked);
2903                 }
2904         }
2905         return true;
2906     },
2907
2908     // private
2909     isHideableColumn : function(c){
2910         return !c.hidden;
2911     },
2912
2913     // private
2914     beforeColMenuShow : function(){
2915         var cm = this.cm,  colCount = cm.getColumnCount();
2916         this.colMenu.removeAll();
2917         for(var i = 0; i < colCount; i++){
2918             if(cm.config[i].hideable !== false){
2919                 this.colMenu.add(new Ext.menu.CheckItem({
2920                     itemId: 'col-'+cm.getColumnId(i),
2921                     text: cm.getColumnHeader(i),
2922                     checked: !cm.isHidden(i),
2923                     hideOnClick:false,
2924                     disabled: cm.config[i].hideable === false
2925                 }));
2926             }
2927         }
2928     },
2929
2930     // private
2931     handleHdDown : function(e, t){
2932         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
2933             e.stopEvent();
2934             var hd = this.findHeaderCell(t);
2935             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
2936             var index = this.getCellIndex(hd);
2937             this.hdCtxIndex = index;
2938             var ms = this.hmenu.items, cm = this.cm;
2939             ms.get('asc').setDisabled(!cm.isSortable(index));
2940             ms.get('desc').setDisabled(!cm.isSortable(index));
2941             this.hmenu.on('hide', function(){
2942                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
2943             }, this, {single:true});
2944             this.hmenu.show(t, 'tl-bl?');
2945         }
2946     },
2947
2948     // private
2949     handleHdOver : function(e, t){
2950         var hd = this.findHeaderCell(t);
2951         if(hd && !this.headersDisabled){
2952             this.activeHdRef = t;
2953             this.activeHdIndex = this.getCellIndex(hd);
2954             var fly = this.fly(hd);
2955             this.activeHdRegion = fly.getRegion();
2956             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
2957                 fly.addClass('x-grid3-hd-over');
2958                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
2959                 if(this.activeHdBtn){
2960                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
2961                 }
2962             }
2963         }
2964     },
2965
2966     // private
2967     handleHdMove : function(e, t){
2968         var hd = this.findHeaderCell(this.activeHdRef);
2969         if(hd && !this.headersDisabled){
2970             var hw = this.splitHandleWidth || 5,
2971                 r = this.activeHdRegion,
2972                 x = e.getPageX(),
2973                 ss = hd.style,
2974                 cur = '';
2975             if(this.grid.enableColumnResize !== false){
2976                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
2977                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
2978                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
2979                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
2980                 }
2981             }
2982             ss.cursor = cur;
2983         }
2984     },
2985
2986     // private
2987     handleHdOut : function(e, t){
2988         var hd = this.findHeaderCell(t);
2989         if(hd && (!Ext.isIE || !e.within(hd, true))){
2990             this.activeHdRef = null;
2991             this.fly(hd).removeClass('x-grid3-hd-over');
2992             hd.style.cursor = '';
2993         }
2994     },
2995
2996     // private
2997     hasRows : function(){
2998         var fc = this.mainBody.dom.firstChild;
2999         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
3000     },
3001
3002     // back compat
3003     bind : function(d, c){
3004         this.initData(d, c);
3005     }
3006 });
3007
3008
3009 // private
3010 // This is a support class used internally by the Grid components
3011 Ext.grid.GridView.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
3012     
3013     constructor: function(grid, hd){
3014         this.grid = grid;
3015         this.view = grid.getView();
3016         this.marker = this.view.resizeMarker;
3017         this.proxy = this.view.resizeProxy;
3018         Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
3019             'gridSplitters' + this.grid.getGridEl().id, {
3020             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
3021         });
3022         this.scroll = false;
3023         this.hw = this.view.splitHandleWidth || 5;
3024     },
3025
3026     b4StartDrag : function(x, y){
3027         this.dragHeadersDisabled = this.view.headersDisabled;
3028         this.view.headersDisabled = true;
3029         var h = this.view.mainWrap.getHeight();
3030         this.marker.setHeight(h);
3031         this.marker.show();
3032         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
3033         this.proxy.setHeight(h);
3034         var w = this.cm.getColumnWidth(this.cellIndex),
3035             minw = Math.max(w-this.grid.minColumnWidth, 0);
3036         this.resetConstraints();
3037         this.setXConstraint(minw, 1000);
3038         this.setYConstraint(0, 0);
3039         this.minX = x - minw;
3040         this.maxX = x + 1000;
3041         this.startPos = x;
3042         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
3043     },
3044
3045     allowHeaderDrag : function(e){
3046         return true;
3047     },
3048
3049     handleMouseDown : function(e){
3050         var t = this.view.findHeaderCell(e.getTarget());
3051         if(t && this.allowHeaderDrag(e)){
3052             var xy = this.view.fly(t).getXY(), 
3053                 x = xy[0], 
3054                 y = xy[1],
3055                 exy = e.getXY(), ex = exy[0],
3056                 w = t.offsetWidth, adjust = false;
3057                 
3058             if((ex - x) <= this.hw){
3059                 adjust = -1;
3060             }else if((x+w) - ex <= this.hw){
3061                 adjust = 0;
3062             }
3063             if(adjust !== false){
3064                 this.cm = this.grid.colModel;
3065                 var ci = this.view.getCellIndex(t);
3066                 if(adjust == -1){
3067                   if (ci + adjust < 0) {
3068                     return;
3069                   }
3070                     while(this.cm.isHidden(ci+adjust)){
3071                         --adjust;
3072                         if(ci+adjust < 0){
3073                             return;
3074                         }
3075                     }
3076                 }
3077                 this.cellIndex = ci+adjust;
3078                 this.split = t.dom;
3079                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
3080                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
3081                 }
3082             }else if(this.view.columnDrag){
3083                 this.view.columnDrag.callHandleMouseDown(e);
3084             }
3085         }
3086     },
3087
3088     endDrag : function(e){
3089         this.marker.hide();
3090         var v = this.view,
3091             endX = Math.max(this.minX, e.getPageX()),
3092             diff = endX - this.startPos,
3093             disabled = this.dragHeadersDisabled;
3094             
3095         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
3096         setTimeout(function(){
3097             v.headersDisabled = disabled;
3098         }, 50);
3099     },
3100
3101     autoOffset : function(){
3102         this.setDelta(0,0);
3103     }
3104 });
3105 // private
3106 // This is a support class used internally by the Grid components
3107 Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {
3108     maxDragWidth: 120,
3109     
3110     constructor : function(grid, hd, hd2){
3111         this.grid = grid;
3112         this.view = grid.getView();
3113         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
3114         Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
3115         if(hd2){
3116             this.setHandleElId(Ext.id(hd));
3117             this.setOuterHandleElId(Ext.id(hd2));
3118         }
3119         this.scroll = false;
3120     },
3121     
3122     getDragData : function(e){
3123         var t = Ext.lib.Event.getTarget(e),
3124             h = this.view.findHeaderCell(t);
3125         if(h){
3126             return {ddel: h.firstChild, header:h};
3127         }
3128         return false;
3129     },
3130
3131     onInitDrag : function(e){
3132         // keep the value here so we can restore it;
3133         this.dragHeadersDisabled = this.view.headersDisabled;
3134         this.view.headersDisabled = true;
3135         var clone = this.dragData.ddel.cloneNode(true);
3136         clone.id = Ext.id();
3137         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
3138         this.proxy.update(clone);
3139         return true;
3140     },
3141
3142     afterValidDrop : function(){
3143         this.completeDrop();
3144     },
3145
3146     afterInvalidDrop : function(){
3147         this.completeDrop();
3148     },
3149     
3150     completeDrop: function(){
3151         var v = this.view,
3152             disabled = this.dragHeadersDisabled;
3153         setTimeout(function(){
3154             v.headersDisabled = disabled;
3155         }, 50);
3156     }
3157 });
3158
3159 // private
3160 // This is a support class used internally by the Grid components
3161 Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {
3162     proxyOffsets : [-4, -9],
3163     fly: Ext.Element.fly,
3164     
3165     constructor : function(grid, hd, hd2){
3166         this.grid = grid;
3167         this.view = grid.getView();
3168         // split the proxies so they don't interfere with mouse events
3169         this.proxyTop = Ext.DomHelper.append(document.body, {
3170             cls:"col-move-top", html:"&#160;"
3171         }, true);
3172         this.proxyBottom = Ext.DomHelper.append(document.body, {
3173             cls:"col-move-bottom", html:"&#160;"
3174         }, true);
3175         this.proxyTop.hide = this.proxyBottom.hide = function(){
3176             this.setLeftTop(-100,-100);
3177             this.setStyle("visibility", "hidden");
3178         };
3179         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
3180         // temporarily disabled
3181         //Ext.dd.ScrollManager.register(this.view.scroller.dom);
3182         Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
3183     },
3184
3185     getTargetFromEvent : function(e){
3186         var t = Ext.lib.Event.getTarget(e),
3187             cindex = this.view.findCellIndex(t);
3188         if(cindex !== false){
3189             return this.view.getHeaderCell(cindex);
3190         }
3191     },
3192
3193     nextVisible : function(h){
3194         var v = this.view, cm = this.grid.colModel;
3195         h = h.nextSibling;
3196         while(h){
3197             if(!cm.isHidden(v.getCellIndex(h))){
3198                 return h;
3199             }
3200             h = h.nextSibling;
3201         }
3202         return null;
3203     },
3204
3205     prevVisible : function(h){
3206         var v = this.view, cm = this.grid.colModel;
3207         h = h.prevSibling;
3208         while(h){
3209             if(!cm.isHidden(v.getCellIndex(h))){
3210                 return h;
3211             }
3212             h = h.prevSibling;
3213         }
3214         return null;
3215     },
3216
3217     positionIndicator : function(h, n, e){
3218         var x = Ext.lib.Event.getPageX(e),
3219             r = Ext.lib.Dom.getRegion(n.firstChild),
3220             px, 
3221             pt, 
3222             py = r.top + this.proxyOffsets[1];
3223         if((r.right - x) <= (r.right-r.left)/2){
3224             px = r.right+this.view.borderWidth;
3225             pt = "after";
3226         }else{
3227             px = r.left;
3228             pt = "before";
3229         }
3230
3231         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
3232             return false;
3233         }
3234
3235         px +=  this.proxyOffsets[0];
3236         this.proxyTop.setLeftTop(px, py);
3237         this.proxyTop.show();
3238         if(!this.bottomOffset){
3239             this.bottomOffset = this.view.mainHd.getHeight();
3240         }
3241         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
3242         this.proxyBottom.show();
3243         return pt;
3244     },
3245
3246     onNodeEnter : function(n, dd, e, data){
3247         if(data.header != n){
3248             this.positionIndicator(data.header, n, e);
3249         }
3250     },
3251
3252     onNodeOver : function(n, dd, e, data){
3253         var result = false;
3254         if(data.header != n){
3255             result = this.positionIndicator(data.header, n, e);
3256         }
3257         if(!result){
3258             this.proxyTop.hide();
3259             this.proxyBottom.hide();
3260         }
3261         return result ? this.dropAllowed : this.dropNotAllowed;
3262     },
3263
3264     onNodeOut : function(n, dd, e, data){
3265         this.proxyTop.hide();
3266         this.proxyBottom.hide();
3267     },
3268
3269     onNodeDrop : function(n, dd, e, data){
3270         var h = data.header;
3271         if(h != n){
3272             var cm = this.grid.colModel,
3273                 x = Ext.lib.Event.getPageX(e),
3274                 r = Ext.lib.Dom.getRegion(n.firstChild),
3275                 pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before",
3276                 oldIndex = this.view.getCellIndex(h),
3277                 newIndex = this.view.getCellIndex(n);
3278             if(pt == "after"){
3279                 newIndex++;
3280             }
3281             if(oldIndex < newIndex){
3282                 newIndex--;
3283             }
3284             cm.moveColumn(oldIndex, newIndex);
3285             return true;
3286         }
3287         return false;
3288     }
3289 });
3290
3291 Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {
3292     
3293     constructor : function(grid, hd){
3294         Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
3295         this.proxy.el.addClass('x-grid3-col-dd');
3296     },
3297     
3298     handleMouseDown : function(e){
3299     },
3300
3301     callHandleMouseDown : function(e){
3302         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
3303     }
3304 });// private
3305 // This is a support class used internally by the Grid components
3306 Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
3307     fly: Ext.Element.fly,
3308     
3309     constructor : function(grid, hd, hd2){
3310         this.grid = grid;
3311         this.view = grid.getView();
3312         this.proxy = this.view.resizeProxy;
3313         Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
3314             "gridSplitters" + this.grid.getGridEl().id, {
3315             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
3316         });
3317         this.setHandleElId(Ext.id(hd));
3318         this.setOuterHandleElId(Ext.id(hd2));
3319         this.scroll = false;
3320     },
3321
3322     b4StartDrag : function(x, y){
3323         this.view.headersDisabled = true;
3324         this.proxy.setHeight(this.view.mainWrap.getHeight());
3325         var w = this.cm.getColumnWidth(this.cellIndex);
3326         var minw = Math.max(w-this.grid.minColumnWidth, 0);
3327         this.resetConstraints();
3328         this.setXConstraint(minw, 1000);
3329         this.setYConstraint(0, 0);
3330         this.minX = x - minw;
3331         this.maxX = x + 1000;
3332         this.startPos = x;
3333         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
3334     },
3335
3336
3337     handleMouseDown : function(e){
3338         var ev = Ext.EventObject.setEvent(e);
3339         var t = this.fly(ev.getTarget());
3340         if(t.hasClass("x-grid-split")){
3341             this.cellIndex = this.view.getCellIndex(t.dom);
3342             this.split = t.dom;
3343             this.cm = this.grid.colModel;
3344             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
3345                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
3346             }
3347         }
3348     },
3349
3350     endDrag : function(e){
3351         this.view.headersDisabled = false;
3352         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
3353         var diff = endX - this.startPos;
3354         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
3355     },
3356
3357     autoOffset : function(){
3358         this.setDelta(0,0);
3359     }
3360 });/**
3361  * @class Ext.grid.GridDragZone
3362  * @extends Ext.dd.DragZone
3363  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
3364  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
3365  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
3366  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
3367  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
3368  * to process the {@link #getDragData data} which is provided.
3369  */
3370 Ext.grid.GridDragZone = function(grid, config){
3371     this.view = grid.getView();
3372     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
3373     this.scroll = false;
3374     this.grid = grid;
3375     this.ddel = document.createElement('div');
3376     this.ddel.className = 'x-grid-dd-wrap';
3377 };
3378
3379 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
3380     ddGroup : "GridDD",
3381
3382     /**
3383      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
3384      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
3385      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
3386      * <p>The data object contains the following properties:<ul>
3387      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
3388      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
3389      * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>
3390      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
3391      * </ul></p>
3392      */
3393     getDragData : function(e){
3394         var t = Ext.lib.Event.getTarget(e);
3395         var rowIndex = this.view.findRowIndex(t);
3396         if(rowIndex !== false){
3397             var sm = this.grid.selModel;
3398             if(!sm.isSelected(rowIndex) || e.hasModifier()){
3399                 sm.handleMouseDown(this.grid, rowIndex, e);
3400             }
3401             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
3402         }
3403         return false;
3404     },
3405
3406     /**
3407      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
3408      * of the data being dragged.</p>
3409      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
3410      */
3411     onInitDrag : function(e){
3412         var data = this.dragData;
3413         this.ddel.innerHTML = this.grid.getDragDropText();
3414         this.proxy.update(this.ddel);
3415         // fire start drag?
3416     },
3417
3418     /**
3419      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
3420      * the selected rows to show that they have not been dragged.
3421      */
3422     afterRepair : function(){
3423         this.dragging = false;
3424     },
3425
3426     /**
3427      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
3428      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
3429      * @param {EventObject} e The mouse up event
3430      * @return {Array} The xy location (e.g. [100, 200])
3431      */
3432     getRepairXY : function(e, data){
3433         return false;
3434     },
3435
3436     onEndDrag : function(data, e){
3437         // fire end drag?
3438     },
3439
3440     onValidDrop : function(dd, e, id){
3441         // fire drag drop?
3442         this.hideProxy();
3443     },
3444
3445     beforeInvalidDrop : function(e, id){
3446
3447     }
3448 });
3449 /**
3450  * @class Ext.grid.ColumnModel
3451  * @extends Ext.util.Observable
3452  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
3453  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
3454  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
3455  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
3456  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
3457  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
3458  * <pre><code>
3459 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
3460  * </code></pre>
3461  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
3462  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
3463  * each record in the store is indexed into the ColumnModel.</p>
3464  * <p>There are two ways to initialize the ColumnModel class:</p>
3465  * <p><u>Initialization Method 1: an Array</u></p>
3466 <pre><code>
3467  var colModel = new Ext.grid.ColumnModel([
3468     { header: "Ticker", width: 60, sortable: true},
3469     { header: "Company Name", width: 150, sortable: true, id: 'company'},
3470     { header: "Market Cap.", width: 100, sortable: true},
3471     { header: "$ Sales", width: 100, sortable: true, renderer: money},
3472     { header: "Employees", width: 100, sortable: true, resizable: false}
3473  ]);
3474  </code></pre>
3475  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
3476  * objects to define the initial layout / display of the columns in the Grid. The order of each
3477  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
3478  * order of the column display.  A Column's display may be initially hidden using the
3479  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
3480  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
3481  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
3482  * {@link Ext.data.Store Store} the column draws its data from is configured through the
3483  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
3484  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
3485  * example above) it will use the column configuration's index in the Array as the index.</p>
3486  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
3487  * <p><u>Initialization Method 2: an Object</u></p>
3488  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
3489  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
3490  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
3491  * for all columns, e.g.:</p><pre><code>
3492  var colModel = new Ext.grid.ColumnModel({
3493     columns: [
3494         { header: "Ticker", width: 60, menuDisabled: false},
3495         { header: "Company Name", width: 150, id: 'company'},
3496         { header: "Market Cap."},
3497         { header: "$ Sales", renderer: money},
3498         { header: "Employees", resizable: false}
3499     ],
3500     defaults: {
3501         sortable: true,
3502         menuDisabled: true,
3503         width: 100
3504     },
3505     listeners: {
3506         {@link #hiddenchange}: function(cm, colIndex, hidden) {
3507             saveConfig(colIndex, hidden);
3508         }
3509     }
3510 });
3511  </code></pre>
3512  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
3513  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
3514  * option. This column could be styled by including the following css:</p><pre><code>
3515  //add this css *after* the core css is loaded
3516 .x-grid3-td-company {
3517     color: red; // entire column will have red font
3518 }
3519 // modify the header row only, adding an icon to the column header
3520 .x-grid3-hd-company {
3521     background: transparent
3522         url(../../resources/images/icons/silk/building.png)
3523         no-repeat 3px 3px ! important;
3524         padding-left:20px;
3525 }
3526  </code></pre>
3527  * Note that the "Company Name" column could be specified as the
3528  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
3529  * @constructor
3530  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
3531  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
3532  */
3533 Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
3534     /**
3535      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
3536      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
3537      * <tt><b>{@link #defaults}</b></tt> config property.
3538      */
3539     defaultWidth: 100,
3540     /**
3541      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
3542      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
3543      * through the <tt><b>{@link #defaults}</b></tt> config property.
3544      */
3545     defaultSortable: false,
3546     /**
3547      * @cfg {Array} columns An Array of object literals.  The config options defined by
3548      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
3549      * individual column definition.
3550      */
3551     /**
3552      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
3553      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
3554      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
3555      */
3556
3557     constructor : function(config){
3558         /**
3559              * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
3560              * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
3561              * be specified.
3562              * @property config
3563              * @type Array
3564              */
3565             if(config.columns){
3566                 Ext.apply(this, config);
3567                 this.setConfig(config.columns, true);
3568             }else{
3569                 this.setConfig(config, true);
3570             }
3571             this.addEvents(
3572                 /**
3573                  * @event widthchange
3574                  * Fires when the width of a column is programmaticially changed using
3575                  * <code>{@link #setColumnWidth}</code>.
3576                  * Note internal resizing suppresses the event from firing. See also
3577                  * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
3578                  * @param {ColumnModel} this
3579                  * @param {Number} columnIndex The column index
3580                  * @param {Number} newWidth The new width
3581                  */
3582                 "widthchange",
3583                 /**
3584                  * @event headerchange
3585                  * Fires when the text of a header changes.
3586                  * @param {ColumnModel} this
3587                  * @param {Number} columnIndex The column index
3588                  * @param {String} newText The new header text
3589                  */
3590                 "headerchange",
3591                 /**
3592                  * @event hiddenchange
3593                  * Fires when a column is hidden or "unhidden".
3594                  * @param {ColumnModel} this
3595                  * @param {Number} columnIndex The column index
3596                  * @param {Boolean} hidden true if hidden, false otherwise
3597                  */
3598                 "hiddenchange",
3599                 /**
3600                  * @event columnmoved
3601                  * Fires when a column is moved.
3602                  * @param {ColumnModel} this
3603                  * @param {Number} oldIndex
3604                  * @param {Number} newIndex
3605                  */
3606                 "columnmoved",
3607                 /**
3608                  * @event configchange
3609                  * Fires when the configuration is changed
3610                  * @param {ColumnModel} this
3611                  */
3612                 "configchange"
3613             );
3614             Ext.grid.ColumnModel.superclass.constructor.call(this);
3615     },
3616
3617     /**
3618      * Returns the id of the column at the specified index.
3619      * @param {Number} index The column index
3620      * @return {String} the id
3621      */
3622     getColumnId : function(index){
3623         return this.config[index].id;
3624     },
3625
3626     getColumnAt : function(index){
3627         return this.config[index];
3628     },
3629
3630     /**
3631      * <p>Reconfigures this column model according to the passed Array of column definition objects.
3632      * For a description of the individual properties of a column definition object, see the
3633      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
3634      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
3635      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
3636      * @param {Array} config Array of Column definition objects.
3637      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
3638      * and destroys existing editors.
3639      */
3640     setConfig : function(config, initial){
3641         var i, c, len;
3642         if(!initial){ // cleanup
3643             delete this.totalWidth;
3644             for(i = 0, len = this.config.length; i < len; i++){
3645                 c = this.config[i];
3646                 if(c.setEditor){
3647                     //check here, in case we have a special column like a CheckboxSelectionModel
3648                     c.setEditor(null);
3649                 }
3650             }
3651         }
3652
3653         // backward compatibility
3654         this.defaults = Ext.apply({
3655             width: this.defaultWidth,
3656             sortable: this.defaultSortable
3657         }, this.defaults);
3658
3659         this.config = config;
3660         this.lookup = {};
3661
3662         for(i = 0, len = config.length; i < len; i++){
3663             c = Ext.applyIf(config[i], this.defaults);
3664             // if no id, create one using column's ordinal position
3665             if(Ext.isEmpty(c.id)){
3666                 c.id = i;
3667             }
3668             if(!c.isColumn){
3669                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
3670                 c = new Cls(c);
3671                 config[i] = c;
3672             }
3673             this.lookup[c.id] = c;
3674         }
3675         if(!initial){
3676             this.fireEvent('configchange', this);
3677         }
3678     },
3679
3680     /**
3681      * Returns the column for a specified id.
3682      * @param {String} id The column id
3683      * @return {Object} the column
3684      */
3685     getColumnById : function(id){
3686         return this.lookup[id];
3687     },
3688
3689     /**
3690      * Returns the index for a specified column id.
3691      * @param {String} id The column id
3692      * @return {Number} the index, or -1 if not found
3693      */
3694     getIndexById : function(id){
3695         for(var i = 0, len = this.config.length; i < len; i++){
3696             if(this.config[i].id == id){
3697                 return i;
3698             }
3699         }
3700         return -1;
3701     },
3702
3703     /**
3704      * Moves a column from one position to another.
3705      * @param {Number} oldIndex The index of the column to move.
3706      * @param {Number} newIndex The position at which to reinsert the coolumn.
3707      */
3708     moveColumn : function(oldIndex, newIndex){
3709         var c = this.config[oldIndex];
3710         this.config.splice(oldIndex, 1);
3711         this.config.splice(newIndex, 0, c);
3712         this.dataMap = null;
3713         this.fireEvent("columnmoved", this, oldIndex, newIndex);
3714     },
3715
3716     /**
3717      * Returns the number of columns.
3718      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
3719      * @return {Number}
3720      */
3721     getColumnCount : function(visibleOnly){
3722         if(visibleOnly === true){
3723             var c = 0;
3724             for(var i = 0, len = this.config.length; i < len; i++){
3725                 if(!this.isHidden(i)){
3726                     c++;
3727                 }
3728             }
3729             return c;
3730         }
3731         return this.config.length;
3732     },
3733
3734     /**
3735      * Returns the column configs that return true by the passed function that is called
3736      * with (columnConfig, index)
3737 <pre><code>
3738 // returns an array of column config objects for all hidden columns
3739 var columns = grid.getColumnModel().getColumnsBy(function(c){
3740   return c.hidden;
3741 });
3742 </code></pre>
3743      * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
3744      * return <code>true</code> if the column is to be included in the returned Array.
3745      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
3746      * is executed. Defaults to this ColumnModel.
3747      * @return {Array} result
3748      */
3749     getColumnsBy : function(fn, scope){
3750         var r = [];
3751         for(var i = 0, len = this.config.length; i < len; i++){
3752             var c = this.config[i];
3753             if(fn.call(scope||this, c, i) === true){
3754                 r[r.length] = c;
3755             }
3756         }
3757         return r;
3758     },
3759
3760     /**
3761      * Returns true if the specified column is sortable.
3762      * @param {Number} col The column index
3763      * @return {Boolean}
3764      */
3765     isSortable : function(col){
3766         return !!this.config[col].sortable;
3767     },
3768
3769     /**
3770      * Returns true if the specified column menu is disabled.
3771      * @param {Number} col The column index
3772      * @return {Boolean}
3773      */
3774     isMenuDisabled : function(col){
3775         return !!this.config[col].menuDisabled;
3776     },
3777
3778     /**
3779      * Returns the rendering (formatting) function defined for the column.
3780      * @param {Number} col The column index.
3781      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
3782      */
3783     getRenderer : function(col){
3784         if(!this.config[col].renderer){
3785             return Ext.grid.ColumnModel.defaultRenderer;
3786         }
3787         return this.config[col].renderer;
3788     },
3789
3790     getRendererScope : function(col){
3791         return this.config[col].scope;
3792     },
3793
3794     /**
3795      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
3796      * default formatting functions.
3797      * @param {Number} col The column index
3798      * @param {Function} fn The function to use to process the cell's raw data
3799      * to return HTML markup for the grid view. The render function is called with
3800      * the following parameters:<ul>
3801      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
3802      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
3803      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
3804      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
3805      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
3806      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
3807      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
3808      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
3809      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>
3810      */
3811     setRenderer : function(col, fn){
3812         this.config[col].renderer = fn;
3813     },
3814
3815     /**
3816      * Returns the width for the specified column.
3817      * @param {Number} col The column index
3818      * @return {Number}
3819      */
3820     getColumnWidth : function(col){
3821         return this.config[col].width;
3822     },
3823
3824     /**
3825      * Sets the width for a column.
3826      * @param {Number} col The column index
3827      * @param {Number} width The new width
3828      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
3829      * event. Defaults to false.
3830      */
3831     setColumnWidth : function(col, width, suppressEvent){
3832         this.config[col].width = width;
3833         this.totalWidth = null;
3834         if(!suppressEvent){
3835              this.fireEvent("widthchange", this, col, width);
3836         }
3837     },
3838
3839     /**
3840      * Returns the total width of all columns.
3841      * @param {Boolean} includeHidden True to include hidden column widths
3842      * @return {Number}
3843      */
3844     getTotalWidth : function(includeHidden){
3845         if(!this.totalWidth){
3846             this.totalWidth = 0;
3847             for(var i = 0, len = this.config.length; i < len; i++){
3848                 if(includeHidden || !this.isHidden(i)){
3849                     this.totalWidth += this.getColumnWidth(i);
3850                 }
3851             }
3852         }
3853         return this.totalWidth;
3854     },
3855
3856     /**
3857      * Returns the header for the specified column.
3858      * @param {Number} col The column index
3859      * @return {String}
3860      */
3861     getColumnHeader : function(col){
3862         return this.config[col].header;
3863     },
3864
3865     /**
3866      * Sets the header for a column.
3867      * @param {Number} col The column index
3868      * @param {String} header The new header
3869      */
3870     setColumnHeader : function(col, header){
3871         this.config[col].header = header;
3872         this.fireEvent("headerchange", this, col, header);
3873     },
3874
3875     /**
3876      * Returns the tooltip for the specified column.
3877      * @param {Number} col The column index
3878      * @return {String}
3879      */
3880     getColumnTooltip : function(col){
3881             return this.config[col].tooltip;
3882     },
3883     /**
3884      * Sets the tooltip for a column.
3885      * @param {Number} col The column index
3886      * @param {String} tooltip The new tooltip
3887      */
3888     setColumnTooltip : function(col, tooltip){
3889             this.config[col].tooltip = tooltip;
3890     },
3891
3892     /**
3893      * Returns the dataIndex for the specified column.
3894 <pre><code>
3895 // Get field name for the column
3896 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
3897 </code></pre>
3898      * @param {Number} col The column index
3899      * @return {String} The column's dataIndex
3900      */
3901     getDataIndex : function(col){
3902         return this.config[col].dataIndex;
3903     },
3904
3905     /**
3906      * Sets the dataIndex for a column.
3907      * @param {Number} col The column index
3908      * @param {String} dataIndex The new dataIndex
3909      */
3910     setDataIndex : function(col, dataIndex){
3911         this.config[col].dataIndex = dataIndex;
3912     },
3913
3914     /**
3915      * Finds the index of the first matching column for the given dataIndex.
3916      * @param {String} col The dataIndex to find
3917      * @return {Number} The column index, or -1 if no match was found
3918      */
3919     findColumnIndex : function(dataIndex){
3920         var c = this.config;
3921         for(var i = 0, len = c.length; i < len; i++){
3922             if(c[i].dataIndex == dataIndex){
3923                 return i;
3924             }
3925         }
3926         return -1;
3927     },
3928
3929     /**
3930      * Returns true if the cell is editable.
3931 <pre><code>
3932 var store = new Ext.data.Store({...});
3933 var colModel = new Ext.grid.ColumnModel({
3934   columns: [...],
3935   isCellEditable: function(col, row) {
3936     var record = store.getAt(row);
3937     if (record.get('readonly')) { // replace with your condition
3938       return false;
3939     }
3940     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
3941   }
3942 });
3943 var grid = new Ext.grid.GridPanel({
3944   store: store,
3945   colModel: colModel,
3946   ...
3947 });
3948 </code></pre>
3949      * @param {Number} colIndex The column index
3950      * @param {Number} rowIndex The row index
3951      * @return {Boolean}
3952      */
3953     isCellEditable : function(colIndex, rowIndex){
3954         var c = this.config[colIndex],
3955             ed = c.editable;
3956
3957         //force boolean
3958         return !!(ed || (!Ext.isDefined(ed) && c.editor));
3959     },
3960
3961     /**
3962      * Returns the editor defined for the cell/column.
3963      * @param {Number} colIndex The column index
3964      * @param {Number} rowIndex The row index
3965      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
3966      * the {@link Ext.form.Field Field} used to edit the cell.
3967      */
3968     getCellEditor : function(colIndex, rowIndex){
3969         return this.config[colIndex].getCellEditor(rowIndex);
3970     },
3971
3972     /**
3973      * Sets if a column is editable.
3974      * @param {Number} col The column index
3975      * @param {Boolean} editable True if the column is editable
3976      */
3977     setEditable : function(col, editable){
3978         this.config[col].editable = editable;
3979     },
3980
3981     /**
3982      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
3983      * <tt>false</tt> otherwise.
3984      * @param {Number} colIndex The column index
3985      * @return {Boolean}
3986      */
3987     isHidden : function(colIndex){
3988         return !!this.config[colIndex].hidden; // ensure returns boolean
3989     },
3990
3991     /**
3992      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
3993      * <tt>false</tt> otherwise.
3994      * @param {Number} colIndex The column index
3995      * @return {Boolean}
3996      */
3997     isFixed : function(colIndex){
3998         return !!this.config[colIndex].fixed;
3999     },
4000
4001     /**
4002      * Returns true if the column can be resized
4003      * @return {Boolean}
4004      */
4005     isResizable : function(colIndex){
4006         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4007     },
4008     /**
4009      * Sets if a column is hidden.
4010 <pre><code>
4011 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
4012 </code></pre>
4013      * @param {Number} colIndex The column index
4014      * @param {Boolean} hidden True if the column is hidden
4015      */
4016     setHidden : function(colIndex, hidden){
4017         var c = this.config[colIndex];
4018         if(c.hidden !== hidden){
4019             c.hidden = hidden;
4020             this.totalWidth = null;
4021             this.fireEvent("hiddenchange", this, colIndex, hidden);
4022         }
4023     },
4024
4025     /**
4026      * Sets the editor for a column and destroys the prior editor.
4027      * @param {Number} col The column index
4028      * @param {Object} editor The editor object
4029      */
4030     setEditor : function(col, editor){
4031         this.config[col].setEditor(editor);
4032     },
4033
4034     /**
4035      * Destroys this column model by purging any event listeners, and removing any editors.
4036      */
4037     destroy : function(){
4038         var c;
4039         for(var i = 0, len = this.config.length; i < len; i++){
4040             c = this.config[i];
4041             if(c.setEditor){
4042                 c.setEditor(null);
4043             }
4044         }
4045         this.purgeListeners();
4046     },
4047     
4048     /**
4049      * @private
4050      * Setup any saved state for the column, ensures that defaults are applied.
4051      */
4052     setState : function(col, state){
4053         Ext.applyIf(this.config[col], state);
4054     }
4055 });
4056
4057 // private
4058 Ext.grid.ColumnModel.defaultRenderer = function(value){
4059     if(typeof value == "string" && value.length < 1){
4060         return "&#160;";
4061     }
4062     return value;
4063 };/**
4064  * @class Ext.grid.AbstractSelectionModel
4065  * @extends Ext.util.Observable
4066  * Abstract base class for grid SelectionModels.  It provides the interface that should be
4067  * implemented by descendant classes.  This class should not be directly instantiated.
4068  * @constructor
4069  */
4070 Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable,  {
4071     /**
4072      * The GridPanel for which this SelectionModel is handling selection. Read-only.
4073      * @type Object
4074      * @property grid
4075      */
4076
4077     constructor : function(){
4078         this.locked = false;
4079         Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
4080     },
4081
4082     /** @ignore Called by the grid automatically. Do not call directly. */
4083     init : function(grid){
4084         this.grid = grid;
4085         if(this.lockOnInit){
4086             delete this.lockOnInit;
4087             this.locked = false;
4088             this.lock();
4089         }
4090         this.initEvents();
4091     },
4092
4093     /**
4094      * Locks the selections.
4095      */
4096     lock : function(){
4097         if(!this.locked){
4098             this.locked = true;
4099             // If the grid has been set, then the view is already initialized.
4100             var g = this.grid;
4101             if(g){
4102                 g.getView().on({
4103                     scope: this,
4104                     beforerefresh: this.sortUnLock,
4105                     refresh: this.sortLock
4106                 });
4107             }else{
4108                 this.lockOnInit = true;
4109             }
4110         }
4111     },
4112
4113     // set the lock states before and after a view refresh
4114     sortLock : function() {
4115         this.locked = true;
4116     },
4117
4118     // set the lock states before and after a view refresh
4119     sortUnLock : function() {
4120         this.locked = false;
4121     },
4122
4123     /**
4124      * Unlocks the selections.
4125      */
4126     unlock : function(){
4127         if(this.locked){
4128             this.locked = false;
4129             var g = this.grid,
4130                 gv;
4131                 
4132             // If the grid has been set, then the view is already initialized.
4133             if(g){
4134                 gv = g.getView();
4135                 gv.un('beforerefresh', this.sortUnLock, this);
4136                 gv.un('refresh', this.sortLock, this);    
4137             }else{
4138                 delete this.lockOnInit;
4139             }
4140         }
4141     },
4142
4143     /**
4144      * Returns true if the selections are locked.
4145      * @return {Boolean}
4146      */
4147     isLocked : function(){
4148         return this.locked;
4149     },
4150
4151     destroy: function(){
4152         this.unlock();
4153         this.purgeListeners();
4154     }
4155 });/**
4156  * @class Ext.grid.RowSelectionModel
4157  * @extends Ext.grid.AbstractSelectionModel
4158  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
4159  * It supports multiple selections and keyboard selection/navigation. The objects stored
4160  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
4161  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
4162  * @constructor
4163  * @param {Object} config
4164  */
4165 Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
4166     /**
4167      * @cfg {Boolean} singleSelect
4168      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
4169      * allowing multiple selections)
4170      */
4171     singleSelect : false,
4172     
4173     constructor : function(config){
4174         Ext.apply(this, config);
4175         this.selections = new Ext.util.MixedCollection(false, function(o){
4176             return o.id;
4177         });
4178
4179         this.last = false;
4180         this.lastActive = false;
4181
4182         this.addEvents(
4183                 /**
4184                  * @event selectionchange
4185                  * Fires when the selection changes
4186                  * @param {SelectionModel} this
4187                  */
4188                 'selectionchange',
4189                 /**
4190                  * @event beforerowselect
4191                  * Fires before a row is selected, return false to cancel the selection.
4192                  * @param {SelectionModel} this
4193                  * @param {Number} rowIndex The index to be selected
4194                  * @param {Boolean} keepExisting False if other selections will be cleared
4195                  * @param {Record} record The record to be selected
4196                  */
4197                 'beforerowselect',
4198                 /**
4199                  * @event rowselect
4200                  * Fires when a row is selected.
4201                  * @param {SelectionModel} this
4202                  * @param {Number} rowIndex The selected index
4203                  * @param {Ext.data.Record} r The selected record
4204                  */
4205                 'rowselect',
4206                 /**
4207                  * @event rowdeselect
4208                  * Fires when a row is deselected.  To prevent deselection
4209                  * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
4210                  * @param {SelectionModel} this
4211                  * @param {Number} rowIndex
4212                  * @param {Record} record
4213                  */
4214                 'rowdeselect'
4215         );
4216         Ext.grid.RowSelectionModel.superclass.constructor.call(this);
4217     },
4218
4219     /**
4220      * @cfg {Boolean} moveEditorOnEnter
4221      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
4222      * or the next row up when shift + enter keys are pressed.
4223      */
4224     // private
4225     initEvents : function(){
4226
4227         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
4228             this.grid.on('rowmousedown', this.handleMouseDown, this);
4229         }
4230
4231         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
4232             'up' : function(e){
4233                 if(!e.shiftKey || this.singleSelect){
4234                     this.selectPrevious(false);
4235                 }else if(this.last !== false && this.lastActive !== false){
4236                     var last = this.last;
4237                     this.selectRange(this.last,  this.lastActive-1);
4238                     this.grid.getView().focusRow(this.lastActive);
4239                     if(last !== false){
4240                         this.last = last;
4241                     }
4242                 }else{
4243                     this.selectFirstRow();
4244                 }
4245             },
4246             'down' : function(e){
4247                 if(!e.shiftKey || this.singleSelect){
4248                     this.selectNext(false);
4249                 }else if(this.last !== false && this.lastActive !== false){
4250                     var last = this.last;
4251                     this.selectRange(this.last,  this.lastActive+1);
4252                     this.grid.getView().focusRow(this.lastActive);
4253                     if(last !== false){
4254                         this.last = last;
4255                     }
4256                 }else{
4257                     this.selectFirstRow();
4258                 }
4259             },
4260             scope: this
4261         });
4262
4263         this.grid.getView().on({
4264             scope: this,
4265             refresh: this.onRefresh,
4266             rowupdated: this.onRowUpdated,
4267             rowremoved: this.onRemove
4268         });
4269     },
4270
4271     // private
4272     onRefresh : function(){
4273         var ds = this.grid.store, index;
4274         var s = this.getSelections();
4275         this.clearSelections(true);
4276         for(var i = 0, len = s.length; i < len; i++){
4277             var r = s[i];
4278             if((index = ds.indexOfId(r.id)) != -1){
4279                 this.selectRow(index, true);
4280             }
4281         }
4282         if(s.length != this.selections.getCount()){
4283             this.fireEvent('selectionchange', this);
4284         }
4285     },
4286
4287     // private
4288     onRemove : function(v, index, r){
4289         if(this.selections.remove(r) !== false){
4290             this.fireEvent('selectionchange', this);
4291         }
4292     },
4293
4294     // private
4295     onRowUpdated : function(v, index, r){
4296         if(this.isSelected(r)){
4297             v.onRowSelect(index);
4298         }
4299     },
4300
4301     /**
4302      * Select records.
4303      * @param {Array} records The records to select
4304      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
4305      */
4306     selectRecords : function(records, keepExisting){
4307         if(!keepExisting){
4308             this.clearSelections();
4309         }
4310         var ds = this.grid.store;
4311         for(var i = 0, len = records.length; i < len; i++){
4312             this.selectRow(ds.indexOf(records[i]), true);
4313         }
4314     },
4315
4316     /**
4317      * Gets the number of selected rows.
4318      * @return {Number}
4319      */
4320     getCount : function(){
4321         return this.selections.length;
4322     },
4323
4324     /**
4325      * Selects the first row in the grid.
4326      */
4327     selectFirstRow : function(){
4328         this.selectRow(0);
4329     },
4330
4331     /**
4332      * Select the last row.
4333      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
4334      */
4335     selectLastRow : function(keepExisting){
4336         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
4337     },
4338
4339     /**
4340      * Selects the row immediately following the last selected row.
4341      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
4342      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
4343      */
4344     selectNext : function(keepExisting){
4345         if(this.hasNext()){
4346             this.selectRow(this.last+1, keepExisting);
4347             this.grid.getView().focusRow(this.last);
4348             return true;
4349         }
4350         return false;
4351     },
4352
4353     /**
4354      * Selects the row that precedes the last selected row.
4355      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
4356      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
4357      */
4358     selectPrevious : function(keepExisting){
4359         if(this.hasPrevious()){
4360             this.selectRow(this.last-1, keepExisting);
4361             this.grid.getView().focusRow(this.last);
4362             return true;
4363         }
4364         return false;
4365     },
4366
4367     /**
4368      * Returns true if there is a next record to select
4369      * @return {Boolean}
4370      */
4371     hasNext : function(){
4372         return this.last !== false && (this.last+1) < this.grid.store.getCount();
4373     },
4374
4375     /**
4376      * Returns true if there is a previous record to select
4377      * @return {Boolean}
4378      */
4379     hasPrevious : function(){
4380         return !!this.last;
4381     },
4382
4383
4384     /**
4385      * Returns the selected records
4386      * @return {Array} Array of selected records
4387      */
4388     getSelections : function(){
4389         return [].concat(this.selections.items);
4390     },
4391
4392     /**
4393      * Returns the first selected record.
4394      * @return {Record}
4395      */
4396     getSelected : function(){
4397         return this.selections.itemAt(0);
4398     },
4399
4400     /**
4401      * Calls the passed function with each selection. If the function returns
4402      * <tt>false</tt>, iteration is stopped and this function returns
4403      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
4404      * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
4405      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
4406      * @return {Boolean} true if all selections were iterated
4407      */
4408     each : function(fn, scope){
4409         var s = this.getSelections();
4410         for(var i = 0, len = s.length; i < len; i++){
4411             if(fn.call(scope || this, s[i], i) === false){
4412                 return false;
4413             }
4414         }
4415         return true;
4416     },
4417
4418     /**
4419      * Clears all selections if the selection model
4420      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
4421      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
4422      * conditional checks and events described in {@link #deselectRow}.
4423      */
4424     clearSelections : function(fast){
4425         if(this.isLocked()){
4426             return;
4427         }
4428         if(fast !== true){
4429             var ds = this.grid.store;
4430             var s = this.selections;
4431             s.each(function(r){
4432                 this.deselectRow(ds.indexOfId(r.id));
4433             }, this);
4434             s.clear();
4435         }else{
4436             this.selections.clear();
4437         }
4438         this.last = false;
4439     },
4440
4441
4442     /**
4443      * Selects all rows if the selection model
4444      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
4445      */
4446     selectAll : function(){
4447         if(this.isLocked()){
4448             return;
4449         }
4450         this.selections.clear();
4451         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
4452             this.selectRow(i, true);
4453         }
4454     },
4455
4456     /**
4457      * Returns <tt>true</tt> if there is a selection.
4458      * @return {Boolean}
4459      */
4460     hasSelection : function(){
4461         return this.selections.length > 0;
4462     },
4463
4464     /**
4465      * Returns <tt>true</tt> if the specified row is selected.
4466      * @param {Number/Record} index The record or index of the record to check
4467      * @return {Boolean}
4468      */
4469     isSelected : function(index){
4470         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
4471         return (r && this.selections.key(r.id) ? true : false);
4472     },
4473
4474     /**
4475      * Returns <tt>true</tt> if the specified record id is selected.
4476      * @param {String} id The id of record to check
4477      * @return {Boolean}
4478      */
4479     isIdSelected : function(id){
4480         return (this.selections.key(id) ? true : false);
4481     },
4482
4483     // private
4484     handleMouseDown : function(g, rowIndex, e){
4485         if(e.button !== 0 || this.isLocked()){
4486             return;
4487         }
4488         var view = this.grid.getView();
4489         if(e.shiftKey && !this.singleSelect && this.last !== false){
4490             var last = this.last;
4491             this.selectRange(last, rowIndex, e.ctrlKey);
4492             this.last = last; // reset the last
4493             view.focusRow(rowIndex);
4494         }else{
4495             var isSelected = this.isSelected(rowIndex);
4496             if(e.ctrlKey && isSelected){
4497                 this.deselectRow(rowIndex);
4498             }else if(!isSelected || this.getCount() > 1){
4499                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
4500                 view.focusRow(rowIndex);
4501             }
4502         }
4503     },
4504
4505     /**
4506      * Selects multiple rows.
4507      * @param {Array} rows Array of the indexes of the row to select
4508      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
4509      * existing selections (defaults to <tt>false</tt>)
4510      */
4511     selectRows : function(rows, keepExisting){
4512         if(!keepExisting){
4513             this.clearSelections();
4514         }
4515         for(var i = 0, len = rows.length; i < len; i++){
4516             this.selectRow(rows[i], true);
4517         }
4518     },
4519
4520     /**
4521      * Selects a range of rows if the selection model
4522      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
4523      * All rows in between startRow and endRow are also selected.
4524      * @param {Number} startRow The index of the first row in the range
4525      * @param {Number} endRow The index of the last row in the range
4526      * @param {Boolean} keepExisting (optional) True to retain existing selections
4527      */
4528     selectRange : function(startRow, endRow, keepExisting){
4529         var i;
4530         if(this.isLocked()){
4531             return;
4532         }
4533         if(!keepExisting){
4534             this.clearSelections();
4535         }
4536         if(startRow <= endRow){
4537             for(i = startRow; i <= endRow; i++){
4538                 this.selectRow(i, true);
4539             }
4540         }else{
4541             for(i = startRow; i >= endRow; i--){
4542                 this.selectRow(i, true);
4543             }
4544         }
4545     },
4546
4547     /**
4548      * Deselects a range of rows if the selection model
4549      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
4550      * All rows in between startRow and endRow are also deselected.
4551      * @param {Number} startRow The index of the first row in the range
4552      * @param {Number} endRow The index of the last row in the range
4553      */
4554     deselectRange : function(startRow, endRow, preventViewNotify){
4555         if(this.isLocked()){
4556             return;
4557         }
4558         for(var i = startRow; i <= endRow; i++){
4559             this.deselectRow(i, preventViewNotify);
4560         }
4561     },
4562
4563     /**
4564      * Selects a row.  Before selecting a row, checks if the selection model
4565      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
4566      * {@link #beforerowselect} event.  If these checks are satisfied the row
4567      * will be selected and followed up by  firing the {@link #rowselect} and
4568      * {@link #selectionchange} events.
4569      * @param {Number} row The index of the row to select
4570      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
4571      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
4572      * prevent notifying the view (disables updating the selected appearance)
4573      */
4574     selectRow : function(index, keepExisting, preventViewNotify){
4575         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
4576             return;
4577         }
4578         var r = this.grid.store.getAt(index);
4579         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
4580             if(!keepExisting || this.singleSelect){
4581                 this.clearSelections();
4582             }
4583             this.selections.add(r);
4584             this.last = this.lastActive = index;
4585             if(!preventViewNotify){
4586                 this.grid.getView().onRowSelect(index);
4587             }
4588             this.fireEvent('rowselect', this, index, r);
4589             this.fireEvent('selectionchange', this);
4590         }
4591     },
4592
4593     /**
4594      * Deselects a row.  Before deselecting a row, checks if the selection model
4595      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
4596      * If this check is satisfied the row will be deselected and followed up by
4597      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
4598      * @param {Number} row The index of the row to deselect
4599      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
4600      * prevent notifying the view (disables updating the selected appearance)
4601      */
4602     deselectRow : function(index, preventViewNotify){
4603         if(this.isLocked()){
4604             return;
4605         }
4606         if(this.last == index){
4607             this.last = false;
4608         }
4609         if(this.lastActive == index){
4610             this.lastActive = false;
4611         }
4612         var r = this.grid.store.getAt(index);
4613         if(r){
4614             this.selections.remove(r);
4615             if(!preventViewNotify){
4616                 this.grid.getView().onRowDeselect(index);
4617             }
4618             this.fireEvent('rowdeselect', this, index, r);
4619             this.fireEvent('selectionchange', this);
4620         }
4621     },
4622
4623     // private
4624     restoreLast : function(){
4625         if(this._last){
4626             this.last = this._last;
4627         }
4628     },
4629
4630     // private
4631     acceptsNav : function(row, col, cm){
4632         return !cm.isHidden(col) && cm.isCellEditable(col, row);
4633     },
4634
4635     // private
4636     onEditorKey : function(field, e){
4637         var k = e.getKey(), 
4638             newCell, 
4639             g = this.grid, 
4640             last = g.lastEdit,
4641             ed = g.activeEditor,
4642             ae, last, r, c;
4643         var shift = e.shiftKey;
4644         if(k == e.TAB){
4645             e.stopEvent();
4646             ed.completeEdit();
4647             if(shift){
4648                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
4649             }else{
4650                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
4651             }
4652         }else if(k == e.ENTER){
4653             if(this.moveEditorOnEnter !== false){
4654                 if(shift){
4655                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
4656                 }else{
4657                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
4658                 }
4659             }
4660         }
4661         if(newCell){
4662             r = newCell[0];
4663             c = newCell[1];
4664
4665             if(last.row != r){
4666                 this.selectRow(r); // *** highlight newly-selected cell and update selection
4667             }
4668
4669             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
4670                 ae = g.activeEditor;
4671                 if(ae && ae.field.triggerBlur){
4672                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
4673                     ae.field.triggerBlur();
4674                 }
4675             }
4676             g.startEditing(r, c);
4677         }
4678     },
4679     
4680     destroy : function(){
4681         if(this.rowNav){
4682             this.rowNav.disable();
4683             this.rowNav = null;
4684         }
4685         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
4686     }
4687 });/**
4688  * @class Ext.grid.Column
4689  * <p>This class encapsulates column configuration data to be used in the initialization of a
4690  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
4691  * <p>While subclasses are provided to render data in different ways, this class renders a passed
4692  * data field unchanged and is usually used for textual columns.</p>
4693  */
4694 Ext.grid.Column = Ext.extend(Object, {
4695     /**
4696      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
4697      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.
4698      * The initial configuration may be dynamically altered using
4699      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
4700      */
4701     /**
4702      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
4703      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
4704      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
4705      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
4706      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
4707      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
4708      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
4709      * unique identifier.
4710      */
4711     /**
4712      * @cfg {String} header Optional. The header text to be used as innerHTML
4713      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to
4714      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
4715      */
4716     /**
4717      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
4718      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
4719      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the
4720      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
4721      */
4722     /**
4723      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
4724      * may be used to specify the text with which to prefix the group field value in the group header line.
4725      * See also {@link #groupRenderer} and
4726      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
4727      */
4728     /**
4729      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
4730      * may be used to specify the function used to format the grouping field value for display in the group
4731      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured
4732      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
4733      * the new value of the group field will be used.</p>
4734      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
4735      * passed the following parameters:
4736      * <div class="mdetail-params"><ul>
4737      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
4738      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
4739      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
4740      * for the row which caused group change.</p></li>
4741      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
4742      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
4743      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
4744      * </ul></div></p>
4745      * <p>The function should return a string value.</p>
4746      */
4747     /**
4748      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
4749      * may be used to specify the text to display when there is an empty group value. Defaults to the
4750      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
4751      */
4752     /**
4753      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
4754      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
4755      * which to draw the column's value.</p>
4756      */
4757     /**
4758      * @cfg {Number} width
4759      * Optional. The initial width in pixels of the column.
4760      * The width of each column can also be affected if any of the following are configured:
4761      * <div class="mdetail-params"><ul>
4762      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
4763      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
4764      * <div class="sub-desc">
4765      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
4766      * re-proportioned (based on the relative initial widths) to fill the width of the grid so
4767      * that no horizontal scrollbar is shown.</p>
4768      * </div></li>
4769      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
4770      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
4771      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
4772      * is reserved for the vertical scrollbar.  The
4773      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
4774      * can be modified to reduce or eliminate the reserved offset.</p>
4775      */
4776     /**
4777      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
4778      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.
4779      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
4780      */
4781     /**
4782      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.
4783      */
4784     /**
4785      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
4786      */
4787     /**
4788      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
4789      */
4790     /**
4791      * @cfg {Boolean} hidden
4792      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.
4793      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.
4794      * If a column is never to be shown, simply do not include this column in the Column Model at all.
4795      */
4796     /**
4797      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips
4798      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
4799      * header's HTML title attribute. Defaults to ''.
4800      */
4801     /**
4802      * @cfg {Mixed} renderer
4803      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
4804      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
4805      * appearance, etc.) before it is rendered). This may be specified in either of three ways:
4806      * <div class="mdetail-params"><ul>
4807      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
4808      * <li>A string which references a property name of the {@link Ext.util.Format} class which
4809      * provides a renderer function.</li>
4810      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
4811      * reference) e.g.:<pre style="margin-left:1.2em"><code>
4812 {
4813     fn: this.gridRenderer,
4814     scope: this
4815 }
4816 </code></pre></li></ul></div>
4817      * If not specified, the default renderer uses the raw data value.</p>
4818      * <p>For information about the renderer function (passed parameters, etc.), see
4819      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
4820 var companyColumn = {
4821    header: 'Company Name',
4822    dataIndex: 'company',
4823    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
4824       // provide the logic depending on business rules
4825       // name of your own choosing to manipulate the cell depending upon
4826       // the data in the underlying Record object.
4827       if (value == 'whatever') {
4828           //metaData.css : String : A CSS class name to add to the TD element of the cell.
4829           //metaData.attr : String : An html attribute definition string to apply to
4830           //                         the data container element within the table
4831           //                         cell (e.g. 'style="color:red;"').
4832           metaData.css = 'name-of-css-class-you-will-define';
4833       }
4834       return value;
4835    }
4836 }
4837      * </code></pre>
4838      * See also {@link #scope}.
4839      */
4840     /**
4841      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
4842      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
4843      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
4844      * <div class="mdetail-params"><ul>
4845      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
4846      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
4847      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
4848      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
4849      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
4850      * </ul></div>
4851      * <p>Configuration properties for the specified <code>xtype</code> may be specified with
4852      * the Column configuration properties, for example:</p>
4853      * <pre><code>
4854 var grid = new Ext.grid.GridPanel({
4855     ...
4856     columns: [{
4857         header: 'Last Updated',
4858         dataIndex: 'lastChange',
4859         width: 85,
4860         sortable: true,
4861         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
4862         xtype: 'datecolumn', // use xtype instead of renderer
4863         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
4864     }, {
4865         ...
4866     }]
4867 });
4868      * </code></pre>
4869      */
4870     /**
4871      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
4872      * renderer.  Defaults to the Column configuration object.
4873      */
4874     /**
4875      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.
4876      */
4877     /**
4878      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
4879      * (excluding headers). Defaults to undefined.
4880      */
4881     /**
4882      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
4883      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use
4884      * {@link Ext.grid.GridPanel#enableColumnHide} instead.
4885      */
4886     /**
4887      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
4888      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
4889      */
4890
4891     /**
4892      * @private
4893      * @cfg {Boolean} isColumn
4894      * Used by ColumnModel setConfig method to avoid reprocessing a Column
4895      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column
4896      * Defaults to true.
4897      */
4898     isColumn : true,
4899
4900     constructor : function(config){
4901         Ext.apply(this, config);
4902
4903         if(Ext.isString(this.renderer)){
4904             this.renderer = Ext.util.Format[this.renderer];
4905         }else if(Ext.isObject(this.renderer)){
4906             this.scope = this.renderer.scope;
4907             this.renderer = this.renderer.fn;
4908         }
4909         if(!this.scope){
4910             this.scope = this;
4911         }
4912
4913         var ed = this.editor;
4914         delete this.editor;
4915         this.setEditor(ed);
4916     },
4917
4918     /**
4919      * Optional. A function which returns displayable data when passed the following parameters:
4920      * <div class="mdetail-params"><ul>
4921      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
4922      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
4923      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
4924      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
4925      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
4926      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
4927      * extracted.</p></li>
4928      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
4929      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
4930      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
4931      * was extracted.</p></li>
4932      * </ul></div>
4933      * @property renderer
4934      * @type Function
4935      */
4936     renderer : function(value){
4937         if(Ext.isString(value) && value.length < 1){
4938             return '&#160;';
4939         }
4940         return value;
4941     },
4942
4943     // private
4944     getEditor: function(rowIndex){
4945         return this.editable !== false ? this.editor : null;
4946     },
4947
4948     /**
4949      * Sets a new editor for this column.
4950      * @param {Ext.Editor/Ext.form.Field} editor The editor to set
4951      */
4952     setEditor : function(editor){
4953         var ed = this.editor;
4954         if(ed){
4955             if(ed.gridEditor){
4956                 ed.gridEditor.destroy();
4957                 delete ed.gridEditor;
4958             }else{
4959                 ed.destroy();
4960             }
4961         }
4962         this.editor = null;
4963         if(editor){
4964             //not an instance, create it
4965             if(!editor.isXType){
4966                 editor = Ext.create(editor, 'textfield');
4967             }
4968             this.editor = editor;
4969         }
4970     },
4971
4972     /**
4973      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
4974      * used to edit the cell.
4975      * @param {Number} rowIndex The row index
4976      * @return {Ext.Editor}
4977      */
4978     getCellEditor: function(rowIndex){
4979         var ed = this.getEditor(rowIndex);
4980         if(ed){
4981             if(!ed.startEdit){
4982                 if(!ed.gridEditor){
4983                     ed.gridEditor = new Ext.grid.GridEditor(ed);
4984                 }
4985                 ed = ed.gridEditor;
4986             }
4987         }
4988         return ed;
4989     }
4990 });
4991
4992 /**
4993  * @class Ext.grid.BooleanColumn
4994  * @extends Ext.grid.Column
4995  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}
4996  * config option of {@link Ext.grid.Column} for more details.</p>
4997  */
4998 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
4999     /**
5000      * @cfg {String} trueText
5001      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
5002      */
5003     trueText: 'true',
5004     /**
5005      * @cfg {String} falseText
5006      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
5007      * <tt>'false'</tt>).
5008      */
5009     falseText: 'false',
5010     /**
5011      * @cfg {String} undefinedText
5012      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
5013      */
5014     undefinedText: '&#160;',
5015
5016     constructor: function(cfg){
5017         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
5018         var t = this.trueText, f = this.falseText, u = this.undefinedText;
5019         this.renderer = function(v){
5020             if(v === undefined){
5021                 return u;
5022             }
5023             if(!v || v === 'false'){
5024                 return f;
5025             }
5026             return t;
5027         };
5028     }
5029 });
5030
5031 /**
5032  * @class Ext.grid.NumberColumn
5033  * @extends Ext.grid.Column
5034  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
5035  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>
5036  */
5037 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
5038     /**
5039      * @cfg {String} format
5040      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
5041      * (defaults to <tt>'0,000.00'</tt>).
5042      */
5043     format : '0,000.00',
5044     constructor: function(cfg){
5045         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
5046         this.renderer = Ext.util.Format.numberRenderer(this.format);
5047     }
5048 });
5049
5050 /**
5051  * @class Ext.grid.DateColumn
5052  * @extends Ext.grid.Column
5053  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
5054  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}
5055  * for more details.</p>
5056  */
5057 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
5058     /**
5059      * @cfg {String} format
5060      * A formatting string as used by {@link Date#format} to format a Date for this Column
5061      * (defaults to <tt>'m/d/Y'</tt>).
5062      */
5063     format : 'm/d/Y',
5064     constructor: function(cfg){
5065         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
5066         this.renderer = Ext.util.Format.dateRenderer(this.format);
5067     }
5068 });
5069
5070 /**
5071  * @class Ext.grid.TemplateColumn
5072  * @extends Ext.grid.Column
5073  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
5074  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
5075  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more
5076  * details.</p>
5077  */
5078 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
5079     /**
5080      * @cfg {String/XTemplate} tpl
5081      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
5082      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
5083      */
5084     constructor: function(cfg){
5085         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
5086         var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);
5087         this.renderer = function(value, p, r){
5088             return tpl.apply(r.data);
5089         };
5090         this.tpl = tpl;
5091     }
5092 });
5093
5094 /*
5095  * @property types
5096  * @type Object
5097  * @member Ext.grid.Column
5098  * @static
5099  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
5100  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
5101  * <p>This contains the following properties</p><div class="mdesc-details"><ul>
5102  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
5103  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
5104  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
5105  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
5106  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
5107  * </ul></div>
5108  */
5109 Ext.grid.Column.types = {
5110     gridcolumn : Ext.grid.Column,
5111     booleancolumn: Ext.grid.BooleanColumn,
5112     numbercolumn: Ext.grid.NumberColumn,
5113     datecolumn: Ext.grid.DateColumn,
5114     templatecolumn: Ext.grid.TemplateColumn
5115 };/**
5116  * @class Ext.grid.RowNumberer
5117  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
5118  * an automatic row numbering column.
5119  * <br>Usage:<br>
5120  <pre><code>
5121  // This is a typical column config with the first column providing row numbers
5122  var colModel = new Ext.grid.ColumnModel([
5123     new Ext.grid.RowNumberer(),
5124     {header: "Name", width: 80, sortable: true},
5125     {header: "Code", width: 50, sortable: true},
5126     {header: "Description", width: 200, sortable: true}
5127  ]);
5128  </code></pre>
5129  * @constructor
5130  * @param {Object} config The configuration options
5131  */
5132 Ext.grid.RowNumberer = Ext.extend(Object, {
5133     /**
5134      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
5135      * number column (defaults to '').
5136      */
5137     header: "",
5138     /**
5139      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
5140      */
5141     width: 23,
5142     /**
5143      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
5144      * @hide
5145      */
5146     sortable: false,
5147     
5148     constructor : function(config){
5149         Ext.apply(this, config);
5150         if(this.rowspan){
5151             this.renderer = this.renderer.createDelegate(this);
5152         }
5153     },
5154
5155     // private
5156     fixed:true,
5157     hideable: false,
5158     menuDisabled:true,
5159     dataIndex: '',
5160     id: 'numberer',
5161     rowspan: undefined,
5162
5163     // private
5164     renderer : function(v, p, record, rowIndex){
5165         if(this.rowspan){
5166             p.cellAttr = 'rowspan="'+this.rowspan+'"';
5167         }
5168         return rowIndex+1;
5169     }
5170 });/**
5171  * @class Ext.grid.CheckboxSelectionModel
5172  * @extends Ext.grid.RowSelectionModel
5173  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
5174  * @constructor
5175  * @param {Object} config The configuration options
5176  */
5177 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
5178
5179     /**
5180      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
5181      * checkbox column (defaults to <tt>false</tt>).
5182      */
5183     /**
5184      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
5185      * checkbox column.  Defaults to:<pre><code>
5186      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
5187      * </code></pre>
5188      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
5189      * and provides support for automatic check all/none behavior on header click. This string
5190      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
5191      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
5192      * <tt>'x-grid3-hd-checker'</tt> class is supplied.
5193      */
5194     header : '<div class="x-grid3-hd-checker">&#160;</div>',
5195     /**
5196      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
5197      */
5198     width : 20,
5199     /**
5200      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
5201      * <tt>false</tt>).
5202      */
5203     sortable : false,
5204
5205     // private
5206     menuDisabled : true,
5207     fixed : true,
5208     hideable: false,
5209     dataIndex : '',
5210     id : 'checker',
5211
5212     constructor : function(){
5213         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
5214
5215         if(this.checkOnly){
5216             this.handleMouseDown = Ext.emptyFn;
5217         }
5218     },
5219
5220     // private
5221     initEvents : function(){
5222         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
5223         this.grid.on('render', function(){
5224             var view = this.grid.getView();
5225             view.mainBody.on('mousedown', this.onMouseDown, this);
5226             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
5227
5228         }, this);
5229     },
5230
5231     // If handleMouseDown was called from another event (enableDragDrop), set a flag so
5232     // onMouseDown does not process it a second time
5233     handleMouseDown : function() {
5234         Ext.grid.CheckboxSelectionModel.superclass.handleMouseDown.apply(this, arguments);
5235         this.mouseHandled = true;
5236     },
5237
5238     // private
5239     onMouseDown : function(e, t){
5240         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
5241             e.stopEvent();
5242             var row = e.getTarget('.x-grid3-row');
5243
5244             // mouseHandled flag check for a duplicate selection (handleMouseDown) call
5245             if(!this.mouseHandled && row){
5246                 var index = row.rowIndex;
5247                 if(this.isSelected(index)){
5248                     this.deselectRow(index);
5249                 }else{
5250                     this.selectRow(index, true);
5251                     this.grid.getView().focusRow(index);
5252                 }
5253             }
5254         }
5255         this.mouseHandled = false;
5256     },
5257
5258     // private
5259     onHdMouseDown : function(e, t){
5260         if(t.className == 'x-grid3-hd-checker'){
5261             e.stopEvent();
5262             var hd = Ext.fly(t.parentNode);
5263             var isChecked = hd.hasClass('x-grid3-hd-checker-on');
5264             if(isChecked){
5265                 hd.removeClass('x-grid3-hd-checker-on');
5266                 this.clearSelections();
5267             }else{
5268                 hd.addClass('x-grid3-hd-checker-on');
5269                 this.selectAll();
5270             }
5271         }
5272     },
5273
5274     // private
5275     renderer : function(v, p, record){
5276         return '<div class="x-grid3-row-checker">&#160;</div>';
5277     }
5278 });