UNPKG

5.19 kBJavaScriptView Raw
1/*******************************************
2 * *
3 * This util was created by Marius Olbertz *
4 * Please thank Marius on GitHub /owlbertz *
5 * or the web http://www.mariusolbertz.de/ *
6 * *
7 ******************************************/
8
9'use strict';
10
11import $ from 'jquery';
12import { rtl as Rtl } from './foundation.util.core';
13
14const keyCodes = {
15 9: 'TAB',
16 13: 'ENTER',
17 27: 'ESCAPE',
18 32: 'SPACE',
19 35: 'END',
20 36: 'HOME',
21 37: 'ARROW_LEFT',
22 38: 'ARROW_UP',
23 39: 'ARROW_RIGHT',
24 40: 'ARROW_DOWN'
25}
26
27var commands = {}
28
29// Functions pulled out to be referenceable from internals
30function findFocusable($element) {
31 if(!$element) {return false; }
32 return $element.find('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]').filter(function() {
33 if (!$(this).is(':visible') || $(this).attr('tabindex') < 0) { return false; } //only have visible elements and those that have a tabindex greater or equal 0
34 return true;
35 });
36}
37
38function parseKey(event) {
39 var key = keyCodes[event.which || event.keyCode] || String.fromCharCode(event.which).toUpperCase();
40
41 // Remove un-printable characters, e.g. for `fromCharCode` calls for CTRL only events
42 key = key.replace(/\W+/, '');
43
44 if (event.shiftKey) key = `SHIFT_${key}`;
45 if (event.ctrlKey) key = `CTRL_${key}`;
46 if (event.altKey) key = `ALT_${key}`;
47
48 // Remove trailing underscore, in case only modifiers were used (e.g. only `CTRL_ALT`)
49 key = key.replace(/_$/, '');
50
51 return key;
52}
53
54var Keyboard = {
55 keys: getKeyCodes(keyCodes),
56
57 /**
58 * Parses the (keyboard) event and returns a String that represents its key
59 * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE
60 * @param {Event} event - the event generated by the event handler
61 * @return String key - String that represents the key pressed
62 */
63 parseKey: parseKey,
64
65 /**
66 * Handles the given (keyboard) event
67 * @param {Event} event - the event generated by the event handler
68 * @param {String} component - Foundation component's name, e.g. Slider or Reveal
69 * @param {Objects} functions - collection of functions that are to be executed
70 */
71 handleKey(event, component, functions) {
72 var commandList = commands[component],
73 keyCode = this.parseKey(event),
74 cmds,
75 command,
76 fn;
77
78 if (!commandList) return console.warn('Component not defined!');
79
80 if (typeof commandList.ltr === 'undefined') { // this component does not differentiate between ltr and rtl
81 cmds = commandList; // use plain list
82 } else { // merge ltr and rtl: if document is rtl, rtl overwrites ltr and vice versa
83 if (Rtl()) cmds = $.extend({}, commandList.ltr, commandList.rtl);
84
85 else cmds = $.extend({}, commandList.rtl, commandList.ltr);
86 }
87 command = cmds[keyCode];
88
89 fn = functions[command];
90 if (fn && typeof fn === 'function') { // execute function if exists
91 var returnValue = fn.apply();
92 if (functions.handled || typeof functions.handled === 'function') { // execute function when event was handled
93 functions.handled(returnValue);
94 }
95 } else {
96 if (functions.unhandled || typeof functions.unhandled === 'function') { // execute function when event was not handled
97 functions.unhandled();
98 }
99 }
100 },
101
102 /**
103 * Finds all focusable elements within the given `$element`
104 * @param {jQuery} $element - jQuery object to search within
105 * @return {jQuery} $focusable - all focusable elements within `$element`
106 */
107
108 findFocusable: findFocusable,
109
110 /**
111 * Returns the component name name
112 * @param {Object} component - Foundation component, e.g. Slider or Reveal
113 * @return String componentName
114 */
115
116 register(componentName, cmds) {
117 commands[componentName] = cmds;
118 },
119
120
121 // TODO9438: These references to Keyboard need to not require global. Will 'this' work in this context?
122 //
123 /**
124 * Traps the focus in the given element.
125 * @param {jQuery} $element jQuery object to trap the foucs into.
126 */
127 trapFocus($element) {
128 var $focusable = findFocusable($element),
129 $firstFocusable = $focusable.eq(0),
130 $lastFocusable = $focusable.eq(-1);
131
132 $element.on('keydown.zf.trapfocus', function(event) {
133 if (event.target === $lastFocusable[0] && parseKey(event) === 'TAB') {
134 event.preventDefault();
135 $firstFocusable.focus();
136 }
137 else if (event.target === $firstFocusable[0] && parseKey(event) === 'SHIFT_TAB') {
138 event.preventDefault();
139 $lastFocusable.focus();
140 }
141 });
142 },
143 /**
144 * Releases the trapped focus from the given element.
145 * @param {jQuery} $element jQuery object to release the focus for.
146 */
147 releaseFocus($element) {
148 $element.off('keydown.zf.trapfocus');
149 }
150}
151
152/*
153 * Constants for easier comparing.
154 * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE
155 */
156function getKeyCodes(kcs) {
157 var k = {};
158 for (var kc in kcs) k[kcs[kc]] = kcs[kc];
159 return k;
160}
161
162export {Keyboard};