vue-editify 0.1.3 → 0.1.5

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