@samitouri / QOS-React-2 / commits / e1c2d02fdd

Create ReactEventListener

Per our discussion friday, this creates ReactEventListener and renames variables to use the "handle" nomenclature.

Pete Hunt committed Jun 14, 2014 at 20:16 UTC e1c2d02fddb40cb0b246d2c2da97b41b6ba378bc
15 files changed +348 -282
src/browser/ReactBrowserEventEmitter.js renamed
+102 -89
@@ -13,29 +13,31 @@
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 - * @providesModule ReactEventEmitter
16 + * @providesModule ReactBrowserEventEmitter
17 * @typechecks static-only
18 */
19
20 "use strict";
21
22 var EventConstants = require('EventConstants');
23 -var EventListener = require('EventListener');
23 var EventPluginHub = require('EventPluginHub');
24 var EventPluginRegistry = require('EventPluginRegistry');
26 -var ExecutionEnvironment = require('ExecutionEnvironment');
25 var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
26 var ViewportMetrics = require('ViewportMetrics');
27
30 -var invariant = require('invariant');
28 var isEventSupported = require('isEventSupported');
29 var merge = require('merge');
30
31 /**
35 - * Summary of `ReactEventEmitter` event handling:
32 + * Summary of `ReactBrowserEventEmitter` event handling:
33 *
37 - * - Top-level delegation is used to trap native browser events. We normalize
38 - * and de-duplicate events to account for browser quirks.
34 + * - Top-level delegation is used to trap most native browser events. This
35 + * may only occur in the main thread and is the responsibility of
36 + * ReactEventListener, which is injected and can therefore support pluggable
37 + * event sources. This is the only work that occurs in the main thread.
38 + *
39 + * - We normalize and de-duplicate events to account for browser quirks. This
40 + * may be done in the worker thread.
41 *
42 * - Forward these native events (with the associated top-level type used to
43 * trap it) to `EventPluginHub`, which in turn will ask plugins if they want
@@ -48,11 +50,16 @@ var merge = require('merge');
50 *
51 * Overview of React and the event system:
52 *
51 - * .
53 * +------------+ .
54 * | DOM | .
55 + * +------------+ .
56 + * | .
57 + * v .
58 + * +------------+ .
59 + * | ReactEvent | .
60 + * | Listener | .
61 * +------------+ . +-----------+
55 - * + . +--------+|SimpleEvent|
62 + * | . +--------+|SimpleEvent|
63 * | . | |Plugin |
64 * +-----|------+ . v +-----------+
65 * | | | . +--------------+ +------------+
@@ -139,68 +146,31 @@ function getListeningForDocument(mountAt) {
146 }
147
148 /**
142 - * Traps top-level events by using event bubbling.
149 + * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
150 + * example:
151 *
144 - * @param {string} topLevelType Record from `EventConstants`.
145 - * @param {string} handlerBaseName Event name (e.g. "click").
146 - * @param {DOMEventTarget} element Element on which to attach listener.
147 - * @return {object} An object with a remove function which will forcefully
148 - * remove the listener.
149 - * @internal
150 - */
151 -function trapBubbledEvent(topLevelType, handlerBaseName, element) {
152 - return EventListener.listen(
153 - element,
154 - handlerBaseName,
155 - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
156 - topLevelType
157 - )
158 - );
159 -}
160 -
161 -/**
162 - * Traps a top-level event by using event capturing.
163 - *
164 - * @param {string} topLevelType Record from `EventConstants`.
165 - * @param {string} handlerBaseName Event name (e.g. "click").
166 - * @param {DOMEventTarget} element Element on which to attach listener.
167 - * @return {object} An object with a remove function which will forcefully
168 - * remove the listener.
169 - * @internal
170 - */
171 -function trapCapturedEvent(topLevelType, handlerBaseName, element) {
172 - return EventListener.capture(
173 - element,
174 - handlerBaseName,
175 - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
176 - topLevelType
177 - )
178 - );
179 -}
180 -
181 -/**
182 - * `ReactEventEmitter` is used to attach top-level event listeners. For example:
183 - *
184 - * ReactEventEmitter.putListener('myID', 'onClick', myFunction);
152 + * ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction);
153 *
154 * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
155 *
156 * @internal
157 */
190 -var ReactEventEmitter = merge(ReactEventEmitterMixin, {
158 +var ReactBrowserEventEmitter = merge(ReactEventEmitterMixin, {
159
160 /**
193 - * React references `ReactEventTopLevelCallback` using this property in order
194 - * to allow dependency injection.
161 + * Injectable event backend
162 */
196 - TopLevelCallbackCreator: null,
163 + ReactEventListener: null,
164
165 injection: {
166 /**
200 - * @param {function} TopLevelCallbackCreator
167 + * @param {object} ReactEventListener
168 */
202 - injectTopLevelCallbackCreator: function(TopLevelCallbackCreator) {
203 - ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
169 + injectReactEventListener: function(ReactEventListener) {
170 + ReactEventListener.setHandleTopLevel(
171 + ReactBrowserEventEmitter.handleTopLevel
172 + );
173 + ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
174 }
175 },
176
@@ -210,13 +180,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
180 * @param {boolean} enabled True if callbacks should be enabled.
181 */
182 setEnabled: function(enabled) {
213 - invariant(
214 - ExecutionEnvironment.canUseDOM,
215 - 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' +
216 - 'This is likely a bug in the framework. Please report immediately.'
217 - );
218 - if (ReactEventEmitter.TopLevelCallbackCreator) {
219 - ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
183 + if (ReactBrowserEventEmitter.ReactEventListener) {
184 + ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);
185 }
186 },
187
@@ -225,8 +190,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
190 */
191 isEnabled: function() {
192 return !!(
228 - ReactEventEmitter.TopLevelCallbackCreator &&
229 - ReactEventEmitter.TopLevelCallbackCreator.isEnabled()
193 + ReactBrowserEventEmitter.ReactEventListener &&
194 + ReactBrowserEventEmitter.ReactEventListener.isEnabled()
195 );
196 },
197
@@ -249,10 +214,10 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
214 * they bubble to document.
215 *
216 * @param {string} registrationName Name of listener (e.g. `onClick`).
252 - * @param {DOMDocument} contentDocument Document which owns the container
217 + * @param {object} contentDocumentHandle Document which owns the container
218 */
254 - listenTo: function(registrationName, contentDocument) {
255 - var mountAt = contentDocument;
219 + listenTo: function(registrationName, contentDocumentHandle) {
220 + var mountAt = contentDocumentHandle;
221 var isListening = getListeningForDocument(mountAt);
222 var dependencies = EventPluginRegistry.
223 registrationNameDependencies[registrationName];
@@ -268,42 +233,79 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
233
234 if (topLevelType === topLevelTypes.topWheel) {
235 if (isEventSupported('wheel')) {
271 - trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt);
236 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
237 + topLevelTypes.topWheel,
238 + 'wheel',
239 + mountAt
240 + );
241 } else if (isEventSupported('mousewheel')) {
273 - trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt);
242 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
243 + topLevelTypes.topWheel,
244 + 'mousewheel',
245 + mountAt
246 + );
247 } else {
248 // Firefox needs to capture a different mouse scroll event.
249 // @see http://www.quirksmode.org/dom/events/tests/scroll.html
277 - trapBubbledEvent(
250 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
251 topLevelTypes.topWheel,
252 'DOMMouseScroll',
280 - mountAt);
253 + mountAt
254 + );
255 }
256 } else if (topLevelType === topLevelTypes.topScroll) {
257
258 if (isEventSupported('scroll', true)) {
285 - trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
259 + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
260 + topLevelTypes.topScroll,
261 + 'scroll',
262 + mountAt
263 + );
264 } else {
287 - trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window);
265 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
266 + topLevelTypes.topScroll,
267 + 'scroll',
268 + ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE
269 + );
270 }
271 } else if (topLevelType === topLevelTypes.topFocus ||
272 topLevelType === topLevelTypes.topBlur) {
273
274 if (isEventSupported('focus', true)) {
293 - trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
294 - trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
275 + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
276 + topLevelTypes.topFocus,
277 + 'focus',
278 + mountAt
279 + );
280 + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
281 + topLevelTypes.topBlur,
282 + 'blur',
283 + mountAt
284 + );
285 } else if (isEventSupported('focusin')) {
286 // IE has `focusin` and `focusout` events which bubble.
287 // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
298 - trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
299 - trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
288 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
289 + topLevelTypes.topFocus,
290 + 'focusin',
291 + mountAt
292 + );
293 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
294 + topLevelTypes.topBlur,
295 + 'focusout',
296 + mountAt
297 + );
298 }
299
300 // to make sure blur and focus event listeners are only attached once
301 isListening[topLevelTypes.topBlur] = true;
302 isListening[topLevelTypes.topFocus] = true;
303 } else if (topEventMapping.hasOwnProperty(dependency)) {
306 - trapBubbledEvent(topLevelType, topEventMapping[dependency], mountAt);
304 + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
305 + topLevelType,
306 + topEventMapping[dependency],
307 + mountAt
308 + );
309 }
310
311 isListening[dependency] = true;
@@ -311,6 +313,22 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
313 }
314 },
315
316 + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
317 + return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
318 + topLevelType,
319 + handlerBaseName,
320 + handle
321 + );
322 + },
323 +
324 + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
325 + return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
326 + topLevelType,
327 + handlerBaseName,
328 + handle
329 + );
330 + },
331 +
332 /**
333 * Listens to window scroll and resize events. We cache scroll values so that
334 * application code can access them without triggering reflows.
@@ -322,8 +340,7 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
340 ensureScrollValueMonitoring: function(){
341 if (!isMonitoringScrollValue) {
342 var refresh = ViewportMetrics.refreshScrollValues;
325 - EventListener.listen(window, 'scroll', refresh);
326 - EventListener.listen(window, 'resize', refresh);
343 + ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
344 isMonitoringScrollValue = true;
345 }
346 },
@@ -338,12 +355,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
355
356 deleteListener: EventPluginHub.deleteListener,
357
341 - deleteAllListeners: EventPluginHub.deleteAllListeners,
342 -
343 - trapBubbledEvent: trapBubbledEvent,
344 -
345 - trapCapturedEvent: trapCapturedEvent
358 + deleteAllListeners: EventPluginHub.deleteAllListeners
359
360 });
361
349 -module.exports = ReactEventEmitter;
362 +module.exports = ReactBrowserEventEmitter;
src/browser/ReactPutListenerQueue.js
+2 -2
@@ -19,7 +19,7 @@
19 "use strict";
20
21 var PooledClass = require('PooledClass');
22 -var ReactEventEmitter = require('ReactEventEmitter');
22 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
23
24 var mixInto = require('mixInto');
25
@@ -39,7 +39,7 @@ mixInto(ReactPutListenerQueue, {
39 putListeners: function() {
40 for (var i = 0; i < this.listenersToPut.length; i++) {
41 var listenerToPut = this.listenersToPut[i];
42 - ReactEventEmitter.putListener(
42 + ReactBrowserEventEmitter.putListener(
43 listenerToPut.rootNodeID,
44 listenerToPut.propKey,
45 listenerToPut.propValue
src/browser/ReactReconcileTransaction.js
+9 -8
@@ -21,7 +21,7 @@
21
22 var CallbackQueue = require('CallbackQueue');
23 var PooledClass = require('PooledClass');
24 -var ReactEventEmitter = require('ReactEventEmitter');
24 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
25 var ReactInputSelection = require('ReactInputSelection');
26 var ReactPutListenerQueue = require('ReactPutListenerQueue');
27 var Transaction = require('Transaction');
@@ -50,21 +50,22 @@ var SELECTION_RESTORATION = {
50 */
51 var EVENT_SUPPRESSION = {
52 /**
53 - * @return {boolean} The enabled status of `ReactEventEmitter` before the
54 - * reconciliation.
53 + * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
54 + * the reconciliation.
55 */
56 initialize: function() {
57 - var currentlyEnabled = ReactEventEmitter.isEnabled();
58 - ReactEventEmitter.setEnabled(false);
57 + var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
58 + ReactBrowserEventEmitter.setEnabled(false);
59 return currentlyEnabled;
60 },
61
62 /**
63 - * @param {boolean} previouslyEnabled Enabled status of `ReactEventEmitter`
64 - * before the reconciliation occured. `close` restores the previous value.
63 + * @param {boolean} previouslyEnabled Enabled status of
64 + * `ReactBrowserEventEmitter` before the reconciliation occured. `close`
65 + * restores the previous value.
66 */
67 close: function(previouslyEnabled) {
67 - ReactEventEmitter.setEnabled(previouslyEnabled);
68 + ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
69 }
70 };
71
src/browser/__tests__/ReactBrowserEventEmitter-test.js renamed
+73 -57
@@ -21,7 +21,7 @@
21 require('mock-modules')
22 .dontMock('EventPluginHub')
23 .dontMock('ReactMount')
24 - .dontMock('ReactEventEmitter')
24 + .dontMock('ReactBrowserEventEmitter')
25 .dontMock('ReactInstanceHandles')
26 .dontMock('EventPluginHub')
27 .dontMock('TapEventPlugin')
@@ -42,7 +42,7 @@ var setID = function(el, id) {
42 var oldGetNode = ReactMount.getNode;
43
44 var EventPluginHub;
45 -var ReactEventEmitter;
45 +var ReactBrowserEventEmitter;
46 var ReactTestUtils;
47 var TapEventPlugin;
48 var EventListener;
@@ -67,12 +67,12 @@ var ON_CHANGE_KEY = keyOf({onChange: null});
67
68
69 /**
70 - * Since `ReactEventEmitter` is fairly well separated from the DOM, we can test
71 - * almost all of `ReactEventEmitter` without ever rendering anything in the DOM.
72 - * As long as we provide IDs that follow `React's` conventional id namespace
73 - * hierarchy. The only reason why we create these DOM nodes, is so that when we
74 - * feed them into `ReactEventEmitter` (through `ReactTestUtils`), the event
75 - * handlers may receive a DOM node to inspect.
70 + * Since `ReactBrowserEventEmitter` is fairly well separated from the DOM, we
71 + * can test almost all of `ReactBrowserEventEmitter` without ever rendering
72 + * anything in the DOM. As long as we provide IDs that follow `React's`
73 + * conventional id namespace hierarchy. The only reason why we create these DOM
74 + * nodes is so that when we feed them into `ReactBrowserEventEmitter` (through
75 + * `ReactTestUtils`), the event handlers may receive a DOM node to inspect.
76 */
77 var CHILD = document.createElement('div');
78 var PARENT = document.createElement('div');
@@ -82,14 +82,15 @@ setID(PARENT, '.0.0.0');
82 setID(GRANDPARENT, '.0.0');
83
84 function registerSimpleTestHandler() {
85 - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER);
86 - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
85 + ReactBrowserEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER);
86 + var listener =
87 + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
88 expect(listener).toEqual(LISTENER);
88 - return ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
89 + return ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
90 }
91
92
92 -describe('ReactEventEmitter', function() {
93 +describe('ReactBrowserEventEmitter', function() {
94 beforeEach(function() {
95 require('mock-modules').dumpCache();
96 LISTENER.mockClear();
@@ -97,7 +98,7 @@ describe('ReactEventEmitter', function() {
98 TapEventPlugin = require('TapEventPlugin');
99 ReactMount = require('ReactMount');
100 EventListener = require('EventListener');
100 - ReactEventEmitter = require('ReactEventEmitter');
101 + ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
102 ReactTestUtils = require('ReactTestUtils');
103 ReactMount.getNode = function(id) {
104 return idToNode[id];
@@ -115,20 +116,23 @@ describe('ReactEventEmitter', function() {
116
117 it('should store a listener correctly', function() {
118 registerSimpleTestHandler();
118 - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
119 + var listener =
120 + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
121 expect(listener).toBe(LISTENER);
122 });
123
124 it('should retrieve a listener correctly', function() {
125 registerSimpleTestHandler();
124 - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
126 + var listener =
127 + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
128 expect(listener).toEqual(LISTENER);
129 });
130
131 it('should clear all handlers when asked to', function() {
132 registerSimpleTestHandler();
130 - ReactEventEmitter.deleteAllListeners(getID(CHILD));
131 - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
133 + ReactBrowserEventEmitter.deleteAllListeners(getID(CHILD));
134 + var listener =
135 + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
136 expect(listener).toBe(undefined);
137 });
138
@@ -138,28 +142,31 @@ describe('ReactEventEmitter', function() {
142 expect(LISTENER.mock.calls.length).toBe(1);
143 });
144
141 - it('should not invoke handlers if ReactEventEmitter is disabled', function() {
142 - registerSimpleTestHandler();
143 - ReactEventEmitter.setEnabled(false);
144 - ReactTestUtils.SimulateNative.click(CHILD);
145 - expect(LISTENER.mock.calls.length).toBe(0);
146 - ReactEventEmitter.setEnabled(true);
147 - ReactTestUtils.SimulateNative.click(CHILD);
148 - expect(LISTENER.mock.calls.length).toBe(1);
149 - });
145 + it(
146 + 'should not invoke handlers if ReactBrowserEventEmitter is disabled',
147 + function() {
148 + registerSimpleTestHandler();
149 + ReactBrowserEventEmitter.setEnabled(false);
150 + ReactTestUtils.SimulateNative.click(CHILD);
151 + expect(LISTENER.mock.calls.length).toBe(0);
152 + ReactBrowserEventEmitter.setEnabled(true);
153 + ReactTestUtils.SimulateNative.click(CHILD);
154 + expect(LISTENER.mock.calls.length).toBe(1);
155 + }
156 + );
157
158 it('should bubble simply', function() {
152 - ReactEventEmitter.putListener(
159 + ReactBrowserEventEmitter.putListener(
160 getID(CHILD),
161 ON_CLICK_KEY,
162 recordID.bind(null, getID(CHILD))
163 );
157 - ReactEventEmitter.putListener(
164 + ReactBrowserEventEmitter.putListener(
165 getID(PARENT),
166 ON_CLICK_KEY,
167 recordID.bind(null, getID(PARENT))
168 );
162 - ReactEventEmitter.putListener(
169 + ReactBrowserEventEmitter.putListener(
170 getID(GRANDPARENT),
171 ON_CLICK_KEY,
172 recordID.bind(null, getID(GRANDPARENT))
@@ -172,7 +179,7 @@ describe('ReactEventEmitter', function() {
179 });
180
181 it('should set currentTarget', function() {
175 - ReactEventEmitter.putListener(
182 + ReactBrowserEventEmitter.putListener(
183 getID(CHILD),
184 ON_CLICK_KEY,
185 function(event) {
@@ -180,7 +187,7 @@ describe('ReactEventEmitter', function() {
187 expect(event.currentTarget).toBe(CHILD);
188 }
189 );
183 - ReactEventEmitter.putListener(
190 + ReactBrowserEventEmitter.putListener(
191 getID(PARENT),
192 ON_CLICK_KEY,
193 function(event) {
@@ -188,7 +195,7 @@ describe('ReactEventEmitter', function() {
195 expect(event.currentTarget).toBe(PARENT);
196 }
197 );
191 - ReactEventEmitter.putListener(
198 + ReactBrowserEventEmitter.putListener(
199 getID(GRANDPARENT),
200 ON_CLICK_KEY,
201 function(event) {
@@ -204,17 +211,17 @@ describe('ReactEventEmitter', function() {
211 });
212
213 it('should support stopPropagation()', function() {
207 - ReactEventEmitter.putListener(
214 + ReactBrowserEventEmitter.putListener(
215 getID(CHILD),
216 ON_CLICK_KEY,
217 recordID.bind(null, getID(CHILD))
218 );
212 - ReactEventEmitter.putListener(
219 + ReactBrowserEventEmitter.putListener(
220 getID(PARENT),
221 ON_CLICK_KEY,
222 recordIDAndStopPropagation.bind(null, getID(PARENT))
223 );
217 - ReactEventEmitter.putListener(
224 + ReactBrowserEventEmitter.putListener(
225 getID(GRANDPARENT),
226 ON_CLICK_KEY,
227 recordID.bind(null, getID(GRANDPARENT))
@@ -226,17 +233,17 @@ describe('ReactEventEmitter', function() {
233 });
234
235 it('should stop after first dispatch if stopPropagation', function() {
229 - ReactEventEmitter.putListener(
236 + ReactBrowserEventEmitter.putListener(
237 getID(CHILD),
238 ON_CLICK_KEY,
239 recordIDAndStopPropagation.bind(null, getID(CHILD))
240 );
234 - ReactEventEmitter.putListener(
241 + ReactBrowserEventEmitter.putListener(
242 getID(PARENT),
243 ON_CLICK_KEY,
244 recordID.bind(null, getID(PARENT))
245 );
239 - ReactEventEmitter.putListener(
246 + ReactBrowserEventEmitter.putListener(
247 getID(GRANDPARENT),
248 ON_CLICK_KEY,
249 recordID.bind(null, getID(GRANDPARENT))
@@ -247,17 +254,17 @@ describe('ReactEventEmitter', function() {
254 });
255
256 it('should stopPropagation if false is returned', function() {
250 - ReactEventEmitter.putListener(
257 + ReactBrowserEventEmitter.putListener(
258 getID(CHILD),
259 ON_CLICK_KEY,
260 recordIDAndReturnFalse.bind(null, getID(CHILD))
261 );
255 - ReactEventEmitter.putListener(
262 + ReactBrowserEventEmitter.putListener(
263 getID(PARENT),
264 ON_CLICK_KEY,
265 recordID.bind(null, getID(PARENT))
266 );
260 - ReactEventEmitter.putListener(
267 + ReactBrowserEventEmitter.putListener(
268 getID(GRANDPARENT),
269 ON_CLICK_KEY,
270 recordID.bind(null, getID(GRANDPARENT))
@@ -279,10 +286,14 @@ describe('ReactEventEmitter', function() {
286 it('should invoke handlers that were removed while bubbling', function() {
287 var handleParentClick = mocks.getMockFunction();
288 var handleChildClick = function(event) {
282 - ReactEventEmitter.deleteAllListeners(getID(PARENT));
289 + ReactBrowserEventEmitter.deleteAllListeners(getID(PARENT));
290 };
284 - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick);
285 - ReactEventEmitter.putListener(
291 + ReactBrowserEventEmitter.putListener(
292 + getID(CHILD),
293 + ON_CLICK_KEY,
294 + handleChildClick
295 + );
296 + ReactBrowserEventEmitter.putListener(
297 getID(PARENT),
298 ON_CLICK_KEY,
299 handleParentClick
@@ -294,19 +305,23 @@ describe('ReactEventEmitter', function() {
305 it('should not invoke newly inserted handlers while bubbling', function() {
306 var handleParentClick = mocks.getMockFunction();
307 var handleChildClick = function(event) {
297 - ReactEventEmitter.putListener(
308 + ReactBrowserEventEmitter.putListener(
309 getID(PARENT),
310 ON_CLICK_KEY,
311 handleParentClick
312 );
313 };
303 - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick);
314 + ReactBrowserEventEmitter.putListener(
315 + getID(CHILD),
316 + ON_CLICK_KEY,
317 + handleChildClick
318 + );
319 ReactTestUtils.Simulate.click(CHILD);
320 expect(handleParentClick.mock.calls.length).toBe(0);
321 });
322
323 it('should infer onTouchTap from a touchStart/End', function() {
309 - ReactEventEmitter.putListener(
324 + ReactBrowserEventEmitter.putListener(
325 getID(CHILD),
326 ON_TOUCH_TAP_KEY,
327 recordID.bind(null, getID(CHILD))
@@ -324,7 +339,7 @@ describe('ReactEventEmitter', function() {
339 });
340
341 it('should infer onTouchTap from when dragging below threshold', function() {
327 - ReactEventEmitter.putListener(
342 + ReactBrowserEventEmitter.putListener(
343 getID(CHILD),
344 ON_TOUCH_TAP_KEY,
345 recordID.bind(null, getID(CHILD))
@@ -342,7 +357,7 @@ describe('ReactEventEmitter', function() {
357 });
358
359 it('should not onTouchTap from when dragging beyond threshold', function() {
345 - ReactEventEmitter.putListener(
360 + ReactBrowserEventEmitter.putListener(
361 getID(CHILD),
362 ON_TOUCH_TAP_KEY,
363 recordID.bind(null, getID(CHILD))
@@ -360,15 +375,15 @@ describe('ReactEventEmitter', function() {
375
376 it('should listen to events only once', function() {
377 spyOn(EventListener, 'listen');
363 - ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
364 - ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
378 + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
379 + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
380 expect(EventListener.listen.callCount).toBe(1);
381 });
382
383 it('should work with event plugins without dependencies', function() {
384 spyOn(EventListener, 'listen');
385
371 - ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
386 + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
387
388 expect(EventListener.listen.argsForCall[0][1]).toBe('click');
389 });
@@ -377,7 +392,7 @@ describe('ReactEventEmitter', function() {
392 spyOn(EventListener, 'listen');
393 spyOn(EventListener, 'capture');
394
380 - ReactEventEmitter.listenTo(ON_CHANGE_KEY, document);
395 + ReactBrowserEventEmitter.listenTo(ON_CHANGE_KEY, document);
396
397 var setEventListeners = [];
398 var listenCalls = EventListener.listen.argsForCall;
@@ -389,7 +404,8 @@ describe('ReactEventEmitter', function() {
404 setEventListeners.push(captureCalls[i][1]);
405 }
406
392 - var module = ReactEventEmitter.registrationNameModules[ON_CHANGE_KEY];
407 + var module =
408 + ReactBrowserEventEmitter.registrationNameModules[ON_CHANGE_KEY];
409 var dependencies = module.eventTypes.change.dependencies;
410 expect(setEventListeners.length).toEqual(dependencies.length);
411
@@ -399,17 +415,17 @@ describe('ReactEventEmitter', function() {
415 });
416
417 it('should bubble onTouchTap', function() {
402 - ReactEventEmitter.putListener(
418 + ReactBrowserEventEmitter.putListener(
419 getID(CHILD),
420 ON_TOUCH_TAP_KEY,
421 recordID.bind(null, getID(CHILD))
422 );
407 - ReactEventEmitter.putListener(
423 + ReactBrowserEventEmitter.putListener(
424 getID(PARENT),
425 ON_TOUCH_TAP_KEY,
426 recordID.bind(null, getID(PARENT))
427 );
412 - ReactEventEmitter.putListener(
428 + ReactBrowserEventEmitter.putListener(
429 getID(GRANDPARENT),
430 ON_TOUCH_TAP_KEY,
431 recordID.bind(null, getID(GRANDPARENT))
src/browser/eventPlugins/ChangeEventPlugin.js
+1 -1
@@ -85,7 +85,7 @@ function manualDispatchChangeEvent(nativeEvent) {
85 EventPropagators.accumulateTwoPhaseDispatches(event);
86
87 // If change and propertychange bubbled, we'd just bind to it like all the
88 - // other events and have it go through ReactEventTopLevelCallback. Since it
88 + // other events and have it go through ReactBrowserEventEmitter. Since it
89 // doesn't, we manually listen for the events and so we have to enqueue and
90 // process the abstract event manually.
91 //
src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js
+2 -2
@@ -26,7 +26,7 @@ describe('AnalyticsEventPlugin', function() {
26 var EventPluginHub;
27 var EventPluginRegistry;
28 var React;
29 - var ReactEventEmitter;
29 + var ReactBrowserEventEmitter;
30 var ReactTestUtils;
31
32 var DefaultEventPluginOrder;
@@ -40,7 +40,7 @@ describe('AnalyticsEventPlugin', function() {
40 EventPluginHub = require('EventPluginHub');
41 EventPluginRegistry = require('EventPluginRegistry');
42 React = require('React');
43 - ReactEventEmitter = require('ReactEventEmitter');
43 + ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
44 ReactTestUtils = require('ReactTestUtils');
45
46 EventPluginRegistry._resetEventPlugins();
src/browser/ui/ReactDOMComponent.js
+5 -5
@@ -24,7 +24,7 @@ var DOMProperty = require('DOMProperty');
24 var DOMPropertyOperations = require('DOMPropertyOperations');
25 var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
26 var ReactComponent = require('ReactComponent');
27 -var ReactEventEmitter = require('ReactEventEmitter');
27 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28 var ReactMount = require('ReactMount');
29 var ReactMultiChild = require('ReactMultiChild');
30 var ReactPerf = require('ReactPerf');
@@ -35,9 +35,9 @@ var keyOf = require('keyOf');
35 var merge = require('merge');
36 var mixInto = require('mixInto');
37
38 -var deleteListener = ReactEventEmitter.deleteListener;
39 -var listenTo = ReactEventEmitter.listenTo;
40 -var registrationNameModules = ReactEventEmitter.registrationNameModules;
38 +var deleteListener = ReactBrowserEventEmitter.deleteListener;
39 +var listenTo = ReactBrowserEventEmitter.listenTo;
40 +var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
41
42 // For quickly matching children type, to test if can be treated as content.
43 var CONTENT_TYPES = {'string': true, 'number': true};
@@ -404,7 +404,7 @@ ReactDOMComponent.Mixin = {
404 */
405 unmountComponent: function() {
406 this.unmountChildren();
407 - ReactEventEmitter.deleteAllListeners(this._rootNodeID);
407 + ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
408 ReactComponent.Mixin.unmountComponent.call(this);
409 }
410
src/browser/ui/ReactDefaultInjection.js
+3 -3
@@ -39,7 +39,7 @@ var ReactDOMInput = require('ReactDOMInput');
39 var ReactDOMOption = require('ReactDOMOption');
40 var ReactDOMSelect = require('ReactDOMSelect');
41 var ReactDOMTextarea = require('ReactDOMTextarea');
42 -var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
42 +var ReactEventListener = require('ReactEventListener');
43 var ReactInjection = require('ReactInjection');
44 var ReactInstanceHandles = require('ReactInstanceHandles');
45 var ReactMount = require('ReactMount');
@@ -51,8 +51,8 @@ var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig');
51 var createFullPageComponent = require('createFullPageComponent');
52
53 function inject() {
54 - ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
55 - ReactEventTopLevelCallback
54 + ReactInjection.EventEmitter.injectReactEventListener(
55 + ReactEventListener
56 );
57
58 /**
src/browser/ui/ReactEventListener.js renamed
+99 -71
@@ -13,27 +13,22 @@
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 - * @providesModule ReactEventTopLevelCallback
16 + * @providesModule ReactEventListener
17 * @typechecks static-only
18 */
19
20 "use strict";
21
22 +var EventListener = require('EventListener');
23 var PooledClass = require('PooledClass');
23 -var ReactEventEmitter = require('ReactEventEmitter');
24 var ReactInstanceHandles = require('ReactInstanceHandles');
25 var ReactMount = require('ReactMount');
26 var ReactUpdates = require('ReactUpdates');
27
28 var getEventTarget = require('getEventTarget');
29 +var getUnboundedScrollPosition = require('getUnboundedScrollPosition');
30 var mixInto = require('mixInto');
31
31 -/**
32 - * @type {boolean}
33 - * @private
34 - */
35 -var _topLevelListenersEnabled = true;
36 -
32 /**
33 * Finds the parent React component of `node`.
34 *
@@ -52,13 +47,24 @@ function findParent(node) {
47 return parent;
48 }
49
55 -/**
56 - * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's
57 - * ancestor list. Separated from createTopLevelCallback to avoid try/finally
58 - * deoptimization.
59 - *
60 - * @param {TopLevelCallbackBookKeeping} bookKeeping
61 - */
50 +// Used to store ancestor hierarchy in top level callback
51 +function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
52 + this.topLevelType = topLevelType;
53 + this.nativeEvent = nativeEvent;
54 + this.ancestors = [];
55 +}
56 +mixInto(TopLevelCallbackBookKeeping, {
57 + destructor: function() {
58 + this.topLevelType = null;
59 + this.nativeEvent = null;
60 + this.ancestors.length = 0;
61 + }
62 +});
63 +PooledClass.addPoolingTo(
64 + TopLevelCallbackBookKeeping,
65 + PooledClass.twoArgumentPooler
66 +);
67 +
68 function handleTopLevelImpl(bookKeeping) {
69 var topLevelTarget = ReactMount.getFirstReactDOM(
70 getEventTarget(bookKeeping.nativeEvent)
@@ -77,7 +83,7 @@ function handleTopLevelImpl(bookKeeping) {
83 for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
84 topLevelTarget = bookKeeping.ancestors[i];
85 var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
80 - ReactEventEmitter.handleTopLevel(
86 + ReactEventListener._handleTopLevel(
87 bookKeeping.topLevelType,
88 topLevelTarget,
89 topLevelTargetID,
@@ -86,75 +92,97 @@ function handleTopLevelImpl(bookKeeping) {
92 }
93 }
94
89 -// Used to store ancestor hierarchy in top level callback
90 -function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
91 - this.topLevelType = topLevelType;
92 - this.nativeEvent = nativeEvent;
93 - this.ancestors = [];
95 +function scrollValueMonitor(cb) {
96 + var scrollPosition = getUnboundedScrollPosition(window);
97 + cb(scrollPosition);
98 }
95 -mixInto(TopLevelCallbackBookKeeping, {
96 - destructor: function() {
97 - this.topLevelType = null;
98 - this.nativeEvent = null;
99 - this.ancestors.length = 0;
100 - }
101 -});
102 -PooledClass.addPoolingTo(
103 - TopLevelCallbackBookKeeping,
104 - PooledClass.twoArgumentPooler
105 -);
99
107 -/**
108 - * Top-level callback creator used to implement event handling using delegation.
109 - * This is used via dependency injection.
110 - */
111 -var ReactEventTopLevelCallback = {
100 +var ReactEventListener = {
101 + _enabled: true,
102 + _handleTopLevel: null,
103 +
104 + WINDOW_HANDLE: window,
105 +
106 + setHandleTopLevel: function(handleTopLevel) {
107 + ReactEventListener._handleTopLevel = handleTopLevel;
108 + },
109
113 - /**
114 - * Sets whether or not any created callbacks should be enabled.
115 - *
116 - * @param {boolean} enabled True if callbacks should be enabled.
117 - */
110 setEnabled: function(enabled) {
119 - _topLevelListenersEnabled = !!enabled;
111 + ReactEventListener._enabled = !!enabled;
112 },
113
114 + isEnabled: function() {
115 + return ReactEventListener._enabled;
116 + },
117 +
118 +
119 /**
123 - * @return {boolean} True if callbacks are enabled.
120 + * Traps top-level events by using event bubbling.
121 + *
122 + * @param {string} topLevelType Record from `EventConstants`.
123 + * @param {string} handlerBaseName Event name (e.g. "click").
124 + * @param {object} handle Element on which to attach listener.
125 + * @return {object} An object with a remove function which will forcefully
126 + * remove the listener.
127 + * @internal
128 */
125 - isEnabled: function() {
126 - return _topLevelListenersEnabled;
129 + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
130 + var element = handle;
131 + if (!element) {
132 + return;
133 + }
134 + return EventListener.listen(
135 + element,
136 + handlerBaseName,
137 + ReactEventListener.dispatchEvent.bind(null, topLevelType)
138 + );
139 },
140
141 /**
130 - * Creates a callback for the supplied `topLevelType` that could be added as
131 - * a listener to the document. The callback computes a `topLevelTarget` which
132 - * should be the root node of a mounted React component where the listener
133 - * is attached.
142 + * Traps a top-level event by using event capturing.
143 *
144 * @param {string} topLevelType Record from `EventConstants`.
136 - * @return {function} Callback for handling top-level events.
145 + * @param {string} handlerBaseName Event name (e.g. "click").
146 + * @param {object} handle Element on which to attach listener.
147 + * @return {object} An object with a remove function which will forcefully
148 + * remove the listener.
149 + * @internal
150 */
138 - createTopLevelCallback: function(topLevelType) {
139 - return function(nativeEvent) {
140 - if (!_topLevelListenersEnabled) {
141 - return;
142 - }
143 -
144 - var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
145 - topLevelType,
146 - nativeEvent
147 - );
148 - try {
149 - // Event queue being processed in the same cycle allows
150 - // `preventDefault`.
151 - ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
152 - } finally {
153 - TopLevelCallbackBookKeeping.release(bookKeeping);
154 - }
155 - };
156 - }
151 + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
152 + var element = handle;
153 + if (!element) {
154 + return;
155 + }
156 + return EventListener.capture(
157 + element,
158 + handlerBaseName,
159 + ReactEventListener.dispatchEvent.bind(null, topLevelType)
160 + );
161 + },
162 +
163 + monitorScrollValue: function(refresh) {
164 + var callback = scrollValueMonitor.bind(null, refresh);
165 + EventListener.listen(window, 'scroll', callback);
166 + EventListener.listen(window, 'resize', callback);
167 + },
168 +
169 + dispatchEvent: function(topLevelType, nativeEvent) {
170 + if (!ReactEventListener._enabled) {
171 + return;
172 + }
173
174 + var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
175 + topLevelType,
176 + nativeEvent
177 + );
178 + try {
179 + // Event queue being processed in the same cycle allows
180 + // `preventDefault`.
181 + ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
182 + } finally {
183 + TopLevelCallbackBookKeeping.release(bookKeeping);
184 + }
185 + }
186 };
187
160 -module.exports = ReactEventTopLevelCallback;
188 +module.exports = ReactEventListener;
src/browser/ui/ReactInjection.js
+2 -2
@@ -24,7 +24,7 @@ var ReactComponent = require('ReactComponent');
24 var ReactCompositeComponent = require('ReactCompositeComponent');
25 var ReactDOM = require('ReactDOM');
26 var ReactEmptyComponent = require('ReactEmptyComponent');
27 -var ReactEventEmitter = require('ReactEventEmitter');
27 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28 var ReactPerf = require('ReactPerf');
29 var ReactRootIndex = require('ReactRootIndex');
30 var ReactUpdates = require('ReactUpdates');
@@ -36,7 +36,7 @@ var ReactInjection = {
36 EmptyComponent: ReactEmptyComponent.injection,
37 EventPluginHub: EventPluginHub.injection,
38 DOM: ReactDOM.injection,
39 - EventEmitter: ReactEventEmitter.injection,
39 + EventEmitter: ReactBrowserEventEmitter.injection,
40 Perf: ReactPerf.injection,
41 RootIndex: ReactRootIndex.injection,
42 Updates: ReactUpdates.injection
src/browser/ui/ReactMount.js
+2 -2
@@ -20,7 +20,7 @@
20
21 var DOMProperty = require('DOMProperty');
22 var ReactCurrentOwner = require('ReactCurrentOwner');
23 -var ReactEventEmitter = require('ReactEventEmitter');
23 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
24 var ReactInstanceHandles = require('ReactInstanceHandles');
25 var ReactPerf = require('ReactPerf');
26
@@ -278,7 +278,7 @@ var ReactMount = {
278 '_registerComponent(...): Target container is not a DOM element.'
279 );
280
281 - ReactEventEmitter.ensureScrollValueMonitoring();
281 + ReactBrowserEventEmitter.ensureScrollValueMonitoring();
282
283 var reactRootID = ReactMount.registerContainer(container);
284 instancesByReactRootID[reactRootID] = nextComponent;
src/browser/ui/__tests__/ReactDOMComponent-test.js
+3 -3
@@ -388,7 +388,7 @@ describe('ReactDOMComponent', function() {
388 describe('unmountComponent', function() {
389 it("should clean up listeners", function() {
390 var React = require('React');
391 - var ReactEventEmitter = require('ReactEventEmitter');
391 + var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
392 var ReactMount = require('ReactMount');
393
394 var container = document.createElement('div');
@@ -401,13 +401,13 @@ describe('ReactDOMComponent', function() {
401 var rootNode = instance.getDOMNode();
402 var rootNodeID = ReactMount.getID(rootNode);
403 expect(
404 - ReactEventEmitter.getListener(rootNodeID, 'onClick')
404 + ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
405 ).toBe(callback);
406
407 React.unmountComponentAtNode(container);
408
409 expect(
410 - ReactEventEmitter.getListener(rootNodeID, 'onClick')
410 + ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
411 ).toBe(undefined);
412 });
413 });
src/browser/ui/__tests__/ReactEventListener-test.js renamed
+36 -27
@@ -19,23 +19,27 @@
19
20 'use strict';
21
22 -require('mock-modules')
23 - .mock('ReactEventEmitter');
22 +var mocks = require('mocks');
23 +
24
25 var EVENT_TARGET_PARAM = 1;
26
27 -describe('ReactEventTopLevelCallback', function() {
27 +describe('ReactEventListener', function() {
28 var React;
29 - var ReactEventTopLevelCallback;
29 +
30 var ReactMount;
31 - var ReactEventEmitter; // mocked
31 + var ReactEventListener;
32 + var handleTopLevel;
33
34 beforeEach(function() {
35 require('mock-modules').dumpCache();
36 React = require('React');
36 - ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
37 +
38 ReactMount = require('ReactMount');
38 - ReactEventEmitter = require('ReactEventEmitter'); // mocked
39 + ReactEventListener = require('ReactEventListener');
40 +
41 + handleTopLevel = mocks.getMockFunction();
42 + ReactEventListener._handleTopLevel = handleTopLevel;
43 });
44
45 describe('Propagation', function() {
@@ -45,15 +49,16 @@ describe('ReactEventTopLevelCallback', function() {
49 var parentContainer = document.createElement('div');
50 var parentControl = <div>Parent</div>;
51 childControl = ReactMount.renderComponent(childControl, childContainer);
48 - parentControl = ReactMount.renderComponent(parentControl, parentContainer);
52 + parentControl =
53 + ReactMount.renderComponent(parentControl, parentContainer);
54 parentControl.getDOMNode().appendChild(childContainer);
55
51 - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
56 + var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
57 callback({
58 target: childControl.getDOMNode()
59 });
60
56 - var calls = ReactEventEmitter.handleTopLevel.mock.calls;
61 + var calls = handleTopLevel.mock.calls;
62 expect(calls.length).toBe(2);
63 expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode());
64 expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -67,17 +72,19 @@ describe('ReactEventTopLevelCallback', function() {
72 var grandParentContainer = document.createElement('div');
73 var grandParentControl = <div>Parent</div>;
74 childControl = ReactMount.renderComponent(childControl, childContainer);
70 - parentControl = ReactMount.renderComponent(parentControl, parentContainer);
71 - grandParentControl = ReactMount.renderComponent(grandParentControl, grandParentContainer);
75 + parentControl =
76 + ReactMount.renderComponent(parentControl, parentContainer);
77 + grandParentControl =
78 + ReactMount.renderComponent(grandParentControl, grandParentContainer);
79 parentControl.getDOMNode().appendChild(childContainer);
80 grandParentControl.getDOMNode().appendChild(parentContainer);
81
75 - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
82 + var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
83 callback({
84 target: childControl.getDOMNode()
85 });
86
80 - var calls = ReactEventEmitter.handleTopLevel.mock.calls;
87 + var calls = handleTopLevel.mock.calls;
88 expect(calls.length).toBe(3);
89 expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode());
90 expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -91,15 +98,16 @@ describe('ReactEventTopLevelCallback', function() {
98 var parentContainer = document.createElement('div');
99 var parentControl = <div>Parent</div>;
100 childControl = ReactMount.renderComponent(childControl, childContainer);
94 - parentControl = ReactMount.renderComponent(parentControl, parentContainer);
101 + parentControl =
102 + ReactMount.renderComponent(parentControl, parentContainer);
103 parentControl.getDOMNode().appendChild(childContainer);
104
97 - // ReactEventEmitter.handleTopLevel might remove the target from the DOM.
98 - // Here, we have handleTopLevel remove the node when the first event
99 - // handlers are called; we'll still expect to receive a second call for
100 - // the parent control.
105 + // ReactBrowserEventEmitter.handleTopLevel might remove the
106 + // target from the DOM. Here, we have handleTopLevel remove the
107 + // node when the first event handlers are called; we'll still
108 + // expect to receive a second call for the parent control.
109 var childNode = childControl.getDOMNode();
102 - ReactEventEmitter.handleTopLevel.mockImplementation(
110 + handleTopLevel.mockImplementation(
111 function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
112 if (topLevelTarget === childNode) {
113 ReactMount.unmountComponentAtNode(childContainer);
@@ -107,12 +115,12 @@ describe('ReactEventTopLevelCallback', function() {
115 }
116 );
117
110 - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
118 + var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
119 callback({
120 target: childNode
121 });
122
115 - var calls = ReactEventEmitter.handleTopLevel.mock.calls;
123 + var calls = handleTopLevel.mock.calls;
124 expect(calls.length).toBe(2);
125 expect(calls[0][EVENT_TARGET_PARAM]).toBe(childNode);
126 expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -134,7 +142,7 @@ describe('ReactEventTopLevelCallback', function() {
142 // Suppose an event handler in each root enqueues an update to the
143 // childControl element -- the two updates should get batched together.
144 var childNode = childControl.getDOMNode();
137 - ReactEventEmitter.handleTopLevel.mockImplementation(
145 + handleTopLevel.mockImplementation(
146 function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
147 ReactMount.renderComponent(
148 <div>{topLevelTarget === childNode ? '1' : '2'}</div>,
@@ -145,12 +153,13 @@ describe('ReactEventTopLevelCallback', function() {
153 }
154 );
155
148 - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
156 + var callback =
157 + ReactEventListener.dispatchEvent.bind(ReactEventListener, 'test');
158 callback({
159 target: childNode
160 });
161
153 - var calls = ReactEventEmitter.handleTopLevel.mock.calls;
162 + var calls = handleTopLevel.mock.calls;
163 expect(calls.length).toBe(2);
164 expect(childNode.textContent).toBe('2');
165 });
@@ -173,12 +182,12 @@ describe('ReactEventTopLevelCallback', function() {
182
183 var instance = ReactMount.renderComponent(<Wrapper />, container);
184
176 - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
185 + var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
186 callback({
187 target: instance.getInner().getDOMNode()
188 });
189
181 - var calls = ReactEventEmitter.handleTopLevel.mock.calls;
190 + var calls = handleTopLevel.mock.calls;
191 expect(calls.length).toBe(1);
192 expect(calls[0][EVENT_TARGET_PARAM]).toBe(instance.getInner().getDOMNode());
193 });
src/browser/ui/dom/components/LocalEventTrapMixin.js
+2 -2
@@ -18,7 +18,7 @@
18
19 "use strict";
20
21 -var ReactEventEmitter = require('ReactEventEmitter');
21 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
22
23 var accumulate = require('accumulate');
24 var forEachAccumulated = require('forEachAccumulated');
@@ -31,7 +31,7 @@ function remove(event) {
31 var LocalEventTrapMixin = {
32 trapBubbledEvent(topLevelType, handlerBaseName) {
33 invariant(this.isMounted(), 'Must be mounted to trap events');
34 - var listener = ReactEventEmitter.trapBubbledEvent(
34 + var listener = ReactBrowserEventEmitter.trapBubbledEvent(
35 topLevelType,
36 handlerBaseName,
37 this.getDOMNode()
src/test/ReactTestUtils.js
+7 -8
@@ -24,7 +24,7 @@ var EventPropagators = require('EventPropagators');
24 var React = require('React');
25 var ReactDescriptor = require('ReactDescriptor');
26 var ReactDOM = require('ReactDOM');
27 -var ReactEventEmitter = require('ReactEventEmitter');
27 +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28 var ReactMount = require('ReactMount');
29 var ReactTextComponent = require('ReactTextComponent');
30 var ReactUpdates = require('ReactUpdates');
@@ -259,12 +259,11 @@ var ReactTestUtils = {
259 * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
260 */
261 simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) {
262 - var virtualHandler =
263 - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
264 - topLevelType
265 - );
262 fakeNativeEvent.target = node;
267 - virtualHandler(fakeNativeEvent);
263 + ReactBrowserEventEmitter.ReactEventListener.dispatchEvent(
264 + topLevelType,
265 + fakeNativeEvent
266 + );
267 },
268
269 /**
@@ -319,7 +318,7 @@ function makeSimulator(eventType) {
318 // We don't use SyntheticEvent.getPooled in order to not have to worry about
319 // properly destroying any properties assigned from `eventData` upon release
320 var event = new SyntheticEvent(
322 - ReactEventEmitter.eventNameDispatchConfigs[eventType],
321 + ReactBrowserEventEmitter.eventNameDispatchConfigs[eventType],
322 ReactMount.getID(node),
323 fakeNativeEvent
324 );
@@ -337,7 +336,7 @@ function buildSimulators() {
336 ReactTestUtils.Simulate = {};
337
338 var eventType;
340 - for (eventType in ReactEventEmitter.eventNameDispatchConfigs) {
339 + for (eventType in ReactBrowserEventEmitter.eventNameDispatchConfigs) {
340 /**
341 * @param {!Element || ReactDOMComponent} domComponentOrNode
342 * @param {?object} eventData Fake event data to use in SyntheticEvent.