vue-editify 0.2.16 → 0.2.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/examples/App.vue +289 -5
  2. package/lib/components/colors/colors.vue.d.ts +9 -0
  3. package/lib/components/colors/props.d.ts +4 -0
  4. package/lib/core/function.d.ts +120 -45
  5. package/lib/core/rule.d.ts +23 -17
  6. package/lib/core/tool.d.ts +1 -13
  7. package/lib/editify/editify.vue.d.ts +10 -1
  8. package/lib/editify/props.d.ts +1 -1
  9. package/lib/editify/toolbar/props.d.ts +1 -1
  10. package/lib/editify/toolbar/toolbar.vue.d.ts +3 -3
  11. package/lib/editify.es.js +13640 -13799
  12. package/lib/editify.umd.js +2 -2
  13. package/lib/feature/align.d.ts +0 -14
  14. package/lib/feature/heading.d.ts +0 -14
  15. package/lib/feature/lineHeight.d.ts +0 -14
  16. package/lib/feature/orderList.d.ts +1 -3
  17. package/lib/feature/task.d.ts +0 -14
  18. package/lib/feature/unorderList.d.ts +1 -3
  19. package/lib/index.d.ts +12 -3
  20. package/package.json +2 -2
  21. package/src/components/button/button.vue +3 -3
  22. package/src/components/checkbox/checkbox.vue +1 -1
  23. package/src/components/colors/colors.vue +4 -4
  24. package/src/components/colors/props.ts +6 -1
  25. package/src/components/insertAttachment/insertAttachment.vue +1 -1
  26. package/src/components/insertImage/insertImage.vue +1 -1
  27. package/src/components/insertLink/insertLink.vue +1 -1
  28. package/src/components/insertVideo/insertVideo.vue +1 -1
  29. package/src/components/layer/layer.vue +9 -3
  30. package/src/components/tooltip/tooltip.vue +1 -1
  31. package/src/components/updateLink/updateLink.vue +1 -1
  32. package/src/core/function.ts +961 -475
  33. package/src/core/rule.ts +85 -367
  34. package/src/core/tool.ts +8 -114
  35. package/src/editify/editify.less +88 -14
  36. package/src/editify/editify.vue +117 -65
  37. package/src/editify/props.ts +1 -1
  38. package/src/editify/toolbar/props.ts +2 -2
  39. package/src/editify/toolbar/toolbar.vue +12 -12
  40. package/src/feature/align.ts +1 -61
  41. package/src/feature/attachment.ts +13 -26
  42. package/src/feature/backColor.ts +1 -0
  43. package/src/feature/foreColor.ts +1 -0
  44. package/src/feature/heading.ts +2 -73
  45. package/src/feature/infoBlock.ts +4 -35
  46. package/src/feature/lineHeight.ts +1 -77
  47. package/src/feature/mathformula.ts +3 -50
  48. package/src/feature/orderList.ts +166 -35
  49. package/src/feature/panel.ts +4 -49
  50. package/src/feature/sub.ts +1 -1
  51. package/src/feature/super.ts +1 -1
  52. package/src/feature/task.ts +1 -55
  53. package/src/feature/unorderList.ts +106 -35
  54. package/src/feature/video.ts +1 -1
  55. package/src/icon/iconfont.css +40 -0
  56. package/src/icon/iconfont.ttf +0 -0
  57. package/src/icon/iconfont.woff +0 -0
  58. package/src/index.ts +14 -8
  59. package/src/locale/en_US.ts +112 -110
  60. package/src/locale/zh_CN.ts +11 -9
@@ -3,8 +3,8 @@
3
3
  */
4
4
  import { common as DapCommon } from 'dap-util'
5
5
  import { AlexEditor, AlexElement, AlexElementsRangeType, AlexElementCreateConfigType } from 'alex-editor'
6
- import { ButtonOptionsItemType } from '@/components/button'
7
- import { cloneData, queryHasValue, getButtonOptionsConfig, ObjectType } from './tool'
6
+ import KaTex from 'katex'
7
+ import { queryHasValue, ObjectType } from './tool'
8
8
 
