"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.calculatePlacement = calculatePlacement;
exports.calculatePosition = calculatePosition;
exports.findOverflowScrollParent = findOverflowScrollParent;
exports.getHorizontalPlacement = getHorizontalPlacement;
exports.getVerticalPlacement = getVerticalPlacement;
exports.isBody = isBody;
exports.isTextNode = isTextNode;
exports.validatePosition = validatePosition;

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function isBody(elem) {
  return elem === document.body;
}

function isTextNode(elem) {
  return elem && elem.nodeType === 3;
}
/**
 * Decides if given fitHeight fits below or above the target taking boundaries into account.
 */


function getVerticalPlacement(target, boundariesElement, fitHeight, alignY, forcePlacement) {
  if (forcePlacement && alignY) {
    return alignY;
  }

  if (!fitHeight) {
    return 'bottom';
  }

  if (isTextNode(target)) {
    target = target.parentElement;
  }

  var boundariesClientRect = boundariesElement.getBoundingClientRect();
  var boundariesHeight = boundariesClientRect.height;
  var boundariesTop = isBody(boundariesElement) ? 0 : boundariesClientRect.top;

  var _target$getBoundingCl = target.getBoundingClientRect(),
      targetTop = _target$getBoundingCl.top,
      targetHeight = _target$getBoundingCl.height;

  var spaceAbove = targetTop - (boundariesTop - boundariesElement.scrollTop);
  var spaceBelow = boundariesTop + boundariesHeight - (targetTop + targetHeight);

  if (spaceBelow >= fitHeight || spaceBelow >= spaceAbove) {
    return 'bottom';
  }

  return 'top';
}
/**
 * Decides if given fitWidth fits to the left or to the right of the target taking boundaries into account.
 */


function getHorizontalPlacement(target, boundariesElement, fitWidth, alignX, forcePlacement) {
  if (forcePlacement && alignX) {
    return alignX;
  }

  if (!fitWidth) {
    return alignX || 'left';
  }

  if (isTextNode(target)) {
    target = target.parentElement;
  }

  var _target$getBoundingCl2 = target.getBoundingClientRect(),
      targetLeft = _target$getBoundingCl2.left,
      targetWidth = _target$getBoundingCl2.width;

  var _boundariesElement$ge = boundariesElement.getBoundingClientRect(),
      boundariesLeft = _boundariesElement$ge.left,
      boundariesWidth = _boundariesElement$ge.width;

  var spaceLeft = targetLeft - boundariesLeft + targetWidth;
  var spaceRight = boundariesLeft + boundariesWidth - targetLeft;

  if (alignX && spaceLeft > fitWidth && spaceRight > fitWidth) {
    return alignX;
  } else if (spaceRight >= fitWidth || spaceRight >= spaceLeft && !alignX) {
    return 'left';
  }

  return 'right';
}

function calculatePlacement(target, boundariesElement, fitWidth, fitHeight, alignX, alignY, forcePlacement) {
  return [getVerticalPlacement(target, boundariesElement, fitHeight, alignY, forcePlacement), getHorizontalPlacement(target, boundariesElement, fitWidth, alignX, forcePlacement)];
}

