'use strict';

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

var _block = require('./block');

var _block2 = _interopRequireDefault(_block);

var _character = require('./character');

var _character2 = _interopRequireDefault(_character);

var _data = require('./data');

var _data2 = _interopRequireDefault(_data);

var _document = require('./document');

var _document2 = _interopRequireDefault(_document);

var _inline = require('./inline');

var _inline2 = _interopRequireDefault(_inline);

var _mark = require('./mark');

var _mark2 = _interopRequireDefault(_mark);

var _selection = require('./selection');

var _selection2 = _interopRequireDefault(_selection);

var _transforms = require('./transforms');

var _transforms2 = _interopRequireDefault(_transforms);

var _text3 = require('./text');

var _text4 = _interopRequireDefault(_text3);

var _uid = require('../utils/uid');

var _uid2 = _interopRequireDefault(_uid);

var _immutable = require('immutable');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Node.
 *
 * And interface that `Document`, `Block` and `Inline` all implement, to make
 * working with the recursive node tree easier.
 */

var Node = {

  /**
   * Assert that the node has a child by `key`.
   *
   * @param {String or Node} key
   */

  assertHasChild: function assertHasChild(key) {
    key = normalizeKey(key);
    if (!this.hasChild(key)) {
      throw new Error('Could not find a child node with key "' + key + '".');
    }
  },


  /**
   * Assert that the node has a descendant by `key`.
   *
   * @param {String or Node} key
   */

  assertHasDescendant: function assertHasDescendant(key) {
    key = normalizeKey(key);
    if (!this.hasDescendant(key)) {
      throw new Error('Could not find a descendant node with key "' + key + '".');
    }
  },


  /**
   * Concat children `nodes` on to the end of the node.
   *
   * @param {List} nodes
   * @return {Node} node
   */

  concatChildren: function concatChildren(nodes) {
    nodes = this.nodes.concat(nodes);
    return this.merge({ nodes: nodes });
  },


  /**
   * Decorate all of the text nodes with a `decorator` function.
   *
   * @param {Function} decorator
   * @return {Node} node
   */

  decorateTexts: function decorateTexts(decorator) {
    return this.mapDescendants(function (child) {
      return child.kind == 'text' ? child.decorateCharacters(decorator) : child;
    });
  },


  /**
   * Recursively find all ancestor nodes by `iterator`.
   *
   * @param {Function} iterator
   * @return {Node} node
   */

  findDescendant: function findDescendant(iterator) {
    return this.nodes.find(iterator) || this.nodes.map(function (node) {
      return node.kind == 'text' ? null : node.findDescendant(iterator);
    }).find(function (exists) {
      return exists;
    });
  },


  /**
   * Recursively filter all ancestor nodes with `iterator`.
   *
   * @param {Function} iterator
   * @return {List} nodes
   */

  filterDescendants: function filterDescendants(iterator) {
    return this.nodes.reduce(function (matches, child, i, nodes) {
      if (iterator(child, i, nodes)) matches = matches.push(child);
      if (child.kind != 'text') matches = matches.concat(child.filterDescendants(iterator));
      return matches;
    }, _block2.default.createList());
  },


  /**
   * Get the closest block nodes for each text node in the node.
   *
   * @return {List} nodes
   */

  getBlocks: function getBlocks() {
    var _this = this;

    return this.getTextNodes().reduce(function (blocks, text) {
      var block = _this.getClosestBlock(text);
      return blocks.includes(block) ? blocks : blocks.push(block);
    }, _block2.default.createList());
  },


  /**
   * Get the closest block nodes for each text node in a `range`.
   *
   * @param {Selection} range
   * @return {List} nodes
   */

  getBlocksAtRange: function getBlocksAtRange(range) {
    var _this2 = this;

    range = range.normalize(this);
    return this.getTextsAtRange(range).map(function (text) {
      return _this2.getClosestBlock(text);
    });
  },


  /**
   * Get a list of the characters in a `range`.
   *
   * @param {Selection} range
   * @return {List} characters
   */

  getCharactersAtRange: function getCharactersAtRange(range) {
    range = range.normalize(this);
    return this.getTextsAtRange(range).reduce(function (characters, text) {
      var chars = text.characters.filter(function (char, i) {
        return isInRange(i, text, range);
      });
      return characters.concat(chars);
    }, _character2.default.createList());
  },


  /**
   * Get children after a child by `key`.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  getChildrenAfter: function getChildrenAfter(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(index + 1);
    return nodes;
  },


  /**
   * Get children after a child by `key`, including the child.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  getChildrenAfterIncluding: function getChildrenAfterIncluding(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(index);
    return nodes;
  },


  /**
   * Get children before a child by `key`.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  getChildrenBefore: function getChildrenBefore(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(0, index);
    return nodes;
  },


  /**
   * Get children before a child by `key`, including the child.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  getChildrenBeforeIncluding: function getChildrenBeforeIncluding(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(0, index + 1);
    return nodes;
  },


  /**
   * Get children between two child keys.
   *
   * @param {String or Node} start
   * @param {String or Node} end
   * @return {Node} node
   */

  getChildrenBetween: function getChildrenBetween(start, end) {
    start = this.getChild(start);
    start = this.nodes.indexOf(start);
    end = this.getChild(end);
    end = this.nodes.indexOf(end);
    return this.nodes.slice(start + 1, end);
  },


  /**
   * Get children between two child keys, including the two children.
   *
   * @param {String or Node} start
   * @param {String or Node} end
   * @return {Node} node
   */

  getChildrenBetweenIncluding: function getChildrenBetweenIncluding(start, end) {
    start = this.getChild(start);
    start = this.nodes.indexOf(start);
    end = this.getChild(end);
    end = this.nodes.indexOf(end);
    return this.nodes.slice(start, end + 1);
  },


  /**
   * Get closest parent of node by `key` that matches `iterator`.
   *
   * @param {String or Node} key
   * @param {Function} iterator
   * @return {Node or Null} node
   */

  getClosest: function getClosest(key, iterator) {
    var node = this.getDescendant(key);

    while (node = this.getParent(node)) {
      if (node == this) return null;
      if (iterator(node)) return node;
    }

    return null;
  },


  /**
   * Get the closest block parent of a `node`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getClosestBlock: function getClosestBlock(key) {
    return this.getClosest(key, function (parent) {
      return parent.kind == 'block';
    });
  },


  /**
   * Get the closest inline parent of a `node`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getClosestInline: function getClosestInline(key) {
    return this.getClosest(key, function (parent) {
      return parent.kind == 'inline';
    });
  },


  /**
   * Get a child node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getChild: function getChild(key) {
    key = normalizeKey(key);
    return this.nodes.find(function (node) {
      return node.key == key;
    });
  },


  /**
   * Get all of the blocks closest to text nodes.
   *
   * @return {List} nodes
   */

  getDeepestBlocks: function getDeepestBlocks() {
    var _this3 = this;

    var texts = this.getTextNodes();
    var set = texts.reduce(function (memo, text) {
      return memo.add(_this3.getClosestBlock(text));
    }, new _immutable.OrderedSet());

    return set.toList();
  },


  /**
   * Get a descendant node by `key`.
   *
   * @param {String} key
   * @return {Node or Null} node
   */

  getDescendant: function getDescendant(key) {
    key = normalizeKey(key);
    return this.findDescendant(function (node) {
      return node.key == key;
    });
  },


  /**
   * Get the depth of a child node by `key`, with optional `startAt`.
   *
   * @param {String or Node} key
   * @param {Number} startAt (optional)
   * @return {Number} depth
   */

  getDepth: function getDepth(key) {
    var startAt = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1];

    key = normalizeKey(key);
    this.assertHasDescendant(key);
    if (this.hasChild(key)) return startAt;
    return this.getHighestChild(key).getDepth(key, startAt + 1);
  },


  /**
   * Get a fragment of the node at a `range`.
   *
   * @param {Selection} range
   * @return {List} nodes
   */

  getFragmentAtRange: function getFragmentAtRange(range) {
    var node = this;
    var nodes = _block2.default.createList();

    // If the range is collapsed, there's nothing to do.
    if (range.isCollapsed) return _document2.default.create({ nodes: nodes });

    // Make sure the children exist.
    var startKey = range.startKey;
    var startOffset = range.startOffset;
    var endKey = range.endKey;
    var endOffset = range.endOffset;

    node.assertHasDescendant(startKey);
    node.assertHasDescendant(endKey);

    // Split at the start and end.
    var start = range.collapseToStart();
    node = node.splitBlockAtRange(start, Infinity);

    var next = node.getNextText(startKey);
    var end = startKey == endKey ? range.collapseToStartOf(next).moveForward(endOffset - startOffset) : range.collapseToEnd();
    node = node.splitBlockAtRange(end, Infinity);

    // Get the start and end nodes.
    var startNode = node.getNextSibling(node.getHighestChild(startKey));
    var endNode = startKey == endKey ? node.getHighestChild(next) : node.getHighestChild(endKey);

    nodes = node.getChildrenBetweenIncluding(startNode, endNode);

    // Return a new document fragment.
    return _document2.default.create({ nodes: nodes });
  },


  /**
   * Get the furthest block parent of a node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getFurthestBlock: function getFurthestBlock(key) {
    var node = this.getDescendant(key);
    var furthest = null;

    while (node = this.getClosestBlock(node)) {
      furthest = node;
    }

    return furthest;
  },


  /**
   * Get the furthest inline parent of a node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getFurthestInline: function getFurthestInline(key) {
    var node = this.getDescendant(key);
    var furthest = null;

    while (node = this.getClosestInline(node)) {
      furthest = node;
    }

    return furthest;
  },


  /**
   * Get the highest child ancestor of a node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getHighestChild: function getHighestChild(key) {
    key = normalizeKey(key);
    return this.nodes.find(function (node) {
      if (node.key == key) return true;
      if (node.kind == 'text') return false;
      return node.hasDescendant(key);
    });
  },


  /**
   * Get the highest parent of a node by `key` which has an only child.
   *
   * @param {String or Node} key
   * @return {Node or Null}
   */

  getHighestOnlyChildParent: function getHighestOnlyChildParent(key) {
    var child = this.getChild(key);
    var match = null;
    var parent = void 0;

    while (parent = this.getParent(child)) {
      if (parent == null || parent.nodes.size > 1) return match;
      match = parent;
      child = parent;
    }
  },


  /**
   * Get the closest inline nodes for each text node in a `range`.
   *
   * @param {Selection} range
   * @return {List} nodes
   */

  getInlinesAtRange: function getInlinesAtRange(range) {
    var _this4 = this;

    range = range.normalize(this);
    if (range.isUnset) return _inline2.default.createList();

    return this.getTextsAtRange(range).map(function (text) {
      return _this4.getClosestInline(text);
    }).filter(function (exists) {
      return exists;
    });
  },


  /**
   * Get a set of the marks in a `range`.
   *
   * @param {Selection} range
   * @return {Set} marks
   */

  getMarksAtRange: function getMarksAtRange(range) {
    range = range.normalize(this);
    var _range = range;
    var startKey = _range.startKey;
    var startOffset = _range.startOffset;

    var marks = _mark2.default.createSet();

    // If the range isn't set, return an empty set.
    if (range.isUnset) return marks;

    // If the range is collapsed at the start of the node, check the previous.
    if (range.isCollapsed && startOffset == 0) {
      var text = this.getDescendant(startKey);
      var previous = this.getPreviousText(startKey);
      if (!previous || !previous.length) return marks;
      var char = previous.characters.get(previous.length - 1);
      return char.marks;
    }

    // If the range is collapsed, check the character before the start.
    if (range.isCollapsed) {
      var _text = this.getDescendant(startKey);
      var _char = _text.characters.get(range.startOffset - 1);
      return _char.marks;
    }

    // Otherwise, get a set of the marks for each character in the range.
    return this.getCharactersAtRange(range).reduce(function (memo, char) {
      return memo.union(char.marks);
    }, marks);
  },


  /**
   * Get the block node before a descendant text node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getNextBlock: function getNextBlock(key) {
    key = normalizeKey(key);
    var child = this.getDescendant(key);
    var last = void 0;

    if (child.kind == 'block') {
      last = child.getTextNodes().last();
    } else {
      var block = this.getClosestBlock(key);
      last = block.getTextNodes().last();
    }

    var next = this.getNextText(last);
    if (!next) return null;

    return this.getClosestBlock(next);
  },


  /**
   * Get the node after a descendant by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getNextSibling: function getNextSibling(key) {
    var node = this.getDescendant(key);
    if (!node) return null;
    return this.getParent(node).nodes.skipUntil(function (child) {
      return child == node;
    }).get(1);
  },


  /**
   * Get the text node after a descendant text node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getNextText: function getNextText(key) {
    key = normalizeKey(key);
    return this.getTextNodes().skipUntil(function (text) {
      return text.key == key;
    }).get(1);
  },


  /**
   * Get the offset for a descendant text node by `key`.
   *
   * @param {String or Node} key
   * @return {Number} offset
   */

  getOffset: function getOffset(key) {
    key = normalizeKey(key);
    this.assertHasDescendant(key);

    // Calculate the offset of the nodes before the highest child.
    var child = this.getHighestChild(key);
    var offset = this.nodes.takeUntil(function (n) {
      return n == child;
    }).reduce(function (memo, n) {
      return memo + n.length;
    }, 0);

    // Recurse if need be.
    return this.hasChild(key) ? offset : offset + child.getOffset(key);
  },


  /**
   * Get the offset from a `range`.
   *
   * @param {Selection} range
   * @return {Number} offset
   */

  getOffsetAtRange: function getOffsetAtRange(range) {
    if (range.isExpanded) {
      throw new Error('The range must be collapsed to calculcate its offset.');
    }

    var startKey = range.startKey;
    var startOffset = range.startOffset;

    var startNode = this.getDescendant(startKey);

    if (!startNode) {
      throw new Error('Could not find a node by startKey "' + startKey + '".');
    }

    return this.getOffset(startKey) + startOffset;
  },


  /**
   * Get the parent of a child node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getParent: function getParent(key) {
    key = normalizeKey(key);
    if (this.hasChild(key)) return this;

    var node = null;

    this.nodes.forEach(function (child) {
      if (child.kind == 'text') return;
      var match = child.getParent(key);
      if (match) node = match;
    });

    return node;
  },


  /**
   * Get the node before a descendant node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getPreviousSibling: function getPreviousSibling(key) {
    var node = this.getDescendant(key);
    if (!node) return null;
    return this.getParent(node).nodes.takeUntil(function (child) {
      return child == node;
    }).last();
  },


  /**
   * Get the text node before a descendant text node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getPreviousText: function getPreviousText(key) {
    key = normalizeKey(key);
    return this.getTextNodes().takeUntil(function (text) {
      return text.key == key;
    }).last();
  },


  /**
   * Get the block node before a descendant text node by `key`.
   *
   * @param {String or Node} key
   * @return {Node or Null} node
   */

  getPreviousBlock: function getPreviousBlock(key) {
    key = normalizeKey(key);
    var child = this.getDescendant(key);
    var first = void 0;

    if (child.kind == 'block') {
      first = child.getTextNodes().first();
    } else {
      var block = this.getClosestBlock(key);
      first = block.getTextNodes().first();
    }

    var previous = this.getPreviousText(first);
    if (!previous) return null;

    return this.getClosestBlock(previous);
  },


  /**
   * Get the descendent text node at an `offset`.
   *
   * @param {String} offset
   * @return {Node or Null} node
   */

  getTextAtOffset: function getTextAtOffset(offset) {
    var length = 0;
    return this.getTextNodes().find(function (text) {
      length += text.length;
      return length >= offset;
    });
  },


  /**
   * Recursively get all of the child text nodes in order of appearance.
   *
   * @return {List} nodes
   */

  getTextNodes: function getTextNodes() {
    return this.nodes.reduce(function (texts, node) {
      return node.kind == 'text' ? texts.push(node) : texts.concat(node.getTextNodes());
    }, _block2.default.createList());
  },


  /**
   * Get all of the text nodes in a `range`.
   *
   * @param {Selection} range
   * @return {List} nodes
   */

  getTextsAtRange: function getTextsAtRange(range) {
    range = range.normalize(this);

    // If the selection is unset, return an empty list.
    if (range.isUnset) return _block2.default.createList();

    var _range2 = range;
    var startKey = _range2.startKey;
    var endKey = _range2.endKey;

    var texts = this.getTextNodes();
    var startText = this.getDescendant(startKey);
    var endText = this.getDescendant(endKey);
    var start = texts.indexOf(startText);
    var end = texts.indexOf(endText);
    return texts.slice(start, end + 1);
  },


  /**
   * Check if a child node exists by `key`.
   *
   * @param {String or Node} key
   * @return {Boolean} exists
   */

  hasChild: function hasChild(key) {
    key = normalizeKey(key);
    return !!this.nodes.find(function (node) {
      return node.key == key;
    });
  },


  /**
   * Recursively check if a child node exists by `key`.
   *
   * @param {String or Node} key
   * @return {Boolean} exists
   */

  hasDescendant: function hasDescendant(key) {
    key = normalizeKey(key);
    return !!this.nodes.find(function (node) {
      return node.kind == 'text' ? node.key == key : node.key == key || node.hasDescendant(key);
    });
  },


  /**
   * Insert child `nodes` after child by `key`.
   *
   * @param {String or Node} key
   * @param {List} nodes
   * @return {Node} node
   */

  insertChildrenAfter: function insertChildrenAfter(key, nodes) {
    key = normalizeKey(key);
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);

    nodes = this.nodes.slice(0, index + 1).concat(nodes).concat(this.nodes.slice(index + 1));

    return this.merge({ nodes: nodes });
  },


  /**
   * Insert child `nodes` before child by `key`.
   *
   * @param {String or Node} key
   * @param {List} nodes
   * @return {Node} node
   */

  insertChildrenBefore: function insertChildrenBefore(key, nodes) {
    key = normalizeKey(key);
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);

    nodes = this.nodes.slice(0, index).concat(nodes).concat(this.nodes.slice(index));

    return this.merge({ nodes: nodes });
  },


  /**
   * Check if the inline nodes are split at a `range`.
   *
   * @param {Selection} range
   * @return {Boolean} isSplit
   */

  isInlineSplitAtRange: function isInlineSplitAtRange(range) {
    range = range.normalize(this);
    if (range.isExpanded) throw new Error();

    var _range3 = range;
    var startKey = _range3.startKey;

    var start = this.getFurthestInline(startKey) || this.getDescendant(startKey);
    return range.isAtStartOf(start) || range.isAtEndOf(start);
  },


  /**
   * Map all children nodes, updating them in their parents.
   *
   * @param {Function} iterator
   * @return {Node} node
   */

  mapDescendants: function mapDescendants(iterator) {
    var nodes = this.nodes.map(function (node, i, original) {
      if (node.kind != 'text') node = node.mapDescendants(iterator);
      return iterator(node, i, original);
    });

    return this.merge({ nodes: nodes });
  },


  /**
   * Normalize the node by joining any two adjacent text child nodes.
   *
   * @return {Node} node
   */

  normalize: function normalize() {
    var node = this;

    // Map this node's descendants, ensuring... ensuring there are no duplicate keys.
    var keys = [];

    node = node.mapDescendants(function (desc) {
      // That there are no duplicate keys.
      if (keys.includes(desc.key)) desc = desc.set('key', (0, _uid2.default)());
      keys.push(desc.key);

      // That void nodes contain no text.
      if (desc.isVoid && desc.length) {
        var text = desc.getTextNodes().first();
        var _characters = text.characters.clear();
        text = text.merge({ characters: _characters });
        var nodes = desc.nodes.clear().push(text);
        desc = desc.merge({ nodes: nodes });
      }

      // That no block or inline node is empty.
      if (desc.kind != 'text' && desc.nodes.size == 0) {
        var _text2 = _text4.default.create();
        var _nodes = desc.nodes.push(_text2);
        desc = desc.merge({ nodes: _nodes });
      }

      return desc;
    });

    // See if there are any adjacent text nodes.
    var first = node.findDescendant(function (child) {
      if (child.kind != 'text') return;
      var parent = node.getParent(child);
      var next = parent.getNextSibling(child);
      return next && next.kind == 'text';
    });

    // If no text nodes are adjacent, abort.
    if (!first) return node;

    // Fix an adjacent text node if one exists.
    var parent = node.getParent(first);
    var isParent = node == parent;
    var second = parent.getNextSibling(first);
    var characters = first.characters.concat(second.characters);
    first = first.merge({ characters: characters });
    parent = parent.updateDescendant(first);

    // Then remove the second text node.
    parent = parent.removeDescendant(second);

    // And update the node.
    node = isParent ? parent : node.updateDescendant(parent);

    // Recurse by normalizing again.
    return node.normalize();
  },


  /**
   * Remove children after a child by `key`.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  removeChildrenAfter: function removeChildrenAfter(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(0, index + 1);
    return this.merge({ nodes: nodes });
  },


  /**
   * Remove children after a child by `key`, including the child.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  removeChildrenAfterIncluding: function removeChildrenAfterIncluding(key) {
    var child = this.getChild(key);
    var index = this.nodes.indexOf(child);
    var nodes = this.nodes.slice(0, index);
    return this.merge({ nodes: nodes });
  },


  /**
   * Remove a `node` from the children node map.
   *
   * @param {String or Node} key
   * @return {Node} node
   */

  removeDescendant: function removeDescendant(key) {
    key = normalizeKey(key);
    this.assertHasDescendant(key);

    var child = this.getChild(key);

    if (child) {
      var _nodes2 = this.nodes.filterNot(function (node) {
        return node == child;
      });
      return this.merge({ nodes: _nodes2 });
    }

    var nodes = this.nodes.map(function (node) {
      return node.kind == 'text' ? node : node.removeDescendant(key);
    });

    return this.merge({ nodes: nodes });
  },


  /**
   * Set a new value for a child node by `key`.
   *
   * @param {Node} node
   * @return {Node} node
   */

  updateDescendant: function updateDescendant(node) {
    this.assertHasDescendant(node);

    if (this.hasChild(node)) {
      var _nodes3 = this.nodes.map(function (child) {
        return child.key == node.key ? node : child;
      });
      return this.merge({ nodes: _nodes3 });
    }

    var nodes = this.nodes.map(function (child) {
      if (child.kind == 'text') return child;
      if (!child.hasDescendant(node)) return child;
      return child.updateDescendant(node);
    });

    return this.merge({ nodes: nodes });
  }
};

/**
 * Normalize a `key`, from a key string or a node.
 *
 * @param {String or Node} key
 * @return {String} key
 */

function normalizeKey(key) {
  if (typeof key == 'string') return key;
  return key.key;
}

/**
 * Check if an `index` of a `text` node is in a `range`.
 *
 * @param {Number} index
 * @param {Text} text
 * @param {Selection} range
 * @return {Set} characters
 */

function isInRange(index, text, range) {
  var startKey = range.startKey;
  var startOffset = range.startOffset;
  var endKey = range.endKey;
  var endOffset = range.endOffset;

  var matcher = void 0;

  if (text.key == startKey && text.key == endKey) {
    return startOffset <= index && index < endOffset;
  } else if (text.key == startKey) {
    return startOffset <= index;
  } else if (text.key == endKey) {
    return index < endOffset;
  } else {
    return true;
  }
}

/**
 * Transforms.
 */

for (var key in _transforms2.default) {
  Node[key] = _transforms2.default[key];
}

/**
 * Export.
 */

exports.default = Node;