text: /**
   * 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('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;
  