/* This file is part of Ext JS 3.4 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact. Build date: 2013-04-03 15:07:25 */ (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 }); }, 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(); } } }); })();