]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/panel/Table.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / panel / Table.js
CommitLineData
6527f429
DM
1/**\r
2 * This class is the base class for both {@link Ext.tree.Panel TreePanel} and\r
3 * {@link Ext.grid.Panel GridPanel}.\r
4 *\r
5 * TablePanel aggregates:\r
6 *\r
7 * - a Selection Model\r
8 * - a View\r
9 * - a Store\r
10 * - Ext.grid.header.Container\r
11 * \r
12 * @mixins Ext.grid.locking.Lockable\r
13 */\r
14Ext.define('Ext.panel.Table', {\r
15 extend: 'Ext.panel.Panel',\r
16\r
17 alias: 'widget.tablepanel',\r
18 \r
19 requires: [\r
20 'Ext.layout.container.Fit'\r
21 ],\r
22\r
23 uses: [\r
24 'Ext.selection.RowModel',\r
25 'Ext.selection.CellModel',\r
26 'Ext.selection.CheckboxModel',\r
27 'Ext.grid.plugin.BufferedRenderer',\r
28 'Ext.grid.header.Container',\r
29 'Ext.grid.locking.Lockable',\r
30 'Ext.grid.NavigationModel'\r
31 ],\r
32\r
33 extraBaseCls: Ext.baseCSSPrefix + 'grid',\r
34 extraBodyCls: Ext.baseCSSPrefix + 'grid-body',\r
35 actionableModeCls: Ext.baseCSSPrefix + 'grid-actionable',\r
36 noHeaderBordersCls: Ext.baseCSSPrefix + 'no-header-borders',\r
37\r
38 defaultBindProperty: 'store',\r
39\r
40 layout: 'fit',\r
41 \r
42 ariaRole: 'grid',\r
43\r
44 config: {\r
45 /**\r
46 * @cfg {Ext.data.Model} selection\r
47 * The selected model. Typically used with {@link #bind binding}.\r
48 */\r
49 selection: null,\r
50\r
51 /**\r
52 * @cfg {Boolean} [headerBorders=`true`]\r
53 * To show no borders around grid headers, configure this as `false`.\r
54 */\r
55 headerBorders: true\r
56 },\r
57\r
58 publishes: ['selection'],\r
59 twoWayBindable: ['selection'],\r
60\r
61 /**\r
62 * @cfg {Boolean} [autoLoad=false]\r
63 * Use `true` to load the store as soon as this component is fully constructed. It is\r
64 * best to initiate the store load this way to allow this component and potentially\r
65 * its plugins (such as `{@link Ext.grid.filters.Filters}`) to be ready to load.\r
66 */\r
67 autoLoad: false,\r
68\r
69 /**\r
70 * @cfg {Boolean} [variableRowHeight=false]\r
71 * @deprecated 5.0.0 Use {@link Ext.grid.column.Column#variableRowHeight} instead.\r
72 * Configure as `true` if the row heights are not all the same height as the first row.\r
73 */\r
74 variableRowHeight: false,\r
75\r
76 /**\r
77 * @cfg {Number} [numFromEdge]\r
78 * This configures the zone which causes new rows to be appended to the view. As soon as the edge\r
79 * of the rendered grid is this number of rows from the edge of the viewport, the view is moved.\r
80 */\r
81 numFromEdge: 2,\r
82\r
83 /**\r
84 * @cfg {Number} [trailingBufferZone]\r
85 * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows\r
86 * are rendered into the DOM. These are removed and added as scrolling demands.\r
87 *\r
88 * This configures the number of extra rows to render on the trailing side of scrolling\r
89 * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.\r
90 */\r
91 trailingBufferZone: 10,\r
92\r
93 /**\r
94 * @cfg {Number} [leadingBufferZone]\r
95 * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows\r
96 * are rendered into the DOM. These are removed and added as scrolling demands.\r
97 *\r
98 * This configures the number of extra rows to render on the leading side of scrolling\r
99 * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.\r
100 */\r
101 leadingBufferZone: 20,\r
102\r
103 /**\r
104 * @property {Boolean} hasView\r
105 * True to indicate that a view has been injected into the panel.\r
106 */\r
107 hasView: false,\r
108\r
109 /**\r
110 * @property items\r
111 * @hide\r
112 */\r
113\r
114 /**\r
115 * @cfg {String} viewType\r
116 * An xtype of view to use. This is automatically set to 'tableview' by {@link Ext.grid.Panel Grid}\r
117 * and to 'treeview' by {@link Ext.tree.Panel Tree}.\r
118 * @protected\r
119 */\r
120 viewType: null,\r
121\r
122 /**\r
123 * @cfg {Object} viewConfig\r
124 * A config object that will be applied to the grid's UI view. Any of the config options available for\r
125 * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.\r
126 */\r
127\r
128 /**\r
129 * @cfg {Ext.view.Table} view\r
130 * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to\r
131 * view (instead of creating an entire View instance).\r
132 */\r
133\r
134 /**\r
135 * @cfg {String} [selType]\r
136 * An xtype of selection model to use. This is used to create selection model if just\r
137 * a config object or nothing at all given in {@link #selModel} config.\r
138 *\r
139 * @deprecated 5.1.0 Use the {@link #selModel}'s `type` property. Or, if no other\r
140 * configs are required, use the string form of selModel.\r
141 */\r
142\r
143 /**\r
144 * @cfg {Ext.selection.Model/Object/String} [selModel=rowmodel]\r
145 * A {@link Ext.selection.Model selection model} instance or config object, or the selection model class's alias string.\r
146 *\r
147 * In latter case its `type` property determines to which type of selection model this config is applied.\r
148 */\r
149\r
150 /**\r
151 * @cfg {Boolean} [multiSelect=false]\r
152 * True to enable 'MULTI' selection mode on selection model.\r
153 * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'MULTI' instead.\r
154 */\r
155\r
156 /**\r
157 * @cfg {Boolean} [simpleSelect=false]\r
158 * True to enable 'SIMPLE' selection mode on selection model.\r
159 * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'SIMPLE' instead.\r
160 */\r
161\r
162 /**\r
163 * @cfg {Ext.data.Store/String/Object} store (required)\r
164 * The data source to which the grid / tree is bound. Acceptable values for this \r
165 * property are:\r
166 *\r
167 * - **any {@link Ext.data.Store Store} class / subclass**\r
168 * - **an {@link Ext.data.Store#storeId ID of a store}**\r
169 * - **a {@link Ext.data.Store Store} config object**. When passing a config you can \r
170 * specify the store type by alias. Passing a config object with a store type will \r
171 * dynamically create a new store of that type when the grid / tree is instantiated.\r
172 *\r
173 * For example:\r
174 * \r
175 * Ext.define('MyApp.store.Customers', {\r
176 * extend: 'Ext.data.Store',\r
177 * alias: 'store.customerstore',\r
178 * fields: ['name']\r
179 * });\r
180 * \r
181 * Ext.create({\r
182 * xtype: 'gridpanel',\r
183 * renderTo: document.body,\r
184 * store: {\r
185 * type: 'customerstore',\r
186 * data: [{\r
187 * name: 'Foo'\r
188 * }]\r
189 * },\r
190 * columns: [{\r
191 * text: 'Name',\r
192 * dataIndex: 'name'\r
193 * }]\r
194 * });\r
195 */\r
196\r
197 /**\r
198 * @cfg {String/Boolean} scroll\r
199 * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.\r
200 * True implies 'both'. False implies 'none'.\r
201 * @deprecated 5.1.0 Use {@link #scrollable} instead\r
202 */\r
203\r
204 /**\r
205 * @cfg {Boolean} [reserveScrollbar=false]\r
206 * Set this to true to **always** leave a scrollbar sized space at the end of the grid content when\r
207 * fitting content into the width of the grid.\r
208 *\r
209 * If the grid's record count fluctuates enough to hide and show the scrollbar regularly, this setting\r
210 * avoids the multiple layouts associated with switching from scrollbar present to scrollbar not present.\r
211 */\r
212\r
213 /**\r
214 * @cfg {Ext.grid.column.Column[]/Object} columns\r
215 * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this\r
216 * grid. Each column definition provides the header text for the column, and a definition of where the data for that\r
217 * column comes from.\r
218 *\r
219 * This can also be a configuration object for a {@link Ext.grid.header.Container HeaderContainer} which may override\r
220 * certain default configurations if necessary. For example, the special layout may be overridden to use a simpler\r
221 * layout, or one can set default values shared by all columns:\r
222 * \r
223 * columns: {\r
224 * items: [\r
225 * {\r
226 * text: "Column A",\r
227 * dataIndex: "field_A"\r
228 * },{\r
229 * text: "Column B",\r
230 * dataIndex: "field_B"\r
231 * }, \r
232 * ...\r
233 * ],\r
234 * defaults: {\r
235 * flex: 1\r
236 * }\r
237 * }\r
238 */\r
239\r
240 /**\r
241 * @cfg {Boolean} forceFit\r
242 * True to force the columns to fit into the available width. Headers are first sized according to configuration,\r
243 * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire\r
244 * content width is used. For more accurate control, it is more optimal to specify a flex setting on the columns\r
245 * that are to be stretched & explicit widths on columns that are not.\r
246 */\r
247\r
248 /**\r
249 * @cfg {Ext.grid.feature.Feature[]/Object[]/Ext.enums.Feature[]} features\r
250 * An array of grid Features to be added to this grid. Can also be just a single feature instead of array.\r
251 *\r
252 * Features config behaves much like {@link #plugins}.\r
253 * A feature can be added by either directly referencing the instance:\r
254 *\r
255 * features: [Ext.create('Ext.grid.feature.GroupingSummary', {groupHeaderTpl: 'Subject: {name}'})],\r
256 *\r
257 * By using config object with ftype:\r
258 *\r
259 * features: [{ftype: 'groupingsummary', groupHeaderTpl: 'Subject: {name}'}],\r
260 *\r
261 * Or with just a ftype:\r
262 *\r
263 * features: ['grouping', 'groupingsummary'],\r
264 *\r
265 * See {@link Ext.enums.Feature} for list of all ftypes.\r
266 */\r
267\r
268 /**\r
269 * @cfg {Boolean} [hideHeaders=false]\r
270 * True to hide column headers.\r
271 */\r
272\r
273 /**\r
274 * @cfg {Boolean} [deferRowRender=false]\r
275 * Configure as `true` to enable deferred row rendering.\r
276 *\r
277 * This allows the View to execute a refresh quickly, with the update of the row structure deferred so\r
278 * that layouts with GridPanels appear, and lay out more quickly.\r
279 */\r
280 deferRowRender: false,\r
281 \r
282 /**\r
283 * @cfg {Boolean} [sortableColumns=true]\r
284 * False to disable column sorting via clicking the header and via the Sorting menu items.\r
285 */\r
286 sortableColumns: true,\r
287\r
288 /**\r
289 * @cfg {Boolean} [multiColumnSort=false]\r
290 * Configure as `true` to have columns remember their sorted state after other columns have been clicked upon to sort.\r
291 *\r
292 * As subsequent columns are clicked upon, they become the new primary sort key.\r
293 *\r
294 * The maximum number of sorters allowed in a Store is configurable via its underlying data collection. See {@link Ext.util.Collection#multiSortLimit}\r
295 */\r
296 multiColumnSort: false,\r
297\r
298 /**\r
299 * @cfg {Boolean} [enableLocking=false]\r
300 * Configure as `true` to enable locking support for this grid. Alternatively, locking will also be automatically\r
301 * enabled if any of the columns in the {@link #columns columns} configuration contain a {@link Ext.grid.column.Column#locked locked} config option.\r
302 * \r
303 * A locking grid is processed in a special way. The configuration options are cloned and *two* grids are created to be the locked (left) side\r
304 * and the normal (right) side. This Panel becomes merely a {@link Ext.container.Container container} which arranges both in an {@link Ext.layout.container.HBox HBox} layout.\r
305 * \r
306 * {@link #plugins Plugins} may be targeted at either locked, or unlocked grid, or, both, in which case the plugin is cloned and used on both sides.\r
307 * \r
308 * Plugins may also be targeted at the containing locking Panel.\r
309 * \r
310 * This is configured by specifying a `lockableScope` property in your plugin which may have the following values:\r
311 * \r
312 * * `"both"` (the default) - The plugin is added to both grids\r
313 * * `"top"` - The plugin is added to the containing Panel\r
314 * * `"locked"` - The plugin is added to the locked (left) grid\r
315 * * `"normal"` - The plugin is added to the normal (right) grid\r
316 *\r
317 * If `both` is specified, then each copy of the plugin gains a property `lockingPartner` which references its sibling on the other side so that they\r
318 * can synchronize operations is necessary.\r
319 * \r
320 * {@link #features Features} may also be configured with `lockableScope` and may target the locked grid, the normal grid or both grids. Features\r
321 * also get a `lockingPartner` reference injected.\r
322 */\r
323 enableLocking: false,\r
324\r
325 /**\r
326 * @private\r
327 * Used to determine where to go down to find views\r
328 * this is here to support locking.\r
329 */\r
330 scrollerOwner: true,\r
331\r
332 /**\r
333 * @cfg {Boolean} [enableColumnMove=true]\r
334 * False to disable column dragging within this grid.\r
335 */\r
336 enableColumnMove: true,\r
337 \r
338 /**\r
339 * @cfg {Boolean} [sealedColumns=false]\r
340 * True to constrain column dragging so that a column cannot be dragged in or out of it's\r
341 * current group. Only relevant while {@link #enableColumnMove} is enabled.\r
342 */\r
343 sealedColumns: false,\r
344\r
345 /**\r
346 * @cfg {Boolean} [enableColumnResize=true]\r
347 * False to disable column resizing within this grid.\r
348 */\r
349 enableColumnResize: true,\r
350\r
351 /**\r
352 * @cfg {Boolean} [enableColumnHide=true]\r
353 * False to disable column hiding within this grid.\r
354 */\r
355\r
356 /**\r
357 * @cfg {Boolean} columnLines Adds column line styling\r
358 */\r
359\r
360 /**\r
361 * @cfg {Boolean} [rowLines=true] Adds row line styling\r
362 */\r
363 rowLines: true,\r
364\r
365 /**\r
366 * @cfg {Boolean} [disableSelection=false]\r
367 * True to disable selection model.\r
368 */\r
369\r
370 /**\r
371 * @cfg {String} emptyText Default text (HTML tags are accepted) to display in the \r
372 * Panel body when the Store is empty. When specified, and the Store is empty, the \r
373 * text will be rendered inside a DIV with the CSS class "x-grid-empty". The emptyText \r
374 * will not display until the first load of the associated store by default. If you \r
375 * want the text to be displayed prior to the first store load use the \r
376 * {@link Ext.view.Table#deferEmptyText deferEmptyText} config in the {@link #viewConfig} config.\r
377 */\r
378 \r
379 /**\r
380 * @cfg {Boolean} [allowDeselect=false]\r
381 * True to allow deselecting a record. This config is forwarded to {@link Ext.selection.Model#allowDeselect}.\r
382 */\r
383 \r
384 /**\r
385 * @cfg {Boolean} [bufferedRenderer=true]\r
386 * Buffered rendering is enabled by default.\r
387 * \r
388 * Configure as `false` to disable buffered rendering. See {@link Ext.grid.plugin.BufferedRenderer}.\r
389 *\r
390 * @since 5.0.0\r
391 */\r
392 bufferedRenderer: true,\r
393\r
394 /**\r
395 * @cfg stateEvents\r
396 * @inheritdoc Ext.state.Stateful#cfg-stateEvents\r
397 * @localdoc By default the following stateEvents are added:\r
398 * \r
399 * - {@link #event-resize} - _(added by Ext.Component)_\r
400 * - {@link #event-collapse} - _(added by Ext.panel.Panel)_\r
401 * - {@link #event-expand} - _(added by Ext.panel.Panel)_\r
402 * - {@link #event-columnresize}\r
403 * - {@link #event-columnmove}\r
404 * - {@link #event-columnhide}\r
405 * - {@link #event-columnshow}\r
406 * - {@link #event-sortchange}\r
407 * - {@link #event-filterchange}\r
408 * - {@link #event-groupchange}\r
409 */\r
410\r
411 /**\r
412 * @property {Boolean} optimizedColumnMove\r
413 * If you are writing a grid plugin or a {Ext.grid.feature.Feature Feature} which creates a column-based structure which\r
414 * needs a view refresh when columns are moved, then set this property in the grid.\r
415 *\r
416 * An example is the built in {@link Ext.grid.feature.AbstractSummary Summary} Feature. This creates summary rows, and the\r
417 * summary columns must be in the same order as the data columns. This plugin sets the `optimizedColumnMove` to `false.\r
418 */\r
419\r
420 /**\r
421 * @property {Ext.panel.Table} ownerGrid\r
422 * A reference to the top-level owning grid component.\r
423 * \r
424 * This is a reference to this GridPanel if this GridPanel is not part of a locked grid arrangement.\r
425 * @readonly\r
426 * @private\r
427 * @since 5.0.0\r
428 */\r
429 ownerGrid: null,\r
430\r
431 colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines',\r
432 rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines',\r
433 noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines',\r
434 hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden',\r
435 hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden',\r
436 resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker',\r
437 emptyCls: Ext.baseCSSPrefix + 'grid-empty',\r
438\r
439 // The TablePanel claims to be focusable, but it does not place a tabIndex\r
440 // on any of its elements.\r
441 // Its focus implementation delegates to its view. TableViews are focusable.\r
442 focusable: true,\r
443\r
444 /**\r
445 * @event viewready\r
446 * Fires when the grid view is available (use this for selecting a default row).\r
447 * @param {Ext.panel.Table} this\r
448 */\r
449\r
450 constructor: function (config) {\r
451 var me = this,\r
452 topGrid = config && config.ownerGrid,\r
453 store;\r
454\r
455 me.ownerGrid = topGrid || me;\r
456\r
457 /**\r
458 * @property {Array} actionables An array of objects which register themselves with a grid panel using\r
459 * {@link #registerActionable} which are consulted upon entry into actionable mode.\r
460 *\r
461 * These must implement the following methods:\r
462 *\r
463 * - activateCell Called when actionable mode is requested upon a cell. A {@link Ext.grid.CellContext CellContext}\r
464 * object is passed. If that cell is actionable by the terms of the callee, the callee should return `true` if it\r
465 * ascertains that the cell is actionable, and that it now contains focusable elements which may be tabbed to. \r
466 * - activateRow Called when the user enters actionable mode in a row. The row DOM is passed. Actionables\r
467 * should take any action they need to prime the row for cell activation which happens as users TAB from cell to cell.\r
468 * @readonly\r
469 */\r
470 me.actionables = topGrid ? topGrid.actionables : []; // One shared array when there's a lockable at the top\r
471\r
472 me.callParent([config]);\r
473\r
474 store = me.store;\r
475\r
476 // Any further changes become stateful.\r
477 store.trackStateChanges = true;\r
478\r
479 if (me.autoLoad) {\r
480 // Note: if there is a store bound by a VM, we (might) do the load in #setStore.\r
481 if (!store.isEmptyStore) {\r
482 store.load();\r
483 }\r
484 }\r
485 },\r
486\r
487 /**\r
488 * \r
489 * @param {Object} actionable An object which has an interest in the implementation of actionable mode in\r
490 * this grid.\r
491 *\r
492 * An actionable object may be a Plugin which upon activation injects tabbable elements or Components into\r
493 * a grid row.\r
494 */\r
495 registerActionable: function(actionable) {\r
496 // If a lockableScope: 'both' plugin/feature registers on each side, only include it in the actionables once.\r
497 Ext.Array.include(this.actionables, actionable);\r
498 },\r
499\r
500 initComponent: function() {\r
501 //<debug>\r
502 if (this.verticalScroller) {\r
503 Ext.raise("The verticalScroller config is not supported.");\r
504 }\r
505 if (!this.viewType) {\r
506 Ext.raise("You must specify a viewType config.");\r
507 }\r
508 if (this.headers) {\r
509 Ext.raise("The headers config is not supported. Please specify columns instead.");\r
510 }\r
511 //</debug>\r
512\r
513 var me = this,\r
514 headerCtCfg = me.columns || me.colModel || [],\r
515 store, view, i, len, bufferedRenderer, columns, viewScroller, headerCt;\r
516\r
517 // Look up the configured Store. If none configured, use the fieldless, empty Store\r
518 // defined in Ext.data.Store.\r
519 store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');\r
520 \r
521 me.enableLocking = me.enableLocking || me.hasLockedColumns(headerCtCfg);\r
522\r
523 // Construct the plugins now rather than in the constructor of AbstractComponent because the component may have a subclass\r
524 // that has overridden initComponent and defined plugins in it. For plugins like RowExpander that rely upon a grid feature,\r
525 // this is a problem because the view needs to know about all its features before it's constructed. Constructing the plugins\r
526 // now ensures that plugins defined in the instance config or in initComponent are all constructed before the view.\r
527 // See EXTJSIV-11927.\r
528 //\r
529 // Note that any components that do not inherit from this class will still have their plugins constructed in\r
530 // AbstractComponent:initComponent.\r
531 if (me.plugins) {\r
532 me.plugins = me.constructPlugins();\r
533 }\r
534\r
535 // Add the row/column line classes to the body element so that the settings are not inherited by docked grids (https://sencha.jira.com/browse/EXTJSIV-9263).\r
536 if (me.columnLines) {\r
537 me.addBodyCls(me.colLinesCls);\r
538 }\r
539\r
540 me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls);\r
541 me.addBodyCls(me.extraBodyCls);\r
542\r
543\r
544 // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a\r
545 // special view will be injected by the Ext.grid.locking.Lockable mixin, so no processing of .\r
546 if (me.enableLocking) {\r
547 me.self.mixin('lockable', Ext.grid.locking.Lockable);\r
548 me.injectLockable();\r
549 headerCt = me.headerCt;\r
550 }\r
551 // Not lockable - create the HeaderContainer\r
552 else {\r
553 // It's a fully instantiated HeaderContainer\r
554 if (headerCtCfg.isRootHeader) {\r
555 if (me.hideHeaders) {\r
556 headerCtCfg.setHeight(0);\r
557 // don't set the hidden property, we still need these to layout\r
558 headerCtCfg.hiddenHeaders = true;\r
559 } else {\r
560 // the header container is not user scrollable, but it has a scroller instance\r
561 // so that we can sync its scroll position with that of the grid view\r
562 headerCtCfg.setScrollable({\r
563 x: false,\r
564 y: false\r
565 });\r
566 }\r
567\r
568 me.headerCt = headerCt = headerCtCfg;\r
569\r
570 headerCt.grid = me;\r
571 headerCt.forceFit = !!me.forceFit;\r
572 headerCt.$initParent = me;\r
573\r
574 // If it's an instance then the column managers were already created and bound to the headerCt.\r
575 me.columnManager = headerCtCfg.columnManager;\r
576 me.visibleColumnManager = headerCtCfg.visibleColumnManager;\r
577 }\r
578 // It's an array of Column definitions, or a config object of a HeaderContainer\r
579 else {\r
580 if (Ext.isArray(headerCtCfg)) {\r
581 headerCtCfg = {\r
582 items: headerCtCfg\r
583 };\r
584 }\r
585 Ext.apply(headerCtCfg, {\r
586 grid: me,\r
587 $initParent: me,\r
588 forceFit: me.forceFit,\r
589 sortable: me.sortableColumns,\r
590 enableColumnMove: me.enableColumnMove,\r
591 enableColumnResize: me.enableColumnResize,\r
592 columnLines: me.columnLines,\r
593 sealed: me.sealedColumns,\r
594 // the header container is not user scrollable, but if it is visible,\r
595 // it has a scroller instance so that we can sync its scroll position with that of the grid view\r
596 scrollable: me.hideHeaders ? undefined : {\r
597 x: false,\r
598 y: false\r
599 }\r
600 });\r
601 if (me.hideHeaders) {\r
602 headerCtCfg.height = 0;\r
603 // don't set the hidden property, we still need these to layout\r
604 headerCtCfg.hiddenHeaders = true;\r
605 }\r
606\r
607 if (Ext.isDefined(me.enableColumnHide)) {\r
608 headerCtCfg.enableColumnHide = me.enableColumnHide;\r
609 }\r
610 me.headerCt = headerCt = new Ext.grid.header.Container(headerCtCfg);\r
611 }\r
612 }\r
613\r
614 // Maintain backward compatibiliy by providing the initial leaf column set as a property.\r
615 me.columns = columns = headerCt.getGridColumns();\r
616\r
617 me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me);\r
618\r
619 me.cls = (me.cls || '') + (' ' + me.extraBaseCls);\r
620\r
621 // autoScroll is not a valid configuration\r
622 delete me.autoScroll;\r
623\r
624 bufferedRenderer = me.plugins && Ext.Array.findBy(me.plugins, function(p) {\r
625 return p.isBufferedRenderer;\r
626 });\r
627\r
628 // If we find one in the plugins, just use that.\r
629 if (bufferedRenderer) {\r
630 me.bufferedRenderer = bufferedRenderer;\r
631 }\r
632\r
633 // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)\r
634 // then a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.\r
635 if (!me.hasView) {\r
636\r
637 // If the Store is paging blocks of the dataset in, then it can only be sorted remotely.\r
638 if (store.isBufferedStore && !store.getRemoteSort()) {\r
639 for (i = 0, len = columns.length; i < len; i++) {\r
640 columns[i].sortable = false;\r
641 }\r
642 }\r
643\r
644 if (me.hideHeaders) {\r
645 me.headerCt.addCls(me.hiddenHeaderCtCls);\r
646 me.addCls(me.hiddenHeaderCls);\r
647 }\r
648\r
649 me.relayHeaderCtEvents(headerCt);\r
650 me.features = me.features || [];\r
651 if (!Ext.isArray(me.features)) {\r
652 me.features = [me.features];\r
653 }\r
654 me.dockedItems = [].concat(me.dockedItems || []);\r
655 me.dockedItems.unshift(headerCt);\r
656 me.viewConfig = me.viewConfig || {};\r
657\r
658 // AbstractDataView will look up a Store configured as an object\r
659 // getView converts viewConfig into a View instance\r
660 view = me.getView();\r
661\r
662 me.items = [view];\r
663 me.hasView = true;\r
664\r
665 // Add a listener to synchronize the horizontal scroll position of the headers\r
666 // with the table view's element... Unless we are not showing headers!\r
667 if (!me.hideHeaders) {\r
668 // sync the horizontal scroll position of the headerCt as the view is scrolled.\r
669 viewScroller = view.getScrollable();\r
670 if (viewScroller) {\r
671 headerCt.getScrollable().addPartner(viewScroller, 'x');\r
672 }\r
673 }\r
674\r
675 // Attach this Panel to the Store\r
676 me.bindStore(store, true);\r
677\r
678 me.mon(view, {\r
679 viewready: me.onViewReady,\r
680 refresh: me.onRestoreHorzScroll,\r
681 scope: me\r
682 });\r
683 }\r
684\r
685 // Whatever kind of View we have, be it a TableView, or a LockingView, we are interested in the selection model\r
686 me.selModel = me.view.getSelectionModel();\r
687 if (me.selModel.isRowModel) {\r
688 me.selModel.on({\r
689 scope: me,\r
690 lastselectedchanged: me.updateBindSelection,\r
691 selectionchange: me.updateBindSelection\r
692 });\r
693 }\r
694\r
695 // Relay events from the View whether it be a LockingView, or a regular GridView\r
696 me.relayEvents(me.view, [\r
697 /**\r
698 * @event beforeitemmousedown\r
699 * @inheritdoc Ext.view.View#beforeitemmousedown\r
700 */\r
701 'beforeitemmousedown',\r
702 /**\r
703 * @event beforeitemmouseup\r
704 * @inheritdoc Ext.view.View#beforeitemmouseup\r
705 */\r
706 'beforeitemmouseup',\r
707 /**\r
708 * @event beforeitemmouseenter\r
709 * @inheritdoc Ext.view.View#beforeitemmouseenter\r
710 */\r
711 'beforeitemmouseenter',\r
712 /**\r
713 * @event beforeitemmouseleave\r
714 * @inheritdoc Ext.view.View#beforeitemmouseleave\r
715 */\r
716 'beforeitemmouseleave',\r
717 /**\r
718 * @event beforeitemclick\r
719 * @inheritdoc Ext.view.View#beforeitemclick\r
720 */\r
721 'beforeitemclick',\r
722 /**\r
723 * @event beforeitemdblclick\r
724 * @inheritdoc Ext.view.View#beforeitemdblclick\r
725 */\r
726 'beforeitemdblclick',\r
727 /**\r
728 * @event beforeitemcontextmenu\r
729 * @inheritdoc Ext.view.View#beforeitemcontextmenu\r
730 */\r
731 'beforeitemcontextmenu',\r
732 /**\r
733 * @event itemmousedown\r
734 * @inheritdoc Ext.view.View#itemmousedown\r
735 */\r
736 'itemmousedown',\r
737 /**\r
738 * @event itemmouseup\r
739 * @inheritdoc Ext.view.View#itemmouseup\r
740 */\r
741 'itemmouseup',\r
742 /**\r
743 * @event itemmouseenter\r
744 * @inheritdoc Ext.view.View#itemmouseenter\r
745 */\r
746 'itemmouseenter',\r
747 /**\r
748 * @event itemmouseleave\r
749 * @inheritdoc Ext.view.View#itemmouseleave\r
750 */\r
751 'itemmouseleave',\r
752 /**\r
753 * @event itemclick\r
754 * @inheritdoc Ext.view.View#itemclick\r
755 */\r
756 'itemclick',\r
757 /**\r
758 * @event itemdblclick\r
759 * @inheritdoc Ext.view.View#itemdblclick\r
760 */\r
761 'itemdblclick',\r
762 /**\r
763 * @event itemcontextmenu\r
764 * @inheritdoc Ext.view.View#itemcontextmenu\r
765 */\r
766 'itemcontextmenu',\r
767 /**\r
768 * @event beforecellclick\r
769 * @inheritdoc Ext.view.Table#beforecellclick\r
770 */\r
771 'beforecellclick',\r
772 /**\r
773 * @event cellclick\r
774 * @inheritdoc Ext.view.Table#cellclick\r
775 */\r
776 'cellclick',\r
777 /**\r
778 * @event beforecelldblclick\r
779 * @inheritdoc Ext.view.Table#beforecelldblclick\r
780 */\r
781 'beforecelldblclick',\r
782 /**\r
783 * @event celldblclick\r
784 * @inheritdoc Ext.view.Table#celldblclick\r
785 */\r
786 'celldblclick',\r
787 /**\r
788 * @event beforecellcontextmenu\r
789 * @inheritdoc Ext.view.Table#beforecellcontextmenu\r
790 */\r
791 'beforecellcontextmenu',\r
792 /**\r
793 * @event cellcontextmenu\r
794 * @inheritdoc Ext.view.Table#cellcontextmenu\r
795 */\r
796 'cellcontextmenu',\r
797 /**\r
798 * @event beforecellmousedown\r
799 * @inheritdoc Ext.view.Table#beforecellmousedown\r
800 */\r
801 'beforecellmousedown',\r
802 /**\r
803 * @event cellmousedown\r
804 * @inheritdoc Ext.view.Table#cellmousedown\r
805 */\r
806 'cellmousedown',\r
807 /**\r
808 * @event beforecellmouseup\r
809 * @inheritdoc Ext.view.Table#beforecellmouseup\r
810 */\r
811 'beforecellmouseup',\r
812 /**\r
813 * @event cellmouseup\r
814 * @inheritdoc Ext.view.Table#cellmouseup\r
815 */\r
816 'cellmouseup',\r
817 /**\r
818 * @event beforecellkeydown\r
819 * @inheritdoc Ext.view.Table#beforecellkeydown\r
820 */\r
821 'beforecellkeydown',\r
822 /**\r
823 * @event cellkeydown\r
824 * @inheritdoc Ext.view.Table#cellkeydown\r
825 */\r
826 'cellkeydown',\r
827 /**\r
828 * @event rowclick\r
829 * @inheritdoc Ext.view.Table#rowclick\r
830 */\r
831 'rowclick',\r
832 /**\r
833 * @event rowdblclick\r
834 * @inheritdoc Ext.view.Table#rowdblclick\r
835 */\r
836 'rowdblclick',\r
837 /**\r
838 * @event rowcontextmenu\r
839 * @inheritdoc Ext.view.Table#rowcontextmenu\r
840 */\r
841 'rowcontextmenu',\r
842 /**\r
843 * @event rowmousedown\r
844 * @inheritdoc Ext.view.Table#rowmousedown\r
845 */\r
846 'rowmousedown',\r
847 /**\r
848 * @event rowmouseup\r
849 * @inheritdoc Ext.view.Table#rowmouseup\r
850 */\r
851 'rowmouseup',\r
852 /**\r
853 * @event rowkeydown\r
854 * @inheritdoc Ext.view.Table#rowkeydown\r
855 */\r
856 'rowkeydown',\r
857 /**\r
858 * @event beforeitemkeydown\r
859 * @inheritdoc Ext.view.Table#beforeitemkeydown\r
860 */\r
861 'beforeitemkeydown',\r
862 /**\r
863 * @event itemkeydown\r
864 * @inheritdoc Ext.view.Table#itemkeydown\r
865 */\r
866 'itemkeydown',\r
867 /**\r
868 * @event beforeitemkeyup\r
869 * @inheritdoc Ext.view.Table#beforeitemkeyup\r
870 */\r
871 'beforeitemkeyup',\r
872 /**\r
873 * @event itemkeyup\r
874 * @inheritdoc Ext.view.Table#itemkeyup\r
875 */\r
876 'itemkeyup',\r
877 /**\r
878 * @event beforeitemkeypress\r
879 * @inheritdoc Ext.view.Table#beforeitemkeypress\r
880 */\r
881 'beforeitemkeypress',\r
882 /**\r
883 * @event itemkeypress\r
884 * @inheritdoc Ext.view.Table#itemkeypress\r
885 */\r
886 'itemkeypress',\r
887 /**\r
888 * @event beforecontainermousedown\r
889 * @inheritdoc Ext.view.View#beforecontainermousedown\r
890 */\r
891 'beforecontainermousedown',\r
892 /**\r
893 * @event beforecontainermouseup\r
894 * @inheritdoc Ext.view.View#beforecontainermouseup\r
895 */\r
896 'beforecontainermouseup',\r
897 /**\r
898 * @event beforecontainermouseover\r
899 * @inheritdoc Ext.view.View#beforecontainermouseover\r
900 */\r
901 'beforecontainermouseover',\r
902 /**\r
903 * @event beforecontainermouseout\r
904 * @inheritdoc Ext.view.View#beforecontainermouseout\r
905 */\r
906 'beforecontainermouseout',\r
907 /**\r
908 * @event beforecontainerclick\r
909 * @inheritdoc Ext.view.View#beforecontainerclick\r
910 */\r
911 'beforecontainerclick',\r
912 /**\r
913 * @event beforecontainerdblclick\r
914 * @inheritdoc Ext.view.View#beforecontainerdblclick\r
915 */\r
916 'beforecontainerdblclick',\r
917 /**\r
918 * @event beforecontainercontextmenu\r
919 * @inheritdoc Ext.view.View#beforecontainercontextmenu\r
920 */\r
921 'beforecontainercontextmenu',\r
922 /**\r
923 * @event beforecontainerkeydown\r
924 * @inheritdoc Ext.view.View#beforecontainerkeydown\r
925 */\r
926 'beforecontainerkeydown',\r
927 /**\r
928 * @event beforecontainerkeyup\r
929 * @inheritdoc Ext.view.View#beforecontainerkeyup\r
930 */\r
931 'beforecontainerkeyup',\r
932 /**\r
933 * @event beforecontainerkeypress\r
934 * @inheritdoc Ext.view.View#beforecontainerkeypress\r
935 */\r
936 'beforecontainerkeypress',\r
937 /**\r
938 * @event containermouseup\r
939 * @inheritdoc Ext.view.View#containermouseup\r
940 */\r
941 'containermouseup',\r
942 /**\r
943 * @event containermousedown\r
944 * @inheritdoc Ext.view.View#containermousedown\r
945 */\r
946 'containermousedown',\r
947 /**\r
948 * @event containermouseover\r
949 * @inheritdoc Ext.view.View#containermouseover\r
950 */\r
951 'containermouseover',\r
952 /**\r
953 * @event containermouseout\r
954 * @inheritdoc Ext.view.View#containermouseout\r
955 */\r
956 'containermouseout',\r
957 /**\r
958 * @event containerclick\r
959 * @inheritdoc Ext.view.View#containerclick\r
960 */\r
961 'containerclick',\r
962 /**\r
963 * @event containerdblclick\r
964 * @inheritdoc Ext.view.View#containerdblclick\r
965 */\r
966 'containerdblclick',\r
967 /**\r
968 * @event containercontextmenu\r
969 * @inheritdoc Ext.view.View#containercontextmenu\r
970 */\r
971 'containercontextmenu',\r
972 /**\r
973 * @event containerkeydown\r
974 * @inheritdoc Ext.view.View#containerkeydown\r
975 */\r
976 'containerkeydown',\r
977 /**\r
978 * @event containerkeyup\r
979 * @inheritdoc Ext.view.View#containerkeyup\r
980 */\r
981 'containerkeyup',\r
982 /**\r
983 * @event containerkeypress\r
984 * @inheritdoc Ext.view.View#containerkeypress\r
985 */\r
986 'containerkeypress',\r
987 /**\r
988 * @event selectionchange\r
989 * @inheritdoc Ext.selection.Model#selectionchange\r
990 */\r
991 'selectionchange',\r
992 /**\r
993 * @event beforeselect\r
994 * @inheritdoc Ext.selection.RowModel#beforeselect\r
995 */\r
996 'beforeselect',\r
997 /**\r
998 * @event select\r
999 * @inheritdoc Ext.selection.RowModel#select\r
1000 */\r
1001 'select',\r
1002 /**\r
1003 * @event beforedeselect\r
1004 * @inheritdoc Ext.selection.RowModel#beforedeselect\r
1005 */\r
1006 'beforedeselect',\r
1007 /**\r
1008 * @event deselect\r
1009 * @inheritdoc Ext.selection.RowModel#deselect\r
1010 */\r
1011 'deselect'\r
1012 ]);\r
1013\r
1014 me.callParent();\r
1015 if (me.enableLocking) {\r
1016 me.afterInjectLockable();\r
1017 } else {\r
1018 delete headerCt.$initParent;\r
1019 }\r
1020 me.addStateEvents(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange', 'filterchange', 'groupchange']);\r
1021 \r
1022 // rowBody feature events\r
1023 /**\r
1024 * @event beforerowbodymousedown\r
1025 * @preventable\r
1026 * @inheritdoc Ext.view.Table#event-beforerowbodymousedown\r
1027 */\r
1028\r
1029 /**\r
1030 * @event beforerowbodymouseup\r
1031 * @preventable\r
1032 * @inheritdoc Ext.view.Table#event-beforerowbodymouseup\r
1033 */\r
1034\r
1035 /**\r
1036 * @event beforerowbodyclick\r
1037 * @preventable\r
1038 * @inheritdoc Ext.view.Table#event-beforerowbodyclick\r
1039 */\r
1040\r
1041 /**\r
1042 * @event beforerowbodydblclick\r
1043 * @preventable\r
1044 * @inheritdoc Ext.view.Table#event-beforerowbodydblclick\r
1045 */\r
1046\r
1047 /**\r
1048 * @event beforerowbodycontextmenu\r
1049 * @preventable\r
1050 * @inheritdoc Ext.view.Table#event-beforerowbodycontextmenu\r
1051 */\r
1052 \r
1053 /**\r
1054 * @event beforerowbodylongpress\r
1055 * @preventable\r
1056 * @inheritdoc Ext.view.Table#event-beforerowbodylongpress\r
1057 */\r
1058\r
1059 /**\r
1060 * @event beforerowbodykeydown\r
1061 * @preventable\r
1062 * @inheritdoc Ext.view.Table#event-beforerowbodykeydown\r
1063 */\r
1064\r
1065 /**\r
1066 * @event beforerowbodykeyup\r
1067 * @preventable\r
1068 * @inheritdoc Ext.view.Table#event-beforerowbodykeyup\r
1069 */\r
1070\r
1071 /**\r
1072 * @event beforerowbodykeypress\r
1073 * @preventable\r
1074 * @inheritdoc Ext.view.Table#event-beforerowbodykeypress\r
1075 */\r
1076\r
1077 /**\r
1078 * @event rowbodymousedown\r
1079 * @inheritdoc Ext.view.Table#event-rowbodymousedown\r
1080 */\r
1081\r
1082 /**\r
1083 * @event rowbodymouseup\r
1084 * @inheritdoc Ext.view.Table#event-rowbodymouseup\r
1085 */\r
1086\r
1087 /**\r
1088 * @event rowbodyclick\r
1089 * @inheritdoc Ext.view.Table#event-rowbodyclick\r
1090 */\r
1091\r
1092 /**\r
1093 * @event rowbodydblclick\r
1094 * @inheritdoc Ext.view.Table#event-rowbodydblclick\r
1095 */\r
1096\r
1097 /**\r
1098 * @event rowbodycontextmenu\r
1099 * @inheritdoc Ext.view.Table#event-rowbodycontextmenu\r
1100 */\r
1101 \r
1102 /**\r
1103 * @event rowbodylongpress\r
1104 * @inheritdoc Ext.view.Table#event-rowbodylongpress\r
1105 */\r
1106\r
1107 /**\r
1108 * @event rowbodykeydown\r
1109 * @inheritdoc Ext.view.Table#event-rowbodykeydown\r
1110 */\r
1111\r
1112 /**\r
1113 * @event rowbodykeyup\r
1114 * @inheritdoc Ext.view.Table#event-rowbodykeyup\r
1115 */\r
1116\r
1117 /**\r
1118 * @event rowbodykeypress\r
1119 * @inheritdoc Ext.view.Table#event-rowbodykeypress\r
1120 */\r
1121 },\r
1122\r
1123 beforeRender: function() {\r
1124 var me = this,\r
1125 bufferedRenderer = me.bufferedRenderer,\r
1126 ariaAttr;\r
1127\r
1128 // If this is the topmost container of a lockable assembly, add the special class body\r
1129 if (me.lockable) {\r
1130 me.getProtoBody().addCls(me.lockingBodyCls);\r
1131 }\r
1132 \r
1133 // Don't create a buffered renderer for a locked grid.\r
1134 else {\r
1135 // If we're auto heighting, we can't buffered render, so don't create it\r
1136 if (bufferedRenderer && me.getSizeModel().height.auto) {\r
1137 //<debug>\r
1138 if (bufferedRenderer.isBufferedRenderer) {\r
1139 Ext.raise('Cannot use buffered rendering with auto height');\r
1140 }\r
1141 //</debug>\r
1142 me.bufferedRenderer = bufferedRenderer = false;\r
1143 }\r
1144 \r
1145 if (bufferedRenderer && !bufferedRenderer.isBufferedRenderer) {\r
1146 // Create a BufferedRenderer as a plugin if we have not already configured with one.\r
1147 bufferedRenderer = {\r
1148 xclass: 'Ext.grid.plugin.BufferedRenderer'\r
1149 };\r
1150 Ext.copy(bufferedRenderer, me, 'variableRowHeight,numFromEdge,trailingBufferZone,leadingBufferZone,scrollToLoadBuffer');\r
1151 me.bufferedRenderer = me.addPlugin(bufferedRenderer);\r
1152 }\r
1153 \r
1154 ariaAttr = me.ariaRenderAttributes || (me.ariaRenderAttributes = {});\r
1155 ariaAttr['aria-readonly'] = !me.isEditable;\r
1156 ariaAttr['aria-multiselectable'] = me.selModel.selectionMode !== 'SINGLE';\r
1157 }\r
1158 \r
1159 me.callParent(arguments);\r
1160 },\r
1161\r
1162 onRender: function() {\r
1163 var me = this,\r
1164 gridPanelBorderWidth,\r
1165 totalColumnWidth;\r
1166\r
1167 // If this is the locked side, include border width in calculated locked grid width.\r
1168 // TODO: Use shrinkWrapDock on the locked grid's headerCt when it works.\r
1169 if (me.isLocked && me.getSizeModel().width.shrinkWrap) {\r
1170 me.shrinkWrapColumns = true;\r
1171 totalColumnWidth = me.headerCt.getTableWidth();\r
1172 //<debug>\r
1173 if (isNaN(totalColumnWidth)) {\r
1174 Ext.raise("Locked columns in an unsized locked side do NOT support a flex width.");\r
1175 }\r
1176 //</debug>\r
1177 gridPanelBorderWidth = me.gridPanelBorderWidth || (me.gridPanelBorderWidth = me.el.getBorderWidth('lr'));\r
1178 me.width = totalColumnWidth + gridPanelBorderWidth;\r
1179 }\r
1180 me.callParent();\r
1181 },\r
1182\r
1183 /**\r
1184 * Gets the {@link Ext.grid.header.Container headercontainer} for this grid / tree.\r
1185 * @return {Ext.grid.header.Container} headercontainer\r
1186 *\r
1187 * **Note:** While a locked grid / tree will return an instance of\r
1188 * {@link Ext.grid.locking.HeaderContainer} you will code to the\r
1189 * {@link Ext.grid.header.Container} API.\r
1190 */\r
1191 getHeaderContainer: function () {\r
1192 return this.getView().getHeaderCt();\r
1193 },\r
1194\r
1195 /**\r
1196 * @inheritdoc Ext.grid.header.Container#getGridColumns\r
1197 */\r
1198 getColumns: function () {\r
1199 return this.getColumnManager().getColumns();\r
1200 },\r
1201\r
1202 /**\r
1203 * @inheritdoc Ext.grid.header.Container#getVisibleGridColumns\r
1204 */\r
1205 getVisibleColumns: function () {\r
1206 return this.getVisibleColumnManager().getColumns();\r
1207 },\r
1208\r
1209 focus: function() {\r
1210 // TablePanel is not focusable, but allow a call to delegate into the view\r
1211 this.getView().focus();\r
1212 },\r
1213 \r
1214 /**\r
1215 * Disables interaction with, and masks this grid's column headers.\r
1216 */\r
1217 disableColumnHeaders: function() {\r
1218 this.headerCt.disable();\r
1219 },\r
1220\r
1221 /**\r
1222 * Enables interaction with, and unmasks this grid's column headers after a call to {#disableColumnHeaders}.\r
1223 */\r
1224 enableColumnHeaders: function() {\r
1225 this.headerCt.enable();\r
1226 },\r
1227\r
1228 /**\r
1229 * @private\r
1230 * Determine if there are any columns with a locked configuration option.\r
1231 */\r
1232 hasLockedColumns: function(columns) {\r
1233 var i,\r
1234 len,\r
1235 column;\r
1236\r
1237 // Fully instantiated HeaderContainer\r
1238 if (columns.isRootHeader) {\r
1239 columns = columns.items.items;\r
1240 }\r
1241 // Config object with items\r
1242 else if (Ext.isObject(columns)) {\r
1243 columns = columns.items;\r
1244 }\r
1245 for (i = 0, len = columns.length; i < len; i++) {\r
1246 column = columns[i];\r
1247 if (!column.processed && column.locked) {\r
1248 return true;\r
1249 }\r
1250 }\r
1251 },\r
1252\r
1253 relayHeaderCtEvents: function (headerCt) {\r
1254 this.relayEvents(headerCt, [\r
1255 /**\r
1256 * @event columnresize\r
1257 * @inheritdoc Ext.grid.header.Container#columnresize\r
1258 */\r
1259 'columnresize',\r
1260 /**\r
1261 * @event columnmove\r
1262 * @inheritdoc Ext.grid.header.Container#columnmove\r
1263 */\r
1264 'columnmove',\r
1265 /**\r
1266 * @event columnhide\r
1267 * @inheritdoc Ext.grid.header.Container#columnhide\r
1268 */\r
1269 'columnhide',\r
1270 /**\r
1271 * @event columnshow\r
1272 * @inheritdoc Ext.grid.header.Container#columnshow\r
1273 */\r
1274 'columnshow',\r
1275 /**\r
1276 * @event columnschanged\r
1277 * @inheritdoc Ext.grid.header.Container#columnschanged\r
1278 */\r
1279 'columnschanged',\r
1280 /**\r
1281 * @event sortchange\r
1282 * @inheritdoc Ext.grid.header.Container#sortchange\r
1283 */\r
1284 'sortchange',\r
1285 /**\r
1286 * @event headerclick\r
1287 * @inheritdoc Ext.grid.header.Container#headerclick\r
1288 */\r
1289 'headerclick',\r
1290 /**\r
1291 * @event headercontextmenu\r
1292 * @inheritdoc Ext.grid.header.Container#headercontextmenu\r
1293 */\r
1294 'headercontextmenu',\r
1295 /**\r
1296 * @event headertriggerclick\r
1297 * @inheritdoc Ext.grid.header.Container#headertriggerclick\r
1298 */\r
1299 'headertriggerclick'\r
1300 ]);\r
1301 },\r
1302\r
1303 getState: function(){\r
1304 var me = this,\r
1305 state = me.callParent(),\r
1306 storeState = me.store.getState();\r
1307\r
1308 state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState());\r
1309\r
1310 if (storeState) {\r
1311 state.storeState = storeState;\r
1312 }\r
1313 return state;\r
1314 },\r
1315\r
1316 applyState: function (state) {\r
1317 var me = this,\r
1318 sorter = state.sort,\r
1319 storeState = state.storeState,\r
1320 store = me.store,\r
1321 columns = state.columns;\r
1322\r
1323 delete state.columns;\r
1324\r
1325 // Ensure superclass has applied *its* state.\r
1326 // Component saves dimensions (and anchor/flex) plus collapsed state.\r
1327 me.callParent(arguments);\r
1328\r
1329 if (columns) {\r
1330 // Column state restoration needs to examine store state\r
1331 me.headerCt.applyColumnsState(columns, storeState);\r
1332 }\r
1333\r
1334 // Old stored sort state. Deprecated and will die out.\r
1335 if (sorter) {\r
1336 if (store.getRemoteSort()) {\r
1337 // Pass false to prevent a sort from occurring.\r
1338 store.sort({\r
1339 property: sorter.property,\r
1340 direction: sorter.direction,\r
1341 root: sorter.root\r
1342 }, null, false);\r
1343 } else {\r
1344 store.sort(sorter.property, sorter.direction);\r
1345 }\r
1346 }\r
1347 // New storeState which encapsulates groupers, sorters and filters.\r
1348 else if (storeState) {\r
1349 store.applyState(storeState);\r
1350 }\r
1351 },\r
1352\r
1353 /**\r
1354 * Returns the store associated with this Panel.\r
1355 * @return {Ext.data.Store} The store\r
1356 */\r
1357 getStore: function(){\r
1358 return this.store;\r
1359 },\r
1360\r
1361 /**\r
1362 * Gets the view for this panel.\r
1363 * @return {Ext.view.Table}\r
1364 */\r
1365 getView: function() {\r
1366 var me = this,\r
1367 scroll, scrollable, viewConfig;\r
1368\r
1369 if (!me.view) {\r
1370 viewConfig = me.viewConfig;\r
1371 scroll = viewConfig.scroll || me.scroll;\r
1372 scrollable = me.scrollable;\r
1373\r
1374 if (scrollable == null && viewConfig.scrollable == null && scroll !== null) {\r
1375 // transform deprecated scroll config into scrollable config\r
1376 if (scroll === true || scroll === 'both') {\r
1377 scrollable = true;\r
1378 } else if (scroll === false || scroll === 'none') {\r
1379 scrollable = false;\r
1380 } else if (scroll === 'vertical') {\r
1381 scrollable = {\r
1382 x: false,\r
1383 y: true\r
1384 };\r
1385 } else if (scroll === 'horizontal') {\r
1386 scrollable = {\r
1387 x: true,\r
1388 y: false\r
1389 };\r
1390 }\r
1391 }\r
1392\r
1393 viewConfig = Ext.apply({\r
1394 // TableView injects the view reference into this grid so that we have a reference as early as possible\r
1395 // and Features need a reference to the grid.\r
1396 // For these reasons, we configure a reference to this grid into the View\r
1397 grid: me,\r
1398 ownerGrid: me.ownerGrid,\r
1399 deferInitialRefresh: me.deferRowRender,\r
1400 variableRowHeight: me.variableRowHeight,\r
1401 preserveScrollOnRefresh: true,\r
1402 trackOver: me.trackMouseOver !== false,\r
1403 throttledUpdate: me.throttledUpdate === true,\r
1404 xtype: me.viewType,\r
1405 store: me.store,\r
1406 headerCt: me.headerCt,\r
1407 columnLines: me.columnLines,\r
1408 rowLines: me.rowLines,\r
1409 navigationModel: 'grid',\r
1410 features: me.features,\r
1411 panel: me,\r
1412 emptyText: me.emptyText || ''\r
1413 }, me.viewConfig);\r
1414\r
1415 if (scrollable != null) {\r
1416 viewConfig.scrollable = scrollable;\r
1417 me.scrollable = null;\r
1418 }\r
1419\r
1420 Ext.create(viewConfig);\r
1421\r
1422 // Normalize the application of the markup wrapping the emptyText config.\r
1423 // `emptyText` can now be defined on the grid as well as on its viewConfig, and this led to the emptyText not\r
1424 // having the wrapping markup when it was defined in the viewConfig. It should be backwards compatible.\r
1425 // Note that in the unlikely event that emptyText is defined on both the grid config and the viewConfig that the viewConfig wins.\r
1426 if (me.view.emptyText) {\r
1427 me.view.emptyText = '<div class="' + me.emptyCls + '">' + me.view.emptyText + '</div>';\r
1428 }\r
1429\r
1430 // TableView's custom component layout, Ext.view.TableLayout requires a reference to the headerCt because it depends on the headerCt doing its work.\r
1431 me.view.getComponentLayout().headerCt = me.headerCt;\r
1432\r
1433 me.mon(me.view, {\r
1434 uievent: me.processEvent,\r
1435 scope: me\r
1436 });\r
1437 me.headerCt.view = me.view;\r
1438\r
1439 // Plugins and features may need to access the view as soon as it is created.\r
1440 if (me.hasListeners.viewcreated) {\r
1441 me.fireEvent('viewcreated', me, me.view);\r
1442 }\r
1443 }\r
1444 return me.view;\r
1445 },\r
1446\r
1447 getColumnManager: function() {\r
1448 return this.columnManager;\r
1449 },\r
1450\r
1451 getVisibleColumnManager: function() {\r
1452 return this.visibleColumnManager;\r
1453 },\r
1454\r
1455 getTopLevelColumnManager: function() {\r
1456 return this.ownerGrid.getColumnManager();\r
1457 },\r
1458\r
1459 getTopLevelVisibleColumnManager: function() {\r
1460 return this.ownerGrid.getVisibleColumnManager();\r
1461 },\r
1462\r
1463 /**\r
1464 * @private\r
1465 * autoScroll is never valid for all classes which extend TablePanel.\r
1466 */\r
1467 setAutoScroll: Ext.emptyFn,\r
1468\r
1469 applyScrollable: function(scrollable) {\r
1470 if (this.view) {\r
1471 this.view.setScrollable(scrollable);\r
1472 }\r
1473\r
1474 return scrollable;\r
1475 },\r
1476\r
1477 getScrollable: function() {\r
1478 return null;\r
1479 },\r
1480\r
1481 /**\r
1482 * @private\r
1483 * Processes UI events from the view. Propagates them to whatever internal Components need to process them.\r
1484 * @param {String} type Event type, eg 'click'\r
1485 * @param {Ext.view.Table} view TableView Component\r
1486 * @param {HTMLElement} cell Cell HTMLElement the event took place within\r
1487 * @param {Number} recordIndex Index of the associated Store Model (-1 if none)\r
1488 * @param {Number} cellIndex Cell index within the row\r
1489 * @param {Ext.event.Event} e Original event\r
1490 */\r
1491 processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {\r
1492 var header = e.position.column;\r
1493\r
1494 if (header) {\r
1495 return header.processEvent.apply(header, arguments);\r
1496 }\r
1497 },\r
1498\r
1499 /**\r
1500 * Scrolls the specified record into view.\r
1501 * @param {Number/String/Ext.data.Model} record The record, record id, or the zero-based position in the dataset to scroll to.\r
1502 * @param {Object} [options] An object containing options to modify the operation.\r
1503 * @param {Boolean} [options.animate] Pass `true` to animate the row into view.\r
1504 * @param {Boolean} [options.highlight] Pass `true` to highlight the row with a glow animation when it is in view.\r
1505 * @param {Boolean} [options.select] Pass as `true` to select the specified row.\r
1506 * @param {Boolean} [options.focus] Pass as `true` to focus the specified row.\r
1507 * @param {Function} [options.callback] A function to execute when the record is in view. This may be necessary if the\r
1508 * first parameter is a record index and the view is backed by a {@link Ext.data.BufferedStore buffered store}\r
1509 * which does not contain that record.\r
1510 * @param {Boolean} options.callback.success `true` if acquiring the record's view node was successful.\r
1511 * @param {Ext.data.Model} options.callback.record If successful, the target record.\r
1512 * @param {HTMLElement} options.callback.node If successful, the record's view node.\r
1513 * @param {Object} [options.scope] The scope (`this` reference) in which the callback function is executed.\r
1514 */\r
1515 ensureVisible: function(record, options) {\r
1516 this.doEnsureVisible(record, options);\r
1517 },\r
1518\r
1519 scrollByDeltaY: function(yDelta, animate) {\r
1520 this.getView().scrollBy(0, yDelta, animate);\r
1521 },\r
1522\r
1523 scrollByDeltaX: function(xDelta, animate) {\r
1524 this.getView().scrollBy(xDelta, 0, animate);\r
1525 },\r
1526\r
1527 afterCollapse: function() {\r
1528 this.saveScrollPos();\r
1529 this.callParent(arguments);\r
1530 },\r
1531\r
1532 afterExpand: function() {\r
1533 this.callParent(arguments);\r
1534 this.restoreScrollPos();\r
1535 },\r
1536\r
1537 saveScrollPos: Ext.emptyFn,\r
1538\r
1539 restoreScrollPos: Ext.emptyFn,\r
1540\r
1541 onHeaderResize: function() {\r
1542 var scroller = this.view.getScrollable(),\r
1543 size;\r
1544\r
1545 if (scroller && scroller.isTouchScroller) {\r
1546 size = scroller.getSize();\r
1547 if (size) {\r
1548 scroller.setSize({\r
1549 x: this.headerCt.getTableWidth(),\r
1550 y: size.y\r
1551 });\r
1552 }\r
1553 }\r
1554 },\r
1555\r
1556 // Update the view when a header moves\r
1557 onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) {\r
1558 var me = this;\r
1559\r
1560 // If there are Features or Plugins which create DOM which must match column order, they set the optimizedColumnMove flag to false.\r
1561 // In this case we must refresh the view on column move.\r
1562 if (me.optimizedColumnMove === false) {\r
1563 me.view.refreshView();\r
1564 }\r
1565\r
1566 // Simplest case for default DOM structure is just to swap the columns round in the view.\r
1567 else {\r
1568 me.view.moveColumn(fromIdx, toIdx, colsToMove);\r
1569 }\r
1570 me.delayScroll();\r
1571 },\r
1572\r
1573 // Section onHeaderHide is invoked after view.\r
1574 onHeaderHide: function(headerCt, header) {\r
1575 var view = this.view;\r
1576 // The headerCt may be hiding multiple children if a leaf level column\r
1577 // causes a parent (and possibly other parents) to be hidden. Only run the refresh\r
1578 // once we're done\r
1579 if (!headerCt.childHideCount && view.refreshCounter) {\r
1580 view.refreshView();\r
1581 }\r
1582 },\r
1583\r
1584 onHeaderShow: function(headerCt, header) {\r
1585 var view = this.view;\r
1586 if (view.refreshCounter) {\r
1587 view.refreshView();\r
1588 }\r
1589 },\r
1590\r
1591 // To be triggered on add/remove/move for a leaf header\r
1592 onHeadersChanged: function(headerCt, header) {\r
1593 var me = this;\r
1594 if (me.rendered && !me.reconfiguring) {\r
1595 me.view.refreshView();\r
1596 me.delayScroll();\r
1597 }\r
1598 },\r
1599\r
1600 delayScroll: function(){\r
1601 var target = this.view;\r
1602 if (target) {\r
1603 // Do not cause a layout by reading scrollX now.\r
1604 // It must be read from the target when the task finally executes.\r
1605 this.scrollTask.delay(10, null, null, [target]);\r
1606 }\r
1607 },\r
1608\r
1609 /**\r
1610 * @private\r
1611 * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready\r
1612 */\r
1613 onViewReady: function() {\r
1614 this.fireEvent('viewready', this); \r
1615 },\r
1616\r
1617 /**\r
1618 * @private\r
1619 * Tracks when things happen to the view and preserves the horizontal scroll position.\r
1620 */\r
1621 onRestoreHorzScroll: function() {\r
1622 var me = this,\r
1623 x = me.scrollXPos;\r
1624\r
1625 if (x) {\r
1626 // We need to restore the body scroll position here\r
1627 me.syncHorizontalScroll(me, true);\r
1628 }\r
1629 },\r
1630\r
1631 getScrollerOwner: function() {\r
1632 var rootCmp = this;\r
1633 if (!this.scrollerOwner) {\r
1634 rootCmp = this.up('[scrollerOwner]');\r
1635 }\r
1636 return rootCmp;\r
1637 },\r
1638\r
1639 /**\r
1640 * Gets left hand side marker for header resizing.\r
1641 * @private\r
1642 */\r
1643 getLhsMarker: function() {\r
1644 var me = this;\r
1645 return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, {\r
1646 role: 'presentation',\r
1647 cls: me.resizeMarkerCls\r
1648 }, true));\r
1649 },\r
1650\r
1651 /**\r
1652 * Gets right hand side marker for header resizing.\r
1653 * @private\r
1654 */\r
1655 getRhsMarker: function() {\r
1656 var me = this;\r
1657\r
1658 return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, {\r
1659 role: 'presentation',\r
1660 cls: me.resizeMarkerCls\r
1661 }, true));\r
1662 },\r
1663\r
1664 /**\r
1665 * Returns the grid's selection. See `{@link Ext.selection.Model#getSelection}`.\r
1666 * @inheritdoc Ext.selection.Model#getSelection\r
1667 */\r
1668 getSelection: function () {\r
1669 return this.getSelectionModel().getSelection();\r
1670 },\r
1671\r
1672 updateSelection: function(selection) {\r
1673 var me = this,\r
1674 sm;\r
1675\r
1676 if (!me.ignoreNextSelection) {\r
1677 me.ignoreNextSelection = true;\r
1678 sm = me.getSelectionModel();\r
1679 if (selection) {\r
1680 sm.select(selection);\r
1681 } else {\r
1682 sm.deselectAll();\r
1683 }\r
1684 me.ignoreNextSelection = false;\r
1685 }\r
1686 },\r
1687\r
1688 updateBindSelection: function(selModel, selection) {\r
1689 var me = this,\r
1690 selected = null;\r
1691\r
1692 if (!me.ignoreNextSelection) {\r
1693 me.ignoreNextSelection = true;\r
1694 if (selection.length) {\r
1695 selected = selModel.getLastSelected();\r
1696 me.hasHadSelection = true;\r
1697 }\r
1698 if (me.hasHadSelection) {\r
1699 me.setSelection(selected);\r
1700 }\r
1701 me.ignoreNextSelection = false;\r
1702 }\r
1703 },\r
1704\r
1705 updateHeaderBorders: function(headerBorders) {\r
1706 this[headerBorders ? 'removeCls' : 'addCls'](this.noHeaderBordersCls);\r
1707 },\r
1708\r
1709 getNavigationModel: function() {\r
1710 return this.getView().getNavigationModel();\r
1711 },\r
1712\r
1713 /**\r
1714 * Returns the selection model being used by this grid's {@link Ext.view.Table view}.\r
1715 * @return {Ext.selection.Model} The selection model being used by this grid's {@link Ext.view.Table view}.\r
1716 */\r
1717 getSelectionModel: function() {\r
1718 return this.getView().getSelectionModel();\r
1719 },\r
1720\r
1721 getScrollTarget: function(){\r
1722 var items = this.getScrollerOwner().query('tableview');\r
1723\r
1724 // Last view has the scroller\r
1725 return items[items.length - 1];\r
1726 },\r
1727\r
1728 syncHorizontalScroll: function(target, setBody) {\r
1729 var me = this,\r
1730 x = me.view.getScrollX(),\r
1731 scrollTarget;\r
1732\r
1733 setBody = setBody === true;\r
1734 // Only set the horizontal scroll if we've changed position,\r
1735 // so that we don't set this on vertical scrolls\r
1736 if (me.rendered && (setBody || x !== me.scrollXPos)) {\r
1737 // Only set the body position if we're reacting to a refresh, otherwise\r
1738 // we just need to set the header.\r
1739 if (setBody) { \r
1740 scrollTarget = me.getScrollTarget();\r
1741 scrollTarget.setScrollX(x);\r
1742 }\r
1743 me.headerCt.setScrollX(x);\r
1744 me.scrollXPos = x;\r
1745 }\r
1746 },\r
1747\r
1748 // template method meant to be overriden\r
1749 onStoreLoad: Ext.emptyFn,\r
1750\r
1751 getEditorParent: function() {\r
1752 return this.body;\r
1753 },\r
1754\r
1755 bindStore: function(store, initial) {\r
1756 var me = this,\r
1757 view = me.getView();\r
1758\r
1759 // Normally, this method will always be called with a valid store (because there is a symmetric\r
1760 // .unbindStore method), but there are cases where this method will be called and passed a null\r
1761 // value, i.e., a panel is used as a pickerfield. See EXTJS-13089.\r
1762 if (store) {\r
1763 // Bind to store immediately because subsequent processing looks for grid's store property\r
1764 me.store = store;\r
1765\r
1766 if (view.store !== store) {\r
1767 // If coming from a reconfigure, we need to set the actual store property on the view. Setting the\r
1768 // store will then also set the dataSource.\r
1769 //\r
1770 // Note that if it's a grid feature then this is sorted out in view.bindStore(), and it's own\r
1771 // implementation of .bindStore() will be called.\r
1772 view.bindStore(store, false);\r
1773 }\r
1774\r
1775 me.mon(store, {\r
1776 load: me.onStoreLoad,\r
1777 scope: me\r
1778 });\r
1779 me.storeRelayers = me.relayEvents(store, [\r
1780 /**\r
1781 * @event filterchange\r
1782 * @inheritdoc Ext.data.Store#filterchange\r
1783 */\r
1784 'filterchange',\r
1785 /**\r
1786 * @event groupchange\r
1787 * @inheritdoc Ext.data.Store#groupchange\r
1788 */\r
1789 'groupchange'\r
1790 ]);\r
1791 } else {\r
1792 me.unbindStore();\r
1793 }\r
1794 },\r
1795\r
1796 unbindStore: function() {\r
1797 var me = this,\r
1798 store = me.store,\r
1799 view;\r
1800\r
1801 if (store) {\r
1802 store.trackStateChanges = false;\r
1803\r
1804 me.store = null;\r
1805\r
1806 me.mun(store, {\r
1807 load: me.onStoreLoad,\r
1808 scope: me\r
1809 });\r
1810\r
1811 Ext.destroy(me.storeRelayers);\r
1812\r
1813 view = me.view;\r
1814 if (view.store) {\r
1815 view.bindStore(null);\r
1816 }\r
1817 }\r
1818 },\r
1819\r
1820 setColumns: function(columns) {\r
1821 // If being reconfigured from zero columns to zero columns, skip operation.\r
1822 // This can happen if columns are being set from a binding and the initial value\r
1823 // of the bound data in the ViewModel is []\r
1824 if (columns.length || this.getColumnManager().getColumns().length) {\r
1825 this.reconfigure(undefined, columns);\r
1826 }\r
1827 },\r
1828\r
1829 /**\r
1830 * A convenience method that fires {@link #reconfigure} with the store param. To set the store AND change columns,\r
1831 * use the {@link #reconfigure reconfigure method}.\r
1832 *\r
1833 * @param {Ext.data.Store} [store] The new store.\r
1834 */\r
1835 setStore: function (store) {\r
1836 this.reconfigure(store);\r
1837\r
1838 if (this.autoLoad && !store.isEmptyStore && !(store.loading || store.isLoaded())) {\r
1839 store.load();\r
1840 }\r
1841 },\r
1842\r
1843 /**\r
1844 * Reconfigures the grid or tree with a new store and/or columns. Stores and columns \r
1845 * may also be passed as params.\r
1846 *\r
1847 * grid.reconfigure(store, columns);\r
1848 *\r
1849 * Additionally, you can pass just a store or columns.\r
1850 *\r
1851 * tree.reconfigure(store);\r
1852 * // or\r
1853 * grid.reconfigure(columns);\r
1854 * // or\r
1855 * tree.reconfigure(null, columns);\r
1856 *\r
1857 * If you're using locked columns, the {@link #enableLocking} config should be set \r
1858 * to `true` before the reconfigure method is executed.\r
1859 *\r
1860 * @param {Ext.data.Store/Object} [store] The new store instance or store config. You can \r
1861 * pass `null` if no new store.\r
1862 * @param {Object[]} [columns] An array of column configs\r
1863 */\r
1864 reconfigure: function(store, columns) {\r
1865 var me = this,\r
1866 oldStore = me.store,\r
1867 headerCt = me.headerCt,\r
1868 lockable = me.lockable,\r
1869 oldColumns = headerCt ? headerCt.items.getRange() : me.columns,\r
1870 view = me.getView(),\r
1871 block, refreshCounter;\r
1872\r
1873 // Allow optional store argument to be fully omitted, and the columns argument to be solo\r
1874 if (arguments.length === 1 && Ext.isArray(store)) {\r
1875 columns = store;\r
1876 store = null;\r
1877 }\r
1878\r
1879 // Make copy in case the beforereconfigure listener mutates it.\r
1880 if (columns) {\r
1881 columns = Ext.Array.slice(columns);\r
1882 }\r
1883\r
1884 me.reconfiguring = true;\r
1885 if (store) {\r
1886 store = Ext.StoreManager.lookup(store);\r
1887 }\r
1888 me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns);\r
1889\r
1890 Ext.suspendLayouts();\r
1891\r
1892 if (lockable) {\r
1893 me.reconfigureLockable(store, columns);\r
1894 } else {\r
1895 // Prevent the view from refreshing until we have resumed layouts and any columns are rendered\r
1896 block = view.blockRefresh;\r
1897 view.blockRefresh = true;\r
1898\r
1899 // The following test compares the result of an assignment of the store var with the oldStore var.\r
1900 // This saves a large amount of code.\r
1901 //\r
1902 // Note that we need to process the store first in case one or more passed columns (if there are any)\r
1903 // have active gridfilters with values which would filter the currently-bound store.\r
1904 if (store && store !== oldStore) {\r
1905 me.unbindStore();\r
1906 me.bindStore(store);\r
1907 }\r
1908\r
1909 if (columns) {\r
1910 // new columns, delete scroll pos\r
1911 delete me.scrollXPos;\r
1912 headerCt.removeAll();\r
1913 headerCt.add(columns);\r
1914 }\r
1915\r
1916 view.blockRefresh = block;\r
1917 refreshCounter = view.refreshCounter;\r
1918 }\r
1919\r
1920 Ext.resumeLayouts(true);\r
1921 if (lockable) {\r
1922 me.afterReconfigureLockable();\r
1923 } else if (view.refreshCounter === refreshCounter) {\r
1924 // If the layout resumption didn't trigger the view to refresh, do it here\r
1925 view.refreshView();\r
1926 }\r
1927\r
1928 me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns);\r
1929 delete me.reconfiguring;\r
1930 },\r
1931\r
1932 beforeDestroy: function(){\r
1933 var me = this,\r
1934 task = me.scrollTask;\r
1935 \r
1936 if (task) {\r
1937 task.cancel();\r
1938 me.scrollTask = null;\r
1939 }\r
1940 Ext.destroy(me.focusEnterLeaveListeners);\r
1941 me.callParent();\r
1942 },\r
1943\r
1944 onDestroy: function(){\r
1945 var me = this;\r
1946 if (me.lockable) {\r
1947 me.destroyLockable();\r
1948 }\r
1949 me.unbindStore();\r
1950 me.callParent();\r
1951 me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null;\r
1952 },\r
1953 \r
1954 destroy: function() {\r
1955 // Clear out references here because other things (plugins/features) may need to know about them during destruction\r
1956 var me = this;\r
1957 me.callParent();\r
1958 if (me.destroyed) {\r
1959 me.view = me.selModel = me.headerCt = null;\r
1960 }\r
1961 },\r
1962\r
1963 privates: {\r
1964 // The focusable flag is set, but there is no focusable element.\r
1965 // Focus is delegated to the view by the focus implementation.\r
1966 initFocusableElement: function() {},\r
1967\r
1968 doEnsureVisible: function(record, options) {\r
1969 // Handle the case where this is a lockable assembly\r
1970 if (this.lockable) {\r
1971 return this.ensureLockedVisible(record, options);\r
1972 }\r
1973\r
1974 // Allow them to pass the record id.\r
1975 if (typeof record !== 'number' && !record.isEntity) {\r
1976 record = this.store.getById(record);\r
1977 }\r
1978 var me = this,\r
1979 view = me.getView(),\r
1980 domNode = view.getNode(record),\r
1981 callback, scope, animate,\r
1982 highlight, select, doFocus, scrollable, column, cell;\r
1983\r
1984 if (options) {\r
1985 callback = options.callback;\r
1986 scope = options.scope;\r
1987 animate = options.animate;\r
1988 highlight = options.highlight;\r
1989 select = options.select;\r
1990 doFocus = options.focus;\r
1991 column = options.column;\r
1992 }\r
1993\r
1994 // Always supercede any prior deferred request\r
1995 if (me.deferredEnsureVisible) {\r
1996 me.deferredEnsureVisible.destroy();\r
1997 }\r
1998\r
1999 // We have not yet run the layout.\r
2000 // Add this to the end of the first sizing process.\r
2001 // By using the resize event, we will come in AFTER any Component's onResize and onBoxReady handling.\r
2002 if (!view.componentLayoutCounter) {\r
2003 me.deferredEnsureVisible = view.on({\r
2004 resize: me.doEnsureVisible,\r
2005 args: Ext.Array.slice(arguments),\r
2006 scope: me,\r
2007 single: true,\r
2008 destroyable: true\r
2009 });\r
2010 return;\r
2011 }\r
2012\r
2013 if (typeof column === 'number') {\r
2014 column = me.ownerGrid.getVisibleColumnManager().getColumns()[column];\r
2015 }\r
2016 \r
2017 // We found the DOM node associated with the record\r
2018 if (domNode) {\r
2019 scrollable = view.getScrollable();\r
2020 if (column) {\r
2021 cell = Ext.fly(domNode).selectNode(column.getCellSelector());\r
2022 }\r
2023 if (scrollable) {\r
2024 scrollable.scrollIntoView(cell || domNode, !!column, animate, highlight);\r
2025 }\r
2026 if (!record.isEntity) {\r
2027 record = view.getRecord(domNode);\r
2028 }\r
2029 if (select) {\r
2030 view.getSelectionModel().select(record);\r
2031 }\r
2032 if (doFocus) {\r
2033 view.getNavigationModel().setPosition(record, 0);\r
2034 }\r
2035 Ext.callback(callback, scope || me, [true, record, domNode]);\r
2036 }\r
2037 // If we didn't find it, it's probably because of buffered rendering\r
2038 else if (view.bufferedRenderer) {\r
2039 view.bufferedRenderer.scrollTo(record, {\r
2040 animate: animate,\r
2041 highlight: highlight,\r
2042 select: select,\r
2043 focus: doFocus,\r
2044 column: column,\r
2045 callback: function(recordIdx, record, domNode) {\r
2046 Ext.callback(callback, scope || me, [true, record, domNode]);\r
2047 }\r
2048 });\r
2049 } else {\r
2050 Ext.callback(callback, scope || me, [false, null]);\r
2051 }\r
2052 },\r
2053 \r
2054 getFocusEl: function() {\r
2055 return this.getView().getFocusEl();\r
2056 },\r
2057\r
2058 /**\r
2059 * Toggles ARIA actionable mode on/off\r
2060 * @param {Boolean} enabled\r
2061 * @return {Boolean} `true` if actionable mode was entered\r
2062 * @private\r
2063 */\r
2064 setActionableMode: function(enabled, position) {\r
2065 // Always set the topmost grid in a lockable assembly\r
2066 var me = this.ownerGrid;\r
2067\r
2068 // Can be called to exit actionable mode upon a focusLeave caused by destruction\r
2069 if (!me.destroying && me.view.setActionableMode(enabled, position) !== false) {\r
2070 me.fireEvent('actionablemodechange', enabled);\r
2071 me[enabled ? 'addCls' : 'removeCls'](me.actionableModeCls);\r
2072 return true;\r
2073 }\r
2074 }\r
2075 }\r
2076});\r