michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: const { observer: keyboardObserver } = require("./observer"); michael@0: const { getKeyForCode, normalize, isFunctionKey, michael@0: MODIFIERS } = require("./utils"); michael@0: michael@0: /** michael@0: * Register a global `hotkey` that executes `listener` when the key combination michael@0: * in `hotkey` is pressed. If more then one `listener` is registered on the same michael@0: * key combination only last one will be executed. michael@0: * michael@0: * @param {string} hotkey michael@0: * Key combination in the format of 'modifier key'. michael@0: * michael@0: * Examples: michael@0: * michael@0: * "accel s" michael@0: * "meta shift i" michael@0: * "control alt d" michael@0: * michael@0: * Modifier keynames: michael@0: * michael@0: * - **shift**: The Shift key. michael@0: * - **alt**: The Alt key. On the Macintosh, this is the Option key. On michael@0: * Macintosh this can only be used in conjunction with another modifier, michael@0: * since `Alt+Letter` combinations are reserved for entering special michael@0: * characters in text. michael@0: * - **meta**: The Meta key. On the Macintosh, this is the Command key. michael@0: * - **control**: The Control key. michael@0: * - **accel**: The key used for keyboard shortcuts on the user's platform, michael@0: * which is Control on Windows and Linux, and Command on Mac. Usually, this michael@0: * would be the value you would use. michael@0: * michael@0: * @param {function} listener michael@0: * Function to execute when the `hotkey` is executed. michael@0: */ michael@0: exports.register = function register(hotkey, listener) { michael@0: hotkey = normalize(hotkey); michael@0: hotkeys[hotkey] = listener; michael@0: }; michael@0: michael@0: /** michael@0: * Unregister a global `hotkey`. If passed `listener` is not the one registered michael@0: * for the given `hotkey`, the call to this function will be ignored. michael@0: * michael@0: * @param {string} hotkey michael@0: * Key combination in the format of 'modifier key'. michael@0: * @param {function} listener michael@0: * Function that will be invoked when the `hotkey` is pressed. michael@0: */ michael@0: exports.unregister = function unregister(hotkey, listener) { michael@0: hotkey = normalize(hotkey); michael@0: if (hotkeys[hotkey] === listener) michael@0: delete hotkeys[hotkey]; michael@0: }; michael@0: michael@0: /** michael@0: * Map of hotkeys and associated functions. michael@0: */ michael@0: const hotkeys = exports.hotkeys = {}; michael@0: michael@0: keyboardObserver.on("keydown", function onKeypress(event, window) { michael@0: let key, modifiers = []; michael@0: let isChar = "isChar" in event && event.isChar; michael@0: let which = "which" in event ? event.which : null; michael@0: let keyCode = "keyCode" in event ? event.keyCode : null; michael@0: michael@0: if ("shiftKey" in event && event.shiftKey) michael@0: modifiers.push("shift"); michael@0: if ("altKey" in event && event.altKey) michael@0: modifiers.push("alt"); michael@0: if ("ctrlKey" in event && event.ctrlKey) michael@0: modifiers.push("control"); michael@0: if ("metaKey" in event && event.metaKey) michael@0: modifiers.push("meta"); michael@0: michael@0: // If it's not a printable character then we fall back to a human readable michael@0: // equivalent of one of the following constants. michael@0: // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl michael@0: key = getKeyForCode(keyCode); michael@0: michael@0: // If only non-function (f1 - f24) key or only modifiers are pressed we don't michael@0: // have a valid combination so we return immediately (Also, sometimes michael@0: // `keyCode` may be one for the modifier which means we do not have a michael@0: // modifier). michael@0: if (!key || (!isFunctionKey(key) && !modifiers.length) || key in MODIFIERS) michael@0: return; michael@0: michael@0: let combination = normalize({ key: key, modifiers: modifiers }); michael@0: let hotkey = hotkeys[combination]; michael@0: michael@0: if (hotkey) { michael@0: try { michael@0: hotkey(); michael@0: } catch (exception) { michael@0: console.exception(exception); michael@0: } finally { michael@0: // Work around bug 582052 by preventing the (nonexistent) default action. michael@0: event.preventDefault(); michael@0: } michael@0: } michael@0: });