"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _transitionManager = _interopRequireDefault(require("./transition-manager"));

var _linearInterpolator = _interopRequireDefault(require("../transitions/linear-interpolator"));

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

var NO_TRANSITION_PROPS = {
  transitionDuration: 0
};
var DEFAULT_INERTIA = 300;

var INERTIA_EASING = function INERTIA_EASING(t) {
  return 1 - (1 - t) * (1 - t);
};

var EVENT_TYPES = {
  WHEEL: ['wheel'],
  PAN: ['panstart', 'panmove', 'panend'],
  PINCH: ['pinchstart', 'pinchmove', 'pinchend'],
  TRIPLE_PAN: ['tripanstart', 'tripanmove', 'tripanend'],
  DOUBLE_TAP: ['doubletap'],
  KEYBOARD: ['keydown']
};
var pinchEventWorkaround = {};

var Controller = function () {
  function Controller(opts) {
    var _this = this;

    (0, _classCallCheck2.default)(this, Controller);
    this.props = void 0;
    this.state = {};
    this.transitionManager = void 0;
    this.eventManager = void 0;
    this.onViewStateChange = void 0;
    this.onStateChange = void 0;
    this.makeViewport = void 0;
    this._controllerState = void 0;
    this._events = {};
    this._interactionState = {
      isDragging: false
    };
    this._customEvents = [];
    this._eventStartBlocked = null;
    this._panMove = false;
    this.invertPan = false;
    this.dragMode = 'rotate';
    this.inertia = 0;
    this.scrollZoom = true;
    this.dragPan = true;
    this.dragRotate = true;
    this.doubleClickZoom = true;
    this.touchZoom = true;
    this.touchRotate = false;
    this.keyboard = true;
    this.transitionManager = new _transitionManager.default(_objectSpread(_objectSpread({}, opts), {}, {
      getControllerState: function getControllerState(props) {
        return new _this.ControllerState(props);
      },
      onViewStateChange: this._onTransition.bind(this),
      onStateChange: this._setInteractionState.bind(this)
    }));
    this.handleEvent = this.handleEvent.bind(this);
    this.eventManager = opts.eventManager;

    this.onViewStateChange = opts.onViewStateChange || function () {};

    this.onStateChange = opts.onStateChange || function () {};

    this.makeViewport = opts.makeViewport;
  }

  var _proto = Controller.prototype;

  _proto.finalize = function finalize() {
    for (var eventName in this._events) {
      if (this._events[eventName]) {
        var _this$eventManager;

        (_this$eventManager = this.eventManager) === null || _this$eventManager === void 0 ? void 0 : _this$eventManager.off(eventName, this.handleEvent);
      }
    }

    this.transitionManager.finalize();
  };

  _proto.handleEvent = function handleEvent(event) {
    this._controllerState = undefined;
    var eventStartBlocked = this._eventStartBlocked;

    switch (event.type) {
      case 'panstart':
        return eventStartBlocked ? false : this._onPanStart(event);

      case 'panmove':
        return this._onPan(event);

      case 'panend':
        return this._onPanEnd(event);

      case 'pinchstart':
        return eventStartBlocked ? false : this._onPinchStart(event);

      case 'pinchmove':
        return this._onPinch(event);

      case 'pinchend':
        return this._onPinchEnd(event);

      case 'tripanstart':
        return eventStartBlocked ? false : this._onTriplePanStart(event);

      case 'tripanmove':
        return this._onTriplePan(event);

      case 'tripanend':
        return this._onTriplePanEnd(event);

      case 'doubletap':
        return this._onDoubleTap(event);

      case 'wheel':
        return this._onWheel(event);

      case 'keydown':
        return this._onKeyDown(event);

      default:
        return false;
    }
  };

  _proto.getCenter = function getCenter(event) {
    var _this$props = this.props,
        x = _this$props.x,
        y = _this$props.y;
    var offsetCenter = event.offsetCenter;
    return [offsetCenter.x - x, offsetCenter.y - y];
  };

  _proto.isPointInBounds = function isPointInBounds(pos, event) {
    var _this$props2 = this.props,
        width = _this$props2.width,
        height = _this$props2.height;

    if (event && event.handled) {
      return false;
    }

    var inside = pos[0] >= 0 && pos[0] <= width && pos[1] >= 0 && pos[1] <= height;

    if (inside && event) {
      event.stopPropagation();
    }

    return inside;
  };

  _proto.isFunctionKeyPressed = function isFunctionKeyPressed(event) {
    var srcEvent = event.srcEvent;
    return Boolean(srcEvent.metaKey || srcEvent.altKey || srcEvent.ctrlKey || srcEvent.shiftKey);
  };

  _proto.isDragging = function isDragging() {
    return this._interactionState.isDragging || false;
  };

  _proto.blockEvents = function blockEvents(timeout) {
    var _this2 = this;

    var timer = setTimeout(function () {
      if (_this2._eventStartBlocked === timer) {
        _this2._eventStartBlocked = null;
      }
    }, timeout);
    this._eventStartBlocked = timer;
  };

  _proto.setProps = function setProps(props) {
    if (props.dragMode) {
      this.dragMode = props.dragMode;
    }

    this.props = props;

    if (!('transitionInterpolator' in props)) {
      props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
    }

    this.transitionManager.processViewStateChange(props);
    var inertia = props.inertia;
    this.inertia = Number.isFinite(inertia) ? inertia : inertia === true ? DEFAULT_INERTIA : 0;
    var _props$scrollZoom = props.scrollZoom,
        scrollZoom = _props$scrollZoom === void 0 ? true : _props$scrollZoom,
        _props$dragPan = props.dragPan,
        dragPan = _props$dragPan === void 0 ? true : _props$dragPan,
        _props$dragRotate = props.dragRotate,
        dragRotate = _props$dragRotate === void 0 ? true : _props$dragRotate,
        _props$doubleClickZoo = props.doubleClickZoom,
        doubleClickZoom = _props$doubleClickZoo === void 0 ? true : _props$doubleClickZoo,
        _props$touchZoom = props.touchZoom,
        touchZoom = _props$touchZoom === void 0 ? true : _props$touchZoom,
        _props$touchRotate = props.touchRotate,
        touchRotate = _props$touchRotate === void 0 ? false : _props$touchRotate,
        _props$keyboard = props.keyboard,
        keyboard = _props$keyboard === void 0 ? true : _props$keyboard;
    var isInteractive = Boolean(this.onViewStateChange);
    this.toggleEvents(EVENT_TYPES.WHEEL, isInteractive && scrollZoom);
    this.toggleEvents(EVENT_TYPES.PAN, isInteractive);
    this.toggleEvents(EVENT_TYPES.PINCH, isInteractive && (touchZoom || touchRotate));
    this.toggleEvents(EVENT_TYPES.TRIPLE_PAN, isInteractive && touchRotate);
    this.toggleEvents(EVENT_TYPES.DOUBLE_TAP, isInteractive && doubleClickZoom);
    this.toggleEvents(EVENT_TYPES.KEYBOARD, isInteractive && keyboard);
    this.scrollZoom = scrollZoom;
    this.dragPan = dragPan;
    this.dragRotate = dragRotate;
    this.doubleClickZoom = doubleClickZoom;
    this.touchZoom = touchZoom;
    this.touchRotate = touchRotate;
    this.keyboard = keyboard;
  };

  _proto.updateTransition = function updateTransition() {
    this.transitionManager.updateTransition();
  };

  _proto.toggleEvents = function toggleEvents(eventNames, enabled) {
    var _this3 = this;

    if (this.eventManager) {
      eventNames.forEach(function (eventName) {
        if (_this3._events[eventName] !== enabled) {
          _this3._events[eventName] = enabled;

          if (enabled) {
            _this3.eventManager.on(eventName, _this3.handleEvent);
          } else {
            _this3.eventManager.off(eventName, _this3.handleEvent);
          }
        }
      });
    }
  };

  _proto.updateViewport = function updateViewport(newControllerState) {
    var extraProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    var interactionState = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

    var viewState = _objectSpread(_objectSpread({}, newControllerState.getViewportProps()), extraProps);

    var changed = this.controllerState !== newControllerState;
    this.state = newControllerState.getState();

    this._setInteractionState(interactionState);

    if (changed) {
      var oldViewState = this.controllerState && this.controllerState.getViewportProps();

      if (this.onViewStateChange) {
        this.onViewStateChange({
          viewState: viewState,
          interactionState: this._interactionState,
          oldViewState: oldViewState
        });
      }
    }
  };

  _proto._onTransition = function _onTransition(params) {
    this.onViewStateChange(_objectSpread(_objectSpread({}, params), {}, {
      interactionState: this._interactionState
    }));
  };

  _proto._setInteractionState = function _setInteractionState(newStates) {
    Object.assign(this._interactionState, newStates);
    this.onStateChange(this._interactionState);
  };

  _proto._onPanStart = function _onPanStart(event) {
    var pos = this.getCenter(event);

    if (!this.isPointInBounds(pos, event)) {
      return false;
    }

    var alternateMode = this.isFunctionKeyPressed(event) || event.rightButton || false;

    if (this.invertPan || this.dragMode === 'pan') {
      alternateMode = !alternateMode;
    }

    var newControllerState = this.controllerState[alternateMode ? 'panStart' : 'rotateStart']({
      pos: pos
    });
    this._panMove = alternateMode;
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true
    });
    return true;
  };

  _proto._onPan = function _onPan(event) {
    if (!this.isDragging()) {
      return false;
    }

    return this._panMove ? this._onPanMove(event) : this._onPanRotate(event);
  };

  _proto._onPanEnd = function _onPanEnd(event) {
    if (!this.isDragging()) {
      return false;
    }

    return this._panMove ? this._onPanMoveEnd(event) : this._onPanRotateEnd(event);
  };

  _proto._onPanMove = function _onPanMove(event) {
    if (!this.dragPan) {
      return false;
    }

    var pos = this.getCenter(event);
    var newControllerState = this.controllerState.pan({
      pos: pos
    });
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true,
      isPanning: true
    });
    return true;
  };

  _proto._onPanMoveEnd = function _onPanMoveEnd(event) {
    var inertia = this.inertia;

    if (this.dragPan && inertia && event.velocity) {
      var pos = this.getCenter(event);
      var endPos = [pos[0] + event.velocityX * inertia / 2, pos[1] + event.velocityY * inertia / 2];
      var newControllerState = this.controllerState.pan({
        pos: endPos
      }).panEnd();
      this.updateViewport(newControllerState, _objectSpread(_objectSpread({}, this._getTransitionProps()), {}, {
        transitionDuration: inertia,
        transitionEasing: INERTIA_EASING
      }), {
        isDragging: false,
        isPanning: true
      });
    } else {
      var _newControllerState = this.controllerState.panEnd();

      this.updateViewport(_newControllerState, null, {
        isDragging: false,
        isPanning: false
      });
    }

    return true;
  };

  _proto._onPanRotate = function _onPanRotate(event) {
    if (!this.dragRotate) {
      return false;
    }

    var pos = this.getCenter(event);
    var newControllerState = this.controllerState.rotate({
      pos: pos
    });
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true,
      isRotating: true
    });
    return true;
  };

  _proto._onPanRotateEnd = function _onPanRotateEnd(event) {
    var inertia = this.inertia;

    if (this.dragRotate && inertia && event.velocity) {
      var pos = this.getCenter(event);
      var endPos = [pos[0] + event.velocityX * inertia / 2, pos[1] + event.velocityY * inertia / 2];
      var newControllerState = this.controllerState.rotate({
        pos: endPos
      }).rotateEnd();
      this.updateViewport(newControllerState, _objectSpread(_objectSpread({}, this._getTransitionProps()), {}, {
        transitionDuration: inertia,
        transitionEasing: INERTIA_EASING
      }), {
        isDragging: false,
        isRotating: true
      });
    } else {
      var _newControllerState2 = this.controllerState.rotateEnd();

      this.updateViewport(_newControllerState2, null, {
        isDragging: false,
        isRotating: false
      });
    }

    return true;
  };

  _proto._onWheel = function _onWheel(event) {
    if (!this.scrollZoom) {
      return false;
    }

    var pos = this.getCenter(event);

    if (!this.isPointInBounds(pos, event)) {
      return false;
    }

    event.srcEvent.preventDefault();

    var _ref = this.scrollZoom === true ? {} : this.scrollZoom,
        _ref$speed = _ref.speed,
        speed = _ref$speed === void 0 ? 0.01 : _ref$speed,
        _ref$smooth = _ref.smooth,
        smooth = _ref$smooth === void 0 ? false : _ref$smooth;

    var delta = event.delta;
    var scale = 2 / (1 + Math.exp(-Math.abs(delta * speed)));

    if (delta < 0 && scale !== 0) {
      scale = 1 / scale;
    }

    var newControllerState = this.controllerState.zoom({
      pos: pos,
      scale: scale
    });
    this.updateViewport(newControllerState, _objectSpread(_objectSpread({}, this._getTransitionProps({
      around: pos
    })), {}, {
      transitionDuration: smooth ? 250 : 1
    }), {
      isZooming: true,
      isPanning: true
    });
    return true;
  };

  _proto._onTriplePanStart = function _onTriplePanStart(event) {
    var pos = this.getCenter(event);

    if (!this.isPointInBounds(pos, event)) {
      return false;
    }

    var newControllerState = this.controllerState.rotateStart({
      pos: pos
    });
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true
    });
    return true;
  };

  _proto._onTriplePan = function _onTriplePan(event) {
    if (!this.touchRotate) {
      return false;
    }

    if (!this.isDragging()) {
      return false;
    }

    var pos = this.getCenter(event);
    pos[0] -= event.deltaX;
    var newControllerState = this.controllerState.rotate({
      pos: pos
    });
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true,
      isRotating: true
    });
    return true;
  };

  _proto._onTriplePanEnd = function _onTriplePanEnd(event) {
    if (!this.isDragging()) {
      return false;
    }

    var inertia = this.inertia;

    if (this.touchRotate && inertia && event.velocityY) {
      var pos = this.getCenter(event);
      var endPos = [pos[0], pos[1] += event.velocityY * inertia / 2];
      var newControllerState = this.controllerState.rotate({
        pos: endPos
      });
      this.updateViewport(newControllerState, _objectSpread(_objectSpread({}, this._getTransitionProps()), {}, {
        transitionDuration: inertia,
        transitionEasing: INERTIA_EASING
      }), {
        isDragging: false,
        isRotating: true
      });
      this.blockEvents(inertia);
    } else {
      var _newControllerState3 = this.controllerState.rotateEnd();

      this.updateViewport(_newControllerState3, null, {
        isDragging: false,
        isRotating: false
      });
    }

    return true;
  };

  _proto._onPinchStart = function _onPinchStart(event) {
    var pos = this.getCenter(event);

    if (!this.isPointInBounds(pos, event)) {
      return false;
    }

    var newControllerState = this.controllerState.zoomStart({
      pos: pos
    }).rotateStart({
      pos: pos
    });
    pinchEventWorkaround._startPinchRotation = event.rotation;
    pinchEventWorkaround._lastPinchEvent = event;
    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true
    });
    return true;
  };

  _proto._onPinch = function _onPinch(event) {
    if (!this.touchZoom && !this.touchRotate) {
      return false;
    }

    if (!this.isDragging()) {
      return false;
    }

    var newControllerState = this.controllerState;

    if (this.touchZoom) {
      var scale = event.scale;
      var pos = this.getCenter(event);
      newControllerState = newControllerState.zoom({
        pos: pos,
        scale: scale
      });
    }

    if (this.touchRotate) {
      var rotation = event.rotation;
      newControllerState = newControllerState.rotate({
        deltaAngleX: pinchEventWorkaround._startPinchRotation - rotation
      });
    }

    this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
      isDragging: true,
      isPanning: this.touchZoom,
      isZooming: this.touchZoom,
      isRotating: this.touchRotate
    });
    pinchEventWorkaround._lastPinchEvent = event;
    return true;
  };

  _proto._onPinchEnd = function _onPinchEnd(event) {
    if (!this.isDragging()) {
      return false;
    }

    var inertia = this.inertia;
    var _lastPinchEvent = pinchEventWorkaround._lastPinchEvent;

    if (this.touchZoom && inertia && _lastPinchEvent && event.scale !== _lastPinchEvent.scale) {
      var pos = this.getCenter(event);
      var newControllerState = this.controllerState.rotateEnd();
      var z = Math.log2(event.scale);

      var velocityZ = (z - Math.log2(_lastPinchEvent.scale)) / (event.deltaTime - _lastPinchEvent.deltaTime);

      var endScale = Math.pow(2, z + velocityZ * inertia / 2);
      newControllerState = newControllerState.zoom({
        pos: pos,
        scale: endScale
      }).zoomEnd();
      this.updateViewport(newControllerState, _objectSpread(_objectSpread({}, this._getTransitionProps({
        around: pos
      })), {}, {
        transitionDuration: inertia,
        transitionEasing: INERTIA_EASING
      }), {
        isDragging: false,
        isPanning: this.touchZoom,
        isZooming: this.touchZoom,
        isRotating: false
      });
      this.blockEvents(inertia);
    } else {
      var _newControllerState4 = this.controllerState.zoomEnd().rotateEnd();

      this.updateViewport(_newControllerState4, null, {
        isDragging: false,
        isPanning: false,
        isZooming: false,
        isRotating: false
      });
    }

    pinchEventWorkaround._startPinchRotation = null;
    pinchEventWorkaround._lastPinchEvent = null;
    return true;
  };

  _proto._onDoubleTap = function _onDoubleTap(event) {
    if (!this.doubleClickZoom) {
      return false;
    }

    var pos = this.getCenter(event);

    if (!this.isPointInBounds(pos, event)) {
      return false;
    }

    var isZoomOut = this.isFunctionKeyPressed(event);
    var newControllerState = this.controllerState.zoom({
      pos: pos,
      scale: isZoomOut ? 0.5 : 2
    });
    this.updateViewport(newControllerState, this._getTransitionProps({
      around: pos
    }), {
      isZooming: true,
      isPanning: true
    });
    this.blockEvents(100);
    return true;
  };

  _proto._onKeyDown = function _onKeyDown(event) {
    if (!this.keyboard) {
      return false;
    }

    var funcKey = this.isFunctionKeyPressed(event);

    var _ref2 = this.keyboard === true ? {} : this.keyboard,
        zoomSpeed = _ref2.zoomSpeed,
        moveSpeed = _ref2.moveSpeed,
        rotateSpeedX = _ref2.rotateSpeedX,
        rotateSpeedY = _ref2.rotateSpeedY;

    var controllerState = this.controllerState;
    var newControllerState;
    var interactionState = {};

    switch (event.srcEvent.code) {
      case 'Minus':
        newControllerState = funcKey ? controllerState.zoomOut(zoomSpeed).zoomOut(zoomSpeed) : controllerState.zoomOut(zoomSpeed);
        interactionState.isZooming = true;
        break;

      case 'Equal':
        newControllerState = funcKey ? controllerState.zoomIn(zoomSpeed).zoomIn(zoomSpeed) : controllerState.zoomIn(zoomSpeed);
        interactionState.isZooming = true;
        break;

      case 'ArrowLeft':
        if (funcKey) {
          newControllerState = controllerState.rotateLeft(rotateSpeedX);
          interactionState.isRotating = true;
        } else {
          newControllerState = controllerState.moveLeft(moveSpeed);
          interactionState.isPanning = true;
        }

        break;

      case 'ArrowRight':
        if (funcKey) {
          newControllerState = controllerState.rotateRight(rotateSpeedX);
          interactionState.isRotating = true;
        } else {
          newControllerState = controllerState.moveRight(moveSpeed);
          interactionState.isPanning = true;
        }

        break;

      case 'ArrowUp':
        if (funcKey) {
          newControllerState = controllerState.rotateUp(rotateSpeedY);
          interactionState.isRotating = true;
        } else {
          newControllerState = controllerState.moveUp(moveSpeed);
          interactionState.isPanning = true;
        }

        break;

      case 'ArrowDown':
        if (funcKey) {
          newControllerState = controllerState.rotateDown(rotateSpeedY);
          interactionState.isRotating = true;
        } else {
          newControllerState = controllerState.moveDown(moveSpeed);
          interactionState.isPanning = true;
        }

        break;

      default:
        return false;
    }

    this.updateViewport(newControllerState, this._getTransitionProps(), interactionState);
    return true;
  };

  _proto._getTransitionProps = function _getTransitionProps(opts) {
    var transition = this.transition;

    if (!transition || !transition.transitionInterpolator) {
      return NO_TRANSITION_PROPS;
    }

    return opts ? _objectSpread(_objectSpread({}, transition), {}, {
      transitionInterpolator: new _linearInterpolator.default(_objectSpread(_objectSpread(_objectSpread({}, opts), transition.transitionInterpolator.opts), {}, {
        makeViewport: this.controllerState.makeViewport
      }))
    }) : transition;
  };

  (0, _createClass2.default)(Controller, [{
    key: "events",
    set: function set(customEvents) {
      this.toggleEvents(this._customEvents, false);
      this.toggleEvents(customEvents, true);
      this._customEvents = customEvents;

      if (this.props) {
        this.setProps(this.props);
      }
    }
  }, {
    key: "controllerState",
    get: function get() {
      this._controllerState = this._controllerState || new this.ControllerState(_objectSpread(_objectSpread({
        makeViewport: this.makeViewport
      }, this.props), this.state));
      return this._controllerState;
    }
  }]);
  return Controller;
}();

exports.default = Controller;
//# sourceMappingURL=controller.js.map