Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / examples / ux / Focus.js
index e71035d..919aef3 100644 (file)
 /*!
- * Ext JS Library 3.1.1
- * Copyright(c) 2006-2010 Ext JS, LLC
+ * Ext JS Library 3.2.0
+ * Copyright(c) 2006-2010 Ext JS, Inc.
  * licensing@extjs.com
  * http://www.extjs.com/license
  */
-(function(){\r
-Ext.ns('Ext.a11y');\r
-\r
-Ext.a11y.Frame = Ext.extend(Object, {\r
-    initialized: false,\r
-    \r
-    constructor: function(size, color){\r
-        this.setSize(size || 1);\r
-        this.setColor(color || '15428B');\r
-    },\r
-    \r
-    init: function(){\r
-        if (!this.initialized) {\r
-            this.sides = [];\r
-            \r
-            var s, i;\r
-            \r
-            this.ct = Ext.DomHelper.append(document.body, {\r
-                cls: 'x-a11y-focusframe'\r
-            }, true);\r
-            \r
-            for (i = 0; i < 4; i++) {\r
-                s = Ext.DomHelper.append(this.ct, {\r
-                    cls: 'x-a11y-focusframe-side',\r
-                    style: 'background-color: #' + this.color\r
-                }, true);\r
-                s.visibilityMode = Ext.Element.DISPLAY;\r
-                this.sides.push(s);\r
-            }\r
-            \r
-            this.frameTask = new Ext.util.DelayedTask(function(el){\r
-                var newEl = Ext.get(el);\r
-                if (newEl != this.curEl) {\r
-                    var w = newEl.getWidth();\r
-                    var h = newEl.getHeight();\r
-                    this.sides[0].show().setSize(w, this.size).anchorTo(el, 'tl', [0, -1]);\r
-                    this.sides[2].show().setSize(w, this.size).anchorTo(el, 'bl', [0, -1]);\r
-                    this.sides[1].show().setSize(this.size, h).anchorTo(el, 'tr', [-1, 0]);\r
-                    this.sides[3].show().setSize(this.size, h).anchorTo(el, 'tl', [-1, 0]);\r
-                    this.curEl = newEl;\r
-                }\r
-            }, this);\r
-            \r
-            this.unframeTask = new Ext.util.DelayedTask(function(){\r
-                if (this.initialized) {\r
-                    this.sides[0].hide();\r
-                    this.sides[1].hide();\r
-                    this.sides[2].hide();\r
-                    this.sides[3].hide();\r
-                    this.curEl = null;\r
-                }\r
-            }, this);\r
-            this.initialized = true;\r
-        }\r
-    },\r
-    \r
-    frame: function(el){\r
-        this.init();\r
-        this.unframeTask.cancel();\r
-        this.frameTask.delay(2, false, false, [el]);\r
-    },\r
-    \r
-    unframe: function(){\r
-        this.init();\r
-        this.unframeTask.delay(2);\r
-    },\r
-    \r
-    setSize: function(size){\r
-        this.size = size;\r
-    },\r
-    \r
-    setColor: function(color){\r
-        this.color = color;\r
-    }\r
-});\r
-\r
-Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');\r
-Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');\r
-\r
-Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {\r
-    constructor: function(el, relayTo, noFrame, frameEl){\r
-        Ext.a11y.Focusable.superclass.constructor.call(this);\r
-        \r
-        this.addEvents('focus', 'blur', 'left', 'right', 'up', 'down', 'esc', 'enter', 'space');\r
-        \r
-        if (el instanceof Ext.Component) {\r
-            this.el = el.el;\r
-            this.setComponent(el);\r
-        }\r
-        else {\r
-            this.el = Ext.get(el);\r
-            this.setComponent(null);\r
-        }\r
-        \r
-        this.setRelayTo(relayTo)\r
-        this.setNoFrame(noFrame);\r
-        this.setFrameEl(frameEl);\r
-        \r
-        this.init();\r
-        \r
-        Ext.a11y.FocusMgr.register(this);\r
-    },\r
-    \r
-    init: function(){\r
-        this.el.dom.tabIndex = '1';\r
-        this.el.addClass('x-a11y-focusable');\r
-        this.el.on({\r
-            focus: this.onFocus,\r
-            blur: this.onBlur,\r
-            keydown: this.onKeyDown,\r
-            scope: this\r
-        });\r
-    },\r
-    \r
-    setRelayTo: function(relayTo){\r
-        this.relayTo = relayTo ? Ext.a11y.FocusMgr.get(relayTo) : null;\r
-    },\r
-    \r
-    setNoFrame: function(noFrame){\r
-        this.noFrame = (noFrame === true) ? true : false;\r
-    },\r
-    \r
-    setFrameEl: function(frameEl){\r
-        this.frameEl = frameEl && Ext.get(frameEl) || this.el;\r
-    },\r
-    \r
-    setComponent: function(cmp){\r
-        this.component = cmp || null;\r
-    },\r
-    \r
-    onKeyDown: function(e, t){\r
-        var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;\r
-        \r
-        tf = (t !== this.el.dom) ? Ext.a11y.FocusMgr.get(t, true) : this;\r
-        if (!tf) {\r
-            // this can happen when you are on a focused item within a panel body\r
-            // that is not a Ext.a11y.Focusable\r
-            tf = Ext.a11y.FocusMgr.get(Ext.fly(t).parent('.x-a11y-focusable'));\r
-        }\r
-        \r
-        if (SK[k] !== undefined) {\r
-            ret = this.fireEvent(SK[k], e, t, tf, this);\r
-        }\r
-        if (ret === false || this.fireEvent('keydown', e, t, tf, this) === false) {\r
-            e.stopEvent();\r
-        }\r
-    },\r
-    \r
-    focus: function(){\r
-        this.el.dom.focus();\r
-    },\r
-    \r
-    blur: function(){\r
-        this.el.dom.blur();\r
-    },\r
-    \r
-    onFocus: function(e, t){\r
-        this.el.addClass('x-a11y-focused');\r
-        if (this.relayTo) {\r
-            this.relayTo.el.addClass('x-a11y-focused-relay');\r
-            if (!this.relayTo.noFrame) {\r
-                Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);\r
-            }\r
-            if (!this.noFrame) {\r
-                Ext.a11y.RelayFrame.frame(this.frameEl);\r
-            }\r
-        }\r
-        else {\r
-            if (!this.noFrame) {\r
-                Ext.a11y.FocusFrame.frame(this.frameEl);\r
-            }\r
-        }\r
-        \r
-        this.fireEvent('focus', e, t, this);\r
-    },\r
-    \r
-    onBlur: function(e, t){\r
-        if (this.relayTo) {\r
-            this.relayTo.el.removeClass('x-a11y-focused-relay');\r
-            Ext.a11y.RelayFrame.unframe();\r
-        }\r
-        this.el.removeClass('x-a11y-focused');\r
-        Ext.a11y.FocusFrame.unframe();\r
-        this.fireEvent('blur', e, t, this);\r
-    },\r
-    \r
-    destroy: function(){\r
-        this.el.un('keydown', this.onKeyDown);\r
-        this.el.un('focus', this.onFocus);\r
-        this.el.un('blur', this.onBlur);\r
-        this.el.removeClass('x-a11y-focusable');\r
-        this.el.removeClass('x-a11y-focused');\r
-        if (this.relayTo) {\r
-            this.relayTo.el.removeClass('x-a11y-focused-relay');\r
-        }\r
-    }\r
-});\r
-\r
-Ext.a11y.FocusItem = Ext.extend(Object, {\r
-    constructor: function(el, enableTabbing){\r
-        Ext.a11y.FocusItem.superclass.constructor.call(this);\r
-        \r
-        this.el = Ext.get(el);\r
-        this.fi = new Ext.a11y.Focusable(el);\r
-        this.fi.setComponent(this);\r
-        \r
-        this.fi.on('tab', this.onTab, this);\r
-        \r
-        this.enableTabbing = enableTabbing === true ? true : false;\r
-    },\r
-    \r
-    getEnterItem: function(){\r
-        if (this.enableTabbing) {\r
-            var items = this.getFocusItems();\r
-            if (items && items.length) {\r
-                return items[0];\r
-            }\r
-        }\r
-    },\r
-    \r
-    getFocusItems: function(){\r
-        if (this.enableTabbing) {\r
-            return this.el.query('a, button, input, select');\r
-        }\r
-        return null;\r
-    },\r
-    \r
-    onTab: function(e, t){\r
-        var items = this.getFocusItems(), i;\r
-        \r
-        if (items && items.length && (i = items.indexOf(t)) !== -1) {\r
-            if (e.shiftKey && i > 0) {\r
-                e.stopEvent();\r
-                items[i - 1].focus();\r
-                Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
-                return;\r
-            }\r
-            else \r
-                if (!e.shiftKey && i < items.length - 1) {\r
-                    e.stopEvent();\r
-                    items[i + 1].focus();\r
-                    Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
-                    return;\r
-                }\r
-        }\r
-    },\r
-    \r
-    focus: function(){\r
-        if (this.enableTabbing) {\r
-            var items = this.getFocusItems();\r
-            if (items && items.length) {\r
-                items[0].focus();\r
-                Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
-                return;\r
-            }\r
-        }\r
-        this.fi.focus();\r
-    },\r
-    \r
-    blur: function(){\r
-        this.fi.blur();\r
-    }\r
-});\r
-\r
-Ext.a11y.FocusMgr = function(){\r
-    var all = new Ext.util.MixedCollection();\r
-    \r
-    return {\r
-        register: function(f){\r
-            all.add(f.el && Ext.id(f.el), f);\r
-        },\r
-        \r
-        unregister: function(f){\r
-            all.remove(f);\r
-        },\r
-        \r
-        get: function(el, noCreate){\r
-            return all.get(Ext.id(el)) || (noCreate ? false : new Ext.a11y.Focusable(el));\r
-        },\r
-        \r
-        all: all\r
-    }\r
-}();\r
-\r
-Ext.a11y.Focusable.SpecialKeys = {};\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';\r
-Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';\r
-\r
-// we use the new observeClass method to fire our new initFocus method on components\r
-Ext.util.Observable.observeClass(Ext.Component);\r
-Ext.Component.on('render', function(cmp){\r
-    cmp.initFocus();\r
-    cmp.initARIA();\r
-});\r
-Ext.override(Ext.Component, {\r
-    initFocus: Ext.emptyFn,\r
-    initARIA: Ext.emptyFn\r
-});\r
-\r
-Ext.override(Ext.Container, {\r
-    isFocusable: true,\r
-    noFocus: false,\r
-    \r
-    // private\r
-    initFocus: function(){\r
-        if (!this.fi && !this.noFocus) {\r
-            this.fi = new Ext.a11y.Focusable(this);\r
-        }\r
-        this.mon(this.fi, {\r
-            focus: this.onFocus,\r
-            blur: this.onBlur,\r
-            tab: this.onTab,\r
-            enter: this.onEnter,\r
-            esc: this.onEsc,\r
-            scope: this\r
-        });\r
-        \r
-        if (this.hidden) {\r
-            this.isFocusable = false;\r
-        }\r
-        \r
-        this.on('show', function(){\r
-            this.isFocusable = true;\r
-        }, this);\r
-        this.on('hide', function(){\r
-            this.isFocusable = false;\r
-        }, this);\r
-    },\r
-    \r
-    focus: function(){\r
-        this.fi.focus();\r
-    },\r
-    \r
-    blur: function(){\r
-        this.fi.blur();\r
-    },\r
-    \r
-    enter: function(){\r
-        var eitem = this.getEnterItem();\r
-        if (eitem) {\r
-            eitem.focus();\r
-        }\r
-    },\r
-    \r
-    onFocus: Ext.emptyFn,\r
-    onBlur: Ext.emptyFn,\r
-    \r
-    onTab: function(e, t, tf){\r
-        var rf = tf.relayTo || tf;\r
-        if (rf.component && rf.component !== this) {\r
-            e.stopEvent();\r
-            var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
-            item.focus();\r
-        }\r
-    },\r
-    \r
-    onEnter: function(e, t, tf){\r
-        // check to see if enter is pressed while "on" the panel\r
-        if (tf.component && tf.component === this) {\r
-            e.stopEvent();\r
-            this.enter();\r
-        }\r
-        e.stopPropagation();\r
-    },\r
-    \r
-    onEsc: function(e, t){\r
-        e.preventDefault();\r
-        \r
-        // check to see if esc is pressed while "inside" the panel\r
-        // or while "on" the panel\r
-        if (t === this.el.dom) {\r
-            // "on" the panel, check if this panel has an owner panel and focus that\r
-            // we dont stop the event in this case so that this same check will be\r
-            // done for this ownerCt\r
-            if (this.ownerCt) {\r
-                this.ownerCt.focus();\r
-            }\r
-        }\r
-        else {\r
-            // we were inside the panel when esc was pressed,\r
-            // so go back "on" the panel\r
-            if (this.ownerCt && this.ownerCt.isFocusable) {\r
-                var si = this.ownerCt.getFocusItems();\r
-                \r
-                if (si && si.getCount() > 1) {\r
-                    e.stopEvent();\r
-                }\r
-            }\r
-            this.focus();\r
-        }\r
-    },\r
-    \r
-    getFocusItems: function(){\r
-        return this.items &&\r
-            this.items.filterBy(function(o){\r
-                return o.isFocusable;\r
-            }) ||\r
-            null;\r
-    },\r
-    \r
-    getEnterItem: function(){\r
-        var ci = this.getFocusItems(), length = ci ? ci.getCount() : 0;\r
-        \r
-        if (length === 1) {\r
-            return ci.first().getEnterItem && ci.first().getEnterItem() || ci.first();\r
-        }\r
-        else if (length > 1) {\r
-            return ci.first();\r
-        }\r
-    },\r
-    \r
-    getNextFocus: function(current){\r
-        var items = this.getFocusItems(), next = current, i = items.indexOf(current), length = items.getCount();\r
-        \r
-        if (i === length - 1) {\r
-            next = items.first();\r
-        }\r
-        else {\r
-            next = items.get(i + 1);\r
-        }\r
-        return next;\r
-    },\r
-    \r
-    getPreviousFocus: function(current){\r
-        var items = this.getFocusItems(), prev = current, i = items.indexOf(current), length = items.getCount();\r
-        \r
-        if (i === 0) {\r
-            prev = items.last();\r
-        }\r
-        else {\r
-            prev = items.get(i - 1);\r
-        }\r
-        return prev;\r
-    },\r
-    \r
-    getFocusable : function() {\r
-        return this.fi;\r
-    }\r
-});\r
-\r
-Ext.override(Ext.Panel, {\r
-    /**\r
-     * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing. Default is <tt>false</tt>.\r
-     */        \r
-    getFocusItems: function(){\r
-        // items gets all the items inside the body\r
-        var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;\r
-\r
-        if (!items) {\r
-            items = new Ext.util.MixedCollection();\r
-            this.bodyFocus = this.bodyFocus || new Ext.a11y.FocusItem(this.body, this.enableTabbing);\r
-            items.add('body', this.bodyFocus);\r
-        }\r
-        // but panels can also have tbar, bbar, fbar\r
-        if (this.tbar && this.topToolbar) {\r
-            items.insert(0, this.topToolbar);\r
-        }\r
-        if (this.bbar && this.bottomToolbar) {\r
-            items.add(this.bottomToolbar);\r
-        }\r
-        if (this.fbar) {\r
-            items.add(this.fbar);\r
-        }\r
-        \r
-        return items;\r
-    }\r
-});\r
-\r
-Ext.override(Ext.TabPanel, {\r
-    // private\r
-    initFocus: function(){\r
-        Ext.TabPanel.superclass.initFocus.call(this);\r
-        this.mon(this.fi, {\r
-            left: this.onLeft,\r
-            right: this.onRight,\r
-            scope: this\r
-        });\r
-    },\r
-    \r
-    onLeft: function(e){\r
-        if (!this.activeTab) {\r
-            return;\r
-        }\r
-        e.stopEvent();\r
-        var prev = this.items.itemAt(this.items.indexOf(this.activeTab) - 1);\r
-        if (prev) {\r
-            this.setActiveTab(prev);\r
-        }\r
-        return false;\r
-    },\r
-    \r
-    onRight: function(e){\r
-        if (!this.activeTab) {\r
-            return;\r
-        }\r
-        e.stopEvent();\r
-        var next = this.items.itemAt(this.items.indexOf(this.activeTab) + 1);\r
-        if (next) {\r
-            this.setActiveTab(next);\r
-        }\r
-        return false;\r
-    }\r
-});\r
-\r
-Ext.override(Ext.tree.TreeNodeUI, {\r
-    // private\r
-    focus: function(){\r
-        this.node.getOwnerTree().bodyFocus.focus();\r
-    }\r
-});\r
-\r
-Ext.override(Ext.tree.TreePanel, {\r
-    // private\r
-    afterRender : function(){\r
-        Ext.tree.TreePanel.superclass.afterRender.call(this);\r
-        this.root.render();\r
-        if(!this.rootVisible){\r
-            this.root.renderChildren();\r
-        }\r
-        this.bodyFocus = new Ext.a11y.FocusItem(this.body.down('.x-tree-root-ct'));\r
-        this.bodyFocus.fi.setFrameEl(this.body);\r
-    } \r
-});\r
-\r
-Ext.override(Ext.grid.GridPanel, {\r
-    initFocus: function(){\r
-        Ext.grid.GridPanel.superclass.initFocus.call(this);\r
-        this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);\r
-        this.bodyFocus.fi.setFrameEl(this.body);\r
-    }\r
-});\r
-\r
-Ext.override(Ext.Button, {\r
-    isFocusable: true,\r
-    noFocus: false,\r
-    \r
-    initFocus: function(){\r
-        Ext.Button.superclass.initFocus.call(this);\r
-        this.fi = this.fi || new Ext.a11y.Focusable(this.btnEl, null, null, this.el);\r
-        this.fi.setComponent(this);\r
-        \r
-        this.mon(this.fi, {\r
-            focus: this.onFocus,\r
-            blur: this.onBlur,\r
-            scope: this\r
-        });\r
-        \r
-        if (this.menu) {\r
-            this.mon(this.fi, 'down', this.showMenu, this);\r
-            this.on('menuhide', this.focus, this);\r
-        }\r
-        \r
-        if (this.hidden) {\r
-            this.isFocusable = false;\r
-        }\r
-        \r
-        this.on('show', function(){\r
-            this.isFocusable = true;\r
-        }, this);\r
-        this.on('hide', function(){\r
-            this.isFocusable = false;\r
-        }, this);\r
-    },\r
-    \r
-    focus: function(){\r
-        this.fi.focus();\r
-    },\r
-    \r
-    blur: function(){\r
-        this.fi.blur();\r
-    },\r
-    \r
-    onFocus: function(){\r
-        if (!this.disabled) {\r
-            this.el.addClass("x-btn-focus");\r
-        }\r
-    },\r
-    \r
-    onBlur: function(){\r
-        this.el.removeClass("x-btn-focus");\r
-    }\r
-});\r
-\r
-Ext.override(Ext.Toolbar, {\r
-    initFocus: function(){\r
-        Ext.Toolbar.superclass.initFocus.call(this);\r
-        this.mon(this.fi, {\r
-            left: this.onLeft,\r
-            right: this.onRight,\r
-            scope: this\r
-        });\r
-        \r
-        this.on('focus', this.onButtonFocus, this, {\r
-            stopEvent: true\r
-        });\r
-    },\r
-    \r
-    add: function(){\r
-        var item = Ext.Toolbar.superclass.add.apply(this, arguments);\r
-        if(!item || !item.events) {\r
-            return item;\r
-        }\r
-        if (item.rendered && item.fi !== undefined) {\r
-            item.fi.setRelayTo(this.el);\r
-            this.relayEvents(item.fi, ['focus']);\r
-        }\r
-        else {\r
-            item.on('render', function(){\r
-                if (item.fi !== undefined) {\r
-                    item.fi.setRelayTo(this.el);\r
-                    this.relayEvents(item.fi, ['focus']);\r
-                }\r
-            }, this, {\r
-                single: true\r
-            });\r
-        }\r
-        return item;\r
-    },\r
-    \r
-    onFocus: function(){\r
-        var items = this.getFocusItems();\r
-        if (items && items.getCount() > 0) {\r
-            if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
-                this.lastFocus.focus();\r
-            }\r
-            else {\r
-                items.first().focus();\r
-            }\r
-        }\r
-    },\r
-    \r
-    onButtonFocus: function(e, t, tf){\r
-        this.lastFocus = tf.component || null;\r
-    },\r
-    \r
-    onLeft: function(e, t, tf){\r
-        e.stopEvent();\r
-        this.getPreviousFocus(tf.component).focus();\r
-    },\r
-    \r
-    onRight: function(e, t, tf){\r
-        e.stopEvent();\r
-        this.getNextFocus(tf.component).focus();\r
-    },\r
-    \r
-    getEnterItem: Ext.emptyFn,\r
-    onTab: Ext.emptyFn,\r
-    onEsc: Ext.emptyFn\r
-});\r
-\r
-Ext.override(Ext.menu.BaseItem, {\r
-    initFocus: function(){\r
-        this.fi = new Ext.a11y.Focusable(this, this.parentMenu && this.parentMenu.el || null, true);\r
-    }\r
-});\r
-\r
-Ext.override(Ext.menu.Menu, {\r
-    initFocus: function(){\r
-        this.fi = new Ext.a11y.Focusable(this);\r
-        this.focusEl = this.fi;\r
-    }\r
-});\r
-\r
-Ext.a11y.WindowMgr = new Ext.WindowGroup();\r
-\r
-Ext.apply(Ext.WindowMgr, {\r
-    bringToFront: function(win){\r
-        Ext.a11y.WindowMgr.bringToFront.call(this, win);\r
-        if (win.modal) {\r
-            win.enter();\r
-        }\r
-        else {\r
-            win.focus();\r
-        }\r
-    }\r
-});\r
-\r
-Ext.override(Ext.Window, {\r
-    initFocus: function(){\r
-        Ext.Window.superclass.initFocus.call(this);\r
-        this.on('beforehide', function(){\r
-            Ext.a11y.RelayFrame.unframe();\r
-            Ext.a11y.FocusFrame.unframe();\r
-        });\r
-    }\r
-});\r
-\r
-Ext.override(Ext.form.Field, {\r
-    isFocusable: true,\r
-    noFocus: false,\r
-    \r
-    initFocus: function(){\r
-        this.fi = this.fi || new Ext.a11y.Focusable(this, null, true);\r
-        \r
-        Ext.form.Field.superclass.initFocus.call(this);\r
-        \r
-        if (this.hidden) {\r
-            this.isFocusable = false;\r
-        }\r
-        \r
-        this.on('show', function(){\r
-            this.isFocusable = true;\r
-        }, this);\r
-        this.on('hide', function(){\r
-            this.isFocusable = false;\r
-        }, this);\r
-    }\r
-});\r
-\r
-Ext.override(Ext.FormPanel, {\r
-    initFocus: function(){\r
-        Ext.FormPanel.superclass.initFocus.call(this);\r
-        this.on('focus', this.onFieldFocus, this, {\r
-            stopEvent: true\r
-        });\r
-    },\r
-    \r
-    // private\r
-    createForm: function(){\r
-        delete this.initialConfig.listeners;\r
-        var form = new Ext.form.BasicForm(null, this.initialConfig);\r
-        form.afterMethod('add', this.formItemAdd, this);\r
-        return form;\r
-    },\r
-    \r
-    formItemAdd: function(item){\r
-        item.on('render', function(field){\r
-            field.fi.setRelayTo(this.el);\r
-            this.relayEvents(field.fi, ['focus']);\r
-        }, this, {\r
-            single: true\r
-        });\r
-    },\r
-    \r
-    onFocus: function(){\r
-        var items = this.getFocusItems();\r
-        if (items && items.getCount() > 0) {\r
-            if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
-                this.lastFocus.focus();\r
-            }\r
-            else {\r
-                items.first().focus();\r
-            }\r
-        }\r
-    },\r
-    \r
-    onFieldFocus: function(e, t, tf){\r
-        this.lastFocus = tf.component || null;\r
-    },\r
-    \r
-    onTab: function(e, t, tf){\r
-        if (tf.relayTo.component === this) {\r
-            var item = e.shiftKey ? this.getPreviousFocus(tf.component) : this.getNextFocus(tf.component);\r
-            \r
-            if (item) {\r
-                ev.stopEvent();\r
-                item.focus();\r
-                return;\r
-            }\r
-        }\r
-        Ext.FormPanel.superclass.onTab.apply(this, arguments);\r
-    },\r
-    \r
-    getNextFocus: function(current){\r
-        var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
-        \r
-        return (i < length - 1) ? items.get(i + 1) : false;\r
-    },\r
-    \r
-    getPreviousFocus: function(current){\r
-        var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
-        \r
-        return (i > 0) ? items.get(i - 1) : false;\r
-    }\r
-});\r
-\r
-Ext.override(Ext.Viewport, {\r
-    initFocus: function(){\r
-        Ext.Viewport.superclass.initFocus.apply(this);\r
-        this.mon(Ext.get(document), 'focus', this.focus, this);\r
-        this.mon(Ext.get(document), 'blur', this.blur, this);\r
-        this.fi.setNoFrame(true);\r
-    },\r
-    \r
-    onTab: function(e, t, tf, f){\r
-        e.stopEvent();\r
-        \r
-        if (tf === f) {\r
-            items = this.getFocusItems();\r
-            if (items && items.getCount() > 0) {\r
-                items.first().focus();\r
-            }\r
-        }\r
-        else {\r
-            var rf = tf.relayTo || tf;\r
-            var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
-            item.focus();\r
-        }\r
-    }\r
-});\r
-    \r
+(function(){
+Ext.ns('Ext.a11y');
+
+Ext.a11y.Frame = Ext.extend(Object, {
+    initialized: false,
+    
+    constructor: function(size, color){
+        this.setSize(size || 1);
+        this.setColor(color || '15428B');
+    },
+    
+    init: function(){
+        if (!this.initialized) {
+            this.sides = [];
+            
+            var s, i;
+            
+            this.ct = Ext.DomHelper.append(document.body, {
+                cls: 'x-a11y-focusframe'
+            }, true);
+            
+            for (i = 0; i < 4; i++) {
+                s = Ext.DomHelper.append(this.ct, {
+                    cls: 'x-a11y-focusframe-side',
+                    style: 'background-color: #' + this.color
+                }, true);
+                s.visibilityMode = Ext.Element.DISPLAY;
+                this.sides.push(s);
+            }
+            
+            this.frameTask = new Ext.util.DelayedTask(function(el){
+                var newEl = Ext.get(el);
+                if (newEl != this.curEl) {
+                    var w = newEl.getWidth();
+                    var h = newEl.getHeight();
+                    this.sides[0].show().setSize(w, this.size).anchorTo(el, 'tl', [0, -1]);
+                    this.sides[2].show().setSize(w, this.size).anchorTo(el, 'bl', [0, -1]);
+                    this.sides[1].show().setSize(this.size, h).anchorTo(el, 'tr', [-1, 0]);
+                    this.sides[3].show().setSize(this.size, h).anchorTo(el, 'tl', [-1, 0]);
+                    this.curEl = newEl;
+                }
+            }, this);
+            
+            this.unframeTask = new Ext.util.DelayedTask(function(){
+                if (this.initialized) {
+                    this.sides[0].hide();
+                    this.sides[1].hide();
+                    this.sides[2].hide();
+                    this.sides[3].hide();
+                    this.curEl = null;
+                }
+            }, this);
+            this.initialized = true;
+        }
+    },
+    
+    frame: function(el){
+        this.init();
+        this.unframeTask.cancel();
+        this.frameTask.delay(2, false, false, [el]);
+    },
+    
+    unframe: function(){
+        this.init();
+        this.unframeTask.delay(2);
+    },
+    
+    setSize: function(size){
+        this.size = size;
+    },
+    
+    setColor: function(color){
+        this.color = color;
+    }
+});
+
+Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');
+Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');
+
+Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {
+    constructor: function(el, relayTo, noFrame, frameEl){
+        Ext.a11y.Focusable.superclass.constructor.call(this);
+        
+        this.addEvents('focus', 'blur', 'left', 'right', 'up', 'down', 'esc', 'enter', 'space');
+        
+        if (el instanceof Ext.Component) {
+            this.el = el.el;
+            this.setComponent(el);
+        }
+        else {
+            this.el = Ext.get(el);
+            this.setComponent(null);
+        }
+        
+        this.setRelayTo(relayTo)
+        this.setNoFrame(noFrame);
+        this.setFrameEl(frameEl);
+        
+        this.init();
+        
+        Ext.a11y.FocusMgr.register(this);
+    },
+    
+    init: function(){
+        this.el.dom.tabIndex = '1';
+        this.el.addClass('x-a11y-focusable');
+        this.el.on({
+            focus: this.onFocus,
+            blur: this.onBlur,
+            keydown: this.onKeyDown,
+            scope: this
+        });
+    },
+    
+    setRelayTo: function(relayTo){
+        this.relayTo = relayTo ? Ext.a11y.FocusMgr.get(relayTo) : null;
+    },
+    
+    setNoFrame: function(noFrame){
+        this.noFrame = (noFrame === true) ? true : false;
+    },
+    
+    setFrameEl: function(frameEl){
+        this.frameEl = frameEl && Ext.get(frameEl) || this.el;
+    },
+    
+    setComponent: function(cmp){
+        this.component = cmp || null;
+    },
+    
+    onKeyDown: function(e, t){
+        var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;
+        
+        tf = (t !== this.el.dom) ? Ext.a11y.FocusMgr.get(t, true) : this;
+        if (!tf) {
+            // this can happen when you are on a focused item within a panel body
+            // that is not a Ext.a11y.Focusable
+            tf = Ext.a11y.FocusMgr.get(Ext.fly(t).parent('.x-a11y-focusable'));
+        }
+        
+        if (SK[k] !== undefined) {
+            ret = this.fireEvent(SK[k], e, t, tf, this);
+        }
+        if (ret === false || this.fireEvent('keydown', e, t, tf, this) === false) {
+            e.stopEvent();
+        }
+    },
+    
+    focus: function(){
+        this.el.dom.focus();
+    },
+    
+    blur: function(){
+        this.el.dom.blur();
+    },
+    
+    onFocus: function(e, t){
+        this.el.addClass('x-a11y-focused');
+        if (this.relayTo) {
+            this.relayTo.el.addClass('x-a11y-focused-relay');
+            if (!this.relayTo.noFrame) {
+                Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);
+            }
+            if (!this.noFrame) {
+                Ext.a11y.RelayFrame.frame(this.frameEl);
+            }
+        }
+        else {
+            if (!this.noFrame) {
+                Ext.a11y.FocusFrame.frame(this.frameEl);
+            }
+        }
+        
+        this.fireEvent('focus', e, t, this);
+    },
+    
+    onBlur: function(e, t){
+        if (this.relayTo) {
+            this.relayTo.el.removeClass('x-a11y-focused-relay');
+            Ext.a11y.RelayFrame.unframe();
+        }
+        this.el.removeClass('x-a11y-focused');
+        Ext.a11y.FocusFrame.unframe();
+        this.fireEvent('blur', e, t, this);
+    },
+    
+    destroy: function(){
+        this.el.un('keydown', this.onKeyDown);
+        this.el.un('focus', this.onFocus);
+        this.el.un('blur', this.onBlur);
+        this.el.removeClass('x-a11y-focusable');
+        this.el.removeClass('x-a11y-focused');
+        if (this.relayTo) {
+            this.relayTo.el.removeClass('x-a11y-focused-relay');
+        }
+    }
+});
+
+Ext.a11y.FocusItem = Ext.extend(Object, {
+    constructor: function(el, enableTabbing){
+        Ext.a11y.FocusItem.superclass.constructor.call(this);
+        
+        this.el = Ext.get(el);
+        this.fi = new Ext.a11y.Focusable(el);
+        this.fi.setComponent(this);
+        
+        this.fi.on('tab', this.onTab, this);
+        
+        this.enableTabbing = enableTabbing === true ? true : false;
+    },
+    
+    getEnterItem: function(){
+        if (this.enableTabbing) {
+            var items = this.getFocusItems();
+            if (items && items.length) {
+                return items[0];
+            }
+        }
+    },
+    
+    getFocusItems: function(){
+        if (this.enableTabbing) {
+            return this.el.query('a, button, input, select');
+        }
+        return null;
+    },
+    
+    onTab: function(e, t){
+        var items = this.getFocusItems(), i;
+        
+        if (items && items.length && (i = items.indexOf(t)) !== -1) {
+            if (e.shiftKey && i > 0) {
+                e.stopEvent();
+                items[i - 1].focus();
+                Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
+                return;
+            }
+            else 
+                if (!e.shiftKey && i < items.length - 1) {
+                    e.stopEvent();
+                    items[i + 1].focus();
+                    Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
+                    return;
+                }
+        }
+    },
+    
+    focus: function(){
+        if (this.enableTabbing) {
+            var items = this.getFocusItems();
+            if (items && items.length) {
+                items[0].focus();
+                Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);
+                return;
+            }
+        }
+        this.fi.focus();
+    },
+    
+    blur: function(){
+        this.fi.blur();
+    }
+});
+
+Ext.a11y.FocusMgr = function(){
+    var all = new Ext.util.MixedCollection();
+    
+    return {
+        register: function(f){
+            all.add(f.el && Ext.id(f.el), f);
+        },
+        
+        unregister: function(f){
+            all.remove(f);
+        },
+        
+        get: function(el, noCreate){
+            return all.get(Ext.id(el)) || (noCreate ? false : new Ext.a11y.Focusable(el));
+        },
+        
+        all: all
+    }
+}();
+
+Ext.a11y.Focusable.SpecialKeys = {};
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';
+
+// we use the new observeClass method to fire our new initFocus method on components
+Ext.util.Observable.observeClass(Ext.Component);
+Ext.Component.on('render', function(cmp){
+    cmp.initFocus();
+    cmp.initARIA();
+});
+Ext.override(Ext.Component, {
+    initFocus: Ext.emptyFn,
+    initARIA: Ext.emptyFn
+});
+
+Ext.override(Ext.Container, {
+    isFocusable: true,
+    noFocus: false,
+    
+    // private
+    initFocus: function(){
+        if (!this.fi && !this.noFocus) {
+            this.fi = new Ext.a11y.Focusable(this);
+        }
+        this.mon(this.fi, {
+            focus: this.onFocus,
+            blur: this.onBlur,
+            tab: this.onTab,
+            enter: this.onEnter,
+            esc: this.onEsc,
+            scope: this
+        });
+        
+        if (this.hidden) {
+            this.isFocusable = false;
+        }
+        
+        this.on('show', function(){
+            this.isFocusable = true;
+        }, this);
+        this.on('hide', function(){
+            this.isFocusable = false;
+        }, this);
+    },
+    
+    focus: function(){
+        this.fi.focus();
+    },
+    
+    blur: function(){
+        this.fi.blur();
+    },
+    
+    enter: function(){
+        var eitem = this.getEnterItem();
+        if (eitem) {
+            eitem.focus();
+        }
+    },
+    
+    onFocus: Ext.emptyFn,
+    onBlur: Ext.emptyFn,
+    
+    onTab: function(e, t, tf){
+        var rf = tf.relayTo || tf;
+        if (rf.component && rf.component !== this) {
+            e.stopEvent();
+            var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);
+            item.focus();
+        }
+    },
+    
+    onEnter: function(e, t, tf){
+        // check to see if enter is pressed while "on" the panel
+        if (tf.component && tf.component === this) {
+            e.stopEvent();
+            this.enter();
+        }
+        e.stopPropagation();
+    },
+    
+    onEsc: function(e, t){
+        e.preventDefault();
+        
+        // check to see if esc is pressed while "inside" the panel
+        // or while "on" the panel
+        if (t === this.el.dom) {
+            // "on" the panel, check if this panel has an owner panel and focus that
+            // we dont stop the event in this case so that this same check will be
+            // done for this ownerCt
+            if (this.ownerCt) {
+                this.ownerCt.focus();
+            }
+        }
+        else {
+            // we were inside the panel when esc was pressed,
+            // so go back "on" the panel
+            if (this.ownerCt && this.ownerCt.isFocusable) {
+                var si = this.ownerCt.getFocusItems();
+                
+                if (si && si.getCount() > 1) {
+                    e.stopEvent();
+                }
+            }
+            this.focus();
+        }
+    },
+    
+    getFocusItems: function(){
+        return this.items &&
+            this.items.filterBy(function(o){
+                return o.isFocusable;
+            }) ||
+            null;
+    },
+    
+    getEnterItem: function(){
+        var ci = this.getFocusItems(), length = ci ? ci.getCount() : 0;
+        
+        if (length === 1) {
+            return ci.first().getEnterItem && ci.first().getEnterItem() || ci.first();
+        }
+        else if (length > 1) {
+            return ci.first();
+        }
+    },
+    
+    getNextFocus: function(current){
+        var items = this.getFocusItems(), next = current, i = items.indexOf(current), length = items.getCount();
+        
+        if (i === length - 1) {
+            next = items.first();
+        }
+        else {
+            next = items.get(i + 1);
+        }
+        return next;
+    },
+    
+    getPreviousFocus: function(current){
+        var items = this.getFocusItems(), prev = current, i = items.indexOf(current), length = items.getCount();
+        
+        if (i === 0) {
+            prev = items.last();
+        }
+        else {
+            prev = items.get(i - 1);
+        }
+        return prev;
+    },
+    
+    getFocusable : function() {
+        return this.fi;
+    }
+});
+
+Ext.override(Ext.Panel, {
+    /**
+     * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing. Default is <tt>false</tt>.
+     */        
+    getFocusItems: function(){
+        // items gets all the items inside the body
+        var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;
+
+        if (!items) {
+            items = new Ext.util.MixedCollection();
+            this.bodyFocus = this.bodyFocus || new Ext.a11y.FocusItem(this.body, this.enableTabbing);
+            items.add('body', this.bodyFocus);
+        }
+        // but panels can also have tbar, bbar, fbar
+        if (this.tbar && this.topToolbar) {
+            items.insert(0, this.topToolbar);
+        }
+        if (this.bbar && this.bottomToolbar) {
+            items.add(this.bottomToolbar);
+        }
+        if (this.fbar) {
+            items.add(this.fbar);
+        }
+        
+        return items;
+    }
+});
+
+Ext.override(Ext.TabPanel, {
+    // private
+    initFocus: function(){
+        Ext.TabPanel.superclass.initFocus.call(this);
+        this.mon(this.fi, {
+            left: this.onLeft,
+            right: this.onRight,
+            scope: this
+        });
+    },
+    
+    onLeft: function(e){
+        if (!this.activeTab) {
+            return;
+        }
+        e.stopEvent();
+        var prev = this.items.itemAt(this.items.indexOf(this.activeTab) - 1);
+        if (prev) {
+            this.setActiveTab(prev);
+        }
+        return false;
+    },
+    
+    onRight: function(e){
+        if (!this.activeTab) {
+            return;
+        }
+        e.stopEvent();
+        var next = this.items.itemAt(this.items.indexOf(this.activeTab) + 1);
+        if (next) {
+            this.setActiveTab(next);
+        }
+        return false;
+    }
+});
+
+Ext.override(Ext.tree.TreeNodeUI, {
+    // private
+    focus: function(){
+        this.node.getOwnerTree().bodyFocus.focus();
+    }
+});
+
+Ext.override(Ext.tree.TreePanel, {
+    // private
+    afterRender : function(){
+        Ext.tree.TreePanel.superclass.afterRender.call(this);
+        this.root.render();
+        if(!this.rootVisible){
+            this.root.renderChildren();
+        }
+        this.bodyFocus = new Ext.a11y.FocusItem(this.body.down('.x-tree-root-ct'));
+        this.bodyFocus.fi.setFrameEl(this.body);
+    } 
+});
+
+Ext.override(Ext.grid.GridPanel, {
+    initFocus: function(){
+        Ext.grid.GridPanel.superclass.initFocus.call(this);
+        this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);
+        this.bodyFocus.fi.setFrameEl(this.body);
+    }
+});
+
+Ext.override(Ext.Button, {
+    isFocusable: true,
+    noFocus: false,
+    
+    initFocus: function(){
+        Ext.Button.superclass.initFocus.call(this);
+        this.fi = this.fi || new Ext.a11y.Focusable(this.btnEl, null, null, this.el);
+        this.fi.setComponent(this);
+        
+        this.mon(this.fi, {
+            focus: this.onFocus,
+            blur: this.onBlur,
+            scope: this
+        });
+        
+        if (this.menu) {
+            this.mon(this.fi, 'down', this.showMenu, this);
+            this.on('menuhide', this.focus, this);
+        }
+        
+        if (this.hidden) {
+            this.isFocusable = false;
+        }
+        
+        this.on('show', function(){
+            this.isFocusable = true;
+        }, this);
+        this.on('hide', function(){
+            this.isFocusable = false;
+        }, this);
+    },
+    
+    focus: function(){
+        this.fi.focus();
+    },
+    
+    blur: function(){
+        this.fi.blur();
+    },
+    
+    onFocus: function(){
+        if (!this.disabled) {
+            this.el.addClass("x-btn-focus");
+        }
+    },
+    
+    onBlur: function(){
+        this.el.removeClass("x-btn-focus");
+    }
+});
+
+Ext.override(Ext.Toolbar, {
+    initFocus: function(){
+        Ext.Toolbar.superclass.initFocus.call(this);
+        this.mon(this.fi, {
+            left: this.onLeft,
+            right: this.onRight,
+            scope: this
+        });
+        
+        this.on('focus', this.onButtonFocus, this, {
+            stopEvent: true
+        });
+    },
+    
+    add: function(){
+        var item = Ext.Toolbar.superclass.add.apply(this, arguments);
+        if(!item || !item.events) {
+            return item;
+        }
+        if (item.rendered && item.fi !== undefined) {
+            item.fi.setRelayTo(this.el);
+            this.relayEvents(item.fi, ['focus']);
+        }
+        else {
+            item.on('render', function(){
+                if (item.fi !== undefined) {
+                    item.fi.setRelayTo(this.el);
+                    this.relayEvents(item.fi, ['focus']);
+                }
+            }, this, {
+                single: true
+            });
+        }
+        return item;
+    },
+    
+    onFocus: function(){
+        var items = this.getFocusItems();
+        if (items && items.getCount() > 0) {
+            if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {
+                this.lastFocus.focus();
+            }
+            else {
+                items.first().focus();
+            }
+        }
+    },
+    
+    onButtonFocus: function(e, t, tf){
+        this.lastFocus = tf.component || null;
+    },
+    
+    onLeft: function(e, t, tf){
+        e.stopEvent();
+        this.getPreviousFocus(tf.component).focus();
+    },
+    
+    onRight: function(e, t, tf){
+        e.stopEvent();
+        this.getNextFocus(tf.component).focus();
+    },
+    
+    getEnterItem: Ext.emptyFn,
+    onTab: Ext.emptyFn,
+    onEsc: Ext.emptyFn
+});
+
+Ext.override(Ext.menu.BaseItem, {
+    initFocus: function(){
+        this.fi = new Ext.a11y.Focusable(this, this.parentMenu && this.parentMenu.el || null, true);
+    }
+});
+
+Ext.override(Ext.menu.Menu, {
+    initFocus: function(){
+        this.fi = new Ext.a11y.Focusable(this);
+        this.focusEl = this.fi;
+    }
+});
+
+Ext.a11y.WindowMgr = new Ext.WindowGroup();
+
+Ext.apply(Ext.WindowMgr, {
+    bringToFront: function(win){
+        Ext.a11y.WindowMgr.bringToFront.call(this, win);
+        if (win.modal) {
+            win.enter();
+        }
+        else {
+            win.focus();
+        }
+    }
+});
+
+Ext.override(Ext.Window, {
+    initFocus: function(){
+        Ext.Window.superclass.initFocus.call(this);
+        this.on('beforehide', function(){
+            Ext.a11y.RelayFrame.unframe();
+            Ext.a11y.FocusFrame.unframe();
+        });
+    }
+});
+
+Ext.override(Ext.form.Field, {
+    isFocusable: true,
+    noFocus: false,
+    
+    initFocus: function(){
+        this.fi = this.fi || new Ext.a11y.Focusable(this, null, true);
+        
+        Ext.form.Field.superclass.initFocus.call(this);
+        
+        if (this.hidden) {
+            this.isFocusable = false;
+        }
+        
+        this.on('show', function(){
+            this.isFocusable = true;
+        }, this);
+        this.on('hide', function(){
+            this.isFocusable = false;
+        }, this);
+    }
+});
+
+Ext.override(Ext.FormPanel, {
+    initFocus: function(){
+        Ext.FormPanel.superclass.initFocus.call(this);
+        this.on('focus', this.onFieldFocus, this, {
+            stopEvent: true
+        });
+    },
+    
+    // private
+    createForm: function(){
+        delete this.initialConfig.listeners;
+        var form = new Ext.form.BasicForm(null, this.initialConfig);
+        form.afterMethod('add', this.formItemAdd, this);
+        return form;
+    },
+    
+    formItemAdd: function(item){
+        item.on('render', function(field){
+            field.fi.setRelayTo(this.el);
+            this.relayEvents(field.fi, ['focus']);
+        }, this, {
+            single: true
+        });
+    },
+    
+    onFocus: function(){
+        var items = this.getFocusItems();
+        if (items && items.getCount() > 0) {
+            if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {
+                this.lastFocus.focus();
+            }
+            else {
+                items.first().focus();
+            }
+        }
+    },
+    
+    onFieldFocus: function(e, t, tf){
+        this.lastFocus = tf.component || null;
+    },
+    
+    onTab: function(e, t, tf){
+        if (tf.relayTo.component === this) {
+            var item = e.shiftKey ? this.getPreviousFocus(tf.component) : this.getNextFocus(tf.component);
+            
+            if (item) {
+                ev.stopEvent();
+                item.focus();
+                return;
+            }
+        }
+        Ext.FormPanel.superclass.onTab.apply(this, arguments);
+    },
+    
+    getNextFocus: function(current){
+        var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();
+        
+        return (i < length - 1) ? items.get(i + 1) : false;
+    },
+    
+    getPreviousFocus: function(current){
+        var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();
+        
+        return (i > 0) ? items.get(i - 1) : false;
+    }
+});
+
+Ext.override(Ext.Viewport, {
+    initFocus: function(){
+        Ext.Viewport.superclass.initFocus.apply(this);
+        this.mon(Ext.get(document), 'focus', this.focus, this);
+        this.mon(Ext.get(document), 'blur', this.blur, this);
+        this.fi.setNoFrame(true);
+    },
+    
+    onTab: function(e, t, tf, f){
+        e.stopEvent();
+        
+        if (tf === f) {
+            items = this.getFocusItems();
+            if (items && items.getCount() > 0) {
+                items.first().focus();
+            }
+        }
+        else {
+            var rf = tf.relayTo || tf;
+            var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);
+            item.focus();
+        }
+    }
+});
+    
 })();
\ No newline at end of file