react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / ReactBrowserEventEmitter.js
81152 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactBrowserEventEmitter9* @typechecks static-only10*/1112"use strict";1314var EventConstants = require('EventConstants');15var EventPluginHub = require('EventPluginHub');16var EventPluginRegistry = require('EventPluginRegistry');17var ReactEventEmitterMixin = require('ReactEventEmitterMixin');18var ViewportMetrics = require('ViewportMetrics');1920var assign = require('Object.assign');21var isEventSupported = require('isEventSupported');2223/**24* Summary of `ReactBrowserEventEmitter` event handling:25*26* - Top-level delegation is used to trap most native browser events. This27* may only occur in the main thread and is the responsibility of28* ReactEventListener, which is injected and can therefore support pluggable29* event sources. This is the only work that occurs in the main thread.30*31* - We normalize and de-duplicate events to account for browser quirks. This32* may be done in the worker thread.33*34* - Forward these native events (with the associated top-level type used to35* trap it) to `EventPluginHub`, which in turn will ask plugins if they want36* to extract any synthetic events.37*38* - The `EventPluginHub` will then process each event by annotating them with39* "dispatches", a sequence of listeners and IDs that care about that event.40*41* - The `EventPluginHub` then dispatches the events.42*43* Overview of React and the event system:44*45* +------------+ .46* | DOM | .47* +------------+ .48* | .49* v .50* +------------+ .51* | ReactEvent | .52* | Listener | .53* +------------+ . +-----------+54* | . +--------+|SimpleEvent|55* | . | |Plugin |56* +-----|------+ . v +-----------+57* | | | . +--------------+ +------------+58* | +-----------.--->|EventPluginHub| | Event |59* | | . | | +-----------+ | Propagators|60* | ReactEvent | . | | |TapEvent | |------------|61* | Emitter | . | |<---+|Plugin | |other plugin|62* | | . | | +-----------+ | utilities |63* | +-----------.--->| | +------------+64* | | | . +--------------+65* +-----|------+ . ^ +-----------+66* | . | |Enter/Leave|67* + . +-------+|Plugin |68* +-------------+ . +-----------+69* | application | .70* |-------------| .71* | | .72* | | .73* +-------------+ .74* .75* React Core . General Purpose Event Plugin System76*/7778var alreadyListeningTo = {};79var isMonitoringScrollValue = false;80var reactTopListenersCounter = 0;8182// For events like 'submit' which don't consistently bubble (which we trap at a83// lower node than `document`), binding at `document` would cause duplicate84// events so we don't include them here85var topEventMapping = {86topBlur: 'blur',87topChange: 'change',88topClick: 'click',89topCompositionEnd: 'compositionend',90topCompositionStart: 'compositionstart',91topCompositionUpdate: 'compositionupdate',92topContextMenu: 'contextmenu',93topCopy: 'copy',94topCut: 'cut',95topDoubleClick: 'dblclick',96topDrag: 'drag',97topDragEnd: 'dragend',98topDragEnter: 'dragenter',99topDragExit: 'dragexit',100topDragLeave: 'dragleave',101topDragOver: 'dragover',102topDragStart: 'dragstart',103topDrop: 'drop',104topFocus: 'focus',105topInput: 'input',106topKeyDown: 'keydown',107topKeyPress: 'keypress',108topKeyUp: 'keyup',109topMouseDown: 'mousedown',110topMouseMove: 'mousemove',111topMouseOut: 'mouseout',112topMouseOver: 'mouseover',113topMouseUp: 'mouseup',114topPaste: 'paste',115topScroll: 'scroll',116topSelectionChange: 'selectionchange',117topTextInput: 'textInput',118topTouchCancel: 'touchcancel',119topTouchEnd: 'touchend',120topTouchMove: 'touchmove',121topTouchStart: 'touchstart',122topWheel: 'wheel'123};124125/**126* To ensure no conflicts with other potential React instances on the page127*/128var topListenersIDKey = "_reactListenersID" + String(Math.random()).slice(2);129130function getListeningForDocument(mountAt) {131// In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`132// directly.133if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {134mountAt[topListenersIDKey] = reactTopListenersCounter++;135alreadyListeningTo[mountAt[topListenersIDKey]] = {};136}137return alreadyListeningTo[mountAt[topListenersIDKey]];138}139140/**141* `ReactBrowserEventEmitter` is used to attach top-level event listeners. For142* example:143*144* ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction);145*146* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.147*148* @internal149*/150var ReactBrowserEventEmitter = assign({}, ReactEventEmitterMixin, {151152/**153* Injectable event backend154*/155ReactEventListener: null,156157injection: {158/**159* @param {object} ReactEventListener160*/161injectReactEventListener: function(ReactEventListener) {162ReactEventListener.setHandleTopLevel(163ReactBrowserEventEmitter.handleTopLevel164);165ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;166}167},168169/**170* Sets whether or not any created callbacks should be enabled.171*172* @param {boolean} enabled True if callbacks should be enabled.173*/174setEnabled: function(enabled) {175if (ReactBrowserEventEmitter.ReactEventListener) {176ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);177}178},179180/**181* @return {boolean} True if callbacks are enabled.182*/183isEnabled: function() {184return !!(185ReactBrowserEventEmitter.ReactEventListener &&186ReactBrowserEventEmitter.ReactEventListener.isEnabled()187);188},189190/**191* We listen for bubbled touch events on the document object.192*193* Firefox v8.01 (and possibly others) exhibited strange behavior when194* mounting `onmousemove` events at some node that was not the document195* element. The symptoms were that if your mouse is not moving over something196* contained within that mount point (for example on the background) the197* top-level listeners for `onmousemove` won't be called. However, if you198* register the `mousemove` on the document object, then it will of course199* catch all `mousemove`s. This along with iOS quirks, justifies restricting200* top-level listeners to the document object only, at least for these201* movement types of events and possibly all events.202*203* @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html204*205* Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but206* they bubble to document.207*208* @param {string} registrationName Name of listener (e.g. `onClick`).209* @param {object} contentDocumentHandle Document which owns the container210*/211listenTo: function(registrationName, contentDocumentHandle) {212var mountAt = contentDocumentHandle;213var isListening = getListeningForDocument(mountAt);214var dependencies = EventPluginRegistry.215registrationNameDependencies[registrationName];216217var topLevelTypes = EventConstants.topLevelTypes;218for (var i = 0, l = dependencies.length; i < l; i++) {219var dependency = dependencies[i];220if (!(221isListening.hasOwnProperty(dependency) &&222isListening[dependency]223)) {224if (dependency === topLevelTypes.topWheel) {225if (isEventSupported('wheel')) {226ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(227topLevelTypes.topWheel,228'wheel',229mountAt230);231} else if (isEventSupported('mousewheel')) {232ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(233topLevelTypes.topWheel,234'mousewheel',235mountAt236);237} else {238// Firefox needs to capture a different mouse scroll event.239// @see http://www.quirksmode.org/dom/events/tests/scroll.html240ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(241topLevelTypes.topWheel,242'DOMMouseScroll',243mountAt244);245}246} else if (dependency === topLevelTypes.topScroll) {247248if (isEventSupported('scroll', true)) {249ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(250topLevelTypes.topScroll,251'scroll',252mountAt253);254} else {255ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(256topLevelTypes.topScroll,257'scroll',258ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE259);260}261} else if (dependency === topLevelTypes.topFocus ||262dependency === topLevelTypes.topBlur) {263264if (isEventSupported('focus', true)) {265ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(266topLevelTypes.topFocus,267'focus',268mountAt269);270ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(271topLevelTypes.topBlur,272'blur',273mountAt274);275} else if (isEventSupported('focusin')) {276// IE has `focusin` and `focusout` events which bubble.277// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html278ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(279topLevelTypes.topFocus,280'focusin',281mountAt282);283ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(284topLevelTypes.topBlur,285'focusout',286mountAt287);288}289290// to make sure blur and focus event listeners are only attached once291isListening[topLevelTypes.topBlur] = true;292isListening[topLevelTypes.topFocus] = true;293} else if (topEventMapping.hasOwnProperty(dependency)) {294ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(295dependency,296topEventMapping[dependency],297mountAt298);299}300301isListening[dependency] = true;302}303}304},305306trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {307return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(308topLevelType,309handlerBaseName,310handle311);312},313314trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {315return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(316topLevelType,317handlerBaseName,318handle319);320},321322/**323* Listens to window scroll and resize events. We cache scroll values so that324* application code can access them without triggering reflows.325*326* NOTE: Scroll events do not bubble.327*328* @see http://www.quirksmode.org/dom/events/scroll.html329*/330ensureScrollValueMonitoring: function(){331if (!isMonitoringScrollValue) {332var refresh = ViewportMetrics.refreshScrollValues;333ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);334isMonitoringScrollValue = true;335}336},337338eventNameDispatchConfigs: EventPluginHub.eventNameDispatchConfigs,339340registrationNameModules: EventPluginHub.registrationNameModules,341342putListener: EventPluginHub.putListener,343344getListener: EventPluginHub.getListener,345346deleteListener: EventPluginHub.deleteListener,347348deleteAllListeners: EventPluginHub.deleteAllListeners349350});351352module.exports = ReactBrowserEventEmitter;353354355