_FocusManager.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.grid.enhanced._FocusManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.grid.enhanced._FocusManager"] = true;
  8. dojo.provide("dojox.grid.enhanced._FocusManager");
  9. dojo.declare("dojox.grid.enhanced._FocusArea",null,{
  10. // summary:
  11. // This is a friend class of _FocusManager
  12. /*=====
  13. // name: string
  14. // Name of this area.
  15. name: "",
  16. // onFocus: function(event, step)
  17. // Called when this area logically gets focus.
  18. // event: Event object
  19. // May be unavailable, should check before use.
  20. // step: Integer
  21. // The distance in the tab sequence from last focused area to this area.
  22. // returns:
  23. // whether this area is successfully focused. If not, the next area will get focus.
  24. onFocus: function(event, step){return true;},
  25. // onBlur: function(event, step)
  26. // Called when this area logically loses focus.
  27. // event: Event object
  28. // May be unavailable, should check before use.
  29. // step: Integer
  30. // The distance in the tab sequence from this area to the area to focus.
  31. // returns:
  32. // If Boolean, means whether this area has successfully blurred. If not, the next area to focus is still this one.
  33. // If String, means the next area to focus is given by this returned name.
  34. onBlur: function(event, step){return true;},
  35. // onMove: function(rowStep, colStep, event)
  36. // Called when focus is moving around within this area.
  37. // rowStep: Integer
  38. // colStep: Integer
  39. // event: Event object
  40. // May be unavailable, should check before use.
  41. onMove: function(rowStep, colStep, event){},
  42. // onKey: function(event, isBubble)
  43. // Called when some key is pressed when focus is logically in this area.
  44. // event: Event object
  45. // isBubble: Boolean
  46. // Whether is in bubble stage (true) or catch stage (false).
  47. // returns:
  48. // If you do NOT want the event to propagate any further along the area stack, return exactly false.
  49. // So if you return nothing (undefined), this event is still propagating.
  50. onKey: function(event, isBubble){return true},
  51. // getRegions: function()
  52. // Define the small regions (dom nodes) in this area.
  53. // returns: Array of dom nodes.
  54. getRegions: function(){},
  55. // onRegionFocus: function(event)
  56. // Connected to the onfocus event of the defined regions (if any)
  57. onRegionFocus: function(event){},
  58. // onRegionBlur: function(event)
  59. // Connected to the onblur event of the defined regions (if any)
  60. onRegionBlur: function(event){},
  61. =====*/
  62. constructor: function(area, focusManager){
  63. this._fm = focusManager;
  64. this._evtStack = [area.name];
  65. var dummy = function(){return true;};
  66. area.onFocus = area.onFocus || dummy;
  67. area.onBlur = area.onBlur || dummy;
  68. area.onMove = area.onMove || dummy;
  69. area.onKeyUp = area.onKeyUp || dummy;
  70. area.onKeyDown = area.onKeyDown || dummy;
  71. dojo.mixin(this, area);
  72. },
  73. move: function(rowStep, colStep, evt){
  74. if(this.name){
  75. var i, len = this._evtStack.length;
  76. for(i = len - 1; i >= 0; --i){
  77. if(this._fm._areas[this._evtStack[i]].onMove(rowStep, colStep, evt) === false){
  78. return false;
  79. }
  80. }
  81. }
  82. return true;
  83. },
  84. _onKeyEvent: function(evt, funcName){
  85. if(this.name){
  86. var i, len = this._evtStack.length;
  87. for(i = len - 1; i >= 0; --i){
  88. if(this._fm._areas[this._evtStack[i]][funcName](evt, false) === false){
  89. return false;
  90. }
  91. }
  92. for(i = 0; i < len; ++i){
  93. if(this._fm._areas[this._evtStack[i]][funcName](evt, true) === false){
  94. return false;
  95. }
  96. }
  97. }
  98. return true;
  99. },
  100. keydown: function(evt){
  101. return this._onKeyEvent(evt, "onKeyDown");
  102. },
  103. keyup: function(evt){
  104. return this._onKeyEvent(evt, "onKeyUp");
  105. },
  106. contentMouseEventPlanner: function(){
  107. return 0;
  108. },
  109. headerMouseEventPlanner: function(){
  110. return 0;
  111. }
  112. });
  113. dojo.declare("dojox.grid.enhanced._FocusManager", dojox.grid._FocusManager, {
  114. _stopEvent: function(evt){
  115. try{
  116. if(evt && evt.preventDefault){
  117. dojo.stopEvent(evt);
  118. }
  119. }catch(e){}
  120. },
  121. constructor: function(grid){
  122. this.grid = grid;
  123. this._areas = {};
  124. this._areaQueue = [];
  125. this._contentMouseEventHandlers = [];
  126. this._headerMouseEventHandlers = [];
  127. this._currentAreaIdx = -1;
  128. this._gridBlured = true;
  129. this._connects.push(dojo.connect(grid, "onBlur", this, "_doBlur"));
  130. this._connects.push(dojo.connect(grid.scroller, "renderPage", this, "_delayedCellFocus"));
  131. this.addArea({
  132. name: "header",
  133. onFocus: dojo.hitch(this, this.focusHeader),
  134. onBlur: dojo.hitch(this, this._blurHeader),
  135. onMove: dojo.hitch(this, this._navHeader),
  136. getRegions: dojo.hitch(this, this._findHeaderCells),
  137. onRegionFocus: dojo.hitch(this, this.doColHeaderFocus),
  138. onRegionBlur: dojo.hitch(this, this.doColHeaderBlur),
  139. onKeyDown: dojo.hitch(this, this._onHeaderKeyDown)
  140. });
  141. this.addArea({
  142. name: "content",
  143. onFocus: dojo.hitch(this, this._focusContent),
  144. onBlur: dojo.hitch(this, this._blurContent),
  145. onMove: dojo.hitch(this, this._navContent),
  146. onKeyDown: dojo.hitch(this, this._onContentKeyDown)
  147. });
  148. this.addArea({
  149. name: "editableCell",
  150. onFocus: dojo.hitch(this, this._focusEditableCell),
  151. onBlur: dojo.hitch(this, this._blurEditableCell),
  152. onKeyDown: dojo.hitch(this, this._onEditableCellKeyDown),
  153. onContentMouseEvent: dojo.hitch(this, this._onEditableCellMouseEvent),
  154. contentMouseEventPlanner: function(evt, areas){ return -1; }
  155. });
  156. this.placeArea("header");
  157. this.placeArea("content");
  158. this.placeArea("editableCell");
  159. this.placeArea("editableCell","above","content");
  160. },
  161. destroy: function(){
  162. for(var name in this._areas){
  163. var area = this._areas[name];
  164. dojo.forEach(area._connects, dojo.disconnect);
  165. area._connects = null;
  166. if(area.uninitialize){
  167. area.uninitialize();
  168. }
  169. }
  170. this.inherited(arguments);
  171. },
  172. addArea: function(area){
  173. if(area.name && dojo.isString(area.name)){
  174. if(this._areas[area.name]){
  175. //Just replace the original area, instead of remove it, so the position does not change.
  176. dojo.forEach(area._connects, dojo.disconnect);
  177. }
  178. this._areas[area.name] = new dojox.grid.enhanced._FocusArea(area, this);
  179. if(area.onHeaderMouseEvent){
  180. this._headerMouseEventHandlers.push(area.name);
  181. }
  182. if(area.onContentMouseEvent){
  183. this._contentMouseEventHandlers.push(area.name);
  184. }
  185. }
  186. },
  187. getArea: function(areaName){
  188. return this._areas[areaName];
  189. },
  190. _bindAreaEvents: function(){
  191. var area, hdl, areas = this._areas;
  192. dojo.forEach(this._areaQueue, function(name){
  193. area = areas[name];
  194. if(!area._initialized && dojo.isFunction(area.initialize)){
  195. area.initialize();
  196. area._initialized = true;
  197. }
  198. if(area.getRegions){
  199. area._regions = area.getRegions() || [];
  200. dojo.forEach(area._connects || [], dojo.disconnect);
  201. area._connects = [];
  202. dojo.forEach(area._regions, function(r){
  203. if(area.onRegionFocus){
  204. hdl = dojo.connect(r, "onfocus", area.onRegionFocus);
  205. area._connects.push(hdl);
  206. }
  207. if(area.onRegionBlur){
  208. hdl = dojo.connect(r, "onblur", area.onRegionBlur);
  209. area._connects.push(hdl);
  210. }
  211. });
  212. }
  213. });
  214. },
  215. removeArea: function(areaName){
  216. var area = this._areas[areaName];
  217. if(area){
  218. this.ignoreArea(areaName);
  219. var i = dojo.indexOf(this._contentMouseEventHandlers, areaName);
  220. if(i >= 0){
  221. this._contentMouseEventHandlers.splice(i, 1);
  222. }
  223. i = dojo.indexOf(this._headerMouseEventHandlers, areaName);
  224. if(i >= 0){
  225. this._headerMouseEventHandlers.splice(i, 1);
  226. }
  227. dojo.forEach(area._connects, dojo.disconnect);
  228. if(area.uninitialize){
  229. area.uninitialize();
  230. }
  231. delete this._areas[areaName];
  232. }
  233. },
  234. currentArea: function(areaName, toBlurOld){
  235. // summary:
  236. // Set current area to the one areaName refers.
  237. // areaName: String
  238. var idx, cai = this._currentAreaIdx;
  239. if(dojo.isString(areaName) && (idx = dojo.indexOf(this._areaQueue, areaName)) >= 0){
  240. if(cai != idx){
  241. this.tabbingOut = false;
  242. if(toBlurOld && cai >= 0 && cai < this._areaQueue.length){
  243. this._areas[this._areaQueue[cai]].onBlur();
  244. }
  245. this._currentAreaIdx = idx;
  246. }
  247. }else{
  248. return (cai < 0 || cai >= this._areaQueue.length) ?
  249. new dojox.grid.enhanced._FocusArea({}, this) :
  250. this._areas[this._areaQueue[this._currentAreaIdx]];
  251. }
  252. return null;
  253. },
  254. placeArea: function(name, pos, otherAreaName){
  255. // summary:
  256. // Place the area refered by *name* at some logical position relative to an existing area.
  257. // example:
  258. // placeArea("myarea","before"|"after",...)
  259. // placeArea("myarea","below"|"above",...)
  260. if(!this._areas[name]){ return; }
  261. var idx = dojo.indexOf(this._areaQueue,otherAreaName);
  262. switch(pos){
  263. case "after":
  264. if(idx >= 0){ ++idx; }
  265. //intentional drop through
  266. case "before":
  267. if(idx >= 0){
  268. this._areaQueue.splice(idx,0,name);
  269. break;
  270. }
  271. //intentional drop through
  272. default:
  273. this._areaQueue.push(name);
  274. break;
  275. case "above":
  276. var isAbove = true;
  277. //intentional drop through
  278. case "below":
  279. var otherArea = this._areas[otherAreaName];
  280. if(otherArea){
  281. if(isAbove){
  282. otherArea._evtStack.push(name);
  283. }else{
  284. otherArea._evtStack.splice(0,0,name);
  285. }
  286. }
  287. }
  288. },
  289. ignoreArea: function(name){
  290. this._areaQueue = dojo.filter(this._areaQueue,function(areaName){
  291. return areaName != name;
  292. });
  293. },
  294. focusArea: function(/* int|string|areaObj */areaId,evt){
  295. var idx;
  296. if(typeof areaId == "number"){
  297. idx = areaId < 0 ? this._areaQueue.length + areaId : areaId;
  298. }else{
  299. idx = dojo.indexOf(this._areaQueue,
  300. dojo.isString(areaId) ? areaId : (areaId && areaId.name));
  301. }
  302. if(idx < 0){ idx = 0; }
  303. var step = idx - this._currentAreaIdx;
  304. this._gridBlured = false;
  305. if(step){
  306. this.tab(step, evt);
  307. }else{
  308. this.currentArea().onFocus(evt, step);
  309. }
  310. },
  311. tab: function(step,evt){
  312. //console.log("===========tab",step,"curArea",this._currentAreaIdx,"areaCnt",this._areaQueue.length);
  313. this._gridBlured = false;
  314. this.tabbingOut = false;
  315. if(step === 0){
  316. return;
  317. }
  318. var cai = this._currentAreaIdx;
  319. var dir = step > 0 ? 1:-1;
  320. if(cai < 0 || cai >= this._areaQueue.length){
  321. cai = (this._currentAreaIdx += step);
  322. }else{
  323. var nextArea = this._areas[this._areaQueue[cai]].onBlur(evt,step);
  324. if(nextArea === true){
  325. cai = (this._currentAreaIdx += step);
  326. }else if(dojo.isString(nextArea) && this._areas[nextArea]){
  327. cai = this._currentAreaIdx = dojo.indexOf(this._areaQueue,nextArea);
  328. }
  329. }
  330. //console.log("target area:",cai);
  331. for(; cai >= 0 && cai < this._areaQueue.length; cai += dir){
  332. this._currentAreaIdx = cai;
  333. if(this._areaQueue[cai] && this._areas[this._areaQueue[cai]].onFocus(evt,step)){
  334. //console.log("final target area:",this._currentAreaIdx);
  335. return;
  336. }
  337. }
  338. //console.log("tab out");
  339. this.tabbingOut = true;
  340. if(step < 0){
  341. this._currentAreaIdx = -1;
  342. dijit.focus(this.grid.domNode);
  343. }else{
  344. this._currentAreaIdx = this._areaQueue.length;
  345. dijit.focus(this.grid.lastFocusNode);
  346. }
  347. },
  348. _onMouseEvent: function(type, evt){
  349. var lowercase = type.toLowerCase(),
  350. handlers = this["_" + lowercase + "MouseEventHandlers"],
  351. res = dojo.map(handlers, function(areaName){
  352. return {
  353. "area": areaName,
  354. "idx": this._areas[areaName][lowercase + "MouseEventPlanner"](evt, handlers)
  355. };
  356. }, this).sort(function(a, b){
  357. return b.idx - a.idx;
  358. }),
  359. resHandlers = dojo.map(res, function(handler){
  360. return res.area;
  361. }),
  362. i = res.length;
  363. while(--i >= 0){
  364. if(this._areas[res[i].area]["on" + type + "MouseEvent"](evt, resHandlers) === false){
  365. return;
  366. }
  367. }
  368. },
  369. contentMouseEvent: function(evt){
  370. this._onMouseEvent("Content", evt);
  371. },
  372. headerMouseEvent: function(evt){
  373. this._onMouseEvent("Header", evt);
  374. },
  375. initFocusView: function(){
  376. // summary:
  377. // Overwritten
  378. this.focusView = this.grid.views.getFirstScrollingView() || this.focusView || this.grid.views.views[0];
  379. this._bindAreaEvents();
  380. },
  381. isNavHeader: function(){
  382. // summary:
  383. // Overwritten
  384. // Check whether currently navigating among column headers.
  385. // return:
  386. // true - focus is on a certain column header | false otherwise
  387. return this._areaQueue[this._currentAreaIdx] == "header";
  388. },
  389. previousKey: function(e){
  390. // summary:
  391. // Overwritten
  392. this.tab(-1,e);
  393. },
  394. nextKey: function(e){
  395. // summary:
  396. // Overwritten
  397. this.tab(1,e);
  398. },
  399. setFocusCell: function(/* Object */inCell, /* Integer */inRowIndex){
  400. // summary:
  401. // Overwritten - focuses the given grid cell
  402. if(inCell){
  403. this.currentArea(this.grid.edit.isEditing() ? "editableCell" : "content", true);
  404. //This is very slow when selecting cells!
  405. //this.focusGridView();
  406. this._focusifyCellNode(false);
  407. this.cell = inCell;
  408. this.rowIndex = inRowIndex;
  409. this._focusifyCellNode(true);
  410. }
  411. this.grid.onCellFocus(this.cell, this.rowIndex);
  412. },
  413. doFocus: function(e){
  414. // summary:
  415. // Overwritten
  416. // trap focus only for grid dom node
  417. // do not focus for scrolling if grid is about to blur
  418. if(e && e.target == e.currentTarget && !this.tabbingOut){
  419. if(this._gridBlured){
  420. this._gridBlured = false;
  421. if(this._currentAreaIdx < 0 || this._currentAreaIdx >= this._areaQueue.length){
  422. this.focusArea(0, e);
  423. }else{
  424. this.focusArea(this._currentAreaIdx, e);
  425. }
  426. }
  427. }else{
  428. this.tabbingOut = false;
  429. }
  430. dojo.stopEvent(e);
  431. },
  432. _doBlur: function(){
  433. this._gridBlured = true;
  434. },
  435. doLastNodeFocus: function(e){
  436. // summary:
  437. // Overwritten
  438. if(this.tabbingOut){
  439. this.tabbingOut = false;
  440. }else{
  441. this.focusArea(-1, e);
  442. }
  443. },
  444. _delayedHeaderFocus: function(){
  445. // summary:
  446. // Overwritten
  447. if(this.isNavHeader() && !dojo.isIE){
  448. this.focusHeader();
  449. }
  450. },
  451. _delayedCellFocus: function(){
  452. // summary:
  453. // Overwritten
  454. this.currentArea("header", true);
  455. this.focusArea(this._currentAreaIdx);
  456. },
  457. _changeMenuBindNode: function(oldBindNode, newBindNode){
  458. var hm = this.grid.headerMenu;
  459. if(hm && this._contextMenuBindNode == oldBindNode){
  460. hm.unBindDomNode(oldBindNode);
  461. hm.bindDomNode(newBindNode);
  462. this._contextMenuBindNode = newBindNode;
  463. }
  464. },
  465. //---------------Header Area------------------------------------------
  466. focusHeader: function(evt, step){ //need a further look why these changes to parent's
  467. // summary:
  468. // Overwritten
  469. var didFocus = false;
  470. this.inherited(arguments);
  471. if(this._colHeadNode && dojo.style(this._colHeadNode, 'display') != "none"){
  472. dijit.focus(this._colHeadNode);
  473. this._stopEvent(evt);
  474. didFocus = true;
  475. }
  476. return didFocus;
  477. },
  478. _blurHeader: function(evt,step){
  479. // summary:
  480. // Overwritten
  481. if(this._colHeadNode){
  482. dojo.removeClass(this._colHeadNode, this.focusClass);
  483. }
  484. dojo.removeAttr(this.grid.domNode,"aria-activedescendant");
  485. // reset contextMenu onto viewsHeaderNode so right mouse on header will invoke (see focusHeader)
  486. this._changeMenuBindNode(this.grid.domNode,this.grid.viewsHeaderNode);
  487. //moved here from nextKey
  488. this._colHeadNode = this._colHeadFocusIdx = null;
  489. return true;
  490. },
  491. _navHeader: function(rowStep, colStep, evt){
  492. var colDir = colStep < 0 ? -1 : 1,
  493. savedIdx = dojo.indexOf(this._findHeaderCells(), this._colHeadNode);
  494. if(savedIdx >= 0 && (evt.shiftKey && evt.ctrlKey)){
  495. this.colSizeAdjust(evt, savedIdx, colDir * 5);
  496. return;
  497. }
  498. this.move(rowStep, colStep);
  499. },
  500. _onHeaderKeyDown: function(e, isBubble){
  501. if(isBubble){
  502. var dk = dojo.keys;
  503. switch(e.keyCode){
  504. case dk.ENTER:
  505. case dk.SPACE:
  506. var colIdx = this.getHeaderIndex();
  507. if(colIdx >= 0 && !this.grid.pluginMgr.isFixedCell(e.cell)/*TODO*/){
  508. this.grid.setSortIndex(colIdx, null, e);
  509. dojo.stopEvent(e);
  510. }
  511. break;
  512. }
  513. }
  514. return true;
  515. },
  516. _setActiveColHeader: function(){
  517. // summary:
  518. // Overwritten
  519. this.inherited(arguments);
  520. //EDG now will decorate event on header key events, if no focus, the cell will be wrong
  521. dijit.focus(this._colHeadNode);
  522. },
  523. //---------------Content Area------------------------------------------
  524. findAndFocusGridCell: function(){
  525. // summary:
  526. // Overwritten
  527. this._focusContent();
  528. },
  529. _focusContent: function(evt,step){
  530. var didFocus = true;
  531. var isEmpty = (this.grid.rowCount === 0); // If grid is empty this.grid.rowCount == 0
  532. if(this.isNoFocusCell() && !isEmpty){
  533. //skip all the hidden cells
  534. for(var i = 0, cell = this.grid.getCell(0); cell && cell.hidden; cell = this.grid.getCell(++i)){}
  535. this.setFocusIndex(0, cell ? i : 0);
  536. }else if(this.cell && !isEmpty){
  537. if(this.focusView && !this.focusView.rowNodes[this.rowIndex]){
  538. // if rowNode for current index is undefined (likely as a result of a sort and because of #7304)
  539. // scroll to that row
  540. this.grid.scrollToRow(this.rowIndex);
  541. this.focusGrid();
  542. }else{
  543. this.setFocusIndex(this.rowIndex, this.cell.index);
  544. }
  545. }else{
  546. didFocus = false;
  547. }
  548. if(didFocus){ this._stopEvent(evt); }
  549. return didFocus;
  550. },
  551. _blurContent: function(evt,step){
  552. this._focusifyCellNode(false);
  553. return true;
  554. },
  555. _navContent: function(rowStep, colStep, evt){
  556. if((this.rowIndex === 0 && rowStep < 0) || (this.rowIndex === this.grid.rowCount - 1 && rowStep > 0)){
  557. return;
  558. }
  559. this._colHeadNode = null;
  560. this.move(rowStep, colStep, evt);
  561. if(evt){
  562. dojo.stopEvent(evt);
  563. }
  564. },
  565. _onContentKeyDown: function(e, isBubble){
  566. if(isBubble){
  567. var dk = dojo.keys, s = this.grid.scroller;
  568. switch(e.keyCode){
  569. case dk.ENTER:
  570. case dk.SPACE:
  571. var g = this.grid;
  572. if(g.indirectSelection){ break; }
  573. g.selection.clickSelect(this.rowIndex, dojo.isCopyKey(e), e.shiftKey);
  574. g.onRowClick(e);
  575. dojo.stopEvent(e);
  576. break;
  577. case dk.PAGE_UP:
  578. if(this.rowIndex !== 0){
  579. if(this.rowIndex != s.firstVisibleRow + 1){
  580. this._navContent(s.firstVisibleRow - this.rowIndex, 0);
  581. }else{
  582. this.grid.setScrollTop(s.findScrollTop(this.rowIndex - 1));
  583. this._navContent(s.firstVisibleRow - s.lastVisibleRow + 1, 0);
  584. }
  585. dojo.stopEvent(e);
  586. }
  587. break;
  588. case dk.PAGE_DOWN:
  589. if(this.rowIndex + 1 != this.grid.rowCount){
  590. dojo.stopEvent(e);
  591. if(this.rowIndex != s.lastVisibleRow - 1){
  592. this._navContent(s.lastVisibleRow - this.rowIndex - 1, 0);
  593. }else{
  594. this.grid.setScrollTop(s.findScrollTop(this.rowIndex + 1));
  595. this._navContent(s.lastVisibleRow - s.firstVisibleRow - 1, 0);
  596. }
  597. dojo.stopEvent(e);
  598. }
  599. break;
  600. }
  601. }
  602. return true;
  603. },
  604. //------------------editable content area-------------------------
  605. _blurFromEditableCell: false,
  606. _isNavigating: false,
  607. _navElems: null,
  608. _focusEditableCell: function(evt,step){
  609. var didFocus = false;
  610. if(this._isNavigating){
  611. didFocus = true;
  612. }else if(this.grid.edit.isEditing() && this.cell){
  613. if(this._blurFromEditableCell || !this._blurEditableCell(evt, step)){
  614. this.setFocusIndex(this.rowIndex,this.cell.index);
  615. didFocus = true;
  616. }
  617. this._stopEvent(evt);
  618. }
  619. return didFocus;
  620. },
  621. _applyEditableCell: function(){
  622. try{
  623. this.grid.edit.apply();
  624. }catch(e){
  625. console.warn("_FocusManager._applyEditableCell() error:", e);
  626. }
  627. },
  628. _blurEditableCell: function(evt,step){
  629. this._blurFromEditableCell = false;
  630. if(this._isNavigating){
  631. var toBlur = true;
  632. if(evt){
  633. var elems = this._navElems;
  634. var firstElem = elems.lowest || elems.first;
  635. var lastElem = elems.last || elems.highest || firstElem;
  636. var target = dojo.isIE ? evt.srcElement : evt.target;
  637. toBlur = target == (step > 0 ? lastElem : firstElem);
  638. }
  639. if(toBlur){
  640. this._isNavigating = false;
  641. return "content";
  642. }
  643. return false;
  644. }else if(this.grid.edit.isEditing() && this.cell){
  645. if(!step || typeof step != "number"){ return false; }
  646. var dir = step > 0 ? 1 : -1;
  647. var cc = this.grid.layout.cellCount;
  648. for(var cell, col = this.cell.index + dir; col >= 0 && col < cc; col += dir){
  649. cell = this.grid.getCell(col);
  650. if(cell.editable){
  651. this.cell = cell;
  652. this._blurFromEditableCell = true;
  653. return false;
  654. }
  655. }
  656. if((this.rowIndex > 0 || dir == 1) && (this.rowIndex < this.grid.rowCount || dir == -1)){
  657. this.rowIndex += dir;
  658. //this.cell = this.grid.getCell(0); //There must be an editable cell, so this is not necessary.
  659. for(col = dir > 0 ? 0 : cc - 1; col >= 0 && col < cc; col += dir){
  660. cell = this.grid.getCell(col);
  661. if(cell.editable){
  662. this.cell = cell;
  663. break;
  664. }
  665. }
  666. this._applyEditableCell();
  667. return "content";
  668. }
  669. }
  670. return true;
  671. },
  672. _initNavigatableElems: function(){
  673. this._navElems = dijit._getTabNavigable(this.cell.getNode(this.rowIndex));
  674. },
  675. _onEditableCellKeyDown: function(e, isBubble){
  676. var dk = dojo.keys,
  677. g = this.grid,
  678. edit = g.edit,
  679. editApplied = false,
  680. toPropagate = true;
  681. switch(e.keyCode){
  682. case dk.ENTER:
  683. if(isBubble && edit.isEditing()){
  684. this._applyEditableCell();
  685. editApplied = true;
  686. dojo.stopEvent(e);
  687. }
  688. //intentional drop through
  689. case dk.SPACE:
  690. if(!isBubble && this._isNavigating){
  691. toPropagate = false;
  692. break;
  693. }
  694. if(isBubble){
  695. if(!this.cell.editable && this.cell.navigatable){
  696. this._initNavigatableElems();
  697. var toFocus = this._navElems.lowest || this._navElems.first;
  698. if(toFocus){
  699. this._isNavigating = true;
  700. dijit.focus(toFocus);
  701. dojo.stopEvent(e);
  702. this.currentArea("editableCell", true);
  703. break;
  704. }
  705. }
  706. if(!editApplied && !edit.isEditing() && !g.pluginMgr.isFixedCell(this.cell)){
  707. edit.setEditCell(this.cell, this.rowIndex);
  708. }
  709. if(editApplied){
  710. this.currentArea("content", true);
  711. }else if(this.cell.editable && g.canEdit()){
  712. this.currentArea("editableCell", true);
  713. }
  714. }
  715. break;
  716. case dk.PAGE_UP:
  717. case dk.PAGE_DOWN:
  718. if(!isBubble && edit.isEditing()){
  719. //prevent propagating to content area
  720. toPropagate = false;
  721. }
  722. break;
  723. case dk.ESCAPE:
  724. if(!isBubble){
  725. edit.cancel();
  726. this.currentArea("content", true);
  727. }
  728. }
  729. return toPropagate;
  730. },
  731. _onEditableCellMouseEvent: function(evt){
  732. if(evt.type == "click"){
  733. var cell = this.cell || evt.cell;
  734. if(cell && !cell.editable && cell.navigatable){
  735. this._initNavigatableElems();
  736. if(this._navElems.lowest || this._navElems.first){
  737. var target = dojo.isIE ? evt.srcElement : evt.target;
  738. if(target != cell.getNode(evt.rowIndex)){
  739. this._isNavigating = true;
  740. this.focusArea("editableCell", evt);
  741. dijit.focus(target);
  742. return false;
  743. }
  744. }
  745. }else if(this.grid.singleClickEdit){
  746. this.currentArea("editableCell");
  747. return false;
  748. }
  749. }
  750. return true;
  751. }
  752. });
  753. }