From bf7826c68dcd9a6504605bb39cfbee6ad8af9a7c Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Wed, 1 Jan 2014 12:26:08 +0100 Subject: [PATCH] Normalize and polyfill KeyboardEvent further, also MouseEvent+TouchEvent KeyboardEvent now normalizes "charCode", "keyCode", "which" across all browsers KeyboardEvent has partial "key"-support for KeyDown/KeyUp and full "key"-support for KeyPress. KeyboardEvent, MouseEvent and TouchEvent now has "getModifierState", polyfill when not implemented. --- docs/docs/ref-05-events.md | 7 ++- src/browser/eventPlugins/SimpleEventPlugin.js | 8 +++- .../syntheticEvents/SyntheticKeyboardEvent.js | 37 ++++++++++++++-- .../syntheticEvents/SyntheticMouseEvent.js | 3 ++ .../syntheticEvents/SyntheticTouchEvent.js | 5 ++- src/browser/ui/dom/getEventKey.js | 40 ++++++++++++++--- src/browser/ui/dom/getEventModifierState.js | 44 +++++++++++++++++++ 7 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 src/browser/ui/dom/getEventModifierState.js diff --git a/docs/docs/ref-05-events.md b/docs/docs/ref-05-events.md index 5ec6e6324cd0..84a7abfb594c 100644 --- a/docs/docs/ref-05-events.md +++ b/docs/docs/ref-05-events.md @@ -62,8 +62,9 @@ Properties: ```javascript boolean altKey -boolean ctrlKey Number charCode +boolean ctrlKey +function getModifierState(key) String key Number keyCode String locale @@ -120,6 +121,7 @@ Number buttons Number clientX Number clientY boolean ctrlKey +function getModifierState(key) boolean metaKey Number pageX Number pageY @@ -147,6 +149,7 @@ Properties: boolean altKey DOMTouchList changedTouches boolean ctrlKey +function getModifierState(key) boolean metaKey boolean shiftKey DOMTouchList targetTouches @@ -181,8 +184,8 @@ onWheel Properties: ```javascript -Number deltaX Number deltaMode +Number deltaX Number deltaY Number deltaZ ``` diff --git a/src/browser/eventPlugins/SimpleEventPlugin.js b/src/browser/eventPlugins/SimpleEventPlugin.js index c71479333835..ae9f08351e1e 100644 --- a/src/browser/eventPlugins/SimpleEventPlugin.js +++ b/src/browser/eventPlugins/SimpleEventPlugin.js @@ -341,8 +341,14 @@ var SimpleEventPlugin = { // @see http://www.w3.org/TR/html5/index.html#events-0 EventConstructor = SyntheticEvent; break; - case topLevelTypes.topKeyDown: case topLevelTypes.topKeyPress: + // FireFox creates a keypress event for function keys too. This removes + // the unwanted keypress events. + if (nativeEvent.charCode === 0) { + return null; + } + /* falls through */ + case topLevelTypes.topKeyDown: case topLevelTypes.topKeyUp: EventConstructor = SyntheticKeyboardEvent; break; diff --git a/src/browser/syntheticEvents/SyntheticKeyboardEvent.js b/src/browser/syntheticEvents/SyntheticKeyboardEvent.js index 3c763c84f646..e4a4000c4435 100644 --- a/src/browser/syntheticEvents/SyntheticKeyboardEvent.js +++ b/src/browser/syntheticEvents/SyntheticKeyboardEvent.js @@ -22,6 +22,7 @@ var SyntheticUIEvent = require('SyntheticUIEvent'); var getEventKey = require('getEventKey'); +var getEventModifierState = require('getEventModifierState'); /** * @interface KeyboardEvent @@ -36,11 +37,39 @@ var KeyboardEventInterface = { metaKey: null, repeat: null, locale: null, + getModifierState: getEventModifierState, // Legacy Interface - 'char': null, - charCode: null, - keyCode: null, - which: null + charCode: function(event) { + // `charCode` is the result of a KeyPress event and represents the value of + // the actual printable character. + + // KeyPress is deprecated but its replacement is not yet final and not + // implemented in any major browser. + if (event.type === 'keypress') { + // IE8 does not implement "charCode", but "keyCode" has the correct value. + return 'charCode' in event ? event.charCode : event.keyCode; + } + return 0; + }, + keyCode: function(event) { + // `keyCode` is the result of a KeyDown/Up event and represents the value of + // physical keyboard key. + + // The actual meaning of the value depends on the users' keyboard layout + // which cannot be detected. Assuming that it is a US keyboard layout + // provides a surprisingly accurate mapping for US and European users. + // Due to this, it is left to the user to implement at this time. + if (event.type === 'keydown' || event.type === 'keyup') { + return event.keyCode; + } + return 0; + }, + which: function(event) { + // `which` is an alias for either `keyCode` or `charCode` depending on the + // type of the event. There is no need to determine the type of the event + // as `keyCode` and `charCode` are either aliased or default to zero. + return event.keyCode || event.charCode; + } }; /** diff --git a/src/browser/syntheticEvents/SyntheticMouseEvent.js b/src/browser/syntheticEvents/SyntheticMouseEvent.js index bf8e366ab4cc..912eca26f7c4 100644 --- a/src/browser/syntheticEvents/SyntheticMouseEvent.js +++ b/src/browser/syntheticEvents/SyntheticMouseEvent.js @@ -22,6 +22,8 @@ var SyntheticUIEvent = require('SyntheticUIEvent'); var ViewportMetrics = require('ViewportMetrics'); +var getEventModifierState = require('getEventModifierState'); + /** * @interface MouseEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ @@ -35,6 +37,7 @@ var MouseEventInterface = { shiftKey: null, altKey: null, metaKey: null, + getEventModifierState: getEventModifierState, button: function(event) { // Webkit, Firefox, IE9+ // which: 1 2 3 diff --git a/src/browser/syntheticEvents/SyntheticTouchEvent.js b/src/browser/syntheticEvents/SyntheticTouchEvent.js index 34bdf66aa90d..b7ad992a51e5 100644 --- a/src/browser/syntheticEvents/SyntheticTouchEvent.js +++ b/src/browser/syntheticEvents/SyntheticTouchEvent.js @@ -21,6 +21,8 @@ var SyntheticUIEvent = require('SyntheticUIEvent'); +var getEventModifierState = require('getEventModifierState'); + /** * @interface TouchEvent * @see http://www.w3.org/TR/touch-events/ @@ -32,7 +34,8 @@ var TouchEventInterface = { altKey: null, metaKey: null, ctrlKey: null, - shiftKey: null + shiftKey: null, + getModifierState: getEventModifierState }; /** diff --git a/src/browser/ui/dom/getEventKey.js b/src/browser/ui/dom/getEventKey.js index 77115e4f1838..ab96ea2f6ddb 100644 --- a/src/browser/ui/dom/getEventKey.js +++ b/src/browser/ui/dom/getEventKey.js @@ -19,8 +19,10 @@ "use strict"; +var invariant = require('invariant'); + /** - * Normalization of deprecated HTML5 "key" values + * Normalization of deprecated HTML5 `key` values * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names */ var normalizeKey = { @@ -39,7 +41,7 @@ var normalizeKey = { }; /** - * Translation from legacy "which/keyCode" to HTML5 "key" + * Translation from legacy `which`/`keyCode` to HTML5 `key` * Only special keys supported, all others depend on keyboard layout or browser * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names */ @@ -77,9 +79,37 @@ var translateToKey = { * @return {string} Normalized `key` property. */ function getEventKey(nativeEvent) { - return 'key' in nativeEvent ? - normalizeKey[nativeEvent.key] || nativeEvent.key : - translateToKey[nativeEvent.which || nativeEvent.keyCode] || 'Unidentified'; + if (nativeEvent.key) { + // Normalize inconsistent values reported by browsers due to + // implementations of a working draft specification. + + // FireFox implements `key` but returns `MozPrintableKey` for all + // printable characters (normalized to `Unidentified`), ignore it. + var key = normalizeKey[nativeEvent.key] || nativeEvent.key; + if (key !== 'Unidentified') { + return key; + } + } + + // Browser does not implement `key`, polyfill as much of it as we can. + if (nativeEvent.type === 'keypress') { + // Create the character from the `charCode` ourselves and use as an almost + // perfect replacement. + var charCode = 'charCode' in nativeEvent ? + nativeEvent.charCode : + nativeEvent.keyCode; + + // The enter-key is technically both printable and non-printable and can + // thus be captured by `keypress`, no other non-printable key should. + return charCode === 13 ? 'Enter' : String.fromCharCode(charCode); + } + if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') { + // While user keyboard layout determines the actual meaning of each + // `keyCode` value, almost all function keys have a universal value. + return translateToKey[nativeEvent.keyCode] || 'Unidentified'; + } + + invariant(false, "Unexpected keyboard event type: %s", nativeEvent.type); } module.exports = getEventKey; diff --git a/src/browser/ui/dom/getEventModifierState.js b/src/browser/ui/dom/getEventModifierState.js new file mode 100644 index 000000000000..6877dc16d52f --- /dev/null +++ b/src/browser/ui/dom/getEventModifierState.js @@ -0,0 +1,44 @@ +/** + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule getEventModifierState + * @typechecks static-only + */ + +"use strict"; + +/** + * Translation from modifier key to the associated property in the event. + * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers + */ + +var modifierKeyToProp = { + 'alt': 'altKey', + 'control': 'ctrlKey', + 'meta': 'metaKey', + 'shift': 'shiftKey' +}; + +function getEventModifierState(nativeEvent) { + // IE8 does not implement getModifierState so we simply map it to the only + // modifier keys exposed by the event itself, does not support Lock-keys. + // Currently, all major browsers except Chrome seems to support Lock-keys. + return nativeEvent.getModifierState || function(keyArg) { + var keyProp = modifierKeyToProp[keyArg.toLowerCase()]; + return keyProp && nativeEvent[keyProp]; + }; +} + +module.exports = getEventModifierState;