Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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