to:sync
This commit is contained in:
parent
8d6354c239
commit
ab00b85c8f
4
main.go
4
main.go
@ -9,6 +9,8 @@ import (
|
|||||||
"mylomen_server/common/constant"
|
"mylomen_server/common/constant"
|
||||||
"mylomen_server/common/utils"
|
"mylomen_server/common/utils"
|
||||||
"mylomen_server/static"
|
"mylomen_server/static"
|
||||||
|
"mylomen_server/static/css"
|
||||||
|
"mylomen_server/static/js"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -67,6 +69,8 @@ func main() {
|
|||||||
|
|
||||||
//static
|
//static
|
||||||
static.InitStaticGroup(e.Group("/v1/static/"))
|
static.InitStaticGroup(e.Group("/v1/static/"))
|
||||||
|
css.InitCssGroup(e.Group("/css"))
|
||||||
|
js.InitJsGroup(e.Group("/js"))
|
||||||
|
|
||||||
//ai
|
//ai
|
||||||
apps.InitAiGroup(e.Group("/v1/ai/", func(next echo.HandlerFunc) echo.HandlerFunc {
|
apps.InitAiGroup(e.Group("/v1/ai/", func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
31
static/css/init.go
Normal file
31
static/css/init.go
Normal file
@ -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
|
||||||
|
}
|
141
static/css/myEdit.css
Normal file
141
static/css/myEdit.css
Normal file
@ -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;
|
||||||
|
}
|
16
static/css/static_router.go
Normal file
16
static/css/static_router.go
Normal file
@ -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)
|
||||||
|
})
|
||||||
|
}
|
78
static/js/init.go
Normal file
78
static/js/init.go
Normal file
@ -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
|
||||||
|
}
|
481
static/js/lib/biz/MyBiz.js
Normal file
481
static/js/lib/biz/MyBiz.js
Normal file
@ -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 = "<br>"
|
||||||
|
//添加一行
|
||||||
|
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 === '<br>') {
|
||||||
|
let curNodeRetainHtml = curP.innerHTML
|
||||||
|
//阻止事件传递
|
||||||
|
window.myEdit.utils.ProhibitDefaultEvent(event);
|
||||||
|
//设置该元素隐藏
|
||||||
|
window.myEdit.ctx.MyDocMap.get(cNo).setHidden(true)
|
||||||
|
//删除当前元素
|
||||||
|
// curP.remove()
|
||||||
|
curP.innerHTML = "<br/>"
|
||||||
|
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 !== '<br>') {
|
||||||
|
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 = "<br>"
|
||||||
|
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 = "<br>";
|
||||||
|
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 = "<br>"
|
||||||
|
|
||||||
|
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;
|
||||||
|
//一个元素节点,例如 <p> 和 <div>。
|
||||||
|
let curPeIsP = curPe.nodeType === 1 && curPe.nodeName === "P";
|
||||||
|
let curPeParentIsP = curPe.parentNode.nodeType === 1 && curPe.parentNode.nodeName === "P";
|
||||||
|
let curPeParentIsDIV = curPe.parentNode.nodeType === 1 && curPe.parentNode.nodeName === "DIV";
|
||||||
|
let curPeParentIsSpan = curPe.parentNode.nodeType === 1 && (curPe.parentNode.nodeName === 'SPAN"' || curPe.parentNode.nodeName === 'SPAN');
|
||||||
|
|
||||||
|
let start = curSec.startOffset;
|
||||||
|
let end = curSec.endOffset;
|
||||||
|
console.log(" 当前选区信息 : ", curSec,
|
||||||
|
"\ncurPe: ", curPe,
|
||||||
|
"\ncurPeP: ", curPe.parentNode, curPe.parentNode.nodeType, curPe.parentNode.nodeName,
|
||||||
|
|
||||||
|
"\ncurPeIsP: ", curPeIsP,
|
||||||
|
"\ncurPeParentIsP : ", curPeParentIsP,
|
||||||
|
"\ncurPeParentIsDIV", curPeParentIsDIV,
|
||||||
|
"\ncurPeParentIsSpan", curPeParentIsSpan,
|
||||||
|
|
||||||
|
"\nclassName: ", className,
|
||||||
|
"\ntart: ", start,
|
||||||
|
"\nend: ", end)
|
||||||
|
|
||||||
|
let curPEle = null;
|
||||||
|
//第一次选择的时候将整行转换成<p><span></span></p>
|
||||||
|
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 += '<span class=' + className + '>' + curStartP.innerText.charAt(j) + '</span>';
|
||||||
|
} else {
|
||||||
|
curHtml += '<span>' + curStartP.innerText.charAt(j) + '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
398
static/js/lib/common/MyUtils.js
Normal file
398
static/js/lib/common/MyUtils.js
Normal file
@ -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
|
||||||
|
* <ul>
|
||||||
|
* <li>IE系列返回值为5,6,7,8,9,10等</li>
|
||||||
|
* <li>gecko系列会返回10900,158900等</li>
|
||||||
|
* <li>webkit系列会返回其build号 (如 522等)</li>
|
||||||
|
* </ul>
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* console.log( '当前浏览器版本号是: ' + UE.browser.version );
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
browser.version = version
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* if ( UE.browser.isCompatible ) {
|
||||||
|
* console.log( '浏览器与UEditor能够良好兼容' );
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
browser.isCompatible =
|
||||||
|
!browser.mobile &&
|
||||||
|
((browser.ie && version >= 6) ||
|
||||||
|
(browser.gecko && version >= 10801) ||
|
||||||
|
(browser.opera && version >= 9.5) ||
|
||||||
|
(browser.air && version >= 1) ||
|
||||||
|
(browser.webkit && version >= 522) ||
|
||||||
|
false)
|
||||||
|
return browser
|
||||||
|
})())
|
||||||
|
//快捷方式
|
||||||
|
window.ie = window.browser.ie;
|
||||||
|
window.webkit = window.browser.webkit;
|
||||||
|
window.gecko = window.browser.gecko;
|
||||||
|
window.opera = window.browser.opera;
|
||||||
|
//log
|
||||||
|
console.log("browser : ", window.browser,
|
||||||
|
"\nie: ", window.ie,
|
||||||
|
"\nwindow.webkit: ", window.webkit,
|
||||||
|
"\nwindow.gecko: ", window.gecko,
|
||||||
|
"\nwindow.opera: ", window.opera)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns 生产uuid
|
||||||
|
*/
|
||||||
|
Uuid() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||||
|
.replace(/[xy]/g, function (c) {
|
||||||
|
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8)
|
||||||
|
return v.toString(16)
|
||||||
|
}).split("-")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阻止默认事件
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
ProhibitDefaultEvent(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 触发事件的元素
|
||||||
|
* @param event
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
GetEventTarget(event) {
|
||||||
|
return event.target
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ParseEvent(e) {
|
||||||
|
return e || window.event //标准化事件处理
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GetKeyCode(event) {
|
||||||
|
return event.keyCode || event.which || event.charCode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前选区。 兼容不同浏览器
|
||||||
|
* @returns {Selection|*}
|
||||||
|
*/
|
||||||
|
GetSelection() {
|
||||||
|
return window.getSelection() || document.selection
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是数字
|
||||||
|
* @param value
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
IsNum(value) {
|
||||||
|
return !isNaN(parseFloat(value)) && isFinite(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在节点node后面插入新节点newNode
|
||||||
|
* @method InsertAfter
|
||||||
|
* @param { Node } node 目标节点
|
||||||
|
* @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
|
||||||
|
* @return { Node } 新插入的节点
|
||||||
|
*/
|
||||||
|
InsertAfter(node, newNode) {
|
||||||
|
return node.nextSibling
|
||||||
|
? node.parentNode.insertBefore(newNode, node.nextSibling)
|
||||||
|
: node.parentNode.appendChild(newNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增一个元素
|
||||||
|
* @param newParagraph
|
||||||
|
*/
|
||||||
|
AddNewParagraph(newParagraph) {
|
||||||
|
//docRoot
|
||||||
|
window.myEdit.ctx.MyRoot.appendChild(newParagraph);
|
||||||
|
|
||||||
|
//mapRoot
|
||||||
|
let myP = new MyDocItem(newParagraph);
|
||||||
|
let curOrder = myP.parseOrder();
|
||||||
|
let uuid = myP.parseUuid();
|
||||||
|
window.myEdit.ctx.MyDocMap.set(curOrder, new MyMapItem(uuid))
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.myEdit.utils.GetSelection().setPosition(newParagraph, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步某一行数据到对应的 map节点
|
||||||
|
* @param docP
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
SyncMapItemChildrenStyle(docP) {
|
||||||
|
//子元素为空不处理
|
||||||
|
let items = docP.childNodes;
|
||||||
|
if (items.length <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//构造参数
|
||||||
|
let curMyP = new MyDocItem(docP);
|
||||||
|
let mapItem = window.myEdit.ctx.getMapItem(curMyP.parseOrder());
|
||||||
|
|
||||||
|
//清空重置
|
||||||
|
// console.log(mapItem);
|
||||||
|
mapItem.getStyle().setChildrenStyleMapNull();
|
||||||
|
//遍历
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
let curItem = items[i];
|
||||||
|
let tmpClassList = curItem.classList;
|
||||||
|
if (tmpClassList != null && tmpClassList.length > 0) {
|
||||||
|
mapItem.getStyle().setChildrenStyle(i, tmpClassList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("sync docP : ", docP, " children: ", docP.children, " childrenMap: ", mapItem.getStyle().getChildrenStyleMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
171
static/js/lib/event/MyEventListener.js
Normal file
171
static/js/lib/event/MyEventListener.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解决事件监听 this 问题,这里 转接一下
|
||||||
|
*/
|
||||||
|
export class MyEventListener {
|
||||||
|
|
||||||
|
constructor(styleClass) {
|
||||||
|
|
||||||
|
//样式事件
|
||||||
|
let styleList = document.getElementsByClassName(styleClass);
|
||||||
|
// console.log(styleList);
|
||||||
|
if (styleList && styleList.length > 0) {
|
||||||
|
for (let i = 0; i < styleList.length; i++) {
|
||||||
|
// console.log(styleList[i]);
|
||||||
|
styleList[i].addEventListener('click', this.SurroundContentsByStyleListener, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurroundContentsByStyleListener(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
console.log("SurroundContentsByStyleListener : ", e, event);
|
||||||
|
//业务
|
||||||
|
window.myEdit.biz.surroundContentsByStyleHandler(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标按下事件 & 键盘组合事件
|
||||||
|
* @param {*} e
|
||||||
|
*/
|
||||||
|
KeydownListener(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
// console.log("this: ", this, e, e.target, "\n event: ", event)
|
||||||
|
|
||||||
|
|
||||||
|
const keyCode = window.myEdit.utils.GetKeyCode(event);
|
||||||
|
const keyCombination = event.ctrlKey
|
||||||
|
const metaKey = event.metaKey
|
||||||
|
// console.log("键盘事件 ", event, keyCombination, metaKey, keyCode)
|
||||||
|
|
||||||
|
// ctrl + c 复制
|
||||||
|
if (keyCombination && keyCode === 67) {
|
||||||
|
// 阻止默认事件
|
||||||
|
window.myEdit.utils.ProhibitDefaultEvent(event);
|
||||||
|
console.log('触发ctrl + c 事件', e.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
//撤销
|
||||||
|
if (metaKey && keyCode === 90) {
|
||||||
|
window.myEdit.biz.cancelHandle(event)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除
|
||||||
|
if (keyCode === 46 || keyCode === 8) {
|
||||||
|
window.myEdit.biz.deleteHandle(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//回车事件
|
||||||
|
if (keyCode === 13 /* && currentNode === key.lastElementChild */) {
|
||||||
|
window.myEdit.biz.enterHandler(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//空格键
|
||||||
|
if (keyCode === 32) {
|
||||||
|
console.log('触发 空格 事件', e.target)
|
||||||
|
window.myEdit.biz.emptyKeyWorkHandler(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 窗口撤销事件
|
||||||
|
* @param e
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
WindowsCtrZHandle(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
const keyCode = window.myEdit.utils.GetKeyCode(event);
|
||||||
|
const metaKey = event.metaKey;
|
||||||
|
if (metaKey && keyCode === 90) {
|
||||||
|
window.myEdit.biz.cancelHandle(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入事件
|
||||||
|
* @param e
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
InputListener(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
window.myEdit.biz.inputHandle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中文输入开始事件
|
||||||
|
* @param e
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
CompositionstartListener(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
window.myEdit.biz.compositionstartHandle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中文输入结束事件
|
||||||
|
* @param e
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
CompositionendListener(e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
window.myEdit.biz.compositionendHandle(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册事件处理器
|
||||||
|
* @param eventName
|
||||||
|
* @param handler
|
||||||
|
*/
|
||||||
|
RegisterEventHandle(eventName, handler) {
|
||||||
|
window.myEdit.ctx.MyRoot.addEventListener(eventName, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听鼠标抬起事件
|
||||||
|
* @param e
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
MouseUp(e) {
|
||||||
|
let styleUtils = document.getElementById("_style_utils");
|
||||||
|
|
||||||
|
styleUtils.addEventListener("mousedown", function (e) {
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
window.myEdit.utils.ProhibitDefaultEvent(event);
|
||||||
|
})
|
||||||
|
|
||||||
|
if (window.myEdit.utils.GetSelection().isCollapsed) {
|
||||||
|
styleUtils.style.display = "none";
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let posX = 0, posY = 0;
|
||||||
|
const event = window.myEdit.utils.ParseEvent(e);
|
||||||
|
// if (event.pageX || event.pageY) {
|
||||||
|
// posX = event.pageX;
|
||||||
|
// posY = event.pageY;
|
||||||
|
// } else if (event.clientX || event.clientY) {
|
||||||
|
// posX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
||||||
|
// posY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
||||||
|
// }
|
||||||
|
|
||||||
|
var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
|
||||||
|
var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
var x = event.pageX || event.clientX + scrollX;
|
||||||
|
var y = event.pageY || event.clientY + scrollY;
|
||||||
|
|
||||||
|
console.log("posX: ", x, " posY: ", y, event.pageX, event.pageY, scrollX, scrollY, event.clientX, event.clientY);
|
||||||
|
|
||||||
|
//
|
||||||
|
styleUtils.style.display = "block";
|
||||||
|
styleUtils.style.left = (event.clientX - 60) + "px";
|
||||||
|
styleUtils.style.top = (event.clientY - 60) + "px";
|
||||||
|
|
||||||
|
// window.myEdit.utils.ProhibitDefaultEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
79
static/js/lib/main.js
Normal file
79
static/js/lib/main.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"use strict";
|
||||||
|
import {MyBiz} from './biz/MyBiz.js'
|
||||||
|
import {MyUtils} from './common/MyUtils.js'
|
||||||
|
import {MyEventListener} from "./event/MyEventListener.js";
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
//init
|
||||||
|
window.myEdit = {
|
||||||
|
/**
|
||||||
|
* 优先初始化 工具
|
||||||
|
*/
|
||||||
|
utils: new MyUtils(),
|
||||||
|
/**
|
||||||
|
* 其次初始化 事件监听
|
||||||
|
*/
|
||||||
|
eventListener: new MyEventListener("fixStyleInnerSpan"),
|
||||||
|
|
||||||
|
ctx: {
|
||||||
|
/**
|
||||||
|
* 文档的根节点
|
||||||
|
*/
|
||||||
|
MyRoot: document.getElementById("noteshare"),
|
||||||
|
/**
|
||||||
|
* 文档的结构树
|
||||||
|
*/
|
||||||
|
MyDocMap: new Map(),
|
||||||
|
|
||||||
|
getMapItem: function (orderNo) {
|
||||||
|
return this.MyDocMap.get(orderNo);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 最新修改的元素. 当前只支持撤销一次
|
||||||
|
*/
|
||||||
|
latestOpDoc: null,
|
||||||
|
/**
|
||||||
|
* 行增加记录。 行号
|
||||||
|
*/
|
||||||
|
rowNo: 0,
|
||||||
|
|
||||||
|
incrementNumThenReturn: function () {
|
||||||
|
return this.rowNo++;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否开始输入中文
|
||||||
|
*/
|
||||||
|
inCompositionEvent: false,
|
||||||
|
|
||||||
|
showTestText: function () {
|
||||||
|
//显示用户的输入内容
|
||||||
|
let userInput = document.getElementById("testInput");
|
||||||
|
if (userInput) {
|
||||||
|
let obj = {}
|
||||||
|
for (let [k, v] of this.MyDocMap.entries()) {
|
||||||
|
if (v["data-hidden"] && v["data-hidden"] === true) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("userInput : ", userInput)
|
||||||
|
userInput.innerText = JSON.stringify(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务处理
|
||||||
|
*/
|
||||||
|
window.myEdit.biz = new MyBiz();
|
||||||
|
|
||||||
|
//窗口撤销事件
|
||||||
|
window.addEventListener('keydown', window.myEdit.eventListener.WindowsCtrZHandle, true);
|
||||||
|
|
||||||
|
//监听鼠标抬起事件
|
||||||
|
document.getElementById("noteshare").addEventListener("mouseup", window.myEdit.eventListener.MouseUp, true);
|
||||||
|
}
|
||||||
|
|
52
static/js/lib/model/InnerStyle.js
Normal file
52
static/js/lib/model/InnerStyle.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import {MyKV} from './MyKV.js'
|
||||||
|
export class InnerStyle {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.nodeType = "p"
|
||||||
|
//map-> index:classList
|
||||||
|
this.childrenStyle = null;
|
||||||
|
//前置包围元素。 如 有序/无序列表
|
||||||
|
this.preStyle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeType() {
|
||||||
|
return this.nodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
setNodeType(nodeType) {
|
||||||
|
this.nodeType = nodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenStyle(index, classList) {
|
||||||
|
if (this.childrenStyle === null) {
|
||||||
|
this.childrenStyle = new Map();
|
||||||
|
}
|
||||||
|
this.childrenStyle.set(index, classList)
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildrenStyle(index) {
|
||||||
|
return this.childrenStyle.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
//前置类型 如 ul ol 代码块 等
|
||||||
|
setPreStyle(k, v) {
|
||||||
|
if (this.preStyle == null) {
|
||||||
|
this.preStyle = new MyKV(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreStyle() {
|
||||||
|
return this.preStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildrenStyleMap() {
|
||||||
|
return this.childrenStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenStyleMapNull() {
|
||||||
|
this.childrenStyle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
static/js/lib/model/MyDocItem.js
Normal file
29
static/js/lib/model/MyDocItem.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑的每一行。 建议不要删除 不要改变
|
||||||
|
*/
|
||||||
|
export class MyDocItem {
|
||||||
|
|
||||||
|
constructor(p) {
|
||||||
|
this.self = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析当前行号
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
parseOrder() {
|
||||||
|
return parseInt(this.self.getAttribute("data-order"));
|
||||||
|
}
|
||||||
|
|
||||||
|
parseId() {
|
||||||
|
return parseInt(this.self.getAttribute("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
parseUuid() {
|
||||||
|
return parseInt(this.self.getAttribute("data-id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
15
static/js/lib/model/MyKV.js
Normal file
15
static/js/lib/model/MyKV.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"use strict";
|
||||||
|
export class MyKV {
|
||||||
|
constructor(k, v) {
|
||||||
|
this.k = k
|
||||||
|
this.v = v
|
||||||
|
|
||||||
|
this.getK = function getK() {
|
||||||
|
return this.k
|
||||||
|
}
|
||||||
|
this.getV = function getV() {
|
||||||
|
return this.v
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
31
static/js/lib/model/MyMapItem.js
Normal file
31
static/js/lib/model/MyMapItem.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
import {InnerStyle} from './InnerStyle.js'
|
||||||
|
|
||||||
|
|
||||||
|
export class MyMapItem {
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id
|
||||||
|
this.hidden = false
|
||||||
|
this.style = new InnerStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
getHidden() {
|
||||||
|
return this.hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
setHidden(val) {
|
||||||
|
this.hidden = val
|
||||||
|
}
|
||||||
|
|
||||||
|
setSource(source) {
|
||||||
|
this.source = source
|
||||||
|
}
|
||||||
|
|
||||||
|
getSource() {
|
||||||
|
return this.source
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyle() {
|
||||||
|
return this.style
|
||||||
|
}
|
||||||
|
}
|
19
static/js/lib/model/MyRecovery.js
Normal file
19
static/js/lib/model/MyRecovery.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"use strict";
|
||||||
|
export class MyRecovery {
|
||||||
|
constructor(data, func) {
|
||||||
|
this.data = data;
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFun() {
|
||||||
|
return this.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
recovery() {
|
||||||
|
this.func();
|
||||||
|
}
|
||||||
|
}
|
15
static/js/static_router.go
Normal file
15
static/js/static_router.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitJsGroup(g *echo.Group) {
|
||||||
|
|
||||||
|
g.GET("/:name", func(c echo.Context) error {
|
||||||
|
jsName := c.Param("name")
|
||||||
|
c.Response().Header().Set("Cache-Control", "max-age=1")
|
||||||
|
return c.Blob(http.StatusOK, "text/javaScript", jsMap[jsName])
|
||||||
|
})
|
||||||
|
}
|
@ -6,187 +6,125 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>📒</title>
|
<title>📒</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/v1/static/css/normalize.css">
|
<link rel="stylesheet" type="text/css" href="./css/normalize.css">
|
||||||
<style>
|
<link rel="stylesheet" type="text/css" href="./css/myEdit.css">
|
||||||
/* 字体 */
|
|
||||||
: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; */
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
position: relative;
|
|
||||||
top: 0;
|
|
||||||
/* height: 8rem; */
|
|
||||||
z-index: 9999;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: #f7dcbc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noteshare {
|
|
||||||
width: 80%;
|
|
||||||
/* width: 21cm; */
|
|
||||||
min-height: 80rem;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
|
|
||||||
/*border: 1px red solid;*/
|
|
||||||
margin: auto auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noteshare p {
|
|
||||||
border: 1px rgb(248, 245, 245) solid;
|
|
||||||
/* border: none; */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#testInput {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
border: 1px rgb(0, 140, 255) solid;
|
|
||||||
margin: 20px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
::selection {
|
|
||||||
color: antiquewhite;
|
|
||||||
background-color: cadetblue;
|
|
||||||
text-shadow: #00a9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.my-divider-item {
|
|
||||||
background-color: grey;
|
|
||||||
width: 1px;
|
|
||||||
height: 2rem;
|
|
||||||
margin: 2px 10px 2px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.childStyleStrong {
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
|
|
||||||
.childStyleI {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.childStyleU {
|
|
||||||
text-decoration: underline;
|
|
||||||
//中划线
|
|
||||||
//text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.childStyleDel {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.childStyleColor {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="display: flex; flex-direction:column; ">
|
<body style="display: flex; flex-direction:column; ">
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div style="display: flex;
|
<div class="fixStylePosition" id="_style_utils">
|
||||||
padding: 1rem 4rem;
|
<div class="fixStyleOut">
|
||||||
justify-content: space-between;
|
<div style="display: flex;">
|
||||||
align-items: center;
|
<div style="display: flex;">
|
||||||
background: #f8f6f4;
|
<div>
|
||||||
margin: 0 auto;">
|
<svg width="1.8rem" height="1.8rem" viewBox="0 0 24 24" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="TextOutlined">
|
||||||
<div class="head-left">
|
<path
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
d="M2 3a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0V4h-7v16h3a1 1 0 1 1 0 2H8a1 1 0 1 1 0-2h3V4H4v3a1 1 0 1 1-2 0V3Z"
|
||||||
<path d="M20 11H7.83L13.42 5.41L12 4L4 12L12 20L13.41 18.59L7.83 13H20V11Z" fill="black"></path>
|
fill="currentColor"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div class="head-center" style="display: flex;
|
<svg width="1.4rem" height="1.4rem" viewBox="0 0 24 24" fill="none"
|
||||||
justify-content: center;
|
xmlns="http://www.w3.org/2000/svg" data-icon="DownBoldOutlined">
|
||||||
align-items: center;
|
<path
|
||||||
max-width: 650px;
|
d="m3.414 7.086-.707.707a1 1 0 0 0 0 1.414l7.778 7.778a2 2 0 0 0 2.829 0l7.778-7.778a1 1 0 0 0 0-1.414l-.707-.707a1 1 0 0 0-1.415 0l-7.07 7.07-7.072-7.07a1 1 0 0 0-1.414 0Z"
|
||||||
background-color: aqua;">
|
fill="currentColor"></path>
|
||||||
<div style="
|
</svg>
|
||||||
position: fixed;
|
|
||||||
z-index: 87;
|
|
||||||
/* top: 200px; */
|
|
||||||
/* left: 324px; */
|
|
||||||
">
|
|
||||||
<div>
|
|
||||||
<div style="display: flex;
|
|
||||||
|
|
||||||
">
|
|
||||||
<div>
|
|
||||||
<button id="myPlusB" data-value="b">加粗</button>
|
|
||||||
</div>
|
|
||||||
<div class="my-divider-item"></div>
|
|
||||||
<div>
|
|
||||||
<button id="myPlusI" data-value="i"><i>I</i></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="my-divider-item"></div>
|
|
||||||
<div>
|
|
||||||
<button id="myPlusU" data-value="u"><u>下划线</u></button>
|
|
||||||
</div>
|
|
||||||
<div class="my-divider-item"></div>
|
|
||||||
<div>
|
|
||||||
<button id="myPlusDel" data-value="del">
|
|
||||||
<del>删除线</del>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="my-divider-item"></div>
|
|
||||||
<div>
|
|
||||||
<button id="myPlusC" data-value="c_red">红色</button>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="my-divider-item"></div>-->
|
|
||||||
<!-- <div>-->
|
|
||||||
<!-- <button onclick="info(this)">info</button>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="my-divider-item"></div>
|
||||||
|
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div>
|
||||||
|
<svg width="1.8rem" height="1.8rem" viewBox="0 0 24 24" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="TypographyOutlined">
|
||||||
|
<path
|
||||||
|
d="M2 4a1 1 0 0 1 1-1h18a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm0 4a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm1 3a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Zm-1 5a1 1 0 0 1 1-1h10a1 1 0 1 1 0 2H3a1 1 0 0 1-1-1Zm1 3a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<svg width="1.4rem" height="1.4rem" viewBox="0 0 24 24" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="DownBoldOutlined">
|
||||||
|
<path
|
||||||
|
d="m3.414 7.086-.707.707a1 1 0 0 0 0 1.414l7.778 7.778a2 2 0 0 0 2.829 0l7.778-7.778a1 1 0 0 0 0-1.414l-.707-.707a1 1 0 0 0-1.415 0l-7.07 7.07-7.072-7.07a1 1 0 0 0-1.414 0Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="head-right">
|
<div class="my-divider-item"></div>
|
||||||
<button>同步</button>
|
<div style="display: flex;">
|
||||||
|
<span class="fixStyleInnerSpan" data-value="b">
|
||||||
|
<svg data-value="b" width="1.8rem" height="1.8rem" viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="BoldOutlined">
|
||||||
|
<path data-value="b" d="M5 2.709C5 2.317 5.317 2 5.709 2h6.734a5.317 5.317 0 0 1 3.686 9.148 5.671 5.671 0 0 1-2.623 10.7H5.71a.709.709 0 0 1-.71-.707V2.71Zm2 7.798h5.443a3.19 3.19 0 0 0 3.19-3.19c0-1.762-1.428-3.317-3.19-3.317H7v6.507Zm0 2.126v7.09h6.507a3.544 3.544 0 0 0 0-7.09H7Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="fixStyleInnerSpan" data-value="del">
|
||||||
|
<svg data-value="del" width="1.8rem" height="1.8rem"
|
||||||
|
viewBox="0 0 24 24" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="HorizontalLineOutlined">
|
||||||
|
<path
|
||||||
|
d="M5.49 7.226A5.107 5.107 0 0 1 6.9 3.831C8.017 2.636 9.718 2 11.819 2c2.142 0 3.779.57 4.867 1.689.4.392.869.958 1.26 1.595.443.723-.191 1.53-1.04 1.53-.606 0-1.039-.447-1.326-.981a2.864 2.864 0 0 0-.362-.517c-.735-.93-1.909-1.419-3.386-1.419-2.404 0-4.154 1.395-4.2 3.393-.02.846.337 1.58.995 2.043h-2.75c-.271-.621-.403-1.332-.385-2.107Zm8.906 6.024H4.038c-.518 0-.938-.38-.938-.897 0-.518.42-.978.938-.978h16.125c.518 0 .937.437.937.954 0 .518-.42.921-.937.921h-2.455c.542.806.96 1.954.934 3.055C18.563 19.82 15.87 22 11.572 22c-2.875 0-5.028-.964-6.13-2.745a6.884 6.884 0 0 1-.545-1.191c-.261-.72.318-1.432 1.084-1.432.574 0 1.034.416 1.24.952.17.445.4.794.733 1.142.805.858 2.104 1.305 3.766 1.305 2.845 0 4.696-1.39 4.747-3.61.024-1.072-.256-1.61-.897-2.42-.473-.598-1.174-.751-1.174-.751Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="fixStyleInnerSpan" data-value="i">
|
||||||
|
<svg data-value="i" width="1.8rem" height="1.8rem" viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="ItalicOutlined">
|
||||||
|
<path
|
||||||
|
d="M14.825 5.077 11.19 18.923h4.052a1.038 1.038 0 1 1 0 2.077H4.954a1.038 1.038 0 1 1 0-2.077h4.053l3.636-13.846H8.591A1.038 1.038 0 1 1 8.59 3h10.287a1.038 1.038 0 0 1 0 2.077h-4.053Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="fixStyleInnerSpan" data-value="u">
|
||||||
|
<svg data-value="u" width="1.8rem" height="1.8rem" viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" data-icon="UnderlineOutlined">
|
||||||
|
<path
|
||||||
|
d="M7.361 3.052a.99.99 0 0 0-.989-.994.998.998 0 0 0-.999.994v5.765c0 4.205 2.601 7.29 6.627 7.29s6.627-3.085 6.627-7.29V3.052a.996.996 0 0 0-.996-.994.992.992 0 0 0-.992.994v5.765c0 3.003-1.763 5.302-4.639 5.302-2.876 0-4.639-2.299-4.639-5.302V3.052ZM3.054 19.42a.988.988 0 0 0-.994.988 1 1 0 0 0 .994 1h17.892a1 1 0 0 0 .994-1.002.987.987 0 0 0-.994-.986H3.054Z"
|
||||||
|
fill="currentColor"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-divider-item"></div>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<svg width="1.8rem" height="1.8rem" viewBox="0 0 18 18" fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M4.2 10.4A1.19 1.19 0 013 9.2 1.19 1.19 0 014.2 8c.23 0 .43.06.6.17.2.1.33.24.44.43.11.18.17.38.17.6 0 .23-.06.43-.17.6-.1.2-.25.34-.43.45-.18.1-.38.16-.6.16zm4.72 0a1.19 1.19 0 01-1.2-1.2A1.19 1.19 0 018.92 8c.22 0 .42.06.6.17.19.1.33.24.44.43.1.18.16.38.16.6a1.22 1.22 0 01-.6 1.04c-.18.11-.38.17-.6.17zm4.72 0a1.19 1.19 0 01-1.2-1.2 1.19 1.19 0 011.2-1.2c.22 0 .42.06.6.17.18.1.33.24.44.43.11.18.16.38.16.6 0 .23-.05.43-.16.6a1.18 1.18 0 01-1.04.6z"
|
||||||
|
fill="#535353"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="my-divider-item"></div>-->
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- <button onclick="info(this)">info</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main style="margin-top: 5rem">
|
||||||
|
<h1>测试编辑</h1>
|
||||||
<div id="noteshare" spellcheck="false" translate="no">
|
<div id="noteshare" spellcheck="false" translate="no">
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
<!-- <div id="testInput" >测试</div>-->
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
<script src="/v1/static/js/utils.js"></script>
|
<script type="module" src="./js/lib/main.js"></script>
|
||||||
<script src="/v1/static/js/styleCmd.js"></script>
|
|
||||||
<script src="/v1/static/js/yanxuelu.js"></script>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
x
Reference in New Issue
Block a user