From ba6fea1bf5eaab71c0abec7d55824419bcd4f3f4 Mon Sep 17 00:00:00 2001 From: CommitSyncScript Date: Thu, 6 Jun 2013 14:40:30 -0700 Subject: [PATCH] Simplify Event Core Summary: This makes a few changes to React Core, most notably `ReactEventEmitter` and `ReactEventTopLevelCallback`. - Changed `ReactEventEmitter` to use `EventListener` (instead of `NormalizedEventListener`). - Deleted `NormalizedEventListener` (which was previously broken). - Created `getEventTarget` which is used to get a normalized `target` from a native event. - Changed `ReactEventTopLevelCallback` to use `getEventTarget`. - Renamed `abstractEventType` to `reactEventType` in `AbstractEvent`. - Reanmed `abstractTargetID` to `reactTargetID` in `AbstractEvent`. - Removed `originatingTopLevelEventType` from `AbstractEvent` (unused and violates encapsulation). - Removed `nativeEvent.target === window` check when refreshing authoritative scroll values (unnecessary). This actually fixes React because `NormalizedEventListener` does not currently do what it promises to do (which is normalizing `target` on the native event). The `target` event is read-only on native events. This also revises documentation and adds `@typechecks` to a few modules. NOTE: Most importantly, this sets the stage for replacing `AbstractEvent` with `ReactEvent` and subclasses, piecemeal. --- src/core/ReactEventEmitter.js | 341 ++++++++++-------- src/core/ReactEventTopLevelCallback.js | 49 ++- src/core/ReactInstanceHandles.js | 15 +- src/dom/getDOMNodeID.js | 5 +- src/dom/getEventTarget.js | 39 ++ src/event/AbstractEvent.js | 18 +- src/event/EventPluginHub.js | 61 ++-- src/event/EventPropagators.js | 19 +- src/event/NormalizedEventListener.js | 84 ----- .../AnalyticsEventPluginFactory.js | 19 +- src/eventPlugins/EnterLeaveEventPlugin.js | 46 ++- src/eventPlugins/ResponderEventPlugin.js | 107 +++--- src/eventPlugins/SimpleEventPlugin.js | 88 +++-- src/eventPlugins/TapEventPlugin.js | 22 +- .../__tests__/ResponderEventPlugin-test.js | 20 +- 15 files changed, 475 insertions(+), 458 deletions(-) create mode 100644 src/dom/getEventTarget.js delete mode 100644 src/event/NormalizedEventListener.js diff --git a/src/core/ReactEventEmitter.js b/src/core/ReactEventEmitter.js index 5e722606c5..dc2a1dd8f9 100644 --- a/src/core/ReactEventEmitter.js +++ b/src/core/ReactEventEmitter.js @@ -14,33 +14,44 @@ * limitations under the License. * * @providesModule ReactEventEmitter + * @typechecks */ "use strict"; var BrowserEnv = require('BrowserEnv'); var EventConstants = require('EventConstants'); +var EventListener = require('EventListener'); var EventPluginHub = require('EventPluginHub'); var ExecutionEnvironment = require('ExecutionEnvironment'); -var NormalizedEventListener = require('NormalizedEventListener'); var invariant = require('invariant'); var isEventSupported = require('isEventSupported'); -var registrationNames = EventPluginHub.registrationNames; -var topLevelTypes = EventConstants.topLevelTypes; -var listen = NormalizedEventListener.listen; -var capture = NormalizedEventListener.capture; - /** - * `ReactEventEmitter` is used to attach top-level event listeners. For example: + * Summary of `ReactEventEmitter` event handling: * - * ReactEventEmitter.putListener('myID', 'onClick', myFunction); + * - We trap low level 'top-level' events. + * + * - We dedupe cross-browser event names into these 'top-level types' so that + * `DOMMouseScroll` or `mouseWheel` both become `topMouseWheel`. + * + * - At this point we have native browser events with the top-level type that + * was used to catch it at the top-level. + * + * - We continuously stream these native events (and their respective top-level + * types) to the event plugin system `EventPluginHub` and ask the plugin + * system if it was able to extract `AbstractEvent` objects. `AbstractEvent` + * objects are the events that applications actually deal with - they are not + * native browser events but cross-browser wrappers. + * + * - When returning the `AbstractEvent` objects, `EventPluginHub` will make + * sure each abstract event is annotated with "dispatches", which are the + * sequence of listeners (and IDs) that care about the event. + * + * - These `AbstractEvent` objects are fed back into the event plugin system, + * which in turn executes these dispatches. * - * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'. - */ - -/** * Overview of React and the event system: * * . @@ -71,6 +82,64 @@ var capture = NormalizedEventListener.capture; * React Core . General Purpose Event Plugin System */ +/** + * Whether or not `ensureListening` has been invoked. + * @type {boolean} + * @private + */ +var _isListening = false; + +/** + * Traps top-level events by using event bubbling. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {DOMEventTarget} element Element on which to attach listener. + * @internal + */ +function trapBubbledEvent(topLevelType, handlerBaseName, element) { + EventListener.listen( + element, + handlerBaseName, + ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( + topLevelType + ) + ); +} + +/** + * Traps a top-level event by using event capturing. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {DOMEventTarget} element Element on which to attach listener. + * @internal + */ +function trapCapturedEvent(topLevelType, handlerBaseName, element) { + EventListener.capture( + element, + handlerBaseName, + ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( + topLevelType + ) + ); +} + +/** + * Listens to window scroll and resize events. We cache scroll values so that + * application code can access them without triggering reflows. + * + * NOTE: Scroll events do not bubble. + * + * @private + * @see http://www.quirksmode.org/dom/events/scroll.html + */ +function registerScrollValueMonitoring() { + var refresh = BrowserEnv.refreshAuthoritativeScrollValues; + EventListener.listen(window, 'scroll', refresh); + EventListener.listen(window, 'resize', refresh); +} + /** * We listen for bubbled touch events on the document object. * @@ -89,96 +158,19 @@ var capture = NormalizedEventListener.capture; * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but * they bubble to document. * - * @see http://www.quirksmode.org/dom/events/keys.html. - */ - -var _isListening = false; - -/** - * Traps top-level events that bubble. Delegates to the main dispatcher - * `handleTopLevel` after performing some basic normalization via - * `TopLevelCallbackCreator.createTopLevelCallback`. - */ -function trapBubbledEvent(topLevelType, handlerBaseName, onWhat) { - listen( - onWhat, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * Traps a top-level event by using event capturing. - */ -function trapCapturedEvent(topLevelType, handlerBaseName, onWhat) { - capture( - onWhat, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * Listens to document scroll and window resize events that may change the - * document scroll values. We store those results so as to discourage - * application code from asking the DOM itself which could trigger additional - * reflows. - */ -function registerDocumentScrollListener() { - listen(window, 'scroll', function(nativeEvent) { - if (nativeEvent.target === window) { - BrowserEnv.refreshAuthoritativeScrollValues(); - } - }); -} - -function registerDocumentResizeListener() { - listen(window, 'resize', function(nativeEvent) { - if (nativeEvent.target === window) { - BrowserEnv.refreshAuthoritativeScrollValues(); - } - }); -} - -/** - * Summary of `ReactEventEmitter` event handling: - * - * - We trap low level 'top-level' events. - * - * - We dedupe cross-browser event names into these 'top-level types' so that - * `DOMMouseScroll` or `mouseWheel` both become `topMouseWheel`. - * - * - At this point we have native browser events with the top-level type that - * was used to catch it at the top-level. - * - * - We continuously stream these native events (and their respective top-level - * types) to the event plugin system `EventPluginHub` and ask the plugin - * system if it was able to extract `AbstractEvent` objects. `AbstractEvent` - * objects are the events that applications actually deal with - they are not - * native browser events but cross-browser wrappers. - * - * - When returning the `AbstractEvent` objects, `EventPluginHub` will make - * sure each abstract event is annotated with "dispatches", which are the - * sequence of listeners (and IDs) that care about the event. - * - * - These `AbstractEvent` objects are fed back into the event plugin system, - * which in turn executes these dispatches. - * + * @param {boolean} touchNotMouse Listen to touch events instead of mouse. * @private + * @see http://www.quirksmode.org/dom/events/keys.html. */ function listenAtTopLevel(touchNotMouse) { invariant( !_isListening, 'listenAtTopLevel(...): Cannot setup top-level listener more than once.' ); + var topLevelTypes = EventConstants.topLevelTypes; var mountAt = document; - registerDocumentScrollListener(); - registerDocumentResizeListener(); + registerScrollValueMonitoring(); trapBubbledEvent(topLevelTypes.topMouseOver, 'mouseover', mountAt); trapBubbledEvent(topLevelTypes.topMouseDown, 'mousedown', mountAt); trapBubbledEvent(topLevelTypes.topMouseUp, 'mouseup', mountAt); @@ -226,82 +218,111 @@ function listenAtTopLevel(touchNotMouse) { } /** - * This is the heart of `ReactEventEmitter`. It simply streams the top-level - * native events to `EventPluginHub`. + * `ReactEventEmitter` is used to attach top-level event listeners. For example: + * + * ReactEventEmitter.putListener('myID', 'onClick', myFunction); + * + * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'. * - * @param {object} topLevelType Record from `EventConstants`. - * @param {Event} nativeEvent A Standard Event with fixed `target` property. - * @param {DOMElement} renderedTarget Element of interest to the framework. - * @param {string} renderedTargetID string ID of `renderedTarget`. * @internal */ -function handleTopLevel( - topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget) { - var abstractEvents = EventPluginHub.extractAbstractEvents( - topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget - ); - - // The event queue being processed in the same cycle allows preventDefault. - EventPluginHub.enqueueAbstractEvents(abstractEvents); - EventPluginHub.processAbstractEventQueue(); -} - -function setEnabled(enabled) { - invariant( - ExecutionEnvironment.canUseDOM, - 'setEnabled(...): Cannot toggle event listening in a Worker thread. This ' + - 'is likely a bug in the framework. Please report immediately.' - ); - ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled); -} - -function isEnabled() { - return ReactEventEmitter.TopLevelCallbackCreator.isEnabled(); -} - -/** - * Ensures that top-level event delegation listeners are listening at `mountAt`. - * There are issues with listening to both touch events and mouse events on the - * top-level, so we make the caller choose which one to listen to. (If there's a - * touch top-level listeners, anchors don't receive clicks for some reason, and - * only in some cases). - * - * @param {boolean} touchNotMouse Listen to touch events instead of mouse. - * @param {object} TopLevelCallbackCreator Module that can create top-level - * callback handlers. - * @internal - */ -function ensureListening(touchNotMouse, TopLevelCallbackCreator) { - invariant( - ExecutionEnvironment.canUseDOM, - 'ensureListening(...): Cannot toggle event listening in a Worker thread. ' + - 'This is likely a bug in the framework. Please report immediately.' - ); - if (!_isListening) { - ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator; - listenAtTopLevel(touchNotMouse); - _isListening = true; - } -} - var ReactEventEmitter = { - TopLevelCallbackCreator: null, // Injectable callback creator. - handleTopLevel: handleTopLevel, - setEnabled: setEnabled, - isEnabled: isEnabled, - ensureListening: ensureListening, - registrationNames: registrationNames, + + /** + * React references `ReactEventTopLevelCallback` using this property in order + * to allow dependency injection via `ensureListening`. + */ + TopLevelCallbackCreator: null, + + /** + * Ensures that top-level event delegation listeners are installed. + * + * There are issues with listening to both touch events and mouse events on + * the top-level, so we make the caller choose which one to listen to. (If + * there's a touch top-level listeners, anchors don't receive clicks for some + * reason, and only in some cases). + * + * @param {boolean} touchNotMouse Listen to touch events instead of mouse. + * @param {object} TopLevelCallbackCreator + */ + ensureListening: function(touchNotMouse, TopLevelCallbackCreator) { + invariant( + ExecutionEnvironment.canUseDOM, + 'ensureListening(...): Cannot toggle event listening in a Worker ' + + 'thread. This is likely a bug in the framework. Please report ' + + 'immediately.' + ); + if (!_isListening) { + ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator; + listenAtTopLevel(touchNotMouse); + _isListening = true; + } + }, + + /** + * Sets whether or not any created callbacks should be enabled. + * + * @param {boolean} enabled True if callbacks should be enabled. + */ + setEnabled: function(enabled) { + invariant( + ExecutionEnvironment.canUseDOM, + 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' + + 'This is likely a bug in the framework. Please report immediately.' + ); + if (ReactEventEmitter.TopLevelCallbackCreator) { + ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled); + } + }, + + /** + * @return {boolean} True if callbacks are enabled. + */ + isEnabled: function() { + return !!( + ReactEventEmitter.TopLevelCallbackCreator && + ReactEventEmitter.TopLevelCallbackCreator.isEnabled() + ); + }, + + /** + * Streams a fired top-level event to `EventPluginHub` where plugins have the + * opportunity to create `ReactEvent`s to be dispatched. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + */ + handleTopLevel: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + var abstractEvents = EventPluginHub.extractAbstractEvents( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent + ); + + // Event queue being processed in the same cycle allows `preventDefault`. + EventPluginHub.enqueueAbstractEvents(abstractEvents); + EventPluginHub.processAbstractEventQueue(); + }, + + registrationNames: EventPluginHub.registrationNames, + putListener: EventPluginHub.putListener, + getListener: EventPluginHub.getListener, + deleteAllListeners: EventPluginHub.deleteAllListeners, + trapBubbledEvent: trapBubbledEvent, + trapCapturedEvent: trapCapturedEvent + }; module.exports = ReactEventEmitter; diff --git a/src/core/ReactEventTopLevelCallback.js b/src/core/ReactEventTopLevelCallback.js index 2b72eceb32..689a96c559 100644 --- a/src/core/ReactEventTopLevelCallback.js +++ b/src/core/ReactEventTopLevelCallback.js @@ -14,6 +14,7 @@ * limitations under the License. * * @providesModule ReactEventTopLevelCallback + * @typechecks */ "use strict"; @@ -23,51 +24,59 @@ var ReactEventEmitter = require('ReactEventEmitter'); var ReactInstanceHandles = require('ReactInstanceHandles'); var getDOMNodeID = require('getDOMNodeID'); +var getEventTarget = require('getEventTarget'); +/** + * @type {boolean} + * @private + */ var _topLevelListenersEnabled = true; +/** + * Top-level callback creator used to implement event handling using delegation. + * This is used via dependency injection in `ReactEventEmitter.ensureListening`. + */ var ReactEventTopLevelCallback = { /** - * @param {boolean} enabled Whether or not all callbacks that have ever been - * created with this module should be enabled. + * Sets whether or not any created callbacks should be enabled. + * + * @param {boolean} enabled True if callbacks should be enabled. */ setEnabled: function(enabled) { _topLevelListenersEnabled = !!enabled; }, + /** + * @return {boolean} True if callbacks are enabled. + */ isEnabled: function() { return _topLevelListenersEnabled; }, /** - * For a given `topLevelType`, creates a callback that could be added as a - * listener to the document. That top level callback will simply fix the - * native events before invoking `handleTopLevel`. + * Creates a callback for the supplied `topLevelType` that could be added as + * a listener to the document. The callback computes a `topLevelTarget` which + * should be the root node of a mounted React component where the listener + * is attached. * - * - Raw native events cannot be trusted to describe their targets correctly - * so we expect that the argument to the nested function has already been - * fixed. But the `target` property may not be something of interest to - * React, so we find the most suitable target. But even at that point, DOM - * Elements (the target ) can't be trusted to describe their IDs correctly - * so we obtain the ID in a reliable manner and pass it to - * `handleTopLevel`. The target/id that we found to be relevant to our - * framework are called `renderedTarget`/`renderedTargetID` respectively. + * @param {string} topLevelType Record from `EventConstants`. + * @return {function} Callback for handling top-level events. */ createTopLevelCallback: function(topLevelType) { - return function(fixedNativeEvent) { + return function(nativeEvent) { if (!_topLevelListenersEnabled) { return; } - var renderedTarget = ReactInstanceHandles.getFirstReactDOM( - fixedNativeEvent.target + var topLevelTarget = ReactInstanceHandles.getFirstReactDOM( + getEventTarget(nativeEvent) ) || ExecutionEnvironment.global; - var renderedTargetID = getDOMNodeID(renderedTarget); + var topLevelTargetID = getDOMNodeID(topLevelTarget) || ''; ReactEventEmitter.handleTopLevel( topLevelType, - fixedNativeEvent, - renderedTargetID, - renderedTarget + topLevelTarget, + topLevelTargetID, + nativeEvent ); }; } diff --git a/src/core/ReactInstanceHandles.js b/src/core/ReactInstanceHandles.js index 27c4e1b575..7528cb7fb0 100644 --- a/src/core/ReactInstanceHandles.js +++ b/src/core/ReactInstanceHandles.js @@ -14,6 +14,7 @@ * limitations under the License. * * @providesModule ReactInstanceHandles + * @typechecks */ "use strict"; @@ -57,13 +58,13 @@ function isValidID(id) { /** * True if the supplied `node` is rendered by React. * - * @param {DOMElement} node DOM Element to check. + * @param {DOMEventTarget} node DOM Element to check. * @return {boolean} True if the DOM Element appears to be rendered by React. * @private */ function isRenderedByReact(node) { var id = getDOMNodeID(node); - return id && id.charAt(0) === SEPARATOR; + return id ? id.charAt(0) === SEPARATOR : false; } /** @@ -140,8 +141,8 @@ var ReactInstanceHandles = { * Traverses up the ancestors of the supplied node to find a node that is a * DOM representation of a React component. * - * @param {DOMElement} node - * @return {?DOMElement} + * @param {?DOMEventTarget} node + * @return {?DOMEventTarget} * @internal */ getFirstReactDOM: function(node) { @@ -159,9 +160,9 @@ var ReactInstanceHandles = { * Finds a node with the supplied `id` inside of the supplied `ancestorNode`. * Exploits the ID naming scheme to perform the search quickly. * - * @param {DOMElement} ancestorNode Search from this root. + * @param {DOMEventTarget} ancestorNode Search from this root. * @pararm {string} id ID of the DOM representation of the component. - * @return {?DOMElement} DOM element with the supplied `id`, if one exists. + * @return {?DOMEventTarget} DOM node with the supplied `id`, if one exists. * @internal */ findComponentRoot: function(ancestorNode, id) { @@ -228,7 +229,7 @@ var ReactInstanceHandles = { * contains the React component with the supplied DOM ID. * * @param {string} id DOM ID of a React component. - * @return {string} DOM ID of the React component that is the root. + * @return {?string} DOM ID of the React component that is the root. * @internal */ getReactRootIDFromNodeID: function(id) { diff --git a/src/dom/getDOMNodeID.js b/src/dom/getDOMNodeID.js index fbd9cfdcbf..1f4abe6974 100644 --- a/src/dom/getDOMNodeID.js +++ b/src/dom/getDOMNodeID.js @@ -14,6 +14,7 @@ * limitations under the License. * * @providesModule getDOMNodeID + * @typechecks */ "use strict"; @@ -23,8 +24,8 @@ * control whose name or ID is "id". However, not all DOM nodes support * `getAttributeNode` (document - which is not a form) so that is checked first. * - * @param {Element} domNode DOM node element to return ID of. - * @returns {string} The ID of `domNode`. + * @param {DOMElement|DOMWindow|DOMDocument} domNode DOM node. + * @returns {string} ID of the supplied `domNode`. */ function getDOMNodeID(domNode) { if (domNode.getAttributeNode) { diff --git a/src/dom/getEventTarget.js b/src/dom/getEventTarget.js new file mode 100644 index 0000000000..9529e3932f --- /dev/null +++ b/src/dom/getEventTarget.js @@ -0,0 +1,39 @@ +/** + * 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 getEventTarget + * @typechecks + */ + +var ExecutionEnvironment = require('ExecutionEnvironment'); + +/** + * Gets the target node from a native browser event by accounting for + * inconsistencies in browser DOM APIs. + * + * @param {object} nativeEvent Native browser event. + * @return {DOMEventTarget} Target node. + */ +function getEventTarget(nativeEvent) { + var target = + nativeEvent.target || + nativeEvent.srcElement || + ExecutionEnvironment.global; + // Safari may fire events on text nodes (Node.TEXT_NODE is 3). + // @see http://www.quirksmode.org/js/events_properties.html + return target.nodeType === 3 ? target.parentNode : target; +} + +module.exports = getEventTarget; diff --git a/src/event/AbstractEvent.js b/src/event/AbstractEvent.js index 9d31b19e39..6930fdd8bf 100644 --- a/src/event/AbstractEvent.js +++ b/src/event/AbstractEvent.js @@ -44,14 +44,12 @@ var MAX_POOL_SIZE = 20; * unreliable native event. */ function AbstractEvent( - abstractEventType, - abstractTargetID, // Allows the abstract target to differ from native. - originatingTopLevelEventType, + reactEventType, + reactTargetID, // Allows the abstract target to differ from native. nativeEvent, data) { - this.type = abstractEventType; - this.abstractTargetID = abstractTargetID || ''; - this.originatingTopLevelEventType = originatingTopLevelEventType; + this.reactEventType = reactEventType; + this.reactTargetID = reactTargetID || ''; this.nativeEvent = nativeEvent; this.data = data; // TODO: Deprecate storing target - doesn't always make sense for some types @@ -263,12 +261,10 @@ AbstractEvent.persistentCloneOf = function(abstractEvent) { throwIf(!(abstractEvent instanceof AbstractEvent), CLONE_TYPE_ERR); } return new AbstractEvent( - abstractEvent.type, - abstractEvent.abstractTargetID, - abstractEvent.originatingTopLevelEventType, + abstractEvent.reactEventType, + abstractEvent.reactTargetID, abstractEvent.nativeEvent, - abstractEvent.data, - abstractEvent.target + abstractEvent.data ); }; diff --git a/src/event/EventPluginHub.js b/src/event/EventPluginHub.js index 5a24d557ce..52c3db0b61 100644 --- a/src/event/EventPluginHub.js +++ b/src/event/EventPluginHub.js @@ -194,15 +194,16 @@ function recordAllRegistrationNames(eventType, PluginModule) { * @param {AbstractEvent} abstractEvent to look at */ function getPluginModuleForAbstractEvent(abstractEvent) { - if (abstractEvent.type.registrationName) { - return registrationNames[abstractEvent.type.registrationName]; + var reactEventType = abstractEvent.reactEventType; + if (reactEventType.registrationName) { + return registrationNames[reactEventType.registrationName]; } else { - for (var phase in abstractEvent.type.phasedRegistrationNames) { - if (!abstractEvent.type.phasedRegistrationNames.hasOwnProperty(phase)) { + for (var phase in reactEventType.phasedRegistrationNames) { + if (!reactEventType.phasedRegistrationNames.hasOwnProperty(phase)) { continue; } var PluginModule = registrationNames[ - abstractEvent.type.phasedRegistrationNames[phase] + reactEventType.phasedRegistrationNames[phase] ]; if (PluginModule) { return PluginModule; @@ -223,36 +224,36 @@ var deleteAllListeners = function(domID) { * Accepts the stream of top level native events, and gives every registered * plugin an opportunity to extract `AbstractEvent`s with annotated dispatches. * - * @param {Enum} topLevelType Record from `EventConstants`. - * @param {Event} nativeEvent A Standard Event with fixed `target` property. - * @param {Element} renderedTarget Element of interest to the framework, usually - * the same as `nativeEvent.target` but occasionally an element immediately - * above `nativeEvent.target` (the first DOM node recognized as one "rendered" - * by the framework at hand.) - * @param {string} renderedTargetID string ID of `renderedTarget`. + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. */ -var extractAbstractEvents = - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) { - var abstractEvents; - var plugins = injection.plugins; - var len = plugins.length; - for (var i = 0; i < len; i++) { - // Not every plugin in the ordering may be loaded at runtime. - var possiblePlugin = plugins[i]; - var extractedAbstractEvents = - possiblePlugin && - possiblePlugin.extractAbstractEvents( - topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget - ); +var extractAbstractEvents = function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + var abstractEvents; + var plugins = injection.plugins; + for (var i = 0, l = plugins.length; i < l; i++) { + // Not every plugin in the ordering may be loaded at runtime. + var possiblePlugin = plugins[i]; + if (possiblePlugin) { + var extractedAbstractEvents = possiblePlugin.extractAbstractEvents( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent + ); if (extractedAbstractEvents) { abstractEvents = accumulate(abstractEvents, extractedAbstractEvents); } } - return abstractEvents; - }; + } + return abstractEvents; +}; var enqueueAbstractEvents = function(abstractEvents) { if (abstractEvents) { diff --git a/src/event/EventPropagators.js b/src/event/EventPropagators.js index c5cfe6bfe5..e7534f7a2d 100644 --- a/src/event/EventPropagators.js +++ b/src/event/EventPropagators.js @@ -58,7 +58,7 @@ var injection = { */ function listenerAtPhase(id, abstractEvent, propagationPhase) { var registrationName = - abstractEvent.type.phasedRegistrationNames[propagationPhase]; + abstractEvent.reactEventType.phasedRegistrationNames[propagationPhase]; return getListener(id, registrationName); } @@ -92,9 +92,9 @@ function accumulateDirectionalDispatches(domID, upwards, abstractEvent) { * have a different target. */ function accumulateTwoPhaseDispatchesSingle(abstractEvent) { - if (abstractEvent && abstractEvent.type.phasedRegistrationNames) { + if (abstractEvent && abstractEvent.reactEventType.phasedRegistrationNames) { injection.InstanceHandle.traverseTwoPhase( - abstractEvent.abstractTargetID, + abstractEvent.reactTargetID, accumulateDirectionalDispatches, abstractEvent ); @@ -105,11 +105,12 @@ function accumulateTwoPhaseDispatchesSingle(abstractEvent) { /** * Accumulates without regard to direction, does not look for phased * registration names. Same as `accumulateDirectDispatchesSingle` but without - * requiring that the `abstractTargetID` be the same as the dispatched ID. + * requiring that the `reactTargetID` be the same as the dispatched ID. */ function accumulateDispatches(id, ignoredDirection, abstractEvent) { - if (abstractEvent && abstractEvent.type.registrationName) { - var listener = getListener(id, abstractEvent.type.registrationName); + if (abstractEvent && abstractEvent.reactEventType.registrationName) { + var registrationName = abstractEvent.reactEventType.registrationName; + var listener = getListener(id, registrationName); if (listener) { abstractEvent._dispatchListeners = accumulate(abstractEvent._dispatchListeners, listener); @@ -120,12 +121,12 @@ function accumulateDispatches(id, ignoredDirection, abstractEvent) { /** * Accumulates dispatches on an `AbstractEvent`, but only for the - * `abstractTargetID`. + * `reactTargetID`. * @param {AbstractEvent} abstractEvent */ function accumulateDirectDispatchesSingle(abstractEvent) { - if (abstractEvent && abstractEvent.type.registrationName) { - accumulateDispatches(abstractEvent.abstractTargetID, null, abstractEvent); + if (abstractEvent && abstractEvent.reactEventType.registrationName) { + accumulateDispatches(abstractEvent.reactTargetID, null, abstractEvent); } } diff --git a/src/event/NormalizedEventListener.js b/src/event/NormalizedEventListener.js deleted file mode 100644 index 4888607b15..0000000000 --- a/src/event/NormalizedEventListener.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * 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 NormalizedEventListener - */ - -var EventListener = require('EventListener'); - -/** - * @param {?Event} eventParam Event parameter from an attached listener. - * @return {Event} Normalized event object. - * @private - */ -function normalizeEvent(eventParam) { - var nativeEvent = eventParam || window.event; - // In some browsers (OLD FF), setting the target throws an error. A good way - // to tell if setting the target will throw an error, is to check if the event - // has a `target` property. Safari events have a `target` but it's not always - // normalized. Even if a `target` property exists, it's good to only set the - // target property if we realize that a change will actually take place. - var hasTargetProperty = 'target' in nativeEvent; - var eventTarget = nativeEvent.target || nativeEvent.srcElement || window; - // Safari may fire events on text nodes (Node.TEXT_NODE is 3) - // @see http://www.quirksmode.org/js/events_properties.html - var textNodeNormalizedTarget = - (eventTarget.nodeType === 3) ? eventTarget.parentNode : eventTarget; - if (!hasTargetProperty || nativeEvent.target !== textNodeNormalizedTarget) { - // TODO: Normalize the object via `merge()` to work with strict mode. - nativeEvent.target = textNodeNormalizedTarget; - } - return nativeEvent; -} - -function createNormalizedCallback(cb) { - return function(unfixedNativeEvent) { - cb(normalizeEvent(unfixedNativeEvent)); - }; -} - -var NormalizedEventListener = { - - /** - * Listens to bubbled events on a DOM node. - * - * NOTE: The listener will be invoked with a normalized event object. - * - * @param {DOMElement} el DOM element to register listener on. - * @param {string} handlerBaseName Event name, e.g. "click". - * @param {function} cb Callback function. - * @public - */ - listen: function(el, handlerBaseName, cb) { - EventListener.listen(el, handlerBaseName, createNormalizedCallback(cb)); - }, - - /** - * Listens to captured events on a DOM node. - * - * NOTE: The listener will be invoked with a normalized event object. - * - * @param {DOMElement} el DOM element to register listener on. - * @param {string} handlerBaseName Event name, e.g. "click". - * @param {function} cb Callback function. - * @public - */ - capture: function(el, handlerBaseName, cb) { - EventListener.capture(el, handlerBaseName, createNormalizedCallback(cb)); - } - -}; - -module.exports = NormalizedEventListener; diff --git a/src/eventPlugins/AnalyticsEventPluginFactory.js b/src/eventPlugins/AnalyticsEventPluginFactory.js index 698c7fb2b4..b06859e00d 100644 --- a/src/eventPlugins/AnalyticsEventPluginFactory.js +++ b/src/eventPlugins/AnalyticsEventPluginFactory.js @@ -136,20 +136,25 @@ if (__DEV__) { * This plugin does not really extract any abstract events. Rather it just looks * at the top level event and bumps up counters as appropriate * - * @see EventPluginHub.extractAbstractEvents + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. + * @see {EventPluginHub.extractAbstractEvents} */ function extractAbstractEvents( topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget) { + topLevelTarget, + topLevelTargetID, + nativeEvent) { var currentEvent = topLevelTypesToAnalyticsEvent[topLevelType]; - if (!currentEvent || !renderedTarget || !renderedTarget.attributes) { + if (!currentEvent || !topLevelTarget || !topLevelTarget.attributes) { return null; } - var analyticsIDAttribute = renderedTarget.attributes[ANALYTICS_ID]; - var analyticsEventsAttribute = renderedTarget.attributes[ANALYTICS_EVENTS]; + var analyticsIDAttribute = topLevelTarget.attributes[ANALYTICS_ID]; + var analyticsEventsAttribute = topLevelTarget.attributes[ANALYTICS_EVENTS]; if(!analyticsIDAttribute || !analyticsEventsAttribute) { return null; } diff --git a/src/eventPlugins/EnterLeaveEventPlugin.js b/src/eventPlugins/EnterLeaveEventPlugin.js index 51b02c42f3..d4a39131e7 100644 --- a/src/eventPlugins/EnterLeaveEventPlugin.js +++ b/src/eventPlugins/EnterLeaveEventPlugin.js @@ -14,6 +14,7 @@ * limitations under the License. * * @providesModule EnterLeaveEventPlugin + * @typechecks */ "use strict"; @@ -36,28 +37,32 @@ var abstractEventTypes = { }; /** - * For almost every interaction we care about, there will be a top level - * `mouseOver` and `mouseOut` event that occur so we can usually only pay - * attention to one of the two (we'll pay attention to the `mouseOut` event) to - * avoid extracting a duplicate event. However, there's one interaction where - * there will be no `mouseOut` event to rely on - mousing from outside the - * browser *into* the chrome. We detect this scenario and only in that case, we - * use the `mouseOver` event. + * For almost every interaction we care about, there will be a top-level + * `mouseover` and `mouseout` event that occurs so only pay attention to one of + * the two (to avoid duplicate events). We use the `mouseout` event. * - * @see EventPluginHub.extractAbstractEvents + * However, there's one interaction where there will be no `mouseout` event to + * rely on - mousing from outside the browser *into* the chrome. We detect this + * scenario and only in that case, we use the `mouseover` event. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. + * @see {EventPluginHub.extractAbstractEvents} */ var extractAbstractEvents = function( topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget) { - + topLevelTarget, + topLevelTargetID, + nativeEvent) { if (topLevelType === topLevelTypes.topMouseOver && (nativeEvent.relatedTarget || nativeEvent.fromElement)) { - return; + return null; } if (topLevelType !== topLevelTypes.topMouseOut && - topLevelType !== topLevelTypes.topMouseOver){ + topLevelType !== topLevelTypes.topMouseOver) { return null; // Must not be a mouse in or mouse out - ignoring. } @@ -65,32 +70,33 @@ var extractAbstractEvents = function( if (topLevelType === topLevelTypes.topMouseOut) { to = getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) || ExecutionEnvironment.global; - from = renderedTarget; + from = topLevelTarget; } else { - to = renderedTarget; + to = topLevelTarget; from = ExecutionEnvironment.global; } // Nothing pertains to our managed components. - if (from === to ) { - return; + if (from === to) { + return null; } var fromID = from ? getDOMNodeID(from) : ''; var toID = to ? getDOMNodeID(to) : ''; + var leave = AbstractEvent.getPooled( abstractEventTypes.mouseLeave, fromID, - topLevelType, nativeEvent ); var enter = AbstractEvent.getPooled( abstractEventTypes.mouseEnter, toID, - topLevelType, nativeEvent ); + EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID); + return [leave, enter]; }; diff --git a/src/eventPlugins/ResponderEventPlugin.js b/src/eventPlugins/ResponderEventPlugin.js index d06d16cd0c..73d60b54c8 100644 --- a/src/eventPlugins/ResponderEventPlugin.js +++ b/src/eventPlugins/ResponderEventPlugin.js @@ -162,14 +162,13 @@ var abstractEventTypes = { */ /** - * @param {TopLevelTypes} topLevelType Top level event type being examined. - * @param {DOMEvent} nativeEvent Native DOM event. + * @param {string} topLevelType Record from `EventConstants`. * @param {string} renderedTargetID ID of deepest React rendered element. - * - * @return {Accumulation} Extracted events. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of extracted `AbstractEvent`s. */ var setResponderAndExtractTransfer = - function(topLevelType, nativeEvent, renderedTargetID) { + function(topLevelType, renderedTargetID, nativeEvent) { var type; var shouldSetEventType = isStartish(topLevelType) ? abstractEventTypes.startShouldSetResponder : @@ -235,7 +234,6 @@ var setResponderAndExtractTransfer = return extracted; }; - /** * A transfer is a negotiation between a currently set responder and the next * element to claim responder status. Any start event could trigger a transfer @@ -253,51 +251,60 @@ function canTriggerTransfer(topLevelType) { (isPressing && isMoveish(topLevelType)); } -var extractAbstractEvents = - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) { - var extracted; - // Must have missed an end event - reset the state here. - if (responderID && isStartish(topLevelType)) { - responderID = null; +/** + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. + * @see {EventPluginHub.extractAbstractEvents} + */ +var extractAbstractEvents = function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + var extracted; + // Must have missed an end event - reset the state here. + if (responderID && isStartish(topLevelType)) { + responderID = null; + } + if (isStartish(topLevelType)) { + isPressing = true; + } else if (isEndish(topLevelType)) { + isPressing = false; + } + if (canTriggerTransfer(topLevelType)) { + var transfer = setResponderAndExtractTransfer( + topLevelType, + topLevelTargetID, + nativeEvent + ); + if (transfer) { + extracted = accumulate(extracted, transfer); } - if (isStartish(topLevelType)) { - isPressing = true; - } else if (isEndish(topLevelType)) { - isPressing = false; - } - if (canTriggerTransfer(topLevelType)) { - var transfer = setResponderAndExtractTransfer( - topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget - ); - if (transfer) { - extracted = accumulate(extracted, transfer); - } - } - // Now that we know the responder is set correctly, we can dispatch - // responder type events (directly to the responder). - var type = isMoveish(topLevelType) ? abstractEventTypes.responderMove : - isEndish(topLevelType) ? abstractEventTypes.responderRelease : - isStartish(topLevelType) ? abstractEventTypes.responderStart : null; - if (type) { - var data = AbstractEvent.normalizePointerData(nativeEvent); - var gesture = AbstractEvent.getPooled( - type, - responderID, - topLevelType, - nativeEvent, - data - ); - EventPropagators.accumulateDirectDispatches(gesture); - extracted = accumulate(extracted, gesture); - } - if (type === abstractEventTypes.responderRelease) { - responderID = null; - } - return extracted; - }; + } + // Now that we know the responder is set correctly, we can dispatch + // responder type events (directly to the responder). + var type = isMoveish(topLevelType) ? abstractEventTypes.responderMove : + isEndish(topLevelType) ? abstractEventTypes.responderRelease : + isStartish(topLevelType) ? abstractEventTypes.responderStart : null; + if (type) { + var data = AbstractEvent.normalizePointerData(nativeEvent); + var gesture = AbstractEvent.getPooled( + type, + responderID, + nativeEvent, + data + ); + EventPropagators.accumulateDirectDispatches(gesture); + extracted = accumulate(extracted, gesture); + } + if (type === abstractEventTypes.responderRelease) { + responderID = null; + } + return extracted; +}; /** * Event plugin for formalizing the negotiation between claiming locks on diff --git a/src/eventPlugins/SimpleEventPlugin.js b/src/eventPlugins/SimpleEventPlugin.js index 0453fde60a..ab794a17b4 100644 --- a/src/eventPlugins/SimpleEventPlugin.js +++ b/src/eventPlugins/SimpleEventPlugin.js @@ -155,6 +155,7 @@ var SimpleEventPlugin = { /** * Same as the default implementation, except cancels the event when return * value is false. + * * @param {AbstractEvent} AbstractEvent to handle * @param {function} Application-level callback * @param {string} domID DOM id to pass to the callback. @@ -168,48 +169,55 @@ var SimpleEventPlugin = { }, /** - * @see EventPluginHub.extractAbstractEvents + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. + * @see {EventPluginHub.extractAbstractEvents} */ - extractAbstractEvents: - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) { - var data; - var abstractEventType = - SimpleEventPlugin.topLevelTypesToAbstract[topLevelType]; - if (!abstractEventType) { - return null; - } - switch(topLevelType) { - case topLevelTypes.topMouseWheel: - data = AbstractEvent.normalizeMouseWheelData(nativeEvent); - break; - case topLevelTypes.topScroll: - data = AbstractEvent.normalizeScrollDataFromTarget(renderedTarget); - break; - case topLevelTypes.topClick: - case topLevelTypes.topDoubleClick: - case topLevelTypes.topChange: - case topLevelTypes.topDOMCharacterDataModified: - case topLevelTypes.topMouseDown: - case topLevelTypes.topMouseUp: - case topLevelTypes.topMouseMove: - case topLevelTypes.topTouchMove: - case topLevelTypes.topTouchStart: - case topLevelTypes.topTouchEnd: - data = AbstractEvent.normalizePointerData(nativeEvent); - break; - default: - data = null; - } - var abstractEvent = AbstractEvent.getPooled( - abstractEventType, - renderedTargetID, - topLevelType, - nativeEvent, - data - ); - EventPropagators.accumulateTwoPhaseDispatches(abstractEvent); - return abstractEvent; + extractAbstractEvents: function( + topLevelType, + topLevelTarget, + topLevelTargetID, + nativeEvent) { + var data; + var abstractEventType = + SimpleEventPlugin.topLevelTypesToAbstract[topLevelType]; + if (!abstractEventType) { + return null; } + switch(topLevelType) { + case topLevelTypes.topMouseWheel: + data = AbstractEvent.normalizeMouseWheelData(nativeEvent); + break; + case topLevelTypes.topScroll: + data = AbstractEvent.normalizeScrollDataFromTarget(topLevelTarget); + break; + case topLevelTypes.topClick: + case topLevelTypes.topDoubleClick: + case topLevelTypes.topChange: + case topLevelTypes.topDOMCharacterDataModified: + case topLevelTypes.topMouseDown: + case topLevelTypes.topMouseUp: + case topLevelTypes.topMouseMove: + case topLevelTypes.topTouchMove: + case topLevelTypes.topTouchStart: + case topLevelTypes.topTouchEnd: + data = AbstractEvent.normalizePointerData(nativeEvent); + break; + default: + data = null; + } + var abstractEvent = AbstractEvent.getPooled( + abstractEventType, + topLevelTargetID, + nativeEvent, + data + ); + EventPropagators.accumulateTwoPhaseDispatches(abstractEvent); + return abstractEvent; + } }; SimpleEventPlugin.topLevelTypesToAbstract = { diff --git a/src/eventPlugins/TapEventPlugin.js b/src/eventPlugins/TapEventPlugin.js index 141b4aea4e..3759bc1cbd 100644 --- a/src/eventPlugins/TapEventPlugin.js +++ b/src/eventPlugins/TapEventPlugin.js @@ -14,6 +14,7 @@ * limitations under the License. * * @providesModule TapEventPlugin + * @typechecks */ "use strict"; @@ -46,26 +47,27 @@ var abstractEventTypes = { }; /** - * @see EventPluginHub.extractAbstractEvents + * @param {string} topLevelType Record from `EventConstants`. + * @param {DOMEventTarget} topLevelTarget The listening component root node. + * @param {string} topLevelTargetID ID of `topLevelTarget`. + * @param {object} nativeEvent Native browser event. + * @return {*} An accumulation of `AbstractEvent`s. + * @see {EventPluginHub.extractAbstractEvents} */ var extractAbstractEvents = function( topLevelType, - nativeEvent, - renderedTargetID, - renderedTarget) { - + topLevelTarget, + topLevelTargetID, + nativeEvent) { if (!isStartish(topLevelType) && !isEndish(topLevelType)) { return; } var abstractEvent; var dist = eventDistance(startCoords, nativeEvent); if (isEndish(topLevelType) && dist < tapMoveThreshold) { - var type = abstractEventTypes.touchTap; - var abstractTargetID = renderedTargetID; abstractEvent = AbstractEvent.getPooled( - type, - abstractTargetID, - topLevelType, + abstractEventTypes.touchTap, + topLevelTargetID, nativeEvent ); } diff --git a/src/eventPlugins/__tests__/ResponderEventPlugin-test.js b/src/eventPlugins/__tests__/ResponderEventPlugin-test.js index 2c8a51a949..58f478991a 100644 --- a/src/eventPlugins/__tests__/ResponderEventPlugin-test.js +++ b/src/eventPlugins/__tests__/ResponderEventPlugin-test.js @@ -167,8 +167,9 @@ var existsInExtraction = function(extracted, test) { function assertGrantEvent(id, extracted) { var test = function(abstractEvent) { return abstractEvent instanceof AbstractEvent && - abstractEvent.type === responderAbstractEventTypes.responderGrant && - abstractEvent.abstractTargetID === id; + abstractEvent.reactEventType === + responderAbstractEventTypes.responderGrant && + abstractEvent.reactTargetID === id; }; expect(ResponderEventPlugin.getResponderID()).toBe(id); expect(existsInExtraction(extracted, test)).toBe(true); @@ -177,8 +178,9 @@ function assertGrantEvent(id, extracted) { function assertResponderMoveEvent(id, extracted) { var test = function(abstractEvent) { return abstractEvent instanceof AbstractEvent && - abstractEvent.type === responderAbstractEventTypes.responderMove && - abstractEvent.abstractTargetID === id; + abstractEvent.reactEventType === + responderAbstractEventTypes.responderMove && + abstractEvent.reactTargetID === id; }; expect(ResponderEventPlugin.getResponderID()).toBe(id); expect(existsInExtraction(extracted, test)).toBe(true); @@ -187,8 +189,9 @@ function assertResponderMoveEvent(id, extracted) { function assertTerminateEvent(id, extracted) { var test = function(abstractEvent) { return abstractEvent instanceof AbstractEvent && - abstractEvent.type === responderAbstractEventTypes.responderTerminate && - abstractEvent.abstractTargetID === id; + abstractEvent.reactEventType === + responderAbstractEventTypes.responderTerminate && + abstractEvent.reactTargetID === id; }; expect(ResponderEventPlugin.getResponderID()).not.toBe(id); expect(existsInExtraction(extracted, test)).toBe(true); @@ -197,8 +200,9 @@ function assertTerminateEvent(id, extracted) { function assertRelease(id, extracted) { var test = function(abstractEvent) { return abstractEvent instanceof AbstractEvent && - abstractEvent.type === responderAbstractEventTypes.responderRelease && - abstractEvent.abstractTargetID === id; + abstractEvent.reactEventType === + responderAbstractEventTypes.responderRelease && + abstractEvent.reactTargetID === id; }; expect(ResponderEventPlugin.getResponderID()).toBe(null); expect(existsInExtraction(extracted, test)).toBe(true);