utils.js 5.8 KB

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