if(!dojo._hasResource["dojox.grid.tests.databaseModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dojox.grid.tests.databaseModel"] = true; dojo.provide("dojox.grid.tests.databaseModel"); dojo.require("dojox.grid._data.model"); // Provides a sparse array that is also traversable inorder // with basic Array: // - iterating by index is slow for large sparse arrays // - for...in iteration is in order of element creation // maintains a secondary index for interating // over sparse elements inorder dojo.declare("dojox.grid.Sparse", null, { constructor: function() { this.clear(); }, clear: function() { this.indices = []; this.values = []; }, length: function() { return this.indices.length; }, set: function(inIndex, inValue) { for (var i=0,l=this.indices.length; i<l; i++) { if (this.indices[i] >= inIndex) break; } if (this.indices[i] != inIndex) this.indices.splice(i, 0, inIndex); this.values[inIndex] = inValue; }, get: function(inIndex) { return this.values[inIndex]; }, remove: function(inIndex) { for (var i=0,l=this.indices.length; i<l; i++) if (this.indices[i] == inIndex) { this.indices.splice(i, 1); break; } delete this.values[inIndex]; }, inorder: function(inFor) { for (var i=0,l=this.indices.length, ix; i<l; i++) { ix = this.indices[i]; if (inFor(this.values[ix], ix) === false) break; } } }); // sample custom model implementation that works with mysql server. dojo.declare("dojox.grid.data.DbTable", dojox.grid.data.Dynamic, { delayedInsertCommit: true, constructor: function(inFields, inData, inServer, inDatabase, inTable) { this.server = inServer; this.database = inDatabase; this.table = inTable; this.stateNames = ['inflight', 'inserting', 'removing', 'error']; this.clearStates(); this.clearSort(); }, clearData: function() { this.cache = [ ]; this.clearStates(); this.inherited(arguments); }, clearStates: function() { this.states = {}; for (var i=0, s; (s=this.stateNames[i]); i++) { delete this.states[s]; this.states[s] = new dojox.grid.Sparse(); } }, // row state information getState: function(inRowIndex) { for (var i=0, r={}, s; (s=this.stateNames[i]); i++) r[s] = this.states[s].get(inRowIndex); return r; }, setState: function(inRowIndex, inState, inValue) { this.states[inState].set(inRowIndex, inValue||true); }, clearState: function(inRowIndex, inState) { if (arguments.length == 1) { for (var i=0, s; (s=this.stateNames[i]); i++) this.states[s].remove(inRowIndex); } else { for (var i=1, l=arguments.length, arg; (i<l) &&((arg=arguments[i])!=undefined); i++) this.states[arg].remove(inRowIndex); } }, setStateForIndexes: function(inRowIndexes, inState, inValue) { for (var i=inRowIndexes.length-1, k; (i>=0) && ((k=inRowIndexes[i])!=undefined); i--) this.setState(k, inState, inValue); }, clearStateForIndexes: function(inRowIndexes, inState) { for (var i=inRowIndexes.length-1, k; (i>=0) && ((k=inRowIndexes[i])!=undefined); i--) this.clearState(k, inState); }, //$ Return boolean stating whether or not an operation is in progress that may change row indexing. isAddRemoving: function() { return Boolean(this.states['inserting'].length() || this.states['removing'].length()); }, isInflight: function() { return Boolean(this.states['inflight'].length()); }, //$ Return boolean stating if the model is currently undergoing any type of edit. isEditing: function() { for (var i=0, r={}, s; (s=this.stateNames[i]); i++) if (this.states[s].length()) return true; }, //$ Return true if ok to modify the given row. Override as needed, using model editing state information. canModify: function(inRowIndex) { return !this.getState(inRowIndex).inflight && !(this.isInflight() && this.isAddRemoving()); }, // server send / receive getSendParams: function(inParams) { var p = { database: this.database || '', table: this.table || '' } return dojo.mixin(p, inParams || {}); }, send: function(inAsync, inParams, inCallbacks) { //console.log('send', inParams.command); var p = this.getSendParams(inParams); var d = dojo.xhrPost({ url: this.server, content: p, handleAs: 'json-comment-filtered', contentType: "application/x-www-form-urlencoded; charset=utf-8", sync: !inAsync }); d.addCallbacks(dojo.hitch(this, "receive", inCallbacks), dojo.hitch(this, "receiveError", inCallbacks)); return d; }, _callback: function(cb, eb, data) { try{ cb && cb(data); } catch(e){ eb && eb(data, e); } }, receive: function(inCallbacks, inData) { inCallbacks && this._callback(inCallbacks.callback, inCallbacks.errback, inData); }, receiveError: function(inCallbacks, inErr) { this._callback(inCallbacks.errback, null, inErr) }, encodeRow: function(inParams, inRow, inPrefix) { for (var i=0, l=inRow.length; i < l; i++) inParams['_' + (inPrefix ? inPrefix : '') + i] = (inRow[i] ? inRow[i] : ''); }, measure: function() { this.send(true, { command: 'info' }, { callback: dojo.hitch(this, this.callbacks.info) }); }, fetchRowCount: function(inCallbacks) { this.send(true, { command: 'count' }, inCallbacks); }, // server commits commitEdit: function(inOldData, inNewData, inRowIndex, inCallbacks) { this.setState(inRowIndex, "inflight", true); var params = {command: 'update'}; this.encodeRow(params, inOldData, 'o'); this.encodeRow(params, inNewData); this.send(true, params, inCallbacks); }, commitInsert: function(inRowIndex, inNewData, inCallbacks) { this.setState(inRowIndex, "inflight", true); var params = {command: 'insert'}; this.encodeRow(params, inNewData); this.send(true, params, inCallbacks); }, // NOTE: supported only in tables with pk commitDelete: function(inRows, inCallbacks) { var params = { command: 'delete', count: inRows.length } var pk = this.getPkIndex(); if (pk < 0) return; for (var i=0; i < inRows.length; i++) { params['_' + i] = inRows[i][pk]; } this.send(true, params, inCallbacks); }, getUpdateCallbacks: function(inRowIndex) { return { callback: dojo.hitch(this, this.callbacks.update, inRowIndex), errback: dojo.hitch(this, this.callbacks.updateError, inRowIndex) }; }, // primary key from fields getPkIndex: function() { for (var i=0, l=this.fields.count(), f; (i<l) && (f=this.fields.get(i)); i++) if (f.Key = 'PRI') return i; return -1; }, // model implementations update: function(inOldData, inNewData, inRowIndex) { var cbs = this.getUpdateCallbacks(inRowIndex); if (this.getState(inRowIndex).inserting) this.commitInsert(inRowIndex, inNewData, cbs); else this.commitEdit(this.cache[inRowIndex] || inOldData, inNewData, inRowIndex, cbs); // set push data immediately to model so reflectd while committing this.setRow(inNewData, inRowIndex); }, insert: function(inData, inRowIndex) { this.setState(inRowIndex, 'inserting', true); if (!this.delayedInsertCommit) this.commitInsert(inRowIndex, inData, this.getUpdateCallbacks(inRowIndex)); return this.inherited(arguments); }, remove: function(inRowIndexes) { var rows = []; for (var i=0, r=0, indexes=[]; (r=inRowIndexes[i]) !== undefined; i++) if (!this.getState(r).inserting) { rows.push(this.getRow(r)); indexes.push(r); this.setState(r, 'removing'); } var cbs = { callback: dojo.hitch(this, this.callbacks.remove, indexes), errback: dojo.hitch(this, this.callbacks.removeError, indexes) }; this.commitDelete(rows, cbs); dojox.grid.data.Dynamic.prototype.remove.apply(this, arguments); }, cancelModifyRow: function(inRowIndex) { if (this.isDelayedInsert(inRowIndex)) { this.removeInsert(inRowIndex); } else this.finishUpdate(inRowIndex); }, finishUpdate: function(inRowIndex, inData) { this.clearState(inRowIndex); var d = (inData&&inData[0]) || this.cache[inRowIndex]; if (d) this.setRow(d, inRowIndex); delete this.cache[inRowIndex]; }, isDelayedInsert: function(inRowIndex) { return (this.delayedInsertCommit && this.getState(inRowIndex).inserting); }, removeInsert: function(inRowIndex) { this.clearState(inRowIndex); dojox.grid.data.Dynamic.prototype.remove.call(this, [inRowIndex]); }, // request data requestRows: function(inRowIndex, inCount) { var params = { command: 'select', orderby: this.sortField, desc: (this.sortDesc ? "true" : ''), offset: inRowIndex, limit: inCount } this.send(true, params, {callback: dojo.hitch(this, this.callbacks.rows, inRowIndex)}); }, // sorting canSort: function () { return true; }, setSort: function(inSortIndex) { this.sortField = this.fields.get(Math.abs(inSortIndex) - 1).name || inSortIndex; this.sortDesc = (inSortIndex < 0); }, sort: function(inSortIndex) { this.setSort(inSortIndex); this.clearData(); }, clearSort: function(){ this.sortField = ''; this.sortDesc = false; }, endModifyRow: function(inRowIndex){ var cache = this.cache[inRowIndex]; var m = false; if(cache){ var data = this.getRow(inRowIndex); if(!dojox.grid.arrayCompare(cache, data)){ m = true; this.update(cache, data, inRowIndex); } } if (!m) this.cancelModifyRow(inRowIndex); }, // server callbacks (called with this == model) callbacks: { update: function(inRowIndex, inData) { console.log('received update', arguments); if (inData.error) this.updateError(inData) else this.finishUpdate(inRowIndex, inData); }, updateError: function(inRowIndex) { this.clearState(inRowIndex, 'inflight'); this.setState(inRowIndex, "error", "update failed: " + inRowIndex); this.rowChange(this.getRow(inRowIndex), inRowIndex); }, remove: function(inRowIndexes) { this.clearStateForIndexes(inRowIndexes); }, removeError: function(inRowIndexes) { this.clearStateForIndexes(inRowIndexes); alert('Removal error. Please refresh.'); }, rows: function(inRowIndex, inData) { //this.beginUpdate(); for (var i=0, l=inData.length; i<l; i++) this.setRow(inData[i], inRowIndex + i); //this.endUpdate(); //this.allChange(); }, count: function(inRowCount) { this.count = Number(inRowCount); this.clearData(); }, info: function(inInfo) { this.fields.clear(); for (var i=0, c; (c=inInfo.columns[i]); i++) { c.name = c.Field; this.fields.set(i, c); } this.table = inInfo.table; this.database = inInfo.database; this.notify("MetaData", arguments); this.callbacks.count.call(this, inInfo.count); } } }); }