vue-editify 0.0.51 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +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
+ }