|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "unstable" |
|
9 }; |
|
10 |
|
11 const { observer: keyboardObserver } = require("./observer"); |
|
12 const { getKeyForCode, normalize, isFunctionKey, |
|
13 MODIFIERS } = require("./utils"); |
|
14 |
|
15 /** |
|
16 * Register a global `hotkey` that executes `listener` when the key combination |
|
17 * in `hotkey` is pressed. If more then one `listener` is registered on the same |
|
18 * key combination only last one will be executed. |
|
19 * |
|
20 * @param {string} hotkey |
|
21 * Key combination in the format of 'modifier key'. |
|
22 * |
|
23 * Examples: |
|
24 * |
|
25 * "accel s" |
|
26 * "meta shift i" |
|
27 * "control alt d" |
|
28 * |
|
29 * Modifier keynames: |
|
30 * |
|
31 * - **shift**: The Shift key. |
|
32 * - **alt**: The Alt key. On the Macintosh, this is the Option key. On |
|
33 * Macintosh this can only be used in conjunction with another modifier, |
|
34 * since `Alt+Letter` combinations are reserved for entering special |
|
35 * characters in text. |
|
36 * - **meta**: The Meta key. On the Macintosh, this is the Command key. |
|
37 * - **control**: The Control key. |
|
38 * - **accel**: The key used for keyboard shortcuts on the user's platform, |
|
39 * which is Control on Windows and Linux, and Command on Mac. Usually, this |
|
40 * would be the value you would use. |
|
41 * |
|
42 * @param {function} listener |
|
43 * Function to execute when the `hotkey` is executed. |
|
44 */ |
|
45 exports.register = function register(hotkey, listener) { |
|
46 hotkey = normalize(hotkey); |
|
47 hotkeys[hotkey] = listener; |
|
48 }; |
|
49 |
|
50 /** |
|
51 * Unregister a global `hotkey`. If passed `listener` is not the one registered |
|
52 * for the given `hotkey`, the call to this function will be ignored. |
|
53 * |
|
54 * @param {string} hotkey |
|
55 * Key combination in the format of 'modifier key'. |
|
56 * @param {function} listener |
|
57 * Function that will be invoked when the `hotkey` is pressed. |
|
58 */ |
|
59 exports.unregister = function unregister(hotkey, listener) { |
|
60 hotkey = normalize(hotkey); |
|
61 if (hotkeys[hotkey] === listener) |
|
62 delete hotkeys[hotkey]; |
|
63 }; |
|
64 |
|
65 /** |
|
66 * Map of hotkeys and associated functions. |
|
67 */ |
|
68 const hotkeys = exports.hotkeys = {}; |
|
69 |
|
70 keyboardObserver.on("keydown", function onKeypress(event, window) { |
|
71 let key, modifiers = []; |
|
72 let isChar = "isChar" in event && event.isChar; |
|
73 let which = "which" in event ? event.which : null; |
|
74 let keyCode = "keyCode" in event ? event.keyCode : null; |
|
75 |
|
76 if ("shiftKey" in event && event.shiftKey) |
|
77 modifiers.push("shift"); |
|
78 if ("altKey" in event && event.altKey) |
|
79 modifiers.push("alt"); |
|
80 if ("ctrlKey" in event && event.ctrlKey) |
|
81 modifiers.push("control"); |
|
82 if ("metaKey" in event && event.metaKey) |
|
83 modifiers.push("meta"); |
|
84 |
|
85 // If it's not a printable character then we fall back to a human readable |
|
86 // equivalent of one of the following constants. |
|
87 // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl |
|
88 key = getKeyForCode(keyCode); |
|
89 |
|
90 // If only non-function (f1 - f24) key or only modifiers are pressed we don't |
|
91 // have a valid combination so we return immediately (Also, sometimes |
|
92 // `keyCode` may be one for the modifier which means we do not have a |
|
93 // modifier). |
|
94 if (!key || (!isFunctionKey(key) && !modifiers.length) || key in MODIFIERS) |
|
95 return; |
|
96 |
|
97 let combination = normalize({ key: key, modifiers: modifiers }); |
|
98 let hotkey = hotkeys[combination]; |
|
99 |
|
100 if (hotkey) { |
|
101 try { |
|
102 hotkey(); |
|
103 } catch (exception) { |
|
104 console.exception(exception); |
|
105 } finally { |
|
106 // Work around bug 582052 by preventing the (nonexistent) default action. |
|
107 event.preventDefault(); |
|
108 } |
|
109 } |
|
110 }); |