"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CustomToolbarComp = void 0;
exports["default"] = MathPlugin;
exports.serialization = exports.inlineMath = void 0;

var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _Functions = _interopRequireDefault(require("@material-ui/icons/Functions"));

var _slate = require("slate");

var _mathToolbar = require("../../../math-toolbar");

var _mathRendering = require("@pie-lib/math-rendering");

var _react = _interopRequireDefault(require("react"));

var _debug = _interopRequireDefault(require("debug"));

var _slatePropTypes = _interopRequireDefault(require("slate-prop-types"));

var _propTypes = _interopRequireDefault(require("prop-types"));

var _serialization = require("../../serialization");

var _isEqual = _interopRequireDefault(require("lodash/isEqual"));

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

var log = (0, _debug["default"])('@pie-lib:editable-html:plugins:math');
var TEXT_NODE = 3;

function generateAdditionalKeys() {
  var keyData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  return keyData.map(function (key) {
    return {
      name: key,
      latex: key,
      write: key,
      label: key
    };
  });
} // eslint-disable-next-line react/display-name


var CustomToolbarComp = /*#__PURE__*/_react["default"].memo(function (props) {
  var node = props.node,
      value = props.value,
      onFocus = props.onFocus,
      onBlur = props.onBlur,
      onClick = props.onClick;

  var _ref = props || {},
      pluginProps = _ref.pluginProps;

  var _ref2 = pluginProps || {},
      math = _ref2.math;

  var _ref3 = math || {},
      keypadMode = _ref3.keypadMode,
      customKeys = _ref3.customKeys,
      _ref3$controlledKeypa = _ref3.controlledKeypadMode,
      controlledKeypadMode = _ref3$controlledKeypa === void 0 ? true : _ref3$controlledKeypa;

  var onDone = function onDone(latex) {
    var update = _objectSpread(_objectSpread({}, node.data.toObject()), {}, {
      latex: latex
    });

    var change = value.change().setNodeByKey(node.key, {
      data: update
    });
    var nextText = value.document.getNextText(node.key);
    change.moveFocusTo(nextText.key, 0).moveAnchorTo(nextText.key, 0);
    props.onToolbarDone(change, false);
  };

  var onChange = function onChange(latex) {
    var update = _objectSpread(_objectSpread({}, node.data.toObject()), {}, {
      latex: latex
    });

    var change = value.change().setNodeByKey(node.key, {
      data: update
    });
    log('call onToolbarChange:', change);
    props.onDataChange(node.key, update);
  };

  var latex = node.data.get('latex');
  return /*#__PURE__*/_react["default"].createElement(_mathToolbar.MathToolbar, {
    autoFocus: true,
    additionalKeys: generateAdditionalKeys(customKeys),
    latex: latex,
    onChange: onChange,
    onDone: onDone,
    onBlur: onBlur,
    onFocus: onFocus,
    onClick: onClick,
    keypadMode: keypadMode,
    controlledKeypadMode: controlledKeypadMode
  });
}, function (prev, next) {
  var node = prev.node,
      _prev$pluginProps = prev.pluginProps;
  _prev$pluginProps = _prev$pluginProps === void 0 ? {} : _prev$pluginProps;
  var _prev$pluginProps$mat = _prev$pluginProps.math;
  _prev$pluginProps$mat = _prev$pluginProps$mat === void 0 ? {} : _prev$pluginProps$mat;
  var keypadMode = _prev$pluginProps$mat.keypadMode,
      controlledKeypadMode = _prev$pluginProps$mat.controlledKeypadMode;
  var nodeNext = next.node,
      _next$pluginProps = next.pluginProps;
  _next$pluginProps = _next$pluginProps === void 0 ? {} : _next$pluginProps;
  var _next$pluginProps$mat = _next$pluginProps.math;
  _next$pluginProps$mat = _next$pluginProps$mat === void 0 ? {} : _next$pluginProps$mat;
  var keypadModeNext = _next$pluginProps$mat.keypadMode,
      controlledKeypadModeNext = _next$pluginProps$mat.controlledKeypadMode;
  var keypadModeChanged = keypadMode !== keypadModeNext;
  var controlledKeypadModeChanged = controlledKeypadMode !== controlledKeypadModeNext;
  var equal = node.equals(nodeNext);
  return equal && !keypadModeChanged && !controlledKeypadModeChanged;
});

