"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.applyPlugins = applyPlugins;
exports.compose = compose;
exports.findAncestorPosition = findAncestorPosition;
exports.getAncestorNodesBetween = getAncestorNodesBetween;
exports.isBlockActive = isBlockActive;
exports.isRangeOfType = isRangeOfType;
exports.isSharedMark = isSharedMark;
exports.sanitiseSelectionMarksForWrapping = void 0;

var _prosemirrorKeymap = require("prosemirror-keymap");

var _schema = require("../models/schema");

var _event = require("../utils/event");

var _core = require("./core");

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread 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 _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }

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

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 applyPlugins() {
  var plugins = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  var defaultSchema = arguments.length > 1 ? arguments[1] : undefined;
  var dispatcher = arguments.length > 2 ? arguments[2] : undefined;
  var config = arguments.length > 3 ? arguments[3] : undefined;
  var nodes = defaultSchema.nodes;
  var marks = defaultSchema.marks || {};
  var nativePlugins = [];
  var controllers = [];
  var schemas = [];
  plugins.forEach(function (p) {
    if (p.schema) {
      Array.isArray(p.schema) ? schemas.push.apply(schemas, _toConsumableArray(p.schema.map(function (S) {
        return new S();
      }))) : schemas.push(new p.schema());
    }

    dealPlugin(p, nativePlugins, controllers, dispatcher, config);
  });
  dealPlugin(new _core.CorePlugin(), nativePlugins, controllers, dispatcher, config);
  schemas.sort(function (s1, s2) {
    return s2.priority - s1.priority;
  }).forEach(function (s) {
    if (s instanceof _schema.InlineSchema) {
      marks[s.name] = s;
    } else {
      nodes[s.name] = s;
    }
  });
  return {
    nodes: nodes,
    marks: marks,
    nativePlugins: nativePlugins,
    controllers: controllers
  };
}

function dealPlugin(plugin, nativePlugins, controllers, dispatcher) {
  var config = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};

  if (plugin.controller) {
    var pController = new plugin.controller();
    controllers.push(pController);

    if (pController.pmPlugins) {
      nativePlugins.push.apply(nativePlugins, _toConsumableArray(pController.pmPlugins.map(function (pm) {
        return typeof pm == 'function' ? pm((0, _event.createDispatch)(dispatcher), config[plugin.name]) : pm;
      })));
    }

    if (pController.keymap) {
      nativePlugins.push((0, _prosemirrorKeymap.keymap)(pController.keymap));
    }
  }
}

function addSchema(nodes, marks, SchemaCtor) {
  var pSchema = new SchemaCtor();

  if (pSchema instanceof _schema.InlineSchema) {
    marks[pSchema.name] = pSchema;
  } else {
    nodes[pSchema.name] = pSchema;
  }
}

function isSharedMark(editor, from, to, type) {
  var attrs = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
  var state = editor.state;
  var allHasMark = true;
  state.doc.nodesBetween(from, to, function (node) {
    if (!node.isText || !allHasMark) return;
    var sameTypeMarks = node.marks.filter(function (m) {
      return m.type == type;
    });

    if (!sameTypeMarks.length) {
      allHasMark = false;
      return;
    }

    sameTypeMarks.forEach(function (mark) {
      if (!allHasMark) return;

      if (Object.keys(attrs).length != Object.keys(mark.attrs).length) {
        allHasMark = false;
        return;
      }

      for (var key in attrs) {
        if (!(key in mark.attrs) || mark.attrs[key] !== attrs[key]) {
          allHasMark = false;
          return;
        }
      }
    });
  });
  return allHasMark;
}
/**
 * Traverse the document until an "ancestor" is found. Any nestable block can be an ancestor.
 */


function findAncestorPosition(doc, pos) {
  var nestableBlocks = ['blockquote', 'bulletList', 'orderedList'];

  if (pos.depth === 1) {
    return pos;
  }

  var node = pos.node(pos.depth);
  var newPos = pos;

  while (pos.depth >= 1) {
    pos = doc.resolve(pos.before(pos.depth));
    node = pos.node(pos.depth);

    if (node && nestableBlocks.indexOf(node.type.name) !== -1) {
      newPos = pos;
    }
  }

  return newPos;
}
/**
 * Returns all top-level ancestor-nodes between $from and $to
 */


function getAncestorNodesBetween(doc, $from, $to) {
  var nodes = Array();
  var maxDepth = findAncestorPosition(doc, $from).depth;
  var current = doc.resolve($from.start(maxDepth));

  while (current.pos <= $to.start($to.depth)) {
    var depth = Math.min(current.depth, maxDepth);
    var node = current.node(depth);

    if (node) {
      nodes.push(node);
    }

    if (depth === 0) {
      break;
    }

    var next = doc.resolve(current.after(depth));

    if (next.start(depth) >= doc.nodeSize - 2) {
      break;
    }

    if (next.depth !== current.depth) {
      next = doc.resolve(next.pos + 2);
    }

    if (next.depth) {
      current = doc.resolve(next.start(next.depth));
    } else {
      current = doc.resolve(next.end(next.depth));
    }
  }

  return nodes;
}
/**
 * Step through block-nodes between $from and $to and returns false if a node is
 * found that isn't of the specified type
 */


function isRangeOfType(doc, $from, $to, nodeType) {
  return getAncestorNodesBetween(doc, $from, $to).filter(function (node) {
    return node.type !== nodeType;
  }).length === 0;
}
/** Helper type for single arg function */


/**
 * Compose 1 to n functions.
 * @param func first function
 * @param funcs additional functions
 */
function compose(func) {
  for (var _len = arguments.length, funcs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    funcs[_key - 1] = arguments[_key];
  }

  var allFuncs = [func].concat(funcs);
  return function composed(raw) {
    return allFuncs.reduceRight(function (memo, func) {
      return func(memo);
    }, raw);
  };
}
/**
 * Removes marks from nodes in the current selection that are not supported
 */


var sanitiseSelectionMarksForWrapping = function sanitiseSelectionMarksForWrapping(state, newParentType) {
  var tr;
  var _state$tr$selection = state.tr.selection,
      from = _state$tr$selection.from,
      to = _state$tr$selection.to;
  state.doc.nodesBetween(from, to, function (node, pos, parent) {
    // If iterate over a node thats out of our defined range
    // We skip here but continue to iterate over its children.
    if (node.isText || pos < from || pos > to) {
      return true;
    }

    node.marks.forEach(function (mark) {
      if (!parent.type.allowsMarkType(mark.type) || newParentType && !newParentType.allowsMarkType(mark.type)) {
        var filteredMarks = node.marks.filter(function (m) {
          return m.type !== mark.type;
        });
        var position = pos > 0 ? pos - 1 : 0;
        tr = (tr || state.tr).setNodeMarkup(position, undefined, node.attrs, filteredMarks);
      }
    });
  }, from);
  return tr;
};

exports.sanitiseSelectionMarksForWrapping = sanitiseSelectionMarksForWrapping;

function isAttrEquals(node, attrs) {
  for (var key in attrs) {
    if (attrs[key] != node.attrs[key]) return false;
  }

  return true;
}

function isBlockActive(state, type) {
  var attrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  var _state$selection = state.selection,
      from = _state$selection.from,
      to = _state$selection.to;
  var active = false;
  state.doc.nodesBetween(from, to, function (node) {
    if (active) return false;
    if (node.type.name == type && isAttrEquals(node, attrs)) active = true;
    return !active;
  });
  return active;
}