UNPKG

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