]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/_KeyNavContainer.js.uncompressed.js
make precache_headlines_idle() start slower
[tt-rss.git] / lib / dijit / _KeyNavContainer.js.uncompressed.js
CommitLineData
1354d172
AD
1define("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 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});