FocusManager.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-FocusManager'>/**
  19. </span> * The FocusManager is responsible for globally:
  20. *
  21. * 1. Managing component focus
  22. * 2. Providing basic keyboard navigation
  23. * 3. (optional) Provide a visual cue for focused components, in the form of a focus ring/frame.
  24. *
  25. * To activate the FocusManager, simply call `Ext.FocusManager.enable();`. In turn, you may
  26. * deactivate the FocusManager by subsequently calling `Ext.FocusManager.disable();`. The
  27. * FocusManager is disabled by default.
  28. *
  29. * To enable the optional focus frame, pass `true` or `{focusFrame: true}` to {@link #method-enable}.
  30. *
  31. * Another feature of the FocusManager is to provide basic keyboard focus navigation scoped to any {@link Ext.container.Container}
  32. * that would like to have navigation between its child {@link Ext.Component}'s.
  33. *
  34. * @author Jarred Nicholls &lt;jarred@sencha.com&gt;
  35. * @docauthor Jarred Nicholls &lt;jarred@sencha.com&gt;
  36. */
  37. Ext.define('Ext.FocusManager', {
  38. singleton: true,
  39. alternateClassName: ['Ext.FocusMgr' ],
  40. mixins: {
  41. observable: 'Ext.util.Observable'
  42. },
  43. requires: [
  44. 'Ext.AbstractComponent',
  45. 'Ext.Component',
  46. 'Ext.ComponentManager',
  47. 'Ext.ComponentQuery',
  48. 'Ext.util.HashMap',
  49. 'Ext.util.KeyNav'
  50. ],
  51. <span id='Ext-FocusManager-property-enabled'> /**
  52. </span> * @property {Boolean} enabled
  53. * Whether or not the FocusManager is currently enabled
  54. */
  55. enabled: false,
  56. <span id='Ext-FocusManager-property-focusedCmp'> /**
  57. </span> * @property {Ext.Component} focusedCmp
  58. * The currently focused component.
  59. */
  60. focusElementCls: Ext.baseCSSPrefix + 'focus-element',
  61. focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',
  62. <span id='Ext-FocusManager-property-whitelist'> /**
  63. </span> * @property {String[]} whitelist
  64. * A list of xtypes that should ignore certain navigation input keys and
  65. * allow for the default browser event/behavior. These input keys include:
  66. *
  67. * 1. Backspace
  68. * 2. Delete
  69. * 3. Left
  70. * 4. Right
  71. * 5. Up
  72. * 6. Down
  73. *
  74. * The FocusManager will not attempt to navigate when a component is an xtype (or descendents thereof)
  75. * that belongs to this whitelist. E.g., an {@link Ext.form.field.Text} should allow
  76. * the user to move the input cursor left and right, and to delete characters, etc.
  77. */
  78. whitelist: [
  79. 'textfield'
  80. ],
  81. constructor: function(config) {
  82. var me = this,
  83. CQ = Ext.ComponentQuery;
  84. me.mixins.observable.constructor.call(me, config);
  85. me.addEvents(
  86. <span id='Ext-FocusManager-event-beforecomponentfocus'> /**
  87. </span> * @event beforecomponentfocus
  88. * Fires before a component becomes focused. Return `false` to prevent
  89. * the component from gaining focus.
  90. * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
  91. * @param {Ext.Component} cmp The component that is being focused
  92. * @param {Ext.Component} previousCmp The component that was previously focused,
  93. * or `undefined` if there was no previously focused component.
  94. */
  95. 'beforecomponentfocus',
  96. <span id='Ext-FocusManager-event-componentfocus'> /**
  97. </span> * @event componentfocus
  98. * Fires after a component becomes focused.
  99. * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
  100. * @param {Ext.Component} cmp The component that has been focused
  101. * @param {Ext.Component} previousCmp The component that was previously focused,
  102. * or `undefined` if there was no previously focused component.
  103. */
  104. 'componentfocus',
  105. <span id='Ext-FocusManager-event-disable'> /**
  106. </span> * @event disable
  107. * Fires when the FocusManager is disabled
  108. * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
  109. */
  110. 'disable',
  111. <span id='Ext-FocusManager-event-enable'> /**
  112. </span> * @event enable
  113. * Fires when the FocusManager is enabled
  114. * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
  115. */
  116. 'enable'
  117. );
  118. me.focusTask = new Ext.util.DelayedTask(me.handleComponentFocus, me);
  119. // Gain control on Component focus, blur, hide and destroy
  120. Ext.override(Ext.AbstractComponent, {
  121. onFocus: function() {
  122. this.callParent(arguments);
  123. if (me.enabled &amp;&amp; this.hasFocus) {
  124. Array.prototype.unshift.call(arguments, this);
  125. me.onComponentFocus.apply(me, arguments);
  126. }
  127. },
  128. onBlur: function() {
  129. this.callParent(arguments);
  130. if (me.enabled &amp;&amp; !this.hasFocus) {
  131. Array.prototype.unshift.call(arguments, this);
  132. me.onComponentBlur.apply(me, arguments);
  133. }
  134. },
  135. onDestroy: function() {
  136. this.callParent(arguments);
  137. if (me.enabled) {
  138. Array.prototype.unshift.call(arguments, this);
  139. me.onComponentDestroy.apply(me, arguments);
  140. }
  141. }
  142. });
  143. Ext.override(Ext.Component, {
  144. afterHide: function() {
  145. this.callParent(arguments);
  146. if (me.enabled) {
  147. Array.prototype.unshift.call(arguments, this);
  148. me.onComponentHide.apply(me, arguments);
  149. }
  150. }
  151. });
  152. // Setup KeyNav that's bound to document to catch all
  153. // unhandled/bubbled key events for navigation
  154. me.keyNav = new Ext.util.KeyNav(Ext.getDoc(), {
  155. disabled: true,
  156. scope: me,
  157. backspace: me.focusLast,
  158. enter: me.navigateIn,
  159. esc: me.navigateOut,
  160. tab: me.navigateSiblings,
  161. space: me.navigateIn,
  162. del: me.focusLast,
  163. left: me.navigateSiblings,
  164. right: me.navigateSiblings,
  165. down: me.navigateSiblings,
  166. up: me.navigateSiblings
  167. });
  168. me.focusData = {};
  169. me.subscribers = new Ext.util.HashMap();
  170. me.focusChain = {};
  171. // Setup some ComponentQuery pseudos
  172. Ext.apply(CQ.pseudos, {
  173. focusable: function(cmps) {
  174. var len = cmps.length,
  175. results = [],
  176. i = 0,
  177. c;
  178. for (; i &lt; len; i++) {
  179. c = cmps[i];
  180. if (c.isFocusable()) {
  181. results.push(c);
  182. }
  183. }
  184. return results;
  185. },
  186. // Return the single next focusable sibling from the current idx in either direction (step -1 or 1)
  187. nextFocus: function(cmps, idx, step) {
  188. step = step || 1;
  189. idx = parseInt(idx, 10);
  190. var len = cmps.length,
  191. i = idx, c;
  192. for (;;) {
  193. // Increment index, and loop round if off either end
  194. if ((i += step) &gt;= len) {
  195. i = 0;
  196. } else if (i &lt; 0) {
  197. i = len - 1;
  198. }
  199. // As soon as we loop back to the starting index, give up, there are no focusable siblings.
  200. if (i === idx) {
  201. return [];
  202. }
  203. // If we have found a focusable sibling, return it
  204. if ((c = cmps[i]).isFocusable()) {
  205. return [c];
  206. }
  207. }
  208. return [];
  209. },
  210. prevFocus: function(cmps, idx) {
  211. return this.nextFocus(cmps, idx, -1);
  212. },
  213. root: function(cmps) {
  214. var len = cmps.length,
  215. results = [],
  216. i = 0,
  217. c;
  218. for (; i &lt; len; i++) {
  219. c = cmps[i];
  220. if (!c.ownerCt) {
  221. results.push(c);
  222. }
  223. }
  224. return results;
  225. }
  226. });
  227. },
  228. <span id='Ext-FocusManager-method-addXTypeToWhitelist'> /**
  229. </span> * Adds the specified xtype to the {@link #whitelist}.
  230. * @param {String/String[]} xtype Adds the xtype(s) to the {@link #whitelist}.
  231. */
  232. addXTypeToWhitelist: function(xtype) {
  233. var me = this;
  234. if (Ext.isArray(xtype)) {
  235. Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
  236. return;
  237. }
  238. if (!Ext.Array.contains(me.whitelist, xtype)) {
  239. me.whitelist.push(xtype);
  240. }
  241. },
  242. clearComponent: function(cmp) {
  243. clearTimeout(this.cmpFocusDelay);
  244. if (!cmp.isDestroyed) {
  245. cmp.blur();
  246. }
  247. },
  248. <span id='Ext-FocusManager-method-disable'> /**
  249. </span> * Disables the FocusManager by turning of all automatic focus management and keyboard navigation
  250. */
  251. disable: function() {
  252. var me = this;
  253. if (!me.enabled) {
  254. return;
  255. }
  256. delete me.options;
  257. me.enabled = false;
  258. me.removeDOM();
  259. // Stop handling key navigation
  260. me.keyNav.disable();
  261. me.fireEvent('disable', me);
  262. },
  263. <span id='Ext-FocusManager-method-enable'> /**
  264. </span> * Enables the FocusManager by turning on all automatic focus management and keyboard navigation
  265. * @param {Boolean/Object} options Either `true`/`false` to turn on the focus frame, or an object
  266. * with the following options:
  267. * @param {Boolean} [focusFrame=false] `true` to show the focus frame around a component when it is focused.
  268. */
  269. enable: function(options) {
  270. var me = this;
  271. if (options === true) {
  272. options = { focusFrame: true };
  273. }
  274. me.options = options = options || {};
  275. if (me.enabled) {
  276. return;
  277. }
  278. // When calling addFocusListener on Containers, the FocusManager must be enabled, otherwise it won't do it.
  279. me.enabled = true;
  280. me.initDOM(options);
  281. // Start handling key navigation
  282. me.keyNav.enable();
  283. // Finally, let's focus our global focus el so we start fresh
  284. me.focusEl.focus();
  285. delete me.focusedCmp;
  286. me.fireEvent('enable', me);
  287. },
  288. focusLast: function(e) {
  289. var me = this;
  290. if (me.isWhitelisted(me.focusedCmp)) {
  291. return true;
  292. }
  293. // Go back to last focused item
  294. if (me.previousFocusedCmp) {
  295. me.previousFocusedCmp.focus();
  296. }
  297. },
  298. getRootComponents: function() {
  299. var me = this,
  300. CQ = Ext.ComponentQuery,
  301. inline = CQ.query(':focusable:root:not([floating])'),
  302. floating = CQ.query(':focusable:root[floating]');
  303. // Floating items should go to the top of our root stack, and be ordered
  304. // by their z-index (highest first)
  305. floating.sort(function(a, b) {
  306. return a.el.getZIndex() &gt; b.el.getZIndex();
  307. });
  308. return floating.concat(inline);
  309. },
  310. initDOM: function(options) {
  311. var me = this,
  312. cls = me.focusFrameCls,
  313. needListeners = Ext.ComponentQuery.query('{getFocusEl()}:not([focusListenerAdded])'),
  314. i = 0, len = needListeners.length;
  315. if (!Ext.isReady) {
  316. return Ext.onReady(me.initDOM, me);
  317. }
  318. // When we are enabled, we must ensure that all Components which return a focusEl that is *not naturally focusable*
  319. // have focus/blur listeners enabled to then trigger onFocus/onBlur handling so that we get to know about their focus action.
  320. // These listeners are not added at initialization unless the FocusManager is enabled at that time.
  321. for (; i &lt; len; i++) {
  322. needListeners[i].addFocusListener();
  323. }
  324. // Make the document body the global focus element
  325. if (!me.focusEl) {
  326. me.focusEl = Ext.getBody();
  327. me.focusEl.dom.tabIndex = -1;
  328. }
  329. // Create global focus frame
  330. if (!me.focusFrame &amp;&amp; options.focusFrame) {
  331. me.focusFrame = Ext.getBody().createChild({
  332. cls: cls,
  333. children: [
  334. { cls: cls + '-top' },
  335. { cls: cls + '-bottom' },
  336. { cls: cls + '-left' },
  337. { cls: cls + '-right' }
  338. ],
  339. style: 'top: -100px; left: -100px;'
  340. });
  341. me.focusFrame.setVisibilityMode(Ext.Element.DISPLAY);
  342. me.focusFrame.hide().setLeftTop(0, 0);
  343. }
  344. },
  345. isWhitelisted: function(cmp) {
  346. return cmp &amp;&amp; Ext.Array.some(this.whitelist, function(x) {
  347. return cmp.isXType(x);
  348. });
  349. },
  350. navigateIn: function(e) {
  351. var me = this,
  352. focusedCmp = me.focusedCmp,
  353. defaultRoot,
  354. firstChild;
  355. if (me.isWhitelisted(focusedCmp)) {
  356. return true;
  357. }
  358. if (!focusedCmp) {
  359. // No focus yet, so focus the first root cmp on the page
  360. defaultRoot = me.getRootComponents()[0];
  361. if (defaultRoot) {
  362. // If the default root is based upon the body, then it will already be focused, and will not fire a focus event to
  363. // trigger its own onFocus processing, so we have to programatically blur it first.
  364. if (defaultRoot.getFocusEl() === me.focusEl) {
  365. me.focusEl.blur();
  366. }
  367. defaultRoot.focus();
  368. }
  369. } else {
  370. // Drill into child ref items of the focused cmp, if applicable.
  371. // This works for any Component with a getRefItems implementation.
  372. firstChild = focusedCmp.hasFocus ? Ext.ComponentQuery.query('&gt;:focusable', focusedCmp)[0] : focusedCmp;
  373. if (firstChild) {
  374. firstChild.focus();
  375. } else {
  376. // Let's try to fire a click event, as if it came from the mouse
  377. if (Ext.isFunction(focusedCmp.onClick)) {
  378. e.button = 0;
  379. focusedCmp.onClick(e);
  380. if (focusedCmp.isVisible(true)) {
  381. focusedCmp.focus();
  382. } else {
  383. me.navigateOut();
  384. }
  385. }
  386. }
  387. }
  388. },
  389. navigateOut: function(e) {
  390. var me = this,
  391. parent;
  392. if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
  393. me.focusEl.focus();
  394. } else {
  395. parent.focus();
  396. }
  397. // In some browsers (Chrome) FocusManager can handle this before other
  398. // handlers. Ext Windows have their own Esc key handling, so we need to
  399. // return true here to allow the event to bubble.
  400. return true;
  401. },
  402. navigateSiblings: function(e, source, parent) {
  403. var me = this,
  404. src = source || me,
  405. key = e.getKey(),
  406. EO = Ext.EventObject,
  407. goBack = e.shiftKey || key == EO.LEFT || key == EO.UP,
  408. checkWhitelist = key == EO.LEFT || key == EO.RIGHT || key == EO.UP || key == EO.DOWN,
  409. nextSelector = goBack ? 'prev' : 'next',
  410. idx, next, focusedCmp, siblings;
  411. focusedCmp = (src.focusedCmp &amp;&amp; src.focusedCmp.comp) || src.focusedCmp;
  412. if (!focusedCmp &amp;&amp; !parent) {
  413. return true;
  414. }
  415. if (checkWhitelist &amp;&amp; me.isWhitelisted(focusedCmp)) {
  416. return true;
  417. }
  418. // If no focused Component, or a root level one was focused, then siblings are root components.
  419. if (!focusedCmp || focusedCmp.is(':root')) {
  420. siblings = me.getRootComponents();
  421. } else {
  422. // Else if the focused component has a parent, get siblings from there
  423. parent = parent || focusedCmp.up();
  424. if (parent) {
  425. siblings = parent.getRefItems();
  426. }
  427. }
  428. // Navigate if we have found siblings.
  429. if (siblings) {
  430. idx = focusedCmp ? Ext.Array.indexOf(siblings, focusedCmp) : -1;
  431. next = Ext.ComponentQuery.query(':' + nextSelector + 'Focus(' + idx + ')', siblings)[0];
  432. if (next &amp;&amp; focusedCmp !== next) {
  433. next.focus();
  434. return next;
  435. }
  436. }
  437. },
  438. onComponentBlur: function(cmp, e) {
  439. var me = this;
  440. if (me.focusedCmp === cmp) {
  441. me.previousFocusedCmp = cmp;
  442. delete me.focusedCmp;
  443. }
  444. if (me.focusFrame) {
  445. me.focusFrame.hide();
  446. }
  447. },
  448. onComponentFocus: function(cmp, e) {
  449. var me = this,
  450. chain = me.focusChain,
  451. parent;
  452. if (!cmp.isFocusable()) {
  453. me.clearComponent(cmp);
  454. // Check our focus chain, so we don't run into a never ending recursion
  455. // If we've attempted (unsuccessfully) to focus this component before,
  456. // then we're caught in a loop of child-&gt;parent-&gt;...-&gt;child and we
  457. // need to cut the loop off rather than feed into it.
  458. if (chain[cmp.id]) {
  459. return;
  460. }
  461. // Try to focus the parent instead
  462. parent = cmp.up();
  463. if (parent) {
  464. // Add component to our focus chain to detect infinite focus loop
  465. // before we fire off an attempt to focus our parent.
  466. // See the comments above.
  467. chain[cmp.id] = true;
  468. parent.focus();
  469. }
  470. return;
  471. }
  472. // Clear our focus chain when we have a focusable component
  473. me.focusChain = {};
  474. // Capture the focusEl to frame now.
  475. // Button returns its encapsulating element during the focus phase
  476. // So that element gets styled and framed.
  477. me.focusTask.delay(10, null, null, [cmp, cmp.getFocusEl()]);
  478. },
  479. handleComponentFocus: function(cmp, focusEl) {
  480. var me = this,
  481. cls,
  482. ff,
  483. fw,
  484. box,
  485. bt,
  486. bl,
  487. bw,
  488. bh,
  489. ft,
  490. fb,
  491. fl,
  492. fr;
  493. if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
  494. me.clearComponent(cmp);
  495. return;
  496. }
  497. me.focusedCmp = cmp;
  498. // If we have a focus frame, show it around the focused component
  499. if (me.shouldShowFocusFrame(cmp)) {
  500. cls = '.' + me.focusFrameCls + '-';
  501. ff = me.focusFrame;
  502. box = focusEl.getPageBox();
  503. // Size the focus frame's t/b/l/r according to the box
  504. // This leaves a hole in the middle of the frame so user
  505. // interaction w/ the mouse can continue
  506. bt = box.top;
  507. bl = box.left;
  508. bw = box.width;
  509. bh = box.height;
  510. ft = ff.child(cls + 'top');
  511. fb = ff.child(cls + 'bottom');
  512. fl = ff.child(cls + 'left');
  513. fr = ff.child(cls + 'right');
  514. ft.setWidth(bw).setLeftTop(bl, bt);
  515. fb.setWidth(bw).setLeftTop(bl, bt + bh - 2);
  516. fl.setHeight(bh - 2).setLeftTop(bl, bt + 2);
  517. fr.setHeight(bh - 2).setLeftTop(bl + bw - 2, bt + 2);
  518. ff.show();
  519. }
  520. me.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
  521. },
  522. onComponentHide: function(cmp) {
  523. var me = this,
  524. cmpHadFocus = false,
  525. focusedCmp = me.focusedCmp,
  526. parent;
  527. if (focusedCmp) {
  528. // See if the Component being hidden was the focused Component, or owns the focused Component
  529. // In these cases, focus needs to be removed from the focused Component to the nearest focusable ancestor
  530. cmpHadFocus = cmp.hasFocus || (cmp.isContainer &amp;&amp; cmp.isAncestor(me.focusedCmp));
  531. }
  532. me.clearComponent(cmp);
  533. // Move focus onto the nearest focusable ancestor, or this is there is none
  534. if (cmpHadFocus &amp;&amp; (parent = cmp.up(':focusable'))) {
  535. parent.focus();
  536. } else {
  537. me.focusEl.focus();
  538. }
  539. },
  540. onComponentDestroy: function() {
  541. },
  542. removeDOM: function() {
  543. var me = this;
  544. // If we are still enabled globally, or there are still subscribers
  545. // then we will halt here, since our DOM stuff is still being used
  546. if (me.enabled || me.subscribers.length) {
  547. return;
  548. }
  549. Ext.destroy(
  550. me.focusFrame
  551. );
  552. delete me.focusEl;
  553. delete me.focusFrame;
  554. },
  555. <span id='Ext-FocusManager-method-removeXTypeFromWhitelist'> /**
  556. </span> * Removes the specified xtype from the {@link #whitelist}.
  557. * @param {String/String[]} xtype Removes the xtype(s) from the {@link #whitelist}.
  558. */
  559. removeXTypeFromWhitelist: function(xtype) {
  560. var me = this;
  561. if (Ext.isArray(xtype)) {
  562. Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
  563. return;
  564. }
  565. Ext.Array.remove(me.whitelist, xtype);
  566. },
  567. setupSubscriberKeys: function(container, keys) {
  568. var me = this,
  569. el = container.getFocusEl(),
  570. scope = keys.scope,
  571. handlers = {
  572. backspace: me.focusLast,
  573. enter: me.navigateIn,
  574. esc: me.navigateOut,
  575. scope: me
  576. },
  577. navSiblings = function(e) {
  578. if (me.focusedCmp === container) {
  579. // Root the sibling navigation to this container, so that we
  580. // can automatically dive into the container, rather than forcing
  581. // the user to hit the enter key to dive in.
  582. return me.navigateSiblings(e, me, container);
  583. } else {
  584. return me.navigateSiblings(e);
  585. }
  586. };
  587. Ext.iterate(keys, function(key, cb) {
  588. handlers[key] = function(e) {
  589. var ret = navSiblings(e);
  590. if (Ext.isFunction(cb) &amp;&amp; cb.call(scope || container, e, ret) === true) {
  591. return true;
  592. }
  593. return ret;
  594. };
  595. }, me);
  596. return new Ext.util.KeyNav(el, handlers);
  597. },
  598. shouldShowFocusFrame: function(cmp) {
  599. var me = this,
  600. opts = me.options || {},
  601. cmpFocusEl = cmp.getFocusEl(),
  602. cmpFocusElTag = Ext.getDom(cmpFocusEl).tagName;
  603. // Do not show a focus frame if
  604. // 1. We are configured not to.
  605. // 2. No Component was passed
  606. if (!me.focusFrame || !cmp) {
  607. return false;
  608. }
  609. // Global trumps
  610. if (opts.focusFrame) {
  611. return true;
  612. }
  613. if (me.focusData[cmp.id].focusFrame) {
  614. return true;
  615. }
  616. return false;
  617. }
  618. });</pre>
  619. </body>
  620. </html>