Changeset 11
- Timestamp:
- 08/29/08 17:11:23 (4 years ago)
- Location:
- trunk
- Files:
-
- 4 modified
-
CHANGELOG (modified) (1 diff)
-
src/BufferedGridView.js (modified) (14 diffs)
-
src/BufferedRowSelectionModel.js (modified) (1 diff)
-
src/BufferedStore.js (modified) (26 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/CHANGELOG
r10 r11 1 1 Version 0.2rc1 2 29-August-2008 3 2 4 - fixes: 5 - BufferedGridView.js: fixed a bug that would not recalculate the scrollbar's 6 height if the number of possible rows to display in the view would exceed the 7 total number of records in the store 3 8 - BufferedRowSelectionModel: "selectRow()" would allow selecting indexes greater than 4 9 the "totalLength"-property of the store; added condition to check whether the index is 5 10 out of bounds (closes google issue 5) 11 - BufferedStore.js: buffer range would not store the number of the total length 12 of the records when last possible range is reached, but instead the number of the 13 start-parameter with the "limit"-parameter, which lead to errors when a last 14 possible record to render is requested that cannot be found in the store 15 16 - enhancements: 17 - BufferedGridView.js: last row in the grid is now clipped instead of removed if 18 it is not fully displayable 19 20 6 21 7 22 Version 0.1.2 -
trunk/src/BufferedGridView.js
r8 r11 128 128 // {{{ --------------------------properties------------------------------------- 129 129 /** 130 * Stores the height of the header. Needed for recalculating scroller inset height. 131 * @param {Number} 132 */ 133 hdHeight : 0, 134 135 /** 136 * Indicates wether the last row in the grid is clipped and thus not fully display. 137 * 1 if clipped, otherwise 0. 138 * @param {Number} 139 */ 140 rowClipped : 0, 141 142 143 /** 130 144 * This is the actual y-scroller that does control sending request to the server 131 145 * based upon the position of the scrolling cursor. … … 239 253 this.lastRowIndex = 0; 240 254 this.lastIndex = 0; 255 this.adjustVisibleRows(); 241 256 this.adjustScrollerPos(-this.liveScroller.dom.scrollTop, true); 242 257 this.showLoadMask(false); … … 244 259 //this.replaceLiveRows(0, true); 245 260 this.fireEvent('cursormove', this, 0, 246 Math.min(this.ds.totalLength, this.visibleRows ),261 Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped), 247 262 this.ds.totalLength); 248 263 } else { … … 363 378 this.liveScroller.on('scroll', this.onLiveScroll, this, {buffer : this.scrollDelay}); 364 379 365 this.mainHd = new E(this.mainWrap.dom.firstChild); 380 var thd = this.mainWrap.dom.firstChild; 381 this.mainHd = new E(thd); 382 383 this.hdHeight = thd.offsetHeight; 384 366 385 this.innerHd = this.mainHd.dom.firstChild; 367 386 this.scroller = new E(this.mainWrap.dom.childNodes[1]); … … 629 648 this.adjustScrollerPos(-this.rowHeight, true); 630 649 this.fireEvent('cursormove', this, this.rowIndex, 631 Math.min(this.ds.totalLength, this.visibleRows ),650 Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped), 632 651 this.ds.totalLength); 633 652 … … 753 772 // the cursor did virtually move 754 773 this.fireEvent('cursormove', this, this.rowIndex, 755 Math.min(this.ds.totalLength, this.visibleRows ),774 Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped), 756 775 this.ds.totalLength); 757 776 … … 812 831 this.processRows(); 813 832 this.fireEvent('cursormove', this, this.rowIndex, 814 Math.min(this.ds.totalLength, this.visibleRows ),833 Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped), 815 834 this.ds.totalLength); 816 835 } … … 1157 1176 1158 1177 var rowInd = row-this.rowIndex; 1159 if (row >= this.rowIndex+this.visibleRows) { 1178 1179 if (this.rowClipped && row == this.rowIndex+this.visibleRows-1) { 1180 this.adjustScrollerPos(this.rowHeight ); 1181 } else if (row >= this.rowIndex+this.visibleRows) { 1160 1182 this.adjustScrollerPos(((row-(this.rowIndex+this.visibleRows))+1)*this.rowHeight); 1161 1183 } else if (row <= this.rowIndex) { … … 1171 1193 cellEl = this.getCell(row-this.rowIndex, col); 1172 1194 } 1195 1173 1196 if(!rowEl){ 1174 1197 return; … … 1245 1268 { 1246 1269 this.fireEvent('cursormove', this, index, 1247 Math.min(this.ds.totalLength, this.visibleRows ),1270 Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped), 1248 1271 this.ds.totalLength); 1249 1272 … … 1420 1443 if (append) { 1421 1444 this.removeRows(0, spill-1); 1422 var html = this.renderRows(cursorBuffer+this.visibleRows-spill, 1423 cursorBuffer+this.visibleRows-1); 1424 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html); 1445 1446 if (cursor+this.visibleRows-1 < this.ds.bufferRange[1]) { 1447 var html = this.renderRows(cursorBuffer+this.visibleRows-spill, 1448 cursorBuffer+this.visibleRows-1); 1449 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html); 1450 } 1425 1451 } else { 1426 1452 this.removeRows(this.visibleRows-spill, this.visibleRows-1); … … 1453 1479 1454 1480 // adjust the height of the scrollbar 1455 this.liveScroller.dom.style.height = this.liveScroller.dom.parentNode.offsetHeight + 1456 (Ext.isGecko 1457 ? ((ds.totalLength > 0 && scrollbar) 1458 ? - this.horizontalScrollOffset 1459 : 0) 1460 : (((ds.totalLength > 0 && scrollbar) 1461 ? 0 : this.horizontalScrollOffset)))+"px"; 1481 1482 var contHeight = this.liveScroller.dom.parentNode.offsetHeight + 1483 (Ext.isGecko 1484 ? ((ds.totalLength > 0 && scrollbar) 1485 ? - this.horizontalScrollOffset 1486 : 0) 1487 : (((ds.totalLength > 0 && scrollbar) 1488 ? 0 : this.horizontalScrollOffset))) 1489 1490 this.liveScroller.dom.style.height = contHeight+"px"; 1491 1462 1492 if (this.rowHeight == -1) { 1463 1493 return; 1464 1494 } 1465 1495 1466 if (ds.totalLength <= this.visibleRows) {1467 this.liveScrollerInset.style.height = "0px";1468 return;1469 }1470 1471 var height = this.rowHeight*ds.totalLength;1472 1473 height += (c.getSize().height-(this.visibleRows*this.rowHeight));1474 1475 if (s crollbar) {1476 h eight -= this.horizontalScrollOffset;1477 } 1478 1479 this.liveScrollerInset.style.height = (height)+"px";1496 // calculate the spill of the inner layer. 1497 // spillHeight will contain the number of pixels 1498 // the inner layer exceeds the scrollbar 1499 var spillHeight = ((this.rowHeight*ds.totalLength)+this.hdHeight)-contHeight; 1500 1501 // hidden rows is the number of rows which cannot be 1502 // displayed and for which a scrollbar needs to be 1503 // rendered. This does alsso take clipped rows into account 1504 var hiddenRows = 0; 1505 if (spillHeight > 0) { 1506 hiddenRows = Math.ceil(spillHeight/this.rowHeight); 1507 } 1508 1509 this.liveScrollerInset.style.height = contHeight+(hiddenRows*this.rowHeight)+"px"; 1480 1510 }, 1481 1511 … … 1514 1544 vh -= this.mainHd.getHeight(); 1515 1545 1546 var totalLength = ds.totalLength || 0; 1547 1516 1548 var visibleRows = Math.max(1, Math.floor(vh/this.rowHeight)); 1517 1549 1518 var totalLength = ds.getTotalCount(); 1519 1520 if (totalLength < this.visibleRows || this.visibleRows == visibleRows) { 1550 this.rowClipped = 0; 1551 if (this.rowHeight / 3 < (vh - (visibleRows*this.rowHeight))) { 1552 visibleRows = Math.min(visibleRows+1, totalLength); 1553 this.rowClipped = 1; 1554 } 1555 1556 // adjusted condition to force rerendering of scrollbar if the 1557 // toalLength is less than visibleRows 1558 if (this.visibleRows == visibleRows) { 1521 1559 return; 1522 1560 } … … 1525 1563 1526 1564 if (this.rowIndex + visibleRows > totalLength) { 1527 this.rowIndex = Math.max(0, ds.totalLength-this.visibleRows);1565 this.rowIndex = Math.max(0, totalLength-visibleRows); 1528 1566 this.lastRowIndex = this.rowIndex; 1529 this.updateLiveRows(this.rowIndex, true);1530 } else { 1531 this.updateLiveRows(this.rowIndex, true);1532 } 1567 } 1568 1569 this.updateLiveRows(this.rowIndex, true); 1570 1533 1571 }, 1534 1572 -
trunk/src/BufferedRowSelectionModel.js
r10 r11 271 271 272 272 273 274 273 /** 275 274 * Deselects a row. -
trunk/src/BufferedStore.js
r4 r11 2 2 * Ext.ux.grid.BufferedStore V0.9 3 3 * Copyright(c) 2007, http://www.siteartwork.de 4 * 4 * 5 5 * Licensed under the terms of the Open Source LGPL 3.0 6 6 * http://www.gnu.org/licenses/lgpl.html … … 28 28 * Pending selections are represented by row indexes which have been selected but 29 29 * which records have not yet been available in the store. The loadSelections method 30 * will initiate a request to the data repository (same url as specified in the 30 * will initiate a request to the data repository (same url as specified in the 31 31 * url config parameter for the store) to fetch the pending selections. The additional 32 * parameter send to the server is the "ranges" parameter, which will hold a json 32 * parameter send to the server is the "ranges" parameter, which will hold a json 33 33 * encoded string representing ranges of row indexes to load from the data repository. 34 34 * As an example, pending selections with the indexes 1,2,3,4,5,9,10,11,16 would 35 35 * have to be translated to [1,5],[9,11],[16]. 36 36 * Please note, that by indexes we do not understand (primary) keys of the data, 37 * but indexes as represented by the view. To get the ranges of pending selections, 38 * you can use the getPendingSelections method of the BufferedRowSelectionModel, which 37 * but indexes as represented by the view. To get the ranges of pending selections, 38 * you can use the getPendingSelections method of the BufferedRowSelectionModel, which 39 39 * should be used as the default selection model of the grid. 40 40 * … … 61 61 * computed by the underlying store's sort algorithm. Whenever a record should be 62 62 * added to the store, the insert index should be calculated and the used as the 63 * parameter for the insert method. The findInsertIndex method will return a value 64 * that equals to Number.MIN_VALUE or Number.MAX_VALUE if the added record would not 63 * parameter for the insert method. The findInsertIndex method will return a value 64 * that equals to Number.MIN_VALUE or Number.MAX_VALUE if the added record would not 65 65 * change the current state of the store. If that happens, this data is not available 66 66 * in the store, and may be requested later on when a new request for new data is made. … … 69 69 * -------- 70 70 * remoteSort will always be set to true, no matter what value the user provides 71 * using the config object. 71 * using the config object. 72 72 * 73 73 * @constructor … … 77 77 */ 78 78 Ext.ux.grid.BufferedStore = function(config) { 79 79 80 80 config = config || {}; 81 81 82 82 // remoteSort will always be set to true. 83 83 config.remoteSort = true; 84 84 85 85 Ext.apply(this, config); 86 86 87 87 this.addEvents({ 88 88 /** … … 112 112 */ 113 113 'selectionsload' : true 114 114 115 115 }); 116 116 117 117 Ext.ux.grid.BufferedStore.superclass.constructor.call(this, config); 118 118 119 119 this.totalLength = 0; 120 120 121 121 /** 122 122 * The array represents the range of rows available in the buffer absolute to … … 125 125 */ 126 126 this.bufferRange = [0, this.bufferSize]; 127 127 128 128 this.on('clear', function (){ 129 129 this.bufferRange = [0, this.bufferSize]; 130 130 }, this); 131 131 132 132 if(this.url && !this.selectionsProxy){ 133 133 this.selectionsProxy = new Ext.data.HttpProxy({url: this.url}); 134 134 } 135 135 136 136 }; 137 137 … … 144 144 */ 145 145 version : null, 146 146 147 147 /** 148 148 * Inserts a record at the position as specified in index. … … 151 151 * the set of data in the underlying store has been changed. 152 152 * If the index equals to 0 and the length of data in the store equals to 153 * bufferSize, the add-event will be triggered with Number.MIN_VALUE to 154 * indicate that a record has been prepended. If the index equals to 153 * bufferSize, the add-event will be triggered with Number.MIN_VALUE to 154 * indicate that a record has been prepended. If the index equals to 155 155 * bufferSize, the method will assume that the record has been appended and 156 156 * trigger the add event with index set to Number.MAX_VALUE. … … 158 158 * Note: 159 159 * ----- 160 * The index parameter is not a view index, but a value in the range of 160 * The index parameter is not a view index, but a value in the range of 161 161 * [0, this.bufferSize]. 162 162 * 163 163 * You are strongly advised to not use this method directly. Instead, call 164 * findInsertIndex wirst and use the return-value as the first parameter for 164 * findInsertIndex wirst and use the return-value as the first parameter for 165 165 * for this method. 166 166 */ … … 169 169 // hooray for haskell! 170 170 records = [].concat(records); 171 172 index = index >= this.bufferSize ? Number.MAX_VALUE : index; 173 174 if (index == Number.MIN_VALUE || index == Number.MAX_VALUE) { 171 172 index = index >= this.bufferSize ? Number.MAX_VALUE : index; 173 174 if (index == Number.MIN_VALUE || index == Number.MAX_VALUE) { 175 175 var l = records.length; 176 176 if (index == Number.MIN_VALUE) { 177 177 this.bufferRange[0] += l; 178 this.bufferRange[1] += l; 178 this.bufferRange[1] += l; 179 179 } 180 180 181 181 this.totalLength += l; 182 182 this.fireEvent("add", this, records, index); 183 183 return; 184 } 184 } 185 185 186 186 var split = false; … … 188 188 if (records.length + index >= this.bufferSize) { 189 189 split = true; 190 insertRecords = records.splice(0, this.bufferSize-index) 190 insertRecords = records.splice(0, this.bufferSize-index) 191 191 } 192 192 this.totalLength += insertRecords.length; 193 193 194 194 for (var i = 0, len = insertRecords.length; i < len; i++) { 195 195 this.data.insert(index, insertRecords[i]); 196 196 insertRecords[i].join(this); 197 197 } 198 198 199 199 while (this.getCount() > this.bufferSize) { 200 200 this.data.remove(this.data.last()); 201 201 } 202 202 203 203 this.fireEvent("add", this, insertRecords, index); 204 204 205 205 if (split == true) { 206 206 this.fireEvent("add", this, records, Number.MAX_VALUE); 207 207 } 208 }, 209 208 }, 209 210 210 /** 211 211 * Remove a Record from the Store and fires the remove event. 212 212 * 213 * If the record is not within the store, the method will try to guess it's 213 * If the record is not within the store, the method will try to guess it's 214 214 * index by calling findInsertIndex. 215 215 * … … 239 239 { 240 240 var index = this.data.indexOf(record); 241 241 242 242 if (index < 0) { 243 243 var ind = this.findInsertIndex(record); … … 251 251 this.bufferRange[1]--; 252 252 this.data.removeAt(index); 253 253 254 254 if(this.pruneModifiedRecords){ 255 255 this.modified.remove(record); 256 256 } 257 257 258 258 this.totalLength -= 1; 259 259 this.fireEvent("remove", this, record, index); … … 270 270 this.totalLength = 0; 271 271 this.data.clear(); 272 272 273 273 if(this.pruneModifiedRecords){ 274 274 this.modified = []; 275 275 } 276 276 this.fireEvent("clear", this); 277 }, 278 277 }, 278 279 279 /** 280 280 * Requests a range of data from the underlying data store. Similiar to the … … 283 283 * Example: To load all records at the positions 1,2,3,4,9,12,13,14, the supplied 284 284 * parameter should equal to [[1,4],[9],[12,14]]. 285 * The request will only be done if the beforeselectionsloaded events return 285 * The request will only be done if the beforeselectionsloaded events return 286 286 * value does not equal to false. 287 287 */ … … 289 289 { 290 290 var max_i = ranges.length; 291 291 292 292 if(max_i > 0 && !this.selectionsProxy.activeRequest 293 293 && this.fireEvent("beforeselectionsload", this, ranges) !== false){ 294 294 295 295 var lParams = this.lastOptions.params; 296 296 297 297 var params = {}; 298 298 params.ranges = Ext.encode(ranges); 299 299 300 300 if (lParams) { 301 301 if (lParams.sort) { … … 306 306 } 307 307 } 308 308 309 309 var options = {}; 310 310 for (var i in this.lastOptions) { 311 311 options.i = this.lastOptions.i; 312 312 } 313 313 314 314 options.ranges = params.ranges; 315 316 this.selectionsProxy.load(params, this.reader, 317 this.selectionsLoaded, this, 318 options); 319 } 320 }, 321 315 316 this.selectionsProxy.load(params, this.reader, 317 this.selectionsLoaded, this, 318 options); 319 } 320 }, 321 322 322 /** 323 323 * Alias for loadRanges. 324 */ 324 */ 325 325 loadSelections : function(ranges) 326 326 { 327 327 this.loadRanges(ranges); 328 328 }, 329 329 330 330 /** 331 331 * Called as a callback by the proxy which loads pending selections. … … 337 337 { 338 338 if (this.checkVersionChange(o, options, success) !== false) { 339 this.fireEvent("selectionsload", this, o.records, Ext.decode(options.ranges)); 339 this.fireEvent("selectionsload", this, o.records, Ext.decode(options.ranges)); 340 340 } else { 341 341 this.fireEvent("selectionsload", this, [], Ext.decode(options.ranges)); … … 344 344 345 345 /** 346 * Checks if the version supplied in <tt>o</tt> differs from the version 346 * Checks if the version supplied in <tt>o</tt> differs from the version 347 347 * property of the current instance of this object and fires the versionchange 348 348 * event if it does. … … 350 350 // private 351 351 checkVersionChange : function(o, options, success) 352 { 352 { 353 353 if(o && success !== false){ 354 354 if (o.version !== undefined) { 355 355 var old = this.version; 356 this.version = o.version; 356 this.version = o.version; 357 357 if (this.version !== old) { 358 358 return this.fireEvent('versionchange', this, old, this.version); 359 359 } 360 } 361 } 362 }, 363 364 /** 365 * The sort procedure tries to respect the current data in the buffer. If the 366 * found index would not be within the bufferRange, Number.MIN_VALUE is returned to 367 * indicate that the record would be sorted below the first record in the buffer 368 * range, while Number.MAX_VALUE would indicate that the record would be added after 360 } 361 } 362 }, 363 364 /** 365 * The sort procedure tries to respect the current data in the buffer. If the 366 * found index would not be within the bufferRange, Number.MIN_VALUE is returned to 367 * indicate that the record would be sorted below the first record in the buffer 368 * range, while Number.MAX_VALUE would indicate that the record would be added after 369 369 * the last record in the buffer range. 370 370 * 371 * The method is not guaranteed to return the relative index of the record 371 * The method is not guaranteed to return the relative index of the record 372 372 * in the data model as returned by the underlying domain model. 373 373 */ … … 377 377 var index = Ext.ux.grid.BufferedStore.superclass.findInsertIndex.call(this, record); 378 378 this.remoteSort = true; 379 379 380 380 if (this.bufferRange[0] > 0 && index == 0) { 381 index = Number.MIN_VALUE; 381 index = Number.MIN_VALUE; 382 382 } else if (index >= this.bufferSize) { 383 383 index = Number.MAX_VALUE; 384 384 } 385 385 386 386 return index; 387 }, 388 387 }, 388 389 389 /** 390 390 * Removed snapshot check … … 400 400 }; 401 401 this.data.sort(direction, fn); 402 }, 403 404 405 406 /** 407 * @cfg {Number} bufferSize The number of records that will at least always 402 }, 403 404 405 406 /** 407 * @cfg {Number} bufferSize The number of records that will at least always 408 408 * be available in the store for rendering. This value will be send to the 409 409 * server as the <tt>limit</tt> parameter and should not change during the 410 * lifetime of a grid component. Note: In a paging grid, this number would 410 * lifetime of a grid component. Note: In a paging grid, this number would 411 411 * indicate the page size. 412 * The value should be set high enough to make a userfirendly scrolling 413 * possible and should be greater than the sum of {nearLimit} and 412 * The value should be set high enough to make a userfirendly scrolling 413 * possible and should be greater than the sum of {nearLimit} and 414 414 * {visibleRows}. Usually, a value in between 150 and 200 is good enough. 415 415 * A lesser value will more often make the store re-request new data, while 416 416 * a larger number will make loading times higher. 417 */ 418 419 417 */ 418 419 420 420 // private 421 421 onMetaChange : function(meta, rtype, o) 422 422 { 423 423 this.version = null; 424 Ext.ux.grid.BufferedStore.superclass.onMetaChange.call(this, meta, rtype, o); 425 }, 426 427 424 Ext.ux.grid.BufferedStore.superclass.onMetaChange.call(this, meta, rtype, o); 425 }, 426 427 428 428 /** 429 429 * Will fire the versionchange event if the version of incoming data has changed. … … 433 433 { 434 434 this.checkVersionChange(o, options, success); 435 435 436 436 // we have to stay in sync with rows that may have been skipped while 437 437 // the request was loading. 438 438 this.bufferRange = [ 439 options.params.start, 440 options.params.start+options.params.limit439 options.params.start, 440 Math.min(options.params.start+options.params.limit, o.totalRecords) 441 441 ]; 442 443 Ext.ux.grid.BufferedStore.superclass.loadRecords.call(this, o, options, success); 444 }, 445 442 443 Ext.ux.grid.BufferedStore.superclass.loadRecords.call(this, o, options, success); 444 }, 445 446 446 /** 447 447 * Get the Record at the specified index. … … 456 456 var modelIndex = index - this.bufferRange[0]; 457 457 return this.data.itemAt(modelIndex); 458 }, 459 458 }, 459 460 460 //--------------------------------------EMPTY----------------------------------- 461 461 // no interface concept, so simply overwrite and leave them empty as for now 462 462 clearFilter : function(){}, 463 isFiltered : function(){}, 464 collect : function(){}, 463 isFiltered : function(){}, 464 collect : function(){}, 465 465 createFilterFn : function(){}, 466 466 sum : function(){}, … … 470 470 queryBy : function(){}, 471 471 find : function(){}, 472 findBy : function(){} 473 472 findBy : function(){} 473 474 474 });