9
9
  export type ElementMatchConfigType = {
10
10
  parsedom?: string
@@ -12,6 +12,257 @@ export type ElementMatchConfigType = {
12
12
  styles?: ObjectType
13
13
  }
14
14
 
15
+ /** --------------------------------代码块操作相关函数------------------------------------------------ */
16
+
17
+ /**
18
+ * 更新代码块内的光标位置
19
+ * @param editor
20
+ * @param element
21
+ * @param originalTextElements
22
+ * @param newElements
23
+ * @returns
24
+ */
25
+ export const updateRangeInPre = (editor: AlexEditor, element: AlexElement, originalTextElements: AlexElement[], newElements: AlexElement[]) => {
26
+ if (!editor.range) {
27
+ return
28
+ }
29
+ //如果虚拟光标的起点在代码块内对虚拟光标的起点进行重新定位
30
+ if (editor.range.anchor.element.getBlock().isEqual(element)) {
31
+ //获取起点所在文本元素的在所有文本元素中的序列
32
+ const elIndex = originalTextElements.findIndex(el => editor.range!.anchor.element.isEqual(el))
33
+ //起点在整个代码内容中的位置
34
+ const offset = originalTextElements.filter((_el, i) => i < elIndex).reduce((total, item) => total + item.textContent!.length, 0) + editor.range.anchor.offset
35
+ //获取pre下新的子孙元素中全部的文本元素
36
+ const newTextElements = AlexElement.flatElements(newElements).filter(el => el.isText() && !el.isEmpty())
37
+ let i = 0
38
+ let index = 0
39
+ //遍历
40
+ while (i < newTextElements.length) {
41
+ let newIndex = index + newTextElements[i].textContent!.length
42
+ if (offset >= index && offset <= newIndex) {
43
+ editor.range.anchor.element = newTextElements[i]
44
+ editor.range.anchor.offset = offset - index
45
+ break
46
+ }
47
+ i++
48
+ index = newIndex
49
+ }
50
+ }
51
+ //如果虚拟光标的终点在代码块内需要对虚拟光标的终点进行重新定位
52
+ if (editor.range.focus.element.getBlock().isEqual(element)) {
53
+ //获取终点所在文本元素的在所有文本元素中的序列
54
+ const elIndex = originalTextElements.findIndex(el => editor.range!.focus.element.isEqual(el))
55
+ //终点在整个代码内容中的位置
56
+ const offset = originalTextElements.filter((_el, i) => i < elIndex).reduce((total, item) => total + item.textContent!.length, 0) + editor.range.focus.offset
57
+ //获取全部的新文本元素
58
+ const newTextElements = AlexElement.flatElements(newElements).filter(el => el.isText() && !el.isEmpty())
59
+ let i = 0
60
+ let index = 0
61
+ //遍历
62
+ while (i < newTextElements.length) {
63
+ let newIndex = index + newTextElements[i].textContent!.length
64
+ if (offset >= index && offset <= newIndex) {
65
+ editor.range.focus.element = newTextElements[i]
66
+ editor.range.focus.offset = offset - index
67
+ break
68
+ }
69
+ i++
70
+ index = newIndex
71
+ }
72
+ }
73
+ }
74
+
75
+ /** --------------------------------表格操作相关函数--------------------------------------------- */
76
+
77
+ /**
78
+ * 自动隐藏被合并的单元格
79
+ * @param editor
80
+ * @param rowElements
81
+ */
82
+ export const autoHideMergedTableCells = (editor: AlexEditor, rowElements: AlexElement[]) => {
83
+ const cells = AlexElement.flatElements(rowElements).filter(item => item.parsedom == 'td')
84
+ cells.forEach(cell => {
85
+ if (cell.hasMarks() && !cell.marks!['data-editify-merged']) {
86
+ //获取colspan
87
+ const colspan = isNaN(Number(cell.marks!['colspan'])) ? 1 : Number(cell.marks!['colspan'])
88
+ //获取rowspan
89
+ const rowspan = isNaN(Number(cell.marks!['rowspan'])) ? 1 : Number(cell.marks!['rowspan'])
90
+ //如果是跨列单元格,隐藏该单元格同行后的colspan-1个单元格
91
+ if (colspan > 1) {
92
+ let el = cell
93
+ let i = 1
94
+ while (i < colspan) {
95
+ const nextCell = editor.getNextElement(el)!
96
+ if (nextCell) {
97
+ if (nextCell.hasMarks()) {
98
+ nextCell.marks!['data-editify-merged'] = 'true'
99
+ } else {
100
+ nextCell.marks = {
101
+ 'data-editify-merged': 'true'
102
+ }
103
+ }
104
+ el = nextCell
105
+ i++
106
+ } else {
107
+ break
108
+ }
109
+ }
110
+ }
111
+ //如果是跨行单元格,隐藏该单元格同列后的rowspan-1行单元格
112
+ if (rowspan > 1) {
113
+ const index = cell.parent!.children!.findIndex(item => item.isEqual(cell))
114
+ let el = cell
115
+ let i = 1
116
+ while (i < rowspan && el && editor.getNextElement(el.parent!)) {
117
+ const nextRow = editor.getNextElement(el.parent!)!
118
+ //根据跨行单元格占据的列数,在后的rowspan-1行中隐藏colspan个单元格
119
+ for (let j = index; j < index + colspan; j++) {
120
+ const current = nextRow.children![j]
121
+ if (current) {
122
+ if (current.hasMarks()) {
123
+ current.marks!['data-editify-merged'] = 'true'
124
+ } else {
125
+ current.marks = {
126
+ 'data-editify-merged': 'true'
127
+ }
128
+ }
129
+ }
130
+ }
131
+ el = nextRow.children![index]
132
+ i++
133
+ }
134
+ }
135
+ }
136
+ })
137
+ }
138
+
139
+ /**
140
+ * 自动补全表格行和列
141
+ * @param editor
142
+ * @param rowElements
143
+ * @param rowNumber
144
+ * @param columnNumber
145
+ */
146
+ export const autocompleteTableCells = (editor: AlexEditor, rowElements: AlexElement[], rowNumber: number, columnNumber: number) => {
147
+ //遍历所有的单元格
148
+ AlexElement.flatElements(rowElements).forEach(item => {
149
+ if (item.parsedom == 'td' && item.hasMarks()) {
150
+ //删除被合并的标识
151
+ if (item.marks!['data-editify-merged']) {
152
+ delete item.marks!['data-editify-merged']
153
+ }
154
+ //获取colspan
155
+ const colspan = isNaN(Number(item.marks!['colspan'])) ? 1 : Number(item.marks!['colspan'])
156
+ //获取rowspan
157
+ const rowspan = isNaN(Number(item.marks!['rowspan'])) ? 1 : Number(item.marks!['rowspan'])
158
+ //针对colspan>1的单元格在后面补全隐藏的单元格
159
+ if (colspan > 1) {
160
+ let i = 1
161
+ //补全的数量小于需要补全的数量并且列总数量小于理论数量
162
+ while (i < colspan && item.parent!.children!.length < columnNumber) {
163
+ const column = AlexElement.create({
164
+ type: 'inblock',
165
+ parsedom: 'td',
166
+ marks: {
167
+ 'data-editify-merged': 'true'
168
+ },
169
+ children: [
170
+ {
171
+ type: 'closed',
172
+ parsedom: 'br'
173
+ }
174
+ ]
175
+ })
176
+ editor.addElementAfter(column, item)
177
+ i++
178
+ }
179
+ }
180
+ //针对rowspan>1的单元格在后面的行中对应位置补全隐藏的单元格
181
+ if (rowspan > 1) {
182
+ let el = item
183
+ let i = 1
184
+ while (i < rowspan && editor.getNextElement(el.parent!) && editor.getNextElement(el.parent!)!.children!.length < columnNumber) {
185
+ //下一行
186
+ const nextRow = editor.getNextElement(el.parent!)!
187
+ //单元格在行中的序列
188
+ const index = el.parent!.children!.findIndex(item => item.isEqual(el))
189
+ //下一行对应的单元格
190
+ const nextCell = nextRow.children![index]
191
+ //根据当前单元格的跨列数补充符合跨列数的隐藏单元格
192
+ for (let j = 0; j < colspan; j++) {
193
+ const column = AlexElement.create({
194
+ type: 'inblock',
195
+ parsedom: 'td',
196
+ marks: {
197
+ 'data-editify-merged': 'true'
198
+ },
199
+ children: [
200
+ {
201
+ type: 'closed',
202
+ parsedom: 'br'
203
+ }
204
+ ]
205
+ })
206
+ if (nextCell) {
207
+ editor.addElementBefore(column, nextCell)
208
+ } else {
209
+ editor.addElementTo(column, nextRow, nextRow.children!.length)
210
+ }
211
+ }
212
+ el = nextRow.children![index]
213
+ i++
214
+ }
215
+ }
216
+ }
217
+ })
218
+ //遍历每一行,如果还缺少列则在后面补全列
219
+ rowElements.forEach(rowElement => {
220
+ //遍历该行的单元格获取总列数
221
+ const number = rowElement.children!.length
222
+ if (number < columnNumber) {
223
+ for (let i = 0; i < columnNumber - number; i++) {
224
+ const column = AlexElement.create({
225
+ type: 'inblock',
226
+ parsedom: 'td',
227
+ children: [
228
+ {
229
+ type: 'closed',
230
+ parsedom: 'br'
231
+ }
232
+ ]
233
+ })
234
+ editor.addElementTo(column, rowElement, rowElement.children!.length)
235
+ }
236
+ }
237
+ })
238
+ //获取总行数
239
+ const length = rowElements.length
240
+ //判断总行数是否小于实际行数则补全行
241
+ if (length < rowNumber) {
242
+ for (let i = 0; i < rowNumber - length; i++) {
243
+ const children: AlexElementCreateConfigType[] = []
244
+ for (let j = 0; j < columnNumber; j++) {
245
+ children.push({
246
+ type: 'inblock',
247
+ parsedom: 'td',
248
+ children: [
249
+ {
250
+ type: 'closed',
251
+ parsedom: 'br'
252
+ }
253
+ ]
254
+ })
255
+ }
256
+ const row = AlexElement.create({
257
+ type: 'inblock',
258
+ parsedom: 'tr',
259
+ children
260
+ })
261
+ rowElements.push(row)
262
+ }
263
+ }
264
+ }
265
+
15
266
  /**
16
267
  * 清空单元格的内容并隐藏
17
268
  * @param editor
@@ -184,6 +435,8 @@ export const getTableSize = (rowElements: AlexElement[]) => {
184
435
  }
185
436
  }
186
437
 
438
+ /** --------------------------------通用的元素判断函数----------------------------------------------- */
439
+
187
440
  /**
188
441
  * Open API:判断元素是否符合指定的条件
189
442
  * @param element
@@ -292,6 +545,8 @@ export const getMatchElementByRange = (editor: AlexEditor, dataRangeCaches: Alex
292
545
  return null
293
546
  }
294
547
 
548
+ /** --------------------------------列表判断函数---------------------------------------------------------- */
549
+
295
550
  /**
296
551
  * Open API:判断元素是否有序或者无序列表,不做空元素判断
297
552
  * @param element
@@ -360,6 +615,8 @@ export const rangeIsInList = (editor: AlexEditor, dataRangeCaches: AlexElementsR
360
615
  })
361
616
  }
362
617
 
618
+ /** --------------------------------任务列表判断函数----------------------------------------------- */
619
+
363
620
  /**
364
621
  * Open API:判断元素是否任务列表,不做空元素判断
365
622
  * @param element
@@ -424,6 +681,8 @@ export const rangeIsInTask = (editor: AlexEditor, dataRangeCaches: AlexElementsR
424
681
  })
425
682
  }
426
683
 
684
+ /** --------------------------------附件判断函数------------------------------------------------- */
685
+
427
686
  /**
428
687
  * Open API:判断元素是否附件,不做空元素判断
429
688
  * @param element
@@ -456,6 +715,8 @@ export const hasAttachmentInRange = (editor: AlexEditor, dataRangeCaches: AlexEl
456
715
  })
457
716
  }
458
717
 
718
+ /** --------------------------------数学公式判断函数--------------------------------------------- */
719
+
459
720
  /**
460
721
  * Open API:判断元素是否数学公式,不做空元素判断
461
722
  * @param element
@@ -502,6 +763,8 @@ export const hasMathformulaInRange = (editor: AlexEditor, dataRangeCaches: AlexE
502
763
  })
503
764
  }
504
765
 
766
+ /** --------------------------------面板判断函数--------------------------------------------------- */
767
+
505
768
  /**
506
769
  * Open API:判断元素是否面板,不做空元素判断
507
770
  * @param el
@@ -548,6 +811,8 @@ export const hasPanelInRange = (editor: AlexEditor, dataRangeCaches: AlexElement
548
811
  })
549
812
  }
550
813
 
814
+ /** --------------------------------信息块判断函数---------------------------------------------- */
815
+
551
816
  /**
552
817
  * Open API:判断元素是否信息块,不做空元素判断
553
818
  * @param el
@@ -612,6 +877,8 @@ export const rangeIsInInfoBlock = (editor: AlexEditor, dataRangeCaches: AlexElem
612
877
  })
613
878
  }
614
879
 
880
+ /** --------------------------------代码块判断函数------------------------------------------------ */
881
+
615
882
  /**
616
883
  * Open API:选区是否含有代码块,不一定是同一个代码块,只要含有代码块即返回true
617
884
  * @param editor
@@ -630,41 +897,7 @@ export const hasPreInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsR
630
897
  })
631
898
  }
632
899
 
633
- /**
634
- * Open API:选区是否含有引用,不一定是同一个引用,只要含有引用即返回true
635
- * @param editor
636
- * @param dataRangeCaches
637
- * @returns
638
- */
639
- export const hasQuoteInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
640
- if (!editor.range) {
641
- return false
642
- }
643
- if (editor.range.anchor.isEqual(editor.range.focus)) {
644
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
645
- }
646
- return dataRangeCaches.flatList.some(item => {
647
- return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
648
- })
649
- }
650
-
651
- /**
652
- * Open API:选区是否含有链接,不一定是同一个链接,只要含有链接即返回true
653
- * @param editor
654
- * @param dataRangeCaches
655
- * @returns
656
- */
657
- export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
658
- if (!editor.range) {
659
- return false
660
- }
661
- if (editor.range.anchor.isEqual(editor.range.focus)) {
662
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'a' })
663
- }
664
- return dataRangeCaches.flatList.some(item => {
665
- return !!getMatchElementByElement(item.element, { parsedom: 'a' })
666
- })
667
- }
900
+ /** --------------------------------表格判断函数------------------------------------------------- */
668
901
 
