You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
8.0 KiB
274 lines
8.0 KiB
/** |
|
* Copyright 2013-2015, Facebook, Inc. |
|
* All rights reserved. |
|
* |
|
* This source code is licensed under the BSD-style license found in the |
|
* LICENSE file in the root directory of this source tree. An additional grant |
|
* of patent rights can be found in the PATENTS file in the same directory. |
|
* |
|
* @providesModule EventPluginHub |
|
*/ |
|
|
|
'use strict'; |
|
|
|
var EventPluginRegistry = require("./EventPluginRegistry"); |
|
var EventPluginUtils = require("./EventPluginUtils"); |
|
|
|
var accumulateInto = require("./accumulateInto"); |
|
var forEachAccumulated = require("./forEachAccumulated"); |
|
var invariant = require("./invariant"); |
|
|
|
/** |
|
* Internal store for event listeners |
|
*/ |
|
var listenerBank = {}; |
|
|
|
/** |
|
* Internal queue of events that have accumulated their dispatches and are |
|
* waiting to have their dispatches executed. |
|
*/ |
|
var eventQueue = null; |
|
|
|
/** |
|
* Dispatches an event and releases it back into the pool, unless persistent. |
|
* |
|
* @param {?object} event Synthetic event to be dispatched. |
|
* @private |
|
*/ |
|
var executeDispatchesAndRelease = function(event) { |
|
if (event) { |
|
var executeDispatch = EventPluginUtils.executeDispatch; |
|
// Plugins can provide custom behavior when dispatching events. |
|
var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event); |
|
if (PluginModule && PluginModule.executeDispatch) { |
|
executeDispatch = PluginModule.executeDispatch; |
|
} |
|
EventPluginUtils.executeDispatchesInOrder(event, executeDispatch); |
|
|
|
if (!event.isPersistent()) { |
|
event.constructor.release(event); |
|
} |
|
} |
|
}; |
|
|
|
/** |
|
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM |
|
* hierarchy given ids of the logical DOM elements involved. |
|
*/ |
|
var InstanceHandle = null; |
|
|
|
function validateInstanceHandle() { |
|
var valid = |
|
InstanceHandle && |
|
InstanceHandle.traverseTwoPhase && |
|
InstanceHandle.traverseEnterLeave; |
|
("production" !== process.env.NODE_ENV ? invariant( |
|
valid, |
|
'InstanceHandle not injected before use!' |
|
) : invariant(valid)); |
|
} |
|
|
|
/** |
|
* This is a unified interface for event plugins to be installed and configured. |
|
* |
|
* Event plugins can implement the following properties: |
|
* |
|
* `extractEvents` {function(string, DOMEventTarget, string, object): *} |
|
* Required. When a top-level event is fired, this method is expected to |
|
* extract synthetic events that will in turn be queued and dispatched. |
|
* |
|
* `eventTypes` {object} |
|
* Optional, plugins that fire events must publish a mapping of registration |
|
* names that are used to register listeners. Values of this mapping must |
|
* be objects that contain `registrationName` or `phasedRegistrationNames`. |
|
* |
|
* `executeDispatch` {function(object, function, string)} |
|
* Optional, allows plugins to override how an event gets dispatched. By |
|
* default, the listener is simply invoked. |
|
* |
|
* Each plugin that is injected into `EventsPluginHub` is immediately operable. |
|
* |
|
* @public |
|
*/ |
|
var EventPluginHub = { |
|
|
|
/** |
|
* Methods for injecting dependencies. |
|
*/ |
|
injection: { |
|
|
|
/** |
|
* @param {object} InjectedMount |
|
* @public |
|
*/ |
|
injectMount: EventPluginUtils.injection.injectMount, |
|
|
|
/** |
|
* @param {object} InjectedInstanceHandle |
|
* @public |
|
*/ |
|
injectInstanceHandle: function(InjectedInstanceHandle) { |
|
InstanceHandle = InjectedInstanceHandle; |
|
if ("production" !== process.env.NODE_ENV) { |
|
validateInstanceHandle(); |
|
} |
|
}, |
|
|
|
getInstanceHandle: function() { |
|
if ("production" !== process.env.NODE_ENV) { |
|
validateInstanceHandle(); |
|
} |
|
return InstanceHandle; |
|
}, |
|
|
|
/** |
|
* @param {array} InjectedEventPluginOrder |
|
* @public |
|
*/ |
|
injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder, |
|
|
|
/** |
|
* @param {object} injectedNamesToPlugins Map from names to plugin modules. |
|
*/ |
|
injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName |
|
|
|
}, |
|
|
|
eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs, |
|
|
|
registrationNameModules: EventPluginRegistry.registrationNameModules, |
|
|
|
/** |
|
* Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. |
|
* |
|
* @param {string} id ID of the DOM element. |
|
* @param {string} registrationName Name of listener (e.g. `onClick`). |
|
* @param {?function} listener The callback to store. |
|
*/ |
|
putListener: function(id, registrationName, listener) { |
|
("production" !== process.env.NODE_ENV ? invariant( |
|
!listener || typeof listener === 'function', |
|
'Expected %s listener to be a function, instead got type %s', |
|
registrationName, typeof listener |
|
) : invariant(!listener || typeof listener === 'function')); |
|
|
|
var bankForRegistrationName = |
|
listenerBank[registrationName] || (listenerBank[registrationName] = {}); |
|
bankForRegistrationName[id] = listener; |
|
}, |
|
|
|
/** |
|
* @param {string} id ID of the DOM element. |
|
* @param {string} registrationName Name of listener (e.g. `onClick`). |
|
* @return {?function} The stored callback. |
|
*/ |
|
getListener: function(id, registrationName) { |
|
var bankForRegistrationName = listenerBank[registrationName]; |
|
return bankForRegistrationName && bankForRegistrationName[id]; |
|
}, |
|
|
|
/** |
|
* Deletes a listener from the registration bank. |
|
* |
|
* @param {string} id ID of the DOM element. |
|
* @param {string} registrationName Name of listener (e.g. `onClick`). |
|
*/ |
|
deleteListener: function(id, registrationName) { |
|
var bankForRegistrationName = listenerBank[registrationName]; |
|
if (bankForRegistrationName) { |
|
delete bankForRegistrationName[id]; |
|
} |
|
}, |
|
|
|
/** |
|
* Deletes all listeners for the DOM element with the supplied ID. |
|
* |
|
* @param {string} id ID of the DOM element. |
|
*/ |
|
deleteAllListeners: function(id) { |
|
for (var registrationName in listenerBank) { |
|
delete listenerBank[registrationName][id]; |
|
} |
|
}, |
|
|
|
/** |
|
* Allows registered plugins an opportunity to extract events from top-level |
|
* native browser events. |
|
* |
|
* @param {string} topLevelType Record from `EventConstants`. |
|
* @param {DOMEventTarget} topLevelTarget The listening component root node. |
|
* @param {string} topLevelTargetID ID of `topLevelTarget`. |
|
* @param {object} nativeEvent Native browser event. |
|
* @return {*} An accumulation of synthetic events. |
|
* @internal |
|
*/ |
|
extractEvents: function( |
|
topLevelType, |
|
topLevelTarget, |
|
topLevelTargetID, |
|
nativeEvent) { |
|
var events; |
|
var plugins = EventPluginRegistry.plugins; |
|
for (var i = 0, l = plugins.length; i < l; i++) { |
|
// Not every plugin in the ordering may be loaded at runtime. |
|
var possiblePlugin = plugins[i]; |
|
if (possiblePlugin) { |
|
var extractedEvents = possiblePlugin.extractEvents( |
|
topLevelType, |
|
topLevelTarget, |
|
topLevelTargetID, |
|
nativeEvent |
|
); |
|
if (extractedEvents) { |
|
events = accumulateInto(events, extractedEvents); |
|
} |
|
} |
|
} |
|
return events; |
|
}, |
|
|
|
/** |
|
* Enqueues a synthetic event that should be dispatched when |
|
* `processEventQueue` is invoked. |
|
* |
|
* @param {*} events An accumulation of synthetic events. |
|
* @internal |
|
*/ |
|
enqueueEvents: function(events) { |
|
if (events) { |
|
eventQueue = accumulateInto(eventQueue, events); |
|
} |
|
}, |
|
|
|
/** |
|
* Dispatches all synthetic events on the event queue. |
|
* |
|
* @internal |
|
*/ |
|
processEventQueue: function() { |
|
// Set `eventQueue` to null before processing it so that we can tell if more |
|
// events get enqueued while processing. |
|
var processingEventQueue = eventQueue; |
|
eventQueue = null; |
|
forEachAccumulated(processingEventQueue, executeDispatchesAndRelease); |
|
("production" !== process.env.NODE_ENV ? invariant( |
|
!eventQueue, |
|
'processEventQueue(): Additional events were enqueued while processing ' + |
|
'an event queue. Support for this has not yet been implemented.' |
|
) : invariant(!eventQueue)); |
|
}, |
|
|
|
/** |
|
* These are needed for tests only. Do not use! |
|
*/ |
|
__purge: function() { |
|
listenerBank = {}; |
|
}, |
|
|
|
__getListenerBank: function() { |
|
return listenerBank; |
|
} |
|
|
|
}; |
|
|
|
module.exports = EventPluginHub;
|
|
|