hotkeys.js 3.6 KB

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