669
902
  /**
670
903
  * Open API:选区是否含有表格,不一定是同一个表格,只要含有表格即返回true
@@ -684,161 +917,103 @@ export const hasTableInRange = (editor: AlexEditor, dataRangeCaches: AlexElement
684
917
  })
685
918
  }
686
919
 
920
+ /** --------------------------------引用判断函数----------------------------------------------- */
921
+
687
922
  /**
688
- * Open API:选区是否含有图片,不一定是同一个图片,只要含有图片即返回true
923
+ * Open API:选区是否含有引用,不一定是同一个引用,只要含有引用即返回true
689
924
  * @param editor
690
925
  * @param dataRangeCaches
691
926
  * @returns
692
927
  */
693
- export const hasImageInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
928
+ export const hasQuoteInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
694
929
  if (!editor.range) {
695
930
  return false
696
931
  }
697
932
  if (editor.range.anchor.isEqual(editor.range.focus)) {
698
- return elementIsMatch(editor.range.anchor.element, { parsedom: 'img' })
933
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
699
934
  }
700
935
  return dataRangeCaches.flatList.some(item => {
701
- return elementIsMatch(item.element, { parsedom: 'img' })
936
+ return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
702
937
  })
703
938
  }
704
939
 
705
940
  /**
706
- * Open API:选区是否含有视频,不一定是同一个视频,只要含有视频即返回true
941
+ * Open API:选区是否全部在引用内,不一定是同一个引用
707
942
  * @param editor
708
943
  * @param dataRangeCaches
709
944
  * @returns
710
945
  */
711
- export const hasVideoInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
946
+ export const rangeIsInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
712
947
  if (!editor.range) {
713
948
  return false
714
949
  }
715
950
  if (editor.range.anchor.isEqual(editor.range.focus)) {
716
- return elementIsMatch(editor.range.anchor.element, { parsedom: 'video' })
951
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
717
952
  }
718
- return dataRangeCaches.flatList.some(item => {
719
- return elementIsMatch(item.element, { parsedom: 'video' })
953
+ return dataRangeCaches.list.every(item => {
954
+ return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
720
955
  })
721
956
  }
722
957
 
958
+ /** --------------------------------链接判断函数-------------------------------------------------- */
959
+
723
960
  /**
724
- * Open API:选区是否全部在引用内,不一定是同一个引用
961
+ * Open API:选区是否含有链接,不一定是同一个链接,只要含有链接即返回true
725
962
  * @param editor
726
963
  * @param dataRangeCaches
727
964
  * @returns
728
965
  */
729
- export const rangeIsInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
966
+ export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
730
967
  if (!editor.range) {
731
968
  return false
732
969
  }
733
970
  if (editor.range.anchor.isEqual(editor.range.focus)) {
734
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
971
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'a' })
735
972
  }
736
- return dataRangeCaches.list.every(item => {
737
- return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
973
+ return dataRangeCaches.flatList.some(item => {
974
+ return !!getMatchElementByElement(item.element, { parsedom: 'a' })
738
975
  })
739
976
  }
740
977
 
978
+ /** --------------------------------图片视频判断函数--------------------------------------------- */
979
+
741
980
  /**
742
- * Open API:查询光标所在的文本元素是否具有某个样式
981
+ * Open API:选区是否含有图片,不一定是同一个图片,只要含有图片即返回true
743
982
  * @param editor
744
983
  * @param dataRangeCaches
745
- * @param name
746
- * @param value
747
984
  * @returns
748
985
  */
749
- export const queryTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
986
+ export const hasImageInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
750
987
  if (!editor.range) {
751
988
  return false
752
989
  }
753
- //起点和终点在一起
754
990
  if (editor.range.anchor.isEqual(editor.range.focus)) {
755
- //如果是文本元素并且具有样式
756
- if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasStyles()) {
757
- return queryHasValue(editor.range.anchor.element.styles!, name, value)
758
- }
759
- //不是文本元素或者没有样式直接返回
760
- return false
761
- }
762
- //起点和终点不在一起获取选区中的文本元素
763
- let result = dataRangeCaches.flatList.filter(item => {
764
- return item.element.isText()
765
- })
766
- //如果不包含文本元素直接返回false
767
- if (result.length == 0) {
768
- return false
991
+ return elementIsMatch(editor.range.anchor.element, { parsedom: 'img' })
769
992
  }
770
- //判断每个文本元素是否都具有该样式
771
- let flag = result.every(item => {
772
- //文本元素含有样式进一步判断
773
- if (item.element.hasStyles()) {
774
- return queryHasValue(item.element.styles!, name, value)
775
- }
776
- //文本元素没有样式直接返回false
777
- return false
993
+ return dataRangeCaches.flatList.some(item => {
994
+ return elementIsMatch(item.element, { parsedom: 'img' })
778
995
  })
779
- return flag
780
996
  }
781
997
 
782
998
  /**
783
- * Open API:查询光标所在的文本元素是否具有某个标记
999
+ * Open API:选区是否含有视频,不一定是同一个视频,只要含有视频即返回true
784
1000
  * @param editor
785
1001
  * @param dataRangeCaches
786
- * @param name
787
- * @param value
788
1002
  * @returns
789
1003
  */
790
- export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
1004
+ export const hasVideoInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
791
1005
  if (!editor.range) {
792
1006
  return false
793
1007
  }
794
- //起点和终点在一起
795
1008
  if (editor.range.anchor.isEqual(editor.range.focus)) {
796
- //如果是文本元素并且具有标记
797
- if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
798
- return queryHasValue(editor.range.anchor.element.marks!, name, value)
799
- }
800
- //不是文本元素或者没有标记直接返回
801
- return false
802
- }
803
- //起点和终点不在一起获取选区中的文本元素
804
- let result = dataRangeCaches.flatList.filter(item => {
805
- return item.element.isText()
806
- })
807
- //如果不包含文本元素直接返回false
808
- if (result.length == 0) {
809
- return false
1009
+ return elementIsMatch(editor.range.anchor.element, { parsedom: 'video' })
810
1010
  }
