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