419 lines
12 KiB
JavaScript
419 lines
12 KiB
JavaScript
![]() |
/** *
|
|||
|
* 工具类
|
|||
|
*/
|
|||
|
(function () {
|
|||
|
|
|||
|
/**
|
|||
|
* 最新修改的元素
|
|||
|
*/
|
|||
|
const latestOpDoc = null
|
|||
|
|
|||
|
class MyRecovery {
|
|||
|
constructor(data, func) {
|
|||
|
this.data = data;
|
|||
|
this.func = func;
|
|||
|
}
|
|||
|
|
|||
|
getData() {
|
|||
|
return this.data;
|
|||
|
}
|
|||
|
|
|||
|
getFun() {
|
|||
|
return this.func;
|
|||
|
}
|
|||
|
|
|||
|
recovery() {
|
|||
|
this.func();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 行数增加记录
|
|||
|
*/
|
|||
|
const num = 0
|
|||
|
|
|||
|
/**
|
|||
|
* 是否开始输入中文
|
|||
|
*/
|
|||
|
const inCompositionEvent = false;
|
|||
|
|
|||
|
/**
|
|||
|
* 我的文档数据
|
|||
|
*/
|
|||
|
const MyDocMap = new Map()
|
|||
|
|
|||
|
|
|||
|
// push,推一个在后面,就是 append 的意思
|
|||
|
// pop,从后面拉走,配合 push 就是栈
|
|||
|
// unshift,从前面加一个数据,就是 insert(it, 0) 的意思
|
|||
|
// shift,前面拉个数据走,配合 shift 就是一个反向栈,配合 push 就是队列
|
|||
|
class MyQueue {
|
|||
|
constructor(size) {
|
|||
|
this.insertIndex = 0
|
|||
|
this.delIndex = 0
|
|||
|
this.capacity = 0;
|
|||
|
this.size = size;
|
|||
|
this.arr = new Array(size);
|
|||
|
}
|
|||
|
|
|||
|
push(handle) {
|
|||
|
if (this.capacity === this.arr.length) {
|
|||
|
console.log("满了");
|
|||
|
this.capacity--;
|
|||
|
this.arr[this.capacity] = null;
|
|||
|
|
|||
|
//todo 替代
|
|||
|
// this.end++;
|
|||
|
}
|
|||
|
this.arr[this.capacity] = handle;
|
|||
|
this.capacity++;
|
|||
|
}
|
|||
|
|
|||
|
pull() {
|
|||
|
if (this.capacity <= 0) {
|
|||
|
return null
|
|||
|
}
|
|||
|
this.capacity--;
|
|||
|
|
|||
|
//到底回退
|
|||
|
if (this.shift > this.size) {
|
|||
|
this.shift = 0;
|
|||
|
}
|
|||
|
|
|||
|
return this.arr[this.shift];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
class MyNode {
|
|||
|
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
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class InnerStyle {
|
|||
|
constructor() {
|
|||
|
this.nodeType = "p"
|
|||
|
//map-> index:classList
|
|||
|
this.childrenStyle = null;
|
|||
|
this.preStyle = null;
|
|||
|
|
|||
|
this.getNodeType = function getNodeType() {
|
|||
|
return this.nodeType
|
|||
|
}
|
|||
|
this.setNodeType = function setNodeType(nodeType) {
|
|||
|
this.nodeType = nodeType
|
|||
|
}
|
|||
|
|
|||
|
//前置类型 如 ul ol 代码块 等
|
|||
|
this.setPreStyle = function setPreStyle(k, v) {
|
|||
|
if (this.preStyle == null) {
|
|||
|
this.preStyle = new MyKV(k, v);
|
|||
|
}
|
|||
|
}
|
|||
|
this.getPreStyle = function getPreStyle() {
|
|||
|
return this.preStyle
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setChildrenStyle(index, classList) {
|
|||
|
if (this.childrenStyle === null) {
|
|||
|
this.childrenStyle = new Map();
|
|||
|
}
|
|||
|
this.childrenStyle.set(index, classList)
|
|||
|
}
|
|||
|
|
|||
|
getChildrenStyle(index) {
|
|||
|
return this.childrenStyle.get(index);
|
|||
|
}
|
|||
|
|
|||
|
getChildrenStyleMap() {
|
|||
|
return this.childrenStyle;
|
|||
|
}
|
|||
|
|
|||
|
setChildrenStyleMapNull() {
|
|||
|
this.childrenStyle = null;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
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
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 主节点元素
|
|||
|
*/
|
|||
|
const MyRoot = document.getElementById("noteshare")
|
|||
|
|
|||
|
|
|||
|
function isNum(value) {
|
|||
|
return !isNaN(parseFloat(value)) && isFinite(value)
|
|||
|
}
|
|||
|
|
|||
|
function getSelection() {
|
|||
|
return window.getSelection() || document.selection
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
*
|
|||
|
* @returns 生产uuid
|
|||
|
*/
|
|||
|
function 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]
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 在目标元素后面插叙 新元素
|
|||
|
* @param {*} newElement
|
|||
|
* @param {*} targetElement
|
|||
|
*/
|
|||
|
function insertAfter(newElement, targetElement) {
|
|||
|
// console.log("开始: ",newElement, targetElement)
|
|||
|
var parent = targetElement.parentNode
|
|||
|
if (parent.lastChild == targetElement) {
|
|||
|
// 如果最后的节点是目标元素,则直接添加。因为默认是最后
|
|||
|
parent.appendChild(newElement)
|
|||
|
} else {
|
|||
|
parent.insertBefore(newElement, targetElement.nextSibling)
|
|||
|
//如果不是,则插入在目标元素的下一个兄弟节点 的前面。也就是目标元素的后面
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function parseOrder(curP) {
|
|||
|
return parseInt(curP.getAttribute("data-order"))
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
*
|
|||
|
* @param {*} curP
|
|||
|
* @param onkeydownHandle
|
|||
|
*/
|
|||
|
function firstCmd(curP, onkeydownHandle) {
|
|||
|
let newParagraph;
|
|||
|
let inputLength = curP.innerText.length
|
|||
|
/**
|
|||
|
* h1 ~ h6
|
|||
|
*/
|
|||
|
if (curP.innerText.startsWith("#") && curP.innerHTML.endsWith(" ")) {
|
|||
|
let curNo = parseOrder(curP)
|
|||
|
let mapNode = utils.MyDocMap.get(curNo)
|
|||
|
|
|||
|
// console.log(curP, " - ", curP.innerHTML, curP.innerHTML.startsWith("# "))
|
|||
|
if (curP.innerHTML.startsWith("# ") || curP.innerHTML.startsWith("# ")) {
|
|||
|
mapNode.getStyle().setNodeType("h1")
|
|||
|
becomeAnotherElement(curP, "h1", onkeydownHandle)
|
|||
|
} else if (curP.innerHTML.startsWith("## ")) {
|
|||
|
mapNode.getStyle().setNodeType("h2")
|
|||
|
becomeAnotherElement(curP, "h2", onkeydownHandle)
|
|||
|
} else if (curP.innerHTML.startsWith("### ")) {
|
|||
|
mapNode.getStyle().setNodeType("h3")
|
|||
|
becomeAnotherElement(curP, "h3", onkeydownHandle)
|
|||
|
} else if (curP.innerHTML.startsWith("#### ")) {
|
|||
|
mapNode.getStyle().setNodeType("h4")
|
|||
|
becomeAnotherElement(curP, "h4", onkeydownHandle)
|
|||
|
} else if (curP.innerHTML.startsWith("##### ")) {
|
|||
|
mapNode.getStyle().setNodeType("h5")
|
|||
|
becomeAnotherElement(curP, "h5", onkeydownHandle)
|
|||
|
} else {
|
|||
|
mapNode.getStyle().setNodeType("h6")
|
|||
|
becomeAnotherElement(curP, "h6", onkeydownHandle)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 无序列表效果
|
|||
|
*/
|
|||
|
if (inputLength === 2 && curP.innerText.startsWith("-") && curP.innerHTML.endsWith(" ")) {
|
|||
|
let curNo = parseOrder(curP)
|
|||
|
let mapNode = utils.MyDocMap.get(curNo)
|
|||
|
mapNode.getStyle().setPreStyle("ul", true)
|
|||
|
|
|||
|
//clean
|
|||
|
curP.innerHTML = ""
|
|||
|
mapNode.setSource("")
|
|||
|
|
|||
|
//根据上一层级元素动态选择 todo
|
|||
|
curP.setAttribute("style", "padding-left: 1rem;")
|
|||
|
//新增元素
|
|||
|
newParagraph = document.createElement("span");
|
|||
|
newParagraph.setAttribute("contenteditable", "false")
|
|||
|
//∙ vs ∘
|
|||
|
newParagraph.innerHTML = "∙ "
|
|||
|
curP.append(newParagraph)
|
|||
|
|
|||
|
//添加一个选区
|
|||
|
var selObj = window.getSelection()
|
|||
|
var rangeObj = document.createRange()
|
|||
|
rangeObj.selectNode(curP)
|
|||
|
selObj.addRange(rangeObj)
|
|||
|
|
|||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
|||
|
window.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 = parseOrder(curP)
|
|||
|
let mapNode = utils.MyDocMap.get(curNo)
|
|||
|
mapNode.getStyle().setPreStyle("ol", num)
|
|||
|
|
|||
|
//clean
|
|||
|
curP.innerHTML = ""
|
|||
|
mapNode.setSource("")
|
|||
|
//todo
|
|||
|
curP.setAttribute("style", "padding-left: 1rem;")
|
|||
|
//新增元素
|
|||
|
newParagraph = document.createElement("span");
|
|||
|
newParagraph.setAttribute("contenteditable", "false")
|
|||
|
newParagraph.innerHTML = num + ". "
|
|||
|
curP.append(newParagraph)
|
|||
|
|
|||
|
//添加一个选区
|
|||
|
var selObj = window.getSelection()
|
|||
|
var rangeObj = document.createRange()
|
|||
|
rangeObj.selectNode(curP)
|
|||
|
selObj.addRange(rangeObj)
|
|||
|
|
|||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
|||
|
window.getSelection().collapse(curP, true)
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 变成另一个元素
|
|||
|
* @param {*} curP
|
|||
|
* @param {*} elementName
|
|||
|
*/
|
|||
|
function becomeAnotherElement(curP, elementName, onkeydownHandle) {
|
|||
|
var newParagraph = document.createElement(elementName)
|
|||
|
newParagraph.setAttribute("contenteditable", "true")
|
|||
|
newParagraph.setAttribute("data-id", curP.getAttribute("data-id"))
|
|||
|
newParagraph.setAttribute("id", curP.getAttribute("data-id"))
|
|||
|
newParagraph.setAttribute("data-order", curP.getAttribute("data-order"))
|
|||
|
newParagraph.onkeydown = onkeydownHandle
|
|||
|
|
|||
|
//todo 支持 有数据的行 在行首输入 #
|
|||
|
// if()
|
|||
|
// switch (elementName){
|
|||
|
// case "h1":
|
|||
|
// }
|
|||
|
newParagraph.innerHTML = "<br>"
|
|||
|
|
|||
|
insertAfter(newParagraph, curP)
|
|||
|
curP.remove()
|
|||
|
|
|||
|
//matData
|
|||
|
let curNo = parseOrder(curP)
|
|||
|
let mapNode = utils.MyDocMap.get(curNo)
|
|||
|
mapNode.setSource("")
|
|||
|
|
|||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
|||
|
window.getSelection().setPosition(newParagraph, 0)
|
|||
|
}
|
|||
|
|
|||
|
function 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"
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function syncOnePInfo(p) {
|
|||
|
//子元素为空不处理
|
|||
|
if (p.children.length <= 0) {
|
|||
|
return
|
|||
|
}
|
|||
|
// let id = p.getAttribute("id");
|
|||
|
let order = parseInt(p.getAttribute("data-order"));
|
|||
|
let curMapData = this.MyDocMap.get(order);
|
|||
|
//清空重置
|
|||
|
curMapData.getStyle().setChildrenStyleMapNull();
|
|||
|
for (let i = 0; i < p.children.length; i++) {
|
|||
|
let curItem = p.children[i];
|
|||
|
let tmpClassList = curItem.classList;
|
|||
|
if (tmpClassList != null && tmpClassList.length > 0) {
|
|||
|
curMapData.getStyle().setChildrenStyle(i, tmpClassList);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
console.log("curPEle : ", p, " children: ", p.children, " childrenMap: ", curMapData.getStyle().getChildrenStyleMap())
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
window.utils = {
|
|||
|
isNum,
|
|||
|
uuid,
|
|||
|
parseOrder,
|
|||
|
getSelection,
|
|||
|
insertAfter,
|
|||
|
firstCmd,
|
|||
|
becomeAnotherElement,
|
|||
|
parseStyleName2ClassName,
|
|||
|
syncOnePInfo,
|
|||
|
|
|||
|
latestOpDoc,
|
|||
|
num,
|
|||
|
inCompositionEvent,
|
|||
|
MyDocMap,
|
|||
|
MyRoot,
|
|||
|
|
|||
|
MyNode,
|
|||
|
InnerStyle,
|
|||
|
MyKV,
|
|||
|
MyQueue,
|
|||
|
MyRecovery,
|
|||
|
}
|
|||
|
|
|||
|
})()
|