source: gs3-extensions/seaweed-debug/trunk/src/events/Keyboard.js@ 25160

Last change on this file since 25160 was 25160, checked in by sjm84, 12 years ago

Initial cut at a version of seaweed for debugging purposes. Check it out live into the web/ext folder

File size: 20.3 KB
Line 
1/*
2 * file: Keyboard.js
3 *
4 * @BEGINLICENSE
5 * Copyright 2010 Brook Novak (email : [email protected])
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 * @ENDLICENSE
18 */
19/*
20 * @see http://unixpapa.com/js/key.html
21 * @see http://www.quirksmode.org/js/keys.html
22 */
23
24/* @DEPENDS: Platform */
25
26bootstrap.provides("events.Keyboard");
27
28(function() {
29
30 // All the keymaps
31 var keymaps = {
32
33 // maps the charcodes of special printable keys to key identifiers
34 specialToCharCode: {
35 8: "Backspace", // The Backspace (Back) key.
36 9: "Tab", // The Horizontal Tabulation (Tab) key.
37 // Note: This key identifier is also used for the
38 // Return (Macintosh numpad) key.
39 13: "Enter", // The Enter key.
40 27: "Escape", // The Escape (Esc) key.
41 32: "Space" // The Space (Spacebar) key.
42 },
43
44 // maps the keycodes of non printable keys to key identifiers
45 keyCodeToId: {
46 16: "Shift", // The Shift key.
47 17: "Control", // The Control (Ctrl) key.
48 18: "Alt", // The Alt (Menu) key.
49 20: "CapsLock", // The CapsLock key
50 224: "Meta", // The Meta key. (Apple Meta and Windows key)
51 37: "Left", // The Left Arrow key.
52 38: "Up", // The Up Arrow key.
53 39: "Right", // The Right Arrow key.
54 40: "Down", // The Down Arrow key.
55 33: "PageUp", // The Page Up key.
56 34: "PageDown", // The Page Down (Next) key.
57 35: "End", // The End key.
58 36: "Home", // The Home key.
59 45: "Insert", // The Insert (Ins) key. (Does not fire in Opera/Win)
60 46: "Delete", // The Delete (Del) Key.
61 112: "F1", // The F1 key.
62 113: "F2", // The F2 key.
63 114: "F3", // The F3 key.
64 115: "F4", // The F4 key.
65 116: "F5", // The F5 key.
66 117: "F6", // The F6 key.
67 118: "F7", // The F7 key.
68 119: "F8", // The F8 key.
69 120: "F9", // The F9 key.
70 121: "F10", // The F10 key.
71 122: "F11", // The F11 key.
72 123: "F12", // The F12 key.
73 144: "NumLock", // The Num Lock key.
74 44: "PrintScreen", // The Print Screen (PrintScrn, SnapShot) key.
75 145: "Scroll", // The scroll lock key
76 19: "Pause", // The pause/break key
77 91: "Win", // The Windows Logo key
78 93: "Apps" // The Application key (Windows Context Menu)
79 },
80
81 // maps the keycodes of the numpad keys to the right charcodes
82 numpadToCharCode: {
83 96: "0".charCodeAt(0),
84 97: "1".charCodeAt(0),
85 98: "2".charCodeAt(0),
86 99: "3".charCodeAt(0),
87 100: "4".charCodeAt(0),
88 101: "5".charCodeAt(0),
89 102: "6".charCodeAt(0),
90 103: "7".charCodeAt(0),
91 104: "8".charCodeAt(0),
92 105: "9".charCodeAt(0),
93 106: "*".charCodeAt(0),
94 107: "+".charCodeAt(0),
95 109: "-".charCodeAt(0),
96 110: ".".charCodeAt(0),
97 111: "/".charCodeAt(0)
98 },
99
100 // Helpers
101 charCodeA: "A".charCodeAt(0),
102 charCodeZ: "Z".charCodeAt(0),
103 charCodea: "a".charCodeAt(0),
104 charCodez: "z".charCodeAt(0),
105 charCode0: "0".charCodeAt(0),
106 charCode9: "9".charCodeAt(0),
107
108 // Platform dependant maps
109 keyCodeFix : {},
110 charCodeToKeyCode : {},
111
112 // Maps keycodes that cannot be distinguished in key press events, to the other keycodes that have also
113 // been assigned to the same key in the key press event. ALl key codes (keys/values) in the map will be
114 // simulated as key presses in the key down event.
115 ambiguousKeyPressCodes : {}
116
117 };
118
119 // Construct inverse maps
120 keymaps.idToKeyCode = {};
121 for (var key in keymaps.keyCodeToId) {
122 keymaps.idToKeyCode[keymaps.keyCodeToId[key]] = parseInt(key, 10);
123 }
124 for (var key in keymaps.specialToCharCode) {
125 keymaps.idToKeyCode[keymaps.specialToCharCode[key]] = parseInt(key, 10);
126 }
127
128 // Setup platform dependant key maps
129 switch (_engine) {
130
131 case _Platform.TRIDENT: // MSHTML
132 keymaps.charCodeToKeyCode = {
133 13: 13,
134 27: 27
135 };
136 break;
137
138 case _Platform.GECKO:
139 keymaps.keyCodeFix = {
140 12: idToKeyCode("NumLock")
141 };
142 break;
143
144 case _Platform.WEBKIT:
145
146 // starting with Safari 3.1 (version 525.13) Apple switched the key
147 // handling to match the IE behaviour.
148 if (_Platform.engineVersion && _Platform.engineVersion < 525.13) { // TODO: Check if safari?
149 keymaps.charCodeToKeyCode = {
150
151 // Safari/Webkit Mappings
152 63289: idToKeyCode("NumLock"),
153 63276: idToKeyCode("PageUp"),
154 63277: idToKeyCode("PageDown"),
155 63275: idToKeyCode("End"),
156 63273: idToKeyCode("Home"),
157 63234: idToKeyCode("Left"),
158 63232: idToKeyCode("Up"),
159 63235: idToKeyCode("Right"),
160 63233: idToKeyCode("Down"),
161 63272: idToKeyCode("Delete"),
162 63302: idToKeyCode("Insert"),
163 63236: idToKeyCode("F1"),
164 63237: idToKeyCode("F2"),
165 63238: idToKeyCode("F3"),
166 63239: idToKeyCode("F4"),
167 63240: idToKeyCode("F5"),
168 63241: idToKeyCode("F6"),
169 63242: idToKeyCode("F7"),
170 63243: idToKeyCode("F8"),
171 63244: idToKeyCode("F9"),
172 63245: idToKeyCode("F10"),
173 63246: idToKeyCode("F11"),
174 63247: idToKeyCode("F12"),
175 63248: idToKeyCode("PrintScreen"),
176 3: idToKeyCode("Enter"),
177 12: idToKeyCode("NumLock"),
178 13: idToKeyCode("Enter")
179 };
180
181 } else { // Modern versions of webkit
182 keymaps.charCodeToKeyCode = {
183 13: 13,
184 27: 27
185 };
186 }
187
188 break;
189
190 case _Platform.PRESTO:
191
192 keymaps.ambiguousKeyPressCodes = {
193 35: 51, // # <=> End
194 36: 52, // $ <=> Home
195 44: 188, // Comma <=> Print Screen
196 45: 109, // - <=> Insert
197 46: 190, // Period <=> Delete
198 91: 219, // [ <=> Windows
199 93: 221 // ] <=> Apps
200 };
201
202 // Add presto specific maps...
203
204 // Inverse ambigious key codes to simulate a keypress for in a key down event,
205 // True values indicate they only should be simulated if the shift modifier is down.
206 // False values are implicit (the remainding ambiguios codes) and should only be simulated
207 // if the shift modifier is not down.
208 keymaps.prestoSimulateInvOnShift = {
209 51: 1, // #
210 52: 1 // $
211 // 188 : 0 // ,
212 // 109 : 0 // -
213 // 190 : 0 // period
214 // 219 : 0 // [
215 // 221 : 0 // ]
216 };
217
218 // The "which" codes to use (as well as alpha numeric codes) as char codes on key presses.
219 keymaps.prestoUseWhichCodes = {
220 33: 1, // ! <=> Page Up
221 34: 1, // " <=> Page Down
222 40: 1, // ( <=> Down
223 39: 1, // ' <=> Right
224 38: 1, // & <=> Up
225 37: 1, // % <=> Left
226 123: 1 // { <=> F12
227 };
228
229 break;
230 }
231
232 // Create inverse maps
233 keymaps.ambiguousKeyPressCodesInv = {};
234 for (var key in keymaps.ambiguousKeyPressCodes) {
235 keymaps.ambiguousKeyPressCodesInv[keymaps.ambiguousKeyPressCodes[key]] = parseInt(key, 10);
236 }
237
238
239 /**
240 * @class A singleton that provides cross-browser/platform keyboard-normalization facilities
241 * @author Brook Novak
242 */
243 de.events.Keyboard = {
244
245
246 /**
247 * Returns the event's "normalizedKey" to the DOM Level 3 Spec of keyIdentifier.
248 * @param {Event} domEvent The dom event to "normalize"
249 *
250 * @param {Boolean} isKeyDown True if the dom event for keydown, false if for keypress.
251 *
252 * @return {String} The key identifier for the given key.
253 * Null if the key ident is unknown or should be extracted from a different event type
254 */
255 getKeyIdentifier : function(domEvent, isKeyDown) {
256
257 var keyCode, // non printable keys. e.g. CTRL, INSERT
258 charCode, // printable symbols. e.g. A,Z,&
259 useGenericMapping = false;
260
261 //debug.println((isKeyDown ? "Keydown" : "KeyPress") + ": cc=" + domEvent.charCode + "kc=" + domEvent.keyCode);
262
263 switch(_engine) {
264
265 case _Platform.TRIDENT:
266
267 if (isKeyDown && (isNonPrintableKeyCode(domEvent.keyCode) || domEvent.keyCode == 8 || domEvent.keyCode == 9))
268 keyCode = domEvent.keyCode;
269
270 // Use keydown if CTRL is down, but keyPress if CTRL is not down
271 if ((!isKeyDown && !domEvent.ctrlKey) || (isKeyDown && domEvent.ctrlKey)) {
272 if (keymaps.charCodeToKeyCode[domEvent.keyCode])
273 keyCode = keymaps.charCodeToKeyCode[domEvent.keyCode];
274 else
275 charCode = domEvent.keyCode;
276 }
277
278 break;
279
280 case _Platform.GECKO:
281 if (isKeyDown) {
282 // Moz doesn't get keypress events for CTRL, ALT or SHIFT,
283 // So raise them on key down
284 if (domEvent.keyCode >= 16 && domEvent.keyCode <= 18)
285 keyCode = domEvent.keyCode;
286
287 } else {
288 keyCode = keymaps.keyCodeFix[domEvent.keyCode] || domEvent.keyCode;
289 charCode = domEvent.charCode;
290 }
291 break;
292
293 case _Platform.WEBKIT:
294
295 if (_browser == _Platform.SAFARI) {
296
297 if (isKeyDown) {
298
299
300 if (_engineVersion && _engineVersion < 525.13)
301 keyCode = keymaps.charCodeToKeyCode[domEvent.charCode] || domEvent.keyCode;
302
303 else keyCode = domEvent.keyCode;
304
305 if (!isNonPrintableKeyCode(keyCode) && !this.isAcceleratorDown(domEvent))
306 keyCode = 0;
307
308 } else { // Key Press get printable charactors
309
310 // starting with Safari 3.1 (verion 525.13) Apple switched the key
311 // handling to match the IE behaviour.
312 if (_engineVersion && _engineVersion < 525.13) {
313
314 if (keymaps.charCodeToKeyCode[domEvent.charCode])
315 keyCode = keymaps.charCodeToKeyCode[domEvent.charCode];
316 else
317 charCode = domEvent.charCode;
318
319 } else {
320
321 if (keymaps.charCodeToKeyCode[domEvent.keyCode])
322 keyCode = keymaps.charCodeToKeyCode[domEvent.keyCode];
323 else
324 charCode = domEvent.keyCode;
325 }
326
327 }
328
329 } else if(_browser == _Platform.CHROME) {
330
331 // Chrome is good, it sets keycode,charcode,which and keyidentifier..
332 if (isKeyDown) {
333
334 // Keycodes can be detected from keydowns
335 if (isNonPrintableKeyCode(domEvent.keyCode))
336 keyCode = domEvent.keyCode;
337
338 // If the accelerator key is down while pressing printable keys the charcodes
339 // appear as key codes in the keydown event
340 else if (this.isAcceleratorDown(domEvent))
341 charCode = domEvent.keyCode;
342
343 } else {
344
345 // Printable keys (charcodes) occur in the keypress event (except when
346 // the accelerator is down).
347 if (domEvent.charCode && !this.isAcceleratorDown(domEvent))
348 charCode = domEvent.charCode;
349
350 }
351
352 } else useGenericMapping = true;
353
354 break;
355
356 case _Platform.PRESTO:
357
358 if (isKeyDown) {
359
360 if (keymaps.ambiguousKeyPressCodesInv[domEvent.keyCode]) {
361 // Avoid simulating key codes which will have a following legit keypress event
362 var simOnShift = keymaps.prestoSimulateInvOnShift[domEvent.keyCode];
363 if ((domEvent.shiftKey && simOnShift) || (!domEvent.shiftKey && !simOnShift))
364 charCode = keymaps.ambiguousKeyPressCodesInv[domEvent.keyCode];
365
366 } else if (keymaps.ambiguousKeyPressCodes[domEvent.keyCode])
367 keyCode = domEvent.keyCode;
368
369 } else { // Key press
370
371 if (domEvent.which && (isAlphaNumericAscii(domEvent.which) || keymaps.prestoUseWhichCodes[domEvent.which]))
372 charCode = domEvent.which;
373 else if (keymaps.keyCodeToId[domEvent.keyCode])
374 keyCode = domEvent.keyCode;
375 else
376 charCode = domEvent.keyCode;
377 }
378 break;
379
380 case _Platform.KHTML:
381
382 // TODO
383 useGenericMapping = true;
384
385 break;
386
387 default:
388 useGenericMapping = true;
389 } // End switch
390
391 if (useGenericMapping && isKeyDown) {
392
393 if (domEvent.keyIdentifier && domEvent.keyIdentifier.length > 0) {
394 // See http://www.w3.org/TR/DOM-Level-3-Events/keyset.html
395
396 // Is the key identifier unicode-encoded?
397 var ucMatch = /^U\+([\dA-Fa-f]+)$/.exec(domEvent.keyIdentifier);
398
399 if (ucMatch) {
400
401 // Extract unicode numerical value
402 var uniNum = parseInt(ucMatch[1], 16);
403
404 // Convert into printable symbol
405 var printable = String.fromCharCode(uniNum);
406
407 if (domEvent.shiftKey)
408 return printable.toUpperCase();
409 else return printable.toLowerCase();
410
411 } else return domEvent.keyIdentifier;
412
413 } else {
414 keyCode = domEvent.keyCode || domEvent.which;
415 charCode = domEvent.charCode;
416 }
417
418 }
419
420 if (keyCode) { // Use keyCode
421
422 // Omit ambiguous key codes: where actual key presses cannot be distinguished since this
423 // platform assigns some single key codes to multiple keys.
424 if (!isKeyDown && keymaps.ambiguousKeyPressCodes[keyCode])
425 return null;
426
427 return keyCodeToId(keyCode);
428
429 } else if (charCode) // Use charCode
430 return charCodeToId(charCode);
431
432 return null;
433 },
434
435 /**
436 * @param {Event} domEvent A dom event
437 * @return {Boolean} True if the accelerator key was down for the given event.s
438 */
439 isAcceleratorDown : function(domEvent) {
440 // Add platform specific accelerator flag
441 return _os == _Platform.MAC ? domEvent.metaKey : domEvent.ctrlKey;
442 }
443
444 }; // End Keyboard singleton
445
446 /**
447 * @param {Number} keyCode A key code to test
448 * @return {Boolean} True if the key code is non-printable. I.E. not a printable symbol.
449 */
450 function isNonPrintableKeyCode(keyCode) {
451 return typeof keymaps.keyCodeToId[keyCode] == "string" || typeof keymaps.specialToCharCode[keyCode] == "string";
452 }
453
454 /**
455 * @param {Number} keyCode a keycode to test
456 * @return {Boolean} True if the given keycode is identifiable.
457 */
458 function isIdentifiableKeyCode(keyCode) {
459 return isAlphaNumericAscii(keyCode) ||
460 keymaps.specialToCharCode[keyCode] || /* Enter, Space, Tab, Backspace */
461 keymaps.numpadToCharCode[keyCode] || /* Numpad */
462 isNonPrintableKeyCode(keyCode); /* non printable keys */
463 }
464
465 /**
466 * @param {Number} code A char-code (or which-code) to test
467 * @return {Boolean} True if the given code is alphanumeric
468 */
469 function isAlphaNumericAscii(code) {
470 return (code >= keymaps.charCodeA && code <= keymaps.charCodeZ) || /* Upper */
471 (code >= keymaps.charCodea && code <= keymaps.charCodez) || /* Lower */
472 (code >= keymaps.charCode0 && code <= keymaps.charCode9); /* Numbers */
473 }
474
475 /**
476 * @param {Number} charCode A charactor code
477 * @return {String} A key identifier for the given charactor code. Undefined if none exists.
478 */
479 function charCodeToId(charCode) {
480 return keymaps.specialToCharCode[charCode] || String.fromCharCode(charCode);
481 }
482
483 /**
484 * @param {Number} keyCode A keycode
485 * @return {String} A key identifier for the given key code. Undefined if none exists.
486 */
487 function keyCodeToId(keyCode) {
488
489 if (isIdentifiableKeyCode(keyCode)) {
490
491 var numPadCharCode = keymaps.numpadToCharCode[keyCode];
492
493 if (numPadCharCode)
494 return String.fromCharCode(numPadCharCode);
495
496 return (keymaps.keyCodeToId[keyCode] || keymaps.specialToCharCode[keyCode] || String.fromCharCode(keyCode));
497
498 } else return null;
499
500 }
501
502 /**
503 * @param {String} keyId The key identifier string to get the keycode for
504 * @return {Number} The standadized keycode of the given key identifier
505 */
506 function idToKeyCode(keyId) {
507 return keymaps.idToKeyCode[keyId] || keyId.charCodeAt(0);
508 }
509
510
511
512}) ();
513
Note: See TracBrowser for help on using the repository browser.