"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isTextNode = exports.isStartPoint = exports.isSelectionAtBlockStart = exports.isSelectionAtBlockEnd = exports.isRangeAcrossBlocks = exports.isLastNode = exports.isLastChild = exports.isFirstNode = exports.isFirstChild = exports.isEndPoint = exports.isBlockTextEmptyAfterSelection = exports.isBlockAboveEmpty = exports.isAncestorEmpty = exports.getSelectedNodeEntryByTypes = exports.getSelectedNodeEntryByType = exports.getSelectedNodeByTypes = exports.getSelectedNodeByType = exports.getSelectedElems = exports.getQueryOptions = exports.getPreviousPath = exports.getPrevNode = exports.getParentNode = exports.getNodesByType = exports.getNodes = exports.getNodeType = exports.getNodePathById = exports.getNodeEntries = exports.getNode = exports.getNextSiblingNodes = exports.getNextNode = exports.getLastChildPath = exports.getLastChild = exports.getInlineNodes = exports.getEditorString = exports.getDeepInlineChildren = exports.getCommonNode = exports.getChildren = exports.getAboveNode = exports.getAboveBlockNode = exports.findPath = exports.findNode = exports.findDescendant = void 0;
var _slate = require("slate");
var _slateReact = require("slate-react");
var _slugid = _interopRequireDefault(require("slugid"));
var _utils = require("../utils");
var _elementTypes = require("../../constants/element-types");
// options
const getQueryOptions = (editor, options) => {
  const {
    match: _match,
    block
  } = options;
  let newMatch = undefined;
  if (_match || block) {
    newMatch = (n, path) => (0, _utils.match)(n, path, _match) && (!block || _slate.Editor.isBlock(editor, n));
  }
  return {
    ...options,
    match: newMatch
  };
};
exports.getQueryOptions = getQueryOptions;
const findPath = (editor, node, defaultPath) => {
  try {
    return _slateReact.ReactEditor.findPath(editor, node);
  } catch {
    return defaultPath;
  }
};

// get node
exports.findPath = findPath;
const getNode = (editor, path) => {
  let node = null;
  try {
    node = _slate.Node.get(editor, path);
  } catch (err) {
    node = null;
  }
  return node;
};
exports.getNode = getNode;
const getNodesByType = function () {
  let target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  let type = arguments.length > 1 ? arguments[1] : undefined;
  let nodes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
  Array.isArray(target) && target.forEach(n => {
    if (n.type === type) {
      nodes.push(n);
    } else {
      if (Array.isArray(n.children) && n.children.length > 0) {
        getNodesByType(n, type, nodes);
      }
    }
  });
  return nodes;
};
exports.getNodesByType = getNodesByType;
const getNodeType = node => {
  return _slate.Element.isElement(node) ? node.type : '';
};
exports.getNodeType = getNodeType;
const getParentNode = (nodes, nodeId) => {
  let parentNode;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    const {
      children: childrenNodes
    } = node;
    if (!Array.isArray(childrenNodes)) continue;
    if (childrenNodes.find(node => node.id === nodeId)) {
      parentNode = node;
    } else {
      parentNode = getParentNode(childrenNodes, nodeId);
    }
    if (!parentNode) continue;
    break;
  }
  return parentNode;
};
exports.getParentNode = getParentNode;
const getNodes = (node, options) => {
  return _slate.Node.nodes(node, options);
};
exports.getNodes = getNodes;
const getCommonNode = (root, path, ancestor) => {
  return _slate.Node.common(root, path, ancestor);
};
exports.getCommonNode = getCommonNode;
const getSelectedNodeByType = (editor, type) => {
  const match = n => getNodeType(n) === type;
  const [nodeEntry] = _slate.Editor.nodes(editor, {
    match,
    universal: true
  });
  return nodeEntry ? nodeEntry[0] : null;
};
exports.getSelectedNodeByType = getSelectedNodeByType;
const getSelectedNodeByTypes = (editor, types) => {
  const match = n => types.includes(getNodeType(n));
  const [nodeEntry] = _slate.Editor.nodes(editor, {
    match,
    universal: true
  });
  return nodeEntry ? nodeEntry[0] : null;
};
exports.getSelectedNodeByTypes = getSelectedNodeByTypes;
const getSelectedNodeEntryByType = (editor, type) => {
  const match = n => getNodeType(n) === type;
  const [nodeEntry] = _slate.Editor.nodes(editor, {
    match,
    universal: false
  });
  return nodeEntry ? nodeEntry : null;
};
exports.getSelectedNodeEntryByType = getSelectedNodeEntryByType;
const getSelectedNodeEntryByTypes = (editor, types) => {
  const match = n => types.includes(getNodeType(n));
  const [nodeEntry] = _slate.Editor.nodes(editor, {
    match,
    universal: false
  });
  return nodeEntry ? nodeEntry : null;
};
exports.getSelectedNodeEntryByTypes = getSelectedNodeEntryByTypes;
const getNodeEntries = (editor, options) => {
  return _slate.Editor.nodes(editor, getQueryOptions(editor, options));
};
exports.getNodeEntries = getNodeEntries;
const getEditorString = (editor, at, options) => {
  if (!at) return '';
  try {
    return _slate.Editor.string(editor, at, options);
  } catch (error) {
    return '';
  }
};

