123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- if(!dojo._hasResource["dojox.grid._Scroller"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
- dojo._hasResource["dojox.grid._Scroller"] = true;
- 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])){
- 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;
- default: break;
- }
- 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.keepPages : 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.colCount; i++){
- this.pageNodes[i] = [];
- }
- },
- getDefaultNodes: function(){
- return this.pageNodes[0] || [];
- },
- // updating
- invalidate: function(){
- this._invalidating = true;
- this.invalidateNodes();
- this.pageHeights = [];
- this.height = (this.pageCount ? (this.pageCount - 1)* this.defaultPageHeight + this.calcLastPageHeight() : 0);
- this.resize();
- this._invalidating = false;
- },
- updateRowCount: function(inRowCount){
- this.invalidateNodes();
- this.rowCount = inRowCount;
- // update page count, adjust document height
- var oldPageCount = this.pageCount;
- if(oldPageCount === 0){
- //We want to have at least 1px in height to keep scroller. Otherwise with an
- //empty grid you can't scroll to see the header.
- this.height = 1;
- }
- this.pageCount = this._getPageCount(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();
- },
- // implementation for page manager
- pageExists: function(inPageIndex){
- return Boolean(this.getDefaultPageNode(inPageIndex));
- },
- measurePage: function(inPageIndex){
- if(this.grid.rowHeight){
- var height = this.grid.rowHeight + 1;
- return ((inPageIndex + 1) * this.rowsPerPage > this.rowCount ?
- this.rowCount - inPageIndex * this.rowsPerPage :
- this.rowsPerPage) * height;
-
- }
- var n = this.getDefaultPageNode(inPageIndex);
- return (n && n.innerHTML) ? n.offsetHeight : undefined;
- },
- positionPage: function(inPageIndex, inPos){
- for(var i=0; i<this.colCount; i++){
- this.pageNodes[i][inPageIndex].style.top = inPos + 'px';
- }
- },
- repositionPages: function(inPageIndex){
- var nodes = this.getDefaultNodes();
- var last = 0;
- for(var i=0; i<this.stack.length; i++){
- last = Math.max(this.stack[i], last);
- }
- //
- var n = nodes[inPageIndex];
- var y = (n ? this.getPageNodePosition(n) + this.getPageHeight(inPageIndex) : 0);
- for(var p=inPageIndex+1; p<=last; p++){
- n = nodes[p];
- if(n){
- if(this.getPageNodePosition(n) == y){
- return;
- }
- this.positionPage(p, y);
- }
- y += this.getPageHeight(p);
- }
- },
- installPage: function(inPageIndex){
- for(var i=0; i<this.colCount; i++){
- this.contentNodes[i].appendChild(this.pageNodes[i][inPageIndex]);
- }
- },
- preparePage: function(inPageIndex, inReuseNode){
- var p = (inReuseNode ? this.popPage() : null);
- for(var i=0; i<this.colCount; i++){
- var nodes = this.pageNodes[i];
- var new_p = (p === null ? this.createPageNode() : this.invalidatePageNode(p, nodes));
- new_p.pageIndex = inPageIndex;
- nodes[inPageIndex] = new_p;
- }
- },
- // rendering implementation
- renderPage: function(inPageIndex){
- var nodes = [];
- var i, j;
- for(i=0; i<this.colCount; i++){
- nodes[i] = this.pageNodes[i][inPageIndex];
- }
- for(i=0, j=inPageIndex*this.rowsPerPage; (i<this.rowsPerPage)&&(j<this.rowCount); i++, j++){
- this.renderRow(j, nodes);
- }
- },
- removePage: function(inPageIndex){
- for(var i=0, j=inPageIndex*this.rowsPerPage; i<this.rowsPerPage; i++, j++){
- this.removeRow(j);
- }
- },
- destroyPage: function(inPageIndex){
- for(var i=0; i<this.colCount; i++){
- var n = this.invalidatePageNode(inPageIndex, this.pageNodes[i]);
- if(n){
- dojo.destroy(n);
- }
- }
- },
- 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;
- }
- for(var i=0; i<this.colCount; i++){
- //We want to have 1px in height min to keep scroller. Otherwise can't scroll
- //and see header in empty grid.
- dojox.grid.util.setStyleHeightPx(this.contentNodes[i], Math.max(1,this.height));
- }
-
- // Calculate the average row height and update the defaults (row and page).
- var needPage = (!this._invalidating);
- if(!needPage){
- var ah = this.grid.get("autoHeight");
- if(typeof ah == "number" && ah <= Math.min(this.rowsPerPage, this.rowCount)){
- needPage = true;
- }
- }
- if(needPage){
- this.needPage(this.page, this.pageTop);
- }
- var rowsOnPage = (this.page < this.pageCount - 1) ? this.rowsPerPage : ((this.rowCount % this.rowsPerPage) || this.rowsPerPage);
- var pageHeight = this.getPageHeight(this.page);
- this.averageRowHeight = (pageHeight > 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, fromBuild, fromAsynRendering){
- if(this.pageExists(inPageIndex)){
- var oh = this.getPageHeight(inPageIndex);
- var h = (this.measurePage(inPageIndex));
- if(h === undefined){
- h = oh;
- }
- this.pageHeights[inPageIndex] = h;
- if(oh != h){
- this.updateContentHeight(h - oh);
- var ah = this.grid.get("autoHeight");
- if((typeof ah == "number" && ah > this.rowCount)||(ah === true && !fromBuild)){
- if(!fromAsynRendering){
- this.grid.sizeChange();
- }else{//fix #11101 by using fromAsynRendering to avoid deadlock
- var ns = this.grid.viewsNode.style;
- ns.height = parseInt(ns.height) + h - oh + 'px';
- this.repositionPages(inPageIndex);
- }
- }else{
- this.repositionPages(inPageIndex);
- }
- }
- return h;
- }
- return 0;
- },
- rowHeightChanged: function(inRowIndex, fromAsynRendering){
- this.updatePageHeight(Math.floor(inRowIndex / this.rowsPerPage), false, fromAsynRendering);
- },
- // 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<this.pageCount; i++, h += ph){
- ph = this.getPageHeight(i);
- if(h + ph >= 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.grid._autoHeight/*fix #10543*/ && this.keepPages&&(this.stack.length >= this.keepPages)), inPos);
- h = this.updatePageHeight(inPageIndex, true);
- }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<this.pageCount)&&((b<0)||(y<b)); p++){
- y += this.needPage(p, y);
- }
- this.firstVisibleRow = this.getFirstVisibleRow(this.page, this.pageTop, inTop);
- this.lastVisibleRow = this.getLastVisibleRow(p - 1, y, b);
- // indicates some page size has been updated
- if(h != this.height){
- this.repositionPages(p-1);
- }
- this.endPacify();
- }
- },
- getScrollBottom: function(inTop){
- return (this.windowHeight >= 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<l && inPageTop<inScrollTop; i++, row++){
- inPageTop += rows[i].offsetHeight;
- }
- return (row ? row - 1 : row);
- },
- getLastVisibleRow: function(inPage, inBottom, inScrollBottom){
- if(!this.pageExists(inPage)){
- return 0;
- }
- var nodes = this.getDefaultNodes();
- var row = this.getLastPageRow(inPage);
- var rows = divkids(nodes[inPage]);
- for(var i=rows.length-1; 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<l; i++){
- h = rows[i].offsetHeight;
- t += h;
- if(t >= 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;
- var i, l;
- for(i=0; i<rowPage; i++){
- t += this.getPageHeight(i);
- }
- this.pageTop = t;
- this.page = rowPage;//fix #10543
- this.needPage(rowPage, this.pageTop);
- var nodes = this.getDefaultNodes();
- var rows = divkids(nodes[rowPage]);
- var r = inRow - this.rowsPerPage * rowPage;
- for(i=0,l=rows.length; i<l && i<r; i++){
- t += rows[i].offsetHeight;
- }
- return t;
- },
- dummy: 0
- });
- })();
- }
|