Path: blob/trunk/third_party/closure/goog/events/keyhandler.js
2868 views
// Copyright 2007 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview This file contains a class for working with keyboard events16* that repeat consistently across browsers and platforms. It also unifies the17* key code so that it is the same in all browsers and platforms.18*19* Different web browsers have very different keyboard event handling. Most20* importantly is that only certain browsers repeat keydown events:21* IE, Opera, FF/Win32, and Safari 3 repeat keydown events.22* FF/Mac and Safari 2 do not.23*24* For the purposes of this code, "Safari 3" means WebKit 525+, when WebKit25* decided that they should try to match IE's key handling behavior.26* Safari 3.0.4, which shipped with Leopard (WebKit 523), has the27* Safari 2 behavior.28*29* Firefox, Safari, Opera prevent on keypress30*31* IE prevents on keydown32*33* Firefox does not fire keypress for shift, ctrl, alt34* Firefox does fire keydown for shift, ctrl, alt, meta35* Firefox does not repeat keydown for shift, ctrl, alt, meta36*37* Firefox does not fire keypress for up and down in an input38*39* Opera fires keypress for shift, ctrl, alt, meta40* Opera does not repeat keypress for shift, ctrl, alt, meta41*42* Safari 2 and 3 do not fire keypress for shift, ctrl, alt43* Safari 2 does not fire keydown for shift, ctrl, alt44* Safari 3 *does* fire keydown for shift, ctrl, alt45*46* IE provides the keycode for keyup/down events and the charcode (in the47* keycode field) for keypress.48*49* Mozilla provides the keycode for keyup/down and the charcode for keypress50* unless it's a non text modifying key in which case the keycode is provided.51*52* Safari 3 provides the keycode and charcode for all events.53*54* Opera provides the keycode for keyup/down event and either the charcode or55* the keycode (in the keycode field) for keypress events.56*57* Firefox x11 doesn't fire keydown events if a another key is already held down58* until the first key is released. This can cause a key event to be fired with59* a keyCode for the first key and a charCode for the second key.60*61* Safari in keypress62*63* charCode keyCode which64* ENTER: 13 13 1365* F1: 63236 63236 6323666* F8: 63243 63243 6324367* ...68* p: 112 112 11269* P: 80 80 8070*71* Firefox, keypress:72*73* charCode keyCode which74* ENTER: 0 13 1375* F1: 0 112 076* F8: 0 119 077* ...78* p: 112 0 11279* P: 80 0 8080*81* Opera, Mac+Win32, keypress:82*83* charCode keyCode which84* ENTER: undefined 13 1385* F1: undefined 112 086* F8: undefined 119 087* ...88* p: undefined 112 11289* P: undefined 80 8090*91* IE7, keydown92*93* charCode keyCode which94* ENTER: undefined 13 undefined95* F1: undefined 112 undefined96* F8: undefined 119 undefined97* ...98* p: undefined 80 undefined99* P: undefined 80 undefined100*101* @author [email protected] (Erik Arvidsson)102* @author [email protected] (Emil A Eklund)103* @see ../demos/keyhandler.html104*/105106goog.provide('goog.events.KeyEvent');107goog.provide('goog.events.KeyHandler');108goog.provide('goog.events.KeyHandler.EventType');109110goog.require('goog.events');111goog.require('goog.events.BrowserEvent');112goog.require('goog.events.EventTarget');113goog.require('goog.events.EventType');114goog.require('goog.events.KeyCodes');115goog.require('goog.userAgent');116117118119/**120* A wrapper around an element that you want to listen to keyboard events on.121* @param {Element|Document=} opt_element The element or document to listen on.122* @param {boolean=} opt_capture Whether to listen for browser events in123* capture phase (defaults to false).124* @constructor125* @extends {goog.events.EventTarget}126* @final127*/128goog.events.KeyHandler = function(opt_element, opt_capture) {129goog.events.EventTarget.call(this);130131if (opt_element) {132this.attach(opt_element, opt_capture);133}134};135goog.inherits(goog.events.KeyHandler, goog.events.EventTarget);136137138/**139* This is the element that we will listen to the real keyboard events on.140* @type {Element|Document|null}141* @private142*/143goog.events.KeyHandler.prototype.element_ = null;144145146/**147* The key for the key press listener.148* @type {goog.events.Key}149* @private150*/151goog.events.KeyHandler.prototype.keyPressKey_ = null;152153154/**155* The key for the key down listener.156* @type {goog.events.Key}157* @private158*/159goog.events.KeyHandler.prototype.keyDownKey_ = null;160161162/**163* The key for the key up listener.164* @type {goog.events.Key}165* @private166*/167goog.events.KeyHandler.prototype.keyUpKey_ = null;168169170/**171* Used to detect keyboard repeat events.172* @private173* @type {number}174*/175goog.events.KeyHandler.prototype.lastKey_ = -1;176177178/**179* Keycode recorded for key down events. As most browsers don't report the180* keycode in the key press event we need to record it in the key down phase.181* @private182* @type {number}183*/184goog.events.KeyHandler.prototype.keyCode_ = -1;185186187/**188* Alt key recorded for key down events. FF on Mac does not report the alt key189* flag in the key press event, we need to record it in the key down phase.190* @type {boolean}191* @private192*/193goog.events.KeyHandler.prototype.altKey_ = false;194195196/**197* Enum type for the events fired by the key handler198* @enum {string}199*/200goog.events.KeyHandler.EventType = {201KEY: 'key'202};203204205/**206* An enumeration of key codes that Safari 2 does incorrectly207* @type {Object}208* @private209*/210goog.events.KeyHandler.safariKey_ = {211'3': goog.events.KeyCodes.ENTER, // 13212'12': goog.events.KeyCodes.NUMLOCK, // 144213'63232': goog.events.KeyCodes.UP, // 38214'63233': goog.events.KeyCodes.DOWN, // 40215'63234': goog.events.KeyCodes.LEFT, // 37216'63235': goog.events.KeyCodes.RIGHT, // 39217'63236': goog.events.KeyCodes.F1, // 112218'63237': goog.events.KeyCodes.F2, // 113219'63238': goog.events.KeyCodes.F3, // 114220'63239': goog.events.KeyCodes.F4, // 115221'63240': goog.events.KeyCodes.F5, // 116222'63241': goog.events.KeyCodes.F6, // 117223'63242': goog.events.KeyCodes.F7, // 118224'63243': goog.events.KeyCodes.F8, // 119225'63244': goog.events.KeyCodes.F9, // 120226'63245': goog.events.KeyCodes.F10, // 121227'63246': goog.events.KeyCodes.F11, // 122228'63247': goog.events.KeyCodes.F12, // 123229'63248': goog.events.KeyCodes.PRINT_SCREEN, // 44230'63272': goog.events.KeyCodes.DELETE, // 46231'63273': goog.events.KeyCodes.HOME, // 36232'63275': goog.events.KeyCodes.END, // 35233'63276': goog.events.KeyCodes.PAGE_UP, // 33234'63277': goog.events.KeyCodes.PAGE_DOWN, // 34235'63289': goog.events.KeyCodes.NUMLOCK, // 144236'63302': goog.events.KeyCodes.INSERT // 45237};238239240/**241* An enumeration of key identifiers currently part of the W3C draft for DOM3242* and their mappings to keyCodes.243* http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set244* This is currently supported in Safari and should be platform independent.245* @type {Object}246* @private247*/248goog.events.KeyHandler.keyIdentifier_ = {249'Up': goog.events.KeyCodes.UP, // 38250'Down': goog.events.KeyCodes.DOWN, // 40251'Left': goog.events.KeyCodes.LEFT, // 37252'Right': goog.events.KeyCodes.RIGHT, // 39253'Enter': goog.events.KeyCodes.ENTER, // 13254'F1': goog.events.KeyCodes.F1, // 112255'F2': goog.events.KeyCodes.F2, // 113256'F3': goog.events.KeyCodes.F3, // 114257'F4': goog.events.KeyCodes.F4, // 115258'F5': goog.events.KeyCodes.F5, // 116259'F6': goog.events.KeyCodes.F6, // 117260'F7': goog.events.KeyCodes.F7, // 118261'F8': goog.events.KeyCodes.F8, // 119262'F9': goog.events.KeyCodes.F9, // 120263'F10': goog.events.KeyCodes.F10, // 121264'F11': goog.events.KeyCodes.F11, // 122265'F12': goog.events.KeyCodes.F12, // 123266'U+007F': goog.events.KeyCodes.DELETE, // 46267'Home': goog.events.KeyCodes.HOME, // 36268'End': goog.events.KeyCodes.END, // 35269'PageUp': goog.events.KeyCodes.PAGE_UP, // 33270'PageDown': goog.events.KeyCodes.PAGE_DOWN, // 34271'Insert': goog.events.KeyCodes.INSERT // 45272};273274275/**276* If true, the KeyEvent fires on keydown. Otherwise, it fires on keypress.277*278* @type {boolean}279* @private280*/281goog.events.KeyHandler.USES_KEYDOWN_ = goog.userAgent.IE ||282goog.userAgent.EDGE ||283goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525');284285286/**287* If true, the alt key flag is saved during the key down and reused when288* handling the key press. FF on Mac does not set the alt flag in the key press289* event.290* @type {boolean}291* @private292*/293goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_ =294goog.userAgent.MAC && goog.userAgent.GECKO;295296297/**298* Records the keycode for browsers that only returns the keycode for key up/299* down events. For browser/key combinations that doesn't trigger a key pressed300* event it also fires the patched key event.301* @param {goog.events.BrowserEvent} e The key down event.302* @private303*/304goog.events.KeyHandler.prototype.handleKeyDown_ = function(e) {305// Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window306// before we've caught a key-up event. If the last-key was one of these we307// reset the state.308if (goog.userAgent.WEBKIT || goog.userAgent.EDGE) {309if (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||310this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey ||311goog.userAgent.MAC && this.lastKey_ == goog.events.KeyCodes.META &&312!e.metaKey) {313this.resetState();314}315}316317if (this.lastKey_ == -1) {318if (e.ctrlKey && e.keyCode != goog.events.KeyCodes.CTRL) {319this.lastKey_ = goog.events.KeyCodes.CTRL;320} else if (e.altKey && e.keyCode != goog.events.KeyCodes.ALT) {321this.lastKey_ = goog.events.KeyCodes.ALT;322} else if (e.metaKey && e.keyCode != goog.events.KeyCodes.META) {323this.lastKey_ = goog.events.KeyCodes.META;324}325}326327if (goog.events.KeyHandler.USES_KEYDOWN_ &&328!goog.events.KeyCodes.firesKeyPressEvent(329e.keyCode, this.lastKey_, e.shiftKey, e.ctrlKey, e.altKey,330e.metaKey)) {331this.handleEvent(e);332} else {333this.keyCode_ = goog.events.KeyCodes.normalizeKeyCode(e.keyCode);334if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {335this.altKey_ = e.altKey;336}337}338};339340341/**342* Resets the stored previous values. Needed to be called for webkit which will343* not generate a key up for meta key operations. This should only be called344* when having finished with repeat key possibilities.345*/346goog.events.KeyHandler.prototype.resetState = function() {347this.lastKey_ = -1;348this.keyCode_ = -1;349};350351352/**353* Clears the stored previous key value, resetting the key repeat status. Uses354* -1 because the Safari 3 Windows beta reports 0 for certain keys (like Home355* and End.)356* @param {goog.events.BrowserEvent} e The keyup event.357* @private358*/359goog.events.KeyHandler.prototype.handleKeyup_ = function(e) {360this.resetState();361this.altKey_ = e.altKey;362};363364365/**366* Handles the events on the element.367* @param {goog.events.BrowserEvent} e The keyboard event sent from the368* browser.369*/370goog.events.KeyHandler.prototype.handleEvent = function(e) {371var be = e.getBrowserEvent();372var keyCode, charCode;373var altKey = be.altKey;374375// IE reports the character code in the keyCode field for keypress events.376// There are two exceptions however, Enter and Escape.377if (goog.userAgent.IE && e.type == goog.events.EventType.KEYPRESS) {378keyCode = this.keyCode_;379charCode = keyCode != goog.events.KeyCodes.ENTER &&380keyCode != goog.events.KeyCodes.ESC ?381be.keyCode :3820;383384// Safari reports the character code in the keyCode field for keypress385// events but also has a charCode field.386} else if (387(goog.userAgent.WEBKIT || goog.userAgent.EDGE) &&388e.type == goog.events.EventType.KEYPRESS) {389keyCode = this.keyCode_;390charCode = be.charCode >= 0 && be.charCode < 63232 &&391goog.events.KeyCodes.isCharacterKey(keyCode) ?392be.charCode :3930;394395// Opera reports the keycode or the character code in the keyCode field.396} else if (goog.userAgent.OPERA && !goog.userAgent.WEBKIT) {397keyCode = this.keyCode_;398charCode = goog.events.KeyCodes.isCharacterKey(keyCode) ? be.keyCode : 0;399400// Mozilla reports the character code in the charCode field.401} else {402keyCode = be.keyCode || this.keyCode_;403charCode = be.charCode || 0;404if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {405altKey = this.altKey_;406}407// On the Mac, shift-/ triggers a question mark char code and no key code408// (normalized to WIN_KEY), so we synthesize the latter.409if (goog.userAgent.MAC && charCode == goog.events.KeyCodes.QUESTION_MARK &&410keyCode == goog.events.KeyCodes.WIN_KEY) {411keyCode = goog.events.KeyCodes.SLASH;412}413}414415keyCode = goog.events.KeyCodes.normalizeKeyCode(keyCode);416var key = keyCode;417418// Correct the key value for certain browser-specific quirks.419if (keyCode) {420if (keyCode >= 63232 && keyCode in goog.events.KeyHandler.safariKey_) {421// NOTE(nicksantos): Safari 3 has fixed this problem,422// this is only needed for Safari 2.423key = goog.events.KeyHandler.safariKey_[keyCode];424} else {425// Safari returns 25 for Shift+Tab instead of 9.426if (keyCode == 25 && e.shiftKey) {427key = 9;428}429}430} else if (431be.keyIdentifier &&432be.keyIdentifier in goog.events.KeyHandler.keyIdentifier_) {433// This is needed for Safari Windows because it currently doesn't give a434// keyCode/which for non printable keys.435key = goog.events.KeyHandler.keyIdentifier_[be.keyIdentifier];436}437438// If we get the same keycode as a keydown/keypress without having seen a439// keyup event, then this event was caused by key repeat.440var repeat = key == this.lastKey_;441this.lastKey_ = key;442443var event = new goog.events.KeyEvent(key, charCode, repeat, be);444event.altKey = altKey;445this.dispatchEvent(event);446};447448449/**450* Returns the element listened on for the real keyboard events.451* @return {Element|Document|null} The element listened on for the real452* keyboard events.453*/454goog.events.KeyHandler.prototype.getElement = function() {455return this.element_;456};457458459/**460* Adds the proper key event listeners to the element.461* @param {Element|Document} element The element to listen on.462* @param {boolean=} opt_capture Whether to listen for browser events in463* capture phase (defaults to false).464*/465goog.events.KeyHandler.prototype.attach = function(element, opt_capture) {466if (this.keyUpKey_) {467this.detach();468}469470this.element_ = element;471472this.keyPressKey_ = goog.events.listen(473this.element_, goog.events.EventType.KEYPRESS, this, opt_capture);474475// Most browsers (Safari 2 being the notable exception) doesn't include the476// keyCode in keypress events (IE has the char code in the keyCode field and477// Mozilla only included the keyCode if there's no charCode). Thus we have to478// listen for keydown to capture the keycode.479this.keyDownKey_ = goog.events.listen(480this.element_, goog.events.EventType.KEYDOWN, this.handleKeyDown_,481opt_capture, this);482483484this.keyUpKey_ = goog.events.listen(485this.element_, goog.events.EventType.KEYUP, this.handleKeyup_,486opt_capture, this);487};488489490/**491* Removes the listeners that may exist.492*/493goog.events.KeyHandler.prototype.detach = function() {494if (this.keyPressKey_) {495goog.events.unlistenByKey(this.keyPressKey_);496goog.events.unlistenByKey(this.keyDownKey_);497goog.events.unlistenByKey(this.keyUpKey_);498this.keyPressKey_ = null;499this.keyDownKey_ = null;500this.keyUpKey_ = null;501}502this.element_ = null;503this.lastKey_ = -1;504this.keyCode_ = -1;505};506507508/** @override */509goog.events.KeyHandler.prototype.disposeInternal = function() {510goog.events.KeyHandler.superClass_.disposeInternal.call(this);511this.detach();512};513514515516/**517* This class is used for the goog.events.KeyHandler.EventType.KEY event and518* it overrides the key code with the fixed key code.519* @param {number} keyCode The adjusted key code.520* @param {number} charCode The unicode character code.521* @param {boolean} repeat Whether this event was generated by keyboard repeat.522* @param {Event} browserEvent Browser event object.523* @constructor524* @extends {goog.events.BrowserEvent}525* @final526*/527goog.events.KeyEvent = function(keyCode, charCode, repeat, browserEvent) {528goog.events.BrowserEvent.call(this, browserEvent);529this.type = goog.events.KeyHandler.EventType.KEY;530531/**532* Keycode of key press.533* @type {number}534*/535this.keyCode = keyCode;536537/**538* Unicode character code.539* @type {number}540*/541this.charCode = charCode;542543/**544* True if this event was generated by keyboard auto-repeat (i.e., the user is545* holding the key down.)546* @type {boolean}547*/548this.repeat = repeat;549};550goog.inherits(goog.events.KeyEvent, goog.events.BrowserEvent);551552553