source: [view]
define("dijit/_KeyNavContainer", ["dojo", "dijit", "dijit/_Container"], function(dojo, dijit) {
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);