2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
33 WebInspector.KeyboardShortcut = function()
38 * Constants for encoding modifier key set as a bit mask.
39 * @see #_makeKeyFromCodeAndModifiers
41 WebInspector.KeyboardShortcut.Modifiers = {
42 None: 0, // Constant for empty modifiers set.
46 Meta: 8, // Command key on Mac, Win key on other platforms.
49 // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
50 return WebInspector.isMac() ? this.Meta : this.Ctrl;
54 /** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */
55 WebInspector.KeyboardShortcut.Key;
57 /** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */
58 WebInspector.KeyboardShortcut.Keys = {
59 Backspace: { code: 8, name: "\u21a4" },
60 Tab: { code: 9, name: { mac: "\u21e5", other: "Tab" } },
61 Enter: { code: 13, name: { mac: "\u21a9", other: "Enter" } },
62 Ctrl: { code: 17, name: "Ctrl" },
63 Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } },
64 Space: { code: 32, name: "Space" },
65 PageUp: { code: 33, name: { mac: "\u21de", other: "PageUp" } }, // also NUM_NORTH_EAST
66 PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } }, // also NUM_SOUTH_EAST
67 End: { code: 35, name: { mac: "\u2197", other: "End" } }, // also NUM_SOUTH_WEST
68 Home: { code: 36, name: { mac: "\u2196", other: "Home" } }, // also NUM_NORTH_WEST
69 Left: { code: 37, name: "\u2190" }, // also NUM_WEST
70 Up: { code: 38, name: "\u2191" }, // also NUM_NORTH
71 Right: { code: 39, name: "\u2192" }, // also NUM_EAST
72 Down: { code: 40, name: "\u2193" }, // also NUM_SOUTH
73 Delete: { code: 46, name: "Del" },
74 Zero: { code: 48, name: "0" },
75 H: { code: 72, name: "H" },
76 Meta: { code: 91, name: "Meta" },
77 F1: { code: 112, name: "F1" },
78 F2: { code: 113, name: "F2" },
79 F3: { code: 114, name: "F3" },
80 F4: { code: 115, name: "F4" },
81 F5: { code: 116, name: "F5" },
82 F6: { code: 117, name: "F6" },
83 F7: { code: 118, name: "F7" },
84 F8: { code: 119, name: "F8" },
85 F9: { code: 120, name: "F9" },
86 F10: { code: 121, name: "F10" },
87 F11: { code: 122, name: "F11" },
88 F12: { code: 123, name: "F12" },
89 Semicolon: { code: 186, name: ";" },
90 Plus: { code: 187, name: "+" },
91 Comma: { code: 188, name: "," },
92 Minus: { code: 189, name: "-" },
93 Period: { code: 190, name: "." },
94 Slash: { code: 191, name: "/" },
95 QuestionMark: { code: 191, name: "?" },
96 Apostrophe: { code: 192, name: "`" },
97 Tilde: { code: 192, name: "Tilde" },
98 Backslash: { code: 220, name: "\\" },
99 SingleQuote: { code: 222, name: "\'" },
102 // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
103 return WebInspector.isMac() ? this.Meta : this.Ctrl;
107 WebInspector.KeyboardShortcut.KeyBindings = {};
110 for (var key in WebInspector.KeyboardShortcut.Keys) {
111 var descriptor = WebInspector.KeyboardShortcut.Keys[key];
112 if (typeof descriptor === "object" && descriptor["code"]) {
113 var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key;
114 WebInspector.KeyboardShortcut.KeyBindings[name] = { code: descriptor["code"] };
120 * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
121 * It is useful for matching pressed keys.
123 * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value.
124 * @param {number=} modifiers Optional list of modifiers passed as additional parameters.
127 WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
129 if (typeof keyCode === "string")
130 keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
131 modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
132 return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
136 * @param {?KeyboardEvent} keyboardEvent
139 WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
141 var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
142 if (keyboardEvent.shiftKey)
143 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
144 if (keyboardEvent.ctrlKey)
145 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
146 if (keyboardEvent.altKey)
147 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
148 if (keyboardEvent.metaKey)
149 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
151 function keyCodeForEvent(keyboardEvent)
153 // Use either a real or a synthetic keyCode (for events originating from extensions).
154 return keyboardEvent.keyCode || keyboardEvent["__keyCode"];
156 return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCodeForEvent(keyboardEvent), modifiers);
160 * @param {?KeyboardEvent} event
163 WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
165 return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
169 * @param {?Event} event
172 WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
174 return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
177 /** @typedef {!{key: number, name: string}} */
178 WebInspector.KeyboardShortcut.Descriptor;
181 * @param {string|!WebInspector.KeyboardShortcut.Key} key
182 * @param {number=} modifiers
183 * @return {!WebInspector.KeyboardShortcut.Descriptor}
185 WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
188 key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
189 name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
194 * @param {string} shortcut
197 WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut = function(shortcut)
199 var parts = shortcut.split(/\+(?!$)/);
201 for (var i = 0; i < parts.length; ++i) {
202 if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") {
203 modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]];
206 console.assert(i === parts.length - 1, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)");
207 var key = WebInspector.KeyboardShortcut.Keys[parts[i]] || WebInspector.KeyboardShortcut.KeyBindings[parts[i]];
208 if (key && key.shiftKey)
209 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
210 return WebInspector.KeyboardShortcut.makeKey(key ? key.code : parts[i].toLowerCase(), modifiers)
212 console.assert(false);
217 * @param {string|!WebInspector.KeyboardShortcut.Key} key
218 * @param {number=} modifiers
221 WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
223 return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
227 * @param {string|!WebInspector.KeyboardShortcut.Key} key
230 WebInspector.KeyboardShortcut._keyName = function(key)
232 if (typeof key === "string")
233 return key.toUpperCase();
234 if (typeof key.name === "string")
236 return key.name[WebInspector.platform()] || key.name.other || '';
240 * @param {number} keyCode
241 * @param {?number} modifiers
244 WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
246 return (keyCode & 255) | (modifiers << 8);
250 * @param {number|undefined} modifiers
253 WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
255 const cmdKey = "\u2318";
256 const optKey = "\u2325";
257 const shiftKey = "\u21e7";
258 const ctrlKey = "\u2303";
260 var isMac = WebInspector.isMac();
262 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
263 res += isMac ? ctrlKey : "Ctrl + ";
264 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
265 res += isMac ? optKey : "Alt + ";
266 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
267 res += isMac ? shiftKey : "Shift + ";
268 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
269 res += isMac ? cmdKey : "Win + ";
275 * @param {!KeyboardEvent} event
277 WebInspector.KeyboardShortcut.handleShortcut = function(event)
279 var key = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
280 var extensions = WebInspector.KeyboardShortcut._keysToActionExtensions[key];
284 function handler(extension)
286 var result = extension.instance().handleAction(event);
289 delete WebInspector.KeyboardShortcut._pendingActionTimer;
293 for (var i = 0; i < extensions.length; ++i) {
294 var ident = event.keyIdentifier;
295 if (/^F\d+|Control|Shift|Alt|Meta|Win|U\+001B$/.test(ident) || event.ctrlKey || event.altKey || event.metaKey) {
296 if (handler(extensions[i]))
299 WebInspector.KeyboardShortcut._pendingActionTimer = setTimeout(handler.bind(null, extensions[i]), 0);
305 WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
307 WebInspector.KeyboardShortcut._onKeyPress = function(event)
309 if (!WebInspector.KeyboardShortcut._pendingActionTimer)
312 var target = event.target;
313 if (WebInspector.isBeingEdited(event.target)) {
314 clearTimeout(WebInspector.KeyboardShortcut._pendingActionTimer);
315 delete WebInspector.KeyboardShortcut._pendingActionTimer;
319 WebInspector.KeyboardShortcut.registerActions = function()
321 document.addEventListener("keypress", WebInspector.KeyboardShortcut._onKeyPress, true);
322 WebInspector.KeyboardShortcut._keysToActionExtensions = {};
323 var extensions = WebInspector.moduleManager.extensions(WebInspector.ActionDelegate);
324 extensions.forEach(registerBindings);
327 * @param {!WebInspector.ModuleManager.Extension} extension
329 function registerBindings(extension)
331 var bindings = extension.descriptor().bindings;
332 for (var i = 0; bindings && i < bindings.length; ++i) {
333 if (!platformMatches(bindings[i].platform))
335 var shortcuts = bindings[i].shortcut.split(/\s+/);
336 shortcuts.forEach(registerShortcut.bind(null, extension));
341 * @param {!WebInspector.ModuleManager.Extension} extension
342 * @param {string} shortcut
344 function registerShortcut(extension, shortcut)
346 var key = WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut(shortcut);
349 if (WebInspector.KeyboardShortcut._keysToActionExtensions[key])
350 WebInspector.KeyboardShortcut._keysToActionExtensions[key].push(extension);
352 WebInspector.KeyboardShortcut._keysToActionExtensions[key] = [extension];
356 * @param {string=} platformsString
359 function platformMatches(platformsString)
361 if (!platformsString)
363 var platforms = platformsString.split(",");
365 var currentPlatform = WebInspector.platform();
366 for (var i = 0; !isMatch && i < platforms.length; ++i)
367 isMatch = platforms[i] === currentPlatform;