source: Dev/branches/rest-dojo-ui/client/dijit/_KeyNavContainer.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 7.9 KB
Line 
1define([
2        "dojo/_base/kernel", // kernel.deprecated
3        "./_Container",
4        "./_FocusMixin",
5        "dojo/_base/array", // array.forEach
6        "dojo/keys", // keys.END keys.HOME
7        "dojo/_base/declare", // declare
8        "dojo/_base/event", // event.stop
9        "dojo/dom-attr", // domAttr.set
10        "dojo/_base/lang" // lang.hitch
11], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
12
13/*=====
14        var _FocusMixin = dijit._FocusMixin;
15        var _Container = dijit._Container;
16=====*/
17
18        // module:
19        //              dijit/_KeyNavContainer
20        // summary:
21        //              A _Container with keyboard navigation of its children.
22
23        return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
24
25                // summary:
26                //              A _Container with keyboard navigation of its children.
27                // description:
28                //              To use this mixin, call connectKeyNavHandlers() in
29                //              postCreate().
30                //              It provides normalized keyboard and focusing code for Container
31                //              widgets.
32
33/*=====
34                // focusedChild: [protected] Widget
35                //              The currently focused child widget, or null if there isn't one
36                focusedChild: null,
37=====*/
38
39                // tabIndex: Integer
40                //              Tab index of the container; same as HTML tabIndex attribute.
41                //              Note then when user tabs into the container, focus is immediately
42                //              moved to the first item in the container.
43                tabIndex: "0",
44
45                connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
46                        // summary:
47                        //              Call in postCreate() to attach the keyboard handlers
48                        //              to the container.
49                        // preKeyCodes: keys[]
50                        //              Key codes for navigating to the previous child.
51                        // nextKeyCodes: keys[]
52                        //              Key codes for navigating to the next child.
53                        // tags:
54                        //              protected
55
56                        // TODO: call this automatically from my own postCreate()
57
58                        var keyCodes = (this._keyNavCodes = {});
59                        var prev = lang.hitch(this, "focusPrev");
60                        var next = lang.hitch(this, "focusNext");
61                        array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
62                        array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
63                        keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
64                        keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
65                        this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
66                        this.connect(this.domNode, "onfocus", "_onContainerFocus");
67                },
68
69                startupKeyNavChildren: function(){
70                        kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
71                },
72
73                startup: function(){
74                        this.inherited(arguments);
75                        array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
76                },
77
78                addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
79                        this.inherited(arguments);
80                        this._startupChild(widget);
81                },
82
83                focus: function(){
84                        // summary:
85                        //              Default focus() implementation: focus the first child.
86                        this.focusFirstChild();
87                },
88
89                focusFirstChild: function(){
90                        // summary:
91                        //              Focus the first focusable child in the container.
92                        // tags:
93                        //              protected
94                        this.focusChild(this._getFirstFocusableChild());
95                },
96
97                focusLastChild: function(){
98                        // summary:
99                        //              Focus the last focusable child in the container.
100                        // tags:
101                        //              protected
102                        this.focusChild(this._getLastFocusableChild());
103                },
104
105                focusNext: function(){
106                        // summary:
107                        //              Focus the next widget
108                        // tags:
109                        //              protected
110                        this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
111                },
112
113                focusPrev: function(){
114                        // summary:
115                        //              Focus the last focusable node in the previous widget
116                        //              (ex: go to the ComboButton icon section rather than button section)
117                        // tags:
118                        //              protected
119                        this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
120                },
121
122                focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
123                        // summary:
124                        //              Focus specified child widget.
125                        // widget:
126                        //              Reference to container's child widget
127                        // last:
128                        //              If true and if widget has multiple focusable nodes, focus the
129                        //              last one instead of the first one
130                        // tags:
131                        //              protected
132
133                        if(!widget){ return; }
134
135                        if(this.focusedChild && widget !== this.focusedChild){
136                                this._onChildBlur(this.focusedChild);   // used by _MenuBase
137                        }
138                        widget.set("tabIndex", this.tabIndex);  // for IE focus outline to appear, must set tabIndex before focs
139                        widget.focus(last ? "end" : "start");
140                        this._set("focusedChild", widget);
141                },
142
143                _startupChild: function(/*dijit._Widget*/ widget){
144                        // summary:
145                        //              Setup for each child widget
146                        // description:
147                        //              Sets tabIndex=-1 on each child, so that the tab key will
148                        //              leave the container rather than visiting each child.
149                        // tags:
150                        //              private
151
152                        widget.set("tabIndex", "-1");
153
154                        this.connect(widget, "_onFocus", function(){
155                                // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
156                                widget.set("tabIndex", this.tabIndex);
157                        });
158                        this.connect(widget, "_onBlur", function(){
159                                widget.set("tabIndex", "-1");
160                        });
161                },
162
163                _onContainerFocus: function(evt){
164                        // summary:
165                        //              Handler for when the container gets focus
166                        // description:
167                        //              Initially the container itself has a tabIndex, but when it gets
168                        //              focus, switch focus to first child...
169                        // tags:
170                        //              private
171
172                        // Note that we can't use _onFocus() because switching focus from the
173                        // _onFocus() handler confuses the focus.js code
174                        // (because it causes _onFocusNode() to be called recursively)
175                        // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
176
177                        // Ignore spurious focus events:
178                        //      1. focus on a child widget bubbles on FF
179                        //      2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
180                        if(evt.target !== this.domNode || this.focusedChild){ return; }
181
182                        this.focusFirstChild();
183
184                        // and then set the container's tabIndex to -1,
185                        // (don't remove as that breaks Safari 4)
186                        // so that tab or shift-tab will go to the fields after/before
187                        // the container, rather than the container itself
188                        domAttr.set(this.domNode, "tabIndex", "-1");
189                },
190
191                _onBlur: function(evt){
192                        // When focus is moved away the container, and its descendant (popup) widgets,
193                        // then restore the container's tabIndex so that user can tab to it again.
194                        // Note that using _onBlur() so that this doesn't happen when focus is shifted
195                        // to one of my child widgets (typically a popup)
196                        if(this.tabIndex){
197                                domAttr.set(this.domNode, "tabIndex", this.tabIndex);
198                        }
199                        this.focusedChild = null;
200                        this.inherited(arguments);
201                },
202
203                _onContainerKeypress: function(evt){
204                        // summary:
205                        //              When a key is pressed, if it's an arrow key etc. then
206                        //              it's handled here.
207                        // tags:
208                        //              private
209                        if(evt.ctrlKey || evt.altKey){ return; }
210                        var func = this._keyNavCodes[evt.charOrCode];
211                        if(func){
212                                func();
213                                event.stop(evt);
214                        }
215                },
216
217                _onChildBlur: function(/*dijit._Widget*/ /*===== widget =====*/){
218                        // summary:
219                        //              Called when focus leaves a child widget to go
220                        //              to a sibling widget.
221                        //              Used by MenuBase.js (TODO: move code there)
222                        // tags:
223                        //              protected
224                },
225
226                _getFirstFocusableChild: function(){
227                        // summary:
228                        //              Returns first child that can be focused
229                        return this._getNextFocusableChild(null, 1);    // dijit._Widget
230                },
231
232                _getLastFocusableChild: function(){
233                        // summary:
234                        //              Returns last child that can be focused
235                        return this._getNextFocusableChild(null, -1);   // dijit._Widget
236                },
237
238                _getNextFocusableChild: function(child, dir){
239                        // summary:
240                        //              Returns the next or previous focusable child, compared
241                        //              to "child"
242                        // child: Widget
243                        //              The current widget
244                        // dir: Integer
245                        //              * 1 = after
246                        //              * -1 = before
247                        if(child){
248                                child = this._getSiblingOfChild(child, dir);
249                        }
250                        var children = this.getChildren();
251                        for(var i=0; i < children.length; i++){
252                                if(!child){
253                                        child = children[(dir>0) ? 0 : (children.length-1)];
254                                }
255                                if(child.isFocusable()){
256                                        return child;   // dijit._Widget
257                                }
258                                child = this._getSiblingOfChild(child, dir);
259                        }
260                        // no focusable child found
261                        return null;    // dijit._Widget
262                }
263        });
264});
Note: See TracBrowser for help on using the repository browser.