diff --git a/main.go b/main.go
index e161cc8..a069520 100644
--- a/main.go
+++ b/main.go
@@ -9,6 +9,8 @@ import (
"mylomen_server/common/constant"
"mylomen_server/common/utils"
"mylomen_server/static"
+ "mylomen_server/static/css"
+ "mylomen_server/static/js"
"net/http"
"os"
"os/signal"
@@ -67,6 +69,8 @@ func main() {
//static
static.InitStaticGroup(e.Group("/v1/static/"))
+ css.InitCssGroup(e.Group("/css"))
+ js.InitJsGroup(e.Group("/js"))
//ai
apps.InitAiGroup(e.Group("/v1/ai/", func(next echo.HandlerFunc) echo.HandlerFunc {
diff --git a/static/css/init.go b/static/css/init.go
new file mode 100644
index 0000000..55ce628
--- /dev/null
+++ b/static/css/init.go
@@ -0,0 +1,31 @@
+package css
+
+import (
+ "embed"
+ "os"
+)
+import _ "embed"
+
+//go:embed *.css
+var cssList embed.FS
+
+var cssMap = initCssMap()
+
+func initCssMap() map[string][]byte {
+ list, err := cssList.ReadDir(".")
+ if err != nil {
+ os.Exit(-1)
+ return nil
+ }
+
+ var dataMap = make(map[string][]byte, len(list))
+ for _, file := range list {
+ //读取配置文件
+ data, err := cssList.ReadFile(file.Name())
+ if err == nil {
+ dataMap[file.Name()] = data
+ }
+ }
+
+ return dataMap
+}
diff --git a/static/css/myEdit.css b/static/css/myEdit.css
new file mode 100644
index 0000000..fc321e4
--- /dev/null
+++ b/static/css/myEdit.css
@@ -0,0 +1,141 @@
+/* 字体 */
+:root {
+ /* font-size: calc(0.5em + 1vw); */
+ font-size: 62.5%;
+}
+
+/* style sheet for "A4" printing */
+@media print and (width: 21cm) and (height: 29.7cm) {
+ @page {
+ margin: 3cm;
+ }
+}
+
+/* style sheet for "letter" printing */
+@media print and (width: 8.5in) and (height: 11in) {
+ @page {
+ margin: 1in;
+ }
+}
+
+* {
+ /* margin: 1px 2px;
+ padding: 1px 2px; */
+ font-family: Roboto-Regular, PingFang SC, SF Pro SC, SF Pro Text, SF Pro Icons, Helvetica Neue, Roboto, Helvetica, Arial, sans-serif;
+ outline: none;
+ /* box-sizing: border-box; */
+}
+
+body {
+ justify-content: center;
+ /*align-items: center;*/
+}
+
+header {
+ position: relative;
+ top: 0;
+ /* height: 8rem; */
+ z-index: 9999;
+ left: 0;
+ right: 0;
+ width: 100%;
+}
+
+#noteshare {
+ width: 90%;
+ /* width: 21cm; */
+ min-height: 10rem;
+ /* font-size: 1.5rem; */
+
+ /*border: 1px red solid;*/
+ margin: auto auto;
+}
+
+#noteshare p {
+ /*border: 1px rgb(248, 245, 245) solid;*/
+ margin: 0 0;
+ padding: 0 0;
+ /* border: none; */
+}
+
+
+#testInput {
+ width: 60%;
+ min-height: 10rem;
+ border: 1px rgb(0, 140, 255) solid;
+ margin: 20px auto;
+ justify-content: center;
+}
+
+::selection {
+ color: antiquewhite;
+ background-color: cadetblue;
+ text-shadow: #00a9ff;
+}
+
+.my-divider-item {
+ background-color: lightgray;
+ width: 1px;
+ height: 2rem;
+ margin: 0.2rem 1.6rem;
+}
+
+.childStyleStrong {
+ font-weight: bold
+}
+
+.childStyleI {
+ font-style: italic;
+}
+
+.childStyleU {
+ text-decoration: underline;
+/ / 中划线 / / text-decoration: line-through;
+}
+
+.childStyleDel {
+ text-decoration: line-through;
+}
+
+.childStyleColor {
+ color: red;
+}
+
+
+.fixStylePosition {
+ display: none;
+ position: fixed;
+ z-index: 87;
+ top: 5rem;
+ left: 10rem;
+ width: auto;
+ height: 2.4rem;
+
+ padding: 0.8rem 0.8rem;
+ /*padding: 0.6rem 1rem 0.6rem 1rem;*/
+ justify-content: center;
+ align-items: center;
+ align-content: center;
+ border-radius: 0.8rem;
+ border: 1px #dee0e3 solid;
+ background-color: rgb(255, 255, 255);
+ /*box-shadow: 0.1rem 0.1rem 0.1rem 0.1rem lightgrey;*/
+ box-shadow: 0 0.4rem 0.8rem rgba(31, 35, 41, 0.1);
+}
+
+.fixStyleOut {
+ /*border: 1px blue solid;*/
+ margin: 0 0;
+ width: auto;
+ height: 2rem;
+ padding: 0.5rem 0.5rem;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ align-content: center;
+}
+
+.fixStyleInnerSpan {
+ margin: 0 1rem;
+}
diff --git a/static/css/static_router.go b/static/css/static_router.go
new file mode 100644
index 0000000..4c49af9
--- /dev/null
+++ b/static/css/static_router.go
@@ -0,0 +1,16 @@
+package css
+
+import (
+ "github.com/labstack/echo/v4"
+ "net/http"
+)
+
+func InitCssGroup(g *echo.Group) {
+
+ g.GET("/:name", func(c echo.Context) error {
+ cssName := c.Param("name")
+ c.Response().Header().Set("Cache-Control", "max-age=1")
+ data, _ := cssMap[cssName]
+ return c.Blob(http.StatusOK, "text/css; charset=utf-8", data)
+ })
+}
diff --git a/static/js/init.go b/static/js/init.go
new file mode 100644
index 0000000..d02753a
--- /dev/null
+++ b/static/js/init.go
@@ -0,0 +1,78 @@
+package js
+
+import (
+ "embed"
+ "io/fs"
+ "os"
+)
+import _ "embed"
+
+//go:embed */*/*.js
+var jsList embed.FS
+
+//go:embed lib/main.js
+var jsMain embed.FS
+
+var jsMap = initJsMap()
+
+func initJsMap() map[string][]byte {
+
+ var dataMap = make(map[string][]byte)
+
+ btys, _ := jsMain.ReadFile("lib/main.js")
+ dataMap["lib/main.js"] = btys
+
+ list := initJsMapInner("lib/biz")
+ if list != nil && len(list) > 0 {
+ for _, file := range list {
+ //读取配置文件
+ data, err := jsList.ReadFile("lib/biz/" + file.Name())
+ if err == nil {
+ dataMap["lib/biz/"+file.Name()] = data
+ }
+ }
+ }
+
+ list = initJsMapInner("lib/common")
+ if list != nil && len(list) > 0 {
+ for _, file := range list {
+ //读取配置文件
+ data, err := jsList.ReadFile("lib/common/" + file.Name())
+ if err == nil {
+ dataMap["lib/common/"+file.Name()] = data
+ }
+ }
+ }
+ list = initJsMapInner("lib/event")
+ if list != nil && len(list) > 0 {
+ for _, file := range list {
+ //读取配置文件
+ data, err := jsList.ReadFile("lib/event/" + file.Name())
+ if err == nil {
+ dataMap["lib/event/"+file.Name()] = data
+ }
+ }
+ }
+ list = initJsMapInner("lib/model")
+ if list != nil && len(list) > 0 {
+ for _, file := range list {
+ //读取配置文件
+ data, err := jsList.ReadFile("lib/model/" + file.Name())
+ if err == nil {
+ dataMap["lib/model/"+file.Name()] = data
+ }
+ }
+ }
+
+ return dataMap
+}
+
+func initJsMapInner(path string) []fs.DirEntry {
+ list, err := jsList.ReadDir(path)
+ if err != nil {
+ os.Exit(-1)
+ return nil
+ }
+
+ return list
+}
diff --git a/static/js/lib/biz/MyBiz.js b/static/js/lib/biz/MyBiz.js
new file mode 100644
index 0000000..b2debbb
--- /dev/null
+++ b/static/js/lib/biz/MyBiz.js
@@ -0,0 +1,481 @@
+"use strict";
+import {MyRecovery} from "../model/MyRecovery.js";
+import {MyDocItem} from "../model/MyDocItem.js";
+import {MyMapItem} from "../model/MyMapItem.js";
+
+
+export class MyBiz {
+
+ constructor() {
+ /**
+ * 输入事件
+ */
+ window.myEdit.eventListener.RegisterEventHandle('input', window.myEdit.eventListener.InputListener);
+ window.myEdit.eventListener.RegisterEventHandle('compositionstart', window.myEdit.eventListener.CompositionstartListener);
+ window.myEdit.eventListener.RegisterEventHandle('compositionend', window.myEdit.eventListener.CompositionendListener);
+
+ // 初始化第一个输入框
+ let newParagraph = document.createElement("p");
+ newParagraph.setAttribute("contenteditable", "true");
+ let uuid = window.myEdit.utils.Uuid()
+ let curOrder = window.myEdit.ctx.incrementNumThenReturn();
+ newParagraph.setAttribute("data-id", uuid)
+ newParagraph.setAttribute("id", uuid)
+ newParagraph.setAttribute("data-order", curOrder)
+ newParagraph.onkeydown = window.myEdit.eventListener.KeydownListener
+ newParagraph.innerHTML = "
"
+ //添加一行
+ window.myEdit.utils.AddNewParagraph(newParagraph);
+ }
+
+ /**
+ * 根据类型名称解析 样式的 class 名称
+ * @param styleName
+ * @returns {string}
+ */
+ parseStyleName2ClassName(styleName) {
+ switch (styleName) {
+ case "b":
+ return "childStyleStrong";
+ case "i":
+ return "childStyleI";
+ case "u":
+ return "childStyleU";
+ case "c_red":
+ return "childStyleColor";
+ case "del":
+ return "childStyleDel"
+ }
+ }
+
+ /**
+ * 输入事件
+ * @param e
+ */
+ inputHandle(e) {
+ let curP = window.myEdit.utils.GetEventTarget(e);
+ this.updateText(curP)
+ }
+
+ /**
+ * 中文输入开始事件
+ * @param e
+ */
+ compositionstartHandle(e) {
+ // console.log("compositionstart")
+ window.myEdit.ctx.inCompositionEvent = true
+ }
+
+ /**
+ * 中文输入结束事件
+ * @param e
+ */
+ compositionendHandle(e) {
+ // console.log("compositionend")
+ let curP = window.myEdit.utils.GetEventTarget(e);
+ this.updateText(curP)
+ //中文输入结束
+ window.myEdit.ctx.inCompositionEvent = false
+ }
+
+
+ /**
+ * 撤销事件
+ * @param event
+ */
+ cancelHandle(event) {
+ console.log('触发ctrl + Z 事件', event.target)
+ if (window.myEdit.ctx.latestOpDoc !== undefined && window.myEdit.ctx.latestOpDoc !== null) {
+ //恢复
+ window.myEdit.ctx.latestOpDoc.recovery();
+ //测试展示
+ window.myEdit.ctx.showTestText()
+ //阻止事件
+ window.myEdit.utils.ProhibitDefaultEvent(event);
+ }
+
+ window.myEdit.ctx.latestOpDoc = null
+ }
+
+ /**
+ * 删除事件
+ * @param event
+ */
+ deleteHandle(event) {
+ let curP = window.myEdit.utils.GetEventTarget(event);
+ let cNo = parseInt(curP.getAttribute("data-order"))
+
+ //维护最近一次编辑的内容
+ if (window.myEdit.ctx.latestOpDoc === undefined
+ || window.myEdit.ctx.latestOpDoc === null
+ || window.myEdit.ctx.latestOpDoc.getData().getAttribute("data-id") !== curP.getAttribute("data-id")) {
+ window.myEdit.ctx.latestOpDoc = new MyRecovery(curP.cloneNode(true), function () {
+ let cNo = parseInt(this.data.getAttribute("data-order"))
+ console.log("恢复", this.data, cNo, " this: ", this)
+ if (cNo > 1) {
+ window.myEdit.utils.InsertAfter(this.data, document.querySelector("#noteshare p[data-order='" + (cNo - 1) + "']"))
+ } else {
+ //添加元素到首位 todo_xxx
+ // window.myEdit.ctx.MyRoot.insertBefore(this.data, window.myEdit.ctx.MyRoot.children[0])
+ window.myEdit.ctx.MyRoot.insertBefore(this.data, window.myEdit.ctx.MyRoot.children[0]);
+ }
+
+ // 恢复该元素展示
+ window.myEdit.ctx.MyDocMap.get(cNo).setHidden(false);
+ })
+ }
+
+
+ //如果是第一行
+ let previousSibling = curP.previousSibling
+ console.log(curP, previousSibling === undefined, previousSibling.id === undefined)
+ if (previousSibling === undefined || previousSibling.id === undefined) {
+ //显示用户的输入内容
+ window.myEdit.ctx.showTestText()
+ return
+ }
+
+
+ console.log('触发删除', curP.innerHTML, cNo)
+ let curS = window.myEdit.utils.GetSelection();
+ // console.log("当前内容: ", curP.innerHTML, " 当前选区 :", curS)
+ //处理前面没有内容,后面还有内容需要拼接到上层的场景
+ if ((curS.isCollapsed && curS.anchorOffset === 0) || curP.innerHTML === '
') {
+ let curNodeRetainHtml = curP.innerHTML
+ //阻止事件传递
+ window.myEdit.utils.ProhibitDefaultEvent(event);
+ //设置该元素隐藏
+ window.myEdit.ctx.MyDocMap.get(cNo).setHidden(true)
+ //删除当前元素
+ // curP.remove()
+ curP.innerHTML = "
"
+ let emptyRowNoList = window.myEdit.ctx.MyDocMap.get("emptyRowNoList");
+ if (emptyRowNoList === undefined || emptyRowNoList === null) {
+ emptyRowNoList = [];
+ window.myEdit.ctx.MyDocMap.set("emptyRowNoList", emptyRowNoList);
+ }
+ emptyRowNoList.push(cNo);
+ //拼接
+ if (curNodeRetainHtml !== '
') {
+ previousSibling.innerHTML = previousSibling.innerHTML + curNodeRetainHtml
+ }
+
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.getSelection().setPosition(previousSibling, 1);
+ }
+
+ //显示用户的输入内容
+ window.myEdit.ctx.showTestText();
+ }
+
+ /**
+ * 回车事件
+ * @param event
+ * @param onkeydownHandle
+ */
+ enterHandler(event) {
+ //阻止事件
+ window.myEdit.utils.ProhibitDefaultEvent(event);
+ //uuid
+ let uuid = window.myEdit.utils.Uuid();
+ //rowNo
+ let rowNo = 0;
+ let emptyRowNoList = window.myEdit.ctx.MyDocMap.get("emptyRowNoList");
+ console.log(emptyRowNoList);
+ if (emptyRowNoList === undefined || emptyRowNoList === null) {
+ rowNo = window.myEdit.ctx.incrementNumThenReturn();
+ //添加新元素
+ let newParagraph = document.createElement("p")
+ newParagraph.setAttribute("contenteditable", "true")
+ newParagraph.setAttribute("data-id", uuid)
+ newParagraph.setAttribute("id", uuid)
+ newParagraph.setAttribute("data-order", rowNo)
+ newParagraph.onkeydown = window.myEdit.eventListener.KeydownListener
+ newParagraph.innerHTML = "
"
+ window.myEdit.ctx.MyRoot.appendChild(newParagraph)
+ window.myEdit.ctx.MyDocMap.set(rowNo, new MyMapItem(uuid))
+
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.getSelection().setPosition(newParagraph, 0);
+ return
+ }
+
+ let sortArr = emptyRowNoList.sort((a, b) => a - b)
+ console.log(sortArr);
+ // 最小值
+ rowNo = sortArr[0];
+ let newParagraph = document.querySelector("#noteshare p[data-order='" + rowNo + "']");
+ newParagraph.innerHTML = "
";
+ window.myEdit.ctx.MyDocMap.get(rowNo).setHidden(false);
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.getSelection().setPosition(newParagraph, 0)
+ }
+
+
+ /**
+ * 更新文档
+ * @param {*} e
+ */
+ updateText(curP) {
+ if (window.myEdit.ctx.inCompositionEvent) {
+ return;
+ }
+
+ let myP = new MyDocItem(curP);
+ let cNo = myP.parseOrder();
+ let mapItem = window.myEdit.ctx.getMapItem(cNo)
+
+ //内容不变则不处理
+ let h5CurLen = curP.innerText.length
+ let myDocNodeLen = (mapItem && mapItem.getSource()) ? mapItem.getSource().length : 0
+ if (h5CurLen === myDocNodeLen) {
+ return
+ }
+
+ mapItem.setSource(curP.innerText)
+ // console.log("curPTx: ", curP.innerText, "MyDocMap : ", utils.MyDocMap)
+
+ //显示用户的输入内容
+ window.myEdit.ctx.showTestText()
+ }
+
+ /**
+ * 空格键处理
+ * @param {*} event
+ */
+ emptyKeyWorkHandler(event) {
+ let curP = window.myEdit.utils.GetEventTarget(event);
+ let myDocItem = new MyDocItem(curP);
+ let inputLength = curP.innerText.length
+ /**
+ * h1 ~ h6
+ */
+ if (curP.innerText.startsWith("#") && curP.innerHTML.endsWith(" ")) {
+ let curNo = myDocItem.parseOrder();
+ let mapNode = window.myEdit.ctx.MyDocMap.get(curNo);
+
+ // console.log(curP, " - ", curP.innerHTML, curP.innerHTML.startsWith("# "))
+ if (curP.innerHTML.startsWith("# ") || curP.innerHTML.startsWith("# ")) {
+ mapNode.getStyle().setNodeType("h1")
+ this.becomeAnotherElement(curP, "h1", onkeydownHandle)
+ } else if (curP.innerHTML.startsWith("## ")) {
+ mapNode.getStyle().setNodeType("h2")
+ this.becomeAnotherElement(curP, "h2", onkeydownHandle)
+ } else if (curP.innerHTML.startsWith("### ")) {
+ mapNode.getStyle().setNodeType("h3")
+ this.becomeAnotherElement(curP, "h3", onkeydownHandle)
+ } else if (curP.innerHTML.startsWith("#### ")) {
+ mapNode.getStyle().setNodeType("h4")
+ this.becomeAnotherElement(curP, "h4", onkeydownHandle)
+ } else if (curP.innerHTML.startsWith("##### ")) {
+ mapNode.getStyle().setNodeType("h5")
+ this.becomeAnotherElement(curP, "h5", onkeydownHandle)
+ } else {
+ mapNode.getStyle().setNodeType("h6")
+ this.becomeAnotherElement(curP, "h6", onkeydownHandle)
+ }
+ }
+
+ /**
+ * 无序列表效果
+ */
+ if (inputLength === 2 && curP.innerText.startsWith("-") && curP.innerHTML.endsWith(" ")) {
+ let curNo = myDocItem.parseOrder();
+ let mapNode = window.myEdit.ctx.MyDocMap.get(curNo);
+ mapNode.getStyle().setPreStyle("ul", true)
+
+ //clean
+ curP.innerHTML = ""
+ mapNode.setSource("")
+
+ //根据上一层级元素动态选择 todo
+ curP.setAttribute("style", "padding-left: 1rem;")
+ //新增元素
+ let newParagraph = document.createElement("span");
+ newParagraph.setAttribute("contenteditable", "false")
+ //∙ vs ∘
+ newParagraph.innerHTML = "∙ "
+ curP.append(newParagraph)
+
+ //添加一个选区
+ var selObj = window.myEdit.utils.GetSelection();
+ var rangeObj = document.createRange()
+ rangeObj.selectNode(curP)
+ selObj.addRange(rangeObj)
+
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.myEdit.utils.GetSelection().collapse(curP, true)
+ }
+
+ /**
+ * 有序列表效果
+ */
+ if (inputLength > 2 && inputLength <= 5 && isNum(curP.innerText.substring(0, inputLength - 2)) && curP.innerHTML.endsWith(". ")) {
+ let num = curP.innerText.substring(0, inputLength - 2)
+ console.log(curP.innerText, num)
+
+ let curNo = myDocItem.parseOrder();
+ let mapNode = window.myEdit.ctx.MyDocMap.get(curNo);
+ mapNode.getStyle().setPreStyle("ol", num)
+
+ //clean
+ curP.innerHTML = ""
+ mapNode.setSource("")
+ //todo
+ curP.setAttribute("style", "padding-left: 1rem;")
+ //新增元素
+ let newParagraph = document.createElement("span");
+ newParagraph.setAttribute("contenteditable", "false");
+ newParagraph.innerHTML = num + ". "
+ curP.append(newParagraph);
+
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.myEdit.utils.GetSelection().collapse(curP, true)
+ }
+
+ }
+
+
+ /**
+ * 变成另一个元素
+ * @param {*} elementName
+ */
+ becomeAnotherElement(elementName) {
+ let newParagraph = document.createElement(elementName)
+ newParagraph.setAttribute("contenteditable", "true")
+ newParagraph.setAttribute("data-id", this.self.getAttribute("data-id"))
+ newParagraph.setAttribute("id", this.self.getAttribute("data-id"))
+ newParagraph.setAttribute("data-order", this.self.getAttribute("data-order"))
+ newParagraph.onkeydown = onkeydownHandle
+
+ //todo 支持 有数据的行 在行首输入 #
+ // if()
+ // switch (elementName){
+ // case "h1":
+ // }
+ newParagraph.innerHTML = "
"
+
+ window.myEdit.utils.InsertAfter(newParagraph, this.self)
+ this.self.remove();
+ this.self = newParagraph;
+
+ //matData
+ let curNo = parseOrder(this.self)
+ let mapNode = utils.MyDocMap.get(curNo)
+ mapNode.setSource("")
+
+ //收起选区到一个点,光标落在一个可编辑元素上
+ window.myEdit.utils.GetSelection().setPosition(newParagraph, 0)
+ }
+
+
+ /**
+ * 包围样式事件处理
+ * @param event
+ */
+ surroundContentsByStyleHandler(event) {
+ let curS = window.myEdit.utils.GetSelection();
+ let curP = window.myEdit.utils.GetEventTarget(event);
+ let styleName = curP.getAttribute("data-value");
+ if (styleName === undefined) {
+ styleName = curP.parentNode.getAttribute("data-value");
+ }
+ let className = this.parseStyleName2ClassName(styleName)
+ //todo 只对 nodeType = p 执行
+ console.log("当前光标信息: ", curS, styleName, " className: ", className, curP)
+
+ for (let i = 0; i < curS.rangeCount; i++) {
+ let curSec = curS.getRangeAt(i);
+ let curPe = curSec.commonAncestorContainer;
+ //一个元素节点,例如
和
+ if (!curPeIsP && curPeParentIsP) { + //没选择,则退出 + if (start === end) { + return + } else { + // div + } + let curStartP = curSec.startContainer.parentElement; + console.log("debug1: ", curStartP, curStartP.nodeType, curStartP.nodeName) + //维护最近一次编辑的内容(暂时只支持恢复最近一次编辑) + window.myEdit.ctx.latestOpDoc = new MyRecovery(curStartP.cloneNode(true), function () { + console.log("恢复上一步样式1", this.innerHTML) + let curEl = document.getElementById(this.data.getAttribute("id")); + curEl.innerHTML = this.data.innerHTML; + + //文本映射 直接覆盖 map 中的 childrenStyle + window.myEdit.utils.SyncMapItemChildrenStyle(this.data); + }) + + let curHtml = "" + for (let j = 0; j < curStartP.innerText.length; j++) { + // console.log(curStartP.innerText.charAt(j)) + if (j >= start && j < end) { + curHtml += '' + curStartP.innerText.charAt(j) + ''; + } else { + curHtml += '' + curStartP.innerText.charAt(j) + ''; + } + } + curStartP.innerHTML = curHtml; + + curPEle = curStartP; + + //光标保持 + // curS.collapseToEnd(); + // window.myEdit.utils.GetSelection().setPosition(newParagraph, 0); + } else { + let tmpPNode = curSec.commonAncestorContainer; + if (!curPeIsP && curPeParentIsSpan) { + tmpPNode = curSec.commonAncestorContainer.parentNode.parentNode; + } + console.log("debug2: ", curSec.commonAncestorContainer, tmpPNode, tmpPNode.children, tmpPNode.childNodes) + //维护最近一次编辑的内容(暂时只支持恢复最近一次编辑) + window.myEdit.ctx.latestOpDoc = new MyRecovery(tmpPNode.cloneNode(true), function () { + console.log("恢复上一步样式1", this.data) + let curEl = document.getElementById(this.data.getAttribute("id")); + curEl.innerHTML = this.data.innerHTML; + //文本映射 直接覆盖 map 中的 childrenStyle + window.myEdit.utils.SyncMapItemChildrenStyle(this.data); + }) + + let myChildren = tmpPNode.childNodes + // let curEleSize = tmpPNode.childNodes.length + for (let j = 0; j < myChildren.length; j++) { + let curEle = myChildren[j] + if (curS.containsNode(curEle, true)) { + curEle.classList.remove(className); + curEle.classList.add(className); + } + } + + curPEle = curSec.commonAncestorContainer; + } + + + //文本映射 直接覆盖 map 中的 childrenStyle + window.myEdit.utils.SyncMapItemChildrenStyle(curPEle); + } + } +} \ No newline at end of file diff --git a/static/js/lib/common/MyUtils.js b/static/js/lib/common/MyUtils.js new file mode 100644 index 0000000..bf84050 --- /dev/null +++ b/static/js/lib/common/MyUtils.js @@ -0,0 +1,398 @@ +"use strict"; + +import {MyDocItem} from "../model/MyDocItem.js"; +import {MyMapItem} from "../model/MyMapItem.js"; + +export class MyUtils { + //todo 判断是哪一种浏览器类型。以及是否是手机 + //todo 初始化屏幕宽高比 + + + MyBrowser = null + + constructor() { + /** + * 提供浏览器检测的模块 + * @unfile + * @module UE.browser + */ + window.browser = ((function () { + var agent = navigator.userAgent.toLowerCase(), + opera = window.opera, + browser = { + /** + * @property {boolean} ie 检测当前浏览器是否为IE + * @example + * ```javascript + * if ( UE.browser.ie ) { + * console.log( '当前浏览器是IE' ); + * } + * ``` + */ + ie: /(msie\s|trident.*rv:)([\w.]+)/i.test(agent), + + /** + * @property {boolean} opera 检测当前浏览器是否为Opera + * @example + * ```javascript + * if ( UE.browser.opera ) { + * console.log( '当前浏览器是Opera' ); + * } + * ``` + */ + opera: !!opera && opera.version, + + /** + * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器 + * @example + * ```javascript + * if ( UE.browser.webkit ) { + * console.log( '当前浏览器是webkit内核浏览器' ); + * } + * ``` + */ + webkit: agent.indexOf(" applewebkit/") > -1, + + /** + * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下 + * @example + * ```javascript + * if ( UE.browser.mac ) { + * console.log( '当前浏览器运行在mac平台下' ); + * } + * ``` + */ + mac: agent.indexOf("macintosh") > -1, + + /** + * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下 + * @example + * ```javascript + * if ( UE.browser.quirks ) { + * console.log( '当前浏览器运行处于“怪异模式”' ); + * } + * ``` + */ + quirks: document.compatMode == "BackCompat" + } + + /** + * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核 + * @example + * ```javascript + * if ( UE.browser.gecko ) { + * console.log( '当前浏览器内核是gecko内核' ); + * } + * ``` + */ + browser.gecko = + navigator.product == "Gecko" && + !browser.webkit && + !browser.opera && + !browser.ie + + var version = 0 + + // Internet Explorer 6.0+ + if (browser.ie) { + var v1 = agent.match(/(?:msie\s([\w.]+))/) + var v2 = agent.match(/(?:trident.*rv:([\w.]+))/) + if (v1 && v2 && v1[1] && v2[1]) { + version = Math.max(v1[1] * 1, v2[1] * 1) + } else if (v1 && v1[1]) { + version = v1[1] * 1 + } else if (v2 && v2[1]) { + version = v2[1] * 1 + } else { + version = 0 + } + + browser.ie11Compat = document.documentMode == 11 + /** + * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie9Compat ) { + * console.log( '当前浏览器运行在IE9兼容模式下' ); + * } + * ``` + */ + browser.ie9Compat = document.documentMode == 9 + + /** + * @property { boolean } ie8 检测浏览器是否是IE8浏览器 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie8 ) { + * console.log( '当前浏览器是IE8浏览器' ); + * } + * ``` + */ + browser.ie8 = !!document.documentMode + + /** + * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie8Compat ) { + * console.log( '当前浏览器运行在IE8兼容模式下' ); + * } + * ``` + */ + browser.ie8Compat = document.documentMode == 8 + + /** + * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie7Compat ) { + * console.log( '当前浏览器运行在IE7兼容模式下' ); + * } + * ``` + */ + browser.ie7Compat = + (version == 7 && !document.documentMode) || document.documentMode == 7 + + /** + * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie6Compat ) { + * console.log( '当前浏览器运行在IE6模式或者怪异模式下' ); + * } + * ``` + */ + browser.ie6Compat = version < 7 || browser.quirks + + browser.ie9above = version > 8 + + browser.ie9below = version < 9 + + browser.ie11above = version > 10 + + browser.ie11below = version < 11 + } + + // Gecko. + if (browser.gecko) { + var geckoRelease = agent.match(/rv:([\d\.]+)/) + if (geckoRelease) { + geckoRelease = geckoRelease[1].split(".") + version = + geckoRelease[0] * 10000 + + (geckoRelease[1] || 0) * 100 + + (geckoRelease[2] || 0) * 1 + } + } + + /** + * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号 + * @warning 如果浏览器不是chrome, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.chrome ) { + * console.log( '当前浏览器是Chrome' ); + * } + * ``` + */ + if (/chrome\/(\d+\.\d)/i.test(agent)) { + browser.chrome = +RegExp["\x241"] + } + + /** + * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号 + * @warning 如果浏览器不是safari, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.safari ) { + * console.log( '当前浏览器是Safari' ); + * } + * ``` + */ + if ( + /(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && + !/chrome/i.test(agent) + ) { + browser.safari = +(RegExp["\x241"] || RegExp["\x242"]) + } + + // Opera 9.50+ + if (browser.opera) version = parseFloat(opera.version()) + + // WebKit 522+ (Safari 3+) + if (browser.webkit) + version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]) + + /** + * @property { Number } version 检测当前浏览器版本号 + * @remind + *