Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/web/ext/pkgs/data-list-views-debug.js @ 76

Revision 76, 52.5 KB checked in by djay, 12 years ago (diff)

Ajout du répertoire web

  • Property svn:executable set to *
Line 
1/*!
2 * Ext JS Library 3.4.0
3 * Copyright(c) 2006-2011 Sencha Inc.
4 * licensing@sencha.com
5 * http://www.sencha.com/license
6 */
7/**
8 * @class Ext.DataView
9 * @extends Ext.BoxComponent
10 * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
11 * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
12 * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
13 * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
14 * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
15 * config must be provided for the DataView to determine what nodes it will be working with.</b>
16 *
17 * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
18 * <pre><code>
19var store = new Ext.data.JsonStore({
20    url: 'get-images.php',
21    root: 'images',
22    fields: [
23        'name', 'url',
24        {name:'size', type: 'float'},
25        {name:'lastmod', type:'date', dateFormat:'timestamp'}
26    ]
27});
28store.load();
29
30var tpl = new Ext.XTemplate(
31    '&lt;tpl for="."&gt;',
32        '&lt;div class="thumb-wrap" id="{name}"&gt;',
33        '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
34        '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
35    '&lt;/tpl&gt;',
36    '&lt;div class="x-clear"&gt;&lt;/div&gt;'
37);
38
39var panel = new Ext.Panel({
40    id:'images-view',
41    frame:true,
42    width:535,
43    autoHeight:true,
44    collapsible:true,
45    layout:'fit',
46    title:'Simple DataView',
47
48    items: new Ext.DataView({
49        store: store,
50        tpl: tpl,
51        autoHeight:true,
52        multiSelect: true,
53        overClass:'x-view-over',
54        itemSelector:'div.thumb-wrap',
55        emptyText: 'No images to display'
56    })
57});
58panel.render(document.body);
59</code></pre>
60 * @constructor
61 * Create a new DataView
62 * @param {Object} config The config object
63 * @xtype dataview
64 */
65Ext.DataView = Ext.extend(Ext.BoxComponent, {
66    /**
67     * @cfg {String/Array} tpl
68     * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
69     * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
70     */
71    /**
72     * @cfg {Ext.data.Store} store
73     * The {@link Ext.data.Store} to bind this DataView to.
74     */
75    /**
76     * @cfg {String} itemSelector
77     * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or
78     * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
79     * working with.
80     */
81    /**
82     * @cfg {Boolean} multiSelect
83     * True to allow selection of more than one item at a time, false to allow selection of only a single item
84     * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
85     */
86    /**
87     * @cfg {Boolean} singleSelect
88     * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
89     * Note that if {@link #multiSelect} = true, this value will be ignored.
90     */
91    /**
92     * @cfg {Boolean} simpleSelect
93     * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
94     * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
95     */
96    /**
97     * @cfg {String} overClass
98     * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
99     */
100    /**
101     * @cfg {String} loadingText
102     * A string to display during data load operations (defaults to undefined).  If specified, this text will be
103     * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
104     * contents will continue to display normally until the new data is loaded and the contents are replaced.
105     */
106    /**
107     * @cfg {String} selectedClass
108     * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
109     */
110    selectedClass : "x-view-selected",
111    /**
112     * @cfg {String} emptyText
113     * The text to display in the view when there is no data to display (defaults to '').
114     */
115    emptyText : "",
116
117    /**
118     * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
119     */
120    deferEmptyText: true,
121    /**
122     * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
123     */
124    trackOver: false,
125   
126    /**
127     * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
128     * you wish to provide custom transition animations via a plugin (defaults to false)
129     */
130    blockRefresh: false,
131
132    //private
133    last: false,
134
135    // private
136    initComponent : function(){
137        Ext.DataView.superclass.initComponent.call(this);
138        if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
139            this.tpl = new Ext.XTemplate(this.tpl);
140        }
141
142        this.addEvents(
143            /**
144             * @event beforeclick
145             * Fires before a click is processed. Returns false to cancel the default action.
146             * @param {Ext.DataView} this
147             * @param {Number} index The index of the target node
148             * @param {HTMLElement} node The target node
149             * @param {Ext.EventObject} e The raw event object
150             */
151            "beforeclick",
152            /**
153             * @event click
154             * Fires when a template node is clicked.
155             * @param {Ext.DataView} this
156             * @param {Number} index The index of the target node
157             * @param {HTMLElement} node The target node
158             * @param {Ext.EventObject} e The raw event object
159             */
160            "click",
161            /**
162             * @event mouseenter
163             * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
164             * @param {Ext.DataView} this
165             * @param {Number} index The index of the target node
166             * @param {HTMLElement} node The target node
167             * @param {Ext.EventObject} e The raw event object
168             */
169            "mouseenter",
170            /**
171             * @event mouseleave
172             * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
173             * @param {Ext.DataView} this
174             * @param {Number} index The index of the target node
175             * @param {HTMLElement} node The target node
176             * @param {Ext.EventObject} e The raw event object
177             */
178            "mouseleave",
179            /**
180             * @event containerclick
181             * Fires when a click occurs and it is not on a template node.
182             * @param {Ext.DataView} this
183             * @param {Ext.EventObject} e The raw event object
184             */
185            "containerclick",
186            /**
187             * @event dblclick
188             * Fires when a template node is double clicked.
189             * @param {Ext.DataView} this
190             * @param {Number} index The index of the target node
191             * @param {HTMLElement} node The target node
192             * @param {Ext.EventObject} e The raw event object
193             */
194            "dblclick",
195            /**
196             * @event contextmenu
197             * Fires when a template node is right clicked.
198             * @param {Ext.DataView} this
199             * @param {Number} index The index of the target node
200             * @param {HTMLElement} node The target node
201             * @param {Ext.EventObject} e The raw event object
202             */
203            "contextmenu",
204            /**
205             * @event containercontextmenu
206             * Fires when a right click occurs that is not on a template node.
207             * @param {Ext.DataView} this
208             * @param {Ext.EventObject} e The raw event object
209             */
210            "containercontextmenu",
211            /**
212             * @event selectionchange
213             * Fires when the selected nodes change.
214             * @param {Ext.DataView} this
215             * @param {Array} selections Array of the selected nodes
216             */
217            "selectionchange",
218
219            /**
220             * @event beforeselect
221             * Fires before a selection is made. If any handlers return false, the selection is cancelled.
222             * @param {Ext.DataView} this
223             * @param {HTMLElement} node The node to be selected
224             * @param {Array} selections Array of currently selected nodes
225             */
226            "beforeselect"
227        );
228
229        this.store = Ext.StoreMgr.lookup(this.store);
230        this.all = new Ext.CompositeElementLite();
231        this.selected = new Ext.CompositeElementLite();
232    },
233
234    // private
235    afterRender : function(){
236        Ext.DataView.superclass.afterRender.call(this);
237
238                this.mon(this.getTemplateTarget(), {
239            "click": this.onClick,
240            "dblclick": this.onDblClick,
241            "contextmenu": this.onContextMenu,
242            scope:this
243        });
244
245        if(this.overClass || this.trackOver){
246            this.mon(this.getTemplateTarget(), {
247                "mouseover": this.onMouseOver,
248                "mouseout": this.onMouseOut,
249                scope:this
250            });
251        }
252
253        if(this.store){
254            this.bindStore(this.store, true);
255        }
256    },
257
258    /**
259     * Refreshes the view by reloading the data from the store and re-rendering the template.
260     */
261    refresh : function() {
262        this.clearSelections(false, true);
263        var el = this.getTemplateTarget(),
264            records = this.store.getRange();
265           
266        el.update('');
267        if(records.length < 1){
268            if(!this.deferEmptyText || this.hasSkippedEmptyText){
269                el.update(this.emptyText);
270            }
271            this.all.clear();
272        }else{
273            this.tpl.overwrite(el, this.collectData(records, 0));
274            this.all.fill(Ext.query(this.itemSelector, el.dom));
275            this.updateIndexes(0);
276        }
277        this.hasSkippedEmptyText = true;
278    },
279
280    getTemplateTarget: function(){
281        return this.el;
282    },
283
284    /**
285     * Function which can be overridden to provide custom formatting for each Record that is used by this
286     * DataView's {@link #tpl template} to render each node.
287     * @param {Array/Object} data The raw data object that was used to create the Record.
288     * @param {Number} recordIndex the index number of the Record being prepared for rendering.
289     * @param {Record} record The Record being prepared for rendering.
290     * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
291     * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
292     */
293    prepareData : function(data){
294        return data;
295    },
296
297    /**
298     * <p>Function which can be overridden which returns the data object passed to this
299     * DataView's {@link #tpl template} to render the whole DataView.</p>
300     * <p>This is usually an Array of data objects, each element of which is processed by an
301     * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
302     * data object as an Array. However, <i>named</i> properties may be placed into the data object to
303     * provide non-repeating data such as headings, totals etc.</p>
304     * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
305     * @param {Number} startIndex the index number of the Record being prepared for rendering.
306     * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
307     * contain <i>named</i> properties.
308     */
309    collectData : function(records, startIndex){
310        var r = [],
311            i = 0,
312            len = records.length;
313        for(; i < len; i++){
314            r[r.length] = this.prepareData(records[i].data, startIndex + i, records[i]);
315        }
316        return r;
317    },
318
319    // private
320    bufferRender : function(records, index){
321        var div = document.createElement('div');
322        this.tpl.overwrite(div, this.collectData(records, index));
323        return Ext.query(this.itemSelector, div);
324    },
325
326    // private
327    onUpdate : function(ds, record){
328        var index = this.store.indexOf(record);
329        if(index > -1){
330            var sel = this.isSelected(index),
331                original = this.all.elements[index],
332                node = this.bufferRender([record], index)[0];
333
334            this.all.replaceElement(index, node, true);
335            if(sel){
336                this.selected.replaceElement(original, node);
337                this.all.item(index).addClass(this.selectedClass);
338            }
339            this.updateIndexes(index, index);
340        }
341    },
342
343    // private
344    onAdd : function(ds, records, index){
345        if(this.all.getCount() === 0){
346            this.refresh();
347            return;
348        }
349        var nodes = this.bufferRender(records, index), n, a = this.all.elements;
350        if(index < this.all.getCount()){
351            n = this.all.item(index).insertSibling(nodes, 'before', true);
352            a.splice.apply(a, [index, 0].concat(nodes));
353        }else{
354            n = this.all.last().insertSibling(nodes, 'after', true);
355            a.push.apply(a, nodes);
356        }
357        this.updateIndexes(index);
358    },
359
360    // private
361    onRemove : function(ds, record, index){
362        this.deselect(index);
363        this.all.removeElement(index, true);
364        this.updateIndexes(index);
365        if (this.store.getCount() === 0){
366            this.refresh();
367        }
368    },
369
370    /**
371     * Refreshes an individual node's data from the store.
372     * @param {Number} index The item's data index in the store
373     */
374    refreshNode : function(index){
375        this.onUpdate(this.store, this.store.getAt(index));
376    },
377
378    // private
379    updateIndexes : function(startIndex, endIndex){
380        var ns = this.all.elements;
381        startIndex = startIndex || 0;
382        endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
383        for(var i = startIndex; i <= endIndex; i++){
384            ns[i].viewIndex = i;
385        }
386    },
387   
388    /**
389     * Returns the store associated with this DataView.
390     * @return {Ext.data.Store} The store
391     */
392    getStore : function(){
393        return this.store;
394    },
395
396    /**
397     * Changes the data store bound to this view and refreshes it.
398     * @param {Store} store The store to bind to this view
399     */
400    bindStore : function(store, initial){
401        if(!initial && this.store){
402            if(store !== this.store && this.store.autoDestroy){
403                this.store.destroy();
404            }else{
405                this.store.un("beforeload", this.onBeforeLoad, this);
406                this.store.un("datachanged", this.onDataChanged, this);
407                this.store.un("add", this.onAdd, this);
408                this.store.un("remove", this.onRemove, this);
409                this.store.un("update", this.onUpdate, this);
410                this.store.un("clear", this.refresh, this);
411            }
412            if(!store){
413                this.store = null;
414            }
415        }
416        if(store){
417            store = Ext.StoreMgr.lookup(store);
418            store.on({
419                scope: this,
420                beforeload: this.onBeforeLoad,
421                datachanged: this.onDataChanged,
422                add: this.onAdd,
423                remove: this.onRemove,
424                update: this.onUpdate,
425                clear: this.refresh
426            });
427        }
428        this.store = store;
429        if(store){
430            this.refresh();
431        }
432    },
433   
434    /**
435     * @private
436     * Calls this.refresh if this.blockRefresh is not true
437     */
438    onDataChanged: function() {
439        if (this.blockRefresh !== true) {
440            this.refresh.apply(this, arguments);
441        }
442    },
443
444    /**
445     * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
446     * @param {HTMLElement} node
447     * @return {HTMLElement} The template node
448     */
449    findItemFromChild : function(node){
450        return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
451    },
452
453    // private
454    onClick : function(e){
455        var item = e.getTarget(this.itemSelector, this.getTemplateTarget()),
456            index;
457        if(item){
458            index = this.indexOf(item);
459            if(this.onItemClick(item, index, e) !== false){
460                this.fireEvent("click", this, index, item, e);
461            }
462        }else{
463            if(this.fireEvent("containerclick", this, e) !== false){
464                this.onContainerClick(e);
465            }
466        }
467    },
468
469    onContainerClick : function(e){
470        this.clearSelections();
471    },
472
473    // private
474    onContextMenu : function(e){
475        var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
476        if(item){
477            this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
478        }else{
479            this.fireEvent("containercontextmenu", this, e);
480        }
481    },
482
483    // private
484    onDblClick : function(e){
485        var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
486        if(item){
487            this.fireEvent("dblclick", this, this.indexOf(item), item, e);
488        }
489    },
490
491    // private
492    onMouseOver : function(e){
493        var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
494        if(item && item !== this.lastItem){
495            this.lastItem = item;
496            Ext.fly(item).addClass(this.overClass);
497            this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
498        }
499    },
500
501    // private
502    onMouseOut : function(e){
503        if(this.lastItem){
504            if(!e.within(this.lastItem, true, true)){
505                Ext.fly(this.lastItem).removeClass(this.overClass);
506                this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
507                delete this.lastItem;
508            }
509        }
510    },
511
512    // private
513    onItemClick : function(item, index, e){
514        if(this.fireEvent("beforeclick", this, index, item, e) === false){
515            return false;
516        }
517        if(this.multiSelect){
518            this.doMultiSelection(item, index, e);
519            e.preventDefault();
520        }else if(this.singleSelect){
521            this.doSingleSelection(item, index, e);
522            e.preventDefault();
523        }
524        return true;
525    },
526
527    // private
528    doSingleSelection : function(item, index, e){
529        if(e.ctrlKey && this.isSelected(index)){
530            this.deselect(index);
531        }else{
532            this.select(index, false);
533        }
534    },
535
536    // private
537    doMultiSelection : function(item, index, e){
538        if(e.shiftKey && this.last !== false){
539            var last = this.last;
540            this.selectRange(last, index, e.ctrlKey);
541            this.last = last; // reset the last
542        }else{
543            if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
544                this.deselect(index);
545            }else{
546                this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
547            }
548        }
549    },
550
551    /**
552     * Gets the number of selected nodes.
553     * @return {Number} The node count
554     */
555    getSelectionCount : function(){
556        return this.selected.getCount();
557    },
558
559    /**
560     * Gets the currently selected nodes.
561     * @return {Array} An array of HTMLElements
562     */
563    getSelectedNodes : function(){
564        return this.selected.elements;
565    },
566
567    /**
568     * Gets the indexes of the selected nodes.
569     * @return {Array} An array of numeric indexes
570     */
571    getSelectedIndexes : function(){
572        var indexes = [], 
573            selected = this.selected.elements,
574            i = 0,
575            len = selected.length;
576           
577        for(; i < len; i++){
578            indexes.push(selected[i].viewIndex);
579        }
580        return indexes;
581    },
582
583    /**
584     * Gets an array of the selected records
585     * @return {Array} An array of {@link Ext.data.Record} objects
586     */
587    getSelectedRecords : function(){
588        return this.getRecords(this.selected.elements);
589    },
590
591    /**
592     * Gets an array of the records from an array of nodes
593     * @param {Array} nodes The nodes to evaluate
594     * @return {Array} records The {@link Ext.data.Record} objects
595     */
596    getRecords : function(nodes){
597        var records = [], 
598            i = 0,
599            len = nodes.length;
600           
601        for(; i < len; i++){
602            records[records.length] = this.store.getAt(nodes[i].viewIndex);
603        }
604        return records;
605    },
606
607    /**
608     * Gets a record from a node
609     * @param {HTMLElement} node The node to evaluate
610     * @return {Record} record The {@link Ext.data.Record} object
611     */
612    getRecord : function(node){
613        return this.store.getAt(node.viewIndex);
614    },
615
616    /**
617     * Clears all selections.
618     * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
619     */
620    clearSelections : function(suppressEvent, skipUpdate){
621        if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
622            if(!skipUpdate){
623                this.selected.removeClass(this.selectedClass);
624            }
625            this.selected.clear();
626            this.last = false;
627            if(!suppressEvent){
628                this.fireEvent("selectionchange", this, this.selected.elements);
629            }
630        }
631    },
632
633    /**
634     * Returns true if the passed node is selected, else false.
635     * @param {HTMLElement/Number/Ext.data.Record} node The node, node index or record to check
636     * @return {Boolean} True if selected, else false
637     */
638    isSelected : function(node){
639        return this.selected.contains(this.getNode(node));
640    },
641
642    /**
643     * Deselects a node.
644     * @param {HTMLElement/Number/Record} node The node, node index or record to deselect
645     */
646    deselect : function(node){
647        if(this.isSelected(node)){
648            node = this.getNode(node);
649            this.selected.removeElement(node);
650            if(this.last == node.viewIndex){
651                this.last = false;
652            }
653            Ext.fly(node).removeClass(this.selectedClass);
654            this.fireEvent("selectionchange", this, this.selected.elements);
655        }
656    },
657
658    /**
659     * Selects a set of nodes.
660     * @param {Array/HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
661     * id of a template node, record associated with a node or an array of any of those to select
662     * @param {Boolean} keepExisting (optional) true to keep existing selections
663     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
664     */
665    select : function(nodeInfo, keepExisting, suppressEvent){
666        if(Ext.isArray(nodeInfo)){
667            if(!keepExisting){
668                this.clearSelections(true);
669            }
670            for(var i = 0, len = nodeInfo.length; i < len; i++){
671                this.select(nodeInfo[i], true, true);
672            }
673            if(!suppressEvent){
674                this.fireEvent("selectionchange", this, this.selected.elements);
675            }
676        } else{
677            var node = this.getNode(nodeInfo);
678            if(!keepExisting){
679                this.clearSelections(true);
680            }
681            if(node && !this.isSelected(node)){
682                if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
683                    Ext.fly(node).addClass(this.selectedClass);
684                    this.selected.add(node);
685                    this.last = node.viewIndex;
686                    if(!suppressEvent){
687                        this.fireEvent("selectionchange", this, this.selected.elements);
688                    }
689                }
690            }
691        }
692    },
693
694    /**
695     * Selects a range of nodes. All nodes between start and end are selected.
696     * @param {Number} start The index of the first node in the range
697     * @param {Number} end The index of the last node in the range
698     * @param {Boolean} keepExisting (optional) True to retain existing selections
699     */
700    selectRange : function(start, end, keepExisting){
701        if(!keepExisting){
702            this.clearSelections(true);
703        }
704        this.select(this.getNodes(start, end), true);
705    },
706
707    /**
708     * Gets a template node.
709     * @param {HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
710     * the id of a template node or the record associated with the node.
711     * @return {HTMLElement} The node or null if it wasn't found
712     */
713    getNode : function(nodeInfo){
714        if(Ext.isString(nodeInfo)){
715            return document.getElementById(nodeInfo);
716        }else if(Ext.isNumber(nodeInfo)){
717            return this.all.elements[nodeInfo];
718        }else if(nodeInfo instanceof Ext.data.Record){
719            var idx = this.store.indexOf(nodeInfo);
720            return this.all.elements[idx];
721        }
722        return nodeInfo;
723    },
724
725    /**
726     * Gets a range nodes.
727     * @param {Number} start (optional) The index of the first node in the range
728     * @param {Number} end (optional) The index of the last node in the range
729     * @return {Array} An array of nodes
730     */
731    getNodes : function(start, end){
732        var ns = this.all.elements,
733            nodes = [],
734            i;
735           
736        start = start || 0;
737        end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
738        if(start <= end){
739            for(i = start; i <= end && ns[i]; i++){
740                nodes.push(ns[i]);
741            }
742        } else{
743            for(i = start; i >= end && ns[i]; i--){
744                nodes.push(ns[i]);
745            }
746        }
747        return nodes;
748    },
749
750    /**
751     * Finds the index of the passed node.
752     * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
753     * or a record associated with a node.
754     * @return {Number} The index of the node or -1
755     */
756    indexOf : function(node){
757        node = this.getNode(node);
758        if(Ext.isNumber(node.viewIndex)){
759            return node.viewIndex;
760        }
761        return this.all.indexOf(node);
762    },
763
764    // private
765    onBeforeLoad : function(){
766        if(this.loadingText){
767            this.clearSelections(false, true);
768            this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
769            this.all.clear();
770        }
771    },
772
773    onDestroy : function(){
774        this.all.clear();
775        this.selected.clear();
776        Ext.DataView.superclass.onDestroy.call(this);
777        this.bindStore(null);
778    }
779});
780
781/**
782 * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
783 * @param {Store} store The store to bind to this view
784 */
785Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
786
787Ext.reg('dataview', Ext.DataView);
788/**
789 * @class Ext.list.ListView
790 * @extends Ext.DataView
791 * <p>Ext.list.ListView is a fast and light-weight implentation of a
792 * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>
793 * <div class="mdetail-params"><ul>
794 * <li>resizable columns</li>
795 * <li>selectable</li>
796 * <li>column widths are initially proportioned by percentage based on the container
797 * width and number of columns</li>
798 * <li>uses templates to render the data in any required format</li>
799 * <li>no horizontal scrolling</li>
800 * <li>no editing</li>
801 * </ul></div>
802 * <p>Example usage:</p>
803 * <pre><code>
804// consume JSON of this form:
805{
806   "images":[
807      {
808         "name":"dance_fever.jpg",
809         "size":2067,
810         "lastmod":1236974993000,
811         "url":"images\/thumbs\/dance_fever.jpg"
812      },
813      {
814         "name":"zack_sink.jpg",
815         "size":2303,
816         "lastmod":1236974993000,
817         "url":"images\/thumbs\/zack_sink.jpg"
818      }
819   ]
820}
821var store = new Ext.data.JsonStore({
822    url: 'get-images.php',
823    root: 'images',
824    fields: [
825        'name', 'url',
826        {name:'size', type: 'float'},
827        {name:'lastmod', type:'date', dateFormat:'timestamp'}
828    ]
829});
830store.load();
831
832var listView = new Ext.list.ListView({
833    store: store,
834    multiSelect: true,
835    emptyText: 'No images to display',
836    reserveScrollOffset: true,
837    columns: [{
838        header: 'File',
839        width: .5,
840        dataIndex: 'name'
841    },{
842        header: 'Last Modified',
843        width: .35,
844        dataIndex: 'lastmod',
845        tpl: '{lastmod:date("m-d h:i a")}'
846    },{
847        header: 'Size',
848        dataIndex: 'size',
849        tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()
850        align: 'right'
851    }]
852});
853
854// put it in a Panel so it looks pretty
855var panel = new Ext.Panel({
856    id:'images-view',
857    width:425,
858    height:250,
859    collapsible:true,
860    layout:'fit',
861    title:'Simple ListView <i>(0 items selected)</i>',
862    items: listView
863});
864panel.render(document.body);
865
866// little bit of feedback
867listView.on('selectionchange', function(view, nodes){
868    var l = nodes.length;
869    var s = l != 1 ? 's' : '';
870    panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
871});
872 * </code></pre>
873 * @constructor
874 * @param {Object} config
875 * @xtype listview
876 */
877Ext.list.ListView = Ext.extend(Ext.DataView, {
878    /**
879     * Set this property to <tt>true</tt> to disable the header click handler disabling sort
880     * (defaults to <tt>false</tt>).
881     * @type Boolean
882     * @property disableHeaders
883     */
884    /**
885     * @cfg {Boolean} hideHeaders
886     * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so
887     * the {@link #internalTpl header row} will be shown).
888     */
889    /**
890     * @cfg {String} itemSelector
891     * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.
892     * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)
893     * that will be used to determine what nodes the ListView will be working with.
894     */
895    itemSelector: 'dl',
896    /**
897     * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to
898     * <tt>'x-list-selected'</tt>). An example overriding the default styling:
899    <pre><code>
900    .x-list-selected {background-color: yellow;}
901    </code></pre>
902     * @type String
903     */
904    selectedClass:'x-list-selected',
905    /**
906     * @cfg {String} overClass The CSS class applied when over a row (defaults to
907     * <tt>'x-list-over'</tt>). An example overriding the default styling:
908    <pre><code>
909    .x-list-over {background-color: orange;}
910    </code></pre>
911     * @type String
912     */
913    overClass:'x-list-over',
914    /**
915     * @cfg {Boolean} reserveScrollOffset
916     * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>
917     * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured
918     * <b><tt>{@link #scrollOffset}</tt></b> immediately.
919     */
920    /**
921     * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to
922     * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
923     * calculated.
924     */
925    scrollOffset : undefined,
926    /**
927     * @cfg {Boolean/Object} columnResize
928     * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}
929     * to enable the columns to be resizable (defaults to <tt>true</tt>).
930     */
931    columnResize: true,
932    /**
933     * @cfg {Array} columns An array of column configuration objects, for example:
934     * <pre><code>
935{
936    align: 'right',
937    dataIndex: 'size',
938    header: 'Size',
939    tpl: '{size:fileSize}',
940    width: .35
941}
942     * </code></pre>
943     * Acceptable properties for each column configuration object are:
944     * <div class="mdetail-params"><ul>
945     * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property
946     * of the column. Defaults to <tt>'left'</tt>.</div></li>
947     * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
948     * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>
949     * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
950     * {@link Ext.grid.Column#header header} for details.</div></li>
951     * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the
952     * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
953     * will be implicitly created using the <tt>dataIndex</tt>.</div></li>
954     * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width
955     * this column should be allocated.  Columns that have no width specified will be
956     * allocated with an equal percentage to fill 100% of the container width.  To easily take
957     * advantage of the full container width, leave the width of at least one column undefined.
958     * Note that if you do not want to take up the full width of the container, the width of
959     * every column needs to be explicitly defined.</div></li>
960     * </ul></div>
961     */
962    /**
963     * @cfg {Boolean/Object} columnSort
964     * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}
965     * to enable the columns to be sortable (defaults to <tt>true</tt>).
966     */
967    columnSort: true,
968    /**
969     * @cfg {String/Array} internalTpl
970     * The template to be used for the header row.  See {@link #tpl} for more details.
971     */
972
973    /*
974     * IE has issues when setting percentage based widths to 100%. Default to 99.
975     */
976    maxColumnWidth: Ext.isIE ? 99 : 100,
977
978    initComponent : function(){
979        if(this.columnResize){
980            this.colResizer = new Ext.list.ColumnResizer(this.colResizer);
981            this.colResizer.init(this);
982        }
983        if(this.columnSort){
984            this.colSorter = new Ext.list.Sorter(this.columnSort);
985            this.colSorter.init(this);
986        }
987        if(!this.internalTpl){
988            this.internalTpl = new Ext.XTemplate(
989                '<div class="x-list-header"><div class="x-list-header-inner">',
990                    '<tpl for="columns">',
991                    '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',
992                        '{header}',
993                    '</em></div>',
994                    '</tpl>',
995                    '<div class="x-clear"></div>',
996                '</div></div>',
997                '<div class="x-list-body"><div class="x-list-body-inner">',
998                '</div></div>'
999            );
1000        }
1001        if(!this.tpl){
1002            this.tpl = new Ext.XTemplate(
1003                '<tpl for="rows">',
1004                    '<dl>',
1005                        '<tpl for="parent.columns">',
1006                        '<dt style="width:{[values.width*100]}%;text-align:{align};">',
1007                        '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',
1008                            '{[values.tpl.apply(parent)]}',
1009                        '</em></dt>',
1010                        '</tpl>',
1011                        '<div class="x-clear"></div>',
1012                    '</dl>',
1013                '</tpl>'
1014            );
1015        };
1016
1017        var cs = this.columns,
1018            allocatedWidth = 0,
1019            colsWithWidth = 0,
1020            len = cs.length,
1021            columns = [];
1022
1023        for(var i = 0; i < len; i++){
1024            var c = cs[i];
1025            if(!c.isColumn) {
1026                c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';
1027                c = Ext.create(c);
1028            }
1029            if(c.width) {
1030                allocatedWidth += c.width*100;
1031                if(allocatedWidth > this.maxColumnWidth){
1032                    c.width -= (allocatedWidth - this.maxColumnWidth) / 100;
1033                }
1034                colsWithWidth++;
1035            }
1036            columns.push(c);
1037        }
1038
1039        cs = this.columns = columns;
1040
1041        // auto calculate missing column widths
1042        if(colsWithWidth < len){
1043            var remaining = len - colsWithWidth;
1044            if(allocatedWidth < this.maxColumnWidth){
1045                var perCol = ((this.maxColumnWidth-allocatedWidth) / remaining)/100;
1046                for(var j = 0; j < len; j++){
1047                    var c = cs[j];
1048                    if(!c.width){
1049                        c.width = perCol;
1050                    }
1051                }
1052            }
1053        }
1054        Ext.list.ListView.superclass.initComponent.call(this);
1055    },
1056
1057    onRender : function(){
1058        this.autoEl = {
1059            cls: 'x-list-wrap'
1060        };
1061        Ext.list.ListView.superclass.onRender.apply(this, arguments);
1062
1063        this.internalTpl.overwrite(this.el, {columns: this.columns});
1064
1065        this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);
1066        this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);
1067
1068        if(this.hideHeaders){
1069            this.el.dom.firstChild.style.display = 'none';
1070        }
1071    },
1072
1073    getTemplateTarget : function(){
1074        return this.innerBody;
1075    },
1076
1077    /**
1078     * <p>Function which can be overridden which returns the data object passed to this
1079     * view's {@link #tpl template} to render the whole ListView. The returned object
1080     * shall contain the following properties:</p>
1081     * <div class="mdetail-params"><ul>
1082     * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>
1083     * <li><b>rows</b> : String<div class="sub-desc">See
1084     * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>
1085     * </ul></div>
1086     * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
1087     * @param {Number} startIndex the index number of the Record being prepared for rendering.
1088     * @return {Object} A data object containing properties to be processed by a repeating
1089     * XTemplate as described above.
1090     */
1091    collectData : function(){
1092        var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);
1093        return {
1094            columns: this.columns,
1095            rows: rs
1096        };
1097    },
1098
1099    verifyInternalSize : function(){
1100        if(this.lastSize){
1101            this.onResize(this.lastSize.width, this.lastSize.height);
1102        }
1103    },
1104
1105    // private
1106    onResize : function(w, h){
1107        var body = this.innerBody.dom,
1108            header = this.innerHd.dom,
1109            scrollWidth = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth()) + 'px',
1110            parentNode;
1111           
1112        if(!body){
1113            return;
1114        }
1115        parentNode = body.parentNode;
1116        if(Ext.isNumber(w)){
1117            if(this.reserveScrollOffset || ((parentNode.offsetWidth - parentNode.clientWidth) > 10)){
1118                body.style.width = scrollWidth;
1119                header.style.width = scrollWidth;
1120            }else{
1121                body.style.width = w + 'px';
1122                header.style.width = w + 'px';
1123                setTimeout(function(){
1124                    if((parentNode.offsetWidth - parentNode.clientWidth) > 10){
1125                        body.style.width = scrollWidth;
1126                        header.style.width = scrollWidth;
1127                    }
1128                }, 10);
1129            }
1130        }
1131        if(Ext.isNumber(h)){
1132            parentNode.style.height = Math.max(0, h - header.parentNode.offsetHeight) + 'px';
1133        }
1134    },
1135
1136    updateIndexes : function(){
1137        Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);
1138        this.verifyInternalSize();
1139    },
1140
1141    findHeaderIndex : function(header){
1142        header = header.dom || header;
1143        var parentNode = header.parentNode, 
1144            children = parentNode.parentNode.childNodes,
1145            i = 0,
1146            c;
1147        for(; c = children[i]; i++){
1148            if(c == parentNode){
1149                return i;
1150            }
1151        }
1152        return -1;
1153    },
1154
1155    setHdWidths : function(){
1156        var els = this.innerHd.dom.getElementsByTagName('div'),
1157            i = 0,
1158            columns = this.columns,
1159            len = columns.length;
1160           
1161        for(; i < len; i++){
1162            els[i].style.width = (columns[i].width*100) + '%';
1163        }
1164    }
1165});
1166
1167Ext.reg('listview', Ext.list.ListView);
1168
1169// Backwards compatibility alias
1170Ext.ListView = Ext.list.ListView;/**
1171 * @class Ext.list.Column
1172 * <p>This class encapsulates column configuration data to be used in the initialization of a
1173 * {@link Ext.list.ListView ListView}.</p>
1174 * <p>While subclasses are provided to render data in different ways, this class renders a passed
1175 * data field unchanged and is usually used for textual columns.</p>
1176 */
1177Ext.list.Column = Ext.extend(Object, {
1178    /**
1179     * @private
1180     * @cfg {Boolean} isColumn
1181     * Used by ListView constructor method to avoid reprocessing a Column
1182     * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column
1183     * Defaults to true.
1184     */
1185    isColumn: true,
1186   
1187    /**
1188     * @cfg {String} align
1189     * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.
1190     */       
1191    align: 'left',
1192    /**
1193     * @cfg {String} header Optional. The header text to be used as innerHTML
1194     * (html tags are accepted) to display in the ListView.  <b>Note</b>: to
1195     * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
1196     */   
1197    header: '',
1198   
1199    /**
1200     * @cfg {Number} width Optional. Percentage of the container width
1201     * this column should be allocated.  Columns that have no width specified will be
1202     * allocated with an equal percentage to fill 100% of the container width.  To easily take
1203     * advantage of the full container width, leave the width of at least one column undefined.
1204     * Note that if you do not want to take up the full width of the container, the width of
1205     * every column needs to be explicitly defined.
1206     */   
1207    width: null,
1208
1209    /**
1210     * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each
1211     * row for this column.
1212     */
1213    cls: '',
1214   
1215    /**
1216     * @cfg {String} tpl Optional. Specify a string to pass as the
1217     * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
1218     * will be implicitly created using the <tt>dataIndex</tt>.
1219     */
1220
1221    /**
1222     * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
1223     * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
1224     * which to draw the column's value.</p>
1225     */
1226   
1227    constructor : function(c){
1228        if(!c.tpl){
1229            c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');
1230        }
1231        else if(Ext.isString(c.tpl)){
1232            c.tpl = new Ext.XTemplate(c.tpl);
1233        }
1234       
1235        Ext.apply(this, c);
1236    }
1237});
1238
1239Ext.reg('lvcolumn', Ext.list.Column);
1240
1241/**
1242 * @class Ext.list.NumberColumn
1243 * @extends Ext.list.Column
1244 * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
1245 * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>
1246 */
1247Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {
1248    /**
1249     * @cfg {String} format
1250     * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
1251     * (defaults to <tt>'0,000.00'</tt>).
1252     */   
1253    format: '0,000.00',
1254   
1255    constructor : function(c) {
1256        c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');       
1257        Ext.list.NumberColumn.superclass.constructor.call(this, c);
1258    }
1259});
1260
1261Ext.reg('lvnumbercolumn', Ext.list.NumberColumn);
1262
1263/**
1264 * @class Ext.list.DateColumn
1265 * @extends Ext.list.Column
1266 * <p>A Column definition class which renders a passed date according to the default locale, or a configured
1267 * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}
1268 * for more details.</p>
1269 */
1270Ext.list.DateColumn = Ext.extend(Ext.list.Column, {
1271    format: 'm/d/Y',
1272    constructor : function(c) {
1273        c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');     
1274        Ext.list.DateColumn.superclass.constructor.call(this, c);
1275    }
1276});
1277Ext.reg('lvdatecolumn', Ext.list.DateColumn);
1278
1279/**
1280 * @class Ext.list.BooleanColumn
1281 * @extends Ext.list.Column
1282 * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}
1283 * config option of {@link Ext.list.Column} for more details.</p>
1284 */
1285Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {
1286    /**
1287     * @cfg {String} trueText
1288     * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
1289     */
1290    trueText: 'true',
1291    /**
1292     * @cfg {String} falseText
1293     * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
1294     * <tt>'false'</tt>).
1295     */
1296    falseText: 'false',
1297    /**
1298     * @cfg {String} undefinedText
1299     * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
1300     */
1301    undefinedText: '&#160;',
1302   
1303    constructor : function(c) {
1304        c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');
1305       
1306        var t = this.trueText, f = this.falseText, u = this.undefinedText;
1307        c.tpl.format = function(v){
1308            if(v === undefined){
1309                return u;
1310            }
1311            if(!v || v === 'false'){
1312                return f;
1313            }
1314            return t;
1315        };
1316       
1317        Ext.list.DateColumn.superclass.constructor.call(this, c);
1318    }
1319});
1320
1321Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**
1322 * @class Ext.list.ColumnResizer
1323 * @extends Ext.util.Observable
1324 * <p>Supporting Class for Ext.list.ListView</p>
1325 * @constructor
1326 * @param {Object} config
1327 */
1328Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {
1329    /**
1330     * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)
1331     */
1332    minPct: .05,
1333
1334    constructor: function(config){
1335        Ext.apply(this, config);
1336        Ext.list.ColumnResizer.superclass.constructor.call(this);
1337    },
1338    init : function(listView){
1339        this.view = listView;
1340        listView.on('render', this.initEvents, this);
1341    },
1342
1343    initEvents : function(view){
1344        view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);
1345        this.tracker = new Ext.dd.DragTracker({
1346            onBeforeStart: this.onBeforeStart.createDelegate(this),
1347            onStart: this.onStart.createDelegate(this),
1348            onDrag: this.onDrag.createDelegate(this),
1349            onEnd: this.onEnd.createDelegate(this),
1350            tolerance: 3,
1351            autoStart: 300
1352        });
1353        this.tracker.initEl(view.innerHd);
1354        view.on('beforedestroy', this.tracker.destroy, this.tracker);
1355    },
1356
1357    handleHdMove : function(e, t){
1358        var handleWidth = 5,
1359            x = e.getPageX(),
1360            header = e.getTarget('em', 3, true);
1361        if(header){
1362            var region = header.getRegion(),
1363                style = header.dom.style,
1364                parentNode = header.dom.parentNode;
1365
1366            if(x - region.left <= handleWidth && parentNode != parentNode.parentNode.firstChild){
1367                this.activeHd = Ext.get(parentNode.previousSibling.firstChild);
1368                style.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
1369            } else if(region.right - x <= handleWidth && parentNode != parentNode.parentNode.lastChild.previousSibling){
1370                this.activeHd = header;
1371                style.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
1372            } else{
1373                delete this.activeHd;
1374                style.cursor = '';
1375            }
1376        }
1377    },
1378
1379    onBeforeStart : function(e){
1380        this.dragHd = this.activeHd;
1381        return !!this.dragHd;
1382    },
1383
1384    onStart: function(e){
1385       
1386        var me = this,
1387            view = me.view,
1388            dragHeader = me.dragHd,
1389            x = me.tracker.getXY()[0];           
1390       
1391        me.proxy = view.el.createChild({cls:'x-list-resizer'});
1392        me.dragX = dragHeader.getX();
1393        me.headerIndex = view.findHeaderIndex(dragHeader);
1394       
1395        me.headersDisabled = view.disableHeaders;
1396        view.disableHeaders = true;
1397       
1398        me.proxy.setHeight(view.el.getHeight());
1399        me.proxy.setX(me.dragX);
1400        me.proxy.setWidth(x - me.dragX);
1401       
1402        this.setBoundaries();
1403       
1404    },
1405   
1406    // Sets up the boundaries for the drag/drop operation
1407    setBoundaries: function(relativeX){
1408        var view = this.view,
1409            headerIndex = this.headerIndex,
1410            width = view.innerHd.getWidth(),
1411            relativeX = view.innerHd.getX(),
1412            minWidth = Math.ceil(width * this.minPct),
1413            maxWidth = width - minWidth,
1414            numColumns = view.columns.length,
1415            headers = view.innerHd.select('em', true),
1416            minX = minWidth + relativeX,
1417            maxX = maxWidth + relativeX,
1418            header;
1419         
1420        if (numColumns == 2) {
1421            this.minX = minX;
1422            this.maxX = maxX;
1423        }else{
1424            header = headers.item(headerIndex + 2);
1425            this.minX = headers.item(headerIndex).getX() + minWidth;
1426            this.maxX = header ? header.getX() - minWidth : maxX;
1427            if (headerIndex == 0) {
1428                // First
1429                this.minX = minX;
1430            } else if (headerIndex == numColumns - 2) {
1431                // Last
1432                this.maxX = maxX;
1433            }
1434        }
1435    },
1436
1437    onDrag: function(e){
1438        var me = this,
1439            cursorX = me.tracker.getXY()[0].constrain(me.minX, me.maxX);
1440           
1441        me.proxy.setWidth(cursorX - this.dragX);
1442    },
1443
1444    onEnd: function(e){
1445        /* calculate desired width by measuring proxy and then remove it */
1446        var newWidth = this.proxy.getWidth(),
1447            index = this.headerIndex,
1448            view = this.view,
1449            columns = view.columns,
1450            width = view.innerHd.getWidth(),
1451            newPercent = Math.ceil(newWidth * view.maxColumnWidth / width) / 100,
1452            disabled = this.headersDisabled,
1453            headerCol = columns[index],
1454            otherCol = columns[index + 1],
1455            totalPercent = headerCol.width + otherCol.width;
1456
1457        this.proxy.remove();
1458
1459        headerCol.width = newPercent;
1460        otherCol.width = totalPercent - newPercent;
1461     
1462        delete this.dragHd;
1463        view.setHdWidths();
1464        view.refresh();
1465       
1466        setTimeout(function(){
1467            view.disableHeaders = disabled;
1468        }, 100);
1469    }
1470});
1471
1472// Backwards compatibility alias
1473Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**
1474 * @class Ext.list.Sorter
1475 * @extends Ext.util.Observable
1476 * <p>Supporting Class for Ext.list.ListView</p>
1477 * @constructor
1478 * @param {Object} config
1479 */
1480Ext.list.Sorter = Ext.extend(Ext.util.Observable, {
1481    /**
1482     * @cfg {Array} sortClasses
1483     * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
1484     */
1485    sortClasses : ["sort-asc", "sort-desc"],
1486
1487    constructor: function(config){
1488        Ext.apply(this, config);
1489        Ext.list.Sorter.superclass.constructor.call(this);
1490    },
1491
1492    init : function(listView){
1493        this.view = listView;
1494        listView.on('render', this.initEvents, this);
1495    },
1496
1497    initEvents : function(view){
1498        view.mon(view.innerHd, 'click', this.onHdClick, this);
1499        view.innerHd.setStyle('cursor', 'pointer');
1500        view.mon(view.store, 'datachanged', this.updateSortState, this);
1501        this.updateSortState.defer(10, this, [view.store]);
1502    },
1503
1504    updateSortState : function(store){
1505        var state = store.getSortState();
1506        if(!state){
1507            return;
1508        }
1509        this.sortState = state;
1510        var cs = this.view.columns, sortColumn = -1;
1511        for(var i = 0, len = cs.length; i < len; i++){
1512            if(cs[i].dataIndex == state.field){
1513                sortColumn = i;
1514                break;
1515            }
1516        }
1517        if(sortColumn != -1){
1518            var sortDir = state.direction;
1519            this.updateSortIcon(sortColumn, sortDir);
1520        }
1521    },
1522
1523    updateSortIcon : function(col, dir){
1524        var sc = this.sortClasses;
1525        var hds = this.view.innerHd.select('em').removeClass(sc);
1526        hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
1527    },
1528
1529    onHdClick : function(e){
1530        var hd = e.getTarget('em', 3);
1531        if(hd && !this.view.disableHeaders){
1532            var index = this.view.findHeaderIndex(hd);
1533            this.view.store.sort(this.view.columns[index].dataIndex);
1534        }
1535    }
1536});
1537
1538// Backwards compatibility alias
1539Ext.ListView.Sorter = Ext.list.Sorter;
Note: See TracBrowser for help on using the repository browser.