<template>
  <div class="v-editor">
    <VMenu class="v-editor-head" />
    <div
      :class="['v-editor-container', { 'is-focus': isFocus }]"
      @click="containerClick"
    >
      <div ref="editorMain" class="v-editor-main">
        <div
          :id="'ved' + _uid"
          ref="editor"
          :class="['v-editor-body', `v-editor-body-${_uid}`]"
          :data-placeholder="placeholder"
          contenteditable
          :style="styles"
          @input="emitJSON"
          @focus="isFocus = true"
          @blur="editorBlur"
        ></div>
      </div>
    </div>
  </div>
</template>

<script>
import VMenu from "./components/VMenu.vue";
import { renderRichText } from "./renderJSON";
import { toJSON } from "./toJSON";
import {
  IMAGE_EXCEED_SIZE_AS_ATTACHMENT,
  JSON_EXCEED_SIZE_AS_ATTACHMENT,
  MAX_AUDIO_SIZE,
  MAX_IMAGE_SIZE,
  MAX_JSON_SIZE,
} from "./constants";
import { pasteHandle } from "./clipboard";
import { answerPointRebuild } from "./components/answerPoint";
import timeMixin from "@/mixins/timeMixin";

export default {
  name: "VEditor",
  components: {
    VMenu,
  },
  mixins: [timeMixin],
  props: {
    placeholder: { type: String, default: "请输入..." },
    value: {
      type: [String, Object],
      // 要么为null，要么一定要遵循结构 body.sections[]
      // const EMPTY_RICH_TEXT
      default: () => {
        return { sections: [] };
      },
    },
    styles: { type: String, default: "" },
    folder: { type: String, default: "" },
    enableAnswerPoint: { type: Boolean, default: false },
    enableFormula: { type: Boolean, default: true },
    enableAudio: { type: Boolean, default: true },
    maxAudioSize: { type: Number, default: MAX_AUDIO_SIZE, required: false },
    maxImageSize: { type: Number, default: MAX_IMAGE_SIZE, required: false },
    imageExceedSizeAsAttachment: {
      type: Number,
      default: IMAGE_EXCEED_SIZE_AS_ATTACHMENT,
      required: false,
    },
    jsonExceedSizeAsAttachment: {
      type: Number,
      default: JSON_EXCEED_SIZE_AS_ATTACHMENT,
      required: false,
    },
    maxJsonSize: { type: Number, default: MAX_JSON_SIZE, required: false },
    emitType: {
      type: String,
      default: "json",
    },
  },
  data() {
    return {
      isFocus: false,
    };
  },
  watch: {
    value(val, oldVal) {
      if (val !== oldVal) {
        if (this.$refs.editor !== document.activeElement) {
          this.initData();
        }
      }
    },
  },
  mounted() {
    this.initData();
    this.$refs.editor.addEventListener("paste", pasteHandle.bind(this));
    this.$refs.editor.addEventListener("click", this.clickEventHandle);
  },
  beforeDestroy() {
    this.clearSetTs();
  },
  methods: {
    initData() {
      if (this.emitType === "html") {
        this.$refs.editor.innerHTML = this.value;
      } else {
        const content = this.value || { sections: [] };
        renderRichText(content, this.$refs.editor, false);
      }
    },
    emitJSON($event) {
      if (!this.$refs.editor.contentEditable) {
        // 不是出于contentEditable则不更新
        return;
      }

      if (this.enableAnswerPoint) answerPointRebuild.bind(this)($event);

      if (this.emitType === "html") {
        const content = this.$refs.editor.innerHTML;
        this.$emit("input", content);
        this.$emit("change", content);
        return;
      }

      this.clearSetTs();
      // 延迟触发input任务，避免频繁触发，同时也等待图片渲染，方便获取图片显示尺寸
      this.addSetTime(() => {
        // console.log("input:" + Math.random());
        this.inputDelaying = false;
        const json = toJSON(this.$refs.editor);
        this.$emit("input", json);
        this.$emit("change", json);
        // this.$emit("on-result", json);
      }, 200);
    },
    emitJsonStrict() {
      if (this.emitType === "html") {
        const content = this.$refs.editor.innerHTML;
        this.$emit("input", content);
        this.$emit("change", content);
        return;
      }

      const json = toJSON(this.$refs.editor);
      this.$emit("input", json);
      this.$emit("change", json);
    },
    containerClick() {
      this.$refs.editor.focus();
      this.isFocus = true;
    },
    // image resize
    clickEventHandle(e) {
      const el = e.target;
      if (el.tagName && el.tagName === "IMG") {
        e.preventDefault();
        e.stopPropagation();

        if (el.dataset.latex) {
          const selection = window.getSelection();
          selection.removeAllRanges();
          const range = document.createRange();
          range.selectNode(el);
          selection.addRange(range);
        }

        this.clearActiveImg();
        el.className = `${el.className} is-active`;
        this.activeResize(el);
        return;
      }
    },
    clearActiveImg() {
      this.$refs.editor.querySelectorAll("img").forEach((imgDom) => {
        if (!imgDom.className.indexOf("is-active")) return;
        let names = imgDom.className
          .split(" ")
          .filter((item) => item && item !== "is-active");

        imgDom.className = names.join(" ");
      });
    },
    clearResizeDom() {
      let doms = this.$refs.editorMain.querySelectorAll(".resize-elem");
      if (!doms.length) return;
      doms.forEach((dom) => {
        dom.parentNode.removeChild(dom);
      });
    },
    activeResize(imgDom) {
      const _this = this;
      let _x = 0;
      let { clientWidth, naturalWidth, naturalHeight } = imgDom;
      let resizeDom = null;

      removeResizeDom();
      createResizeDom();

      function createResizeDom() {
        resizeDom = document.createElement("div");
        resizeDom.className = "resize-elem";
        resizeDom.style.position = "absolute";
        resizeDom.style.width = "16px";
        resizeDom.style.height = "16px";
        resizeDom.style.backgroundColor = "#fff";
        resizeDom.style.backgroundImage =
          "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAX1JREFUWEftlTFLw1AQx+/S2dEvUbe8g05CHbroKm7iIHQW1LlxraAfQHDoKDjqJNhBhJJ32SzOrvoRSv/yJIEaoq8kxi7JFAi534//e3fHtOKHV8ynRqBJoEnAm4Ax5jFt1Xdmnqbvb8z8Gsfxc9U2XlagWwAaq+rWfwjcEdF2DjQNgmAvjuOX2gVEZAjgdBHEzA/W2l5VuPvfewQiEgEY5GHMfGut3a0q4RUIw/CImS+JaArghpmjDMrM19bawyoSXgER6c3n86jVavXdmYdhGDHzYiIjVT0oK+EVKCqclwAQJUlyVkailIADFdyNb23Z6XTak8kkmxs/upUWSCX2AYwWqn9JiMjApUJE66r68VsylQRc4YI7MSaibHBtqupTrQKuuDHmhIjO8yAA/SRJrmoXSCXczsiP7AtVPa5doOAYMua9qu7UKiAiXQDZxsyzvAur8iXMiK7tZrPZBoB2EAQGwJr75tuYfyZQZggttYzKFl72vyaBJoEmgU/j14shnky4jQAAAABJRU5ErkJggg==')";
        resizeDom.style.backgroundSize = "100% 100%";
        resizeDom.style.borderTop = "1px solid #1886fe";
        resizeDom.style.borderLeft = "1px solid #1886fe";
        resizeDom.style.cursor = "nwse-resize";
        resizeDom.style.zIndex = 9999;
        _this.$refs.editorMain.appendChild(resizeDom);

        resizeResizeDom();
      }

      function removeResizeDom() {
        _this.clearResizeDom();
        resizeDom = null;
      }

      function resizeResizeDom() {
        const imgPos = imgDom.getBoundingClientRect();
        const mainPos = _this.$refs.editorMain.getBoundingClientRect();
        let relativeLeft = imgPos.x - mainPos.x;
        let relativeTop = imgPos.y - mainPos.y;
        resizeDom.style.left = imgPos.width + relativeLeft - 16 + "px";
        resizeDom.style.top = imgPos.height + relativeTop - 16 + "px";
      }

      function getValidSize(originSize, size) {
        if (size <= 10) return Math.min(originSize, 10);
        if (size >= 2 * originSize) return 2 * originSize;
        return size;
      }
      // 只允许鼠标左键触发
      function moveHandle(e) {
        if (e.button !== 0) return;
        e.preventDefault();
        e.stopPropagation();

        let width = getValidSize(naturalWidth, e.pageX - _x + clientWidth);
        let height = (width * naturalHeight) / naturalWidth;

        imgDom.style.width = width + "px";
        imgDom.style.height = height + "px";

        resizeResizeDom();
      }

      function upHandle(e) {
        _this.emitJsonStrict();

        if (e.button !== 0) return;
        e.preventDefault();
        e.stopPropagation();

        document.removeEventListener("mousemove", moveHandle);
        document.removeEventListener("mouseup", upHandle);
      }

      resizeDom.addEventListener("mousedown", function (e) {
        if (e.button !== 0) return;
        _x = e.pageX;
        e.preventDefault();
        e.stopPropagation();

        document.addEventListener("mousemove", moveHandle);
        document.addEventListener("mouseup", upHandle);
      });
    },
    editorBlur() {
      this.isFocus = false;
      this.clearActiveImg();
      this.clearResizeDom();
    },
  },
};
</script>

