Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / AbstractView.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-view-AbstractView'>/**
19 </span> * @class Ext.view.AbstractView
20  * @extends Ext.Component
21  * This is an abstract superclass and should not be used directly. Please see {@link Ext.view.View}.
22  * @private
23  */
24 Ext.define('Ext.view.AbstractView', {
25     extend: 'Ext.Component',
26     alternateClassName: 'Ext.view.AbstractView',
27     requires: [
28         'Ext.LoadMask',
29         'Ext.data.StoreManager',
30         'Ext.CompositeElementLite',
31         'Ext.DomQuery',
32         'Ext.selection.DataViewModel'
33     ],
34
35     inheritableStatics: {
36         getRecord: function(node) {
37             return this.getBoundView(node).getRecord(node);
38         },
39
40         getBoundView: function(node) {
41             return Ext.getCmp(node.boundView);
42         }
43     },
44
45 <span id='Ext-view-AbstractView-cfg-tpl'>    /**
46 </span>     * @cfg {String/String[]/Ext.XTemplate} tpl (required)
47      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
48      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
49      */
50 <span id='Ext-view-AbstractView-cfg-store'>    /**
51 </span>     * @cfg {Ext.data.Store} store (required)
52      * The {@link Ext.data.Store} to bind this DataView to.
53      */
54
55 <span id='Ext-view-AbstractView-cfg-deferInitialRefresh'>    /**
56 </span>     * @cfg {Boolean} deferInitialRefresh
57      * &lt;p&gt;Defaults to &lt;code&gt;true&lt;/code&gt; to defer the initial refresh of the view.&lt;/p&gt;
58      * &lt;p&gt;This allows the View to execute its render and initial layout more quickly because the process will not be encumbered
59      * by the expensive update of the view structure.&lt;/p&gt;
60      * &lt;p&gt;&lt;b&gt;Important: &lt;/b&gt;Be aware that this will mean that the View's item elements will not be available immediately upon render, so
61      * &lt;i&gt;selection&lt;/i&gt; may not take place at render time. To access a View's item elements as soon as possible, use the {@link #viewready} event.
62      * Or set &lt;code&gt;deferInitialrefresh&lt;/code&gt; to false, but this will be at the cost of slower rendering.&lt;/p&gt;
63      */
64     deferInitialRefresh: true,
65
66 <span id='Ext-view-AbstractView-cfg-itemSelector'>    /**
67 </span>     * @cfg {String} itemSelector (required)
68      * &lt;b&gt;This is a required setting&lt;/b&gt;. A simple CSS selector (e.g. &lt;tt&gt;div.some-class&lt;/tt&gt; or
69      * &lt;tt&gt;span:first-child&lt;/tt&gt;) that will be used to determine what nodes this DataView will be
70      * working with. The itemSelector is used to map DOM nodes to records. As such, there should
71      * only be one root level element that matches the selector for each record.
72      */
73
74 <span id='Ext-view-AbstractView-cfg-itemCls'>    /**
75 </span>     * @cfg {String} itemCls
76      * Specifies the class to be assigned to each element in the view when used in conjunction with the
77      * {@link #itemTpl} configuration.
78      */
79     itemCls: Ext.baseCSSPrefix + 'dataview-item',
80
81 <span id='Ext-view-AbstractView-cfg-itemTpl'>    /**
82 </span>     * @cfg {String/String[]/Ext.XTemplate} itemTpl
83      * The inner portion of the item template to be rendered. Follows an XTemplate
84      * structure and will be placed inside of a tpl.
85      */
86
87 <span id='Ext-view-AbstractView-cfg-overItemCls'>    /**
88 </span>     * @cfg {String} overItemCls
89      * A CSS class to apply to each item in the view on mouseover.
90      * Ensure {@link #trackOver} is set to `true` to make use of this.
91      */
92
93 <span id='Ext-view-AbstractView-cfg-loadingText'>    /**
94 </span>     * @cfg {String} loadingText
95      * A string to display during data load operations.  If specified, this text will be
96      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
97      * contents will continue to display normally until the new data is loaded and the contents are replaced.
98      */
99     loadingText: 'Loading...',
100
101 <span id='Ext-view-AbstractView-cfg-loadMask'>    /**
102 </span>     * @cfg {Boolean/Object} loadMask
103      * False to disable a load mask from displaying will the view is loading. This can also be a
104      * {@link Ext.LoadMask} configuration object.
105      */
106     loadMask: true,
107
108 <span id='Ext-view-AbstractView-cfg-loadingCls'>    /**
109 </span>     * @cfg {String} loadingCls
110      * The CSS class to apply to the loading message element. Defaults to Ext.LoadMask.prototype.msgCls &quot;x-mask-loading&quot;.
111      */
112
113 <span id='Ext-view-AbstractView-cfg-loadingUseMsg'>    /**
114 </span>     * @cfg {Boolean} loadingUseMsg
115      * Whether or not to use the loading message.
116      * @private
117      */
118     loadingUseMsg: true,
119
120
121 <span id='Ext-view-AbstractView-cfg-loadingHeight'>    /**
122 </span>     * @cfg {Number} loadingHeight
123      * If specified, gives an explicit height for the data view when it is showing the {@link #loadingText},
124      * if that is specified. This is useful to prevent the view's height from collapsing to zero when the
125      * loading mask is applied and there are no other contents in the data view.
126      */
127
128 <span id='Ext-view-AbstractView-cfg-selectedItemCls'>    /**
129 </span>     * @cfg {String} [selectedItemCls='x-view-selected']
130      * A CSS class to apply to each selected item in the view.
131      */
132     selectedItemCls: Ext.baseCSSPrefix + 'item-selected',
133
134 <span id='Ext-view-AbstractView-cfg-emptyText'>    /**
135 </span>     * @cfg {String} emptyText
136      * The text to display in the view when there is no data to display.
137      * Note that when using local data the emptyText will not be displayed unless you set
138      * the {@link #deferEmptyText} option to false.
139      */
140     emptyText: &quot;&quot;,
141
142 <span id='Ext-view-AbstractView-cfg-deferEmptyText'>    /**
143 </span>     * @cfg {Boolean} deferEmptyText
144      * True to defer emptyText being applied until the store's first load.
145      */
146     deferEmptyText: true,
147
148 <span id='Ext-view-AbstractView-cfg-trackOver'>    /**
149 </span>     * @cfg {Boolean} trackOver
150      * True to enable mouseenter and mouseleave events
151      */
152     trackOver: false,
153
154 <span id='Ext-view-AbstractView-cfg-blockRefresh'>    /**
155 </span>     * @cfg {Boolean} blockRefresh
156      * Set this to true to ignore datachanged events on the bound store. This is useful if
157      * you wish to provide custom transition animations via a plugin
158      */
159     blockRefresh: false,
160
161 <span id='Ext-view-AbstractView-cfg-disableSelection'>    /**
162 </span>     * @cfg {Boolean} disableSelection
163      * True to disable selection within the DataView. This configuration will lock the selection model
164      * that the DataView uses.
165      */
166
167
168     //private
169     last: false,
170
171     triggerEvent: 'itemclick',
172     triggerCtEvent: 'containerclick',
173
174     addCmpEvents: function() {
175
176     },
177
178     // private
179     initComponent : function(){
180         var me = this,
181             isDef = Ext.isDefined,
182             itemTpl = me.itemTpl,
183             memberFn = {};
184
185         if (itemTpl) {
186             if (Ext.isArray(itemTpl)) {
187                 // string array
188                 itemTpl = itemTpl.join('');
189             } else if (Ext.isObject(itemTpl)) {
190                 // tpl instance
191                 memberFn = Ext.apply(memberFn, itemTpl.initialConfig);
192                 itemTpl = itemTpl.html;
193             }
194
195             if (!me.itemSelector) {
196                 me.itemSelector = '.' + me.itemCls;
197             }
198
199             itemTpl = Ext.String.format('&lt;tpl for=&quot;.&quot;&gt;&lt;div class=&quot;{0}&quot;&gt;{1}&lt;/div&gt;&lt;/tpl&gt;', me.itemCls, itemTpl);
200             me.tpl = Ext.create('Ext.XTemplate', itemTpl, memberFn);
201         }
202
203         //&lt;debug&gt;
204         if (!isDef(me.tpl) || !isDef(me.itemSelector)) {
205             Ext.Error.raise({
206                 sourceClass: 'Ext.view.View',
207                 tpl: me.tpl,
208                 itemSelector: me.itemSelector,
209                 msg: &quot;DataView requires both tpl and itemSelector configurations to be defined.&quot;
210             });
211         }
212         //&lt;/debug&gt;
213
214         me.callParent();
215         if(Ext.isString(me.tpl) || Ext.isArray(me.tpl)){
216             me.tpl = Ext.create('Ext.XTemplate', me.tpl);
217         }
218
219         //&lt;debug&gt;
220         // backwards compat alias for overClass/selectedClass
221         // TODO: Consider support for overCls generation Ext.Component config
222         if (isDef(me.overCls) || isDef(me.overClass)) {
223             if (Ext.isDefined(Ext.global.console)) {
224                 Ext.global.console.warn('Ext.view.View: Using the deprecated overCls or overClass configuration. Use overItemCls instead.');
225             }
226             me.overItemCls = me.overCls || me.overClass;
227             delete me.overCls;
228             delete me.overClass;
229         }
230
231         if (me.overItemCls) {
232             me.trackOver = true;
233         }
234
235         if (isDef(me.selectedCls) || isDef(me.selectedClass)) {
236             if (Ext.isDefined(Ext.global.console)) {
237                 Ext.global.console.warn('Ext.view.View: Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls instead.');
238             }
239             me.selectedItemCls = me.selectedCls || me.selectedClass;
240             delete me.selectedCls;
241             delete me.selectedClass;
242         }
243         //&lt;/debug&gt;
244
245         me.addEvents(
246 <span id='Ext-view-AbstractView-event-beforerefresh'>            /**
247 </span>             * @event beforerefresh
248              * Fires before the view is refreshed
249              * @param {Ext.view.View} this The DataView object
250              */
251             'beforerefresh',
252 <span id='Ext-view-AbstractView-event-refresh'>            /**
253 </span>             * @event refresh
254              * Fires when the view is refreshed
255              * @param {Ext.view.View} this The DataView object
256              */
257             'refresh',
258 <span id='Ext-view-AbstractView-event-viewready'>            /**
259 </span>             * @event viewready
260              * Fires when the View's item elements representing Store items has been rendered. If the {@link #deferInitialRefresh} flag
261              * was set (and it is &lt;code&gt;true&lt;/code&gt; by default), this will be &lt;b&gt;after&lt;/b&gt; initial render, and no items will be available
262              * for selection until this event fires.
263              * @param {Ext.view.View} this
264              */
265             'viewready',
266 <span id='Ext-view-AbstractView-event-itemupdate'>            /**
267 </span>             * @event itemupdate
268              * Fires when the node associated with an individual record is updated
269              * @param {Ext.data.Model} record The model instance
270              * @param {Number} index The index of the record/node
271              * @param {HTMLElement} node The node that has just been updated
272              */
273             'itemupdate',
274 <span id='Ext-view-AbstractView-event-itemadd'>            /**
275 </span>             * @event itemadd
276              * Fires when the nodes associated with an recordset have been added to the underlying store
277              * @param {Ext.data.Model[]} records The model instance
278              * @param {Number} index The index at which the set of record/nodes starts
279              * @param {HTMLElement[]} node The node that has just been updated
280              */
281             'itemadd',
282 <span id='Ext-view-AbstractView-event-itemremove'>            /**
283 </span>             * @event itemremove
284              * Fires when the node associated with an individual record is removed
285              * @param {Ext.data.Model} record The model instance
286              * @param {Number} index The index of the record/node
287              */
288             'itemremove'
289         );
290
291         me.addCmpEvents();
292
293         // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.
294         me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
295         me.all = new Ext.CompositeElementLite();
296     },
297
298     onRender: function() {
299         var me = this,
300             mask = me.loadMask,
301             cfg = {
302                 msg: me.loadingText,
303                 msgCls: me.loadingCls,
304                 useMsg: me.loadingUseMsg
305             };
306
307         me.callParent(arguments);
308
309         if (mask) {
310             // either a config object
311             if (Ext.isObject(mask)) {
312                 cfg = Ext.apply(cfg, mask);
313             }
314             // Attach the LoadMask to a *Component* so that it can be sensitive to resizing during long loads.
315             // If this DataView is floating, then mask this DataView.
316             // Otherwise, mask its owning Container (or this, if there *is* no owning Container).
317             // LoadMask captures the element upon render.
318             me.loadMask = Ext.create('Ext.LoadMask', me, cfg);
319             me.loadMask.on({
320                 scope: me,
321                 beforeshow: me.onMaskBeforeShow,
322                 hide: me.onMaskHide
323             });
324         }
325     },
326
327     onMaskBeforeShow: function(){
328         var loadingHeight = this.loadingHeight;
329         
330         this.getSelectionModel().deselectAll();
331         if (loadingHeight) {
332             this.setCalculatedSize(undefined, loadingHeight);
333         }
334     },
335
336     onMaskHide: function(){
337         var me = this;
338         
339         if (!me.destroying &amp;&amp; me.loadingHeight) {
340             me.setHeight(me.height);
341         }
342     },
343
344     afterRender: function() {
345         this.callParent(arguments);
346
347         // Init the SelectionModel after any on('render') listeners have been added.
348         // Drag plugins create a DragDrop instance in a render listener, and that needs
349         // to see an itemmousedown event first.
350         this.getSelectionModel().bindComponent(this);
351     },
352
353 <span id='Ext-view-AbstractView-method-getSelectionModel'>    /**
354 </span>     * Gets the selection model for this view.
355      * @return {Ext.selection.Model} The selection model
356      */
357     getSelectionModel: function(){
358         var me = this,
359             mode = 'SINGLE';
360
361         if (!me.selModel) {
362             me.selModel = {};
363         }
364
365         if (me.simpleSelect) {
366             mode = 'SIMPLE';
367         } else if (me.multiSelect) {
368             mode = 'MULTI';
369         }
370
371         Ext.applyIf(me.selModel, {
372             allowDeselect: me.allowDeselect,
373             mode: mode
374         });
375
376         if (!me.selModel.events) {
377             me.selModel = Ext.create('Ext.selection.DataViewModel', me.selModel);
378         }
379
380         if (!me.selModel.hasRelaySetup) {
381             me.relayEvents(me.selModel, [
382                 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
383             ]);
384             me.selModel.hasRelaySetup = true;
385         }
386
387         // lock the selection model if user
388         // has disabled selection
389         if (me.disableSelection) {
390             me.selModel.locked = true;
391         }
392
393         return me.selModel;
394     },
395
396 <span id='Ext-view-AbstractView-method-refresh'>    /**
397 </span>     * Refreshes the view by reloading the data from the store and re-rendering the template.
398      */
399     refresh: function() {
400         var me = this,
401             el,
402             records;
403
404         if (!me.rendered || me.isDestroyed) {
405             return;
406         }
407
408         me.fireEvent('beforerefresh', me);
409         el = me.getTargetEl();
410         records = me.store.getRange();
411
412         el.update('');
413         if (records.length &lt; 1) {
414             if (!me.deferEmptyText || me.hasSkippedEmptyText) {
415                 el.update(me.emptyText);
416             }
417             me.all.clear();
418         } else {
419             me.tpl.overwrite(el, me.collectData(records, 0));
420             me.all.fill(Ext.query(me.getItemSelector(), el.dom));
421             me.updateIndexes(0);
422         }
423
424         me.selModel.refresh();
425         me.hasSkippedEmptyText = true;
426         me.fireEvent('refresh', me);
427
428         // Upon first refresh, fire the viewready event.
429         // Reconfiguring the grid &quot;renews&quot; this event.
430         if (!me.viewReady) {
431             // Fire an event when deferred content becomes available.
432             // This supports grid Panel's deferRowRender capability
433             me.viewReady = true;
434             me.fireEvent('viewready', me);
435         }
436     },
437
438 <span id='Ext-view-AbstractView-method-prepareData'>    /**
439 </span>     * Function which can be overridden to provide custom formatting for each Record that is used by this
440      * DataView's {@link #tpl template} to render each node.
441      * @param {Object/Object[]} data The raw data object that was used to create the Record.
442      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
443      * @param {Ext.data.Model} record The Record being prepared for rendering.
444      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
445      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
446      */
447     prepareData: function(data, index, record) {
448         if (record) {
449             Ext.apply(data, record.getAssociatedData());
450         }
451         return data;
452     },
453
454 <span id='Ext-view-AbstractView-method-collectData'>    /**
455 </span>     * &lt;p&gt;Function which can be overridden which returns the data object passed to this
456      * DataView's {@link #tpl template} to render the whole DataView.&lt;/p&gt;
457      * &lt;p&gt;This is usually an Array of data objects, each element of which is processed by an
458      * {@link Ext.XTemplate XTemplate} which uses &lt;tt&gt;'&amp;lt;tpl for=&quot;.&quot;&amp;gt;'&lt;/tt&gt; to iterate over its supplied
459      * data object as an Array. However, &lt;i&gt;named&lt;/i&gt; properties may be placed into the data object to
460      * provide non-repeating data such as headings, totals etc.&lt;/p&gt;
461      * @param {Ext.data.Model[]} records An Array of {@link Ext.data.Model}s to be rendered into the DataView.
462      * @param {Number} startIndex the index number of the Record being prepared for rendering.
463      * @return {Object[]} An Array of data objects to be processed by a repeating XTemplate. May also
464      * contain &lt;i&gt;named&lt;/i&gt; properties.
465      */
466     collectData : function(records, startIndex){
467         var r = [],
468             i = 0,
469             len = records.length,
470             record;
471
472         for(; i &lt; len; i++){
473             record = records[i];
474             r[r.length] = this.prepareData(record[record.persistenceProperty], startIndex + i, record);
475         }
476         return r;
477     },
478
479     // private
480     bufferRender : function(records, index){
481         var div = document.createElement('div');
482         this.tpl.overwrite(div, this.collectData(records, index));
483         return Ext.query(this.getItemSelector(), div);
484     },
485
486     // private
487     onUpdate : function(ds, record){
488         var me = this,
489             index = me.store.indexOf(record),
490             node;
491
492         if (index &gt; -1){
493             node = me.bufferRender([record], index)[0];
494             // ensure the node actually exists in the DOM
495             if (me.getNode(record)) {
496                 me.all.replaceElement(index, node, true);
497                 me.updateIndexes(index, index);
498                 // Maintain selection after update
499                 // TODO: Move to approriate event handler.
500                 me.selModel.refresh();
501                 me.fireEvent('itemupdate', record, index, node);
502             }
503         }
504
505     },
506
507     // private
508     onAdd : function(ds, records, index) {
509         var me = this,
510             nodes;
511
512         if (me.all.getCount() === 0) {
513             me.refresh();
514             return;
515         }
516
517         nodes = me.bufferRender(records, index);
518         me.doAdd(nodes, records, index);
519
520         me.selModel.refresh();
521         me.updateIndexes(index);
522         me.fireEvent('itemadd', records, index, nodes);
523     },
524
525     doAdd: function(nodes, records, index) {
526         var all = this.all;
527
528         if (index &lt; all.getCount()) {
529             all.item(index).insertSibling(nodes, 'before', true);
530         } else {
531             all.last().insertSibling(nodes, 'after', true);
532         }
533
534         Ext.Array.insert(all.elements, index, nodes);
535     },
536
537     // private
538     onRemove : function(ds, record, index) {
539         var me = this;
540
541         me.doRemove(record, index);
542         me.updateIndexes(index);
543         if (me.store.getCount() === 0){
544             me.refresh();
545         }
546         me.fireEvent('itemremove', record, index);
547     },
548
549     doRemove: function(record, index) {
550         this.all.removeElement(index, true);
551     },
552
553 <span id='Ext-view-AbstractView-method-refreshNode'>    /**
554 </span>     * Refreshes an individual node's data from the store.
555      * @param {Number} index The item's data index in the store
556      */
557     refreshNode : function(index){
558         this.onUpdate(this.store, this.store.getAt(index));
559     },
560
561     // private
562     updateIndexes : function(startIndex, endIndex) {
563         var ns = this.all.elements,
564             records = this.store.getRange(),
565             i;
566             
567         startIndex = startIndex || 0;
568         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
569         for(i = startIndex; i &lt;= endIndex; i++){
570             ns[i].viewIndex = i;
571             ns[i].viewRecordId = records[i].internalId;
572             if (!ns[i].boundView) {
573                 ns[i].boundView = this.id;
574             }
575         }
576     },
577
578 <span id='Ext-view-AbstractView-method-getStore'>    /**
579 </span>     * Returns the store associated with this DataView.
580      * @return {Ext.data.Store} The store
581      */
582     getStore : function(){
583         return this.store;
584     },
585
586 <span id='Ext-view-AbstractView-method-bindStore'>    /**
587 </span>     * Changes the data store bound to this view and refreshes it.
588      * @param {Ext.data.Store} store The store to bind to this view
589      */
590     bindStore : function(store, initial) {
591         var me = this,
592             maskStore;
593
594         if (!initial &amp;&amp; me.store) {
595             if (store !== me.store &amp;&amp; me.store.autoDestroy) {
596                 me.store.destroyStore();
597             }
598             else {
599                 me.mun(me.store, {
600                     scope: me,
601                     datachanged: me.onDataChanged,
602                     add: me.onAdd,
603                     remove: me.onRemove,
604                     update: me.onUpdate,
605                     clear: me.refresh
606                 });
607             }
608             if (!store) {
609                 // Ensure we have an instantiated LoadMask before we unbind it.
610                 if (me.loadMask &amp;&amp; me.loadMask.bindStore) {
611                     me.loadMask.bindStore(null);
612                 }
613                 me.store = null;
614             }
615         }
616         if (store) {
617             store = Ext.data.StoreManager.lookup(store);
618             me.mon(store, {
619                 scope: me,
620                 datachanged: me.onDataChanged,
621                 add: me.onAdd,
622                 remove: me.onRemove,
623                 update: me.onUpdate,
624                 clear: me.refresh
625             });
626             // Ensure we have an instantiated LoadMask before we bind it.
627             if (me.loadMask &amp;&amp; me.loadMask.bindStore) {
628                 // View's store is a NodeStore, use owning TreePanel's Store
629                 if (Ext.Array.contains(store.alias, 'store.node')) {
630                     maskStore = this.ownerCt.store;
631                 } else {
632                     maskStore = store;
633                 }
634                 me.loadMask.bindStore(maskStore);
635             }
636         }
637
638         // Flag to say that initial refresh has not been performed.
639         // Set here rather than at initialization time, so that a reconfigure with a new store will refire viewready
640         me.viewReady = false;
641
642         me.store = store;
643         // Bind the store to our selection model
644         me.getSelectionModel().bind(store);
645
646         /*
647          * This code used to have checks for:
648          * if (store &amp;&amp; (!initial || store.getCount() || me.emptyText)) {
649          * Instead, just trigger a refresh and let the view itself figure out
650          * what needs to happen. It can cause incorrect display if our store
651          * has no data.
652          */
653         if (store) {
654             if (initial &amp;&amp; me.deferInitialRefresh) {
655                 Ext.Function.defer(function () {
656                     if (!me.isDestroyed) {
657                         me.refresh(true);
658                     }
659                 }, 1);
660             } else {
661                 me.refresh(true);
662             }
663         }
664     },
665
666 <span id='Ext-view-AbstractView-method-onDataChanged'>    /**
667 </span>     * @private
668      * Calls this.refresh if this.blockRefresh is not true
669      */
670     onDataChanged: function() {
671         if (this.blockRefresh !== true) {
672             this.refresh.apply(this, arguments);
673         }
674     },
675
676 <span id='Ext-view-AbstractView-method-findItemByChild'>    /**
677 </span>     * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
678      * @param {HTMLElement} node
679      * @return {HTMLElement} The template node
680      */
681     findItemByChild: function(node){
682         return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
683     },
684
685 <span id='Ext-view-AbstractView-method-findTargetByEvent'>    /**
686 </span>     * Returns the template node by the Ext.EventObject or null if it is not found.
687      * @param {Ext.EventObject} e
688      */
689     findTargetByEvent: function(e) {
690         return e.getTarget(this.getItemSelector(), this.getTargetEl());
691     },
692
693
694 <span id='Ext-view-AbstractView-method-getSelectedNodes'>    /**
695 </span>     * Gets the currently selected nodes.
696      * @return {HTMLElement[]} An array of HTMLElements
697      */
698     getSelectedNodes: function(){
699         var nodes   = [],
700             records = this.selModel.getSelection(),
701             ln = records.length,
702             i  = 0;
703
704         for (; i &lt; ln; i++) {
705             nodes.push(this.getNode(records[i]));
706         }
707
708         return nodes;
709     },
710
711 <span id='Ext-view-AbstractView-method-getRecords'>    /**
712 </span>     * Gets an array of the records from an array of nodes
713      * @param {HTMLElement[]} nodes The nodes to evaluate
714      * @return {Ext.data.Model[]} records The {@link Ext.data.Model} objects
715      */
716     getRecords: function(nodes) {
717         var records = [],
718             i = 0,
719             len = nodes.length,
720             data = this.store.data;
721
722         for (; i &lt; len; i++) {
723             records[records.length] = data.getByKey(nodes[i].viewRecordId);
724         }
725
726         return records;
727     },
728
729 <span id='Ext-view-AbstractView-method-getRecord'>    /**
730 </span>     * Gets a record from a node
731      * @param {Ext.Element/HTMLElement} node The node to evaluate
732      *
733      * @return {Ext.data.Model} record The {@link Ext.data.Model} object
734      */
735     getRecord: function(node){
736         return this.store.data.getByKey(Ext.getDom(node).viewRecordId);
737     },
738
739
740 <span id='Ext-view-AbstractView-method-isSelected'>    /**
741 </span>     * Returns true if the passed node is selected, else false.
742      * @param {HTMLElement/Number/Ext.data.Model} node The node, node index or record to check
743      * @return {Boolean} True if selected, else false
744      */
745     isSelected : function(node) {
746         // TODO: El/Idx/Record
747         var r = this.getRecord(node);
748         return this.selModel.isSelected(r);
749     },
750
751 <span id='Ext-view-AbstractView-method-select'>    /**
752 </span>     * Selects a record instance by record instance or index.
753      * @param {Ext.data.Model[]/Number} records An array of records or an index
754      * @param {Boolean} [keepExisting] True to keep existing selections
755      * @param {Boolean} [suppressEvent] Set to true to not fire a select event
756      */
757     select: function(records, keepExisting, suppressEvent) {
758         this.selModel.select(records, keepExisting, suppressEvent);
759     },
760
761 <span id='Ext-view-AbstractView-method-deselect'>    /**
762 </span>     * Deselects a record instance by record instance or index.
763      * @param {Ext.data.Model[]/Number} records An array of records or an index
764      * @param {Boolean} [suppressEvent] Set to true to not fire a deselect event
765      */
766     deselect: function(records, suppressEvent) {
767         this.selModel.deselect(records, suppressEvent);
768     },
769
770 <span id='Ext-view-AbstractView-method-getNode'>    /**
771 </span>     * Gets a template node.
772      * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node,
773      * the id of a template node or the record associated with the node.
774      * @return {HTMLElement} The node or null if it wasn't found
775      */
776     getNode : function(nodeInfo) {
777         if (!this.rendered) {
778             return null;
779         }
780         if (Ext.isString(nodeInfo)) {
781             return document.getElementById(nodeInfo);
782         }
783         if (Ext.isNumber(nodeInfo)) {
784             return this.all.elements[nodeInfo];
785         }
786         if (nodeInfo instanceof Ext.data.Model) {
787             return this.getNodeByRecord(nodeInfo);
788         }
789         return nodeInfo; // already an HTMLElement
790     },
791
792 <span id='Ext-view-AbstractView-method-getNodeByRecord'>    /**
793 </span>     * @private
794      */
795     getNodeByRecord: function(record) {
796         var ns = this.all.elements,
797             ln = ns.length,
798             i = 0;
799
800         for (; i &lt; ln; i++) {
801             if (ns[i].viewRecordId === record.internalId) {
802                 return ns[i];
803             }
804         }
805
806         return null;
807     },
808
809 <span id='Ext-view-AbstractView-method-getNodes'>    /**
810 </span>     * Gets a range nodes.
811      * @param {Number} start (optional) The index of the first node in the range
812      * @param {Number} end (optional) The index of the last node in the range
813      * @return {HTMLElement[]} An array of nodes
814      */
815     getNodes: function(start, end) {
816         var ns = this.all.elements,
817             nodes = [],
818             i;
819
820         start = start || 0;
821         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
822         if (start &lt;= end) {
823             for (i = start; i &lt;= end &amp;&amp; ns[i]; i++) {
824                 nodes.push(ns[i]);
825             }
826         } else {
827             for (i = start; i &gt;= end &amp;&amp; ns[i]; i--) {
828                 nodes.push(ns[i]);
829             }
830         }
831         return nodes;
832     },
833
834 <span id='Ext-view-AbstractView-method-indexOf'>    /**
835 </span>     * Finds the index of the passed node.
836      * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
837      * or a record associated with a node.
838      * @return {Number} The index of the node or -1
839      */
840     indexOf: function(node) {
841         node = this.getNode(node);
842         if (Ext.isNumber(node.viewIndex)) {
843             return node.viewIndex;
844         }
845         return this.all.indexOf(node);
846     },
847
848     onDestroy : function() {
849         var me = this;
850
851         me.all.clear();
852         me.callParent();
853         me.bindStore(null);
854         me.selModel.destroy();
855     },
856
857     // invoked by the selection model to maintain visual UI cues
858     onItemSelect: function(record) {
859         var node = this.getNode(record);
860         
861         if (node) {
862             Ext.fly(node).addCls(this.selectedItemCls);
863         }
864     },
865
866     // invoked by the selection model to maintain visual UI cues
867     onItemDeselect: function(record) {
868         var node = this.getNode(record);
869         
870         if (node) {
871             Ext.fly(node).removeCls(this.selectedItemCls);
872         }
873     },
874
875     getItemSelector: function() {
876         return this.itemSelector;
877     }
878 }, function() {
879     // all of this information is available directly
880     // from the SelectionModel itself, the only added methods
881     // to DataView regarding selection will perform some transformation/lookup
882     // between HTMLElement/Nodes to records and vice versa.
883     Ext.deprecate('extjs', '4.0', function() {
884         Ext.view.AbstractView.override({
885 <span id='Ext-view-AbstractView-cfg-multiSelect'>            /**
886 </span>             * @cfg {Boolean} [multiSelect=false]
887              * True to allow selection of more than one item at a time, false to allow selection of only a single item
888              * at a time or no selection at all, depending on the value of {@link #singleSelect}.
889              */
890 <span id='Ext-view-AbstractView-cfg-singleSelect'>            /**
891 </span>             * @cfg {Boolean} [singleSelect=false]
892              * True to allow selection of exactly one item at a time, false to allow no selection at all.
893              * Note that if {@link #multiSelect} = true, this value will be ignored.
894              */
895 <span id='Ext-view-AbstractView-cfg-simpleSelect'>            /**
896 </span>             * @cfg {Boolean} [simpleSelect=false]
897              * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
898              * false to force the user to hold Ctrl or Shift to select more than on item.
899              */
900
901 <span id='Ext-view-AbstractView-method-getSelectionCount'>            /**
902 </span>             * Gets the number of selected nodes.
903              * @return {Number} The node count
904              */
905             getSelectionCount : function(){
906                 if (Ext.global.console) {
907                     Ext.global.console.warn(&quot;DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel&quot;);
908                 }
909                 return this.selModel.getSelection().length;
910             },
911
912 <span id='Ext-view-AbstractView-method-getSelectedRecords'>            /**
913 </span>             * Gets an array of the selected records
914              * @return {Ext.data.Model[]} An array of {@link Ext.data.Model} objects
915              */
916             getSelectedRecords : function(){
917                 if (Ext.global.console) {
918                     Ext.global.console.warn(&quot;DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel&quot;);
919                 }
920                 return this.selModel.getSelection();
921             },
922
923             select: function(records, keepExisting, supressEvents) {
924                 if (Ext.global.console) {
925                     Ext.global.console.warn(&quot;DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()&quot;);
926                 }
927                 var sm = this.getSelectionModel();
928                 return sm.select.apply(sm, arguments);
929             },
930
931             clearSelections: function() {
932                 if (Ext.global.console) {
933                     Ext.global.console.warn(&quot;DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()&quot;);
934                 }
935                 var sm = this.getSelectionModel();
936                 return sm.deselectAll();
937             }
938         });
939     });
940 });
941 </pre>
942 </body>
943 </html>