r1014 - in branches/dev/grid: tests/visual ui
Author: paul.bakaus
Date: Wed Nov 26 08:21:04 2008
New Revision: 1014
Added:
branches/dev/grid/tests/visual/infiniteScrolling.html
branches/dev/grid/ui/ui.infiniteScrolling.js
Modified:
branches/dev/grid/tests/visual/grid.html
branches/dev/grid/ui/ui.grid.js
Log:
infiniteScrolling: initial implementation of a utility that solves infinite
table scrolling and provides callbacks to retrieve and fill rows
grid: supports and implements infinite scrolling (by disabling
this.options.pagination, see visual test for working prototype)
Modified: branches/dev/grid/tests/visual/grid.html
==============================================================================
--- branches/dev/grid/tests/visual/grid.html (original)
+++ branches/dev/grid/tests/visual/grid.html Wed Nov 26 08:21:04 2008
@@ -9,8 +9,9 @@
<script type="text/javascript" src="../../ui/ui.core.js"></script>
<script type="text/javascript" src="../../ui/ui.grid.js"></script>
<script type="text/javascript" src="../../ui/ui.gridmodel.js"></script>
+ <script type="text/javascript"
src="../../ui/ui.infiniteScrolling.js"></script>
<script type="text/javascript"
src="../../dev-selectable-ui/ui.selectable.js"></script>
- <script type="text/javascript" src="../../ui/ui.resizable.js"></script>
+
@@ -67,7 +68,8 @@
url: "../data/employees-json.php",
limit: 20,
height: 200,
- toolbar: false
+ toolbar: false,
+ pagination: false
});
});
Added: branches/dev/grid/tests/visual/infiniteScrolling.html
==============================================================================
--- (empty file)
+++ branches/dev/grid/tests/visual/infiniteScrolling.html Wed Nov 26
08:21:04 2008
@@ -0,0 +1,94 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title>grid</title>
+ <link rel="stylesheet" href="../../themes/default/ui.all.css"
type="text/css" media="screen" title="no title" charset="utf-8">
+
+ <script type="text/javascript" src="../../jquery-1.2.6.js"></script>
+ <script type="text/javascript" src="../../ui/ui.core.js"></script>
+ <script type="text/javascript"
src="../../ui/ui.infiniteScrolling.js"></script>
+
+
+
+ <style type="text/css" media="screen">
+
+ body, html {
+ font-size: 10px;
+ font-family: Arial;
+ }
+
+ td {
+ padding: 3px;
+ border: 2px solid #bbb;
+ }
+
+
+ </style>
+
+ <script type="text/javascript">
+
+ $(document).ready(function() {
+
+ $('.scrollme').infiniteScrolling({
+ total: 1000,
+ template: '<tr class="{$1}"><td>{$2}</td><td>{$3}</td></tr>',
+ scroll: function(e, ui) {
+
+ var data = [];
+ for (var i=0; i < 20; i++) {
+ data.push({ 1: 'test', 2: 'Row '+(ui.start+i), 3: 'Entry' });
+ };
+
+ window.setTimeout(function() {
+
+ ui.fill({
+ block: ui.block,
+ data: data
+ });
+
+ }, 1000);
+
+
+
+ }
+ });
+
+ });
+
+
+
+ </script>
+
+ </head>
+ <body>
+
+ <div class='scrollme' style='width: 500px; height: 500px; overflow:
auto; background: #eee; border: 5px solid black;'>
+ <table cellpadding='0' cellspacing='0' width='100%'>
+ <tbody>
+ <tr><td>Row 1</td><td>Entry (init)</td></tr>
+ <tr><td>Row 2</td><td>Entry (init)</td></tr>
+ <tr><td>Row 3</td><td>Entry (init)</td></tr>
+ <tr><td>Row 4</td><td>Entry (init)</td></tr>
+ <tr><td>Row 5</td><td>Entry (init)</td></tr>
+ <tr><td>Row 6</td><td>Entry (init)</td></tr>
+ <tr><td>Row 7</td><td>Entry (init)</td></tr>
+ <tr><td>Row 8</td><td>Entry (init)</td></tr>
+ <tr><td>Row 9</td><td>Entry (init)</td></tr>
+ <tr><td>Row 10</td><td>Entry (init)</td></tr>
+ <tr><td>Row 11</td><td>Entry (init)</td></tr>
+ <tr><td>Row 12</td><td>Entry (init)</td></tr>
+ <tr><td>Row 13</td><td>Entry (init)</td></tr>
+ <tr><td>Row 14</td><td>Entry (init)</td></tr>
+ <tr><td>Row 15</td><td>Entry (init)</td></tr>
+ <tr><td>Row 16</td><td>Entry (init)</td></tr>
+ <tr><td>Row 17</td><td>Entry (init)</td></tr>
+ <tr><td>Row 18</td><td>Entry (init)</td></tr>
+ <tr><td>Row 19</td><td>Entry (init)</td></tr>
+ <tr><td>Row 20</td><td>Entry (init)</td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ </body>
+</html>
\ No newline at end of file
Modified: branches/dev/grid/ui/ui.grid.js
==============================================================================
--- branches/dev/grid/ui/ui.grid.js (original)
+++ branches/dev/grid/ui/ui.grid.js Wed Nov 26 08:21:04 2008
@@ -179,22 +179,59 @@
self._updatePagination(response);
}
- if(o && o.columns) {
+
+ if(!self.infiniteScrolling)
self.content.empty();
+
+ if(o && o.columns) {
self.columnsContainer.empty();
self._addColumns(response.columns);
- } else {
- self.content.empty();
}
- for (var i=0; i < response.records.length; i++) {
- self._addRow(response.records[i]);
- };
- self._syncColumnWidth();
+ if(self.infiniteScrolling) {
+
+ var data = [];
+ for (var i=0; i < response.records.length; i++) {
+ data.push(self._addRow(response.records[i]));
+ };
+
+ o.fill({
+ block: o.block,
+ data: data
+ });
+
+ } else {
- $('div.ui-grid-limits', self.footer)
- .html('Result ' + options.start + '-' + (options.start +
options.limit) + ' of ' + response.totalRecords);
+ for (var i=0; i < response.records.length; i++) {
+ self._addRow(response.records[i]);
+ };
+
+ self._syncColumnWidth();
+
+ }
+
+ //Initiate infinite scrolling if we don't use pagination and total
records exceed the displayed records
+ if(!self.infiniteScrolling && !self.options.pagination &&
self.options.limit < response.totalRecords) {
+
+ self.infiniteScrolling = true;
+ self.infiniteScrollingJustInitiated = true;
+ $('div.ui-grid-content', self.grid).infiniteScrolling({
+ total: response.totalRecords,
+ block: 10,
+ scroll: function(e, ui) {
+ self.offset = ui.start;
+ self._update({ fill: ui.fill, block: ui.block });
+ },
+ update: function(e, ui) {
+ $('div.ui-grid-limits', self.footer).html('Result ' + ui.firstItem
+ '-' + ui.lastItem + ' of ' +
$(this).infiniteScrolling('option', 'total'));
+ }
+ });
+
+ }
+
+ if(!self.infiniteScrolling)
+ $('div.ui-grid-limits', self.footer).html('Result ' + options.start
+ '-' + (options.start + options.limit) + ' of ' + response.totalRecords);
});
@@ -218,7 +255,8 @@
_addColumns: function(item) {
this.columns = item;
- var totalWidth = 0;
+ var totalWidth = 25;
+
for (var i=0; i < item.length; i++) {
var column = $('<td class="ui-grid-column-header
ui-default-state"><div>'+item[i].label+'</div></td>')
.width(item[i].width)
@@ -227,15 +265,19 @@
totalWidth += item[i].width;
};
+ //This column is the last and only used to serve as placeholder for a
non-existant scrollbar
+ $('<td class="ui-grid-column-header
ui-default-state"><div></div></td>').width(25).appendTo(this.columnsContainer);
+
+ //Update the total width of the wrapper of the column headers
this.columnsContainer.parent().parent().width(totalWidth);
},
- _addRow: function(item) {
+ _addRow: function(item, dontAdd) {
- var row = $('<tr class="ui-grid-row"></tr>')
- .appendTo(this.content)
- .hover(function() {
+ var row = $('<tr class="ui-grid-row"></tr>');
+ if(!dontAdd) row.appendTo(this.content);
+ row.hover(function() {
$(this).addClass('ui-grid-row-hover');
}, function() {
$(this).removeClass('ui-grid-row-hover');
@@ -245,6 +287,8 @@
$('<td class="ui-grid-column
ui-active-state"><div>'+item[this.columns[i].id]+'</div></td>')
.appendTo(row);
};
+
+ return row;
}
Added: branches/dev/grid/ui/ui.infiniteScrolling.js
==============================================================================
--- (empty file)
+++ branches/dev/grid/ui/ui.infiniteScrolling.js Wed Nov 26 08:21:04 2008
@@ -0,0 +1,130 @@
+/*
+ * jQuery UI Dialog @VERSION
+ *
+ * Copyright (c) 2008 Richard D. Worth (rdworth.org)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * ui.core.js
+ * ui.draggable.js
+ * ui.resizable.js
+ */
+(function($) {
+
+$.widget("ui.infiniteScrolling", {
+
+ _init: function() {
+
+ var self = this;
+ this.tbody = $('> table > tbody', this.element);
+ this.height = this.element.height();
+ this.rowHeight = $('tr', this.tbody).height();
+
+ //Prepare the cache that tells us what already has been loaded and what
not
+ this._prepareCache();
+
+ //Alocate all rows in this.options.total to change the scrollbar size
+ this._allocateRows();
+
+ //Call update the first time to retrieve the first set of rows
+ this._update();
+
+ this.element.bind('scroll', function(event) {
+ self._update(event);
+ });
+
+ },
+
+ _prepareCache: function() {
+
+ this.cache = new Array(Math.ceil(this.options.total /
this.options.block));
+ var alreadyCached = Math.floor($('tr', this.tbody).length /
this.options.block);
+ for (var i=0; i < alreadyCached; i++) {
+ this.cache[i] = (new Date()).getTime();
+ };
+
+ },
+
+ _allocateRows: function() {
+
+ var num = this.options.total - $('tr', this.tbody).length;
+ var colspan = $('tr:eq(0) > td', this.tbody).length;
+ var newHTML = '';
+
+ for (var i=0; i < num; i++) {
+ newHTML += '<tr><td
style="height:'+this.rowHeight+'px;border:0;padding:0;margin:0;"
colspan="'+colspan+'"></td></tr>';
+ };
+
+ //Appending this whole block to innerHTML is drastically faster than
individual appends in the loop above
+ this.tbody[0].innerHTML += newHTML;
+
+ },
+
+ _update: function(event) {
+
+ var self = this;
+ var start = this.element[0].scrollTop;
+ var stop = start + this.height;
+
+ var firstItem = Math.floor(start / this.rowHeight);
+ var lastItem = Math.ceil(stop / this.rowHeight);
+
+ var firstBlock = Math.round(firstItem / this.options.block);
+ var lastBlock = Math.round(lastItem / this.options.block);
+
+ for (var i=firstBlock - this.options.preload; i <= lastBlock +
this.options.preload; i++) {
+
+ if(i < 0 || i >= this.cache.length) continue;
+ if(this.cache[i]) continue;
+
+ this.cache[i] = (new Date()).getTime(); //TODO: Revalidation option
+ this._trigger('scroll', event, { block: i, start: i *
this.options.block, fill: function() { return self.fill.apply(self,
arguments); } });
+
+ };
+
+ this._trigger('update', event, { firstBlock: firstBlock, lastBlock:
lastBlock, firstItem: firstItem, lastItem: lastItem });
+
+ },
+
+ fill: function(o) {
+
+ var rows = this.tbody[0].rows;
+
+ for (var i=0; i < o.data.length; i++) {
+
+ if(o.data[i].jquery || o.data[i].nodeType || o.data[i].constructor ==
String) {
+ var template = o.data[i];
+ } else {
+ var template = this.options.template;
+ for(var r in o.data[i]) {
+ template = template.replace(new RegExp('\\{\\$'+r+'\\}', 'g'),
o.data[i][r]);
+ }
+ }
+
+
+ //I'm sure there is a better way to do this..
+ this.tbody[0].replaceChild($(template)[0], rows[(o.block *
this.options.block)+i]);
+
+ };
+
+ }
+
+});
+
+$.extend($.ui.infiniteScrolling, {
+ version: "@VERSION",
+ defaults: {
+ total: 1000,
+ block: 20,
+ preload: 1
+ }
+
+});
+
+
+
+
+})(jQuery);