[76] | 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.list.ListView |
---|
| 9 | * @extends Ext.DataView |
---|
| 10 | * <p>Ext.list.ListView is a fast and light-weight implentation of a |
---|
| 11 | * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p> |
---|
| 12 | * <div class="mdetail-params"><ul> |
---|
| 13 | * <li>resizable columns</li> |
---|
| 14 | * <li>selectable</li> |
---|
| 15 | * <li>column widths are initially proportioned by percentage based on the container |
---|
| 16 | * width and number of columns</li> |
---|
| 17 | * <li>uses templates to render the data in any required format</li> |
---|
| 18 | * <li>no horizontal scrolling</li> |
---|
| 19 | * <li>no editing</li> |
---|
| 20 | * </ul></div> |
---|
| 21 | * <p>Example usage:</p> |
---|
| 22 | * <pre><code> |
---|
| 23 | // consume JSON of this form: |
---|
| 24 | { |
---|
| 25 | "images":[ |
---|
| 26 | { |
---|
| 27 | "name":"dance_fever.jpg", |
---|
| 28 | "size":2067, |
---|
| 29 | "lastmod":1236974993000, |
---|
| 30 | "url":"images\/thumbs\/dance_fever.jpg" |
---|
| 31 | }, |
---|
| 32 | { |
---|
| 33 | "name":"zack_sink.jpg", |
---|
| 34 | "size":2303, |
---|
| 35 | "lastmod":1236974993000, |
---|
| 36 | "url":"images\/thumbs\/zack_sink.jpg" |
---|
| 37 | } |
---|
| 38 | ] |
---|
| 39 | } |
---|
| 40 | var store = new Ext.data.JsonStore({ |
---|
| 41 | url: 'get-images.php', |
---|
| 42 | root: 'images', |
---|
| 43 | fields: [ |
---|
| 44 | 'name', 'url', |
---|
| 45 | {name:'size', type: 'float'}, |
---|
| 46 | {name:'lastmod', type:'date', dateFormat:'timestamp'} |
---|
| 47 | ] |
---|
| 48 | }); |
---|
| 49 | store.load(); |
---|
| 50 | |
---|
| 51 | var listView = new Ext.list.ListView({ |
---|
| 52 | store: store, |
---|
| 53 | multiSelect: true, |
---|
| 54 | emptyText: 'No images to display', |
---|
| 55 | reserveScrollOffset: true, |
---|
| 56 | columns: [{ |
---|
| 57 | header: 'File', |
---|
| 58 | width: .5, |
---|
| 59 | dataIndex: 'name' |
---|
| 60 | },{ |
---|
| 61 | header: 'Last Modified', |
---|
| 62 | width: .35, |
---|
| 63 | dataIndex: 'lastmod', |
---|
| 64 | tpl: '{lastmod:date("m-d h:i a")}' |
---|
| 65 | },{ |
---|
| 66 | header: 'Size', |
---|
| 67 | dataIndex: 'size', |
---|
| 68 | tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize() |
---|
| 69 | align: 'right' |
---|
| 70 | }] |
---|
| 71 | }); |
---|
| 72 | |
---|
| 73 | // put it in a Panel so it looks pretty |
---|
| 74 | var panel = new Ext.Panel({ |
---|
| 75 | id:'images-view', |
---|
| 76 | width:425, |
---|
| 77 | height:250, |
---|
| 78 | collapsible:true, |
---|
| 79 | layout:'fit', |
---|
| 80 | title:'Simple ListView <i>(0 items selected)</i>', |
---|
| 81 | items: listView |
---|
| 82 | }); |
---|
| 83 | panel.render(document.body); |
---|
| 84 | |
---|
| 85 | // little bit of feedback |
---|
| 86 | listView.on('selectionchange', function(view, nodes){ |
---|
| 87 | var l = nodes.length; |
---|
| 88 | var s = l != 1 ? 's' : ''; |
---|
| 89 | panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>'); |
---|
| 90 | }); |
---|
| 91 | * </code></pre> |
---|
| 92 | * @constructor |
---|
| 93 | * @param {Object} config |
---|
| 94 | * @xtype listview |
---|
| 95 | */ |
---|
| 96 | Ext.list.ListView = Ext.extend(Ext.DataView, { |
---|
| 97 | /** |
---|
| 98 | * Set this property to <tt>true</tt> to disable the header click handler disabling sort |
---|
| 99 | * (defaults to <tt>false</tt>). |
---|
| 100 | * @type Boolean |
---|
| 101 | * @property disableHeaders |
---|
| 102 | */ |
---|
| 103 | /** |
---|
| 104 | * @cfg {Boolean} hideHeaders |
---|
| 105 | * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so |
---|
| 106 | * the {@link #internalTpl header row} will be shown). |
---|
| 107 | */ |
---|
| 108 | /** |
---|
| 109 | * @cfg {String} itemSelector |
---|
| 110 | * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>. |
---|
| 111 | * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>) |
---|
| 112 | * that will be used to determine what nodes the ListView will be working with. |
---|
| 113 | */ |
---|
| 114 | itemSelector: 'dl', |
---|
| 115 | /** |
---|
| 116 | * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to |
---|
| 117 | * <tt>'x-list-selected'</tt>). An example overriding the default styling: |
---|
| 118 | <pre><code> |
---|
| 119 | .x-list-selected {background-color: yellow;} |
---|
| 120 | </code></pre> |
---|
| 121 | * @type String |
---|
| 122 | */ |
---|
| 123 | selectedClass:'x-list-selected', |
---|
| 124 | /** |
---|
| 125 | * @cfg {String} overClass The CSS class applied when over a row (defaults to |
---|
| 126 | * <tt>'x-list-over'</tt>). An example overriding the default styling: |
---|
| 127 | <pre><code> |
---|
| 128 | .x-list-over {background-color: orange;} |
---|
| 129 | </code></pre> |
---|
| 130 | * @type String |
---|
| 131 | */ |
---|
| 132 | overClass:'x-list-over', |
---|
| 133 | /** |
---|
| 134 | * @cfg {Boolean} reserveScrollOffset |
---|
| 135 | * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b> |
---|
| 136 | * for 10 milliseconds. Specify <tt>true</tt> to account for the configured |
---|
| 137 | * <b><tt>{@link #scrollOffset}</tt></b> immediately. |
---|
| 138 | */ |
---|
| 139 | /** |
---|
| 140 | * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to |
---|
| 141 | * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically |
---|
| 142 | * calculated. |
---|
| 143 | */ |
---|
| 144 | scrollOffset : undefined, |
---|
| 145 | /** |
---|
| 146 | * @cfg {Boolean/Object} columnResize |
---|
| 147 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer} |
---|
| 148 | * to enable the columns to be resizable (defaults to <tt>true</tt>). |
---|
| 149 | */ |
---|
| 150 | columnResize: true, |
---|
| 151 | /** |
---|
| 152 | * @cfg {Array} columns An array of column configuration objects, for example: |
---|
| 153 | * <pre><code> |
---|
| 154 | { |
---|
| 155 | align: 'right', |
---|
| 156 | dataIndex: 'size', |
---|
| 157 | header: 'Size', |
---|
| 158 | tpl: '{size:fileSize}', |
---|
| 159 | width: .35 |
---|
| 160 | } |
---|
| 161 | * </code></pre> |
---|
| 162 | * Acceptable properties for each column configuration object are: |
---|
| 163 | * <div class="mdetail-params"><ul> |
---|
| 164 | * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property |
---|
| 165 | * of the column. Defaults to <tt>'left'</tt>.</div></li> |
---|
| 166 | * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
| 167 | * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li> |
---|
| 168 | * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
| 169 | * {@link Ext.grid.Column#header header} for details.</div></li> |
---|
| 170 | * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the |
---|
| 171 | * configuration string for {@link Ext.XTemplate}. By default an {@link Ext.XTemplate} |
---|
| 172 | * will be implicitly created using the <tt>dataIndex</tt>.</div></li> |
---|
| 173 | * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width |
---|
| 174 | * this column should be allocated. Columns that have no width specified will be |
---|
| 175 | * allocated with an equal percentage to fill 100% of the container width. To easily take |
---|
| 176 | * advantage of the full container width, leave the width of at least one column undefined. |
---|
| 177 | * Note that if you do not want to take up the full width of the container, the width of |
---|
| 178 | * every column needs to be explicitly defined.</div></li> |
---|
| 179 | * </ul></div> |
---|
| 180 | */ |
---|
| 181 | /** |
---|
| 182 | * @cfg {Boolean/Object} columnSort |
---|
| 183 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter} |
---|
| 184 | * to enable the columns to be sortable (defaults to <tt>true</tt>). |
---|
| 185 | */ |
---|
| 186 | columnSort: true, |
---|
| 187 | /** |
---|
| 188 | * @cfg {String/Array} internalTpl |
---|
| 189 | * The template to be used for the header row. See {@link #tpl} for more details. |
---|
| 190 | */ |
---|
| 191 | |
---|
| 192 | /* |
---|
| 193 | * IE has issues when setting percentage based widths to 100%. Default to 99. |
---|
| 194 | */ |
---|
| 195 | maxColumnWidth: Ext.isIE ? 99 : 100, |
---|
| 196 | |
---|
| 197 | initComponent : function(){ |
---|
| 198 | if(this.columnResize){ |
---|
| 199 | this.colResizer = new Ext.list.ColumnResizer(this.colResizer); |
---|
| 200 | this.colResizer.init(this); |
---|
| 201 | } |
---|
| 202 | if(this.columnSort){ |
---|
| 203 | this.colSorter = new Ext.list.Sorter(this.columnSort); |
---|
| 204 | this.colSorter.init(this); |
---|
| 205 | } |
---|
| 206 | if(!this.internalTpl){ |
---|
| 207 | this.internalTpl = new Ext.XTemplate( |
---|
| 208 | '<div class="x-list-header"><div class="x-list-header-inner">', |
---|
| 209 | '<tpl for="columns">', |
---|
| 210 | '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">', |
---|
| 211 | '{header}', |
---|
| 212 | '</em></div>', |
---|
| 213 | '</tpl>', |
---|
| 214 | '<div class="x-clear"></div>', |
---|
| 215 | '</div></div>', |
---|
| 216 | '<div class="x-list-body"><div class="x-list-body-inner">', |
---|
| 217 | '</div></div>' |
---|
| 218 | ); |
---|
| 219 | } |
---|
| 220 | if(!this.tpl){ |
---|
| 221 | this.tpl = new Ext.XTemplate( |
---|
| 222 | '<tpl for="rows">', |
---|
| 223 | '<dl>', |
---|
| 224 | '<tpl for="parent.columns">', |
---|
| 225 | '<dt style="width:{[values.width*100]}%;text-align:{align};">', |
---|
| 226 | '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">', |
---|
| 227 | '{[values.tpl.apply(parent)]}', |
---|
| 228 | '</em></dt>', |
---|
| 229 | '</tpl>', |
---|
| 230 | '<div class="x-clear"></div>', |
---|
| 231 | '</dl>', |
---|
| 232 | '</tpl>' |
---|
| 233 | ); |
---|
| 234 | }; |
---|
| 235 | |
---|
| 236 | var cs = this.columns, |
---|
| 237 | allocatedWidth = 0, |
---|
| 238 | colsWithWidth = 0, |
---|
| 239 | len = cs.length, |
---|
| 240 | columns = []; |
---|
| 241 | |
---|
| 242 | for(var i = 0; i < len; i++){ |
---|
| 243 | var c = cs[i]; |
---|
| 244 | if(!c.isColumn) { |
---|
| 245 | c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn'; |
---|
| 246 | c = Ext.create(c); |
---|
| 247 | } |
---|
| 248 | if(c.width) { |
---|
| 249 | allocatedWidth += c.width*100; |
---|
| 250 | if(allocatedWidth > this.maxColumnWidth){ |
---|
| 251 | c.width -= (allocatedWidth - this.maxColumnWidth) / 100; |
---|
| 252 | } |
---|
| 253 | colsWithWidth++; |
---|
| 254 | } |
---|
| 255 | columns.push(c); |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | cs = this.columns = columns; |
---|
| 259 | |
---|
| 260 | // auto calculate missing column widths |
---|
| 261 | if(colsWithWidth < len){ |
---|
| 262 | var remaining = len - colsWithWidth; |
---|
| 263 | if(allocatedWidth < this.maxColumnWidth){ |
---|
| 264 | var perCol = ((this.maxColumnWidth-allocatedWidth) / remaining)/100; |
---|
| 265 | for(var j = 0; j < len; j++){ |
---|
| 266 | var c = cs[j]; |
---|
| 267 | if(!c.width){ |
---|
| 268 | c.width = perCol; |
---|
| 269 | } |
---|
| 270 | } |
---|
| 271 | } |
---|
| 272 | } |
---|
| 273 | Ext.list.ListView.superclass.initComponent.call(this); |
---|
| 274 | }, |
---|
| 275 | |
---|
| 276 | onRender : function(){ |
---|
| 277 | this.autoEl = { |
---|
| 278 | cls: 'x-list-wrap' |
---|
| 279 | }; |
---|
| 280 | Ext.list.ListView.superclass.onRender.apply(this, arguments); |
---|
| 281 | |
---|
| 282 | this.internalTpl.overwrite(this.el, {columns: this.columns}); |
---|
| 283 | |
---|
| 284 | this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild); |
---|
| 285 | this.innerHd = Ext.get(this.el.dom.firstChild.firstChild); |
---|
| 286 | |
---|
| 287 | if(this.hideHeaders){ |
---|
| 288 | this.el.dom.firstChild.style.display = 'none'; |
---|
| 289 | } |
---|
| 290 | }, |
---|
| 291 | |
---|
| 292 | getTemplateTarget : function(){ |
---|
| 293 | return this.innerBody; |
---|
| 294 | }, |
---|
| 295 | |
---|
| 296 | /** |
---|
| 297 | * <p>Function which can be overridden which returns the data object passed to this |
---|
| 298 | * view's {@link #tpl template} to render the whole ListView. The returned object |
---|
| 299 | * shall contain the following properties:</p> |
---|
| 300 | * <div class="mdetail-params"><ul> |
---|
| 301 | * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li> |
---|
| 302 | * <li><b>rows</b> : String<div class="sub-desc">See |
---|
| 303 | * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li> |
---|
| 304 | * </ul></div> |
---|
| 305 | * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView. |
---|
| 306 | * @param {Number} startIndex the index number of the Record being prepared for rendering. |
---|
| 307 | * @return {Object} A data object containing properties to be processed by a repeating |
---|
| 308 | * XTemplate as described above. |
---|
| 309 | */ |
---|
| 310 | collectData : function(){ |
---|
| 311 | var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments); |
---|
| 312 | return { |
---|
| 313 | columns: this.columns, |
---|
| 314 | rows: rs |
---|
| 315 | }; |
---|
| 316 | }, |
---|
| 317 | |
---|
| 318 | verifyInternalSize : function(){ |
---|
| 319 | if(this.lastSize){ |
---|
| 320 | this.onResize(this.lastSize.width, this.lastSize.height); |
---|
| 321 | } |
---|
| 322 | }, |
---|
| 323 | |
---|
| 324 | // private |
---|
| 325 | onResize : function(w, h){ |
---|
| 326 | var body = this.innerBody.dom, |
---|
| 327 | header = this.innerHd.dom, |
---|
| 328 | scrollWidth = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth()) + 'px', |
---|
| 329 | parentNode; |
---|
| 330 | |
---|
| 331 | if(!body){ |
---|
| 332 | return; |
---|
| 333 | } |
---|
| 334 | parentNode = body.parentNode; |
---|
| 335 | if(Ext.isNumber(w)){ |
---|
| 336 | if(this.reserveScrollOffset || ((parentNode.offsetWidth - parentNode.clientWidth) > 10)){ |
---|
| 337 | body.style.width = scrollWidth; |
---|
| 338 | header.style.width = scrollWidth; |
---|
| 339 | }else{ |
---|
| 340 | body.style.width = w + 'px'; |
---|
| 341 | header.style.width = w + 'px'; |
---|
| 342 | setTimeout(function(){ |
---|
| 343 | if((parentNode.offsetWidth - parentNode.clientWidth) > 10){ |
---|
| 344 | body.style.width = scrollWidth; |
---|
| 345 | header.style.width = scrollWidth; |
---|
| 346 | } |
---|
| 347 | }, 10); |
---|
| 348 | } |
---|
| 349 | } |
---|
| 350 | if(Ext.isNumber(h)){ |
---|
| 351 | parentNode.style.height = Math.max(0, h - header.parentNode.offsetHeight) + 'px'; |
---|
| 352 | } |
---|
| 353 | }, |
---|
| 354 | |
---|
| 355 | updateIndexes : function(){ |
---|
| 356 | Ext.list.ListView.superclass.updateIndexes.apply(this, arguments); |
---|
| 357 | this.verifyInternalSize(); |
---|
| 358 | }, |
---|
| 359 | |
---|
| 360 | findHeaderIndex : function(header){ |
---|
| 361 | header = header.dom || header; |
---|
| 362 | var parentNode = header.parentNode, |
---|
| 363 | children = parentNode.parentNode.childNodes, |
---|
| 364 | i = 0, |
---|
| 365 | c; |
---|
| 366 | for(; c = children[i]; i++){ |
---|
| 367 | if(c == parentNode){ |
---|
| 368 | return i; |
---|
| 369 | } |
---|
| 370 | } |
---|
| 371 | return -1; |
---|
| 372 | }, |
---|
| 373 | |
---|
| 374 | setHdWidths : function(){ |
---|
| 375 | var els = this.innerHd.dom.getElementsByTagName('div'), |
---|
| 376 | i = 0, |
---|
| 377 | columns = this.columns, |
---|
| 378 | len = columns.length; |
---|
| 379 | |
---|
| 380 | for(; i < len; i++){ |
---|
| 381 | els[i].style.width = (columns[i].width*100) + '%'; |
---|
| 382 | } |
---|
| 383 | } |
---|
| 384 | }); |
---|
| 385 | |
---|
| 386 | Ext.reg('listview', Ext.list.ListView); |
---|
| 387 | |
---|
| 388 | // Backwards compatibility alias |
---|
| 389 | Ext.ListView = Ext.list.ListView; |
---|