X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/2e847cf21b8ab9d15fa167b315ca5b2fa92638fc..6a7e4474cba9d8be4b2ec445e10f1691f7277c50:/examples/ux/Focus.js diff --git a/examples/ux/Focus.js b/examples/ux/Focus.js index e71035d8..919aef30 100644 --- a/examples/ux/Focus.js +++ b/examples/ux/Focus.js @@ -1,814 +1,814 @@ /*! - * 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(){ -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(); - } - } -}); - +(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(); + } + } +}); + })(); \ No newline at end of file