You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1396 lines
41 KiB
1396 lines
41 KiB
define([ |
|
"dojo/_base/kernel", |
|
"../main", |
|
"dojo/_base/declare", |
|
"./_Events", |
|
"./_Scroller", |
|
"./_Layout", |
|
"./_View", |
|
"./_ViewManager", |
|
"./_RowManager", |
|
"./_FocusManager", |
|
"./_EditManager", |
|
"./Selection", |
|
"./_RowSelector", |
|
"./util", |
|
"dijit/_Widget", |
|
"dijit/_TemplatedMixin", |
|
"dijit/CheckedMenuItem", |
|
"dojo/text!./resources/_Grid.html", |
|
"dojo/string", |
|
"dojo/_base/array", |
|
"dojo/_base/lang", |
|
"dojo/_base/sniff", |
|
"dojox/html/metrics", |
|
"dojo/_base/html", |
|
"dojo/query", |
|
"dojo/dnd/common", |
|
"dojo/i18n!dijit/nls/loading" |
|
], function(dojo, dojox, declare, _Events, _Scroller, _Layout, _View, _ViewManager, |
|
_RowManager, _FocusManager, _EditManager, Selection, _RowSelector, util, _Widget, |
|
_TemplatedMixin, CheckedMenuItem, template, string, array, lang, has, metrics, html, query){ |
|
|
|
// NOTE: this is for backwards compatibility with Dojo 1.3 |
|
if(!dojo.isCopyKey){ |
|
dojo.isCopyKey = dojo.dnd.getCopyKeyState; |
|
} |
|
/*===== |
|
dojox.grid.__CellDef = { |
|
// name: String? |
|
// The text to use in the header of the grid for this cell. |
|
// get: Function? |
|
// function(rowIndex){} rowIndex is of type Integer. This |
|
// function will be called when a cell requests data. Returns the |
|
// unformatted data for the cell. |
|
// value: String? |
|
// If "get" is not specified, this is used as the data for the cell. |
|
// defaultValue: String? |
|
// If "get" and "value" aren't specified or if "get" returns an undefined |
|
// value, this is used as the data for the cell. "formatter" is not run |
|
// on this if "get" returns an undefined value. |
|
// formatter: Function? |
|
// function(data, rowIndex){} data is of type anything, rowIndex |
|
// is of type Integer. This function will be called after the cell |
|
// has its data but before it passes it back to the grid to render. |
|
// Returns the formatted version of the cell's data. |
|
// type: dojox.grid.cells._Base|Function? |
|
// TODO |
|
// editable: Boolean? |
|
// Whether this cell should be editable or not. |
|
// hidden: Boolean? |
|
// If true, the cell will not be displayed. |
|
// noresize: Boolean? |
|
// If true, the cell will not be able to be resized. |
|
// width: Integer|String? |
|
// A CSS size. If it's an Integer, the width will be in em's. |
|
// colSpan: Integer? |
|
// How many columns to span this cell. Will not work in the first |
|
// sub-row of cells. |
|
// rowSpan: Integer? |
|
// How many sub-rows to span this cell. |
|
// styles: String? |
|
// A string of styles to apply to both the header cell and main |
|
// grid cells. Must end in a ';'. |
|
// headerStyles: String? |
|
// A string of styles to apply to just the header cell. Must end |
|
// in a ';' |
|
// cellStyles: String? |
|
// A string of styles to apply to just the main grid cells. Must |
|
// end in a ';' |
|
// classes: String? |
|
// A space separated list of classes to apply to both the header |
|
// cell and the main grid cells. |
|
// headerClasses: String? |
|
// A space separated list of classes to apply to just the header |
|
// cell. |
|
// cellClasses: String? |
|
// A space separated list of classes to apply to just the main |
|
// grid cells. |
|
// attrs: String? |
|
// A space separated string of attribute='value' pairs to add to |
|
// the header cell element and main grid cell elements. |
|
}; |
|
=====*/ |
|
|
|
/*===== |
|
dojox.grid.__ViewDef = { |
|
// noscroll: Boolean? |
|
// If true, no scrollbars will be rendered without scrollbars. |
|
// width: Integer|String? |
|
// A CSS size. If it's an Integer, the width will be in em's. If |
|
// "noscroll" is true, this value is ignored. |
|
// cells: dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]]? |
|
// The structure of the cells within this grid. |
|
// type: String? |
|
// A string containing the constructor of a subclass of |
|
// dojox.grid._View. If this is not specified, dojox.grid._View |
|
// is used. |
|
// defaultCell: dojox.grid.__CellDef? |
|
// A cell definition with default values for all cells in this view. If |
|
// a property is defined in a cell definition in the "cells" array and |
|
// this property, the cell definition's property will override this |
|
// property's property. |
|
// onBeforeRow: Function? |
|
// function(rowIndex, cells){} rowIndex is of type Integer, cells |
|
// is of type Array[dojox.grid.__CellDef[]]. This function is called |
|
// before each row of data is rendered. Before the header is |
|
// rendered, rowIndex will be -1. "cells" is a reference to the |
|
// internal structure of this view's cells so any changes you make to |
|
// it will persist between calls. |
|
// onAfterRow: Function? |
|
// function(rowIndex, cells, rowNode){} rowIndex is of type Integer, cells |
|
// is of type Array[dojox.grid.__CellDef[]], rowNode is of type DOMNode. |
|
// This function is called after each row of data is rendered. After the |
|
// header is rendered, rowIndex will be -1. "cells" is a reference to the |
|
// internal structure of this view's cells so any changes you make to |
|
// it will persist between calls. |
|
}; |
|
=====*/ |
|
|
|
var _Grid = declare('dojox.grid._Grid', |
|
[ _Widget, _TemplatedMixin, _Events ], |
|
{ |
|
// summary: |
|
// A grid widget with virtual scrolling, cell editing, complex rows, |
|
// sorting, fixed columns, sizeable columns, etc. |
|
// |
|
// description: |
|
// _Grid provides the full set of grid features without any |
|
// direct connection to a data store. |
|
// |
|
// The grid exposes a get function for the grid, or optionally |
|
// individual columns, to populate cell contents. |
|
// |
|
// The grid is rendered based on its structure, an object describing |
|
// column and cell layout. |
|
// |
|
// example: |
|
// A quick sample: |
|
// |
|
// define a get function |
|
// | function get(inRowIndex){ // called in cell context |
|
// | return [this.index, inRowIndex].join(', '); |
|
// | } |
|
// |
|
// 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: get }] |
|
// | ]} |
|
// | ]; |
|
// |
|
// | <div id="grid" |
|
// | rowCount="100" get="get" |
|
// | structure="structure" |
|
// | dojoType="dojox.grid._Grid"></div> |
|
|
|
templateString: template, |
|
|
|
// classTag: String |
|
// CSS class applied to the grid's domNode |
|
classTag: 'dojoxGrid', |
|
|
|
// settings |
|
// rowCount: Integer |
|
// Number of rows to display. |
|
rowCount: 5, |
|
|
|
// keepRows: Integer |
|
// Number of rows to keep in the rendering cache. |
|
keepRows: 75, |
|
|
|
// rowsPerPage: Integer |
|
// Number of rows to render at a time. |
|
rowsPerPage: 25, |
|
|
|
// autoWidth: Boolean |
|
// If autoWidth is true, grid width is automatically set to fit the data. |
|
autoWidth: false, |
|
|
|
// initialWidth: String |
|
// A css string to use to set our initial width (only used if autoWidth |
|
// is true). The first rendering of the grid will be this width, any |
|
// resizing of columns, etc will result in the grid switching to |
|
// autoWidth mode. Note, this width will override any styling in a |
|
// stylesheet or directly on the node. |
|
initialWidth: "", |
|
|
|
// autoHeight: Boolean|Integer |
|
// If autoHeight is true, grid height is automatically set to fit the data. |
|
// If it is an integer, the height will be automatically set to fit the data |
|
// if there are fewer than that many rows - and the height will be set to show |
|
// that many rows if there are more |
|
autoHeight: '', |
|
|
|
// rowHeight: Integer |
|
// If rowHeight is set to a positive number, it will define the height of the rows |
|
// in pixels. This can provide a significant performance advantage, since it |
|
// eliminates the need to measure row sizes during rendering, which is one |
|
// the primary bottlenecks in the DataGrid's performance. |
|
rowHeight: 0, |
|
|
|
// autoRender: Boolean |
|
// If autoRender is true, grid will render itself after initialization. |
|
autoRender: true, |
|
|
|
// defaultHeight: String |
|
// default height of the grid, measured in any valid css unit. |
|
defaultHeight: '15em', |
|
|
|
// height: String |
|
// explicit height of the grid, measured in any valid css unit. This will be populated (and overridden) |
|
// if the height: css attribute exists on the source node. |
|
height: '', |
|
|
|
// structure: dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]] |
|
// View layout definition. |
|
structure: null, |
|
|
|
// elasticView: Integer |
|
// Override defaults and make the indexed grid view elastic, thus filling available horizontal space. |
|
elasticView: -1, |
|
|
|
// singleClickEdit: boolean |
|
// Single-click starts editing. Default is double-click |
|
singleClickEdit: false, |
|
|
|
// selectionMode: String |
|
// Set the selection mode of grid's Selection. Value must be 'single', 'multiple', |
|
// or 'extended'. Default is 'extended'. |
|
selectionMode: 'extended', |
|
|
|
// rowSelector: Boolean|String |
|
// If set to true, will add a row selector view to this grid. If set to a CSS width, will add |
|
// a row selector of that width to this grid. |
|
rowSelector: '', |
|
|
|
// columnReordering: Boolean |
|
// If set to true, will add drag and drop reordering to views with one row of columns. |
|
columnReordering: false, |
|
|
|
// headerMenu: dijit.Menu |
|
// If set to a dijit.Menu, will use this as a context menu for the grid headers. |
|
headerMenu: null, |
|
|
|
// placeholderLabel: String |
|
// Label of placeholders to search for in the header menu to replace with column toggling |
|
// menu items. |
|
placeholderLabel: "GridColumns", |
|
|
|
// selectable: Boolean |
|
// Set to true if you want to be able to select the text within the grid. |
|
selectable: false, |
|
|
|
// Used to store the last two clicks, to ensure double-clicking occurs based on the intended row |
|
_click: null, |
|
|
|
// loadingMessage: String |
|
// Message that shows while the grid is loading |
|
loadingMessage: "<span class='dojoxGridLoading'>${loadingState}</span>", |
|
|
|
// errorMessage: String |
|
// Message that shows when the grid encounters an error loading |
|
errorMessage: "<span class='dojoxGridError'>${errorState}</span>", |
|
|
|
// noDataMessage: String |
|
// Message that shows if the grid has no data - wrap it in a |
|
// span with class 'dojoxGridNoData' if you want it to be |
|
// styled similar to the loading and error messages |
|
noDataMessage: "", |
|
|
|
// escapeHTMLInData: Boolean |
|
// This will escape HTML brackets from the data to prevent HTML from |
|
// user-inputted data being rendered with may contain JavaScript and result in |
|
// XSS attacks. This is true by default, and it is recommended that it remain |
|
// true. Setting this to false will allow data to be displayed in the grid without |
|
// filtering, and should be only used if it is known that the data won't contain |
|
// malicious scripts. If HTML is needed in grid cells, it is recommended that |
|
// you use the formatter function to generate the HTML (the output of |
|
// formatter functions is not filtered, even with escapeHTMLInData set to true). |
|
escapeHTMLInData: true, |
|
|
|
// formatterScope: Object |
|
// An object to execute format functions within. If not set, the |
|
// format functions will execute within the scope of the cell that |
|
// has a format function. |
|
formatterScope: null, |
|
|
|
// editable: boolean |
|
// indicates if the grid contains editable cells, default is false |
|
// set to true if editable cell encountered during rendering |
|
editable: false, |
|
|
|
// summary: String |
|
// Customizable summary descriptions which will be added to grid.domNode |
|
summary: '', |
|
_setSummaryAttr: 'domNode', |
|
|
|
// sortInfo: [private] Number |
|
sortInfo: 0, |
|
|
|
// _placeholders: [private] Array |
|
_placeholders: null, |
|
|
|
// _layoutClass: Object |
|
// The class to use for our layout - can be overridden by grid subclasses |
|
_layoutClass: _Layout, |
|
|
|
// initialization |
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
if(!this.domNode.getAttribute('tabIndex')){ |
|
this.domNode.tabIndex = "0"; |
|
} |
|
this.createScroller(); |
|
this.createLayout(); |
|
this.createViews(); |
|
this.createManagers(); |
|
|
|
this.createSelection(); |
|
|
|
this.connect(this.selection, "onSelected", "onSelected"); |
|
this.connect(this.selection, "onDeselected", "onDeselected"); |
|
this.connect(this.selection, "onChanged", "onSelectionChanged"); |
|
|
|
metrics.initOnFontResize(); |
|
this.connect(metrics, "onFontResize", "textSizeChanged"); |
|
util.funnelEvents(this.domNode, this, 'doKeyEvent', util.keyEvents); |
|
if (this.selectionMode != "none") { |
|
this.domNode.setAttribute("aria-multiselectable", this.selectionMode == "single" ? "false" : "true"); |
|
} |
|
|
|
html.addClass(this.domNode, this.classTag); |
|
if(!this.isLeftToRight()){ |
|
html.addClass(this.domNode, this.classTag+"Rtl"); |
|
} |
|
if(this.rowHeight > 0){ |
|
html.addClass(this.viewsNode, this.classTag + "FixedRowHeight"); |
|
} |
|
}, |
|
|
|
postMixInProperties: function(){ |
|
this.inherited(arguments); |
|
var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang); |
|
this.loadingMessage = string.substitute(this.loadingMessage, messages); |
|
this.errorMessage = string.substitute(this.errorMessage, messages); |
|
if(this.srcNodeRef && this.srcNodeRef.style.height){ |
|
this.height = this.srcNodeRef.style.height; |
|
} |
|
// Call this to update our autoheight to start out |
|
this._setAutoHeightAttr(this.autoHeight, true); |
|
this.lastScrollTop = this.scrollTop = 0; |
|
}, |
|
|
|
postCreate: function(){ |
|
this._placeholders = []; |
|
this._setHeaderMenuAttr(this.headerMenu); |
|
this._setStructureAttr(this.structure); |
|
this._click = []; |
|
this.inherited(arguments); |
|
if(this.domNode && this.autoWidth && this.initialWidth){ |
|
this.domNode.style.width = this.initialWidth; |
|
} |
|
if (this.domNode && !this.editable){ |
|
// default value for aria-readonly is false, set to true if grid is not editable |
|
html.attr(this.domNode,"aria-readonly", "true"); |
|
} |
|
}, |
|
|
|
destroy: function(){ |
|
this.domNode.onReveal = null; |
|
this.domNode.onSizeChange = null; |
|
|
|
// Fixes IE domNode leak |
|
delete this._click; |
|
|
|
if(this.scroller){ |
|
this.scroller.destroy(); |
|
delete this.scroller; |
|
} |
|
this.edit.destroy(); |
|
delete this.edit; |
|
this.views.destroyViews(); |
|
if(this.focus){ |
|
this.focus.destroy(); |
|
delete this.focus; |
|
} |
|
if(this.headerMenu&&this._placeholders.length){ |
|
array.forEach(this._placeholders, function(p){ p.unReplace(true); }); |
|
this.headerMenu.unBindDomNode(this.viewsHeaderNode); |
|
} |
|
this.inherited(arguments); |
|
}, |
|
|
|
_setAutoHeightAttr: function(ah, skipRender){ |
|
// Calculate our autoheight - turn it into a boolean or an integer |
|
if(typeof ah == "string"){ |
|
if(!ah || ah == "false"){ |
|
ah = false; |
|
}else if (ah == "true"){ |
|
ah = true; |
|
}else{ |
|
ah = window.parseInt(ah, 10); |
|
} |
|
} |
|
if(typeof ah == "number"){ |
|
if(isNaN(ah)){ |
|
ah = false; |
|
} |
|
// Autoheight must be at least 1, if it's a number. If it's |
|
// less than 0, we'll take that to mean "all" rows (same as |
|
// autoHeight=true - if it is equal to zero, we'll take that |
|
// to mean autoHeight=false |
|
if(ah < 0){ |
|
ah = true; |
|
}else if (ah === 0){ |
|
ah = false; |
|
} |
|
} |
|
this.autoHeight = ah; |
|
if(typeof ah == "boolean"){ |
|
this._autoHeight = ah; |
|
}else if(typeof ah == "number"){ |
|
this._autoHeight = (ah >= this.get('rowCount')); |
|
}else{ |
|
this._autoHeight = false; |
|
} |
|
if(this._started && !skipRender){ |
|
this.render(); |
|
} |
|
}, |
|
|
|
_getRowCountAttr: function(){ |
|
return this.updating && this.invalidated && this.invalidated.rowCount != undefined ? |
|
this.invalidated.rowCount : this.rowCount; |
|
}, |
|
|
|
textSizeChanged: function(){ |
|
this.render(); |
|
}, |
|
|
|
sizeChange: function(){ |
|
this.update(); |
|
}, |
|
|
|
createManagers: function(){ |
|
// summary: |
|
// create grid managers for various tasks including rows, focus, selection, editing |
|
|
|
// row manager |
|
this.rows = new _RowManager(this); |
|
// focus manager |
|
this.focus = new _FocusManager(this); |
|
// edit manager |
|
this.edit = new _EditManager(this); |
|
}, |
|
|
|
createSelection: function(){ |
|
// summary: Creates a new Grid selection manager. |
|
|
|
// selection manager |
|
this.selection = new Selection(this); |
|
}, |
|
|
|
createScroller: function(){ |
|
// summary: |
|
// Creates a new virtual scroller |
|
this.scroller = new _Scroller(); |
|
this.scroller.grid = this; |
|
this.scroller.renderRow = lang.hitch(this, "renderRow"); |
|
this.scroller.removeRow = lang.hitch(this, "rowRemoved"); |
|
}, |
|
|
|
createLayout: function(){ |
|
// summary: |
|
// Creates a new Grid layout |
|
this.layout = new this._layoutClass(this); |
|
this.connect(this.layout, "moveColumn", "onMoveColumn"); |
|
}, |
|
|
|
onMoveColumn: function(){ |
|
this.update(); |
|
}, |
|
|
|
onResizeColumn: function(/*int*/ cellIdx){ |
|
// Called when a column is resized. |
|
}, |
|
|
|
// views |
|
createViews: function(){ |
|
this.views = new _ViewManager(this); |
|
this.views.createView = lang.hitch(this, "createView"); |
|
}, |
|
|
|
createView: function(inClass, idx){ |
|
var c = lang.getObject(inClass); |
|
var view = new c({ grid: this, index: idx }); |
|
this.viewsNode.appendChild(view.domNode); |
|
this.viewsHeaderNode.appendChild(view.headerNode); |
|
this.views.addView(view); |
|
html.attr(this.domNode, "align", this.isLeftToRight() ? 'left' : 'right'); |
|
return view; |
|
}, |
|
|
|
buildViews: function(){ |
|
for(var i=0, vs; (vs=this.layout.structure[i]); i++){ |
|
this.createView(vs.type || dojox._scopeName + ".grid._View", i).setStructure(vs); |
|
} |
|
this.scroller.setContentNodes(this.views.getContentNodes()); |
|
}, |
|
|
|
_setStructureAttr: function(structure){ |
|
var s = structure; |
|
if(s && lang.isString(s)){ |
|
dojo.deprecated("dojox.grid._Grid.set('structure', 'objVar')", "use dojox.grid._Grid.set('structure', objVar) instead", "2.0"); |
|
s=lang.getObject(s); |
|
} |
|
this.structure = s; |
|
if(!s){ |
|
if(this.layout.structure){ |
|
s = this.layout.structure; |
|
}else{ |
|
return; |
|
} |
|
} |
|
this.views.destroyViews(); |
|
this.focus.focusView = null; |
|
if(s !== this.layout.structure){ |
|
this.layout.setStructure(s); |
|
} |
|
this._structureChanged(); |
|
}, |
|
|
|
setStructure: function(/* dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]] */ inStructure){ |
|
// summary: |
|
// Install a new structure and rebuild the grid. |
|
dojo.deprecated("dojox.grid._Grid.setStructure(obj)", "use dojox.grid._Grid.set('structure', obj) instead.", "2.0"); |
|
this._setStructureAttr(inStructure); |
|
}, |
|
|
|
getColumnTogglingItems: function(){ |
|
// summary: |
|
// returns an array of dijit.CheckedMenuItem widgets that can be |
|
// added to a menu for toggling columns on and off. |
|
var items, checkedItems = []; |
|
items = array.map(this.layout.cells, function(cell){ |
|
if(!cell.menuItems){ cell.menuItems = []; } |
|
|
|
var self = this; |
|
var item = new CheckedMenuItem({ |
|
label: cell.name, |
|
checked: !cell.hidden, |
|
_gridCell: cell, |
|
onChange: function(checked){ |
|
if(self.layout.setColumnVisibility(this._gridCell.index, checked)){ |
|
var items = this._gridCell.menuItems; |
|
if(items.length > 1){ |
|
array.forEach(items, function(item){ |
|
if(item !== this){ |
|
item.setAttribute("checked", checked); |
|
} |
|
}, this); |
|
} |
|
checked = array.filter(self.layout.cells, function(c){ |
|
if(c.menuItems.length > 1){ |
|
array.forEach(c.menuItems, "item.set('disabled', false);"); |
|
}else{ |
|
c.menuItems[0].set('disabled', false); |
|
} |
|
return !c.hidden; |
|
}); |
|
if(checked.length == 1){ |
|
array.forEach(checked[0].menuItems, "item.set('disabled', true);"); |
|
} |
|
} |
|
}, |
|
destroy: function(){ |
|
var index = array.indexOf(this._gridCell.menuItems, this); |
|
this._gridCell.menuItems.splice(index, 1); |
|
delete this._gridCell; |
|
CheckedMenuItem.prototype.destroy.apply(this, arguments); |
|
} |
|
}); |
|
cell.menuItems.push(item); |
|
if(!cell.hidden) { |
|
checkedItems.push(item); |
|
} |
|
return item; |
|
}, this); // dijit.CheckedMenuItem[] |
|
if(checkedItems.length == 1) { |
|
checkedItems[0].set('disabled', true); |
|
} |
|
return items; |
|
}, |
|
|
|
_setHeaderMenuAttr: function(menu){ |
|
if(this._placeholders && this._placeholders.length){ |
|
array.forEach(this._placeholders, function(p){ |
|
p.unReplace(true); |
|
}); |
|
this._placeholders = []; |
|
} |
|
if(this.headerMenu){ |
|
this.headerMenu.unBindDomNode(this.viewsHeaderNode); |
|
} |
|
this.headerMenu = menu; |
|
if(!menu){ return; } |
|
|
|
this.headerMenu.bindDomNode(this.viewsHeaderNode); |
|
if(this.headerMenu.getPlaceholders){ |
|
this._placeholders = this.headerMenu.getPlaceholders(this.placeholderLabel); |
|
} |
|
}, |
|
|
|
setHeaderMenu: function(/* dijit.Menu */ menu){ |
|
dojo.deprecated("dojox.grid._Grid.setHeaderMenu(obj)", "use dojox.grid._Grid.set('headerMenu', obj) instead.", "2.0"); |
|
this._setHeaderMenuAttr(menu); |
|
}, |
|
|
|
setupHeaderMenu: function(){ |
|
if(this._placeholders && this._placeholders.length){ |
|
array.forEach(this._placeholders, function(p){ |
|
if(p._replaced){ |
|
p.unReplace(true); |
|
} |
|
p.replace(this.getColumnTogglingItems()); |
|
}, this); |
|
} |
|
}, |
|
|
|
_fetch: function(start){ |
|
this.setScrollTop(0); |
|
}, |
|
|
|
getItem: function(inRowIndex){ |
|
return null; |
|
}, |
|
|
|
showMessage: function(message){ |
|
if(message){ |
|
this.messagesNode.innerHTML = message; |
|
this.messagesNode.style.display = ""; |
|
}else{ |
|
this.messagesNode.innerHTML = ""; |
|
this.messagesNode.style.display = "none"; |
|
} |
|
}, |
|
|
|
_structureChanged: function() { |
|
this.buildViews(); |
|
if(this.autoRender && this._started){ |
|
this.render(); |
|
} |
|
}, |
|
|
|
hasLayout: function() { |
|
return this.layout.cells.length; |
|
}, |
|
|
|
// sizing |
|
resize: function(changeSize, resultSize){ |
|
// summary: |
|
// Update the grid's rendering dimensions and resize it |
|
|
|
// Calling sizeChange calls update() which calls _resize...so let's |
|
// save our input values, if any, and use them there when it gets |
|
// called. This saves us an extra call to _resize(), which can |
|
// get kind of heavy. |
|
|
|
this._pendingChangeSize = changeSize; |
|
this._pendingResultSize = resultSize; |
|
this.sizeChange(); |
|
}, |
|
|
|
_getPadBorder: function() { |
|
this._padBorder = this._padBorder || html._getPadBorderExtents(this.domNode); |
|
return this._padBorder; |
|
}, |
|
|
|
_getHeaderHeight: function(){ |
|
var vns = this.viewsHeaderNode.style, t = vns.display == "none" ? 0 : this.views.measureHeader(); |
|
vns.height = t + 'px'; |
|
// header heights are reset during measuring so must be normalized after measuring. |
|
this.views.normalizeHeaderNodeHeight(); |
|
return t; |
|
}, |
|
|
|
_resize: function(changeSize, resultSize){ |
|
// Restore our pending values, if any |
|
changeSize = changeSize || this._pendingChangeSize; |
|
resultSize = resultSize || this._pendingResultSize; |
|
delete this._pendingChangeSize; |
|
delete this._pendingResultSize; |
|
// if we have set up everything except the DOM, we cannot resize |
|
if(!this.domNode){ return; } |
|
var pn = this.domNode.parentNode; |
|
if(!pn || pn.nodeType != 1 || !this.hasLayout() || pn.style.visibility == "hidden" || pn.style.display == "none"){ |
|
return; |
|
} |
|
// useful measurement |
|
var padBorder = this._getPadBorder(); |
|
var hh = undefined; |
|
var h; |
|
// grid height |
|
if(this._autoHeight){ |
|
this.domNode.style.height = 'auto'; |
|
}else if(typeof this.autoHeight == "number"){ |
|
h = hh = this._getHeaderHeight(); |
|
h += (this.scroller.averageRowHeight * this.autoHeight); |
|
this.domNode.style.height = h + "px"; |
|
}else if(this.domNode.clientHeight <= padBorder.h){ |
|
if(pn == document.body){ |
|
this.domNode.style.height = this.defaultHeight; |
|
}else if(this.height){ |
|
this.domNode.style.height = this.height; |
|
}else{ |
|
this.fitTo = "parent"; |
|
} |
|
} |
|
// if we are given dimensions, size the grid's domNode to those dimensions |
|
if(resultSize){ |
|
changeSize = resultSize; |
|
} |
|
if(!this._autoHeight && changeSize){ |
|
html.marginBox(this.domNode, changeSize); |
|
this.height = this.domNode.style.height; |
|
delete this.fitTo; |
|
}else if(this.fitTo == "parent"){ |
|
h = this._parentContentBoxHeight = (this._parentContentBoxHeight > 0 ? this._parentContentBoxHeight : html._getContentBox(pn).h); |
|
this.domNode.style.height = Math.max(0, h) + "px"; |
|
} |
|
|
|
var hasFlex = array.some(this.views.views, function(v){ return v.flexCells; }); |
|
|
|
if(!this._autoHeight && (h || html._getContentBox(this.domNode).h) === 0){ |
|
// We need to hide the header, since the Grid is essentially hidden. |
|
this.viewsHeaderNode.style.display = "none"; |
|
}else{ |
|
// Otherwise, show the header and give it an appropriate height. |
|
this.viewsHeaderNode.style.display = "block"; |
|
if(!hasFlex && hh === undefined){ |
|
hh = this._getHeaderHeight(); |
|
} |
|
} |
|
if(hasFlex){ |
|
hh = undefined; |
|
} |
|
|
|
// NOTE: it is essential that width be applied before height |
|
// Header height can only be calculated properly after view widths have been set. |
|
// This is because flex column width is naturally 0 in Firefox. |
|
// Therefore prior to width sizing flex columns with spaces are maximally wrapped |
|
// and calculated to be too tall. |
|
this.adaptWidth(); |
|
this.adaptHeight(hh); |
|
|
|
this.postresize(); |
|
}, |
|
|
|
adaptWidth: function() { |
|
// summary: |
|
// sets width and position for views and update grid width if necessary |
|
// tags: |
|
// private |
|
var doAutoWidth = (!this.initialWidth && this.autoWidth); |
|
var w = doAutoWidth ? 0 : this.domNode.clientWidth || (this.domNode.offsetWidth - this._getPadBorder().w), |
|
vw = this.views.arrange(1, w); |
|
this.views.onEach("adaptWidth"); |
|
if(doAutoWidth){ |
|
this.domNode.style.width = vw + "px"; |
|
} |
|
}, |
|
|
|
adaptHeight: function(inHeaderHeight){ |
|
// summary: |
|
// measures and normalizes header height, then sets view heights, and then updates scroller |
|
// content extent |
|
// tags: |
|
// private |
|
var t = inHeaderHeight === undefined ? this._getHeaderHeight() : inHeaderHeight; |
|
var h = (this._autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0); |
|
this.views.onEach('setSize', [0, h]); |
|
this.views.onEach('adaptHeight'); |
|
if(!this._autoHeight){ |
|
var numScroll = 0, numNoScroll = 0; |
|
var noScrolls = array.filter(this.views.views, function(v){ |
|
var has = v.hasHScrollbar(); |
|
if(has){ numScroll++; }else{ numNoScroll++; } |
|
return (!has); |
|
}); |
|
if(numScroll > 0 && numNoScroll > 0){ |
|
array.forEach(noScrolls, function(v){ |
|
v.adaptHeight(true); |
|
}); |
|
} |
|
} |
|
if(this.autoHeight === true || h != -1 || (typeof this.autoHeight == "number" && this.autoHeight >= this.get('rowCount'))){ |
|
this.scroller.windowHeight = h; |
|
}else{ |
|
this.scroller.windowHeight = Math.max(this.domNode.clientHeight - t, 0); |
|
} |
|
}, |
|
|
|
// startup |
|
startup: function(){ |
|
if(this._started){return;} |
|
this.inherited(arguments); |
|
if(this.autoRender){ |
|
this.render(); |
|
} |
|
}, |
|
|
|
// render |
|
render: function(){ |
|
// summary: |
|
// Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and |
|
// scrolling states, see Update. |
|
|
|
if(!this.domNode){return;} |
|
if(!this._started){return;} |
|
|
|
if(!this.hasLayout()) { |
|
this.scroller.init(0, this.keepRows, this.rowsPerPage); |
|
return; |
|
} |
|
// |
|
this.update = this.defaultUpdate; |
|
this._render(); |
|
}, |
|
|
|
_render: function(){ |
|
this.scroller.init(this.get('rowCount'), this.keepRows, this.rowsPerPage); |
|
this.prerender(); |
|
this.setScrollTop(0); |
|
this.postrender(); |
|
}, |
|
|
|
prerender: function(){ |
|
// if autoHeight, make sure scroller knows not to virtualize; everything must be rendered. |
|
this.keepRows = this._autoHeight ? 0 : this.keepRows; |
|
this.scroller.setKeepInfo(this.keepRows); |
|
this.views.render(); |
|
this._resize(); |
|
}, |
|
|
|
postrender: function(){ |
|
this.postresize(); |
|
this.focus.initFocusView(); |
|
// make rows unselectable |
|
html.setSelectable(this.domNode, this.selectable); |
|
}, |
|
|
|
postresize: function(){ |
|
// views are position absolute, so they do not inflate the parent |
|
if(this._autoHeight){ |
|
var size = Math.max(this.views.measureContent()) + 'px'; |
|
|
|
this.viewsNode.style.height = size; |
|
} |
|
}, |
|
|
|
renderRow: function(inRowIndex, inNodes){ |
|
// summary: |
|
// used internally to render rows |
|
// tags: |
|
// private |
|
this.views.renderRow(inRowIndex, inNodes, this._skipRowRenormalize); |
|
}, |
|
|
|
rowRemoved: function(inRowIndex){ |
|
// summary: |
|
// used internally to remove rows |
|
// tags: |
|
// private |
|
this.views.rowRemoved(inRowIndex); |
|
}, |
|
|
|
invalidated: null, |
|
|
|
updating: false, |
|
|
|
beginUpdate: function(){ |
|
// summary: |
|
// Use to make multiple changes to rows while queueing row updating. |
|
// NOTE: not currently supporting nested begin/endUpdate calls |
|
this.invalidated = []; |
|
this.updating = true; |
|
}, |
|
|
|
endUpdate: function(){ |
|
// summary: |
|
// Use after calling beginUpdate to render any changes made to rows. |
|
this.updating = false; |
|
var i = this.invalidated, r; |
|
if(i.all){ |
|
this.update(); |
|
}else if(i.rowCount != undefined){ |
|
this.updateRowCount(i.rowCount); |
|
}else{ |
|
for(r in i){ |
|
this.updateRow(Number(r)); |
|
} |
|
} |
|
this.invalidated = []; |
|
}, |
|
|
|
// update |
|
defaultUpdate: function(){ |
|
// note: initial update calls render and subsequently this function. |
|
if(!this.domNode){return;} |
|
if(this.updating){ |
|
this.invalidated.all = true; |
|
return; |
|
} |
|
//this.edit.saveState(inRowIndex); |
|
this.lastScrollTop = this.scrollTop; |
|
this.prerender(); |
|
this.scroller.invalidateNodes(); |
|
this.setScrollTop(this.lastScrollTop); |
|
this.postrender(); |
|
//this.edit.restoreState(inRowIndex); |
|
}, |
|
|
|
update: function(){ |
|
// summary: |
|
// Update the grid, retaining edit and scrolling states. |
|
this.render(); |
|
}, |
|
|
|
updateRow: function(inRowIndex){ |
|
// summary: |
|
// Render a single row. |
|
// inRowIndex: Integer |
|
// Index of the row to render |
|
inRowIndex = Number(inRowIndex); |
|
if(this.updating){ |
|
this.invalidated[inRowIndex]=true; |
|
}else{ |
|
this.views.updateRow(inRowIndex); |
|
this.scroller.rowHeightChanged(inRowIndex); |
|
} |
|
}, |
|
|
|
updateRows: function(startIndex, howMany){ |
|
// summary: |
|
// Render consecutive rows at once. |
|
// startIndex: Integer |
|
// Index of the starting row to render |
|
// howMany: Integer |
|
// How many rows to update. |
|
startIndex = Number(startIndex); |
|
howMany = Number(howMany); |
|
var i; |
|
if(this.updating){ |
|
for(i=0; i<howMany; i++){ |
|
this.invalidated[i+startIndex]=true; |
|
} |
|
}else{ |
|
for(i=0; i<howMany; i++){ |
|
this.views.updateRow(i+startIndex, this._skipRowRenormalize); |
|
} |
|
this.scroller.rowHeightChanged(startIndex); |
|
} |
|
}, |
|
|
|
updateRowCount: function(inRowCount){ |
|
// summary: |
|
// Change the number of rows. |
|
// inRowCount: int |
|
// Number of rows in the grid. |
|
if(this.updating){ |
|
this.invalidated.rowCount = inRowCount; |
|
}else{ |
|
this.rowCount = inRowCount; |
|
this._setAutoHeightAttr(this.autoHeight, true); |
|
if(this.layout.cells.length){ |
|
this.scroller.updateRowCount(inRowCount); |
|
} |
|
if(this.layout.cells.length){ |
|
this.setScrollTop(this.scrollTop); |
|
} |
|
this._resize(); |
|
} |
|
}, |
|
|
|
updateRowStyles: function(inRowIndex){ |
|
// summary: |
|
// Update the styles for a row after it's state has changed. |
|
this.views.updateRowStyles(inRowIndex); |
|
}, |
|
getRowNode: function(inRowIndex){ |
|
// summary: |
|
// find the rowNode that is not a rowSelector |
|
if (this.focus.focusView && !(this.focus.focusView instanceof _RowSelector)){ |
|
return this.focus.focusView.rowNodes[inRowIndex]; |
|
}else{ // search through views |
|
for (var i = 0, cView; (cView = this.views.views[i]); i++) { |
|
if (!(cView instanceof _RowSelector)) { |
|
return cView.rowNodes[inRowIndex]; |
|
} |
|
} |
|
} |
|
return null; |
|
}, |
|
rowHeightChanged: function(inRowIndex){ |
|
// summary: |
|
// Update grid when the height of a row has changed. Row height is handled automatically as rows |
|
// are rendered. Use this function only to update a row's height outside the normal rendering process. |
|
// inRowIndex: Integer |
|
// index of the row that has changed height |
|
|
|
this.views.renormalizeRow(inRowIndex); |
|
this.scroller.rowHeightChanged(inRowIndex); |
|
}, |
|
|
|
// fastScroll: Boolean |
|
// flag modifies vertical scrolling behavior. Defaults to true but set to false for slower |
|
// scroll performance but more immediate scrolling feedback |
|
fastScroll: true, |
|
|
|
delayScroll: false, |
|
|
|
// scrollRedrawThreshold: int |
|
// pixel distance a user must scroll vertically to trigger grid scrolling. |
|
scrollRedrawThreshold: (has('ie') ? 100 : 50), |
|
|
|
// scroll methods |
|
scrollTo: function(inTop){ |
|
// summary: |
|
// Vertically scroll the grid to a given pixel position |
|
// inTop: Integer |
|
// vertical position of the grid in pixels |
|
if(!this.fastScroll){ |
|
this.setScrollTop(inTop); |
|
return; |
|
} |
|
var delta = Math.abs(this.lastScrollTop - inTop); |
|
this.lastScrollTop = inTop; |
|
if(delta > this.scrollRedrawThreshold || this.delayScroll){ |
|
this.delayScroll = true; |
|
this.scrollTop = inTop; |
|
this.views.setScrollTop(inTop); |
|
if(this._pendingScroll){ |
|
window.clearTimeout(this._pendingScroll); |
|
} |
|
var _this = this; |
|
this._pendingScroll = window.setTimeout(function(){ |
|
delete _this._pendingScroll; |
|
_this.finishScrollJob(); |
|
}, 200); |
|
}else{ |
|
this.setScrollTop(inTop); |
|
} |
|
}, |
|
|
|
finishScrollJob: function(){ |
|
this.delayScroll = false; |
|
this.setScrollTop(this.scrollTop); |
|
}, |
|
|
|
setScrollTop: function(inTop){ |
|
this.scroller.scroll(this.views.setScrollTop(inTop)); |
|
}, |
|
|
|
scrollToRow: function(inRowIndex){ |
|
// summary: |
|
// Scroll the grid to a specific row. |
|
// inRowIndex: Integer |
|
// grid row index |
|
this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1); |
|
}, |
|
|
|
styleRowNode: function(inRowIndex, inRowNode){ |
|
// summary: |
|
// styling (used internally to style individual parts of a row) |
|
// tags: |
|
// private |
|
if(inRowNode){ |
|
this.rows.styleRowNode(inRowIndex, inRowNode); |
|
} |
|
}, |
|
|
|
// called when the mouse leaves the grid so we can deselect all hover rows |
|
_mouseOut: function(e){ |
|
this.rows.setOverRow(-2); |
|
}, |
|
|
|
// cells |
|
getCell: function(inIndex){ |
|
// summary: |
|
// Retrieves the cell object for a given grid column. |
|
// inIndex: Integer |
|
// Grid column index of cell to retrieve |
|
// returns: |
|
// a grid cell |
|
return this.layout.cells[inIndex]; |
|
}, |
|
|
|
setCellWidth: function(inIndex, inUnitWidth){ |
|
this.getCell(inIndex).unitWidth = inUnitWidth; |
|
}, |
|
|
|
getCellName: function(inCell){ |
|
// summary: |
|
// Returns the cell name of a passed cell |
|
return "Cell " + inCell.index; // String |
|
}, |
|
|
|
// sorting |
|
canSort: function(inSortInfo){ |
|
// summary: |
|
// Determines if the grid can be sorted |
|
// inSortInfo: Integer |
|
// Sort information, 1-based index of column on which to sort, positive for an ascending sort |
|
// and negative for a descending sort |
|
// returns: Boolean |
|
// True if grid can be sorted on the given column in the given direction |
|
}, |
|
|
|
sort: function(){ |
|
}, |
|
|
|
getSortAsc: function(inSortInfo){ |
|
// summary: |
|
// Returns true if grid is sorted in an ascending direction. |
|
inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo; |
|
return Boolean(inSortInfo > 0); // Boolean |
|
}, |
|
|
|
getSortIndex: function(inSortInfo){ |
|
// summary: |
|
// Returns the index of the column on which the grid is sorted |
|
inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo; |
|
return Math.abs(inSortInfo) - 1; // Integer |
|
}, |
|
|
|
setSortIndex: function(inIndex, inAsc){ |
|
// summary: |
|
// Sort the grid on a column in a specified direction |
|
// inIndex: Integer |
|
// Column index on which to sort. |
|
// inAsc: Boolean |
|
// If true, sort the grid in ascending order, otherwise in descending order |
|
var si = inIndex +1; |
|
if(inAsc != undefined){ |
|
si *= (inAsc ? 1 : -1); |
|
} else if(this.getSortIndex() == inIndex){ |
|
si = -this.sortInfo; |
|
} |
|
this.setSortInfo(si); |
|
}, |
|
|
|
setSortInfo: function(inSortInfo){ |
|
if(this.canSort(inSortInfo)){ |
|
this.sortInfo = inSortInfo; |
|
this.sort(); |
|
this.update(); |
|
} |
|
}, |
|
|
|
// DOM event handler |
|
doKeyEvent: function(e){ |
|
e.dispatch = 'do' + e.type; |
|
this.onKeyEvent(e); |
|
}, |
|
|
|
// event dispatch |
|
//: protected |
|
_dispatch: function(m, e){ |
|
if(m in this){ |
|
return this[m](e); |
|
} |
|
return false; |
|
}, |
|
|
|
dispatchKeyEvent: function(e){ |
|
this._dispatch(e.dispatch, e); |
|
}, |
|
|
|
dispatchContentEvent: function(e){ |
|
this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e); |
|
}, |
|
|
|
dispatchHeaderEvent: function(e){ |
|
e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e); |
|
}, |
|
|
|
dokeydown: function(e){ |
|
this.onKeyDown(e); |
|
}, |
|
|
|
doclick: function(e){ |
|
if(e.cellNode){ |
|
this.onCellClick(e); |
|
}else{ |
|
this.onRowClick(e); |
|
} |
|
}, |
|
|
|
dodblclick: function(e){ |
|
if(e.cellNode){ |
|
this.onCellDblClick(e); |
|
}else{ |
|
this.onRowDblClick(e); |
|
} |
|
}, |
|
|
|
docontextmenu: function(e){ |
|
if(e.cellNode){ |
|
this.onCellContextMenu(e); |
|
}else{ |
|
this.onRowContextMenu(e); |
|
} |
|
}, |
|
|
|
doheaderclick: function(e){ |
|
if(e.cellNode){ |
|
this.onHeaderCellClick(e); |
|
}else{ |
|
this.onHeaderClick(e); |
|
} |
|
}, |
|
|
|
doheaderdblclick: function(e){ |
|
if(e.cellNode){ |
|
this.onHeaderCellDblClick(e); |
|
}else{ |
|
this.onHeaderDblClick(e); |
|
} |
|
}, |
|
|
|
doheadercontextmenu: function(e){ |
|
if(e.cellNode){ |
|
this.onHeaderCellContextMenu(e); |
|
}else{ |
|
this.onHeaderContextMenu(e); |
|
} |
|
}, |
|
|
|
// override to modify editing process |
|
doStartEdit: function(inCell, inRowIndex){ |
|
this.onStartEdit(inCell, inRowIndex); |
|
}, |
|
|
|
doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){ |
|
this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex); |
|
}, |
|
|
|
doCancelEdit: function(inRowIndex){ |
|
this.onCancelEdit(inRowIndex); |
|
}, |
|
|
|
doApplyEdit: function(inRowIndex){ |
|
this.onApplyEdit(inRowIndex); |
|
}, |
|
|
|
// row editing |
|
addRow: function(){ |
|
// summary: |
|
// Add a row to the grid. |
|
this.updateRowCount(this.get('rowCount')+1); |
|
}, |
|
|
|
removeSelectedRows: function(){ |
|
// summary: |
|
// Remove the selected rows from the grid. |
|
if(this.allItemsSelected){ |
|
this.updateRowCount(0); |
|
}else{ |
|
this.updateRowCount(Math.max(0, this.get('rowCount') - this.selection.getSelected().length)); |
|
} |
|
this.selection.clear(); |
|
} |
|
|
|
}); |
|
|
|
_Grid.markupFactory = function(props, node, ctor, cellFunc){ |
|
var widthFromAttr = function(n){ |
|
var w = html.attr(n, "width")||"auto"; |
|
if((w != "auto")&&(w.slice(-2) != "em")&&(w.slice(-1) != "%")){ |
|
w = parseInt(w, 10)+"px"; |
|
} |
|
return w; |
|
}; |
|
// if(!props.store){ console.debug("no store!"); } |
|
// 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 = query("> colgroup", node).map(function(cg){ |
|
var sv = html.attr(cg, "span"); |
|
var v = { |
|
noscroll: (html.attr(cg, "noscroll") == "true") ? true : false, |
|
__span: (!!sv ? parseInt(sv, 10) : 1), |
|
cells: [] |
|
}; |
|
if(html.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 |
|
query("thead > tr", node).forEach(function(tr, tr_idx){ |
|
var cellCount = 0; |
|
var viewIdx = 0; |
|
var lastViewIdx; |
|
var cView = null; |
|
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: lang.trim(html.attr(th, "name")||th.innerHTML), |
|
colSpan: parseInt(html.attr(th, "colspan")||1, 10), |
|
type: lang.trim(html.attr(th, "cellType")||""), |
|
id: lang.trim(html.attr(th,"id")||"") |
|
}; |
|
cellCount += cell.colSpan; |
|
var rowSpan = html.attr(th, "rowspan"); |
|
if(rowSpan){ |
|
cell.rowSpan = rowSpan; |
|
} |
|
if(html.hasAttr(th, "width")){ |
|
cell.width = widthFromAttr(th); |
|
} |
|
if(html.hasAttr(th, "relWidth")){ |
|
cell.relWidth = window.parseInt(html.attr(th, "relWidth"), 10); |
|
} |
|
if(html.hasAttr(th, "hidden")){ |
|
cell.hidden = (html.attr(th, "hidden") == "true" || html.attr(th, "hidden") === true/*always boolean true in Chrome*/); |
|
} |
|
|
|
if(cellFunc){ |
|
cellFunc(th, cell); |
|
} |
|
|
|
cell.type = cell.type ? lang.getObject(cell.type) : dojox.grid.cells.Cell; |
|
|
|
if(cell.type && cell.type.markupFactory){ |
|
cell.type.markupFactory(th, cell); |
|
} |
|
|
|
if(!cView.cells[tr_idx]){ |
|
cView.cells[tr_idx] = []; |
|
} |
|
cView.cells[tr_idx].push(cell); |
|
}); |
|
}); |
|
} |
|
|
|
return new ctor(props, node); |
|
}; |
|
|
|
return _Grid; |
|
|
|
});
|
|
|