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._data.model']){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource['dojox.grid._data.model'] = true;
3
dojo.provide('dojox.grid._data.model');
4
dojo.require('dojox.grid._data.fields');
5
 
6
dojo.declare("dojox.grid.data.Model", null, {
7
	// summary:
8
	//	Base abstract grid data model.
9
	//	Makes no assumptions about the structure of grid data.
10
	constructor: function(inFields, inData){
11
		this.observers = [];
12
		this.fields = new dojox.grid.data.Fields();
13
		if(inFields){
14
			this.fields.set(inFields);
15
		}
16
		this.setData(inData);
17
	},
18
	count: 0,
19
	updating: 0,
20
	// observers
21
	observer: function(inObserver, inPrefix){
22
		this.observers.push({o: inObserver, p: inPrefix||'model' });
23
	},
24
	notObserver: function(inObserver){
25
		for(var i=0, m, o; (o=this.observers[i]); i++){
26
			if(o.o==inObserver){
27
				this.observers.splice(i, 1);
28
				return;
29
			}
30
		}
31
	},
32
	notify: function(inMsg, inArgs){
33
		if(!this.isUpdating()){
34
			var a = inArgs || [];
35
			for(var i=0, m, o; (o=this.observers[i]); i++){
36
				m = o.p + inMsg, o = o.o;
37
				(m in o)&&(o[m].apply(o, a));
38
			}
39
		}
40
	},
41
	// updates
42
	clear: function(){
43
		this.fields.clear();
44
		this.clearData();
45
	},
46
	beginUpdate: function(){
47
		this.updating++;
48
	},
49
	endUpdate: function(){
50
		if(this.updating){
51
			this.updating--;
52
		}
53
		/*if(this.updating){
54
			if(!(--this.updating)){
55
				this.change();
56
			}
57
		}
58
		}*/
59
	},
60
	isUpdating: function(){
61
		return Boolean(this.updating);
62
	},
63
	// data
64
	clearData: function(){
65
		this.setData(null);
66
	},
67
	// observer events
68
	change: function(){
69
		this.notify("Change", arguments);
70
	},
71
	insertion: function(/* index */){
72
		this.notify("Insertion", arguments);
73
		this.notify("Change", arguments);
74
	},
75
	removal: function(/* keys */){
76
		this.notify("Removal", arguments);
77
		this.notify("Change", arguments);
78
	},
79
	// insert
80
	insert: function(inData /*, index */){
81
		if(!this._insert.apply(this, arguments)){
82
			return false;
83
		}
84
		this.insertion.apply(this, dojo._toArray(arguments, 1));
85
		return true;
86
	},
87
	// remove
88
	remove: function(inData /*, index */){
89
		if(!this._remove.apply(this, arguments)){
90
			return false;
91
		}
92
		this.removal.apply(this, arguments);
93
		return true;
94
	},
95
	// sort
96
	canSort: function(/* (+|-)column_index+1, ... */){
97
		return this.sort != null;
98
	},
99
	makeComparator: function(inIndices){
100
		var idx, col, field, result = null;
101
		for(var i=inIndices.length-1; i>=0; i--){
102
			idx = inIndices[i];
103
			col = Math.abs(idx) - 1;
104
			if(col >= 0){
105
				field = this.fields.get(col);
106
				result = this.generateComparator(field.compare, field.key, idx > 0, result);
107
			}
108
		}
109
		return result;
110
	},
111
	sort: null,
112
	dummy: 0
113
});
114
 
115
dojo.declare("dojox.grid.data.Rows", dojox.grid.data.Model, {
116
	// observer events
117
	allChange: function(){
118
		this.notify("AllChange", arguments);
119
		this.notify("Change", arguments);
120
	},
121
	rowChange: function(){
122
		this.notify("RowChange", arguments);
123
	},
124
	datumChange: function(){
125
		this.notify("DatumChange", arguments);
126
	},
127
	// copyRow: function(inRowIndex); // abstract
128
	// update
129
	beginModifyRow: function(inRowIndex){
130
		if(!this.cache[inRowIndex]){
131
			this.cache[inRowIndex] = this.copyRow(inRowIndex);
132
		}
133
	},
134
	endModifyRow: function(inRowIndex){
135
		var cache = this.cache[inRowIndex];
136
		if(cache){
137
			var data = this.getRow(inRowIndex);
138
			if(!dojox.grid.arrayCompare(cache, data)){
139
				this.update(cache, data, inRowIndex);
140
			}
141
			delete this.cache[inRowIndex];
142
		}
143
	},
144
	cancelModifyRow: function(inRowIndex){
145
		var cache = this.cache[inRowIndex];
146
		if(cache){
147
			this.setRow(cache, inRowIndex);
148
			delete this.cache[inRowIndex];
149
		}
150
	},
151
	generateComparator: function(inCompare, inField, inTrueForAscend, inSubCompare){
152
		return function(a, b){
153
			var ineq = inCompare(a[inField], b[inField]);
154
			return ineq ? (inTrueForAscend ? ineq : -ineq) : inSubCompare && inSubCompare(a, b);
155
		}
156
	}
157
});
158
 
