'use strict';

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

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _block2 = require('../models/block');

var _block3 = _interopRequireDefault(_block2);

var _document = require('../models/document');

var _document2 = _interopRequireDefault(_document);

var _inline = require('../models/inline');

var _inline2 = _interopRequireDefault(_inline);

var _mark = require('../models/mark');

var _mark2 = _interopRequireDefault(_mark);

var _raw = require('./raw');

var _raw2 = _interopRequireDefault(_raw);

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

var _state2 = _interopRequireDefault(_state);

var _text = require('../models/text');

var _text2 = _interopRequireDefault(_text);

var _cheerio = require('cheerio');

var _cheerio2 = _interopRequireDefault(_cheerio);

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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * A rule to serialize text nodes.
 */

var TEXT_RULE = {
  deserialize: function deserialize(el) {
    if (el.type != 'text') return;
    return {
      kind: 'text',
      ranges: [{
        text: el.data
      }]
    };
  }
};

/**
 * A rule to serialize <br> nodes.
 */

var BR_RULE = {
  deserialize: function deserialize(el) {
    if (el.tagName != 'br') return;
    return {
      kind: 'text',
      ranges: [{
        text: '\n'
      }]
    };
  }
};

/**
 * HTML serializer.
 */

var Html = function () {

  /**
   * Create a new serializer with `rules`.
   *
   * @param {Array} rules
   * @return {Html} serializer
   */

  function Html() {
    var rules = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    _classCallCheck(this, Html);

    this.rules = [].concat(_toConsumableArray(rules), [TEXT_RULE, BR_RULE]);
  }

  /**
   * Deserialize pasted HTML.
   *
   * @param {String} html
   * @return {State} state
   */

  _createClass(Html, [{
    key: 'deserialize',
    value: function deserialize(html) {
      var $ = _cheerio2.default.load(html).root();
      var children = $.children().toArray();
      var nodes = this.deserializeElements(children);

      // HACK: ensure for now that all top-level inline are wrapping into a block.
      nodes = nodes.reduce(function (memo, node, i, original) {
        if (node.kind == 'block') {
          memo.push(node);
          return memo;
        }

        if (i > 0 && original[i - 1].kind != 'block') {
          var _block = memo[memo.length - 1];
          _block.nodes.push(node);
          return memo;
        }

        var block = {
          kind: 'block',
          type: 'paragraph',
          nodes: [node]
        };

        memo.push(block);
        return memo;
      }, []);

      var state = _raw2.default.deserialize({ nodes: nodes });
      return state;
    }

    /**
     * Deserialize an array of Cheerio `elements`.
     *
     * @param {Array} elements
     * @return {Array} nodes
     */

  }, {
    key: 'deserializeElements',
    value: function deserializeElements() {
      var _this = this;

      var elements = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

      var nodes = [];

      elements.forEach(function (element) {
        var node = _this.deserializeElement(element);
        if (!node) return;
        if (Array.isArray(node)) {
          nodes = nodes.concat(node);
        } else {
          nodes.push(node);
        }
      });

      return nodes;
    }

    /**
     * Deserialize a Cheerio `element`.
     *
     * @param {Object} element
     * @return {Mixed} node
     */

  }, {
    key: 'deserializeElement',
    value: function deserializeElement(element) {
      var _this2 = this;

      var node = void 0;

      var next = function next(elements) {
        return Array.isArray(elements) ? _this2.deserializeElements(elements) : _this2.deserializeElement(elements);
      };

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

      try {
        for (var _iterator = this.rules[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var rule = _step.value;

          var ret = rule.deserialize(element, next);
          if (!ret) continue;
          node = ret.kind == 'mark' ? this.deserializeMark(ret) : ret;
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      return node || next(element.children);
    }

    /**
     * Deserialize a `mark` object.
     *
     * @param {Object} mark
     * @return {Array} nodes
     */

  }, {
    key: 'deserializeMark',
    value: function deserializeMark(mark) {
      var _this3 = this;

      var type = mark.type;
      var data = mark.data;


      var applyMark = function applyMark(node) {
        if (node.kind == 'mark') return _this3.deserializeMark(node);

        if (node.kind != 'text') {
          node.nodes = node.nodes.map(applyMark);
        } else {
          node.ranges = node.ranges.map(function (range) {
            range.marks = range.marks || [];
            range.marks.push({ type: type, data: data });
            return range;
          });
        }

        return node;
      };

      return mark.nodes.reduce(function (nodes, node) {
        var ret = applyMark(node);
        if (Array.isArray(ret)) return nodes.concat(ret);
        nodes.push(ret);
        return nodes;
      }, []);
    }
  }]);

  return Html;
}();

/**
 * Export.
 */

exports.default = Html;