123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- 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.keepPages = Math.max(Math.ceil(this.keepRows
- / this.rowsPerPage), 2);
- this.invalidate();
- if (this.scrollboxNode) {
- this.scrollboxNode.scrollTop = 0;
- this.scroll(0);
- this.scrollboxNode.onscroll = dojo.hitch(this, 'onscroll');
- }
- },
- // 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 = allGetServerTime().getTime();
- },
- doPacify : function() {
- var result = (allGetServerTime().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.left = '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.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 < 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 + 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 < this.stack.length; i++) {
- last = Math.max(this.stack[i], last);
- }
- //
- var n = inNodes[inPageIndex];
- var y = (n ? this.getPageNodePosition(n)
- + this.getPageHeight(inPageIndex) : 0);
- // console.log('detected height change, repositioning from #%d (%d)
- // @ %d ', inPageIndex + 1, last, y, this.pageHeights[0]);
- //
- for (var p = inPageIndex + 1; p <= last; p++) {
- n = inNodes[p];
- if (n) {
- // console.log('#%d @ %d', inPageIndex, y,
- // this.getPageNodePosition(n));
- if (this.getPageNodePosition(n) == y) {
- return;
- }
- // console.log('placing page %d at %d', p, y);
- this.positionPage(p, y);
- }
- y += this.getPageHeight(p);
- }
- },
- invalidatePageNode : function(inPageIndex, inNodes) {
- var p = inNodes[inPageIndex];
- if (p) {
- delete inNodes[inPageIndex];
- this.removePage(inPageIndex, p);
- dojox.grid.cleanNode(p);
- p.innerHTML = '';
- }
- return p;
- },
- preparePageNode : function(inPageIndex, inReusePageIndex, inNodes) {
- var p = (inReusePageIndex === null ? this.createPageNode() : this
- .invalidatePageNode(inReusePageIndex, inNodes));
- p.pageIndex = inPageIndex;
- p.id = 'page-' + inPageIndex;
- inNodes[inPageIndex] = p;
- },
- // implementation for page manager
- pageExists : function(inPageIndex) {
- return Boolean(this.getDefaultPageNode(inPageIndex));
- },
- measurePage : function(inPageIndex) {
- return this.getDefaultPageNode(inPageIndex).offsetHeight;
- },
- positionPage : function(inPageIndex, inPos) {
- this.positionPageNode(this.getDefaultPageNode(inPageIndex), inPos);
- },
- repositionPages : function(inPageIndex) {
- this.repositionPageNodes(inPageIndex, this.getDefaultNodes());
- },
- preparePage : function(inPageIndex, inReuseNode) {
- this.preparePageNode(inPageIndex, (inReuseNode
- ? this.popPage()
- : null), this.getDefaultNodes());
- },
- installPage : function(inPageIndex) {
- this.contentNode.appendChild(this.getDefaultPageNode(inPageIndex));
- },
- destroyPage : function(inPageIndex) {
- var p = this
- .invalidatePageNode(inPageIndex, this.getDefaultNodes());
- dojox.grid.removeNode(p);
- },
- // rendering implementation
- renderPage : function(inPageIndex) {
- var node = this.pageNodes[inPageIndex];
- for (var i = 0, j = inPageIndex * this.rowsPerPage; (i < this.rowsPerPage)
- && (j < this.rowCount); i++, j++) {
- this.renderRow(j, node);
- }
- },
- removePage : function(inPageIndex) {
- for (var i = 0, j = inPageIndex * this.rowsPerPage; i < this.rowsPerPage; i++, j++) {
- this.removeRow(j);
- }
- },
- // scroll control
- getPageRow : function(inPage) {
- return inPage * this.rowsPerPage;
- },
- getLastPageRow : function(inPage) {
- return Math.min(this.rowCount, this.getPageRow(inPage + 1)) - 1;
- },
- getFirstVisibleRowNodes : function(inPage, inPageTop, inScrollTop,
- inNodes) {
- var row = this.getPageRow(inPage);
- var rows = dojox.grid.divkids(inNodes[inPage]);
- for (var i = 0, l = rows.length; i < l && inPageTop < inScrollTop; i++, row++) {
- inPageTop += rows[i].offsetHeight;
- }
- return (row ? row - 1 : row);
- },
- getFirstVisibleRow : function(inPage, inPageTop, inScrollTop) {
- if (!this.pageExists(inPage)) {
- return 0;
- }
- return this.getFirstVisibleRowNodes(inPage, inPageTop, inScrollTop,
- this.getDefaultNodes());
- },
- getLastVisibleRowNodes : function(inPage, inBottom, inScrollBottom,
- inNodes) {
- var row = this.getLastPageRow(inPage);
- var rows = dojox.grid.divkids(inNodes[inPage]);
- for (var i = rows.length - 1; 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 < l; i++) {
- h = rows[i].offsetHeight;
- t += h;
- if (t >= 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 < rowPage; i++) {
- t += this.getPageHeight(i);
- }
- this.pageTop = t;
- this.needPage(rowPage, this.pageTop);
- var rows = dojox.grid.divkids(inNodes[rowPage]);
- var r = inRow - this.rowsPerPage * rowPage;
- for (var i = 0, l = rows.length; i < l && i < r; i++) {
- t += rows[i].offsetHeight;
- }
- return t;
- },
- findTopRow : function(inScrollTop) {
- return this.findTopRowForNodes(inScrollTop, this.getDefaultNodes());
- },
- findScrollTop : function(inRow) {
- return this.findScrollTopForNodes(inRow, this.getDefaultNodes());
- },
- dummy : 0
- });
- dojo.declare('dojox.grid.scroller.columns', dojox.grid.scroller, {
- // summary:
- // Virtual scroller class that scrolls list of columns. Owned by grid
- // and used internally
- // for virtual scrolling.
- constructor : function(inContentNodes) {
- this.setContentNodes(inContentNodes);
- },
- // 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] || [];
- },
- scroll : function(inTop) {
- if (this.colCount) {
- dojox.grid.scroller.prototype.scroll.call(this, inTop);
- }
- },
- // resize
- resize : function() {
- if (this.scrollboxNode) {
- this.windowHeight = this.scrollboxNode.clientHeight;
- }
- for (var i = 0; i < this.colCount; i++) {
- dojox.grid.setStyleHeightPx(this.contentNodes[i], this.height);
- }
- },
- // implementation for page manager
- positionPage : function(inPageIndex, inPos) {
- for (var i = 0; i < this.colCount; i++) {
- this.positionPageNode(this.pageNodes[i][inPageIndex], inPos);
- }
- },
- preparePage : function(inPageIndex, inReuseNode) {
- var p = (inReuseNode ? this.popPage() : null);
- for (var i = 0; i < this.colCount; i++) {
- this.preparePageNode(inPageIndex, p, this.pageNodes[i]);
- }
- },
- installPage : function(inPageIndex) {
- for (var i = 0; i < this.colCount; i++) {
- this.contentNodes[i]
- .appendChild(this.pageNodes[i][inPageIndex]);
- }
- },
- destroyPage : function(inPageIndex) {
- for (var i = 0; i < this.colCount; i++) {
- dojox.grid.removeNode(this.invalidatePageNode(inPageIndex,
- this.pageNodes[i]));
- }
- },
- // rendering implementation
- renderPage : function(inPageIndex) {
- var nodes = [];
- for (var i = 0; i < this.colCount; i++) {
- nodes[i] = this.pageNodes[i][inPageIndex];
- }
- // this.renderRows(inPageIndex*this.rowsPerPage, this.rowsPerPage,
- // nodes);
- for (var i = 0, j = inPageIndex * this.rowsPerPage; (i < this.rowsPerPage)
- && (j < this.rowCount); i++, j++) {
- this.renderRow(j, nodes);
- }
- }
- });
- }
|