811
- //判断每个文本元素是否都具有该标记
812
- let flag = result.every(item => {
813
- //文本元素含有标记进一步判断
814
- if (item.element.hasMarks()) {
815
- return queryHasValue(item.element.marks!, name, value)
816
- }
817
- //文本元素没有标记直接返回false
818
- return false
1011
+ return dataRangeCaches.flatList.some(item => {
1012
+ return elementIsMatch(item.element, { parsedom: 'video' })
819
1013
  })
820
- return flag
821
1014
  }
822
1015
 
823
- /**
824
- * Open API:获取选区内的文字内容
825
- * @param dataRangeCaches
826
- * @returns
827
- */
828
- export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
829
- //存在选区的情况下预置链接文本值
830
- let text = ''
831
- dataRangeCaches.flatList.forEach(item => {
832
- if (item.element.isText()) {
833
- if (item.offset) {
834
- text += item.element.textContent!.substring(item.offset[0], item.offset[1])
835
- } else {
836
- text += item.element.textContent || ''
837
- }
838
- }
839
- })
840
- return text
841
- }
1016
+ /** --------------------文本元素标记和样式相关函数--------------------------------------- */
842
1017
 
843
1018
  /**
844
1019
  * 获取光标选取内的扁平化元素数组(可能会分割文本元素导致stack变更,同时也会更新选取元素和光标位置)
@@ -907,32 +1082,355 @@ export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: Alex
907
1082
  }
908
1083
 
909
1084
  /**
910
- * 将某个元素转为段落标签
911
- * @param element
912
- */
913
- export const elementToParagraph = (element: AlexElement) => {
914
- element.marks = null
915
- element.styles = null
916
- element.parsedom = AlexElement.BLOCK_NODE
917
- }
918
-
919
- /**
920
- * 其他元素转为有序或者无序列表
921
- * @param element
922
- * @param ordered
1085
+ * Open API:查询光标所在的文本元素是否具有某个样式
1086
+ * @param editor
1087
+ * @param dataRangeCaches
1088
+ * @param name
1089
+ * @param value
923
1090
  * @returns
924
1091
  */