// children & child
exports.getEditorString = getEditorString;
const getChildren = nodeEntry => {
  const [node, path] = nodeEntry;
  if (_slate.Element.isAncestor(node)) {
    const {
      children
    } = node;
    return children.map((child, index) => {
      const childPath = path.concat([index]);
      return [child, childPath];
    });
  }
  return [];
};
exports.getChildren = getChildren;
const getLastChild = nodeEntry => {
  const [node, path] = nodeEntry;
  if (_slate.Text.isText(node)) return null;
  if (!node.children.length) return null;
  const children = node.children;
  return [children[children.length - 1], path.concat([children.length - 1])];
};
exports.getLastChild = getLastChild;
const getDeepInlineChildren = (editor, _ref) => {
  let {
    children
  } = _ref;
  const inlineChildren = [];
  for (let child of children) {
    if (_slate.Editor.isBlock(editor, child[0])) {
      inlineChildren.push(...getDeepInlineChildren(editor, {
        children: getChildren(child)
      }));
    } else {
      inlineChildren.push(child);
    }
  }
  return inlineChildren;
};
exports.getDeepInlineChildren = getDeepInlineChildren;
const getLastChildPath = nodeEntry => {
  const lastChild = getLastChild(nodeEntry);
  if (!lastChild) return nodeEntry[1].concat([-1]);
  return lastChild[1];
};
exports.getLastChildPath = getLastChildPath;
const getPreviousPath = path => {
  if (path.length === 0) return;
  const last = path[path.length - 1];
  if (last <= 0) return;
  return path.slice(0, -1).concat(last - 1);
};
exports.getPreviousPath = getPreviousPath;
const isFirstChild = (nodeEntry, childPath) => {
  const children = getChildren(nodeEntry);
  const firstChild = children[0] || [];
  const firstChildPath = firstChild[1];
  return _slate.Path.equals(firstChildPath, childPath);
};
exports.isFirstChild = isFirstChild;
const isLastChild = (nodeEntry, childPath) => {
  const lastChildPath = getLastChildPath(nodeEntry);
  return _slate.Path.equals(lastChildPath, childPath);
};
exports.isLastChild = isLastChild;
const getSelectedElems = editor => {
  const elems = [];
  const nodeEntries = _slate.Editor.nodes(editor, {
    universal: true
  });
  for (let nodeEntry of nodeEntries) {
    const [node] = nodeEntry;
    if (_slate.Element.isElement(node)) elems.push(node);
  }
  return elems;
};

// siblings
exports.getSelectedElems = getSelectedElems;
const getNextSiblingNodes = (ancestorEntry, path) => {
  const [ancestor, ancestorPath] = ancestorEntry;
  const leafIndex = path[ancestorPath.length];
  const siblings = [];
  const ancestorChildren = ancestor.children;
  if (leafIndex + 1 < ancestor.children.length) {
    for (let i = leafIndex + 1; i < ancestor.children.length; i++) {
      siblings.push(ancestorChildren[i]);
    }
  }
  return siblings;
};

