diff options
| author | Tristan Zur <tzur@web.web.ccwn.org> | 2014-03-27 22:27:47 +0100 |
|---|---|---|
| committer | Tristan Zur <tzur@web.web.ccwn.org> | 2014-03-27 22:27:47 +0100 |
| commit | b62676ca5d3d6f6ba3f019ea3f99722e165a98d8 (patch) | |
| tree | 86722cb80f07d4569f90088eeaea2fc2f6e2ef94 /js/dojo-1.6/dojox/grid/_Grid.js | |
Diffstat (limited to 'js/dojo-1.6/dojox/grid/_Grid.js')
| -rw-r--r-- | js/dojo-1.6/dojox/grid/_Grid.js | 1382 |
1 files changed, 1382 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/grid/_Grid.js b/js/dojo-1.6/dojox/grid/_Grid.js new file mode 100644 index 0000000..7fdcf30 --- /dev/null +++ b/js/dojo-1.6/dojox/grid/_Grid.js @@ -0,0 +1,1382 @@ +/*
+ Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
+ Available via Academic Free License >= 2.1 OR the modified BSD license.
+ see: http://dojotoolkit.org/license for details
+*/
+
+
+if(!dojo._hasResource["dojox.grid._Grid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.grid._Grid"] = true;
+dojo.provide("dojox.grid._Grid");
+
+dojo.require("dijit.dijit");
+dojo.require("dijit.Menu");
+
+dojo.require("dojox.html.metrics");
+dojo.require("dojox.grid.util");
+dojo.require("dojox.grid._Scroller");
+dojo.require("dojox.grid._Layout");
+dojo.require("dojox.grid._View");
+dojo.require("dojox.grid._ViewManager");
+dojo.require("dojox.grid._RowManager");
+dojo.require("dojox.grid._FocusManager");
+dojo.require("dojox.grid._EditManager");
+dojo.require("dojox.grid.Selection");
+dojo.require("dojox.grid._RowSelector");
+dojo.require("dojox.grid._Events");
+
+
+dojo.requireLocalization("dijit", "loading", null, "ROOT,ar,ca,cs,da,de,el,es,fi,fr,he,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
+
+(function(){
+ // NOTE: this is for backwards compatibility with Dojo 1.3
+ if(!dojo.isCopyKey){
+ dojo.isCopyKey = dojo.dnd.getCopyKeyState;
+ }
+ /*=====
+ dojox.grid.__CellDef = function(){
+ // 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.
+ this.name = name;
+ this.value = value;
+ this.get = get;
+ this.formatter = formatter;
+ this.type = type;
+ this.editable = editable;
+ this.hidden = hidden;
+ this.width = width;
+ this.colSpan = colSpan;
+ this.rowSpan = rowSpan;
+ this.styles = styles;
+ this.headerStyles = headerStyles;
+ this.cellStyles = cellStyles;
+ this.classes = classes;
+ this.headerClasses = headerClasses;
+ this.cellClasses = cellClasses;
+ this.attrs = attrs;
+ }
+ =====*/
+
+ /*=====
+ dojox.grid.__ViewDef = function(){
+ // 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.
+ this.noscroll = noscroll;
+ this.width = width;
+ this.cells = cells;
+ this.type = type;
+ this.defaultCell = defaultCell;
+ this.onBeforeRow = onBeforeRow;
+ this.onAfterRow = onAfterRow;
+ }
+ =====*/
+
+ dojo.declare('dojox.grid._Grid',
+ [ dijit._Widget, dijit._Templated, dojox.grid._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:"<div hidefocus=\"hidefocus\" role=\"grid\" dojoAttachEvent=\"onmouseout:_mouseOut\">\r\n\t<div class=\"dojoxGridMasterHeader\" dojoAttachPoint=\"viewsHeaderNode\" role=\"presentation\"></div>\r\n\t<div class=\"dojoxGridMasterView\" dojoAttachPoint=\"viewsNode\" role=\"presentation\"></div>\r\n\t<div class=\"dojoxGridMasterMessages\" style=\"display: none;\" dojoAttachPoint=\"messagesNode\"></div>\r\n\t<span dojoAttachPoint=\"lastFocusNode\" tabindex=\"0\"></span>\r\n</div>\r\n",
+
+ // 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 defintion.
+ 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,
+
+ // private
+ sortInfo: 0,
+ themeable: true,
+ _placeholders: null,
+
+ // _layoutClass: Object
+ // The class to use for our layout - can be overridden by grid subclasses
+ _layoutClass: dojox.grid._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");
+
+ dojox.html.metrics.initOnFontResize();
+ this.connect(dojox.html.metrics, "onFontResize", "textSizeChanged");
+ dojox.grid.util.funnelEvents(this.domNode, this, 'doKeyEvent', dojox.grid.util.keyEvents);
+ if (this.selectionMode != "none") {
+ dojo.attr(this.domNode, "aria-multiselectable", this.selectionMode == "single" ? "false" : "true");
+ }
+
+ dojo.addClass(this.domNode, this.classTag);
+ if(!this.isLeftToRight()){
+ dojo.addClass(this.domNode, this.classTag+"Rtl");
+ }
+ },
+
+ postMixInProperties: function(){
+ this.inherited(arguments);
+ var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
+ this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
+ this.errorMessage = dojo.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
+ dojo.attr(this.domNode,"aria-readonly", "true");
+ }
+ },
+
+ destroy: function(){
+ this.domNode.onReveal = null;
+ this.domNode.onSizeChange = null;
+
+ // Fixes IE domNode leak
+ delete this._click;
+
+ this.edit.destroy();
+ delete this.edit;
+
+ this.views.destroyViews();
+ if(this.scroller){
+ this.scroller.destroy();
+ delete this.scroller;
+ }
+ if(this.focus){
+ this.focus.destroy();
+ delete this.focus;
+ }
+ if(this.headerMenu&&this._placeholders.length){
+ dojo.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 dojox.grid._RowManager(this);
+ // focus manager
+ this.focus = new dojox.grid._FocusManager(this);
+ // edit manager
+ this.edit = new dojox.grid._EditManager(this);
+ },
+
+ createSelection: function(){
+ // summary: Creates a new Grid selection manager.
+
+ // selection manager
+ this.selection = new dojox.grid.Selection(this);
+ },
+
+ createScroller: function(){
+ // summary: Creates a new virtual scroller
+ this.scroller = new dojox.grid._Scroller();
+ this.scroller.grid = this;
+ this.scroller.renderRow = dojo.hitch(this, "renderRow");
+ this.scroller.removeRow = dojo.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.render();
+ },
+
+ onResizeColumn: function(/*int*/ cellIdx){
+ // Called when a column is resized.
+ },
+
+ // views
+ createViews: function(){
+ this.views = new dojox.grid._ViewManager(this);
+ this.views.createView = dojo.hitch(this, "createView");
+ },
+
+ createView: function(inClass, idx){
+ var c = dojo.getObject(inClass);
+ var view = new c({ grid: this, index: idx });
+ this.viewsNode.appendChild(view.domNode);
+ this.viewsHeaderNode.appendChild(view.headerNode);
+ this.views.addView(view);
+ dojo.attr(this.domNode, "align", dojo._isBodyLtr() ? '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 && dojo.isString(s)){
+ dojo.deprecated("dojox.grid._Grid.set('structure', 'objVar')", "use dojox.grid._Grid.set('structure', objVar) instead", "2.0");
+ s=dojo.getObject(s);
+ }
+ this.structure = s;
+ if(!s){
+ if(this.layout.structure){
+ s = this.layout.structure;
+ }else{
+ return;
+ }
+ }
+ this.views.destroyViews();
+ 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.
+ return dojo.map(this.layout.cells, function(cell){
+ if(!cell.menuItems){ cell.menuItems = []; }
+
+ var self = this;
+ var item = new dijit.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){
+ dojo.forEach(items, function(item){
+ if(item !== this){
+ item.setAttribute("checked", checked);
+ }
+ }, this);
+ }
+ checked = dojo.filter(self.layout.cells, function(c){
+ if(c.menuItems.length > 1){
+ dojo.forEach(c.menuItems, "item.set('disabled', false);");
+ }else{
+ c.menuItems[0].set('disabled', false);
+ }
+ return !c.hidden;
+ });
+ if(checked.length == 1){
+ dojo.forEach(checked[0].menuItems, "item.set('disabled', true);");
+ }
+ }
+ },
+ destroy: function(){
+ var index = dojo.indexOf(this._gridCell.menuItems, this);
+ this._gridCell.menuItems.splice(index, 1);
+ delete this._gridCell;
+ dijit.CheckedMenuItem.prototype.destroy.apply(this, arguments);
+ }
+ });
+ cell.menuItems.push(item);
+ return item;
+ }, this); // dijit.CheckedMenuItem[]
+ },
+
+ _setHeaderMenuAttr: function(menu){
+ if(this._placeholders && this._placeholders.length){
+ dojo.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){
+ dojo.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 || dojo._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(changeSize){
+ dojo.marginBox(this.domNode, changeSize);
+ this.height = this.domNode.style.height;
+ delete this.fitTo;
+ }else if(this.fitTo == "parent"){
+ h = this._parentContentBoxHeight = this._parentContentBoxHeight || dojo._getContentBox(pn).h;
+ this.domNode.style.height = Math.max(0, h) + "px";
+ }
+
+ var hasFlex = dojo.some(this.views.views, function(v){ return v.flexCells; });
+
+ if(!this._autoHeight && (h || dojo._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() {
+ // private: sets width and position for views and update grid width if necessary
+ 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){
+ // private: measures and normalizes header height, then sets view heights, and then updates scroller
+ // content extent
+ 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 = dojo.filter(this.views.views, function(v){
+ var has = v.hasHScrollbar();
+ if(has){ numScroll++; }else{ numNoScroll++; }
+ return (!has);
+ });
+ if(numScroll > 0 && numNoScroll > 0){
+ dojo.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
+ dojo.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: private, used internally to render rows
+ this.views.renderRow(inRowIndex, inNodes, this._skipRowRenormalize);
+ },
+
+ rowRemoved: function(inRowIndex){
+ // summary: private, used internally to remove rows
+ 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);
+ }
+ this._resize();
+ if(this.layout.cells.length){
+ this.setScrollTop(this.scrollTop);
+ }
+ }
+ },
+
+ 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 dojox.grid._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 dojox.grid._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: (dojo.isIE ? 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);
+ },
+
+ // styling (private, used internally to style individual parts of a row)
+ styleRowNode: function(inRowIndex, inRowNode){
+ 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();
+ }
+
+ });
+
+ dojox.grid._Grid.markupFactory = function(props, node, ctor, cellFunc){
+ var d = dojo;
+ var widthFromAttr = function(n){
+ var w = d.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 = 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, 10) : 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),
+ colSpan: parseInt(d.attr(th, "colspan")||1, 10),
+ type: d.trim(d.attr(th, "cellType")||""),
+ id: d.trim(d.attr(th,"id")||"")
+ };
+ cellCount += cell.colSpan;
+ var rowSpan = d.attr(th, "rowspan");
+ if(rowSpan){
+ cell.rowSpan = rowSpan;
+ }
+ if(d.hasAttr(th, "width")){
+ cell.width = widthFromAttr(th);
+ }
+ if(d.hasAttr(th, "relWidth")){
+ cell.relWidth = window.parseInt(dojo.attr(th, "relWidth"), 10);
+ }
+ if(d.hasAttr(th, "hidden")){
+ cell.hidden = (d.attr(th, "hidden") == "true" || d.attr(th, "hidden") === true/*always boolean true in Chrome*/);
+ }
+
+ if(cellFunc){
+ cellFunc(th, cell);
+ }
+
+ cell.type = cell.type ? dojo.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);
+ };
+})();
+
+}
|
