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