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