exports.CustomToolbarComp = CustomToolbarComp;
CustomToolbarComp.propTypes = {
  node: _slatePropTypes["default"].node.isRequired,
  value: _slatePropTypes["default"].value,
  onToolbarDone: _propTypes["default"].func,
  onDataChange: _propTypes["default"].func,
  onFocus: _propTypes["default"].func,
  onClick: _propTypes["default"].func,
  onBlur: _propTypes["default"].func
};

function MathPlugin(opts) {
  MathPlugin.mathMlOptions = {
    mmlOutput: opts.mmlOutput,
    mmlEditing: opts.mmlEditing
  };
  return {
    name: 'math',
    toolbar: {
      icon: /*#__PURE__*/_react["default"].createElement(_Functions["default"], null),
      onClick: function onClick(value, onChange) {
        log('[insertMath]');
        var math = inlineMath();
        var change = value.change().insertInline(math);
        onChange(change);
      },
      supports: function supports(node) {
        return node && node.object === 'inline' && node.type === 'math';
      },

      /**
       * Return a react component function
       * @param node {Slate.Node}
       * @param value {Slate.Value}
       * @param onDone {(change?: Slate.Change, finishEditing :boolea) => void} - a function to call once the toolbar
       *   has made any changes, call with the node.key and a data object.
       */
      CustomToolbarComp: CustomToolbarComp
    },
    schema: {
      document: {
        match: [{
          type: 'math'
        }]
      }
    },
    pluginStyles: function pluginStyles(node, parentNode, p) {
      if (p) {
        return {
          position: 'absolute',
          top: 'initial'
        };
      }
    },
    renderNode: function renderNode(props) {
      if (props.node.type === 'math') {
        log('[renderNode]: data:', props.node.data);
        return /*#__PURE__*/_react["default"].createElement(_mathToolbar.MathPreview, props);
      }
      /**
       * Here for rendering mathml content
       */


      if (props.node.type === 'mathml') {
        var html = props.node.data.get('html');
        return /*#__PURE__*/_react["default"].createElement("span", (0, _extends2["default"])({}, props.attributes, {
          dangerouslySetInnerHTML: {
            __html: html
          }
        }));
      }
    }
  };
}

MathPlugin.ROUND_BRACKETS = 'round_brackets';
MathPlugin.SQUARE_BRACKETS = 'square_brackets';
MathPlugin.DOLLAR = 'dollar';
MathPlugin.DOUBLE_DOLLAR = 'double_dollar';
MathPlugin.mathMlOptions = {};
MathPlugin.propTypes = {
  attributes: _propTypes["default"].object,
  node: _slatePropTypes["default"].node
};

var inlineMath = function inlineMath() {
  return _slate.Inline.create({
    object: 'inline',
    type: 'math',
    isVoid: true,
    data: {
      latex: ''
    }
  });
};

exports.inlineMath = inlineMath;

var htmlDecode = function htmlDecode(input) {
  var doc = new DOMParser().parseFromString(input, 'text/html');
  return doc.documentElement.textContent;
};

var getTagName = function getTagName(el) {
  return (el && el.tagName || '').toLowerCase();
};
/**
 * Makes sure that strings that contain stuff like:
 * x<y are not transformed into x by the DOMParser because it thinks
 * that <y is the start of a dom element tag
 * @param input
 * @returns {*}
 */


var lessThanHandling = function lessThanHandling(input) {
  var arrowSplit = input.split('<'); // if we don't have at least 2 characters there's no point in checking

  if (input.length > 2) {
    return arrowSplit.reduce(function (st, part) {
      /*
       We check if this element resulted is:
       div - continuation of a beginning of a HTML element
       /div - closing of a HTML tag
       br/> - beginning and closing of a html TAG
       */
      if (part.match(/<[a-zA-Z/][\s\S]*>/gi)) {
        return "".concat(st).concat(st ? '<' : '').concat(part);
      }

      return "".concat(st).concat(st ? '&lt;' : '').concat(part);
    }, '');
  }

  return input;
};

function fixLatexExpression(latexInput) {
  // for some reason, mmlToLatex parses () incorrectly - or at least in a way that our interpreter can not use them
  // Replace '\\left.' and '\\right.' with an empty string
  return latexInput.replace(/\\left\.\s*|\\right\.\s*/g, '');
}

