diff --git a/src/core/ReactDefaultInjection.js b/src/core/ReactDefaultInjection.js index 75044d849f..8cecbccd49 100644 --- a/src/core/ReactDefaultInjection.js +++ b/src/core/ReactDefaultInjection.js @@ -24,6 +24,7 @@ var ReactDOMForm = require('ReactDOMForm'); var DefaultEventPluginOrder = require('DefaultEventPluginOrder'); var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin'); var EventPluginHub = require('EventPluginHub'); +var InputEventPlugin = require('InputEventPlugin'); var ReactInstanceHandles = require('ReactInstanceHandles'); var SimpleEventPlugin = require('SimpleEventPlugin'); @@ -40,7 +41,8 @@ function inject() { */ EventPluginHub.injection.injectEventPluginsByName({ 'SimpleEventPlugin': SimpleEventPlugin, - 'EnterLeaveEventPlugin': EnterLeaveEventPlugin + 'EnterLeaveEventPlugin': EnterLeaveEventPlugin, + 'InputEventPlugin': InputEventPlugin }); /* diff --git a/src/core/ReactEvent.js b/src/core/ReactEvent.js index 702c8816c3..8386f859b9 100644 --- a/src/core/ReactEvent.js +++ b/src/core/ReactEvent.js @@ -211,8 +211,10 @@ function listenAtTopLevel(touchNotMouse) { trapBubbledEvent(topLevelTypes.topKeyUp, 'keyup', mountAt); trapBubbledEvent(topLevelTypes.topKeyPress, 'keypress', mountAt); trapBubbledEvent(topLevelTypes.topKeyDown, 'keydown', mountAt); - trapBubbledEvent(topLevelTypes.topInput, 'input', mountAt); trapBubbledEvent(topLevelTypes.topChange, 'change', mountAt); + trapBubbledEvent(topLevelTypes.topInput, 'input', mountAt); + trapBubbledEvent(topLevelTypes.topCut, 'cut', mountAt); + trapBubbledEvent(topLevelTypes.topPaste, 'paste', mountAt); trapBubbledEvent( topLevelTypes.topDOMCharacterDataModified, 'DOMCharacterDataModified', diff --git a/src/event/EventConstants.js b/src/event/EventConstants.js index 0af7b9d0b2..8067d512aa 100644 --- a/src/event/EventConstants.js +++ b/src/event/EventConstants.js @@ -29,6 +29,7 @@ var topLevelTypes = keyMirror({ topBlur: null, topChange: null, topClick: null, + topCut: null, topDOMCharacterDataModified: null, topDoubleClick: null, topFocus: null, @@ -42,6 +43,7 @@ var topLevelTypes = keyMirror({ topMouseOver: null, topMouseUp: null, topMouseWheel: null, + topPaste: null, topScroll: null, topSubmit: null, topTouchCancel: null, diff --git a/src/eventPlugins/DefaultEventPluginOrder.js b/src/eventPlugins/DefaultEventPluginOrder.js index 81eecc37c6..3de3bbe9c7 100644 --- a/src/eventPlugins/DefaultEventPluginOrder.js +++ b/src/eventPlugins/DefaultEventPluginOrder.js @@ -34,6 +34,7 @@ var DefaultEventPluginOrder = [ keyOf({SimpleEventPlugin: null}), keyOf({TapEventPlugin: null}), keyOf({EnterLeaveEventPlugin: null}), + keyOf({InputEventPlugin: null}), keyOf({AnalyticsEventPlugin: null}) ]; diff --git a/src/eventPlugins/InputEventPlugin.js b/src/eventPlugins/InputEventPlugin.js new file mode 100644 index 0000000000..440ee252de --- /dev/null +++ b/src/eventPlugins/InputEventPlugin.js @@ -0,0 +1,99 @@ +/** + * 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 InputEventPlugin + */ + +"use strict"; + +var AbstractEvent = require('AbstractEvent'); +var EventConstants = require('EventConstants'); +var EventPluginUtils = require('EventPluginUtils'); +var EventPropagators = require('EventPropagators'); + +var keyOf = require('keyOf'); + +var topLevelTypes = EventConstants.topLevelTypes; + +var abstractEventTypes = { + input: { + phasedRegistrationNames: { + bubbled: keyOf({onInput: null}), + captured: keyOf({onInputCapture: null}) + } + } +}; + +/** + * @see EventPluginHub.extractAbstractEvents + */ +var extractAbstractEvents = function( + topLevelType, + nativeEvent, + renderedTargetID, + renderedTarget) { + + var defer, key; + switch (topLevelType) { + case topLevelTypes.topInput: + // When the native input event is triggered, we definitely want to + // forward it along. However, IE9's input event doesn't get triggered + // when deleting text, and IE8 doesn't support input at all, so we + // simulate it on change, cut, paste, and keydown. + case topLevelTypes.topChange: + defer = false; + break; + case topLevelTypes.topCut: + case topLevelTypes.topPaste: + defer = true; + break; + case topLevelTypes.topKeyDown: + key = nativeEvent.keyCode; + // Ignore command, modifiers, and arrow keys, respectively + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) { + return; + } + defer = true; + break; + default: + return; + } + + var type = abstractEventTypes.input; + var abstractTargetID = renderedTargetID; + var abstractEvent = AbstractEvent.getPooled( + type, + abstractTargetID, + topLevelType, + nativeEvent + ); + EventPropagators.accumulateTwoPhaseDispatches(abstractEvent); + + if (defer) { + setTimeout(function() { + EventPluginHub.enqueueAbstractEvents(abstractEvent); + EventPluginHub.processAbstractEventQueue(); + }, 0); + } else { + return abstractEvent; + } +}; + +var InputEventPlugin = { + abstractEventTypes: abstractEventTypes, + extractAbstractEvents: extractAbstractEvents +}; + +module.exports = InputEventPlugin; diff --git a/src/eventPlugins/SimpleEventPlugin.js b/src/eventPlugins/SimpleEventPlugin.js index 0453fde60a..7591ee3542 100644 --- a/src/eventPlugins/SimpleEventPlugin.js +++ b/src/eventPlugins/SimpleEventPlugin.js @@ -108,12 +108,6 @@ var SimpleEventPlugin = { captured: keyOf({onKeyDownCapture: true}) } }, - input: { - phasedRegistrationNames: { - bubbled: keyOf({onInput: true}), - captured: keyOf({onInputCapture: true}) - } - }, focus: { phasedRegistrationNames: { bubbled: keyOf({onFocus: true}), @@ -226,7 +220,6 @@ SimpleEventPlugin.topLevelTypesToAbstract = { topKeyUp: SimpleEventPlugin.abstractEventTypes.keyUp, topKeyPress: SimpleEventPlugin.abstractEventTypes.keyPress, topKeyDown: SimpleEventPlugin.abstractEventTypes.keyDown, - topInput: SimpleEventPlugin.abstractEventTypes.input, topFocus: SimpleEventPlugin.abstractEventTypes.focus, topBlur: SimpleEventPlugin.abstractEventTypes.blur, topScroll: SimpleEventPlugin.abstractEventTypes.scroll,