UNPKG

62.2 kBJavaScriptView Raw
1/** @license React v16.9.0
2 * react-dom-unstable-native-dependencies.development.js
3 *
4 * Copyright (c) Facebook, Inc. and its affiliates.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE file in the root directory of this source tree.
8 */
9
10'use strict';
11
12(function (global, factory) {
13 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react-dom'), require('react')) :
14 typeof define === 'function' && define.amd ? define(['react-dom', 'react'], factory) :
15 (global.ReactDOMUnstableNativeDependencies = factory(global.ReactDOM,global.React));
16}(this, (function (ReactDOM,React) { 'use strict';
17
18// Do not require this module directly! Use normal `invariant` calls with
19// template literal strings. The messages will be converted to ReactError during
20// build, and in production they will be minified.
21
22// Do not require this module directly! Use normal `invariant` calls with
23// template literal strings. The messages will be converted to ReactError during
24// build, and in production they will be minified.
25
26function ReactError(error) {
27 error.name = 'Invariant Violation';
28 return error;
29}
30
31/**
32 * Use invariant() to assert state which your program assumes to be true.
33 *
34 * Provide sprintf-style format (only %s is supported) and arguments
35 * to provide information about what broke and what you were
36 * expecting.
37 *
38 * The invariant message will be stripped in production, but the invariant
39 * will remain to ensure logic does not differ in production.
40 */
41
42{
43 // In DEV mode, we swap out invokeGuardedCallback for a special version
44 // that plays more nicely with the browser's DevTools. The idea is to preserve
45 // "Pause on exceptions" behavior. Because React wraps all user-provided
46 // functions in invokeGuardedCallback, and the production version of
47 // invokeGuardedCallback uses a try-catch, all user exceptions are treated
48 // like caught exceptions, and the DevTools won't pause unless the developer
49 // takes the extra step of enabling pause on caught exceptions. This is
50 // unintuitive, though, because even though React has caught the error, from
51 // the developer's perspective, the error is uncaught.
52 //
53 // To preserve the expected "Pause on exceptions" behavior, we don't use a
54 // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake
55 // DOM node, and call the user-provided callback from inside an event handler
56 // for that fake event. If the callback throws, the error is "captured" using
57 // a global event handler. But because the error happens in a different
58 // event loop context, it does not interrupt the normal program flow.
59 // Effectively, this gives us try-catch behavior without actually using
60 // try-catch. Neat!
61
62 // Check that the browser supports the APIs we need to implement our special
63 // DEV version of invokeGuardedCallback
64 if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
65 var fakeNode = document.createElement('react');
66
67
68 }
69}
70
71/**
72 * Call a function while guarding against errors that happens within it.
73 * Returns an error if it throws, otherwise null.
74 *
75 * In production, this is implemented using a try-catch. The reason we don't
76 * use a try-catch directly is so that we can swap out a different
77 * implementation in DEV mode.
78 *
79 * @param {String} name of the guard to use for logging or debugging
80 * @param {Function} func The function to invoke
81 * @param {*} context The context to use when calling the function
82 * @param {...*} args Arguments for function
83 */
84
85
86/**
87 * Same as invokeGuardedCallback, but instead of returning an error, it stores
88 * it in a global so it can be rethrown by `rethrowCaughtError` later.
89 * TODO: See if caughtError and rethrowError can be unified.
90 *
91 * @param {String} name of the guard to use for logging or debugging
92 * @param {Function} func The function to invoke
93 * @param {*} context The context to use when calling the function
94 * @param {...*} args Arguments for function
95 */
96
97
98/**
99 * During execution of guarded functions we will capture the first error which
100 * we will rethrow to be handled by the top level error handler.
101 */
102
103/**
104 * Similar to invariant but only logs a warning if the condition is not met.
105 * This can be used to log issues in development environments in critical
106 * paths. Removing the logging code for production environments will keep the
107 * same logic and follow the same code paths.
108 */
109
110var warningWithoutStack = function () {};
111
112{
113 warningWithoutStack = function (condition, format) {
114 for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
115 args[_key - 2] = arguments[_key];
116 }
117
118 if (format === undefined) {
119 throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
120 }
121 if (args.length > 8) {
122 // Check before the condition to catch violations early.
123 throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
124 }
125 if (condition) {
126 return;
127 }
128 if (typeof console !== 'undefined') {
129 var argsWithFormat = args.map(function (item) {
130 return '' + item;
131 });
132 argsWithFormat.unshift('Warning: ' + format);
133
134 // We intentionally don't use spread (or .apply) directly because it
135 // breaks IE9: https://github.com/facebook/react/issues/13610
136 Function.prototype.apply.call(console.error, console, argsWithFormat);
137 }
138 try {
139 // --- Welcome to debugging React ---
140 // This error was thrown as a convenience so that you can use this stack
141 // to find the callsite that caused this warning to fire.
142 var argIndex = 0;
143 var message = 'Warning: ' + format.replace(/%s/g, function () {
144 return args[argIndex++];
145 });
146 throw new Error(message);
147 } catch (x) {}
148 };
149}
150
151var warningWithoutStack$1 = warningWithoutStack;
152
153var getFiberCurrentPropsFromNode$1 = null;
154var getInstanceFromNode$1 = null;
155var getNodeFromInstance$1 = null;
156
157function setComponentTree(getFiberCurrentPropsFromNodeImpl, getInstanceFromNodeImpl, getNodeFromInstanceImpl) {
158 getFiberCurrentPropsFromNode$1 = getFiberCurrentPropsFromNodeImpl;
159 getInstanceFromNode$1 = getInstanceFromNodeImpl;
160 getNodeFromInstance$1 = getNodeFromInstanceImpl;
161 {
162 !(getNodeFromInstance$1 && getInstanceFromNode$1) ? warningWithoutStack$1(false, 'EventPluginUtils.setComponentTree(...): Injected ' + 'module is missing getNodeFromInstance or getInstanceFromNode.') : void 0;
163 }
164}
165
166var validateEventDispatches = void 0;
167{
168 validateEventDispatches = function (event) {
169 var dispatchListeners = event._dispatchListeners;
170 var dispatchInstances = event._dispatchInstances;
171
172 var listenersIsArr = Array.isArray(dispatchListeners);
173 var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0;
174
175 var instancesIsArr = Array.isArray(dispatchInstances);
176 var instancesLen = instancesIsArr ? dispatchInstances.length : dispatchInstances ? 1 : 0;
177
178 !(instancesIsArr === listenersIsArr && instancesLen === listenersLen) ? warningWithoutStack$1(false, 'EventPluginUtils: Invalid `event`.') : void 0;
179 };
180}
181
182/**
183 * Dispatch the event to the listener.
184 * @param {SyntheticEvent} event SyntheticEvent to handle
185 * @param {function} listener Application-level callback
186 * @param {*} inst Internal component instance
187 */
188
189
190/**
191 * Standard/simple iteration through an event's collected dispatches.
192 */
193
194
195/**
196 * Standard/simple iteration through an event's collected dispatches, but stops
197 * at the first dispatch execution returning true, and returns that id.
198 *
199 * @return {?string} id of the first dispatch execution who's listener returns
200 * true, or null if no listener returned true.
201 */
202function executeDispatchesInOrderStopAtTrueImpl(event) {
203 var dispatchListeners = event._dispatchListeners;
204 var dispatchInstances = event._dispatchInstances;
205 {
206 validateEventDispatches(event);
207 }
208 if (Array.isArray(dispatchListeners)) {
209 for (var i = 0; i < dispatchListeners.length; i++) {
210 if (event.isPropagationStopped()) {
211 break;
212 }
213 // Listeners and Instances are two parallel arrays that are always in sync.
214 if (dispatchListeners[i](event, dispatchInstances[i])) {
215 return dispatchInstances[i];
216 }
217 }
218 } else if (dispatchListeners) {
219 if (dispatchListeners(event, dispatchInstances)) {
220 return dispatchInstances;
221 }
222 }
223 return null;
224}
225
226/**
227 * @see executeDispatchesInOrderStopAtTrueImpl
228 */
229function executeDispatchesInOrderStopAtTrue(event) {
230 var ret = executeDispatchesInOrderStopAtTrueImpl(event);
231 event._dispatchInstances = null;
232 event._dispatchListeners = null;
233 return ret;
234}
235
236/**
237 * Execution of a "direct" dispatch - there must be at most one dispatch
238 * accumulated on the event or it is considered an error. It doesn't really make
239 * sense for an event with multiple dispatches (bubbled) to keep track of the
240 * return values at each dispatch execution, but it does tend to make sense when
241 * dealing with "direct" dispatches.
242 *
243 * @return {*} The return value of executing the single dispatch.
244 */
245function executeDirectDispatch(event) {
246 {
247 validateEventDispatches(event);
248 }
249 var dispatchListener = event._dispatchListeners;
250 var dispatchInstance = event._dispatchInstances;
251 (function () {
252 if (!!Array.isArray(dispatchListener)) {
253 {
254 throw ReactError(Error('executeDirectDispatch(...): Invalid `event`.'));
255 }
256 }
257 })();
258 event.currentTarget = dispatchListener ? getNodeFromInstance$1(dispatchInstance) : null;
259 var res = dispatchListener ? dispatchListener(event) : null;
260 event.currentTarget = null;
261 event._dispatchListeners = null;
262 event._dispatchInstances = null;
263 return res;
264}
265
266/**
267 * @param {SyntheticEvent} event
268 * @return {boolean} True iff number of dispatches accumulated is greater than 0.
269 */
270function hasDispatches(event) {
271 return !!event._dispatchListeners;
272}
273
274// Before we know whether it is function or class
275 // Root of a host tree. Could be nested inside another node.
276 // A subtree. Could be an entry point to a different renderer.
277var HostComponent = 5;
278
279function getParent(inst) {
280 do {
281 inst = inst.return;
282 // TODO: If this is a HostRoot we might want to bail out.
283 // That is depending on if we want nested subtrees (layers) to bubble
284 // events to their parent. We could also go through parentNode on the
285 // host node but that wouldn't work for React Native and doesn't let us
286 // do the portal feature.
287 } while (inst && inst.tag !== HostComponent);
288 if (inst) {
289 return inst;
290 }
291 return null;
292}
293
294/**
295 * Return the lowest common ancestor of A and B, or null if they are in
296 * different trees.
297 */
298function getLowestCommonAncestor(instA, instB) {
299 var depthA = 0;
300 for (var tempA = instA; tempA; tempA = getParent(tempA)) {
301 depthA++;
302 }
303 var depthB = 0;
304 for (var tempB = instB; tempB; tempB = getParent(tempB)) {
305 depthB++;
306 }
307
308 // If A is deeper, crawl up.
309 while (depthA - depthB > 0) {
310 instA = getParent(instA);
311 depthA--;
312 }
313
314 // If B is deeper, crawl up.
315 while (depthB - depthA > 0) {
316 instB = getParent(instB);
317 depthB--;
318 }
319
320 // Walk in lockstep until we find a match.
321 var depth = depthA;
322 while (depth--) {
323 if (instA === instB || instA === instB.alternate) {
324 return instA;
325 }
326 instA = getParent(instA);
327 instB = getParent(instB);
328 }
329 return null;
330}
331
332/**
333 * Return if A is an ancestor of B.
334 */
335function isAncestor(instA, instB) {
336 while (instB) {
337 if (instA === instB || instA === instB.alternate) {
338 return true;
339 }
340 instB = getParent(instB);
341 }
342 return false;
343}
344
345/**
346 * Return the parent instance of the passed-in instance.
347 */
348function getParentInstance(inst) {
349 return getParent(inst);
350}
351
352/**
353 * Simulates the traversal of a two-phase, capture/bubble event dispatch.
354 */
355function traverseTwoPhase(inst, fn, arg) {
356 var path = [];
357 while (inst) {
358 path.push(inst);
359 inst = getParent(inst);
360 }
361 var i = void 0;
362 for (i = path.length; i-- > 0;) {
363 fn(path[i], 'captured', arg);
364 }
365 for (i = 0; i < path.length; i++) {
366 fn(path[i], 'bubbled', arg);
367 }
368}
369
370/**
371 * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
372 * should would receive a `mouseEnter` or `mouseLeave` event.
373 *
374 * Does not invoke the callback on the nearest common ancestor because nothing
375 * "entered" or "left" that element.
376 */
377
378/**
379 * Registers plugins so that they can extract and dispatch events.
380 *
381 * @see {EventPluginHub}
382 */
383
384/**
385 * Ordered list of injected plugins.
386 */
387
388
389/**
390 * Mapping from event name to dispatch config
391 */
392
393
394/**
395 * Mapping from registration name to plugin module
396 */
397
398
399/**
400 * Mapping from registration name to event name
401 */
402
403
404/**
405 * Mapping from lowercase registration names to the properly cased version,
406 * used to warn in the case of missing event handlers. Available
407 * only in true.
408 * @type {Object}
409 */
410
411// Trust the developer to only use possibleRegistrationNames in true
412
413/**
414 * Injects an ordering of plugins (by plugin name). This allows the ordering
415 * to be decoupled from injection of the actual plugins so that ordering is
416 * always deterministic regardless of packaging, on-the-fly injection, etc.
417 *
418 * @param {array} InjectedEventPluginOrder
419 * @internal
420 * @see {EventPluginHub.injection.injectEventPluginOrder}
421 */
422
423
424/**
425 * Injects plugins to be used by `EventPluginHub`. The plugin names must be
426 * in the ordering injected by `injectEventPluginOrder`.
427 *
428 * Plugins can be injected as part of page initialization or on-the-fly.
429 *
430 * @param {object} injectedNamesToPlugins Map from names to plugin modules.
431 * @internal
432 * @see {EventPluginHub.injection.injectEventPluginsByName}
433 */
434
435/**
436 * Accumulates items that must not be null or undefined into the first one. This
437 * is used to conserve memory by avoiding array allocations, and thus sacrifices
438 * API cleanness. Since `current` can be null before being passed in and not
439 * null after this function, make sure to assign it back to `current`:
440 *
441 * `a = accumulateInto(a, b);`
442 *
443 * This API should be sparingly used. Try `accumulate` for something cleaner.
444 *
445 * @return {*|array<*>} An accumulation of items.
446 */
447
448function accumulateInto(current, next) {
449 (function () {
450 if (!(next != null)) {
451 {
452 throw ReactError(Error('accumulateInto(...): Accumulated items must not be null or undefined.'));
453 }
454 }
455 })();
456
457 if (current == null) {
458 return next;
459 }
460
461 // Both are not empty. Warning: Never call x.concat(y) when you are not
462 // certain that x is an Array (x could be a string with concat method).
463 if (Array.isArray(current)) {
464 if (Array.isArray(next)) {
465 current.push.apply(current, next);
466 return current;
467 }
468 current.push(next);
469 return current;
470 }
471
472 if (Array.isArray(next)) {
473 // A bit too dangerous to mutate `next`.
474 return [current].concat(next);
475 }
476
477 return [current, next];
478}
479
480/**
481 * @param {array} arr an "accumulation" of items which is either an Array or
482 * a single item. Useful when paired with the `accumulate` module. This is a
483 * simple utility that allows us to reason about a collection of items, but
484 * handling the case when there is exactly one item (and we do not need to
485 * allocate an array).
486 * @param {function} cb Callback invoked with each element or a collection.
487 * @param {?} [scope] Scope used as `this` in a callback.
488 */
489function forEachAccumulated(arr, cb, scope) {
490 if (Array.isArray(arr)) {
491 arr.forEach(cb, scope);
492 } else if (arr) {
493 cb.call(scope, arr);
494 }
495}
496
497function isInteractive(tag) {
498 return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
499}
500
501function shouldPreventMouseEvent(name, type, props) {
502 switch (name) {
503 case 'onClick':
504 case 'onClickCapture':
505 case 'onDoubleClick':
506 case 'onDoubleClickCapture':
507 case 'onMouseDown':
508 case 'onMouseDownCapture':
509 case 'onMouseMove':
510 case 'onMouseMoveCapture':
511 case 'onMouseUp':
512 case 'onMouseUpCapture':
513 return !!(props.disabled && isInteractive(type));
514 default:
515 return false;
516 }
517}
518
519/**
520 * This is a unified interface for event plugins to be installed and configured.
521 *
522 * Event plugins can implement the following properties:
523 *
524 * `extractEvents` {function(string, DOMEventTarget, string, object): *}
525 * Required. When a top-level event is fired, this method is expected to
526 * extract synthetic events that will in turn be queued and dispatched.
527 *
528 * `eventTypes` {object}
529 * Optional, plugins that fire events must publish a mapping of registration
530 * names that are used to register listeners. Values of this mapping must
531 * be objects that contain `registrationName` or `phasedRegistrationNames`.
532 *
533 * `executeDispatch` {function(object, function, string)}
534 * Optional, allows plugins to override how an event gets dispatched. By
535 * default, the listener is simply invoked.
536 *
537 * Each plugin that is injected into `EventsPluginHub` is immediately operable.
538 *
539 * @public
540 */
541
542/**
543 * Methods for injecting dependencies.
544 */
545
546
547/**
548 * @param {object} inst The instance, which is the source of events.
549 * @param {string} registrationName Name of listener (e.g. `onClick`).
550 * @return {?function} The stored callback.
551 */
552function getListener(inst, registrationName) {
553 var listener = void 0;
554
555 // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
556 // live here; needs to be moved to a better place soon
557 var stateNode = inst.stateNode;
558 if (!stateNode) {
559 // Work in progress (ex: onload events in incremental mode).
560 return null;
561 }
562 var props = getFiberCurrentPropsFromNode$1(stateNode);
563 if (!props) {
564 // Work in progress.
565 return null;
566 }
567 listener = props[registrationName];
568 if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
569 return null;
570 }
571 (function () {
572 if (!(!listener || typeof listener === 'function')) {
573 {
574 throw ReactError(Error('Expected `' + registrationName + '` listener to be a function, instead got a value of `' + typeof listener + '` type.'));
575 }
576 }
577 })();
578 return listener;
579}
580
581/**
582 * Some event types have a notion of different registration names for different
583 * "phases" of propagation. This finds listeners by a given phase.
584 */
585function listenerAtPhase(inst, event, propagationPhase) {
586 var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase];
587 return getListener(inst, registrationName);
588}
589
590/**
591 * A small set of propagation patterns, each of which will accept a small amount
592 * of information, and generate a set of "dispatch ready event objects" - which
593 * are sets of events that have already been annotated with a set of dispatched
594 * listener functions/ids. The API is designed this way to discourage these
595 * propagation strategies from actually executing the dispatches, since we
596 * always want to collect the entire set of dispatches before executing even a
597 * single one.
598 */
599
600/**
601 * Tags a `SyntheticEvent` with dispatched listeners. Creating this function
602 * here, allows us to not have to bind or create functions for each event.
603 * Mutating the event's members allows us to not have to create a wrapping
604 * "dispatch" object that pairs the event with the listener.
605 */
606function accumulateDirectionalDispatches(inst, phase, event) {
607 {
608 !inst ? warningWithoutStack$1(false, 'Dispatching inst must not be null') : void 0;
609 }
610 var listener = listenerAtPhase(inst, event, phase);
611 if (listener) {
612 event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
613 event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
614 }
615}
616
617/**
618 * Collect dispatches (must be entirely collected before dispatching - see unit
619 * tests). Lazily allocate the array to conserve memory. We must loop through
620 * each event and perform the traversal for each one. We cannot perform a
621 * single traversal for the entire collection of events because each event may
622 * have a different target.
623 */
624function accumulateTwoPhaseDispatchesSingle(event) {
625 if (event && event.dispatchConfig.phasedRegistrationNames) {
626 traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
627 }
628}
629
630/**
631 * Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
632 */
633function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
634 if (event && event.dispatchConfig.phasedRegistrationNames) {
635 var targetInst = event._targetInst;
636 var parentInst = targetInst ? getParentInstance(targetInst) : null;
637 traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
638 }
639}
640
641/**
642 * Accumulates without regard to direction, does not look for phased
643 * registration names. Same as `accumulateDirectDispatchesSingle` but without
644 * requiring that the `dispatchMarker` be the same as the dispatched ID.
645 */
646function accumulateDispatches(inst, ignoredDirection, event) {
647 if (inst && event && event.dispatchConfig.registrationName) {
648 var registrationName = event.dispatchConfig.registrationName;
649 var listener = getListener(inst, registrationName);
650 if (listener) {
651 event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
652 event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
653 }
654 }
655}
656
657/**
658 * Accumulates dispatches on an `SyntheticEvent`, but only for the
659 * `dispatchMarker`.
660 * @param {SyntheticEvent} event
661 */
662function accumulateDirectDispatchesSingle(event) {
663 if (event && event.dispatchConfig.registrationName) {
664 accumulateDispatches(event._targetInst, null, event);
665 }
666}
667
668function accumulateTwoPhaseDispatches(events) {
669 forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
670}
671
672function accumulateTwoPhaseDispatchesSkipTarget(events) {
673 forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
674}
675
676
677
678function accumulateDirectDispatches(events) {
679 forEachAccumulated(events, accumulateDirectDispatchesSingle);
680}
681
682var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
683
684var _assign = ReactInternals.assign;
685
686/* eslint valid-typeof: 0 */
687
688var EVENT_POOL_SIZE = 10;
689
690/**
691 * @interface Event
692 * @see http://www.w3.org/TR/DOM-Level-3-Events/
693 */
694var EventInterface = {
695 type: null,
696 target: null,
697 // currentTarget is set when dispatching; no use in copying it here
698 currentTarget: function () {
699 return null;
700 },
701 eventPhase: null,
702 bubbles: null,
703 cancelable: null,
704 timeStamp: function (event) {
705 return event.timeStamp || Date.now();
706 },
707 defaultPrevented: null,
708 isTrusted: null
709};
710
711function functionThatReturnsTrue() {
712 return true;
713}
714
715function functionThatReturnsFalse() {
716 return false;
717}
718
719/**
720 * Synthetic events are dispatched by event plugins, typically in response to a
721 * top-level event delegation handler.
722 *
723 * These systems should generally use pooling to reduce the frequency of garbage
724 * collection. The system should check `isPersistent` to determine whether the
725 * event should be released into the pool after being dispatched. Users that
726 * need a persisted event should invoke `persist`.
727 *
728 * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
729 * normalizing browser quirks. Subclasses do not necessarily have to implement a
730 * DOM interface; custom application-specific events can also subclass this.
731 *
732 * @param {object} dispatchConfig Configuration used to dispatch this event.
733 * @param {*} targetInst Marker identifying the event target.
734 * @param {object} nativeEvent Native browser event.
735 * @param {DOMEventTarget} nativeEventTarget Target node.
736 */
737function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
738 {
739 // these have a getter/setter for warnings
740 delete this.nativeEvent;
741 delete this.preventDefault;
742 delete this.stopPropagation;
743 delete this.isDefaultPrevented;
744 delete this.isPropagationStopped;
745 }
746
747 this.dispatchConfig = dispatchConfig;
748 this._targetInst = targetInst;
749 this.nativeEvent = nativeEvent;
750
751 var Interface = this.constructor.Interface;
752 for (var propName in Interface) {
753 if (!Interface.hasOwnProperty(propName)) {
754 continue;
755 }
756 {
757 delete this[propName]; // this has a getter/setter for warnings
758 }
759 var normalize = Interface[propName];
760 if (normalize) {
761 this[propName] = normalize(nativeEvent);
762 } else {
763 if (propName === 'target') {
764 this.target = nativeEventTarget;
765 } else {
766 this[propName] = nativeEvent[propName];
767 }
768 }
769 }
770
771 var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
772 if (defaultPrevented) {
773 this.isDefaultPrevented = functionThatReturnsTrue;
774 } else {
775 this.isDefaultPrevented = functionThatReturnsFalse;
776 }
777 this.isPropagationStopped = functionThatReturnsFalse;
778 return this;
779}
780
781_assign(SyntheticEvent.prototype, {
782 preventDefault: function () {
783 this.defaultPrevented = true;
784 var event = this.nativeEvent;
785 if (!event) {
786 return;
787 }
788
789 if (event.preventDefault) {
790 event.preventDefault();
791 } else if (typeof event.returnValue !== 'unknown') {
792 event.returnValue = false;
793 }
794 this.isDefaultPrevented = functionThatReturnsTrue;
795 },
796
797 stopPropagation: function () {
798 var event = this.nativeEvent;
799 if (!event) {
800 return;
801 }
802
803 if (event.stopPropagation) {
804 event.stopPropagation();
805 } else if (typeof event.cancelBubble !== 'unknown') {
806 // The ChangeEventPlugin registers a "propertychange" event for
807 // IE. This event does not support bubbling or cancelling, and
808 // any references to cancelBubble throw "Member not found". A
809 // typeof check of "unknown" circumvents this issue (and is also
810 // IE specific).
811 event.cancelBubble = true;
812 }
813
814 this.isPropagationStopped = functionThatReturnsTrue;
815 },
816
817 /**
818 * We release all dispatched `SyntheticEvent`s after each event loop, adding
819 * them back into the pool. This allows a way to hold onto a reference that
820 * won't be added back into the pool.
821 */
822 persist: function () {
823 this.isPersistent = functionThatReturnsTrue;
824 },
825
826 /**
827 * Checks if this event should be released back into the pool.
828 *
829 * @return {boolean} True if this should not be released, false otherwise.
830 */
831 isPersistent: functionThatReturnsFalse,
832
833 /**
834 * `PooledClass` looks for `destructor` on each instance it releases.
835 */
836 destructor: function () {
837 var Interface = this.constructor.Interface;
838 for (var propName in Interface) {
839 {
840 Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName]));
841 }
842 }
843 this.dispatchConfig = null;
844 this._targetInst = null;
845 this.nativeEvent = null;
846 this.isDefaultPrevented = functionThatReturnsFalse;
847 this.isPropagationStopped = functionThatReturnsFalse;
848 this._dispatchListeners = null;
849 this._dispatchInstances = null;
850 {
851 Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
852 Object.defineProperty(this, 'isDefaultPrevented', getPooledWarningPropertyDefinition('isDefaultPrevented', functionThatReturnsFalse));
853 Object.defineProperty(this, 'isPropagationStopped', getPooledWarningPropertyDefinition('isPropagationStopped', functionThatReturnsFalse));
854 Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', function () {}));
855 Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', function () {}));
856 }
857 }
858});
859
860SyntheticEvent.Interface = EventInterface;
861
862/**
863 * Helper to reduce boilerplate when creating subclasses.
864 */
865SyntheticEvent.extend = function (Interface) {
866 var Super = this;
867
868 var E = function () {};
869 E.prototype = Super.prototype;
870 var prototype = new E();
871
872 function Class() {
873 return Super.apply(this, arguments);
874 }
875 _assign(prototype, Class.prototype);
876 Class.prototype = prototype;
877 Class.prototype.constructor = Class;
878
879 Class.Interface = _assign({}, Super.Interface, Interface);
880 Class.extend = Super.extend;
881 addEventPoolingTo(Class);
882
883 return Class;
884};
885
886addEventPoolingTo(SyntheticEvent);
887
888/**
889 * Helper to nullify syntheticEvent instance properties when destructing
890 *
891 * @param {String} propName
892 * @param {?object} getVal
893 * @return {object} defineProperty object
894 */
895function getPooledWarningPropertyDefinition(propName, getVal) {
896 var isFunction = typeof getVal === 'function';
897 return {
898 configurable: true,
899 set: set,
900 get: get
901 };
902
903 function set(val) {
904 var action = isFunction ? 'setting the method' : 'setting the property';
905 warn(action, 'This is effectively a no-op');
906 return val;
907 }
908
909 function get() {
910 var action = isFunction ? 'accessing the method' : 'accessing the property';
911 var result = isFunction ? 'This is a no-op function' : 'This is set to null';
912 warn(action, result);
913 return getVal;
914 }
915
916 function warn(action, result) {
917 var warningCondition = false;
918 !warningCondition ? warningWithoutStack$1(false, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) : void 0;
919 }
920}
921
922function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
923 var EventConstructor = this;
924 if (EventConstructor.eventPool.length) {
925 var instance = EventConstructor.eventPool.pop();
926 EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);
927 return instance;
928 }
929 return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
930}
931
932function releasePooledEvent(event) {
933 var EventConstructor = this;
934 (function () {
935 if (!(event instanceof EventConstructor)) {
936 {
937 throw ReactError(Error('Trying to release an event instance into a pool of a different type.'));
938 }
939 }
940 })();
941 event.destructor();
942 if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
943 EventConstructor.eventPool.push(event);
944 }
945}
946
947function addEventPoolingTo(EventConstructor) {
948 EventConstructor.eventPool = [];
949 EventConstructor.getPooled = getPooledEvent;
950 EventConstructor.release = releasePooledEvent;
951}
952
953/**
954 * `touchHistory` isn't actually on the native event, but putting it in the
955 * interface will ensure that it is cleaned up when pooled/destroyed. The
956 * `ResponderEventPlugin` will populate it appropriately.
957 */
958var ResponderSyntheticEvent = SyntheticEvent.extend({
959 touchHistory: function (nativeEvent) {
960 return null; // Actually doesn't even look at the native event.
961 }
962});
963
964// Note: ideally these would be imported from DOMTopLevelEventTypes,
965// but our build system currently doesn't let us do that from a fork.
966
967var TOP_TOUCH_START = 'touchstart';
968var TOP_TOUCH_MOVE = 'touchmove';
969var TOP_TOUCH_END = 'touchend';
970var TOP_TOUCH_CANCEL = 'touchcancel';
971var TOP_SCROLL = 'scroll';
972var TOP_SELECTION_CHANGE = 'selectionchange';
973var TOP_MOUSE_DOWN = 'mousedown';
974var TOP_MOUSE_MOVE = 'mousemove';
975var TOP_MOUSE_UP = 'mouseup';
976
977function isStartish(topLevelType) {
978 return topLevelType === TOP_TOUCH_START || topLevelType === TOP_MOUSE_DOWN;
979}
980
981function isMoveish(topLevelType) {
982 return topLevelType === TOP_TOUCH_MOVE || topLevelType === TOP_MOUSE_MOVE;
983}
984
985function isEndish(topLevelType) {
986 return topLevelType === TOP_TOUCH_END || topLevelType === TOP_TOUCH_CANCEL || topLevelType === TOP_MOUSE_UP;
987}
988
989var startDependencies = [TOP_TOUCH_START, TOP_MOUSE_DOWN];
990var moveDependencies = [TOP_TOUCH_MOVE, TOP_MOUSE_MOVE];
991var endDependencies = [TOP_TOUCH_CANCEL, TOP_TOUCH_END, TOP_MOUSE_UP];
992
993/**
994 * Tracks the position and time of each active touch by `touch.identifier`. We
995 * should typically only see IDs in the range of 1-20 because IDs get recycled
996 * when touches end and start again.
997 */
998
999
1000var MAX_TOUCH_BANK = 20;
1001var touchBank = [];
1002var touchHistory = {
1003 touchBank: touchBank,
1004 numberActiveTouches: 0,
1005 // If there is only one active touch, we remember its location. This prevents
1006 // us having to loop through all of the touches all the time in the most
1007 // common case.
1008 indexOfSingleActiveTouch: -1,
1009 mostRecentTimeStamp: 0
1010};
1011
1012function timestampForTouch(touch) {
1013 // The legacy internal implementation provides "timeStamp", which has been
1014 // renamed to "timestamp". Let both work for now while we iron it out
1015 // TODO (evv): rename timeStamp to timestamp in internal code
1016 return touch.timeStamp || touch.timestamp;
1017}
1018
1019/**
1020 * TODO: Instead of making gestures recompute filtered velocity, we could
1021 * include a built in velocity computation that can be reused globally.
1022 */
1023function createTouchRecord(touch) {
1024 return {
1025 touchActive: true,
1026 startPageX: touch.pageX,
1027 startPageY: touch.pageY,
1028 startTimeStamp: timestampForTouch(touch),
1029 currentPageX: touch.pageX,
1030 currentPageY: touch.pageY,
1031 currentTimeStamp: timestampForTouch(touch),
1032 previousPageX: touch.pageX,
1033 previousPageY: touch.pageY,
1034 previousTimeStamp: timestampForTouch(touch)
1035 };
1036}
1037
1038function resetTouchRecord(touchRecord, touch) {
1039 touchRecord.touchActive = true;
1040 touchRecord.startPageX = touch.pageX;
1041 touchRecord.startPageY = touch.pageY;
1042 touchRecord.startTimeStamp = timestampForTouch(touch);
1043 touchRecord.currentPageX = touch.pageX;
1044 touchRecord.currentPageY = touch.pageY;
1045 touchRecord.currentTimeStamp = timestampForTouch(touch);
1046 touchRecord.previousPageX = touch.pageX;
1047 touchRecord.previousPageY = touch.pageY;
1048 touchRecord.previousTimeStamp = timestampForTouch(touch);
1049}
1050
1051function getTouchIdentifier(_ref) {
1052 var identifier = _ref.identifier;
1053
1054 (function () {
1055 if (!(identifier != null)) {
1056 {
1057 throw ReactError(Error('Touch object is missing identifier.'));
1058 }
1059 }
1060 })();
1061 {
1062 !(identifier <= MAX_TOUCH_BANK) ? warningWithoutStack$1(false, 'Touch identifier %s is greater than maximum supported %s which causes ' + 'performance issues backfilling array locations for all of the indices.', identifier, MAX_TOUCH_BANK) : void 0;
1063 }
1064 return identifier;
1065}
1066
1067function recordTouchStart(touch) {
1068 var identifier = getTouchIdentifier(touch);
1069 var touchRecord = touchBank[identifier];
1070 if (touchRecord) {
1071 resetTouchRecord(touchRecord, touch);
1072 } else {
1073 touchBank[identifier] = createTouchRecord(touch);
1074 }
1075 touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
1076}
1077
1078function recordTouchMove(touch) {
1079 var touchRecord = touchBank[getTouchIdentifier(touch)];
1080 if (touchRecord) {
1081 touchRecord.touchActive = true;
1082 touchRecord.previousPageX = touchRecord.currentPageX;
1083 touchRecord.previousPageY = touchRecord.currentPageY;
1084 touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
1085 touchRecord.currentPageX = touch.pageX;
1086 touchRecord.currentPageY = touch.pageY;
1087 touchRecord.currentTimeStamp = timestampForTouch(touch);
1088 touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
1089 } else {
1090 console.warn('Cannot record touch move without a touch start.\n' + 'Touch Move: %s\n', 'Touch Bank: %s', printTouch(touch), printTouchBank());
1091 }
1092}
1093
1094function recordTouchEnd(touch) {
1095 var touchRecord = touchBank[getTouchIdentifier(touch)];
1096 if (touchRecord) {
1097 touchRecord.touchActive = false;
1098 touchRecord.previousPageX = touchRecord.currentPageX;
1099 touchRecord.previousPageY = touchRecord.currentPageY;
1100 touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
1101 touchRecord.currentPageX = touch.pageX;
1102 touchRecord.currentPageY = touch.pageY;
1103 touchRecord.currentTimeStamp = timestampForTouch(touch);
1104 touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
1105 } else {
1106 console.warn('Cannot record touch end without a touch start.\n' + 'Touch End: %s\n', 'Touch Bank: %s', printTouch(touch), printTouchBank());
1107 }
1108}
1109
1110function printTouch(touch) {
1111 return JSON.stringify({
1112 identifier: touch.identifier,
1113 pageX: touch.pageX,
1114 pageY: touch.pageY,
1115 timestamp: timestampForTouch(touch)
1116 });
1117}
1118
1119function printTouchBank() {
1120 var printed = JSON.stringify(touchBank.slice(0, MAX_TOUCH_BANK));
1121 if (touchBank.length > MAX_TOUCH_BANK) {
1122 printed += ' (original size: ' + touchBank.length + ')';
1123 }
1124 return printed;
1125}
1126
1127var ResponderTouchHistoryStore = {
1128 recordTouchTrack: function (topLevelType, nativeEvent) {
1129 if (isMoveish(topLevelType)) {
1130 nativeEvent.changedTouches.forEach(recordTouchMove);
1131 } else if (isStartish(topLevelType)) {
1132 nativeEvent.changedTouches.forEach(recordTouchStart);
1133 touchHistory.numberActiveTouches = nativeEvent.touches.length;
1134 if (touchHistory.numberActiveTouches === 1) {
1135 touchHistory.indexOfSingleActiveTouch = nativeEvent.touches[0].identifier;
1136 }
1137 } else if (isEndish(topLevelType)) {
1138 nativeEvent.changedTouches.forEach(recordTouchEnd);
1139 touchHistory.numberActiveTouches = nativeEvent.touches.length;
1140 if (touchHistory.numberActiveTouches === 1) {
1141 for (var i = 0; i < touchBank.length; i++) {
1142 var touchTrackToCheck = touchBank[i];
1143 if (touchTrackToCheck != null && touchTrackToCheck.touchActive) {
1144 touchHistory.indexOfSingleActiveTouch = i;
1145 break;
1146 }
1147 }
1148 {
1149 var activeRecord = touchBank[touchHistory.indexOfSingleActiveTouch];
1150 !(activeRecord != null && activeRecord.touchActive) ? warningWithoutStack$1(false, 'Cannot find single active touch.') : void 0;
1151 }
1152 }
1153 }
1154 },
1155
1156
1157 touchHistory: touchHistory
1158};
1159
1160/**
1161 * Accumulates items that must not be null or undefined.
1162 *
1163 * This is used to conserve memory by avoiding array allocations.
1164 *
1165 * @return {*|array<*>} An accumulation of items.
1166 */
1167function accumulate(current, next) {
1168 (function () {
1169 if (!(next != null)) {
1170 {
1171 throw ReactError(Error('accumulate(...): Accumulated items must not be null or undefined.'));
1172 }
1173 }
1174 })();
1175
1176 if (current == null) {
1177 return next;
1178 }
1179
1180 // Both are not empty. Warning: Never call x.concat(y) when you are not
1181 // certain that x is an Array (x could be a string with concat method).
1182 if (Array.isArray(current)) {
1183 return current.concat(next);
1184 }
1185
1186 if (Array.isArray(next)) {
1187 return [current].concat(next);
1188 }
1189
1190 return [current, next];
1191}
1192
1193/**
1194 * Instance of element that should respond to touch/move types of interactions,
1195 * as indicated explicitly by relevant callbacks.
1196 */
1197var responderInst = null;
1198
1199/**
1200 * Count of current touches. A textInput should become responder iff the
1201 * selection changes while there is a touch on the screen.
1202 */
1203var trackedTouchCount = 0;
1204
1205var changeResponder = function (nextResponderInst, blockHostResponder) {
1206 var oldResponderInst = responderInst;
1207 responderInst = nextResponderInst;
1208 if (ResponderEventPlugin.GlobalResponderHandler !== null) {
1209 ResponderEventPlugin.GlobalResponderHandler.onChange(oldResponderInst, nextResponderInst, blockHostResponder);
1210 }
1211};
1212
1213var eventTypes = {
1214 /**
1215 * On a `touchStart`/`mouseDown`, is it desired that this element become the
1216 * responder?
1217 */
1218 startShouldSetResponder: {
1219 phasedRegistrationNames: {
1220 bubbled: 'onStartShouldSetResponder',
1221 captured: 'onStartShouldSetResponderCapture'
1222 },
1223 dependencies: startDependencies
1224 },
1225
1226 /**
1227 * On a `scroll`, is it desired that this element become the responder? This
1228 * is usually not needed, but should be used to retroactively infer that a
1229 * `touchStart` had occurred during momentum scroll. During a momentum scroll,
1230 * a touch start will be immediately followed by a scroll event if the view is
1231 * currently scrolling.
1232 *
1233 * TODO: This shouldn't bubble.
1234 */
1235 scrollShouldSetResponder: {
1236 phasedRegistrationNames: {
1237 bubbled: 'onScrollShouldSetResponder',
1238 captured: 'onScrollShouldSetResponderCapture'
1239 },
1240 dependencies: [TOP_SCROLL]
1241 },
1242
1243 /**
1244 * On text selection change, should this element become the responder? This
1245 * is needed for text inputs or other views with native selection, so the
1246 * JS view can claim the responder.
1247 *
1248 * TODO: This shouldn't bubble.
1249 */
1250 selectionChangeShouldSetResponder: {
1251 phasedRegistrationNames: {
1252 bubbled: 'onSelectionChangeShouldSetResponder',
1253 captured: 'onSelectionChangeShouldSetResponderCapture'
1254 },
1255 dependencies: [TOP_SELECTION_CHANGE]
1256 },
1257
1258 /**
1259 * On a `touchMove`/`mouseMove`, is it desired that this element become the
1260 * responder?
1261 */
1262 moveShouldSetResponder: {
1263 phasedRegistrationNames: {
1264 bubbled: 'onMoveShouldSetResponder',
1265 captured: 'onMoveShouldSetResponderCapture'
1266 },
1267 dependencies: moveDependencies
1268 },
1269
1270 /**
1271 * Direct responder events dispatched directly to responder. Do not bubble.
1272 */
1273 responderStart: {
1274 registrationName: 'onResponderStart',
1275 dependencies: startDependencies
1276 },
1277 responderMove: {
1278 registrationName: 'onResponderMove',
1279 dependencies: moveDependencies
1280 },
1281 responderEnd: {
1282 registrationName: 'onResponderEnd',
1283 dependencies: endDependencies
1284 },
1285 responderRelease: {
1286 registrationName: 'onResponderRelease',
1287 dependencies: endDependencies
1288 },
1289 responderTerminationRequest: {
1290 registrationName: 'onResponderTerminationRequest',
1291 dependencies: []
1292 },
1293 responderGrant: {
1294 registrationName: 'onResponderGrant',
1295 dependencies: []
1296 },
1297 responderReject: {
1298 registrationName: 'onResponderReject',
1299 dependencies: []
1300 },
1301 responderTerminate: {
1302 registrationName: 'onResponderTerminate',
1303 dependencies: []
1304 }
1305};
1306
1307/**
1308 *
1309 * Responder System:
1310 * ----------------
1311 *
1312 * - A global, solitary "interaction lock" on a view.
1313 * - If a node becomes the responder, it should convey visual feedback
1314 * immediately to indicate so, either by highlighting or moving accordingly.
1315 * - To be the responder means, that touches are exclusively important to that
1316 * responder view, and no other view.
1317 * - While touches are still occurring, the responder lock can be transferred to
1318 * a new view, but only to increasingly "higher" views (meaning ancestors of
1319 * the current responder).
1320 *
1321 * Responder being granted:
1322 * ------------------------
1323 *
1324 * - Touch starts, moves, and scrolls can cause an ID to become the responder.
1325 * - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to
1326 * the "appropriate place".
1327 * - If nothing is currently the responder, the "appropriate place" is the
1328 * initiating event's `targetID`.
1329 * - If something *is* already the responder, the "appropriate place" is the
1330 * first common ancestor of the event target and the current `responderInst`.
1331 * - Some negotiation happens: See the timing diagram below.
1332 * - Scrolled views automatically become responder. The reasoning is that a
1333 * platform scroll view that isn't built on top of the responder system has
1334 * began scrolling, and the active responder must now be notified that the
1335 * interaction is no longer locked to it - the system has taken over.
1336 *
1337 * - Responder being released:
1338 * As soon as no more touches that *started* inside of descendants of the
1339 * *current* responderInst, an `onResponderRelease` event is dispatched to the
1340 * current responder, and the responder lock is released.
1341 *
1342 * TODO:
1343 * - on "end", a callback hook for `onResponderEndShouldRemainResponder` that
1344 * determines if the responder lock should remain.
1345 * - If a view shouldn't "remain" the responder, any active touches should by
1346 * default be considered "dead" and do not influence future negotiations or
1347 * bubble paths. It should be as if those touches do not exist.
1348 * -- For multitouch: Usually a translate-z will choose to "remain" responder
1349 * after one out of many touches ended. For translate-y, usually the view
1350 * doesn't wish to "remain" responder after one of many touches end.
1351 * - Consider building this on top of a `stopPropagation` model similar to
1352 * `W3C` events.
1353 * - Ensure that `onResponderTerminate` is called on touch cancels, whether or
1354 * not `onResponderTerminationRequest` returns `true` or `false`.
1355 *
1356 */
1357
1358/* Negotiation Performed
1359 +-----------------------+
1360 / \
1361Process low level events to + Current Responder + wantsResponderID
1362determine who to perform negot-| (if any exists at all) |
1363iation/transition | Otherwise just pass through|
1364-------------------------------+----------------------------+------------------+
1365Bubble to find first ID | |
1366to return true:wantsResponderID| |
1367 | |
1368 +-------------+ | |
1369 | onTouchStart| | |
1370 +------+------+ none | |
1371 | return| |
1372+-----------v-------------+true| +------------------------+ |
1373|onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+
1374+-----------+-------------+ | +------------------------+ | |
1375 | | | +--------+-------+
1376 | returned true for| false:REJECT +-------->|onResponderReject
1377 | wantsResponderID | | | +----------------+
1378 | (now attempt | +------------------+-----+ |
1379 | handoff) | | onResponder | |
1380 +------------------->| TerminationRequest| |
1381 | +------------------+-----+ |
1382 | | | +----------------+
1383 | true:GRANT +-------->|onResponderGrant|
1384 | | +--------+-------+
1385 | +------------------------+ | |
1386 | | onResponderTerminate |<-----------+
1387 | +------------------+-----+ |
1388 | | | +----------------+
1389 | +-------->|onResponderStart|
1390 | | +----------------+
1391Bubble to find first ID | |
1392to return true:wantsResponderID| |
1393 | |
1394 +-------------+ | |
1395 | onTouchMove | | |
1396 +------+------+ none | |
1397 | return| |
1398+-----------v-------------+true| +------------------------+ |
1399|onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+
1400+-----------+-------------+ | +------------------------+ | |
1401 | | | +--------+-------+
1402 | returned true for| false:REJECT +-------->|onResponderRejec|
1403 | wantsResponderID | | | +----------------+
1404 | (now attempt | +------------------+-----+ |
1405 | handoff) | | onResponder | |
1406 +------------------->| TerminationRequest| |
1407 | +------------------+-----+ |
1408 | | | +----------------+
1409 | true:GRANT +-------->|onResponderGrant|
1410 | | +--------+-------+
1411 | +------------------------+ | |
1412 | | onResponderTerminate |<-----------+
1413 | +------------------+-----+ |
1414 | | | +----------------+
1415 | +-------->|onResponderMove |
1416 | | +----------------+
1417 | |
1418 | |
1419 Some active touch started| |
1420 inside current responder | +------------------------+ |
1421 +------------------------->| onResponderEnd | |
1422 | | +------------------------+ |
1423 +---+---------+ | |
1424 | onTouchEnd | | |
1425 +---+---------+ | |
1426 | | +------------------------+ |
1427 +------------------------->| onResponderEnd | |
1428 No active touches started| +-----------+------------+ |
1429 inside current responder | | |
1430 | v |
1431 | +------------------------+ |
1432 | | onResponderRelease | |
1433 | +------------------------+ |
1434 | |
1435 + + */
1436
1437/**
1438 * A note about event ordering in the `EventPluginHub`.
1439 *
1440 * Suppose plugins are injected in the following order:
1441 *
1442 * `[R, S, C]`
1443 *
1444 * To help illustrate the example, assume `S` is `SimpleEventPlugin` (for
1445 * `onClick` etc) and `R` is `ResponderEventPlugin`.
1446 *
1447 * "Deferred-Dispatched Events":
1448 *
1449 * - The current event plugin system will traverse the list of injected plugins,
1450 * in order, and extract events by collecting the plugin's return value of
1451 * `extractEvents()`.
1452 * - These events that are returned from `extractEvents` are "deferred
1453 * dispatched events".
1454 * - When returned from `extractEvents`, deferred-dispatched events contain an
1455 * "accumulation" of deferred dispatches.
1456 * - These deferred dispatches are accumulated/collected before they are
1457 * returned, but processed at a later time by the `EventPluginHub` (hence the
1458 * name deferred).
1459 *
1460 * In the process of returning their deferred-dispatched events, event plugins
1461 * themselves can dispatch events on-demand without returning them from
1462 * `extractEvents`. Plugins might want to do this, so that they can use event
1463 * dispatching as a tool that helps them decide which events should be extracted
1464 * in the first place.
1465 *
1466 * "On-Demand-Dispatched Events":
1467 *
1468 * - On-demand-dispatched events are not returned from `extractEvents`.
1469 * - On-demand-dispatched events are dispatched during the process of returning
1470 * the deferred-dispatched events.
1471 * - They should not have side effects.
1472 * - They should be avoided, and/or eventually be replaced with another
1473 * abstraction that allows event plugins to perform multiple "rounds" of event
1474 * extraction.
1475 *
1476 * Therefore, the sequence of event dispatches becomes:
1477 *
1478 * - `R`s on-demand events (if any) (dispatched by `R` on-demand)
1479 * - `S`s on-demand events (if any) (dispatched by `S` on-demand)
1480 * - `C`s on-demand events (if any) (dispatched by `C` on-demand)
1481 * - `R`s extracted events (if any) (dispatched by `EventPluginHub`)
1482 * - `S`s extracted events (if any) (dispatched by `EventPluginHub`)
1483 * - `C`s extracted events (if any) (dispatched by `EventPluginHub`)
1484 *
1485 * In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
1486 * on-demand dispatch returns `true` (and some other details are satisfied) the
1487 * `onResponderGrant` deferred dispatched event is returned from
1488 * `extractEvents`. The sequence of dispatch executions in this case
1489 * will appear as follows:
1490 *
1491 * - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
1492 * - `touchStartCapture` (`EventPluginHub` dispatches as usual)
1493 * - `touchStart` (`EventPluginHub` dispatches as usual)
1494 * - `responderGrant/Reject` (`EventPluginHub` dispatches as usual)
1495 */
1496
1497function setResponderAndExtractTransfer(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
1498 var shouldSetEventType = isStartish(topLevelType) ? eventTypes.startShouldSetResponder : isMoveish(topLevelType) ? eventTypes.moveShouldSetResponder : topLevelType === TOP_SELECTION_CHANGE ? eventTypes.selectionChangeShouldSetResponder : eventTypes.scrollShouldSetResponder;
1499
1500 // TODO: stop one short of the current responder.
1501 var bubbleShouldSetFrom = !responderInst ? targetInst : getLowestCommonAncestor(responderInst, targetInst);
1502
1503 // When capturing/bubbling the "shouldSet" event, we want to skip the target
1504 // (deepest ID) if it happens to be the current responder. The reasoning:
1505 // It's strange to get an `onMoveShouldSetResponder` when you're *already*
1506 // the responder.
1507 var skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderInst;
1508 var shouldSetEvent = ResponderSyntheticEvent.getPooled(shouldSetEventType, bubbleShouldSetFrom, nativeEvent, nativeEventTarget);
1509 shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1510 if (skipOverBubbleShouldSetFrom) {
1511 accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
1512 } else {
1513 accumulateTwoPhaseDispatches(shouldSetEvent);
1514 }
1515 var wantsResponderInst = executeDispatchesInOrderStopAtTrue(shouldSetEvent);
1516 if (!shouldSetEvent.isPersistent()) {
1517 shouldSetEvent.constructor.release(shouldSetEvent);
1518 }
1519
1520 if (!wantsResponderInst || wantsResponderInst === responderInst) {
1521 return null;
1522 }
1523 var extracted = void 0;
1524 var grantEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderGrant, wantsResponderInst, nativeEvent, nativeEventTarget);
1525 grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1526
1527 accumulateDirectDispatches(grantEvent);
1528 var blockHostResponder = executeDirectDispatch(grantEvent) === true;
1529 if (responderInst) {
1530 var terminationRequestEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderTerminationRequest, responderInst, nativeEvent, nativeEventTarget);
1531 terminationRequestEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1532 accumulateDirectDispatches(terminationRequestEvent);
1533 var shouldSwitch = !hasDispatches(terminationRequestEvent) || executeDirectDispatch(terminationRequestEvent);
1534 if (!terminationRequestEvent.isPersistent()) {
1535 terminationRequestEvent.constructor.release(terminationRequestEvent);
1536 }
1537
1538 if (shouldSwitch) {
1539 var terminateEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderTerminate, responderInst, nativeEvent, nativeEventTarget);
1540 terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1541 accumulateDirectDispatches(terminateEvent);
1542 extracted = accumulate(extracted, [grantEvent, terminateEvent]);
1543 changeResponder(wantsResponderInst, blockHostResponder);
1544 } else {
1545 var rejectEvent = ResponderSyntheticEvent.getPooled(eventTypes.responderReject, wantsResponderInst, nativeEvent, nativeEventTarget);
1546 rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1547 accumulateDirectDispatches(rejectEvent);
1548 extracted = accumulate(extracted, rejectEvent);
1549 }
1550 } else {
1551 extracted = accumulate(extracted, grantEvent);
1552 changeResponder(wantsResponderInst, blockHostResponder);
1553 }
1554 return extracted;
1555}
1556
1557/**
1558 * A transfer is a negotiation between a currently set responder and the next
1559 * element to claim responder status. Any start event could trigger a transfer
1560 * of responderInst. Any move event could trigger a transfer.
1561 *
1562 * @param {string} topLevelType Record from `BrowserEventConstants`.
1563 * @return {boolean} True if a transfer of responder could possibly occur.
1564 */
1565function canTriggerTransfer(topLevelType, topLevelInst, nativeEvent) {
1566 return topLevelInst && (
1567 // responderIgnoreScroll: We are trying to migrate away from specifically
1568 // tracking native scroll events here and responderIgnoreScroll indicates we
1569 // will send topTouchCancel to handle canceling touch events instead
1570 topLevelType === TOP_SCROLL && !nativeEvent.responderIgnoreScroll || trackedTouchCount > 0 && topLevelType === TOP_SELECTION_CHANGE || isStartish(topLevelType) || isMoveish(topLevelType));
1571}
1572
1573/**
1574 * Returns whether or not this touch end event makes it such that there are no
1575 * longer any touches that started inside of the current `responderInst`.
1576 *
1577 * @param {NativeEvent} nativeEvent Native touch end event.
1578 * @return {boolean} Whether or not this touch end event ends the responder.
1579 */
1580function noResponderTouches(nativeEvent) {
1581 var touches = nativeEvent.touches;
1582 if (!touches || touches.length === 0) {
1583 return true;
1584 }
1585 for (var i = 0; i < touches.length; i++) {
1586 var activeTouch = touches[i];
1587 var target = activeTouch.target;
1588 if (target !== null && target !== undefined && target !== 0) {
1589 // Is the original touch location inside of the current responder?
1590 var targetInst = getInstanceFromNode$1(target);
1591 if (isAncestor(responderInst, targetInst)) {
1592 return false;
1593 }
1594 }
1595 }
1596 return true;
1597}
1598
1599var ResponderEventPlugin = {
1600 /* For unit testing only */
1601 _getResponder: function () {
1602 return responderInst;
1603 },
1604
1605 eventTypes: eventTypes,
1606
1607 /**
1608 * We must be resilient to `targetInst` being `null` on `touchMove` or
1609 * `touchEnd`. On certain platforms, this means that a native scroll has
1610 * assumed control and the original touch targets are destroyed.
1611 */
1612 extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
1613 if (isStartish(topLevelType)) {
1614 trackedTouchCount += 1;
1615 } else if (isEndish(topLevelType)) {
1616 if (trackedTouchCount >= 0) {
1617 trackedTouchCount -= 1;
1618 } else {
1619 console.error('Ended a touch event which was not counted in `trackedTouchCount`.');
1620 return null;
1621 }
1622 }
1623
1624 ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
1625
1626 var extracted = canTriggerTransfer(topLevelType, targetInst, nativeEvent) ? setResponderAndExtractTransfer(topLevelType, targetInst, nativeEvent, nativeEventTarget) : null;
1627 // Responder may or may not have transferred on a new touch start/move.
1628 // Regardless, whoever is the responder after any potential transfer, we
1629 // direct all touch start/move/ends to them in the form of
1630 // `onResponderMove/Start/End`. These will be called for *every* additional
1631 // finger that move/start/end, dispatched directly to whoever is the
1632 // current responder at that moment, until the responder is "released".
1633 //
1634 // These multiple individual change touch events are are always bookended
1635 // by `onResponderGrant`, and one of
1636 // (`onResponderRelease/onResponderTerminate`).
1637 var isResponderTouchStart = responderInst && isStartish(topLevelType);
1638 var isResponderTouchMove = responderInst && isMoveish(topLevelType);
1639 var isResponderTouchEnd = responderInst && isEndish(topLevelType);
1640 var incrementalTouch = isResponderTouchStart ? eventTypes.responderStart : isResponderTouchMove ? eventTypes.responderMove : isResponderTouchEnd ? eventTypes.responderEnd : null;
1641
1642 if (incrementalTouch) {
1643 var gesture = ResponderSyntheticEvent.getPooled(incrementalTouch, responderInst, nativeEvent, nativeEventTarget);
1644 gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
1645 accumulateDirectDispatches(gesture);
1646 extracted = accumulate(extracted, gesture);
1647 }
1648
1649 var isResponderTerminate = responderInst && topLevelType === TOP_TOUCH_CANCEL;
1650 var isResponderRelease = responderInst && !isResponderTerminate && isEndish(topLevelType) && noResponderTouches(nativeEvent);
1651 var finalTouch = isResponderTerminate ? eventTypes.responderTerminate : isResponderRelease ? eventTypes.responderRelease : null;
1652 if (finalTouch) {
1653 var finalEvent = ResponderSyntheticEvent.getPooled(finalTouch, responderInst, nativeEvent, nativeEventTarget);
1654 finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
1655 accumulateDirectDispatches(finalEvent);
1656 extracted = accumulate(extracted, finalEvent);
1657 changeResponder(null);
1658 }
1659
1660 return extracted;
1661 },
1662
1663 GlobalResponderHandler: null,
1664
1665 injection: {
1666 /**
1667 * @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
1668 * Object that handles any change in responder. Use this to inject
1669 * integration with an existing touch handling system etc.
1670 */
1671 injectGlobalResponderHandler: function (GlobalResponderHandler) {
1672 ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
1673 }
1674 }
1675};
1676
1677// Inject react-dom's ComponentTree into this module.
1678// Keep in sync with ReactDOM.js, ReactTestUtils.js, and ReactTestUtilsAct.js:
1679var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events;
1680var getInstanceFromNode = _ReactDOM$__SECRET_IN[0];
1681var getNodeFromInstance = _ReactDOM$__SECRET_IN[1];
1682var getFiberCurrentPropsFromNode = _ReactDOM$__SECRET_IN[2];
1683var injectEventPluginsByName = _ReactDOM$__SECRET_IN[3];
1684
1685
1686setComponentTree(getFiberCurrentPropsFromNode, getInstanceFromNode, getNodeFromInstance);
1687
1688
1689
1690var ReactDOMUnstableNativeDependencies = Object.freeze({
1691 ResponderEventPlugin: ResponderEventPlugin,
1692 ResponderTouchHistoryStore: ResponderTouchHistoryStore,
1693 injectEventPluginsByName: injectEventPluginsByName
1694});
1695
1696var unstableNativeDependencies = ReactDOMUnstableNativeDependencies;
1697
1698return unstableNativeDependencies;
1699
1700})));