| 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;});
 |