Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / KeyboardShortcut.js
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  */
33 WebInspector.KeyboardShortcut = function()
34 {
35 }
36
37 /**
38  * Constants for encoding modifier key set as a bit mask.
39  * @see #_makeKeyFromCodeAndModifiers
40  */
41 WebInspector.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 };
53
54 /** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */
55 WebInspector.KeyboardShortcut.Key;
56
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: "\'" },
100     get CtrlOrMeta()
101     {
102         // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
103         return WebInspector.isMac() ? this.Meta : this.Ctrl;
104     },
105 };
106
107 WebInspector.KeyboardShortcut.KeyBindings = {};
108
109 (function() {
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"] };
115         }
116     }
117 })();
118
119 /**
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.
122  *
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.
125  * @return {number}
126  */
127 WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
128 {
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);
133 }
134
135 /**
136  * @param {?KeyboardEvent} keyboardEvent
137  * @return {number}
138  */
139 WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
140 {
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;
150
151     function keyCodeForEvent(keyboardEvent)
152     {
153         // Use either a real or a synthetic keyCode (for events originating from extensions).
154         return keyboardEvent.keyCode || keyboardEvent["__keyCode"];
155     }
156     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCodeForEvent(keyboardEvent), modifiers);
157 }
158
159 /**
160  * @param {?KeyboardEvent} event
161  * @return {boolean}
162  */
163 WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
164 {
165     return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
166 }
167
168 /**
169  * @param {?Event} event
170  * @return {boolean}
171  */
172 WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
173 {
174     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
175 }
176
177 /** @typedef {!{key: number, name: string}} */
178 WebInspector.KeyboardShortcut.Descriptor;
179
180 /**
181  * @param {string|!WebInspector.KeyboardShortcut.Key} key
182  * @param {number=} modifiers
183  * @return {!WebInspector.KeyboardShortcut.Descriptor}
184  */
185 WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
186 {
187     return {
188         key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
189         name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
190     };
191 }
192
193 /**
194  * @param {string} shortcut
195  * @return {number}
196  */
197 WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut = function(shortcut)
198 {
199     var parts = shortcut.split(/\+(?!$)/);
200     var modifiers = 0;
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]];
204             continue;
205         }
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)
211     }
212     console.assert(false);
213     return 0;
214 }
215
216 /**
217  * @param {string|!WebInspector.KeyboardShortcut.Key} key
218  * @param {number=} modifiers
219  * @return {string}
220  */
221 WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
222 {
223     return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
224 }
225
226 /**
227  * @param {string|!WebInspector.KeyboardShortcut.Key} key
228  * @return {string}
229  */
230 WebInspector.KeyboardShortcut._keyName = function(key)
231 {
232     if (typeof key === "string")
233         return key.toUpperCase();
234     if (typeof key.name === "string")
235         return key.name;
236     return key.name[WebInspector.platform()] || key.name.other || '';
237 }
238
239 /**
240  * @param {number} keyCode
241  * @param {?number} modifiers
242  * @return {number}
243  */
244 WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
245 {
246     return (keyCode & 255) | (modifiers << 8);
247 };
248
249 /**
250  * @param {number|undefined} modifiers
251  * @return {string}
252  */
253 WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
254 {
255     const cmdKey = "\u2318";
256     const optKey = "\u2325";
257     const shiftKey = "\u21e7";
258     const ctrlKey = "\u2303";
259
260     var isMac = WebInspector.isMac();
261     var res = "";
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 + ";
270
271     return res;
272 };
273
274 /**
275  * @param {!KeyboardEvent} event
276  */
277 WebInspector.KeyboardShortcut.handleShortcut = function(event)
278 {
279     var key = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
280     var extensions = WebInspector.KeyboardShortcut._keysToActionExtensions[key];
281     if (!extensions)
282         return;
283
284     function handler(extension)
285     {
286         var result = extension.instance().handleAction(event);
287         if (result)
288             event.consume(true);
289         delete WebInspector.KeyboardShortcut._pendingActionTimer;
290         return result;
291     }
292
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]))
297                 return;
298         } else {
299             WebInspector.KeyboardShortcut._pendingActionTimer = setTimeout(handler.bind(null, extensions[i]), 0);
300             break;
301         }
302     }
303 }
304
305 WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
306
307 WebInspector.KeyboardShortcut._onKeyPress = function(event)
308 {
309     if (!WebInspector.KeyboardShortcut._pendingActionTimer)
310         return;
311
312     var target = event.target;
313     if (WebInspector.isBeingEdited(event.target)) {
314         clearTimeout(WebInspector.KeyboardShortcut._pendingActionTimer);
315         delete WebInspector.KeyboardShortcut._pendingActionTimer;
316     }
317 }
318
319 WebInspector.KeyboardShortcut.registerActions = function()
320 {
321     document.addEventListener("keypress", WebInspector.KeyboardShortcut._onKeyPress, true);
322     WebInspector.KeyboardShortcut._keysToActionExtensions = {};
323     var extensions = WebInspector.moduleManager.extensions(WebInspector.ActionDelegate);
324     extensions.forEach(registerBindings);
325
326     /**
327      * @param {!WebInspector.ModuleManager.Extension} extension
328      */
329     function registerBindings(extension)
330     {
331         var bindings = extension.descriptor().bindings;
332         for (var i = 0; bindings && i < bindings.length; ++i) {
333             if (!platformMatches(bindings[i].platform))
334                 continue;
335             var shortcuts = bindings[i].shortcut.split(/\s+/);
336             shortcuts.forEach(registerShortcut.bind(null, extension));
337         }
338     }
339
340     /**
341      * @param {!WebInspector.ModuleManager.Extension} extension
342      * @param {string} shortcut
343      */
344     function registerShortcut(extension, shortcut)
345     {
346         var key = WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut(shortcut);
347         if (!key)
348             return;
349         if (WebInspector.KeyboardShortcut._keysToActionExtensions[key])
350             WebInspector.KeyboardShortcut._keysToActionExtensions[key].push(extension);
351         else
352             WebInspector.KeyboardShortcut._keysToActionExtensions[key] = [extension];
353     }
354
355     /**
356      * @param {string=} platformsString
357      * @return {boolean}
358      */
359     function platformMatches(platformsString)
360     {
361         if (!platformsString)
362             return true;
363         var platforms = platformsString.split(",");
364         var isMatch = false;
365         var currentPlatform = WebInspector.platform();
366         for (var i = 0; !isMatch && i < platforms.length; ++i)
367             isMatch = platforms[i] === currentPlatform;
368         return isMatch;
369     }
370 }