'use strict';

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

var _content = require('./content');

var _content2 = _interopRequireDefault(_content);

var _core = require('../plugins/core');

var _core2 = _interopRequireDefault(_core);

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _state2 = require('../models/state');

var _state3 = _interopRequireDefault(_state2);

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

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

/**
 * Editor.
 */

var Editor = function (_React$Component) {
  _inherits(Editor, _React$Component);

  /**
   * When created, compute the plugins from `props`.
   *
   * @param {Object} props
   */

  /**
   * Properties.
   */

  function Editor(props) {
    _classCallCheck(this, Editor);

    var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Editor).call(this, props));

    _initialiseProps.call(_this);

    _this.state = {};
    _this.state.plugins = _this.resolvePlugins(props);
    _this.state.state = _this.resolveState(props.state);
    return _this;
  }

  /**
   * When the `props` are updated, recompute the plugins.
   *
   * @param {Object} props
   */

  /**
   * Blur the editor.
   */

  /**
   * Focus the editor.
   */

  /**
   * Get the editor's current `state`.
   *
   * @return {State} state
   */

  /**
   * When the `state` changes, pass through plugins, then bubble up.
   *
   * @param {State} state
   */

  /**
   * When an event by `name` fires, pass it through the plugins, and update the
   * state if one of them chooses to.
   *
   * @param {String} name
   * @param {Mixed} ...args
   */

  /**
   * On before input.
   *
   * @param {Mixed} ...args
   */

  /**
   * On key down.
   *
   * @param {Mixed} ...args
   */

  /**
   * On paste.
   *
   * @param {Mixed} ...args
   */

  /**
   * Render the editor.
   *
   * @return {Element} element
   */

  /**
   * Render a `node`, cascading through the plugins.
   *
   * @param {Node} node
   * @return {Element} element
   */

  /**
   * Render a `mark`, cascading through the plugins.
   *
   * @param {Mark} mark
   * @return {Object} style
   */

  /**
   * Resolve the editor's current plugins from `props` when they change.
   *
   * Add a plugin made from the editor's own `props` at the beginning of the
   * stack. That way, you can add a `onKeyDown` handler to the editor itself,
   * and it will override all of the existing plugins.
   *
   * Also add the "core" functionality plugin that handles the most basic events
   * for the editor, like delete characters and such.
   *
   * @param {Object} props
   * @return {Array} plugins
   */

  /**
   * Resolve the editor's current state from `props` when they change.
   *
   * This is where we handle decorating the text nodes with the decorator
   * functions, so that they are always accounted for when rendering.
   *
   * @param {State} state
   * @return {State} state
   */

  return Editor;
}(_react2.default.Component);

/**
 * Export.
 */

Editor.propTypes = {
  className: _react2.default.PropTypes.string,
  onBeforeInput: _react2.default.PropTypes.func,
  onChange: _react2.default.PropTypes.func.isRequired,
  onKeyDown: _react2.default.PropTypes.func,
  onPaste: _react2.default.PropTypes.func,
  placeholder: _react2.default.PropTypes.any,
  placeholderClassName: _react2.default.PropTypes.string,
  placeholderStyle: _react2.default.PropTypes.object,
  plugins: _react2.default.PropTypes.array,
  renderDecorations: _react2.default.PropTypes.func,
  renderMark: _react2.default.PropTypes.func,
  renderNode: _react2.default.PropTypes.func,
  state: _react2.default.PropTypes.object.isRequired,
  style: _react2.default.PropTypes.object
};
Editor.defaultProps = {
  plugins: []
};

