Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / docs / source / DataView.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.2.1
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.com/license
14  */
15 <div id="cls-Ext.DataView"></div>/**
16  * @class Ext.DataView
17  * @extends Ext.BoxComponent
18  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
19  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
20  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
21  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
22  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
23  * config must be provided for the DataView to determine what nodes it will be working with.</b>
24  *
25  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
26  * <pre><code>
27 var store = new Ext.data.JsonStore({
28     url: 'get-images.php',
29     root: 'images',
30     fields: [
31         'name', 'url',
32         {name:'size', type: 'float'},
33         {name:'lastmod', type:'date', dateFormat:'timestamp'}
34     ]
35 });
36 store.load();
37
38 var tpl = new Ext.XTemplate(
39     '&lt;tpl for="."&gt;',
40         '&lt;div class="thumb-wrap" id="{name}"&gt;',
41         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
42         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
43     '&lt;/tpl&gt;',
44     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
45 );
46
47 var panel = new Ext.Panel({
48     id:'images-view',
49     frame:true,
50     width:535,
51     autoHeight:true,
52     collapsible:true,
53     layout:'fit',
54     title:'Simple DataView',
55
56     items: new Ext.DataView({
57         store: store,
58         tpl: tpl,
59         autoHeight:true,
60         multiSelect: true,
61         overClass:'x-view-over',
62         itemSelector:'div.thumb-wrap',
63         emptyText: 'No images to display'
64     })
65 });
66 panel.render(document.body);
67 </code></pre>
68  * @constructor
69  * Create a new DataView
70  * @param {Object} config The config object
71  * @xtype dataview
72  */
73 Ext.DataView = Ext.extend(Ext.BoxComponent, {
74     <div id="cfg-Ext.DataView-tpl"></div>/**
75      * @cfg {String/Array} tpl
76      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
77      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
78      */
79     <div id="cfg-Ext.DataView-store"></div>/**
80      * @cfg {Ext.data.Store} store
81      * The {@link Ext.data.Store} to bind this DataView to.
82      */
83     <div id="cfg-Ext.DataView-itemSelector"></div>/**
84      * @cfg {String} itemSelector
85      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
86      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
87      * working with.
88      */
89     <div id="cfg-Ext.DataView-multiSelect"></div>/**
90      * @cfg {Boolean} multiSelect
91      * True to allow selection of more than one item at a time, false to allow selection of only a single item
92      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
93      */
94     <div id="cfg-Ext.DataView-singleSelect"></div>/**
95      * @cfg {Boolean} singleSelect
96      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
97      * Note that if {@link #multiSelect} = true, this value will be ignored.
98      */
99     <div id="cfg-Ext.DataView-simpleSelect"></div>/**
100      * @cfg {Boolean} simpleSelect
101      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
102      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
103      */
104     <div id="cfg-Ext.DataView-overClass"></div>/**
105      * @cfg {String} overClass
106      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
107      */
108     <div id="cfg-Ext.DataView-loadingText"></div>/**
109      * @cfg {String} loadingText
110      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
111      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
112      * contents will continue to display normally until the new data is loaded and the contents are replaced.
113      */
114     <div id="cfg-Ext.DataView-selectedClass"></div>/**
115      * @cfg {String} selectedClass
116      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
117      */
118     selectedClass : "x-view-selected",
119     <div id="cfg-Ext.DataView-emptyText"></div>/**
120      * @cfg {String} emptyText
121      * The text to display in the view when there is no data to display (defaults to '').
122      */
123     emptyText : "",
124
125     <div id="cfg-Ext.DataView-deferEmptyText"></div>/**
126      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
127      */
128     deferEmptyText: true,
129     <div id="cfg-Ext.DataView-trackOver"></div>/**
130      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
131      */
132     trackOver: false,
133     
134     <div id="cfg-Ext.DataView-blockRefresh"></div>/**
135      * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
136      * you wish to provide custom transition animations via a plugin (defaults to false)
137      */
138     blockRefresh: false,
139
140     //private
141     last: false,
142
143     // private
144     initComponent : function(){
145         Ext.DataView.superclass.initComponent.call(this);
146         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
147             this.tpl = new Ext.XTemplate(this.tpl);
148         }
149
150         this.addEvents(
151             <div id="event-Ext.DataView-beforeclick"></div>/**
152              * @event beforeclick
153              * Fires before a click is processed. Returns false to cancel the default action.
154              * @param {Ext.DataView} this
155              * @param {Number} index The index of the target node
156              * @param {HTMLElement} node The target node
157              * @param {Ext.EventObject} e The raw event object
158              */
159             "beforeclick",
160             <div id="event-Ext.DataView-click"></div>/**
161              * @event click
162              * Fires when a template node is clicked.
163              * @param {Ext.DataView} this
164              * @param {Number} index The index of the target node
165              * @param {HTMLElement} node The target node
166              * @param {Ext.EventObject} e The raw event object
167              */
168             "click",
169             <div id="event-Ext.DataView-mouseenter"></div>/**
170              * @event mouseenter
171              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
172              * @param {Ext.DataView} this
173              * @param {Number} index The index of the target node
174              * @param {HTMLElement} node The target node
175              * @param {Ext.EventObject} e The raw event object
176              */
177             "mouseenter",
178             <div id="event-Ext.DataView-mouseleave"></div>/**
179              * @event mouseleave
180              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
181              * @param {Ext.DataView} this
182              * @param {Number} index The index of the target node
183              * @param {HTMLElement} node The target node
184              * @param {Ext.EventObject} e The raw event object
185              */
186             "mouseleave",
187             <div id="event-Ext.DataView-containerclick"></div>/**
188              * @event containerclick
189              * Fires when a click occurs and it is not on a template node.
190              * @param {Ext.DataView} this
191              * @param {Ext.EventObject} e The raw event object
192              */
193             "containerclick",
194             <div id="event-Ext.DataView-dblclick"></div>/**
195              * @event dblclick
196              * Fires when a template node is double clicked.
197              * @param {Ext.DataView} this
198              * @param {Number} index The index of the target node
199              * @param {HTMLElement} node The target node
200              * @param {Ext.EventObject} e The raw event object
201              */
202             "dblclick",
203             <div id="event-Ext.DataView-contextmenu"></div>/**
204              * @event contextmenu
205              * Fires when a template node is right clicked.
206              * @param {Ext.DataView} this
207              * @param {Number} index The index of the target node
208              * @param {HTMLElement} node The target node
209              * @param {Ext.EventObject} e The raw event object
210              */
211             "contextmenu",
212             <div id="event-Ext.DataView-containercontextmenu"></div>/**
213              * @event containercontextmenu
214              * Fires when a right click occurs that is not on a template node.
215              * @param {Ext.DataView} this
216              * @param {Ext.EventObject} e The raw event object
217              */
218             "containercontextmenu",
219             <div id="event-Ext.DataView-selectionchange"></div>/**
220              * @event selectionchange
221              * Fires when the selected nodes change.
222              * @param {Ext.DataView} this
223              * @param {Array} selections Array of the selected nodes
224              */
225             "selectionchange",
226
227             <div id="event-Ext.DataView-beforeselect"></div>/**
228              * @event beforeselect
229              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
230              * @param {Ext.DataView} this
231              * @param {HTMLElement} node The node to be selected
232              * @param {Array} selections Array of currently selected nodes
233              */
234             "beforeselect"
235         );
236
237         this.store = Ext.StoreMgr.lookup(this.store);
238         this.all = new Ext.CompositeElementLite();
239         this.selected = new Ext.CompositeElementLite();
240     },
241
242     // private
243     afterRender : function(){
244         Ext.DataView.superclass.afterRender.call(this);
245
246                 this.mon(this.getTemplateTarget(), {
247             "click": this.onClick,
248             "dblclick": this.onDblClick,
249             "contextmenu": this.onContextMenu,
250             scope:this
251         });
252
253         if(this.overClass || this.trackOver){
254             this.mon(this.getTemplateTarget(), {
255                 "mouseover": this.onMouseOver,
256                 "mouseout": this.onMouseOut,
257                 scope:this
258             });
259         }
260
261         if(this.store){
262             this.bindStore(this.store, true);
263         }
264     },
265
266     <div id="method-Ext.DataView-refresh"></div>/**
267      * Refreshes the view by reloading the data from the store and re-rendering the template.
268      */
269     refresh : function() {
270         this.clearSelections(false, true);
271         var el = this.getTemplateTarget();
272         el.update("");
273         var records = this.store.getRange();
274         if(records.length < 1){
275             if(!this.deferEmptyText || this.hasSkippedEmptyText){
276                 el.update(this.emptyText);
277             }
278             this.all.clear();
279         }else{
280             this.tpl.overwrite(el, this.collectData(records, 0));
281             this.all.fill(Ext.query(this.itemSelector, el.dom));
282             this.updateIndexes(0);
283         }
284         this.hasSkippedEmptyText = true;
285     },
286
287     getTemplateTarget: function(){
288         return this.el;
289     },
290
291     <div id="method-Ext.DataView-prepareData"></div>/**
292      * Function which can be overridden to provide custom formatting for each Record that is used by this
293      * DataView's {@link #tpl template} to render each node.
294      * @param {Array/Object} data The raw data object that was used to create the Record.
295      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
296      * @param {Record} record The Record being prepared for rendering.
297      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
298      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
299      */
300     prepareData : function(data){
301         return data;
302     },
303
304     <div id="method-Ext.DataView-collectData"></div>/**
305      * <p>Function which can be overridden which returns the data object passed to this
306      * DataView's {@link #tpl template} to render the whole DataView.</p>
307      * <p>This is usually an Array of data objects, each element of which is processed by an
308      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
309      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
310      * provide non-repeating data such as headings, totals etc.</p>
311      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
312      * @param {Number} startIndex the index number of the Record being prepared for rendering.
313      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
314      * contain <i>named</i> properties.
315      */
316     collectData : function(records, startIndex){
317         var r = [];
318         for(var i = 0, len = records.length; i < len; i++){
319             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
320         }
321         return r;
322     },
323
324     // private
325     bufferRender : function(records){
326         var div = document.createElement('div');
327         this.tpl.overwrite(div, this.collectData(records));
328         return Ext.query(this.itemSelector, div);
329     },
330
331     // private
332     onUpdate : function(ds, record){
333         var index = this.store.indexOf(record);
334         if(index > -1){
335             var sel = this.isSelected(index);
336             var original = this.all.elements[index];
337             var node = this.bufferRender([record], index)[0];
338
339             this.all.replaceElement(index, node, true);
340             if(sel){
341                 this.selected.replaceElement(original, node);
342                 this.all.item(index).addClass(this.selectedClass);
343             }
344             this.updateIndexes(index, index);
345         }
346     },
347
348     // private
349     onAdd : function(ds, records, index){
350         if(this.all.getCount() === 0){
351             this.refresh();
352             return;
353         }
354         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
355         if(index < this.all.getCount()){
356             n = this.all.item(index).insertSibling(nodes, 'before', true);
357             a.splice.apply(a, [index, 0].concat(nodes));
358         }else{
359             n = this.all.last().insertSibling(nodes, 'after', true);
360             a.push.apply(a, nodes);
361         }
362         this.updateIndexes(index);
363     },
364
365     // private
366     onRemove : function(ds, record, index){
367         this.deselect(index);
368         this.all.removeElement(index, true);
369         this.updateIndexes(index);
370         if (this.store.getCount() === 0){
371             this.refresh();
372         }
373     },
374
375     <div id="method-Ext.DataView-refreshNode"></div>/**
376      * Refreshes an individual node's data from the store.
377      * @param {Number} index The item's data index in the store
378      */
379     refreshNode : function(index){
380         this.onUpdate(this.store, this.store.getAt(index));
381     },
382
383     // private
384     updateIndexes : function(startIndex, endIndex){
385         var ns = this.all.elements;
386         startIndex = startIndex || 0;
387         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
388         for(var i = startIndex; i <= endIndex; i++){
389             ns[i].viewIndex = i;
390         }
391     },
392     
393     <div id="method-Ext.DataView-getStore"></div>/**
394      * Returns the store associated with this DataView.
395      * @return {Ext.data.Store} The store
396      */
397     getStore : function(){
398         return this.store;
399     },
400
401     <div id="method-Ext.DataView-bindStore"></div>/**
402      * Changes the data store bound to this view and refreshes it.
403      * @param {Store} store The store to bind to this view
404      */
405     bindStore : function(store, initial){
406         if(!initial && this.store){
407             if(store !== this.store && this.store.autoDestroy){
408                 this.store.destroy();
409             }else{
410                 this.store.un("beforeload", this.onBeforeLoad, this);
411                 this.store.un("datachanged", this.onDataChanged, this);
412                 this.store.un("add", this.onAdd, this);
413                 this.store.un("remove", this.onRemove, this);
414                 this.store.un("update", this.onUpdate, this);
415                 this.store.un("clear", this.refresh, this);
416             }
417             if(!store){
418                 this.store = null;
419             }
420         }
421         if(store){
422             store = Ext.StoreMgr.lookup(store);
423             store.on({
424                 scope: this,
425                 beforeload: this.onBeforeLoad,
426                 datachanged: this.onDataChanged,
427                 add: this.onAdd,
428                 remove: this.onRemove,
429                 update: this.onUpdate,
430                 clear: this.refresh
431             });
432         }
433         this.store = store;
434         if(store){
435             this.refresh();
436         }
437     },
438     
439     /**
440      * @private
441      * Calls this.refresh if this.blockRefresh is not true
442      */
443     onDataChanged: function() {
444         if (this.blockRefresh !== true) {
445             this.refresh.apply(this, arguments);
446         }
447     },
448
449     <div id="method-Ext.DataView-findItemFromChild"></div>/**
450      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
451      * @param {HTMLElement} node
452      * @return {HTMLElement} The template node
453      */
454     findItemFromChild : function(node){
455         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
456     },
457
458     // private
459     onClick : function(e){
460         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
461         if(item){
462             var index = this.indexOf(item);
463             if(this.onItemClick(item, index, e) !== false){
464                 this.fireEvent("click", this, index, item, e);
465             }
466         }else{
467             if(this.fireEvent("containerclick", this, e) !== false){
468                 this.onContainerClick(e);
469             }
470         }
471     },
472
473     onContainerClick : function(e){
474         this.clearSelections();
475     },
476
477     // private
478     onContextMenu : function(e){
479         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
480         if(item){
481             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
482         }else{
483             this.fireEvent("containercontextmenu", this, e);
484         }
485     },
486
487     // private
488     onDblClick : function(e){
489         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
490         if(item){
491             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
492         }
493     },
494
495     // private
496     onMouseOver : function(e){
497         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
498         if(item && item !== this.lastItem){
499             this.lastItem = item;
500             Ext.fly(item).addClass(this.overClass);
501             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
502         }
503     },
504
505     // private
506     onMouseOut : function(e){
507         if(this.lastItem){
508             if(!e.within(this.lastItem, true, true)){
509                 Ext.fly(this.lastItem).removeClass(this.overClass);
510                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
511                 delete this.lastItem;
512             }
513         }
514     },
515
516     // private
517     onItemClick : function(item, index, e){
518         if(this.fireEvent("beforeclick", this, index, item, e) === false){
519             return false;
520         }
521         if(this.multiSelect){
522             this.doMultiSelection(item, index, e);
523             e.preventDefault();
524         }else if(this.singleSelect){
525             this.doSingleSelection(item, index, e);
526             e.preventDefault();
527         }
528         return true;
529     },
530
531     // private
532     doSingleSelection : function(item, index, e){
533         if(e.ctrlKey && this.isSelected(index)){
534             this.deselect(index);
535         }else{
536             this.select(index, false);
537         }
538     },
539
540     // private
541     doMultiSelection : function(item, index, e){
542         if(e.shiftKey && this.last !== false){
543             var last = this.last;
544             this.selectRange(last, index, e.ctrlKey);
545             this.last = last; // reset the last
546         }else{
547             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
548                 this.deselect(index);
549             }else{
550                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
551             }
552         }
553     },
554
555     <div id="method-Ext.DataView-getSelectionCount"></div>/**
556      * Gets the number of selected nodes.
557      * @return {Number} The node count
558      */
559     getSelectionCount : function(){
560         return this.selected.getCount();
561     },
562
563     <div id="method-Ext.DataView-getSelectedNodes"></div>/**
564      * Gets the currently selected nodes.
565      * @return {Array} An array of HTMLElements
566      */
567     getSelectedNodes : function(){
568         return this.selected.elements;
569     },
570
571     <div id="method-Ext.DataView-getSelectedIndexes"></div>/**
572      * Gets the indexes of the selected nodes.
573      * @return {Array} An array of numeric indexes
574      */
575     getSelectedIndexes : function(){
576         var indexes = [], s = this.selected.elements;
577         for(var i = 0, len = s.length; i < len; i++){
578             indexes.push(s[i].viewIndex);
579         }
580         return indexes;
581     },
582
583     <div id="method-Ext.DataView-getSelectedRecords"></div>/**
584      * Gets an array of the selected records
585      * @return {Array} An array of {@link Ext.data.Record} objects
586      */
587     getSelectedRecords : function(){
588         var r = [], s = this.selected.elements;
589         for(var i = 0, len = s.length; i < len; i++){
590             r[r.length] = this.store.getAt(s[i].viewIndex);
591         }
592         return r;
593     },
594
595     <div id="method-Ext.DataView-getRecords"></div>/**
596      * Gets an array of the records from an array of nodes
597      * @param {Array} nodes The nodes to evaluate
598      * @return {Array} records The {@link Ext.data.Record} objects
599      */
600     getRecords : function(nodes){
601         var r = [], s = nodes;
602         for(var i = 0, len = s.length; i < len; i++){
603             r[r.length] = this.store.getAt(s[i].viewIndex);
604         }
605         return r;
606     },
607
608     <div id="method-Ext.DataView-getRecord"></div>/**
609      * Gets a record from a node
610      * @param {HTMLElement} node The node to evaluate
611      * @return {Record} record The {@link Ext.data.Record} object
612      */
613     getRecord : function(node){
614         return this.store.getAt(node.viewIndex);
615     },
616
617     <div id="method-Ext.DataView-clearSelections"></div>/**
618      * Clears all selections.
619      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
620      */
621     clearSelections : function(suppressEvent, skipUpdate){
622         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
623             if(!skipUpdate){
624                 this.selected.removeClass(this.selectedClass);
625             }
626             this.selected.clear();
627             this.last = false;
628             if(!suppressEvent){
629                 this.fireEvent("selectionchange", this, this.selected.elements);
630             }
631         }
632     },
633
634     <div id="method-Ext.DataView-isSelected"></div>/**
635      * Returns true if the passed node is selected, else false.
636      * @param {HTMLElement/Number/Ext.data.Record} node The node, node index or record to check
637      * @return {Boolean} True if selected, else false
638      */
639     isSelected : function(node){
640         return this.selected.contains(this.getNode(node));
641     },
642
643     <div id="method-Ext.DataView-deselect"></div>/**
644      * Deselects a node.
645      * @param {HTMLElement/Number/Record} node The node, node index or record to deselect
646      */
647     deselect : function(node){
648         if(this.isSelected(node)){
649             node = this.getNode(node);
650             this.selected.removeElement(node);
651             if(this.last == node.viewIndex){
652                 this.last = false;
653             }
654             Ext.fly(node).removeClass(this.selectedClass);
655             this.fireEvent("selectionchange", this, this.selected.elements);
656         }
657     },
658
659     <div id="method-Ext.DataView-select"></div>/**
660      * Selects a set of nodes.
661      * @param {Array/HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
662      * id of a template node, record associated with a node or an array of any of those to select
663      * @param {Boolean} keepExisting (optional) true to keep existing selections
664      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
665      */
666     select : function(nodeInfo, keepExisting, suppressEvent){
667         if(Ext.isArray(nodeInfo)){
668             if(!keepExisting){
669                 this.clearSelections(true);
670             }
671             for(var i = 0, len = nodeInfo.length; i < len; i++){
672                 this.select(nodeInfo[i], true, true);
673             }
674             if(!suppressEvent){
675                 this.fireEvent("selectionchange", this, this.selected.elements);
676             }
677         } else{
678             var node = this.getNode(nodeInfo);
679             if(!keepExisting){
680                 this.clearSelections(true);
681             }
682             if(node && !this.isSelected(node)){
683                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
684                     Ext.fly(node).addClass(this.selectedClass);
685                     this.selected.add(node);
686                     this.last = node.viewIndex;
687                     if(!suppressEvent){
688                         this.fireEvent("selectionchange", this, this.selected.elements);
689                     }
690                 }
691             }
692         }
693     },
694
695     <div id="method-Ext.DataView-selectRange"></div>/**
696      * Selects a range of nodes. All nodes between start and end are selected.
697      * @param {Number} start The index of the first node in the range
698      * @param {Number} end The index of the last node in the range
699      * @param {Boolean} keepExisting (optional) True to retain existing selections
700      */
701     selectRange : function(start, end, keepExisting){
702         if(!keepExisting){
703             this.clearSelections(true);
704         }
705         this.select(this.getNodes(start, end), true);
706     },
707
708     <div id="method-Ext.DataView-getNode"></div>/**
709      * Gets a template node.
710      * @param {HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node, 
711      * the id of a template node or the record associated with the node.
712      * @return {HTMLElement} The node or null if it wasn't found
713      */
714     getNode : function(nodeInfo){
715         if(Ext.isString(nodeInfo)){
716             return document.getElementById(nodeInfo);
717         }else if(Ext.isNumber(nodeInfo)){
718             return this.all.elements[nodeInfo];
719         }else if(nodeInfo instanceof Ext.data.Record){
720             var idx = this.store.indexOf(nodeInfo);
721             return this.all.elements[idx];
722         }
723         return nodeInfo;
724     },
725
726     <div id="method-Ext.DataView-getNodes"></div>/**
727      * Gets a range nodes.
728      * @param {Number} start (optional) The index of the first node in the range
729      * @param {Number} end (optional) The index of the last node in the range
730      * @return {Array} An array of nodes
731      */
732     getNodes : function(start, end){
733         var ns = this.all.elements;
734         start = start || 0;
735         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
736         var nodes = [], i;
737         if(start <= end){
738             for(i = start; i <= end && ns[i]; i++){
739                 nodes.push(ns[i]);
740             }
741         } else{
742             for(i = start; i >= end && ns[i]; i--){
743                 nodes.push(ns[i]);
744             }
745         }
746         return nodes;
747     },
748
749     <div id="method-Ext.DataView-indexOf"></div>/**
750      * Finds the index of the passed node.
751      * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
752      * or a record associated with a node.
753      * @return {Number} The index of the node or -1
754      */
755     indexOf : function(node){
756         node = this.getNode(node);
757         if(Ext.isNumber(node.viewIndex)){
758             return node.viewIndex;
759         }
760         return this.all.indexOf(node);
761     },
762
763     // private
764     onBeforeLoad : function(){
765         if(this.loadingText){
766             this.clearSelections(false, true);
767             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
768             this.all.clear();
769         }
770     },
771
772     onDestroy : function(){
773         this.all.clear();
774         this.selected.clear();
775         Ext.DataView.superclass.onDestroy.call(this);
776         this.bindStore(null);
777     }
778 });
779
780 <div id="method-Ext.DataView-setStore"></div>/**
781  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
782  * @param {Store} store The store to bind to this view
783  */
784 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
785
786 Ext.reg('dataview', Ext.DataView);
787 </pre>    
788 </body>
789 </html>