// parent
exports.getNextSiblingNodes = getNextSiblingNodes;
const getAboveNode = (editor, options) => {
  return _slate.Editor.above(editor, getQueryOptions(editor, options));
};
exports.getAboveNode = getAboveNode;
const getAboveBlockNode = (editor, options) => {
  return getAboveNode(editor, {
    ...options,
    block: true
  });
};
exports.getAboveBlockNode = getAboveBlockNode;
const getPrevNode = editor => {
  const [lowerNode, lowerPath] = getAboveNode(editor, {
    mode: 'lowest',
    match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
  });
  const [heightNode, heightPath] = getAboveNode(editor, {
    mode: 'highest',
    match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
  });
  let prevNode = null;
  try {
    prevNode = _slate.Editor.previous(editor, {
      at: lowerPath,
      match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
    });
  } catch (error) {
    prevNode = null;
  }
  if (lowerNode.id !== heightNode.id && !prevNode) {
    try {
      prevNode = _slate.Editor.previous(editor, {
        at: heightPath,
        match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
      });
    } catch (error) {
      prevNode = null;
    }
  }
  return prevNode;
};
exports.getPrevNode = getPrevNode;
const getNextNode = editor => {
  const [lowerNode, lowerPath] = getAboveNode(editor, {
    mode: 'lowest',
    match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
  });
  const [heightNode, heightPath] = getAboveNode(editor, {
    mode: 'highest',
    match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
  });
  let nextNode = null;
  try {
    nextNode = _slate.Editor.next(editor, {
      at: lowerPath,
      match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
    });
  } catch (error) {
    nextNode = null;
  }
  if (lowerNode.id !== heightNode.id && !nextNode) {
    try {
      nextNode = _slate.Editor.next(editor, {
        at: heightPath,
        match: n => _slate.Element.isElement(n) && _slate.Editor.isBlock(editor, n)
      });
    } catch (error) {
      nextNode = null;
    }
  }
  return nextNode;
};

// find node
exports.getNextNode = getNextNode;
const findNode = (editor, options) => {
  const nodeEntries = _slate.Editor.nodes(editor, {
    at: editor.selection || [],
    ...getQueryOptions(editor, options)
  });
  const _nodeEntries = Array.from(nodeEntries);
  if (_nodeEntries[0]) {
    return _nodeEntries[0];
  }
  return undefined;
};
exports.findNode = findNode;
const findDescendant = (editor, options) => {
  const {
    at = editor.selection,
    reverse = false,
    voids = false,
    match: _match
  } = options;
  if (!at) return;
  let from;
  let to;
  if (_slate.Span.isSpan(at)) {
    [from, to] = at;
  } else if (_slate.Range.isRange(at)) {
    const first = _slate.Editor.first(editor, at);
    const last = _slate.Editor.last(editor, at);
    from = reverse ? last : first;
    to = reverse ? first : last;
  }
  let root = [editor, []];
  if (_slate.Path.isPath(at)) {
    root = _slate.Editor.node(editor, at);
  }
  const nodeEntries = _slate.Node.descendants(root[0], {
    from,
    to,
    reverse,
    pass: _ref2 => {
      let [n] = _ref2;
      return voids ? false : _slate.Editor.isVoid(editor, n);
    }
  });
  for (const [node, path] of nodeEntries) {
    if ((0, _utils.match)(node, path, _match)) {
      return [node, at.concat(path)];
    }
  }
  return undefined;
};

