Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / examples / ux / Focus.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 (function(){\r
8 Ext.ns('Ext.a11y');\r
9 \r
10 Ext.a11y.Frame = Ext.extend(Object, {\r
11     initialized: false,\r
12     \r
13     constructor: function(size, color){\r
14         this.setSize(size || 1);\r
15         this.setColor(color || '15428B');\r
16     },\r
17     \r
18     init: function(){\r
19         if (!this.initialized) {\r
20             this.sides = [];\r
21             \r
22             var s, i;\r
23             \r
24             this.ct = Ext.DomHelper.append(document.body, {\r
25                 cls: 'x-a11y-focusframe'\r
26             }, true);\r
27             \r
28             for (i = 0; i < 4; i++) {\r
29                 s = Ext.DomHelper.append(this.ct, {\r
30                     cls: 'x-a11y-focusframe-side',\r
31                     style: 'background-color: #' + this.color\r
32                 }, true);\r
33                 s.visibilityMode = Ext.Element.DISPLAY;\r
34                 this.sides.push(s);\r
35             }\r
36             \r
37             this.frameTask = new Ext.util.DelayedTask(function(el){\r
38                 var newEl = Ext.get(el);\r
39                 if (newEl != this.curEl) {\r
40                     var w = newEl.getWidth();\r
41                     var h = newEl.getHeight();\r
42                     this.sides[0].show().setSize(w, this.size).anchorTo(el, 'tl', [0, -1]);\r
43                     this.sides[2].show().setSize(w, this.size).anchorTo(el, 'bl', [0, -1]);\r
44                     this.sides[1].show().setSize(this.size, h).anchorTo(el, 'tr', [-1, 0]);\r
45                     this.sides[3].show().setSize(this.size, h).anchorTo(el, 'tl', [-1, 0]);\r
46                     this.curEl = newEl;\r
47                 }\r
48             }, this);\r
49             \r
50             this.unframeTask = new Ext.util.DelayedTask(function(){\r
51                 if (this.initialized) {\r
52                     this.sides[0].hide();\r
53                     this.sides[1].hide();\r
54                     this.sides[2].hide();\r
55                     this.sides[3].hide();\r
56                     this.curEl = null;\r
57                 }\r
58             }, this);\r
59             this.initialized = true;\r
60         }\r
61     },\r
62     \r
63     frame: function(el){\r
64         this.init();\r
65         this.unframeTask.cancel();\r
66         this.frameTask.delay(2, false, false, [el]);\r
67     },\r
68     \r
69     unframe: function(){\r
70         this.init();\r
71         this.unframeTask.delay(2);\r
72     },\r
73     \r
74     setSize: function(size){\r
75         this.size = size;\r
76     },\r
77     \r
78     setColor: function(color){\r
79         this.color = color;\r
80     }\r
81 });\r
82 \r
83 Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');\r
84 Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');\r
85 \r
86 Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {\r
87     constructor: function(el, relayTo, noFrame, frameEl){\r
88         Ext.a11y.Focusable.superclass.constructor.call(this);\r
89         \r
90         this.addEvents('focus', 'blur', 'left', 'right', 'up', 'down', 'esc', 'enter', 'space');\r
91         \r
92         if (el instanceof Ext.Component) {\r
93             this.el = el.el;\r
94             this.setComponent(el);\r
95         }\r
96         else {\r
97             this.el = Ext.get(el);\r
98             this.setComponent(null);\r
99         }\r
100         \r
101         this.setRelayTo(relayTo)\r
102         this.setNoFrame(noFrame);\r
103         this.setFrameEl(frameEl);\r
104         \r
105         this.init();\r
106         \r
107         Ext.a11y.FocusMgr.register(this);\r
108     },\r
109     \r
110     init: function(){\r
111         this.el.dom.tabIndex = '1';\r
112         this.el.addClass('x-a11y-focusable');\r
113         this.el.on({\r
114             focus: this.onFocus,\r
115             blur: this.onBlur,\r
116             keydown: this.onKeyDown,\r
117             scope: this\r
118         });\r
119     },\r
120     \r
121     setRelayTo: function(relayTo){\r
122         this.relayTo = relayTo ? Ext.a11y.FocusMgr.get(relayTo) : null;\r
123     },\r
124     \r
125     setNoFrame: function(noFrame){\r
126         this.noFrame = (noFrame === true) ? true : false;\r
127     },\r
128     \r
129     setFrameEl: function(frameEl){\r
130         this.frameEl = frameEl && Ext.get(frameEl) || this.el;\r
131     },\r
132     \r
133     setComponent: function(cmp){\r
134         this.component = cmp || null;\r
135     },\r
136     \r
137     onKeyDown: function(e, t){\r
138         var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;\r
139         \r
140         tf = (t !== this.el.dom) ? Ext.a11y.FocusMgr.get(t, true) : this;\r
141         if (!tf) {\r
142             // this can happen when you are on a focused item within a panel body\r
143             // that is not a Ext.a11y.Focusable\r
144             tf = Ext.a11y.FocusMgr.get(Ext.fly(t).parent('.x-a11y-focusable'));\r
145         }\r
146         \r
147         if (SK[k] !== undefined) {\r
148             ret = this.fireEvent(SK[k], e, t, tf, this);\r
149         }\r
150         if (ret === false || this.fireEvent('keydown', e, t, tf, this) === false) {\r
151             e.stopEvent();\r
152         }\r
153     },\r
154     \r
155     focus: function(){\r
156         this.el.dom.focus();\r
157     },\r
158     \r
159     blur: function(){\r
160         this.el.dom.blur();\r
161     },\r
162     \r
163     onFocus: function(e, t){\r
164         this.el.addClass('x-a11y-focused');\r
165         if (this.relayTo) {\r
166             this.relayTo.el.addClass('x-a11y-focused-relay');\r
167             if (!this.relayTo.noFrame) {\r
168                 Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);\r
169             }\r
170             if (!this.noFrame) {\r
171                 Ext.a11y.RelayFrame.frame(this.frameEl);\r
172             }\r
173         }\r
174         else {\r
175             if (!this.noFrame) {\r
176                 Ext.a11y.FocusFrame.frame(this.frameEl);\r
177             }\r
178         }\r
179         \r
180         this.fireEvent('focus', e, t, this);\r
181     },\r
182     \r
183     onBlur: function(e, t){\r
184         if (this.relayTo) {\r
185             this.relayTo.el.removeClass('x-a11y-focused-relay');\r
186             Ext.a11y.RelayFrame.unframe();\r
187         }\r
188         this.el.removeClass('x-a11y-focused');\r
189         Ext.a11y.FocusFrame.unframe();\r
190         this.fireEvent('blur', e, t, this);\r
191     },\r
192     \r
193     destroy: function(){\r
194         this.el.un('keydown', this.onKeyDown);\r
195         this.el.un('focus', this.onFocus);\r
196         this.el.un('blur', this.onBlur);\r
197         this.el.removeClass('x-a11y-focusable');\r
198         this.el.removeClass('x-a11y-focused');\r
199         if (this.relayTo) {\r
200             this.relayTo.el.removeClass('x-a11y-focused-relay');\r
201         }\r
202     }\r
203 });\r
204 \r
205 Ext.a11y.FocusItem = Ext.extend(Object, {\r
206     constructor: function(el, enableTabbing){\r
207         Ext.a11y.FocusItem.superclass.constructor.call(this);\r
208         \r
209         this.el = Ext.get(el);\r
210         this.fi = new Ext.a11y.Focusable(el);\r
211         this.fi.setComponent(this);\r
212         \r
213         this.fi.on('tab', this.onTab, this);\r
214         \r
215         this.enableTabbing = enableTabbing === true ? true : false;\r
216     },\r
217     \r
218     getEnterItem: function(){\r
219         if (this.enableTabbing) {\r
220             var items = this.getFocusItems();\r
221             if (items && items.length) {\r
222                 return items[0];\r
223             }\r
224         }\r
225     },\r
226     \r
227     getFocusItems: function(){\r
228         if (this.enableTabbing) {\r
229             return this.el.query('a, button, input, select');\r
230         }\r
231         return null;\r
232     },\r
233     \r
234     onTab: function(e, t){\r
235         var items = this.getFocusItems(), i;\r
236         \r
237         if (items && items.length && (i = items.indexOf(t)) !== -1) {\r
238             if (e.shiftKey && i > 0) {\r
239                 e.stopEvent();\r
240                 items[i - 1].focus();\r
241                 Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
242                 return;\r
243             }\r
244             else \r
245                 if (!e.shiftKey && i < items.length - 1) {\r
246                     e.stopEvent();\r
247                     items[i + 1].focus();\r
248                     Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
249                     return;\r
250                 }\r
251         }\r
252     },\r
253     \r
254     focus: function(){\r
255         if (this.enableTabbing) {\r
256             var items = this.getFocusItems();\r
257             if (items && items.length) {\r
258                 items[0].focus();\r
259                 Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
260                 return;\r
261             }\r
262         }\r
263         this.fi.focus();\r
264     },\r
265     \r
266     blur: function(){\r
267         this.fi.blur();\r
268     }\r
269 });\r
270 \r
271 Ext.a11y.FocusMgr = function(){\r
272     var all = new Ext.util.MixedCollection();\r
273     \r
274     return {\r
275         register: function(f){\r
276             all.add(f.el && Ext.id(f.el), f);\r
277         },\r
278         \r
279         unregister: function(f){\r
280             all.remove(f);\r
281         },\r
282         \r
283         get: function(el, noCreate){\r
284             return all.get(Ext.id(el)) || (noCreate ? false : new Ext.a11y.Focusable(el));\r
285         },\r
286         \r
287         all: all\r
288     }\r
289 }();\r
290 \r
291 Ext.a11y.Focusable.SpecialKeys = {};\r
292 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';\r
293 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';\r
294 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';\r
295 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';\r
296 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';\r
297 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';\r
298 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';\r
299 Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';\r
300 \r
301 // we use the new observeClass method to fire our new initFocus method on components\r
302 Ext.util.Observable.observeClass(Ext.Component);\r
303 Ext.Component.on('render', function(cmp){\r
304     cmp.initFocus();\r
305     cmp.initARIA();\r
306 });\r
307 Ext.override(Ext.Component, {\r
308     initFocus: Ext.emptyFn,\r
309     initARIA: Ext.emptyFn\r
310 });\r
311 \r
312 Ext.override(Ext.Container, {\r
313     isFocusable: true,\r
314     noFocus: false,\r
315     \r
316     // private\r
317     initFocus: function(){\r
318         if (!this.fi && !this.noFocus) {\r
319             this.fi = new Ext.a11y.Focusable(this);\r
320         }\r
321         this.mon(this.fi, {\r
322             focus: this.onFocus,\r
323             blur: this.onBlur,\r
324             tab: this.onTab,\r
325             enter: this.onEnter,\r
326             esc: this.onEsc,\r
327             scope: this\r
328         });\r
329         \r
330         if (this.hidden) {\r
331             this.isFocusable = false;\r
332         }\r
333         \r
334         this.on('show', function(){\r
335             this.isFocusable = true;\r
336         }, this);\r
337         this.on('hide', function(){\r
338             this.isFocusable = false;\r
339         }, this);\r
340     },\r
341     \r
342     focus: function(){\r
343         this.fi.focus();\r
344     },\r
345     \r
346     blur: function(){\r
347         this.fi.blur();\r
348     },\r
349     \r
350     enter: function(){\r
351         var eitem = this.getEnterItem();\r
352         if (eitem) {\r
353             eitem.focus();\r
354         }\r
355     },\r
356     \r
357     onFocus: Ext.emptyFn,\r
358     onBlur: Ext.emptyFn,\r
359     \r
360     onTab: function(e, t, tf){\r
361         var rf = tf.relayTo || tf;\r
362         if (rf.component && rf.component !== this) {\r
363             e.stopEvent();\r
364             var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
365             item.focus();\r
366         }\r
367     },\r
368     \r
369     onEnter: function(e, t, tf){\r
370         // check to see if enter is pressed while "on" the panel\r
371         if (tf.component && tf.component === this) {\r
372             e.stopEvent();\r
373             this.enter();\r
374         }\r
375         e.stopPropagation();\r
376     },\r
377     \r
378     onEsc: function(e, t){\r
379         e.preventDefault();\r
380         \r
381         // check to see if esc is pressed while "inside" the panel\r
382         // or while "on" the panel\r
383         if (t === this.el.dom) {\r
384             // "on" the panel, check if this panel has an owner panel and focus that\r
385             // we dont stop the event in this case so that this same check will be\r
386             // done for this ownerCt\r
387             if (this.ownerCt) {\r
388                 this.ownerCt.focus();\r
389             }\r
390         }\r
391         else {\r
392             // we were inside the panel when esc was pressed,\r
393             // so go back "on" the panel\r
394             if (this.ownerCt && this.ownerCt.isFocusable) {\r
395                 var si = this.ownerCt.getFocusItems();\r
396                 \r
397                 if (si && si.getCount() > 1) {\r
398                     e.stopEvent();\r
399                 }\r
400             }\r
401             this.focus();\r
402         }\r
403     },\r
404     \r
405     getFocusItems: function(){\r
406         return this.items &&\r
407             this.items.filterBy(function(o){\r
408                 return o.isFocusable;\r
409             }) ||\r
410             null;\r
411     },\r
412     \r
413     getEnterItem: function(){\r
414         var ci = this.getFocusItems(), length = ci ? ci.getCount() : 0;\r
415         \r
416         if (length === 1) {\r
417             return ci.first().getEnterItem && ci.first().getEnterItem() || ci.first();\r
418         }\r
419         else if (length > 1) {\r
420             return ci.first();\r
421         }\r
422     },\r
423     \r
424     getNextFocus: function(current){\r
425         var items = this.getFocusItems(), next = current, i = items.indexOf(current), length = items.getCount();\r
426         \r
427         if (i === length - 1) {\r
428             next = items.first();\r
429         }\r
430         else {\r
431             next = items.get(i + 1);\r
432         }\r
433         return next;\r
434     },\r
435     \r
436     getPreviousFocus: function(current){\r
437         var items = this.getFocusItems(), prev = current, i = items.indexOf(current), length = items.getCount();\r
438         \r
439         if (i === 0) {\r
440             prev = items.last();\r
441         }\r
442         else {\r
443             prev = items.get(i - 1);\r
444         }\r
445         return prev;\r
446     },\r
447     \r
448     getFocusable : function() {\r
449         return this.fi;\r
450     }\r
451 });\r
452 \r
453 Ext.override(Ext.Panel, {\r
454     /**\r
455      * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing. Default is <tt>false</tt>.\r
456      */        \r
457     getFocusItems: function(){\r
458         // items gets all the items inside the body\r
459         var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;\r
460 \r
461         if (!items) {\r
462             items = new Ext.util.MixedCollection();\r
463             this.bodyFocus = this.bodyFocus || new Ext.a11y.FocusItem(this.body, this.enableTabbing);\r
464             items.add('body', this.bodyFocus);\r
465         }\r
466         // but panels can also have tbar, bbar, fbar\r
467         if (this.tbar && this.topToolbar) {\r
468             items.insert(0, this.topToolbar);\r
469         }\r
470         if (this.bbar && this.bottomToolbar) {\r
471             items.add(this.bottomToolbar);\r
472         }\r
473         if (this.fbar) {\r
474             items.add(this.fbar);\r
475         }\r
476         \r
477         return items;\r
478     }\r
479 });\r
480 \r
481 Ext.override(Ext.TabPanel, {\r
482     // private\r
483     initFocus: function(){\r
484         Ext.TabPanel.superclass.initFocus.call(this);\r
485         this.mon(this.fi, {\r
486             left: this.onLeft,\r
487             right: this.onRight,\r
488             scope: this\r
489         });\r
490     },\r
491     \r
492     onLeft: function(e){\r
493         if (!this.activeTab) {\r
494             return;\r
495         }\r
496         e.stopEvent();\r
497         var prev = this.items.itemAt(this.items.indexOf(this.activeTab) - 1);\r
498         if (prev) {\r
499             this.setActiveTab(prev);\r
500         }\r
501         return false;\r
502     },\r
503     \r
504     onRight: function(e){\r
505         if (!this.activeTab) {\r
506             return;\r
507         }\r
508         e.stopEvent();\r
509         var next = this.items.itemAt(this.items.indexOf(this.activeTab) + 1);\r
510         if (next) {\r
511             this.setActiveTab(next);\r
512         }\r
513         return false;\r
514     }\r
515 });\r
516 \r
517 Ext.override(Ext.tree.TreeNodeUI, {\r
518     // private\r
519     focus: function(){\r
520         this.node.getOwnerTree().bodyFocus.focus();\r
521     }\r
522 });\r
523 \r
524 Ext.override(Ext.tree.TreePanel, {\r
525     // private\r
526     afterRender : function(){\r
527         Ext.tree.TreePanel.superclass.afterRender.call(this);\r
528         this.root.render();\r
529         if(!this.rootVisible){\r
530             this.root.renderChildren();\r
531         }\r
532         this.bodyFocus = new Ext.a11y.FocusItem(this.body.down('.x-tree-root-ct'));\r
533         this.bodyFocus.fi.setFrameEl(this.body);\r
534     } \r
535 });\r
536 \r
537 Ext.override(Ext.grid.GridPanel, {\r
538     initFocus: function(){\r
539         Ext.grid.GridPanel.superclass.initFocus.call(this);\r
540         this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);\r
541         this.bodyFocus.fi.setFrameEl(this.body);\r
542     }\r
543 });\r
544 \r
545 Ext.override(Ext.Button, {\r
546     isFocusable: true,\r
547     noFocus: false,\r
548     \r
549     initFocus: function(){\r
550         Ext.Button.superclass.initFocus.call(this);\r
551         this.fi = this.fi || new Ext.a11y.Focusable(this.btnEl, null, null, this.el);\r
552         this.fi.setComponent(this);\r
553         \r
554         this.mon(this.fi, {\r
555             focus: this.onFocus,\r
556             blur: this.onBlur,\r
557             scope: this\r
558         });\r
559         \r
560         if (this.menu) {\r
561             this.mon(this.fi, 'down', this.showMenu, this);\r
562             this.on('menuhide', this.focus, this);\r
563         }\r
564         \r
565         if (this.hidden) {\r
566             this.isFocusable = false;\r
567         }\r
568         \r
569         this.on('show', function(){\r
570             this.isFocusable = true;\r
571         }, this);\r
572         this.on('hide', function(){\r
573             this.isFocusable = false;\r
574         }, this);\r
575     },\r
576     \r
577     focus: function(){\r
578         this.fi.focus();\r
579     },\r
580     \r
581     blur: function(){\r
582         this.fi.blur();\r
583     },\r
584     \r
585     onFocus: function(){\r
586         if (!this.disabled) {\r
587             this.el.addClass("x-btn-focus");\r
588         }\r
589     },\r
590     \r
591     onBlur: function(){\r
592         this.el.removeClass("x-btn-focus");\r
593     }\r
594 });\r
595 \r
596 Ext.override(Ext.Toolbar, {\r
597     initFocus: function(){\r
598         Ext.Toolbar.superclass.initFocus.call(this);\r
599         this.mon(this.fi, {\r
600             left: this.onLeft,\r
601             right: this.onRight,\r
602             scope: this\r
603         });\r
604         \r
605         this.on('focus', this.onButtonFocus, this, {\r
606             stopEvent: true\r
607         });\r
608     },\r
609     \r
610     add: function(){\r
611         var item = Ext.Toolbar.superclass.add.apply(this, arguments);\r
612         if(!item || !item.events) {\r
613             return item;\r
614         }\r
615         if (item.rendered && item.fi !== undefined) {\r
616             item.fi.setRelayTo(this.el);\r
617             this.relayEvents(item.fi, ['focus']);\r
618         }\r
619         else {\r
620             item.on('render', function(){\r
621                 if (item.fi !== undefined) {\r
622                     item.fi.setRelayTo(this.el);\r
623                     this.relayEvents(item.fi, ['focus']);\r
624                 }\r
625             }, this, {\r
626                 single: true\r
627             });\r
628         }\r
629         return item;\r
630     },\r
631     \r
632     onFocus: function(){\r
633         var items = this.getFocusItems();\r
634         if (items && items.getCount() > 0) {\r
635             if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
636                 this.lastFocus.focus();\r
637             }\r
638             else {\r
639                 items.first().focus();\r
640             }\r
641         }\r
642     },\r
643     \r
644     onButtonFocus: function(e, t, tf){\r
645         this.lastFocus = tf.component || null;\r
646     },\r
647     \r
648     onLeft: function(e, t, tf){\r
649         e.stopEvent();\r
650         this.getPreviousFocus(tf.component).focus();\r
651     },\r
652     \r
653     onRight: function(e, t, tf){\r
654         e.stopEvent();\r
655         this.getNextFocus(tf.component).focus();\r
656     },\r
657     \r
658     getEnterItem: Ext.emptyFn,\r
659     onTab: Ext.emptyFn,\r
660     onEsc: Ext.emptyFn\r
661 });\r
662 \r
663 Ext.override(Ext.menu.BaseItem, {\r
664     initFocus: function(){\r
665         this.fi = new Ext.a11y.Focusable(this, this.parentMenu && this.parentMenu.el || null, true);\r
666     }\r
667 });\r
668 \r
669 Ext.override(Ext.menu.Menu, {\r
670     initFocus: function(){\r
671         this.fi = new Ext.a11y.Focusable(this);\r
672         this.focusEl = this.fi;\r
673     }\r
674 });\r
675 \r
676 Ext.a11y.WindowMgr = new Ext.WindowGroup();\r
677 \r
678 Ext.apply(Ext.WindowMgr, {\r
679     bringToFront: function(win){\r
680         Ext.a11y.WindowMgr.bringToFront.call(this, win);\r
681         if (win.modal) {\r
682             win.enter();\r
683         }\r
684         else {\r
685             win.focus();\r
686         }\r
687     }\r
688 });\r
689 \r
690 Ext.override(Ext.Window, {\r
691     initFocus: function(){\r
692         Ext.Window.superclass.initFocus.call(this);\r
693         this.on('beforehide', function(){\r
694             Ext.a11y.RelayFrame.unframe();\r
695             Ext.a11y.FocusFrame.unframe();\r
696         });\r
697     }\r
698 });\r
699 \r
700 Ext.override(Ext.form.Field, {\r
701     isFocusable: true,\r
702     noFocus: false,\r
703     \r
704     initFocus: function(){\r
705         this.fi = this.fi || new Ext.a11y.Focusable(this, null, true);\r
706         \r
707         Ext.form.Field.superclass.initFocus.call(this);\r
708         \r
709         if (this.hidden) {\r
710             this.isFocusable = false;\r
711         }\r
712         \r
713         this.on('show', function(){\r
714             this.isFocusable = true;\r
715         }, this);\r
716         this.on('hide', function(){\r
717             this.isFocusable = false;\r
718         }, this);\r
719     }\r
720 });\r
721 \r
722 Ext.override(Ext.FormPanel, {\r
723     initFocus: function(){\r
724         Ext.FormPanel.superclass.initFocus.call(this);\r
725         this.on('focus', this.onFieldFocus, this, {\r
726             stopEvent: true\r
727         });\r
728     },\r
729     \r
730     // private\r
731     createForm: function(){\r
732         delete this.initialConfig.listeners;\r
733         var form = new Ext.form.BasicForm(null, this.initialConfig);\r
734         form.afterMethod('add', this.formItemAdd, this);\r
735         return form;\r
736     },\r
737     \r
738     formItemAdd: function(item){\r
739         item.on('render', function(field){\r
740             field.fi.setRelayTo(this.el);\r
741             this.relayEvents(field.fi, ['focus']);\r
742         }, this, {\r
743             single: true\r
744         });\r
745     },\r
746     \r
747     onFocus: function(){\r
748         var items = this.getFocusItems();\r
749         if (items && items.getCount() > 0) {\r
750             if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
751                 this.lastFocus.focus();\r
752             }\r
753             else {\r
754                 items.first().focus();\r
755             }\r
756         }\r
757     },\r
758     \r
759     onFieldFocus: function(e, t, tf){\r
760         this.lastFocus = tf.component || null;\r
761     },\r
762     \r
763     onTab: function(e, t, tf){\r
764         if (tf.relayTo.component === this) {\r
765             var item = e.shiftKey ? this.getPreviousFocus(tf.component) : this.getNextFocus(tf.component);\r
766             \r
767             if (item) {\r
768                 ev.stopEvent();\r
769                 item.focus();\r
770                 return;\r
771             }\r
772         }\r
773         Ext.FormPanel.superclass.onTab.apply(this, arguments);\r
774     },\r
775     \r
776     getNextFocus: function(current){\r
777         var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
778         \r
779         return (i < length - 1) ? items.get(i + 1) : false;\r
780     },\r
781     \r
782     getPreviousFocus: function(current){\r
783         var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
784         \r
785         return (i > 0) ? items.get(i - 1) : false;\r
786     }\r
787 });\r
788 \r
789 Ext.override(Ext.Viewport, {\r
790     initFocus: function(){\r
791         Ext.Viewport.superclass.initFocus.apply(this);\r
792         this.mon(Ext.get(document), 'focus', this.focus, this);\r
793         this.mon(Ext.get(document), 'blur', this.blur, this);\r
794         this.fi.setNoFrame(true);\r
795     },\r
796     \r
797     onTab: function(e, t, tf, f){\r
798         e.stopEvent();\r
799         \r
800         if (tf === f) {\r
801             items = this.getFocusItems();\r
802             if (items && items.getCount() > 0) {\r
803                 items.first().focus();\r
804             }\r
805         }\r
806         else {\r
807             var rf = tf.relayTo || tf;\r
808             var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
809             item.focus();\r
810         }\r
811     }\r
812 });\r
813     \r
814 })();