vue-editify 0.1.19 → 0.1.20

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