1313 * See the License for the specific language governing permissions and
1414 * limitations under the License.
1515 *
16- * @providesModule ReactEventEmitter
16+ * @providesModule ReactBrowserEventEmitter
1717 * @typechecks static-only
1818 */
1919
2020"use strict" ;
2121
2222var EventConstants = require ( 'EventConstants' ) ;
23- var EventListener = require ( 'EventListener' ) ;
2423var EventPluginHub = require ( 'EventPluginHub' ) ;
2524var EventPluginRegistry = require ( 'EventPluginRegistry' ) ;
26- var ExecutionEnvironment = require ( 'ExecutionEnvironment' ) ;
2725var ReactEventEmitterMixin = require ( 'ReactEventEmitterMixin' ) ;
2826var ViewportMetrics = require ( 'ViewportMetrics' ) ;
2927
30- var invariant = require ( 'invariant' ) ;
3128var isEventSupported = require ( 'isEventSupported' ) ;
3229var merge = require ( 'merge' ) ;
3330
3431/**
35- * Summary of `ReactEventEmitter ` event handling:
32+ * Summary of `ReactBrowserEventEmitter ` event handling:
3633 *
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.
3941 *
4042 * - Forward these native events (with the associated top-level type used to
4143 * trap it) to `EventPluginHub`, which in turn will ask plugins if they want
@@ -48,11 +50,16 @@ var merge = require('merge');
4850 *
4951 * Overview of React and the event system:
5052 *
51- * .
5253 * +------------+ .
5354 * | DOM | .
55+ * +------------+ .
56+ * | .
57+ * v .
58+ * +------------+ .
59+ * | ReactEvent | .
60+ * | Listener | .
5461 * +------------+ . +-----------+
55- * + . +--------+|SimpleEvent|
62+ * | . +--------+|SimpleEvent|
5663 * | . | |Plugin |
5764 * +-----|------+ . v +-----------+
5865 * | | | . +--------------+ +------------+
@@ -139,68 +146,31 @@ function getListeningForDocument(mountAt) {
139146}
140147
141148/**
142- * Traps top-level events by using event bubbling.
149+ * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
150+ * example:
143151 *
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);
185153 *
186154 * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
187155 *
188156 * @internal
189157 */
190- var ReactEventEmitter = merge ( ReactEventEmitterMixin , {
158+ var ReactBrowserEventEmitter = merge ( ReactEventEmitterMixin , {
191159
192160 /**
193- * React references `ReactEventTopLevelCallback` using this property in order
194- * to allow dependency injection.
161+ * Injectable event backend
195162 */
196- TopLevelCallbackCreator : null ,
163+ ReactEventListener : null ,
197164
198165 injection : {
199166 /**
200- * @param {function } TopLevelCallbackCreator
167+ * @param {object } ReactEventListener
201168 */
202- injectTopLevelCallbackCreator : function ( TopLevelCallbackCreator ) {
203- ReactEventEmitter . TopLevelCallbackCreator = TopLevelCallbackCreator ;
169+ injectReactEventListener : function ( ReactEventListener ) {
170+ ReactEventListener . setHandleTopLevel (
171+ ReactBrowserEventEmitter . handleTopLevel
172+ ) ;
173+ ReactBrowserEventEmitter . ReactEventListener = ReactEventListener ;
204174 }
205175 } ,
206176
@@ -210,13 +180,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
210180 * @param {boolean } enabled True if callbacks should be enabled.
211181 */
212182 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 ) ;
220185 }
221186 } ,
222187
@@ -225,8 +190,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
225190 */
226191 isEnabled : function ( ) {
227192 return ! ! (
228- ReactEventEmitter . TopLevelCallbackCreator &&
229- ReactEventEmitter . TopLevelCallbackCreator . isEnabled ( )
193+ ReactBrowserEventEmitter . ReactEventListener &&
194+ ReactBrowserEventEmitter . ReactEventListener . isEnabled ( )
230195 ) ;
231196 } ,
232197
@@ -249,10 +214,10 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
249214 * they bubble to document.
250215 *
251216 * @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
253218 */
254- listenTo : function ( registrationName , contentDocument ) {
255- var mountAt = contentDocument ;
219+ listenTo : function ( registrationName , contentDocumentHandle ) {
220+ var mountAt = contentDocumentHandle ;
256221 var isListening = getListeningForDocument ( mountAt ) ;
257222 var dependencies = EventPluginRegistry .
258223 registrationNameDependencies [ registrationName ] ;
@@ -268,49 +233,102 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
268233
269234 if ( topLevelType === topLevelTypes . topWheel ) {
270235 if ( isEventSupported ( 'wheel' ) ) {
271- trapBubbledEvent ( topLevelTypes . topWheel , 'wheel' , mountAt ) ;
236+ ReactBrowserEventEmitter . ReactEventListener . trapBubbledEvent (
237+ topLevelTypes . topWheel ,
238+ 'wheel' ,
239+ mountAt
240+ ) ;
272241 } else if ( isEventSupported ( 'mousewheel' ) ) {
273- trapBubbledEvent ( topLevelTypes . topWheel , 'mousewheel' , mountAt ) ;
242+ ReactBrowserEventEmitter . ReactEventListener . trapBubbledEvent (
243+ topLevelTypes . topWheel ,
244+ 'mousewheel' ,
245+ mountAt
246+ ) ;
274247 } else {
275248 // Firefox needs to capture a different mouse scroll event.
276249 // @see http://www.quirksmode.org/dom/events/tests/scroll.html
277- trapBubbledEvent (
250+ ReactBrowserEventEmitter . ReactEventListener . trapBubbledEvent (
278251 topLevelTypes . topWheel ,
279252 'DOMMouseScroll' ,
280- mountAt ) ;
253+ mountAt
254+ ) ;
281255 }
282256 } else if ( topLevelType === topLevelTypes . topScroll ) {
283257
284258 if ( isEventSupported ( 'scroll' , true ) ) {
285- trapCapturedEvent ( topLevelTypes . topScroll , 'scroll' , mountAt ) ;
259+ ReactBrowserEventEmitter . ReactEventListener . trapCapturedEvent (
260+ topLevelTypes . topScroll ,
261+ 'scroll' ,
262+ mountAt
263+ ) ;
286264 } else {
287- trapBubbledEvent ( topLevelTypes . topScroll , 'scroll' , window ) ;
265+ ReactBrowserEventEmitter . ReactEventListener . trapBubbledEvent (
266+ topLevelTypes . topScroll ,
267+ 'scroll' ,
268+ ReactBrowserEventEmitter . ReactEventListener . WINDOW_HANDLE
269+ ) ;
288270 }
289271 } else if ( topLevelType === topLevelTypes . topFocus ||
290272 topLevelType === topLevelTypes . topBlur ) {
291273
292274 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+ ) ;
295285 } else if ( isEventSupported ( 'focusin' ) ) {
296286 // IE has `focusin` and `focusout` events which bubble.
297287 // @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+ ) ;
300298 }
301299
302300 // to make sure blur and focus event listeners are only attached once
303301 isListening [ topLevelTypes . topBlur ] = true ;
304302 isListening [ topLevelTypes . topFocus ] = true ;
305303 } else if ( topEventMapping . hasOwnProperty ( dependency ) ) {
306- trapBubbledEvent ( topLevelType , topEventMapping [ dependency ] , mountAt ) ;
304+ ReactBrowserEventEmitter . ReactEventListener . trapBubbledEvent (
305+ topLevelType ,
306+ topEventMapping [ dependency ] ,
307+ mountAt
308+ ) ;
307309 }
308310
309311 isListening [ dependency ] = true ;
310312 }
311313 }
312314 } ,
313315
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+
314332 /**
315333 * Listens to window scroll and resize events. We cache scroll values so that
316334 * application code can access them without triggering reflows.
@@ -322,8 +340,7 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
322340 ensureScrollValueMonitoring : function ( ) {
323341 if ( ! isMonitoringScrollValue ) {
324342 var refresh = ViewportMetrics . refreshScrollValues ;
325- EventListener . listen ( window , 'scroll' , refresh ) ;
326- EventListener . listen ( window , 'resize' , refresh ) ;
343+ ReactBrowserEventEmitter . ReactEventListener . monitorScrollValue ( refresh ) ;
327344 isMonitoringScrollValue = true ;
328345 }
329346 } ,
@@ -338,12 +355,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
338355
339356 deleteListener : EventPluginHub . deleteListener ,
340357
341- deleteAllListeners : EventPluginHub . deleteAllListeners ,
342-
343- trapBubbledEvent : trapBubbledEvent ,
344-
345- trapCapturedEvent : trapCapturedEvent
358+ deleteAllListeners : EventPluginHub . deleteAllListeners
346359
347360} ) ;
348361
349- module . exports = ReactEventEmitter ;
362+ module . exports = ReactBrowserEventEmitter ;
0 commit comments