wangeditor
Version:
wangEditor - 轻量级 web 富文本编辑器,配置方便,使用简单,开源免费
170 lines (161 loc) • 5.38 kB
text/typescript
/**
* @description 数据整理
* @author fangzhicong
*/
import { TargetPosition, DiffNodes, Compile } from '../type'
import { UA, toArray } from '../../../../utils/util'
/**
* 数据类型
*/
export function compileType(data: string) {
switch (data) {
case 'childList':
return 'node'
case 'attributes':
return 'attr'
default:
return 'text'
}
}
/**
* 获取当前的文本内容
*/
export function compileValue(data: MutationRecord) {
switch (data.type) {
case 'attributes':
return (data.target as Element).getAttribute(data.attributeName as string) || ''
case 'characterData':
return data.target.textContent
default:
return ''
}
}
/**
* addedNodes/removedNodes
*/
export function complieNodes(data: MutationRecord) {
const temp: DiffNodes = {}
if (data.addedNodes.length) {
temp.add = toArray(data.addedNodes)
}
if (data.removedNodes.length) {
temp.remove = toArray(data.removedNodes)
}
return temp
}
/**
* addedNodes/removedNodes 的相对位置
*/
export function compliePosition(data: MutationRecord) {
let temp: TargetPosition
if (data.previousSibling) {
temp = {
type: 'before',
target: data.previousSibling,
}
} else if (data.nextSibling) {
temp = {
type: 'after',
target: data.nextSibling,
}
} else {
temp = {
type: 'parent',
target: data.target,
}
}
return temp
}
/**
* 补全 Firefox 数据的特殊标签
*/
const tag = ['UL', 'OL', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6']
/**
* 将 MutationRecord 转换成自定义格式的数据
*/
export default function compile(data: MutationRecord[]) {
const temp: Compile[] = []
// 以下两个变量是兼容 Firefox 时使用到的
// 前一次操作为删除元素节点
let removeNode: Node | false = false
// 连续的节点删除记录
const removeCache: Node[] = []
data.forEach((record, index) => {
const item: Compile = {
type: compileType(record.type),
target: record.target,
attr: record.attributeName || '',
value: compileValue(record) || '',
oldValue: record.oldValue || '',
nodes: complieNodes(record),
position: compliePosition(record),
}
temp.push(item)
// 兼容 Firefox,补全数据(这几十行代码写得吐血,跟 IE 有得一拼)
if (!UA.isFirefox) {
return
}
// 正常的数据:缩进、行高、超链接、对齐方式、引用、插入表情、插入图片、分割线、表格、插入代码
// 普通的数据补全:标题(纯文本内容)、加粗、斜体、删除线、下划线、颜色、背景色、字体、字号、列表(纯文本内容)
// 特殊的数据补全:标题(包含 HTMLElement)、列表(包含 HTMLElement 或 ul -> ol 或 ol -> ul 或 Enter)
if (removeNode && record.addedNodes.length && record.addedNodes[0].nodeType == 1) {
// 需要被全数据的目标节点
const replenishNode = record.addedNodes[0]
const replenishData: Compile = {
type: 'node',
target: replenishNode,
attr: '',
value: '',
oldValue: '',
nodes: {
add: [removeNode],
},
position: {
type: 'parent',
target: replenishNode,
},
}
// 特殊的标签:['UL', 'OL', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6']
if (tag.indexOf(replenishNode.nodeName) != -1) {
replenishData.nodes.add = toArray(replenishNode.childNodes)
temp.push(replenishData)
}
// 上一个删除元素是文本节点
else if (removeNode.nodeType == 3) {
if (contains(replenishNode, removeCache)) {
replenishData.nodes.add = toArray(replenishNode.childNodes)
}
temp.push(replenishData)
}
// 上一个删除元素是 Element && 由近到远的删除元素至少有一个是需要补全数据节点的子节点
else if (
tag.indexOf(record.target.nodeName) == -1 &&
contains(replenishNode, removeCache)
) {
replenishData.nodes.add = toArray(replenishNode.childNodes)
temp.push(replenishData)
}
}
// 记录本次的节点信息
if (item.type == 'node' && record.removedNodes.length == 1) {
removeNode = record.removedNodes[0]
removeCache.push(removeNode)
} else {
removeNode = false
removeCache.length = 0
}
})
return temp
}
// 删除元素的历史记录中包含有多少个目标节点的子元素
function contains(tar: Node, childs: Node[]) {
let count = 0
for (let i = childs.length - 1; i > 0; i--) {
if (tar.contains(childs[i])) {
count++
} else {
break
}
}
return count
}