159
dojo.declare("dojox.grid.data.Table", dojox.grid.data.Rows, {
160
	// summary:
161
	//	Basic grid data model for static data in the form of an array of rows
162
	//	that are arrays of cell data
163
	constructor: function(){
164
		this.cache = [];
165
	},
166
	colCount: 0, // tables introduce cols
167
	data: null,
168
	cache: null,
169
	// morphology
170
	measure: function(){
171
		this.count = this.getRowCount();
172
		this.colCount = this.getColCount();
173
		this.allChange();
174
		//this.notify("Measure");
175
	},
176
	getRowCount: function(){
177
		return (this.data ? this.data.length : 0);
178
	},
179
	getColCount: function(){
180
		return (this.data && this.data.length ? this.data[0].length : this.fields.count());
181
	},
182
	badIndex: function(inCaller, inDescriptor){
183
		console.debug('dojox.grid.data.Table: badIndex');
184
	},
185
	isGoodIndex: function(inRowIndex, inColIndex){
186
		return (inRowIndex >= 0 && inRowIndex < this.count && (arguments.length < 2 || (inColIndex >= 0 && inColIndex < this.colCount)));
187
	},
188
	// access
189
	getRow: function(inRowIndex){
190
		return this.data[inRowIndex];
191
	},
192
	copyRow: function(inRowIndex){
193
		return this.getRow(inRowIndex).slice(0);
194
	},
195
	getDatum: function(inRowIndex, inColIndex){
196
		return this.data[inRowIndex][inColIndex];
197
	},
198
	get: function(){
199
		throw('Plain "get" no longer supported. Use "getRow" or "getDatum".');
200
	},
201
	setData: function(inData){
202
		this.data = (inData || []);
203
		this.allChange();
204
	},
205
	setRow: function(inData, inRowIndex){
206
		this.data[inRowIndex] = inData;
207
		this.rowChange(inData, inRowIndex);
208
		this.change();
209
	},
210
	setDatum: function(inDatum, inRowIndex, inColIndex){
211
		this.data[inRowIndex][inColIndex] = inDatum;
212
		this.datumChange(inDatum, inRowIndex, inColIndex);
213
	},
214
	set: function(){
215
		throw('Plain "set" no longer supported. Use "setData", "setRow", or "setDatum".');
216
	},
217
	setRows: function(inData, inRowIndex){
218
		for(var i=0, l=inData.length, r=inRowIndex; i<l; i++, r++){
219
			this.setRow(inData[i], r);
220
		}
221
	},
222
	// update
223
	update: function(inOldData, inNewData, inRowIndex){
224
		//delete this.cache[inRowIndex];
225
		//this.setRow(inNewData, inRowIndex);
226
		return true;
227
	},
228
	// insert
229
	_insert: function(inData, inRowIndex){
230
		dojox.grid.arrayInsert(this.data, inRowIndex, inData);
231
		this.count++;
232
		return true;
233
	},
234
	// remove
235
	_remove: function(inKeys){
236
		for(var i=inKeys.length-1; i>=0; i--){
237
			dojox.grid.arrayRemove(this.data, inKeys[i]);
238
		}
239
		this.count -= inKeys.length;
240
		return true;
241
	},
242
	// sort
243
	sort: function(/* (+|-)column_index+1, ... */){
244
		this.data.sort(this.makeComparator(arguments));
245
	},
246
	swap: function(inIndexA, inIndexB){
247
		dojox.grid.arraySwap(this.data, inIndexA, inIndexB);
248
		this.rowChange(this.getRow(inIndexA), inIndexA);
249
		this.rowChange(this.getRow(inIndexB), inIndexB);
250
		this.change();
251
	},
252
	dummy: 0
253
});
254
 
