12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394 |
- require({cache:{
- 'url:dojox/grid/resources/_Grid.html':"<div hidefocus=\"hidefocus\" role=\"grid\" dojoAttachEvent=\"onmouseout:_mouseOut\">\n\t<div class=\"dojoxGridMasterHeader\" dojoAttachPoint=\"viewsHeaderNode\" role=\"presentation\"></div>\n\t<div class=\"dojoxGridMasterView\" dojoAttachPoint=\"viewsNode\" role=\"presentation\"></div>\n\t<div class=\"dojoxGridMasterMessages\" style=\"display: none;\" dojoAttachPoint=\"messagesNode\"></div>\n\t<span dojoAttachPoint=\"lastFocusNode\" tabindex=\"0\"></span>\n</div>\n"}});
- define("dojox/grid/_Grid", [
- "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 = 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;
- }
- =====*/
- 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 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: _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");
- }
- },
-
- 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() {
- // 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 = 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: 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 || this.edit._noUpdate){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 _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);
- },
- // 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();
- }
- });
- _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;
- });
|