925
- export const elementToList = (element: AlexElement, ordered: boolean | undefined = false) => {
926
- //如果是列表则返回
927
- if (elementIsList(element, ordered)) {
928
- return
929
- }
930
- //先转为段落
931
- elementToParagraph(element)
932
- //然后转为列表
933
- element.parsedom = 'div'
934
- if (!element.hasMarks()) {
935
- element.marks = {}
1092
+ export const queryTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
1093
+ if (!editor.range) {
1094
+ return false
1095
+ }
1096
+ //起点和终点在一起
1097
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1098
+ //如果是文本元素并且具有样式
1099
+ if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasStyles()) {
1100
+ return queryHasValue(editor.range.anchor.element.styles!, name, value)
1101
+ }
1102
+ //不是文本元素或者没有样式直接返回
1103
+ return false
1104
+ }
1105
+ //起点和终点不在一起获取选区中的文本元素
1106
+ let result = dataRangeCaches.flatList.filter(item => {
1107
+ return item.element.isText()
1108
+ })
1109
+ //如果不包含文本元素直接返回false
1110
+ if (result.length == 0) {
1111
+ return false
1112
+ }
1113
+ //判断每个文本元素是否都具有该样式
1114
+ let flag = result.every(item => {
1115
+ //文本元素含有样式进一步判断
1116
+ if (item.element.hasStyles()) {
1117
+ return queryHasValue(item.element.styles!, name, value)
1118
+ }
1119
+ //文本元素没有样式直接返回false
1120
+ return false
1121
+ })
1122
+ return flag
1123
+ }
1124
+
1125
+ /**
1126
+ * Open API:设置文本元素的样式
1127
+ * @param editor
1128
+ * @param dataRangeCaches
1129
+ * @param styles 值为{ 'font-weight':'bold' }这类格式
1130
+ * @returns
1131
+ */
1132
+ export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
1133
+ if (!editor.range) {
1134
+ return
1135
+ }
1136
+ //起点和终点在一起
1137
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1138
+ //如果是空白文本元素直接设置样式
1139
+ if (editor.range.anchor.element.isSpaceText()) {
1140
+ if (editor.range.anchor.element.hasStyles()) {
1141
+ Object.assign(editor.range.anchor.element.styles!, DapCommon.clone(styles))
1142
+ } else {
1143
+ editor.range.anchor.element.styles = DapCommon.clone(styles)
1144
+ }
1145
+ }
1146
+ //如果是文本元素
1147
+ else if (editor.range.anchor.element.isText()) {
1148
+ //新建一个空白文本元素
1149
+ const el = AlexElement.getSpaceElement()
1150
+ //继承文本元素的样式和标记
1151
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1152
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1153
+ //设置样式
1154
+ if (el.hasStyles()) {
1155
+ Object.assign(el.styles!, DapCommon.clone(styles))
1156
+ } else {
1157
+ el.styles = DapCommon.clone(styles)
1158
+ }
1159
+ //插入空白文本元素
1160
+ editor.insertElement(el)
1161
+ }
1162
+ //如果是自闭合元素
1163
+ else {
1164
+ const el = AlexElement.getSpaceElement()
1165
+ el.styles = DapCommon.clone(styles)
1166
+ editor.insertElement(el)
1167
+ }
1168
+ }
1169
+ //不在同一个点
1170
+ else {
1171
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1172
+ elements.forEach(ele => {
1173
+ if (ele.isText()) {
1174
+ if (ele.hasStyles()) {
1175
+ Object.assign(ele.styles!, DapCommon.clone(styles))
1176
+ } else {
1177
+ ele.styles = DapCommon.clone(styles)
1178
+ }
1179
+ }
1180
+ })
1181
+ }
1182
+ }
1183
+
1184
+ /**
1185
+ * Open API:移除文本元素的样式
1186
+ * @param editor
1187
+ * @param dataRangeCaches
1188
+ * @param styleNames 样式名称数组,如果不存在则移除全部样式
1189
+ * @returns
1190
+ */
1191
+ export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
1192
+ if (!editor.range) {
1193
+ return
1194
+ }
1195
+ //移除样式的方法
1196
+ const removeFn = (el: AlexElement) => {
1197
+ //如果参数是数组,表示删除指定的样式
1198
+ if (Array.isArray(styleNames)) {
1199
+ if (el.hasStyles()) {
1200
+ let styles: ObjectType = {}
1201
+ Object.keys(el.styles!).forEach(key => {
1202
+ if (!styleNames.includes(key)) {
1203
+ styles[key] = el.styles![key]
1204
+ }
1205
+ })
1206
+ el.styles = styles
1207
+ }
1208
+ }
1209
+ //如果没有参数,则表示删除所有的样式
1210
+ else {
1211
+ el.styles = null
1212
+ }
1213
+ }
1214
+ //如果起点和终点在一起
1215
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1216
+ //如果是空白文本元素直接移除样式
1217
+ if (editor.range.anchor.element.isSpaceText()) {
1218
+ removeFn(editor.range.anchor.element)
1219
+ }
1220
+ //如果是文本元素则新建一个空白文本元素
1221
+ else if (editor.range.anchor.element.isText()) {
1222
+ const el = AlexElement.getSpaceElement()
1223
+ //继承文本元素的样式和标记
1224
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1225
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1226
+ //移除样式
1227
+ removeFn(el)
1228
+ //插入
1229
+ editor.insertElement(el)
1230
+ }
1231
+ }
1232
+ //起点和终点不在一起
1233
+ else {
1234
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1235
+ elements.forEach(ele => {
1236
+ if (ele.isText()) {
1237
+ removeFn(ele)
1238
+ }
1239
+ })
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * Open API:查询光标所在的文本元素是否具有某个标记
1245
+ * @param editor
1246
+ * @param dataRangeCaches
1247
+ * @param name
1248
+ * @param value
1249
+ * @returns
1250
+ */
1251
+ export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
1252
+ if (!editor.range) {
1253
+ return false
1254
+ }
1255
+ //起点和终点在一起
1256
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1257
+ //如果是文本元素并且具有标记
1258
+ if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
1259
+ return queryHasValue(editor.range.anchor.element.marks!, name, value)
1260
+ }
1261
+ //不是文本元素或者没有标记直接返回
1262
+ return false
1263
+ }
1264
+ //起点和终点不在一起获取选区中的文本元素
1265
+ let result = dataRangeCaches.flatList.filter(item => {
1266
+ return item.element.isText()
1267
+ })
1268
+ //如果不包含文本元素直接返回false
1269
+ if (result.length == 0) {
1270
+ return false
1271
+ }
1272
+ //判断每个文本元素是否都具有该标记
1273
+ let flag = result.every(item => {
1274
+ //文本元素含有标记进一步判断
1275
+ if (item.element.hasMarks()) {
1276
+ return queryHasValue(item.element.marks!, name, value)
1277
+ }
1278
+ //文本元素没有标记直接返回false
1279
+ return false
1280
+ })
1281
+ return flag
1282
+ }
1283
+
1284
+ /**
1285
+ * Open API:设置文本元素的标记
1286
+ * @param editor
1287
+ * @param dataRangeCaches
1288
+ * @param marks 值为{ 'class':'a' }这类格式
1289
+ * @returns
1290
+ */
1291
+ export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
1292
+ if (!editor.range) {
1293
+ return
1294
+ }
1295
+ if (!DapCommon.isObject(marks)) {
1296
+ throw new Error('The argument must be an object')
1297
+ }
1298
+ //起点和终点在一起
1299
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1300
+ //如果是空白文本元素直接设置标记
1301
+ if (editor.range.anchor.element.isSpaceText()) {
1302
+ if (editor.range.anchor.element.hasMarks()) {
1303
+ Object.assign(editor.range.anchor.element.marks!, DapCommon.clone(marks))
1304
+ } else {
1305
+ editor.range.anchor.element.marks = DapCommon.clone(marks)
1306
+ }
1307
+ }
1308
+ //如果是文本元素
1309
+ else if (editor.range.anchor.element.isText()) {
1310
+ //新建一个空白文本元素
1311
+ const el = AlexElement.getSpaceElement()
1312
+ //继承文本元素的样式和标记
1313
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1314
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1315
+ //设置标记
1316
+ if (el.hasMarks()) {
1317
+ Object.assign(el.marks!, DapCommon.clone(marks))
1318
+ } else {
1319
+ el.marks = DapCommon.clone(marks)
1320
+ }
1321
+ //插入空白文本元素
1322
+ editor.insertElement(el)
1323
+ }
1324
+ //如果是自闭合元素
1325
+ else {
1326
+ const el = AlexElement.getSpaceElement()
1327
+ el.marks = DapCommon.clone(marks)
1328
+ editor.insertElement(el)
1329
+ }
1330
+ }
1331
+ //不在同一个点
1332
+ else {
1333
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1334
+ elements.forEach(ele => {
1335
+ if (ele.isText()) {
1336
+ if (ele.hasMarks()) {
1337
+ Object.assign(ele.marks!, DapCommon.clone(marks))
1338
+ } else {
1339
+ ele.marks = DapCommon.clone(marks)
1340
+ }
1341
+ }
1342
+ })
1343
+ }
1344
+ }
1345
+
1346
+ /**
1347
+ * Open API:移除文本元素的标记
1348
+ * @param editor
1349
+ * @param dataRangeCaches
1350
+ * @param markNames 标记名称数组,如果不存在则移除全部标记
1351
+ * @returns
1352
+ */
1353
+ export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
1354
+ if (!editor.range) {
1355
+ return
1356
+ }
1357
+ //移除样式的方法
1358
+ const removeFn = (el: AlexElement) => {
1359
+ //如果参数是数组,表示删除指定的样式
1360
+ if (Array.isArray(markNames)) {
1361
+ if (el.hasMarks()) {
1362
+ let marks: ObjectType = {}
1363
+ Object.keys(el.marks!).forEach(key => {
1364
+ if (!markNames.includes(key)) {
1365
+ marks[key] = el.marks![key]
1366
+ }
1367
+ })
1368
+ el.marks = marks
1369
+ }
1370
+ }
1371
+ //如果没有参数,则表示删除所有的样式
1372
+ else {
1373
+ el.marks = null
1374
+ }
1375
+ }
1376
+ //如果起点和终点在一起
1377
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1378
+ //如果是空白文本元素直接移除标记
1379
+ if (editor.range.anchor.element.isSpaceText()) {
1380
+ removeFn(editor.range.anchor.element)
1381
+ }
1382
+ //如果是文本元素则新建一个空白文本元素
1383
+ else if (editor.range.anchor.element.isText()) {
1384
+ const el = AlexElement.getSpaceElement()
1385
+ //继承文本元素的样式和标记
1386
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1387
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1388
+ //移除标记
1389
+ removeFn(el)
1390
+ //插入
1391
+ editor.insertElement(el)
1392
+ }
1393
+ }
1394
+ //起点和终点不在一起
1395
+ else {
1396
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1397
+ elements.forEach(ele => {
1398
+ if (ele.isText()) {
1399
+ removeFn(ele)
1400
+ }
1401
+ })
1402
+ }
1403
+ }
1404
+
1405
+ /** --------------------------------元素转换函数-------------------------------------------------- */
1406
+
1407
+ /**
1408
+ * 将某个元素转为段落标签
1409
+ * @param element
1410
+ */
1411
+ export const elementToParagraph = (element: AlexElement) => {
1412
+ element.marks = null
1413
+ element.styles = null
1414
+ element.parsedom = AlexElement.BLOCK_NODE
1415
+ }
1416
+
1417
+ /**
1418
+ * 其他元素转为有序或者无序列表
1419
+ * @param element
1420
+ * @param ordered
1421
+ * @returns
1422
+ */
1423
+ export const elementToList = (element: AlexElement, ordered: boolean | undefined = false) => {
1424
+ //如果是列表则返回
1425
+ if (elementIsList(element, ordered)) {
1426
+ return
1427
+ }
1428
+ //先转为段落
1429
+ elementToParagraph(element)
1430
+ //然后转为列表
1431
+ element.parsedom = 'div'
1432
+ if (!element.hasMarks()) {
1433
+ element.marks = {}
936
1434
  }
937
1435
  element.marks!['data-editify-list'] = ordered ? 'ol' : 'ul'
938
1436
  }
@@ -957,22 +1455,70 @@ export const elementToTask = (element: AlexElement) => {
957
1455
  element.marks!['data-editify-task'] = 'uncheck'
958
1456
  }
959
1457
 
