data-list-views-debug.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:51k
源码类别:

JavaScript

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.1.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ /**  * @class Ext.DataView  * @extends Ext.BoxComponent  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}  * config must be provided for the DataView to determine what nodes it will be working with.</b>  *  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>  * <pre><code> var store = new Ext.data.JsonStore({     url: 'get-images.php',     root: 'images',     fields: [         'name', 'url',         {name:'size', type: 'float'},         {name:'lastmod', type:'date', dateFormat:'timestamp'}     ] }); store.load(); var tpl = new Ext.XTemplate(     '&lt;tpl for="."&gt;',         '&lt;div class="thumb-wrap" id="{name}"&gt;',         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',     '&lt;/tpl&gt;',     '&lt;div class="x-clear"&gt;&lt;/div&gt;' ); var panel = new Ext.Panel({     id:'images-view',     frame:true,     width:535,     autoHeight:true,     collapsible:true,     layout:'fit',     title:'Simple DataView',     items: new Ext.DataView({         store: store,         tpl: tpl,         autoHeight:true,         multiSelect: true,         overClass:'x-view-over',         itemSelector:'div.thumb-wrap',         emptyText: 'No images to display'     }) }); panel.render(document.body); </code></pre>  * @constructor  * Create a new DataView  * @param {Object} config The config object  * @xtype dataview  */ Ext.DataView = Ext.extend(Ext.BoxComponent, {     /**      * @cfg {String/Array} tpl      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.      */     /**      * @cfg {Ext.data.Store} store      * The {@link Ext.data.Store} to bind this DataView to.      */     /**      * @cfg {String} itemSelector      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or       * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be      * working with.      */     /**      * @cfg {Boolean} multiSelect      * True to allow selection of more than one item at a time, false to allow selection of only a single item      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).      */     /**      * @cfg {Boolean} singleSelect      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).      * Note that if {@link #multiSelect} = true, this value will be ignored.      */     /**      * @cfg {Boolean} simpleSelect      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).      */     /**      * @cfg {String} overClass      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).      */     /**      * @cfg {String} loadingText      * A string to display during data load operations (defaults to undefined).  If specified, this text will be      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's      * contents will continue to display normally until the new data is loaded and the contents are replaced.      */     /**      * @cfg {String} selectedClass      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').      */     selectedClass : "x-view-selected",     /**      * @cfg {String} emptyText      * The text to display in the view when there is no data to display (defaults to '').      */     emptyText : "",     /**      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load      */     deferEmptyText: true,     /**      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events      */     trackOver: false,     //private     last: false,     // private     initComponent : function(){         Ext.DataView.superclass.initComponent.call(this);         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){             this.tpl = new Ext.XTemplate(this.tpl);         }         this.addEvents(             /**              * @event beforeclick              * Fires before a click is processed. Returns false to cancel the default action.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "beforeclick",             /**              * @event click              * Fires when a template node is clicked.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "click",             /**              * @event mouseenter              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "mouseenter",             /**              * @event mouseleave              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "mouseleave",             /**              * @event containerclick              * Fires when a click occurs and it is not on a template node.              * @param {Ext.DataView} this              * @param {Ext.EventObject} e The raw event object              */             "containerclick",             /**              * @event dblclick              * Fires when a template node is double clicked.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "dblclick",             /**              * @event contextmenu              * Fires when a template node is right clicked.              * @param {Ext.DataView} this              * @param {Number} index The index of the target node              * @param {HTMLElement} node The target node              * @param {Ext.EventObject} e The raw event object              */             "contextmenu",             /**              * @event containercontextmenu              * Fires when a right click occurs that is not on a template node.              * @param {Ext.DataView} this              * @param {Ext.EventObject} e The raw event object              */             "containercontextmenu",             /**              * @event selectionchange              * Fires when the selected nodes change.              * @param {Ext.DataView} this              * @param {Array} selections Array of the selected nodes              */             "selectionchange",             /**              * @event beforeselect              * Fires before a selection is made. If any handlers return false, the selection is cancelled.              * @param {Ext.DataView} this              * @param {HTMLElement} node The node to be selected              * @param {Array} selections Array of currently selected nodes              */             "beforeselect"         );         this.store = Ext.StoreMgr.lookup(this.store);         this.all = new Ext.CompositeElementLite();         this.selected = new Ext.CompositeElementLite();     },     // private     afterRender : function(){         Ext.DataView.superclass.afterRender.call(this); this.mon(this.getTemplateTarget(), {             "click": this.onClick,             "dblclick": this.onDblClick,             "contextmenu": this.onContextMenu,             scope:this         });         if(this.overClass || this.trackOver){             this.mon(this.getTemplateTarget(), {                 "mouseover": this.onMouseOver,                 "mouseout": this.onMouseOut,                 scope:this             });         }         if(this.store){             this.bindStore(this.store, true);         }     },     /**      * Refreshes the view by reloading the data from the store and re-rendering the template.      */     refresh : function(){         this.clearSelections(false, true);         var el = this.getTemplateTarget();         el.update("");         var records = this.store.getRange();         if(records.length < 1){             if(!this.deferEmptyText || this.hasSkippedEmptyText){                 el.update(this.emptyText);             }             this.all.clear();         }else{             this.tpl.overwrite(el, this.collectData(records, 0));             this.all.fill(Ext.query(this.itemSelector, el.dom));             this.updateIndexes(0);         }         this.hasSkippedEmptyText = true;     },     getTemplateTarget: function(){         return this.el;     },     /**      * Function which can be overridden to provide custom formatting for each Record that is used by this      * DataView's {@link #tpl template} to render each node.      * @param {Array/Object} data The raw data object that was used to create the Record.      * @param {Number} recordIndex the index number of the Record being prepared for rendering.      * @param {Record} record The Record being prepared for rendering.      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))      */     prepareData : function(data){         return data;     },     /**      * <p>Function which can be overridden which returns the data object passed to this      * DataView's {@link #tpl template} to render the whole DataView.</p>      * <p>This is usually an Array of data objects, each element of which is processed by an      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied      * data object as an Array. However, <i>named</i> properties may be placed into the data object to      * provide non-repeating data such as headings, totals etc.</p>      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.      * @param {Number} startIndex the index number of the Record being prepared for rendering.      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also      * contain <i>named</i> properties.      */     collectData : function(records, startIndex){         var r = [];         for(var i = 0, len = records.length; i < len; i++){             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);         }         return r;     },     // private     bufferRender : function(records){         var div = document.createElement('div');         this.tpl.overwrite(div, this.collectData(records));         return Ext.query(this.itemSelector, div);     },     // private     onUpdate : function(ds, record){         var index = this.store.indexOf(record);         if(index > -1){             var sel = this.isSelected(index);             var original = this.all.elements[index];             var node = this.bufferRender([record], index)[0];             this.all.replaceElement(index, node, true);             if(sel){                 this.selected.replaceElement(original, node);                 this.all.item(index).addClass(this.selectedClass);             }             this.updateIndexes(index, index);         }     },     // private     onAdd : function(ds, records, index){         if(this.all.getCount() === 0){             this.refresh();             return;         }         var nodes = this.bufferRender(records, index), n, a = this.all.elements;         if(index < this.all.getCount()){             n = this.all.item(index).insertSibling(nodes, 'before', true);             a.splice.apply(a, [index, 0].concat(nodes));         }else{             n = this.all.last().insertSibling(nodes, 'after', true);             a.push.apply(a, nodes);         }         this.updateIndexes(index);     },     // private     onRemove : function(ds, record, index){         this.deselect(index);         this.all.removeElement(index, true);         this.updateIndexes(index);         if (this.store.getCount() === 0){             this.refresh();         }     },     /**      * Refreshes an individual node's data from the store.      * @param {Number} index The item's data index in the store      */     refreshNode : function(index){         this.onUpdate(this.store, this.store.getAt(index));     },     // private     updateIndexes : function(startIndex, endIndex){         var ns = this.all.elements;         startIndex = startIndex || 0;         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));         for(var i = startIndex; i <= endIndex; i++){             ns[i].viewIndex = i;         }     },          /**      * Returns the store associated with this DataView.      * @return {Ext.data.Store} The store      */     getStore : function(){         return this.store;     },     /**      * Changes the data store bound to this view and refreshes it.      * @param {Store} store The store to bind to this view      */     bindStore : function(store, initial){         if(!initial && this.store){             if(store !== this.store && this.store.autoDestroy){                 this.store.destroy();             }else{                 this.store.un("beforeload", this.onBeforeLoad, this);                 this.store.un("datachanged", this.refresh, this);                 this.store.un("add", this.onAdd, this);                 this.store.un("remove", this.onRemove, this);                 this.store.un("update", this.onUpdate, this);                 this.store.un("clear", this.refresh, this);             }             if(!store){                 this.store = null;             }         }         if(store){             store = Ext.StoreMgr.lookup(store);             store.on({                 scope: this,                 beforeload: this.onBeforeLoad,                 datachanged: this.refresh,                 add: this.onAdd,                 remove: this.onRemove,                 update: this.onUpdate,                 clear: this.refresh             });         }         this.store = store;         if(store){             this.refresh();         }     },     /**      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.      * @param {HTMLElement} node      * @return {HTMLElement} The template node      */     findItemFromChild : function(node){         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());     },     // private     onClick : function(e){         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());         if(item){             var index = this.indexOf(item);             if(this.onItemClick(item, index, e) !== false){                 this.fireEvent("click", this, index, item, e);             }         }else{             if(this.fireEvent("containerclick", this, e) !== false){                 this.onContainerClick(e);             }         }     },     onContainerClick : function(e){         this.clearSelections();     },     // private     onContextMenu : function(e){         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());         if(item){             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);         }else{             this.fireEvent("containercontextmenu", this, e);         }     },     // private     onDblClick : function(e){         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());         if(item){             this.fireEvent("dblclick", this, this.indexOf(item), item, e);         }     },     // private     onMouseOver : function(e){         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());         if(item && item !== this.lastItem){             this.lastItem = item;             Ext.fly(item).addClass(this.overClass);             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);         }     },     // private     onMouseOut : function(e){         if(this.lastItem){             if(!e.within(this.lastItem, true, true)){                 Ext.fly(this.lastItem).removeClass(this.overClass);                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);                 delete this.lastItem;             }         }     },     // private     onItemClick : function(item, index, e){         if(this.fireEvent("beforeclick", this, index, item, e) === false){             return false;         }         if(this.multiSelect){             this.doMultiSelection(item, index, e);             e.preventDefault();         }else if(this.singleSelect){             this.doSingleSelection(item, index, e);             e.preventDefault();         }         return true;     },     // private     doSingleSelection : function(item, index, e){         if(e.ctrlKey && this.isSelected(index)){             this.deselect(index);         }else{             this.select(index, false);         }     },     // private     doMultiSelection : function(item, index, e){         if(e.shiftKey && this.last !== false){             var last = this.last;             this.selectRange(last, index, e.ctrlKey);             this.last = last; // reset the last         }else{             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){                 this.deselect(index);             }else{                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);             }         }     },     /**      * Gets the number of selected nodes.      * @return {Number} The node count      */     getSelectionCount : function(){         return this.selected.getCount();     },     /**      * Gets the currently selected nodes.      * @return {Array} An array of HTMLElements      */     getSelectedNodes : function(){         return this.selected.elements;     },     /**      * Gets the indexes of the selected nodes.      * @return {Array} An array of numeric indexes      */     getSelectedIndexes : function(){         var indexes = [], s = this.selected.elements;         for(var i = 0, len = s.length; i < len; i++){             indexes.push(s[i].viewIndex);         }         return indexes;     },     /**      * Gets an array of the selected records      * @return {Array} An array of {@link Ext.data.Record} objects      */     getSelectedRecords : function(){         var r = [], s = this.selected.elements;         for(var i = 0, len = s.length; i < len; i++){             r[r.length] = this.store.getAt(s[i].viewIndex);         }         return r;     },     /**      * Gets an array of the records from an array of nodes      * @param {Array} nodes The nodes to evaluate      * @return {Array} records The {@link Ext.data.Record} objects      */     getRecords : function(nodes){         var r = [], s = nodes;         for(var i = 0, len = s.length; i < len; i++){             r[r.length] = this.store.getAt(s[i].viewIndex);         }         return r;     },     /**      * Gets a record from a node      * @param {HTMLElement} node The node to evaluate      * @return {Record} record The {@link Ext.data.Record} object      */     getRecord : function(node){         return this.store.getAt(node.viewIndex);     },     /**      * Clears all selections.      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event      */     clearSelections : function(suppressEvent, skipUpdate){         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){             if(!skipUpdate){                 this.selected.removeClass(this.selectedClass);             }             this.selected.clear();             this.last = false;             if(!suppressEvent){                 this.fireEvent("selectionchange", this, this.selected.elements);             }         }     },     /**      * Returns true if the passed node is selected, else false.      * @param {HTMLElement/Number} node The node or node index to check      * @return {Boolean} True if selected, else false      */     isSelected : function(node){         return this.selected.contains(this.getNode(node));     },     /**      * Deselects a node.      * @param {HTMLElement/Number} node The node to deselect      */     deselect : function(node){         if(this.isSelected(node)){             node = this.getNode(node);             this.selected.removeElement(node);             if(this.last == node.viewIndex){                 this.last = false;             }             Ext.fly(node).removeClass(this.selectedClass);             this.fireEvent("selectionchange", this, this.selected.elements);         }     },     /**      * Selects a set of nodes.      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node,      * id of a template node or an array of any of those to select      * @param {Boolean} keepExisting (optional) true to keep existing selections      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent      */     select : function(nodeInfo, keepExisting, suppressEvent){         if(Ext.isArray(nodeInfo)){             if(!keepExisting){                 this.clearSelections(true);             }             for(var i = 0, len = nodeInfo.length; i < len; i++){                 this.select(nodeInfo[i], true, true);             }             if(!suppressEvent){                 this.fireEvent("selectionchange", this, this.selected.elements);             }         } else{             var node = this.getNode(nodeInfo);             if(!keepExisting){                 this.clearSelections(true);             }             if(node && !this.isSelected(node)){                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){                     Ext.fly(node).addClass(this.selectedClass);                     this.selected.add(node);                     this.last = node.viewIndex;                     if(!suppressEvent){                         this.fireEvent("selectionchange", this, this.selected.elements);                     }                 }             }         }     },     /**      * Selects a range of nodes. All nodes between start and end are selected.      * @param {Number} start The index of the first node in the range      * @param {Number} end The index of the last node in the range      * @param {Boolean} keepExisting (optional) True to retain existing selections      */     selectRange : function(start, end, keepExisting){         if(!keepExisting){             this.clearSelections(true);         }         this.select(this.getNodes(start, end), true);     },     /**      * Gets a template node.      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node      * @return {HTMLElement} The node or null if it wasn't found      */     getNode : function(nodeInfo){         if(Ext.isString(nodeInfo)){             return document.getElementById(nodeInfo);         }else if(Ext.isNumber(nodeInfo)){             return this.all.elements[nodeInfo];         }         return nodeInfo;     },     /**      * Gets a range nodes.      * @param {Number} start (optional) The index of the first node in the range      * @param {Number} end (optional) The index of the last node in the range      * @return {Array} An array of nodes      */     getNodes : function(start, end){         var ns = this.all.elements;         start = start || 0;         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;         var nodes = [], i;         if(start <= end){             for(i = start; i <= end && ns[i]; i++){                 nodes.push(ns[i]);             }         } else{             for(i = start; i >= end && ns[i]; i--){                 nodes.push(ns[i]);             }         }         return nodes;     },     /**      * Finds the index of the passed node.      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node      * @return {Number} The index of the node or -1      */     indexOf : function(node){         node = this.getNode(node);         if(Ext.isNumber(node.viewIndex)){             return node.viewIndex;         }         return this.all.indexOf(node);     },     // private     onBeforeLoad : function(){         if(this.loadingText){             this.clearSelections(false, true);             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');             this.all.clear();         }     },     onDestroy : function(){         this.all.clear();         this.selected.clear();         Ext.DataView.superclass.onDestroy.call(this);         this.bindStore(null);     } }); /**  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)  * @param {Store} store The store to bind to this view  */ Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore; Ext.reg('dataview', Ext.DataView); /**
  2.  * @class Ext.list.ListView
  3.  * @extends Ext.DataView
  4.  * <p>Ext.list.ListView is a fast and light-weight implentation of a
  5.  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>
  6.  * <div class="mdetail-params"><ul>
  7.  * <li>resizable columns</li>
  8.  * <li>selectable</li>
  9.  * <li>column widths are initially proportioned by percentage based on the container
  10.  * width and number of columns</li>
  11.  * <li>uses templates to render the data in any required format</li>
  12.  * <li>no horizontal scrolling</li>
  13.  * <li>no editing</li>
  14.  * </ul></div>
  15.  * <p>Example usage:</p>
  16.  * <pre><code>
  17. // consume JSON of this form:
  18. {
  19.    "images":[
  20.       {
  21.          "name":"dance_fever.jpg",
  22.          "size":2067,
  23.          "lastmod":1236974993000,
  24.          "url":"images/thumbs/dance_fever.jpg"
  25.       },
  26.       {
  27.          "name":"zack_sink.jpg",
  28.          "size":2303,
  29.          "lastmod":1236974993000,
  30.          "url":"images/thumbs/zack_sink.jpg"
  31.       }
  32.    ]
  33. var store = new Ext.data.JsonStore({
  34.     url: 'get-images.php',
  35.     root: 'images',
  36.     fields: [
  37.         'name', 'url',
  38.         {name:'size', type: 'float'},
  39.         {name:'lastmod', type:'date', dateFormat:'timestamp'}
  40.     ]
  41. });
  42. store.load();
  43. var listView = new Ext.list.ListView({
  44.     store: store,
  45.     multiSelect: true,
  46.     emptyText: 'No images to display',
  47.     reserveScrollOffset: true,
  48.     columns: [{
  49.         header: 'File',
  50.         width: .5,
  51.         dataIndex: 'name'
  52.     },{
  53.         header: 'Last Modified',
  54.         width: .35, 
  55.         dataIndex: 'lastmod',
  56.         tpl: '{lastmod:date("m-d h:i a")}'
  57.     },{
  58.         header: 'Size',
  59.         dataIndex: 'size',
  60.         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()
  61.         align: 'right'
  62.     }]
  63. });
  64. // put it in a Panel so it looks pretty
  65. var panel = new Ext.Panel({
  66.     id:'images-view',
  67.     width:425,
  68.     height:250,
  69.     collapsible:true,
  70.     layout:'fit',
  71.     title:'Simple ListView <i>(0 items selected)</i>',
  72.     items: listView
  73. });
  74. panel.render(document.body);
  75. // little bit of feedback
  76. listView.on('selectionchange', function(view, nodes){
  77.     var l = nodes.length;
  78.     var s = l != 1 ? 's' : '';
  79.     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
  80. });
  81.  * </code></pre>
  82.  * @constructor
  83.  * @param {Object} config
  84.  * @xtype listview
  85.  */
  86. Ext.list.ListView = Ext.extend(Ext.DataView, {
  87.     /**
  88.      * Set this property to <tt>true</tt> to disable the header click handler disabling sort
  89.      * (defaults to <tt>false</tt>).
  90.      * @type Boolean
  91.      * @property disableHeaders
  92.      */
  93.     /**
  94.      * @cfg {Boolean} hideHeaders
  95.      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so
  96.      * the {@link #internalTpl header row} will be shown).
  97.      */
  98.     /**
  99.      * @cfg {String} itemSelector
  100.      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.
  101.      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)
  102.      * that will be used to determine what nodes the ListView will be working with.   
  103.      */
  104.     itemSelector: 'dl',
  105.     /**
  106.      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to
  107.      * <tt>'x-list-selected'</tt>). An example overriding the default styling:
  108.     <pre><code>
  109.     .x-list-selected {background-color: yellow;}
  110.     </code></pre>
  111.      * @type String
  112.      */
  113.     selectedClass:'x-list-selected',
  114.     /**
  115.      * @cfg {String} overClass The CSS class applied when over a row (defaults to
  116.      * <tt>'x-list-over'</tt>). An example overriding the default styling:
  117.     <pre><code>
  118.     .x-list-over {background-color: orange;}
  119.     </code></pre>
  120.      * @type String
  121.      */
  122.     overClass:'x-list-over',
  123.     /**
  124.      * @cfg {Boolean} reserveScrollOffset
  125.      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>
  126.      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured
  127.      * <b><tt>{@link #scrollOffset}</tt></b> immediately.
  128.      */
  129.     /**
  130.      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to
  131.      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
  132.      * calculated.
  133.      */
  134.     scrollOffset : undefined,
  135.     /**
  136.      * @cfg {Boolean/Object} columnResize
  137.      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}
  138.      * to enable the columns to be resizable (defaults to <tt>true</tt>).
  139.      */
  140.     columnResize: true,
  141.     /**
  142.      * @cfg {Array} columns An array of column configuration objects, for example:
  143.      * <pre><code>
  144. {
  145.     align: 'right',
  146.     dataIndex: 'size',
  147.     header: 'Size',
  148.     tpl: '{size:fileSize}',
  149.     width: .35
  150. }
  151.      * </code></pre> 
  152.      * Acceptable properties for each column configuration object are:
  153.      * <div class="mdetail-params"><ul>
  154.      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property
  155.      * of the column. Defaults to <tt>'left'</tt>.</div></li>
  156.      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
  157.      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>
  158.      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
  159.      * {@link Ext.grid.Column#header header} for details.</div></li>
  160.      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the
  161.      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
  162.      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>
  163.      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width
  164.      * this column should be allocated.  Columns that have no width specified will be
  165.      * allocated with an equal percentage to fill 100% of the container width.  To easily take
  166.      * advantage of the full container width, leave the width of at least one column undefined.
  167.      * Note that if you do not want to take up the full width of the container, the width of
  168.      * every column needs to be explicitly defined.</div></li>
  169.      * </ul></div>
  170.      */
  171.     /**
  172.      * @cfg {Boolean/Object} columnSort
  173.      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}
  174.      * to enable the columns to be sortable (defaults to <tt>true</tt>).
  175.      */
  176.     columnSort: true,
  177.     /**
  178.      * @cfg {String/Array} internalTpl
  179.      * The template to be used for the header row.  See {@link #tpl} for more details.
  180.      */
  181.     /*
  182.      * IE has issues when setting percentage based widths to 100%. Default to 99.
  183.      */
  184.     maxWidth: Ext.isIE ? 99 : 100,
  185.     
  186.     initComponent : function(){
  187.         if(this.columnResize){
  188.             this.colResizer = new Ext.list.ColumnResizer(this.colResizer);
  189.             this.colResizer.init(this);
  190.         }
  191.         if(this.columnSort){
  192.             this.colSorter = new Ext.list.Sorter(this.columnSort);
  193.             this.colSorter.init(this);
  194.         }
  195.         if(!this.internalTpl){
  196.             this.internalTpl = new Ext.XTemplate(
  197.                 '<div class="x-list-header"><div class="x-list-header-inner">',
  198.                     '<tpl for="columns">',
  199.                     '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',
  200.                         '{header}',
  201.                     '</em></div>',
  202.                     '</tpl>',
  203.                     '<div class="x-clear"></div>',
  204.                 '</div></div>',
  205.                 '<div class="x-list-body"><div class="x-list-body-inner">',
  206.                 '</div></div>'
  207.             );
  208.         }
  209.         if(!this.tpl){
  210.             this.tpl = new Ext.XTemplate(
  211.                 '<tpl for="rows">',
  212.                     '<dl>',
  213.                         '<tpl for="parent.columns">',
  214.                         '<dt style="width:{[values.width*100]}%;text-align:{align};">',
  215.                         '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',
  216.                             '{[values.tpl.apply(parent)]}',
  217.                         '</em></dt>',
  218.                         '</tpl>',
  219.                         '<div class="x-clear"></div>',
  220.                     '</dl>',
  221.                 '</tpl>'
  222.             );
  223.         };
  224.         
  225.         var cs = this.columns, 
  226.             allocatedWidth = 0, 
  227.             colsWithWidth = 0, 
  228.             len = cs.length, 
  229.             columns = [];
  230.             
  231.         for(var i = 0; i < len; i++){
  232.             var c = cs[i];
  233.             if(!c.isColumn) {
  234.                 c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';
  235.                 c = Ext.create(c);
  236.             }
  237.             if(c.width) {
  238.                 allocatedWidth += c.width*100;
  239.                 colsWithWidth++;
  240.             }
  241.             columns.push(c);
  242.         }
  243.         
  244.         cs = this.columns = columns;
  245.         
  246.         // auto calculate missing column widths
  247.         if(colsWithWidth < len){
  248.             var remaining = len - colsWithWidth;
  249.             if(allocatedWidth < this.maxWidth){
  250.                 var perCol = ((this.maxWidth-allocatedWidth) / remaining)/100;
  251.                 for(var j = 0; j < len; j++){
  252.                     var c = cs[j];
  253.                     if(!c.width){
  254.                         c.width = perCol;
  255.                     }
  256.                 }
  257.             }
  258.         }
  259.         Ext.list.ListView.superclass.initComponent.call(this);
  260.     },
  261.     onRender : function(){
  262.         this.autoEl = {
  263.             cls: 'x-list-wrap'  
  264.         };
  265.         Ext.list.ListView.superclass.onRender.apply(this, arguments);
  266.         this.internalTpl.overwrite(this.el, {columns: this.columns});
  267.         
  268.         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);
  269.         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);
  270.         if(this.hideHeaders){
  271.             this.el.dom.firstChild.style.display = 'none';
  272.         }
  273.     },
  274.     getTemplateTarget : function(){
  275.         return this.innerBody;
  276.     },
  277.     /**
  278.      * <p>Function which can be overridden which returns the data object passed to this
  279.      * view's {@link #tpl template} to render the whole ListView. The returned object 
  280.      * shall contain the following properties:</p>
  281.      * <div class="mdetail-params"><ul>
  282.      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>
  283.      * <li><b>rows</b> : String<div class="sub-desc">See
  284.      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>
  285.      * </ul></div>
  286.      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
  287.      * @param {Number} startIndex the index number of the Record being prepared for rendering.
  288.      * @return {Object} A data object containing properties to be processed by a repeating
  289.      * XTemplate as described above.
  290.      */
  291.     collectData : function(){
  292.         var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);
  293.         return {
  294.             columns: this.columns,
  295.             rows: rs
  296.         }
  297.     },
  298.     verifyInternalSize : function(){
  299.         if(this.lastSize){
  300.             this.onResize(this.lastSize.width, this.lastSize.height);
  301.         }
  302.     },
  303.     // private
  304.     onResize : function(w, h){
  305.         var bd = this.innerBody.dom;
  306.         var hd = this.innerHd.dom
  307.         if(!bd){
  308.             return;
  309.         }
  310.         var bdp = bd.parentNode;
  311.         if(Ext.isNumber(w)){
  312.             var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
  313.             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){
  314.                 bd.style.width = sw + 'px';
  315.                 hd.style.width = sw + 'px';
  316.             }else{
  317.                 bd.style.width = w + 'px';
  318.                 hd.style.width = w + 'px';
  319.                 setTimeout(function(){
  320.                     if((bdp.offsetWidth - bdp.clientWidth) > 10){
  321.                         bd.style.width = sw + 'px';
  322.                         hd.style.width = sw + 'px';
  323.                     }
  324.                 }, 10);
  325.             }
  326.         }
  327.         if(Ext.isNumber(h)){
  328.             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';
  329.         }
  330.     },
  331.     updateIndexes : function(){
  332.         Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);
  333.         this.verifyInternalSize();
  334.     },
  335.     findHeaderIndex : function(hd){
  336.         hd = hd.dom || hd;
  337.         var pn = hd.parentNode, cs = pn.parentNode.childNodes;
  338.         for(var i = 0, c; c = cs[i]; i++){
  339.             if(c == pn){
  340.                 return i;
  341.             }
  342.         }
  343.         return -1;
  344.     },
  345.     setHdWidths : function(){
  346.         var els = this.innerHd.dom.getElementsByTagName('div');
  347.         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){
  348.             els[i].style.width = (cs[i].width*100) + '%';
  349.         }
  350.     }
  351. });
  352. Ext.reg('listview', Ext.list.ListView);
  353. // Backwards compatibility alias
  354. Ext.ListView = Ext.list.ListView;/**  * @class Ext.list.Column  * <p>This class encapsulates column configuration data to be used in the initialization of a  * {@link Ext.list.ListView ListView}.</p>  * <p>While subclasses are provided to render data in different ways, this class renders a passed  * data field unchanged and is usually used for textual columns.</p>  */ Ext.list.Column = Ext.extend(Object, {     /**      * @private      * @cfg {Boolean} isColumn      * Used by ListView constructor method to avoid reprocessing a Column      * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column      * Defaults to true.      */     isColumn: true,          /**      * @cfg {String} align      * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.      */             align: 'left',     /**      * @cfg {String} header Optional. The header text to be used as innerHTML      * (html tags are accepted) to display in the ListView.  <b>Note</b>: to      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.      */         header: '',          /**      * @cfg {Number} width Optional. Percentage of the container width      * this column should be allocated.  Columns that have no width specified will be      * allocated with an equal percentage to fill 100% of the container width.  To easily take      * advantage of the full container width, leave the width of at least one column undefined.      * Note that if you do not want to take up the full width of the container, the width of      * every column needs to be explicitly defined.      */         width: null,     /**      * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each      * row for this column.      */     cls: '',          /**      * @cfg {String} tpl Optional. Specify a string to pass as the      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}      * will be implicitly created using the <tt>dataIndex</tt>.      */     /**      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the      * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from      * which to draw the column's value.</p>      */          constructor : function(c){         if(!c.tpl){             c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');         }         else if(Ext.isString(c.tpl)){             c.tpl = new Ext.XTemplate(c.tpl);         }                  Ext.apply(this, c);     } }); Ext.reg('lvcolumn', Ext.list.Column); /**  * @class Ext.list.NumberColumn  * @extends Ext.list.Column  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the  * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>  */ Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {     /**      * @cfg {String} format      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column      * (defaults to <tt>'0,000.00'</tt>).      */         format: '0,000.00',          constructor : function(c) {         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');                Ext.list.NumberColumn.superclass.constructor.call(this, c);     } }); Ext.reg('lvnumbercolumn', Ext.list.NumberColumn); /**  * @class Ext.list.DateColumn  * @extends Ext.list.Column  * <p>A Column definition class which renders a passed date according to the default locale, or a configured  * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}  * for more details.</p>  */ Ext.list.DateColumn = Ext.extend(Ext.list.Column, {     format: 'm/d/Y',     constructor : function(c) {         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');               Ext.list.DateColumn.superclass.constructor.call(this, c);     } }); Ext.reg('lvdatecolumn', Ext.list.DateColumn); /**  * @class Ext.list.BooleanColumn  * @extends Ext.list.Column  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}  * config option of {@link Ext.list.Column} for more details.</p>  */ Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {     /**      * @cfg {String} trueText      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).      */     trueText: 'true',     /**      * @cfg {String} falseText      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to      * <tt>'false'</tt>).      */     falseText: 'false',     /**      * @cfg {String} undefinedText      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).      */     undefinedText: '&#160;',          constructor : function(c) {         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');                  var t = this.trueText, f = this.falseText, u = this.undefinedText;         c.tpl.format = function(v){             if(v === undefined){                 return u;             }             if(!v || v === 'false'){                 return f;             }             return t;         };                  Ext.list.DateColumn.superclass.constructor.call(this, c);     } }); Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**
  355.  * @class Ext.list.ColumnResizer
  356.  * @extends Ext.util.Observable
  357.  * <p>Supporting Class for Ext.list.ListView</p>
  358.  * @constructor
  359.  * @param {Object} config
  360.  */
  361. Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {
  362.     /**
  363.      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)
  364.      */
  365.     minPct: .05,
  366.     constructor: function(config){
  367.         Ext.apply(this, config);
  368.         Ext.list.ColumnResizer.superclass.constructor.call(this);
  369.     },
  370.     init : function(listView){
  371.         this.view = listView;
  372.         listView.on('render', this.initEvents, this);
  373.     },
  374.     initEvents : function(view){
  375.         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);
  376.         this.tracker = new Ext.dd.DragTracker({
  377.             onBeforeStart: this.onBeforeStart.createDelegate(this),
  378.             onStart: this.onStart.createDelegate(this),
  379.             onDrag: this.onDrag.createDelegate(this),
  380.             onEnd: this.onEnd.createDelegate(this),
  381.             tolerance: 3,
  382.             autoStart: 300
  383.         });
  384.         this.tracker.initEl(view.innerHd);
  385.         view.on('beforedestroy', this.tracker.destroy, this.tracker);
  386.     },
  387.     handleHdMove : function(e, t){
  388.         var hw = 5,
  389.             x = e.getPageX(),
  390.             hd = e.getTarget('em', 3, true);
  391.         if(hd){
  392.             var r = hd.getRegion(),
  393.                 ss = hd.dom.style,
  394.                 pn = hd.dom.parentNode;
  395.             if(x - r.left <= hw && pn != pn.parentNode.firstChild){
  396.                 this.activeHd = Ext.get(pn.previousSibling.firstChild);
  397.                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
  398.             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){
  399.                 this.activeHd = hd;
  400.                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
  401.             } else{
  402.                 delete this.activeHd;
  403.                 ss.cursor = '';
  404.             }
  405.         }
  406.     },
  407.     onBeforeStart : function(e){
  408.         this.dragHd = this.activeHd;
  409.         return !!this.dragHd;
  410.     },
  411.     onStart: function(e){
  412.         this.view.disableHeaders = true;
  413.         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});
  414.         this.proxy.setHeight(this.view.el.getHeight());
  415.         var x = this.tracker.getXY()[0],
  416.             w = this.view.innerHd.getWidth();
  417.         this.hdX = this.dragHd.getX();
  418.         this.hdIndex = this.view.findHeaderIndex(this.dragHd);
  419.         this.proxy.setX(this.hdX);
  420.         this.proxy.setWidth(x-this.hdX);
  421.         this.minWidth = w*this.minPct;
  422.         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));
  423.     },
  424.     onDrag: function(e){
  425.         var cursorX = this.tracker.getXY()[0];
  426.         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));
  427.     },
  428.     onEnd: function(e){
  429.         /* calculate desired width by measuring proxy and then remove it */
  430.         var nw = this.proxy.getWidth();
  431.         this.proxy.remove();
  432.         var index = this.hdIndex,
  433.             vw = this.view,
  434.             cs = vw.columns,
  435.             len = cs.length,
  436.             w = this.view.innerHd.getWidth(),
  437.             minPct = this.minPct * 100,
  438.             pct = Math.ceil((nw * vw.maxWidth) / w),
  439.             diff = (cs[index].width * 100) - pct,
  440.             each = Math.floor(diff / (len-1-index)),
  441.             mod = diff - (each * (len-1-index));
  442.         for(var i = index+1; i < len; i++){
  443.             var cw = (cs[i].width * 100) + each,
  444.                 ncw = Math.max(minPct, cw);
  445.             if(cw != ncw){
  446.                 mod += cw - ncw;
  447.             }
  448.             cs[i].width = ncw / 100;
  449.         }
  450.         cs[index].width = pct / 100;
  451.         cs[index+1].width += (mod / 100);
  452.         delete this.dragHd;
  453.         vw.setHdWidths();
  454.         vw.refresh();
  455.         setTimeout(function(){
  456.             vw.disableHeaders = false;
  457.         }, 100);
  458.     }
  459. });
  460. // Backwards compatibility alias
  461. Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**
  462.  * @class Ext.list.Sorter
  463.  * @extends Ext.util.Observable
  464.  * <p>Supporting Class for Ext.list.ListView</p>
  465.  * @constructor
  466.  * @param {Object} config
  467.  */
  468. Ext.list.Sorter = Ext.extend(Ext.util.Observable, {
  469.     /**
  470.      * @cfg {Array} sortClasses
  471.      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
  472.      */
  473.     sortClasses : ["sort-asc", "sort-desc"],
  474.     constructor: function(config){
  475.         Ext.apply(this, config);
  476.         Ext.list.Sorter.superclass.constructor.call(this);
  477.     },
  478.     init : function(listView){
  479.         this.view = listView;
  480.         listView.on('render', this.initEvents, this);
  481.     },
  482.     initEvents : function(view){
  483.         view.mon(view.innerHd, 'click', this.onHdClick, this);
  484.         view.innerHd.setStyle('cursor', 'pointer');
  485.         view.mon(view.store, 'datachanged', this.updateSortState, this);
  486.         this.updateSortState.defer(10, this, [view.store]);
  487.     },
  488.     updateSortState : function(store){
  489.         var state = store.getSortState();
  490.         if(!state){
  491.             return;
  492.         }
  493.         this.sortState = state;
  494.         var cs = this.view.columns, sortColumn = -1;
  495.         for(var i = 0, len = cs.length; i < len; i++){
  496.             if(cs[i].dataIndex == state.field){
  497.                 sortColumn = i;
  498.                 break;
  499.             }
  500.         }
  501.         if(sortColumn != -1){
  502.             var sortDir = state.direction;
  503.             this.updateSortIcon(sortColumn, sortDir);
  504.         }
  505.     },
  506.     updateSortIcon : function(col, dir){
  507.         var sc = this.sortClasses;
  508.         var hds = this.view.innerHd.select('em').removeClass(sc);
  509.         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
  510.     },
  511.     onHdClick : function(e){
  512.         var hd = e.getTarget('em', 3);
  513.         if(hd && !this.view.disableHeaders){
  514.             var index = this.view.findHeaderIndex(hd);
  515.             this.view.store.sort(this.view.columns[index].dataIndex);
  516.         }
  517.     }
  518. });
  519. // Backwards compatibility alias
  520. Ext.ListView.Sorter = Ext.list.Sorter;