1 /* 2 * © 2009 ROBO Design 3 * http://www.robodesign.ro 4 * 5 * $Date: 2009-04-22 15:40:44 +0300 $ 6 */ 7 8 /** 9 * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a> 10 * @fileOverview Minimal JavaScript library which provides functionality for 11 * cross-browser compatibility support. 12 */ 13 14 /** 15 * @namespace Holds methods and properties necessary throughout the entire 16 * application. 17 */ 18 var lib = {}; 19 20 /** 21 * @namespace Holds browser information. 22 */ 23 lib.browser = {}; 24 25 (function () { 26 var ua = ''; 27 28 if (window.navigator && window.navigator.userAgent) { 29 ua = window.navigator.userAgent.toLowerCase(); 30 } 31 32 /** 33 * @type Boolean 34 */ 35 lib.browser.opera = window.opera ? true : /\bopera\b/.test(ua); 36 37 /** 38 * Webkit is the render engine used primarily by Safari. It's also used by 39 * Google Chrome and GNOME Epiphany. 40 * 41 * @type Boolean 42 */ 43 lib.browser.webkit = /\b(applewebkit|webkit)\b/.test(ua); 44 45 /** 46 * Firefox uses the Gecko render engine. 47 * 48 * @type Boolean 49 */ 50 // In some variations of the User Agent strings provided by Opera, Firefox is 51 // mentioned. 52 lib.browser.firefox = /\bfirefox\b/.test(ua) && !lib.browser.opera; 53 54 /** 55 * Gecko is the render engine used by Firefox and related products. 56 * 57 * @type Boolean 58 */ 59 // Typically, the user agent string of WebKit also mentions Gecko. Additionally, 60 // Opera mentions Gecko for tricking some sites. 61 lib.browser.gecko = /\bgecko\b/.test(ua) && !lib.browser.opera && !lib.browser.webkit; 62 63 /** 64 * Microsoft Internet Explorer. The future of computing. 65 * 66 * @type Boolean 67 */ 68 // Again, Opera allows users to easily fake the UA. 69 lib.browser.msie = /\bmsie\b/.test(ua) && !lib.browser.opera; 70 71 /** 72 * Presto is the render engine used by Opera. 73 * 74 * @type Boolean 75 */ 76 // Older versions of Opera did not mention Presto in the UA string. 77 lib.browser.presto = /\bpresto\b/.test(ua) || lib.browser.opera; 78 79 80 /** 81 * Browser operating system 82 * 83 * @type String 84 */ 85 lib.browser.os = (ua.match(/\b(windows|linux)\b/) || [])[1]; 86 87 delete ua; 88 })(); 89 90 91 /** 92 * @namespace Holds methods and properties necessary for DOM manipulation. 93 */ 94 lib.dom = {}; 95 96 /** 97 * @namespace Holds the list of virtual key identifiers and a few characters, 98 * each being associated to a key code commonly used by Web browsers. 99 * 100 * @private 101 */ 102 lib.dom.keyNames = { 103 Help: 6, 104 Backspace: 8, 105 Tab: 9, 106 Clear: 12, 107 Enter: 13, 108 Shift: 16, 109 Control: 17, 110 Alt: 18, 111 Pause: 19, 112 CapsLock: 20, 113 Cancel: 24, 114 'Escape': 27, 115 Space: 32, 116 PageUp: 33, 117 PageDown: 34, 118 End: 35, 119 Home: 36, 120 Left: 37, 121 Up: 38, 122 Right: 39, 123 Down: 40, 124 PrintScreen: 44, 125 Insert: 45, 126 'Delete': 46, 127 Win: 91, 128 ContextMenu: 93, 129 '*': 106, 130 '+': 107, 131 F1: 112, 132 F2: 113, 133 F3: 114, 134 F4: 115, 135 F5: 116, 136 F6: 117, 137 F7: 118, 138 F8: 119, 139 F9: 120, 140 F10: 121, 141 F11: 122, 142 F12: 123, 143 NumLock: 144, 144 ';': 186, 145 '=': 187, 146 ',': 188, 147 '-': 189, 148 '.': 190, 149 '/': 191, 150 '`': 192, 151 '[': 219, 152 '\\': 220, 153 ']': 221, 154 "'": 222 155 }; 156 157 /** 158 * @namespace Holds the list of codes, each being associated to a virtual key 159 * identifier. 160 * 161 * @private 162 */ 163 lib.dom.keyCodes = { 164 /* 165 * For almost each key code, these comments give the key name, the 166 * keyIdentifier from the DOM 3 Events spec and the Unicode character 167 * information (if you would use the decimal code for direct conversion to 168 * a character, e.g. String.fromCharCode()). Obviously, the Unicode character 169 * information is not to be used, since these are only virtual key codes (not 170 * really char codes) associated to key names. 171 * 172 * Each key name in here tries to follow the same style as the defined 173 * keyIdentifiers from the DOM 3 Events. Thus for the Page Down button, 174 * 'PageDown' is used (not other variations like 'pag-up'), and so on. 175 * 176 * Multiple key codes might be associated to the same key - it's not an error. 177 * 178 * Note that this list is not an exhaustive key codes list. This means that 179 * for key A or for key 0, the script will do String.fromCharCode(keyCode), to 180 * determine the key. For the case of alpha-numeric keys, this works fine. 181 */ 182 183 /* 184 * Key: Enter 185 * Unicode: U+0003 [End of text] 186 * 187 * Note 1: This keyCode is only used in Safari 2 (older Webkit) for the Enter 188 * key. 189 * 190 * Note 2: In Gecko this keyCode is used for the Cancel key (see 191 * DOM_VK_CANCEL). 192 */ 193 3: 'Enter', 194 195 /* 196 * Key: Help 197 * Unicode: U+0006 [Acknowledge] 198 * 199 * Note: Taken from Gecko (DOM_VK_HELP). 200 */ 201 6: 'Help', 202 203 /* 204 * Key: Backspace 205 * Unicode: U+0008 [Backspace] 206 * keyIdentifier: U+0008 207 */ 208 8: 'Backspace', 209 210 /* 211 * Key: Tab 212 * Unicode: U+0009 [Horizontal tab] 213 * keyIdentifier: U+0009 214 */ 215 9: 'Tab', 216 217 /* 218 * Key: Enter 219 * Unicode: U+0010 [Line feed (LF) / New line (NL) / End of line (EOL)] 220 * 221 * Note: Taken from the Unicode characters list. If it ends up as a keyCode in 222 * some event, it's simply considered as being the Enter key. 223 */ 224 10: 'Enter', 225 226 /* 227 * Key: NumPad_Center 228 * Unicode: U+000C [Form feed] 229 * keyIdentifier: Clear 230 * 231 * Note 1: This keyCode is used when NumLock is off, and the user pressed the 232 * 5 key on the numeric pad. 233 * 234 * Note 2: Safari 2 (older Webkit) assigns this keyCode to the NumLock key 235 * itself. 236 */ 237 12: 'Clear', 238 239 /* 240 * Key: Enter 241 * Unicode: U+000D [Carriage return (CR)] 242 * keyIdentifier: Enter 243 * 244 * Note 1: This is the keyCode used by most of the Web browsers when the Enter 245 * key is pressed. 246 * 247 * Note 2: Gecko associates the DOM_VK_RETURN to this keyCode. 248 */ 249 13: 'Enter', 250 251 /* 252 * Key: Enter 253 * Unicode: U+000E [Shift out] 254 * 255 * Note: Taken from Gecko (DOM_VK_ENTER). 256 */ 257 14: 'Enter', 258 259 /* 260 * Key: Shift 261 * Unicode: U+0010 [Data link escape] 262 * keyIdentifier: Shift 263 * 264 * Note: In older Safari (Webkit) versions Shift+Tab is assigned a different 265 * keyCode: keyCode 25. 266 */ 267 16: 'Shift', 268 269 /* 270 * Key: Control 271 * Unicode: U+0011 [Device control one] 272 * keyIdentifier: Control 273 */ 274 17: 'Control', 275 276 /* 277 * Key: Alt 278 * Unicode: U+0012 [Device control two] 279 * keyIdentifier: Alt 280 */ 281 18: 'Alt', 282 283 /* 284 * Key: Pause 285 * Unicode: U+0013 [Device control three] 286 * keyIdentifier: Pause 287 */ 288 19: 'Pause', 289 290 /* 291 * Key: CapsLock 292 * Unicode: U+0014 [Device control four] 293 * keyIdentifier: CapsLock 294 */ 295 20: 'CapsLock', 296 297 /* 298 * Key: Cancel 299 * Unicode: U+0018 [Cancel] 300 * keyIdentifier: U+0018 301 */ 302 24: 'Cancel', 303 304 /* 305 * Key: Escape 306 * Unicode: U+001B [Escape] 307 * keyIdentifier: U+001B 308 */ 309 27: 'Escape', 310 311 /* 312 * Key: Space 313 * Unicode: U+0020 [Space] 314 * keyIdentifier: U+0020 315 */ 316 32: 'Space', 317 318 /* 319 * Key: PageUp or NumPad_North_East 320 * Unicode: U+0021 ! [Exclamation mark] 321 * keyIdentifier: PageUp 322 */ 323 33: 'PageUp', 324 325 /* 326 * Key: PageDown or NumPad_South_East 327 * Unicode: U+0022 " [Quotation mark] 328 * keyIdentifier: PageDown 329 */ 330 34: 'PageDown', 331 332 /* 333 * Key: End or NumPad_South_West 334 * Unicode: U+0023 # [Number sign] 335 * keyIdentifier: PageDown 336 */ 337 35: 'End', 338 339 /* 340 * Key: Home or NumPad_North_West 341 * Unicode: U+0024 $ [Dollar sign] 342 * keyIdentifier: Home 343 */ 344 36: 'Home', 345 346 /* 347 * Key: Left or NumPad_West 348 * Unicode: U+0025 % [Percent sign] 349 * keyIdentifier: Left 350 */ 351 37: 'Left', 352 353 /* 354 * Key: Up or NumPad_North 355 * Unicode: U+0026 & [Ampersand] 356 * keyIdentifier: Up 357 */ 358 38: 'Up', 359 360 /* 361 * Key: Right or NumPad_East 362 * Unicode: U+0027 ' [Apostrophe] 363 * keyIdentifier: Right 364 */ 365 39: 'Right', 366 367 /* 368 * Key: Down or NumPad_South 369 * Unicode: U+0028 ( [Left parenthesis] 370 * keyIdentifier: Down 371 */ 372 40: 'Down', 373 374 /* 375 * Key: PrintScreen 376 * Unicode: U+002C , [Comma] 377 * keyIdentifier: PrintScreen 378 */ 379 //44: 'PrintScreen', 380 381 /* 382 * Key: Insert or NumPad_Insert 383 * Unicode: U+002D - [Hyphen-Minus] 384 * keyIdentifier: Insert 385 */ 386 45: 'Insert', 387 388 /* 389 * Key: Delete or NumPad_Delete 390 * Unicode: U+002E . [Full stop / period] 391 * keyIdentifier: U+007F 392 */ 393 46: 'Delete', 394 395 /* 396 * Key: WinLeft 397 * Unicode: U+005B [ [Left square bracket] 398 * keyIdentifier: Win 399 * 400 * Disabled: rarely needed. 401 */ 402 //91: 'Win', 403 404 /* 405 * Key: WinRight 406 * Unicode: U+005C \ [Reverse solidus / Backslash] 407 * keyIdentifier: Win 408 */ 409 //92: 'Win', 410 411 /* 412 * Key: Menu/ContextMenu 413 * Unicode: U+005D ] [Right square bracket] 414 * keyIdentifier: ... 415 * 416 * Disabled: Is it Meta? Is it Menu, ContextMenu, what? Too much mess. 417 */ 418 //93: 'ContextMenu', 419 420 /* 421 * Key: NumPad_0 422 * Unicode: U+0060 ` [Grave accent] 423 * keyIdentifier: 0 424 */ 425 96: '0', 426 427 /* 428 * Key: NumPad_1 429 * Unicode: U+0061 a [Latin small letter a] 430 * keyIdentifier: 1 431 */ 432 97: '1', 433 434 /* 435 * Key: NumPad_2 436 * Unicode: U+0062 b [Latin small letter b] 437 * keyIdentifier: 2 438 */ 439 98: '2', 440 441 /* 442 * Key: NumPad_3 443 * Unicode: U+0063 c [Latin small letter c] 444 * keyIdentifier: 3 445 */ 446 99: '3', 447 448 /* 449 * Key: NumPad_4 450 * Unicode: U+0064 d [Latin small letter d] 451 * keyIdentifier: 4 452 */ 453 100: '4', 454 455 /* 456 * Key: NumPad_5 457 * Unicode: U+0065 e [Latin small letter e] 458 * keyIdentifier: 5 459 */ 460 101: '5', 461 462 /* 463 * Key: NumPad_6 464 * Unicode: U+0066 f [Latin small letter f] 465 * keyIdentifier: 6 466 */ 467 102: '6', 468 469 /* 470 * Key: NumPad_7 471 * Unicode: U+0067 g [Latin small letter g] 472 * keyIdentifier: 7 473 */ 474 103: '7', 475 476 /* 477 * Key: NumPad_8 478 * Unicode: U+0068 h [Latin small letter h] 479 * keyIdentifier: 8 480 */ 481 104: '8', 482 483 /* 484 * Key: NumPad_9 485 * Unicode: U+0069 i [Latin small letter i] 486 * keyIdentifier: 9 487 */ 488 105: '9', 489 490 /* 491 * Key: NumPad_Multiply 492 * Unicode: U+0070 j [Latin small letter j] 493 * keyIdentifier: U+002A * [Asterisk / Star] 494 */ 495 106: '*', 496 497 /* 498 * Key: NumPad_Plus 499 * Unicode: U+0071 k [Latin small letter k] 500 * keyIdentifier: U+002B + [Plus] 501 */ 502 107: '+', 503 504 /* 505 * Key: NumPad_Minus 506 * Unicode: U+0073 m [Latin small letter m] 507 * keyIdentifier: U+002D + [Hyphen / Minus] 508 */ 509 109: '-', 510 511 /* 512 * Key: NumPad_Period 513 * Unicode: U+0074 n [Latin small letter n] 514 * keyIdentifier: U+002E . [Period] 515 */ 516 110: '.', 517 518 /* 519 * Key: NumPad_Division 520 * Unicode: U+0075 o [Latin small letter o] 521 * keyIdentifier: U+002F / [Solidus / Slash] 522 */ 523 111: '/', 524 525 112: 'F1', // p 526 113: 'F2', // q 527 114: 'F3', // r 528 115: 'F4', // s 529 116: 'F5', // t 530 117: 'F6', // u 531 118: 'F7', // v 532 119: 'F8', // w 533 120: 'F9', // x 534 121: 'F10', // y 535 122: 'F11', // z 536 123: 'F12', // { 537 538 /* 539 * Key: Delete 540 * Unicode: U+007F [Delete] 541 * keyIdentifier: U+007F 542 */ 543 127: 'Delete', 544 545 /* 546 * Key: NumLock 547 * Unicode: U+0090 [Device control string] 548 * keyIdentifier: NumLock 549 */ 550 144: 'NumLock', 551 552 186: ';', // º (Masculine ordinal indicator) 553 187: '=', // » 554 188: ',', // ¼ 555 189: '-', // ½ 556 190: '.', // ¾ 557 191: '/', // ¿ 558 192: '`', // À 559 219: '[', // Û 560 220: '\\', // Ü 561 221: ']', // Ý 562 222: "'" // Þ (Latin capital letter thorn) 563 564 //224: 'Win', // à 565 //229: 'WinIME', // å or WinIME or something else in Webkit 566 //255: 'NumLock', // ÿ, Gecko and Chrome, Windows XP in VirtualBox 567 //376: 'NumLock' // Ÿ, Opera, Windows XP in VirtualBox 568 }; 569 570 if (lib.browser.gecko) { 571 lib.dom.keyCodes[3] = 'Cancel'; // DOM_VK_CANCEL 572 } 573 574 /** 575 * @namespace Holds a list of common wrong key codes in Web browsers. 576 * 577 * @private 578 */ 579 lib.dom.keyCodes_fixes = { 580 42: lib.dom.keyNames['*'], // char * to key * 581 47: lib.dom.keyNames['/'], // char / to key / 582 59: lib.dom.keyNames[';'], // char ; to key ; 583 61: lib.dom.keyNames['='], // char = to key = 584 96: 48, // NumPad_0 to char 0 585 97: 49, // NumPad_1 to char 1 586 98: 50, // NumPad_2 to char 2 587 99: 51, // NumPad_3 to char 3 588 100: 52, // NumPad_4 to char 4 589 101: 53, // NumPad_5 to char 5 590 102: 54, // NumPad_6 to char 6 591 103: 55, // NumPad_7 to char 7 592 104: 56, // NumPad_8 to char 8 593 105: 57, // NumPad_9 to char 9 594 //106: 56, // NumPad_Multiply to char 8 595 //107: 187, // NumPad_Plus to key = 596 109: lib.dom.keyNames['-'], // NumPad_Minus to key - 597 110: lib.dom.keyNames['.'], // NumPad_Period to key . 598 111: lib.dom.keyNames['/'] // NumPad_Division to key / 599 }; 600 601 /** 602 * @namespace Holds the list of broken key codes generated by older Webkit 603 * (Safari 2). 604 * 605 * @private 606 */ 607 lib.dom.keyCodes_Safari2 = { 608 63232: lib.dom.keyNames.Up, // 38 609 63233: lib.dom.keyNames.Down, // 40 610 63234: lib.dom.keyNames.Left, // 37 611 63235: lib.dom.keyNames.Right, // 39 612 63236: lib.dom.keyNames.F1, // 112 613 63237: lib.dom.keyNames.F2, // 113 614 63238: lib.dom.keyNames.F3, // 114 615 63239: lib.dom.keyNames.F4, // 115 616 63240: lib.dom.keyNames.F5, // 116 617 63241: lib.dom.keyNames.F6, // 117 618 63242: lib.dom.keyNames.F7, // 118 619 63243: lib.dom.keyNames.F8, // 119 620 63244: lib.dom.keyNames.F9, // 120 621 63245: lib.dom.keyNames.F10, // 121 622 63246: lib.dom.keyNames.F11, // 122 623 63247: lib.dom.keyNames.F12, // 123 624 63248: lib.dom.keyNames.PrintScreen, // 44 625 63272: lib.dom.keyNames['Delete'], // 46 626 63273: lib.dom.keyNames.Home, // 36 627 63275: lib.dom.keyNames.End, // 35 628 63276: lib.dom.keyNames.PageUp, // 33 629 63277: lib.dom.keyNames.PageDown, // 34 630 63289: lib.dom.keyNames.NumLock, // 144 631 63302: lib.dom.keyNames.Insert // 45 632 }; 633 634 635 /** 636 * A complete keyboard events cross-browser compatibility layer. 637 * 638 * <p>Unfortunately, due to the important differences across Web browsers, 639 * simply using the available properties in a single keyboard event is not 640 * enough to accurately determine the key the user pressed. Thus, one needs to 641 * have event handlers for all keyboard-related events <code>keydown</code>, 642 * <code>keypress</code> and <code>keyup</code>. 643 * 644 * <p>This class provides a complete keyboard event compatibility layer. For any 645 * new instance you provide the DOM element you want to listen events for, and 646 * the event handlers for any of the three events <code>keydown</code> 647 * / <code>keypress</code> / <code>keyup</code>. 648 * 649 * <p>Your event handlers will receive the original DOM Event object, with 650 * several new properties defined: 651 * 652 * <ul> 653 * <li><var>event.keyCode_</var> holds the correct code for event key. 654 * 655 * <li><var>event.key_</var> holds the key the user pressed. It can be either 656 * a key name like "PageDown", "Delete", "Enter", or it is a character like 657 * "A", "1", or "[". 658 * 659 * <li><var>event.charCode_</var> holds the Unicode character decimal code. 660 * 661 * <li><var>event.char_</var> holds the character generated by the event. 662 * 663 * <li><var>event.repeat_</var> is a boolean property telling if the 664 * <code>keypress</code> event is repeated - the user is holding down the key 665 * for a long-enough period of time to generate multiple events. 666 * </ul> 667 * 668 * <p>The character-related properties, <var>charCode_</var> and 669 * <var>char_</var> are only available in the <code>keypress</code> and 670 * <code>keyup</code> event objects. 671 * 672 * <p>This class will ensure that the <code>keypress</code> event is always 673 * fired in Webkit and MSIE for all keys, except modifiers. For modifier keys 674 * like <kbd>Shift</kbd>, <kbd>Control</kbd>, and <kbd>Alt</kbd>, the 675 * <code>keypress</code> event will not be fired, even if the Web browser does 676 * it. 677 * 678 * <p>Some user agents like Webkit repeat the <code>keydown</code> event while 679 * the user holds down a key. This class will ensure that only the 680 * <code>keypress</code> event is repeated. 681 * 682 * <p>If you want to prevent the default action for an event, you should prevent 683 * it on <code>keypress</code>. This class will prevent the default action for 684 * <code>keydown</code> if need (in MSIE). 685 * 686 * @example 687 * <code>var <var>klogger</var> = function (<var>ev</var>) { 688 * console.log(<var>ev</var>.type + 689 * ' keyCode_ ' + <var>ev</var>.keyCode_ + 690 * ' key_ ' + <var>ev</var>.key_ + 691 * ' charCode_ ' + <var>ev</var>.charCode_ + 692 * ' char_ ' + <var>ev</var>.char_ + 693 * ' repeat_ ' + <var>ev</var>.repeat_); 694 * }; 695 * 696 * var <var>kbListener</var> = new lib.dom.KeyboardEventListener(window, 697 * {keydown: <var>klogger</var>, 698 * keypress: <var>klogger</var>, 699 * keyup: <var>klogger</var>});</code> 700 * 701 * // later when you're done... 702 * <code><var>kbListener</var>.detach();</code> 703 * 704 * @class A complete keyboard events cross-browser compatibility layer. 705 * 706 * @param {Element} elem_ The DOM Element you want to listen events for. 707 * 708 * @param {Object} handlers_ The object holding the list of event handlers 709 * associated to the name of each keyboard event you want to listen. To listen 710 * for all the three keyboard events use <code>{keydown: <var>fn1</var>, 711 * keypress: <var>fn2</var>, keyup: <var>fn3</var>}</code>. 712 * 713 * @throws {TypeError} If the <var>handlers_</var> object does not contain any 714 * event handler. 715 */ 716 lib.dom.KeyboardEventListener = function (elem_, handlers_) { 717 /* 718 Technical details: 719 720 For the keyup and keydown events the keyCode provided is that of the virtual 721 key irrespective of other modifiers (e.g. Shift). Generally, during the 722 keypress event, the keyCode holds the Unicode value of the character 723 resulted from the key press, say an alphabetic upper/lower-case char, 724 depending on the actual intent of the user and depending on the currently 725 active keyboard layout. 726 727 Examples: 728 * Pressing p you get keyCode 80 in keyup/keydown, and keyCode 112 in 729 keypress. String.fromCharCode(80) = 'P' and String.fromCharCode(112) = 'p'. 730 * Pressing P you get keyCode 80 in all events. 731 * Pressing F1 you get keyCode 112 in keyup, keydown and keypress. 732 * Pressing 9 you get keyCode 57 in all events. 733 * Pressing Shift+9 you get keyCode 57 in keyup/keydown, and keyCode 40 in 734 keypress. String.fromCharCode(57) = '9' and String.fromCharCode(40) = '('. 735 736 * Using the Greek layout when you press v on an US keyboard you get the 737 output character ω. The keyup/keydown events hold keyCode 86 which is V. 738 This does make sense, since it's the virtual key code we are dealing with 739 - not the character code, not the result of pressing the key. The keypress 740 event will hold keyCode 969 (ω). 741 742 * Pressing NumPad_Minus you get keyCode 109 in keyup/keydown and keyCode 45 743 in keypress. Again, this happens because in keyup/keydown you don't get the 744 character code, you get the key code, as indicated above. For 745 your information: String.fromCharCode(109) = 'm' and String.fromCharCode(45) 746 = '-'. 747 748 Therefore, we need to map all the codes of several keys, like F1-F12, 749 Escape, Enter, Tab, etc. This map is held by lib.dom.keyCodes. It associates, 750 for example, code 112 to F1, or 13 to Enter. This map is used to detect 751 virtual keys in all events. 752 753 (This is only the general story, details about browser-specific differences 754 follow below.) 755 756 If the code given by the browser doesn't appear in keyCode maps, it's used 757 as is. The key_ returned is that of String.fromCharCode(keyCode). 758 759 In all browsers we consider all events having keyCode <= 32, as being events 760 generated by a virtual key (not a character). As such, the keyCode value is 761 always searched in lib.dom.keyCodes. 762 763 As you might notice from the above description, in the keypress event we 764 cannot tell the difference from say F1 and p, because both give the code 765 112. In Gecko and Webkit we can tell the difference because these UAs also 766 set the charCode event property when the key generates a character. If F1 is 767 pressed, or some other virtual key, charCode is never set. 768 769 In Opera the charCode property is never set. However, the 'which' event 770 property is not set for several virtual keys. This means we can tell the 771 difference between a character and a virtual key. However, there's a catch: 772 not *all* virtual keys have the 'which' property unset. Known exceptions: 773 Backspace (8), Tab (9), Enter (13), Shift (16), Control (17), Alt (18), 774 Pause (19), Escape (27), End (35), Home (36), Insert (45), Delete (46) and 775 NumLock (144). Given we already consider any keyCode <= 32 being one of some 776 virtual key, fewer exceptions remain. We only have the End, Home, Insert, 777 Delete and the NumLock keys which cannot be 100% properly detected in the 778 keypress event, in Opera. To properly detect End/Home we can check if the 779 Shift modifier is active or not. If the user wants # instead of End, then 780 Shift must be active. The same goes for $ and Home. Thus we now only have 781 the '-' (Insert) and the '.' (Delete) characters incorrectly detected as 782 being Insert/Delete. 783 784 The above brings us to one of the main visible difference, when comparing 785 the lib.dom.KeyboardEventListener class and the simple 786 lib.dom.KeyboardEvent.getKey() function. In getKey(), for the keypress event 787 we cannot accurately determine the exact key, because it requires checking 788 the keyCode used for the keydown event. The KeyboardEventListener 789 class monitors all the keyboard events, ensuring a more accurate key 790 detection. 791 792 Different keyboard layouts and international characters are generally 793 supported. Tested and known to work with the Cyrillic alphabet (Greek 794 keyboard layout) and with the US Dvorak keyboard layouts. 795 796 Opera does not fire the keyup event for international characters when 797 running on Linux. For example, this happens with the Greek keyboard layout, 798 when trying Cyrillic characters. 799 800 Gecko gives no keyCode/charCode/which for international characters when 801 running on Linux, in the keyup/keydown events. Thus, all such keys remain 802 unidentified for these two events. For the keypress event there are no 803 issues with such characters. 804 805 Webkit and Konqueror 4 also implement the keyIdentifier property from the 806 DOM 3 Events specification. In theory, this should be great, but it's not 807 without problems. Sometimes keyCode/charCode/which are all 0, but 808 keyIdentifier is properly set. For several virtual keys the keyIdentifier 809 value is simply 'U+0000'. Thus, the keyIdentifier is used only if the value 810 is not 'Unidentified' / 'U+0000', and only when keyCode/charCode/which are 811 not available. 812 813 Konqueror 4 does not use the 'U+XXXX' notation for Unicode characters. It 814 simply gives the character, directly. 815 816 Additionally, Konqueror seems to have some problems with several keyCodes in 817 keydown/keyup. For example, the key '[' gives keyCode 91 instead of 219. 818 Thus, it effectively gives the Unicode for the character, not the key code. 819 This issue is visible with other key as well. 820 821 NumPad_Clear is unidentified on Linux in all browsers, but it works on 822 Windows. 823 824 In MSIE the keypress event is only fired for characters and for Escape, 825 Space and Enter. Similarly, Webkit only fires the keypress event for 826 characters. However, Webkit does not fire keypress for Escape. 827 828 International characters and different keyboard layouts seem to work fine in 829 MSIE as well. 830 831 As of MSIE 4.0, the keypress event fires for the following keys: 832 * Letters: A - Z (uppercase and lowercase) 833 * Numerals: 0 - 9 834 * Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~ 835 * System: Escape (27), Space (32), Enter (13) 836 837 Documentation about the keypress event: 838 http://msdn.microsoft.com/en-us/library/ms536939(VS.85).aspx 839 840 As of MSIE 4.0, the keydown event fires for the following keys: 841 * Editing: Delete (46), Insert (45) 842 * Function: F1 - F12 843 * Letters: A - Z (uppercase and lowercase) 844 * Navigation: Home, End, Left, Right, Up, Down 845 * Numerals: 0 - 9 846 * Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~ 847 * System: Escape (27), Space (32), Shift (16), Tab (9) 848 849 As of MSIE 5, the event also fires for the following keys: 850 * Editing: Backspace (8) 851 * Navigation: PageUp (33), PageDown (34) 852 * System: Shift+Tab (9) 853 854 Documentation about the keydown event: 855 http://msdn.microsoft.com/en-us/library/ms536938(VS.85).aspx 856 857 As of MSIE 4.0, the keyup event fires for the following keys: 858 * Editing: Delete, Insert 859 * Function: F1 - F12 860 * Letters: A - Z (uppercase and lowercase) 861 * Navigation: Home (36), End (35), Left (37), Right (39), Up (38), Down (40) 862 * Numerals: 0 - 9 863 * Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~ 864 * System: Escape (27), Space (32), Shift (16), Tab (9) 865 866 As of MSIE 5, the event also fires for the following keys: 867 * Editing: Backspace (8) 868 * Navigation: PageUp (33), PageDown (34) 869 * System: Shift+Tab (9) 870 871 Documentation about the keyup event: 872 http://msdn.microsoft.com/en-us/library/ms536940(VS.85).aspx 873 874 For further gory details and a different implementation see: 875 http://code.google.com/p/doctype/source/browse/trunk/goog/events/keycodes.js 876 http://code.google.com/p/doctype/source/browse/trunk/goog/events/keyhandler.js 877 878 Opera keydown/keyup: 879 These events fire for all keys, including for modifiers. 880 keyCode is always set. 881 charCode is never set. 882 which is always set. 883 keyIdentifier is always undefined. 884 885 Opera keypress: 886 This event fires for all keys, except for modifiers themselves. 887 keyCode is always set. 888 charCode is never set. 889 which is set for all characters. which = 0 for several virtual keys. 890 which is known to be set for: Backspace (8), Tab (9), Enter (13), Shift 891 (16), Control (17), Alt (18), Pause (19), Escape (27), End (35), Home 892 (36), Insert (45), Delete (46), NumLock (144). 893 which is known to be unset for: F1 - F12, PageUp (33), PageDown (34), Left 894 (37), Up (38), Right (39), Down (40). 895 keyIdentifier is always undefined. 896 897 MSIE keyup/keypress/keydown: 898 Event firing conditions are described above. 899 keyCode is always set. 900 charCode is never set. 901 which is never set. 902 keyIdentifier is always undefined. 903 904 Webkit keydown/keyup: 905 These events fires for all keys, including for modifiers. 906 keyCode is always set. 907 charCode is never set. 908 which is always set. 909 keyIdentifier is always set. 910 911 Webkit keypress: 912 This event fires for characters keys, similarly to MSIE (see above info). 913 keyCode is always set. 914 charCode is always set for all characters. 915 which is always set. 916 keyIdentifier is null. 917 918 Gecko keydown/keyup: 919 These events fire for all keys, including for modifiers. 920 keyCode is always set. 921 charCode is never set. 922 which is always set. 923 keyIdentifier is always undefined. 924 925 Gecko keypress: 926 This event fires for all keys, except for modifiers themselves. 927 keyCode is only set for virtual keys, not for characters. 928 charCode is always set for all characters. 929 which is always set for all characters and for the Enter virtual key. 930 keyIdentifier is always undefined. 931 932 Another important difference between the KeyboardEventListener class and the 933 getKey() function is that the class tries to ensure that the keypress event 934 is fired for the handler, even if the Web browser does not do it natively. 935 Also, the class tries to provide a consistent approach to keyboard event 936 repetition when the user holds down a key for longer periods of time, by 937 repeating only the keypress event. 938 939 On Linux, Opera, Firefox and Konqueror do not repeat the keydown event, only 940 keypress. On Windows, Opera, Firefox and MSIE do repeat the keydown and 941 keypress events while the user holds down the key. Webkit repeats the 942 keydown and the keypress (when it fires) events on both systems. 943 944 The default action can be prevented for during keydown in MSIE, and during 945 keypress for the other browsers. In Webkit when keypress doesn't fire, 946 keydown needs to be prevented. 947 948 The KeyboardEventListener class tries to bring consistency. The keydown 949 event never repeats, only the keypress event repeats and it always fires for 950 all keys. The keypress event never fires for modifiers. Events should always 951 be prevented during keypress - the class deals with preventing the event 952 during keydown or keypress as needed in Webkit and MSIE. 953 954 If no code/keyIdentifier is given by the browser, the getKey() function 955 returns null. In the case of the KeyboardEventListener class, keyCode_ 956 / key_ / charCode_ / char_ will be null or undefined. 957 */ 958 959 /** 960 * During a keyboard event flow, this holds the current key code, starting 961 * from the <code>keydown</code> event. 962 * 963 * @private 964 * @type Number 965 */ 966 var keyCode_ = null; 967 968 /** 969 * During a keyboard event flow, this holds the current key, starting from the 970 * <code>keydown</code> event. 971 * 972 * @private 973 * @type String 974 */ 975 var key_ = null; 976 977 /** 978 * During a keyboard event flow, this holds the current character code, 979 * starting from the <code>keypress</code> event. 980 * 981 * @private 982 * @type Number 983 */ 984 var charCode_ = null; 985 986 /** 987 * During a keyboard event flow, this holds the current character, starting 988 * from the <code>keypress</code> event. 989 * 990 * @private 991 * @type String 992 */ 993 var char_ = null; 994 995 /** 996 * True if the current keyboard event is repeating. This happens when the user 997 * holds down a key for longer periods of time. 998 * 999 * @private 1000 * @type Boolean 1001 */ 1002 var repeat_ = false; 1003 1004 1005 if (!handlers_) { 1006 throw new TypeError('The first argument must be of type an object.'); 1007 } 1008 1009 if (!handlers_.keydown && !handlers_.keypress && !handlers_.keyup) { 1010 throw new TypeError('The provided handlers object has no keyboard event' + 1011 'handler.'); 1012 } 1013 1014 if (handlers_.keydown && typeof handlers_.keydown != 'function') { 1015 throw new TypeError('The keydown event handler is not a function!'); 1016 } 1017 if (handlers_.keypress && typeof handlers_.keypress != 'function') { 1018 throw new TypeError('The keypress event handler is not a function!'); 1019 } 1020 if (handlers_.keyup && typeof handlers_.keyup != 'function') { 1021 throw new TypeError('The keyup event handler is not a function!'); 1022 } 1023 1024 /** 1025 * Attach the keyboard event listeners to the current DOM element. 1026 */ 1027 this.attach = function () { 1028 keyCode_ = null; 1029 key_ = null; 1030 charCode_ = null; 1031 char_ = null; 1032 repeat_ = false; 1033 1034 // FIXME: I have some ideas for a solution to the problem of having multiple 1035 // event handlers like these attached to the same element. Somehow, only one 1036 // should do all the needed work. 1037 1038 elem_.addEventListener('keydown', keydown, false); 1039 elem_.addEventListener('keypress', keypress, false); 1040 elem_.addEventListener('keyup', keyup, false); 1041 }; 1042 1043 /** 1044 * Detach the keyboard event listeners from the current DOM element. 1045 */ 1046 this.detach = function () { 1047 elem_.removeEventListener('keydown', keydown, false); 1048 elem_.removeEventListener('keypress', keypress, false); 1049 elem_.removeEventListener('keyup', keyup, false); 1050 1051 keyCode_ = null; 1052 key_ = null; 1053 charCode_ = null; 1054 char_ = null; 1055 repeat_ = false; 1056 }; 1057 1058 /** 1059 * Dispatch an event. 1060 * 1061 * <p>This function simply invokes the handler for the event of the given 1062 * <var>type</var>. The handler receives the <var>ev</var> event. 1063 * 1064 * @private 1065 * @param {String} type The event type to dispatch. 1066 * @param {Event} ev The DOM Event object to dispatch to the handler. 1067 */ 1068 function dispatch (type, ev) { 1069 if (!handlers_[type]) { 1070 return; 1071 } 1072 1073 var handler = handlers_[type]; 1074 1075 if (type == ev.type) { 1076 handler.call(elem_, ev); 1077 1078 } else { 1079 // This happens when the keydown event tries to dispatch a keypress event. 1080 1081 // FIXME: I could use createEvent() ... food for thought for later 1082 var ev_new = {}; 1083 lib.extend(ev_new, ev); 1084 ev_new.type = type; 1085 1086 // Make sure preventDefault() is not borked... 1087 ev_new.preventDefault = function () { 1088 ev.preventDefault(); 1089 }; 1090 1091 handler.call(elem_, ev_new); 1092 } 1093 }; 1094 1095 /** 1096 * The <code>keydown</code> event handler. This function determines the key 1097 * pressed by the user, and checks if the <code>keypress</code> event will 1098 * fire in the current Web browser, or not. If it does not, a synthetic 1099 * <code>keypress</code> event will be fired. 1100 * 1101 * @private 1102 * @param {Event} ev The DOM Event object. 1103 */ 1104 function keydown (ev) { 1105 var prevKey = key_; 1106 1107 charCode_ = null; 1108 char_ = null; 1109 1110 findKeyCode(ev); 1111 1112 ev.keyCode_ = keyCode_; 1113 ev.key_ = key_; 1114 ev.repeat_ = key_ && prevKey == key_ ? true : false; 1115 1116 repeat_ = ev.repeat_; 1117 1118 // When the user holds down a key for a longer period of time, the keypress 1119 // event is generally repeated. However, in Webkit keydown is repeated (and 1120 // keypress if it fires keypress for the key). As such, we do not dispatch 1121 // the keydown event when a key event starts to be repeated. 1122 if (!repeat_) { 1123 dispatch('keydown', ev); 1124 } 1125 1126 // MSIE and Webkit only fire the keypress event for characters 1127 // (alpha-numeric and symbols). 1128 if (!isModifierKey(key_) && !firesKeyPress(ev)) { 1129 ev.type_ = 'keydown'; 1130 keypress(ev); 1131 } 1132 }; 1133 1134 /** 1135 * The <code>keypress</code> event handler. This function determines the 1136 * character generated by the keyboard event. 1137 * 1138 * @private 1139 * @param {Event} ev The DOM Event object. 1140 */ 1141 function keypress (ev) { 1142 // We reuse the keyCode_/key_ from the keydown event, because ev.keyCode 1143 // generally holds the character code during the keypress event. 1144 // However, if keyCode_ is not available, try to determine the key for this 1145 // event as well. 1146 if (!keyCode_) { 1147 findKeyCode(ev); 1148 repeat_ = false; 1149 } 1150 1151 ev.keyCode_ = keyCode_; 1152 ev.key_ = key_; 1153 1154 findCharCode(ev); 1155 1156 ev.charCode_ = charCode_; 1157 ev.char_ = char_; 1158 1159 // Any subsequent keypress event is considered a repeated keypress (the user 1160 // is holding down the key). 1161 ev.repeat_ = repeat_; 1162 if (!repeat_) { 1163 repeat_ = true; 1164 } 1165 1166 if (!isModifierKey(key_)) { 1167 dispatch('keypress', ev); 1168 } 1169 }; 1170 1171 /** 1172 * The <code>keyup</code> event handler. 1173 * 1174 * @private 1175 * @param {Event} ev The DOM Event object. 1176 */ 1177 function keyup (ev) { 1178 /* 1179 * Try to determine the keyCode_ for keyup again, even if we might already 1180 * have it from keydown. This is needed because the user might press some 1181 * key which only generates the keydown and keypress events, after which 1182 * a sudden keyup event is fired for a completely different key. 1183 * 1184 * Example: in Opera press F2 then Escape. It will first generate two 1185 * events, keydown and keypress, for the F2 key. When you press Escape to 1186 * close the dialog box, the script receives keyup for Escape. 1187 */ 1188 findKeyCode(ev); 1189 1190 ev.keyCode_ = keyCode_; 1191 ev.key_ = key_; 1192 1193 // Provide the character info from the keypress event in keyup as well. 1194 ev.charCode_ = charCode_; 1195 ev.char_ = char_; 1196 1197 dispatch('keyup', ev); 1198 1199 keyCode_ = null; 1200 key_ = null; 1201 charCode_ = null; 1202 char_ = null; 1203 repeat_ = false; 1204 }; 1205 1206 /** 1207 * Tells if the <var>key</var> is a modifier or not. 1208 * 1209 * @private 1210 * @param {String} key The key name. 1211 * @returns {Boolean} True if the <var>key</var> is a modifier, or false if 1212 * not. 1213 */ 1214 function isModifierKey (key) { 1215 switch (key) { 1216 case 'Shift': 1217 case 'Control': 1218 case 'Alt': 1219 case 'Meta': 1220 case 'Win': 1221 return true; 1222 default: 1223 return false; 1224 } 1225 }; 1226 1227 /** 1228 * Tells if the current Web browser will fire the <code>keypress</code> event 1229 * for the current <code>keydown</code> event object. 1230 * 1231 * @private 1232 * @param {Event} ev The DOM Event object. 1233 * @returns {Boolean} True if the Web browser will fire 1234 * a <code>keypress</code> event, or false if not. 1235 */ 1236 function firesKeyPress (ev) { 1237 if (!lib.browser.msie && !lib.browser.webkit) { 1238 return true; 1239 } 1240 1241 // Check if the key is a character key, or not. 1242 // If it's not a character, then keypress will not fire. 1243 // Known exceptions: keypress fires for Space, Enter and Escape in MSIE. 1244 if (key_ && key_ != 'Space' && key_ != 'Enter' && key_ != 'Escape' && 1245 key_.length != 1) { 1246 return false; 1247 } 1248 1249 // Webkit doesn't fire keypress for Escape as well ... 1250 if (lib.browser.webkit && key_ == 'Escape') { 1251 return false; 1252 } 1253 1254 // MSIE does not fire keypress if you hold Control / Alt down, while Shift 1255 // is off. Albeit, based on testing I am not completely sure if Shift needs 1256 // to be down or not. Sometimes MSIE won't fire keypress even if I hold 1257 // Shift down, and sometimes it does. Eh. 1258 if (lib.browser.msie && !ev.shiftKey && (ev.ctrlKey || ev.altKey)) { 1259 return false; 1260 } 1261 1262 return true; 1263 }; 1264 1265 /** 1266 * Determine the key and the key code for the current DOM Event object. This 1267 * function updates the <var>keyCode_</var> and the <var>key_</var> variables 1268 * to hold the result. 1269 * 1270 * @private 1271 * @param {Event} ev The DOM Event object. 1272 */ 1273 function findKeyCode (ev) { 1274 /* 1275 * If the event has no keyCode/which/keyIdentifier values, then simply do 1276 * not overwrite any existing keyCode_/key_. 1277 */ 1278 if (ev.type == 'keyup' && !ev.keyCode && !ev.which && (!ev.keyIdentifier || 1279 ev.keyIdentifier == 'Unidentified' || ev.keyIdentifier == 'U+0000')) { 1280 return; 1281 } 1282 1283 keyCode_ = null; 1284 key_ = null; 1285 1286 // Try to use keyCode/which. 1287 if (ev.keyCode || ev.which) { 1288 keyCode_ = ev.keyCode || ev.which; 1289 1290 // Fix Webkit quirks 1291 if (lib.browser.webkit) { 1292 // Old Webkit gives keyCode 25 when Shift+Tab is used. 1293 if (keyCode_ == 25 && this.shiftKey) { 1294 keyCode_ = lib.dom.keyNames.Tab; 1295 } else if (keyCode_ >= 63232 && keyCode_ in lib.dom.keyCodes_Safari2) { 1296 // Old Webkit gives wrong values for several keys. 1297 keyCode_ = lib.dom.keyCodes_Safari2[keyCode_]; 1298 } 1299 } 1300 1301 // Fix keyCode quirks in all browsers. 1302 if (keyCode_ in lib.dom.keyCodes_fixes) { 1303 keyCode_ = lib.dom.keyCodes_fixes[keyCode_]; 1304 } 1305 1306 key_ = lib.dom.keyCodes[keyCode_] || String.fromCharCode(keyCode_); 1307 1308 return; 1309 } 1310 1311 // Try to use ev.keyIdentifier. This is only available in Webkit and 1312 // Konqueror 4, each having some quirks. Sometimes the property is needed, 1313 // because keyCode/which are not always available. 1314 1315 var key = null, 1316 keyCode = null, 1317 id = ev.keyIdentifier; 1318 1319 if (!id || id == 'Unidentified' || id == 'U+0000') { 1320 return; 1321 } 1322 1323 if (id.substr(0, 2) == 'U+') { 1324 // Webkit gives character codes using the 'U+XXXX' notation, as per spec. 1325 keyCode = parseInt(id.substr(2), 16); 1326 1327 } else if (id.length == 1) { 1328 // Konqueror 4 implements keyIdentifier, and they provide the Unicode 1329 // character directly, instead of using the 'U+XXXX' notation. 1330 keyCode = id.charCodeAt(0); 1331 key = id; 1332 1333 } else { 1334 /* 1335 * Common keyIdentifiers like 'PageDown' are used as they are. 1336 * We determine the common keyCode used by Web browsers, from the 1337 * lib.dom.keyNames object. 1338 */ 1339 keyCode_ = lib.dom.keyNames[id] || null; 1340 key_ = id; 1341 1342 return; 1343 } 1344 1345 // Some keyIdentifiers like 'U+007F' (127: Delete) need to become key names. 1346 if (keyCode in lib.dom.keyCodes && (keyCode <= 32 || keyCode == 127 || keyCode 1347 == 144)) { 1348 key_ = lib.dom.keyCodes[keyCode]; 1349 } else { 1350 if (!key) { 1351 key = String.fromCharCode(keyCode); 1352 } 1353 1354 // Konqueror gives lower-case chars 1355 key_ = key.toUpperCase(); 1356 if (key != key_) { 1357 keyCode = key_.charCodeAt(0); 1358 } 1359 } 1360 1361 // Correct the keyCode, make sure it's a common keyCode, not the Unicode 1362 // decimal representation of the character. 1363 if (key_ == 'Delete' || key_.length == 1 && key_ in lib.dom.keyNames) { 1364 keyCode = lib.dom.keyNames[key_]; 1365 } 1366 1367 keyCode_ = keyCode; 1368 }; 1369 1370 /** 1371 * Determine the character and the character code for the current DOM Event 1372 * object. This function updates the <var>charCode_</var> and the 1373 * <var>char_</var> variables to hold the result. 1374 * 1375 * @private 1376 * @param {Event} ev The DOM Event object. 1377 */ 1378 function findCharCode (ev) { 1379 charCode_ = null; 1380 char_ = null; 1381 1382 // Webkit and Gecko implement ev.charCode. 1383 if (ev.charCode) { 1384 charCode_ = ev.charCode; 1385 char_ = String.fromCharCode(ev.charCode); 1386 1387 return; 1388 } 1389 1390 // Try the keyCode mess. 1391 if (ev.keyCode || ev.which) { 1392 var keyCode = ev.keyCode || ev.which; 1393 1394 var force = false; 1395 1396 // We accept some keyCodes. 1397 switch (keyCode) { 1398 case lib.dom.keyNames.Tab: 1399 case lib.dom.keyNames.Enter: 1400 case lib.dom.keyNames.Space: 1401 force = true; 1402 } 1403 1404 // Do not consider the keyCode a character code, if during the keydown 1405 // event it was determined the key does not generate a character, unless 1406 // it's Tab, Enter or Space. 1407 if (!force && key_ && key_.length != 1) { 1408 return; 1409 } 1410 1411 // If the keypress event at hand is synthetically dispatched by keydown, 1412 // then special treatment is needed. This happens only in Webkit and MSIE. 1413 if (ev.type_ == 'keydown') { 1414 var key = lib.dom.keyCodes[keyCode]; 1415 // Check if the keyCode points to a single character. 1416 // If it does, use it. 1417 if (key && key.length == 1) { 1418 charCode_ = key.charCodeAt(0); // keyCodes != charCodes 1419 char_ = key; 1420 } 1421 } else if (keyCode >= 32 || force) { 1422 // For normal keypress events, we are done. 1423 charCode_ = keyCode; 1424 char_ = String.fromCharCode(keyCode); 1425 } 1426 1427 if (charCode_) { 1428 return; 1429 } 1430 } 1431 1432 /* 1433 * Webkit and Konqueror do not provide a keyIdentifier in the keypress 1434 * event, as per spec. However, in the unlikely case when the keyCode is 1435 * missing, and the keyIdentifier is available, we use it. 1436 * 1437 * This property might be used when a synthetic keypress event is generated 1438 * by the keydown event, and keyCode/charCode/which are all not available. 1439 */ 1440 1441 var c = null, 1442 charCode = null, 1443 id = ev.keyIdentifier; 1444 1445 if (id && id != 'Unidentified' && id != 'U+0000' && 1446 (id.substr(0, 2) == 'U+' || id.length == 1)) { 1447 1448 // Characters in Konqueror... 1449 if (id.length == 1) { 1450 charCode = id.charCodeAt(0); 1451 c = id; 1452 1453 } else { 1454 // Webkit uses the 'U+XXXX' notation as per spec. 1455 charCode = parseInt(id.substr(2), 16); 1456 } 1457 1458 if (charCode == lib.dom.keyNames.Tab || 1459 charCode == lib.dom.keyNames.Enter || 1460 charCode >= 32 && charCode != 127 && 1461 charCode != lib.dom.keyNames.NumLock) { 1462 1463 charCode_ = charCode; 1464 char_ = c || String.fromCharCode(charCode); 1465 1466 return; 1467 } 1468 } 1469 1470 // Try to use the key determined from the previous keydown event, if it 1471 // holds a character. 1472 if (key_ && key_.length == 1) { 1473 charCode_ = key_.charCodeAt(0); 1474 char_ = key_; 1475 } 1476 }; 1477 1478 this.attach(); 1479 }; 1480 1481 // lib.dom.KeyboardEvent.getKey() is not included here. You can get it from the 1482 // libmacrame project: http://code.google.com/p/libmacrame. 1483 1484 // vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix: 1485