<template>
  <el-dialog
    class="formula-editor-dialog"
    :visible.sync="modalIsShow"
    title="公式编辑"
    top="20px"
    width="1000px"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    append-to-body
    @opened="openedHandle"
    @close="closeHandle"
  >
    <div class="formula-editor">
      <FormulaMenu @select-menu="selectMenuHandle" />
      <div
        ref="editorBodyRef"
        :class="['formula-input', { 'is-focus': isFocus }]"
      ></div>
      <div class="formula-preview">
        <h3>效果预览</h3>
        <div ref="previewBodyRef" class="preview-body"></div>
      </div>
    </div>

    <div slot="footer">
      <el-button size="small" type="primary" @click="confirm">确认</el-button>
      <el-button size="small" @click="cancel">取消</el-button>
    </div>
  </el-dialog>
</template>

<script>
import { debounce } from "lodash";
import FormulaMenu from "./FormulaMenu.vue";
import { renderLatexToSVG, svgToImage } from "./mathjax";
import editorStyleCont from "./editorStyle";
import { getInputContent } from "./input";

export default {
  name: "FormulaEditor",
  components: { FormulaMenu },
  props: {
    placeholder: {
      type: String,
      default: "请输入...",
    },
    content: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      modalIsShow: false,
      inputLatex: "",
      inited: false,
      editorBodyContainer: null,
      editorBodyFrame: null,
      isFocus: false,
    };
  },
  methods: {
    cancel() {
      this.modalIsShow = false;
    },
    open() {
      this.modalIsShow = true;
    },
    openedHandle() {
      if (this.inited) return;
      this.initEditorBody();
      this.selectMenuHandle({ output: this.content });
    },
    closeHandle() {
      this.inited = false;
      this.$refs.editorBodyRef.innerHTML = "";
      this.$refs.previewBodyRef.innerHTML = "";
    },
    initEditorBody() {
      const editorBodyFrame = document.createElement("iframe");
      editorBodyFrame.setAttribute("frameborder", 0);
      editorBodyFrame.style = `width:100%;height:100%;`;
      this.$refs.editorBodyRef.appendChild(editorBodyFrame);
      const editorStyles = document.createElement("style");
      editorStyles.innerHTML = editorStyleCont;
      editorBodyFrame.contentDocument.head.appendChild(editorStyles);
      const editorBodyContainer = document.createElement("div");
      editorBodyContainer.className = "editor-container";
      editorBodyFrame.contentDocument.body.appendChild(editorBodyContainer);
      editorBodyContainer.contentEditable = true;
      editorBodyContainer.dataset.placeholder = this.placeholder;
      this.editorBodyContainer = editorBodyContainer;
      this.editorBodyFrame = editorBodyFrame;

      this.inited = true;

      this.registEvent();
    },
    registEvent() {
      this.editorBodyContainer.addEventListener("input", this.inputChange);
      this.editorBodyContainer.addEventListener(
        "paste",
        this.editorBodyPasteEventHanle
      );
      this.editorBodyContainer.addEventListener(
        "focus",
        this.editorBodyFocusEventHanle
      );
      this.editorBodyContainer.addEventListener(
        "blur",
        this.editorBodyBlurEventHanle
      );
    },
    // event
    editorBodyFocusEventHanle(e) {
      e.preventDefault();
      e.stopPropagation();
      this.isFocus = true;
    },
    editorBodyBlurEventHanle(e) {
      e.preventDefault();
      e.stopPropagation();
      this.isFocus = false;
    },
    editorBodyPasteEventHanle(e) {
      e.preventDefault();
      e.stopPropagation();

      const clipboardData = e.clipboardData || window.clipboardData;
      const plainText = clipboardData.getData("text/plain");
      this.selectMenuHandle({ output: plainText });
    },
    // menu
    selectMenuHandle(data) {
      const selection = this.editorBodyFrame.contentWindow.getSelection();
      if (!selection.focusNode) this.editorBodyContainer.focus();
      this.editorBodyFrame.contentDocument.execCommand(
        "insertText",
        false,
        data.output
      );
      this.toParse();
    },
    inputChange: debounce(function () {
      this.toParse();
    }, 300),
    toParse() {
      try {
        this.inputLatex = getInputContent(this.editorBodyContainer);
        const svg = renderLatexToSVG(this.inputLatex);
        this.$refs.previewBodyRef.innerHTML = svg;
      } catch (error) {
        console.error(error);
        this.$message.error("解析错误！");
      }
    },
    async confirm() {
      if (!this.inputLatex) {
        this.$emit("confirm", { dataUrl: "", latex: "" });
        this.cancel();
        return;
      }

      const svdDom = this.$refs.previewBodyRef.querySelector("svg");
      const rate = 3;
      this.svgSize = {
        width: svdDom.clientWidth,
        height: svdDom.clientHeight,
      };

      const result = await svgToImage(
        svdDom.outerHTML,
        svdDom.clientWidth * rate,
        svdDom.clientHeight * rate
      ).catch((e) => {
        console.error(e);
      });

      if (!result) {
        this.$message.error("生成图片失败！");
        return;
      }

      this.$emit("confirm", {
        dataUrl: result,
        latex: this.inputLatex,
        param: {
          ...this.svgSize,
        },
      });
      this.cancel();
    },
  },
};
</script>
