Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81152 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule EventPluginHub
10
*/
11
12
"use strict";
13
14
var EventPluginRegistry = require('EventPluginRegistry');
15
var EventPluginUtils = require('EventPluginUtils');
16
17
var accumulateInto = require('accumulateInto');
18
var forEachAccumulated = require('forEachAccumulated');
19
var invariant = require('invariant');
20
21
/**
22
* Internal store for event listeners
23
*/
24
var listenerBank = {};
25
26
/**
27
* Internal queue of events that have accumulated their dispatches and are
28
* waiting to have their dispatches executed.
29
*/
30
var eventQueue = null;
31
32
/**
33
* Dispatches an event and releases it back into the pool, unless persistent.
34
*
35
* @param {?object} event Synthetic event to be dispatched.
36
* @private
37
*/
38
var executeDispatchesAndRelease = function(event) {
39
if (event) {
40
var executeDispatch = EventPluginUtils.executeDispatch;
41
// Plugins can provide custom behavior when dispatching events.
42
var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event);
43
if (PluginModule && PluginModule.executeDispatch) {
44
executeDispatch = PluginModule.executeDispatch;
45
}
46
EventPluginUtils.executeDispatchesInOrder(event, executeDispatch);
47
48
if (!event.isPersistent()) {
49
event.constructor.release(event);
50
}
51
}
52
};
53
54
/**
55
* - `InstanceHandle`: [required] Module that performs logical traversals of DOM
56
* hierarchy given ids of the logical DOM elements involved.
57
*/
58
var InstanceHandle = null;
59
60
function validateInstanceHandle() {
61
var invalid = !InstanceHandle||
62
!InstanceHandle.traverseTwoPhase ||
63
!InstanceHandle.traverseEnterLeave;
64
if (invalid) {
65
throw new Error('InstanceHandle not injected before use!');
66
}
67
}
68
69
/**
70
* This is a unified interface for event plugins to be installed and configured.
71
*
72
* Event plugins can implement the following properties:
73
*
74
* `extractEvents` {function(string, DOMEventTarget, string, object): *}
75
* Required. When a top-level event is fired, this method is expected to
76
* extract synthetic events that will in turn be queued and dispatched.
77
*
78
* `eventTypes` {object}
79
* Optional, plugins that fire events must publish a mapping of registration
80
* names that are used to register listeners. Values of this mapping must
81
* be objects that contain `registrationName` or `phasedRegistrationNames`.
82
*
83
* `executeDispatch` {function(object, function, string)}
84
* Optional, allows plugins to override how an event gets dispatched. By
85
* default, the listener is simply invoked.
86
*
87
* Each plugin that is injected into `EventsPluginHub` is immediately operable.
88
*
89
* @public
90
*/
91
var EventPluginHub = {
92
93
/**
94
* Methods for injecting dependencies.
95
*/
96
injection: {
97
98
/**
99
* @param {object} InjectedMount
100
* @public
101
*/
102
injectMount: EventPluginUtils.injection.injectMount,
103
104
/**
105
* @param {object} InjectedInstanceHandle
106
* @public
107
*/
108
injectInstanceHandle: function(InjectedInstanceHandle) {
109
InstanceHandle = InjectedInstanceHandle;
110
if (__DEV__) {
111
validateInstanceHandle();
112
}
113
},
114
115
getInstanceHandle: function() {
116
if (__DEV__) {
117
validateInstanceHandle();
118
}
119
return InstanceHandle;
120
},
121
122
/**
123
* @param {array} InjectedEventPluginOrder
124
* @public
125
*/
126
injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
127
128
/**
129
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
130
*/
131
injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName
132
133
},
134
135
eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,
136
137
registrationNameModules: EventPluginRegistry.registrationNameModules,
138
139
/**
140
* Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent.
141
*
142
* @param {string} id ID of the DOM element.
143
* @param {string} registrationName Name of listener (e.g. `onClick`).
144
* @param {?function} listener The callback to store.
145
*/
146
putListener: function(id, registrationName, listener) {
147
invariant(
148
!listener || typeof listener === 'function',
149
'Expected %s listener to be a function, instead got type %s',
150
registrationName, typeof listener
151
);
152
153
var bankForRegistrationName =
154
listenerBank[registrationName] || (listenerBank[registrationName] = {});
155
bankForRegistrationName[id] = listener;
156
},
157
158
/**
159
* @param {string} id ID of the DOM element.
160
* @param {string} registrationName Name of listener (e.g. `onClick`).
161
* @return {?function} The stored callback.
162
*/
163
getListener: function(id, registrationName) {
164
var bankForRegistrationName = listenerBank[registrationName];
165
return bankForRegistrationName && bankForRegistrationName[id];
166
},
167
168
/**
169
* Deletes a listener from the registration bank.
170
*
171
* @param {string} id ID of the DOM element.
172
* @param {string} registrationName Name of listener (e.g. `onClick`).
173
*/
174
deleteListener: function(id, registrationName) {
175
var bankForRegistrationName = listenerBank[registrationName];
176
if (bankForRegistrationName) {
177
delete bankForRegistrationName[id];
178
}
179
},
180
181
/**
182
* Deletes all listeners for the DOM element with the supplied ID.
183
*
184
* @param {string} id ID of the DOM element.
185
*/
186
deleteAllListeners: function(id) {
187
for (var registrationName in listenerBank) {
188
delete listenerBank[registrationName][id];
189
}
190
},
191
192
/**
193
* Allows registered plugins an opportunity to extract events from top-level
194
* native browser events.
195
*
196
* @param {string} topLevelType Record from `EventConstants`.
197
* @param {DOMEventTarget} topLevelTarget The listening component root node.
198
* @param {string} topLevelTargetID ID of `topLevelTarget`.
199
* @param {object} nativeEvent Native browser event.
200
* @return {*} An accumulation of synthetic events.
201
* @internal
202
*/
203
extractEvents: function(
204
topLevelType,
205
topLevelTarget,
206
topLevelTargetID,
207
nativeEvent) {
208
var events;
209
var plugins = EventPluginRegistry.plugins;
210
for (var i = 0, l = plugins.length; i < l; i++) {
211
// Not every plugin in the ordering may be loaded at runtime.
212
var possiblePlugin = plugins[i];
213
if (possiblePlugin) {
214
var extractedEvents = possiblePlugin.extractEvents(
215
topLevelType,
216
topLevelTarget,
217
topLevelTargetID,
218
nativeEvent
219
);
220
if (extractedEvents) {
221
events = accumulateInto(events, extractedEvents);
222
}
223
}
224
}
225
return events;
226
},
227
228
/**
229
* Enqueues a synthetic event that should be dispatched when
230
* `processEventQueue` is invoked.
231
*
232
* @param {*} events An accumulation of synthetic events.
233
* @internal
234
*/
235
enqueueEvents: function(events) {
236
if (events) {
237
eventQueue = accumulateInto(eventQueue, events);
238
}
239
},
240
241
/**
242
* Dispatches all synthetic events on the event queue.
243
*
244
* @internal
245
*/
246
processEventQueue: function() {
247
// Set `eventQueue` to null before processing it so that we can tell if more
248
// events get enqueued while processing.
249
var processingEventQueue = eventQueue;
250
eventQueue = null;
251
forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);
252
invariant(
253
!eventQueue,
254
'processEventQueue(): Additional events were enqueued while processing ' +
255
'an event queue. Support for this has not yet been implemented.'
256
);
257
},
258
259
/**
260
* These are needed for tests only. Do not use!
261
*/
262
__purge: function() {
263
listenerBank = {};
264
},
265
266
__getListenerBank: function() {
267
return listenerBank;
268
}
269
270
};
271
272
module.exports = EventPluginHub;
273
274