From e44a7e37b6c7b5961adaffc62b9042b8d442938e Mon Sep 17 00:00:00 2001 From: mensonge Date: Thu, 13 Nov 2008 09:49:11 +0000 Subject: New feature: basic Ajax suggestion for tags and implementation of Dojo toolkit git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f --- includes/js/dojox/grid/_data/dijitEditors.js | 170 ++++++ includes/js/dojox/grid/_data/editors.js | 239 +++++++++ includes/js/dojox/grid/_data/fields.js | 104 ++++ includes/js/dojox/grid/_data/model.js | 762 +++++++++++++++++++++++++++ 4 files changed, 1275 insertions(+) create mode 100644 includes/js/dojox/grid/_data/dijitEditors.js create mode 100644 includes/js/dojox/grid/_data/editors.js create mode 100644 includes/js/dojox/grid/_data/fields.js create mode 100644 includes/js/dojox/grid/_data/model.js (limited to 'includes/js/dojox/grid/_data') diff --git a/includes/js/dojox/grid/_data/dijitEditors.js b/includes/js/dojox/grid/_data/dijitEditors.js new file mode 100644 index 0000000..695de44 --- /dev/null +++ b/includes/js/dojox/grid/_data/dijitEditors.js @@ -0,0 +1,170 @@ +if(!dojo._hasResource["dojox.grid._data.dijitEditors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojox.grid._data.dijitEditors"] = true; +dojo.provide("dojox.grid._data.dijitEditors"); +dojo.require("dojox.grid._data.editors"); +dojo.require("dijit.form.DateTextBox"); +dojo.require("dijit.form.TimeTextBox"); +dojo.require("dijit.form.ComboBox"); +dojo.require("dojo.data.ItemFileReadStore"); +dojo.require("dijit.form.CheckBox"); +dojo.require("dijit.form.TextBox"); +dojo.require("dijit.form.NumberSpinner"); +dojo.require("dijit.form.NumberTextBox"); +dojo.require("dijit.form.CurrencyTextBox"); +dojo.require("dijit.form.Slider"); +dojo.require("dijit.Editor"); + +dojo.declare("dojox.grid.editors.Dijit", dojox.grid.editors.base, { + editorClass: "dijit.form.TextBox", + constructor: function(inCell){ + this.editor = null; + this.editorClass = dojo.getObject(this.cell.editorClass || this.editorClass); + }, + format: function(inDatum, inRowIndex){ + this.needFormatNode(inDatum, inRowIndex); + return "
"; + }, + getValue: function(inRowIndex){ + return this.editor.getValue(); + }, + setValue: function(inRowIndex, inValue){ + if(this.editor&&this.editor.setValue){ + this.editor.setValue(inValue); + }else{ + this.inherited(arguments); + } + }, + getEditorProps: function(inDatum){ + return dojo.mixin({}, this.cell.editorProps||{}, { + constraints: dojo.mixin({}, this.cell.constraint) || {}, //TODO: really just for ValidationTextBoxes + value: inDatum + }); + }, + createEditor: function(inNode, inDatum, inRowIndex){ + return new this.editorClass(this.getEditorProps(inDatum), inNode); + + }, + attachEditor: function(inNode, inDatum, inRowIndex){ + inNode.appendChild(this.editor.domNode); + this.setValue(inRowIndex, inDatum); + }, + formatNode: function(inNode, inDatum, inRowIndex){ + if(!this.editorClass){ + return inDatum; + } + if(!this.editor){ + this.editor = this.createEditor.apply(this, arguments); + }else{ + this.attachEditor.apply(this, arguments); + } + this.sizeEditor.apply(this, arguments); + this.cell.grid.rowHeightChanged(inRowIndex); + this.focus(); + }, + sizeEditor: function(inNode, inDatum, inRowIndex){ + var + p = this.cell.getNode(inRowIndex), + box = dojo.contentBox(p); + dojo.marginBox(this.editor.domNode, {w: box.w}); + }, + focus: function(inRowIndex, inNode){ + if(this.editor){ + setTimeout(dojo.hitch(this.editor, function(){ + dojox.grid.fire(this, "focus"); + }), 0); + } + }, + _finish: function(inRowIndex){ + this.inherited(arguments); + dojox.grid.removeNode(this.editor.domNode); + } +}); + +dojo.declare("dojox.grid.editors.ComboBox", dojox.grid.editors.Dijit, { + editorClass: "dijit.form.ComboBox", + getEditorProps: function(inDatum){ + var items=[]; + dojo.forEach(this.cell.options, function(o){ + items.push({name: o, value: o}); + }); + var store = new dojo.data.ItemFileReadStore({data: {identifier:"name", items: items}}); + return dojo.mixin({}, this.cell.editorProps||{}, { + value: inDatum, + store: store + }); + }, + getValue: function(){ + var e = this.editor; + // make sure to apply the displayed value + e.setDisplayedValue(e.getDisplayedValue()); + return e.getValue(); + } +}); + +dojo.declare("dojox.grid.editors.DateTextBox", dojox.grid.editors.Dijit, { + editorClass: "dijit.form.DateTextBox", + setValue: function(inRowIndex, inValue){ + if(this.editor){ + this.editor.setValue(new Date(inValue)); + }else{ + this.inherited(arguments); + } + }, + getEditorProps: function(inDatum){ + return dojo.mixin(this.inherited(arguments), { + value: new Date(inDatum) + }); + } +}); + + +dojo.declare("dojox.grid.editors.CheckBox", dojox.grid.editors.Dijit, { + editorClass: "dijit.form.CheckBox", + getValue: function(){ + return this.editor.checked; + }, + setValue: function(inRowIndex, inValue){ + if(this.editor&&this.editor.setAttribute){ + this.editor.setAttribute("checked", inValue); + }else{ + this.inherited(arguments); + } + }, + sizeEditor: function(inNode, inDatum, inRowIndex){ + return; + } +}); + + +dojo.declare("dojox.grid.editors.Editor", dojox.grid.editors.Dijit, { + editorClass: "dijit.Editor", + getEditorProps: function(inDatum){ + return dojo.mixin({}, this.cell.editorProps||{}, { + height: this.cell.editorHeight || "100px" + }); + }, + createEditor: function(inNode, inDatum, inRowIndex){ + // editor needs its value set after creation + var editor = new this.editorClass(this.getEditorProps(inDatum), inNode); + dojo.connect(editor, 'onLoad', dojo.hitch(this, 'populateEditor')); + return editor; + }, + formatNode: function(inNode, inDatum, inRowIndex){ + this.content = inDatum; + this.inherited(arguments); + if(dojo.isMoz){ + // FIXME: seem to need to reopen the editor and display the toolbar + var e = this.editor; + e.open(); + if(this.cell.editorToolbar){ + dojo.place(e.toolbar.domNode, e.editingArea, "before"); + } + } + }, + populateEditor: function(){ + this.editor.setValue(this.content); + this.editor.placeCursorAtEnd(); + } +}); + +} diff --git a/includes/js/dojox/grid/_data/editors.js b/includes/js/dojox/grid/_data/editors.js new file mode 100644 index 0000000..48f76cc --- /dev/null +++ b/includes/js/dojox/grid/_data/editors.js @@ -0,0 +1,239 @@ +if(!dojo._hasResource["dojox.grid._data.editors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojox.grid._data.editors"] = true; +dojo.provide("dojox.grid._data.editors"); +dojo.provide("dojox.grid.editors"); + +dojo.declare("dojox.grid.editors.Base", null, { + // summary: + // base grid editor class. Other grid editors should inherited from this class. + constructor: function(inCell){ + this.cell = inCell; + }, + //private + _valueProp: "value", + _formatPending: false, + format: function(inDatum, inRowIndex){ + // summary: + // formats the cell for editing + // inDatum: anything + // cell data to edit + // inRowIndex: int + // grid row index + // returns: string of html to place in grid cell + }, + //protected + needFormatNode: function(inDatum, inRowIndex){ + this._formatPending = true; + dojox.grid.whenIdle(this, "_formatNode", inDatum, inRowIndex); + }, + cancelFormatNode: function(){ + this._formatPending = false; + }, + //private + _formatNode: function(inDatum, inRowIndex){ + if(this._formatPending){ + this._formatPending = false; + // make cell selectable + dojo.setSelectable(this.cell.grid.domNode, true); + this.formatNode(this.getNode(inRowIndex), inDatum, inRowIndex); + } + }, + //protected + getNode: function(inRowIndex){ + return (this.cell.getNode(inRowIndex) || 0).firstChild || 0; + }, + formatNode: function(inNode, inDatum, inRowIndex){ + // summary: + // format the editing dom node. Use when editor is a widget. + // inNode: dom node + // dom node for the editor + // inDatum: anything + // cell data to edit + // inRowIndex: int + // grid row index + if(dojo.isIE){ + // IE sux bad + dojox.grid.whenIdle(this, "focus", inRowIndex, inNode); + }else{ + this.focus(inRowIndex, inNode); + } + }, + dispatchEvent: function(m, e){ + if(m in this){ + return this[m](e); + } + }, + //public + getValue: function(inRowIndex){ + // summary: + // returns value entered into editor + // inRowIndex: int + // grid row index + // returns: + // value of editor + return this.getNode(inRowIndex)[this._valueProp]; + }, + setValue: function(inRowIndex, inValue){ + // summary: + // set the value of the grid editor + // inRowIndex: int + // grid row index + // inValue: anything + // value of editor + var n = this.getNode(inRowIndex); + if(n){ + n[this._valueProp] = inValue + }; + }, + focus: function(inRowIndex, inNode){ + // summary: + // focus the grid editor + // inRowIndex: int + // grid row index + // inNode: dom node + // editor node + dojox.grid.focusSelectNode(inNode || this.getNode(inRowIndex)); + }, + save: function(inRowIndex){ + // summary: + // save editor state + // inRowIndex: int + // grid row index + this.value = this.value || this.getValue(inRowIndex); + //console.log("save", this.value, inCell.index, inRowIndex); + }, + restore: function(inRowIndex){ + // summary: + // restore editor state + // inRowIndex: int + // grid row index + this.setValue(inRowIndex, this.value); + //console.log("restore", this.value, inCell.index, inRowIndex); + }, + //protected + _finish: function(inRowIndex){ + // summary: + // called when editing is completed to clean up editor + // inRowIndex: int + // grid row index + dojo.setSelectable(this.cell.grid.domNode, false); + this.cancelFormatNode(this.cell); + }, + //public + apply: function(inRowIndex){ + // summary: + // apply edit from cell editor + // inRowIndex: int + // grid row index + this.cell.applyEdit(this.getValue(inRowIndex), inRowIndex); + this._finish(inRowIndex); + }, + cancel: function(inRowIndex){ + // summary: + // cancel cell edit + // inRowIndex: int + // grid row index + this.cell.cancelEdit(inRowIndex); + this._finish(inRowIndex); + } +}); +dojox.grid.editors.base = dojox.grid.editors.Base; // back-compat + +dojo.declare("dojox.grid.editors.Input", dojox.grid.editors.Base, { + // summary + // grid cell editor that provides a standard text input box + constructor: function(inCell){ + this.keyFilter = this.keyFilter || this.cell.keyFilter; + }, + // keyFilter: object + // optional regex for disallowing keypresses + keyFilter: null, + format: function(inDatum, inRowIndex){ + this.needFormatNode(inDatum, inRowIndex); + return ''; + }, + formatNode: function(inNode, inDatum, inRowIndex){ + this.inherited(arguments); + // FIXME: feels too specific for this interface + this.cell.registerOnBlur(inNode, inRowIndex); + }, + doKey: function(e){ + if(this.keyFilter){ + var key = String.fromCharCode(e.charCode); + if(key.search(this.keyFilter) == -1){ + dojo.stopEvent(e); + } + } + }, + _finish: function(inRowIndex){ + this.inherited(arguments); + var n = this.getNode(inRowIndex); + try{ + dojox.grid.fire(n, "blur"); + }catch(e){} + } +}); +dojox.grid.editors.input = dojox.grid.editors.Input; // back compat + +dojo.declare("dojox.grid.editors.Select", dojox.grid.editors.Input, { + // summary: + // grid cell editor that provides a standard select + // options: text of each item + // values: value for each item + // returnIndex: editor returns only the index of the selected option and not the value + constructor: function(inCell){ + this.options = this.options || this.cell.options; + this.values = this.values || this.cell.values || this.options; + }, + format: function(inDatum, inRowIndex){ + this.needFormatNode(inDatum, inRowIndex); + var h = [ ''); + return h.join(''); + }, + getValue: function(inRowIndex){ + var n = this.getNode(inRowIndex); + if(n){ + var i = n.selectedIndex, o = n.options[i]; + return this.cell.returnIndex ? i : o.value || o.innerHTML; + } + } +}); +dojox.grid.editors.select = dojox.grid.editors.Select; // back compat + +dojo.declare("dojox.grid.editors.AlwaysOn", dojox.grid.editors.Input, { + // summary: + // grid cell editor that is always on, regardless of grid editing state + // alwaysOn: boolean + // flag to use editor to format grid cell regardless of editing state. + alwaysOn: true, + _formatNode: function(inDatum, inRowIndex){ + this.formatNode(this.getNode(inRowIndex), inDatum, inRowIndex); + }, + applyStaticValue: function(inRowIndex){ + var e = this.cell.grid.edit; + e.applyCellEdit(this.getValue(inRowIndex), this.cell, inRowIndex); + e.start(this.cell, inRowIndex, true); + } +}); +dojox.grid.editors.alwaysOn = dojox.grid.editors.AlwaysOn; // back-compat + +dojo.declare("dojox.grid.editors.Bool", dojox.grid.editors.AlwaysOn, { + // summary: + // grid cell editor that provides a standard checkbox that is always on + _valueProp: "checked", + format: function(inDatum, inRowIndex){ + return ''; + }, + doclick: function(e){ + if(e.target.tagName == 'INPUT'){ + this.applyStaticValue(e.rowIndex); + } + } +}); +dojox.grid.editors.bool = dojox.grid.editors.Bool; // back-compat + +} diff --git a/includes/js/dojox/grid/_data/fields.js b/includes/js/dojox/grid/_data/fields.js new file mode 100644 index 0000000..230bede --- /dev/null +++ b/includes/js/dojox/grid/_data/fields.js @@ -0,0 +1,104 @@ +if(!dojo._hasResource["dojox.grid._data.fields"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojox.grid._data.fields"] = true; +dojo.provide("dojox.grid._data.fields"); + +dojo.declare("dojox.grid.data.Mixer", null, { + // summary: + // basic collection class that provides a default value for items + + constructor: function(){ + this.defaultValue = {}; + this.values = []; + }, + count: function(){ + return this.values.length; + }, + clear: function(){ + this.values = []; + }, + build: function(inIndex){ + var result = dojo.mixin({owner: this}, this.defaultValue); + result.key = inIndex; + this.values[inIndex] = result; + return result; + }, + getDefault: function(){ + return this.defaultValue; + }, + setDefault: function(inField /*[, inField2, ... inFieldN] */){ + for(var i=0, a; (a = arguments[i]); i++){ + dojo.mixin(this.defaultValue, a); + } + }, + get: function(inIndex){ + return this.values[inIndex] || this.build(inIndex); + }, + _set: function(inIndex, inField /*[, inField2, ... inFieldN] */){ + // each field argument can be a single field object of an array of field objects + var v = this.get(inIndex); + for(var i=1; i= this.values.length){ + this.values[inIndex] = inProps; + }else{ + this.values.splice(inIndex, 0, inProps); + } + }, + remove: function(inIndex){ + this.values.splice(inIndex, 1); + }, + swap: function(inIndexA, inIndexB){ + dojox.grid.arraySwap(this.values, inIndexA, inIndexB); + }, + move: function(inFromIndex, inToIndex){ + dojox.grid.arrayMove(this.values, inFromIndex, inToIndex); + } +}); + +dojox.grid.data.compare = function(a, b){ + return (a > b ? 1 : (a == b ? 0 : -1)); +} + +dojo.declare('dojox.grid.data.Field', null, { + constructor: function(inName){ + this.name = inName; + this.compare = dojox.grid.data.compare; + }, + na: dojox.grid.na +}); + +dojo.declare('dojox.grid.data.Fields', dojox.grid.data.Mixer, { + constructor: function(inFieldClass){ + var fieldClass = inFieldClass ? inFieldClass : dojox.grid.data.Field; + this.defaultValue = new fieldClass(); + }, + indexOf: function(inKey){ + for(var i=0; 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]; + } + } +}); + +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=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(); + } + }, + allChange: function(){ + this.notify("FieldsChange"); + this.inherited(arguments); + }, + autoAssignFields: function(){ + var d = this.data[0], i = 0, field; + for(var f in d){ + field = this.fields.get(i++); + if (!dojo.isString(field.key)){ + field.key = f; + } + } + }, + setData: function(inData){ + this.data = (inData || []); + this.autoAssignFields(); + this.allChange(); + }, + 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.Dynamic; + +// 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 = {}; + this._currentlyProcessing = []; + if(args){ + dojo.mixin(this, args); + } + if(this.store){ + var f = this.store.getFeatures(); + this._canNotify = f['dojo.data.api.Notification']; + this._canWrite = f['dojo.data.api.Write']; + this._canIdentify = f['dojo.data.api.Identity']; + if(this._canNotify){ + dojo.connect(this.store, "onSet", this, "_storeDatumChange"); + dojo.connect(this.store, "onDelete", this, "_storeDatumDelete"); + dojo.connect(this.store, "onNew", this, "_storeDatumNew"); + } + if(this._canWrite) { + dojo.connect(this.store, "revert", this, "refresh"); + } + } + }, + markupFactory: function(args, node){ + return new dojox.grid.data.DojoData(null, null, args); + }, + query: { name: "*" }, // default, stupid query + store: null, + _currentlyProcessing: null, + _canNotify: false, + _canWrite: false, + _canIdentify: false, + _rowIdentities: {}, + clientSort: false, + sortFields: null, + queryOptions: null, + + // 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 + }, + _createRow: function(item){ + var row = {}; + row.__dojo_data_item = item; + dojo.forEach(this.fields.values, function(a){ + value = this.store.getValue(item, a.name); + row[a.name] = (value === undefined || value === null)?"":value; + }, this); + return row; + }, + processRows: function(items, request){ + // console.debug(arguments); + if(!items || items.length == 0){ return; } + this._setupFields(items[0]); + dojo.forEach(items, function(item, idx){ + var row = this._createRow(item); + this._setRowId(item, request.start, idx); + this.setRow(row, request.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, + sort: this.sortFields, + queryOptions: this.queryOptions, + onBegin: dojo.hitch(this, "beginReturn"), + onComplete: dojo.hitch(this, "processRows"), // add to deferred? + onError: dojo.hitch(this, "processError") + }; + 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]; + } + }, + _setRowId: function(item, offset, idx){ + // FIXME: where else do we need to keep this in sync? + //Handle stores that implement identity and try to handle those that do not. + if (this._canIdentify) { + this._rowIdentities[this.store.getIdentity(item)] = {rowId: offset+idx, item: item}; + }else{ + var identity = dojo.toJson(this.query) + ":start:" + offset + ":idx:" + idx + ":sort:" + dojo.toJson(this.sortFields); + this._rowIdentities[identity] = {rowId: offset+idx, item: item}; + } + }, + _getRowId: function(item, isNotItem){ + // summary: + // Function determine the row index for a particular item + // item: + // The store item to examine to determine row index. + // isNotItem: + // Boolean flag to indicate if the item passed is a store item or not. + var rowId = null; + //Handle identity and nonidentity capable stores. + if(this._canIdentify && !isNotItem){ + rowId = this._rowIdentities[this.store.getIdentity(item)].rowId; + }else{ + //Not efficient, but without identity support, + //not a better way to do it. Basically, do our best to locate it + //This may or may not work, but best we can do here. + var id; + for(id in this._rowIdentities){ + if(this._rowIdentities[id].item === item){ + rowId = this._rowIdentities[id].rowId; + break; + } + } + } + return rowId; + }, + _storeDatumChange: function(item, attr, oldVal, newVal){ + // the store has changed some data under us, need to update the display + var rowId = this._getRowId(item); + var row = this.getRow(rowId); + if(row){ + row[attr] = newVal; + var colId = this.fields._nameMaps[attr]; + this.notify("DatumChange", [ newVal, rowId, colId ]); + } + }, + _storeDatumDelete: function(item){ + if(dojo.indexOf(this._currentlyProcessing, item) != -1) + return; + // the store has deleted some item under us, need to remove that item from + // the view if possible. It may be the deleted item isn't even in the grid. + var rowId = this._getRowId(item, true); + if(rowId != null){ + this._removeItems([rowId]); + } + }, + _storeDatumNew: function(item){ + if(this._disableNew){ + return; + } + // the store has added some item under us, need to add it to the view. + this._insertItem(item, this.data.length); + }, + insert: function(item, index){ + // Push the given item back to the store + this._disableNew = true; + var i = this.store.newItem(item); + this._disableNew = false; + this._insertItem(i, index); + }, + _insertItem: function(storeItem, index){ + // Set up our fields if we haven't already + if(!this.fields._nameMaps){ + this._setupFields(storeItem); + } + var row = this._createRow(storeItem); + for(var i in this._rowIdentities){ //increment all the remaining row ids up one + var rowIdentity = this._rowIdentities[i]; + if(rowIdentity.rowId >= index){ + rowIdentity.rowId++; + } + } + this._setRowId(storeItem, 0, index); + dojox.grid.data.Dynamic.prototype.insert.apply(this, [row, index]); + }, + 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); + }, + remove: function(inRowIndexes){ + // summary: + // Function to remove a set of items from the store based on the row index. + // inRowIndexes: + // An array of row indexes from the grid to remove from the store. + /* Call delete on the store */ + for(var i=inRowIndexes.length-1; i>=0; i--){ + // Need to find the item, then remove each from the data store + var item = this.data[inRowIndexes[i]].__dojo_data_item; + this._currentlyProcessing.push(item); + this.store.deleteItem(item); + } + /* Remove from internal data structure and the view */ + this._removeItems(inRowIndexes); + this._currentlyProcessing = []; + }, + _removeItems: function(inRowIndexes /*array*/){ + // summary: + // Function to remove a set of items from the store based on the row index. + // inRowIndexes: + // An array of row indexes from the grid to remove from the store. + dojox.grid.data.Dynamic.prototype.remove.apply(this, arguments); + // Rebuild _rowIdentities + this._rowIdentities = {}; + for (var i = 0; i < this.data.length; i++){ + this._setRowId(this.data[i].__dojo_data_item, 0, i); + } + }, + canSort: function(){ + // Q: Return true and re-issue the queries? + // A: Return true only. Re-issue the query in 'sort'. + // Note, above are original comments :) + return true; + }, + sort: function(colIndex){ + var col = Math.abs(colIndex) - 1; + this.sortFields = [{'attribute': this.fields.values[col].name, 'descending': (colIndex>0)}]; + + // Since we're relying on the data store to sort, we have to refresh our data. + this.refresh(); + }, + refresh: function(){ + // summary: + // Function to cause the model to re-query the store and rebuild the current viewport. + this.clearData(true); + this.requestRows(); + }, + clearData: function(/* boolean */ keepStore){ + this._rowIdentities = {}; + this.pages = []; + this.bop = this.eop = -1; + this.count = 0; + this.setData((keepStore?this.store:[])); + }, + processError: function(error, request){ + // summary: + // Hook function to trap error messages from the store and emit them. + // Intended for connecting to and handling the error object or at least reporting it. + // + // error: + // The error object returned by the store when a problem occurred. + // request: + // The request object that caused the error. + console.log(error); + } +}); + +} -- cgit v1.2.3