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