]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojox/grid/_grid/scroller.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojox / grid / _grid / scroller.js
1 if(!dojo._hasResource['dojox.grid._grid.scroller']){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource['dojox.grid._grid.scroller'] = true;
3 dojo.provide('dojox.grid._grid.scroller');
4
5 dojo.declare('dojox.grid.scroller.base', null, {
6         // summary:
7         //      virtual scrollbox, abstract class
8         //      Content must in /rows/
9         //      Rows are managed in contiguous sets called /pages/
10         //      There are a fixed # of rows per page
11         //      The minimum rendered unit is a page
12         constructor: function(){
13                 this.pageHeights = [];
14                 this.stack = [];
15         },
16         // specified
17         rowCount: 0, // total number of rows to manage
18         defaultRowHeight: 10, // default height of a row
19         keepRows: 100, // maximum number of rows that should exist at one time
20         contentNode: null, // node to contain pages
21         scrollboxNode: null, // node that controls scrolling
22         // calculated
23         defaultPageHeight: 0, // default height of a page
24         keepPages: 10, // maximum number of pages that should exists at one time
25         pageCount: 0,
26         windowHeight: 0,
27         firstVisibleRow: 0,
28         lastVisibleRow: 0,
29         // private
30         page: 0,
31         pageTop: 0,
32         // init
33         init: function(inRowCount, inKeepRows, inRowsPerPage){
34                 switch(arguments.length){
35                         case 3: this.rowsPerPage = inRowsPerPage;
36                         case 2: this.keepRows = inKeepRows;
37                         case 1: this.rowCount = inRowCount;
38                 }
39                 this.defaultPageHeight = this.defaultRowHeight * this.rowsPerPage;
40                 //this.defaultPageHeight = this.defaultRowHeight * Math.min(this.rowsPerPage, this.rowCount);
41                 this.pageCount = Math.ceil(this.rowCount / this.rowsPerPage);
42                 this.setKeepInfo(this.keepRows);
43                 this.invalidate();
44                 if(this.scrollboxNode){
45                         this.scrollboxNode.scrollTop = 0;
46                         this.scroll(0);
47                         this.scrollboxNode.onscroll = dojo.hitch(this, 'onscroll');
48                 }
49         },
50         setKeepInfo: function(inKeepRows){
51                 this.keepRows = inKeepRows;
52                 this.keepPages = !this.keepRows ? this.keepRows : Math.max(Math.ceil(this.keepRows / this.rowsPerPage), 2);
53         },
54         // updating
55         invalidate: function(){
56                 this.invalidateNodes();
57                 this.pageHeights = [];
58                 this.height = (this.pageCount ? (this.pageCount - 1)* this.defaultPageHeight + this.calcLastPageHeight() : 0);
59                 this.resize();
60         },
61         updateRowCount: function(inRowCount){
62                 this.invalidateNodes();
63                 this.rowCount = inRowCount;
64                 // update page count, adjust document height
65                 oldPageCount = this.pageCount;
66                 this.pageCount = Math.ceil(this.rowCount / this.rowsPerPage);
67                 if(this.pageCount < oldPageCount){
68                         for(var i=oldPageCount-1; i>=this.pageCount; i--){
69                                 this.height -= this.getPageHeight(i);
70                                 delete this.pageHeights[i]
71                         }
72                 }else if(this.pageCount > oldPageCount){
73                         this.height += this.defaultPageHeight * (this.pageCount - oldPageCount - 1) + this.calcLastPageHeight();
74                 }
75                 this.resize();
76         },
77         // abstract interface
78         pageExists: function(inPageIndex){
79         },
80         measurePage: function(inPageIndex){
81         },
82         positionPage: function(inPageIndex, inPos){
83         },
84         repositionPages: function(inPageIndex){
85         },
86         installPage: function(inPageIndex){
87         },
88         preparePage: function(inPageIndex, inPos, inReuseNode){
89         },
90         renderPage: function(inPageIndex){
91         },
92         removePage: function(inPageIndex){
93         },
94         pacify: function(inShouldPacify){
95         },
96         // pacification
97         pacifying: false,
98         pacifyTicks: 200,
99         setPacifying: function(inPacifying){
100                 if(this.pacifying != inPacifying){
101                         this.pacifying = inPacifying;
102                         this.pacify(this.pacifying);
103                 }
104         },
105         startPacify: function(){
106                 this.startPacifyTicks = new Date().getTime();
107         },
108         doPacify: function(){
109                 var result = (new Date().getTime() - this.startPacifyTicks) > this.pacifyTicks;
110                 this.setPacifying(true);
111                 this.startPacify();
112                 return result;
113         },
114         endPacify: function(){
115                 this.setPacifying(false);
116         },
117         // default sizing implementation
118         resize: function(){
119                 if(this.scrollboxNode){
120                         this.windowHeight = this.scrollboxNode.clientHeight;
121                 }
122                 dojox.grid.setStyleHeightPx(this.contentNode, this.height);
123         },
124         calcLastPageHeight: function(){
125                 if(!this.pageCount){
126                         return 0;
127                 }
128                 var lastPage = this.pageCount - 1;
129                 var lastPageHeight = ((this.rowCount % this.rowsPerPage)||(this.rowsPerPage)) * this.defaultRowHeight;
130                 this.pageHeights[lastPage] = lastPageHeight;
131                 return lastPageHeight;
132         },
133         updateContentHeight: function(inDh){
134                 this.height += inDh;
135                 this.resize();
136         },
137         updatePageHeight: function(inPageIndex){
138                 if(this.pageExists(inPageIndex)){
139                         var oh = this.getPageHeight(inPageIndex);
140                         var h = (this.measurePage(inPageIndex))||(oh);
141                         this.pageHeights[inPageIndex] = h;
142                         if((h)&&(oh != h)){
143                                 this.updateContentHeight(h - oh)
144                                 this.repositionPages(inPageIndex);
145                         }
146                 }
147         },
148         rowHeightChanged: function(inRowIndex){
149                 this.updatePageHeight(Math.floor(inRowIndex / this.rowsPerPage));
150         },
151         // scroller core
152         invalidateNodes: function(){
153                 while(this.stack.length){
154                         this.destroyPage(this.popPage());
155                 }
156         },
157         createPageNode: function(){
158                 var p = document.createElement('div');
159                 p.style.position = 'absolute';
160                 //p.style.width = '100%';
161                 p.style[dojo._isBodyLtr() ? "left" : "right"] = '0';
162                 return p;
163         },
164         getPageHeight: function(inPageIndex){
165                 var ph = this.pageHeights[inPageIndex];
166                 return (ph !== undefined ? ph : this.defaultPageHeight);
167         },
168         // FIXME: this is not a stack, it's a FIFO list
169         pushPage: function(inPageIndex){
170                 return this.stack.push(inPageIndex);
171         },
172         popPage: function(){
173                 return this.stack.shift();
174         },
175         findPage: function(inTop){
176                 var i = 0, h = 0;
177                 for(var ph = 0; i<this.pageCount; i++, h += ph){
178                         ph = this.getPageHeight(i);
179                         if(h + ph >= inTop){
180                                 break;
181                         }
182                 }
183                 this.page = i;
184                 this.pageTop = h;
185         },
186         buildPage: function(inPageIndex, inReuseNode, inPos){
187                 this.preparePage(inPageIndex, inReuseNode);
188                 this.positionPage(inPageIndex, inPos);
189                 // order of operations is key below
190                 this.installPage(inPageIndex);
191                 this.renderPage(inPageIndex);
192                 // order of operations is key above
193                 this.pushPage(inPageIndex);
194         },
195         needPage: function(inPageIndex, inPos){
196                 var h = this.getPageHeight(inPageIndex), oh = h;
197                 if(!this.pageExists(inPageIndex)){
198                         this.buildPage(inPageIndex, this.keepPages&&(this.stack.length >= this.keepPages), inPos);
199                         h = this.measurePage(inPageIndex) || h;
200                         this.pageHeights[inPageIndex] = h;
201                         if(h && (oh != h)){
202                                 this.updateContentHeight(h - oh)
203                         }
204                 }else{
205                         this.positionPage(inPageIndex, inPos);
206                 }
207                 return h;
208         },
209         onscroll: function(){
210                 this.scroll(this.scrollboxNode.scrollTop);
211         },
212         scroll: function(inTop){
213                 this.startPacify();
214                 this.findPage(inTop);
215                 var h = this.height;
216                 var b = this.getScrollBottom(inTop);
217                 for(var p=this.page, y=this.pageTop; (p<this.pageCount)&&((b<0)||(y<b)); p++){
218                         y += this.needPage(p, y);
219                 }
220                 this.firstVisibleRow = this.getFirstVisibleRow(this.page, this.pageTop, inTop);
221                 this.lastVisibleRow = this.getLastVisibleRow(p - 1, y, b);
222                 // indicates some page size has been updated
223                 if(h != this.height){
224                         this.repositionPages(p-1);
225                 }
226                 this.endPacify();
227         },
228         getScrollBottom: function(inTop){
229                 return (this.windowHeight >= 0 ? inTop + this.windowHeight : -1);
230         },
231         // events
232         processNodeEvent: function(e, inNode){
233                 var t = e.target;
234                 while(t && (t != inNode) && t.parentNode && (t.parentNode.parentNode != inNode)){
235                         t = t.parentNode;
236                 }
237                 if(!t || !t.parentNode || (t.parentNode.parentNode != inNode)){
238                         return false;
239                 }
240                 var page = t.parentNode;
241                 e.topRowIndex = page.pageIndex * this.rowsPerPage;
242                 e.rowIndex = e.topRowIndex + dojox.grid.indexInParent(t);
243                 e.rowTarget = t;
244                 return true;
245         },
246         processEvent: function(e){
247                 return this.processNodeEvent(e, this.contentNode);
248         },
249         dummy: 0
250 });
251
252 dojo.declare('dojox.grid.scroller', dojox.grid.scroller.base, {
253         // summary:
254         //      virtual scroller class, makes no assumption about shape of items being scrolled
255         constructor: function(){
256                 this.pageNodes = [];
257         },
258         // virtual rendering interface
259         renderRow: function(inRowIndex, inPageNode){
260         },
261         removeRow: function(inRowIndex){
262         },
263         // page node operations
264         getDefaultNodes: function(){
265                 return this.pageNodes;
266         },
267         getDefaultPageNode: function(inPageIndex){
268                 return this.getDefaultNodes()[inPageIndex];
269         },
270         positionPageNode: function(inNode, inPos){
271                 inNode.style.top = inPos + 'px';
272         },
273         getPageNodePosition: function(inNode){
274                 return inNode.offsetTop;
275         },
276         repositionPageNodes: function(inPageIndex, inNodes){
277                 var last = 0;
278                 for(var i=0; i<this.stack.length; i++){
279                         last = Math.max(this.stack[i], last);
280                 }
281                 //
282                 var n = inNodes[inPageIndex];
283                 var y = (n ? this.getPageNodePosition(n) + this.getPageHeight(inPageIndex) : 0);
284                 //console.log('detected height change, repositioning from #%d (%d) @ %d ', inPageIndex + 1, last, y, this.pageHeights[0]);
285                 //
286                 for(var p=inPageIndex+1; p<=last; p++){
287                         n = inNodes[p];
288                         if(n){
289                                 //console.log('#%d @ %d', inPageIndex, y, this.getPageNodePosition(n));
290                                 if(this.getPageNodePosition(n) == y){
291                                         return;
292                                 }
293                                 //console.log('placing page %d at %d', p, y);
294                                 this.positionPage(p, y);
295                         }
296                         y += this.getPageHeight(p);
297                 }
298         },
299         invalidatePageNode: function(inPageIndex, inNodes){
300                 var p = inNodes[inPageIndex];
301                 if(p){
302                         delete inNodes[inPageIndex];
303                         this.removePage(inPageIndex, p);
304                         dojox.grid.cleanNode(p);
305                         p.innerHTML = '';
306                 }
307                 return p;
308         },
309         preparePageNode: function(inPageIndex, inReusePageIndex, inNodes){
310                 var p = (inReusePageIndex === null ? this.createPageNode() : this.invalidatePageNode(inReusePageIndex, inNodes));
311                 p.pageIndex = inPageIndex;
312                 p.id = (this._pageIdPrefix || "") + 'page-' + inPageIndex;
313                 inNodes[inPageIndex] = p;
314         },
315         // implementation for page manager
316         pageExists: function(inPageIndex){
317                 return Boolean(this.getDefaultPageNode(inPageIndex));
318         },
319         measurePage: function(inPageIndex){
320                 return this.getDefaultPageNode(inPageIndex).offsetHeight;
321         },
322         positionPage: function(inPageIndex, inPos){
323                 this.positionPageNode(this.getDefaultPageNode(inPageIndex), inPos);
324         },
325         repositionPages: function(inPageIndex){
326                 this.repositionPageNodes(inPageIndex, this.getDefaultNodes());
327         },
328         preparePage: function(inPageIndex, inReuseNode){
329                 this.preparePageNode(inPageIndex, (inReuseNode ? this.popPage() : null), this.getDefaultNodes());
330         },
331         installPage: function(inPageIndex){
332                 this.contentNode.appendChild(this.getDefaultPageNode(inPageIndex));
333         },
334         destroyPage: function(inPageIndex){
335                 var p = this.invalidatePageNode(inPageIndex, this.getDefaultNodes());
336                 dojox.grid.removeNode(p);
337         },
338         // rendering implementation
339         renderPage: function(inPageIndex){
340                 var node = this.pageNodes[inPageIndex];
341                 for(var i=0, j=inPageIndex*this.rowsPerPage; (i<this.rowsPerPage)&&(j<this.rowCount); i++, j++){
342                         this.renderRow(j, node);
343                 }
344         },
345         removePage: function(inPageIndex){
346                 for(var i=0, j=inPageIndex*this.rowsPerPage; i<this.rowsPerPage; i++, j++){
347                         this.removeRow(j);
348                 }
349         },
350         // scroll control
351         getPageRow: function(inPage){
352                 return inPage * this.rowsPerPage;
353         },
354         getLastPageRow: function(inPage){
355                 return Math.min(this.rowCount, this.getPageRow(inPage + 1)) - 1;
356         },
357         getFirstVisibleRowNodes: function(inPage, inPageTop, inScrollTop, inNodes){
358                 var row = this.getPageRow(inPage);
359                 var rows = dojox.grid.divkids(inNodes[inPage]);
360                 for(var i=0,l=rows.length; i<l && inPageTop<inScrollTop; i++, row++){
361                         inPageTop += rows[i].offsetHeight;
362                 }
363                 return (row ? row - 1 : row);
364         },
365         getFirstVisibleRow: function(inPage, inPageTop, inScrollTop){
366                 if(!this.pageExists(inPage)){
367                         return 0;
368                 }
369                 return this.getFirstVisibleRowNodes(inPage, inPageTop, inScrollTop, this.getDefaultNodes());
370         },
371         getLastVisibleRowNodes: function(inPage, inBottom, inScrollBottom, inNodes){
372                 var row = this.getLastPageRow(inPage);
373                 var rows = dojox.grid.divkids(inNodes[inPage]);
374                 for(var i=rows.length-1; i>=0 && inBottom>inScrollBottom; i--, row--){
375                         inBottom -= rows[i].offsetHeight;
376                 }
377                 return row + 1;
378         },
379         getLastVisibleRow: function(inPage, inBottom, inScrollBottom){
380                 if(!this.pageExists(inPage)){
381                         return 0;
382                 }
383                 return this.getLastVisibleRowNodes(inPage, inBottom, inScrollBottom, this.getDefaultNodes());
384         },
385         findTopRowForNodes: function(inScrollTop, inNodes){
386                 var rows = dojox.grid.divkids(inNodes[this.page]);
387                 for(var i=0,l=rows.length,t=this.pageTop,h; i<l; i++){
388                         h = rows[i].offsetHeight;
389                         t += h;
390                         if(t >= inScrollTop){
391                                 this.offset = h - (t - inScrollTop);
392                                 return i + this.page * this.rowsPerPage;
393                         }
394                 }
395                 return -1;
396         },
397         findScrollTopForNodes: function(inRow, inNodes){
398                 var rowPage = Math.floor(inRow / this.rowsPerPage);
399                 var t = 0;
400                 for(var i=0; i<rowPage; i++){
401                         t += this.getPageHeight(i);
402                 }
403                 this.pageTop = t;
404                 this.needPage(rowPage, this.pageTop);
405                 var rows = dojox.grid.divkids(inNodes[rowPage]);
406                 var r = inRow - this.rowsPerPage * rowPage;
407                 for(var i=0,l=rows.length; i<l && i<r; i++){
408                         t += rows[i].offsetHeight;
409                 }
410                 return t;
411         },
412         findTopRow: function(inScrollTop){
413                 return this.findTopRowForNodes(inScrollTop, this.getDefaultNodes());
414         },
415         findScrollTop: function(inRow){
416                 return this.findScrollTopForNodes(inRow, this.getDefaultNodes());
417         },
418         dummy: 0
419 });
420
421 dojo.declare('dojox.grid.scroller.columns', dojox.grid.scroller, {
422         // summary:
423         //      Virtual scroller class that scrolls list of columns. Owned by grid and used internally
424         //      for virtual scrolling.
425         constructor: function(inContentNodes){
426                 this.setContentNodes(inContentNodes);
427         },
428         // nodes
429         setContentNodes: function(inNodes){
430                 this.contentNodes = inNodes;
431                 this.colCount = (this.contentNodes ? this.contentNodes.length : 0);
432                 this.pageNodes = [];
433                 for(var i=0; i<this.colCount; i++){
434                         this.pageNodes[i] = [];
435                 }
436         },
437         getDefaultNodes: function(){
438                 return this.pageNodes[0] || [];
439         },
440         scroll: function(inTop) {
441                 if(this.colCount){
442                         dojox.grid.scroller.prototype.scroll.call(this, inTop);
443                 }
444         },
445         // resize
446         resize: function(){
447                 if(this.scrollboxNode){
448                         this.windowHeight = this.scrollboxNode.clientHeight;
449                 }
450                 for(var i=0; i<this.colCount; i++){
451                         dojox.grid.setStyleHeightPx(this.contentNodes[i], this.height);
452                 }
453         },
454         // implementation for page manager
455         positionPage: function(inPageIndex, inPos){
456                 for(var i=0; i<this.colCount; i++){
457                         this.positionPageNode(this.pageNodes[i][inPageIndex], inPos);
458                 }
459         },
460         preparePage: function(inPageIndex, inReuseNode){
461                 var p = (inReuseNode ? this.popPage() : null);
462                 for(var i=0; i<this.colCount; i++){
463                         this.preparePageNode(inPageIndex, p, this.pageNodes[i]);
464                 }
465         },
466         installPage: function(inPageIndex){
467                 for(var i=0; i<this.colCount; i++){
468                         this.contentNodes[i].appendChild(this.pageNodes[i][inPageIndex]);
469                 }
470         },
471         destroyPage: function(inPageIndex){
472                 for(var i=0; i<this.colCount; i++){
473                         dojox.grid.removeNode(this.invalidatePageNode(inPageIndex, this.pageNodes[i]));
474                 }
475         },
476         // rendering implementation
477         renderPage: function(inPageIndex){
478                 var nodes = [];
479                 for(var i=0; i<this.colCount; i++){
480                         nodes[i] = this.pageNodes[i][inPageIndex];
481                 }
482                 //this.renderRows(inPageIndex*this.rowsPerPage, this.rowsPerPage, nodes);
483                 for(var i=0, j=inPageIndex*this.rowsPerPage; (i<this.rowsPerPage)&&(j<this.rowCount); i++, j++){
484                         this.renderRow(j, nodes);
485                 }
486         }
487 });
488
489 }