123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- 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,
- // observers
- observer : 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));
- }
- }
- },
- // updates
- clear : 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);
- },
- // data
- clearData : function() {
- this.setData(null);
- },
- // observer events
- change : 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);
- },
- // insert
- insert : function(inData /* , index */) {
- if (!this._insert.apply(this, arguments)) {
- return false;
- }
- this.insertion.apply(this, dojo._toArray(arguments, 1));
- return true;
- },
- // remove
- remove : function(inData /* , index */) {
- if (!this._remove.apply(this, arguments)) {
- return false;
- }
- this.removal.apply(this, arguments);
- return true;
- },
- // sort
- canSort : 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 events
- allChange : 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
- // update
- beginModifyRow : 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 data
- constructor : function() {
- this.cache = [];
- },
- colCount : 0, // tables introduce cols
- data : null,
- cache : null,
- // morphology
- measure : 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)));
- },
- // access
- getRow : 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);
- }
- },
- // update
- update : 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;
- },
- // sort
- sort : 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 received
- constructor : function() {
- this.page = [];
- this.pages = [];
- },
- page : null,
- pages : null,
- rowsPerPage : 100,
- requests : 0,
- bop : -1,
- eop : -1,
- // data
- clearData : 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();
- },
- // paging
- requestsPending : 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]);
- },
- // removal
- removePages : 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);
- },
- // access
- getRow : 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');
- }
- },
- // sort
- canSort : 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 contract
- dojo.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 query
- store : null,
- _canNotify : false,
- _canWrite : false,
- _rowIdentities : {},
- clientSort : false,
- // data
- setData : 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 fields
- if (this.fields._nameMaps) {
- return;
- }
- // set up field/index mappings
- var 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 name
- return {
- 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 data
- requestRows : 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 eventing
- copyRow : 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
- // display
- var 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 back
- var 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 data
- this.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);
- },
- // sort
- canSort : function() {
- // Q: Return true and re-issue the queries?
- // A: Return true only. Re-issue the query in 'sort'.
- return this.clientSort;
- }
- });
- }
|