diff options
Diffstat (limited to 'lib/dijit/_KeyNavContainer.js')
-rw-r--r-- | lib/dijit/_KeyNavContainer.js | 338 |
1 files changed, 252 insertions, 86 deletions
diff --git a/lib/dijit/_KeyNavContainer.js b/lib/dijit/_KeyNavContainer.js index 839ba8319..aa1bcad4b 100644 --- a/lib/dijit/_KeyNavContainer.js +++ b/lib/dijit/_KeyNavContainer.js @@ -1,95 +1,261 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit._KeyNavContainer"]){ -dojo._hasResource["dijit._KeyNavContainer"]=true; +if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._KeyNavContainer"] = true; dojo.provide("dijit._KeyNavContainer"); dojo.require("dijit._Container"); -dojo.declare("dijit._KeyNavContainer",dijit._Container,{tabIndex:"0",_keyNavCodes:{},connectKeyNavHandlers:function(_1,_2){ -var _3=(this._keyNavCodes={}); -var _4=dojo.hitch(this,this.focusPrev); -var _5=dojo.hitch(this,this.focusNext); -dojo.forEach(_1,function(_6){ -_3[_6]=_4; -}); -dojo.forEach(_2,function(_7){ -_3[_7]=_5; -}); -this.connect(this.domNode,"onkeypress","_onContainerKeypress"); -this.connect(this.domNode,"onfocus","_onContainerFocus"); -},startupKeyNavChildren:function(){ -dojo.forEach(this.getChildren(),dojo.hitch(this,"_startupChild")); -},addChild:function(_8,_9){ -dijit._KeyNavContainer.superclass.addChild.apply(this,arguments); -this._startupChild(_8); -},focus:function(){ -this.focusFirstChild(); -},focusFirstChild:function(){ -var _a=this._getFirstFocusableChild(); -if(_a){ -this.focusChild(_a); -} -},focusNext:function(){ -var _b=this._getNextFocusableChild(this.focusedChild,1); -this.focusChild(_b); -},focusPrev:function(){ -var _c=this._getNextFocusableChild(this.focusedChild,-1); -this.focusChild(_c,true); -},focusChild:function(_d,_e){ -if(this.focusedChild&&_d!==this.focusedChild){ -this._onChildBlur(this.focusedChild); -} -_d.focus(_e?"end":"start"); -this.focusedChild=_d; -},_startupChild:function(_f){ -_f.set("tabIndex","-1"); -this.connect(_f,"_onFocus",function(){ -_f.set("tabIndex",this.tabIndex); -}); -this.connect(_f,"_onBlur",function(){ -_f.set("tabIndex","-1"); -}); -},_onContainerFocus:function(evt){ -if(evt.target!==this.domNode){ -return; -} -this.focusFirstChild(); -dojo.attr(this.domNode,"tabIndex","-1"); -},_onBlur:function(evt){ -if(this.tabIndex){ -dojo.attr(this.domNode,"tabIndex",this.tabIndex); -} -this.inherited(arguments); -},_onContainerKeypress:function(evt){ -if(evt.ctrlKey||evt.altKey){ -return; -} -var _10=this._keyNavCodes[evt.charOrCode]; -if(_10){ -_10(); -dojo.stopEvent(evt); -} -},_onChildBlur:function(_11){ -},_getFirstFocusableChild:function(){ -return this._getNextFocusableChild(null,1); -},_getNextFocusableChild:function(_12,dir){ -if(_12){ -_12=this._getSiblingOfChild(_12,dir); -} -var _13=this.getChildren(); -for(var i=0;i<_13.length;i++){ -if(!_12){ -_12=_13[(dir>0)?0:(_13.length-1)]; -} -if(_12.isFocusable()){ -return _12; -} -_12=this._getSiblingOfChild(_12,dir); -} -return null; -}}); + + +dojo.declare("dijit._KeyNavContainer", + dijit._Container, + { + + // summary: + // A _Container with keyboard navigation of its children. + // description: + // To use this mixin, call connectKeyNavHandlers() in + // postCreate() and call startupKeyNavChildren() in startup(). + // It provides normalized keyboard and focusing code for Container + // widgets. +/*===== + // focusedChild: [protected] Widget + // The currently focused child widget, or null if there isn't one + focusedChild: null, +=====*/ + + // tabIndex: Integer + // Tab index of the container; same as HTML tabIndex attribute. + // Note then when user tabs into the container, focus is immediately + // moved to the first item in the container. + tabIndex: "0", + + _keyNavCodes: {}, + + connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){ + // summary: + // Call in postCreate() to attach the keyboard handlers + // to the container. + // preKeyCodes: dojo.keys[] + // Key codes for navigating to the previous child. + // nextKeyCodes: dojo.keys[] + // Key codes for navigating to the next child. + // tags: + // protected + + var keyCodes = (this._keyNavCodes = {}); + var prev = dojo.hitch(this, this.focusPrev); + var next = dojo.hitch(this, this.focusNext); + dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; }); + dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; }); + keyCodes[dojo.keys.HOME] = dojo.hitch(this, "focusFirstChild"); + keyCodes[dojo.keys.END] = dojo.hitch(this, "focusLastChild"); + this.connect(this.domNode, "onkeypress", "_onContainerKeypress"); + this.connect(this.domNode, "onfocus", "_onContainerFocus"); + }, + + startupKeyNavChildren: function(){ + // summary: + // Call in startup() to set child tabindexes to -1 + // tags: + // protected + dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild")); + }, + + addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){ + // summary: + // Add a child to our _Container + dijit._KeyNavContainer.superclass.addChild.apply(this, arguments); + this._startupChild(widget); + }, + + focus: function(){ + // summary: + // Default focus() implementation: focus the first child. + this.focusFirstChild(); + }, + + focusFirstChild: function(){ + // summary: + // Focus the first focusable child in the container. + // tags: + // protected + var child = this._getFirstFocusableChild(); + if(child){ // edge case: Menu could be empty or hidden + this.focusChild(child); + } + }, + + focusLastChild: function(){ + // summary: + // Focus the last focusable child in the container. + // tags: + // protected + var child = this._getLastFocusableChild(); + if(child){ // edge case: Menu could be empty or hidden + this.focusChild(child); + } + }, + + focusNext: function(){ + // summary: + // Focus the next widget + // tags: + // protected + var child = this._getNextFocusableChild(this.focusedChild, 1); + this.focusChild(child); + }, + + focusPrev: function(){ + // summary: + // Focus the last focusable node in the previous widget + // (ex: go to the ComboButton icon section rather than button section) + // tags: + // protected + var child = this._getNextFocusableChild(this.focusedChild, -1); + this.focusChild(child, true); + }, + + focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){ + // summary: + // Focus widget. + // widget: + // Reference to container's child widget + // last: + // If true and if widget has multiple focusable nodes, focus the + // last one instead of the first one + // tags: + // protected + + if(this.focusedChild && widget !== this.focusedChild){ + this._onChildBlur(this.focusedChild); + } + widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs + widget.focus(last ? "end" : "start"); + this._set("focusedChild", widget); + }, + + _startupChild: function(/*dijit._Widget*/ widget){ + // summary: + // Setup for each child widget + // description: + // Sets tabIndex=-1 on each child, so that the tab key will + // leave the container rather than visiting each child. + // tags: + // private + + widget.set("tabIndex", "-1"); + + this.connect(widget, "_onFocus", function(){ + // Set valid tabIndex so tabbing away from widget goes to right place, see #10272 + widget.set("tabIndex", this.tabIndex); + }); + this.connect(widget, "_onBlur", function(){ + widget.set("tabIndex", "-1"); + }); + }, + + _onContainerFocus: function(evt){ + // summary: + // Handler for when the container gets focus + // description: + // Initially the container itself has a tabIndex, but when it gets + // focus, switch focus to first child... + // tags: + // private + + // Note that we can't use _onFocus() because switching focus from the + // _onFocus() handler confuses the focus.js code + // (because it causes _onFocusNode() to be called recursively) + + // focus bubbles on Firefox, + // so just make sure that focus has really gone to the container + if(evt.target !== this.domNode){ return; } + + this.focusFirstChild(); + + // and then set the container's tabIndex to -1, + // (don't remove as that breaks Safari 4) + // so that tab or shift-tab will go to the fields after/before + // the container, rather than the container itself + dojo.attr(this.domNode, "tabIndex", "-1"); + }, + + _onBlur: function(evt){ + // When focus is moved away the container, and its descendant (popup) widgets, + // then restore the container's tabIndex so that user can tab to it again. + // Note that using _onBlur() so that this doesn't happen when focus is shifted + // to one of my child widgets (typically a popup) + if(this.tabIndex){ + dojo.attr(this.domNode, "tabIndex", this.tabIndex); + } + this.inherited(arguments); + }, + + _onContainerKeypress: function(evt){ + // summary: + // When a key is pressed, if it's an arrow key etc. then + // it's handled here. + // tags: + // private + if(evt.ctrlKey || evt.altKey){ return; } + var func = this._keyNavCodes[evt.charOrCode]; + if(func){ + func(); + dojo.stopEvent(evt); + } + }, + + _onChildBlur: function(/*dijit._Widget*/ widget){ + // summary: + // Called when focus leaves a child widget to go + // to a sibling widget. + // tags: + // protected + }, + + _getFirstFocusableChild: function(){ + // summary: + // Returns first child that can be focused + return this._getNextFocusableChild(null, 1); // dijit._Widget + }, + + _getLastFocusableChild: function(){ + // summary: + // Returns last child that can be focused + return this._getNextFocusableChild(null, -1); // dijit._Widget + }, + + _getNextFocusableChild: function(child, dir){ + // summary: + // Returns the next or previous focusable child, compared + // to "child" + // child: Widget + // The current widget + // dir: Integer + // * 1 = after + // * -1 = before + if(child){ + child = this._getSiblingOfChild(child, dir); + } + var children = this.getChildren(); + for(var i=0; i < children.length; i++){ + if(!child){ + child = children[(dir>0) ? 0 : (children.length-1)]; + } + if(child.isFocusable()){ + return child; // dijit._Widget + } + child = this._getSiblingOfChild(child, dir); + } + // no focusable child found + return null; // dijit._Widget + } + } +); + } |