// is
exports.findDescendant = findDescendant;
const isStartPoint = (editor, point, at) => {
  return !!point && _slate.Editor.isStart(editor, point, at);
};
exports.isStartPoint = isStartPoint;
const isEndPoint = (editor, point, at) => {
  return !!point && _slate.Editor.isEnd(editor, point, at);
};
exports.isEndPoint = isEndPoint;
const isBlockTextEmptyAfterSelection = editor => {
  if (!editor.selection) return false;
  const blockAbove = _slate.Editor.above(editor, {
    block: true
  });
  if (!blockAbove) return false;
  const cursor = editor.selection.focus;
  const selectionParentEntry = _slate.Editor.parent(editor, editor.selection);
  if (!selectionParentEntry) return false;
  const [, selectionParentPath] = selectionParentEntry;
  if (!isEndPoint(editor, cursor, selectionParentPath)) return false;
  const siblingNodes = getNextSiblingNodes(blockAbove, cursor.path);
  if (siblingNodes.length) {
    // 子节点包含 文本节点
    for (const siblingNode of siblingNodes) {
      if (_slate.Text.isText(siblingNode) && siblingNode.text) {
        return false;
      }
    }
  } else {
    return isEndPoint(editor, cursor, blockAbove[1]);
  }
  return true;
};
exports.isBlockTextEmptyAfterSelection = isBlockTextEmptyAfterSelection;
const isRangeAcrossBlocks = function (editor) {
  let {
    at,
    ...options
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  if (!at) at = editor.selection;
  if (!at) return;
  const [start, end] = _slate.Range.edges(at);
  const startBlock = getAboveBlockNode(editor, {
    at: start,
    ...options
  });
  const endBlock = getAboveBlockNode(editor, {
    at: end,
    ...options
  });
  if (!startBlock && !endBlock) return;
  if (!startBlock || !endBlock) return true;
  return !_slate.Path.equals(startBlock[1], endBlock[1]);
};
exports.isRangeAcrossBlocks = isRangeAcrossBlocks;
const isAncestorEmpty = (editor, node) => {
  return !_slate.Node.string(node) && !node.children.some(n => _slate.Editor.isInline(editor, n));
};
exports.isAncestorEmpty = isAncestorEmpty;
const isBlockAboveEmpty = editor => {
  var _getAboveBlockNode;
  const block = (_getAboveBlockNode = getAboveBlockNode(editor)) === null || _getAboveBlockNode === void 0 ? void 0 : _getAboveBlockNode[0];
  if (!block) return false;
  return isAncestorEmpty(editor, block);
};
exports.isBlockAboveEmpty = isBlockAboveEmpty;
const isSelectionAtBlockStart = (editor, options) => {
  var _getAboveBlockNode2;
  const {
    selection
  } = editor;
  if (!selection) return false;
  const path = (_getAboveBlockNode2 = getAboveBlockNode(editor, options)) === null || _getAboveBlockNode2 === void 0 ? void 0 : _getAboveBlockNode2[1];
  if (!path) return false;
  return isStartPoint(editor, selection.focus, path) || _slate.Range.isExpanded(editor.selection) && isStartPoint(editor, selection.anchor, path);
};
exports.isSelectionAtBlockStart = isSelectionAtBlockStart;
const isSelectionAtBlockEnd = (editor, options) => {
  var _getAboveBlockNode3, _editor$selection;
  const path = (_getAboveBlockNode3 = getAboveBlockNode(editor, options)) === null || _getAboveBlockNode3 === void 0 ? void 0 : _getAboveBlockNode3[1];
  return !!path && isEndPoint(editor, (_editor$selection = editor.selection) === null || _editor$selection === void 0 ? void 0 : _editor$selection.focus, path);
};
exports.isSelectionAtBlockEnd = isSelectionAtBlockEnd;
const isFirstNode = (editor, node) => {
  const editorChildren = editor.children || [];
  return editorChildren[0] === node;
};
exports.isFirstNode = isFirstNode;
const isLastNode = (editor, node) => {
  const editorChildren = editor.children || [];
  const editorChildrenLength = editorChildren.length;
  return editorChildren[editorChildrenLength - 1] === node;
};
exports.isLastNode = isLastNode;
const isTextNode = node => {
  if (!node) return false;
  if (Reflect.has(node, 'children')) return false;
};
exports.isTextNode = isTextNode;
const getInlineNodes = node => {
  if (!node.type) return [node];
  // image
  if (node.type === _elementTypes.IMAGE) return [node];
  // link
  if (node.type === _elementTypes.LINK) return [node];
  // column
  if (node.type === _elementTypes.COLUMN) return [node];
  const {
    children
  } = node;
  const defaultNode = {
    id: _slugid.default.nice(),
    text: ''
  };
  if (!children) return [defaultNode];
  const result = children.map(item => {
    // text
    if (!item.type) return item;
    // image
    if (item.type === _elementTypes.IMAGE) return item;
    // link
    if (item.type === _elementTypes.LINK) return item;
    // column
    if (item.type === _elementTypes.COLUMN) return item;
    return getInlineNodes(node);
  });
  return result.flat();
};
exports.getInlineNodes = getInlineNodes;
const getNodePathById = function (rootNode, nodeId) {
  let path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
  if (rootNode.id === nodeId) return path;
  const {
    children = []
  } = rootNode;
  for (let i = 0; i < children.length; i++) {
    const child = children[i];
    path.push(i);
    const nodePath = getNodePathById(child, nodeId, path);
    if (nodePath) return nodePath;
    path.pop();
  }
  return null;
};
exports.getNodePathById = getNodePathById;