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