'use strict';

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

var _key3 = require('../utils/key');

var _key4 = _interopRequireDefault(_key3);

var _placeholder = require('../components/placeholder');

var _placeholder2 = _interopRequireDefault(_placeholder);

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _keycode = require('keycode');

var _keycode2 = _interopRequireDefault(_keycode);

var _environment = require('../utils/environment');

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

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; }

/**
 * The default plugin.
 *
 * @param {Object} options
 *   @property {Element} placeholder
 *   @property {String} placeholderClassName
 *   @property {Object} placeholderStyle
 * @return {Object}
 */

function Plugin() {
  var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  var placeholder = options.placeholder;
  var placeholderClassName = options.placeholderClassName;
  var placeholderStyle = options.placeholderStyle;

  /**
   * Define a default block renderer.
   *
   * @type {Component}
   */

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

    function DEFAULT_BLOCK() {
      var _Object$getPrototypeO;

      var _temp, _this, _ret;

      _classCallCheck(this, DEFAULT_BLOCK);

      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }

      return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_Object$getPrototypeO = Object.getPrototypeOf(DEFAULT_BLOCK)).call.apply(_Object$getPrototypeO, [this].concat(args))), _this), _this.render = function () {
        var _this$props = _this.props;
        var attributes = _this$props.attributes;
        var children = _this$props.children;

        return _react2.default.createElement(
          'div',
          attributes,
          _this.renderPlaceholder(),
          children
        );
      }, _this.renderPlaceholder = function () {
        if (!placeholder) return null;
        var _this$props2 = _this.props;
        var node = _this$props2.node;
        var state = _this$props2.state;


        return _react2.default.createElement(
          _placeholder2.default,
          {
            className: placeholderClassName,
            node: node,
            parent: state.document,
            state: state,
            style: placeholderStyle
          },
          placeholder
        );
      }, _temp), _possibleConstructorReturn(_this, _ret);
    }

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

  /**
   * Define a default inline renderer.
   *
   * @type {Component}
   */

  DEFAULT_BLOCK.propTypes = {
    attributes: _react2.default.PropTypes.object.isRequired,
    children: _react2.default.PropTypes.any.isRequired,
    node: _react2.default.PropTypes.object.isRequired,
    state: _react2.default.PropTypes.object.isRequired
  };

  var DEFAULT_INLINE = function (_React$Component2) {
    _inherits(DEFAULT_INLINE, _React$Component2);

    function DEFAULT_INLINE() {
      var _Object$getPrototypeO2;

      var _temp2, _this2, _ret2;

      _classCallCheck(this, DEFAULT_INLINE);

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

      return _ret2 = (_temp2 = (_this2 = _possibleConstructorReturn(this, (_Object$getPrototypeO2 = Object.getPrototypeOf(DEFAULT_INLINE)).call.apply(_Object$getPrototypeO2, [this].concat(args))), _this2), _this2.render = function () {
        var _this2$props = _this2.props;
        var attributes = _this2$props.attributes;
        var children = _this2$props.children;

        return _react2.default.createElement(
          'span',
          attributes,
          children
        );
      }, _temp2), _possibleConstructorReturn(_this2, _ret2);
    }

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

  /**
   * Define a default mark renderer.
   *
   * @type {Object}
   */

  DEFAULT_INLINE.propTypes = {
    attributes: _react2.default.PropTypes.object.isRequired,
    children: _react2.default.PropTypes.any.isRequired,
    node: _react2.default.PropTypes.object.isRequired,
    state: _react2.default.PropTypes.object.isRequired
  };
  var DEFAULT_MARK = {};

  /**
   * Return the plugin.
   */

  return {

    /**
     * The core `onBeforeInput` handler.
     *
     * If the current selection is expanded, we have to re-render.
     *
     * If the next state resolves a new list of decorations for any of its text
     * nodes, we have to re-render.
     *
     * Otherwise, we can allow the default, native text insertion, avoiding a
     * re-render for improved performance.
     *
     * @param {Event} e
     * @param {State} state
     * @param {Editor} editor
     * @return {State or Null} newState
     */

    onBeforeInput: function onBeforeInput(e, state, editor) {
      var transform = state.transform().insertText(e.data);
      var synthetic = transform.apply();
      var resolved = editor.resolveState(synthetic);

      var isSynthenic = state.isExpanded || !resolved.equals(synthetic);

      if (isSynthenic) e.preventDefault();

      return isSynthenic ? synthetic : transform.apply({ isNative: true });
    },


    /**
     * The core `onKeyDown` handler.
     *
     * @param {Event} e
     * @param {State} state
     * @param {Editor} editor
     * @return {State or Null} newState
     */

    onKeyDown: function onKeyDown(e, state, editor) {
      var key = (0, _keycode2.default)(e.which);
      var transform = state.transform();

      switch (key) {
        case 'enter':
          {
            return transform.splitBlock().apply();
          }

        case 'backspace':
          {
            return _key4.default.isWord(e) ? transform.backspaceWord().apply() : transform.deleteBackward().apply();
          }

        case 'delete':
          {
            return _key4.default.isWord(e) ? transform.deleteWord().apply() : transform.deleteForward().apply();
          }

        case 'up':
          {
            if (state.isExpanded) return;
            var first = state.blocks.first();
            if (!first || !first.isVoid) return;
            e.preventDefault();
            return transform.collapseToEndOfPreviousBlock().apply();
          }

        case 'down':
          {
            if (state.isExpanded) return;
            var _first = state.blocks.first();
            if (!_first || !_first.isVoid) return;
            e.preventDefault();
            return transform.collapseToStartOfNextBlock().apply();
          }

        case 'left':
          {
            if (state.isExpanded) return;
            var node = state.blocks.first() || state.inlines.first();
            if (!node || !node.isVoid) return;
            e.preventDefault();
            return transform.collapseToEndOfPreviousText().apply();
          }

        case 'right':
          {
            if (state.isExpanded) return;
            var _node = state.blocks.first() || state.inlines.first();
            if (!_node || !_node.isVoid) return;
            e.preventDefault();
            return transform.collapseToStartOfNextText().apply();
          }

        case 'y':
          {
            if (!_key4.default.isWindowsCommand(e)) return;
            return transform.redo();
          }

        case 'z':
          {
            if (!_key4.default.isCommand(e)) return;
            return _environment.IS_MAC && _key4.default.isShift(e) ? transform.redo() : transform.undo();
          }
      }
    },


    /**
     * The core `onPaste` handler, which treats everything as plain text.
     *
     * @param {Event} e
     * @param {Object} paste
     * @param {State} state
     * @param {Editor} editor
     * @return {State or Null} newState
     */

    onPaste: function onPaste(e, paste, state, editor) {
      if (paste.type == 'files') return;

      var transform = state.transform();

      paste.text.split('\n').forEach(function (line, i) {
        if (i > 0) transform = transform.splitBlock();
        transform = transform.insertText(line);
      });

      return transform.apply();
    },


    /**
     * The core `node` renderer, which uses plain `<div>` or `<span>` depending on
     * what kind of node it is.
     *
     * @param {Node} node
     * @return {Component} component
     */

    renderNode: function renderNode(node) {
      return node.kind == 'block' ? DEFAULT_BLOCK : DEFAULT_INLINE;
    },


    /**
     * The core `mark` renderer, with no styles.
     *
     * @param {Mark} mark
     * @return {Object} style
     */

    renderMark: function renderMark(mark) {
      return DEFAULT_MARK;
    }
  };
}

/**
 * Export.
 */

exports.default = Plugin;