(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
true to enable tabbing. Default is false.
     */
       
    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
       
});
   
},
   
    addItem
: function(item){
       
Ext.Toolbar.superclass.add.apply(this, arguments);
       
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();
       
}
   
}
});
   
})();