if(!dojo._hasResource['dojox.grid._grid.scroller']){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource['dojox.grid._grid.scroller'] = true; dojo.provide('dojox.grid._grid.scroller'); dojo.declare('dojox.grid.scroller.base', null, { // summary: // virtual scrollbox, abstract class // Content must in /rows/ // Rows are managed in contiguous sets called /pages/ // There are a fixed # of rows per page // The minimum rendered unit is a page constructor: function(){ this.pageHeights = []; this.stack = []; }, // specified rowCount: 0, // total number of rows to manage defaultRowHeight: 10, // default height of a row keepRows: 100, // maximum number of rows that should exist at one time contentNode: null, // node to contain pages scrollboxNode: null, // node that controls scrolling // calculated defaultPageHeight: 0, // default height of a page keepPages: 10, // maximum number of pages that should exists at one time pageCount: 0, windowHeight: 0, firstVisibleRow: 0, lastVisibleRow: 0, // private page: 0, pageTop: 0, // init init: function(inRowCount, inKeepRows, inRowsPerPage){ switch(arguments.length){ case 3: this.rowsPerPage = inRowsPerPage; case 2: this.keepRows = inKeepRows; case 1: this.rowCount = inRowCount; } this.defaultPageHeight = this.defaultRowHeight * this.rowsPerPage; //this.defaultPageHeight = this.defaultRowHeight * Math.min(this.rowsPerPage, this.rowCount); this.pageCount = Math.ceil(this.rowCount / this.rowsPerPage); this.setKeepInfo(this.keepRows); this.invalidate(); if(this.scrollboxNode){ this.scrollboxNode.scrollTop = 0; this.scroll(0); this.scrollboxNode.onscroll = dojo.hitch(this, 'onscroll'); } }, setKeepInfo: function(inKeepRows){ this.keepRows = inKeepRows; this.keepPages = !this.keepRows ? this.keepRows : Math.max(Math.ceil(this.keepRows / this.rowsPerPage), 2); }, // updating invalidate: function(){ this.invalidateNodes(); this.pageHeights = []; this.height = (this.pageCount ? (this.pageCount - 1)* this.defaultPageHeight + this.calcLastPageHeight() : 0); this.resize(); }, updateRowCount: function(inRowCount){ this.invalidateNodes(); this.rowCount = inRowCount; // update page count, adjust document height oldPageCount = this.pageCount; this.pageCount = Math.ceil(this.rowCount / this.rowsPerPage); if(this.pageCount < oldPageCount){ for(var i=oldPageCount-1; i>=this.pageCount; i--){ this.height -= this.getPageHeight(i); delete this.pageHeights[i] } }else if(this.pageCount > oldPageCount){ this.height += this.defaultPageHeight * (this.pageCount - oldPageCount - 1) + this.calcLastPageHeight(); } this.resize(); }, // abstract interface pageExists: function(inPageIndex){ }, measurePage: function(inPageIndex){ }, positionPage: function(inPageIndex, inPos){ }, repositionPages: function(inPageIndex){ }, installPage: function(inPageIndex){ }, preparePage: function(inPageIndex, inPos, inReuseNode){ }, renderPage: function(inPageIndex){ }, removePage: function(inPageIndex){ }, pacify: function(inShouldPacify){ }, // pacification pacifying: false, pacifyTicks: 200, setPacifying: function(inPacifying){ if(this.pacifying != inPacifying){ this.pacifying = inPacifying; this.pacify(this.pacifying); } }, startPacify: function(){ this.startPacifyTicks = new Date().getTime(); }, doPacify: function(){ var result = (new Date().getTime() - this.startPacifyTicks) > this.pacifyTicks; this.setPacifying(true); this.startPacify(); return result; }, endPacify: function(){ this.setPacifying(false); }, // default sizing implementation resize: function(){ if(this.scrollboxNode){ this.windowHeight = this.scrollboxNode.clientHeight; } dojox.grid.setStyleHeightPx(this.contentNode, this.height); }, calcLastPageHeight: function(){ if(!this.pageCount){ return 0; } var lastPage = this.pageCount - 1; var lastPageHeight = ((this.rowCount % this.rowsPerPage)||(this.rowsPerPage)) * this.defaultRowHeight; this.pageHeights[lastPage] = lastPageHeight; return lastPageHeight; }, updateContentHeight: function(inDh){ this.height += inDh; this.resize(); }, updatePageHeight: function(inPageIndex){ if(this.pageExists(inPageIndex)){ var oh = this.getPageHeight(inPageIndex); var h = (this.measurePage(inPageIndex))||(oh); this.pageHeights[inPageIndex] = h; if((h)&&(oh != h)){ this.updateContentHeight(h - oh) this.repositionPages(inPageIndex); } } }, rowHeightChanged: function(inRowIndex){ this.updatePageHeight(Math.floor(inRowIndex / this.rowsPerPage)); }, // scroller core invalidateNodes: function(){ while(this.stack.length){ this.destroyPage(this.popPage()); } }, createPageNode: function(){ var p = document.createElement('div'); p.style.position = 'absolute'; //p.style.width = '100%'; p.style[dojo._isBodyLtr() ? "left" : "right"] = '0'; return p; }, getPageHeight: function(inPageIndex){ var ph = this.pageHeights[inPageIndex]; return (ph !== undefined ? ph : this.defaultPageHeight); }, // FIXME: this is not a stack, it's a FIFO list pushPage: function(inPageIndex){ return this.stack.push(inPageIndex); }, popPage: function(){ return this.stack.shift(); }, findPage: function(inTop){ var i = 0, h = 0; for(var ph = 0; i= inTop){ break; } } this.page = i; this.pageTop = h; }, buildPage: function(inPageIndex, inReuseNode, inPos){ this.preparePage(inPageIndex, inReuseNode); this.positionPage(inPageIndex, inPos); // order of operations is key below this.installPage(inPageIndex); this.renderPage(inPageIndex); // order of operations is key above this.pushPage(inPageIndex); }, needPage: function(inPageIndex, inPos){ var h = this.getPageHeight(inPageIndex), oh = h; if(!this.pageExists(inPageIndex)){ this.buildPage(inPageIndex, this.keepPages&&(this.stack.length >= this.keepPages), inPos); h = this.measurePage(inPageIndex) || h; this.pageHeights[inPageIndex] = h; if(h && (oh != h)){ this.updateContentHeight(h - oh) } }else{ this.positionPage(inPageIndex, inPos); } return h; }, onscroll: function(){ this.scroll(this.scrollboxNode.scrollTop); }, scroll: function(inTop){ this.startPacify(); this.findPage(inTop); var h = this.height; var b = this.getScrollBottom(inTop); for(var p=this.page, y=this.pageTop; (p= 0 ? inTop + this.windowHeight : -1); }, // events processNodeEvent: function(e, inNode){ var t = e.target; while(t && (t != inNode) && t.parentNode && (t.parentNode.parentNode != inNode)){ t = t.parentNode; } if(!t || !t.parentNode || (t.parentNode.parentNode != inNode)){ return false; } var page = t.parentNode; e.topRowIndex = page.pageIndex * this.rowsPerPage; e.rowIndex = e.topRowIndex + dojox.grid.indexInParent(t); e.rowTarget = t; return true; }, processEvent: function(e){ return this.processNodeEvent(e, this.contentNode); }, dummy: 0 }); dojo.declare('dojox.grid.scroller', dojox.grid.scroller.base, { // summary: // virtual scroller class, makes no assumption about shape of items being scrolled constructor: function(){ this.pageNodes = []; }, // virtual rendering interface renderRow: function(inRowIndex, inPageNode){ }, removeRow: function(inRowIndex){ }, // page node operations getDefaultNodes: function(){ return this.pageNodes; }, getDefaultPageNode: function(inPageIndex){ return this.getDefaultNodes()[inPageIndex]; }, positionPageNode: function(inNode, inPos){ inNode.style.top = inPos + 'px'; }, getPageNodePosition: function(inNode){ return inNode.offsetTop; }, repositionPageNodes: function(inPageIndex, inNodes){ var last = 0; for(var i=0; i=0 && inBottom>inScrollBottom; i--, row--){ inBottom -= rows[i].offsetHeight; } return row + 1; }, getLastVisibleRow: function(inPage, inBottom, inScrollBottom){ if(!this.pageExists(inPage)){ return 0; } return this.getLastVisibleRowNodes(inPage, inBottom, inScrollBottom, this.getDefaultNodes()); }, findTopRowForNodes: function(inScrollTop, inNodes){ var rows = dojox.grid.divkids(inNodes[this.page]); for(var i=0,l=rows.length,t=this.pageTop,h; i= inScrollTop){ this.offset = h - (t - inScrollTop); return i + this.page * this.rowsPerPage; } } return -1; }, findScrollTopForNodes: function(inRow, inNodes){ var rowPage = Math.floor(inRow / this.rowsPerPage); var t = 0; for(var i=0; i