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.VirtualGrid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.grid.VirtualGrid"] = true;
3
dojo.provide("dojox.grid.VirtualGrid");
4
dojo.require("dojox.grid._grid.lib");
5
dojo.require("dojox.grid._grid.scroller");
6
dojo.require("dojox.grid._grid.view");
7
dojo.require("dojox.grid._grid.views");
8
dojo.require("dojox.grid._grid.layout");
9
dojo.require("dojox.grid._grid.rows");
10
dojo.require("dojox.grid._grid.focus");
11
dojo.require("dojox.grid._grid.selection");
12
dojo.require("dojox.grid._grid.edit");
13
dojo.require("dojox.grid._grid.rowbar");
14
dojo.require("dojox.grid._grid.publicEvents");
15
 
16
dojo.declare('dojox.VirtualGrid',
17
	[ dijit._Widget, dijit._Templated ],
18
{
19
	// summary:
20
	// 		A grid widget with virtual scrolling, cell editing, complex rows,
21
	// 		sorting, fixed columns, sizeable columns, etc.
22
	//
23
	//	description:
24
	//		VirtualGrid provides the full set of grid features without any
25
	//		direct connection to a data store.
26
	//
27
	//		The grid exposes a get function for the grid, or optionally
28
	//		individual columns, to populate cell contents.
29
	//
30
	//		The grid is rendered based on its structure, an object describing
31
	//		column and cell layout.
32
	//
33
	//	example:
34
	//		A quick sample:
35
	//
36
	//		define a get function
37
	//	|	function get(inRowIndex){ // called in cell context
38
	//	|		return [this.index, inRowIndex].join(', ');
39
	//	|	}
40
	//
41
	//		define the grid structure:
42
	//	|	var structure = [ // array of view objects
43
	//	|		{ cells: [// array of rows, a row is an array of cells
44
	//	|			[
45
	//	|				{ name: "Alpha", width: 6 },
46
	//	|				{ name: "Beta" },
47
	//	|				{ name: "Gamma", get: get }]
48
	//	|		]}
49
	//	|	];
50
	//
51
	//	|	<div id="grid"
52
	//	|		rowCount="100" get="get"
53
	//	|		structure="structure"
54
	//	|		dojoType="dojox.VirtualGrid"></div>
55
 
56
	templateString: '<div class="dojoxGrid" hidefocus="hidefocus" role="wairole:grid"><div class="dojoxGrid-master-header" dojoAttachPoint="headerNode"></div><div class="dojoxGrid-master-view" dojoAttachPoint="viewsNode"></div><span dojoAttachPoint="lastFocusNode" tabindex="0"></span></div>',
57
	// classTag: string
58
	// css class applied to the grid's domNode
59
	classTag: 'dojoxGrid',
60
	get: function(inRowIndex){
61
		/* summary: Default data getter.
62
			description:
63
				Provides data to display in a grid cell. Called in grid cell context.
64
				So this.cell.index is the column index.
65
			inRowIndex: integer
66
				row for which to provide data
67
			returns:
68
				data to display for a given grid cell.
69
		*/
70
	},
71
	// settings
72
	// rowCount: int
73
	//	Number of rows to display.
74
	rowCount: 5,
75
	// keepRows: int
76
	//	Number of rows to keep in the rendering cache.
77
	keepRows: 75,
78
	// rowsPerPage: int
79
	//	Number of rows to render at a time.
80
	rowsPerPage: 25,
81
	// autoWidth: boolean
82
	//	If autoWidth is true, grid width is automatically set to fit the data.
83
	autoWidth: false,
84
	// autoHeight: boolean
85
	//	If autoHeight is true, grid height is automatically set to fit the data.
86
	autoHeight: false,
87
	// autoRender: boolean
88
	//	If autoRender is true, grid will render itself after initialization.
89
	autoRender: true,
90
	// defaultHeight: string
91
	//	default height of the grid, measured in any valid css unit.
92
	defaultHeight: '15em',
93
	// structure: object or string
94
	//	View layout defintion. Can be set to a layout object, or to the (string) name of a layout object.
95
	structure: '',
96
	// elasticView: int
97
	//	Override defaults and make the indexed grid view elastic, thus filling available horizontal space.
98
	elasticView: -1,
99
	// singleClickEdit: boolean
100
	//	Single-click starts editing. Default is double-click
101
	singleClickEdit: false,
102
	// private
103
	sortInfo: 0,
104
	themeable: true,
105
	// initialization
106
	buildRendering: function(){
107
		this.inherited(arguments);
108
		// reset get from blank function (needed for markup parsing) to null, if not changed
109
		if(this.get == dojox.VirtualGrid.prototype.get){
110
			this.get = null;
111
		}
112
		if(!this.domNode.getAttribute('tabIndex')){
113
			this.domNode.tabIndex = "0";
114
		}
115
		this.createScroller();
116
		this.createLayout();
117
		this.createViews();
118
		this.createManagers();
119
		dojox.grid.initTextSizePoll();
120
		this.connect(dojox.grid, "textSizeChanged", "textSizeChanged");
121
		dojox.grid.funnelEvents(this.domNode, this, 'doKeyEvent', dojox.grid.keyEvents);
122
		this.connect(this, "onShow", "renderOnIdle");
123
	},
124
	postCreate: function(){
125
		// replace stock styleChanged with one that triggers an update
126
		this.styleChanged = this._styleChanged;
127
		this.setStructure(this.structure);
128
	},
129
	destroy: function(){
130
		this.domNode.onReveal = null;
131
		this.domNode.onSizeChange = null;
132
		this.edit.destroy();
133
		this.views.destroyViews();
134
		this.inherited(arguments);
135
	},
136
	styleChanged: function(){
137
		this.setStyledClass(this.domNode, '');
138
	},
139
	_styleChanged: function(){
140
		this.styleChanged();
141
		this.update();
142
	},
143
	textSizeChanged: function(){
144
		setTimeout(dojo.hitch(this, "_textSizeChanged"), 1);
145
	},
146
	_textSizeChanged: function(){
147
		if(this.domNode){
148
			this.views.forEach(function(v){
149
				v.content.update();
150
			});
151
			this.render();
152
		}
153
	},
154
	sizeChange: function(){
155
		dojox.grid.jobs.job(this.id + 'SizeChange', 50, dojo.hitch(this, "update"));
156
	},
157
	renderOnIdle: function() {
158
		setTimeout(dojo.hitch(this, "render"), 1);
159
	},
160
	// managers
161
	createManagers: function(){
162
		// summary:
163
		//	create grid managers for various tasks including rows, focus, selection, editing
164
 
165
		// row manager
166
		this.rows = new dojox.grid.rows(this);
167
		// focus manager
168
		this.focus = new dojox.grid.focus(this);
169
		// selection manager
170
		this.selection = new dojox.grid.selection(this);
171
		// edit manager
172
		this.edit = new dojox.grid.edit(this);
173
	},
174
	// virtual scroller
175
	createScroller: function(){
176
		this.scroller = new dojox.grid.scroller.columns();
177
		this.scroller.renderRow = dojo.hitch(this, "renderRow");
178
		this.scroller.removeRow = dojo.hitch(this, "rowRemoved");
179
	},
180
	// layout
181
	createLayout: function(){
182
		this.layout = new dojox.grid.layout(this);
183
	},
184
	// views
185
	createViews: function(){
186
		this.views = new dojox.grid.views(this);
187
		this.views.createView = dojo.hitch(this, "createView");
188
	},
189
	createView: function(inClass){
190
		var c = eval(inClass);
191
		var view = new c({ grid: this });
192
		this.viewsNode.appendChild(view.domNode);
193
		this.headerNode.appendChild(view.headerNode);
194
		this.views.addView(view);
195
		return view;
196
	},
197
	buildViews: function(){
198
		for(var i=0, vs; (vs=this.layout.structure[i]); i++){
199
			this.createView(vs.type || "dojox.GridView").setStructure(vs);
200
		}
201
		this.scroller.setContentNodes(this.views.getContentNodes());
202
	},
203
	setStructure: function(inStructure){
204
		// summary:
205
		//		Install a new structure and rebuild the grid.
206
		// inStructure: Object
207
		//		Structure object defines the grid layout and provides various
208
		//		options for grid views and columns
209
		//	description:
210
		//		A grid structure is an array of view objects. A view object can
211
		//		specify a view type (view class), width, noscroll (boolean flag
212
		//		for view scrolling), and cells. Cells is an array of objects
213
		//		corresponding to each grid column. The view cells object is an
214
		//		array of subrows comprising a single row. Each subrow is an
215
		//		array of column objects. A column object can have a name,
216
		//		width, value (default), get function to provide data, styles,
217
		//		and span attributes (rowSpan, colSpan).
218
 
219
		this.views.destroyViews();
220
		this.structure = inStructure;
221
		if((this.structure)&&(dojo.isString(this.structure))){
222
			this.structure=dojox.grid.getProp(this.structure);
223
		}
224
		if(!this.structure){
225
			this.structure=window["layout"];
226
		}
227
		if(!this.structure){
228
			return;
229
		}
230
		this.layout.setStructure(this.structure);
231
		this._structureChanged();
232
	},
233
	_structureChanged: function() {
234
		this.buildViews();
235
		if(this.autoRender){
236
			this.render();
237
		}
238
	},
239
	// sizing
240
	resize: function(){
241
		// summary:
242
		//		Update the grid's rendering dimensions and resize it
243
 
244
		// FIXME: If grid is not sized explicitly, sometimes bogus scrollbars
245
		// can appear in our container, which may require an extra call to 'resize'
246
		// to sort out.
247
 
248
		// if we have set up everything except the DOM, we cannot resize
249
		if(!this.domNode.parentNode){
250
			return;
251
		}
252
		// useful measurement
253
		var padBorder = dojo._getPadBorderExtents(this.domNode);
254
		// grid height
255
		if(this.autoHeight){
256
			this.domNode.style.height = 'auto';
257
			this.viewsNode.style.height = '';
258
		}else if(this.flex > 0){
259
		}else if(this.domNode.clientHeight <= padBorder.h){
260
			if(this.domNode.parentNode == document.body){
261
				this.domNode.style.height = this.defaultHeight;
262
			}else{
263
				this.fitTo = "parent";
264
			}
265
		}
266
		if(this.fitTo == "parent"){
267
			var h = dojo._getContentBox(this.domNode.parentNode).h;
268
			dojo.marginBox(this.domNode, { h: Math.max(0, h) });
269
		}
270
		// header height
271
		var t = this.views.measureHeader();
272
		this.headerNode.style.height = t + 'px';
273
		// content extent
274
		var l = 1, h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
275
		if(this.autoWidth){
276
			// grid width set to total width
277
			this.domNode.style.width = this.views.arrange(l, 0, 0, h) + 'px';
278
		}else{
279
			// views fit to our clientWidth
280
			var w = this.domNode.clientWidth || (this.domNode.offsetWidth - padBorder.w);
281
			this.views.arrange(l, 0, w, h);
282
		}
283
		// virtual scroller height
284
		this.scroller.windowHeight = h;
285
		// default row height (FIXME: use running average(?), remove magic #)
286
		this.scroller.defaultRowHeight = this.rows.getDefaultHeightPx() + 1;
287
		this.postresize();
288
	},
289
	resizeHeight: function(){
290
		var t = this.views.measureHeader();
291
		this.headerNode.style.height = t + 'px';
292
		// content extent
293
		var h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
294
		//this.views.arrange(0, 0, 0, h);
295
		this.views.onEach('setSize', [0, h]);
296
		this.views.onEach('resizeHeight');
297
		this.scroller.windowHeight = h;
298
	},
299
	// render
300
	render: function(){
301
		// summary:
302
		//	Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and
303
		// scrolling states, see Update.
304
 
305
		if(!this.domNode){return;}
306
		//
307
		this.update = this.defaultUpdate;
308
		this.scroller.init(this.rowCount, this.keepRows, this.rowsPerPage);
309
		this.prerender();
310
		this.setScrollTop(0);
311
		this.postrender();
312
	},
313
	prerender: function(){
314
		this.views.render();
315
		this.resize();
316
	},
317
	postrender: function(){
318
		this.postresize();
319
		this.focus.initFocusView();
320
		// make rows unselectable
321
		dojo.setSelectable(this.domNode, false);
322
	},
323
	postresize: function(){
324
		// views are position absolute, so they do not inflate the parent
325
		if(this.autoHeight){
326
			this.viewsNode.style.height = this.views.measureContent() + 'px';
327
		}
328
	},
329
	// private, used internally to render rows
330
	renderRow: function(inRowIndex, inNodes){
331
		this.views.renderRow(inRowIndex, inNodes);
332
	},
333
	// private, used internally to remove rows
334
	rowRemoved: function(inRowIndex){
335
		this.views.rowRemoved(inRowIndex);
336
	},
337
	invalidated: null,
338
	updating: false,
339
	beginUpdate: function(){
340
		// summary:
341
		//	Use to make multiple changes to rows while queueing row updating.
342
		// NOTE: not currently supporting nested begin/endUpdate calls
343
		this.invalidated = [];
344
		this.updating = true;
345
	},
346
	endUpdate: function(){
347
		// summary:
348
		//	Use after calling beginUpdate to render any changes made to rows.
349
		this.updating = false;
350
		var i = this.invalidated;
351
		if(i.all){
352
			this.update();
353
		}else if(i.rowCount != undefined){
354
			this.updateRowCount(i.rowCount);
355
		}else{
356
			for(r in i){
357
				this.updateRow(Number(r));
358
			}
359
		}
360
		this.invalidated = null;
361
	},
362
	// update
363
	defaultUpdate: function(){
364
		// note: initial update calls render and subsequently this function.
365
		if(this.updating){
366
			this.invalidated.all = true;
367
			return;
368
		}
369
		//this.edit.saveState(inRowIndex);
370
		this.prerender();
371
		this.scroller.invalidateNodes();
372
		this.setScrollTop(this.scrollTop);
373
		this.postrender();
374
		//this.edit.restoreState(inRowIndex);
375
	},
376
	update: function(){
377
		// summary:
378
		//	Update the grid, retaining edit and scrolling states.
379
		this.render();
380
	},
381
	updateRow: function(inRowIndex){
382
		// summary:
383
		//	Render a single row.
384
		// inRowIndex: int
385
		//	index of the row to render
386
		inRowIndex = Number(inRowIndex);
387
		if(this.updating){
388
			this.invalidated[inRowIndex]=true;
389
			return;
390
		}
391
		this.views.updateRow(inRowIndex, this.rows.getHeight(inRowIndex));
392
		this.scroller.rowHeightChanged(inRowIndex);
393
	},
394
	updateRowCount: function(inRowCount){
395
		//summary:
396
		//	Change the number of rows.
397
		// inRowCount: int
398
		//	Number of rows in the grid.
399
		if(this.updating){
400
			this.invalidated.rowCount = inRowCount;
401
			return;
402
		}
403
		this.rowCount = inRowCount;
404
		this.scroller.updateRowCount(inRowCount);
405
		this.setScrollTop(this.scrollTop);
406
		this.resize();
407
	},
408
	updateRowStyles: function(inRowIndex){
409
		// summary:
410
		//	Update the styles for a row after it's state has changed.
411
 
412
		this.views.updateRowStyles(inRowIndex);
413
	},
414
	rowHeightChanged: function(inRowIndex){
415
		// summary:
416
		//	Update grid when the height of a row has changed. Row height is handled automatically as rows
417
		//	are rendered. Use this function only to update a row's height outside the normal rendering process.
418
		// inRowIndex: int
419
		// index of the row that has changed height
420
 
421
		this.views.renormalizeRow(inRowIndex);
422
		this.scroller.rowHeightChanged(inRowIndex);
423
	},
424
	// fastScroll: boolean
425
	//	flag modifies vertical scrolling behavior. Defaults to true but set to false for slower
426
	//	scroll performance but more immediate scrolling feedback
427
	fastScroll: true,
428
	delayScroll: false,
429
	// scrollRedrawThreshold: int
430
	//	pixel distance a user must scroll vertically to trigger grid scrolling.
431
	scrollRedrawThreshold: (dojo.isIE ? 100 : 50),
432
	// scroll methods
433
	scrollTo: function(inTop){
434
		// summary:
435
		//	Vertically scroll the grid to a given pixel position
436
		// inTop: int
437
		//	vertical position of the grid in pixels
438
		if(!this.fastScroll){
439
			this.setScrollTop(inTop);
440
			return;
441
		}
442
		var delta = Math.abs(this.lastScrollTop - inTop);
443
		this.lastScrollTop = inTop;
444
		if(delta > this.scrollRedrawThreshold || this.delayScroll){
445
			this.delayScroll = true;
446
			this.scrollTop = inTop;
447
			this.views.setScrollTop(inTop);
448
			dojox.grid.jobs.job('dojoxGrid-scroll', 200, dojo.hitch(this, "finishScrollJob"));
449
		}else{
450
			this.setScrollTop(inTop);
451
		}
452
	},
453
	finishScrollJob: function(){
454
		this.delayScroll = false;
455
		this.setScrollTop(this.scrollTop);
456
	},
457
	setScrollTop: function(inTop){
458
		this.scrollTop = this.views.setScrollTop(inTop);
459
		this.scroller.scroll(this.scrollTop);
460
	},
461
	scrollToRow: function(inRowIndex){
462
		// summary:
463
		//	Scroll the grid to a specific row.
464
		// inRowIndex: int
465
		// grid row index
466
		this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1);
467
	},
468
	// styling (private, used internally to style individual parts of a row)
469
	styleRowNode: function(inRowIndex, inRowNode){
470
		if(inRowNode){
471
			this.rows.styleRowNode(inRowIndex, inRowNode);
472
		}
473
	},
474
	// cells
475
	getCell: function(inIndex){
476
		// summary:
477
		//	retrieves the cell object for a given grid column.
478
		// inIndex: int
479
		// grid column index of cell to retrieve
480
		// returns:
481
		//	a grid cell
482
		return this.layout.cells[inIndex];
483
	},
484
	setCellWidth: function(inIndex, inUnitWidth) {
485
		this.getCell(inIndex).unitWidth = inUnitWidth;
486
	},
487
	getCellName: function(inCell){
488
		return "Cell " + inCell.index;
489
	},
490
	// sorting
491
	canSort: function(inSortInfo){
492
		// summary:
493
		//	determines if the grid can be sorted
494
		// inSortInfo: int
495
		//	Sort information, 1-based index of column on which to sort, positive for an ascending sort
496
		// and negative for a descending sort
497
		// returns:
498
		//	true if grid can be sorted on the given column in the given direction
499
	},
500
	sort: function(){
501
	},
502
	getSortAsc: function(inSortInfo){
503
		// summary:
504
		//	returns true if grid is sorted in an ascending direction.
505
		inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
506
		return Boolean(inSortInfo > 0);
507
	},
508
	getSortIndex: function(inSortInfo){
509
		// summary:
510
		//	returns the index of the column on which the grid is sorted
511
		inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
512
		return Math.abs(inSortInfo) - 1;
513
	},
514
	setSortIndex: function(inIndex, inAsc){
515
		// summary:
516
		// Sort the grid on a column in a specified direction
517
		// inIndex: int
518
		// Column index on which to sort.
519
		// inAsc: boolean
520
		// If true, sort the grid in ascending order, otherwise in descending order
521
		var si = inIndex +1;
522
		if(inAsc != undefined){
523
			si *= (inAsc ? 1 : -1);
524
		} else if(this.getSortIndex() == inIndex){
525
			si = -this.sortInfo;
526
		}
527
		this.setSortInfo(si);
528
	},
529
	setSortInfo: function(inSortInfo){
530
		if(this.canSort(inSortInfo)){
531
			this.sortInfo = inSortInfo;
532
			this.sort();
533
			this.update();
534
		}
535
	},
536
	// DOM event handler
537
	doKeyEvent: function(e){
538
		e.dispatch = 'do' + e.type;
539
		this.onKeyEvent(e);
540
	},
541
	// event dispatch
542
	//: protected
543
	_dispatch: function(m, e){
544
		if(m in this){
545
			return this[m](e);
546
		}
547
	},
548
	dispatchKeyEvent: function(e){
549
		this._dispatch(e.dispatch, e);
550
	},
551
	dispatchContentEvent: function(e){
552
		this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e);
553
	},
