Blame | Last modification | View Log | RSS feed
if(!dojo._hasResource['dojox.grid._data.model']){ //_hasResource checks added by build. Do not use _hasResource directly in your code.dojo._hasResource['dojox.grid._data.model'] = true;dojo.provide('dojox.grid._data.model');dojo.require('dojox.grid._data.fields');dojo.declare("dojox.grid.data.Model", null, {// summary:// Base abstract grid data model.// Makes no assumptions about the structure of grid data.constructor: function(inFields, inData){this.observers = [];this.fields = new dojox.grid.data.Fields();if(inFields){this.fields.set(inFields);}this.setData(inData);},count: 0,updating: 0,// observersobserver: function(inObserver, inPrefix){this.observers.push({o: inObserver, p: inPrefix||'model' });},notObserver: function(inObserver){for(var i=0, m, o; (o=this.observers[i]); i++){if(o.o==inObserver){this.observers.splice(i, 1);return;}}},notify: function(inMsg, inArgs){if(!this.isUpdating()){var a = inArgs || [];for(var i=0, m, o; (o=this.observers[i]); i++){m = o.p + inMsg, o = o.o;(m in o)&&(o[m].apply(o, a));}}},// updatesclear: function(){this.fields.clear();this.clearData();},beginUpdate: function(){this.updating++;},endUpdate: function(){if(this.updating){this.updating--;}/*if(this.updating){if(!(--this.updating)){this.change();}}}*/},isUpdating: function(){return Boolean(this.updating);},// dataclearData: function(){this.setData(null);},// observer eventschange: function(){this.notify("Change", arguments);},insertion: function(/* index */){this.notify("Insertion", arguments);this.notify("Change", arguments);},removal: function(/* keys */){this.notify("Removal", arguments);this.notify("Change", arguments);},// insertinsert: function(inData /*, index */){if(!this._insert.apply(this, arguments)){return false;}this.insertion.apply(this, dojo._toArray(arguments, 1));return true;},// removeremove: function(inData /*, index */){if(!this._remove.apply(this, arguments)){return false;}this.removal.apply(this, arguments);return true;},// sortcanSort: function(/* (+|-)column_index+1, ... */){return this.sort != null;},makeComparator: function(inIndices){var idx, col, field, result = null;for(var i=inIndices.length-1; i>=0; i--){idx = inIndices[i];col = Math.abs(idx) - 1;if(col >= 0){field = this.fields.get(col);result = this.generateComparator(field.compare, field.key, idx > 0, result);}}return result;},sort: null,dummy: 0});dojo.declare("dojox.grid.data.Rows", dojox.grid.data.Model, {// observer eventsallChange: function(){this.notify("AllChange", arguments);this.notify("Change", arguments);},rowChange: function(){this.notify("RowChange", arguments);},datumChange: function(){this.notify("DatumChange", arguments);},// copyRow: function(inRowIndex); // abstract// updatebeginModifyRow: function(inRowIndex){if(!this.cache[inRowIndex]){this.cache[inRowIndex] = this.copyRow(inRowIndex);}},endModifyRow: function(inRowIndex){var cache = this.cache[inRowIndex];if(cache){var data = this.getRow(inRowIndex);if(!dojox.grid.arrayCompare(cache, data)){this.update(cache, data, inRowIndex);}delete this.cache[inRowIndex];}},cancelModifyRow: function(inRowIndex){var cache = this.cache[inRowIndex];if(cache){this.setRow(cache, inRowIndex);delete this.cache[inRowIndex];}},generateComparator: function(inCompare, inField, inTrueForAscend, inSubCompare){return function(a, b){var ineq = inCompare(a[inField], b[inField]);return ineq ? (inTrueForAscend ? ineq : -ineq) : inSubCompare && inSubCompare(a, b);}}});dojo.declare("dojox.grid.data.Table", dojox.grid.data.Rows, {// summary:// Basic grid data model for static data in the form of an array of rows// that are arrays of cell dataconstructor: function(){this.cache = [];},colCount: 0, // tables introduce colsdata: null,cache: null,// morphologymeasure: function(){this.count = this.getRowCount();this.colCount = this.getColCount();this.allChange();//this.notify("Measure");},getRowCount: function(){return (this.data ? this.data.length : 0);},getColCount: function(){return (this.data && this.data.length ? this.data[0].length : this.fields.count());},badIndex: function(inCaller, inDescriptor){console.debug('dojox.grid.data.Table: badIndex');},isGoodIndex: function(inRowIndex, inColIndex){return (inRowIndex >= 0 && inRowIndex < this.count && (arguments.length < 2 || (inColIndex >= 0 && inColIndex < this.colCount)));},// accessgetRow: function(inRowIndex){return this.data[inRowIndex];},copyRow: function(inRowIndex){return this.getRow(inRowIndex).slice(0);},getDatum: function(inRowIndex, inColIndex){return this.data[inRowIndex][inColIndex];},get: function(){throw('Plain "get" no longer supported. Use "getRow" or "getDatum".');},setData: function(inData){this.data = (inData || []);this.allChange();},setRow: function(inData, inRowIndex){this.data[inRowIndex] = inData;this.rowChange(inData, inRowIndex);this.change();},setDatum: function(inDatum, inRowIndex, inColIndex){this.data[inRowIndex][inColIndex] = inDatum;this.datumChange(inDatum, inRowIndex, inColIndex);},set: function(){throw('Plain "set" no longer supported. Use "setData", "setRow", or "setDatum".');},setRows: function(inData, inRowIndex){for(var i=0, l=inData.length, r=inRowIndex; i<l; i++, r++){this.setRow(inData[i], r);}},// updateupdate: function(inOldData, inNewData, inRowIndex){//delete this.cache[inRowIndex];//this.setRow(inNewData, inRowIndex);return true;},// insert_insert: function(inData, inRowIndex){dojox.grid.arrayInsert(this.data, inRowIndex, inData);this.count++;return true;},// remove_remove: function(inKeys){for(var i=inKeys.length-1; i>=0; i--){dojox.grid.arrayRemove(this.data, inKeys[i]);}this.count -= inKeys.length;return true;},// sortsort: function(/* (+|-)column_index+1, ... */){this.data.sort(this.makeComparator(arguments));},swap: function(inIndexA, inIndexB){dojox.grid.arraySwap(this.data, inIndexA, inIndexB);this.rowChange(this.getRow(inIndexA), inIndexA);this.rowChange(this.getRow(inIndexB), inIndexB);this.change();},dummy: 0});dojo.declare("dojox.grid.data.Objects", dojox.grid.data.Table, {constructor: function(inFields, inData, inKey){if(!inFields){this.autoAssignFields();}},autoAssignFields: function(){var d = this.data[0], i = 0;for(var f in d){this.fields.get(i++).key = f;}},getDatum: function(inRowIndex, inColIndex){return this.data[inRowIndex][this.fields.get(inColIndex).key];}});dojo.declare("dojox.grid.data.Dynamic", dojox.grid.data.Table, {// summary:// Grid data model for dynamic data such as data retrieved from a server.// Retrieves data automatically when requested and provides notification when data is receivedconstructor: function(){this.page = [];this.pages = [];},page: null,pages: null,rowsPerPage: 100,requests: 0,bop: -1,eop: -1,// dataclearData: function(){this.pages = [];this.bop = this.eop = -1;this.setData([]);},getRowCount: function(){return this.count;},getColCount: function(){return this.fields.count();},setRowCount: function(inCount){this.count = inCount;this.change();},// pagingrequestsPending: function(inBoolean){},rowToPage: function(inRowIndex){return (this.rowsPerPage ? Math.floor(inRowIndex / this.rowsPerPage) : inRowIndex);},pageToRow: function(inPageIndex){return (this.rowsPerPage ? this.rowsPerPage * inPageIndex : inPageIndex);},requestRows: function(inRowIndex, inCount){// summary:// stub. Fill in to perform actual data row fetching logic. The// returning logic must provide the data back to the system via// setRow},rowsProvided: function(inRowIndex, inCount){this.requests--;if(this.requests == 0){this.requestsPending(false);}},requestPage: function(inPageIndex){var row = this.pageToRow(inPageIndex);var count = Math.min(this.rowsPerPage, this.count - row);if(count > 0){this.requests++;this.requestsPending(true);setTimeout(dojo.hitch(this, "requestRows", row, count), 1);//this.requestRows(row, count);}},needPage: function(inPageIndex){if(!this.pages[inPageIndex]){this.pages[inPageIndex] = true;this.requestPage(inPageIndex);}},preparePage: function(inRowIndex, inColIndex){if(inRowIndex < this.bop || inRowIndex >= this.eop){var pageIndex = this.rowToPage(inRowIndex);this.needPage(pageIndex);this.bop = pageIndex * this.rowsPerPage;this.eop = this.bop + (this.rowsPerPage || this.count);}},isRowLoaded: function(inRowIndex){return Boolean(this.data[inRowIndex]);},// removalremovePages: function(inRowIndexes){for(var i=0, r; ((r=inRowIndexes[i]) != undefined); i++){this.pages[this.rowToPage(r)] = false;}this.bop = this.eop =-1;},remove: function(inRowIndexes){this.removePages(inRowIndexes);dojox.grid.data.Table.prototype.remove.apply(this, arguments);},// accessgetRow: function(inRowIndex){var row = this.data[inRowIndex];if(!row){this.preparePage(inRowIndex);}return row;},getDatum: function(inRowIndex, inColIndex){var row = this.getRow(inRowIndex);return (row ? row[inColIndex] : this.fields.get(inColIndex).na);},setDatum: function(inDatum, inRowIndex, inColIndex){var row = this.getRow(inRowIndex);if(row){row[inColIndex] = inDatum;this.datumChange(inDatum, inRowIndex, inColIndex);}else{console.debug('[' + this.declaredClass + '] dojox.grid.data.dynamic.set: cannot set data on an non-loaded row');}},// sortcanSort: function(){return false;}});// FIXME: deprecated: (included for backward compatibility only)dojox.grid.data.table = dojox.grid.data.Table;dojox.grid.data.dynamic = dojox.grid.data.Dyanamic;// we treat dojo.data stores as dynamic stores because no matter how they got// here, they should always fill that contractdojo.declare("dojox.grid.data.DojoData", dojox.grid.data.Dynamic, {// summary:// A grid data model for dynamic data retreived from a store which// implements the dojo.data API set. Retrieves data automatically when// requested and provides notification when data is received// description:// This store subclasses the Dynamic grid data object in order to// provide paginated data access support, notification and view// updates for stores which support those features, and simple// field/column mapping for all dojo.data stores.constructor: function(inFields, inData, args){this.count = 1;this._rowIdentities = {};if(args){dojo.mixin(this, args);}if(this.store){// NOTE: we assume Read and Identity APIs for all stores!var f = this.store.getFeatures();this._canNotify = f['dojo.data.api.Notification'];this._canWrite = f['dojo.data.api.Write'];if(this._canNotify){dojo.connect(this.store, "onSet", this, "_storeDatumChange");}}},markupFactory: function(args, node){return new dojox.grid.data.DojoData(null, null, args);},query: { name: "*" }, // default, stupid querystore: null,_canNotify: false,_canWrite: false,_rowIdentities: {},clientSort: false,// datasetData: function(inData){this.store = inData;this.data = [];this.allChange();},setRowCount: function(inCount){//console.debug("inCount:", inCount);this.count = inCount;this.allChange();},beginReturn: function(inCount){if(this.count != inCount){// this.setRowCount(0);// this.clear();// console.debug(this.count, inCount);this.setRowCount(inCount);}},_setupFields: function(dataItem){// abort if we already have setup fieldsif(this.fields._nameMaps){return;}// set up field/index mappingsvar m = {};//console.debug("setting up fields", m);var fields = dojo.map(this.store.getAttributes(dataItem),function(item, idx){m[item] = idx;m[idx+".idx"] = item;// name == display name, key = property namereturn { name: item, key: item };},this);this.fields._nameMaps = m;// console.debug("new fields:", fields);this.fields.set(fields);this.notify("FieldsChange");},_getRowFromItem: function(item){// gets us the row object (and row index) of an item},processRows: function(items, store){// console.debug(arguments);if(!items){ return; }this._setupFields(items[0]);dojo.forEach(items, function(item, idx){var row = {};row.__dojo_data_item = item;dojo.forEach(this.fields.values, function(a){row[a.name] = this.store.getValue(item, a.name)||"";}, this);// FIXME: where else do we need to keep this in sync?this._rowIdentities[this.store.getIdentity(item)] = store.start+idx;this.setRow(row, store.start+idx);}, this);// FIXME:// Q: scott, steve, how the hell do we actually get this to update// the visible UI for these rows?// A: the goal is that Grid automatically updates to reflect changes// in model. In this case, setRow -> rowChanged -> (observed by) Grid -> modelRowChange -> updateRow},// request datarequestRows: function(inRowIndex, inCount){var row = inRowIndex || 0;var params = {start: row,count: this.rowsPerPage,query: this.query,onBegin: dojo.hitch(this, "beginReturn"),// onItem: dojo.hitch(console, "debug"),onComplete: dojo.hitch(this, "processRows") // add to deferred?}// console.debug("requestRows:", row, this.rowsPerPage);this.store.fetch(params);},getDatum: function(inRowIndex, inColIndex){//console.debug("getDatum", inRowIndex, inColIndex);var row = this.getRow(inRowIndex);var field = this.fields.values[inColIndex];return row && field ? row[field.name] : field ? field.na : '?';//var idx = row && this.fields._nameMaps[inColIndex+".idx"];//return (row ? row[idx] : this.fields.get(inColIndex).na);},setDatum: function(inDatum, inRowIndex, inColIndex){var n = this.fields._nameMaps[inColIndex+".idx"];// console.debug("setDatum:", "n:"+n, inDatum, inRowIndex, inColIndex);if(n){this.data[inRowIndex][n] = inDatum;this.datumChange(inDatum, inRowIndex, inColIndex);}},// modification, update and store eventingcopyRow: function(inRowIndex){var row = {};var backstop = {};var src = this.getRow(inRowIndex);for(var x in src){if(src[x] != backstop[x]){row[x] = src[x];}}return row;},_attrCompare: function(cache, data){dojo.forEach(this.fields.values, function(a){if(cache[a.name] != data[a.name]){ return false; }}, this);return true;},endModifyRow: function(inRowIndex){var cache = this.cache[inRowIndex];if(cache){var data = this.getRow(inRowIndex);if(!this._attrCompare(cache, data)){this.update(cache, data, inRowIndex);}delete this.cache[inRowIndex];}},cancelModifyRow: function(inRowIndex){// console.debug("cancelModifyRow", arguments);var cache = this.cache[inRowIndex];if(cache){this.setRow(cache, inRowIndex);delete this.cache[inRowIndex];}},_storeDatumChange: function(item, attr, oldVal, newVal){// the store has changed some data under us, need to update the displayvar rowId = this._rowIdentities[this.store.getIdentity(item)];var row = this.getRow(rowId);row[attr] = newVal;var colId = this.fields._nameMaps[attr];this.notify("DatumChange", [ newVal, rowId, colId ]);},datumChange: function(value, rowIdx, colIdx){if(this._canWrite){// we're chaning some data, which means we need to write backvar row = this.getRow(rowIdx);var field = this.fields._nameMaps[colIdx+".idx"];this.store.setValue(row.__dojo_data_item, field, value);// we don't need to call DatumChange, an eventing store will tell// us about the row change events}else{// we can't write back, so just go ahead and change our local copy// of the datathis.notify("DatumChange", arguments);}},insertion: function(/* index */){console.debug("Insertion", arguments);this.notify("Insertion", arguments);this.notify("Change", arguments);},removal: function(/* keys */){console.debug("Removal", arguments);this.notify("Removal", arguments);this.notify("Change", arguments);},// sortcanSort: function(){// Q: Return true and re-issue the queries?// A: Return true only. Re-issue the query in 'sort'.return this.clientSort;}});}