"use strict";

var _typeof = require("@babel/runtime-corejs3/helpers/typeof");

var _WeakMap = require("@babel/runtime-corejs3/core-js-stable/weak-map");

var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor");

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

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

var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));

var _getNodeName = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/getNodeName"));

var _getOffsetParent = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/getOffsetParent"));

var _getParentNode = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/getParentNode"));

var _getWindow = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/getWindow"));

var _getWindowScroll = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/getWindowScroll"));

var _instanceOf = require("@popperjs/core/lib/dom-utils/instanceOf");

var _isScrollParent = _interopRequireDefault(require("@popperjs/core/lib/dom-utils/isScrollParent"));

var _resizeObserverPolyfill = _interopRequireDefault(require("resize-observer-polyfill"));

var _rxjs = require("rxjs");

var op = _interopRequireWildcard(require("rxjs/operators"));

var _utils = require("../utils");

function _getRequireWildcardCache(nodeInterop) { if (typeof _WeakMap !== "function") return null; var cacheBabelInterop = new _WeakMap(); var cacheNodeInterop = new _WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && _Object$getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? _Object$getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function isWindow(arg) {
  return arg.toString() === '[object Window]' || arg.toString() === '[object global]';
}

function isBody(arg) {
  return (0, _getNodeName.default)(arg) === 'body';
}

function isHtml(arg) {
  return (0, _getNodeName.default)(arg) === 'html';
}

function isHtmlOrBody(arg) {
  return isHtml(arg) || isBody(arg);
} // 计算从 start（子元素）到 stop（祖先元素）之间所有元素的 scrollTop 或 scrollLeft 的和
// 注意 start 和 stop 都是 INCLUSIVE 的，即两者的 scrollTop 或 scrollLeft 都会统计在内


function accumulateScrollOffset(start, stop, scrollOffsetKey) {
  var result = 0;
  var elem = start;

  while (elem != null) {
    result += elem[scrollOffsetKey];

    if (elem === stop || isWindow(stop) && isHtmlOrBody(elem)) {
      break;
    }

    elem = elem.parentElement;
  }

  if (isWindow(stop)) {
    result += (0, _getWindowScroll.default)(elem)[scrollOffsetKey];
  }

  return result;
}
/**
 * 获取 target 相对于 base 的布局大小和相对位置。
 * 注意该方法会考虑滚动所带来的影响
 */


function getRelativeLayoutRect(base, target) {
  if (isWindow(target) || isHtmlOrBody(target)) {
    return {
      left: 0,
      right: window.innerWidth,
      top: 0,
      bottom: window.innerHeight
    };
  }

  var deltaX = 0;
  var deltaY = 0;
  var elem = target;

  while (elem != null && elem != base) {
    deltaY += elem.offsetTop;
    deltaX += elem.offsetLeft;
    var offsetParent = (0, _getOffsetParent.default)(elem);
    deltaY -= accumulateScrollOffset(elem.parentElement, offsetParent, 'scrollTop');
    deltaX -= accumulateScrollOffset(elem.parentElement, offsetParent, 'scrollLeft');

    if (isWindow(offsetParent)) {
      break;
    }

    deltaY += offsetParent.clientTop;
    deltaX += offsetParent.clientLeft;
    elem = offsetParent;
  }

  return {
    top: deltaY,
    bottom: deltaY + target.offsetHeight,
    left: deltaX,
    right: deltaX + target.offsetWidth
  };
}

function findCommonOffsetAncestor(target, scrollParent) {
  if (isWindow(scrollParent)) {
    return scrollParent;
  }

  var offsetParents = listOffsetParents(target);

  if ((0, _includes.default)(offsetParents).call(offsetParents, scrollParent)) {
    return scrollParent;
  }

  return (0, _getOffsetParent.default)(scrollParent);
} // 列出 target 元素上层的所有 offset parents


function listOffsetParents(target) {
  var result = [];
  var elem = target;

  while (true) {
    if (isWindow(elem)) {
      break;
    }

    elem = (0, _getOffsetParent.default)(elem);
    result.push(elem);
  }

  return result;
}

function fromScrollEvent(element) {
  return (0, _rxjs.fromEvent)(element, 'scroll', {
    passive: true
  });
}

function fromResizeEvent(element) {
  if (isWindow(element)) {
    return (0, _rxjs.fromEvent)(element, 'resize', {
      passive: true
    });
  }

  return new _rxjs.Observable(function (subscriber) {
    var resizeObserver = new _resizeObserverPolyfill.default(function (entries) {
      subscriber.next(entries);
    });
    resizeObserver.observe(element);
    return function () {
      resizeObserver.disconnect();
    };
  });
}

function getScrollParent(elem) {
  var _context;

  if ((0, _includes.default)(_context = ['html', 'body', '#document']).call(_context, (0, _getNodeName.default)(elem))) {
    return (0, _getWindow.default)(elem);
  }

  if ((0, _instanceOf.isHTMLElement)(elem) && (0, _isScrollParent.default)(elem)) {
    return elem;
  }

  return getScrollParent((0, _getParentNode.default)(elem));
} // 获取 target 相对于「它的滚动父元素」的可见部分的大小与位置


function getRichVisibleRectsStream(target, structureMayChange$, virtualDebugLabel) {
  return structureMayChange$.pipe(op.startWith('init'), op.map(function () {
    // target 的第一个滚动父元素，我们认为这就是虚拟滚动发生的地方
    // 即虚拟滚动不考虑「更上层元素发生滚动」的情况
    // 直接从父元素开始查找，防止自身设置overflow属性导致是同一个元素
    var scrollParent = getScrollParent((0, _getParentNode.default)(target)); // target 和 scrollParent 的共同 offset 祖先，作为布局尺寸计算时的参照元素

    var commonOffsetAncestor = findCommonOffsetAncestor(target, scrollParent);
    return {
      scrollParent: scrollParent,
      commonOffsetAncestor: commonOffsetAncestor
    };
  }), op.distinctUntilChanged(_utils.shallowEqual), op.tap(function (structure) {
    if (virtualDebugLabel) {
      console.log("%c[ali-react-table STRUCTURE ".concat(virtualDebugLabel, "]"), 'color: #4f9052; font-weight: bold', '\ntarget:', target, '\nscrollParent:', structure.scrollParent, '\ncommonOffsetAncestor:', structure.commonOffsetAncestor);
    }
  }), op.switchMap(function (_ref) {
    var scrollParent = _ref.scrollParent,
        commonOffsetAncestor = _ref.commonOffsetAncestor;
    var events$ = (0, _rxjs.merge)(fromScrollEvent(scrollParent), fromResizeEvent(scrollParent), fromResizeEvent(target));
    return events$.pipe(op.map(function (event) {
      return {
        targetRect: getRelativeLayoutRect(commonOffsetAncestor, target),
        scrollParentRect: getRelativeLayoutRect(commonOffsetAncestor, scrollParent),
        event: event
      };
    }), op.map(function (_ref2) {
      var event = _ref2.event,
          scrollParentRect = _ref2.scrollParentRect,
          targetRect = _ref2.targetRect;
      return {
        event: event,
        targetRect: targetRect,
        scrollParentRect: scrollParentRect,
        offsetY: Math.max(0, scrollParentRect.top - targetRect.top),
        // 表格的横向滚动总是发生在表格内部，所以这里不需要计算 offsetX
        // offsetX: Math.max(0, scrollParentRect.left - targetRect.left),
        clipRect: {
          left: Math.max(targetRect.left, scrollParentRect.left),
          top: Math.max(targetRect.top, scrollParentRect.top),
          right: Math.min(targetRect.right, scrollParentRect.right),
          bottom: Math.min(targetRect.bottom, scrollParentRect.bottom)
        }
      };
    }));
  }), op.tap(function (rects) {
    if (virtualDebugLabel) {
      console.log("%c[ali-react-table RECTS ".concat(virtualDebugLabel, "]"), 'color: #4f9052; font-weight: bold', '\noffsetY:', rects.offsetY, '\ntargetRect:', rects.targetRect, '\nscrollParentRect:', rects.scrollParentRect, '\nclipRect:', rects.clipRect, '\nevent:', rects.event);
    }
  }));
}