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