vue-editify 0.1.18 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. package/README.md +3 -3
  2. package/examples/App.vue +62 -61
  3. package/examples/main.ts +4 -4
  4. package/lib/components/button/button.vue.d.ts +11 -11
  5. package/lib/components/checkbox/checkbox.vue.d.ts +8 -8
  6. package/lib/components/colors/colors.vue.d.ts +4 -4
  7. package/lib/components/icon/icon.vue.d.ts +1 -1
  8. package/lib/components/insertImage/insertImage.vue.d.ts +9 -9
  9. package/lib/components/insertLink/insertLink.vue.d.ts +2 -2
  10. package/lib/components/insertTable/insertTable.vue.d.ts +2 -2
  11. package/lib/components/insertVideo/insertVideo.vue.d.ts +9 -9
  12. package/lib/components/layer/layer.vue.d.ts +9 -9
  13. package/lib/components/menu/menu.vue.d.ts +4 -4
  14. package/lib/components/toolbar/toolbar.vue.d.ts +9 -9
  15. package/lib/components/tooltip/tooltip.vue.d.ts +1 -1
  16. package/lib/components/triangle/triangle.vue.d.ts +4 -4
  17. package/lib/editify/editify.vue.d.ts +68 -68
  18. package/lib/editify.es.js +17 -17
  19. package/lib/editify.umd.js +1 -1
  20. package/lib/index.d.ts +1 -1
  21. package/lib/style.css +1 -1
  22. package/package.json +45 -45
  23. package/src/components/button/button.less +145 -145
  24. package/src/components/button/button.vue +197 -197
  25. package/src/components/button/props.ts +95 -95
  26. package/src/components/checkbox/checkbox.less +84 -84
  27. package/src/components/checkbox/checkbox.vue +68 -68
  28. package/src/components/checkbox/props.ts +49 -49
  29. package/src/components/colors/colors.less +75 -75
  30. package/src/components/colors/colors.vue +36 -36
  31. package/src/components/colors/props.ts +29 -29
  32. package/src/components/icon/icon.less +14 -14
  33. package/src/components/icon/icon.vue +12 -12
  34. package/src/components/icon/props.ts +11 -11
  35. package/src/components/insertImage/insertImage.less +135 -135
  36. package/src/components/insertImage/insertImage.vue +146 -146
  37. package/src/components/insertImage/props.ts +43 -43
  38. package/src/components/insertLink/insertLink.less +64 -64
  39. package/src/components/insertLink/insertLink.vue +58 -58
  40. package/src/components/insertLink/props.ts +16 -16
  41. package/src/components/insertTable/insertTable.less +54 -54
  42. package/src/components/insertTable/insertTable.vue +85 -85
  43. package/src/components/insertTable/props.ts +27 -27
  44. package/src/components/insertVideo/insertVideo.less +135 -135
  45. package/src/components/insertVideo/insertVideo.vue +146 -146
  46. package/src/components/insertVideo/props.ts +43 -43
  47. package/src/components/layer/layer.less +49 -49
  48. package/src/components/layer/layer.vue +598 -598
  49. package/src/components/layer/props.ts +71 -71
  50. package/src/components/menu/menu.less +63 -63
  51. package/src/components/menu/menu.vue +1569 -1569
  52. package/src/components/menu/props.ts +17 -17
  53. package/src/components/toolbar/props.ts +35 -35
  54. package/src/components/toolbar/toolbar.less +89 -89
  55. package/src/components/toolbar/toolbar.vue +1101 -1101
  56. package/src/components/tooltip/props.ts +21 -21
  57. package/src/components/tooltip/tooltip.less +23 -23
  58. package/src/components/tooltip/tooltip.vue +37 -37
  59. package/src/components/triangle/props.ts +26 -26
  60. package/src/components/triangle/triangle.less +79 -79
  61. package/src/components/triangle/triangle.vue +65 -65
  62. package/src/core/function.ts +1144 -1144
  63. package/src/core/rule.ts +259 -259
  64. package/src/core/tool.ts +1137 -1137
  65. package/src/css/base.less +30 -30
  66. package/src/css/hljs.less +54 -54
  67. package/src/editify/editify.less +404 -403
  68. package/src/editify/editify.vue +803 -803
  69. package/src/editify/props.ts +156 -156
  70. package/src/hljs/index.ts +197 -197
  71. package/src/icon/iconfont.css +219 -219
  72. package/src/index.ts +32 -32
  73. package/src/locale/en_US.ts +88 -88
  74. package/src/locale/index.ts +12 -12
  75. package/src/locale/zh_CN.ts +88 -88
  76. package/tsconfig.json +27 -27
  77. package/tsconfig.node.json +11 -11
  78. package/vite-env.d.ts +1 -1
  79. package/vite.config.ts +42 -42
