vue-editify 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +3 -3
  2. package/examples/App.vue +62 -53
  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 +88 -70
  18. package/lib/editify/props.d.ts +11 -3
  19. package/lib/editify.es.js +65 -46
  20. package/lib/editify.umd.js +1 -1
  21. package/lib/index.d.ts +1 -1
  22. package/lib/style.css +1 -1
  23. package/package.json +45 -45
  24. package/src/components/button/button.less +145 -145
  25. package/src/components/button/button.vue +197 -197
  26. package/src/components/button/props.ts +95 -95
  27. package/src/components/checkbox/checkbox.less +84 -84
  28. package/src/components/checkbox/checkbox.vue +68 -68
  29. package/src/components/checkbox/props.ts +49 -49
  30. package/src/components/colors/colors.less +75 -75
  31. package/src/components/colors/colors.vue +36 -36
  32. package/src/components/colors/props.ts +29 -29
  33. package/src/components/icon/icon.less +14 -14
  34. package/src/components/icon/icon.vue +12 -12
  35. package/src/components/icon/props.ts +11 -11
  36. package/src/components/insertImage/insertImage.less +135 -135
  37. package/src/components/insertImage/insertImage.vue +146 -146
  38. package/src/components/insertImage/props.ts +43 -43
  39. package/src/components/insertLink/insertLink.less +64 -64
  40. package/src/components/insertLink/insertLink.vue +58 -58
  41. package/src/components/insertLink/props.ts +16 -16
  42. package/src/components/insertTable/insertTable.less +54 -54
  43. package/src/components/insertTable/insertTable.vue +85 -85
  44. package/src/components/insertTable/props.ts +27 -27
  45. package/src/components/insertVideo/insertVideo.less +135 -135
  46. package/src/components/insertVideo/insertVideo.vue +146 -146
  47. package/src/components/insertVideo/props.ts +43 -43
  48. package/src/components/layer/layer.less +49 -49
  49. package/src/components/layer/layer.vue +598 -598
  50. package/src/components/layer/props.ts +71 -71
  51. package/src/components/menu/menu.less +63 -63
  52. package/src/components/menu/menu.vue +1569 -1569
  53. package/src/components/menu/props.ts +17 -17
  54. package/src/components/toolbar/props.ts +35 -35
  55. package/src/components/toolbar/toolbar.less +89 -89
  56. package/src/components/toolbar/toolbar.vue +1101 -1101
  57. package/src/components/tooltip/props.ts +21 -21
  58. package/src/components/tooltip/tooltip.less +23 -23
  59. package/src/components/tooltip/tooltip.vue +37 -37
  60. package/src/components/triangle/props.ts +26 -26
  61. package/src/components/triangle/triangle.less +79 -79
  62. package/src/components/triangle/triangle.vue +65 -65
  63. package/src/core/function.ts +1144 -1144
  64. package/src/core/rule.ts +259 -259
  65. package/src/core/tool.ts +1137 -1137
  66. package/src/css/base.less +30 -30
  67. package/src/css/hljs.less +54 -54
  68. package/src/editify/editify.less +404 -403
  69. package/src/editify/editify.vue +803 -792
  70. package/src/editify/props.ts +156 -146
  71. package/src/hljs/index.ts +197 -197
  72. package/src/icon/iconfont.css +219 -219
  73. package/src/index.ts +32 -32
  74. package/src/locale/en_US.ts +88 -88
  75. package/src/locale/index.ts +12 -12
  76. package/src/locale/zh_CN.ts +88 -88
  77. package/tsconfig.json +27 -27
  78. package/tsconfig.node.json +11 -11
  79. package/vite-env.d.ts +1 -1
  80. 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
+ }