var _initialiseProps = function _initialiseProps() {
  var _this2 = this;

  this.componentWillReceiveProps = function (props) {
    _this2.setState({ plugins: _this2.resolvePlugins(props) });
    _this2.setState({ state: _this2.resolveState(props.state) });
  };

  this.blur = function () {
    var state = _this2.state.state.transform().blur().apply();

    _this2.onChange(state);
  };

  this.focus = function () {
    var state = _this2.state.state.transform().focus().apply();

    _this2.onChange(state);
  };

  this.getState = function () {
    return _this2.state.state;
  };

  this.onChange = function (state) {
    if (state == _this2.state.state) return;

    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = _this2.state.plugins[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var plugin = _step.value;

        if (!plugin.onChange) continue;
        var newState = plugin.onChange(state, _this2);
        if (newState == null) continue;
        state = newState;
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }

    _this2.props.onChange(state);
  };

  this.onEvent = function (name) {
    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      args[_key - 1] = arguments[_key];
    }

    var _iteratorNormalCompletion2 = true;
    var _didIteratorError2 = false;
    var _iteratorError2 = undefined;

    try {
      for (var _iterator2 = _this2.state.plugins[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
        var plugin = _step2.value;

        if (!plugin[name]) continue;
        var newState = plugin[name].apply(plugin, args.concat([_this2.state.state, _this2]));
        if (!newState) continue;
        _this2.onChange(newState);
        break;
      }
    } catch (err) {
      _didIteratorError2 = true;
      _iteratorError2 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion2 && _iterator2.return) {
          _iterator2.return();
        }
      } finally {
        if (_didIteratorError2) {
          throw _iteratorError2;
        }
      }
    }
  };

  this.onBeforeInput = function () {
    for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      args[_key2] = arguments[_key2];
    }

    _this2.onEvent.apply(_this2, ['onBeforeInput'].concat(args));
  };

  this.onKeyDown = function () {
    for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
      args[_key3] = arguments[_key3];
    }

    _this2.onEvent.apply(_this2, ['onKeyDown'].concat(args));
  };

  this.onPaste = function () {
    for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
      args[_key4] = arguments[_key4];
    }

    _this2.onEvent.apply(_this2, ['onPaste'].concat(args));
  };

  this.render = function () {
    return _react2.default.createElement(_content2.default, {
      className: _this2.props.className,
      editor: _this2,
      state: _this2.state.state,
      onChange: _this2.onChange,
      renderMark: _this2.renderMark,
      renderNode: _this2.renderNode,
      onPaste: _this2.onPaste,
      onBeforeInput: _this2.onBeforeInput,
      onKeyDown: _this2.onKeyDown,
      style: _this2.props.style
    });
  };

  this.renderNode = function (node) {
    var _iteratorNormalCompletion3 = true;
    var _didIteratorError3 = false;
    var _iteratorError3 = undefined;

    try {
      for (var _iterator3 = _this2.state.plugins[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
        var plugin = _step3.value;

        if (!plugin.renderNode) continue;
        var component = plugin.renderNode(node, _this2.state.state, _this2);
        if (component) return component;
      }
    } catch (err) {
      _didIteratorError3 = true;
      _iteratorError3 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion3 && _iterator3.return) {
          _iterator3.return();
        }
      } finally {
        if (_didIteratorError3) {
          throw _iteratorError3;
        }
      }
    }
  };

  this.renderMark = function (mark) {
    var _iteratorNormalCompletion4 = true;
    var _didIteratorError4 = false;
    var _iteratorError4 = undefined;

    try {
      for (var _iterator4 = _this2.state.plugins[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
        var plugin = _step4.value;

        if (!plugin.renderMark) continue;
        var style = plugin.renderMark(mark, _this2.state.state, _this2);
        if (style) return style;
      }
    } catch (err) {
      _didIteratorError4 = true;
      _iteratorError4 = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion4 && _iterator4.return) {
          _iterator4.return();
        }
      } finally {
        if (_didIteratorError4) {
          throw _iteratorError4;
        }
      }
    }
  };

  this.resolvePlugins = function (props) {
    var onChange = props.onChange;
    var plugins = props.plugins;

    var editorPlugin = _objectWithoutProperties(props, ['onChange', 'plugins']);

    var corePlugin = (0, _core2.default)(props);
    return [editorPlugin].concat(_toConsumableArray(plugins), [corePlugin]);
  };

  this.resolveState = function (state) {
    var plugins = _this2.state.plugins;
    var _state = state;
    var document = _state.document;


    document = document.decorateTexts(function (text) {
      var _iteratorNormalCompletion5 = true;
      var _didIteratorError5 = false;
      var _iteratorError5 = undefined;

      try {
        for (var _iterator5 = plugins[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
          var plugin = _step5.value;

          if (!plugin.renderDecorations) continue;
          var characters = plugin.renderDecorations(text, state, _this2);
          if (characters) return characters;
        }
      } catch (err) {
        _didIteratorError5 = true;
        _iteratorError5 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion5 && _iterator5.return) {
            _iterator5.return();
          }
        } finally {
          if (_didIteratorError5) {
            throw _iteratorError5;
          }
        }
      }
    });

    state = state.merge({ document: document });
    return state;
  };
};

exports.default = Editor;