1 | /**
|
2 | * Copyright 2013-present, 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 | */
|
10 |
|
11 | ;
|
12 |
|
13 | var EventPluginUtils = require('./EventPluginUtils');
|
14 | var EventPropagators = require('./EventPropagators');
|
15 | var ResponderSyntheticEvent = require('./ResponderSyntheticEvent');
|
16 | var ResponderTouchHistoryStore = require('./ResponderTouchHistoryStore');
|
17 |
|
18 | var accumulate = require('./accumulate');
|
19 |
|
20 | var isStartish = EventPluginUtils.isStartish;
|
21 | var isMoveish = EventPluginUtils.isMoveish;
|
22 | var isEndish = EventPluginUtils.isEndish;
|
23 | var executeDirectDispatch = EventPluginUtils.executeDirectDispatch;
|
24 | var hasDispatches = EventPluginUtils.hasDispatches;
|
25 | var executeDispatchesInOrderStopAtTrue = EventPluginUtils.executeDispatchesInOrderStopAtTrue;
|
26 |
|
27 | /**
|
28 | * Instance of element that should respond to touch/move types of interactions,
|
29 | * as indicated explicitly by relevant callbacks.
|
30 | */
|
31 | var responderInst = null;
|
32 |
|
33 | /**
|
34 | * Count of current touches. A textInput should become responder iff the
|
35 | * selection changes while there is a touch on the screen.
|
36 | */
|
37 | var trackedTouchCount = 0;
|
38 |
|
39 | /**
|
40 | * Last reported number of active touches.
|
41 | */
|
42 | var previousActiveTouches = 0;
|
43 |
|
44 | var changeResponder = function (nextResponderInst, blockHostResponder) {
|
45 | var oldResponderInst = responderInst;
|
46 | responderInst = nextResponderInst;
|
47 | if (ResponderEventPlugin.GlobalResponderHandler !== null) {
|
48 | ResponderEventPlugin.GlobalResponderHandler.onChange(oldResponderInst, nextResponderInst, blockHostResponder);
|
49 | }
|
50 | };
|
51 |
|
52 | var eventTypes = {
|
53 | /**
|
54 | * On a `touchStart`/`mouseDown`, is it desired that this element become the
|
55 | * responder?
|
56 | */
|
57 | startShouldSetResponder: {
|
58 | phasedRegistrationNames: {
|
59 | bubbled: 'onStartShouldSetResponder',
|
60 | captured: 'onStartShouldSetResponderCapture'
|
61 | }
|
62 | },
|
63 |
|
64 | /**
|
65 | * On a `scroll`, is it desired that this element become the responder? This
|
66 | * is usually not needed, but should be used to retroactively infer that a
|
67 | * `touchStart` had occurred during momentum scroll. During a momentum scroll,
|
68 | * a touch start will be immediately followed by a scroll event if the view is
|
69 | * currently scrolling.
|
70 | *
|
71 | * TODO: This shouldn't bubble.
|
72 | */
|
73 | scrollShouldSetResponder: {
|
74 | phasedRegistrationNames: {
|
75 | bubbled: 'onScrollShouldSetResponder',
|
76 | captured: 'onScrollShouldSetResponderCapture'
|
77 | }
|
78 | },
|
79 |
|
80 | /**
|
81 | * On text selection change, should this element become the responder? This
|
82 | * is needed for text inputs or other views with native selection, so the
|
83 | * JS view can claim the responder.
|
84 | *
|
85 | * TODO: This shouldn't bubble.
|
86 | */
|
87 | selectionChangeShouldSetResponder: {
|
88 | phasedRegistrationNames: {
|
89 | bubbled: 'onSelectionChangeShouldSetResponder',
|
90 | captured: 'onSelectionChangeShouldSetResponderCapture'
|
91 | }
|
92 | },
|
93 |
|
94 | /**
|
95 | * On a `touchMove`/`mouseMove`, is it desired that this element become the
|
96 | * responder?
|
97 | */
|
98 | moveShouldSetResponder: {
|
99 | phasedRegistrationNames: {
|
100 | bubbled: 'onMoveShouldSetResponder',
|
101 | captured: 'onMoveShouldSetResponderCapture'
|
102 | }
|
103 | },
|
104 |
|
105 | /**
|
106 | * Direct responder events dispatched directly to responder. Do not bubble.
|
107 | */
|
108 | responderStart: { registrationName: 'onResponderStart' },
|
109 | responderMove: { registrationName: 'onResponderMove' },
|
110 | responderEnd: { registrationName: 'onResponderEnd' },
|
111 | responderRelease: { registrationName: 'onResponderRelease' },
|
112 | responderTerminationRequest: {
|
113 | registrationName: 'onResponderTerminationRequest'
|
114 | },
|
115 | responderGrant: { registrationName: 'onResponderGrant' },
|
116 | responderReject: { registrationName: 'onResponderReject' },
|
117 | responderTerminate: { registrationName: 'onResponderTerminate' }
|
118 | };
|
119 |
|
120 | /**
|
121 | *
|
122 | * Responder System:
|
123 | * ----------------
|
124 | *
|
125 | * - A global, solitary "interaction lock" on a view.
|
126 | * - If a node becomes the responder, it should convey visual feedback
|
127 | * immediately to indicate so, either by highlighting or moving accordingly.
|
128 | * - To be the responder means, that touches are exclusively important to that
|
129 | * responder view, and no other view.
|
130 | * - While touches are still occurring, the responder lock can be transferred to
|
131 | * a new view, but only to increasingly "higher" views (meaning ancestors of
|
132 | * the current responder).
|
133 | *
|
134 | * Responder being granted:
|
135 | * ------------------------
|
136 | *
|
137 | * - Touch starts, moves, and scrolls can cause an ID to become the responder.
|
138 | * - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to
|
139 | * the "appropriate place".
|
140 | * - If nothing is currently the responder, the "appropriate place" is the
|
141 | * initiating event's `targetID`.
|
142 | * - If something *is* already the responder, the "appropriate place" is the
|
143 | * first common ancestor of the event target and the current `responderInst`.
|
144 | * - Some negotiation happens: See the timing diagram below.
|
145 | * - Scrolled views automatically become responder. The reasoning is that a
|
146 | * platform scroll view that isn't built on top of the responder system has
|
147 | * began scrolling, and the active responder must now be notified that the
|
148 | * interaction is no longer locked to it - the system has taken over.
|
149 | *
|
150 | * - Responder being released:
|
151 | * As soon as no more touches that *started* inside of descendants of the
|
152 | * *current* responderInst, an `onResponderRelease` event is dispatched to the
|
153 | * current responder, and the responder lock is released.
|
154 | *
|
155 | * TODO:
|
156 | * - on "end", a callback hook for `onResponderEndShouldRemainResponder` that
|
157 | * determines if the responder lock should remain.
|
158 | * - If a view shouldn't "remain" the responder, any active touches should by
|
159 | * default be considered "dead" and do not influence future negotiations or
|
160 | * bubble paths. It should be as if those touches do not exist.
|
161 | * -- For multitouch: Usually a translate-z will choose to "remain" responder
|
162 | * after one out of many touches ended. For translate-y, usually the view
|
163 | * doesn't wish to "remain" responder after one of many touches end.
|
164 | * - Consider building this on top of a `stopPropagation` model similar to
|
165 | * `W3C` events.
|
166 | * - Ensure that `onResponderTerminate` is called on touch cancels, whether or
|
167 | * not `onResponderTerminationRequest` returns `true` or `false`.
|
168 | *
|
169 | */
|
170 |
|
171 | /* Negotiation Performed
|
172 | +-----------------------+
|
173 | / \
|
174 | Process low level events to + Current Responder + wantsResponderID
|
175 | determine who to perform negot-| (if any exists at all) |
|
176 | iation/transition | Otherwise just pass through|
|
177 | -------------------------------+----------------------------+------------------+
|
178 | Bubble to find first ID | |
|
179 | to return true:wantsResponderID| |
|
180 | | |
|
181 | +-------------+ | |
|
182 | | onTouchStart| | |
|
183 | +------+------+ none | |
|
184 | | return| |
|
185 | +-----------v-------------+true| +------------------------+ |
|
186 | |onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+
|
187 | +-----------+-------------+ | +------------------------+ | |
|
188 | | | | +--------+-------+
|
189 | | returned true for| false:REJECT +-------->|onResponderReject
|
190 | | wantsResponderID | | | +----------------+
|
191 | | (now attempt | +------------------+-----+ |
|
192 | | handoff) | | onResponder | |
|
193 | +------------------->| TerminationRequest| |
|
194 | | +------------------+-----+ |
|
195 | | | | +----------------+
|
196 | | true:GRANT +-------->|onResponderGrant|
|
197 | | | +--------+-------+
|
198 | | +------------------------+ | |
|
199 | | | onResponderTerminate |<-----------+
|
200 | | +------------------+-----+ |
|
201 | | | | +----------------+
|
202 | | +-------->|onResponderStart|
|
203 | | | +----------------+
|
204 | Bubble to find first ID | |
|
205 | to return true:wantsResponderID| |
|
206 | | |
|
207 | +-------------+ | |
|
208 | | onTouchMove | | |
|
209 | +------+------+ none | |
|
210 | | return| |
|
211 | +-----------v-------------+true| +------------------------+ |
|
212 | |onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+
|
213 | +-----------+-------------+ | +------------------------+ | |
|
214 | | | | +--------+-------+
|
215 | | returned true for| false:REJECT +-------->|onResponderRejec|
|
216 | | wantsResponderID | | | +----------------+
|
217 | | (now attempt | +------------------+-----+ |
|
218 | | handoff) | | onResponder | |
|
219 | +------------------->| TerminationRequest| |
|
220 | | +------------------+-----+ |
|
221 | | | | +----------------+
|
222 | | true:GRANT +-------->|onResponderGrant|
|
223 | | | +--------+-------+
|
224 | | +------------------------+ | |
|
225 | | | onResponderTerminate |<-----------+
|
226 | | +------------------+-----+ |
|
227 | | | | +----------------+
|
228 | | +-------->|onResponderMove |
|
229 | | | +----------------+
|
230 | | |
|
231 | | |
|
232 | Some active touch started| |
|
233 | inside current responder | +------------------------+ |
|
234 | +------------------------->| onResponderEnd | |
|
235 | | | +------------------------+ |
|
236 | +---+---------+ | |
|
237 | | onTouchEnd | | |
|
238 | +---+---------+ | |
|
239 | | | +------------------------+ |
|
240 | +------------------------->| onResponderEnd | |
|
241 | No active touches started| +-----------+------------+ |
|
242 | inside current responder | | |
|
243 | | v |
|
244 | | +------------------------+ |
|
245 | | | onResponderRelease | |
|
246 | | +------------------------+ |
|
247 | | |
|
248 | + + */
|
249 |
|
250 | /**
|
251 | * A note about event ordering in the `EventPluginHub`.
|
252 | *
|
253 | * Suppose plugins are injected in the following order:
|
254 | *
|
255 | * `[R, S, C]`
|
256 | *
|
257 | * To help illustrate the example, assume `S` is `SimpleEventPlugin` (for
|
258 | * `onClick` etc) and `R` is `ResponderEventPlugin`.
|
259 | *
|
260 | * "Deferred-Dispatched Events":
|
261 | *
|
262 | * - The current event plugin system will traverse the list of injected plugins,
|
263 | * in order, and extract events by collecting the plugin's return value of
|
264 | * `extractEvents()`.
|
265 | * - These events that are returned from `extractEvents` are "deferred
|
266 | * dispatched events".
|
267 | * - When returned from `extractEvents`, deferred-dispatched events contain an
|
268 | * "accumulation" of deferred dispatches.
|
269 | * - These deferred dispatches are accumulated/collected before they are
|
270 | * returned, but processed at a later time by the `EventPluginHub` (hence the
|
271 | * name deferred).
|
272 | *
|
273 | * In the process of returning their deferred-dispatched events, event plugins
|
274 | * themselves can dispatch events on-demand without returning them from
|
275 | * `extractEvents`. Plugins might want to do this, so that they can use event
|
276 | * dispatching as a tool that helps them decide which events should be extracted
|
277 | * in the first place.
|
278 | *
|
279 | * "On-Demand-Dispatched Events":
|
280 | *
|
281 | * - On-demand-dispatched events are not returned from `extractEvents`.
|
282 | * - On-demand-dispatched events are dispatched during the process of returning
|
283 | * the deferred-dispatched events.
|
284 | * - They should not have side effects.
|
285 | * - They should be avoided, and/or eventually be replaced with another
|
286 | * abstraction that allows event plugins to perform multiple "rounds" of event
|
287 | * extraction.
|
288 | *
|
289 | * Therefore, the sequence of event dispatches becomes:
|
290 | *
|
291 | * - `R`s on-demand events (if any) (dispatched by `R` on-demand)
|
292 | * - `S`s on-demand events (if any) (dispatched by `S` on-demand)
|
293 | * - `C`s on-demand events (if any) (dispatched by `C` on-demand)
|
294 | * - `R`s extracted events (if any) (dispatched by `EventPluginHub`)
|
295 | * - `S`s extracted events (if any) (dispatched by `EventPluginHub`)
|
296 | * - `C`s extracted events (if any) (dispatched by `EventPluginHub`)
|
297 | *
|
298 | * In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
|
299 | * on-demand dispatch returns `true` (and some other details are satisfied) the
|
300 | * `onResponderGrant` deferred dispatched event is returned from
|
301 | * `extractEvents`. The sequence of dispatch executions in this case
|
302 | * will appear as follows:
|
303 | *
|
304 | * - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
|
305 | * - `touchStartCapture` (`EventPluginHub` dispatches as usual)
|
306 | * - `touchStart` (`EventPluginHub` dispatches as usual)
|
307 | * - `responderGrant/Reject` (`EventPluginHub` dispatches as usual)
|
308 | */
|
309 |
|
310 | function setResponderAndExtractTransfer(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
|
311 | var shouldSetEventType = isStartish(topLevelType) ? eventTypes.startShouldSetResponder : isMoveish(topLevelType) ? eventTypes.moveShouldSetResponder : topLevelType === 'topSelectionChange' ? eventTypes.selectionChangeShouldSetResponder : eventTypes.scrollShouldSetResponder;
|
312 |
|
313 | // TODO: stop one short of the current responder.
|
314 | var bubbleShouldSetFrom = !responderInst ? targetInst : EventPluginUtils.getLowestCommonAncestor(responderInst, targetInst);
|
315 |
|
316 | // When capturing/bubbling the "shouldSet" event, we want to skip the target
|
317 | // (deepest ID) if it happens to be the current responder. The reasoning:
|
318 | // It's strange to get an `onMoveShouldSetResponder` when you're *already*
|
319 | // the responder.
|
320 | var skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderInst;
|
321 | var shouldSetEvent = ResponderSyntheticEvent.getPooled(shouldSetEventType, bubbleShouldSetFrom, nativeEvent, nativeEventTarget);
|
322 | shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
323 | if (skipOverBubbleShouldSetFrom) {
|
324 | EventPropagators.accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
|
325 | } else {
|
326 | EventPropagators.accumulateTwoPhaseDispatches(shouldSetEvent);
|
327 | }
|
328 | var wantsResponderInst = executeDispatchesInOrderStopAtTrue(shouldSetEvent);
|
329 | if (!shouldSetEvent.isPersistent()) {
|
330 | shouldSetEvent.constructor.release(shouldSetEvent);
|
331 | }
|
332 |
|
333 | if (!wantsResponderInst || wantsResponderInst === responderInst) {
|
334 | return null;
|
335 | }
|
336 | var extracted;
|
337 | var grantEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderGrant, wantsResponderInst, nativeEvent, nativeEventTarget);
|
338 | grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
339 |
|
340 | EventPropagators.accumulateDirectDispatches(grantEvent);
|
341 | var blockHostResponder = executeDirectDispatch(grantEvent) === true;
|
342 | if (responderInst) {
|
343 | var terminationRequestEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderTerminationRequest, responderInst, nativeEvent, nativeEventTarget);
|
344 | terminationRequestEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
345 | EventPropagators.accumulateDirectDispatches(terminationRequestEvent);
|
346 | var shouldSwitch = !hasDispatches(terminationRequestEvent) || executeDirectDispatch(terminationRequestEvent);
|
347 | if (!terminationRequestEvent.isPersistent()) {
|
348 | terminationRequestEvent.constructor.release(terminationRequestEvent);
|
349 | }
|
350 |
|
351 | if (shouldSwitch) {
|
352 | var terminateEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderTerminate, responderInst, nativeEvent, nativeEventTarget);
|
353 | terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
354 | EventPropagators.accumulateDirectDispatches(terminateEvent);
|
355 | extracted = accumulate(extracted, [grantEvent, terminateEvent]);
|
356 | changeResponder(wantsResponderInst, blockHostResponder);
|
357 | } else {
|
358 | var rejectEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderReject, wantsResponderInst, nativeEvent, nativeEventTarget);
|
359 | rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
360 | EventPropagators.accumulateDirectDispatches(rejectEvent);
|
361 | extracted = accumulate(extracted, rejectEvent);
|
362 | }
|
363 | } else {
|
364 | extracted = accumulate(extracted, grantEvent);
|
365 | changeResponder(wantsResponderInst, blockHostResponder);
|
366 | }
|
367 | return extracted;
|
368 | }
|
369 |
|
370 | /**
|
371 | * A transfer is a negotiation between a currently set responder and the next
|
372 | * element to claim responder status. Any start event could trigger a transfer
|
373 | * of responderInst. Any move event could trigger a transfer.
|
374 | *
|
375 | * @param {string} topLevelType Record from `EventConstants`.
|
376 | * @return {boolean} True if a transfer of responder could possibly occur.
|
377 | */
|
378 | function canTriggerTransfer(topLevelType, topLevelInst, nativeEvent) {
|
379 | return topLevelInst && (
|
380 | // responderIgnoreScroll: We are trying to migrate away from specifically
|
381 | // tracking native scroll events here and responderIgnoreScroll indicates we
|
382 | // will send topTouchCancel to handle canceling touch events instead
|
383 | topLevelType === 'topScroll' && !nativeEvent.responderIgnoreScroll || trackedTouchCount > 0 && topLevelType === 'topSelectionChange' || isStartish(topLevelType) || isMoveish(topLevelType));
|
384 | }
|
385 |
|
386 | /**
|
387 | * Returns whether or not this touch end event makes it such that there are no
|
388 | * longer any touches that started inside of the current `responderInst`.
|
389 | *
|
390 | * @param {NativeEvent} nativeEvent Native touch end event.
|
391 | * @return {boolean} Whether or not this touch end event ends the responder.
|
392 | */
|
393 | function noResponderTouches(nativeEvent) {
|
394 | var touches = nativeEvent.touches;
|
395 | if (!touches || touches.length === 0) {
|
396 | return true;
|
397 | }
|
398 | for (var i = 0; i < touches.length; i++) {
|
399 | var activeTouch = touches[i];
|
400 | var target = activeTouch.target;
|
401 | if (target !== null && target !== undefined && target !== 0) {
|
402 | // Is the original touch location inside of the current responder?
|
403 | var targetInst = EventPluginUtils.getInstanceFromNode(target);
|
404 | if (EventPluginUtils.isAncestor(responderInst, targetInst)) {
|
405 | return false;
|
406 | }
|
407 | }
|
408 | }
|
409 | return true;
|
410 | }
|
411 |
|
412 | var ResponderEventPlugin = {
|
413 | /* For unit testing only */
|
414 | _getResponderID: function () {
|
415 | return responderInst ? responderInst._rootNodeID : null;
|
416 | },
|
417 |
|
418 | eventTypes: eventTypes,
|
419 |
|
420 | /**
|
421 | * We must be resilient to `targetInst` being `null` on `touchMove` or
|
422 | * `touchEnd`. On certain platforms, this means that a native scroll has
|
423 | * assumed control and the original touch targets are destroyed.
|
424 | */
|
425 | extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
|
426 | if (isStartish(topLevelType)) {
|
427 | trackedTouchCount += 1;
|
428 | } else if (isEndish(topLevelType)) {
|
429 | if (trackedTouchCount >= 0) {
|
430 | trackedTouchCount -= 1;
|
431 | } else {
|
432 | console.error('Ended a touch event which was not counted in `trackedTouchCount`.');
|
433 | return null;
|
434 | }
|
435 | }
|
436 |
|
437 | ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
|
438 |
|
439 | var extracted = canTriggerTransfer(topLevelType, targetInst, nativeEvent) ? setResponderAndExtractTransfer(topLevelType, targetInst, nativeEvent, nativeEventTarget) : null;
|
440 | // Responder may or may not have transferred on a new touch start/move.
|
441 | // Regardless, whoever is the responder after any potential transfer, we
|
442 | // direct all touch start/move/ends to them in the form of
|
443 | // `onResponderMove/Start/End`. These will be called for *every* additional
|
444 | // finger that move/start/end, dispatched directly to whoever is the
|
445 | // current responder at that moment, until the responder is "released".
|
446 | //
|
447 | // These multiple individual change touch events are are always bookended
|
448 | // by `onResponderGrant`, and one of
|
449 | // (`onResponderRelease/onResponderTerminate`).
|
450 | var isResponderTouchStart = responderInst && isStartish(topLevelType);
|
451 | var isResponderTouchMove = responderInst && isMoveish(topLevelType);
|
452 | var isResponderTouchEnd = responderInst && isEndish(topLevelType);
|
453 | var incrementalTouch = isResponderTouchStart ? eventTypes.responderStart : isResponderTouchMove ? eventTypes.responderMove : isResponderTouchEnd ? eventTypes.responderEnd : null;
|
454 |
|
455 | if (incrementalTouch) {
|
456 | var gesture = ResponderSyntheticEvent.getPooled(incrementalTouch, responderInst, nativeEvent, nativeEventTarget);
|
457 | gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
458 | EventPropagators.accumulateDirectDispatches(gesture);
|
459 | extracted = accumulate(extracted, gesture);
|
460 | }
|
461 |
|
462 | var isResponderTerminate = responderInst && topLevelType === 'topTouchCancel';
|
463 | var isResponderRelease = responderInst && !isResponderTerminate && isEndish(topLevelType) && noResponderTouches(nativeEvent);
|
464 | var finalTouch = isResponderTerminate ? eventTypes.responderTerminate : isResponderRelease ? eventTypes.responderRelease : null;
|
465 | if (finalTouch) {
|
466 | var finalEvent = ResponderSyntheticEvent.getPooled(finalTouch, responderInst, nativeEvent, nativeEventTarget);
|
467 | finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
468 | EventPropagators.accumulateDirectDispatches(finalEvent);
|
469 | extracted = accumulate(extracted, finalEvent);
|
470 | changeResponder(null);
|
471 | }
|
472 |
|
473 | var numberActiveTouches = ResponderTouchHistoryStore.touchHistory.numberActiveTouches;
|
474 | if (ResponderEventPlugin.GlobalInteractionHandler && numberActiveTouches !== previousActiveTouches) {
|
475 | ResponderEventPlugin.GlobalInteractionHandler.onChange(numberActiveTouches);
|
476 | }
|
477 | previousActiveTouches = numberActiveTouches;
|
478 |
|
479 | return extracted;
|
480 | },
|
481 |
|
482 | GlobalResponderHandler: null,
|
483 | GlobalInteractionHandler: null,
|
484 |
|
485 | injection: {
|
486 | /**
|
487 | * @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
|
488 | * Object that handles any change in responder. Use this to inject
|
489 | * integration with an existing touch handling system etc.
|
490 | */
|
491 | injectGlobalResponderHandler: function (GlobalResponderHandler) {
|
492 | ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
|
493 | },
|
494 |
|
495 | /**
|
496 | * @param {{onChange: (numberActiveTouches) => void} GlobalInteractionHandler
|
497 | * Object that handles any change in the number of active touches.
|
498 | */
|
499 | injectGlobalInteractionHandler: function (GlobalInteractionHandler) {
|
500 | ResponderEventPlugin.GlobalInteractionHandler = GlobalInteractionHandler;
|
501 | }
|
502 | }
|
503 | };
|
504 |
|
505 | module.exports = ResponderEventPlugin; |
\ | No newline at end of file |