"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.wholeSelectionHasMarks = exports.isMarkExcluded = exports.isMarkAllowedInRange = exports.anyMarkActive = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _model = require("@atlaskit/editor-prosemirror/model");
var _cellSelection = require("@atlaskit/editor-tables/cell-selection");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
/**
 * Determine if a mark of a specific type exists anywhere in the selection.
 */
var anyMarkActive = exports.anyMarkActive = function anyMarkActive(state, markType) {
  var _state$selection = state.selection,
    $from = _state$selection.$from,
    from = _state$selection.from,
    to = _state$selection.to,
    empty = _state$selection.empty;
  if (empty) {
    return !!markType.isInSet(state.storedMarks || $from.marks());
  }
  var rangeHasMark = false;
  if (state.selection instanceof _cellSelection.CellSelection) {
    state.selection.forEachCell(function (cell, cellPos) {
      var from = cellPos;
      var to = cellPos + cell.nodeSize;
      if (!rangeHasMark) {
        rangeHasMark = state.doc.rangeHasMark(from, to, markType);
      }
    });
  } else {
    rangeHasMark = state.doc.rangeHasMark(from, to, markType);
  }
  return rangeHasMark;
};
var allMarksAreRuledOut = function allMarksAreRuledOut(marks) {
  return Array.from(marks.values()).every(function (mark) {
    return !mark;
  });
};

/**
 * Check if a selection contains any of the specifiged marks. The whole selection
 * must have the mark for it to be considered active.
 * @param state The editor state
 * @param markTypes The marks to check for
 * @returns A map indicating which marks are present in the selection
 * @example
 * const markTypes = editorState.schema.marks;
 * const activeMarks = wholeSelectionHasMarks(editorState, [markTypes.strong, markTypes.em, markTypes.etc]);
 */
var wholeSelectionHasMarks = exports.wholeSelectionHasMarks = function wholeSelectionHasMarks(state, markTypes) {
  var _state$selection2 = state.selection,
    $from = _state$selection2.$from,
    from = _state$selection2.from,
    to = _state$selection2.to,
    empty = _state$selection2.empty;
  if (empty) {
    return new Map(markTypes.map(function (markType) {
      return [markType, !!markType.isInSet(state.storedMarks || $from.marks())];
    }));
  }
  if (state.selection instanceof _cellSelection.CellSelection) {
    return cellSelectionHasMarks(state.doc, state.selection, markTypes);
  }
  return wholeRangeHasMarks(from, to, state.doc, markTypes);
};
var cellSelectionHasMarks = function cellSelectionHasMarks(doc, selection, markTypes) {
  // Warning: This is micro-optimized and is a total pain to actually read.
  // Will attempt to explain in comments.

  // Start with map of booleans for each mark
  var cellsHaveMarks = new Map(markTypes.map(function (markType) {
    return [markType, true];
  }));
  selection.forEachCell(function (cell, cellPos) {
    // Early exit if all marks are already ruled out
    if (allMarksAreRuledOut(cellsHaveMarks)) {
      return;
    }
    var from = cellPos;
    var to = cellPos + cell.nodeSize;

    // On first cell just do a regular check
    if (!cellsHaveMarks) {
      cellsHaveMarks = wholeRangeHasMarks(from, to, doc, markTypes);
    } else {
      // Find the marks that are still true ie the ones that haven't been ruled out in
      // previous cells. The idea here is to whittle down the list of marks so that we check
      // less and less as we go, giving `wholeRangeHasMarks` more opportunities to exit early
      var marksToCheck = [];
      var _iterator = _createForOfIteratorHelper(cellsHaveMarks),
        _step;
      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var _step$value = (0, _slicedToArray2.default)(_step.value, 2),
            markType = _step$value[0],
            hasMark = _step$value[1];
          if (hasMark) {
            marksToCheck.push(markType);
          }
        }

        // Look specifically for the marks that are not yet ruled out.
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }
      var cellHasMarks = wholeRangeHasMarks(from, to, doc, marksToCheck);

      // Map these results back into the original array of results and repeat!
      var _iterator2 = _createForOfIteratorHelper(cellHasMarks),
        _step2;
      try {
        for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
          var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2),
            _markType = _step2$value[0],
            _hasMark = _step2$value[1];
          cellsHaveMarks.set(_markType, _hasMark);
        }
      } catch (err) {
        _iterator2.e(err);
      } finally {
        _iterator2.f();
      }
    }
  });
  return cellsHaveMarks;
};
var wholeRangeHasMarks = function wholeRangeHasMarks(from, to, doc, markTypes) {
  var hasMarks = new Map(markTypes.map(function (markType) {
    return [markType, true];
  }));
  var hasNoMarks = new Map(markTypes.map(function (markType) {
    return [markType, false];
  }));
  var isTextContent = false;
  doc.nodesBetween(from, to, function (node) {
    if (allMarksAreRuledOut(hasMarks)) {
      // This won't be a true early exit, but will prevent diving into nodes and
      // any checks further down the function.
      return false;
    }
    if (!node.type.isText) {
      return true; // continue traversing
    }
    isTextContent = true;
    var _iterator3 = _createForOfIteratorHelper(hasMarks),
      _step3;
    try {
      var _loop = function _loop() {
        var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2),
          markType = _step3$value[0],
          hasMark = _step3$value[1];
        if (!hasMark) {
          return 1; // continue
          // already ruled out the mark, skip further checks
        }
        var value = markType instanceof _model.Mark ? markType.isInSet(node.marks) : node.marks.some(function (mark) {
          return mark.type === markType;
        });
        hasMarks.set(markType, value);
      };
      for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
        if (_loop()) continue;
      }
    } catch (err) {
      _iterator3.e(err);
    } finally {
      _iterator3.f();
    }
  });
  return isTextContent ? hasMarks : hasNoMarks;
};
var isMarkAllowedInRange = exports.isMarkAllowedInRange = function isMarkAllowedInRange(doc, ranges, type) {
  var _loop2 = function _loop2() {
      var _ranges$i = ranges[i],
        $from = _ranges$i.$from,
        $to = _ranges$i.$to;
      var can = $from.depth === 0 ? doc.type.allowsMarkType(type) : false;
      doc.nodesBetween($from.pos, $to.pos, function (node) {
        if (can) {
          return false;
        }
        can = node.inlineContent && node.type.allowsMarkType(type);
        return;
      });
      if (can) {
        return {
          v: can
        };
      }
    },
    _ret;
  for (var i = 0; i < ranges.length; i++) {
    _ret = _loop2();
    if (_ret) return _ret.v;
  }
  return false;
};
var isMarkExcluded = exports.isMarkExcluded = function isMarkExcluded(type, marks) {
  if (marks) {
    return marks.some(function (mark) {
      return mark.type !== type && mark.type.excludes(type);
    });
  }
  return false;
};