content: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   */
  
  import type {
    ReactResponderEvent,
    ReactResponderContext,
  } from 'shared/ReactTypes';
  import type {EventPriority} from 'shared/ReactTypes';
  
  import React from 'react';
  import {UserBlockingEvent, DiscreteEvent} from 'shared/ReactTypes';
  
  const targetEventTypes = ['pointerdown'];
  const rootEventTypes = [
    'pointerup',
    'pointercancel',
    {name: 'pointermove', passive: false},
  ];
  
  // In the case we don't have PointerEvents (Safari), we listen to touch events
  // too
  if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
    targetEventTypes.push('touchstart', 'mousedown');
    rootEventTypes.push('mouseup', 'mousemove', 'touchend', 'touchcancel', {
      name: 'touchmove',
      passive: false,
    });
  }
  
  type EventData = {
    diffX: number,
    diffY: number,
  };
  type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
  
  type SwipeEvent = {|
    target: Element | Document,
    type: SwipeEventType,
    timeStamp: number,
    diffX?: number,
    diffY?: number,
  |};
  
  function createSwipeEvent(
    context: ReactResponderContext,
    type: SwipeEventType,
    target: Element | Document,
    eventData?: EventData,
  ): SwipeEvent {
    return context.objectAssign(
      {
        target,
        type,
        timeStamp: context.getTimeStamp(),
      },
      eventData,
    );
  }
  
  function dispatchSwipeEvent(
    context: ReactResponderContext,
    name: SwipeEventType,
    listener: SwipeEvent => void,
    state: SwipeState,
    eventPriority: EventPriority,
    eventData?: EventData,
  ) {
    const target = ((state.swipeTarget: any): Element | Document);
    const syntheticEvent = createSwipeEvent(context, name, target, eventData);
    context.dispatchEvent(syntheticEvent, listener, eventPriority);
  }
  
  type SwipeState = {
    direction: number,
    isSwiping: boolean,
    lastDirection: number,
    startX: number,
    startY: number,
    touchId: null | number,
    swipeTarget: null | Element | Document,
    x: number,
    y: number,
  };
  
  const SwipeResponder = {
    targetEventTypes,
    createInitialState(): SwipeState {
      return {
        direction: 0,
        isSwiping: false,
        lastDirection: 0,
        startX: 0,
        startY: 0,
        touchId: null,
        swipeTarget: null,
        x: 0,
        y: 0,
      };
    },
    allowMultipleHostChildren: false,
    stopLocalPropagation: true,
    onEvent(
      event: ReactResponderEvent,
      context: ReactResponderContext,
      props: Object,
      state: SwipeState,
    ): void {
      const {target, type, nativeEvent} = event;
  
      switch (type) {
        case 'touchstart':
        case 'mousedown':
        case 'pointerdown': {
          if (!state.isSwiping) {
            let obj = nativeEvent;
            if (type === 'touchstart') {
              obj = (nativeEvent: any).targetTouches[0];
              state.touchId = obj.identifier;
            }
            const x = (obj: any).screenX;
            const y = (obj: any).screenY;
  
            let shouldEnableSwiping = true;
  
            if (props.onShouldClaimOwnership && props.onShouldClaimOwnership()) {
              shouldEnableSwiping = context.requestGlobalOwnership();
            }
            if (shouldEnableSwiping) {
              state.isSwiping = true;
              state.startX = x;
              state.startY = y;
              state.x = x;
              state.y = y;
              state.swipeTarget = target;
              context.addRootEventTypes(rootEventTypes);
            } else {
              state.touchId = null;
            }
          }
          break;
        }
      }
    },
    onRootEvent(
      event: ReactResponderEvent,
      context: ReactResponderContext,
      props: Object,
      state: SwipeState,
    ): void {
      const {type, nativeEvent} = event;
  
      switch (type) {
        case 'touchmove':
        case 'mousemove':
        case 'pointermove': {
          if (event.passive) {
            return;
          }
          if (state.isSwiping) {
            let obj = null;
            if (type === 'touchmove') {
              const targetTouches = (nativeEvent: any).targetTouches;
              for (let i = 0; i < targetTouches.length; i++) {
                if (state.touchId === targetTouches[i].identifier) {
                  obj = targetTouches[i];
                  break;
                }
              }
            } else {
              obj = nativeEvent;
            }
            if (obj === null) {
              state.isSwiping = false;
              state.swipeTarget = null;
              state.touchId = null;
              context.removeRootEventTypes(rootEventTypes);
              return;
            }
            const x = (obj: any).screenX;
            const y = (obj: any).screenY;
            if (x < state.x && props.onSwipeLeft) {
              state.direction = 3;
            } else if (x > state.x && props.onSwipeRight) {
              state.direction = 1;
            }
            state.x = x;
            state.y = y;
            if (props.onSwipeMove) {
              const eventData = {
                diffX: x - state.startX,
                diffY: y - state.startY,
              };
              dispatchSwipeEvent(
                context,
                'swipemove',
                props.onSwipeMove,
                state,
                UserBlockingEvent,
                eventData,
              );
              (nativeEvent: any).preventDefault();
            }
          }
          break;
        }
        case 'pointercancel':
        case 'touchcancel':
        case 'touchend':
        case 'mouseup':
        case 'pointerup': {
          if (state.isSwiping) {
            if (state.x === state.startX && state.y === state.startY) {
              return;
            }
            if (props.onShouldClaimOwnership) {
              context.releaseOwnership();
            }
            const direction = state.direction;
            const lastDirection = state.lastDirection;
            if (direction !== lastDirection) {
              if (props.onSwipeLeft && direction === 3) {
                dispatchSwipeEvent(
                  context,
                  'swipeleft',
                  props.onSwipeLeft,
                  state,
                  DiscreteEvent,
                );
              } else if (props.onSwipeRight && direction === 1) {
                dispatchSwipeEvent(
                  context,
                  'swiperight',
                  props.onSwipeRight,
                  state,
                  DiscreteEvent,
                );
              }
            }
            if (props.onSwipeEnd) {
              dispatchSwipeEvent(
                context,
                'swipeend',
                props.onSwipeEnd,
                state,
                DiscreteEvent,
              );
            }
            state.lastDirection = direction;
            state.isSwiping = false;
            state.swipeTarget = null;
            state.touchId = null;
            context.removeRootEventTypes(rootEventTypes);
          }
          break;
        }
      }
    },
  };
  
  export default React.unstable_createEventComponent(SwipeResponder, 'Swipe');
  