8398c9048d
code was modified slightly, so the code differs from the original downloadable 1.9.5 version
367 lines
9.6 KiB
JavaScript
367 lines
9.6 KiB
JavaScript
dojo.provide("dojox.grid.compat.Grid");
|
|
dojo.require("dojox.grid.compat.VirtualGrid");
|
|
dojo.require("dojox.grid.compat._data.model");
|
|
dojo.require("dojox.grid.compat._data.editors");
|
|
dojo.require("dojox.grid.compat._data.dijitEditors");
|
|
|
|
// FIXME:
|
|
// we are at the wrong location!
|
|
|
|
dojo.declare('dojox.Grid', dojox.VirtualGrid, {
|
|
// summary:
|
|
// A grid widget with virtual scrolling, cell editing, complex rows,
|
|
// sorting, fixed columns, sizeable columns, etc.
|
|
// description:
|
|
// Grid is a subclass of VirtualGrid, providing binding to a data
|
|
// store.
|
|
// example:
|
|
// define the grid structure:
|
|
// | var structure = [ // array of view objects
|
|
// | { cells: [// array of rows, a row is an array of cells
|
|
// | [ { name: "Alpha", width: 6 },
|
|
// | { name: "Beta" },
|
|
// | { name: "Gamma", get: formatFunction }
|
|
// | ]
|
|
// | ]}
|
|
// | ];
|
|
//
|
|
// define a grid data model
|
|
// | var model = new dojox.grid.data.table(null, data);
|
|
// |
|
|
// | <div id="grid" model="model" structure="structure"
|
|
// | dojoType="dojox.VirtualGrid"></div>
|
|
//
|
|
|
|
// model:
|
|
// string or object grid data model
|
|
model: 'dojox.grid.data.Table',
|
|
|
|
// life cycle
|
|
postCreate: function(){
|
|
if(this.model){
|
|
var m = this.model;
|
|
if(dojo.isString(m)){
|
|
m = dojo.getObject(m);
|
|
}
|
|
this.model = (dojo.isFunction(m)) ? new m() : m;
|
|
this._setModel(this.model);
|
|
}
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
destroy: function(){
|
|
this.setModel(null);
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
// structure
|
|
_structureChanged: function() {
|
|
this.indexCellFields();
|
|
this.inherited(arguments);
|
|
},
|
|
|
|
// model
|
|
_setModel: function(inModel){
|
|
// if(!inModel){ return; }
|
|
this.model = inModel;
|
|
if(this.model){
|
|
this.model.observer(this);
|
|
this.model.measure();
|
|
this.indexCellFields();
|
|
}
|
|
},
|
|
|
|
setModel: function(inModel){
|
|
// summary:
|
|
// Set the grid's data model
|
|
// inModel: Object
|
|
// Model object, usually an instance of a dojox.grid.data.Model
|
|
// subclass
|
|
if(this.model){
|
|
this.model.notObserver(this);
|
|
}
|
|
this._setModel(inModel);
|
|
},
|
|
|
|
|
|
get: function(inRowIndex){
|
|
// summary: data socket (called in cell's context)
|
|
return this.grid.model.getDatum(inRowIndex, this.fieldIndex);
|
|
},
|
|
|
|
// model modifications
|
|
modelAllChange: function(){
|
|
this.rowCount = (this.model ? this.model.getRowCount() : 0);
|
|
this.updateRowCount(this.rowCount);
|
|
},
|
|
|
|
modelBeginUpdate: function(){
|
|
this.beginUpdate();
|
|
},
|
|
|
|
modelEndUpdate: function(){
|
|
this.endUpdate();
|
|
},
|
|
|
|
modelRowChange: function(inData, inRowIndex){
|
|
this.updateRow(inRowIndex);
|
|
},
|
|
|
|
modelDatumChange: function(inDatum, inRowIndex, inFieldIndex){
|
|
this.updateRow(inRowIndex);
|
|
},
|
|
|
|
modelFieldsChange: function() {
|
|
this.indexCellFields();
|
|
this.render();
|
|
},
|
|
|
|
// model insertion
|
|
modelInsertion: function(inRowIndex){
|
|
this.updateRowCount(this.model.getRowCount());
|
|
},
|
|
|
|
// model removal
|
|
modelRemoval: function(inKeys){
|
|
this.updateRowCount(this.model.getRowCount());
|
|
},
|
|
|
|
// cells
|
|
getCellName: function(inCell){
|
|
var v = this.model.fields.values, i = inCell.fieldIndex;
|
|
return i>=0 && i<v.length && v[i].name || this.inherited(arguments);
|
|
},
|
|
|
|
indexCellFields: function(){
|
|
var cells = this.layout.cells;
|
|
for(var i=0, c; cells && (c=cells[i]); i++){
|
|
if(dojo.isString(c.field)){
|
|
c.fieldIndex = this.model.fields.indexOf(c.field);
|
|
}
|
|
}
|
|
},
|
|
|
|
// utility
|
|
refresh: function(){
|
|
// summary:
|
|
// Re-render the grid, getting new data from the model
|
|
this.edit.cancel();
|
|
this.model.measure();
|
|
},
|
|
|
|
// sorting
|
|
canSort: function(inSortInfo){
|
|
var f = this.getSortField(inSortInfo);
|
|
// 0 is not a valid sort field
|
|
return f && this.model.canSort(f);
|
|
},
|
|
|
|
getSortField: function(inSortInfo){
|
|
// summary:
|
|
// Retrieves the model field on which to sort data.
|
|
// inSortInfo: Integer
|
|
// 1-based grid column index; positive if sort is ascending, otherwise negative
|
|
var c = this.getCell(this.getSortIndex(inSortInfo));
|
|
// we expect c.fieldIndex == -1 for non model fields
|
|
// that yields a getSortField value of 0, which can be detected as invalid
|
|
return (c.fieldIndex+1) * (this.sortInfo > 0 ? 1 : -1);
|
|
},
|
|
|
|
sort: function(){
|
|
this.edit.apply();
|
|
this.model.sort(this.getSortField());
|
|
},
|
|
|
|
// row editing
|
|
addRow: function(inRowData, inIndex){
|
|
this.edit.apply();
|
|
var i = inIndex || -1;
|
|
if(i<0){
|
|
i = this.selection.getFirstSelected() || 0;
|
|
}
|
|
if(i<0){
|
|
i = 0;
|
|
}
|
|
this.model.insert(inRowData, i);
|
|
this.model.beginModifyRow(i);
|
|
// begin editing row
|
|
// FIXME: add to edit
|
|
for(var j=0, c; ((c=this.getCell(j)) && !c.editor); j++){}
|
|
if(c&&c.editor){
|
|
this.edit.setEditCell(c, i);
|
|
this.focus.setFocusCell(c, i);
|
|
}else{
|
|
this.focus.setFocusCell(this.getCell(0), i);
|
|
}
|
|
},
|
|
|
|
removeSelectedRows: function(){
|
|
this.edit.apply();
|
|
var s = this.selection.getSelected();
|
|
if(s.length){
|
|
this.model.remove(s);
|
|
this.selection.clear();
|
|
}
|
|
},
|
|
|
|
//: protected
|
|
// editing
|
|
canEdit: function(inCell, inRowIndex){
|
|
// summary:
|
|
// Determines if a given cell may be edited
|
|
// inCell: Object
|
|
// A grid cell
|
|
// inRowIndex: Integer
|
|
// Grid row index
|
|
// returns: Boolean
|
|
// True if given cell may be edited
|
|
return (this.model.canModify ? this.model.canModify(inRowIndex) : true);
|
|
},
|
|
|
|
doStartEdit: function(inCell, inRowIndex){
|
|
this.model.beginModifyRow(inRowIndex);
|
|
this.onStartEdit(inCell, inRowIndex);
|
|
},
|
|
|
|
doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
|
|
this.model.setDatum(inValue, inRowIndex, inFieldIndex);
|
|
this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
|
|
},
|
|
|
|
doCancelEdit: function(inRowIndex){
|
|
this.model.cancelModifyRow(inRowIndex);
|
|
this.onCancelEdit.apply(this, arguments);
|
|
},
|
|
|
|
doApplyEdit: function(inRowIndex){
|
|
this.model.endModifyRow(inRowIndex);
|
|
this.onApplyEdit(inRowIndex);
|
|
},
|
|
|
|
styleRowState: function(inRow){
|
|
// summary: Perform row styling
|
|
if(this.model.getState){
|
|
var states=this.model.getState(inRow.index), c='';
|
|
for(var i=0, ss=["inflight", "error", "inserting"], s; s=ss[i]; i++){
|
|
if(states[s]){
|
|
c = ' dojoxGrid-row-' + s;
|
|
break;
|
|
}
|
|
}
|
|
inRow.customClasses += c;
|
|
}
|
|
},
|
|
|
|
onStyleRow: function(inRow){
|
|
this.styleRowState(inRow);
|
|
this.inherited(arguments);
|
|
}
|
|
|
|
});
|
|
|
|
dojox.Grid.markupFactory = function(props, node, ctor){
|
|
// handle setting up a data model for a store if one
|
|
// isn't provided. There are some caveats:
|
|
// * we only really handle dojo.data sources well. They're the future
|
|
// so it's no big deal, but it's something to be aware of.
|
|
// * I'm pretty sure that colgroup introspection is missing some of
|
|
// the available settable properties.
|
|
// * No handling of cell formatting and content getting is done
|
|
var d = dojo;
|
|
var widthFromAttr = function(n){
|
|
var w = d.attr(n, "width")||"auto";
|
|
if((w != "auto")&&(w.substr(-2) != "em")){
|
|
w = parseInt(w)+"px";
|
|
}
|
|
return w;
|
|
}
|
|
if(!props.model && d.hasAttr(node, "store")){
|
|
// if a model isn't specified and we point to a store, assume
|
|
// we're also folding the definition for a model up into the
|
|
// inline ctor for the Grid. This will then take properties
|
|
// like "query", "rowsPerPage", and "clientSort" from the grid
|
|
// definition.
|
|
var mNode = node.cloneNode(false);
|
|
d.attr(mNode, {
|
|
"jsId": null,
|
|
"dojoType": d.attr(node, "dataModelClass") || "dojox.grid.data.DojoData"
|
|
});
|
|
props.model = d.parser.instantiate([mNode])[0];
|
|
}
|
|
// if(!props.model){ console.debug("no model!"); }
|
|
// if a structure isn't referenced, do we have enough
|
|
// data to try to build one automatically?
|
|
if( !props.structure &&
|
|
node.nodeName.toLowerCase() == "table"){
|
|
|
|
// try to discover a structure
|
|
props.structure = d.query("> colgroup", node).map(function(cg){
|
|
var sv = d.attr(cg, "span");
|
|
var v = {
|
|
noscroll: (d.attr(cg, "noscroll") == "true") ? true : false,
|
|
__span: (!!sv ? parseInt(sv) : 1),
|
|
cells: []
|
|
};
|
|
if(d.hasAttr(cg, "width")){
|
|
v.width = widthFromAttr(cg);
|
|
}
|
|
return v; // for vendetta
|
|
});
|
|
if(!props.structure.length){
|
|
props.structure.push({
|
|
__span: Infinity,
|
|
cells: [] // catch-all view
|
|
});
|
|
}
|
|
// check to see if we're gonna have more than one view
|
|
|
|
// for each tr in our th, create a row of cells
|
|
d.query("thead > tr", node).forEach(function(tr, tr_idx){
|
|
var cellCount = 0;
|
|
var viewIdx = 0;
|
|
var lastViewIdx;
|
|
var cView = null;
|
|
d.query("> th", tr).map(function(th){
|
|
// what view will this cell go into?
|
|
|
|
// NOTE:
|
|
// to prevent extraneous iteration, we start counters over
|
|
// for each row, incrementing over the surface area of the
|
|
// structure that colgroup processing generates and
|
|
// creating cell objects for each <th> to place into those
|
|
// cell groups. There's a lot of state-keepking logic
|
|
// here, but it is what it has to be.
|
|
if(!cView){ // current view book keeping
|
|
lastViewIdx = 0;
|
|
cView = props.structure[0];
|
|
}else if(cellCount >= (lastViewIdx+cView.__span)){
|
|
viewIdx++;
|
|
// move to allocating things into the next view
|
|
lastViewIdx += cView.__span;
|
|
var lastView = cView;
|
|
cView = props.structure[viewIdx];
|
|
}
|
|
|
|
// actually define the cell from what markup hands us
|
|
var cell = {
|
|
name: d.trim(d.attr(th, "name")||th.innerHTML),
|
|
field: d.trim(d.attr(th, "field")||""),
|
|
colSpan: parseInt(d.attr(th, "colspan")||1)
|
|
};
|
|
cellCount += cell.colSpan;
|
|
cell.field = cell.field||cell.name;
|
|
cell.width = widthFromAttr(th);
|
|
if(!cView.cells[tr_idx]){
|
|
cView.cells[tr_idx] = [];
|
|
}
|
|
cView.cells[tr_idx].push(cell);
|
|
});
|
|
});
|
|
// console.debug(dojo.toJson(props.structure, true));
|
|
}
|
|
return new dojox.Grid(props, node);
|
|
}
|
|
|
|
|
|
// alias us to the right location
|
|
dojox.grid.Grid = dojox.Grid;
|