255
dojo.declare("dojox.grid.data.Objects", dojox.grid.data.Table, {
256
	constructor: function(inFields, inData, inKey){
257
		if(!inFields){
258
			this.autoAssignFields();
259
		}
260
	},
261
	autoAssignFields: function(){
262
		var d = this.data[0], i = 0;
263
		for(var f in d){
264
			this.fields.get(i++).key = f;
265
		}
266
	},
267
	getDatum: function(inRowIndex, inColIndex){
268
		return this.data[inRowIndex][this.fields.get(inColIndex).key];
269
	}
270
});
271
 
272
dojo.declare("dojox.grid.data.Dynamic", dojox.grid.data.Table, {
273
	// summary:
274
	//	Grid data model for dynamic data such as data retrieved from a server.
275
	//	Retrieves data automatically when requested and provides notification when data is received
276
	constructor: function(){
277
		this.page = [];
278
		this.pages = [];
279
	},
280
	page: null,
281
	pages: null,
282
	rowsPerPage: 100,
283
	requests: 0,
284
	bop: -1,
285
	eop: -1,
286
	// data
287
	clearData: function(){
288
		this.pages = [];
289
		this.bop = this.eop = -1;
290
		this.setData([]);
291
	},
292
	getRowCount: function(){
293
		return this.count;
294
	},
295
	getColCount: function(){
296
		return this.fields.count();
297
	},
298
	setRowCount: function(inCount){
299
		this.count = inCount;
300
		this.change();
301
	},
302
	// paging
303
	requestsPending: function(inBoolean){
304
	},
305
	rowToPage: function(inRowIndex){
306
		return (this.rowsPerPage ? Math.floor(inRowIndex / this.rowsPerPage) : inRowIndex);
307
	},
308
	pageToRow: function(inPageIndex){
309
		return (this.rowsPerPage ? this.rowsPerPage * inPageIndex : inPageIndex);
310
	},
311
	requestRows: function(inRowIndex, inCount){
312
		// summary:
313
		//		stub. Fill in to perform actual data row fetching logic. The
314
		//		returning logic must provide the data back to the system via
315
		//		setRow
316
	},
317
	rowsProvided: function(inRowIndex, inCount){
318
		this.requests--;
319
		if(this.requests == 0){
320
			this.requestsPending(false);
321
		}
322
	},
323
	requestPage: function(inPageIndex){
324
		var row = this.pageToRow(inPageIndex);
325
		var count = Math.min(this.rowsPerPage, this.count - row);
326
		if(count > 0){
327
			this.requests++;
328
			this.requestsPending(true);
329
			setTimeout(dojo.hitch(this, "requestRows", row, count), 1);
330
			//this.requestRows(row, count);
331
		}
332
	},
333
	needPage: function(inPageIndex){
334
		if(!this.pages[inPageIndex]){
335
			this.pages[inPageIndex] = true;
336
			this.requestPage(inPageIndex);
337
		}
338
	},
339
	preparePage: function(inRowIndex, inColIndex){
340
		if(inRowIndex < this.bop || inRowIndex >= this.eop){
341
			var pageIndex = this.rowToPage(inRowIndex);
342
			this.needPage(pageIndex);
343
			this.bop = pageIndex * this.rowsPerPage;
344
			this.eop = this.bop + (this.rowsPerPage || this.count);
345
		}
346
	},
347
	isRowLoaded: function(inRowIndex){
348
		return Boolean(this.data[inRowIndex]);
349
	},
350
	// removal
351
	removePages: function(inRowIndexes){
352
		for(var i=0, r; ((r=inRowIndexes[i]) != undefined); i++){
353
			this.pages[this.rowToPage(r)] = false;
354
		}
355
		this.bop = this.eop =-1;
356
	},
357
	remove: function(inRowIndexes){
358
		this.removePages(inRowIndexes);
359
		dojox.grid.data.Table.prototype.remove.apply(this, arguments);
360
	},
361
	// access
362
	getRow: function(inRowIndex){
363
		var row = this.data[inRowIndex];
364
		if(!row){
365
			this.preparePage(inRowIndex);
366
		}
367
		return row;
368
	},
369
	getDatum: function(inRowIndex, inColIndex){
370
		var row = this.getRow(inRowIndex);
371
		return (row ? row[inColIndex] : this.fields.get(inColIndex).na);
372
	},
373
	setDatum: function(inDatum, inRowIndex, inColIndex){
374
		var row = this.getRow(inRowIndex);
375
		if(row){
376
			row[inColIndex] = inDatum;
377
			this.datumChange(inDatum, inRowIndex, inColIndex);
378
		}else{
379
			console.debug('[' + this.declaredClass + '] dojox.grid.data.dynamic.set: cannot set data on an non-loaded row');
380
		}
381
	},
382
	// sort
383
	canSort: function(){
384
		return false;
385
	}
386
});
387
 
