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