var calculateHorizontalPlacement = function calculateHorizontalPlacement(_ref) {
  var placement = _ref.placement,
      targetLeft = _ref.targetLeft,
      targetRight = _ref.targetRight,
      targetWidth = _ref.targetWidth,
      isPopupParentBody = _ref.isPopupParentBody,
      popupOffsetParentLeft = _ref.popupOffsetParentLeft,
      popupOffsetParentRight = _ref.popupOffsetParentRight,
      popupOffsetParentScrollLeft = _ref.popupOffsetParentScrollLeft,
      popupOffsetParentClientWidth = _ref.popupOffsetParentClientWidth,
      popupClientWidth = _ref.popupClientWidth,
      offset = _ref.offset,
      _ref$allowOutOfBounds = _ref.allowOutOfBounds,
      allowOutOfBounds = _ref$allowOutOfBounds === void 0 ? false : _ref$allowOutOfBounds;
  var position = {};

  if (placement === 'left') {
    position.left = Math.ceil(targetLeft - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
  } else if (placement === 'center') {
    position.left = Math.ceil(targetLeft - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0] + targetWidth / 2 - popupClientWidth / 2);
  } else if (placement === 'end') {
    var left = Math.ceil(targetRight - popupOffsetParentLeft + (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
    position.left = left;
  } else {
    position.right = Math.ceil(popupOffsetParentRight - targetRight - (isPopupParentBody ? 0 : popupOffsetParentScrollLeft) + offset[0]);
  }

  if (!allowOutOfBounds) {
    if (position.left !== undefined) {
      position.left = getPopupXInsideParent(position.left, popupClientWidth, popupOffsetParentClientWidth);
    }

    if (position.right !== undefined) {
      position.right = getPopupXInsideParent(position.right, popupClientWidth, popupOffsetParentClientWidth);
    }
  }

  return position;
};

var getPopupXInsideParent = function getPopupXInsideParent(x, popupClientWidth, popupOffsetParentClientWidth) {
  // minimum distance the popup can be from the edge of its parent
  var minPopupMargin = 1; // prevent going too far right

  if (popupOffsetParentClientWidth < x + popupClientWidth) {
    x = popupOffsetParentClientWidth - popupClientWidth - minPopupMargin;
  } // prevent going too far left


  return Math.max(minPopupMargin, x);
};

var calculateVerticalStickBottom = function calculateVerticalStickBottom(_ref2) {
  var target = _ref2.target,
      targetTop = _ref2.targetTop,
      targetHeight = _ref2.targetHeight,
      popup = _ref2.popup,
      offset = _ref2.offset,
      position = _ref2.position;
  var scrollParent = findOverflowScrollParent(target);

  var newPos = _objectSpread({}, position);

  if (scrollParent) {
    var topOffsetTop = targetTop - scrollParent.getBoundingClientRect().top;
    var targetEnd = targetHeight + topOffsetTop;

    if (scrollParent.clientHeight - targetEnd <= popup.clientHeight + offset[1] * 2 && topOffsetTop < scrollParent.clientHeight) {
      var scroll = targetEnd - scrollParent.clientHeight + offset[1] * 2;
      var top = newPos.top || 0;
      top = top - (scroll + popup.clientHeight);
      newPos.top = top;
    }
  }

  return newPos;
};

var calculateVerticalStickTop = function calculateVerticalStickTop(_ref3) {
  var target = _ref3.target,
      targetTop = _ref3.targetTop,
      targetHeight = _ref3.targetHeight,
      popupOffsetParentHeight = _ref3.popupOffsetParentHeight,
      popupOffsetParent = _ref3.popupOffsetParent,
      offset = _ref3.offset,
      position = _ref3.position,
      placement = _ref3.placement;
  var scrollParent = findOverflowScrollParent(target);

  var newPos = _objectSpread({}, position);

  if (scrollParent) {
    var _scrollParent$getBoun = scrollParent.getBoundingClientRect(),
        scrollParentTop = _scrollParent$getBoun.top;

    var topBoundary = targetTop - scrollParentTop;
    var scrollParentScrollTop = scrollParent.scrollTop;

    if (topBoundary < 0) {
      var isBelowNodeBoundary = targetTop + (scrollParentScrollTop - scrollParentTop) + targetHeight + offset[1] < scrollParentScrollTop;

      if (placement === 'top') {
        if (isBelowNodeBoundary) {
          newPos.bottom = popupOffsetParentHeight - (topBoundary + popupOffsetParent.scrollTop + targetHeight);
        } else {
          newPos.bottom = topBoundary + (newPos.bottom || 0);
        }
      }

      if (placement === 'start') {
        if (isBelowNodeBoundary) {
          newPos.top = topBoundary + popupOffsetParent.scrollTop + targetHeight;
        } else {
          newPos.top = Math.abs(topBoundary) + (newPos.top || 0) + offset[1];
        }
      }
    }
  }

  return newPos;
};

var calculateVerticalPlacement = function calculateVerticalPlacement(_ref4) {
  var placement = _ref4.placement,
      targetTop = _ref4.targetTop,
      targetHeight = _ref4.targetHeight,
      isPopupParentBody = _ref4.isPopupParentBody,
      popupOffsetParentHeight = _ref4.popupOffsetParentHeight,
      popupOffsetParentTop = _ref4.popupOffsetParentTop,
      popupOffsetParentScrollTop = _ref4.popupOffsetParentScrollTop,
      borderBottomWidth = _ref4.borderBottomWidth,
      offset = _ref4.offset;
  var position = {};

  if (placement === 'top') {
    position.bottom = Math.ceil(popupOffsetParentHeight - (targetTop - popupOffsetParentTop) - (isPopupParentBody ? 0 : popupOffsetParentScrollTop) - borderBottomWidth + offset[1]);
  } else if (placement === 'start') {
    position.top = Math.ceil(targetTop - popupOffsetParentTop - offset[1] + (isPopupParentBody ? 0 : popupOffsetParentScrollTop));
  } else {
    var top = Math.ceil(targetTop - popupOffsetParentTop + targetHeight + (isPopupParentBody ? 0 : popupOffsetParentScrollTop) - borderBottomWidth + offset[1]);
    position.top = top;
  }

  return position;
};
/**
 * Calculates relative coordinates for placing popup along with the target.
 * Uses placement from calculatePlacement.
 */


function calculatePosition(_ref5) {
  var placement = _ref5.placement,
      target = _ref5.target,
      popup = _ref5.popup,
      offset = _ref5.offset,
      stick = _ref5.stick,
      _ref5$allowOutOfBound = _ref5.allowOutOfBounds,
      allowOutOfBounds = _ref5$allowOutOfBound === void 0 ? false : _ref5$allowOutOfBound,
      rect = _ref5.rect;
  var position = {};

  if (!target || !popup || !popup.offsetParent) {
    return position;
  }

  if (isTextNode(target)) {
    target = target.parentElement;
  }

  var popupOffsetParent = popup.offsetParent;
  var offsetParentStyle = popupOffsetParent.style;
  var borderBottomWidth = 0;

  if (offsetParentStyle && offsetParentStyle.borderBottomWidth) {
    borderBottomWidth = parseInt(offsetParentStyle.borderBottomWidth, 10);
  }

  var _placement = _slicedToArray(placement, 2),
      verticalPlacement = _placement[0],
      horizontalPlacement = _placement[1];

  var _ref6 = rect ? rect : popupOffsetParent.getBoundingClientRect(),
      popupOffsetParentTop = _ref6.top,
      popupOffsetParentLeft = _ref6.left,
      popupOffsetParentRight = _ref6.right,
      popupOffsetParentHeight = _ref6.height;

  var _target$getBoundingCl3 = target.getBoundingClientRect(),
      targetTop = _target$getBoundingCl3.top,
      targetLeft = _target$getBoundingCl3.left,
      targetRight = _target$getBoundingCl3.right,
      targetHeight = _target$getBoundingCl3.height,
      targetWidth = _target$getBoundingCl3.width;

  var isPopupParentBody = isBody(popupOffsetParent);
  var verticalPosition = calculateVerticalPlacement({
    placement: verticalPlacement,
    targetTop: targetTop,
    isPopupParentBody: isPopupParentBody,
    popupOffsetParentHeight: popupOffsetParentHeight,
    popupOffsetParentTop: popupOffsetParentTop,
    popupOffsetParentScrollTop: popupOffsetParent.scrollTop || 0,
    targetHeight: targetHeight,
    borderBottomWidth: borderBottomWidth,
    offset: offset
  });
  position = _objectSpread(_objectSpread({}, position), verticalPosition);

  if ((verticalPlacement === 'top' || verticalPlacement === 'start') && stick) {
    position = calculateVerticalStickTop({
      target: target,
      targetTop: targetTop,
      targetHeight: targetHeight,
      popupOffsetParentHeight: popupOffsetParentHeight,
      popupOffsetParent: popupOffsetParent,
      popup: popup,
      offset: offset,
      position: position,
      placement: verticalPlacement
    });
  }

  if (verticalPlacement !== 'top' && verticalPlacement !== 'start' && stick) {
    position = calculateVerticalStickBottom({
      target: target,
      targetTop: targetTop,
      targetHeight: targetHeight,
      popup: popup,
      offset: offset,
      position: position
    });
  }

  var horizontalPosition = calculateHorizontalPlacement({
    placement: horizontalPlacement,
    targetLeft: targetLeft,
    targetRight: targetRight,
    targetWidth: targetWidth,
    isPopupParentBody: isPopupParentBody,
    popupOffsetParentLeft: popupOffsetParentLeft,
    popupOffsetParentRight: popupOffsetParentRight,
    popupOffsetParentScrollLeft: popupOffsetParent.scrollLeft || 0,
    popupOffsetParentClientWidth: popup.offsetParent.clientWidth,
    popupClientWidth: popup.clientWidth || 0,
    offset: offset,
    allowOutOfBounds: allowOutOfBounds
  });
  position = _objectSpread(_objectSpread({}, position), horizontalPosition);
  return position;
}

function validatePosition(popup) {
  // popup.offsetParent does not exist if the popup element is not mounted
  if (!popup || !popup.offsetParent) {
    return false;
  }

  return true;
}
/**
 * Traverse DOM Tree upwards looking for popup parents with "overflow: scroll".
 */


function findOverflowScrollParent(popup) {
  var parent = popup;

  if (!parent) {
    return false;
  }

  while (parent = parent.parentElement) {
    // IE11 on Window 8 doesn't show styles from CSS when accessing through element.style property.
    var style = window.getComputedStyle(parent);

    if (style.overflow === 'scroll' || style.overflowX === 'scroll' || style.overflowY === 'scroll' || parent.classList.contains('fabric-editor-popup-scroll-parent')) {
      return parent;
    }
  }

  return false;
}