'use strict';

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

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _escapeRegexString = require('escape-regex-string');

var _escapeRegexString2 = _interopRequireDefault(_escapeRegexString);

var _componentType = require('component-type');

var _componentType2 = _interopRequireDefault(_componentType);

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

/**
 * A Slate plugin to automatically replace a string of matching text when typed.
 *
 * @param {Mixed} ...args
 * @return {Object}
 */

function AutoReplaceTextPlugin() {
  var opts = void 0;

  // Allow for double string argument shorthand.
  if (arguments.length > 1) {
    opts = (arguments.length <= 2 ? undefined : arguments[2]) ? _extends({}, arguments.length <= 2 ? undefined : arguments[2]) : {};
    var parsed = parseTrigger(arguments.length <= 0 ? undefined : arguments[0]);
    opts.trigger = normalizeTrigger(parsed.trigger);
    opts.matchBefore = parsed.matchBefore;
    opts.replacement = normalizeReplacement(arguments.length <= 1 ? undefined : arguments[1]);
  } else {
    opts = _extends({}, arguments.length <= 0 ? undefined : arguments[0]);
    opts.trigger = normalizeTrigger(opts.trigger);
    opts.replacement = normalizeReplacement(opts.replacement);
  }

  var _opts = opts;
  var trigger = _opts.trigger;
  var matchBefore = _opts.matchBefore;
  var matchAfter = _opts.matchAfter;
  var replacement = _opts.replacement;
  var onlyIn = _opts.onlyIn;
  var ignoreIn = _opts.ignoreIn;


  if (!trigger) throw new Error('You must provide a `trigger`.');
  if (!replacement) throw new Error('You must provide a `replacement`.');

  /**
   * On before input, trigger a replacement if the character matches.
   *
   * @param {Event} e
   * @param {State} state
   * @param {Editor} editor
   * @return {State}
   */

  function onBeforeInput(e, state, editor) {
    if (state.isExpanded) return;
    if (!trigger(e)) return;

    var block = state.startBlock;
    var type = block.type;
    if (onlyIn && !onlyIn.includes(type)) return;
    if (ignoreIn && ignoreIn.includes(type)) return;

    var matches = getMatches(state);
    if (!matches) return;

    var _getOffsets = getOffsets(matches, state.startOffset);

    var start = _getOffsets.start;
    var end = _getOffsets.end;

    var text = replacement(e.data, matches);

    e.preventDefault();

    return state.transform().moveToOffsets(start, end).insertText(text).apply();
  }

  /**
   * Try to match a `state` with the `matchBefore` and `matchAfter` regexes.
   *
   * @param {State} state
   * @return {Object}
   */

  function getMatches(state) {
    var startText = state.startText;
    var startOffset = state.startOffset;
    var text = startText.text;

    var after = null;
    var before = null;

    if (matchAfter) {
      var string = text.slice(startOffset);
      after = string.match(matchAfter);
    }

    if (matchBefore) {
      var _string = text.slice(0, startOffset);
      before = _string.match(matchBefore);
    }

    // If both sides, require that both are matched, otherwise null.
    if (matchBefore && matchAfter && !before) after = null;
    if (matchBefore && matchAfter && !after) before = null;

    // Return null unless we have a match.
    if (!before && !after) return null;
    return { before: before, after: after };
  }

  /**
   * Return the offsets for `matches` with `start` offset.
   *
   * @param {Object} matches
   * @param {Number} start
   * @return {Object}
   */

  function getOffsets(matches, start) {
    var before = matches.before;
    var after = matches.after;

    var end = start;

    if (before && before[1]) start -= before[1].length;
    if (after && after[1]) end += after[1].length;

    return { start: start, end: end };
  }

  /**
   * Return the plugin.
   */

  return {
    onBeforeInput: onBeforeInput
  };
}

/**
 * Parse a `trigger` string into a trigger and before matcher.
 *
 * @param {String} trigger
 * @return {Object}
 */

function parseTrigger(trigger) {
  if ((0, _componentType2.default)(trigger) == 'regexp') {
    return {
      trigger: trigger,
      matchBefore: undefined
    };
  }

  var index = trigger.length - 1;
  var before = trigger.slice(0, index);
  var last = trigger.slice(index);
  return {
    trigger: last,
    matchBefore: new RegExp('(' + (0, _escapeRegexString2.default)(before) + ')$')
  };
}

/**
 * Normalize a `trigger` option to a matching function.
 *
 * @param {Mixed} trigger
 * @return {Function}
 */

function normalizeTrigger(trigger) {
  switch ((0, _componentType2.default)(trigger)) {
    case 'regexp':
      return function (e) {
        return !!e.data.match(trigger);
      };
    case 'string':
      return function (e) {
        return e.data == trigger;
      };
  }
}

/**
 * Normalize a `replacement` option to a function.
 *
 * @param {Mixed} replacement
 * @return {Function}
 */

function normalizeReplacement(replacement) {
  switch ((0, _componentType2.default)(replacement)) {
    case 'function':
      return replacement;
    case 'string':
      return function (char) {
        return replacement;
      };
  }
}

/**
 * Export.
 */

exports.default = AutoReplaceTextPlugin;