dojo.provide("dojox.grid._Scroller"); (function(){ var indexInParent = function(inNode){ var i=0, n, p=inNode.parentNode; while((n = p.childNodes[i++])){ if(n == inNode){ return i - 1; } } return -1; }; var cleanNode = function(inNode){ if(!inNode){ return; } var filter = function(inW){ return inW.domNode && dojo.isDescendant(inW.domNode, inNode, true); } var ws = dijit.registry.filter(filter); for(var i=0, w; (w=ws[i]); i++){ w.destroy(); } delete ws; }; var getTagName = function(inNodeOrId){ var node = dojo.byId(inNodeOrId); return (node && node.tagName ? node.tagName.toLowerCase() : ''); }; var nodeKids = function(inNode, inTag){ var result = []; var i=0, n; while((n = inNode.childNodes[i++])){ if(getTagName(n) == inTag){ result.push(n); } } return result; }; var divkids = function(inNode){ return nodeKids(inNode, 'div'); }; dojo.declare("dojox.grid._Scroller", null, { constructor: function(inContentNodes){ this.setContentNodes(inContentNodes); this.pageHeights = []; this.pageNodes = []; this.stack = []; }, // specified rowCount: 0, // total number of rows to manage defaultRowHeight: 32, // 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, averageRowHeight: 0, // the average height of a row // 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.pageCount = this._getPageCount(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'); } }, _getPageCount: function(rowCount, rowsPerPage){ return rowCount ? (Math.ceil(rowCount / rowsPerPage) || 1) : 0; }, destroy: function(){ this.invalidateNodes(); delete this.contentNodes; delete this.contentNode; delete this.scrollboxNode; }, setKeepInfo: function(inKeepRows){ this.keepRows = inKeepRows; this.keepPages = !this.keepRows ? this.keepRows : Math.max(Math.ceil(this.keepRows / this.rowsPerPage), 2); }, // nodes setContentNodes: function(inNodes){ this.contentNodes = inNodes; this.colCount = (this.contentNodes ? this.contentNodes.length : 0); this.pageNodes = []; for(var i=0; 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(); }, // implementation for page manager pageExists: function(inPageIndex){ return Boolean(this.getDefaultPageNode(inPageIndex)); }, measurePage: function(inPageIndex){ var n = this.getDefaultPageNode(inPageIndex); return (n&&n.innerHTML) ? n.offsetHeight : 0; }, positionPage: function(inPageIndex, inPos){ for(var i=0; i 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; } for(var i=0; i 0 && rowsOnPage > 0) ? (pageHeight / rowsOnPage) : 0; }, 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'); dojo.attr(p,"role","presentation"); 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.grid.scrollTop = inTop; if(this.colCount){ 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 + indexInParent(t); e.rowTarget = t; return true; }, processEvent: function(e){ return this.processNodeEvent(e, this.contentNode); }, // virtual rendering interface renderRow: function(inRowIndex, inPageNode){ }, removeRow: function(inRowIndex){ }, // page node operations getDefaultPageNode: function(inPageIndex){ return this.getDefaultNodes()[inPageIndex]; }, positionPageNode: function(inNode, inPos){ }, getPageNodePosition: function(inNode){ return inNode.offsetTop; }, invalidatePageNode: function(inPageIndex, inNodes){ var p = inNodes[inPageIndex]; if(p){ delete inNodes[inPageIndex]; this.removePage(inPageIndex, p); cleanNode(p); p.innerHTML = ''; } return p; }, // scroll control getPageRow: function(inPage){ return inPage * this.rowsPerPage; }, getLastPageRow: function(inPage){ return Math.min(this.rowCount, this.getPageRow(inPage + 1)) - 1; }, getFirstVisibleRow: function(inPage, inPageTop, inScrollTop){ if(!this.pageExists(inPage)){ return 0; } var row = this.getPageRow(inPage); var nodes = this.getDefaultNodes(); var rows = divkids(nodes[inPage]); for(var i=0,l=rows.length; i=0 && inBottom>inScrollBottom; i--, row--){ inBottom -= rows[i].offsetHeight; } return row + 1; }, findTopRow: function(inScrollTop){ var nodes = this.getDefaultNodes(); var rows = divkids(nodes[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; }, findScrollTop: function(inRow){ var rowPage = Math.floor(inRow / this.rowsPerPage); var t = 0; for(var i=0; i