_FocusManager.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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._FocusManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.grid._FocusManager"] = true;
  8. dojo.provide("dojox.grid._FocusManager");
  9. dojo.require("dojox.grid.util");
  10. // focus management
  11. dojo.declare("dojox.grid._FocusManager", null, {
  12. // summary:
  13. // Controls grid cell focus. Owned by grid and used internally for focusing.
  14. // Note: grid cell actually receives keyboard input only when cell is being edited.
  15. constructor: function(inGrid){
  16. this.grid = inGrid;
  17. this.cell = null;
  18. this.rowIndex = -1;
  19. this._connects = [];
  20. this._headerConnects = [];
  21. this.headerMenu = this.grid.headerMenu;
  22. this._connects.push(dojo.connect(this.grid.domNode, "onfocus", this, "doFocus"));
  23. this._connects.push(dojo.connect(this.grid.domNode, "onblur", this, "doBlur"));
  24. this._connects.push(dojo.connect(this.grid.domNode, "mousedown", this, "_mouseDown"));
  25. this._connects.push(dojo.connect(this.grid.domNode, "mouseup", this, "_mouseUp"));
  26. this._connects.push(dojo.connect(this.grid.domNode, "oncontextmenu", this, "doContextMenu"));
  27. this._connects.push(dojo.connect(this.grid.lastFocusNode, "onfocus", this, "doLastNodeFocus"));
  28. this._connects.push(dojo.connect(this.grid.lastFocusNode, "onblur", this, "doLastNodeBlur"));
  29. this._connects.push(dojo.connect(this.grid,"_onFetchComplete", this, "_delayedCellFocus"));
  30. this._connects.push(dojo.connect(this.grid,"postrender", this, "_delayedHeaderFocus"));
  31. },
  32. destroy: function(){
  33. dojo.forEach(this._connects, dojo.disconnect);
  34. dojo.forEach(this._headerConnects, dojo.disconnect);
  35. delete this.grid;
  36. delete this.cell;
  37. },
  38. _colHeadNode: null,
  39. _colHeadFocusIdx: null,
  40. _contextMenuBindNode: null,
  41. tabbingOut: false,
  42. focusClass: "dojoxGridCellFocus",
  43. focusView: null,
  44. initFocusView: function(){
  45. this.focusView = this.grid.views.getFirstScrollingView() || this.focusView || this.grid.views.views[0];
  46. this._initColumnHeaders();
  47. },
  48. isFocusCell: function(inCell, inRowIndex){
  49. // summary:
  50. // states if the given cell is focused
  51. // inCell: object
  52. // grid cell object
  53. // inRowIndex: int
  54. // grid row index
  55. // returns:
  56. // true of the given grid cell is focused
  57. return (this.cell == inCell) && (this.rowIndex == inRowIndex);
  58. },
  59. isLastFocusCell: function(){
  60. if(this.cell){
  61. return (this.rowIndex == this.grid.rowCount-1) && (this.cell.index == this.grid.layout.cellCount-1);
  62. }
  63. return false;
  64. },
  65. isFirstFocusCell: function(){
  66. if(this.cell){
  67. return (this.rowIndex === 0) && (this.cell.index === 0);
  68. }
  69. return false;
  70. },
  71. isNoFocusCell: function(){
  72. return (this.rowIndex < 0) || !this.cell;
  73. },
  74. isNavHeader: function(){
  75. // summary:
  76. // states whether currently navigating among column headers.
  77. // returns:
  78. // true if focus is on a column header; false otherwise.
  79. return (!!this._colHeadNode);
  80. },
  81. getHeaderIndex: function(){
  82. // summary:
  83. // if one of the column headers currently has focus, return its index.
  84. // returns:
  85. // index of the focused column header, or -1 if none have focus.
  86. if(this._colHeadNode){
  87. return dojo.indexOf(this._findHeaderCells(), this._colHeadNode);
  88. }else{
  89. return -1;
  90. }
  91. },
  92. _focusifyCellNode: function(inBork){
  93. var n = this.cell && this.cell.getNode(this.rowIndex);
  94. if(n){
  95. dojo.toggleClass(n, this.focusClass, inBork);
  96. if(inBork){
  97. var sl = this.scrollIntoView();
  98. try{
  99. if(!this.grid.edit.isEditing()){
  100. dojox.grid.util.fire(n, "focus");
  101. if(sl){ this.cell.view.scrollboxNode.scrollLeft = sl; }
  102. }
  103. }catch(e){}
  104. }
  105. }
  106. },
  107. _delayedCellFocus: function(){
  108. if(this.isNavHeader()||!this.grid._focused){
  109. return;
  110. }
  111. var n = this.cell && this.cell.getNode(this.rowIndex);
  112. if(n){
  113. try{
  114. if(!this.grid.edit.isEditing()){
  115. dojo.toggleClass(n, this.focusClass, true);
  116. if(this._colHeadNode){
  117. this.blurHeader();
  118. }
  119. dojox.grid.util.fire(n, "focus");
  120. }
  121. }
  122. catch(e){}
  123. }
  124. },
  125. _delayedHeaderFocus: function(){
  126. if(this.isNavHeader()){
  127. this.focusHeader();
  128. this.grid.domNode.focus();
  129. }
  130. },
  131. _initColumnHeaders: function(){
  132. dojo.forEach(this._headerConnects, dojo.disconnect);
  133. this._headerConnects = [];
  134. var headers = this._findHeaderCells();
  135. for(var i = 0; i < headers.length; i++){
  136. this._headerConnects.push(dojo.connect(headers[i], "onfocus", this, "doColHeaderFocus"));
  137. this._headerConnects.push(dojo.connect(headers[i], "onblur", this, "doColHeaderBlur"));
  138. }
  139. },
  140. _findHeaderCells: function(){
  141. // This should be a one liner:
  142. // dojo.query("th[tabindex=-1]", this.grid.viewsHeaderNode);
  143. // But there is a bug in dojo.query() for IE -- see trac #7037.
  144. var allHeads = dojo.query("th", this.grid.viewsHeaderNode);
  145. var headers = [];
  146. for (var i = 0; i < allHeads.length; i++){
  147. var aHead = allHeads[i];
  148. var hasTabIdx = dojo.hasAttr(aHead, "tabIndex");
  149. var tabindex = dojo.attr(aHead, "tabIndex");
  150. if (hasTabIdx && tabindex < 0) {
  151. headers.push(aHead);
  152. }
  153. }
  154. return headers;
  155. },
  156. _setActiveColHeader: function(/*Node*/colHeaderNode, /*Integer*/colFocusIdx, /*Integer*/ prevColFocusIdx){
  157. //console.log("setActiveColHeader() - colHeaderNode:colFocusIdx:prevColFocusIdx = " + colHeaderNode + ":" + colFocusIdx + ":" + prevColFocusIdx);
  158. dojo.attr(this.grid.domNode, "aria-activedescendant",colHeaderNode.id);
  159. if (prevColFocusIdx != null && prevColFocusIdx >= 0 && prevColFocusIdx != colFocusIdx){
  160. dojo.toggleClass(this._findHeaderCells()[prevColFocusIdx],this.focusClass,false);
  161. }
  162. dojo.toggleClass(colHeaderNode,this.focusClass, true);
  163. this._colHeadNode = colHeaderNode;
  164. this._colHeadFocusIdx = colFocusIdx;
  165. this._scrollHeader(this._colHeadFocusIdx);
  166. },
  167. scrollIntoView: function(){
  168. var info = (this.cell ? this._scrollInfo(this.cell) : null);
  169. if(!info || !info.s){
  170. return null;
  171. }
  172. var rt = this.grid.scroller.findScrollTop(this.rowIndex);
  173. // place cell within horizontal view
  174. if(info.n && info.sr){
  175. if(info.n.offsetLeft + info.n.offsetWidth > info.sr.l + info.sr.w){
  176. info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w;
  177. }else if(info.n.offsetLeft < info.sr.l){
  178. info.s.scrollLeft = info.n.offsetLeft;
  179. }
  180. }
  181. // place cell within vertical view
  182. if(info.r && info.sr){
  183. if(rt + info.r.offsetHeight > info.sr.t + info.sr.h){
  184. this.grid.setScrollTop(rt + info.r.offsetHeight - info.sr.h);
  185. }else if(rt < info.sr.t){
  186. this.grid.setScrollTop(rt);
  187. }
  188. }
  189. return info.s.scrollLeft;
  190. },
  191. _scrollInfo: function(cell, domNode){
  192. if(cell){
  193. var cl = cell,
  194. sbn = cl.view.scrollboxNode,
  195. sbnr = {
  196. w: sbn.clientWidth,
  197. l: sbn.scrollLeft,
  198. t: sbn.scrollTop,
  199. h: sbn.clientHeight
  200. },
  201. rn = cl.view.getRowNode(this.rowIndex);
  202. return {
  203. c: cl,
  204. s: sbn,
  205. sr: sbnr,
  206. n: (domNode ? domNode : cell.getNode(this.rowIndex)),
  207. r: rn
  208. };
  209. }
  210. return null;
  211. },
  212. _scrollHeader: function(currentIdx){
  213. var info = null;
  214. if(this._colHeadNode){
  215. var cell = this.grid.getCell(currentIdx);
  216. if(!cell){ return; }
  217. info = this._scrollInfo(cell, cell.getNode(0));
  218. }
  219. if(info && info.s && info.sr && info.n){
  220. // scroll horizontally as needed.
  221. var scroll = info.sr.l + info.sr.w;
  222. if(info.n.offsetLeft + info.n.offsetWidth > scroll){
  223. info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w;
  224. }else if(info.n.offsetLeft < info.sr.l){
  225. info.s.scrollLeft = info.n.offsetLeft;
  226. }else if(dojo.isIE <= 7 && cell && cell.view.headerNode){
  227. // Trac 7158: scroll dojoxGridHeader for IE7 and lower
  228. cell.view.headerNode.scrollLeft = info.s.scrollLeft;
  229. }
  230. }
  231. },
  232. _isHeaderHidden: function(){
  233. // summary:
  234. // determine if the grid headers are hidden
  235. // relies on documented technique of setting .dojoxGridHeader { display:none; }
  236. // returns: Boolean
  237. // true if headers are hidden
  238. // false if headers are not hidden
  239. var curView = this.focusView;
  240. if (!curView){
  241. // find one so we can determine if headers are hidden
  242. // there is no focusView after adding items to empty grid (test_data_grid_empty.html)
  243. for (var i = 0, cView; (cView = this.grid.views.views[i]); i++) {
  244. if(cView.headerNode ){
  245. curView=cView;
  246. break;
  247. }
  248. }
  249. }
  250. return (curView && dojo.getComputedStyle(curView.headerNode).display == "none");
  251. },
  252. colSizeAdjust: function (e, colIdx, delta){ // adjust the column specified by colIdx by the specified delta px
  253. var headers = this._findHeaderCells();
  254. var view = this.focusView;
  255. if (!view) {
  256. for (var i = 0, cView; (cView = this.grid.views.views[i]); i++) {
  257. // find first view with a tableMap in order to work with empty grid
  258. if(cView.header.tableMap.map ){
  259. view=cView;
  260. break;
  261. }
  262. }
  263. }
  264. var curHeader = headers[colIdx];
  265. if (!view || (colIdx == headers.length-1 && colIdx === 0)){
  266. return; // can't adjust single col. grid
  267. }
  268. view.content.baseDecorateEvent(e);
  269. // need to adjust event with header cell info since focus is no longer on header cell
  270. e.cellNode = curHeader; //this.findCellTarget(e.target, e.rowNode);
  271. e.cellIndex = view.content.getCellNodeIndex(e.cellNode);
  272. e.cell = (e.cellIndex >= 0 ? this.grid.getCell(e.cellIndex) : null);
  273. if (view.header.canResize(e)){
  274. var deltaObj = {
  275. l: delta
  276. };
  277. var drag = view.header.colResizeSetup(e,false);
  278. view.header.doResizeColumn(drag, null, deltaObj);
  279. view.update();
  280. }
  281. },
  282. styleRow: function(inRow){
  283. return;
  284. },
  285. setFocusIndex: function(inRowIndex, inCellIndex){
  286. // summary:
  287. // focuses the given grid cell
  288. // inRowIndex: int
  289. // grid row index
  290. // inCellIndex: int
  291. // grid cell index
  292. this.setFocusCell(this.grid.getCell(inCellIndex), inRowIndex);
  293. },
  294. setFocusCell: function(inCell, inRowIndex){
  295. // summary:
  296. // focuses the given grid cell
  297. // inCell: object
  298. // grid cell object
  299. // inRowIndex: int
  300. // grid row index
  301. if(inCell && !this.isFocusCell(inCell, inRowIndex)){
  302. this.tabbingOut = false;
  303. if (this._colHeadNode){
  304. this.blurHeader();
  305. }
  306. this._colHeadNode = this._colHeadFocusIdx = null;
  307. this.focusGridView();
  308. this._focusifyCellNode(false);
  309. this.cell = inCell;
  310. this.rowIndex = inRowIndex;
  311. this._focusifyCellNode(true);
  312. }
  313. // even if this cell isFocusCell, the document focus may need to be rejiggered
  314. // call opera on delay to prevent keypress from altering focus
  315. if(dojo.isOpera){
  316. setTimeout(dojo.hitch(this.grid, 'onCellFocus', this.cell, this.rowIndex), 1);
  317. }else{
  318. this.grid.onCellFocus(this.cell, this.rowIndex);
  319. }
  320. },
  321. next: function(){
  322. // summary:
  323. // focus next grid cell
  324. if(this.cell){
  325. var row=this.rowIndex, col=this.cell.index+1, cc=this.grid.layout.cellCount-1, rc=this.grid.rowCount-1;
  326. if(col > cc){
  327. col = 0;
  328. row++;
  329. }
  330. if(row > rc){
  331. col = cc;
  332. row = rc;
  333. }
  334. if(this.grid.edit.isEditing()){ //when editing, only navigate to editable cells
  335. var nextCell = this.grid.getCell(col);
  336. if (!this.isLastFocusCell() && (!nextCell.editable ||
  337. this.grid.canEdit && !this.grid.canEdit(nextCell, row))){
  338. this.cell=nextCell;
  339. this.rowIndex=row;
  340. this.next();
  341. return;
  342. }
  343. }
  344. this.setFocusIndex(row, col);
  345. }
  346. },
  347. previous: function(){
  348. // summary:
  349. // focus previous grid cell
  350. if(this.cell){
  351. var row=(this.rowIndex || 0), col=(this.cell.index || 0) - 1;
  352. if(col < 0){
  353. col = this.grid.layout.cellCount-1;
  354. row--;
  355. }
  356. if(row < 0){
  357. row = 0;
  358. col = 0;
  359. }
  360. if(this.grid.edit.isEditing()){ //when editing, only navigate to editable cells
  361. var prevCell = this.grid.getCell(col);
  362. if (!this.isFirstFocusCell() && !prevCell.editable){
  363. this.cell=prevCell;
  364. this.rowIndex=row;
  365. this.previous();
  366. return;
  367. }
  368. }
  369. this.setFocusIndex(row, col);
  370. }
  371. },
  372. move: function(inRowDelta, inColDelta) {
  373. // summary:
  374. // focus grid cell or simulate focus to column header based on position relative to current focus
  375. // inRowDelta: int
  376. // vertical distance from current focus
  377. // inColDelta: int
  378. // horizontal distance from current focus
  379. var colDir = inColDelta < 0 ? -1 : 1;
  380. // Handle column headers.
  381. if(this.isNavHeader()){
  382. var headers = this._findHeaderCells();
  383. var savedIdx = currentIdx = dojo.indexOf(headers, this._colHeadNode);
  384. currentIdx += inColDelta;
  385. while(currentIdx >=0 && currentIdx < headers.length && headers[currentIdx].style.display == "none"){
  386. // skip over hidden column headers
  387. currentIdx += colDir;
  388. }
  389. if((currentIdx >= 0) && (currentIdx < headers.length)){
  390. this._setActiveColHeader(headers[currentIdx],currentIdx, savedIdx);
  391. }
  392. }else{
  393. if(this.cell){
  394. // Handle grid proper.
  395. var sc = this.grid.scroller,
  396. r = this.rowIndex,
  397. rc = this.grid.rowCount-1,
  398. row = Math.min(rc, Math.max(0, r+inRowDelta));
  399. if(inRowDelta){
  400. if(inRowDelta>0){
  401. if(row > sc.getLastPageRow(sc.page)){
  402. //need to load additional data, let scroller do that
  403. this.grid.setScrollTop(this.grid.scrollTop+sc.findScrollTop(row)-sc.findScrollTop(r));
  404. }
  405. }else if(inRowDelta<0){
  406. if(row <= sc.getPageRow(sc.page)){
  407. //need to load additional data, let scroller do that
  408. this.grid.setScrollTop(this.grid.scrollTop-sc.findScrollTop(r)-sc.findScrollTop(row));
  409. }
  410. }
  411. }
  412. var cc = this.grid.layout.cellCount-1,
  413. i = this.cell.index,
  414. col = Math.min(cc, Math.max(0, i+inColDelta));
  415. var cell = this.grid.getCell(col);
  416. while(col>=0 && col < cc && cell && cell.hidden === true){
  417. // skip hidden cells
  418. col += colDir;
  419. cell = this.grid.getCell(col);
  420. }
  421. if (!cell || cell.hidden === true){
  422. // don't change col if would move to hidden
  423. col = i;
  424. }
  425. //skip hidden row|cell
  426. var n = cell.getNode(row);
  427. if(!n && inRowDelta){
  428. if((row + inRowDelta) >= 0 && (row + inRowDelta) <= rc){
  429. this.move(inRowDelta > 0 ? ++inRowDelta : --inRowDelta, inColDelta);
  430. }
  431. return;
  432. }else if((!n || dojo.style(n, "display") === "none") && inColDelta){
  433. if((col + inColDelta) >= 0 && (col + inColDelta) <= cc){
  434. this.move(inRowDelta, inColDelta > 0 ? ++inColDelta : --inColDelta);
  435. }
  436. return;
  437. }
  438. this.setFocusIndex(row, col);
  439. if(inRowDelta){
  440. this.grid.updateRow(r);
  441. }
  442. }
  443. }
  444. },
  445. previousKey: function(e){
  446. if(this.grid.edit.isEditing()){
  447. dojo.stopEvent(e);
  448. this.previous();
  449. }else if(!this.isNavHeader() && !this._isHeaderHidden()) {
  450. this.grid.domNode.focus(); // will call doFocus and set focus into header.
  451. dojo.stopEvent(e);
  452. }else{
  453. this.tabOut(this.grid.domNode);
  454. if (this._colHeadFocusIdx != null) { // clear grid header focus
  455. dojo.toggleClass(this._findHeaderCells()[this._colHeadFocusIdx], this.focusClass, false);
  456. this._colHeadFocusIdx = null;
  457. }
  458. this._focusifyCellNode(false);
  459. }
  460. },
  461. nextKey: function(e) {
  462. var isEmpty = (this.grid.rowCount === 0);
  463. if(e.target === this.grid.domNode && this._colHeadFocusIdx == null){
  464. this.focusHeader();
  465. dojo.stopEvent(e);
  466. }else if(this.isNavHeader()){
  467. // if tabbing from col header, then go to grid proper.
  468. this.blurHeader();
  469. if(!this.findAndFocusGridCell()){
  470. this.tabOut(this.grid.lastFocusNode);
  471. }
  472. this._colHeadNode = this._colHeadFocusIdx= null;
  473. }else if(this.grid.edit.isEditing()){
  474. dojo.stopEvent(e);
  475. this.next();
  476. }else{
  477. this.tabOut(this.grid.lastFocusNode);
  478. }
  479. },
  480. tabOut: function(inFocusNode){
  481. this.tabbingOut = true;
  482. inFocusNode.focus();
  483. },
  484. focusGridView: function(){
  485. dojox.grid.util.fire(this.focusView, "focus");
  486. },
  487. focusGrid: function(inSkipFocusCell){
  488. this.focusGridView();
  489. this._focusifyCellNode(true);
  490. },
  491. findAndFocusGridCell: function(){
  492. // summary:
  493. // find the first focusable grid cell
  494. // returns: Boolean
  495. // true if focus was set to a cell
  496. // false if no cell found to set focus onto
  497. var didFocus = true;
  498. var isEmpty = (this.grid.rowCount === 0); // If grid is empty this.grid.rowCount == 0
  499. if (this.isNoFocusCell() && !isEmpty){
  500. var cellIdx = 0;
  501. var cell = this.grid.getCell(cellIdx);
  502. if (cell.hidden) {
  503. // if first cell isn't visible, use _colHeadFocusIdx
  504. // could also use a while loop to find first visible cell - not sure that is worth it
  505. cellIdx = this.isNavHeader() ? this._colHeadFocusIdx : 0;
  506. }
  507. this.setFocusIndex(0, cellIdx);
  508. }
  509. else if (this.cell && !isEmpty){
  510. if (this.focusView && !this.focusView.rowNodes[this.rowIndex]){
  511. // if rowNode for current index is undefined (likely as a result of a sort and because of #7304)
  512. // scroll to that row
  513. this.grid.scrollToRow(this.rowIndex);
  514. }
  515. this.focusGrid();
  516. }else {
  517. didFocus = false;
  518. }
  519. this._colHeadNode = this._colHeadFocusIdx= null;
  520. return didFocus;
  521. },
  522. focusHeader: function(){
  523. var headerNodes = this._findHeaderCells();
  524. var saveColHeadFocusIdx = this._colHeadFocusIdx;
  525. if (this._isHeaderHidden()){
  526. // grid header is hidden, focus a cell
  527. this.findAndFocusGridCell();
  528. }
  529. else if (!this._colHeadFocusIdx) {
  530. if (this.isNoFocusCell()) {
  531. this._colHeadFocusIdx = 0;
  532. }
  533. else {
  534. this._colHeadFocusIdx = this.cell.index;
  535. }
  536. }
  537. this._colHeadNode = headerNodes[this._colHeadFocusIdx];
  538. while(this._colHeadNode && this._colHeadFocusIdx >=0 && this._colHeadFocusIdx < headerNodes.length &&
  539. this._colHeadNode.style.display == "none"){
  540. // skip over hidden column headers
  541. this._colHeadFocusIdx++;
  542. this._colHeadNode = headerNodes[this._colHeadFocusIdx];
  543. }
  544. if(this._colHeadNode && this._colHeadNode.style.display != "none"){
  545. // Column header cells know longer receive actual focus. So, for keyboard invocation of
  546. // contextMenu to work, the contextMenu must be bound to the grid.domNode rather than the viewsHeaderNode.
  547. // unbind the contextmenu from the viewsHeaderNode and to the grid when header cells are active. Reset
  548. // the binding back to the viewsHeaderNode when header cells are no longer acive (in blurHeader) #10483
  549. if (this.headerMenu && this._contextMenuBindNode != this.grid.domNode){
  550. this.headerMenu.unBindDomNode(this.grid.viewsHeaderNode);
  551. this.headerMenu.bindDomNode(this.grid.domNode);
  552. this._contextMenuBindNode = this.grid.domNode;
  553. }
  554. this._setActiveColHeader(this._colHeadNode, this._colHeadFocusIdx, saveColHeadFocusIdx);
  555. this._scrollHeader(this._colHeadFocusIdx);
  556. this._focusifyCellNode(false);
  557. }else {
  558. // all col head nodes are hidden - focus the grid
  559. this.findAndFocusGridCell();
  560. }
  561. },
  562. blurHeader: function(){
  563. dojo.removeClass(this._colHeadNode, this.focusClass);
  564. dojo.removeAttr(this.grid.domNode,"aria-activedescendant");
  565. // reset contextMenu onto viewsHeaderNode so right mouse on header will invoke (see focusHeader)
  566. if (this.headerMenu && this._contextMenuBindNode == this.grid.domNode) {
  567. var viewsHeader = this.grid.viewsHeaderNode;
  568. this.headerMenu.unBindDomNode(this.grid.domNode);
  569. this.headerMenu.bindDomNode(viewsHeader);
  570. this._contextMenuBindNode = viewsHeader;
  571. }
  572. },
  573. doFocus: function(e){
  574. // trap focus only for grid dom node
  575. if(e && e.target != e.currentTarget){
  576. dojo.stopEvent(e);
  577. return;
  578. }
  579. // don't change focus if clicking on scroller bar
  580. if(this._clickFocus){
  581. return;
  582. }
  583. // do not focus for scrolling if grid is about to blur
  584. if(!this.tabbingOut){
  585. this.focusHeader();
  586. }
  587. this.tabbingOut = false;
  588. dojo.stopEvent(e);
  589. },
  590. doBlur: function(e){
  591. dojo.stopEvent(e); // FF2
  592. },
  593. doContextMenu: function(e){
  594. //stop contextMenu event if no header Menu to prevent default/browser contextMenu
  595. if (!this.headerMenu){
  596. dojo.stopEvent(e);
  597. }
  598. },
  599. doLastNodeFocus: function(e){
  600. if (this.tabbingOut){
  601. this._focusifyCellNode(false);
  602. }else if(this.grid.rowCount >0){
  603. if (this.isNoFocusCell()){
  604. this.setFocusIndex(0,0);
  605. }
  606. this._focusifyCellNode(true);
  607. }else {
  608. this.focusHeader();
  609. }
  610. this.tabbingOut = false;
  611. dojo.stopEvent(e); // FF2
  612. },
  613. doLastNodeBlur: function(e){
  614. dojo.stopEvent(e); // FF2
  615. },
  616. doColHeaderFocus: function(e){
  617. this._setActiveColHeader(e.target,dojo.attr(e.target, "idx"),this._colHeadFocusIdx);
  618. this._scrollHeader(this.getHeaderIndex());
  619. dojo.stopEvent(e);
  620. },
  621. doColHeaderBlur: function(e){
  622. dojo.toggleClass(e.target, this.focusClass, false);
  623. },
  624. _mouseDown: function(e){
  625. // a flag indicating grid is being focused by clicking
  626. this._clickFocus = dojo.some(this.grid.views.views, function(v){
  627. return v.scrollboxNode === e.target;
  628. });
  629. },
  630. _mouseUp: function(e){
  631. this._clickFocus = false;
  632. }
  633. });
  634. }