@kaitify/core 0.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/examples/App.vue +342 -0
- package/examples/content.js +1 -0
- package/examples/main.ts +4 -0
- package/examples/test.html +23 -0
- package/lib/extensions/Extension.d.ts +172 -0
- package/lib/extensions/align/index.d.ts +10 -0
- package/lib/extensions/attachment/index.d.ts +29 -0
- package/lib/extensions/back-color/index.d.ts +9 -0
- package/lib/extensions/blockquote/index.d.ts +12 -0
- package/lib/extensions/bold/index.d.ts +9 -0
- package/lib/extensions/code/index.d.ts +12 -0
- package/lib/extensions/code-block/hljs.d.ts +12 -0
- package/lib/extensions/code-block/index.d.ts +15 -0
- package/lib/extensions/color/index.d.ts +9 -0
- package/lib/extensions/font-family/index.d.ts +9 -0
- package/lib/extensions/font-size/index.d.ts +9 -0
- package/lib/extensions/heading/index.d.ts +13 -0
- package/lib/extensions/history/index.d.ts +10 -0
- package/lib/extensions/horizontal/index.d.ts +7 -0
- package/lib/extensions/image/index.d.ts +26 -0
- package/lib/extensions/indent/index.d.ts +8 -0
- package/lib/extensions/index.d.ts +29 -0
- package/lib/extensions/italic/index.d.ts +9 -0
- package/lib/extensions/line-height/index.d.ts +9 -0
- package/lib/extensions/link/index.d.ts +27 -0
- package/lib/extensions/list/index.d.ts +18 -0
- package/lib/extensions/math/index.d.ts +11 -0
- package/lib/extensions/strikethrough/index.d.ts +9 -0
- package/lib/extensions/subscript/index.d.ts +9 -0
- package/lib/extensions/superscript/index.d.ts +9 -0
- package/lib/extensions/table/index.d.ts +21 -0
- package/lib/extensions/task/index.d.ts +12 -0
- package/lib/extensions/text/index.d.ts +14 -0
- package/lib/extensions/underline/index.d.ts +9 -0
- package/lib/extensions/video/index.d.ts +27 -0
- package/lib/index.d.ts +3 -0
- package/lib/kaitify-core.es.js +38337 -0
- package/lib/kaitify-core.umd.js +2 -0
- package/lib/model/Editor.d.ts +504 -0
- package/lib/model/History.d.ts +42 -0
- package/lib/model/KNode.d.ts +258 -0
- package/lib/model/Selection.d.ts +29 -0
- package/lib/model/config/dom-observe.d.ts +10 -0
- package/lib/model/config/event-handler.d.ts +33 -0
- package/lib/model/config/format-patch.d.ts +25 -0
- package/lib/model/config/format-rules.d.ts +37 -0
- package/lib/model/config/function.d.ts +84 -0
- package/lib/model/index.d.ts +6 -0
- package/lib/tools/index.d.ts +49 -0
- package/lib/view/index.d.ts +21 -0
- package/lib/view/js-render/dom-patch.d.ts +65 -0
- package/lib/view/js-render/index.d.ts +5 -0
- package/package.json +52 -0
- package/src/css/style.less +56 -0
- package/src/css/var.less +45 -0
- package/src/extensions/Extension.ts +200 -0
- package/src/extensions/align/index.ts +115 -0
- package/src/extensions/attachment/icon.svg +1 -0
- package/src/extensions/attachment/index.ts +293 -0
- package/src/extensions/attachment/style.less +25 -0
- package/src/extensions/back-color/index.ts +56 -0
- package/src/extensions/blockquote/index.ts +144 -0
- package/src/extensions/blockquote/style.less +16 -0
- package/src/extensions/bold/index.ts +77 -0
- package/src/extensions/code/index.ts +295 -0
- package/src/extensions/code/style.less +14 -0
- package/src/extensions/code-block/hljs.less +183 -0
- package/src/extensions/code-block/hljs.ts +95 -0
- package/src/extensions/code-block/index.ts +308 -0
- package/src/extensions/code-block/style.less +20 -0
- package/src/extensions/color/index.ts +56 -0
- package/src/extensions/font-family/index.ts +80 -0
- package/src/extensions/font-size/index.ts +56 -0
- package/src/extensions/heading/index.ts +164 -0
- package/src/extensions/heading/style.less +42 -0
- package/src/extensions/history/index.ts +96 -0
- package/src/extensions/horizontal/index.ts +45 -0
- package/src/extensions/horizontal/style.less +13 -0
- package/src/extensions/image/index.ts +242 -0
- package/src/extensions/image/style.less +8 -0
- package/src/extensions/indent/index.ts +98 -0
- package/src/extensions/index.ts +29 -0
- package/src/extensions/italic/index.ts +77 -0
- package/src/extensions/line-height/index.ts +113 -0
- package/src/extensions/link/index.ts +184 -0
- package/src/extensions/link/style.less +19 -0
- package/src/extensions/list/index.ts +410 -0
- package/src/extensions/list/style.less +19 -0
- package/src/extensions/math/index.ts +233 -0
- package/src/extensions/math/style.less +21 -0
- package/src/extensions/strikethrough/index.ts +78 -0
- package/src/extensions/subscript/index.ts +77 -0
- package/src/extensions/superscript/index.ts +77 -0
- package/src/extensions/table/index.ts +1148 -0
- package/src/extensions/table/style.less +71 -0
- package/src/extensions/task/index.ts +243 -0
- package/src/extensions/task/style.less +59 -0
- package/src/extensions/text/index.ts +359 -0
- package/src/extensions/underline/index.ts +78 -0
- package/src/extensions/video/index.ts +273 -0
- package/src/extensions/video/style.less +8 -0
- package/src/index.ts +9 -0
- package/src/model/Editor.ts +1963 -0
- package/src/model/History.ts +115 -0
- package/src/model/KNode.ts +677 -0
- package/src/model/Selection.ts +39 -0
- package/src/model/config/dom-observe.ts +184 -0
- package/src/model/config/event-handler.ts +237 -0
- package/src/model/config/format-patch.ts +215 -0
- package/src/model/config/format-rules.ts +218 -0
- package/src/model/config/function.ts +1018 -0
- package/src/model/index.ts +6 -0
- package/src/tools/index.ts +156 -0
- package/src/view/index.ts +46 -0
- package/src/view/js-render/dom-patch.ts +324 -0
- package/src/view/js-render/index.ts +210 -0
- package/vite-env.d.ts +2 -0
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
//这里放的都是和编辑器相关的方法,但是不想对外暴露的
|
|
2
|
+
import { common as DapCommon, file as DapFile } from 'dap-util'
|
|
3
|
+
import { Extension } from '@/extensions'
|
|
4
|
+
import { isContains } from '@/tools'
|
|
5
|
+
import { Editor } from '../Editor'
|
|
6
|
+
import { KNode, KNodeMarksType, KNodeStylesType } from '../KNode'
|
|
7
|
+
import { RuleFunctionType } from './format-rules'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 获取选区内的可聚焦节点所在的块节点数组
|
|
11
|
+
*/
|
|
12
|
+
export const getSelectionBlockNodes = function (this: Editor) {
|
|
13
|
+
const focusNodes = this.getFocusNodesBySelection('all')
|
|
14
|
+
const blockNodes: KNode[] = []
|
|
15
|
+
focusNodes.forEach(item => {
|
|
16
|
+
const blockNode = item.getBlock()
|
|
17
|
+
if (!blockNodes.some(node => node.isEqual(blockNode))) {
|
|
18
|
+
blockNodes.push(blockNode)
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
return blockNodes
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 打散指定的节点,将其分裂成多个节点,如果子孙节点还有子节点则继续打散
|
|
26
|
+
*/
|
|
27
|
+
export const splitNodeToNodes = function (this: Editor, node: KNode) {
|
|
28
|
+
if (node.hasChildren()) {
|
|
29
|
+
node.children!.forEach(item => {
|
|
30
|
+
if (!item.isClosed()) {
|
|
31
|
+
item.marks = { ...(item.marks || {}), ...(node.marks || {}) }
|
|
32
|
+
item.styles = { ...(item.styles || {}), ...(node.styles || {}) }
|
|
33
|
+
}
|
|
34
|
+
this.addNodeBefore(item, node)
|
|
35
|
+
splitNodeToNodes.apply(this, [item])
|
|
36
|
+
})
|
|
37
|
+
node.children = []
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 清空固定块节点的内容
|
|
43
|
+
*/
|
|
44
|
+
export const emptyFixedBlock = function (this: Editor, node: KNode) {
|
|
45
|
+
if (!node.isBlock()) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
if (node.hasChildren()) {
|
|
49
|
+
node.children!.forEach(item => {
|
|
50
|
+
//如果是固定的块节点
|
|
51
|
+
if (item.isBlock() && item.fixed) {
|
|
52
|
+
emptyFixedBlock.apply(this, [item])
|
|
53
|
+
}
|
|
54
|
+
//其他情况下
|
|
55
|
+
else {
|
|
56
|
+
item.toEmpty()
|
|
57
|
+
if (item.parent!.isEmpty()) {
|
|
58
|
+
const placeholderNode = KNode.createPlaceholder()
|
|
59
|
+
this.addNode(placeholderNode, item.parent!)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 该方法目前只为delete方法内部使用:将后一个块节点与前一个块节点合并
|
|
68
|
+
*/
|
|
69
|
+
export const mergeBlock = function (this: Editor, node: KNode, target: KNode) {
|
|
70
|
+
//不是块节点则不处理
|
|
71
|
+
if (!node.isBlock() || !target.isBlock()) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
//空节点不处理
|
|
75
|
+
if (node.isEmpty() || target.isEmpty()) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
const uneditableNode = node.getUneditable()
|
|
79
|
+
//是用户操作的删除行为并且前一个块节点是不可编辑的,则直接删除前一个块节点
|
|
80
|
+
if (this.isUserDelection && uneditableNode) {
|
|
81
|
+
uneditableNode.toEmpty()
|
|
82
|
+
}
|
|
83
|
+
//否则走正常删除逻辑
|
|
84
|
+
else {
|
|
85
|
+
const nodes = target.children!.map(item => {
|
|
86
|
+
item.parent = node
|
|
87
|
+
return item
|
|
88
|
+
})
|
|
89
|
+
node.children!.push(...nodes)
|
|
90
|
+
target.children = []
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 判断编辑器内的指定节点是否可以进行合并操作,parent表示和父节点进行合并,prevSibling表示和前一个兄弟节点进行合并,nextSibling表示和下一个兄弟节点合并,如果可以返回合并的对象节点
|
|
96
|
+
*/
|
|
97
|
+
export const getAllowMergeNode = function (this: Editor, node: KNode, type: 'parent' | 'prevSibling' | 'nextSibling') {
|
|
98
|
+
//排除空节点
|
|
99
|
+
if (node.isEmpty()) {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
//排除没有父节点的节点
|
|
103
|
+
if (!node.parent) {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
//排除锁定的节点
|
|
107
|
+
if (node.locked) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
//与前一个兄弟节点合并
|
|
111
|
+
if (type == 'prevSibling') {
|
|
112
|
+
const previousNode = node.getPrevious(node.parent.children!)
|
|
113
|
+
//没有兄弟节点
|
|
114
|
+
if (!previousNode) {
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
//文本节点
|
|
118
|
+
if (node.isText()) {
|
|
119
|
+
//可以和前一个节点合并
|
|
120
|
+
if (previousNode.isText() && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
|
|
121
|
+
return previousNode
|
|
122
|
+
}
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
//行内节点
|
|
126
|
+
if (node.isInline()) {
|
|
127
|
+
//可以和前一个节点合并
|
|
128
|
+
if (previousNode.isInline() && previousNode.tag == node.tag && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
|
|
129
|
+
return previousNode
|
|
130
|
+
}
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
//与后一个兄弟节点合并
|
|
136
|
+
if (type == 'nextSibling') {
|
|
137
|
+
const nextNode = node.getNext(node.parent.children!)
|
|
138
|
+
//没有兄弟节点
|
|
139
|
+
if (!nextNode) {
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
//文本节点
|
|
143
|
+
if (node.isText()) {
|
|
144
|
+
//可以和后一个节点合并
|
|
145
|
+
if (nextNode.isText() && nextNode.isEqualMarks(node) && nextNode.isEqualStyles(node)) {
|
|
146
|
+
return nextNode
|
|
147
|
+
}
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
//行内节点
|
|
151
|
+
if (node.isInline()) {
|
|
152
|
+
//可以和后一个节点合并
|
|
153
|
+
if (nextNode.isInline() && nextNode.tag == node.tag && nextNode.isEqualMarks(node) && nextNode.isEqualStyles(node)) {
|
|
154
|
+
return nextNode
|
|
155
|
+
}
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
return null
|
|
159
|
+
}
|
|
160
|
+
//父子节点合并
|
|
161
|
+
if (type == 'parent') {
|
|
162
|
+
//父节点不止一个子节点
|
|
163
|
+
if (node.parent!.children!.length > 1) {
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
//文本节点
|
|
167
|
+
if (node.isText()) {
|
|
168
|
+
//父节点是行内节点,并且渲染标签是文本标签
|
|
169
|
+
if (node.parent!.isInline() && node.parent!.tag == this.textRenderTag) {
|
|
170
|
+
return node.parent!
|
|
171
|
+
}
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
174
|
+
//行内节点和块节点,如果渲染标签一致并且类型一致
|
|
175
|
+
if (node.type == node.parent!.type && node.tag == node.parent!.tag) {
|
|
176
|
+
return node.parent!
|
|
177
|
+
}
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 对编辑器内的某个节点执行合并操作,parent表示和父节点进行合并,prevSibling表示和前一个兄弟节点进行合并,nextSibling表示和下一个兄弟节点合并(可能会更新光标)
|
|
185
|
+
*/
|
|
186
|
+
export const applyMergeNode = function (this: Editor, node: KNode, type: 'parent' | 'prevSibling' | 'nextSibling') {
|
|
187
|
+
//合并的对象节点
|
|
188
|
+
const targetNode = getAllowMergeNode.apply(this, [node, type])
|
|
189
|
+
if (!targetNode) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
//和前一个兄弟节点合并
|
|
193
|
+
if (type == 'prevSibling') {
|
|
194
|
+
//文本节点
|
|
195
|
+
if (node.isText()) {
|
|
196
|
+
//起点在前一个节点上
|
|
197
|
+
if (this.isSelectionInNode(targetNode, 'start')) {
|
|
198
|
+
this.selection.start!.node = node
|
|
199
|
+
}
|
|
200
|
+
//终点在前一个节点上
|
|
201
|
+
if (this.isSelectionInNode(targetNode, 'end')) {
|
|
202
|
+
this.selection.end!.node = node
|
|
203
|
+
}
|
|
204
|
+
//将前一个节点的文本内容给后一个节点
|
|
205
|
+
node.textContent = targetNode.textContent! + node.textContent!
|
|
206
|
+
//删除被合并的节点
|
|
207
|
+
const index = targetNode.parent!.children!.findIndex(item => {
|
|
208
|
+
return targetNode.isEqual(item)
|
|
209
|
+
})
|
|
210
|
+
targetNode.parent!.children!.splice(index, 1)
|
|
211
|
+
}
|
|
212
|
+
//行内节点
|
|
213
|
+
else if (node.isInline()) {
|
|
214
|
+
//合并前一个节点的子节点数组
|
|
215
|
+
node.children = [...targetNode.children!, ...node.children!].map(item => {
|
|
216
|
+
item.parent = node
|
|
217
|
+
return item
|
|
218
|
+
})
|
|
219
|
+
//删除被合并的节点
|
|
220
|
+
const index = targetNode.parent!.children!.findIndex(item => {
|
|
221
|
+
return targetNode.isEqual(item)
|
|
222
|
+
})
|
|
223
|
+
targetNode.parent!.children!.splice(index, 1)
|
|
224
|
+
//继续对子节点进行合并
|
|
225
|
+
if (node.hasChildren() && node.children!.length > 1) {
|
|
226
|
+
let index = 0
|
|
227
|
+
//因为父子节点的合并操作会导致children没有,此时判断一下hasChildren
|
|
228
|
+
while (node.hasChildren() && index <= node.children!.length - 2) {
|
|
229
|
+
const newTargetNode = getAllowMergeNode.apply(this, [node.children![index], 'nextSibling'])
|
|
230
|
+
if (newTargetNode) {
|
|
231
|
+
applyMergeNode.apply(this, [node.children![index], 'nextSibling'])
|
|
232
|
+
//子节点合并后可能只有一个子节点了,此时进行父子节点合并操作
|
|
233
|
+
if (node.hasChildren() && node.children!.length == 1) {
|
|
234
|
+
applyMergeNode.apply(this, [node.children![0], 'parent'])
|
|
235
|
+
}
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
index++
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//和后一个节点合并
|
|
244
|
+
if (type == 'nextSibling') {
|
|
245
|
+
//文本节点
|
|
246
|
+
if (node.isText()) {
|
|
247
|
+
//起点在后一个节点上
|
|
248
|
+
if (this.isSelectionInNode(targetNode, 'start')) {
|
|
249
|
+
this.selection.start!.node = node
|
|
250
|
+
this.selection.start!.offset = node.textContent!.length + this.selection.start!.offset
|
|
251
|
+
}
|
|
252
|
+
//终点在后一个节点上
|
|
253
|
+
if (this.isSelectionInNode(targetNode, 'end')) {
|
|
254
|
+
this.selection.end!.node = node
|
|
255
|
+
this.selection.end!.offset = node.textContent!.length + this.selection.end!.offset
|
|
256
|
+
}
|
|
257
|
+
//将后一个节点的文本内容给前一个节点
|
|
258
|
+
node.textContent! += targetNode.textContent!
|
|
259
|
+
//删除被合并的节点
|
|
260
|
+
const index = targetNode.parent!.children!.findIndex(item => {
|
|
261
|
+
return targetNode.isEqual(item)
|
|
262
|
+
})
|
|
263
|
+
targetNode.parent!.children!.splice(index, 1)
|
|
264
|
+
}
|
|
265
|
+
//行内节点
|
|
266
|
+
else if (node.isInline()) {
|
|
267
|
+
//合并后一个节点的子节点数组
|
|
268
|
+
node.children = [...node.children!, ...targetNode.children!].map(item => {
|
|
269
|
+
item.parent = node
|
|
270
|
+
return item
|
|
271
|
+
})
|
|
272
|
+
//删除被合并的节点
|
|
273
|
+
const index = targetNode.parent!.children!.findIndex(item => {
|
|
274
|
+
return targetNode.isEqual(item)
|
|
275
|
+
})
|
|
276
|
+
targetNode.parent!.children!.splice(index, 1)
|
|
277
|
+
//继续对子节点进行合并
|
|
278
|
+
if (node.hasChildren() && node.children!.length > 1) {
|
|
279
|
+
let index = 0
|
|
280
|
+
//因为父子节点的合并操作会导致children没有,此时判断一下hasChildren
|
|
281
|
+
while (node.hasChildren() && index <= node.children!.length - 2) {
|
|
282
|
+
const newTargetNode = getAllowMergeNode.apply(this, [node.children![index], 'nextSibling'])
|
|
283
|
+
if (newTargetNode) {
|
|
284
|
+
applyMergeNode.apply(this, [node.children![index], 'nextSibling'])
|
|
285
|
+
//子节点合并后可能只有一个子节点了,此时进行父子节点合并操作
|
|
286
|
+
if (node.hasChildren() && node.children!.length == 1) {
|
|
287
|
+
applyMergeNode.apply(this, [node.children![0], 'parent'])
|
|
288
|
+
}
|
|
289
|
+
continue
|
|
290
|
+
}
|
|
291
|
+
index++
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
//父子节点合并
|
|
297
|
+
if (type == 'parent') {
|
|
298
|
+
//文本节点
|
|
299
|
+
if (node.isText()) {
|
|
300
|
+
targetNode.type = 'text'
|
|
301
|
+
targetNode.tag = undefined
|
|
302
|
+
//如果子节点有标记
|
|
303
|
+
if (node.hasMarks()) {
|
|
304
|
+
if (targetNode.hasMarks()) {
|
|
305
|
+
Object.assign(targetNode.marks!, DapCommon.clone(node.marks!))
|
|
306
|
+
} else {
|
|
307
|
+
targetNode.marks = DapCommon.clone(node.marks!)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
//如果子节点有样式
|
|
311
|
+
if (node.hasStyles()) {
|
|
312
|
+
if (targetNode.hasStyles()) {
|
|
313
|
+
Object.assign(targetNode.styles!, DapCommon.clone(node.styles!))
|
|
314
|
+
} else {
|
|
315
|
+
targetNode.styles = DapCommon.clone(node.styles!)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
targetNode.textContent = node.textContent
|
|
319
|
+
targetNode.children = undefined
|
|
320
|
+
//如果起点在子节点上则更新到父节点上
|
|
321
|
+
if (this.isSelectionInNode(node, 'start')) {
|
|
322
|
+
this.selection.start!.node = targetNode
|
|
323
|
+
}
|
|
324
|
+
//如果终点在子节点上则更新到父节点上
|
|
325
|
+
if (this.isSelectionInNode(node, 'end')) {
|
|
326
|
+
this.selection.end!.node = targetNode
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
//行内节点或者块节点
|
|
330
|
+
else {
|
|
331
|
+
//如果子节点有标记
|
|
332
|
+
if (node.hasMarks()) {
|
|
333
|
+
if (targetNode.hasMarks()) {
|
|
334
|
+
Object.assign(targetNode.marks!, DapCommon.clone(node.marks))
|
|
335
|
+
} else {
|
|
336
|
+
targetNode.marks = DapCommon.clone(node.marks)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
//如果子节点有样式
|
|
340
|
+
if (node.hasStyles()) {
|
|
341
|
+
if (targetNode.hasStyles()) {
|
|
342
|
+
Object.assign(targetNode.styles!, DapCommon.clone(node.styles))
|
|
343
|
+
} else {
|
|
344
|
+
targetNode.styles = DapCommon.clone(node.styles)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//如果子节点也有子节点
|
|
348
|
+
if (node.hasChildren()) {
|
|
349
|
+
targetNode.children = [...node.children!]
|
|
350
|
+
targetNode.children.forEach(item => {
|
|
351
|
+
item.parent = targetNode
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
//子节点与父节点合并后再对父节点进行处理
|
|
355
|
+
if (targetNode.hasChildren() && targetNode.children!.length == 1) {
|
|
356
|
+
//再次父子节点进行合并
|
|
357
|
+
if (getAllowMergeNode.apply(this, [targetNode.children![0], 'parent'])) {
|
|
358
|
+
applyMergeNode.apply(this, [targetNode.children![0], 'parent'])
|
|
359
|
+
//父子节点合并后,可能父节点需要再和兄弟节点进行合并
|
|
360
|
+
if (getAllowMergeNode.apply(this, [targetNode, 'prevSibling'])) {
|
|
361
|
+
applyMergeNode.apply(this, [targetNode, 'prevSibling'])
|
|
362
|
+
} else if (getAllowMergeNode.apply(this, [targetNode, 'nextSibling'])) {
|
|
363
|
+
applyMergeNode.apply(this, [targetNode, 'nextSibling'])
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 将编辑器内的某个非块级节点转为默认块级节点
|
|
373
|
+
*/
|
|
374
|
+
export const convertToBlock = function (this: Editor, node: KNode) {
|
|
375
|
+
if (node.isBlock()) {
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
const newNode = node.clone(true)
|
|
379
|
+
//该节点是文本节点和闭合节点,处理光标问题
|
|
380
|
+
if (node.isText() || node.isClosed()) {
|
|
381
|
+
if (this.isSelectionInNode(node, 'start')) {
|
|
382
|
+
this.selection.start!.node = newNode
|
|
383
|
+
}
|
|
384
|
+
if (this.isSelectionInNode(node, 'end')) {
|
|
385
|
+
this.selection.end!.node = newNode
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
node.type = 'block'
|
|
389
|
+
node.tag = this.blockRenderTag
|
|
390
|
+
node.marks = undefined
|
|
391
|
+
node.styles = undefined
|
|
392
|
+
node.textContent = undefined
|
|
393
|
+
node.children = [newNode]
|
|
394
|
+
newNode.parent = node
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* 对节点数组使用指定规则进行格式化,nodes是需要格式化的节点数组,sourceNodes是格式化的节点所在的源数组
|
|
399
|
+
*/
|
|
400
|
+
export const formatNodes = function (this: Editor, rule: RuleFunctionType, nodes: KNode[], sourceNodes: KNode[]) {
|
|
401
|
+
let i = 0
|
|
402
|
+
while (i < nodes.length) {
|
|
403
|
+
const node = nodes[i]
|
|
404
|
+
//空节点
|
|
405
|
+
if (node.isEmpty()) {
|
|
406
|
+
if (this.isSelectionInNode(node, 'start')) {
|
|
407
|
+
this.updateSelectionRecently('start')
|
|
408
|
+
}
|
|
409
|
+
if (this.isSelectionInNode(node, 'end')) {
|
|
410
|
+
this.updateSelectionRecently('end')
|
|
411
|
+
}
|
|
412
|
+
const index = sourceNodes.findIndex(item => item.isEqual(node))
|
|
413
|
+
sourceNodes.splice(index, 1)
|
|
414
|
+
}
|
|
415
|
+
//非空节点
|
|
416
|
+
else {
|
|
417
|
+
//对节点使用该规则进行格式化
|
|
418
|
+
rule({ editor: this, node })
|
|
419
|
+
//格式化后变成空节点
|
|
420
|
+
if (node.isEmpty()) {
|
|
421
|
+
if (this.isSelectionInNode(node, 'start')) {
|
|
422
|
+
this.updateSelectionRecently('start')
|
|
423
|
+
}
|
|
424
|
+
if (this.isSelectionInNode(node, 'end')) {
|
|
425
|
+
this.updateSelectionRecently('end')
|
|
426
|
+
}
|
|
427
|
+
const index = sourceNodes.findIndex(item => item.isEqual(node))
|
|
428
|
+
sourceNodes.splice(index, 1)
|
|
429
|
+
}
|
|
430
|
+
//格式化后不是空节点
|
|
431
|
+
else {
|
|
432
|
+
//如果当前节点不是块节点,但是却是在根部,则转为块节点
|
|
433
|
+
if (!node.isBlock() && this.stackNodes === sourceNodes) {
|
|
434
|
+
convertToBlock.apply(this, [node])
|
|
435
|
+
}
|
|
436
|
+
//对子节点进行格式化
|
|
437
|
+
if (node.hasChildren()) {
|
|
438
|
+
formatNodes.apply(this, [rule, node.children!, node.children!])
|
|
439
|
+
}
|
|
440
|
+
//子节点格式化后变成空节点
|
|
441
|
+
if (node.isEmpty()) {
|
|
442
|
+
if (this.isSelectionInNode(node, 'start')) {
|
|
443
|
+
this.updateSelectionRecently('start')
|
|
444
|
+
}
|
|
445
|
+
if (this.isSelectionInNode(node, 'end')) {
|
|
446
|
+
this.updateSelectionRecently('end')
|
|
447
|
+
}
|
|
448
|
+
const index = sourceNodes.findIndex(item => item.isEqual(node))
|
|
449
|
+
sourceNodes.splice(index, 1)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
i++
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* 注册扩展
|
|
459
|
+
*/
|
|
460
|
+
export const registerExtension = function (this: Editor, extension: Extension) {
|
|
461
|
+
//是否已注册
|
|
462
|
+
if (extension.registered) return
|
|
463
|
+
|
|
464
|
+
//设置已注册
|
|
465
|
+
extension.registered = true
|
|
466
|
+
|
|
467
|
+
if (extension.emptyRenderTags) {
|
|
468
|
+
this.emptyRenderTags = [...extension.emptyRenderTags, ...this.emptyRenderTags]
|
|
469
|
+
}
|
|
470
|
+
if (extension.extraKeepTags) {
|
|
471
|
+
this.extraKeepTags = [...extension.extraKeepTags, ...this.extraKeepTags]
|
|
472
|
+
}
|
|
473
|
+
if (extension.formatRules) {
|
|
474
|
+
this.formatRules = [...extension.formatRules, ...this.formatRules]
|
|
475
|
+
}
|
|
476
|
+
if (extension.domParseNodeCallback) {
|
|
477
|
+
const fn = this.domParseNodeCallback
|
|
478
|
+
this.domParseNodeCallback = node => {
|
|
479
|
+
node = extension.domParseNodeCallback!.apply(this, [node])
|
|
480
|
+
if (fn) node = fn.apply(this, [node])
|
|
481
|
+
return node
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (extension.onSelectionUpdate) {
|
|
485
|
+
const fn = this.onSelectionUpdate
|
|
486
|
+
this.onSelectionUpdate = selection => {
|
|
487
|
+
extension.onSelectionUpdate!.apply(this, [selection])
|
|
488
|
+
if (fn) fn.apply(this, [selection])
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (extension.onInsertParagraph) {
|
|
492
|
+
const fn = this.onInsertParagraph
|
|
493
|
+
this.onInsertParagraph = node => {
|
|
494
|
+
extension.onInsertParagraph!.apply(this, [node])
|
|
495
|
+
if (fn) fn.apply(this, [node])
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (extension.onDeleteComplete) {
|
|
499
|
+
const fn = this.onDeleteComplete
|
|
500
|
+
this.onDeleteComplete = () => {
|
|
501
|
+
extension.onDeleteComplete!.apply(this)
|
|
502
|
+
if (fn) fn.apply(this)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (extension.onKeydown) {
|
|
506
|
+
const fn = this.onKeydown
|
|
507
|
+
this.onKeydown = event => {
|
|
508
|
+
extension.onKeydown!.apply(this, [event])
|
|
509
|
+
if (fn) fn.apply(this, [event])
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (extension.onKeyup) {
|
|
513
|
+
const fn = this.onKeyup
|
|
514
|
+
this.onKeyup = event => {
|
|
515
|
+
extension.onKeyup!.apply(this, [event])
|
|
516
|
+
if (fn) fn.apply(this, [event])
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (extension.onFocus) {
|
|
520
|
+
const fn = this.onFocus
|
|
521
|
+
this.onFocus = event => {
|
|
522
|
+
extension.onFocus!.apply(this, [event])
|
|
523
|
+
if (fn) fn.apply(this, [event])
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (extension.onBlur) {
|
|
527
|
+
const fn = this.onBlur
|
|
528
|
+
this.onBlur = event => {
|
|
529
|
+
extension.onBlur!.apply(this, [event])
|
|
530
|
+
if (fn) fn.apply(this, [event])
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (extension.pasteKeepMarks) {
|
|
534
|
+
const fn = this.pasteKeepMarks
|
|
535
|
+
this.pasteKeepMarks = node => {
|
|
536
|
+
const marks = extension.pasteKeepMarks!.apply(this, [node])
|
|
537
|
+
if (fn) Object.assign(marks, fn.apply(this, [node]))
|
|
538
|
+
return marks
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (extension.pasteKeepStyles) {
|
|
542
|
+
const fn = this.pasteKeepStyles
|
|
543
|
+
this.pasteKeepStyles = node => {
|
|
544
|
+
const styles = extension.pasteKeepStyles!.apply(this, [node])
|
|
545
|
+
if (fn) Object.assign(styles, fn.apply(this, [node]))
|
|
546
|
+
return styles
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (extension.beforeUpdateView) {
|
|
550
|
+
const fn = this.beforeUpdateView
|
|
551
|
+
this.beforeUpdateView = () => {
|
|
552
|
+
extension.beforeUpdateView!.apply(this)
|
|
553
|
+
if (fn) fn.apply(this)
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (extension.afterUpdateView) {
|
|
557
|
+
const fn = this.afterUpdateView
|
|
558
|
+
this.afterUpdateView = () => {
|
|
559
|
+
extension.afterUpdateView!.apply(this)
|
|
560
|
+
if (fn) fn.apply(this)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (extension.onDetachMentBlockFromParentCallback) {
|
|
564
|
+
const fn = this.onDetachMentBlockFromParentCallback
|
|
565
|
+
this.onDetachMentBlockFromParentCallback = node => {
|
|
566
|
+
const useDefault = extension.onDetachMentBlockFromParentCallback!.apply(this, [node])
|
|
567
|
+
if (fn) return fn.apply(this, [node]) && useDefault
|
|
568
|
+
return useDefault
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (extension.beforePatchNodeToFormat) {
|
|
572
|
+
const fn = this.beforePatchNodeToFormat
|
|
573
|
+
this.beforePatchNodeToFormat = node => {
|
|
574
|
+
node = extension.beforePatchNodeToFormat!.apply(this, [node])
|
|
575
|
+
if (fn) node = fn.apply(this, [node])
|
|
576
|
+
return node
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (extension.addCommands) {
|
|
580
|
+
const commands = extension.addCommands.apply(this)
|
|
581
|
+
this.commands = { ...this.commands, ...commands }
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* 根据真实光标更新selection,返回布尔值表示是否更新成功
|
|
587
|
+
*/
|
|
588
|
+
export const updateSelection = function (this: Editor) {
|
|
589
|
+
if (!this.$el) {
|
|
590
|
+
return false
|
|
591
|
+
}
|
|
592
|
+
const realSelection = window.getSelection()
|
|
593
|
+
if (realSelection && realSelection.rangeCount) {
|
|
594
|
+
const range = realSelection.getRangeAt(0)
|
|
595
|
+
//光标在编辑器内
|
|
596
|
+
if (isContains(this.$el!, range.startContainer) && isContains(this.$el!, range.endContainer)) {
|
|
597
|
+
//如果光标起点是文本
|
|
598
|
+
if (range.startContainer.nodeType == 3) {
|
|
599
|
+
this.selection.start = {
|
|
600
|
+
node: this.findNode(range.startContainer.parentNode as HTMLElement),
|
|
601
|
+
offset: range.startOffset
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
//如果光标起点是元素
|
|
605
|
+
else if (range.startContainer.nodeType == 1) {
|
|
606
|
+
const childDoms = Array.from(range.startContainer.childNodes)
|
|
607
|
+
//存在子元素
|
|
608
|
+
if (childDoms.length) {
|
|
609
|
+
const dom = childDoms[range.startOffset] ? childDoms[range.startOffset] : childDoms[range.startOffset - 1]
|
|
610
|
+
//元素
|
|
611
|
+
if (dom.nodeType == 1) {
|
|
612
|
+
if (childDoms[range.startOffset]) {
|
|
613
|
+
this.setSelectionBefore(this.findNode(dom as HTMLElement), 'start')
|
|
614
|
+
} else {
|
|
615
|
+
this.setSelectionAfter(this.findNode(dom as HTMLElement), 'start')
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
//文本
|
|
619
|
+
else if (dom.nodeType == 3) {
|
|
620
|
+
this.selection.start = {
|
|
621
|
+
node: this.findNode(dom.parentNode as HTMLElement),
|
|
622
|
+
offset: childDoms[range.startOffset] ? 0 : dom.textContent!.length
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
//没有子元素,应当是闭合节点
|
|
627
|
+
else {
|
|
628
|
+
this.selection.start = {
|
|
629
|
+
node: this.findNode(range.startContainer as HTMLElement),
|
|
630
|
+
offset: 0
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
//如果光标终点是文本
|
|
635
|
+
if (range.endContainer.nodeType == 3) {
|
|
636
|
+
this.selection.end = {
|
|
637
|
+
node: this.findNode(range.endContainer.parentNode as HTMLElement),
|
|
638
|
+
offset: range.endOffset
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
//如果光标终点是元素
|
|
642
|
+
else if (range.endContainer.nodeType == 1) {
|
|
643
|
+
const childDoms = Array.from(range.endContainer.childNodes)
|
|
644
|
+
//存在子元素
|
|
645
|
+
if (childDoms.length) {
|
|
646
|
+
const dom = childDoms[range.endOffset] ? childDoms[range.endOffset] : childDoms[range.endOffset - 1]
|
|
647
|
+
//元素
|
|
648
|
+
if (dom.nodeType == 1) {
|
|
649
|
+
if (childDoms[range.endOffset]) {
|
|
650
|
+
this.setSelectionBefore(this.findNode(dom as HTMLElement), 'end')
|
|
651
|
+
} else {
|
|
652
|
+
this.setSelectionAfter(this.findNode(dom as HTMLElement), 'end')
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
//文本
|
|
656
|
+
else if (dom.nodeType == 3) {
|
|
657
|
+
this.selection.end = {
|
|
658
|
+
node: this.findNode(dom.parentNode as HTMLElement),
|
|
659
|
+
offset: childDoms[range.endOffset] ? 0 : dom.textContent!.length
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
//没有子元素,应当是闭合节点
|
|
664
|
+
else {
|
|
665
|
+
this.selection.end = {
|
|
666
|
+
node: this.findNode(range.endContainer as HTMLElement),
|
|
667
|
+
offset: 1
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return true
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return false
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* 纠正光标位置,返回布尔值表示是否存在纠正行为
|
|
679
|
+
*/
|
|
680
|
+
export const redressSelection = function (this: Editor) {
|
|
681
|
+
if (!this.selection.focused()) {
|
|
682
|
+
return false
|
|
683
|
+
}
|
|
684
|
+
let startNode = this.selection.start!.node
|
|
685
|
+
let endNode = this.selection.end!.node
|
|
686
|
+
let startOffset = this.selection.start!.offset
|
|
687
|
+
let endOffset = this.selection.end!.offset
|
|
688
|
+
|
|
689
|
+
//起点和终点是相邻的两个节点并且位置紧邻则纠正光标位置
|
|
690
|
+
const startNextNode = this.getNextSelectionNode(startNode)
|
|
691
|
+
if (startNextNode && startNextNode.isEqual(endNode) && startOffset == (startNode.isText() ? startNode.textContent!.length : 1) && endOffset == 0) {
|
|
692
|
+
endNode = startNode
|
|
693
|
+
endOffset = startOffset
|
|
694
|
+
this.selection.end!.node = endNode
|
|
695
|
+
this.selection.end!.offset = endOffset
|
|
696
|
+
return true
|
|
697
|
+
}
|
|
698
|
+
return false
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* 初始化校验编辑器的节点数组,如果编辑器的节点数组为空或者都是空节点,则初始化创建一个只有占位符的段落
|
|
703
|
+
*/
|
|
704
|
+
export const checkNodes = function (this: Editor) {
|
|
705
|
+
const nodes = this.stackNodes.filter(item => {
|
|
706
|
+
return !item.isEmpty() && !item.void
|
|
707
|
+
})
|
|
708
|
+
if (nodes.length == 0) {
|
|
709
|
+
const node = KNode.create({
|
|
710
|
+
type: 'block',
|
|
711
|
+
tag: this.blockRenderTag
|
|
712
|
+
})
|
|
713
|
+
const placeholder = KNode.createPlaceholder()
|
|
714
|
+
this.addNode(placeholder, node)
|
|
715
|
+
this.stackNodes = [node]
|
|
716
|
+
if (this.selection.focused()) {
|
|
717
|
+
this.setSelectionBefore(placeholder)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* 粘贴时对节点的标记和样式的保留处理
|
|
724
|
+
*/
|
|
725
|
+
export const handlerForPasteKeepMarksAndStyles = function (this: Editor, nodes: KNode[]) {
|
|
726
|
+
nodes.forEach(node => {
|
|
727
|
+
const marks: KNodeMarksType = {}
|
|
728
|
+
const styles: KNodeStylesType = {}
|
|
729
|
+
//处理需要保留的标记
|
|
730
|
+
if (node.hasMarks()) {
|
|
731
|
+
//contenteditable属性保留
|
|
732
|
+
if (node.marks!.hasOwnProperty('contenteditable')) {
|
|
733
|
+
marks['contenteditable'] = node.marks!['contenteditable']
|
|
734
|
+
}
|
|
735
|
+
//name属性保留
|
|
736
|
+
if (node.marks!.hasOwnProperty('name')) {
|
|
737
|
+
marks['name'] = node.marks!['name']
|
|
738
|
+
}
|
|
739
|
+
//disabled属性保留
|
|
740
|
+
if (node.marks!.hasOwnProperty('disabled')) {
|
|
741
|
+
marks['disabled'] = node.marks!['disabled']
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
//自定义标记保留
|
|
745
|
+
if (typeof this.pasteKeepMarks == 'function') {
|
|
746
|
+
const extendMarks = this.pasteKeepMarks.apply(this, [node])
|
|
747
|
+
Object.assign(marks, extendMarks)
|
|
748
|
+
}
|
|
749
|
+
//自定义样式保留
|
|
750
|
+
if (typeof this.pasteKeepStyles == 'function') {
|
|
751
|
+
const extendStyles = this.pasteKeepStyles.apply(this, [node])
|
|
752
|
+
Object.assign(styles, extendStyles)
|
|
753
|
+
}
|
|
754
|
+
//将处理后的样式和标记给节点
|
|
755
|
+
node.marks = marks
|
|
756
|
+
node.styles = styles
|
|
757
|
+
//处理子节点
|
|
758
|
+
if (node.hasChildren()) {
|
|
759
|
+
handlerForPasteKeepMarksAndStyles.apply(this, [node.children!])
|
|
760
|
+
}
|
|
761
|
+
})
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* 粘贴时对文件的处理
|
|
766
|
+
*/
|
|
767
|
+
export const handlerForPasteFiles = async function (this: Editor, files: FileList) {
|
|
768
|
+
const length = files.length
|
|
769
|
+
for (let i = 0; i < length; i++) {
|
|
770
|
+
//图片粘贴
|
|
771
|
+
if (files[i].type.startsWith('image/')) {
|
|
772
|
+
//是否走默认逻辑
|
|
773
|
+
const useDefault = typeof this.onPasteImage == 'function' ? await this.onPasteImage.apply(this, [files[i]]) : true
|
|
774
|
+
//走默认逻辑
|
|
775
|
+
if (useDefault) {
|
|
776
|
+
const url = await DapFile.dataFileToBase64(files[i])
|
|
777
|
+
const image = KNode.create({
|
|
778
|
+
type: 'closed',
|
|
779
|
+
tag: 'img',
|
|
780
|
+
marks: {
|
|
781
|
+
src: url,
|
|
782
|
+
alt: files[i].name || ''
|
|
783
|
+
}
|
|
784
|
+
})
|
|
785
|
+
this.insertNode(image)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
//视频粘贴
|
|
789
|
+
else if (files[i].type.startsWith('video/')) {
|
|
790
|
+
//是否走默认逻辑
|
|
791
|
+
const useDefault = typeof this.onPasteVideo == 'function' ? await this.onPasteVideo.apply(this, [files[i]]) : true
|
|
792
|
+
//走默认逻辑
|
|
793
|
+
if (useDefault) {
|
|
794
|
+
const url = await DapFile.dataFileToBase64(files[i])
|
|
795
|
+
const video = KNode.create({
|
|
796
|
+
type: 'closed',
|
|
797
|
+
tag: 'video',
|
|
798
|
+
marks: {
|
|
799
|
+
src: url,
|
|
800
|
+
alt: files[i].name || ''
|
|
801
|
+
}
|
|
802
|
+
})
|
|
803
|
+
this.insertNode(video)
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
//其他文件粘贴
|
|
807
|
+
else if (typeof this.onPasteFile == 'function') {
|
|
808
|
+
this.onPasteFile.apply(this, [files[i]])
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* 处理某个节点数组,针对为空的块级节点补充占位符
|
|
815
|
+
*/
|
|
816
|
+
export const fillPlaceholderToEmptyBlock = function (this: Editor, nodes: KNode[]) {
|
|
817
|
+
const length = nodes.length
|
|
818
|
+
for (let i = 0; i < length; i++) {
|
|
819
|
+
if (nodes[i].isBlock()) {
|
|
820
|
+
//是空节点,补充占位符
|
|
821
|
+
if (nodes[i].isEmpty()) {
|
|
822
|
+
const placeholderNode = KNode.createPlaceholder()
|
|
823
|
+
nodes[i].children = [placeholderNode]
|
|
824
|
+
placeholderNode.parent = nodes[i]
|
|
825
|
+
}
|
|
826
|
+
//不是空节点,继续遍历子节点
|
|
827
|
+
else if (nodes[i].hasChildren()) {
|
|
828
|
+
fillPlaceholderToEmptyBlock.apply(this, [nodes[i].children!])
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* 粘贴处理
|
|
836
|
+
*/
|
|
837
|
+
export const handlerForPasteDrop = async function (this: Editor, dataTransfer: DataTransfer) {
|
|
838
|
+
//html内容
|
|
839
|
+
const html = dataTransfer.getData('text/html')
|
|
840
|
+
//文本内容
|
|
841
|
+
const text = dataTransfer.getData('text/plain')
|
|
842
|
+
//文件数组
|
|
843
|
+
const files = dataTransfer.files
|
|
844
|
+
//设置了优先粘贴文件,则处理文件粘贴
|
|
845
|
+
if (files.length && this.priorityPasteFiles) {
|
|
846
|
+
await handlerForPasteFiles.apply(this, [files])
|
|
847
|
+
}
|
|
848
|
+
//允许粘贴html,则处理html粘贴
|
|
849
|
+
else if (html && this.allowPasteHtml) {
|
|
850
|
+
//将html转为节点数组
|
|
851
|
+
const nodes = this.htmlParseNode(html).filter(item => {
|
|
852
|
+
return !item.isEmpty()
|
|
853
|
+
})
|
|
854
|
+
//节点数组内的空块节点补充占位符
|
|
855
|
+
fillPlaceholderToEmptyBlock.apply(this, [nodes])
|
|
856
|
+
//粘贴时对节点的标记和样式的保留处理
|
|
857
|
+
handlerForPasteKeepMarksAndStyles.apply(this, [nodes])
|
|
858
|
+
//是否走默认逻辑
|
|
859
|
+
const useDefault = typeof this.onPasteHtml == 'function' ? await this.onPasteHtml.apply(this, [nodes, html]) : true
|
|
860
|
+
//走默认逻辑
|
|
861
|
+
if (useDefault) {
|
|
862
|
+
this.insertNode(nodes[0])
|
|
863
|
+
for (let i = nodes.length - 1; i >= 1; i--) {
|
|
864
|
+
this.addNodeAfter(nodes[i], nodes[0])
|
|
865
|
+
}
|
|
866
|
+
this.setSelectionAfter(nodes[nodes.length - 1], 'all')
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
//有文本内容
|
|
870
|
+
else if (text) {
|
|
871
|
+
//是否走默认逻辑
|
|
872
|
+
const useDefault = typeof this.onPasteText == 'function' ? await this.onPasteText.apply(this, [text]) : true
|
|
873
|
+
//走默认逻辑
|
|
874
|
+
if (useDefault) {
|
|
875
|
+
this.insertText(text)
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
//有文件
|
|
879
|
+
else if (files.length) {
|
|
880
|
+
await handlerForPasteFiles.apply(this, [files])
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* 将指定的非固定块节点从父节点(非固定块节点)中抽离,插入到和父节点同级的位置
|
|
886
|
+
*/
|
|
887
|
+
export const removeBlockFromParentToSameLevel = function (this: Editor, node: KNode) {
|
|
888
|
+
//块节点的父节点
|
|
889
|
+
const parentNode = node.parent!
|
|
890
|
+
//获取该块节点在父节点中的位置
|
|
891
|
+
const index = parentNode.children!.findIndex(item => item.isEqual(node))
|
|
892
|
+
//该块节点在父节点第一个
|
|
893
|
+
if (index == 0) {
|
|
894
|
+
//将块节点移到父节点之前
|
|
895
|
+
parentNode.children!.splice(index, 1)
|
|
896
|
+
this.addNodeBefore(node, parentNode)
|
|
897
|
+
}
|
|
898
|
+
//该块节点在父节点的最后一个
|
|
899
|
+
else if (index == parentNode.children!.length - 1) {
|
|
900
|
+
//将块节点移到父节点之后
|
|
901
|
+
parentNode.children!.splice(index, 1)
|
|
902
|
+
this.addNodeAfter(node, parentNode)
|
|
903
|
+
}
|
|
904
|
+
//该块节点在父节点中间
|
|
905
|
+
else {
|
|
906
|
+
//克隆父节点
|
|
907
|
+
const newParentNode = parentNode.clone(false)
|
|
908
|
+
//获取父节点的子节点数组
|
|
909
|
+
const children = parentNode.children!
|
|
910
|
+
//重新设置父节点的子节点
|
|
911
|
+
parentNode.children! = children.slice(0, index)
|
|
912
|
+
//设置克隆的父节点的子节点
|
|
913
|
+
newParentNode.children = children.slice(index + 1).map(item => {
|
|
914
|
+
item.parent = newParentNode
|
|
915
|
+
return item
|
|
916
|
+
})
|
|
917
|
+
//将块节点移动到父节点后
|
|
918
|
+
this.addNodeAfter(node, parentNode)
|
|
919
|
+
//将克隆的父节点添加到块节点后
|
|
920
|
+
this.addNodeAfter(newParentNode, node)
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* 光标所在的块节点不是只有占位,且非固定块节点,非代码块样式的块节点,在该块节点内正常换行方法
|
|
926
|
+
*/
|
|
927
|
+
export const handlerForNormalInsertParagraph = function (this: Editor) {
|
|
928
|
+
//光标所在节点
|
|
929
|
+
const node = this.selection.start!.node
|
|
930
|
+
//光标在节点里的偏移值
|
|
931
|
+
const offset = this.selection.start!.offset
|
|
932
|
+
//光标所在块节点
|
|
933
|
+
const blockNode = node.getBlock()
|
|
934
|
+
//获取块节点内第一个可以设置光标的节点
|
|
935
|
+
const firstSelectionNode = this.getFirstSelectionNodeInChildren(blockNode)!
|
|
936
|
+
//获取块节点内最后一个可以设置光标的节点
|
|
937
|
+
const lastSelectionNode = this.getLastSelectionNodeInChildren(blockNode)!
|
|
938
|
+
//光标在块节点的起始处
|
|
939
|
+
if (firstSelectionNode.isEqual(node) && offset == 0) {
|
|
940
|
+
const newBlockNode = blockNode.clone(false)
|
|
941
|
+
const placeholderNode = KNode.createPlaceholder()
|
|
942
|
+
this.addNode(placeholderNode, newBlockNode)
|
|
943
|
+
this.addNodeBefore(newBlockNode, blockNode)
|
|
944
|
+
//触发换行事件
|
|
945
|
+
if (typeof this.onInsertParagraph == 'function') {
|
|
946
|
+
this.onInsertParagraph.apply(this, [blockNode])
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
//光标在块节点的末尾处
|
|
950
|
+
else if (lastSelectionNode.isEqual(node) && offset == (node.isText() ? node.textContent!.length : 1)) {
|
|
951
|
+
const newBlockNode = blockNode.clone(false)
|
|
952
|
+
const placeholderNode = KNode.createPlaceholder()
|
|
953
|
+
this.addNode(placeholderNode, newBlockNode)
|
|
954
|
+
this.addNodeAfter(newBlockNode, blockNode)
|
|
955
|
+
this.setSelectionBefore(placeholderNode)
|
|
956
|
+
//触发换行事件
|
|
957
|
+
if (typeof this.onInsertParagraph == 'function') {
|
|
958
|
+
this.onInsertParagraph.apply(this, [newBlockNode])
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
//光标在块节点的中间
|
|
962
|
+
else {
|
|
963
|
+
//创建新的块节点
|
|
964
|
+
const newBlockNode = blockNode.clone(true)
|
|
965
|
+
//插入到光标所在块节点之后
|
|
966
|
+
this.addNodeAfter(newBlockNode, blockNode)
|
|
967
|
+
//记录光标所在节点在块节点中的序列
|
|
968
|
+
const index = KNode.flat(blockNode.children!).findIndex(item => {
|
|
969
|
+
return this.selection.start!.node.isEqual(item)
|
|
970
|
+
})
|
|
971
|
+
//记录光标的偏移值
|
|
972
|
+
const offset = this.selection.start!.offset
|
|
973
|
+
//将光标终点移动到块节点最后
|
|
974
|
+
this.setSelectionAfter(lastSelectionNode, 'end')
|
|
975
|
+
//删除原块节点光标所在位置后面的部分
|
|
976
|
+
this.delete()
|
|
977
|
+
//将光标起点移动到新块节点的起始处
|
|
978
|
+
this.setSelectionBefore(newBlockNode, 'start')
|
|
979
|
+
//将光标终点移动到新块节点中与老块节点对应的位置
|
|
980
|
+
this.selection.end!.node = KNode.flat(newBlockNode.children!)[index]
|
|
981
|
+
this.selection.end!.offset = offset
|
|
982
|
+
//删除新块节点光标所在位置前面的部分
|
|
983
|
+
this.delete()
|
|
984
|
+
//触发事件
|
|
985
|
+
if (typeof this.onInsertParagraph == 'function') {
|
|
986
|
+
this.onInsertParagraph.apply(this, [newBlockNode])
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* 设置placeholder,在每次视图更新时调用此方法
|
|
993
|
+
*/
|
|
994
|
+
export const setPlaceholder = function (this: Editor) {
|
|
995
|
+
//编辑器内只有一个块节点且是只有占位符的段落
|
|
996
|
+
if (this.stackNodes.length == 1 && this.isParagraph(this.stackNodes[0]) && this.stackNodes[0].allIsPlaceholder()) {
|
|
997
|
+
this.$el!.classList.add('showPlaceholder')
|
|
998
|
+
} else {
|
|
999
|
+
this.$el!.classList.remove('showPlaceholder')
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* 合并扩展数组(只能用在扩展注册之前)
|
|
1005
|
+
*/
|
|
1006
|
+
export const mergeExtensions = function (this: Editor, from: Extension[]) {
|
|
1007
|
+
//将编辑器自身内置的扩展转成Map,name作为key
|
|
1008
|
+
const sourceExtensionMap: { [name: string]: Extension } = {}
|
|
1009
|
+
this.extensions.forEach(extension => {
|
|
1010
|
+
sourceExtensionMap[extension.name] = extension
|
|
1011
|
+
})
|
|
1012
|
+
//遍历配置的扩展,加入到Map,如果原有有同名扩展则覆盖
|
|
1013
|
+
from.forEach(extension => {
|
|
1014
|
+
sourceExtensionMap[extension.name] = extension
|
|
1015
|
+
})
|
|
1016
|
+
//返回整合后的扩展
|
|
1017
|
+
return Object.values(sourceExtensionMap)
|
|
1018
|
+
}
|