1458
+ /** --------------------------------封装的功能函数----------------------------------------------- */
1459
+
1460
+ /**
1461
+ * Open API:获取选区内的文字内容
1462
+ * @param dataRangeCaches
1463
+ * @returns
1464
+ */
1465
+ export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
1466
+ //存在选区的情况下预置链接文本值
1467
+ let text = ''
1468
+ dataRangeCaches.flatList.forEach(item => {
1469
+ if (item.element.isText()) {
1470
+ if (item.offset) {
1471
+ text += item.element.textContent!.substring(item.offset[0], item.offset[1])
1472
+ } else {
1473
+ text += item.element.textContent || ''
1474
+ }
1475
+ }
1476
+ })
1477
+ return text
1478
+ }
1479
+
1480
+ /**
1481
+ * Open API:给元素两侧加上空白文本元素
1482
+ * @param editor
1483
+ * @param element
1484
+ */
1485
+ export const addSpaceTextToBothSides = (editor: AlexEditor, element: AlexElement) => {
1486
+ const previousElement = editor.getPreviousElement(element)
1487
+ const newTextElement = editor.getNextElement(element)
1488
+ //如果不存在前一个元素或者前一个元素不是空白元素则设置空白元素
1489
+ if (!previousElement || !previousElement.isSpaceText()) {
1490
+ const spaceText = AlexElement.getSpaceElement()
1491
+ editor.addElementBefore(spaceText, element)
1492
+ }
1493
+ //如果不存在后一个元素或者后一个元素不是空白元素则设置空白元素
1494
+ if (!newTextElement || !newTextElement.isSpaceText()) {
1495
+ const spaceText = AlexElement.getSpaceElement()
1496
+ editor.addElementAfter(spaceText, element)
1497
+ }
1498
+ //如果光标在视频上则更新光标位置
1499
+ if (editor.range && element.isContains(editor.range.anchor.element)) {
1500
+ editor.range.anchor.moveToEnd(editor.getNextElement(element)!)
1501
+ }
1502
+ if (editor.range && element.isContains(editor.range.focus.element)) {
1503
+ editor.range.focus.moveToEnd(editor.getNextElement(element)!)
1504
+ }
1505
+ }
1506
+
1507
+ /** --------------------------------菜单功能函数----------------------------------------------------- */
1508
+
960
1509
  /**
961
- * 设置标题
1510
+ * OpenAPI:设置标题,支持h1-h6和p
962
1511
  * @param editor
963
1512
  * @param dataRangeCaches
964
1513
  * @param editTrans
965
1514
  * @param parsedom
966
1515
  * @returns
967
1516
  */
968
- export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, editTrans: (key: string) => any, parsedom: string) => {
1517
+ export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, parsedom: string) => {
969
1518
  if (!editor.range) {
970
1519
  return
971
1520
  }
972
- const values = getButtonOptionsConfig(editTrans).heading!.map(item => {
973
- return (<ButtonOptionsItemType>item).value
974
- })
975
- if (!values.includes(parsedom)) {
1521
+ if (!['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].includes(parsedom)) {
976
1522
  throw new Error('The parameter supports only h1-h6 and p')
977
1523
  }
978
1524
  if (editor.range.anchor.isEqual(editor.range.focus)) {
@@ -1152,244 +1698,40 @@ export const setAlign = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeT
1152
1698
  if (block.hasStyles()) {
1153
1699
  block.styles!['text-align'] = value
1154
1700
  } else {
1155
- block.styles = {
1156
- 'text-align': value
1157
- }
1158
- }
1159
- }
1160
- } else {
1161
- dataRangeCaches.list.forEach(el => {
1162
- if (el.element.isBlock() || el.element.isInblock()) {
1163
- if (el.element.hasStyles()) {
1164
- el.element.styles!['text-align'] = value
1165
- } else {
1166
- el.element.styles = {
1167
- 'text-align': value
1168
- }
1169
- }
1170
- } else {
1171
- const block = el.element.getBlock()
1172
- const inblock = el.element.getInblock()
1173
- if (inblock) {
1174
- if (inblock.hasStyles()) {
1175
- inblock.styles!['text-align'] = value
1176
- } else {
1177
- inblock.styles = {
1178
- 'text-align': value
1179
- }
1180
- }
1181
- } else {
1182
- if (block.hasStyles()) {
1183
- block.styles!['text-align'] = value
1184
- } else {
1185
- block.styles = {
1186
- 'text-align': value
1187
- }
1188
- }
1189
- }
1190
- }
1191
- })
1192
- }
1193
- }
1194
-
1195
- /**
1196
- * Open API:插入或者取消 有序或者无序列表
1197
- * @param editor
1198
- * @param dataRangeCaches
1199
- * @param ordered 为true表示有序列表
1200
- * @returns
1201
- */
1202
- export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
1203
- if (!editor.range) {
1204
- return
1205
- }
1206
- //是否都在列表内
1207
- const flag = rangeIsInList(editor, dataRangeCaches, ordered)
1208
- //起点和终点在一起
1209
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1210
- const block = editor.range.anchor.element.getBlock()
1211
- if (flag) {
1212
- elementToParagraph(block)
1213
- } else {
1214
- elementToList(block, ordered)
1215
- }
1216
- }
1217
- //起点和终点不在一起
1218
- else {
1219
- let blocks: AlexElement[] = []
1220
- dataRangeCaches.list.forEach(item => {
1221
- const block = item.element.getBlock()
1222
- const exist = blocks.some(el => block.isEqual(el))
1223
- if (!exist) {
1224
- blocks.push(block)
1225
- }
1226
- })
1227
- blocks.forEach(block => {
1228
- if (flag) {
1229
- elementToParagraph(block)
1230
- } else {
1231
- elementToList(block, ordered)
1232
- }
1233
- })
1234
- }
1235
- }
1236
-
1237
- /**
1238
- * Open API:插入或者取消任务列表
1239
- * @param editor
1240
- * @param dataRangeCaches
1241
- * @returns
1242
- */
1243
- export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1244
- if (!editor.range) {
1245
- return
1246
- }
1247
- //是否都在任务列表里
1248
- const flag = rangeIsInTask(editor, dataRangeCaches)
1249
- //起点和终点在一起
1250
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1251
- const block = editor.range.anchor.element.getBlock()
1252
- if (flag) {
1253
- elementToParagraph(block)
1254
- } else {
1255
- elementToTask(block)
1256
- }
1257
- }
1258
- //起点和终点不在一起
1259
- else {
1260
- let blocks: AlexElement[] = []
1261
- dataRangeCaches.list.forEach(item => {
1262
- const block = item.element.getBlock()
1263
- const exist = blocks.some(el => block.isEqual(el))
1264
- if (!exist) {
1265
- blocks.push(block)
1266
- }
1267
- })
1268
- blocks.forEach(block => {
1269
- if (flag) {
1270
- elementToParagraph(block)
1271
- } else {
1272
- elementToTask(block)
1273
- }
1274
- })
1275
- }
1276
- }
1277
-
1278
- /**
1279
- * Open API:设置文本元素的样式
1280
- * @param editor
1281
- * @param dataRangeCaches
1282
- * @param styles 值为{ 'font-weight':'bold' }这类格式
1283
- * @returns
1284
- */
1285
- export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
1286
- if (!editor.range) {
1287
- return
1288
- }
1289
- //起点和终点在一起
1290
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1291
- //如果是空白文本元素直接设置样式
1292
- if (editor.range.anchor.element.isSpaceText()) {
1293
- if (editor.range.anchor.element.hasStyles()) {
1294
- Object.assign(editor.range.anchor.element.styles!, cloneData(styles))
1295
- } else {
1296
- editor.range.anchor.element.styles = cloneData(styles)
1297
- }
1298
- }
1299
- //如果是文本元素
1300
- else if (editor.range.anchor.element.isText()) {
1301
- //新建一个空白文本元素
1302
- const el = AlexElement.getSpaceElement()
1303
- //继承文本元素的样式和标记
1304
- el.styles = cloneData(editor.range.anchor.element.styles)
1305
- el.marks = cloneData(editor.range.anchor.element.marks)
1306
- //设置样式
1307
- if (el.hasStyles()) {
1308
- Object.assign(el.styles!, cloneData(styles))
1309
- } else {
1310
- el.styles = cloneData(styles)
1311
- }
1312
- //插入空白文本元素
1313
- editor.insertElement(el)
1314
- }
1315
- //如果是自闭合元素
1316
- else {
1317
- const el = AlexElement.getSpaceElement()
1318
- el.styles = cloneData(styles)
1319
- editor.insertElement(el)
1320
- }
1321
- }
1322
- //不在同一个点
1323
- else {
1324
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1325
- elements.forEach(ele => {
1326
- if (ele.isText()) {
1327
- if (ele.hasStyles()) {
1328
- Object.assign(ele.styles!, cloneData(styles))
1329
- } else {
1330
- ele.styles = cloneData(styles)
1331
- }
1332
- }
1333
- })
1334
- }
1335
- }
1336
-
1337
- /**
1338
- * Open API:设置文本元素的标记
1339
- * @param editor
1340
- * @param dataRangeCaches
1341
- * @param marks 值为{ 'class':'a' }这类格式
1342
- * @returns
1343
- */
1344
- export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
1345
- if (!editor.range) {
1346
- return
1347
- }
1348
- if (!DapCommon.isObject(marks)) {
1349
- throw new Error('The argument must be an object')
1350
- }
1351
- //起点和终点在一起
1352
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1353
- //如果是空白文本元素直接设置标记
1354
- if (editor.range.anchor.element.isSpaceText()) {
1355
- if (editor.range.anchor.element.hasMarks()) {
1356
- Object.assign(editor.range.anchor.element.marks!, cloneData(marks))
1357
- } else {
1358
- editor.range.anchor.element.marks = cloneData(marks)
1359
- }
1360
- }
1361
- //如果是文本元素
1362
- else if (editor.range.anchor.element.isText()) {
1363
- //新建一个空白文本元素
1364
- const el = AlexElement.getSpaceElement()
1365
- //继承文本元素的样式和标记
1366
- el.styles = cloneData(editor.range.anchor.element.styles)
1367
- el.marks = cloneData(editor.range.anchor.element.marks)
1368
- //设置标记
1369
- if (el.hasMarks()) {
1370
- Object.assign(el.marks!, cloneData(marks))
1371
- } else {
1372
- el.marks = cloneData(marks)
1373
- }
1374
- //插入空白文本元素
1375
- editor.insertElement(el)
1376
- }
1377
- //如果是自闭合元素
1378
- else {
1379
- const el = AlexElement.getSpaceElement()
1380
- el.marks = cloneData(marks)
1381
- editor.insertElement(el)
1701
+ block.styles = {
1702
+ 'text-align': value
1703
+ }
1704
+ }
1382
1705
  }
