/*
	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.enhanced._FocusManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.enhanced._FocusManager"] = true;
dojo.provide("dojox.grid.enhanced._FocusManager");

dojo.declare("dojox.grid.enhanced._FocusArea",null,{
	// summary:
	//		This is a friend class of _FocusManager
/*=====
		// name: string
		//		Name of this area.
		name: "",
		
		// onFocus: function(event, step)
		//		Called when this area logically gets focus.
		//		event: Event object
		//				May be unavailable, should check before use.
		//		step: Integer
		//				The distance in the tab sequence from last focused area to this area.
		//		returns:
		//				whether this area is successfully focused. If not, the next area will get focus.
		onFocus: function(event, step){return true;},
		
		// onBlur: function(event, step)
		//		Called when this area logically loses focus.
		//		event: Event object
		//				May be unavailable, should check before use.
		//		step: Integer
		//				The distance in the tab sequence from this area to the area to focus.
		//		returns:
		//				If Boolean, means whether this area has successfully blurred. If not, the next area to focus is still this one.
		//				If String, means the next area to focus is given by this returned name.
		onBlur: function(event, step){return true;},
		
		// onMove: function(rowStep, colStep, event)
		//		Called when focus is moving around within this area.
		//		rowStep: Integer
		//		colStep: Integer
		//		event: Event object
		//				May be unavailable, should check before use.
		onMove: function(rowStep, colStep, event){},
		
		// onKey: function(event, isBubble)
		//		Called when some key is pressed when focus is logically in this area.
		//		event: Event object
		//		isBubble: Boolean
		//				Whether is in bubble stage (true) or catch stage (false).
		//		returns:
		//				If you do NOT want the event to propagate any further along the area stack, return exactly false.
		//				So if you return nothing (undefined), this event is still propagating.
		onKey: function(event, isBubble){return true},
		
		// getRegions: function()
		//		Define the small regions (dom nodes) in this area.
		//		returns: Array of dom nodes.
		getRegions: function(){},
		
		// onRegionFocus: function(event)
		//		Connected to the onfocus event of the defined regions (if any)
		onRegionFocus: function(event){},
		
		// onRegionBlur: function(event)
		//		Connected to the onblur event of the defined regions (if any)
		onRegionBlur: function(event){},
=====*/
	constructor: function(area, focusManager){
		this._fm = focusManager;
		this._evtStack = [area.name];
		var dummy = function(){return true;};
		area.onFocus = area.onFocus || dummy;
		area.onBlur = area.onBlur || dummy;
		area.onMove = area.onMove || dummy;
		area.onKeyUp = area.onKeyUp || dummy;
		area.onKeyDown = area.onKeyDown || dummy;
		dojo.mixin(this, area);
	},
	move: function(rowStep, colStep, evt){
		if(this.name){
			var i, len = this._evtStack.length;
			for(i = len - 1; i >= 0; --i){
				if(this._fm._areas[this._evtStack[i]].onMove(rowStep, colStep, evt) === false){
					return false;
				}
			}
		}
		return true;
	},
	_onKeyEvent: function(evt, funcName){
		if(this.name){
			var i, len = this._evtStack.length;
			for(i = len - 1; i >= 0; --i){
				if(this._fm._areas[this._evtStack[i]][funcName](evt, false) === false){
					return false;
				}
			}
			for(i = 0; i < len; ++i){
				if(this._fm._areas[this._evtStack[i]][funcName](evt, true) === false){
					return false;
				}
			}
		}
		return true;
	},
	keydown: function(evt){
		return this._onKeyEvent(evt, "onKeyDown");
	},
	keyup: function(evt){
		return this._onKeyEvent(evt, "onKeyUp");
	},
	contentMouseEventPlanner: function(){
		return 0;
	},
	headerMouseEventPlanner: function(){
		return 0;
	}
});
dojo.declare("dojox.grid.enhanced._FocusManager", dojox.grid._FocusManager, {
	_stopEvent: function(evt){
		try{
			if(evt && evt.preventDefault){
				dojo.stopEvent(evt);
			}
		}catch(e){}
	},
	
	constructor: function(grid){
		this.grid = grid;
		this._areas = {};
		this._areaQueue = [];
		this._contentMouseEventHandlers = [];
		this._headerMouseEventHandlers = [];
		this._currentAreaIdx = -1;
		this._gridBlured = true;
		this._connects.push(dojo.connect(grid, "onBlur", this, "_doBlur"));
		
		this.addArea({
			name: "header",
			onFocus: dojo.hitch(this, this.focusHeader),
			onBlur: dojo.hitch(this, this._blurHeader),
			onMove: dojo.hitch(this, this._navHeader),
			getRegions: dojo.hitch(this, this._findHeaderCells),
			onRegionFocus: dojo.hitch(this, this.doColHeaderFocus),
			onRegionBlur: dojo.hitch(this, this.doColHeaderBlur),
			onKeyDown: dojo.hitch(this, this._onHeaderKeyDown)
		});
		this.addArea({
			name: "content",
			onFocus: dojo.hitch(this, this._focusContent),
			onBlur: dojo.hitch(this, this._blurContent),
			onMove: dojo.hitch(this, this._navContent),
			onKeyDown: dojo.hitch(this, this._onContentKeyDown)
		});
		this.addArea({
			name: "editableCell",
			onFocus: dojo.hitch(this, this._focusEditableCell),
			onBlur: dojo.hitch(this, this._blurEditableCell),
			onKeyDown: dojo.hitch(this, this._onEditableCellKeyDown),
			onContentMouseEvent: dojo.hitch(this, this._onEditableCellMouseEvent),
			contentMouseEventPlanner: function(evt, areas){ return -1; }
		});
		this.placeArea("header");
		this.placeArea("content");
		this.placeArea("editableCell");
		this.placeArea("editableCell","above","content");
	},
	destroy: function(){
		for(var name in this._areas){
			var area = this._areas[name];
			dojo.forEach(area._connects, dojo.disconnect);
			area._connects = null;
			if(area.uninitialize){
				area.uninitialize();
			}
		}
		this.inherited(arguments);
	},
	addArea: function(area){
		if(area.name && dojo.isString(area.name)){
			if(this._areas[area.name]){
				//Just replace the original area, instead of remove it, so the position does not change.
				dojo.forEach(area._connects, dojo.disconnect);
			}
			this._areas[area.name] = new dojox.grid.enhanced._FocusArea(area, this);
			if(area.onHeaderMouseEvent){
				this._headerMouseEventHandlers.push(area.name);
			}
			if(area.onContentMouseEvent){
				this._contentMouseEventHandlers.push(area.name);
			}
		}
	},
	getArea: function(areaName){
		return this._areas[areaName];
	},
	_bindAreaEvents: function(){
		var area, hdl, areas = this._areas;
		dojo.forEach(this._areaQueue, function(name){
			area = areas[name];
			if(!area._initialized && dojo.isFunction(area.initialize)){
				area.initialize();
				area._initialized = true;
			}
			if(area.getRegions){
				area._regions = area.getRegions() || [];
				dojo.forEach(area._connects || [], dojo.disconnect);
				area._connects = [];
				dojo.forEach(area._regions, function(r){
					if(area.onRegionFocus){
						hdl = dojo.connect(r, "onfocus", area.onRegionFocus);
						area._connects.push(hdl);
					}
					if(area.onRegionBlur){
						hdl = dojo.connect(r, "onblur", area.onRegionBlur);
						area._connects.push(hdl);
					}
				});
			}
		});
	},
	removeArea: function(areaName){
		var area = this._areas[areaName];
		if(area){
			this.ignoreArea(areaName);
			var i = dojo.indexOf(this._contentMouseEventHandlers, areaName);
			if(i >= 0){
				this._contentMouseEventHandlers.splice(i, 1);
			}
			i = dojo.indexOf(this._headerMouseEventHandlers, areaName);
			if(i >= 0){
				this._headerMouseEventHandlers.splice(i, 1);
			}
			dojo.forEach(area._connects, dojo.disconnect);
			if(area.uninitialize){
				area.uninitialize();
			}
			delete this._areas[areaName];
		}
	},
	currentArea: function(areaName, toBlurOld){
		// summary:
		//		Set current area to the one areaName refers.
		// areaName: String
		var idx, cai = this._currentAreaIdx;
		if(dojo.isString(areaName) && (idx = dojo.indexOf(this._areaQueue, areaName)) >= 0){
			if(cai != idx){
				this.tabbingOut = false;
				if(toBlurOld && cai >= 0 && cai < this._areaQueue.length){
					this._areas[this._areaQueue[cai]].onBlur();
				}
				this._currentAreaIdx = idx;
			}
		}else{
			return (cai < 0 || cai >= this._areaQueue.length) ?
				new dojox.grid.enhanced._FocusArea({}, this) :
				this._areas[this._areaQueue[this._currentAreaIdx]];
		}
		return null;
	},
	placeArea: function(name, pos, otherAreaName){
		// summary:
		//		Place the area refered by *name* at some logical position relative to an existing area.
		// example:
		//		placeArea("myarea","before"|"after",...)
		//		placeArea("myarea","below"|"above",...)
		if(!this._areas[name]){ return; }
		var idx = dojo.indexOf(this._areaQueue,otherAreaName);
		switch(pos){
			case "after":
				if(idx >= 0){ ++idx; }
				//intentional drop through
			case "before":
				if(idx >= 0){
					this._areaQueue.splice(idx,0,name);
					break;
				}
				//intentional drop through
			default:
				this._areaQueue.push(name);
				break;
			case "above":
				var isAbove = true;
				//intentional drop through
			case "below":
				var otherArea = this._areas[otherAreaName];
				if(otherArea){
					if(isAbove){
						otherArea._evtStack.push(name);
					}else{
						otherArea._evtStack.splice(0,0,name);
					}
				}
		}
	},
	ignoreArea: function(name){
		this._areaQueue = dojo.filter(this._areaQueue,function(areaName){
			return areaName != name;
		});
	},
	focusArea: function(/* int|string|areaObj */areaId,evt){
		var idx;
		if(typeof areaId == "number"){
			idx = areaId < 0 ? this._areaQueue.length + areaId : areaId;
		}else{
			idx = dojo.indexOf(this._areaQueue,
				dojo.isString(areaId) ? areaId : (areaId && areaId.name));
		}
		if(idx < 0){ idx = 0; }
		var step = idx - this._currentAreaIdx;
		this._gridBlured = false;
		if(step){
			this.tab(step, evt);
		}else{
			this.currentArea().onFocus(evt, step);
		}
	},
	tab: function(step,evt){
		//console.log("===========tab",step,"curArea",this._currentAreaIdx,"areaCnt",this._areaQueue.length);
		this._gridBlured = false;
		this.tabbingOut = false;
		if(step === 0){
			return;
		}
		var cai = this._currentAreaIdx;
		var dir = step > 0 ? 1:-1;
		if(cai < 0 || cai >= this._areaQueue.length){
			cai = (this._currentAreaIdx += step);
		}else{
			var nextArea = this._areas[this._areaQueue[cai]].onBlur(evt,step);
			if(nextArea === true){
				cai = (this._currentAreaIdx += step);
			}else if(dojo.isString(nextArea) && this._areas[nextArea]){
				cai = this._currentAreaIdx = dojo.indexOf(this._areaQueue,nextArea);
			}
		}
		//console.log("target area:",cai);
		for(; cai >= 0 && cai < this._areaQueue.length; cai += dir){
			this._currentAreaIdx = cai;
			if(this._areaQueue[cai] && this._areas[this._areaQueue[cai]].onFocus(evt,step)){
				//console.log("final target area:",this._currentAreaIdx);
				return;
			}
		}
		//console.log("tab out");
		this.tabbingOut = true;
		if(step < 0){
			this._currentAreaIdx = -1;
			dijit.focus(this.grid.domNode);
		}else{
			this._currentAreaIdx = this._areaQueue.length;
			dijit.focus(this.grid.lastFocusNode);
		}
	},
	_onMouseEvent: function(type, evt){
		var lowercase = type.toLowerCase(),
			handlers = this["_" + lowercase + "MouseEventHandlers"],
			res = dojo.map(handlers, function(areaName){
				return {
					"area": areaName,
					"idx": this._areas[areaName][lowercase + "MouseEventPlanner"](evt, handlers)
				};
			}, this).sort(function(a, b){
				return b.idx - a.idx;
			}),
			resHandlers = dojo.map(res, function(handler){
				return res.area;
			}),
			i = res.length;
		while(--i >= 0){
			if(this._areas[res[i].area]["on" + type + "MouseEvent"](evt, resHandlers) === false){
				return;
			}
		}
	},
	contentMouseEvent: function(evt){
		this._onMouseEvent("Content", evt);
	},
	headerMouseEvent: function(evt){
		this._onMouseEvent("Header", evt);
	},
	initFocusView: function(){
		// summary:
		//		Overwritten
		this.focusView = this.grid.views.getFirstScrollingView() || this.focusView || this.grid.views.views[0];
		this._bindAreaEvents();
	},
	isNavHeader: function(){
		// summary:
		//		Overwritten
		//		Check whether currently navigating among column headers.
		// return:
		//		true - focus is on a certain column header | false otherwise
		return this._areaQueue[this._currentAreaIdx] == "header";
	},
	previousKey: function(e){
		// summary:
		//		Overwritten
		this.tab(-1,e);
	},
	nextKey: function(e){
		// summary:
		//		Overwritten
		this.tab(1,e);
	},
	setFocusCell: function(/* Object */inCell, /* Integer */inRowIndex){
		// summary:
		//		Overwritten - focuses the given grid cell
		if(inCell){
			this.currentArea(this.grid.edit.isEditing() ? "editableCell" : "content", true);
			//This is very slow when selecting cells!
			//this.focusGridView();
			this._focusifyCellNode(false);
			this.cell = inCell;
			this.rowIndex = inRowIndex;
			this._focusifyCellNode(true);
		}
		this.grid.onCellFocus(this.cell, this.rowIndex);
	},
	doFocus: function(e){
		// summary:
		//		Overwritten
		//		trap focus only for grid dom node
		//		do not focus for scrolling if grid is about to blur
		if(e && e.target == e.currentTarget && !this.tabbingOut){
			if(this._gridBlured){
				this._gridBlured = false;
				if(this._currentAreaIdx < 0 || this._currentAreaIdx >= this._areaQueue.length){
					this.focusArea(0, e);
				}else{
					this.focusArea(this._currentAreaIdx, e);
				}
			}
		}else{
			this.tabbingOut = false;
		}
		dojo.stopEvent(e);
	},
	_doBlur: function(){
		this._gridBlured = true;
	},
	doLastNodeFocus: function(e){
		// summary:
		//		Overwritten
		if(this.tabbingOut){
			this.tabbingOut = false;
		}else{
			this.focusArea(-1, e);
		}
	},
	_delayedHeaderFocus: function(){
		// summary:
		//		Overwritten
		if(this.isNavHeader()){
			this.focusHeader();
		}
	},
	_delayedCellFocus: function(){
		// summary:
		//		Overwritten
		if(this.currentArea().name == "content"){
			this.focusArea(this._currentAreaIdx);
		}
	},
	_changeMenuBindNode: function(oldBindNode, newBindNode){
		var hm = this.grid.headerMenu;
		if(hm && this._contextMenuBindNode == oldBindNode){
			hm.unBindDomNode(oldBindNode);
			hm.bindDomNode(newBindNode);
			this._contextMenuBindNode = newBindNode;
		}
	},
	//---------------Header Area------------------------------------------
	focusHeader: function(evt, step){ //need a further look why these changes to parent's
		// summary:
		//		Overwritten
		var didFocus = false;
		this.inherited(arguments);
		if(this._colHeadNode && dojo.style(this._colHeadNode, 'display') != "none"){
			dijit.focus(this._colHeadNode);
			this._stopEvent(evt);
			didFocus = true;
		}
		return didFocus;
	},
	_blurHeader: function(evt,step){
		// summary:
		//		Overwritten
		if(this._colHeadNode){
			dojo.removeClass(this._colHeadNode, this.focusClass);
		}
		dojo.removeAttr(this.grid.domNode,"aria-activedescendant");
		// reset contextMenu onto viewsHeaderNode so right mouse on header will invoke (see focusHeader)
		this._changeMenuBindNode(this.grid.domNode,this.grid.viewsHeaderNode);
		//moved here from nextKey
		this._colHeadNode = this._colHeadFocusIdx = null;
		return true;
	},
	_navHeader: function(rowStep, colStep, evt){
		var colDir = colStep < 0 ? -1 : 1,
			savedIdx = dojo.indexOf(this._findHeaderCells(), this._colHeadNode);
		if(savedIdx >= 0 && (evt.shiftKey && evt.ctrlKey)){
			this.colSizeAdjust(evt, savedIdx, colDir * 5);
			return;
		}
		this.move(rowStep, colStep);
	},
	_onHeaderKeyDown: function(e, isBubble){
		if(isBubble){
			var dk = dojo.keys;
			switch(e.keyCode){
				case dk.ENTER:
				case dk.SPACE:
					var colIdx = this.getHeaderIndex();
					if(colIdx >= 0 && !this.grid.pluginMgr.isFixedCell(e.cell)/*TODO*/){
						this.grid.setSortIndex(colIdx, null, e);
						dojo.stopEvent(e);
					}
					break;
			}
		}
		return true;
	},
	_setActiveColHeader: function(){
		// summary:
		//		Overwritten
		this.inherited(arguments);
		//EDG now will decorate event on header key events, if no focus, the cell will be wrong
		dijit.focus(this._colHeadNode);
	},
	//---------------Content Area------------------------------------------
	findAndFocusGridCell: function(){
		// summary:
		//		Overwritten
		this._focusContent();
	},
	_focusContent: function(evt,step){
		var didFocus = true;
		var isEmpty = (this.grid.rowCount === 0); // If grid is empty this.grid.rowCount == 0
		if(this.isNoFocusCell() && !isEmpty){
			//skip all the hidden cells
			for(var i = 0, cell = this.grid.getCell(0); cell && cell.hidden; cell = this.grid.getCell(++i)){}
			this.setFocusIndex(0, cell ? i : 0);
		}else if(this.cell && !isEmpty){
			if(this.focusView && !this.focusView.rowNodes[this.rowIndex]){
				// if rowNode for current index is undefined (likely as a result of a sort and because of #7304)
				// scroll to that row
				this.grid.scrollToRow(this.rowIndex);
				this.focusGrid();
			}else{
				this.setFocusIndex(this.rowIndex, this.cell.index);
			}
		}else{
			didFocus = false;
		}
		if(didFocus){ this._stopEvent(evt); }
		return didFocus;
	},
	_blurContent: function(evt,step){
		this._focusifyCellNode(false);
		return true;
	},
	_navContent: function(rowStep, colStep, evt){
		if((this.rowIndex === 0 && rowStep < 0) || (this.rowIndex === this.grid.rowCount - 1 && rowStep > 0)){
			return;
		}
		this._colHeadNode = null;
		this.move(rowStep, colStep, evt);
		if(evt){
			dojo.stopEvent(evt);
		}
	},
	move: function(inRowDelta, inColDelta){
		// summary:
		//		Overwritten
		this.inherited(arguments);
		var cell = this.cell, row = this.rowIndex;
		if(!this.isNavHeader() && cell){
			if(inRowDelta !== 0){//jump over hidden rows
				var node = cell.view.getRowNode(row);//row node
				if(node && dojo.style(node, "display") === "none"){
					this.move(inRowDelta > 0 ? 1 : -1, inColDelta);
				}
			}else if(inColDelta !== 0){//jump over hidden cells
				var node = cell.getNode(row);//cell node
				if(node && dojo.style(node, "display") === "none"){
					this.move(inRowDelta, inColDelta > 0 ? 1 : -1);
				}
			}
		}
	},
	_onContentKeyDown: function(e, isBubble){
		if(isBubble){
			var dk = dojo.keys, s = this.grid.scroller;
			switch(e.keyCode){
				case dk.ENTER:
				case dk.SPACE:
					var g = this.grid;
					if(g.indirectSelection){ break; }
					g.selection.clickSelect(this.rowIndex, dojo.isCopyKey(e), e.shiftKey);
					g.onRowClick(e);
					dojo.stopEvent(e);
					break;
				case dk.PAGE_UP:
					if(this.rowIndex !== 0){
						if(this.rowIndex != s.firstVisibleRow + 1){
							this._navContent(s.firstVisibleRow - this.rowIndex, 0);
						}else{
							this.grid.setScrollTop(s.findScrollTop(this.rowIndex - 1));
							this._navContent(s.firstVisibleRow - s.lastVisibleRow + 1, 0);
						}
						dojo.stopEvent(e);
					}
					break;
				case dk.PAGE_DOWN:
					if(this.rowIndex + 1 != this.grid.rowCount){
						dojo.stopEvent(e);
						if(this.rowIndex != s.lastVisibleRow - 1){
							this._navContent(s.lastVisibleRow - this.rowIndex - 1, 0);
						}else{
							this.grid.setScrollTop(s.findScrollTop(this.rowIndex + 1));
							this._navContent(s.lastVisibleRow - s.firstVisibleRow - 1, 0);
						}
						dojo.stopEvent(e);
					}
					break;
			}
		}
		return true;
	},
	//------------------editable content area-------------------------
	_blurFromEditableCell: false,
	_isNavigating: false,
	_navElems: null,
	_focusEditableCell: function(evt,step){
		var didFocus = false;
		if(this._isNavigating){
			didFocus = true;
		}else if(this.grid.edit.isEditing() && this.cell){
			if(this._blurFromEditableCell || !this._blurEditableCell(evt, step)){
				this.setFocusIndex(this.rowIndex,this.cell.index);
				didFocus = true;
			}
			this._stopEvent(evt);
		}
		return didFocus;
	},
	_applyEditableCell: function(){
		try{
			this.grid.edit.apply();
		}catch(e){
			console.warn("_FocusManager._applyEditableCell() error:", e);
		}
	},
	_blurEditableCell: function(evt,step){
		this._blurFromEditableCell = false;
		if(this._isNavigating){
			var toBlur = true;
			if(evt){
				var elems = this._navElems;
				var firstElem = elems.lowest || elems.first;
				var lastElem = elems.last || elems.highest || firstElem;
				var target = dojo.isIE ? evt.srcElement : evt.target;
				toBlur = target == (step > 0 ? lastElem : firstElem);
			}
			if(toBlur){
				this._isNavigating = false;
				return "content";
			}
			return false;
		}else if(this.grid.edit.isEditing() && this.cell){
			if(!step || typeof step != "number"){ return false; }
			var dir = step > 0 ? 1 : -1;
			var cc = this.grid.layout.cellCount;
			for(var cell, col = this.cell.index + dir; col >= 0 && col < cc; col += dir){
				cell = this.grid.getCell(col);
				if(cell.editable){
					this.cell = cell;
					this._blurFromEditableCell = true;
					return false;
				}
			}
			if((this.rowIndex > 0 || dir == 1) && (this.rowIndex < this.grid.rowCount || dir == -1)){
				this.rowIndex += dir;
				//this.cell = this.grid.getCell(0); //There must be an editable cell, so this is not necessary.
				for(col = dir > 0 ? 0 : cc - 1; col >= 0 && col < cc; col += dir){
					cell = this.grid.getCell(col);
					if(cell.editable){
						this.cell = cell;
						break;
					}
				}
				this._applyEditableCell();
				return "content";
			}
		}
		return true;
	},
	_initNavigatableElems: function(){
		this._navElems = dijit._getTabNavigable(this.cell.getNode(this.rowIndex));
	},
	_onEditableCellKeyDown: function(e, isBubble){
		var dk = dojo.keys,
			g = this.grid,
			edit = g.edit,
			editApplied = false,
			toPropagate = true;
		switch(e.keyCode){
			case dk.ENTER:
				if(isBubble && edit.isEditing()){
					this._applyEditableCell();
					editApplied = true;
				}
				//intentional drop through
			case dk.SPACE:
				if(!isBubble && this._isNavigating){
					toPropagate = false;
					break;
				}
				if(isBubble){
					if(!this.cell.editable && this.cell.navigatable){
						this._initNavigatableElems();
						var toFocus = this._navElems.lowest || this._navElems.first;
						if(toFocus){
							this._isNavigating = true;
							dijit.focus(toFocus);
							dojo.stopEvent(e);
							this.currentArea("editableCell", true);
							break;
						}
					}
					if(!editApplied && !edit.isEditing() && !g.pluginMgr.isFixedCell(this.cell)){
						edit.setEditCell(this.cell, this.rowIndex);
					}
					if(editApplied){
						this.currentArea("content", true);
					}else if(this.cell.editable && g.canEdit()){
						this.currentArea("editableCell", true);
					}
				}
				break;
			case dk.PAGE_UP:
			case dk.PAGE_DOWN:
				if(!isBubble && edit.isEditing()){
					//prevent propagating to content area
					toPropagate = false;
				}
				break;
			case dk.ESCAPE:
				if(!isBubble){
					edit.cancel();
					this.currentArea("content", true);
				}
		}
		return toPropagate;
	},
	_onEditableCellMouseEvent: function(evt){
		if(evt.type == "click"){
			var cell = this.cell || evt.cell;
			if(cell && !cell.editable && cell.navigatable){
				this._initNavigatableElems();
				if(this._navElems.lowest || this._navElems.first){
					var target = dojo.isIE ? evt.srcElement : evt.target;
					if(target != cell.getNode(evt.rowIndex)){
						this._isNavigating = true;
						this.focusArea("editableCell", evt);
						dijit.focus(target);
						return false;
					}
				}
			}else if(this.grid.singleClickEdit){
				this.currentArea("editableCell");
				return false;
			}
		}
		return true;
	}
});

}
