• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
31 * @constructor
32 */
33WebInspector.KeyboardShortcut = function()
34{
35}
36
37/**
38 * Constants for encoding modifier key set as a bit mask.
39 * @see #_makeKeyFromCodeAndModifiers
40 */
41WebInspector.KeyboardShortcut.Modifiers = {
42    None: 0,   // Constant for empty modifiers set.
43    Shift: 1,
44    Ctrl: 2,
45    Alt: 4,
46    Meta: 8,   // Command key on Mac, Win key on other platforms.
47    get CtrlOrMeta()
48    {
49        // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
50        return WebInspector.isMac() ? this.Meta : this.Ctrl;
51    },
52    get ShiftOrOption()
53    {
54        // Shift on Mac, Alt on other platforms
55        return WebInspector.isMac() ? this.Shift : this.Alt;
56    }
57};
58
59/** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */
60WebInspector.KeyboardShortcut.Key;
61
62/** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */
63WebInspector.KeyboardShortcut.Keys = {
64    Backspace: { code: 8, name: "\u21a4" },
65    Tab: { code: 9, name: { mac: "\u21e5", other: "Tab" } },
66    Enter: { code: 13, name: { mac: "\u21a9", other: "Enter" } },
67    Ctrl: { code: 17, name: "Ctrl" },
68    Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } },
69    Space: { code: 32, name: "Space" },
70    PageUp: { code: 33,  name: { mac: "\u21de", other: "PageUp" } },      // also NUM_NORTH_EAST
71    PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } },   // also NUM_SOUTH_EAST
72    End: { code: 35, name: { mac: "\u2197", other: "End" } },             // also NUM_SOUTH_WEST
73    Home: { code: 36, name: { mac: "\u2196", other: "Home" } },           // also NUM_NORTH_WEST
74    Left: { code: 37, name: "\u2190" },           // also NUM_WEST
75    Up: { code: 38, name: "\u2191" },             // also NUM_NORTH
76    Right: { code: 39, name: "\u2192" },          // also NUM_EAST
77    Down: { code: 40, name: "\u2193" },           // also NUM_SOUTH
78    Delete: { code: 46, name: "Del" },
79    Zero: { code: 48, name: "0" },
80    H: { code: 72, name: "H" },
81    Meta: { code: 91, name: "Meta" },
82    F1: { code: 112, name: "F1" },
83    F2: { code: 113, name: "F2" },
84    F3: { code: 114, name: "F3" },
85    F4: { code: 115, name: "F4" },
86    F5: { code: 116, name: "F5" },
87    F6: { code: 117, name: "F6" },
88    F7: { code: 118, name: "F7" },
89    F8: { code: 119, name: "F8" },
90    F9: { code: 120, name: "F9" },
91    F10: { code: 121, name: "F10" },
92    F11: { code: 122, name: "F11" },
93    F12: { code: 123, name: "F12" },
94    Semicolon: { code: 186, name: ";" },
95    NumpadPlus: { code: 107, name: "Numpad +" },
96    NumpadMinus: { code: 109, name: "Numpad -" },
97    Numpad0: { code: 96, name: "Numpad 0" },
98    Plus: { code: 187, name: "+" },
99    Comma: { code: 188, name: "," },
100    Minus: { code: 189, name: "-" },
101    Period: { code: 190, name: "." },
102    Slash: { code: 191, name: "/" },
103    QuestionMark: { code: 191, name: "?" },
104    Apostrophe: { code: 192, name: "`" },
105    Tilde: { code: 192, name: "Tilde" },
106    Backslash: { code: 220, name: "\\" },
107    SingleQuote: { code: 222, name: "\'" },
108    get CtrlOrMeta()
109    {
110        // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
111        return WebInspector.isMac() ? this.Meta : this.Ctrl;
112    },
113};
114
115WebInspector.KeyboardShortcut.KeyBindings = {};
116
117(function() {
118    for (var key in WebInspector.KeyboardShortcut.Keys) {
119        var descriptor = WebInspector.KeyboardShortcut.Keys[key];
120        if (typeof descriptor === "object" && descriptor["code"]) {
121            var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key;
122            WebInspector.KeyboardShortcut.KeyBindings[name] = { code: descriptor["code"] };
123        }
124    }
125})();
126
127/**
128 * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
129 * It is useful for matching pressed keys.
130 *
131 * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value.
132 * @param {number=} modifiers Optional list of modifiers passed as additional parameters.
133 * @return {number}
134 */
135WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
136{
137    if (typeof keyCode === "string")
138        keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
139    modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
140    return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
141}
142
143/**
144 * @param {?KeyboardEvent} keyboardEvent
145 * @return {number}
146 */
147WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
148{
149    var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
150    if (keyboardEvent.shiftKey)
151        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
152    if (keyboardEvent.ctrlKey)
153        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
154    if (keyboardEvent.altKey)
155        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
156    if (keyboardEvent.metaKey)
157        modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
158
159    function keyCodeForEvent(keyboardEvent)
160    {
161        // Use either a real or a synthetic keyCode (for events originating from extensions).
162        return keyboardEvent.keyCode || keyboardEvent["__keyCode"];
163    }
164    return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCodeForEvent(keyboardEvent), modifiers);
165}
166
167/**
168 * @param {?KeyboardEvent} event
169 * @return {boolean}
170 */
171WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
172{
173    return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
174}
175
176/**
177 * @param {?Event} event
178 * @return {boolean}
179 */
180WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
181{
182    return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
183}
184
185/** @typedef {!{key: number, name: string}} */
186WebInspector.KeyboardShortcut.Descriptor;
187
188/**
189 * @param {string|!WebInspector.KeyboardShortcut.Key} key
190 * @param {number=} modifiers
191 * @return {!WebInspector.KeyboardShortcut.Descriptor}
192 */
193WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
194{
195    return {
196        key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
197        name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
198    };
199}
200
201/**
202 * @param {string} shortcut
203 * @return {number}
204 */
205WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut = function(shortcut)
206{
207    var parts = shortcut.split(/\+(?!$)/);
208    var modifiers = 0;
209    for (var i = 0; i < parts.length; ++i) {
210        if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") {
211            modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]];
212            continue;
213        }
214        console.assert(i === parts.length - 1, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)");
215        var key = WebInspector.KeyboardShortcut.Keys[parts[i]] || WebInspector.KeyboardShortcut.KeyBindings[parts[i]];
216        if (key && key.shiftKey)
217            modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
218        return WebInspector.KeyboardShortcut.makeKey(key ? key.code : parts[i].toLowerCase(), modifiers)
219    }
220    console.assert(false);
221    return 0;
222}
223
224/**
225 * @param {string|!WebInspector.KeyboardShortcut.Key} key
226 * @param {number=} modifiers
227 * @return {string}
228 */
229WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
230{
231    return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
232}
233
234/**
235 * @param {string|!WebInspector.KeyboardShortcut.Key} key
236 * @return {string}
237 */
238WebInspector.KeyboardShortcut._keyName = function(key)
239{
240    if (typeof key === "string")
241        return key.toUpperCase();
242    if (typeof key.name === "string")
243        return key.name;
244    return key.name[WebInspector.platform()] || key.name.other || '';
245}
246
247/**
248 * @param {number} keyCode
249 * @param {?number} modifiers
250 * @return {number}
251 */
252WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
253{
254    return (keyCode & 255) | (modifiers << 8);
255};
256
257/**
258 * @param {number} key
259 * @return {!{keyCode: number, modifiers: number}}
260 */
261WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey = function(key)
262{
263    return { keyCode: key & 255, modifiers: key >> 8 };
264}
265
266/**
267 * @param {number|undefined} modifiers
268 * @return {string}
269 */
270WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
271{
272    const cmdKey = "\u2318";
273    const optKey = "\u2325";
274    const shiftKey = "\u21e7";
275    const ctrlKey = "\u2303";
276
277    var isMac = WebInspector.isMac();
278    var res = "";
279    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
280        res += isMac ? ctrlKey : "Ctrl + ";
281    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
282        res += isMac ? optKey : "Alt + ";
283    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
284        res += isMac ? shiftKey : "Shift + ";
285    if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
286        res += isMac ? cmdKey : "Win + ";
287
288    return res;
289};
290
291WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
292