1383
- }
1384
- //不在同一个点
1385
- else {
1386
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1387
- elements.forEach(ele => {
1388
- if (ele.isText()) {
1389
- if (ele.hasMarks()) {
1390
- Object.assign(ele.marks!, cloneData(marks))
1706
+ } else {
1707
+ dataRangeCaches.list.forEach(el => {
1708
+ if (el.element.isBlock() || el.element.isInblock()) {
1709
+ if (el.element.hasStyles()) {
1710
+ el.element.styles!['text-align'] = value
1711
+ } else {
1712
+ el.element.styles = {
1713
+ 'text-align': value
1714
+ }
1715
+ }
1716
+ } else {
1717
+ const block = el.element.getBlock()
1718
+ const inblock = el.element.getInblock()
1719
+ if (inblock) {
1720
+ if (inblock.hasStyles()) {
1721
+ inblock.styles!['text-align'] = value
1722
+ } else {
1723
+ inblock.styles = {
1724
+ 'text-align': value
1725
+ }
1726
+ }
1391
1727
  } else {
1392
- ele.marks = cloneData(marks)
1728
+ if (block.hasStyles()) {
1729
+ block.styles!['text-align'] = value
1730
+ } else {
1731
+ block.styles = {
1732
+ 'text-align': value
1733
+ }
1734
+ }
1393
1735
  }
1394
1736
  }
1395
1737
  })
@@ -1397,125 +1739,90 @@ export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRan
1397
1739
  }
1398
1740
 
1399
1741
  /**
1400
- * Open API:移除文本元素的样式
1742
+ * Open API:插入或者取消 有序或者无序列表
1401
1743
  * @param editor
1402
1744
  * @param dataRangeCaches
1403
- * @param styleNames 样式名称数组,如果不存在则移除全部样式
1745
+ * @param ordered 为true表示有序列表
1404
1746
  * @returns
1405
1747
  */
1406
- export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
1748
+ export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
1407
1749
  if (!editor.range) {
1408
1750
  return
1409
1751
  }
1410
- //移除样式的方法
1411
- const removeFn = (el: AlexElement) => {
1412
- //如果参数是数组,表示删除指定的样式
1413
- if (Array.isArray(styleNames)) {
1414
- if (el.hasStyles()) {
1415
- let styles: ObjectType = {}
1416
- Object.keys(el.styles!).forEach(key => {
1417
- if (!styleNames.includes(key)) {
1418
- styles[key] = el.styles![key]
1419
- }
1420
- })
1421
- el.styles = styles
1422
- }
1423
- }
1424
- //如果没有参数,则表示删除所有的样式
1425
- else {
1426
- el.styles = null
1427
- }
1428
- }
1429
- //如果起点和终点在一起
1752
+ //是否都在列表内
1753
+ const flag = rangeIsInList(editor, dataRangeCaches, ordered)
1754
+ //起点和终点在一起
1430
1755
  if (editor.range.anchor.isEqual(editor.range.focus)) {
1431
- //如果是空白文本元素直接移除样式
1432
- if (editor.range.anchor.element.isSpaceText()) {
1433
- removeFn(editor.range.anchor.element)
1434
- }
1435
- //如果是文本元素则新建一个空白文本元素
1436
- else if (editor.range.anchor.element.isText()) {
1437
- const el = AlexElement.getSpaceElement()
1438
- //继承文本元素的样式和标记
1439
- el.styles = cloneData(editor.range.anchor.element.styles)
1440
- el.marks = cloneData(editor.range.anchor.element.marks)
1441
- //移除样式
1442
- removeFn(el)
1443
- //插入
1444
- editor.insertElement(el)
1756
+ const block = editor.range.anchor.element.getBlock()
1757
+ if (flag) {
1758
+ elementToParagraph(block)
1759
+ } else {
1760
+ elementToList(block, ordered)
1445
1761
  }
1446
1762
  }
1447
1763
  //起点和终点不在一起
1448
1764
  else {
1449
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1450
- elements.forEach(ele => {
1451
- if (ele.isText()) {
1452
- removeFn(ele)
1765
+ let blocks: AlexElement[] = []
1766
+ dataRangeCaches.list.forEach(item => {
1767
+ const block = item.element.getBlock()
1768
+ const exist = blocks.some(el => block.isEqual(el))
1769
+ if (!exist) {
1770
+ blocks.push(block)
1771
+ }
1772
+ })
1773
+ blocks.forEach(block => {
1774
+ if (flag) {
1775
+ elementToParagraph(block)
1776
+ } else {
1777
+ elementToList(block, ordered)
1453
1778
  }
1454
1779
  })
1455
1780
  }
1456
1781
  }
1457
1782
 
1458
1783
  /**
1459
- * Open API:移除文本元素的标记
1784
+ * Open API:插入或者取消任务列表
1460
1785
  * @param editor
1461
1786
  * @param dataRangeCaches
1462
- * @param markNames 标记名称数组,如果不存在则移除全部标记
1463
1787
  * @returns
1464
1788
  */
1465
- export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
1789
+ export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1466
1790
  if (!editor.range) {
1467
1791
  return
1468
1792
  }
1469
- //移除样式的方法
1470
- const removeFn = (el: AlexElement) => {
1471
- //如果参数是数组,表示删除指定的样式
1472
- if (Array.isArray(markNames)) {
1473
- if (el.hasMarks()) {
1474
- let marks: ObjectType = {}
1475
- Object.keys(el.marks!).forEach(key => {
1476
- if (!markNames.includes(key)) {
1477
- marks[key] = el.marks![key]
1478
- }
1479
- })
1480
- el.marks = marks
1481
- }
1482
- }
1483
- //如果没有参数,则表示删除所有的样式
1484
- else {
1485
- el.marks = null
1486
- }
1487
- }
1488
- //如果起点和终点在一起
1793
+ //是否都在任务列表里
1794
+ const flag = rangeIsInTask(editor, dataRangeCaches)
1795
+ //起点和终点在一起
1489
1796
  if (editor.range.anchor.isEqual(editor.range.focus)) {
1490
- //如果是空白文本元素直接移除标记
1491
- if (editor.range.anchor.element.isSpaceText()) {
1492
- removeFn(editor.range.anchor.element)
1493
- }
1494
- //如果是文本元素则新建一个空白文本元素
1495
- else if (editor.range.anchor.element.isText()) {
1496
- const el = AlexElement.getSpaceElement()
1497
- //继承文本元素的样式和标记
1498
- el.styles = cloneData(editor.range.anchor.element.styles)
1499
- el.marks = cloneData(editor.range.anchor.element.marks)
1500
- //移除标记
1501
- removeFn(el)
1502
- //插入
1503
- editor.insertElement(el)
1797
+ const block = editor.range.anchor.element.getBlock()
1798
+ if (flag) {
1799
+ elementToParagraph(block)
1800
+ } else {
1801
+ elementToTask(block)
1504
1802
  }
1505
1803
  }
1506
1804
  //起点和终点不在一起
1507
1805
  else {
1508
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1509
- elements.forEach(ele => {
1510
- if (ele.isText()) {
1511
- removeFn(ele)
1806
+ let blocks: AlexElement[] = []
1807
+ dataRangeCaches.list.forEach(item => {
1808
+ const block = item.element.getBlock()
1809
+ const exist = blocks.some(el => block.isEqual(el))
1810
+ if (!exist) {
1811
+ blocks.push(block)
1812
+ }
1813
+ })
1814
+ blocks.forEach(block => {
1815
+ if (flag) {
1816
+ elementToParagraph(block)
1817
+ } else {
1818
+ elementToTask(block)
1512
1819
  }
1513
1820
  })
1514
1821
  }
1515
1822
  }
1516
1823
 
1517
1824
  /**
1518
- * Open API:设置块元素或者根级块元素的行高
1825
+ * Open API:设置内部块元素或者根级块元素的行高
1519
1826
  * @param editor
1520
1827
  * @param dataRangeCaches
1521
1828
  * @param value
@@ -1844,3 +2151,182 @@ export const insertSeparator = (editor: AlexEditor) => {
1844
2151
  editor.range.anchor.moveToEnd(separator)
1845
2152
  editor.range.focus.moveToEnd(separator)
1846
2153
  }
2154
+
2155
+ /**
2156
+ * Open API:插入附件
2157
+ * @param editor
2158
+ * @param url 附件地址
2159
+ * @param name 附件名称
2160
+ */
2161
+ export const insertAttachment = (editor: AlexEditor, url: string, name: string) => {
2162
+ const marks: ObjectType = {
2163
+ 'data-editify-attachment': url,
2164
+ 'data-editify-attachment-name': name
2165
+ }
2166
+ //创建元素
2167
+ const attachmentElement = AlexElement.create({
2168
+ type: 'closed',
2169
+ parsedom: 'span',
2170
+ marks
2171
+ })
2172
+ //插入编辑器
2173
+ editor.insertElement(attachmentElement)
2174
+ //移动光标到新插入的元素
2175
+ editor.range!.anchor.moveToEnd(attachmentElement)
2176
+ editor.range!.focus.moveToEnd(attachmentElement)
2177
+ }
2178
+
2179
+ /**
2180
+ * Open API:插入数学公式
2181
+ * @param editor
2182
+ * @param dataRangeCaches
2183
+ * @param mathContent 数学公式字符串
2184
+ * @param errorCallback 错误处理
2185
+ */
2186
+ export const insertMathformula = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, mathContent: string, errorCallback?: (err: Error) => void) => {
2187
+ if (!mathContent) {
2188
+ return
2189
+ }
2190
+ //获取选区所在的数学公式元素
2191
+ const mathformulaElement = getMatchElementByRange(editor, dataRangeCaches, {
2192
+ parsedom: 'span',
2193
+ marks: {
2194
+ 'data-editify-mathformula': true
2195
+ }
2196
+ })
2197
+ //如果在数学公式下
2198
+ if (mathformulaElement) {
2199
+ //清除该数学公式
2200
+ mathformulaElement.toEmpty()
2201
+ //移动光标到后一个元素上
2202
+ const nextElement = editor.getNextElement(mathformulaElement)!
2203
+ editor.range!.anchor.moveToStart(nextElement)
2204
+ editor.range!.focus.moveToStart(nextElement)
2205
+ }
2206
+ //定义转换后的mathml内容
2207
+ let mathml: string = ''
2208
+ try {
2209
+ //获取转换后的mathml
2210
+ mathml = KaTex.renderToString(mathContent, {
2211
+ output: 'mathml',
2212
+ throwOnError: true
2213
+ })
2214
+ } catch (error) {
2215
+ mathml = ''
2216
+ if (typeof errorCallback == 'function') {
2217
+ errorCallback(error as Error)
2218
+ }
2219
+ }
2220
+ //如果mathml存在则表示数学公式渲染成功则插入到编辑器
2221
+ if (mathml) {
2222
+ //设置最终的html内容
2223
+ const html = `<span data-editify-mathformula="${mathContent}" contenteditable="false">${mathml}</span>`
2224
+ //html内容转为元素数组
2225
+ const elements = editor.parseHtml(html)
2226
+ //插入编辑器
2227
+ editor.insertElement(elements[0])
2228
+ //移动光标到新插入的元素
2229
+ editor.range!.anchor.moveToEnd(elements[0])
2230
+ editor.range!.focus.moveToEnd(elements[0])
2231
+ //渲染
2232
+ editor.domRender()
2233
+ editor.rangeRender()
2234
+ }
2235
+ }
2236
+
2237
+ /**
2238
+ * Open API:插入信息块
2239
+ * @param editor
2240
+ * @param dataRangeCaches
2241
+ */
2242
+ export const insertInfoBlock = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
2243
+ //是否都在信息块里
2244
+ const flag = rangeIsInInfoBlock(editor, dataRangeCaches)
2245
+ //起点和终点在一起
2246
+ if (editor.range!.anchor.isEqual(editor.range!.focus)) {
2247
+ const block = editor.range!.anchor.element.getBlock()
2248
+ elementToParagraph(block)
2249
+ if (!flag) {
2250
+ block.parsedom = 'div'
2251
+ block.marks = {
2252
+ 'data-editify-info': 'true'
2253
+ }
2254
+ }
2255
+ }
2256
+ //起点和终点不在一起
2257
+ else {
2258
+ let blocks: AlexElement[] = []
2259
+ dataRangeCaches.list.forEach(item => {
2260
+ const block = item.element.getBlock()
2261
+ const exist = blocks.some(el => block.isEqual(el))
2262
+ if (!exist) {
2263
+ blocks.push(block)
2264
+ }
2265
+ })
2266
+ blocks.forEach(block => {
2267
+ elementToParagraph(block)
2268
+ if (!flag) {
2269
+ block.parsedom = 'div'
2270
+ block.marks = {
2271
+ 'data-editify-info': 'true'
2272
+ }
2273
+ }
2274
+ })
2275
+ }
2276
+ }
2277
+
2278
+ /**
2279
+ * Open API:插入面板
2280
+ * @param editor
2281
+ * @param panelTitle 面板标题
2282
+ * @param panelContent 面板内容
2283
+ */
2284
+ export const insertPanel = (editor: AlexEditor, panelTitle: string, panelContent: string) => {
2285
+ const panelElement = AlexElement.create({
2286
+ type: 'block',
2287
+ parsedom: 'div',
2288
+ marks: {
2289
+ 'data-editify-panel': 'true'
2290
+ },
2291
+ children: [
2292
+ {
2293
+ type: 'inblock',
2294
+ parsedom: 'div',
2295
+ behavior: 'block',
2296
+ children: [
2297
+ {
2298
+ type: 'text',
2299
+ textcontent: panelTitle
2300
+ }
2301
+ ]
2302
+ },
2303
+ {
2304
+ type: 'inblock',
2305
+ parsedom: 'div',
2306
+ behavior: 'block',
2307
+ children: [
2308
+ {
2309
+ type: 'text',
2310
+ textcontent: panelContent
2311
+ }
2312
+ ]
2313
+ }
2314
+ ]
2315
+ })
2316
+ editor.insertElement(panelElement)
2317
+ //面板后面插入段落
2318
+ const paragraph = AlexElement.create({
2319
+ type: 'block',
2320
+ parsedom: AlexElement.BLOCK_NODE,
2321
+ children: [
2322
+ {
2323
+ type: 'closed',
2324
+ parsedom: 'br'
2325
+ }
2326
+ ]
2327
+ })
2328
+ editor.addElementAfter(paragraph, panelElement)
2329
+ //移动光标到新插入的元素
2330
+ editor.range!.anchor.moveToEnd(panelElement.children![0])
2331
+ editor.range!.focus.moveToEnd(panelElement.children![0])
2332
+ }