329 lines
12 KiB
JavaScript
329 lines
12 KiB
JavaScript
/** * 样式 */
|
||
(function (utils) {
|
||
function inputHandle(e) {
|
||
if (!utils.inCompositionEvent) {
|
||
updateText(e)
|
||
}
|
||
}
|
||
|
||
function compositionstartHandle(e) {
|
||
// console.log("compositionstart")
|
||
utils.inCompositionEvent = true
|
||
}
|
||
|
||
function compositionendHandle(e) {
|
||
// console.log("compositionend")
|
||
updateText(e)
|
||
utils.inCompositionEvent = false
|
||
}
|
||
|
||
|
||
/**
|
||
* 鼠标按下事件
|
||
*/
|
||
function windowsCtrZHandle(e) {
|
||
const event = e || window.event //标准化事件处理
|
||
const keyCode = event.keyCode || event.which || event.charCode
|
||
const metaKey = event.metaKey
|
||
// console.log("windows键盘事件 ", event, keyCombination, metaKey, keyCode)
|
||
|
||
//撤销
|
||
if (metaKey && keyCode === 90) {
|
||
console.log('触发ctrl + Z 事件', e.target)
|
||
if (utils.latestOpDoc !== undefined && utils.latestOpDoc !== null) {
|
||
// //
|
||
// let cNo = parseInt(utils.latestOpDoc.getAttribute("data-order"))
|
||
// console.log("恢复", utils.latestOpDoc, cNo)
|
||
// if (cNo > 1) {
|
||
// let qq = "#noteshare p[data-order='" + (cNo - 1) + "']"
|
||
// // console.log("qq: ", qq)
|
||
// utils.insertAfter(utils.latestOpDoc, document.querySelector(qq))
|
||
// } else {
|
||
// //添加元素到首位
|
||
// yanxuelu.insertBefore(utils.latestOpDoc, utils.MyRoot.children[0])
|
||
// }
|
||
// utils.MyDocMap.get(cNo).setHidden(false)
|
||
|
||
//恢复
|
||
utils.latestOpDoc.recovery();
|
||
|
||
// utils.MyDocMap.get(cNo).setSource(latestOpDoc.innerText)
|
||
//
|
||
showTestText()
|
||
|
||
event.preventDefault()
|
||
event.returnValue = false
|
||
}
|
||
|
||
utils.latestOpDoc = null
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* 鼠标按下事件 & 键盘组合事件
|
||
* @param {*} e
|
||
*/
|
||
function onkeydownHandle(e) {
|
||
const event = e || window.event //标准化事件处理
|
||
// window.event 等价于 event参数
|
||
// 有些浏览器除了通过keyCode获取输入键code,还可以通过which,charCode获取,这么写是出于浏览器兼容性考虑
|
||
const keyCode = event.keyCode || event.which || event.charCode
|
||
const keyCombination = event.ctrlKey
|
||
const metaKey = event.metaKey
|
||
// console.log("键盘事件 ", event, keyCombination, metaKey, keyCode)
|
||
|
||
// ctrl + c 复制
|
||
if (keyCombination && keyCode === 67) {
|
||
// 阻止默认事件
|
||
event.preventDefault()
|
||
event.returnValue = false
|
||
console.log('触发ctrl + c 事件', e.target)
|
||
}
|
||
|
||
//撤销
|
||
if (metaKey && keyCode === 90) {
|
||
// console.log('触发ctrl + Z 事件', e.target)
|
||
if (utils.latestOpDoc !== undefined && utils.latestOpDoc !== null) {
|
||
// console.log(utils.latestOpDoc)
|
||
//恢复
|
||
utils.latestOpDoc.recovery();
|
||
//
|
||
// console.log(MyDocMap.get(cNo))
|
||
let cNo = parseInt(utils.latestOpDoc.getData().getAttribute("data-order"))
|
||
utils.MyDocMap.get(cNo).setHidden(false)
|
||
// utils.MyDocMap.get(cNo).setSource(latestOpDoc.innerText)
|
||
//
|
||
showTestText()
|
||
|
||
//阻止事件
|
||
event.preventDefault()
|
||
event.returnValue = false
|
||
}
|
||
|
||
utils.latestOpDoc = null
|
||
}
|
||
|
||
//删除
|
||
if (keyCode === 46 || keyCode === 8) {
|
||
let curP = event.target
|
||
let cNo = parseInt(curP.getAttribute("data-order"))
|
||
|
||
//维护最近一次编辑的内容
|
||
if (utils.latestOpDoc === undefined || utils.latestOpDoc === null || utils.latestOpDoc.getData().getAttribute("data-id") !== curP.getAttribute("data-id")) {
|
||
utils.latestOpDoc = new utils.MyRecovery(curP.cloneNode(true), function () {
|
||
let cNo = parseInt(this.data.getAttribute("data-order"))
|
||
console.log("恢复", this.data, cNo)
|
||
if (cNo > 1) {
|
||
utils.insertAfter(this.data, document.querySelector("#noteshare p[data-order='" + (cNo - 1) + "']"))
|
||
} else {
|
||
//添加元素到首位
|
||
utils.MyRoot.insertBefore(this.data, utils.MyRoot.children[0])
|
||
}
|
||
|
||
// 恢复该元素展示
|
||
utils.MyDocMap.get(cNo).setHidden(false)
|
||
})
|
||
}
|
||
|
||
|
||
//如果是第一行
|
||
let previousSibling = curP.previousSibling
|
||
// console.log(curP, previousSibling == undefined ,previousSibling.id == undefined)
|
||
if (previousSibling === undefined || previousSibling.id === undefined) {
|
||
//显示用户的输入内容
|
||
showTestText()
|
||
return
|
||
}
|
||
|
||
|
||
// console.log('触发删除', curP.innerHTML, cNo, utils.num, utils.MyDocMap.get(cNo + 1))
|
||
let curS = window.getSelection()
|
||
// console.log("当前内容: ", curP.innerHTML, " 当前选区 :", curS)
|
||
//处理前面没有内容,后面还有内容需要拼接到上层的场景
|
||
if ((curS.isCollapsed && curS.anchorOffset === 0) || curP.innerHTML === '<br>') {
|
||
let curNodeRetainHtml = curP.innerHTML
|
||
|
||
//异步修正顺序(第一行删除不掉)
|
||
// if (curP.innerHTML === '<br>' ) {
|
||
// console.log("删除当前行。选取信息: ", curS)
|
||
//阻止事件传递
|
||
event.preventDefault()
|
||
event.returnValue = false
|
||
|
||
//设置该元素隐藏
|
||
utils.MyDocMap.get(cNo).setHidden(true)
|
||
//删除当前元素
|
||
curP.remove()
|
||
//拼接
|
||
if (curNodeRetainHtml !== '<br>') {
|
||
previousSibling.innerHTML = previousSibling.innerHTML + curNodeRetainHtml
|
||
}
|
||
|
||
//收起选区到一个点,光标落在一个可编辑元素上
|
||
window.getSelection().setPosition(previousSibling, 1);
|
||
}
|
||
|
||
//显示用户的输入内容
|
||
showTestText();
|
||
}
|
||
|
||
//回车事件
|
||
if (keyCode === 13 /* && currentNode === key.lastElementChild */) {
|
||
event.preventDefault()
|
||
let uuid = utils.uuid()
|
||
let curOrder = ++(utils.num)
|
||
var newParagraph = document.createElement("p")
|
||
newParagraph.setAttribute("contenteditable", "true")
|
||
newParagraph.setAttribute("data-id", uuid)
|
||
newParagraph.setAttribute("id", uuid)
|
||
newParagraph.setAttribute("data-order", curOrder)
|
||
newParagraph.onkeydown = onkeydownHandle
|
||
newParagraph.focus()
|
||
newParagraph.innerHTML = "<br>"
|
||
utils.MyRoot.appendChild(newParagraph)
|
||
utils.MyDocMap.set(curOrder, new utils.MyNode(uuid))
|
||
|
||
//收起选区到一个点,光标落在一个可编辑元素上
|
||
window.getSelection().setPosition(newParagraph, 0)
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
*/
|
||
function showTestText() {
|
||
//显示用户的输入内容
|
||
const obj = {};
|
||
for ([k, v] of utils.MyDocMap.entries()) {
|
||
if (v.getHidden() && v.getHidden() === true) {
|
||
continue
|
||
}
|
||
obj[k] = v
|
||
}
|
||
|
||
// console.log("当前文档结构数: ",obj);
|
||
|
||
// var userInput = document.getElementById("testInput")
|
||
// userInput.value = JSON.stringify(obj)
|
||
}
|
||
|
||
/**
|
||
* 更新文档
|
||
* @param {*} e
|
||
*/
|
||
function updateText(e) {
|
||
let curP = e.target
|
||
|
||
//是否需要变更元素类型
|
||
utils.firstCmd(curP, onkeydownHandle)
|
||
|
||
|
||
let cNo = parseInt(curP.getAttribute("data-order"))
|
||
let cp = utils.MyDocMap.get(cNo)
|
||
|
||
//内容不变则不处理
|
||
let h5CurLen = curP.innerText.length
|
||
let myDocNodeLen = (cp && cp.getSource()) ? cp.getSource().length : 0
|
||
if (h5CurLen === myDocNodeLen) {
|
||
return
|
||
}
|
||
|
||
cp.setSource(curP.innerText)
|
||
// console.log("curPTx: ", curP.innerText, "MyDocMap : ", utils.MyDocMap)
|
||
|
||
//显示用户的输入内容
|
||
showTestText()
|
||
}
|
||
|
||
|
||
function surroundContentsByStyle() {
|
||
let curS = utils.getSelection()
|
||
let curSec = curS.getRangeAt(0)
|
||
let styleName = this.getAttribute("data-value");
|
||
let className = utils.parseStyleName2ClassName(styleName)
|
||
//todo 只对 nodeType = p 执行
|
||
// console.log("当前光标信息: ", curS, " 当前选区信息 : ", curSec)
|
||
|
||
for (let i = 0; i < curS.rangeCount; i++) {
|
||
let curSec = curS.getRangeAt(i)
|
||
|
||
let curEleSize = curSec.commonAncestorContainer.childNodes.length
|
||
|
||
let curStartP = curSec.startContainer.parentElement
|
||
//todo 先支持 一行。
|
||
let start = curSec.startOffset;
|
||
let end = curSec.endOffset;
|
||
// console.log(curStartP.cloneNode(true), styleName, start, end, " curEleSize: ", curEleSize)
|
||
|
||
let curPEle = null;
|
||
//第一次选择的时候将整行转换成<p><span></span></p>
|
||
if (curEleSize === 0) {
|
||
//没选择,则退出
|
||
if (start === end) {
|
||
return
|
||
}
|
||
// console.log("debug1: ", curSec.commonAncestorContainer)
|
||
//维护最近一次编辑的内容(暂时只支持恢复最近一次编辑)
|
||
utils.latestOpDoc = new utils.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
|
||
utils.syncOnePInfo(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 contenteditable="true" class=' + className + '>' + curStartP.innerText.charAt(j) + '</span>';
|
||
} else {
|
||
curHtml += '<span>' + curStartP.innerText.charAt(j) + '</span>';
|
||
}
|
||
}
|
||
curStartP.innerHTML = curHtml;
|
||
|
||
curPEle = curStartP;
|
||
} else {
|
||
// console.log("debug2: ", curSec.commonAncestorContainer)
|
||
//维护最近一次编辑的内容(暂时只支持恢复最近一次编辑)
|
||
utils.latestOpDoc = new utils.MyRecovery(curSec.commonAncestorContainer.cloneNode(true), function () {
|
||
console.log("恢复上一步样式1", this.data)
|
||
let curEl = document.getElementById(this.data.getAttribute("id"));
|
||
curEl.innerHTML = this.data.innerHTML;
|
||
//文本映射 直接覆盖 map 中的 childrenStyle
|
||
utils.syncOnePInfo(this.data);
|
||
})
|
||
|
||
let myChildren = curSec.commonAncestorContainer.children
|
||
for (let j = 0; j < curEleSize; j++) {
|
||
let curEle = myChildren[j]
|
||
if (curS.containsNode(curEle, true)) {
|
||
curEle.classList.remove(className);
|
||
curEle.classList.add(className);
|
||
}
|
||
}
|
||
|
||
curPEle = curSec.commonAncestorContainer;
|
||
}
|
||
|
||
|
||
//文本映射 直接覆盖 map 中的 childrenStyle
|
||
utils.syncOnePInfo(curPEle)
|
||
}
|
||
}
|
||
|
||
window.styleCmd = {
|
||
inputHandle, compositionstartHandle, compositionendHandle,
|
||
onkeydownHandle, windowsCtrZHandle,
|
||
surroundContentsByStyle,
|
||
|
||
showTestText,
|
||
updateText,
|
||
|
||
}
|
||
|
||
})(utils) |