"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("../../../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 _blockTags = require("../../block-tags");

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: {
      ariaLabel: '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 arrowHandlingCase = function arrowHandlingCase(input) {
  /*
  If we have a < character followed by a letter
  we make sure to replace it with a &lt; sign instead
   */
  return input.replace(/<([a-zA-Z]*)/g, '&lt;$1');
};

function replaceLeftRight(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 convertLatexToMathMl = function convertLatexToMathMl(_ref4) {
  var latex = _ref4.latex,
      decoded = _ref4.decoded,
      wrapper = _ref4.wrapper;

  var removeEmptyMos = function removeEmptyMos(mmlFromLatex) {
    // Regular expression to match <mo>&#x2061;</mo> and <mo ...>&#x2061;</mo>, which get added when using log with base
    // not sure why they get added, but they add an extra space which is not needed
    var regex = /<mo(?: [^>]*)?>&#x2061;<\/mo>/g; // Replace all occurrences of the matched patterns

    return mmlFromLatex.replace(regex, '');
  };

  var handled = arrowHandlingCase(decoded);
  var latexToConvert = "<span data-latex=\"\" data-raw=\"".concat(handled, "\">").concat((0, _mathRendering.wrapMath)(handled, wrapper), "</span>"); // use math rendering (MathJax) to convert latex to mathMl

  var mathMlFromLatex = (0, _mathRendering.renderMath)(latexToConvert, {
    skipWaitForMathRenderingLib: true
  }); // if renderMath returned the exact same string that we sent, it just means that the conversion could not be done

  var conversionDidNotWork = (0, _isEqual["default"])(latexToConvert, mathMlFromLatex);
  mathMlFromLatex = removeEmptyMos(mathMlFromLatex); // we convert resulted mathml to latex to check if the resulted mathMl can be converted back to latex if user wants to edit it later

  var latexFromMathMl = mathMlFromLatex ? (0, _mathRendering.mmlToLatex)(mathMlFromLatex) : ''; // we need to remove all the spaces from the latex to be able to compare it

  var strippedL = latex.replace(/\s/g, '');
  var strippedNewL = latexFromMathMl.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 = replaceLeftRight(latexFromMathMl); // 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
    // eslint-disable-next-line no-console

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

  return {
    mathMlFromLatex: mathMlFromLatex,
    conversionDidNotWork: conversionDidNotWork
  };
};

var convertMathMlToLatex = function convertMathMlToLatex(mathMl) {
  var htmlWithRemovedSpaces = mathMl.replaceAll('&nbsp;', ' ');
  var htmlToUse = (0, _mathRendering.mmlToLatex)(htmlWithRemovedSpaces);
  var latex = htmlDecode(htmlToUse); // todo fix this in mathml-to-latex

  return replaceLeftRight(latex);
};

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 = _blockTags.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 mathMl = hasMathChild ? el.innerHTML : el.outerHTML;

      if (MathPlugin.mathMlOptions.mmlEditing) {
        var _unWrapMath = (0, _mathRendering.unWrapMath)(convertMathMlToLatex(mathMl)),
            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: mathMl
        }
      };
    }

    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 latex = object.data.get('latex');
      var wrapper = object.data.get('wrapper');
      var decoded = htmlDecode(arrowHandlingCase(latex));
      log('[serialize] latex: ', latex);

      if (MathPlugin.mathMlOptions.mmlOutput) {
        var _convertLatexToMathMl = convertLatexToMathMl({
          latex: latex,
          decoded: decoded,
          wrapper: wrapper
        }),
            mathMlFromLatex = _convertLatexToMathMl.mathMlFromLatex,
            conversionDidNotWork = _convertLatexToMathMl.conversionDidNotWork;

        if (conversionDidNotWork) {
          // this means we could not convert latex to mathMl, so just return the latex version,
          // same as we would do if mmlOutput would not be enabled
          return /*#__PURE__*/_react["default"].createElement("span", {
            "data-latex": "",
            "data-raw": decoded
          }, (0, _mathRendering.wrapMath)(decoded, wrapper));
        }

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

      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