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 get ShiftOrOption() 53 { 54 // Option on Mac, Shift on other platforms 55 return WebInspector.isMac() ? this.Alt : this.Shift; 56 } 57 }; 58 59 /** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */ 60 WebInspector.KeyboardShortcut.Key; 61 62 /** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */ 63 WebInspector.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 Shift: { code: 16, name: { mac: "\u21e7", other: "Shift" } }, 68 Ctrl: { code: 17, name: "Ctrl" }, 69 Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } }, 70 Space: { code: 32, name: "Space" }, 71 PageUp: { code: 33, name: { mac: "\u21de", other: "PageUp" } }, // also NUM_NORTH_EAST 72 PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } }, // also NUM_SOUTH_EAST 73 End: { code: 35, name: { mac: "\u2197", other: "End" } }, // also NUM_SOUTH_WEST 74 Home: { code: 36, name: { mac: "\u2196", other: "Home" } }, // also NUM_NORTH_WEST 75 Left: { code: 37, name: "\u2190" }, // also NUM_WEST 76 Up: { code: 38, name: "\u2191" }, // also NUM_NORTH 77 Right: { code: 39, name: "\u2192" }, // also NUM_EAST 78 Down: { code: 40, name: "\u2193" }, // also NUM_SOUTH 79 Delete: { code: 46, name: "Del" }, 80 Zero: { code: 48, name: "0" }, 81 H: { code: 72, name: "H" }, 82 Meta: { code: 91, name: "Meta" }, 83 F1: { code: 112, name: "F1" }, 84 F2: { code: 113, name: "F2" }, 85 F3: { code: 114, name: "F3" }, 86 F4: { code: 115, name: "F4" }, 87 F5: { code: 116, name: "F5" }, 88 F6: { code: 117, name: "F6" }, 89 F7: { code: 118, name: "F7" }, 90 F8: { code: 119, name: "F8" }, 91 F9: { code: 120, name: "F9" }, 92 F10: { code: 121, name: "F10" }, 93 F11: { code: 122, name: "F11" }, 94 F12: { code: 123, name: "F12" }, 95 Semicolon: { code: 186, name: ";" }, 96 NumpadPlus: { code: 107, name: "Numpad +" }, 97 NumpadMinus: { code: 109, name: "Numpad -" }, 98 Numpad0: { code: 96, name: "Numpad 0" }, 99 Plus: { code: 187, name: "+" }, 100 Comma: { code: 188, name: "," }, 101 Minus: { code: 189, name: "-" }, 102 Period: { code: 190, name: "." }, 103 Slash: { code: 191, name: "/" }, 104 QuestionMark: { code: 191, name: "?" }, 105 Apostrophe: { code: 192, name: "`" }, 106 Tilde: { code: 192, name: "Tilde" }, 107 Backslash: { code: 220, name: "\\" }, 108 SingleQuote: { code: 222, name: "\'" }, 109 get CtrlOrMeta() 110 { 111 // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms 112 return WebInspector.isMac() ? this.Meta : this.Ctrl; 113 }, 114 }; 115 116 WebInspector.KeyboardShortcut.KeyBindings = {}; 117 118 (function() { 119 for (var key in WebInspector.KeyboardShortcut.Keys) { 120 var descriptor = WebInspector.KeyboardShortcut.Keys[key]; 121 if (typeof descriptor === "object" && descriptor["code"]) { 122 var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key; 123 WebInspector.KeyboardShortcut.KeyBindings[name] = descriptor; 124 } 125 } 126 })(); 127 128 /** 129 * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits. 130 * It is useful for matching pressed keys. 131 * 132 * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value. 133 * @param {number=} modifiers Optional list of modifiers passed as additional parameters. 134 * @return {number} 135 */ 136 WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers) 137 { 138 if (typeof keyCode === "string") 139 keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0); 140 modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None; 141 return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers); 142 } 143 144 /** 145 * @param {?KeyboardEvent} keyboardEvent 146 * @return {number} 147 */ 148 WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent) 149 { 150 var modifiers = WebInspector.KeyboardShortcut.Modifiers.None; 151 if (keyboardEvent.shiftKey) 152 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift; 153 if (keyboardEvent.ctrlKey) 154 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl; 155 if (keyboardEvent.altKey) 156 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt; 157 if (keyboardEvent.metaKey) 158 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta; 159 160 // Use either a real or a synthetic keyCode (for events originating from extensions). 161 var keyCode = keyboardEvent.keyCode || keyboardEvent["__keyCode"]; 162 return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers); 163 } 164 165 /** 166 * @param {?KeyboardEvent} keyboardEvent 167 * @return {number} 168 */ 169 WebInspector.KeyboardShortcut.makeKeyFromEventIgnoringModifiers = function(keyboardEvent) 170 { 171 var keyCode = keyboardEvent.keyCode || keyboardEvent["__keyCode"]; 172 return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, WebInspector.KeyboardShortcut.Modifiers.None); 173 } 174 175 /** 176 * @param {?KeyboardEvent} event 177 * @return {boolean} 178 */ 179 WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event) 180 { 181 return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey; 182 } 183 184 /** 185 * @param {!Event} event 186 * @return {boolean} 187 */ 188 WebInspector.KeyboardShortcut.hasNoModifiers = function(event) 189 { 190 return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey; 191 } 192 193 /** @typedef {!{key: number, name: string}} */ 194 WebInspector.KeyboardShortcut.Descriptor; 195 196 /** 197 * @param {string|!WebInspector.KeyboardShortcut.Key} key 198 * @param {number=} modifiers 199 * @return {!WebInspector.KeyboardShortcut.Descriptor} 200 */ 201 WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers) 202 { 203 return { 204 key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers), 205 name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers) 206 }; 207 } 208 209 /** 210 * @param {string} shortcut 211 * @return {?WebInspector.KeyboardShortcut.Descriptor} 212 */ 213 WebInspector.KeyboardShortcut.makeDescriptorFromBindingShortcut = function(shortcut) 214 { 215 var parts = shortcut.split(/\+(?!$)/); 216 var modifiers = 0; 217 var keyString; 218 for (var i = 0; i < parts.length; ++i) { 219 if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") { 220 modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]]; 221 continue; 222 } 223 console.assert(i === parts.length - 1, "Only one key other than modifier is allowed in shortcut <" + shortcut + ">"); 224 keyString = parts[i]; 225 break; 226 } 227 console.assert(keyString, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)"); 228 if (!keyString) 229 return null; 230 231 var key = WebInspector.KeyboardShortcut.Keys[keyString] || WebInspector.KeyboardShortcut.KeyBindings[keyString]; 232 if (key && key.shiftKey) 233 modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift; 234 return WebInspector.KeyboardShortcut.makeDescriptor(key ? key : keyString, modifiers); 235 } 236 237 /** 238 * @param {string|!WebInspector.KeyboardShortcut.Key} key 239 * @param {number=} modifiers 240 * @return {string} 241 */ 242 WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers) 243 { 244 return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key); 245 } 246 247 /** 248 * @param {string|!WebInspector.KeyboardShortcut.Key} key 249 * @return {string} 250 */ 251 WebInspector.KeyboardShortcut._keyName = function(key) 252 { 253 if (typeof key === "string") 254 return key.toUpperCase(); 255 if (typeof key.name === "string") 256 return key.name; 257 return key.name[WebInspector.platform()] || key.name.other || ''; 258 } 259 260 /** 261 * @param {number} keyCode 262 * @param {?number} modifiers 263 * @return {number} 264 */ 265 WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers) 266 { 267 return (keyCode & 255) | (modifiers << 8); 268 }; 269 270 /** 271 * @param {number} key 272 * @return {!{keyCode: number, modifiers: number}} 273 */ 274 WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey = function(key) 275 { 276 return { keyCode: key & 255, modifiers: key >> 8 }; 277 } 278 279 /** 280 * @param {number|undefined} modifiers 281 * @return {string} 282 */ 283 WebInspector.KeyboardShortcut._modifiersToString = function(modifiers) 284 { 285 const cmdKey = "\u2318"; 286 const optKey = "\u2325"; 287 const shiftKey = "\u21e7"; 288 const ctrlKey = "\u2303"; 289 290 var isMac = WebInspector.isMac(); 291 var res = ""; 292 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl) 293 res += isMac ? ctrlKey : "Ctrl + "; 294 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt) 295 res += isMac ? optKey : "Alt + "; 296 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift) 297 res += isMac ? shiftKey : "Shift + "; 298 if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta) 299 res += isMac ? cmdKey : "Win + "; 300 301 return res; 302 }; 303 304 WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta); 305