554
	dispatchHeaderEvent: function(e){
555
		e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e);
556
	},
557
	dokeydown: function(e){
558
		this.onKeyDown(e);
559
	},
560
	doclick: function(e){
561
		if(e.cellNode){
562
			this.onCellClick(e);
563
		}else{
564
			this.onRowClick(e);
565
		}
566
	},
567
	dodblclick: function(e){
568
		if(e.cellNode){
569
			this.onCellDblClick(e);
570
		}else{
571
			this.onRowDblClick(e);
572
		}
573
	},
574
	docontextmenu: function(e){
575
		if(e.cellNode){
576
			this.onCellContextMenu(e);
577
		}else{
578
			this.onRowContextMenu(e);
579
		}
580
	},
581
	doheaderclick: function(e){
582
		if(e.cellNode){
583
			this.onHeaderCellClick(e);
584
		}else{
585
			this.onHeaderClick(e);
586
		}
587
	},
588
	doheaderdblclick: function(e){
589
		if(e.cellNode){
590
			this.onHeaderCellDblClick(e);
591
		}else{
592
			this.onHeaderDblClick(e);
593
		}
594
	},
595
	doheadercontextmenu: function(e){
596
		if(e.cellNode){
597
			this.onHeaderCellContextMenu(e);
598
		}else{
599
			this.onHeaderContextMenu(e);
600
		}
601
	},
602
	// override to modify editing process
603
	doStartEdit: function(inCell, inRowIndex){
604
		this.onStartEdit(inCell, inRowIndex);
605
	},
606
	doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
607
		this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
608
	},
609
	doCancelEdit: function(inRowIndex){
610
		this.onCancelEdit(inRowIndex);
611
	},
612
	doApplyEdit: function(inRowIndex){
613
		this.onApplyEdit(inRowIndex);
614
	},
615
	// row editing
616
	addRow: function(){
617
		// summary:
618
		//	add a row to the grid.
619
		this.updateRowCount(this.rowCount+1);
620
	},
621
	removeSelectedRows: function(){
622
		// summary:
623
		//	remove the selected rows from the grid.
624
		this.updateRowCount(Math.max(0, this.rowCount - this.selection.getSelected().length));
625
		this.selection.clear();
626
	}
627
});
628
 
629
dojo.mixin(dojox.VirtualGrid.prototype, dojox.grid.publicEvents);
630
 
631
}