react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / event / EventPluginHub.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 EventPluginHub9*/1011"use strict";1213var EventPluginRegistry = require('EventPluginRegistry');14var EventPluginUtils = require('EventPluginUtils');1516var accumulateInto = require('accumulateInto');17var forEachAccumulated = require('forEachAccumulated');18var invariant = require('invariant');1920/**21* Internal store for event listeners22*/23var listenerBank = {};2425/**26* Internal queue of events that have accumulated their dispatches and are27* waiting to have their dispatches executed.28*/29var eventQueue = null;3031/**32* Dispatches an event and releases it back into the pool, unless persistent.33*34* @param {?object} event Synthetic event to be dispatched.35* @private36*/37var executeDispatchesAndRelease = function(event) {38if (event) {39var executeDispatch = EventPluginUtils.executeDispatch;40// Plugins can provide custom behavior when dispatching events.41var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event);42if (PluginModule && PluginModule.executeDispatch) {43executeDispatch = PluginModule.executeDispatch;44}45EventPluginUtils.executeDispatchesInOrder(event, executeDispatch);4647if (!event.isPersistent()) {48event.constructor.release(event);49}50}51};5253/**54* - `InstanceHandle`: [required] Module that performs logical traversals of DOM55* hierarchy given ids of the logical DOM elements involved.56*/57var InstanceHandle = null;5859function validateInstanceHandle() {60var invalid = !InstanceHandle||61!InstanceHandle.traverseTwoPhase ||62!InstanceHandle.traverseEnterLeave;63if (invalid) {64throw new Error('InstanceHandle not injected before use!');65}66}6768/**69* This is a unified interface for event plugins to be installed and configured.70*71* Event plugins can implement the following properties:72*73* `extractEvents` {function(string, DOMEventTarget, string, object): *}74* Required. When a top-level event is fired, this method is expected to75* extract synthetic events that will in turn be queued and dispatched.76*77* `eventTypes` {object}78* Optional, plugins that fire events must publish a mapping of registration79* names that are used to register listeners. Values of this mapping must80* be objects that contain `registrationName` or `phasedRegistrationNames`.81*82* `executeDispatch` {function(object, function, string)}83* Optional, allows plugins to override how an event gets dispatched. By84* default, the listener is simply invoked.85*86* Each plugin that is injected into `EventsPluginHub` is immediately operable.87*88* @public89*/90var EventPluginHub = {9192/**93* Methods for injecting dependencies.94*/95injection: {9697/**98* @param {object} InjectedMount99* @public100*/101injectMount: EventPluginUtils.injection.injectMount,102103/**104* @param {object} InjectedInstanceHandle105* @public106*/107injectInstanceHandle: function(InjectedInstanceHandle) {108InstanceHandle = InjectedInstanceHandle;109if (__DEV__) {110validateInstanceHandle();111}112},113114getInstanceHandle: function() {115if (__DEV__) {116validateInstanceHandle();117}118return InstanceHandle;119},120121/**122* @param {array} InjectedEventPluginOrder123* @public124*/125injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,126127/**128* @param {object} injectedNamesToPlugins Map from names to plugin modules.129*/130injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName131132},133134eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,135136registrationNameModules: EventPluginRegistry.registrationNameModules,137138/**139* Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent.140*141* @param {string} id ID of the DOM element.142* @param {string} registrationName Name of listener (e.g. `onClick`).143* @param {?function} listener The callback to store.144*/145putListener: function(id, registrationName, listener) {146invariant(147!listener || typeof listener === 'function',148'Expected %s listener to be a function, instead got type %s',149registrationName, typeof listener150);151152var bankForRegistrationName =153listenerBank[registrationName] || (listenerBank[registrationName] = {});154bankForRegistrationName[id] = listener;155},156157/**158* @param {string} id ID of the DOM element.159* @param {string} registrationName Name of listener (e.g. `onClick`).160* @return {?function} The stored callback.161*/162getListener: function(id, registrationName) {163var bankForRegistrationName = listenerBank[registrationName];164return bankForRegistrationName && bankForRegistrationName[id];165},166167/**168* Deletes a listener from the registration bank.169*170* @param {string} id ID of the DOM element.171* @param {string} registrationName Name of listener (e.g. `onClick`).172*/173deleteListener: function(id, registrationName) {174var bankForRegistrationName = listenerBank[registrationName];175if (bankForRegistrationName) {176delete bankForRegistrationName[id];177}178},179180/**181* Deletes all listeners for the DOM element with the supplied ID.182*183* @param {string} id ID of the DOM element.184*/185deleteAllListeners: function(id) {186for (var registrationName in listenerBank) {187delete listenerBank[registrationName][id];188}189},190191/**192* Allows registered plugins an opportunity to extract events from top-level193* native browser events.194*195* @param {string} topLevelType Record from `EventConstants`.196* @param {DOMEventTarget} topLevelTarget The listening component root node.197* @param {string} topLevelTargetID ID of `topLevelTarget`.198* @param {object} nativeEvent Native browser event.199* @return {*} An accumulation of synthetic events.200* @internal201*/202extractEvents: function(203topLevelType,204topLevelTarget,205topLevelTargetID,206nativeEvent) {207var events;208var plugins = EventPluginRegistry.plugins;209for (var i = 0, l = plugins.length; i < l; i++) {210// Not every plugin in the ordering may be loaded at runtime.211var possiblePlugin = plugins[i];212if (possiblePlugin) {213var extractedEvents = possiblePlugin.extractEvents(214topLevelType,215topLevelTarget,216topLevelTargetID,217nativeEvent218);219if (extractedEvents) {220events = accumulateInto(events, extractedEvents);221}222}223}224return events;225},226227/**228* Enqueues a synthetic event that should be dispatched when229* `processEventQueue` is invoked.230*231* @param {*} events An accumulation of synthetic events.232* @internal233*/234enqueueEvents: function(events) {235if (events) {236eventQueue = accumulateInto(eventQueue, events);237}238},239240/**241* Dispatches all synthetic events on the event queue.242*243* @internal244*/245processEventQueue: function() {246// Set `eventQueue` to null before processing it so that we can tell if more247// events get enqueued while processing.248var processingEventQueue = eventQueue;249eventQueue = null;250forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);251invariant(252!eventQueue,253'processEventQueue(): Additional events were enqueued while processing ' +254'an event queue. Support for this has not yet been implemented.'255);256},257258/**259* These are needed for tests only. Do not use!260*/261__purge: function() {262listenerBank = {};263},264265__getListenerBank: function() {266return listenerBank;267}268269};270271module.exports = EventPluginHub;272273274