<style>
.v-editor {
  position: relative;
  line-height: 20px;
}
.v-editor-head {
  position: absolute;
  left: 2px;
  right: 2px;
  background-color: #fff;
  top: 2px;
  z-index: 9;
  border-bottom: 1px solid #f0f0f0;
}

.v-editor-container {
  border: 1px solid #e4e7ed;
  border-radius: 5px;
  min-height: 100px;
  max-height: 300px;
  padding: 26px 0 0 0;
  overflow: auto;
  white-space: pre-wrap;
}
.v-editor-container.is-focus {
  border-color: #1886fe;
}
.v-editor-main {
  position: relative;
  min-height: 100%;
}

.v-editor-body {
  outline: none;
  min-height: 100%;
  padding: 8px;
}
.v-editor-body[contenteditable="true"]:empty:not(:focus)::before {
  content: attr(data-placeholder);
  color: grey;
}

.v-editor-body div {
  min-height: 18px;
  line-height: 20px;
}

.v-editor-body img {
  max-width: 100%;
}
.v-editor-body img[data-is-answer-point] {
  max-height: 16px;
  display: inline-block;
  vertical-align: text-top;
  border-bottom: 1px solid #000;
}

.v-editor-body img.audio {
  height: 16px;
  padding: 0 2px;
  display: inline-block;
  margin-bottom: 4px;
}
.v-editor-body img[data-is-image] {
  /* max-height: 42px; */
  display: inline-block;
}
.v-editor-body img[data-is-image].is-active {
  box-shadow: 0 0 1px 1px #1886fe;
}

.v-editor-body audio {
  height: 16px;
  width: 180px;
  display: inline-block;
  margin-bottom: -4px;
}

.v-editor-body ::-webkit-media-controls-mute-button {
  display: none !important;
}

.v-editor-body ::-webkit-media-controls-volume-slider {
  display: none !important;
}

.v-editor-body b {
  font-weight: bold;
}
.v-editor-body i {
  font-style: italic;
}
.v-editor-body u {
  text-decoration: underline;
}
</style>
