aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.7/packages/api-utils/lib/keyboard/utils.js
blob: 4693a1214bb26bb16307b2bb6ca9519debc76d11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const { Cc, Ci } = require("chrome");
const runtime = require("../runtime");
const { isString } = require("../type");
const array = require("../array");


const SWP = "{{SEPARATOR}}";
const SEPARATOR = "-"
const INVALID_COMBINATION = "Hotkey key combination must contain one or more " +
                            "modifiers and only one key";

// Map of modifier key mappings.
const MODIFIERS = exports.MODIFIERS = {
  'accel': runtime.OS === "Darwin" ? 'meta' : 'control',
  'meta': 'meta',
  'control': 'control',
  'ctrl': 'control',
  'option': 'alt',
  'command': 'meta',
  'alt': 'alt',
  'shift': 'shift'
};

// Hash of key:code pairs for all the chars supported by `nsIDOMKeyEvent`.
// This is just a copy of the `nsIDOMKeyEvent` hash with normalized names.
// @See: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl
const CODES = exports.CODES = new function Codes() {
  let nsIDOMKeyEvent = Ci.nsIDOMKeyEvent;
  // Names that will be substituted with a shorter analogs.
  let aliases = {
    'subtract':     '-',
    'add':          '+',
    'equals':       '=',
    'slash':        '/',
    'backslash':    '\\',
    'openbracket':  '[',
    'closebracket': ']',
    'quote':        '\'',
    'backquote':    '`',
    'period':       '.',
    'semicolon':    ';',
    'comma':        ','
  };

  // Normalizing keys and copying values to `this` object.
  Object.keys(nsIDOMKeyEvent).filter(function(key) {
    // Filter out only key codes.
    return key.indexOf('DOM_VK') === 0;
  }).map(function(key) {
    // Map to key:values
    return [ key, nsIDOMKeyEvent[key] ];
  }).map(function([key, value]) {
    return [ key.replace('DOM_VK_', '').replace('_', '').toLowerCase(), value ];
  }).forEach(function ([ key, value ]) {
    this[aliases[key] || key] = value;
  }, this);
};

// Inverted `CODES` hash of `code:key`.
const KEYS = exports.KEYS = new function Keys() {
  Object.keys(CODES).forEach(function(key) {
    this[CODES[key]] = key;
  }, this)
}

exports.getKeyForCode = function getKeyForCode(code) {
  return (code in KEYS) && KEYS[code];
};
exports.getCodeForKey = function getCodeForKey(key) {
  return (key in CODES) && CODES[key];
};

/**
 * Utility function that takes string or JSON that defines a `hotkey` and
 * returns normalized string version of it.
 * @param {JSON|String} hotkey
 * @param {String} [separator=" "]
 *    Optional string that represents separator used to concatenate keys in the
 *    given `hotkey`.
 * @returns {String}
 * @examples
 *
 *    require("keyboard/hotkeys").normalize("b Shift accel");
 *    // 'control shift b' -> on windows & linux
 *    // 'meta shift b'    -> on mac
 *    require("keyboard/hotkeys").normalize("alt-d-shift", "-");
 *    // 'alt shift d'
 */
var normalize = exports.normalize = function normalize(hotkey, separator) {
  if (!isString(hotkey))
    hotkey = toString(hotkey, separator);
  return toString(toJSON(hotkey, separator), separator);
};

/*
 * Utility function that splits a string of characters that defines a `hotkey`
 * into modifier keys and the defining key.
 * @param {String} hotkey
 * @param {String} [separator=" "]
 *    Optional string that represents separator used to concatenate keys in the
 *    given `hotkey`.
 * @returns {JSON}
 * @examples
 *
 *    require("keyboard/hotkeys").toJSON("accel shift b");
 *    // { key: 'b', modifiers: [ 'control', 'shift' ] } -> on windows & linux
 *    // { key: 'b', modifiers: [ 'meta', 'shift' ] }    -> on mac
 *
 *    require("keyboard/hotkeys").normalize("alt-d-shift", "-");
 *    // { key: 'd', modifiers: [ 'alt', 'shift' ] }
 */
var toJSON = exports.toJSON = function toJSON(hotkey, separator) {
  separator = separator || SEPARATOR;
  // Since default separator is `-`, combination may take form of `alt--`. To
  // avoid misbehavior we replace `--` with `-{{SEPARATOR}}` where
  // `{{SEPARATOR}}` can be swapped later.
  hotkey = hotkey.toLowerCase().replace(separator + separator, separator + SWP);

  let value = {};
  let modifiers = [];
  let keys = hotkey.split(separator);
  keys.forEach(function(name) {
    // If name is `SEPARATOR` than we swap it back.
    if (name === SWP)
      name = separator;
    if (name in MODIFIERS) {
      array.add(modifiers, MODIFIERS[name]);
    } else {
      if (!value.key)
        value.key = name;
      else
        throw new TypeError(INVALID_COMBINATION);
    }
  });

  if (!value.key)
      throw new TypeError(INVALID_COMBINATION);

  value.modifiers = modifiers.sort();
  return value;
};

/**
 * Utility function that takes object that defines a `hotkey` and returns
 * string representation of it.
 *
 * _Please note that this function does not validates data neither it normalizes
 * it, if you are unsure that data is well formed use `normalize` function
 * instead.
 *
 * @param {JSON} hotkey
 * @param {String} [separator=" "]
 *    Optional string that represents separator used to concatenate keys in the
 *    given `hotkey`.
 * @returns {String}
 * @examples
 *
 *    require("keyboard/hotkeys").toString({
 *      key: 'b',
 *      modifiers: [ 'control', 'shift' ]
 *    }, '+');
 *    // 'control+shift+b
 *
 */
var toString = exports.toString = function toString(hotkey, separator) {
  let keys = hotkey.modifiers.slice();
  keys.push(hotkey.key);
  return keys.join(separator || SEPARATOR);
};

/**
 * Utility function takes `key` name and returns `true` if it's function key
 * (F1, ..., F24) and `false` if it's not.
 */
var isFunctionKey = exports.isFunctionKey = function isFunctionKey(key) {
  var $
  return key[0].toLowerCase() === 'f' &&
         ($ = parseInt(key.substr(1)), 0 < $ && $ < 25);
};