388
// FIXME: deprecated: (included for backward compatibility only)
389
dojox.grid.data.table = dojox.grid.data.Table;
390
dojox.grid.data.dynamic = dojox.grid.data.Dyanamic;
391
 
392
// we treat dojo.data stores as dynamic stores because no matter how they got
393
// here, they should always fill that contract
394
dojo.declare("dojox.grid.data.DojoData", dojox.grid.data.Dynamic, {
395
	//	summary:
396
	//		A grid data model for dynamic data retreived from a store which
397
	//		implements the dojo.data API set. Retrieves data automatically when
398
	//		requested and provides notification when data is received
399
	//	description:
400
	//		This store subclasses the Dynamic grid data object in order to
401
	//		provide paginated data access support, notification and view
402
	//		updates for stores which support those features, and simple
403
	//		field/column mapping for all dojo.data stores.
404
	constructor: function(inFields, inData, args){
405
		this.count = 1;
406
		this._rowIdentities = {};
407
		if(args){
408
			dojo.mixin(this, args);
409
		}
410
		if(this.store){
411
			// NOTE: we assume Read and Identity APIs for all stores!
412
			var f = this.store.getFeatures();
413
			this._canNotify = f['dojo.data.api.Notification'];
414
			this._canWrite = f['dojo.data.api.Write'];
415
 
416
			if(this._canNotify){
417
				dojo.connect(this.store, "onSet", this, "_storeDatumChange");
418
			}
419
		}
420
	},
421
	markupFactory: function(args, node){
422
		return new dojox.grid.data.DojoData(null, null, args);
423
	},
424
	query: { name: "*" }, // default, stupid query
425
	store: null,
426
	_canNotify: false,
427
	_canWrite: false,
428
	_rowIdentities: {},
429
	clientSort: false,
430
	// data
431
	setData: function(inData){
432
		this.store = inData;
433
		this.data = [];
434
		this.allChange();
435
	},
436
	setRowCount: function(inCount){
437
		//console.debug("inCount:", inCount);
438
		this.count = inCount;
439
		this.allChange();
440
	},
441
	beginReturn: function(inCount){
442
		if(this.count != inCount){
443
			// this.setRowCount(0);
444
			// this.clear();
445
			// console.debug(this.count, inCount);
446
			this.setRowCount(inCount);
447
		}
448
	},
449
	_setupFields: function(dataItem){
450
		// abort if we already have setup fields
451
		if(this.fields._nameMaps){
452
			return;
453
		}
454
		// set up field/index mappings
455
		var m = {};
456
		//console.debug("setting up fields", m);
457
		var fields = dojo.map(this.store.getAttributes(dataItem),
458
			function(item, idx){
459
				m[item] = idx;
460
				m[idx+".idx"] = item;
461
				// name == display name, key = property name
462
				return { name: item, key: item };
463
			},
464
			this
465
		);
466
		this.fields._nameMaps = m;
467
		// console.debug("new fields:", fields);
468
		this.fields.set(fields);
469
		this.notify("FieldsChange");
470
	},
471
	_getRowFromItem: function(item){
472
		// gets us the row object (and row index) of an item
473
	},
474
	processRows: function(items, store){
475
		// console.debug(arguments);
476
		if(!items){ return; }
477
		this._setupFields(items[0]);
478
		dojo.forEach(items, function(item, idx){
479
			var row = {};
480
			row.__dojo_data_item = item;
481
			dojo.forEach(this.fields.values, function(a){
482
				row[a.name] = this.store.getValue(item, a.name)||"";
483
			}, this);
484
			// FIXME: where else do we need to keep this in sync?
485
			this._rowIdentities[this.store.getIdentity(item)] = store.start+idx;
486
			this.setRow(row, store.start+idx);
487
		}, this);
488
		// FIXME:
489
		//	Q: scott, steve, how the hell do we actually get this to update
490
		//		the visible UI for these rows?
491
		//	A: the goal is that Grid automatically updates to reflect changes
492
		//		in model. In this case, setRow -> rowChanged -> (observed by) Grid -> modelRowChange -> updateRow
493
	},
494
	// request data
495
	requestRows: function(inRowIndex, inCount){
496
		var row  = inRowIndex || 0;
497
		var params = {
498
			start: row,
499
			count: this.rowsPerPage,
500
			query: this.query,
501
			onBegin: dojo.hitch(this, "beginReturn"),
502
			//	onItem: dojo.hitch(console, "debug"),
503
			onComplete: dojo.hitch(this, "processRows") // add to deferred?
504
		}
505
		// console.debug("requestRows:", row, this.rowsPerPage);
506
		this.store.fetch(params);
507
	},
508
	getDatum: function(inRowIndex, inColIndex){
509
		//console.debug("getDatum", inRowIndex, inColIndex);
510
		var row = this.getRow(inRowIndex);
511
		var field = this.fields.values[inColIndex];
512
		return row && field ? row[field.name] : field ? field.na : '?';
513
		//var idx = row && this.fields._nameMaps[inColIndex+".idx"];
514
		//return (row ? row[idx] : this.fields.get(inColIndex).na);
515
	},
516
	setDatum: function(inDatum, inRowIndex, inColIndex){
517
		var n = this.fields._nameMaps[inColIndex+".idx"];
518
		// console.debug("setDatum:", "n:"+n, inDatum, inRowIndex, inColIndex);
519
		if(n){
520
			this.data[inRowIndex][n] = inDatum;
521
			this.datumChange(inDatum, inRowIndex, inColIndex);
522
		}
523
	},
524
	// modification, update and store eventing
525
	copyRow: function(inRowIndex){
526
		var row = {};
527
		var backstop = {};
528
		var src = this.getRow(inRowIndex);
529
		for(var x in src){
530
			if(src[x] != backstop[x]){
531
				row[x] = src[x];
532
			}
533
		}
534
		return row;
535
	},
536
	_attrCompare: function(cache, data){
537
		dojo.forEach(this.fields.values, function(a){
538
			if(cache[a.name] != data[a.name]){ return false; }
539
		}, this);
540
		return true;
541
	},
542
	endModifyRow: function(inRowIndex){
543
		var cache = this.cache[inRowIndex];
544
		if(cache){
545
			var data = this.getRow(inRowIndex);
546
			if(!this._attrCompare(cache, data)){
547
				this.update(cache, data, inRowIndex);
548
			}
549
			delete this.cache[inRowIndex];
550
		}
551
	},
552
	cancelModifyRow: function(inRowIndex){
553
		// console.debug("cancelModifyRow", arguments);
554
		var cache = this.cache[inRowIndex];
555
		if(cache){
556
			this.setRow(cache, inRowIndex);
557
			delete this.cache[inRowIndex];
558
		}
559
	},
560
	_storeDatumChange: function(item, attr, oldVal, newVal){
561
		// the store has changed some data under us, need to update the display
562
		var rowId = this._rowIdentities[this.store.getIdentity(item)];
563
		var row = this.getRow(rowId);
564
		row[attr] = newVal;
565
		var colId = this.fields._nameMaps[attr];
566
		this.notify("DatumChange", [ newVal, rowId, colId ]);
567
	},
568
	datumChange: function(value, rowIdx, colIdx){
569
		if(this._canWrite){
570
			// we're chaning some data, which means we need to write back
571
			var row = this.getRow(rowIdx);
572
			var field = this.fields._nameMaps[colIdx+".idx"];
573
			this.store.setValue(row.__dojo_data_item, field, value);
574
			// we don't need to call DatumChange, an eventing store will tell
575
			// us about the row change events
576
		}else{
577
			// we can't write back, so just go ahead and change our local copy
578
			// of the data
579
			this.notify("DatumChange", arguments);
580
		}
581
	},
582
	insertion: function(/* index */){
583
		console.debug("Insertion", arguments);
584
		this.notify("Insertion", arguments);
585
		this.notify("Change", arguments);
586
	},
587
	removal: function(/* keys */){
588
		console.debug("Removal", arguments);
589
		this.notify("Removal", arguments);
590
		this.notify("Change", arguments);
591
	},
592
	// sort
593
	canSort: function(){
594
		// Q: Return true and re-issue the queries?
595
		// A: Return true only. Re-issue the query in 'sort'.
596
		return this.clientSort;
597
	}
598
});
599
 
600
}