]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/ui/KeyboardNavigation.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / ui / KeyboardNavigation.js
1 /**
2  * KeyboardNavigation.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /**
12  * This class handles keyboard navigation of controls and elements.
13  *
14  * @class tinymce.ui.KeyboardNavigation
15  */
16 define("tinymce/ui/KeyboardNavigation", [
17         "tinymce/ui/DomUtils"
18 ], function(DomUtils) {
19         "use strict";
20
21         /**
22          * Create a new KeyboardNavigation instance to handle the focus for a specific element.
23          *
24          * @constructor
25          * @param {Object} settings the settings object to define how keyboard navigation works.
26          *
27          * @setting {tinymce.ui.Control} root the root control navigation focus movement is scoped to this root.
28          * @setting {Array} items an array containing the items to move focus between. Every object in this array must have an
29          *                        id attribute which maps to the actual DOM element and it must be able to have focus i.e. tabIndex=-1.
30          * @setting {Function} onCancel the callback for when the user presses escape or otherwise indicates canceling.
31          * @setting {Function} onAction (optional) the action handler to call when the user activates an item.
32          * @setting {Boolean} enableLeftRight (optional, default) when true, the up/down arrows move through items.
33          * @setting {Boolean} enableUpDown (optional) when true, the up/down arrows move through items.
34          * Note for both up/down and left/right explicitly set both enableLeftRight and enableUpDown to true.
35          */
36         return function(settings) {
37                 var root = settings.root, enableUpDown = settings.enableUpDown !== false;
38                 var enableLeftRight = settings.enableLeftRight !== false;
39                 var items = settings.items, focussedId;
40
41                 /**
42                  * Initializes the items array if needed. This will collect items/elements
43                  * from the specified root control.
44                  */
45                 function initItems() {
46                         if (!items) {
47                                 items = [];
48
49                                 if (root.find) {
50                                         // Root is a container then get child elements using the UI API
51                                         root.find('*').each(function(ctrl) {
52                                                 if (ctrl.canFocus) {
53                                                         items.push(ctrl.getEl());
54                                                 }
55                                         });
56                                 } else {
57                                         // Root is a control/widget then get the child elements of that control
58                                         var elements = root.getEl().getElementsByTagName('*');
59                                         for (var i = 0; i < elements.length; i++) {
60                                                 if (elements[i].id && elements[i]) {
61                                                         items.push(elements[i]);
62                                                 }
63                                         }
64                                 }
65                         }
66                 }
67
68                 /**
69                  * Returns the currently focused element.
70                  *
71                  * @private
72                  * @return {Element} Currently focused element.
73                  */
74                 function getFocusElement() {
75                         return document.getElementById(focussedId);
76                 }
77
78                 /**
79                  * Returns the currently focused elements wai aria role.
80                  *
81                  * @private
82                  * @param {Element} elm Optional element to get role from.
83                  * @return {String} Role of specified element.
84                  */
85                 function getRole(elm) {
86                         elm = elm || getFocusElement();
87
88                         return elm && elm.getAttribute('role');
89                 }
90
91                 /**
92                  * Returns the role of the parent element.
93                  *
94                  * @private
95                  * @param {Element} elm Optional element to get parent role from.
96                  * @return {String} Role of the first parent that has a role.
97                  */
98                 function getParentRole(elm) {
99                         var role, parent = elm || getFocusElement();
100
101                         while ((parent = parent.parentNode)) {
102                                 if ((role = getRole(parent))) {
103                                         return role;
104                                 }
105                         }
106                 }
107
108                 /**
109                  * Returns an wai aria property by name.
110                  *
111                  * @private
112                  * @param {String} name Name of the aria property to get for example "disabled".
113                  * @return {String} Aria property value.
114                  */
115                 function getAriaProp(name) {
116                         var elm = document.getElementById(focussedId);
117
118                         if (elm) {
119                                 return elm.getAttribute('aria-' + name);
120                         }
121                 }
122
123                 /**
124                  * Executes the onAction event callback. This is when the user presses enter/space.
125                  *
126                  * @private
127                  */
128                 function action() {
129                         var focusElm = getFocusElement();
130
131                         if (focusElm && (focusElm.nodeName == "TEXTAREA" || focusElm.type == "text")) {
132                                 return;
133                         }
134
135                         if (settings.onAction) {
136                                 settings.onAction(focussedId);
137                         } else {
138                                 DomUtils.fire(getFocusElement(), 'click', {keyboard: true});
139                         }
140
141                         return true;
142                 }
143
144                 /**
145                  * Cancels the current navigation. The same as pressing the Esc key.
146                  *
147                  * @method cancel
148                  */
149                 function cancel() {
150                         var focusElm;
151
152                         if (settings.onCancel) {
153                                 if ((focusElm = getFocusElement())) {
154                                         focusElm.blur();
155                                 }
156
157                                 settings.onCancel();
158                         } else {
159                                 settings.root.fire('cancel');
160                         }
161                 }
162
163                 /**
164                  * Moves the focus to the next or previous item. It will wrap to start/end if it can't move.
165                  *
166                  * @method moveFocus
167                  * @param {Number} dir Direction for move -1 or 1.
168                  */
169                 function moveFocus(dir) {
170                         var idx = -1, focusElm, i;
171                         var visibleItems = [];
172
173                         function isVisible(elm) {
174                                 var rootElm = root ? root.getEl() : document.body;
175
176                                 while (elm && elm != rootElm) {
177                                         if (elm.style.display == 'none') {
178                                                 return false;
179                                         }
180
181                                         elm = elm.parentNode;
182                                 }
183
184                                 return true;
185                         }
186
187                         initItems();
188
189                         // TODO: Optimize this, will be slow on lots of items
190                         i = visibleItems.length;
191                         for (i = 0; i < items.length; i++) {
192                                 if (isVisible(items[i])) {
193                                         visibleItems.push(items[i]);
194                                 }
195                         }
196
197                         i = visibleItems.length;
198                         while (i--) {
199                                 if (visibleItems[i].id === focussedId) {
200                                         idx = i;
201                                         break;
202                                 }
203                         }
204
205                         idx += dir;
206                         if (idx < 0) {
207                                 idx = visibleItems.length - 1;
208                         } else if (idx >= visibleItems.length) {
209                                 idx = 0;
210                         }
211
212                         focusElm = visibleItems[idx];
213                         focusElm.focus();
214                         focussedId = focusElm.id;
215
216                         if (settings.actOnFocus) {
217                                 action();
218                         }
219                 }
220
221                 /**
222                  * Moves focus to the first item or the last focused item if root is a toolbar.
223                  *
224                  * @method focusFirst
225                  * @return {[type]} [description]
226                  */
227                 function focusFirst() {
228                         var i, rootRole;
229
230                         rootRole = getRole(settings.root.getEl());
231                         initItems();
232
233                         i = items.length;
234                         while (i--) {
235                                 if (rootRole == 'toolbar' && items[i].id === focussedId) {
236                                         items[i].focus();
237                                         return;
238                                 }
239                         }
240
241                         items[0].focus();
242                 }
243
244                 // Handle accessible keys
245                 root.on('keydown', function(e) {
246                         var DOM_VK_LEFT = 37, DOM_VK_RIGHT = 39, DOM_VK_UP = 38, DOM_VK_DOWN = 40;
247                         var DOM_VK_ESCAPE = 27, DOM_VK_ENTER = 14, DOM_VK_RETURN = 13, DOM_VK_SPACE = 32, DOM_VK_TAB = 9;
248                         var preventDefault;
249
250                         switch (e.keyCode) {
251                                 case DOM_VK_LEFT:
252                                         if (enableLeftRight) {
253                                                 if (settings.leftAction) {
254                                                         settings.leftAction();
255                                                 } else {
256                                                         moveFocus(-1);
257                                                 }
258
259                                                 preventDefault = true;
260                                         }
261                                         break;
262
263                                 case DOM_VK_RIGHT:
264                                         if (enableLeftRight) {
265                                                 if (getRole() == 'menuitem' && getParentRole() == 'menu') {
266                                                         if (getAriaProp('haspopup')) {
267                                                                 action();
268                                                         }
269                                                 } else {
270                                                         moveFocus(1);
271                                                 }
272
273                                                 preventDefault = true;
274                                         }
275                                         break;
276
277                                 case DOM_VK_UP:
278                                         if (enableUpDown) {
279                                                 moveFocus(-1);
280                                                 preventDefault = true;
281                                         }
282                                         break;
283
284                                 case DOM_VK_DOWN:
285                                         if (enableUpDown) {
286                                                 if (getRole() == 'menuitem' && getParentRole() == 'menubar') {
287                                                         action();
288                                                 } else if (getRole() == 'button' && getAriaProp('haspopup')) {
289                                                         action();
290                                                 } else {
291                                                         moveFocus(1);
292                                                 }
293
294                                                 preventDefault = true;
295                                         }
296                                         break;
297
298                                 case DOM_VK_TAB:
299                                         preventDefault = true;
300
301                                         if (e.shiftKey) {
302                                                 moveFocus(-1);
303                                         } else {
304                                                 moveFocus(1);
305                                         }
306                                         break;
307
308                                 case DOM_VK_ESCAPE:
309                                         preventDefault = true;
310                                         cancel();
311                                         break;
312
313                                 case DOM_VK_ENTER:
314                                 case DOM_VK_RETURN:
315                                 case DOM_VK_SPACE:
316                                         preventDefault = action();
317                                         break;
318                         }
319
320                         if (preventDefault) {
321                                 e.stopPropagation();
322                                 e.preventDefault();
323                         }
324                 });
325
326                 // Init on focus in
327                 root.on('focusin', function(e) {
328                         initItems();
329                         focussedId = e.target.id;
330                 });
331
332                 return {
333                         moveFocus: moveFocus,
334                         focusFirst: focusFirst,
335                         cancel: cancel
336                 };
337         };
338 });