mirror of
https://github.com/facebook/react.git
synced 2026-02-26 01:35:00 +00:00
248 lines
6.9 KiB
JavaScript
248 lines
6.9 KiB
JavaScript
/**
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {DispatchConfig} from './ReactSyntheticEventType';
|
|
import type {
|
|
AnyNativeEvent,
|
|
PluginName,
|
|
PluginModule,
|
|
} from './PluginModuleType';
|
|
|
|
import invariant from 'fbjs/lib/invariant';
|
|
|
|
type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>};
|
|
type EventPluginOrder = null | Array<PluginName>;
|
|
|
|
/**
|
|
* Injectable ordering of event plugins.
|
|
*/
|
|
let eventPluginOrder: EventPluginOrder = null;
|
|
|
|
/**
|
|
* Injectable mapping from names to event plugin modules.
|
|
*/
|
|
const namesToPlugins: NamesToPlugins = {};
|
|
|
|
/**
|
|
* Recomputes the plugin list using the injected plugins and plugin ordering.
|
|
*
|
|
* @private
|
|
*/
|
|
function recomputePluginOrdering(): void {
|
|
if (!eventPluginOrder) {
|
|
// Wait until an `eventPluginOrder` is injected.
|
|
return;
|
|
}
|
|
for (const pluginName in namesToPlugins) {
|
|
const pluginModule = namesToPlugins[pluginName];
|
|
const pluginIndex = eventPluginOrder.indexOf(pluginName);
|
|
invariant(
|
|
pluginIndex > -1,
|
|
'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
|
|
'the plugin ordering, `%s`.',
|
|
pluginName,
|
|
);
|
|
if (plugins[pluginIndex]) {
|
|
continue;
|
|
}
|
|
invariant(
|
|
pluginModule.extractEvents,
|
|
'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
|
|
'method, but `%s` does not.',
|
|
pluginName,
|
|
);
|
|
plugins[pluginIndex] = pluginModule;
|
|
const publishedEvents = pluginModule.eventTypes;
|
|
for (const eventName in publishedEvents) {
|
|
invariant(
|
|
publishEventForPlugin(
|
|
publishedEvents[eventName],
|
|
pluginModule,
|
|
eventName,
|
|
),
|
|
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
|
|
eventName,
|
|
pluginName,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Publishes an event so that it can be dispatched by the supplied plugin.
|
|
*
|
|
* @param {object} dispatchConfig Dispatch configuration for the event.
|
|
* @param {object} PluginModule Plugin publishing the event.
|
|
* @return {boolean} True if the event was successfully published.
|
|
* @private
|
|
*/
|
|
function publishEventForPlugin(
|
|
dispatchConfig: DispatchConfig,
|
|
pluginModule: PluginModule<AnyNativeEvent>,
|
|
eventName: string,
|
|
): boolean {
|
|
invariant(
|
|
!eventNameDispatchConfigs.hasOwnProperty(eventName),
|
|
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
|
'event name, `%s`.',
|
|
eventName,
|
|
);
|
|
eventNameDispatchConfigs[eventName] = dispatchConfig;
|
|
|
|
const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
|
|
if (phasedRegistrationNames) {
|
|
for (const phaseName in phasedRegistrationNames) {
|
|
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
|
|
const phasedRegistrationName = phasedRegistrationNames[phaseName];
|
|
publishRegistrationName(
|
|
phasedRegistrationName,
|
|
pluginModule,
|
|
eventName,
|
|
);
|
|
}
|
|
}
|
|
return true;
|
|
} else if (dispatchConfig.registrationName) {
|
|
publishRegistrationName(
|
|
dispatchConfig.registrationName,
|
|
pluginModule,
|
|
eventName,
|
|
);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Publishes a registration name that is used to identify dispatched events.
|
|
*
|
|
* @param {string} registrationName Registration name to add.
|
|
* @param {object} PluginModule Plugin publishing the event.
|
|
* @private
|
|
*/
|
|
function publishRegistrationName(
|
|
registrationName: string,
|
|
pluginModule: PluginModule<AnyNativeEvent>,
|
|
eventName: string,
|
|
): void {
|
|
invariant(
|
|
!registrationNameModules[registrationName],
|
|
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
|
'registration name, `%s`.',
|
|
registrationName,
|
|
);
|
|
registrationNameModules[registrationName] = pluginModule;
|
|
registrationNameDependencies[registrationName] =
|
|
pluginModule.eventTypes[eventName].dependencies;
|
|
|
|
if (__DEV__) {
|
|
const lowerCasedName = registrationName.toLowerCase();
|
|
possibleRegistrationNames[lowerCasedName] = registrationName;
|
|
|
|
if (registrationName === 'onDoubleClick') {
|
|
possibleRegistrationNames.ondblclick = registrationName;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers plugins so that they can extract and dispatch events.
|
|
*
|
|
* @see {EventPluginHub}
|
|
*/
|
|
|
|
/**
|
|
* Ordered list of injected plugins.
|
|
*/
|
|
export const plugins = [];
|
|
|
|
/**
|
|
* Mapping from event name to dispatch config
|
|
*/
|
|
export const eventNameDispatchConfigs = {};
|
|
|
|
/**
|
|
* Mapping from registration name to plugin module
|
|
*/
|
|
export const registrationNameModules = {};
|
|
|
|
/**
|
|
* Mapping from registration name to event name
|
|
*/
|
|
export const registrationNameDependencies = {};
|
|
|
|
/**
|
|
* Mapping from lowercase registration names to the properly cased version,
|
|
* used to warn in the case of missing event handlers. Available
|
|
* only in __DEV__.
|
|
* @type {Object}
|
|
*/
|
|
export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
|
|
// Trust the developer to only use possibleRegistrationNames in __DEV__
|
|
|
|
/**
|
|
* Injects an ordering of plugins (by plugin name). This allows the ordering
|
|
* to be decoupled from injection of the actual plugins so that ordering is
|
|
* always deterministic regardless of packaging, on-the-fly injection, etc.
|
|
*
|
|
* @param {array} InjectedEventPluginOrder
|
|
* @internal
|
|
* @see {EventPluginHub.injection.injectEventPluginOrder}
|
|
*/
|
|
export function injectEventPluginOrder(
|
|
injectedEventPluginOrder: EventPluginOrder,
|
|
): void {
|
|
invariant(
|
|
!eventPluginOrder,
|
|
'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
|
|
'once. You are likely trying to load more than one copy of React.',
|
|
);
|
|
// Clone the ordering so it cannot be dynamically mutated.
|
|
eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
|
|
recomputePluginOrdering();
|
|
}
|
|
|
|
/**
|
|
* Injects plugins to be used by `EventPluginHub`. The plugin names must be
|
|
* in the ordering injected by `injectEventPluginOrder`.
|
|
*
|
|
* Plugins can be injected as part of page initialization or on-the-fly.
|
|
*
|
|
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
|
* @internal
|
|
* @see {EventPluginHub.injection.injectEventPluginsByName}
|
|
*/
|
|
export function injectEventPluginsByName(
|
|
injectedNamesToPlugins: NamesToPlugins,
|
|
): void {
|
|
let isOrderingDirty = false;
|
|
for (const pluginName in injectedNamesToPlugins) {
|
|
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
|
|
continue;
|
|
}
|
|
const pluginModule = injectedNamesToPlugins[pluginName];
|
|
if (
|
|
!namesToPlugins.hasOwnProperty(pluginName) ||
|
|
namesToPlugins[pluginName] !== pluginModule
|
|
) {
|
|
invariant(
|
|
!namesToPlugins[pluginName],
|
|
'EventPluginRegistry: Cannot inject two different event plugins ' +
|
|
'using the same name, `%s`.',
|
|
pluginName,
|
|
);
|
|
namesToPlugins[pluginName] = pluginModule;
|
|
isOrderingDirty = true;
|
|
}
|
|
}
|
|
if (isOrderingDirty) {
|
|
recomputePluginOrdering();
|
|
}
|
|
}
|