Ticket #2013: keySelector.js

File keySelector.js, 5.7 KB (added by ngduonglam, on 03/01/2015 at 08:11:12 AM)

Replaced "for each" loops with "for of" loops

Line 
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
5Cu.import("resource://gre/modules/Services.jsm");
6
7let validModifiers =
8{
9  ACCEL: null,
10  CTRL: "control",
11  CONTROL: "control",
12  SHIFT: "shift",
13  ALT: "alt",
14  META: "meta",
15  __proto__: null
16};
17
18/**
19 * Sets the correct value of validModifiers.ACCEL.
20 */
21function initAccelKey()
22{
23  validModifiers.ACCEL = "control";
24  try
25  {
26    let accelKey = Services.prefs.getIntPref("ui.key.accelKey");
27    if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL)
28      validModifiers.ACCEL = "control";
29    else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_ALT)
30      validModifiers.ACCEL = "alt";
31    else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_META)
32      validModifiers.ACCEL = "meta";
33  }
34  catch(e)
35  {
36    Cu.reportError(e);
37  }
38}
39
40exports.KeySelector = KeySelector;
41
42/**
43 * This class provides capabilities to find and use available keyboard shortcut
44 * keys.
45 * @param {ChromeWindow} window   the window where to look up existing shortcut
46 *                                keys
47 * @constructor
48 */
49function KeySelector(window)
50{
51  this._initExistingShortcuts(window);
52}
53KeySelector.prototype =
54{
55  /**
56   * Map listing existing shortcut keys as its keys.
57   * @type Object
58   */
59  _existingShortcuts: null,
60
61  /**
62   * Sets up _existingShortcuts property for a window.
63   */
64  _initExistingShortcuts: function(/**ChromeWindow*/ window)
65  {
66    if (!validModifiers.ACCEL)
67      initAccelKey();
68
69    this._existingShortcuts = {__proto__: null};
70
71    let keys = window.document.getElementsByTagName("key");
72    for (let i = 0; i < keys.length; i++)
73    {
74      let key = keys[i];
75      let keyData =
76      {
77        shift: false,
78        meta: false,
79        alt: false,
80        control: false,
81        char: null,
82        code: null
83      };
84
85      let keyChar = key.getAttribute("key");
86      if (keyChar && keyChar.length == 1)
87        keyData.char = keyChar.toUpperCase();
88
89      let keyCode = key.getAttribute("keycode");
90      if (keyCode && "DOM_" + keyCode.toUpperCase() in Ci.nsIDOMKeyEvent)
91        keyData.code = Ci.nsIDOMKeyEvent["DOM_" + keyCode.toUpperCase()];
92
93      if (!keyData.char && !keyData.code)
94        continue;
95
96      let keyModifiers = key.getAttribute("modifiers");
97      if (keyModifiers)
98        for (let modifier of keyModifiers.toUpperCase().match(/\w+/g))
99          if (modifier in validModifiers)
100            keyData[validModifiers[modifier]] = true;
101
102      let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" ");
103      this._existingShortcuts[canonical] = true;
104    }
105  },
106
107  /**
108   * Selects a keyboard shortcut variant that isn't already taken,
109   * parses it into an object.
110   */
111  selectKey: function(/**String*/ variants) /**Object*/
112  {
113    for (let variant of variants.split(/\s*,\s*/))
114    {
115      if (!variant)
116        continue;
117
118      let keyData =
119      {
120        shift: false,
121        meta: false,
122        alt: false,
123        control: false,
124        char: null,
125        code: null,
126        codeName: null
127      };
128      for (let part of variant.toUpperCase().split(/\s+/))
129      {
130        if (part in validModifiers)
131          keyData[validModifiers[part]] = true;
132        else if (part.length == 1)
133          keyData.char = part;
134        else if ("DOM_VK_" + part in Ci.nsIDOMKeyEvent)
135        {
136          keyData.code = Ci.nsIDOMKeyEvent["DOM_VK_" + part];
137          keyData.codeName = "VK_" + part;
138        }
139      }
140
141      if (!keyData.char && !keyData.code)
142        continue;
143
144      let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" ");
145      if (canonical in this._existingShortcuts)
146        continue;
147
148      return keyData;
149    }
150
151    return null;
152  }
153};
154
155/**
156 * Creates the text representation for a key.
157 * @static
158 */
159KeySelector.getTextForKey = function (/**Object*/ key) /**String*/
160{
161  if (!key)
162    return null;
163
164  if (!("text" in key))
165  {
166    key.text = null;
167    try
168    {
169      let stringBundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
170      let parts = [];
171      if (key.control)
172        parts.push(stringBundle.GetStringFromName("VK_CONTROL"));
173      if (key.alt)
174        parts.push(stringBundle.GetStringFromName("VK_ALT"));
175      if (key.meta)
176        parts.push(stringBundle.GetStringFromName("VK_META"));
177      if (key.shift)
178        parts.push(stringBundle.GetStringFromName("VK_SHIFT"));
179      if (key.char)
180        parts.push(key.char.toUpperCase());
181      else
182      {
183        let stringBundle2 = Services.strings.createBundle("chrome://global/locale/keys.properties");
184        parts.push(stringBundle2.GetStringFromName(key.codeName));
185      }
186      key.text = parts.join(stringBundle.GetStringFromName("MODIFIER_SEPARATOR"));
187    }
188    catch (e)
189    {
190      Cu.reportError(e);
191      return null;
192    }
193  }
194  return key.text;
195};
196
197/**
198 * Tests whether a keypress event matches the given key.
199 * @static
200 */
201KeySelector.matchesKey = function(/**Event*/ event, /**Object*/ key) /**Boolean*/
202{
203  if (event.defaultPrevented || !key)
204    return false;
205  if (key.shift != event.shiftKey || key.alt != event.altKey)
206    return false;
207  if (key.meta != event.metaKey || key.control != event.ctrlKey)
208    return false;
209
210  if (key.char && event.charCode && String.fromCharCode(event.charCode).toUpperCase() == key.char)
211    return true;
212  if (key.code && event.keyCode && event.keyCode == key.code)
213    return true;
214  return false;
215};