|
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 { Cc, Ci } = require("chrome"); |
|
12 const runtime = require("../system/runtime"); |
|
13 const { isString } = require("../lang/type"); |
|
14 const array = require("../util/array"); |
|
15 |
|
16 |
|
17 const SWP = "{{SEPARATOR}}"; |
|
18 const SEPARATOR = "-" |
|
19 const INVALID_COMBINATION = "Hotkey key combination must contain one or more " + |
|
20 "modifiers and only one key"; |
|
21 |
|
22 // Map of modifier key mappings. |
|
23 const MODIFIERS = exports.MODIFIERS = { |
|
24 'accel': runtime.OS === "Darwin" ? 'meta' : 'control', |
|
25 'meta': 'meta', |
|
26 'control': 'control', |
|
27 'ctrl': 'control', |
|
28 'option': 'alt', |
|
29 'command': 'meta', |
|
30 'alt': 'alt', |
|
31 'shift': 'shift' |
|
32 }; |
|
33 |
|
34 // Hash of key:code pairs for all the chars supported by `nsIDOMKeyEvent`. |
|
35 // This is just a copy of the `nsIDOMKeyEvent` hash with normalized names. |
|
36 // @See: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl |
|
37 const CODES = exports.CODES = new function Codes() { |
|
38 let nsIDOMKeyEvent = Ci.nsIDOMKeyEvent; |
|
39 // Names that will be substituted with a shorter analogs. |
|
40 let aliases = { |
|
41 'subtract': '-', |
|
42 'add': '+', |
|
43 'equals': '=', |
|
44 'slash': '/', |
|
45 'backslash': '\\', |
|
46 'openbracket': '[', |
|
47 'closebracket': ']', |
|
48 'quote': '\'', |
|
49 'backquote': '`', |
|
50 'period': '.', |
|
51 'semicolon': ';', |
|
52 'comma': ',' |
|
53 }; |
|
54 |
|
55 // Normalizing keys and copying values to `this` object. |
|
56 Object.keys(nsIDOMKeyEvent).filter(function(key) { |
|
57 // Filter out only key codes. |
|
58 return key.indexOf('DOM_VK') === 0; |
|
59 }).map(function(key) { |
|
60 // Map to key:values |
|
61 return [ key, nsIDOMKeyEvent[key] ]; |
|
62 }).map(function([key, value]) { |
|
63 return [ key.replace('DOM_VK_', '').replace('_', '').toLowerCase(), value ]; |
|
64 }).forEach(function ([ key, value ]) { |
|
65 this[aliases[key] || key] = value; |
|
66 }, this); |
|
67 }; |
|
68 |
|
69 // Inverted `CODES` hash of `code:key`. |
|
70 const KEYS = exports.KEYS = new function Keys() { |
|
71 Object.keys(CODES).forEach(function(key) { |
|
72 this[CODES[key]] = key; |
|
73 }, this) |
|
74 } |
|
75 |
|
76 exports.getKeyForCode = function getKeyForCode(code) { |
|
77 return (code in KEYS) && KEYS[code]; |
|
78 }; |
|
79 exports.getCodeForKey = function getCodeForKey(key) { |
|
80 return (key in CODES) && CODES[key]; |
|
81 }; |
|
82 |
|
83 /** |
|
84 * Utility function that takes string or JSON that defines a `hotkey` and |
|
85 * returns normalized string version of it. |
|
86 * @param {JSON|String} hotkey |
|
87 * @param {String} [separator=" "] |
|
88 * Optional string that represents separator used to concatenate keys in the |
|
89 * given `hotkey`. |
|
90 * @returns {String} |
|
91 * @examples |
|
92 * |
|
93 * require("keyboard/hotkeys").normalize("b Shift accel"); |
|
94 * // 'control shift b' -> on windows & linux |
|
95 * // 'meta shift b' -> on mac |
|
96 * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); |
|
97 * // 'alt shift d' |
|
98 */ |
|
99 var normalize = exports.normalize = function normalize(hotkey, separator) { |
|
100 if (!isString(hotkey)) |
|
101 hotkey = toString(hotkey, separator); |
|
102 return toString(toJSON(hotkey, separator), separator); |
|
103 }; |
|
104 |
|
105 /* |
|
106 * Utility function that splits a string of characters that defines a `hotkey` |
|
107 * into modifier keys and the defining key. |
|
108 * @param {String} hotkey |
|
109 * @param {String} [separator=" "] |
|
110 * Optional string that represents separator used to concatenate keys in the |
|
111 * given `hotkey`. |
|
112 * @returns {JSON} |
|
113 * @examples |
|
114 * |
|
115 * require("keyboard/hotkeys").toJSON("accel shift b"); |
|
116 * // { key: 'b', modifiers: [ 'control', 'shift' ] } -> on windows & linux |
|
117 * // { key: 'b', modifiers: [ 'meta', 'shift' ] } -> on mac |
|
118 * |
|
119 * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); |
|
120 * // { key: 'd', modifiers: [ 'alt', 'shift' ] } |
|
121 */ |
|
122 var toJSON = exports.toJSON = function toJSON(hotkey, separator) { |
|
123 separator = separator || SEPARATOR; |
|
124 // Since default separator is `-`, combination may take form of `alt--`. To |
|
125 // avoid misbehavior we replace `--` with `-{{SEPARATOR}}` where |
|
126 // `{{SEPARATOR}}` can be swapped later. |
|
127 hotkey = hotkey.toLowerCase().replace(separator + separator, separator + SWP); |
|
128 |
|
129 let value = {}; |
|
130 let modifiers = []; |
|
131 let keys = hotkey.split(separator); |
|
132 keys.forEach(function(name) { |
|
133 // If name is `SEPARATOR` than we swap it back. |
|
134 if (name === SWP) |
|
135 name = separator; |
|
136 if (name in MODIFIERS) { |
|
137 array.add(modifiers, MODIFIERS[name]); |
|
138 } else { |
|
139 if (!value.key) |
|
140 value.key = name; |
|
141 else |
|
142 throw new TypeError(INVALID_COMBINATION); |
|
143 } |
|
144 }); |
|
145 |
|
146 if (!value.key) |
|
147 throw new TypeError(INVALID_COMBINATION); |
|
148 |
|
149 value.modifiers = modifiers.sort(); |
|
150 return value; |
|
151 }; |
|
152 |
|
153 /** |
|
154 * Utility function that takes object that defines a `hotkey` and returns |
|
155 * string representation of it. |
|
156 * |
|
157 * _Please note that this function does not validates data neither it normalizes |
|
158 * it, if you are unsure that data is well formed use `normalize` function |
|
159 * instead. |
|
160 * |
|
161 * @param {JSON} hotkey |
|
162 * @param {String} [separator=" "] |
|
163 * Optional string that represents separator used to concatenate keys in the |
|
164 * given `hotkey`. |
|
165 * @returns {String} |
|
166 * @examples |
|
167 * |
|
168 * require("keyboard/hotkeys").toString({ |
|
169 * key: 'b', |
|
170 * modifiers: [ 'control', 'shift' ] |
|
171 * }, '+'); |
|
172 * // 'control+shift+b |
|
173 * |
|
174 */ |
|
175 var toString = exports.toString = function toString(hotkey, separator) { |
|
176 let keys = hotkey.modifiers.slice(); |
|
177 keys.push(hotkey.key); |
|
178 return keys.join(separator || SEPARATOR); |
|
179 }; |
|
180 |
|
181 /** |
|
182 * Utility function takes `key` name and returns `true` if it's function key |
|
183 * (F1, ..., F24) and `false` if it's not. |
|
184 */ |
|
185 var isFunctionKey = exports.isFunctionKey = function isFunctionKey(key) { |
|
186 var $ |
|
187 return key[0].toLowerCase() === 'f' && |
|
188 ($ = parseInt(key.substr(1)), 0 < $ && $ < 25); |
|
189 }; |