UNPKG

react-native

Version:

A framework for building native apps using React

337 lines (306 loc) • 10 kB
/** * Copyright 2013-present, 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 EventPluginRegistry * @flow */ 'use strict'; import type { DispatchConfig, ReactSyntheticEvent, } from 'ReactSyntheticEventType'; import type { AnyNativeEvent, PluginName, PluginModule, } from 'PluginModuleType'; type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>}; type EventPluginOrder = null | Array<PluginName>; var invariant = require('fbjs/lib/invariant'); /** * Injectable ordering of event plugins. */ var eventPluginOrder: EventPluginOrder = null; /** * Injectable mapping from names to event plugin modules. */ var 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 (var pluginName in namesToPlugins) { var pluginModule = namesToPlugins[pluginName]; var pluginIndex = eventPluginOrder.indexOf(pluginName); invariant( pluginIndex > -1, 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' + 'the plugin ordering, `%s`.', pluginName, ); if (EventPluginRegistry.plugins[pluginIndex]) { continue; } invariant( pluginModule.extractEvents, 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' + 'method, but `%s` does not.', pluginName, ); EventPluginRegistry.plugins[pluginIndex] = pluginModule; var publishedEvents = pluginModule.eventTypes; for (var 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( !EventPluginRegistry.eventNameDispatchConfigs.hasOwnProperty(eventName), 'EventPluginHub: More than one plugin attempted to publish the same ' + 'event name, `%s`.', eventName, ); EventPluginRegistry.eventNameDispatchConfigs[eventName] = dispatchConfig; var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames; if (phasedRegistrationNames) { for (var phaseName in phasedRegistrationNames) { if (phasedRegistrationNames.hasOwnProperty(phaseName)) { var 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 and * can be used with `EventPluginHub.putListener` to register listeners. * * @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( !EventPluginRegistry.registrationNameModules[registrationName], 'EventPluginHub: More than one plugin attempted to publish the same ' + 'registration name, `%s`.', registrationName, ); EventPluginRegistry.registrationNameModules[registrationName] = pluginModule; EventPluginRegistry.registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies; if (__DEV__) { var lowerCasedName = registrationName.toLowerCase(); EventPluginRegistry.possibleRegistrationNames[lowerCasedName] = registrationName; if (registrationName === 'onDoubleClick') { EventPluginRegistry.possibleRegistrationNames.ondblclick = registrationName; } } } /** * Registers plugins so that they can extract and dispatch events. * * @see {EventPluginHub} */ var EventPluginRegistry = { /** * Ordered list of injected plugins. */ plugins: [], /** * Mapping from event name to dispatch config */ eventNameDispatchConfigs: {}, /** * Mapping from registration name to plugin module */ registrationNameModules: {}, /** * Mapping from registration name to event name */ 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} */ 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} */ injectEventPluginOrder: function( 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} */ injectEventPluginsByName: function( injectedNamesToPlugins: NamesToPlugins ): void { var isOrderingDirty = false; for (var pluginName in injectedNamesToPlugins) { if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { continue; } var 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(); } }, /** * Looks up the plugin for the supplied event. * * @param {object} event A synthetic event. * @return {?object} The plugin that created the supplied event. * @internal */ getPluginModuleForEvent: function( event: ReactSyntheticEvent, ): null | PluginModule<AnyNativeEvent> { var dispatchConfig = event.dispatchConfig; if (dispatchConfig.registrationName) { return EventPluginRegistry.registrationNameModules[ dispatchConfig.registrationName ] || null; } if (dispatchConfig.phasedRegistrationNames !== undefined) { // pulling phasedRegistrationNames out of dispatchConfig helps Flow see // that it is not undefined. var {phasedRegistrationNames} = dispatchConfig; for (var phase in phasedRegistrationNames) { if (!phasedRegistrationNames.hasOwnProperty(phase)) { continue; } var pluginModule = EventPluginRegistry.registrationNameModules[ phasedRegistrationNames[phase] ]; if (pluginModule) { return pluginModule; } } } return null; }, /** * Exposed for unit testing. * @private */ _resetEventPlugins: function(): void { eventPluginOrder = null; for (var pluginName in namesToPlugins) { if (namesToPlugins.hasOwnProperty(pluginName)) { delete namesToPlugins[pluginName]; } } EventPluginRegistry.plugins.length = 0; var eventNameDispatchConfigs = EventPluginRegistry.eventNameDispatchConfigs; for (var eventName in eventNameDispatchConfigs) { if (eventNameDispatchConfigs.hasOwnProperty(eventName)) { delete eventNameDispatchConfigs[eventName]; } } var registrationNameModules = EventPluginRegistry.registrationNameModules; for (var registrationName in registrationNameModules) { if (registrationNameModules.hasOwnProperty(registrationName)) { delete registrationNameModules[registrationName]; } } if (__DEV__) { var possibleRegistrationNames = EventPluginRegistry.possibleRegistrationNames; for (var lowerCasedName in possibleRegistrationNames) { if (possibleRegistrationNames.hasOwnProperty(lowerCasedName)) { delete possibleRegistrationNames[lowerCasedName]; } } } }, }; module.exports = EventPluginRegistry;