@samitouri / QOS-React-2 / commits / ba6fea1bf5
Fork 0

The library for web and native user interfaces.

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.

CommitSyncScript committed Jun 6, 2013 at 14:40 UTC ba6fea1bf5eaab71c0abec7d55824419bcd4f3f4
15 files changed +457 -440
src/core/ReactEventEmitter.js
+163 -142
@@ -14,33 +14,44 @@
14 * limitations under the License.
15 *
16 * @providesModule ReactEventEmitter
17 + * @typechecks
18 */
19
20 "use strict";
21
22 var BrowserEnv = require('BrowserEnv');
23 var EventConstants = require('EventConstants');
24 +var EventListener = require('EventListener');
25 var EventPluginHub = require('EventPluginHub');
26 var ExecutionEnvironment = require('ExecutionEnvironment');
25 -var NormalizedEventListener = require('NormalizedEventListener');
27
28 var invariant = require('invariant');
29 var isEventSupported = require('isEventSupported');
30
30 -var registrationNames = EventPluginHub.registrationNames;
31 -var topLevelTypes = EventConstants.topLevelTypes;
32 -var listen = NormalizedEventListener.listen;
33 -var capture = NormalizedEventListener.capture;
34 -
31 /**
36 - * `ReactEventEmitter` is used to attach top-level event listeners. For example:
32 + * Summary of `ReactEventEmitter` event handling:
33 *
38 - * ReactEventEmitter.putListener('myID', 'onClick', myFunction);
34 + * - We trap low level 'top-level' events.
35 + *
36 + * - We dedupe cross-browser event names into these 'top-level types' so that
37 + * `DOMMouseScroll` or `mouseWheel` both become `topMouseWheel`.
38 + *
39 + * - At this point we have native browser events with the top-level type that
40 + * was used to catch it at the top-level.
41 + *
42 + * - We continuously stream these native events (and their respective top-level
43 + * types) to the event plugin system `EventPluginHub` and ask the plugin
44 + * system if it was able to extract `AbstractEvent` objects. `AbstractEvent`
45 + * objects are the events that applications actually deal with - they are not
46 + * native browser events but cross-browser wrappers.
47 + *
48 + * - When returning the `AbstractEvent` objects, `EventPluginHub` will make
49 + * sure each abstract event is annotated with "dispatches", which are the
50 + * sequence of listeners (and IDs) that care about the event.
51 + *
52 + * - These `AbstractEvent` objects are fed back into the event plugin system,
53 + * which in turn executes these dispatches.
54 *
40 - * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
41 - */
42 -
43 -/**
55 * Overview of React and the event system:
56 *
57 * .
@@ -72,36 +83,23 @@ var capture = NormalizedEventListener.capture;
83 */
84
85 /**
75 - * We listen for bubbled touch events on the document object.
76 - *
77 - * Firefox v8.01 (and possibly others) exhibited strange behavior when mounting
78 - * `onmousemove` events at some node that was not the document element. The
79 - * symptoms were that if your mouse is not moving over something contained
80 - * within that mount point (for example on the background) the top-level
81 - * listeners for `onmousemove` won't be called. However, if you register the
82 - * `mousemove` on the document object, then it will of course catch all
83 - * `mousemove`s. This along with iOS quirks, justifies restricting top-level
84 - * listeners to the document object only, at least for these movement types of
85 - * events and possibly all events.
86 - *
87 - * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
88 - *
89 - * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
90 - * they bubble to document.
91 - *
92 - * @see http://www.quirksmode.org/dom/events/keys.html.
86 + * Whether or not `ensureListening` has been invoked.
87 + * @type {boolean}
88 + * @private
89 */
94 -
90 var _isListening = false;
91
92 /**
98 - * Traps top-level events that bubble. Delegates to the main dispatcher
99 - * `handleTopLevel` after performing some basic normalization via
100 - * `TopLevelCallbackCreator.createTopLevelCallback`.
93 + * Traps top-level events by using event bubbling.
94 + *
95 + * @param {string} topLevelType Record from `EventConstants`.
96 + * @param {string} handlerBaseName Event name (e.g. "click").
97 + * @param {DOMEventTarget} element Element on which to attach listener.
98 + * @internal
99 */
102 -function trapBubbledEvent(topLevelType, handlerBaseName, onWhat) {
103 - listen(
104 - onWhat,
100 +function trapBubbledEvent(topLevelType, handlerBaseName, element) {
101 + EventListener.listen(
102 + element,
103 handlerBaseName,
104 ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
105 topLevelType
@@ -111,10 +109,15 @@ function trapBubbledEvent(topLevelType, handlerBaseName, onWhat) {
109
110 /**
111 * Traps a top-level event by using event capturing.
112 + *
113 + * @param {string} topLevelType Record from `EventConstants`.
114 + * @param {string} handlerBaseName Event name (e.g. "click").
115 + * @param {DOMEventTarget} element Element on which to attach listener.
116 + * @internal
117 */
115 -function trapCapturedEvent(topLevelType, handlerBaseName, onWhat) {
116 - capture(
117 - onWhat,
118 +function trapCapturedEvent(topLevelType, handlerBaseName, element) {
119 + EventListener.capture(
120 + element,
121 handlerBaseName,
122 ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
123 topLevelType
@@ -123,62 +126,51 @@ function trapCapturedEvent(topLevelType, handlerBaseName, onWhat) {
126 }
127
128 /**
126 - * Listens to document scroll and window resize events that may change the
127 - * document scroll values. We store those results so as to discourage
128 - * application code from asking the DOM itself which could trigger additional
129 - * reflows.
129 + * Listens to window scroll and resize events. We cache scroll values so that
130 + * application code can access them without triggering reflows.
131 + *
132 + * NOTE: Scroll events do not bubble.
133 + *
134 + * @private
135 + * @see http://www.quirksmode.org/dom/events/scroll.html
136 */
131 -function registerDocumentScrollListener() {
132 - listen(window, 'scroll', function(nativeEvent) {
133 - if (nativeEvent.target === window) {
134 - BrowserEnv.refreshAuthoritativeScrollValues();
135 - }
136 - });
137 -}
138 -
139 -function registerDocumentResizeListener() {
140 - listen(window, 'resize', function(nativeEvent) {
141 - if (nativeEvent.target === window) {
142 - BrowserEnv.refreshAuthoritativeScrollValues();
143 - }
144 - });
137 +function registerScrollValueMonitoring() {
138 + var refresh = BrowserEnv.refreshAuthoritativeScrollValues;
139 + EventListener.listen(window, 'scroll', refresh);
140 + EventListener.listen(window, 'resize', refresh);
141 }
142
143 /**
148 - * Summary of `ReactEventEmitter` event handling:
149 - *
150 - * - We trap low level 'top-level' events.
151 - *
152 - * - We dedupe cross-browser event names into these 'top-level types' so that
153 - * `DOMMouseScroll` or `mouseWheel` both become `topMouseWheel`.
154 - *
155 - * - At this point we have native browser events with the top-level type that
156 - * was used to catch it at the top-level.
144 + * We listen for bubbled touch events on the document object.
145 *
158 - * - We continuously stream these native events (and their respective top-level
159 - * types) to the event plugin system `EventPluginHub` and ask the plugin
160 - * system if it was able to extract `AbstractEvent` objects. `AbstractEvent`
161 - * objects are the events that applications actually deal with - they are not
162 - * native browser events but cross-browser wrappers.
146 + * Firefox v8.01 (and possibly others) exhibited strange behavior when mounting
147 + * `onmousemove` events at some node that was not the document element. The
148 + * symptoms were that if your mouse is not moving over something contained
149 + * within that mount point (for example on the background) the top-level
150 + * listeners for `onmousemove` won't be called. However, if you register the
151 + * `mousemove` on the document object, then it will of course catch all
152 + * `mousemove`s. This along with iOS quirks, justifies restricting top-level
153 + * listeners to the document object only, at least for these movement types of
154 + * events and possibly all events.
155 *
164 - * - When returning the `AbstractEvent` objects, `EventPluginHub` will make
165 - * sure each abstract event is annotated with "dispatches", which are the
166 - * sequence of listeners (and IDs) that care about the event.
156 + * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
157 *
168 - * - These `AbstractEvent` objects are fed back into the event plugin system,
169 - * which in turn executes these dispatches.
158 + * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
159 + * they bubble to document.
160 *
161 + * @param {boolean} touchNotMouse Listen to touch events instead of mouse.
162 * @private
163 + * @see http://www.quirksmode.org/dom/events/keys.html.
164 */
165 function listenAtTopLevel(touchNotMouse) {
166 invariant(
167 !_isListening,
168 'listenAtTopLevel(...): Cannot setup top-level listener more than once.'
169 );
170 + var topLevelTypes = EventConstants.topLevelTypes;
171 var mountAt = document;
172
180 - registerDocumentScrollListener();
181 - registerDocumentResizeListener();
173 + registerScrollValueMonitoring();
174 trapBubbledEvent(topLevelTypes.topMouseOver, 'mouseover', mountAt);
175 trapBubbledEvent(topLevelTypes.topMouseDown, 'mousedown', mountAt);
176 trapBubbledEvent(topLevelTypes.topMouseUp, 'mouseup', mountAt);
@@ -226,82 +218,111 @@ function listenAtTopLevel(touchNotMouse) {
218 }
219
220 /**
229 - * This is the heart of `ReactEventEmitter`. It simply streams the top-level
230 - * native events to `EventPluginHub`.
221 + * `ReactEventEmitter` is used to attach top-level event listeners. For example:
222 + *
223 + * ReactEventEmitter.putListener('myID', 'onClick', myFunction);
224 + *
225 + * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
226 *
232 - * @param {object} topLevelType Record from `EventConstants`.
233 - * @param {Event} nativeEvent A Standard Event with fixed `target` property.
234 - * @param {DOMElement} renderedTarget Element of interest to the framework.
235 - * @param {string} renderedTargetID string ID of `renderedTarget`.
227 * @internal
228 */
238 -function handleTopLevel(
239 - topLevelType,
240 - nativeEvent,
241 - renderedTargetID,
242 - renderedTarget) {
243 - var abstractEvents = EventPluginHub.extractAbstractEvents(
244 - topLevelType,
245 - nativeEvent,
246 - renderedTargetID,
247 - renderedTarget
248 - );
229 +var ReactEventEmitter = {
230
250 - // The event queue being processed in the same cycle allows preventDefault.
251 - EventPluginHub.enqueueAbstractEvents(abstractEvents);
252 - EventPluginHub.processAbstractEventQueue();
253 -}
231 + /**
232 + * React references `ReactEventTopLevelCallback` using this property in order
233 + * to allow dependency injection via `ensureListening`.
234 + */
235 + TopLevelCallbackCreator: null,
236
255 -function setEnabled(enabled) {
256 - invariant(
257 - ExecutionEnvironment.canUseDOM,
258 - 'setEnabled(...): Cannot toggle event listening in a Worker thread. This ' +
259 - 'is likely a bug in the framework. Please report immediately.'
260 - );
261 - ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
262 -}
237 + /**
238 + * Ensures that top-level event delegation listeners are installed.
239 + *
240 + * There are issues with listening to both touch events and mouse events on
241 + * the top-level, so we make the caller choose which one to listen to. (If
242 + * there's a touch top-level listeners, anchors don't receive clicks for some
243 + * reason, and only in some cases).
244 + *
245 + * @param {boolean} touchNotMouse Listen to touch events instead of mouse.
246 + * @param {object} TopLevelCallbackCreator
247 + */
248 + ensureListening: function(touchNotMouse, TopLevelCallbackCreator) {
249 + invariant(
250 + ExecutionEnvironment.canUseDOM,
251 + 'ensureListening(...): Cannot toggle event listening in a Worker ' +
252 + 'thread. This is likely a bug in the framework. Please report ' +
253 + 'immediately.'
254 + );
255 + if (!_isListening) {
256 + ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
257 + listenAtTopLevel(touchNotMouse);
258 + _isListening = true;
259 + }
260 + },
261
264 -function isEnabled() {
265 - return ReactEventEmitter.TopLevelCallbackCreator.isEnabled();
266 -}
262 + /**
263 + * Sets whether or not any created callbacks should be enabled.
264 + *
265 + * @param {boolean} enabled True if callbacks should be enabled.
266 + */
267 + setEnabled: function(enabled) {
268 + invariant(
269 + ExecutionEnvironment.canUseDOM,
270 + 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' +
271 + 'This is likely a bug in the framework. Please report immediately.'
272 + );
273 + if (ReactEventEmitter.TopLevelCallbackCreator) {
274 + ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
275 + }
276 + },
277
268 -/**
269 - * Ensures that top-level event delegation listeners are listening at `mountAt`.
270 - * There are issues with listening to both touch events and mouse events on the
271 - * top-level, so we make the caller choose which one to listen to. (If there's a
272 - * touch top-level listeners, anchors don't receive clicks for some reason, and
273 - * only in some cases).
274 - *
275 - * @param {boolean} touchNotMouse Listen to touch events instead of mouse.
276 - * @param {object} TopLevelCallbackCreator Module that can create top-level
277 - * callback handlers.
278 - * @internal
279 - */
280 -function ensureListening(touchNotMouse, TopLevelCallbackCreator) {
281 - invariant(
282 - ExecutionEnvironment.canUseDOM,
283 - 'ensureListening(...): Cannot toggle event listening in a Worker thread. ' +
284 - 'This is likely a bug in the framework. Please report immediately.'
285 - );
286 - if (!_isListening) {
287 - ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
288 - listenAtTopLevel(touchNotMouse);
289 - _isListening = true;
290 - }
291 -}
278 + /**
279 + * @return {boolean} True if callbacks are enabled.
280 + */
281 + isEnabled: function() {
282 + return !!(
283 + ReactEventEmitter.TopLevelCallbackCreator &&
284 + ReactEventEmitter.TopLevelCallbackCreator.isEnabled()
285 + );
286 + },
287 +
288 + /**
289 + * Streams a fired top-level event to `EventPluginHub` where plugins have the
290 + * opportunity to create `ReactEvent`s to be dispatched.
291 + *
292 + * @param {string} topLevelType Record from `EventConstants`.
293 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
294 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
295 + * @param {object} nativeEvent Native browser event.
296 + */
297 + handleTopLevel: function(
298 + topLevelType,
299 + topLevelTarget,
300 + topLevelTargetID,
301 + nativeEvent) {
302 + var abstractEvents = EventPluginHub.extractAbstractEvents(
303 + topLevelType,
304 + topLevelTarget,
305 + topLevelTargetID,
306 + nativeEvent
307 + );
308 +
309 + // Event queue being processed in the same cycle allows `preventDefault`.
310 + EventPluginHub.enqueueAbstractEvents(abstractEvents);
311 + EventPluginHub.processAbstractEventQueue();
312 + },
313 +
314 + registrationNames: EventPluginHub.registrationNames,
315
293 -var ReactEventEmitter = {
294 - TopLevelCallbackCreator: null, // Injectable callback creator.
295 - handleTopLevel: handleTopLevel,
296 - setEnabled: setEnabled,
297 - isEnabled: isEnabled,
298 - ensureListening: ensureListening,
299 - registrationNames: registrationNames,
316 putListener: EventPluginHub.putListener,
317 +
318 getListener: EventPluginHub.getListener,
319 +
320 deleteAllListeners: EventPluginHub.deleteAllListeners,
321 +
322 trapBubbledEvent: trapBubbledEvent,
323 +
324 trapCapturedEvent: trapCapturedEvent
325 +
326 };
327
328 module.exports = ReactEventEmitter;
src/core/ReactEventTopLevelCallback.js
+29 -20
@@ -14,6 +14,7 @@
14 * limitations under the License.
15 *
16 * @providesModule ReactEventTopLevelCallback
17 + * @typechecks
18 */
19
20 "use strict";
@@ -23,51 +24,59 @@ var ReactEventEmitter = require('ReactEventEmitter');
24 var ReactInstanceHandles = require('ReactInstanceHandles');
25
26 var getDOMNodeID = require('getDOMNodeID');
27 +var getEventTarget = require('getEventTarget');
28
29 +/**
30 + * @type {boolean}
31 + * @private
32 + */
33 var _topLevelListenersEnabled = true;
34
35 +/**
36 + * Top-level callback creator used to implement event handling using delegation.
37 + * This is used via dependency injection in `ReactEventEmitter.ensureListening`.
38 + */
39 var ReactEventTopLevelCallback = {
40
41 /**
32 - * @param {boolean} enabled Whether or not all callbacks that have ever been
33 - * created with this module should be enabled.
42 + * Sets whether or not any created callbacks should be enabled.
43 + *
44 + * @param {boolean} enabled True if callbacks should be enabled.
45 */
46 setEnabled: function(enabled) {
47 _topLevelListenersEnabled = !!enabled;
48 },
49
50 + /**
51 + * @return {boolean} True if callbacks are enabled.
52 + */
53 isEnabled: function() {
54 return _topLevelListenersEnabled;
55 },
56
57 /**
44 - * For a given `topLevelType`, creates a callback that could be added as a
45 - * listener to the document. That top level callback will simply fix the
46 - * native events before invoking `handleTopLevel`.
58 + * Creates a callback for the supplied `topLevelType` that could be added as
59 + * a listener to the document. The callback computes a `topLevelTarget` which
60 + * should be the root node of a mounted React component where the listener
61 + * is attached.
62 *
48 - * - Raw native events cannot be trusted to describe their targets correctly
49 - * so we expect that the argument to the nested function has already been
50 - * fixed. But the `target` property may not be something of interest to
51 - * React, so we find the most suitable target. But even at that point, DOM
52 - * Elements (the target ) can't be trusted to describe their IDs correctly
53 - * so we obtain the ID in a reliable manner and pass it to
54 - * `handleTopLevel`. The target/id that we found to be relevant to our
55 - * framework are called `renderedTarget`/`renderedTargetID` respectively.
63 + * @param {string} topLevelType Record from `EventConstants`.
64 + * @return {function} Callback for handling top-level events.
65 */
66 createTopLevelCallback: function(topLevelType) {
58 - return function(fixedNativeEvent) {
67 + return function(nativeEvent) {
68 if (!_topLevelListenersEnabled) {
69 return;
70 }
62 - var renderedTarget = ReactInstanceHandles.getFirstReactDOM(
63 - fixedNativeEvent.target
71 + var topLevelTarget = ReactInstanceHandles.getFirstReactDOM(
72 + getEventTarget(nativeEvent)
73 ) || ExecutionEnvironment.global;
65 - var renderedTargetID = getDOMNodeID(renderedTarget);
74 + var topLevelTargetID = getDOMNodeID(topLevelTarget) || '';
75 ReactEventEmitter.handleTopLevel(
76 topLevelType,
68 - fixedNativeEvent,
69 - renderedTargetID,
70 - renderedTarget
77 + topLevelTarget,
78 + topLevelTargetID,
79 + nativeEvent
80 );
81 };
82 }
src/core/ReactInstanceHandles.js
+8 -7
@@ -14,6 +14,7 @@
14 * limitations under the License.
15 *
16 * @providesModule ReactInstanceHandles
17 + * @typechecks
18 */
19
20 "use strict";
@@ -57,13 +58,13 @@ function isValidID(id) {
58 /**
59 * True if the supplied `node` is rendered by React.
60 *
60 - * @param {DOMElement} node DOM Element to check.
61 + * @param {DOMEventTarget} node DOM Element to check.
62 * @return {boolean} True if the DOM Element appears to be rendered by React.
63 * @private
64 */
65 function isRenderedByReact(node) {
66 var id = getDOMNodeID(node);
66 - return id && id.charAt(0) === SEPARATOR;
67 + return id ? id.charAt(0) === SEPARATOR : false;
68 }
69
70 /**
@@ -140,8 +141,8 @@ var ReactInstanceHandles = {
141 * Traverses up the ancestors of the supplied node to find a node that is a
142 * DOM representation of a React component.
143 *
143 - * @param {DOMElement} node
144 - * @return {?DOMElement}
144 + * @param {?DOMEventTarget} node
145 + * @return {?DOMEventTarget}
146 * @internal
147 */
148 getFirstReactDOM: function(node) {
@@ -159,9 +160,9 @@ var ReactInstanceHandles = {
160 * Finds a node with the supplied `id` inside of the supplied `ancestorNode`.
161 * Exploits the ID naming scheme to perform the search quickly.
162 *
162 - * @param {DOMElement} ancestorNode Search from this root.
163 + * @param {DOMEventTarget} ancestorNode Search from this root.
164 * @pararm {string} id ID of the DOM representation of the component.
164 - * @return {?DOMElement} DOM element with the supplied `id`, if one exists.
165 + * @return {?DOMEventTarget} DOM node with the supplied `id`, if one exists.
166 * @internal
167 */
168 findComponentRoot: function(ancestorNode, id) {
@@ -228,7 +229,7 @@ var ReactInstanceHandles = {
229 * contains the React component with the supplied DOM ID.
230 *
231 * @param {string} id DOM ID of a React component.
231 - * @return {string} DOM ID of the React component that is the root.
232 + * @return {?string} DOM ID of the React component that is the root.
233 * @internal
234 */
235 getReactRootIDFromNodeID: function(id) {
src/dom/getDOMNodeID.js
+3 -2
@@ -14,6 +14,7 @@
14 * limitations under the License.
15 *
16 * @providesModule getDOMNodeID
17 + * @typechecks
18 */
19
20 "use strict";
@@ -23,8 +24,8 @@
24 * control whose name or ID is "id". However, not all DOM nodes support
25 * `getAttributeNode` (document - which is not a form) so that is checked first.
26 *
26 - * @param {Element} domNode DOM node element to return ID of.
27 - * @returns {string} The ID of `domNode`.
27 + * @param {DOMElement|DOMWindow|DOMDocument} domNode DOM node.
28 + * @returns {string} ID of the supplied `domNode`.
29 */
30 function getDOMNodeID(domNode) {
31 if (domNode.getAttributeNode) {
src/dom/getEventTarget.js new
+39
@@ -0,0 +1,39 @@
1 +/**
2 + * Copyright 2013 Facebook, Inc.
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + *
16 + * @providesModule getEventTarget
17 + * @typechecks
18 + */
19 +
20 +var ExecutionEnvironment = require('ExecutionEnvironment');
21 +
22 +/**
23 + * Gets the target node from a native browser event by accounting for
24 + * inconsistencies in browser DOM APIs.
25 + *
26 + * @param {object} nativeEvent Native browser event.
27 + * @return {DOMEventTarget} Target node.
28 + */
29 +function getEventTarget(nativeEvent) {
30 + var target =
31 + nativeEvent.target ||
32 + nativeEvent.srcElement ||
33 + ExecutionEnvironment.global;
34 + // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
35 + // @see http://www.quirksmode.org/js/events_properties.html
36 + return target.nodeType === 3 ? target.parentNode : target;
37 +}
38 +
39 +module.exports = getEventTarget;
src/event/AbstractEvent.js
+7 -11
@@ -44,14 +44,12 @@ var MAX_POOL_SIZE = 20;
44 * unreliable native event.
45 */
46 function AbstractEvent(
47 - abstractEventType,
48 - abstractTargetID, // Allows the abstract target to differ from native.
49 - originatingTopLevelEventType,
47 + reactEventType,
48 + reactTargetID, // Allows the abstract target to differ from native.
49 nativeEvent,
50 data) {
52 - this.type = abstractEventType;
53 - this.abstractTargetID = abstractTargetID || '';
54 - this.originatingTopLevelEventType = originatingTopLevelEventType;
51 + this.reactEventType = reactEventType;
52 + this.reactTargetID = reactTargetID || '';
53 this.nativeEvent = nativeEvent;
54 this.data = data;
55 // TODO: Deprecate storing target - doesn't always make sense for some types
@@ -263,12 +261,10 @@ AbstractEvent.persistentCloneOf = function(abstractEvent) {
261 throwIf(!(abstractEvent instanceof AbstractEvent), CLONE_TYPE_ERR);
262 }
263 return new AbstractEvent(
266 - abstractEvent.type,
267 - abstractEvent.abstractTargetID,
268 - abstractEvent.originatingTopLevelEventType,
264 + abstractEvent.reactEventType,
265 + abstractEvent.reactTargetID,
266 abstractEvent.nativeEvent,
270 - abstractEvent.data,
271 - abstractEvent.target
267 + abstractEvent.data
268 );
269 };
270
src/event/EventPluginHub.js
+31 -30
@@ -194,15 +194,16 @@ function recordAllRegistrationNames(eventType, PluginModule) {
194 * @param {AbstractEvent} abstractEvent to look at
195 */
196 function getPluginModuleForAbstractEvent(abstractEvent) {
197 - if (abstractEvent.type.registrationName) {
198 - return registrationNames[abstractEvent.type.registrationName];
197 + var reactEventType = abstractEvent.reactEventType;
198 + if (reactEventType.registrationName) {
199 + return registrationNames[reactEventType.registrationName];
200 } else {
200 - for (var phase in abstractEvent.type.phasedRegistrationNames) {
201 - if (!abstractEvent.type.phasedRegistrationNames.hasOwnProperty(phase)) {
201 + for (var phase in reactEventType.phasedRegistrationNames) {
202 + if (!reactEventType.phasedRegistrationNames.hasOwnProperty(phase)) {
203 continue;
204 }
205 var PluginModule = registrationNames[
205 - abstractEvent.type.phasedRegistrationNames[phase]
206 + reactEventType.phasedRegistrationNames[phase]
207 ];
208 if (PluginModule) {
209 return PluginModule;
@@ -223,36 +224,36 @@ var deleteAllListeners = function(domID) {
224 * Accepts the stream of top level native events, and gives every registered
225 * plugin an opportunity to extract `AbstractEvent`s with annotated dispatches.
226 *
226 - * @param {Enum} topLevelType Record from `EventConstants`.
227 - * @param {Event} nativeEvent A Standard Event with fixed `target` property.
228 - * @param {Element} renderedTarget Element of interest to the framework, usually
229 - * the same as `nativeEvent.target` but occasionally an element immediately
230 - * above `nativeEvent.target` (the first DOM node recognized as one "rendered"
231 - * by the framework at hand.)
232 - * @param {string} renderedTargetID string ID of `renderedTarget`.
227 + * @param {string} topLevelType Record from `EventConstants`.
228 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
229 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
230 + * @param {object} nativeEvent Native browser event.
231 + * @return {*} An accumulation of `AbstractEvent`s.
232 */
234 -var extractAbstractEvents =
235 - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) {
236 - var abstractEvents;
237 - var plugins = injection.plugins;
238 - var len = plugins.length;
239 - for (var i = 0; i < len; i++) {
240 - // Not every plugin in the ordering may be loaded at runtime.
241 - var possiblePlugin = plugins[i];
242 - var extractedAbstractEvents =
243 - possiblePlugin &&
244 - possiblePlugin.extractAbstractEvents(
245 - topLevelType,
246 - nativeEvent,
247 - renderedTargetID,
248 - renderedTarget
249 - );
233 +var extractAbstractEvents = function(
234 + topLevelType,
235 + topLevelTarget,
236 + topLevelTargetID,
237 + nativeEvent) {
238 + var abstractEvents;
239 + var plugins = injection.plugins;
240 + for (var i = 0, l = plugins.length; i < l; i++) {
241 + // Not every plugin in the ordering may be loaded at runtime.
242 + var possiblePlugin = plugins[i];
243 + if (possiblePlugin) {
244 + var extractedAbstractEvents = possiblePlugin.extractAbstractEvents(
245 + topLevelType,
246 + topLevelTarget,
247 + topLevelTargetID,
248 + nativeEvent
249 + );
250 if (extractedAbstractEvents) {
251 abstractEvents = accumulate(abstractEvents, extractedAbstractEvents);
252 }
253 }
254 - return abstractEvents;
255 - };
254 + }
255 + return abstractEvents;
256 +};
257
258 var enqueueAbstractEvents = function(abstractEvents) {
259 if (abstractEvents) {
src/event/EventPropagators.js
+10 -9
@@ -58,7 +58,7 @@ var injection = {
58 */
59 function listenerAtPhase(id, abstractEvent, propagationPhase) {
60 var registrationName =
61 - abstractEvent.type.phasedRegistrationNames[propagationPhase];
61 + abstractEvent.reactEventType.phasedRegistrationNames[propagationPhase];
62 return getListener(id, registrationName);
63 }
64
@@ -92,9 +92,9 @@ function accumulateDirectionalDispatches(domID, upwards, abstractEvent) {
92 * have a different target.
93 */
94 function accumulateTwoPhaseDispatchesSingle(abstractEvent) {
95 - if (abstractEvent && abstractEvent.type.phasedRegistrationNames) {
95 + if (abstractEvent && abstractEvent.reactEventType.phasedRegistrationNames) {
96 injection.InstanceHandle.traverseTwoPhase(
97 - abstractEvent.abstractTargetID,
97 + abstractEvent.reactTargetID,
98 accumulateDirectionalDispatches,
99 abstractEvent
100 );
@@ -105,11 +105,12 @@ function accumulateTwoPhaseDispatchesSingle(abstractEvent) {
105 /**
106 * Accumulates without regard to direction, does not look for phased
107 * registration names. Same as `accumulateDirectDispatchesSingle` but without
108 - * requiring that the `abstractTargetID` be the same as the dispatched ID.
108 + * requiring that the `reactTargetID` be the same as the dispatched ID.
109 */
110 function accumulateDispatches(id, ignoredDirection, abstractEvent) {
111 - if (abstractEvent && abstractEvent.type.registrationName) {
112 - var listener = getListener(id, abstractEvent.type.registrationName);
111 + if (abstractEvent && abstractEvent.reactEventType.registrationName) {
112 + var registrationName = abstractEvent.reactEventType.registrationName;
113 + var listener = getListener(id, registrationName);
114 if (listener) {
115 abstractEvent._dispatchListeners =
116 accumulate(abstractEvent._dispatchListeners, listener);
@@ -120,12 +121,12 @@ function accumulateDispatches(id, ignoredDirection, abstractEvent) {
121
122 /**
123 * Accumulates dispatches on an `AbstractEvent`, but only for the
123 - * `abstractTargetID`.
124 + * `reactTargetID`.
125 * @param {AbstractEvent} abstractEvent
126 */
127 function accumulateDirectDispatchesSingle(abstractEvent) {
127 - if (abstractEvent && abstractEvent.type.registrationName) {
128 - accumulateDispatches(abstractEvent.abstractTargetID, null, abstractEvent);
128 + if (abstractEvent && abstractEvent.reactEventType.registrationName) {
129 + accumulateDispatches(abstractEvent.reactTargetID, null, abstractEvent);
130 }
131 }
132
src/event/NormalizedEventListener.js deleted
-84
@@ -1,84 +0,0 @@
1 -/**
2 - * Copyright 2013 Facebook, Inc.
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - *
16 - * @providesModule NormalizedEventListener
17 - */
18 -
19 -var EventListener = require('EventListener');
20 -
21 -/**
22 - * @param {?Event} eventParam Event parameter from an attached listener.
23 - * @return {Event} Normalized event object.
24 - * @private
25 - */
26 -function normalizeEvent(eventParam) {
27 - var nativeEvent = eventParam || window.event;
28 - // In some browsers (OLD FF), setting the target throws an error. A good way
29 - // to tell if setting the target will throw an error, is to check if the event
30 - // has a `target` property. Safari events have a `target` but it's not always
31 - // normalized. Even if a `target` property exists, it's good to only set the
32 - // target property if we realize that a change will actually take place.
33 - var hasTargetProperty = 'target' in nativeEvent;
34 - var eventTarget = nativeEvent.target || nativeEvent.srcElement || window;
35 - // Safari may fire events on text nodes (Node.TEXT_NODE is 3)
36 - // @see http://www.quirksmode.org/js/events_properties.html
37 - var textNodeNormalizedTarget =
38 - (eventTarget.nodeType === 3) ? eventTarget.parentNode : eventTarget;
39 - if (!hasTargetProperty || nativeEvent.target !== textNodeNormalizedTarget) {
40 - // TODO: Normalize the object via `merge()` to work with strict mode.
41 - nativeEvent.target = textNodeNormalizedTarget;
42 - }
43 - return nativeEvent;
44 -}
45 -
46 -function createNormalizedCallback(cb) {
47 - return function(unfixedNativeEvent) {
48 - cb(normalizeEvent(unfixedNativeEvent));
49 - };
50 -}
51 -
52 -var NormalizedEventListener = {
53 -
54 - /**
55 - * Listens to bubbled events on a DOM node.
56 - *
57 - * NOTE: The listener will be invoked with a normalized event object.
58 - *
59 - * @param {DOMElement} el DOM element to register listener on.
60 - * @param {string} handlerBaseName Event name, e.g. "click".
61 - * @param {function} cb Callback function.
62 - * @public
63 - */
64 - listen: function(el, handlerBaseName, cb) {
65 - EventListener.listen(el, handlerBaseName, createNormalizedCallback(cb));
66 - },
67 -
68 - /**
69 - * Listens to captured events on a DOM node.
70 - *
71 - * NOTE: The listener will be invoked with a normalized event object.
72 - *
73 - * @param {DOMElement} el DOM element to register listener on.
74 - * @param {string} handlerBaseName Event name, e.g. "click".
75 - * @param {function} cb Callback function.
76 - * @public
77 - */
78 - capture: function(el, handlerBaseName, cb) {
79 - EventListener.capture(el, handlerBaseName, createNormalizedCallback(cb));
80 - }
81 -
82 -};
83 -
84 -module.exports = NormalizedEventListener;
src/eventPlugins/AnalyticsEventPluginFactory.js
+12 -7
@@ -136,20 +136,25 @@ if (__DEV__) {
136 * This plugin does not really extract any abstract events. Rather it just looks
137 * at the top level event and bumps up counters as appropriate
138 *
139 - * @see EventPluginHub.extractAbstractEvents
139 + * @param {string} topLevelType Record from `EventConstants`.
140 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
141 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
142 + * @param {object} nativeEvent Native browser event.
143 + * @return {*} An accumulation of `AbstractEvent`s.
144 + * @see {EventPluginHub.extractAbstractEvents}
145 */
146 function extractAbstractEvents(
147 topLevelType,
143 - nativeEvent,
144 - renderedTargetID,
145 - renderedTarget) {
148 + topLevelTarget,
149 + topLevelTargetID,
150 + nativeEvent) {
151 var currentEvent = topLevelTypesToAnalyticsEvent[topLevelType];
147 - if (!currentEvent || !renderedTarget || !renderedTarget.attributes) {
152 + if (!currentEvent || !topLevelTarget || !topLevelTarget.attributes) {
153 return null;
154 }
155
151 - var analyticsIDAttribute = renderedTarget.attributes[ANALYTICS_ID];
152 - var analyticsEventsAttribute = renderedTarget.attributes[ANALYTICS_EVENTS];
156 + var analyticsIDAttribute = topLevelTarget.attributes[ANALYTICS_ID];
157 + var analyticsEventsAttribute = topLevelTarget.attributes[ANALYTICS_EVENTS];
158 if(!analyticsIDAttribute || !analyticsEventsAttribute) {
159 return null;
160 }
src/eventPlugins/EnterLeaveEventPlugin.js
+26 -20
@@ -14,6 +14,7 @@
14 * limitations under the License.
15 *
16 * @providesModule EnterLeaveEventPlugin
17 + * @typechecks
18 */
19
20 "use strict";
@@ -36,28 +37,32 @@ var abstractEventTypes = {
37 };
38
39 /**
39 - * For almost every interaction we care about, there will be a top level
40 - * `mouseOver` and `mouseOut` event that occur so we can usually only pay
41 - * attention to one of the two (we'll pay attention to the `mouseOut` event) to
42 - * avoid extracting a duplicate event. However, there's one interaction where
43 - * there will be no `mouseOut` event to rely on - mousing from outside the
44 - * browser *into* the chrome. We detect this scenario and only in that case, we
45 - * use the `mouseOver` event.
40 + * For almost every interaction we care about, there will be a top-level
41 + * `mouseover` and `mouseout` event that occurs so only pay attention to one of
42 + * the two (to avoid duplicate events). We use the `mouseout` event.
43 *
47 - * @see EventPluginHub.extractAbstractEvents
44 + * However, there's one interaction where there will be no `mouseout` event to
45 + * rely on - mousing from outside the browser *into* the chrome. We detect this
46 + * scenario and only in that case, we use the `mouseover` event.
47 + *
48 + * @param {string} topLevelType Record from `EventConstants`.
49 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
50 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
51 + * @param {object} nativeEvent Native browser event.
52 + * @return {*} An accumulation of `AbstractEvent`s.
53 + * @see {EventPluginHub.extractAbstractEvents}
54 */
55 var extractAbstractEvents = function(
56 topLevelType,
51 - nativeEvent,
52 - renderedTargetID,
53 - renderedTarget) {
54 -
57 + topLevelTarget,
58 + topLevelTargetID,
59 + nativeEvent) {
60 if (topLevelType === topLevelTypes.topMouseOver &&
61 (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
57 - return;
62 + return null;
63 }
64 if (topLevelType !== topLevelTypes.topMouseOut &&
60 - topLevelType !== topLevelTypes.topMouseOver){
65 + topLevelType !== topLevelTypes.topMouseOver) {
66 return null; // Must not be a mouse in or mouse out - ignoring.
67 }
68
@@ -65,32 +70,33 @@ var extractAbstractEvents = function(
70 if (topLevelType === topLevelTypes.topMouseOut) {
71 to = getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) ||
72 ExecutionEnvironment.global;
68 - from = renderedTarget;
73 + from = topLevelTarget;
74 } else {
70 - to = renderedTarget;
75 + to = topLevelTarget;
76 from = ExecutionEnvironment.global;
77 }
78
79 // Nothing pertains to our managed components.
75 - if (from === to ) {
76 - return;
80 + if (from === to) {
81 + return null;
82 }
83
84 var fromID = from ? getDOMNodeID(from) : '';
85 var toID = to ? getDOMNodeID(to) : '';
86 +
87 var leave = AbstractEvent.getPooled(
88 abstractEventTypes.mouseLeave,
89 fromID,
84 - topLevelType,
90 nativeEvent
91 );
92 var enter = AbstractEvent.getPooled(
93 abstractEventTypes.mouseEnter,
94 toID,
90 - topLevelType,
95 nativeEvent
96 );
97 +
98 EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
99 +
100 return [leave, enter];
101 };
102
src/eventPlugins/ResponderEventPlugin.js
+57 -50
@@ -162,14 +162,13 @@ var abstractEventTypes = {
162 */
163
164 /**
165 - * @param {TopLevelTypes} topLevelType Top level event type being examined.
166 - * @param {DOMEvent} nativeEvent Native DOM event.
165 + * @param {string} topLevelType Record from `EventConstants`.
166 * @param {string} renderedTargetID ID of deepest React rendered element.
168 - *
169 - * @return {Accumulation<AbstractEvent>} Extracted events.
167 + * @param {object} nativeEvent Native browser event.
168 + * @return {*} An accumulation of extracted `AbstractEvent`s.
169 */
170 var setResponderAndExtractTransfer =
172 - function(topLevelType, nativeEvent, renderedTargetID) {
171 + function(topLevelType, renderedTargetID, nativeEvent) {
172 var type;
173 var shouldSetEventType =
174 isStartish(topLevelType) ? abstractEventTypes.startShouldSetResponder :
@@ -235,7 +234,6 @@ var setResponderAndExtractTransfer =
234 return extracted;
235 };
236
238 -
237 /**
238 * A transfer is a negotiation between a currently set responder and the next
239 * element to claim responder status. Any start event could trigger a transfer
@@ -253,51 +251,60 @@ function canTriggerTransfer(topLevelType) {
251 (isPressing && isMoveish(topLevelType));
252 }
253
256 -var extractAbstractEvents =
257 - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) {
258 - var extracted;
259 - // Must have missed an end event - reset the state here.
260 - if (responderID && isStartish(topLevelType)) {
261 - responderID = null;
262 - }
263 - if (isStartish(topLevelType)) {
264 - isPressing = true;
265 - } else if (isEndish(topLevelType)) {
266 - isPressing = false;
267 - }
268 - if (canTriggerTransfer(topLevelType)) {
269 - var transfer = setResponderAndExtractTransfer(
270 - topLevelType,
271 - nativeEvent,
272 - renderedTargetID,
273 - renderedTarget
274 - );
275 - if (transfer) {
276 - extracted = accumulate(extracted, transfer);
277 - }
278 - }
279 - // Now that we know the responder is set correctly, we can dispatch
280 - // responder type events (directly to the responder).
281 - var type = isMoveish(topLevelType) ? abstractEventTypes.responderMove :
282 - isEndish(topLevelType) ? abstractEventTypes.responderRelease :
283 - isStartish(topLevelType) ? abstractEventTypes.responderStart : null;
284 - if (type) {
285 - var data = AbstractEvent.normalizePointerData(nativeEvent);
286 - var gesture = AbstractEvent.getPooled(
287 - type,
288 - responderID,
289 - topLevelType,
290 - nativeEvent,
291 - data
292 - );
293 - EventPropagators.accumulateDirectDispatches(gesture);
294 - extracted = accumulate(extracted, gesture);
295 - }
296 - if (type === abstractEventTypes.responderRelease) {
297 - responderID = null;
254 +/**
255 + * @param {string} topLevelType Record from `EventConstants`.
256 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
257 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
258 + * @param {object} nativeEvent Native browser event.
259 + * @return {*} An accumulation of `AbstractEvent`s.
260 + * @see {EventPluginHub.extractAbstractEvents}
261 + */
262 +var extractAbstractEvents = function(
263 + topLevelType,
264 + topLevelTarget,
265 + topLevelTargetID,
266 + nativeEvent) {
267 + var extracted;
268 + // Must have missed an end event - reset the state here.
269 + if (responderID && isStartish(topLevelType)) {
270 + responderID = null;
271 + }
272 + if (isStartish(topLevelType)) {
273 + isPressing = true;
274 + } else if (isEndish(topLevelType)) {
275 + isPressing = false;
276 + }
277 + if (canTriggerTransfer(topLevelType)) {
278 + var transfer = setResponderAndExtractTransfer(
279 + topLevelType,
280 + topLevelTargetID,
281 + nativeEvent
282 + );
283 + if (transfer) {
284 + extracted = accumulate(extracted, transfer);
285 }
299 - return extracted;
300 - };
286 + }
287 + // Now that we know the responder is set correctly, we can dispatch
288 + // responder type events (directly to the responder).
289 + var type = isMoveish(topLevelType) ? abstractEventTypes.responderMove :
290 + isEndish(topLevelType) ? abstractEventTypes.responderRelease :
291 + isStartish(topLevelType) ? abstractEventTypes.responderStart : null;
292 + if (type) {
293 + var data = AbstractEvent.normalizePointerData(nativeEvent);
294 + var gesture = AbstractEvent.getPooled(
295 + type,
296 + responderID,
297 + nativeEvent,
298 + data
299 + );
300 + EventPropagators.accumulateDirectDispatches(gesture);
301 + extracted = accumulate(extracted, gesture);
302 + }
303 + if (type === abstractEventTypes.responderRelease) {
304 + responderID = null;
305 + }
306 + return extracted;
307 +};
308
309 /**
310 * Event plugin for formalizing the negotiation between claiming locks on
src/eventPlugins/SimpleEventPlugin.js
+48 -40
@@ -155,6 +155,7 @@ var SimpleEventPlugin = {
155 /**
156 * Same as the default implementation, except cancels the event when return
157 * value is false.
158 + *
159 * @param {AbstractEvent} AbstractEvent to handle
160 * @param {function} Application-level callback
161 * @param {string} domID DOM id to pass to the callback.
@@ -168,48 +169,55 @@ var SimpleEventPlugin = {
169 },
170
171 /**
171 - * @see EventPluginHub.extractAbstractEvents
172 + * @param {string} topLevelType Record from `EventConstants`.
173 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
174 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
175 + * @param {object} nativeEvent Native browser event.
176 + * @return {*} An accumulation of `AbstractEvent`s.
177 + * @see {EventPluginHub.extractAbstractEvents}
178 */
173 - extractAbstractEvents:
174 - function(topLevelType, nativeEvent, renderedTargetID, renderedTarget) {
175 - var data;
176 - var abstractEventType =
177 - SimpleEventPlugin.topLevelTypesToAbstract[topLevelType];
178 - if (!abstractEventType) {
179 - return null;
180 - }
181 - switch(topLevelType) {
182 - case topLevelTypes.topMouseWheel:
183 - data = AbstractEvent.normalizeMouseWheelData(nativeEvent);
184 - break;
185 - case topLevelTypes.topScroll:
186 - data = AbstractEvent.normalizeScrollDataFromTarget(renderedTarget);
187 - break;
188 - case topLevelTypes.topClick:
189 - case topLevelTypes.topDoubleClick:
190 - case topLevelTypes.topChange:
191 - case topLevelTypes.topDOMCharacterDataModified:
192 - case topLevelTypes.topMouseDown:
193 - case topLevelTypes.topMouseUp:
194 - case topLevelTypes.topMouseMove:
195 - case topLevelTypes.topTouchMove:
196 - case topLevelTypes.topTouchStart:
197 - case topLevelTypes.topTouchEnd:
198 - data = AbstractEvent.normalizePointerData(nativeEvent);
199 - break;
200 - default:
201 - data = null;
202 - }
203 - var abstractEvent = AbstractEvent.getPooled(
204 - abstractEventType,
205 - renderedTargetID,
206 - topLevelType,
207 - nativeEvent,
208 - data
209 - );
210 - EventPropagators.accumulateTwoPhaseDispatches(abstractEvent);
211 - return abstractEvent;
179 + extractAbstractEvents: function(
180 + topLevelType,
181 + topLevelTarget,
182 + topLevelTargetID,
183 + nativeEvent) {
184 + var data;
185 + var abstractEventType =
186 + SimpleEventPlugin.topLevelTypesToAbstract[topLevelType];
187 + if (!abstractEventType) {
188 + return null;
189 }
190 + switch(topLevelType) {
191 + case topLevelTypes.topMouseWheel:
192 + data = AbstractEvent.normalizeMouseWheelData(nativeEvent);
193 + break;
194 + case topLevelTypes.topScroll:
195 + data = AbstractEvent.normalizeScrollDataFromTarget(topLevelTarget);
196 + break;
197 + case topLevelTypes.topClick:
198 + case topLevelTypes.topDoubleClick:
199 + case topLevelTypes.topChange:
200 + case topLevelTypes.topDOMCharacterDataModified:
201 + case topLevelTypes.topMouseDown:
202 + case topLevelTypes.topMouseUp:
203 + case topLevelTypes.topMouseMove:
204 + case topLevelTypes.topTouchMove:
205 + case topLevelTypes.topTouchStart:
206 + case topLevelTypes.topTouchEnd:
207 + data = AbstractEvent.normalizePointerData(nativeEvent);
208 + break;
209 + default:
210 + data = null;
211 + }
212 + var abstractEvent = AbstractEvent.getPooled(
213 + abstractEventType,
214 + topLevelTargetID,
215 + nativeEvent,
216 + data
217 + );
218 + EventPropagators.accumulateTwoPhaseDispatches(abstractEvent);
219 + return abstractEvent;
220 + }
221 };
222
223 SimpleEventPlugin.topLevelTypesToAbstract = {
src/eventPlugins/TapEventPlugin.js
+12 -10
@@ -14,6 +14,7 @@
14 * limitations under the License.
15 *
16 * @providesModule TapEventPlugin
17 + * @typechecks
18 */
19
20 "use strict";
@@ -46,26 +47,27 @@ var abstractEventTypes = {
47 };
48
49 /**
49 - * @see EventPluginHub.extractAbstractEvents
50 + * @param {string} topLevelType Record from `EventConstants`.
51 + * @param {DOMEventTarget} topLevelTarget The listening component root node.
52 + * @param {string} topLevelTargetID ID of `topLevelTarget`.
53 + * @param {object} nativeEvent Native browser event.
54 + * @return {*} An accumulation of `AbstractEvent`s.
55 + * @see {EventPluginHub.extractAbstractEvents}
56 */
57 var extractAbstractEvents = function(
58 topLevelType,
53 - nativeEvent,
54 - renderedTargetID,
55 - renderedTarget) {
56 -
59 + topLevelTarget,
60 + topLevelTargetID,
61 + nativeEvent) {
62 if (!isStartish(topLevelType) && !isEndish(topLevelType)) {
63 return;
64 }
65 var abstractEvent;
66 var dist = eventDistance(startCoords, nativeEvent);
67 if (isEndish(topLevelType) && dist < tapMoveThreshold) {
63 - var type = abstractEventTypes.touchTap;
64 - var abstractTargetID = renderedTargetID;
68 abstractEvent = AbstractEvent.getPooled(
66 - type,
67 - abstractTargetID,
68 - topLevelType,
69 + abstractEventTypes.touchTap,
70 + topLevelTargetID,
71 nativeEvent
72 );
73 }
src/eventPlugins/__tests__/ResponderEventPlugin-test.js
+12 -8
@@ -167,8 +167,9 @@ var existsInExtraction = function(extracted, test) {
167 function assertGrantEvent(id, extracted) {
168 var test = function(abstractEvent) {
169 return abstractEvent instanceof AbstractEvent &&
170 - abstractEvent.type === responderAbstractEventTypes.responderGrant &&
171 - abstractEvent.abstractTargetID === id;
170 + abstractEvent.reactEventType ===
171 + responderAbstractEventTypes.responderGrant &&
172 + abstractEvent.reactTargetID === id;
173 };
174 expect(ResponderEventPlugin.getResponderID()).toBe(id);
175 expect(existsInExtraction(extracted, test)).toBe(true);
@@ -177,8 +178,9 @@ function assertGrantEvent(id, extracted) {
178 function assertResponderMoveEvent(id, extracted) {
179 var test = function(abstractEvent) {
180 return abstractEvent instanceof AbstractEvent &&
180 - abstractEvent.type === responderAbstractEventTypes.responderMove &&
181 - abstractEvent.abstractTargetID === id;
181 + abstractEvent.reactEventType ===
182 + responderAbstractEventTypes.responderMove &&
183 + abstractEvent.reactTargetID === id;
184 };
185 expect(ResponderEventPlugin.getResponderID()).toBe(id);
186 expect(existsInExtraction(extracted, test)).toBe(true);
@@ -187,8 +189,9 @@ function assertResponderMoveEvent(id, extracted) {
189 function assertTerminateEvent(id, extracted) {
190 var test = function(abstractEvent) {
191 return abstractEvent instanceof AbstractEvent &&
190 - abstractEvent.type === responderAbstractEventTypes.responderTerminate &&
191 - abstractEvent.abstractTargetID === id;
192 + abstractEvent.reactEventType ===
193 + responderAbstractEventTypes.responderTerminate &&
194 + abstractEvent.reactTargetID === id;
195 };
196 expect(ResponderEventPlugin.getResponderID()).not.toBe(id);
197 expect(existsInExtraction(extracted, test)).toBe(true);
@@ -197,8 +200,9 @@ function assertTerminateEvent(id, extracted) {
200 function assertRelease(id, extracted) {
201 var test = function(abstractEvent) {
202 return abstractEvent instanceof AbstractEvent &&
200 - abstractEvent.type === responderAbstractEventTypes.responderRelease &&
201 - abstractEvent.abstractTargetID === id;
203 + abstractEvent.reactEventType ===
204 + responderAbstractEventTypes.responderRelease &&
205 + abstractEvent.reactTargetID === id;
206 };
207 expect(ResponderEventPlugin.getResponderID()).toBe(null);
208 expect(existsInExtraction(extracted, test)).toBe(true);