Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 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 ReactEventListener
10
* @typechecks static-only
11
*/
12
13
"use strict";
14
15
var EventListener = require('EventListener');
16
var ExecutionEnvironment = require('ExecutionEnvironment');
17
var PooledClass = require('PooledClass');
18
var ReactInstanceHandles = require('ReactInstanceHandles');
19
var ReactMount = require('ReactMount');
20
var ReactUpdates = require('ReactUpdates');
21
22
var assign = require('Object.assign');
23
var getEventTarget = require('getEventTarget');
24
var getUnboundedScrollPosition = require('getUnboundedScrollPosition');
25
26
/**
27
* Finds the parent React component of `node`.
28
*
29
* @param {*} node
30
* @return {?DOMEventTarget} Parent container, or `null` if the specified node
31
* is not nested.
32
*/
33
function findParent(node) {
34
// TODO: It may be a good idea to cache this to prevent unnecessary DOM
35
// traversal, but caching is difficult to do correctly without using a
36
// mutation observer to listen for all DOM changes.
37
var nodeID = ReactMount.getID(node);
38
var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
39
var container = ReactMount.findReactContainerForID(rootID);
40
var parent = ReactMount.getFirstReactDOM(container);
41
return parent;
42
}
43
44
// Used to store ancestor hierarchy in top level callback
45
function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
46
this.topLevelType = topLevelType;
47
this.nativeEvent = nativeEvent;
48
this.ancestors = [];
49
}
50
assign(TopLevelCallbackBookKeeping.prototype, {
51
destructor: function() {
52
this.topLevelType = null;
53
this.nativeEvent = null;
54
this.ancestors.length = 0;
55
}
56
});
57
PooledClass.addPoolingTo(
58
TopLevelCallbackBookKeeping,
59
PooledClass.twoArgumentPooler
60
);
61
62
function handleTopLevelImpl(bookKeeping) {
63
var topLevelTarget = ReactMount.getFirstReactDOM(
64
getEventTarget(bookKeeping.nativeEvent)
65
) || window;
66
67
// Loop through the hierarchy, in case there's any nested components.
68
// It's important that we build the array of ancestors before calling any
69
// event handlers, because event handlers can modify the DOM, leading to
70
// inconsistencies with ReactMount's node cache. See #1105.
71
var ancestor = topLevelTarget;
72
while (ancestor) {
73
bookKeeping.ancestors.push(ancestor);
74
ancestor = findParent(ancestor);
75
}
76
77
for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
78
topLevelTarget = bookKeeping.ancestors[i];
79
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
80
ReactEventListener._handleTopLevel(
81
bookKeeping.topLevelType,
82
topLevelTarget,
83
topLevelTargetID,
84
bookKeeping.nativeEvent
85
);
86
}
87
}
88
89
function scrollValueMonitor(cb) {
90
var scrollPosition = getUnboundedScrollPosition(window);
91
cb(scrollPosition);
92
}
93
94
var ReactEventListener = {
95
_enabled: true,
96
_handleTopLevel: null,
97
98
WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,
99
100
setHandleTopLevel: function(handleTopLevel) {
101
ReactEventListener._handleTopLevel = handleTopLevel;
102
},
103
104
setEnabled: function(enabled) {
105
ReactEventListener._enabled = !!enabled;
106
},
107
108
isEnabled: function() {
109
return ReactEventListener._enabled;
110
},
111
112
113
/**
114
* Traps top-level events by using event bubbling.
115
*
116
* @param {string} topLevelType Record from `EventConstants`.
117
* @param {string} handlerBaseName Event name (e.g. "click").
118
* @param {object} handle Element on which to attach listener.
119
* @return {object} An object with a remove function which will forcefully
120
* remove the listener.
121
* @internal
122
*/
123
trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
124
var element = handle;
125
if (!element) {
126
return;
127
}
128
return EventListener.listen(
129
element,
130
handlerBaseName,
131
ReactEventListener.dispatchEvent.bind(null, topLevelType)
132
);
133
},
134
135
/**
136
* Traps a top-level event by using event capturing.
137
*
138
* @param {string} topLevelType Record from `EventConstants`.
139
* @param {string} handlerBaseName Event name (e.g. "click").
140
* @param {object} handle Element on which to attach listener.
141
* @return {object} An object with a remove function which will forcefully
142
* remove the listener.
143
* @internal
144
*/
145
trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
146
var element = handle;
147
if (!element) {
148
return;
149
}
150
return EventListener.capture(
151
element,
152
handlerBaseName,
153
ReactEventListener.dispatchEvent.bind(null, topLevelType)
154
);
155
},
156
157
monitorScrollValue: function(refresh) {
158
var callback = scrollValueMonitor.bind(null, refresh);
159
EventListener.listen(window, 'scroll', callback);
160
EventListener.listen(window, 'resize', callback);
161
},
162
163
dispatchEvent: function(topLevelType, nativeEvent) {
164
if (!ReactEventListener._enabled) {
165
return;
166
}
167
168
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
169
topLevelType,
170
nativeEvent
171
);
172
try {
173
// Event queue being processed in the same cycle allows
174
// `preventDefault`.
175
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
176
} finally {
177
TopLevelCallbackBookKeeping.release(bookKeeping);
178
}
179
}
180
};
181
182
module.exports = ReactEventListener;
183
184