var serialization = {
  deserialize: function deserialize(el) {
    var tagName = getTagName(el);
    /**
     * This is used for when there's a wrapper over the mathml element.
     * Because of this slate rule: "Only allow block nodes or inline and text nodes in blocks."
     * The element that contains only the mathml is removed (along with the math) because it has
     * an inline child and the block is of type block
     * This is for legacy content only since our math rendering is valid for the core slate rules
     */

    var hasMathChild = _serialization.BLOCK_TAGS[tagName] && el.childNodes.length === 1 && getTagName(el.firstChild) === 'math';
    log('[deserialize] name: ', tagName);
    /**
     * This is here in order to be able to render mathml content
     */

    if (tagName === 'math' || el.dataset && el.dataset.type === 'mathml' || hasMathChild) {
      var newHtml = hasMathChild ? el.innerHTML : el.outerHTML;

      if (MathPlugin.mathMlOptions.mmlEditing) {
        // todo fix this in mathml-to-latex
        var htmlWithRemovedSpaces = newHtml.replaceAll("&nbsp;", " ");
        var htmlToUse = (0, _mathRendering.mmlToLatex)(htmlWithRemovedSpaces);
        var latex = htmlDecode(htmlToUse); // todo fix this in mathml-to-latex

        var correctedLatex = fixLatexExpression(latex);

        var _unWrapMath = (0, _mathRendering.unWrapMath)(correctedLatex),
            unwrapped = _unWrapMath.unwrapped,
            wrapType = _unWrapMath.wrapType;

        return {
          object: 'inline',
          type: 'math',
          isVoid: true,
          nodes: [],
          data: {
            latex: unwrapped,
            wrapper: wrapType
          }
        };
      }

      return {
        object: 'inline',
        isVoid: true,
        type: 'mathml',
        data: {
          html: newHtml
        }
      };
    }

    if (el.nodeType === TEXT_NODE) {
      return;
    }

    if (tagName !== 'span') {
      return;
    }

    var hasLatex = el.hasAttribute('data-latex') || el.hasAttribute('latex');

    if (hasLatex) {
      var _latex = htmlDecode(el.innerHTML);

      var _unWrapMath2 = (0, _mathRendering.unWrapMath)(_latex),
          _unwrapped = _unWrapMath2.unwrapped,
          _wrapType = _unWrapMath2.wrapType;

      log('[deserialize]: noBrackets: ', _unwrapped, _wrapType);
      return {
        object: 'inline',
        type: 'math',
        isVoid: true,
        nodes: [],
        data: {
          latex: _unwrapped,
          wrapper: _wrapType
        }
      };
    }
  },
  serialize: function serialize(object) {
    if (object.type === 'math') {
      var l = object.data.get('latex');
      var wrapper = object.data.get('wrapper');
      log('[serialize] latex: ', l);
      var decoded = htmlDecode(lessThanHandling(l));

      if (MathPlugin.mathMlOptions.mmlOutput) {
        var res = (0, _mathRendering.renderMath)("<span data-latex=\"\" data-raw=\"".concat(decoded, "\">").concat((0, _mathRendering.wrapMath)(decoded, wrapper), "</span>"));
        var newLatex = (0, _mathRendering.mmlToLatex)(res); // we need to remove all the spaces from the latex to be able to compare it

        var strippedL = l.replace(/\s/g, '');
        var strippedNewL = newLatex.replace(/\s/g, ''); // we check if the latex keeps his form after being converted to mathml and back to latex
        // if it does we can safely convert it to mathml

        if (!(0, _isEqual["default"])(strippedL, strippedNewL)) {
          var correctedLatex = fixLatexExpression(newLatex); // As George requested in PD-3167, I will set the new mathML anyway, and also log differences
          // if it doesn't we keep the latex version

          console.log('This latex can not be safely converted to mathml so we will keep the latex version!!!', {
            initialLatex: l,
            newLatex: newLatex,
            correctedLatex: correctedLatex,
            mathML: res
          });
        }

        return /*#__PURE__*/_react["default"].createElement("span", {
          "data-type": "mathml",
          dangerouslySetInnerHTML: {
            __html: res
          }
        });
      }

      return /*#__PURE__*/_react["default"].createElement("span", {
        "data-latex": "",
        "data-raw": decoded
      }, (0, _mathRendering.wrapMath)(decoded, wrapper));
    }
    /**
     * Here for rendering mathml content
     */


    if (object.type === 'mathml') {
      var html = object.data.get('html');
      return /*#__PURE__*/_react["default"].createElement("span", {
        "data-type": "mathml",
        dangerouslySetInnerHTML: {
          __html: html
        }
      });
    }
  }
};
exports.serialization = serialization;
//# sourceMappingURL=index.js.map