@@ -1,1144 +1,1144 @@
1
- /**
2
- * 这里的方法都是对编辑器内容元素进行判断或者操作的方法,不涉及到格式化、dom渲染和光标渲染
3
- */
4
- import { AlexElement, AlexElementsRangeType, AlexEditor } from 'alex-editor'
5
- import { common as DapCommon } from 'dap-util'
6
- import { cloneData, queryHasValue, getButtonOptionsConfig, ObjectType } from './tool'
7
- import { ButtonOptionsItemType } from '../components/button/props'
8
-
9
- //判断元素是否在某个标签下,如果是返回该标签对应的元素,否则返回null
10
- export const getParsedomElementByElement = (element: AlexElement, parsedom: string): AlexElement | null => {
11
- if (element.isBlock()) {
12
- return element.parsedom == parsedom ? element : null
13
- }
14
- if (!element.isText() && element.parsedom == parsedom) {
15
- return element
16
- }
17
- return getParsedomElementByElement(element.parent!, parsedom)
18
- }
19
-
20
- //获取光标是否在指定标签下,如果是返回该标签对应的元素,否则返回null
21
- export const getCurrentParsedomElement = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, parsedom: string) => {
22
- if (!editor.range) {
23
- return null
24
- }
25
- if (editor.range.anchor.element.isEqual(editor.range.focus.element)) {
26
- return getParsedomElementByElement(editor.range.anchor.element, parsedom)
27
- }
28
- const arr = dataRangeCaches.list.map(item => {
29
- return getParsedomElementByElement(item.element, parsedom)
30
- })
31
- let hasNull = arr.some(el => {
32
- return el == null
33
- })
34
- //如果存在null,则表示有的选区元素不在指定标签下,返回null
35
- if (hasNull) {
36
- return null
37
- }
38
- //如果只有一个元素,则返回该元素
39
- if (arr.length == 1) {
40
- return arr[0]!
41
- }
42
- //默认数组中的元素都相等
43
- let flag = true
44
- for (let i = 1; i < arr.length; i++) {
45
- if (!arr[i]!.isEqual(arr[0]!)) {
46
- flag = false
47
- break
48
- }
49
- }
50
- //如果相等,则返回该元素
51
- if (flag) {
52
- return arr[0]
53
- }
54
- return null
55
- }
56
-
57
- //判断元素是否在有序列表或者无序列表下
58
- export const elementIsInList = (element: AlexElement, ordered: boolean): boolean => {
59
- if (isList(element, ordered)) {
60
- return true
61
- }
62
- if (element.parent) {
63
- return elementIsInList(element.parent, ordered)
64
- }
65
- return false
66
- }
67
-
68
- //判断元素是否在任务列表下
69
- export const elementIsInTask = (element: AlexElement): boolean => {
70
- if (isTask(element)) {
71
- return true
72
- }
73
- if (element.parent) {
74
- return elementIsInTask(element.parent)
75
- }
76
- return false
77
- }
78
-
79
- //判断元素是否有序或者无序列表
80
- export const isList = function (element: AlexElement, ordered: boolean | undefined = false) {
81
- return element.parsedom == 'div' && element.hasMarks() && element.marks!['data-editify-list'] == (ordered ? 'ol' : 'ul')
82
- }
83
-
84
- //判断元素是否任务列表
85
- export const isTask = function (element: AlexElement) {
86
- return element.parsedom == 'div' && element.hasMarks() && element.marks!.hasOwnProperty('data-editify-task')
87
- }
88
-
89
- //选区是否含有代码块
90
- export const hasPreInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
91
- if (!editor.range) {
92
- return false
93
- }
94
- if (editor.range.anchor.isEqual(editor.range.focus)) {
95
- return !!getParsedomElementByElement(editor.range.anchor.element, 'pre')
96
- }
97
- return dataRangeCaches.flatList.some(item => {
98
- return !!getParsedomElementByElement(item.element, 'pre')
99
- })
100
- }
101
-
102
- //选区是否全部在代码块内
103
- export const isRangeInPre = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
104
- if (!editor.range) {
105
- return false
106
- }
107
- if (editor.range.anchor.isEqual(editor.range.focus)) {
108
- return !!getParsedomElementByElement(editor.range.anchor.element, 'pre')
109
- }
110
- return dataRangeCaches.list.every(item => {
111
- return !!getParsedomElementByElement(item.element, 'pre')
112
- })
113
- }
114
-
115
- //选区是否含有引用
116
- export const hasQuoteInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
117
- if (!editor.range) {
118
- return false
119
- }
120
- if (editor.range.anchor.isEqual(editor.range.focus)) {
121
- return !!getParsedomElementByElement(editor.range.anchor.element, 'blockquote')
122
- }
123
- return dataRangeCaches.flatList.some(item => {
124
- return !!getParsedomElementByElement(item.element, 'blockquote')
125
- })
126
- }
127
-
128
- //选区是否全部在引用内
129
- export const isRangeInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
130
- if (!editor.range) {
131
- return false
132
- }
133
- if (editor.range.anchor.isEqual(editor.range.focus)) {
134
- return !!getParsedomElementByElement(editor.range.anchor.element, 'blockquote')
135
- }
136
- return dataRangeCaches.list.every(item => {
137
- return !!getParsedomElementByElement(item.element, 'blockquote')
138
- })
139
- }
140
-
141
- //选区是否含有有序列表或者无序列表
142
- export const hasListInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean | undefined = false) => {
143
- if (!editor.range) {
144
- return false
145
- }
146
- if (editor.range.anchor.isEqual(editor.range.focus)) {
147
- return elementIsInList(editor.range.anchor.element, ordered)
148
- }
149
- return dataRangeCaches.flatList.some(item => {
150
- return elementIsInList(item.element, ordered)
151
- })
152
- }
153
-
154
- //选区是否全部在有序列表或者无序列表内
155
- export const isRangeInList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean | undefined = false) => {
156
- if (!editor.range) {
157
- return false
158
- }
159
- if (editor.range.anchor.isEqual(editor.range.focus)) {
160
- return elementIsInList(editor.range.anchor.element, ordered)
161
- }
162
- return dataRangeCaches.list.every(item => {
163
- return elementIsInList(item.element, ordered)
164
- })
165
- }
166
-
167
- //选区是否含有任务列表
168
- export const hasTaskInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
169
- if (!editor.range) {
170
- return false
171
- }
172
- if (editor.range.anchor.isEqual(editor.range.focus)) {
173
- return elementIsInTask(editor.range.anchor.element)
174
- }
175
- return dataRangeCaches.flatList.some(item => {
176
- return elementIsInTask(item.element)
177
- })
178
- }
179
-
180
- //选区是否全部在任务列表里
181
- export const isRangeInTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
182
- if (!editor.range) {
183
- return false
184
- }
185
- if (editor.range.anchor.isEqual(editor.range.focus)) {
186
- return elementIsInTask(editor.range.anchor.element)
187
- }
188
- return dataRangeCaches.list.every(item => {
189
- return elementIsInTask(item.element)
190
- })
191
- }
192
-
193
- //选区是否含有链接
194
- export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
195
- if (!editor.range) {
196
- return false
197
- }
198
- if (editor.range.anchor.isEqual(editor.range.focus)) {
199
- return !!getParsedomElementByElement(editor.range.anchor.element, 'a')
200
- }
201
- return dataRangeCaches.flatList.some(item => {
202
- return !!getParsedomElementByElement(item.element, 'a')
203
- })
204
- }
205
-
206
- //选区是否含有表格
207
- export const hasTableInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
208
- if (!editor.range) {
209
- return false
210
- }
211
- if (editor.range.anchor.isEqual(editor.range.focus)) {
212
- return !!getParsedomElementByElement(editor.range.anchor.element, 'table')
213
- }
214
- return dataRangeCaches.flatList.some(item => {
215
- return !!getParsedomElementByElement(item.element, 'table')
216
- })
217
- }
218
-
219
- //选区是否含有图片
220
- export const hasImageInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
221
- if (!editor.range) {
222
- return false
223
- }
224
- if (editor.range.anchor.isEqual(editor.range.focus)) {
225
- return !!getParsedomElementByElement(editor.range.anchor.element, 'img')
226
- }
227
- return dataRangeCaches.flatList.some(item => {
228
- return !!getParsedomElementByElement(item.element, 'img')
229
- })
230
- }
231
-
232
- //选区是否含有视频
233
- export const hasVideoInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
234
- if (!editor.range) {
235
- return false
236
- }
237
- if (editor.range.anchor.isEqual(editor.range.focus)) {
238
- return !!getParsedomElementByElement(editor.range.anchor.element, 'video')
239
- }
240
- return dataRangeCaches.flatList.some(item => {
241
- return !!getParsedomElementByElement(item.element, 'video')
242
- })
243
- }
244
-
245
- //查询光标所在的文本元素是否具有某个样式
246
- export const queryTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
247
- if (!editor.range) {
248
- return false
249
- }
250
- //起点和终点在一起
251
- if (editor.range.anchor.isEqual(editor.range.focus)) {
252
- //如果是文本元素并且具有样式
253
- if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasStyles()) {
254
- return queryHasValue(editor.range.anchor.element.styles!, name, value)
255
- }
256
- //不是文本元素或者没有样式直接返回
257
- return false
258
- }
259
- //起点和终点不在一起获取选区中的文本元素
260
- let result = dataRangeCaches.flatList.filter(item => {
261
- return item.element.isText()
262
- })
263
- //如果不包含文本元素直接返回false
264
- if (result.length == 0) {
265
- return false
266
- }
267
- //判断每个文本元素是否都具有该样式
268
- let flag = result.every(item => {
269
- //文本元素含有样式进一步判断
270
- if (item.element.hasStyles()) {
271
- return queryHasValue(item.element.styles!, name, value)
272
- }
273
- //文本元素没有样式直接返回false
274
- return false
275
- })
276
- return flag
277
- }
278
-
279
- //查询光标所在的文本元素是否具有某个标记
280
- export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
281
- if (!editor.range) {
282
- return false
283
- }
284
- //起点和终点在一起
285
- if (editor.range.anchor.isEqual(editor.range.focus)) {
286
- //如果是文本元素并且具有标记
287
- if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
288
- return queryHasValue(editor.range.anchor.element.marks!, name, value)
289
- }
290
- //不是文本元素或者没有标记直接返回
291
- return false
292
- }
293
- //起点和终点不在一起获取选区中的文本元素
294
- let result = dataRangeCaches.flatList.filter(item => {
295
- return item.element.isText()
296
- })
297
- //如果不包含文本元素直接返回false
298
- if (result.length == 0) {
299
- return false
300
- }
301
- //判断每个文本元素是否都具有该标记
302
- let flag = result.every(item => {
303
- //文本元素含有标记进一步判断
304
- if (item.element.hasMarks()) {
305
- return queryHasValue(item.element.marks!, name, value)
306
- }
307
- //文本元素没有标记直接返回false
308
- return false
309
- })
310
- return flag
311
- }
312
-
313
- //获取选区内的文字内容,用于预置链接文字
314
- export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
315
- //存在选区的情况下预置链接文本值
316
- let text = ''
317
- dataRangeCaches.flatList.forEach(item => {
318
- if (item.element.isText()) {
319
- if (item.offset) {
320
- text += item.element.textContent!.substring(item.offset[0], item.offset[1])
321
- } else {
322
- text += item.element.textContent || ''
323
- }
324
- }
325
- })
326
- return text
327
- }
328
-
329
- //获取光标选取内的扁平化元素数组(可能会分割文本元素导致stack变更,同时也会更新选取元素和光标位置)
330
- export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
331
- if (!editor.range) {
332
- return []
333
- }
334
- //获取选区数据的长度
335
- let length = dataRangeCaches.flatList.length
336
- //返回的元素数组
337
- let elements = []
338
- //遍历选区数据
339
- for (let i = 0; i < length; i++) {
340
- const item = dataRangeCaches.flatList[i]
341
- //如果存在offset那么一定是文本元素
342
- if (item.offset) {
343
- let selectEl = null
344
- //文本元素前面一部分在光标范围内
345
- if (item.offset[0] == 0 && item.offset[1] < item.element.textContent!.length) {
346
- const el = item.element.clone()
347
- item.element.textContent = item.element.textContent!.substring(0, item.offset[1])
348
- el.textContent = el.textContent!.substring(item.offset[1])
349
- editor.addElementAfter(el, item.element)
350
- selectEl = item.element
351
- }
352
- //文本元素后面一部分在光标范围内
353
- else if (item.offset[1] == item.element.textContent!.length && item.offset[0] > 0) {
354
- const el = item.element.clone()
355
- item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
356
- el.textContent = el.textContent!.substring(item.offset[0])
357
- editor.addElementAfter(el, item.element)
358
- selectEl = el
359
- }
360
- //文本元素的中间一部分在光标范围内
361
- else if (item.offset[0] > 0 && item.offset[1] < item.element.textContent!.length) {
362
- const el = item.element.clone()
363
- const el2 = item.element.clone()
364
- item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
365
- el.textContent = el.textContent!.substring(item.offset[0], item.offset[1])
366
- el2.textContent = el2.textContent!.substring(item.offset[1])
367
- editor.addElementAfter(el, item.element)
368
- editor.addElementAfter(el2, el)
369
- selectEl = el
370
- }
371
- //如果selectEl存在证明文本元素被分割了
372
- if (selectEl) {
373
- //如果i为0的话肯定是起点
374
- if (i == 0) {
375
- editor.range.anchor.moveToStart(selectEl)
376
- }
377
- //如果i是最后一个序列的话肯定是终点
378
- if (i == length - 1) {
379
- editor.range.focus.moveToEnd(selectEl)
380
- }
381
- elements.push(selectEl)
382
- }
383
- } else {
384
- elements.push(item.element)
385
- }
386
- }
387
- return elements
388
- }
389
-
390
- //将某个元素转为段落标签
391
- export const elementToParagraph = function (element: AlexElement) {
392
- element.marks = null
393
- element.styles = null
394
- element.parsedom = AlexElement.BLOCK_NODE
395
- }
396
-
397
- //其他元素转为有序或者无序列表
398
- export const elementToList = function (element: AlexElement, ordered: boolean | undefined = false) {
399
- //如果是列表则返回
400
- if (isList(element, ordered)) {
401
- return
402
- }
403
- //先转为段落
404
- elementToParagraph(element)
405
- //然后转为列表
406
- element.parsedom = 'div'
407
- if (!element.hasMarks()) {
408
- element.marks = {}
409
- }
410
- element.marks!['data-editify-list'] = ordered ? 'ol' : 'ul'
411
- }
412
-
413
- //其他元素转为任务列表
414
- export const elementToTask = function (element: AlexElement) {
415
- //如果是任务列表则返回
416
- if (isTask(element)) {
417
- return
418
- }
419
- //先转为段落
420
- elementToParagraph(element)
421
- //然后转为任务列表
422
- element.parsedom = 'div'
423
- if (!element.hasMarks()) {
424
- element.marks = {}
425
- }
426
- element.marks!['data-editify-task'] = 'uncheck'
427
- }
428
-
429
- //设置标题
430
- export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, editTrans: (key: string) => any, parsedom: string) => {
431
- if (!editor.range) {
432
- return
433
- }
434
- const values = getButtonOptionsConfig(editTrans).heading!.map(item => {
435
- return (<ButtonOptionsItemType>item).value
436
- })
437
- if (!values.includes(parsedom)) {
438
- throw new Error('The parameter supports only h1-h6 and p')
439
- }
440
- if (editor.range.anchor.isEqual(editor.range.focus)) {
441
- const block = editor.range.anchor.element.getBlock()
442
- //先转为段落
443
- elementToParagraph(block)
444
- //设置标题
445
- block.parsedom = parsedom
446
- } else {
447
- dataRangeCaches.list.forEach(el => {
448
- if (el.element.isBlock()) {
449
- elementToParagraph(el.element)
450
- el.element.parsedom = parsedom
451
- } else {
452
- const block = el.element.getBlock()
453
- elementToParagraph(block)
454
- block.parsedom = parsedom
455
- }
456
- })
457
- }
458
- }
459
-
460
- //根级块元素或者内部块元素增加缩进
461
- export const setIndentIncrease = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
462
- if (!editor.range) {
463
- return
464
- }
465
- const fn = (element: AlexElement) => {
466
- if (element.hasStyles()) {
467
- if (element.styles!.hasOwnProperty('text-indent')) {
468
- let val = element.styles!['text-indent']
469
- if (val.endsWith('em')) {
470
- val = parseFloat(val)
471
- } else {
472
- val = 0
473
- }
474
- element.styles!['text-indent'] = `${val + 2}em`
475
- } else {
476
- element.styles!['text-indent'] = '2em'
477
- }
478
- } else {
479
- element.styles = {
480
- 'text-indent': '2em'
481
- }
482
- }
483
- }
484
- if (editor.range.anchor.isEqual(editor.range.focus)) {
485
- const block = editor.range.anchor.element.getBlock()
486
- const inblock = editor.range.anchor.element.getInblock()
487
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
488
- fn(inblock)
489
- } else if (!block.isPreStyle()) {
490
- fn(block)
491
- }
492
- } else {
493
- dataRangeCaches.list.forEach(item => {
494
- const block = item.element.getBlock()
495
- const inblock = item.element.getInblock()
496
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
497
- fn(inblock)
498
- } else if (!block.isPreStyle()) {
499
- fn(block)
500
- }
501
- })
502
- }
503
- }
504
-
505
- //根级块元素或者内部块元素减少缩进
506
- export const setIndentDecrease = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
507
- if (!editor.range) {
508
- return
509
- }
510
- const fn = (element: AlexElement) => {
511
- if (element.hasStyles() && element.styles!.hasOwnProperty('text-indent')) {
512
- let val = element.styles!['text-indent']
513
- if (val.endsWith('em')) {
514
- val = parseFloat(val)
515
- } else {
516
- val = 0
517
- }
518
- element.styles!['text-indent'] = `${val - 2 >= 0 ? val - 2 : 0}em`
519
- }
520
- }
521
- if (editor.range.anchor.isEqual(editor.range.focus)) {
522
- const block = editor.range.anchor.element.getBlock()
523
- const inblock = editor.range.anchor.element.getInblock()
524
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
525
- fn(inblock)
526
- } else if (!block.isPreStyle()) {
527
- fn(block)
528
- }
529
- } else {
530
- dataRangeCaches.list.forEach(item => {
531
- const block = item.element.getBlock()
532
- const inblock = item.element.getInblock()
533
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
534
- fn(inblock)
535
- } else if (!block.isPreStyle()) {
536
- fn(block)
537
- }
538
- })
539
- }
540
- }
541
-
542
- //插入或者取消引用
543
- export const setQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
544
- if (!editor.range) {
545
- return
546
- }
547
- //是否都在引用里
548
- const flag = isRangeInQuote(editor, dataRangeCaches)
549
- //起点和终点在一起
550
- if (editor.range.anchor.isEqual(editor.range.focus)) {
551
- const block = editor.range.anchor.element.getBlock()
552
- elementToParagraph(block)
553
- if (!flag) {
554
- block.parsedom = 'blockquote'
555
- }
556
- }
557
- //起点和终点不在一起
558
- else {
559
- let blocks: AlexElement[] = []
560
- dataRangeCaches.list.forEach(item => {
561
- const block = item.element.getBlock()
562
- const exist = blocks.some(el => block.isEqual(el))
563
- if (!exist) {
564
- blocks.push(block)
565
- }
566
- })
567
- blocks.forEach(block => {
568
- elementToParagraph(block)
569
- if (!flag) {
570
- block.parsedom = 'blockquote'
571
- }
572
- })
573
- }
574
- }
575
-
576
- //设置对齐方式,参数取值justify/left/right/center
577
- export const setAlign = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: string) => {
578
- if (!editor.range) {
579
- return
580
- }
581
- if (editor.range.anchor.isEqual(editor.range.focus)) {
582
- const block = editor.range.anchor.element.getBlock()
583
- const inblock = editor.range.anchor.element.getInblock()
584
- if (inblock) {
585
- if (inblock.hasStyles()) {
586
- inblock.styles!['text-align'] = value
587
- } else {
588
- inblock.styles = {
589
- 'text-align': value
590
- }
591
- }
592
- } else {
593
- if (block.hasStyles()) {
594
- block.styles!['text-align'] = value
595
- } else {
596
- block.styles = {
597
- 'text-align': value
598
- }
599
- }
600
- }
601
- } else {
602
- dataRangeCaches.list.forEach(el => {
603
- if (el.element.isBlock() || el.element.isInblock()) {
604
- if (el.element.hasStyles()) {
605
- el.element.styles!['text-align'] = value
606
- } else {
607
- el.element.styles = {
608
- 'text-align': value
609
- }
610
- }
611
- } else {
612
- const block = el.element.getBlock()
613
- const inblock = el.element.getInblock()
614
- if (inblock) {
615
- if (inblock.hasStyles()) {
616
- inblock.styles!['text-align'] = value
617
- } else {
618
- inblock.styles = {
619
- 'text-align': value
620
- }
621
- }
622
- } else {
623
- if (block.hasStyles()) {
624
- block.styles!['text-align'] = value
625
- } else {
626
- block.styles = {
627
- 'text-align': value
628
- }
629
- }
630
- }
631
- }
632
- })
633
- }
634
- }
635
-
636
- //插入或者取消 有序或者无序列表 ordered为true表示有序列表
637
- export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
638
- if (!editor.range) {
639
- return
640
- }
641
- //是否都在列表内
642
- const flag = isRangeInList(editor, dataRangeCaches, ordered)
643
- //起点和终点在一起
644
- if (editor.range.anchor.isEqual(editor.range.focus)) {
645
- const block = editor.range.anchor.element.getBlock()
646
- if (flag) {
647
- elementToParagraph(block)
648
- } else {
649
- elementToList(block, ordered)
650
- }
651
- }
652
- //起点和终点不在一起
653
- else {
654
- let blocks: AlexElement[] = []
655
- dataRangeCaches.list.forEach(item => {
656
- const block = item.element.getBlock()
657
- const exist = blocks.some(el => block.isEqual(el))
658
- if (!exist) {
659
- blocks.push(block)
660
- }
661
- })
662
- blocks.forEach(block => {
663
- if (flag) {
664
- elementToParagraph(block)
665
- } else {
666
- elementToList(block, ordered)
667
- }
668
- })
669
- }
670
- }
671
-
672
- //插入或者取消任务列表
673
- export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
674
- if (!editor.range) {
675
- return
676
- }
677
- //是否都在任务列表那
678
- const flag = isRangeInTask(editor, dataRangeCaches)
679
- //起点和终点在一起
680
- if (editor.range.anchor.isEqual(editor.range.focus)) {
681
- const block = editor.range.anchor.element.getBlock()
682
- if (flag) {
683
- elementToParagraph(block)
684
- } else {
685
- elementToTask(block)
686
- }
687
- }
688
- //起点和终点不在一起
689
- else {
690
- let blocks: AlexElement[] = []
691
- dataRangeCaches.list.forEach(item => {
692
- const block = item.element.getBlock()
693
- const exist = blocks.some(el => block.isEqual(el))
694
- if (!exist) {
695
- blocks.push(block)
696
- }
697
- })
698
- blocks.forEach(block => {
699
- if (flag) {
700
- elementToParagraph(block)
701
- } else {
702
- elementToTask(block)
703
- }
704
- })
705
- }
706
- }
707
-
708
- //设置文本元素的样式,styles为{ 'font-weight':'bold' }这类格式
709
- export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
710
- if (!editor.range) {
711
- return
712
- }
713
- //起点和终点在一起
714
- if (editor.range.anchor.isEqual(editor.range.focus)) {
715
- //如果是空白文本元素直接设置样式
716
- if (editor.range.anchor.element.isSpaceText()) {
717
- if (editor.range.anchor.element.hasStyles()) {
718
- Object.assign(editor.range.anchor.element.styles!, cloneData(styles))
719
- } else {
720
- editor.range.anchor.element.styles = cloneData(styles)
721
- }
722
- }
723
- //如果是文本元素
724
- else if (editor.range.anchor.element.isText()) {
725
- //新建一个空白文本元素
726
- const el = AlexElement.getSpaceElement()
727
- //继承文本元素的样式和标记
728
- el.styles = cloneData(editor.range.anchor.element.styles)
729
- el.marks = cloneData(editor.range.anchor.element.marks)
730
- //设置样式
731
- if (el.hasStyles()) {
732
- Object.assign(el.styles!, cloneData(styles))
733
- } else {
734
- el.styles = cloneData(styles)
735
- }
736
- //插入空白文本元素
737
- editor.insertElement(el)
738
- }
739
- //如果是自闭合元素
740
- else {
741
- const el = AlexElement.getSpaceElement()
742
- el.styles = cloneData(styles)
743
- editor.insertElement(el)
744
- }
745
- }
746
- //不在同一个点
747
- else {
748
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
749
- elements.forEach(ele => {
750
- if (ele.isText()) {
751
- if (ele.hasStyles()) {
752
- Object.assign(ele.styles!, cloneData(styles))
753
- } else {
754
- ele.styles = cloneData(styles)
755
- }
756
- }
757
- })
758
- }
759
- }
760
-
761
- //设置文本元素的标记,marks为{ 'class':'a' }这类格式
762
- export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
763
- if (!editor.range) {
764
- return
765
- }
766
- if (!DapCommon.isObject(marks)) {
767
- throw new Error('The argument must be an object')
768
- }
769
- //起点和终点在一起
770
- if (editor.range.anchor.isEqual(editor.range.focus)) {
771
- //如果是空白文本元素直接设置标记
772
- if (editor.range.anchor.element.isSpaceText()) {
773
- if (editor.range.anchor.element.hasMarks()) {
774
- Object.assign(editor.range.anchor.element.marks!, cloneData(marks))
775
- } else {
776
- editor.range.anchor.element.marks = cloneData(marks)
777
- }
778
- }
779
- //如果是文本元素
780
- else if (editor.range.anchor.element.isText()) {
781
- //新建一个空白文本元素
782
- const el = AlexElement.getSpaceElement()
783
- //继承文本元素的样式和标记
784
- el.styles = cloneData(editor.range.anchor.element.styles)
785
- el.marks = cloneData(editor.range.anchor.element.marks)
786
- //设置标记
787
- if (el.hasMarks()) {
788
- Object.assign(el.marks!, cloneData(marks))
789
- } else {
790
- el.marks = cloneData(marks)
791
- }
792
- //插入空白文本元素
793
- editor.insertElement(el)
794
- }
795
- //如果是自闭合元素
796
- else {
797
- const el = AlexElement.getSpaceElement()
798
- el.marks = cloneData(marks)
799
- editor.insertElement(el)
800
- }
801
- }
802
- //不在同一个点
803
- else {
804
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
805
- elements.forEach(ele => {
806
- if (ele.isText()) {
807
- if (ele.hasMarks()) {
808
- Object.assign(ele.marks!, cloneData(marks))
809
- } else {
810
- ele.marks = cloneData(marks)
811
- }
812
- }
813
- })
814
- }
815
- }
816
-
817
- //移除文本元素的样式,styleNames是样式名称数组,如果不存在则移除全部样式
818
- export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
819
- if (!editor.range) {
820
- return
821
- }
822
- //移除样式的方法
823
- const removeFn = (el: AlexElement) => {
824
- //如果参数是数组,表示删除指定的样式
825
- if (Array.isArray(styleNames)) {
826
- if (el.hasStyles()) {
827
- let styles: ObjectType = {}
828
- Object.keys(el.styles!).forEach(key => {
829
- if (!styleNames.includes(key)) {
830
- styles[key] = el.styles![key]
831
- }
832
- })
833
- el.styles = styles
834
- }
835
- }
836
- //如果没有参数,则表示删除所有的样式
837
- else {
838
- el.styles = null
839
- }
840
- }
841
- //如果起点和终点在一起
842
- if (editor.range.anchor.isEqual(editor.range.focus)) {
843
- //如果是空白文本元素直接移除样式
844
- if (editor.range.anchor.element.isSpaceText()) {
845
- removeFn(editor.range.anchor.element)
846
- }
847
- //如果是文本元素则新建一个空白文本元素
848
- else if (editor.range.anchor.element.isText()) {
849
- const el = AlexElement.getSpaceElement()
850
- //继承文本元素的样式和标记
851
- el.styles = cloneData(editor.range.anchor.element.styles)
852
- el.marks = cloneData(editor.range.anchor.element.marks)
853
- //移除样式
854
- removeFn(el)
855
- //插入
856
- editor.insertElement(el)
857
- }
858
- }
859
- //起点和终点不在一起
860
- else {
861
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
862
- elements.forEach(ele => {
863
- if (ele.isText()) {
864
- removeFn(ele)
865
- }
866
- })
867
- }
868
- }
869
-
870
- //移除文本元素的标记,markNames是标记名称数组,如果不存在则移除全部标记
871
- export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
872
- if (!editor.range) {
873
- return
874
- }
875
- //移除样式的方法
876
- const removeFn = (el: AlexElement) => {
877
- //如果参数是数组,表示删除指定的样式
878
- if (Array.isArray(markNames)) {
879
- if (el.hasMarks()) {
880
- let marks: ObjectType = {}
881
- Object.keys(el.marks!).forEach(key => {
882
- if (!markNames.includes(key)) {
883
- marks[key] = el.marks![key]
884
- }
885
- })
886
- el.marks = marks
887
- }
888
- }
889
- //如果没有参数,则表示删除所有的样式
890
- else {
891
- el.marks = null
892
- }
893
- }
894
- //如果起点和终点在一起
895
- if (editor.range.anchor.isEqual(editor.range.focus)) {
896
- //如果是空白文本元素直接移除标记
897
- if (editor.range.anchor.element.isSpaceText()) {
898
- removeFn(editor.range.anchor.element)
899
- }
900
- //如果是文本元素则新建一个空白文本元素
901
- else if (editor.range.anchor.element.isText()) {
902
- const el = AlexElement.getSpaceElement()
903
- //继承文本元素的样式和标记
904
- el.styles = cloneData(editor.range.anchor.element.styles)
905
- el.marks = cloneData(editor.range.anchor.element.marks)
906
- //移除标记
907
- removeFn(el)
908
- //插入
909
- editor.insertElement(el)
910
- }
911
- }
912
- //起点和终点不在一起
913
- else {
914
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
915
- elements.forEach(ele => {
916
- if (ele.isText()) {
917
- removeFn(ele)
918
- }
919
- })
920
- }
921
- }
922
-
923
- //设置块元素或者根级块元素的行高
924
- export const setLineHeight = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: string | number) => {
925
- if (!editor.range) {
926
- return
927
- }
928
- if (editor.range.anchor.isEqual(editor.range.focus)) {
929
- const block = editor.range.anchor.element.getBlock()
930
- const inblock = editor.range.anchor.element.getInblock()
931
- if (inblock) {
932
- if (inblock.hasStyles()) {
933
- inblock.styles!['line-height'] = value
934
- } else {
935
- inblock.styles = {
936
- 'line-height': value
937
- }
938
- }
939
- } else {
940
- if (block.hasStyles()) {
941
- block.styles!['line-height'] = value
942
- } else {
943
- block.styles = {
944
- 'line-height': value
945
- }
946
- }
947
- }
948
- } else {
949
- dataRangeCaches.list.forEach(el => {
950
- if (el.element.isBlock() || el.element.isInblock()) {
951
- if (el.element.hasStyles()) {
952
- el.element.styles!['line-height'] = value
953
- } else {
954
- el.element.styles = {
955
- 'line-height': value
956
- }
957
- }
958
- } else {
959
- const block = el.element.getBlock()
960
- const inblock = el.element.getInblock()
961
- if (inblock) {
962
- if (inblock.hasStyles()) {
963
- inblock.styles!['line-height'] = value
964
- } else {
965
- inblock.styles = {
966
- 'line-height': value
967
- }
968
- }
969
- } else {
970
- if (block.hasStyles()) {
971
- block.styles!['line-height'] = value
972
- } else {
973
- block.styles = {
974
- 'line-height': value
975
- }
976
- }
977
- }
978
- }
979
- })
980
- }
981
- }
982
-
983
- //插入链接
984
- export const insertLink = (editor: AlexEditor, text: string, url: string, newOpen: boolean) => {
985
- if (!editor.range) {
986
- return
987
- }
988
- if (!text) {
989
- text = url
990
- }
991
- const marks: ObjectType = {
992
- href: url
993
- }
994
- if (newOpen) {
995
- marks.target = '_blank'
996
- }
997
- const linkEle = new AlexElement('inline', 'a', marks, null, null)
998
- const textEle = new AlexElement('text', null, null, null, text)
999
- editor.addElementTo(textEle, linkEle)
1000
- editor.insertElement(linkEle)
1001
- }
1002
-
1003
- //插入图片
1004
- export const insertImage = (editor: AlexEditor, value: string) => {
1005
- if (!editor.range) {
1006
- return
1007
- }
1008
- const image = new AlexElement(
1009
- 'closed',
1010
- 'img',
1011
- {
1012
- src: value
1013
- },
1014
- null,
1015
- null
1016
- )
1017
- editor.insertElement(image)
1018
- }
1019
-
1020
- //插入视频
1021
- export const insertVideo = (editor: AlexEditor, value: string) => {
1022
- if (!editor.range) {
1023
- return
1024
- }
1025
- const video = new AlexElement(
1026
- 'closed',
1027
- 'video',
1028
- {
1029
- src: value
1030
- },
1031
- null,
1032
- null
1033
- )
1034
- editor.insertElement(video)
1035
- const leftSpace = AlexElement.getSpaceElement()
1036
- const rightSpace = AlexElement.getSpaceElement()
1037
- editor.addElementAfter(rightSpace, video)
1038
- editor.addElementBefore(leftSpace, video)
1039
- editor.range.anchor.moveToEnd(rightSpace)
1040
- editor.range.focus.moveToEnd(rightSpace)
1041
- }
1042
-
1043
- //插入表格
1044
- export const insertTable = (editor: AlexEditor, rowLength: number, colLength: number) => {
1045
- if (!editor.range) {
1046
- return
1047
- }
1048
- const table = new AlexElement('block', 'table', null, null, null)
1049
- const tbody = new AlexElement('inblock', 'tbody', null, null, null)
1050
- editor.addElementTo(tbody, table)
1051
- for (let i = 0; i < rowLength; i++) {
1052
- const row = new AlexElement('inblock', 'tr', null, null, null)
1053
- for (let j = 0; j < colLength; j++) {
1054
- const column = new AlexElement('inblock', 'td', null, null, null)
1055
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1056
- editor.addElementTo(breakEl, column)
1057
- editor.addElementTo(column, row)
1058
- }
1059
- editor.addElementTo(row, tbody)
1060
- }
1061
- editor.insertElement(table)
1062
- //在表格后创建一个段落
1063
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1064
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1065
- editor.addElementTo(breakEl, paragraph)
1066
- editor.addElementAfter(paragraph, table)
1067
- editor.range.anchor.moveToStart(tbody)
1068
- editor.range.focus.moveToStart(tbody)
1069
- }
1070
-
1071
- //插入或者取消代码块
1072
- export const insertCodeBlock = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1073
- if (!editor.range) {
1074
- return
1075
- }
1076
- const pre = getCurrentParsedomElement(editor, dataRangeCaches, 'pre')
1077
- if (pre) {
1078
- let content = ''
1079
- AlexElement.flatElements(pre.children!)
1080
- .filter(item => {
1081
- return item.isText()
1082
- })
1083
- .forEach(item => {
1084
- content += item.textContent
1085
- })
1086
- const splits = content.split('\n')
1087
- splits.forEach(item => {
1088
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1089
- const text = new AlexElement('text', null, null, null, item)
1090
- editor.addElementTo(text, paragraph)
1091
- editor.addElementBefore(paragraph, pre)
1092
- })
1093
- pre.toEmpty()
1094
- } else {
1095
- //起点和终点在一起
1096
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1097
- const block = editor.range.anchor.element.getBlock()
1098
- elementToParagraph(block)
1099
- block.parsedom = 'pre'
1100
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1101
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1102
- editor.addElementTo(breakEl, paragraph)
1103
- editor.addElementAfter(paragraph, block)
1104
- }
1105
- //起点和终点不在一起
1106
- else {
1107
- editor.range.anchor.moveToStart(dataRangeCaches.list[0].element.getBlock())
1108
- editor.range.focus.moveToEnd(dataRangeCaches.list[dataRangeCaches.list.length - 1].element.getBlock())
1109
- const res = dataRangeCaches.flatList.filter(el => el.element.isText())
1110
- const obj: ObjectType = {}
1111
- res.forEach(el => {
1112
- if (obj[el.element.getBlock().key]) {
1113
- obj[el.element.getBlock().key].push(el.element.clone())
1114
- } else {
1115
- obj[el.element.getBlock().key] = [el.element.clone()]
1116
- }
1117
- })
1118
- const pre = new AlexElement('block', 'pre', null, null, null)
1119
- Object.keys(obj).forEach((key, index) => {
1120
- if (index > 0) {
1121
- const text = new AlexElement('text', null, null, null, '\n')
1122
- if (pre.hasChildren()) {
1123
- editor.addElementTo(text, pre, pre.children!.length)
1124
- } else {
1125
- editor.addElementTo(text, pre)
1126
- }
1127
- }
1128
- obj[key].forEach((el: AlexElement) => {
1129
- if (pre.hasChildren()) {
1130
- editor.addElementTo(el, pre, pre.children!.length)
1131
- } else {
1132
- editor.addElementTo(el, pre)
1133
- }
1134
- })
1135
- })
1136
- editor.delete()
1137
- editor.insertElement(pre)
1138
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1139
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1140
- editor.addElementTo(breakEl, paragraph)
1141
- editor.addElementAfter(paragraph, pre)
1142
- }
1143
- }
1144
- }
1
+ /**
2
+ * 这里的方法都是对编辑器内容元素进行判断或者操作的方法,不涉及到格式化、dom渲染和光标渲染
3
+ */
4
+ import { AlexElement, AlexElementsRangeType, AlexEditor } from 'alex-editor'
5
+ import { common as DapCommon } from 'dap-util'
6
+ import { cloneData, queryHasValue, getButtonOptionsConfig, ObjectType } from './tool'
7
+ import { ButtonOptionsItemType } from '../components/button/props'
8
+
9
+ //判断元素是否在某个标签下,如果是返回该标签对应的元素,否则返回null
10
+ export const getParsedomElementByElement = (element: AlexElement, parsedom: string): AlexElement | null => {
11
+ if (element.isBlock()) {
12
+ return element.parsedom == parsedom ? element : null
13
+ }
14
+ if (!element.isText() && element.parsedom == parsedom) {
15
+ return element
16
+ }
17
+ return getParsedomElementByElement(element.parent!, parsedom)
18
+ }
19
+
20
+ //获取光标是否在指定标签下,如果是返回该标签对应的元素,否则返回null
21
+ export const getCurrentParsedomElement = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, parsedom: string) => {
22
+ if (!editor.range) {
23
+ return null
24
+ }
25
+ if (editor.range.anchor.element.isEqual(editor.range.focus.element)) {
26
+ return getParsedomElementByElement(editor.range.anchor.element, parsedom)
27
+ }
28
+ const arr = dataRangeCaches.list.map(item => {
29
+ return getParsedomElementByElement(item.element, parsedom)
30
+ })
31
+ let hasNull = arr.some(el => {
32
+ return el == null
33
+ })
34
+ //如果存在null,则表示有的选区元素不在指定标签下,返回null
35
+ if (hasNull) {
36
+ return null
37
+ }
38
+ //如果只有一个元素,则返回该元素
39
+ if (arr.length == 1) {
40
+ return arr[0]!
41
+ }
42
+ //默认数组中的元素都相等
43
+ let flag = true
44
+ for (let i = 1; i < arr.length; i++) {
45
+ if (!arr[i]!.isEqual(arr[0]!)) {
46
+ flag = false
47
+ break
48
+ }
49
+ }
50
+ //如果相等,则返回该元素
51
+ if (flag) {
52
+ return arr[0]
53
+ }
54
+ return null
55
+ }
56
+
57
+ //判断元素是否在有序列表或者无序列表下
58
+ export const elementIsInList = (element: AlexElement, ordered: boolean): boolean => {
59
+ if (isList(element, ordered)) {
60
+ return true
61
+ }
62
+ if (element.parent) {
63
+ return elementIsInList(element.parent, ordered)
64
+ }
65
+ return false
66
+ }
67
+
68
+ //判断元素是否在任务列表下
69
+ export const elementIsInTask = (element: AlexElement): boolean => {
70
+ if (isTask(element)) {
71
+ return true
72
+ }
73
+ if (element.parent) {
74
+ return elementIsInTask(element.parent)
75
+ }
76
+ return false
77
+ }
78
+
79
+ //判断元素是否有序或者无序列表
80
+ export const isList = function (element: AlexElement, ordered: boolean | undefined = false) {
81
+ return element.parsedom == 'div' && element.hasMarks() && element.marks!['data-editify-list'] == (ordered ? 'ol' : 'ul')
82
+ }
83
+
84
+ //判断元素是否任务列表
85
+ export const isTask = function (element: AlexElement) {
86
+ return element.parsedom == 'div' && element.hasMarks() && element.marks!.hasOwnProperty('data-editify-task')
87
+ }
88
+
89
+ //选区是否含有代码块
90
+ export const hasPreInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
91
+ if (!editor.range) {
92
+ return false
93
+ }
94
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
95
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'pre')
96
+ }
97
+ return dataRangeCaches.flatList.some(item => {
98
+ return !!getParsedomElementByElement(item.element, 'pre')
99
+ })
100
+ }
101
+
102
+ //选区是否全部在代码块内
103
+ export const isRangeInPre = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
104
+ if (!editor.range) {
105
+ return false
106
+ }
107
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
108
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'pre')
109
+ }
110
+ return dataRangeCaches.list.every(item => {
111
+ return !!getParsedomElementByElement(item.element, 'pre')
112
+ })
113
+ }
114
+
115
+ //选区是否含有引用
116
+ export const hasQuoteInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
117
+ if (!editor.range) {
118
+ return false
119
+ }
120
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
121
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'blockquote')
122
+ }
123
+ return dataRangeCaches.flatList.some(item => {
124
+ return !!getParsedomElementByElement(item.element, 'blockquote')
125
+ })
126
+ }
127
+
128
+ //选区是否全部在引用内
129
+ export const isRangeInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
130
+ if (!editor.range) {
131
+ return false
132
+ }
133
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
134
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'blockquote')
135
+ }
136
+ return dataRangeCaches.list.every(item => {
137
+ return !!getParsedomElementByElement(item.element, 'blockquote')
138
+ })
139
+ }
140
+
141
+ //选区是否含有有序列表或者无序列表
142
+ export const hasListInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean | undefined = false) => {
143
+ if (!editor.range) {
144
+ return false
145
+ }
146
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
147
+ return elementIsInList(editor.range.anchor.element, ordered)
148
+ }
149
+ return dataRangeCaches.flatList.some(item => {
150
+ return elementIsInList(item.element, ordered)
151
+ })
152
+ }
153
+
154
+ //选区是否全部在有序列表或者无序列表内
155
+ export const isRangeInList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean | undefined = false) => {
156
+ if (!editor.range) {
157
+ return false
158
+ }
159
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
160
+ return elementIsInList(editor.range.anchor.element, ordered)
161
+ }
162
+ return dataRangeCaches.list.every(item => {
163
+ return elementIsInList(item.element, ordered)
164
+ })
165
+ }
166
+
167
+ //选区是否含有任务列表
168
+ export const hasTaskInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
169
+ if (!editor.range) {
170
+ return false
171
+ }
172
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
173
+ return elementIsInTask(editor.range.anchor.element)
174
+ }
175
+ return dataRangeCaches.flatList.some(item => {
176
+ return elementIsInTask(item.element)
177
+ })
178
+ }
179
+
180
+ //选区是否全部在任务列表里
181
+ export const isRangeInTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
182
+ if (!editor.range) {
183
+ return false
184
+ }
185
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
186
+ return elementIsInTask(editor.range.anchor.element)
187
+ }
188
+ return dataRangeCaches.list.every(item => {
189
+ return elementIsInTask(item.element)
190
+ })
191
+ }
192
+
193
+ //选区是否含有链接
194
+ export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
195
+ if (!editor.range) {
196
+ return false
197
+ }
198
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
199
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'a')
200
+ }
201
+ return dataRangeCaches.flatList.some(item => {
202
+ return !!getParsedomElementByElement(item.element, 'a')
203
+ })
204
+ }
205
+
206
+ //选区是否含有表格
207
+ export const hasTableInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
208
+ if (!editor.range) {
209
+ return false
210
+ }
211
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
212
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'table')
213
+ }
214
+ return dataRangeCaches.flatList.some(item => {
215
+ return !!getParsedomElementByElement(item.element, 'table')
216
+ })
217
+ }
218
+
219
+ //选区是否含有图片
220
+ export const hasImageInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
221
+ if (!editor.range) {
222
+ return false
223
+ }
224
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
225
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'img')
226
+ }
227
+ return dataRangeCaches.flatList.some(item => {
228
+ return !!getParsedomElementByElement(item.element, 'img')
229
+ })
230
+ }
231
+
232
+ //选区是否含有视频
233
+ export const hasVideoInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
234
+ if (!editor.range) {
235
+ return false
236
+ }
237
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
238
+ return !!getParsedomElementByElement(editor.range.anchor.element, 'video')
239
+ }
240
+ return dataRangeCaches.flatList.some(item => {
241
+ return !!getParsedomElementByElement(item.element, 'video')
242
+ })
243
+ }
244
+
245
+ //查询光标所在的文本元素是否具有某个样式
246
+ export const queryTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
247
+ if (!editor.range) {
248
+ return false
249
+ }
250
+ //起点和终点在一起
251
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
252
+ //如果是文本元素并且具有样式
253
+ if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasStyles()) {
254
+ return queryHasValue(editor.range.anchor.element.styles!, name, value)
255
+ }
256
+ //不是文本元素或者没有样式直接返回
257
+ return false
258
+ }
259
+ //起点和终点不在一起获取选区中的文本元素
260
+ let result = dataRangeCaches.flatList.filter(item => {
261
+ return item.element.isText()
262
+ })
263
+ //如果不包含文本元素直接返回false
264
+ if (result.length == 0) {
265
+ return false
266
+ }
267
+ //判断每个文本元素是否都具有该样式
268
+ let flag = result.every(item => {
269
+ //文本元素含有样式进一步判断
270
+ if (item.element.hasStyles()) {
271
+ return queryHasValue(item.element.styles!, name, value)
272
+ }
273
+ //文本元素没有样式直接返回false
274
+ return false
275
+ })
276
+ return flag
277
+ }
278
+
279
+ //查询光标所在的文本元素是否具有某个标记
280
+ export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
281
+ if (!editor.range) {
282
+ return false
283
+ }
284
+ //起点和终点在一起
285
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
286
+ //如果是文本元素并且具有标记
287
+ if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
288
+ return queryHasValue(editor.range.anchor.element.marks!, name, value)
289
+ }
290
+ //不是文本元素或者没有标记直接返回
291
+ return false
292
+ }
293
+ //起点和终点不在一起获取选区中的文本元素
294
+ let result = dataRangeCaches.flatList.filter(item => {
295
+ return item.element.isText()
296
+ })
297
+ //如果不包含文本元素直接返回false
298
+ if (result.length == 0) {
299
+ return false
300
+ }
301
+ //判断每个文本元素是否都具有该标记
302
+ let flag = result.every(item => {
303
+ //文本元素含有标记进一步判断
304
+ if (item.element.hasMarks()) {
305
+ return queryHasValue(item.element.marks!, name, value)
306
+ }
307
+ //文本元素没有标记直接返回false
308
+ return false
309
+ })
310
+ return flag
311
+ }
312
+
313
+ //获取选区内的文字内容,用于预置链接文字
314
+ export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
315
+ //存在选区的情况下预置链接文本值
316
+ let text = ''
317
+ dataRangeCaches.flatList.forEach(item => {
318
+ if (item.element.isText()) {
319
+ if (item.offset) {
320
+ text += item.element.textContent!.substring(item.offset[0], item.offset[1])
321
+ } else {
322
+ text += item.element.textContent || ''
323
+ }
324
+ }
325
+ })
326
+ return text
327
+ }
328
+
329
+ //获取光标选取内的扁平化元素数组(可能会分割文本元素导致stack变更,同时也会更新选取元素和光标位置)
330
+ export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
331
+ if (!editor.range) {
332
+ return []
333
+ }
334
+ //获取选区数据的长度
335
+ let length = dataRangeCaches.flatList.length
336
+ //返回的元素数组
337
+ let elements = []
338
+ //遍历选区数据
339
+ for (let i = 0; i < length; i++) {
340
+ const item = dataRangeCaches.flatList[i]
341
+ //如果存在offset那么一定是文本元素
342
+ if (item.offset) {
343
+ let selectEl = null
344
+ //文本元素前面一部分在光标范围内
345
+ if (item.offset[0] == 0 && item.offset[1] < item.element.textContent!.length) {
346
+ const el = item.element.clone()
347
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[1])
348
+ el.textContent = el.textContent!.substring(item.offset[1])
349
+ editor.addElementAfter(el, item.element)
350
+ selectEl = item.element
351
+ }
352
+ //文本元素后面一部分在光标范围内
353
+ else if (item.offset[1] == item.element.textContent!.length && item.offset[0] > 0) {
354
+ const el = item.element.clone()
355
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
356
+ el.textContent = el.textContent!.substring(item.offset[0])
357
+ editor.addElementAfter(el, item.element)
358
+ selectEl = el
359
+ }
360
+ //文本元素的中间一部分在光标范围内
361
+ else if (item.offset[0] > 0 && item.offset[1] < item.element.textContent!.length) {
362
+ const el = item.element.clone()
363
+ const el2 = item.element.clone()
364
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
365
+ el.textContent = el.textContent!.substring(item.offset[0], item.offset[1])
366
+ el2.textContent = el2.textContent!.substring(item.offset[1])
367
+ editor.addElementAfter(el, item.element)
368
+ editor.addElementAfter(el2, el)
369
+ selectEl = el
370
+ }
371
+ //如果selectEl存在证明文本元素被分割了
372
+ if (selectEl) {
373
+ //如果i为0的话肯定是起点
374
+ if (i == 0) {
375
+ editor.range.anchor.moveToStart(selectEl)
376
+ }
377
+ //如果i是最后一个序列的话肯定是终点
378
+ if (i == length - 1) {
379
+ editor.range.focus.moveToEnd(selectEl)
380
+ }
381
+ elements.push(selectEl)
382
+ }
383
+ } else {
384
+ elements.push(item.element)
385
+ }
386
+ }
387
+ return elements
388
+ }
389
+
390
+ //将某个元素转为段落标签
391
+ export const elementToParagraph = function (element: AlexElement) {
392
+ element.marks = null
393
+ element.styles = null
394
+ element.parsedom = AlexElement.BLOCK_NODE
395
+ }
396
+
397
+ //其他元素转为有序或者无序列表
398
+ export const elementToList = function (element: AlexElement, ordered: boolean | undefined = false) {
399
+ //如果是列表则返回
400
+ if (isList(element, ordered)) {
401
+ return
402
+ }
403
+ //先转为段落
404
+ elementToParagraph(element)
405
+ //然后转为列表
406
+ element.parsedom = 'div'
407
+ if (!element.hasMarks()) {
408
+ element.marks = {}
409
+ }
410
+ element.marks!['data-editify-list'] = ordered ? 'ol' : 'ul'
411
+ }
412
+
413
+ //其他元素转为任务列表
414
+ export const elementToTask = function (element: AlexElement) {
415
+ //如果是任务列表则返回
416
+ if (isTask(element)) {
417
+ return
418
+ }
419
+ //先转为段落
420
+ elementToParagraph(element)
421
+ //然后转为任务列表
422
+ element.parsedom = 'div'
423
+ if (!element.hasMarks()) {
424
+ element.marks = {}
425
+ }
426
+ element.marks!['data-editify-task'] = 'uncheck'
427
+ }
428
+
429
+ //设置标题
430
+ export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, editTrans: (key: string) => any, parsedom: string) => {
431
+ if (!editor.range) {
432
+ return
433
+ }
434
+ const values = getButtonOptionsConfig(editTrans).heading!.map(item => {
435
+ return (<ButtonOptionsItemType>item).value
436
+ })
437
+ if (!values.includes(parsedom)) {
438
+ throw new Error('The parameter supports only h1-h6 and p')
439
+ }
440
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
441
+ const block = editor.range.anchor.element.getBlock()
442
+ //先转为段落
443
+ elementToParagraph(block)
444
+ //设置标题
445
+ block.parsedom = parsedom
446
+ } else {
447
+ dataRangeCaches.list.forEach(el => {
448
+ if (el.element.isBlock()) {
449
+ elementToParagraph(el.element)
450
+ el.element.parsedom = parsedom
451
+ } else {
452
+ const block = el.element.getBlock()
453
+ elementToParagraph(block)
454
+ block.parsedom = parsedom
455
+ }
456
+ })
457
+ }
458
+ }
459
+
460
+ //根级块元素或者内部块元素增加缩进
461
+ export const setIndentIncrease = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
462
+ if (!editor.range) {
463
+ return
464
+ }
465
+ const fn = (element: AlexElement) => {
466
+ if (element.hasStyles()) {
467
+ if (element.styles!.hasOwnProperty('text-indent')) {
468
+ let val = element.styles!['text-indent']
469
+ if (val.endsWith('em')) {
470
+ val = parseFloat(val)
471
+ } else {
472
+ val = 0
473
+ }
474
+ element.styles!['text-indent'] = `${val + 2}em`
475
+ } else {
476
+ element.styles!['text-indent'] = '2em'
477
+ }
478
+ } else {
479
+ element.styles = {
480
+ 'text-indent': '2em'
481
+ }
482
+ }
483
+ }
484
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
485
+ const block = editor.range.anchor.element.getBlock()
486
+ const inblock = editor.range.anchor.element.getInblock()
487
+ if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
488
+ fn(inblock)
489
+ } else if (!block.isPreStyle()) {
490
+ fn(block)
491
+ }
492
+ } else {
493
+ dataRangeCaches.list.forEach(item => {
494
+ const block = item.element.getBlock()
495
+ const inblock = item.element.getInblock()
496
+ if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
497
+ fn(inblock)
498
+ } else if (!block.isPreStyle()) {
499
+ fn(block)
500
+ }
501
+ })
502
+ }
503
+ }
504
+
505
+ //根级块元素或者内部块元素减少缩进
506
+ export const setIndentDecrease = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
507
+ if (!editor.range) {
508
+ return
509
+ }
510
+ const fn = (element: AlexElement) => {
511
+ if (element.hasStyles() && element.styles!.hasOwnProperty('text-indent')) {
512
+ let val = element.styles!['text-indent']
513
+ if (val.endsWith('em')) {
514
+ val = parseFloat(val)
515
+ } else {
516
+ val = 0
517
+ }
518
+ element.styles!['text-indent'] = `${val - 2 >= 0 ? val - 2 : 0}em`
519
+ }
520
+ }
521
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
522
+ const block = editor.range.anchor.element.getBlock()
523
+ const inblock = editor.range.anchor.element.getInblock()
524
+ if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
525
+ fn(inblock)
526
+ } else if (!block.isPreStyle()) {
527
+ fn(block)
528
+ }
529
+ } else {
530
+ dataRangeCaches.list.forEach(item => {
531
+ const block = item.element.getBlock()
532
+ const inblock = item.element.getInblock()
533
+ if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
534
+ fn(inblock)
535
+ } else if (!block.isPreStyle()) {
536
+ fn(block)
537
+ }
538
+ })
539
+ }
540
+ }
541
+
542
+ //插入或者取消引用
543
+ export const setQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
544
+ if (!editor.range) {
545
+ return
546
+ }
547
+ //是否都在引用里
548
+ const flag = isRangeInQuote(editor, dataRangeCaches)
549
+ //起点和终点在一起
550
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
551
+ const block = editor.range.anchor.element.getBlock()
552
+ elementToParagraph(block)
553
+ if (!flag) {
554
+ block.parsedom = 'blockquote'
555
+ }
556
+ }
557
+ //起点和终点不在一起
558
+ else {
559
+ let blocks: AlexElement[] = []
560
+ dataRangeCaches.list.forEach(item => {
561
+ const block = item.element.getBlock()
562
+ const exist = blocks.some(el => block.isEqual(el))
563
+ if (!exist) {
564
+ blocks.push(block)
565
+ }
566
+ })
567
+ blocks.forEach(block => {
568
+ elementToParagraph(block)
569
+ if (!flag) {
570
+ block.parsedom = 'blockquote'
571
+ }
572
+ })
573
+ }
574
+ }
575
+
576
+ //设置对齐方式,参数取值justify/left/right/center
577
+ export const setAlign = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: string) => {
578
+ if (!editor.range) {
579
+ return
580
+ }
581
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
582
+ const block = editor.range.anchor.element.getBlock()
583
+ const inblock = editor.range.anchor.element.getInblock()
584
+ if (inblock) {
585
+ if (inblock.hasStyles()) {
586
+ inblock.styles!['text-align'] = value
587
+ } else {
588
+ inblock.styles = {
589
+ 'text-align': value
590
+ }
591
+ }
592
+ } else {
593
+ if (block.hasStyles()) {
594
+ block.styles!['text-align'] = value
595
+ } else {
596
+ block.styles = {
597
+ 'text-align': value
598
+ }
599
+ }
600
+ }
601
+ } else {
602
+ dataRangeCaches.list.forEach(el => {
603
+ if (el.element.isBlock() || el.element.isInblock()) {
604
+ if (el.element.hasStyles()) {
605
+ el.element.styles!['text-align'] = value
606
+ } else {
607
+ el.element.styles = {
608
+ 'text-align': value
609
+ }
610
+ }
611
+ } else {
612
+ const block = el.element.getBlock()
613
+ const inblock = el.element.getInblock()
614
+ if (inblock) {
615
+ if (inblock.hasStyles()) {
616
+ inblock.styles!['text-align'] = value
617
+ } else {
618
+ inblock.styles = {
619
+ 'text-align': value
620
+ }
621
+ }
622
+ } else {
623
+ if (block.hasStyles()) {
624
+ block.styles!['text-align'] = value
625
+ } else {
626
+ block.styles = {
627
+ 'text-align': value
628
+ }
629
+ }
630
+ }
631
+ }
632
+ })
633
+ }
634
+ }
635
+
636
+ //插入或者取消 有序或者无序列表 ordered为true表示有序列表
637
+ export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
638
+ if (!editor.range) {
639
+ return
640
+ }
641
+ //是否都在列表内
642
+ const flag = isRangeInList(editor, dataRangeCaches, ordered)
643
+ //起点和终点在一起
644
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
645
+ const block = editor.range.anchor.element.getBlock()
646
+ if (flag) {
647
+ elementToParagraph(block)
648
+ } else {
649
+ elementToList(block, ordered)
650
+ }
651
+ }
652
+ //起点和终点不在一起
653
+ else {
654
+ let blocks: AlexElement[] = []
655
+ dataRangeCaches.list.forEach(item => {
656
+ const block = item.element.getBlock()
657
+ const exist = blocks.some(el => block.isEqual(el))
658
+ if (!exist) {
659
+ blocks.push(block)
660
+ }
661
+ })
662
+ blocks.forEach(block => {
663
+ if (flag) {
664
+ elementToParagraph(block)
665
+ } else {
666
+ elementToList(block, ordered)
667
+ }
668
+ })
669
+ }
670
+ }
671
+
672
+ //插入或者取消任务列表
673
+ export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
674
+ if (!editor.range) {
675
+ return
676
+ }
677
+ //是否都在任务列表那
678
+ const flag = isRangeInTask(editor, dataRangeCaches)
679
+ //起点和终点在一起
680
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
681
+ const block = editor.range.anchor.element.getBlock()
682
+ if (flag) {
683
+ elementToParagraph(block)
684
+ } else {
685
+ elementToTask(block)
686
+ }
687
+ }
688
+ //起点和终点不在一起
689
+ else {
690
+ let blocks: AlexElement[] = []
691
+ dataRangeCaches.list.forEach(item => {
692
+ const block = item.element.getBlock()
693
+ const exist = blocks.some(el => block.isEqual(el))
694
+ if (!exist) {
695
+ blocks.push(block)
696
+ }
697
+ })
698
+ blocks.forEach(block => {
699
+ if (flag) {
700
+ elementToParagraph(block)
701
+ } else {
702
+ elementToTask(block)
703
+ }
704
+ })
705
+ }
706
+ }
707
+
708
+ //设置文本元素的样式,styles为{ 'font-weight':'bold' }这类格式
709
+ export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
710
+ if (!editor.range) {
711
+ return
712
+ }
713
+ //起点和终点在一起
714
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
715
+ //如果是空白文本元素直接设置样式
716
+ if (editor.range.anchor.element.isSpaceText()) {
717
+ if (editor.range.anchor.element.hasStyles()) {
718
+ Object.assign(editor.range.anchor.element.styles!, cloneData(styles))
719
+ } else {
720
+ editor.range.anchor.element.styles = cloneData(styles)
721
+ }
722
+ }
723
+ //如果是文本元素
724
+ else if (editor.range.anchor.element.isText()) {
725
+ //新建一个空白文本元素
726
+ const el = AlexElement.getSpaceElement()
727
+ //继承文本元素的样式和标记
728
+ el.styles = cloneData(editor.range.anchor.element.styles)
729
+ el.marks = cloneData(editor.range.anchor.element.marks)
730
+ //设置样式
731
+ if (el.hasStyles()) {
732
+ Object.assign(el.styles!, cloneData(styles))
733
+ } else {
734
+ el.styles = cloneData(styles)
735
+ }
736
+ //插入空白文本元素
737
+ editor.insertElement(el)
738
+ }
739
+ //如果是自闭合元素
740
+ else {
741
+ const el = AlexElement.getSpaceElement()
742
+ el.styles = cloneData(styles)
743
+ editor.insertElement(el)
744
+ }
745
+ }
746
+ //不在同一个点
747
+ else {
748
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
749
+ elements.forEach(ele => {
750
+ if (ele.isText()) {
751
+ if (ele.hasStyles()) {
752
+ Object.assign(ele.styles!, cloneData(styles))
753
+ } else {
754
+ ele.styles = cloneData(styles)
755
+ }
756
+ }
757
+ })
758
+ }
759
+ }
760
+
761
+ //设置文本元素的标记,marks为{ 'class':'a' }这类格式
762
+ export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
763
+ if (!editor.range) {
764
+ return
765
+ }
766
+ if (!DapCommon.isObject(marks)) {
767
+ throw new Error('The argument must be an object')
768
+ }
769
+ //起点和终点在一起
770
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
771
+ //如果是空白文本元素直接设置标记
772
+ if (editor.range.anchor.element.isSpaceText()) {
773
+ if (editor.range.anchor.element.hasMarks()) {
774
+ Object.assign(editor.range.anchor.element.marks!, cloneData(marks))
775
+ } else {
776
+ editor.range.anchor.element.marks = cloneData(marks)
777
+ }
778
+ }
779
+ //如果是文本元素
780
+ else if (editor.range.anchor.element.isText()) {
781
+ //新建一个空白文本元素
782
+ const el = AlexElement.getSpaceElement()
783
+ //继承文本元素的样式和标记
784
+ el.styles = cloneData(editor.range.anchor.element.styles)
785
+ el.marks = cloneData(editor.range.anchor.element.marks)
786
+ //设置标记
787
+ if (el.hasMarks()) {
788
+ Object.assign(el.marks!, cloneData(marks))
789
+ } else {
790
+ el.marks = cloneData(marks)
791
+ }
792
+ //插入空白文本元素
793
+ editor.insertElement(el)
794
+ }
795
+ //如果是自闭合元素
796
+ else {
797
+ const el = AlexElement.getSpaceElement()
798
+ el.marks = cloneData(marks)
799
+ editor.insertElement(el)
800
+ }
801
+ }
802
+ //不在同一个点
803
+ else {
804
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
805
+ elements.forEach(ele => {
806
+ if (ele.isText()) {
807
+ if (ele.hasMarks()) {
808
+ Object.assign(ele.marks!, cloneData(marks))
809
+ } else {
810
+ ele.marks = cloneData(marks)
811
+ }
812
+ }
813
+ })
814
+ }
815
+ }
816
+
817
+ //移除文本元素的样式,styleNames是样式名称数组,如果不存在则移除全部样式
818
+ export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
819
+ if (!editor.range) {
820
+ return
821
+ }
822
+ //移除样式的方法
823
+ const removeFn = (el: AlexElement) => {
824
+ //如果参数是数组,表示删除指定的样式
825
+ if (Array.isArray(styleNames)) {
826
+ if (el.hasStyles()) {
827
+ let styles: ObjectType = {}
828
+ Object.keys(el.styles!).forEach(key => {
829
+ if (!styleNames.includes(key)) {
830
+ styles[key] = el.styles![key]
831
+ }
832
+ })
833
+ el.styles = styles
834
+ }
835
+ }
836
+ //如果没有参数,则表示删除所有的样式
837
+ else {
838
+ el.styles = null
839
+ }
840
+ }
841
+ //如果起点和终点在一起
842
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
843
+ //如果是空白文本元素直接移除样式
844
+ if (editor.range.anchor.element.isSpaceText()) {
845
+ removeFn(editor.range.anchor.element)
846
+ }
847
+ //如果是文本元素则新建一个空白文本元素
848
+ else if (editor.range.anchor.element.isText()) {
849
+ const el = AlexElement.getSpaceElement()
850
+ //继承文本元素的样式和标记
851
+ el.styles = cloneData(editor.range.anchor.element.styles)
852
+ el.marks = cloneData(editor.range.anchor.element.marks)
853
+ //移除样式
854
+ removeFn(el)
855
+ //插入
856
+ editor.insertElement(el)
857
+ }
858
+ }
859
+ //起点和终点不在一起
860
+ else {
861
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
862
+ elements.forEach(ele => {
863
+ if (ele.isText()) {
864
+ removeFn(ele)
865
+ }
866
+ })
867
+ }
868
+ }
869
+
870
+ //移除文本元素的标记,markNames是标记名称数组,如果不存在则移除全部标记
871
+ export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
872
+ if (!editor.range) {
873
+ return
874
+ }
875
+ //移除样式的方法
876
+ const removeFn = (el: AlexElement) => {
877
+ //如果参数是数组,表示删除指定的样式
878
+ if (Array.isArray(markNames)) {
879
+ if (el.hasMarks()) {
880
+ let marks: ObjectType = {}
881
+ Object.keys(el.marks!).forEach(key => {
882
+ if (!markNames.includes(key)) {
883
+ marks[key] = el.marks![key]
884
+ }
885
+ })
886
+ el.marks = marks
887
+ }
888
+ }
889
+ //如果没有参数,则表示删除所有的样式
890
+ else {
891
+ el.marks = null
892
+ }
893
+ }
894
+ //如果起点和终点在一起
895
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
896
+ //如果是空白文本元素直接移除标记
897
+ if (editor.range.anchor.element.isSpaceText()) {
898
+ removeFn(editor.range.anchor.element)
899
+ }
900
+ //如果是文本元素则新建一个空白文本元素
901
+ else if (editor.range.anchor.element.isText()) {
902
+ const el = AlexElement.getSpaceElement()
903
+ //继承文本元素的样式和标记
904
+ el.styles = cloneData(editor.range.anchor.element.styles)
905
+ el.marks = cloneData(editor.range.anchor.element.marks)
906
+ //移除标记
907
+ removeFn(el)
908
+ //插入
909
+ editor.insertElement(el)
910
+ }
911
+ }
912
+ //起点和终点不在一起
913
+ else {
914
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
915
+ elements.forEach(ele => {
916
+ if (ele.isText()) {
917
+ removeFn(ele)
918
+ }
919
+ })
920
+ }
921
+ }
922
+
923
+ //设置块元素或者根级块元素的行高
924
+ export const setLineHeight = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: string | number) => {
925
+ if (!editor.range) {
926
+ return
927
+ }
928
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
929
+ const block = editor.range.anchor.element.getBlock()
930
+ const inblock = editor.range.anchor.element.getInblock()
931
+ if (inblock) {
932
+ if (inblock.hasStyles()) {
933
+ inblock.styles!['line-height'] = value
934
+ } else {
935
+ inblock.styles = {
936
+ 'line-height': value
937
+ }
938
+ }
939
+ } else {
940
+ if (block.hasStyles()) {
941
+ block.styles!['line-height'] = value
942
+ } else {
943
+ block.styles = {
944
+ 'line-height': value
945
+ }
946
+ }
947
+ }
948
+ } else {
949
+ dataRangeCaches.list.forEach(el => {
950
+ if (el.element.isBlock() || el.element.isInblock()) {
951
+ if (el.element.hasStyles()) {
952
+ el.element.styles!['line-height'] = value
953
+ } else {
954
+ el.element.styles = {
955
+ 'line-height': value
956
+ }
957
+ }
958
+ } else {
959
+ const block = el.element.getBlock()
960
+ const inblock = el.element.getInblock()
961
+ if (inblock) {
962
+ if (inblock.hasStyles()) {
963
+ inblock.styles!['line-height'] = value
964
+ } else {
965
+ inblock.styles = {
966
+ 'line-height': value
967
+ }
968
+ }
969
+ } else {
970
+ if (block.hasStyles()) {
971
+ block.styles!['line-height'] = value
972
+ } else {
973
+ block.styles = {
974
+ 'line-height': value
975
+ }
976
+ }
977
+ }
978
+ }
979
+ })
980
+ }
981
+ }
982
+
983
+ //插入链接
984
+ export const insertLink = (editor: AlexEditor, text: string, url: string, newOpen: boolean) => {
985
+ if (!editor.range) {
986
+ return
987
+ }
988
+ if (!text) {
989
+ text = url
990
+ }
991
+ const marks: ObjectType = {
992
+ href: url
993
+ }
994
+ if (newOpen) {
995
+ marks.target = '_blank'
996
+ }
997
+ const linkEle = new AlexElement('inline', 'a', marks, null, null)
998
+ const textEle = new AlexElement('text', null, null, null, text)
999
+ editor.addElementTo(textEle, linkEle)
1000
+ editor.insertElement(linkEle)
1001
+ }
1002
+
1003
+ //插入图片
1004
+ export const insertImage = (editor: AlexEditor, value: string) => {
1005
+ if (!editor.range) {
1006
+ return
1007
+ }
1008
+ const image = new AlexElement(
1009
+ 'closed',
1010
+ 'img',
1011
+ {
1012
+ src: value
1013
+ },
1014
+ null,
1015
+ null
1016
+ )
1017
+ editor.insertElement(image)
1018
+ }
1019
+
1020
+ //插入视频
1021
+ export const insertVideo = (editor: AlexEditor, value: string) => {
1022
+ if (!editor.range) {
1023
+ return
1024
+ }
1025
+ const video = new AlexElement(
1026
+ 'closed',
1027
+ 'video',
1028
+ {
1029
+ src: value
1030
+ },
1031
+ null,
1032
+ null
1033
+ )
1034
+ editor.insertElement(video)
1035
+ const leftSpace = AlexElement.getSpaceElement()
1036
+ const rightSpace = AlexElement.getSpaceElement()
1037
+ editor.addElementAfter(rightSpace, video)
1038
+ editor.addElementBefore(leftSpace, video)
1039
+ editor.range.anchor.moveToEnd(rightSpace)
1040
+ editor.range.focus.moveToEnd(rightSpace)
1041
+ }
1042
+
1043
+ //插入表格
1044
+ export const insertTable = (editor: AlexEditor, rowLength: number, colLength: number) => {
1045
+ if (!editor.range) {
1046
+ return
1047
+ }
1048
+ const table = new AlexElement('block', 'table', null, null, null)
1049
+ const tbody = new AlexElement('inblock', 'tbody', null, null, null)
1050
+ editor.addElementTo(tbody, table)
1051
+ for (let i = 0; i < rowLength; i++) {
1052
+ const row = new AlexElement('inblock', 'tr', null, null, null)
1053
+ for (let j = 0; j < colLength; j++) {
1054
+ const column = new AlexElement('inblock', 'td', null, null, null)
1055
+ const breakEl = new AlexElement('closed', 'br', null, null, null)
1056
+ editor.addElementTo(breakEl, column)
1057
+ editor.addElementTo(column, row)
1058
+ }
1059
+ editor.addElementTo(row, tbody)
1060
+ }
1061
+ editor.insertElement(table)
1062
+ //在表格后创建一个段落
1063
+ const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1064
+ const breakEl = new AlexElement('closed', 'br', null, null, null)
1065
+ editor.addElementTo(breakEl, paragraph)
1066
+ editor.addElementAfter(paragraph, table)
1067
+ editor.range.anchor.moveToStart(tbody)
1068
+ editor.range.focus.moveToStart(tbody)
1069
+ }
1070
+
1071
+ //插入或者取消代码块
1072
+ export const insertCodeBlock = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1073
+ if (!editor.range) {
1074
+ return
1075
+ }
1076
+ const pre = getCurrentParsedomElement(editor, dataRangeCaches, 'pre')
1077
+ if (pre) {
1078
+ let content = ''
1079
+ AlexElement.flatElements(pre.children!)
1080
+ .filter(item => {
1081
+ return item.isText()
1082
+ })
1083
+ .forEach(item => {
1084
+ content += item.textContent
1085
+ })
1086
+ const splits = content.split('\n')
1087
+ splits.forEach(item => {
1088
+ const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1089
+ const text = new AlexElement('text', null, null, null, item)
1090
+ editor.addElementTo(text, paragraph)
1091
+ editor.addElementBefore(paragraph, pre)
1092
+ })
1093
+ pre.toEmpty()
1094
+ } else {
1095
+ //起点和终点在一起
1096
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1097
+ const block = editor.range.anchor.element.getBlock()
1098
+ elementToParagraph(block)
1099
+ block.parsedom = 'pre'
1100
+ const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1101
+ const breakEl = new AlexElement('closed', 'br', null, null, null)
1102
+ editor.addElementTo(breakEl, paragraph)
1103
+ editor.addElementAfter(paragraph, block)
1104
+ }
1105
+ //起点和终点不在一起
1106
+ else {
1107
+ editor.range.anchor.moveToStart(dataRangeCaches.list[0].element.getBlock())
1108
+ editor.range.focus.moveToEnd(dataRangeCaches.list[dataRangeCaches.list.length - 1].element.getBlock())
1109
+ const res = dataRangeCaches.flatList.filter(el => el.element.isText())
1110
+ const obj: ObjectType = {}
1111
+ res.forEach(el => {
1112
+ if (obj[el.element.getBlock().key]) {
1113
+ obj[el.element.getBlock().key].push(el.element.clone())
1114
+ } else {
1115
+ obj[el.element.getBlock().key] = [el.element.clone()]
1116
+ }
1117
+ })
1118
+ const pre = new AlexElement('block', 'pre', null, null, null)
1119
+ Object.keys(obj).forEach((key, index) => {
1120
+ if (index > 0) {
1121
+ const text = new AlexElement('text', null, null, null, '\n')
1122
+ if (pre.hasChildren()) {
1123
+ editor.addElementTo(text, pre, pre.children!.length)
1124
+ } else {
1125
+ editor.addElementTo(text, pre)
1126
+ }
1127
+ }
1128
+ obj[key].forEach((el: AlexElement) => {
1129
+ if (pre.hasChildren()) {
1130
+ editor.addElementTo(el, pre, pre.children!.length)
1131
+ } else {
1132
+ editor.addElementTo(el, pre)
1133
+ }
1134
+ })
1135
+ })
1136
+ editor.delete()
1137
+ editor.insertElement(pre)
1138
+ const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1139
+ const breakEl = new AlexElement('closed', 'br', null, null, null)
1140
+ editor.addElementTo(breakEl, paragraph)
1141
+ editor.addElementAfter(paragraph, pre)
1142
+ }
1143
+ }
1144
+ }