suneditor 2.46.2 → 3.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +7 -0
- package/.eslintrc.json +64 -0
- package/CONTRIBUTING.md +36 -0
- package/LICENSE.txt +1 -1
- package/README.md +11 -1560
- package/package.json +94 -70
- package/src/assets/icons/_default.js +194 -0
- package/src/assets/suneditor-content.css +642 -0
- package/src/assets/suneditor.css +3378 -0
- package/src/core/base/eventHandlers/handler_toolbar.js +114 -0
- package/src/core/base/eventHandlers/handler_ww_clipboard.js +31 -0
- package/src/core/base/eventHandlers/handler_ww_dragDrop.js +69 -0
- package/src/core/base/eventHandlers/handler_ww_key_input.js +978 -0
- package/src/core/base/eventHandlers/handler_ww_mouse.js +118 -0
- package/src/core/base/eventManager.js +1129 -0
- package/src/core/base/events.js +320 -0
- package/src/core/base/history.js +301 -0
- package/src/core/class/char.js +146 -0
- package/src/core/class/component.js +624 -0
- package/src/core/class/format.js +3255 -0
- package/src/core/class/html.js +1621 -0
- package/src/core/class/menu.js +260 -0
- package/src/core/class/nodeTransform.js +379 -0
- package/src/core/class/notice.js +42 -0
- package/src/core/class/offset.js +578 -0
- package/src/core/class/selection.js +508 -0
- package/src/core/class/shortcuts.js +38 -0
- package/src/core/class/toolbar.js +440 -0
- package/src/core/class/viewer.js +646 -0
- package/src/core/editor.js +1588 -0
- package/src/core/section/actives.js +107 -0
- package/src/core/section/constructor.js +1237 -0
- package/src/core/section/context.js +97 -0
- package/src/editorInjector/_classes.js +22 -0
- package/src/editorInjector/_core.js +28 -0
- package/src/editorInjector/index.js +13 -0
- package/src/helper/converter.js +313 -0
- package/src/helper/domUtils.js +1177 -0
- package/src/helper/env.js +250 -0
- package/src/helper/index.js +19 -0
- package/src/helper/numbers.js +68 -0
- package/src/helper/unicode.js +43 -0
- package/src/langs/ckb.js +161 -0
- package/src/langs/cs.js +161 -0
- package/src/langs/da.js +161 -0
- package/src/langs/de.js +162 -0
- package/src/langs/en.js +199 -0
- package/src/langs/es.js +162 -0
- package/src/langs/fa.js +159 -0
- package/src/langs/fr.js +161 -0
- package/src/langs/he.js +162 -0
- package/src/{lang → langs}/index.js +0 -2
- package/src/langs/it.js +162 -0
- package/src/langs/ja.js +162 -0
- package/src/langs/ko.js +162 -0
- package/src/langs/lv.js +162 -0
- package/src/langs/nl.js +162 -0
- package/src/langs/pl.js +162 -0
- package/src/langs/pt_br.js +162 -0
- package/src/langs/ro.js +162 -0
- package/src/langs/ru.js +162 -0
- package/src/langs/se.js +162 -0
- package/src/langs/tr.js +159 -0
- package/src/langs/ua.js +162 -0
- package/src/langs/ur.js +162 -0
- package/src/langs/zh_cn.js +162 -0
- package/src/modules/ApiManager.js +168 -0
- package/src/modules/ColorPicker.js +302 -0
- package/src/modules/Controller.js +315 -0
- package/src/modules/Figure.js +1174 -0
- package/src/modules/FileBrowser.js +271 -0
- package/src/modules/FileManager.js +290 -0
- package/src/modules/HueSlider.js +513 -0
- package/src/modules/Modal.js +177 -0
- package/src/modules/ModalAnchorEditor.js +494 -0
- package/src/modules/SelectMenu.js +447 -0
- package/src/modules/_DragHandle.js +16 -0
- package/src/modules/index.js +14 -0
- package/src/plugins/command/blockquote.js +47 -47
- package/src/plugins/command/exportPdf.js +168 -0
- package/src/plugins/command/fileUpload.js +389 -0
- package/src/plugins/command/list_bulleted.js +112 -0
- package/src/plugins/command/list_numbered.js +115 -0
- package/src/plugins/dropdown/align.js +143 -0
- package/src/plugins/dropdown/backgroundColor.js +73 -0
- package/src/plugins/dropdown/font.js +113 -0
- package/src/plugins/dropdown/fontColor.js +73 -0
- package/src/plugins/dropdown/formatBlock.js +141 -0
- package/src/plugins/dropdown/hr.js +111 -0
- package/src/plugins/dropdown/layout.js +72 -0
- package/src/plugins/dropdown/lineHeight.js +114 -0
- package/src/plugins/dropdown/list.js +107 -0
- package/src/plugins/dropdown/paragraphStyle.js +117 -0
- package/src/plugins/dropdown/table.js +2810 -0
- package/src/plugins/dropdown/template.js +71 -0
- package/src/plugins/dropdown/textStyle.js +137 -0
- package/src/plugins/field/mention.js +172 -0
- package/src/plugins/fileBrowser/imageGallery.js +76 -59
- package/src/plugins/index.js +86 -24
- package/src/plugins/input/fontSize.js +357 -0
- package/src/plugins/modal/audio.js +492 -0
- package/src/plugins/modal/image.js +1062 -0
- package/src/plugins/modal/link.js +211 -0
- package/src/plugins/modal/math.js +347 -0
- package/src/plugins/modal/video.js +870 -0
- package/src/suneditor.js +62 -67
- package/src/themes/test.css +61 -0
- package/typings/CommandPlugin.d.ts +8 -0
- package/typings/DialogPlugin.d.ts +20 -0
- package/typings/FileBrowserPlugin.d.ts +30 -0
- package/typings/Lang.d.ts +124 -0
- package/typings/Module.d.ts +15 -0
- package/typings/Plugin.d.ts +42 -0
- package/typings/SubmenuPlugin.d.ts +8 -0
- package/typings/_classes.d.ts +17 -0
- package/typings/_colorPicker.d.ts +60 -0
- package/typings/_core.d.ts +55 -0
- package/typings/align.d.ts +5 -0
- package/{src/plugins/dialog → typings}/audio.d.ts +1 -1
- package/typings/backgroundColor.d.ts +5 -0
- package/{src/plugins/command → typings}/blockquote.d.ts +1 -1
- package/typings/char.d.ts +39 -0
- package/typings/component.d.ts +38 -0
- package/typings/context.d.ts +39 -0
- package/typings/converter.d.ts +33 -0
- package/typings/dialog.d.ts +28 -0
- package/typings/domUtils.d.ts +361 -0
- package/typings/editor.d.ts +7 -0
- package/typings/editor.ts +542 -0
- package/typings/env.d.ts +70 -0
- package/typings/eventManager.d.ts +37 -0
- package/typings/events.d.ts +262 -0
- package/typings/fileBrowser.d.ts +42 -0
- package/typings/fileManager.d.ts +67 -0
- package/typings/font.d.ts +5 -0
- package/typings/fontColor.d.ts +5 -0
- package/typings/fontSize.d.ts +5 -0
- package/typings/format.d.ts +191 -0
- package/typings/formatBlock.d.ts +5 -0
- package/typings/history.d.ts +48 -0
- package/typings/horizontalRule.d.ts +5 -0
- package/{src/plugins/dialog → typings}/image.d.ts +1 -1
- package/{src/plugins/fileBrowser → typings}/imageGallery.d.ts +1 -1
- package/typings/index.d.ts +21 -0
- package/{src/plugins/modules/index.d.ts → typings/index.modules.d.ts} +3 -3
- package/typings/index.plugins.d.ts +58 -0
- package/typings/lineHeight.d.ts +5 -0
- package/{src/plugins/dialog → typings}/link.d.ts +1 -1
- package/typings/list.d.ts +5 -0
- package/{src/plugins/dialog → typings}/math.d.ts +1 -1
- package/typings/mediaContainer.d.ts +25 -0
- package/typings/node.d.ts +57 -0
- package/typings/notice.d.ts +16 -0
- package/typings/numbers.d.ts +29 -0
- package/typings/offset.d.ts +24 -0
- package/typings/options.d.ts +589 -0
- package/typings/paragraphStyle.d.ts +5 -0
- package/typings/resizing.d.ts +141 -0
- package/typings/selection.d.ts +94 -0
- package/typings/shortcuts.d.ts +13 -0
- package/typings/suneditor.d.ts +9 -0
- package/typings/table.d.ts +5 -0
- package/typings/template.d.ts +5 -0
- package/typings/textStyle.d.ts +5 -0
- package/typings/toolbar.d.ts +32 -0
- package/typings/unicode.d.ts +25 -0
- package/{src/plugins/dialog → typings}/video.d.ts +1 -1
- package/dist/css/suneditor.min.css +0 -1
- package/dist/suneditor.min.js +0 -2
- package/src/assets/css/suneditor-contents.css +0 -562
- package/src/assets/css/suneditor.css +0 -566
- package/src/assets/defaultIcons.js +0 -103
- package/src/lang/Lang.d.ts +0 -144
- package/src/lang/ckb.d.ts +0 -5
- package/src/lang/ckb.js +0 -188
- package/src/lang/cs.d.ts +0 -5
- package/src/lang/cs.js +0 -188
- package/src/lang/da.d.ts +0 -5
- package/src/lang/da.js +0 -191
- package/src/lang/de.d.ts +0 -5
- package/src/lang/de.js +0 -188
- package/src/lang/en.d.ts +0 -5
- package/src/lang/en.js +0 -188
- package/src/lang/es.d.ts +0 -5
- package/src/lang/es.js +0 -188
- package/src/lang/fa.d.ts +0 -5
- package/src/lang/fa.js +0 -188
- package/src/lang/fr.d.ts +0 -5
- package/src/lang/fr.js +0 -188
- package/src/lang/he.d.ts +0 -5
- package/src/lang/he.js +0 -188
- package/src/lang/index.d.ts +0 -23
- package/src/lang/it.d.ts +0 -5
- package/src/lang/it.js +0 -188
- package/src/lang/ja.d.ts +0 -5
- package/src/lang/ja.js +0 -188
- package/src/lang/ko.d.ts +0 -5
- package/src/lang/ko.js +0 -188
- package/src/lang/lv.d.ts +0 -5
- package/src/lang/lv.js +0 -188
- package/src/lang/nl.d.ts +0 -5
- package/src/lang/nl.js +0 -188
- package/src/lang/pl.d.ts +0 -5
- package/src/lang/pl.js +0 -188
- package/src/lang/pt_br.d.ts +0 -5
- package/src/lang/pt_br.js +0 -189
- package/src/lang/ro.d.ts +0 -5
- package/src/lang/ro.js +0 -188
- package/src/lang/ru.d.ts +0 -5
- package/src/lang/ru.js +0 -188
- package/src/lang/se.d.ts +0 -5
- package/src/lang/se.js +0 -191
- package/src/lang/tr.d.ts +0 -5
- package/src/lang/tr.js +0 -191
- package/src/lang/ua.d.ts +0 -5
- package/src/lang/ua.js +0 -188
- package/src/lang/ur.d.ts +0 -5
- package/src/lang/ur.js +0 -188
- package/src/lang/zh_cn.d.ts +0 -5
- package/src/lang/zh_cn.js +0 -187
- package/src/lib/constructor.js +0 -954
- package/src/lib/context.d.ts +0 -42
- package/src/lib/context.js +0 -71
- package/src/lib/core.d.ts +0 -1135
- package/src/lib/core.js +0 -9395
- package/src/lib/history.d.ts +0 -48
- package/src/lib/history.js +0 -219
- package/src/lib/util.d.ts +0 -678
- package/src/lib/util.js +0 -2131
- package/src/options.d.ts +0 -608
- package/src/plugins/CommandPlugin.d.ts +0 -8
- package/src/plugins/DialogPlugin.d.ts +0 -20
- package/src/plugins/FileBrowserPlugin.d.ts +0 -30
- package/src/plugins/Module.d.ts +0 -15
- package/src/plugins/Plugin.d.ts +0 -42
- package/src/plugins/SubmenuPlugin.d.ts +0 -8
- package/src/plugins/dialog/audio.js +0 -559
- package/src/plugins/dialog/image.js +0 -1126
- package/src/plugins/dialog/link.js +0 -223
- package/src/plugins/dialog/math.js +0 -295
- package/src/plugins/dialog/mention.js +0 -242
- package/src/plugins/dialog/video.js +0 -979
- package/src/plugins/index.d.ts +0 -79
- package/src/plugins/modules/_anchor.js +0 -461
- package/src/plugins/modules/_colorPicker.d.ts +0 -60
- package/src/plugins/modules/_colorPicker.js +0 -201
- package/src/plugins/modules/_notice.d.ts +0 -21
- package/src/plugins/modules/_notice.js +0 -72
- package/src/plugins/modules/_selectMenu.js +0 -119
- package/src/plugins/modules/component.d.ts +0 -25
- package/src/plugins/modules/component.js +0 -81
- package/src/plugins/modules/dialog.d.ts +0 -28
- package/src/plugins/modules/dialog.js +0 -175
- package/src/plugins/modules/fileBrowser.d.ts +0 -42
- package/src/plugins/modules/fileBrowser.js +0 -374
- package/src/plugins/modules/fileManager.d.ts +0 -67
- package/src/plugins/modules/fileManager.js +0 -326
- package/src/plugins/modules/index.js +0 -9
- package/src/plugins/modules/resizing.d.ts +0 -154
- package/src/plugins/modules/resizing.js +0 -903
- package/src/plugins/submenu/align.d.ts +0 -5
- package/src/plugins/submenu/align.js +0 -160
- package/src/plugins/submenu/font.d.ts +0 -5
- package/src/plugins/submenu/font.js +0 -123
- package/src/plugins/submenu/fontColor.d.ts +0 -5
- package/src/plugins/submenu/fontColor.js +0 -101
- package/src/plugins/submenu/fontSize.d.ts +0 -5
- package/src/plugins/submenu/fontSize.js +0 -112
- package/src/plugins/submenu/formatBlock.d.ts +0 -5
- package/src/plugins/submenu/formatBlock.js +0 -273
- package/src/plugins/submenu/hiliteColor.d.ts +0 -5
- package/src/plugins/submenu/hiliteColor.js +0 -102
- package/src/plugins/submenu/horizontalRule.d.ts +0 -5
- package/src/plugins/submenu/horizontalRule.js +0 -98
- package/src/plugins/submenu/lineHeight.d.ts +0 -5
- package/src/plugins/submenu/lineHeight.js +0 -104
- package/src/plugins/submenu/list.d.ts +0 -5
- package/src/plugins/submenu/list.js +0 -456
- package/src/plugins/submenu/paragraphStyle.d.ts +0 -5
- package/src/plugins/submenu/paragraphStyle.js +0 -135
- package/src/plugins/submenu/table.d.ts +0 -5
- package/src/plugins/submenu/table.js +0 -1431
- package/src/plugins/submenu/template.d.ts +0 -5
- package/src/plugins/submenu/template.js +0 -72
- package/src/plugins/submenu/textStyle.d.ts +0 -5
- package/src/plugins/submenu/textStyle.js +0 -167
- package/src/suneditor.d.ts +0 -9
- package/src/suneditor_build.js +0 -18
- /package/{src/plugins/dialog → typings}/mention.d.ts +0 -0
|
@@ -0,0 +1,3255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Format class
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
+
import { domUtils, unicode, numbers, env } from '../../helper';
|
|
7
|
+
|
|
8
|
+
const Format = function (editor) {
|
|
9
|
+
CoreInjector.call(this, editor);
|
|
10
|
+
|
|
11
|
+
// members
|
|
12
|
+
this._listCamel = this.options.get('__listCommonStyle');
|
|
13
|
+
this._listKebab = env.camelToKebabCase(this.options.get('__listCommonStyle'));
|
|
14
|
+
this._formatLineCheck = this.options.get('formatLine').reg;
|
|
15
|
+
this._formatBrLineCheck = this.options.get('formatBrLine').reg;
|
|
16
|
+
this._formatBlockCheck = this.options.get('formatBlock').reg;
|
|
17
|
+
this._formatClosureBlockCheck = this.options.get('formatClosureBlock').reg;
|
|
18
|
+
this._formatClosureBrLineCheck = this.options.get('formatClosureBrLine').reg;
|
|
19
|
+
this._textStyleTagsCheck = new RegExp('^(' + this.options.get('textStyleTags') + ')$', 'i');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
Format.prototype = {
|
|
23
|
+
/**
|
|
24
|
+
* @description Replace the line tag of the current selection.
|
|
25
|
+
* @param {Element} element Line element (P, DIV..)
|
|
26
|
+
*/
|
|
27
|
+
setLine(element) {
|
|
28
|
+
if (!this.isLine(element)) {
|
|
29
|
+
throw new Error('[SUNEDITOR.format.setLine.fail] The "element" must satisfy "format.isLine()".');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const info = this._lineWork();
|
|
33
|
+
const lines = info.lines;
|
|
34
|
+
const className = element.className;
|
|
35
|
+
const value = element.nodeName;
|
|
36
|
+
let first = info.firstNode;
|
|
37
|
+
let last = info.lastNode;
|
|
38
|
+
|
|
39
|
+
for (let i = 0, len = lines.length, node, newFormat; i < len; i++) {
|
|
40
|
+
node = lines[i];
|
|
41
|
+
|
|
42
|
+
if ((node.nodeName !== value || (node.className.match(/(\s|^)__se__format__[^\s]+/) || [''])[0].trim() !== className) && !this.component.is(node)) {
|
|
43
|
+
newFormat = element.cloneNode(false);
|
|
44
|
+
domUtils.copyFormatAttributes(newFormat, node);
|
|
45
|
+
newFormat.innerHTML = node.innerHTML;
|
|
46
|
+
|
|
47
|
+
node.parentNode.replaceChild(newFormat, node);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (i === 0) first = newFormat || node;
|
|
51
|
+
if (i === len - 1) last = newFormat || node;
|
|
52
|
+
newFormat = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.selection.setRange(domUtils.getNodeFromPath(info.firstPath, first), info.startOffset, domUtils.getNodeFromPath(info.lastPath, last), info.endOffset);
|
|
56
|
+
this.history.push(false);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @description If a parent node that contains an argument node finds a format node (format.isLine), it returns that node.
|
|
61
|
+
* @param {Node} node Reference node.
|
|
62
|
+
* @param {Function|null} validation Additional validation function.
|
|
63
|
+
* @returns {Element|null}
|
|
64
|
+
*/
|
|
65
|
+
getLine(node, validation) {
|
|
66
|
+
if (!node) return null;
|
|
67
|
+
if (!validation) {
|
|
68
|
+
validation = function () {
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
while (node) {
|
|
74
|
+
if (domUtils.isWysiwygFrame(node)) return null;
|
|
75
|
+
if (this.isBlock(node)) return node.firstElementChild;
|
|
76
|
+
if (this.isLine(node) && validation(node)) return node;
|
|
77
|
+
|
|
78
|
+
node = node.parentNode;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @description Replace the br-line tag of the current selection.
|
|
86
|
+
* @param {Element} element BR-Line element (PRE..)
|
|
87
|
+
*/
|
|
88
|
+
setBrLine(element) {
|
|
89
|
+
if (!this.isBrLine(element)) {
|
|
90
|
+
throw new Error('[SUNEDITOR.format.setBrLine.fail] The "element" must satisfy "format.isBrLine()".');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const lines = this._lineWork().lines;
|
|
94
|
+
const len = lines.length - 1;
|
|
95
|
+
let parentNode = lines[len].parentNode;
|
|
96
|
+
let freeElement = element.cloneNode(false);
|
|
97
|
+
const focusElement = freeElement;
|
|
98
|
+
|
|
99
|
+
for (let i = len, f, html, before, next, inner, isComp, first = true; i >= 0; i--) {
|
|
100
|
+
f = lines[i];
|
|
101
|
+
if (f === (!lines[i + 1] ? null : lines[i + 1].parentNode)) continue;
|
|
102
|
+
|
|
103
|
+
isComp = this.component.is(f);
|
|
104
|
+
html = isComp ? '' : f.innerHTML.replace(/(?!>)\s+(?=<)|\n/g, ' ');
|
|
105
|
+
before = domUtils.getParentElement(f, (current) => current.parentNode === parentNode);
|
|
106
|
+
|
|
107
|
+
if (parentNode !== f.parentNode || isComp) {
|
|
108
|
+
if (this.isLine(parentNode)) {
|
|
109
|
+
parentNode.parentNode.insertBefore(freeElement, parentNode.nextSibling);
|
|
110
|
+
parentNode = parentNode.parentNode;
|
|
111
|
+
} else {
|
|
112
|
+
parentNode.insertBefore(freeElement, before ? before.nextSibling : null);
|
|
113
|
+
parentNode = f.parentNode;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
next = freeElement.nextSibling;
|
|
117
|
+
if (next && freeElement.nodeName === next.nodeName && domUtils.isSameAttributes(freeElement, next)) {
|
|
118
|
+
freeElement.innerHTML += '<BR>' + next.innerHTML;
|
|
119
|
+
domUtils.removeItem(next);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
freeElement = element.cloneNode(false);
|
|
123
|
+
first = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
inner = freeElement.innerHTML;
|
|
127
|
+
freeElement.innerHTML = (first || !html || !inner || /<br>$/i.test(html) ? html : html + '<BR>') + inner;
|
|
128
|
+
|
|
129
|
+
if (i === 0) {
|
|
130
|
+
parentNode.insertBefore(freeElement, f);
|
|
131
|
+
next = f.nextSibling;
|
|
132
|
+
if (next && freeElement.nodeName === next.nodeName && domUtils.isSameAttributes(freeElement, next)) {
|
|
133
|
+
freeElement.innerHTML += '<BR>' + next.innerHTML;
|
|
134
|
+
domUtils.removeItem(next);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const prev = freeElement.previousSibling;
|
|
138
|
+
if (prev && freeElement.nodeName === prev.nodeName && domUtils.isSameAttributes(freeElement, prev)) {
|
|
139
|
+
prev.innerHTML += '<BR>' + freeElement.innerHTML;
|
|
140
|
+
domUtils.removeItem(freeElement);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!isComp) domUtils.removeItem(f);
|
|
145
|
+
if (html) first = false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.selection.setRange(focusElement, 0, focusElement, 0);
|
|
149
|
+
this.history.push(false);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @description If a parent node that contains an argument node finds a free format node (format.isBrLine), it returns that node.
|
|
154
|
+
* @param {Node} element Reference node.
|
|
155
|
+
* @param {Function|null} validation Additional validation function.
|
|
156
|
+
* @returns {Element|null}
|
|
157
|
+
*/
|
|
158
|
+
getBrLine(element, validation) {
|
|
159
|
+
if (!element) return null;
|
|
160
|
+
if (!validation) {
|
|
161
|
+
validation = function () {
|
|
162
|
+
return true;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
while (element) {
|
|
167
|
+
if (domUtils.isWysiwygFrame(element)) return null;
|
|
168
|
+
if (this.isBrLine(element) && validation(element)) return element;
|
|
169
|
+
|
|
170
|
+
element = element.parentNode;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return null;
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @description Append format element to sibling node of argument element.
|
|
178
|
+
* If the "lineNode" argument value is present, the tag of that argument value is inserted,
|
|
179
|
+
* If not, the currently selected format tag is inserted.
|
|
180
|
+
* @param {Element} element Insert as siblings of that element
|
|
181
|
+
* @param {string|Element|null} lineNode Node name or node obejct to be inserted
|
|
182
|
+
* @returns {Element}
|
|
183
|
+
*/
|
|
184
|
+
addLine(element, lineNode) {
|
|
185
|
+
if (!element || !element.parentNode) return null;
|
|
186
|
+
|
|
187
|
+
const currentFormatEl = this.getLine(this.selection.getNode(), null);
|
|
188
|
+
let oFormat = null;
|
|
189
|
+
if (!this.isBrLine(element) && this.isBrLine(currentFormatEl || element.parentNode)) {
|
|
190
|
+
oFormat = domUtils.createElement('BR');
|
|
191
|
+
} else {
|
|
192
|
+
const oFormatName = lineNode
|
|
193
|
+
? typeof lineNode === 'string'
|
|
194
|
+
? lineNode
|
|
195
|
+
: lineNode.nodeName
|
|
196
|
+
: this.isLine(currentFormatEl) && !this.isBlock(currentFormatEl) && !this.isBrLine(currentFormatEl)
|
|
197
|
+
? currentFormatEl.nodeName
|
|
198
|
+
: this.options.get('defaultLine');
|
|
199
|
+
oFormat = domUtils.createElement(oFormatName, null, '<br>');
|
|
200
|
+
if ((lineNode && typeof lineNode !== 'string') || (!lineNode && this.isLine(currentFormatEl))) {
|
|
201
|
+
domUtils.copyTagAttributes(oFormat, lineNode || currentFormatEl, ['id']);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (domUtils.isTableCell(element)) element.insertBefore(oFormat, element.nextElementSibling);
|
|
206
|
+
else element.parentNode.insertBefore(oFormat, element.nextElementSibling);
|
|
207
|
+
|
|
208
|
+
return oFormat;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @description If a parent node that contains an argument node finds a format node (format.isBlock), it returns that node.
|
|
213
|
+
* @param {Node} element Reference node.
|
|
214
|
+
* @param {Function|null} validation Additional validation function.
|
|
215
|
+
* @returns {Element|null}
|
|
216
|
+
*/
|
|
217
|
+
getBlock(element, validation) {
|
|
218
|
+
if (!element) return null;
|
|
219
|
+
if (!validation) {
|
|
220
|
+
validation = function () {
|
|
221
|
+
return true;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
while (element) {
|
|
226
|
+
if (domUtils.isWysiwygFrame(element)) return null;
|
|
227
|
+
if (this.isBlock(element) && !/^(THEAD|TBODY|TR)$/i.test(element.nodeName) && validation(element)) return element;
|
|
228
|
+
element = element.parentNode;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return null;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @description Appended all selected format Element to the argument element and insert
|
|
236
|
+
* @param {Element} block Element of wrap the arguments (BLOCKQUOTE...)
|
|
237
|
+
*/
|
|
238
|
+
applyBlock(block) {
|
|
239
|
+
this.selection.getRangeAndAddLine(this.selection.getRange(), null);
|
|
240
|
+
const rangeLines = this.getLinesAndComponents(false);
|
|
241
|
+
if (!rangeLines || rangeLines.length === 0) return;
|
|
242
|
+
|
|
243
|
+
linesLoop: for (let i = 0, len = rangeLines.length, line, nested, fEl, lEl, f, l; i < len; i++) {
|
|
244
|
+
line = rangeLines[i];
|
|
245
|
+
if (!domUtils.isListCell(line)) continue;
|
|
246
|
+
|
|
247
|
+
nested = line.lastElementChild;
|
|
248
|
+
if (nested && domUtils.isListCell(line.nextElementSibling) && rangeLines.includes(line.nextElementSibling)) {
|
|
249
|
+
lEl = nested.lastElementChild;
|
|
250
|
+
if (rangeLines.includes(lEl)) {
|
|
251
|
+
let list = null;
|
|
252
|
+
while ((list = lEl.lastElementChild)) {
|
|
253
|
+
if (domUtils.isList(list)) {
|
|
254
|
+
if (rangeLines.includes(list.lastElementChild)) {
|
|
255
|
+
lEl = list.lastElementChild;
|
|
256
|
+
} else {
|
|
257
|
+
continue linesLoop;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fEl = nested.firstElementChild;
|
|
263
|
+
f = rangeLines.indexOf(fEl);
|
|
264
|
+
l = rangeLines.indexOf(lEl);
|
|
265
|
+
rangeLines.splice(f, l - f + 1);
|
|
266
|
+
len = rangeLines.length;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const last = rangeLines[rangeLines.length - 1];
|
|
273
|
+
let standTag, beforeTag, pElement;
|
|
274
|
+
|
|
275
|
+
if (this.isBlock(last) || this.isLine(last)) {
|
|
276
|
+
standTag = last;
|
|
277
|
+
} else {
|
|
278
|
+
standTag = this.getBlock(last, null) || this.getLine(last, null);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (domUtils.isTableCell(standTag)) {
|
|
282
|
+
beforeTag = null;
|
|
283
|
+
pElement = standTag;
|
|
284
|
+
} else {
|
|
285
|
+
beforeTag = standTag.nextSibling;
|
|
286
|
+
pElement = standTag.parentNode;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
block = block.cloneNode(false);
|
|
290
|
+
let parentDepth = domUtils.getNodeDepth(standTag);
|
|
291
|
+
let listParent = null;
|
|
292
|
+
const lineArr = [];
|
|
293
|
+
const removeItems = (parent, origin, before) => {
|
|
294
|
+
let cc = null;
|
|
295
|
+
if (parent !== origin && !domUtils.isTableElements(origin)) {
|
|
296
|
+
if (origin && domUtils.getNodeDepth(parent) === domUtils.getNodeDepth(origin)) return before;
|
|
297
|
+
cc = this.nodeTransform.removeAllParents(origin, null, parent);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return cc ? cc.ec : before;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
for (let i = 0, len = rangeLines.length, line, originParent, depth, before, nextLine, nextList, nested; i < len; i++) {
|
|
304
|
+
line = rangeLines[i];
|
|
305
|
+
originParent = line.parentNode;
|
|
306
|
+
if (!originParent || block.contains(originParent)) continue;
|
|
307
|
+
|
|
308
|
+
depth = domUtils.getNodeDepth(line);
|
|
309
|
+
|
|
310
|
+
if (domUtils.isList(originParent)) {
|
|
311
|
+
if (listParent === null) {
|
|
312
|
+
if (nextList) {
|
|
313
|
+
listParent = nextList;
|
|
314
|
+
nested = true;
|
|
315
|
+
nextList = null;
|
|
316
|
+
} else {
|
|
317
|
+
listParent = originParent.cloneNode(false);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
lineArr.push(line);
|
|
322
|
+
nextLine = rangeLines[i + 1];
|
|
323
|
+
|
|
324
|
+
if (i === len - 1 || nextLine?.parentNode !== originParent) {
|
|
325
|
+
// nested list
|
|
326
|
+
if (line.contains(nextLine?.parentNode)) {
|
|
327
|
+
nextList = nextLine.parentNode.cloneNode(false);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let list = originParent.parentNode,
|
|
331
|
+
p;
|
|
332
|
+
while (domUtils.isList(list)) {
|
|
333
|
+
p = domUtils.createElement(list.nodeName);
|
|
334
|
+
p.appendChild(listParent);
|
|
335
|
+
listParent = p;
|
|
336
|
+
list = list.parentNode;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const edge = this.removeBlock(originParent, lineArr, null, true, true);
|
|
340
|
+
|
|
341
|
+
if (parentDepth >= depth) {
|
|
342
|
+
parentDepth = depth;
|
|
343
|
+
pElement = edge.cc;
|
|
344
|
+
beforeTag = removeItems(pElement, originParent, edge.ec);
|
|
345
|
+
if (beforeTag) pElement = beforeTag.parentNode;
|
|
346
|
+
} else if (pElement === edge.cc) {
|
|
347
|
+
beforeTag = edge.ec;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (pElement !== edge.cc) {
|
|
351
|
+
before = removeItems(pElement, edge.cc, before);
|
|
352
|
+
if (before !== undefined) beforeTag = before;
|
|
353
|
+
else beforeTag = edge.cc;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
for (let c = 0, cLen = edge.removeArray.length; c < cLen; c++) {
|
|
357
|
+
listParent.appendChild(edge.removeArray[c]);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!nested) block.appendChild(listParent);
|
|
361
|
+
if (nextList) edge.removeArray[edge.removeArray.length - 1].appendChild(nextList);
|
|
362
|
+
listParent = null;
|
|
363
|
+
nested = false;
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
if (parentDepth >= depth) {
|
|
367
|
+
parentDepth = depth;
|
|
368
|
+
pElement = originParent;
|
|
369
|
+
beforeTag = line.nextSibling;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
block.appendChild(line);
|
|
373
|
+
|
|
374
|
+
if (pElement !== originParent) {
|
|
375
|
+
before = removeItems(pElement, originParent);
|
|
376
|
+
if (before !== undefined) beforeTag = before;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this.editor.effectNode = null;
|
|
382
|
+
this.nodeTransform.mergeSameTags(block, null, false);
|
|
383
|
+
this.nodeTransform.mergeNestedTags(block, (current) => domUtils.isList(current));
|
|
384
|
+
|
|
385
|
+
// Nested list
|
|
386
|
+
if (beforeTag && domUtils.getNodeDepth(beforeTag) > 0 && (domUtils.isList(beforeTag.parentNode) || domUtils.isList(beforeTag.parentNode.parentNode))) {
|
|
387
|
+
const depthFormat = domUtils.getParentElement(beforeTag, (current) => this.isBlock(current) && !domUtils.isList(current));
|
|
388
|
+
const splitRange = this.nodeTransform.split(beforeTag, null, !depthFormat ? 0 : domUtils.getNodeDepth(depthFormat) + 1);
|
|
389
|
+
splitRange.parentNode.insertBefore(block, splitRange);
|
|
390
|
+
} else {
|
|
391
|
+
// basic
|
|
392
|
+
pElement.insertBefore(block, beforeTag);
|
|
393
|
+
removeItems(block, beforeTag);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const edge = domUtils.getEdgeChildNodes(block.firstElementChild, block.lastElementChild);
|
|
397
|
+
if (rangeLines.length > 1) {
|
|
398
|
+
this.selection.setRange(edge.sc, 0, edge.ec, edge.ec.textContent.length);
|
|
399
|
+
} else {
|
|
400
|
+
this.selection.setRange(edge.ec, edge.ec.textContent.length, edge.ec, edge.ec.textContent.length);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
this.history.push(false);
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @description The elements of the "selectedFormats" array are detached from the "rangeElement" element. ("LI" tags are converted to "P" tags)
|
|
408
|
+
* When "selectedFormats" is null, all elements are detached and return {cc: parentNode, sc: nextSibling, ec: previousSibling, removeArray: [Array of removed elements]}.
|
|
409
|
+
* @param {Element} rangeElement Range format element (PRE, BLOCKQUOTE, OL, UL...)
|
|
410
|
+
* @param {Array|null} selectedFormats Array of format elements (P, DIV, LI...) to remove.
|
|
411
|
+
* If null, Applies to all elements and return {cc: parentNode, sc: nextSibling, ec: previousSibling}
|
|
412
|
+
* @param {Element|null} newRangeElement The node(rangeElement) to replace the currently wrapped node.
|
|
413
|
+
* @param {boolean} remove If true, deleted without detached.
|
|
414
|
+
* @param {boolean} notHistoryPush When true, it does not update the history stack and the selection object and return EdgeNodes (domUtils.getEdgeChildNodes)
|
|
415
|
+
* @returns {Object}
|
|
416
|
+
*/
|
|
417
|
+
removeBlock(rangeElement, selectedFormats, newRangeElement, remove, notHistoryPush) {
|
|
418
|
+
const range = this.selection.getRange();
|
|
419
|
+
let so = range.startOffset;
|
|
420
|
+
let eo = range.endOffset;
|
|
421
|
+
|
|
422
|
+
let children = domUtils.getListChildNodes(rangeElement, (current) => current.parentNode === rangeElement);
|
|
423
|
+
let parent = rangeElement.parentNode;
|
|
424
|
+
let firstNode = null;
|
|
425
|
+
let lastNode = null;
|
|
426
|
+
let rangeEl = rangeElement.cloneNode(false);
|
|
427
|
+
|
|
428
|
+
const removeArray = [];
|
|
429
|
+
const newList = domUtils.isList(newRangeElement);
|
|
430
|
+
let insertedNew = false;
|
|
431
|
+
let reset = false;
|
|
432
|
+
let moveComplete = false;
|
|
433
|
+
|
|
434
|
+
const appendNode = (parentEl, insNode, sibling, originNode) => {
|
|
435
|
+
if (domUtils.isZeroWith(insNode)) {
|
|
436
|
+
insNode.innerHTML = unicode.zeroWidthSpace;
|
|
437
|
+
so = eo = 1;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (insNode.nodeType === 3) {
|
|
441
|
+
parentEl.insertBefore(insNode, sibling);
|
|
442
|
+
return insNode;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const insChildren = (moveComplete ? insNode : originNode).childNodes;
|
|
446
|
+
let format = insNode.cloneNode(false);
|
|
447
|
+
let first = null;
|
|
448
|
+
let c = null;
|
|
449
|
+
|
|
450
|
+
while (insChildren[0]) {
|
|
451
|
+
c = insChildren[0];
|
|
452
|
+
if (this._notTextNode(c) && !domUtils.isBreak(c) && !domUtils.isListCell(format)) {
|
|
453
|
+
if (format.childNodes.length > 0) {
|
|
454
|
+
if (!first) first = format;
|
|
455
|
+
parentEl.insertBefore(format, sibling);
|
|
456
|
+
format = insNode.cloneNode(false);
|
|
457
|
+
}
|
|
458
|
+
parentEl.insertBefore(c, sibling);
|
|
459
|
+
if (!first) first = c;
|
|
460
|
+
} else {
|
|
461
|
+
format.appendChild(c);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (format.childNodes.length > 0) {
|
|
466
|
+
if (domUtils.isListCell(parentEl) && domUtils.isListCell(format) && domUtils.isList(sibling)) {
|
|
467
|
+
if (newList) {
|
|
468
|
+
first = sibling;
|
|
469
|
+
while (sibling) {
|
|
470
|
+
format.appendChild(sibling);
|
|
471
|
+
sibling = sibling.nextSibling;
|
|
472
|
+
}
|
|
473
|
+
parentEl.parentNode.insertBefore(format, parentEl.nextElementSibling);
|
|
474
|
+
} else {
|
|
475
|
+
const originNext = originNode.nextElementSibling;
|
|
476
|
+
const detachRange = this._removeNestedList(originNode, false);
|
|
477
|
+
if (rangeElement !== detachRange || originNext !== originNode.nextElementSibling) {
|
|
478
|
+
const fChildren = format.childNodes;
|
|
479
|
+
while (fChildren[0]) {
|
|
480
|
+
originNode.appendChild(fChildren[0]);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
rangeElement = detachRange;
|
|
484
|
+
reset = true;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
parentEl.insertBefore(format, sibling);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!first) first = format;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return first;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// detach loop
|
|
498
|
+
for (let i = 0, len = children.length, insNode, lineIndex, next; i < len; i++) {
|
|
499
|
+
insNode = children[i];
|
|
500
|
+
if (insNode.nodeType === 3 && domUtils.isList(rangeEl)) continue;
|
|
501
|
+
|
|
502
|
+
moveComplete = false;
|
|
503
|
+
if (remove && i === 0) {
|
|
504
|
+
if (!selectedFormats || selectedFormats.length === len || selectedFormats[0] === insNode) {
|
|
505
|
+
firstNode = rangeElement.previousSibling;
|
|
506
|
+
} else {
|
|
507
|
+
firstNode = rangeEl;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (selectedFormats) lineIndex = selectedFormats.indexOf(insNode);
|
|
512
|
+
if (selectedFormats && lineIndex === -1) {
|
|
513
|
+
if (!rangeEl) rangeEl = rangeElement.cloneNode(false);
|
|
514
|
+
rangeEl.appendChild(insNode);
|
|
515
|
+
} else {
|
|
516
|
+
if (selectedFormats) next = selectedFormats[lineIndex + 1];
|
|
517
|
+
if (rangeEl && rangeEl.children.length > 0) {
|
|
518
|
+
parent.insertBefore(rangeEl, rangeElement);
|
|
519
|
+
rangeEl = null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!newList && domUtils.isListCell(insNode)) {
|
|
523
|
+
if (next && domUtils.getNodeDepth(insNode) !== domUtils.getNodeDepth(next) && (domUtils.isListCell(parent) || domUtils.getArrayItem(insNode.children, domUtils.isList, false))) {
|
|
524
|
+
const insNext = insNode.nextElementSibling;
|
|
525
|
+
const detachRange = this._removeNestedList(insNode, false);
|
|
526
|
+
if (rangeElement !== detachRange || insNext !== insNode.nextElementSibling) {
|
|
527
|
+
rangeElement = detachRange;
|
|
528
|
+
reset = true;
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
const inner = insNode;
|
|
532
|
+
insNode = domUtils.createElement(
|
|
533
|
+
remove ? inner.nodeName : domUtils.isList(rangeElement.parentNode) || domUtils.isListCell(rangeElement.parentNode) ? 'LI' : domUtils.isTableCell(rangeElement.parentNode) ? 'DIV' : this.options.get('defaultLine')
|
|
534
|
+
);
|
|
535
|
+
const isCell = domUtils.isListCell(insNode);
|
|
536
|
+
const innerChildren = inner.childNodes;
|
|
537
|
+
while (innerChildren[0]) {
|
|
538
|
+
if (domUtils.isList(innerChildren[0]) && !isCell) break;
|
|
539
|
+
insNode.appendChild(innerChildren[0]);
|
|
540
|
+
}
|
|
541
|
+
domUtils.copyFormatAttributes(insNode, inner);
|
|
542
|
+
moveComplete = true;
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
insNode = insNode.cloneNode(false);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (!reset) {
|
|
549
|
+
if (!remove) {
|
|
550
|
+
if (newRangeElement) {
|
|
551
|
+
if (!insertedNew) {
|
|
552
|
+
parent.insertBefore(newRangeElement, rangeElement);
|
|
553
|
+
insertedNew = true;
|
|
554
|
+
}
|
|
555
|
+
insNode = appendNode(newRangeElement, insNode, null, children[i]);
|
|
556
|
+
} else {
|
|
557
|
+
insNode = appendNode(parent, insNode, rangeElement, children[i]);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (!reset) {
|
|
561
|
+
if (selectedFormats) {
|
|
562
|
+
lastNode = insNode;
|
|
563
|
+
if (!firstNode) {
|
|
564
|
+
firstNode = insNode;
|
|
565
|
+
}
|
|
566
|
+
} else if (!firstNode) {
|
|
567
|
+
firstNode = lastNode = insNode;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
removeArray.push(insNode);
|
|
572
|
+
domUtils.removeItem(children[i]);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (reset) {
|
|
576
|
+
reset = moveComplete = false;
|
|
577
|
+
children = domUtils.getListChildNodes(rangeElement, (current) => current.parentNode === rangeElement);
|
|
578
|
+
rangeEl = rangeElement.cloneNode(false);
|
|
579
|
+
parent = rangeElement.parentNode;
|
|
580
|
+
i = -1;
|
|
581
|
+
len = children.length;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const rangeParent = rangeElement.parentNode;
|
|
589
|
+
let rangeRight = rangeElement.nextSibling;
|
|
590
|
+
if (rangeEl?.children.length > 0) {
|
|
591
|
+
rangeParent.insertBefore(rangeEl, rangeRight);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (newRangeElement) firstNode = newRangeElement.previousSibling;
|
|
595
|
+
else if (!firstNode) firstNode = rangeElement.previousSibling;
|
|
596
|
+
rangeRight = rangeElement.nextSibling !== rangeEl ? rangeElement.nextSibling : rangeEl ? rangeEl.nextSibling : null;
|
|
597
|
+
|
|
598
|
+
if (rangeElement.children.length === 0 || rangeElement.textContent.length === 0) {
|
|
599
|
+
domUtils.removeItem(rangeElement);
|
|
600
|
+
} else {
|
|
601
|
+
this.nodeTransform.removeEmptyNode(rangeElement, null, false);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
let edge = null;
|
|
605
|
+
if (remove) {
|
|
606
|
+
edge = {
|
|
607
|
+
cc: rangeParent,
|
|
608
|
+
sc: firstNode,
|
|
609
|
+
so: so,
|
|
610
|
+
ec: rangeRight,
|
|
611
|
+
eo: eo,
|
|
612
|
+
removeArray: removeArray
|
|
613
|
+
};
|
|
614
|
+
} else {
|
|
615
|
+
if (!firstNode) firstNode = lastNode;
|
|
616
|
+
if (!lastNode) lastNode = firstNode;
|
|
617
|
+
const childEdge = domUtils.getEdgeChildNodes(firstNode, lastNode.parentNode ? firstNode : lastNode);
|
|
618
|
+
edge = {
|
|
619
|
+
cc: (childEdge.sc || childEdge.ec).parentNode,
|
|
620
|
+
sc: childEdge.sc,
|
|
621
|
+
so: so,
|
|
622
|
+
ec: childEdge.ec,
|
|
623
|
+
eo: eo,
|
|
624
|
+
removeArray: null
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
this.editor.effectNode = null;
|
|
629
|
+
if (notHistoryPush) return edge;
|
|
630
|
+
|
|
631
|
+
if (!remove && edge) {
|
|
632
|
+
if (!selectedFormats) {
|
|
633
|
+
this.selection.setRange(edge.sc, 0, edge.sc, 0);
|
|
634
|
+
} else {
|
|
635
|
+
this.selection.setRange(edge.sc, so, edge.ec, eo);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
this.history.push(false);
|
|
640
|
+
},
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* @description Append all selected format Element to the list and insert.
|
|
644
|
+
* @param {string} type List type. (ol | ul):[listStyleType]
|
|
645
|
+
* @param {Element} selectedCells Format elements or list cells.
|
|
646
|
+
* @param {boolean} nested If true, indenting existing list cells.
|
|
647
|
+
*/
|
|
648
|
+
applyList(type, selectedCells, nested) {
|
|
649
|
+
const listTag = (type.split(':')[0] || 'ol').toUpperCase();
|
|
650
|
+
const listStyle = type.split(':')[1] || '';
|
|
651
|
+
|
|
652
|
+
let range = this.selection.getRange();
|
|
653
|
+
let selectedFormats = !selectedCells ? this.getLinesAndComponents(false) : selectedCells;
|
|
654
|
+
|
|
655
|
+
if (selectedFormats.length === 0) {
|
|
656
|
+
if (selectedCells) return;
|
|
657
|
+
range = this.selection.getRangeAndAddLine(range, null);
|
|
658
|
+
selectedFormats = this.getLinesAndComponents(false);
|
|
659
|
+
if (selectedFormats.length === 0) return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
domUtils.sortNodeByDepth(selectedFormats, true);
|
|
663
|
+
|
|
664
|
+
// merge
|
|
665
|
+
const firstSel = selectedFormats[0];
|
|
666
|
+
const lastSel = selectedFormats[selectedFormats.length - 1];
|
|
667
|
+
let topEl = (domUtils.isListCell(firstSel) || this.component.is(firstSel)) && !firstSel.previousElementSibling ? firstSel.parentNode.previousElementSibling : firstSel.previousElementSibling;
|
|
668
|
+
let bottomEl = (domUtils.isListCell(lastSel) || this.component.is(lastSel)) && !lastSel.nextElementSibling ? lastSel.parentNode.nextElementSibling : lastSel.nextElementSibling;
|
|
669
|
+
|
|
670
|
+
const isCollapsed = range.collapsed;
|
|
671
|
+
const originRange = {
|
|
672
|
+
sc: range.startContainer,
|
|
673
|
+
so: range.startContainer === range.endContainer && domUtils.isZeroWith(range.startContainer) && range.startOffset === 0 && range.endOffset === 1 ? range.endOffset : range.startOffset,
|
|
674
|
+
ec: range.endContainer,
|
|
675
|
+
eo: range.endOffset
|
|
676
|
+
};
|
|
677
|
+
let afterRange = null;
|
|
678
|
+
let isRemove = true;
|
|
679
|
+
|
|
680
|
+
for (let i = 0, len = selectedFormats.length; i < len; i++) {
|
|
681
|
+
if (!domUtils.isList(this.getBlock(selectedFormats[i], (current) => this.getBlock(current) && current !== selectedFormats[i]))) {
|
|
682
|
+
isRemove = false;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (isRemove && (!topEl || firstSel.tagName !== topEl.tagName || listTag !== topEl.tagName.toUpperCase()) && (!bottomEl || lastSel.tagName !== bottomEl.tagName || listTag !== bottomEl.tagName.toUpperCase())) {
|
|
688
|
+
if (nested) {
|
|
689
|
+
for (let i = 0, len = selectedFormats.length; i < len; i++) {
|
|
690
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
691
|
+
if (selectedFormats[j].contains(selectedFormats[i])) {
|
|
692
|
+
selectedFormats.splice(i, 1);
|
|
693
|
+
i--;
|
|
694
|
+
len--;
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const currentFormat = this.getBlock(firstSel);
|
|
702
|
+
const cancel = currentFormat?.tagName === listTag;
|
|
703
|
+
let rangeArr, tempList;
|
|
704
|
+
const passComponent = (current) => {
|
|
705
|
+
return !this.component.is(current);
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
if (!cancel) {
|
|
709
|
+
tempList = domUtils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
for (let i = 0, len = selectedFormats.length, r, o; i < len; i++) {
|
|
713
|
+
o = this.getBlock(selectedFormats[i], passComponent);
|
|
714
|
+
if (!o || !domUtils.isList(o)) continue;
|
|
715
|
+
|
|
716
|
+
if (!r) {
|
|
717
|
+
r = o;
|
|
718
|
+
rangeArr = {
|
|
719
|
+
r: r,
|
|
720
|
+
f: [domUtils.getParentElement(selectedFormats[i], 'LI')]
|
|
721
|
+
};
|
|
722
|
+
} else {
|
|
723
|
+
if (r !== o) {
|
|
724
|
+
if (nested && domUtils.isListCell(o.parentNode)) {
|
|
725
|
+
this._detachNested(rangeArr.f);
|
|
726
|
+
} else {
|
|
727
|
+
afterRange = this.removeBlock(rangeArr.f[0].parentNode, rangeArr.f, tempList, false, true);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
o = selectedFormats[i].parentNode;
|
|
731
|
+
if (!cancel) {
|
|
732
|
+
tempList = domUtils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
r = o;
|
|
736
|
+
rangeArr = {
|
|
737
|
+
r: r,
|
|
738
|
+
f: [domUtils.getParentElement(selectedFormats[i], 'LI')]
|
|
739
|
+
};
|
|
740
|
+
} else {
|
|
741
|
+
rangeArr.f.push(domUtils.getParentElement(selectedFormats[i], 'LI'));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (i === len - 1) {
|
|
746
|
+
if (nested && domUtils.isListCell(o.parentNode)) {
|
|
747
|
+
this._detachNested(rangeArr.f);
|
|
748
|
+
} else {
|
|
749
|
+
afterRange = this.removeBlock(rangeArr.f[0].parentNode, rangeArr.f, tempList, false, true);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
} else {
|
|
754
|
+
const topElParent = topEl ? topEl.parentNode : topEl;
|
|
755
|
+
const bottomElParent = bottomEl ? bottomEl.parentNode : bottomEl;
|
|
756
|
+
topEl = topElParent && !domUtils.isWysiwygFrame(topElParent) && topElParent.nodeName === listTag ? topElParent : topEl;
|
|
757
|
+
bottomEl = bottomElParent && !domUtils.isWysiwygFrame(bottomElParent) && bottomElParent.nodeName === listTag ? bottomElParent : bottomEl;
|
|
758
|
+
|
|
759
|
+
const mergeTop = topEl?.tagName === listTag;
|
|
760
|
+
const mergeBottom = bottomEl?.tagName === listTag;
|
|
761
|
+
|
|
762
|
+
let list = mergeTop ? topEl : domUtils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
|
|
763
|
+
let firstList = null;
|
|
764
|
+
let topNumber = null;
|
|
765
|
+
// let lastList = null;
|
|
766
|
+
// let bottomNumber = null;
|
|
767
|
+
|
|
768
|
+
const passComponent = (current) => {
|
|
769
|
+
return !this.component.is(current) && !domUtils.isList(current);
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
for (let i = 0, len = selectedFormats.length, newCell, fTag, isCell, next, originParent, nextParent, parentTag, siblingTag, rangeTag; i < len; i++) {
|
|
773
|
+
fTag = selectedFormats[i];
|
|
774
|
+
if (fTag.childNodes.length === 0 && !this._isIgnoreNodeChange(fTag)) {
|
|
775
|
+
domUtils.removeItem(fTag);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
next = selectedFormats[i + 1];
|
|
779
|
+
originParent = fTag.parentNode;
|
|
780
|
+
nextParent = next ? next.parentNode : null;
|
|
781
|
+
isCell = domUtils.isListCell(fTag);
|
|
782
|
+
rangeTag = this.isBlock(originParent) ? originParent : null;
|
|
783
|
+
parentTag = isCell && !domUtils.isWysiwygFrame(originParent) ? originParent.parentNode : originParent;
|
|
784
|
+
siblingTag = isCell && !domUtils.isWysiwygFrame(originParent) ? (!next || domUtils.isListCell(parentTag) ? originParent : originParent.nextSibling) : fTag.nextSibling;
|
|
785
|
+
|
|
786
|
+
newCell = domUtils.createElement('LI');
|
|
787
|
+
domUtils.copyFormatAttributes(newCell, fTag);
|
|
788
|
+
if (this.component.is(fTag)) {
|
|
789
|
+
const isHR = /^HR$/i.test(fTag.nodeName);
|
|
790
|
+
if (!isHR) newCell.innerHTML = '<br>';
|
|
791
|
+
newCell.innerHTML += fTag.outerHTML;
|
|
792
|
+
if (isHR) newCell.innerHTML += '<br>';
|
|
793
|
+
} else {
|
|
794
|
+
const fChildren = fTag.childNodes;
|
|
795
|
+
while (fChildren[0]) {
|
|
796
|
+
newCell.appendChild(fChildren[0]);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
list.appendChild(newCell);
|
|
800
|
+
|
|
801
|
+
// if (!next) lastList = list;
|
|
802
|
+
if (!next || parentTag !== nextParent || this.isBlock(siblingTag)) {
|
|
803
|
+
if (!firstList) firstList = list;
|
|
804
|
+
if ((!mergeTop || !next || parentTag !== nextParent) && !(next && domUtils.isList(nextParent) && nextParent === originParent)) {
|
|
805
|
+
if (list.parentNode !== parentTag) parentTag.insertBefore(list, siblingTag);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
domUtils.removeItem(fTag);
|
|
810
|
+
if (mergeTop && topNumber === null) topNumber = list.children.length - 1;
|
|
811
|
+
if (
|
|
812
|
+
next &&
|
|
813
|
+
(this.getBlock(nextParent, passComponent) !== this.getBlock(originParent, passComponent) ||
|
|
814
|
+
(domUtils.isList(nextParent) && domUtils.isList(originParent) && domUtils.getNodeDepth(nextParent) !== domUtils.getNodeDepth(originParent)))
|
|
815
|
+
) {
|
|
816
|
+
list = domUtils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (rangeTag?.children.length === 0) domUtils.removeItem(rangeTag);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (topNumber) {
|
|
823
|
+
firstList = firstList.children[topNumber];
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (mergeBottom) {
|
|
827
|
+
// bottomNumber = list.children.length - 1;
|
|
828
|
+
list.innerHTML += bottomEl.innerHTML;
|
|
829
|
+
// lastList = list.children[bottomNumber] || lastList;
|
|
830
|
+
domUtils.removeItem(bottomEl);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
this.editor.effectNode = null;
|
|
835
|
+
return !isRemove || !isCollapsed ? originRange : afterRange;
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* @description "selectedCells" array are detached from the list element.
|
|
840
|
+
* The return value is applied when the first and last lines of "selectedFormats" are "LI" respectively.
|
|
841
|
+
* @param {Array} selectedCells Array of format elements (LI, P...) to remove.
|
|
842
|
+
* @param {boolean} remove If true, It does not just remove the list, it deletes the content.
|
|
843
|
+
* @returns {Object} {sc: <LI>, ec: <LI>}.
|
|
844
|
+
*/
|
|
845
|
+
removeList(selectedCells, remove) {
|
|
846
|
+
let rangeArr = {};
|
|
847
|
+
let listFirst = false;
|
|
848
|
+
let listLast = false;
|
|
849
|
+
let first = null;
|
|
850
|
+
let last = null;
|
|
851
|
+
const passComponent = (current) => {
|
|
852
|
+
return !this.component.is(current);
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
for (let i = 0, len = selectedCells.length, r, o, lastIndex, isList; i < len; i++) {
|
|
856
|
+
lastIndex = i === len - 1;
|
|
857
|
+
o = this.getBlock(selectedCells[i], passComponent);
|
|
858
|
+
isList = domUtils.isList(o);
|
|
859
|
+
if (!r && isList) {
|
|
860
|
+
r = o;
|
|
861
|
+
rangeArr = {
|
|
862
|
+
r: r,
|
|
863
|
+
f: [domUtils.getParentElement(selectedCells[i], 'LI')]
|
|
864
|
+
};
|
|
865
|
+
if (i === 0) listFirst = true;
|
|
866
|
+
} else if (r && isList) {
|
|
867
|
+
if (r !== o) {
|
|
868
|
+
const edge = this.detachRangeFormatElement(rangeArr.f[0].parentNode, rangeArr.f, null, remove, true);
|
|
869
|
+
o = selectedCells[i].parentNode;
|
|
870
|
+
if (listFirst) {
|
|
871
|
+
first = edge.sc;
|
|
872
|
+
listFirst = false;
|
|
873
|
+
}
|
|
874
|
+
if (lastIndex) last = edge.ec;
|
|
875
|
+
|
|
876
|
+
if (isList) {
|
|
877
|
+
r = o;
|
|
878
|
+
rangeArr = {
|
|
879
|
+
r: r,
|
|
880
|
+
f: [domUtils.getParentElement(selectedCells[i], 'LI')]
|
|
881
|
+
};
|
|
882
|
+
if (lastIndex) listLast = true;
|
|
883
|
+
} else {
|
|
884
|
+
r = null;
|
|
885
|
+
}
|
|
886
|
+
} else {
|
|
887
|
+
rangeArr.f.push(domUtils.getParentElement(selectedCells[i], 'LI'));
|
|
888
|
+
if (lastIndex) listLast = true;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (lastIndex && domUtils.isList(r)) {
|
|
893
|
+
const edge = this.detachRangeFormatElement(rangeArr.f[0].parentNode, rangeArr.f, null, remove, true);
|
|
894
|
+
if (listLast || len === 1) last = edge.ec;
|
|
895
|
+
if (listFirst) first = edge.sc || last;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
sc: first,
|
|
901
|
+
ec: last
|
|
902
|
+
};
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* @description Indent more the selected lines.
|
|
907
|
+
* margin size - 'status.indentSize'px
|
|
908
|
+
*/
|
|
909
|
+
indent() {
|
|
910
|
+
const range = this.selection.getRange();
|
|
911
|
+
const sc = range.startContainer;
|
|
912
|
+
const ec = range.endContainer;
|
|
913
|
+
const so = range.startOffset;
|
|
914
|
+
const eo = range.endOffset;
|
|
915
|
+
|
|
916
|
+
const lines = this.getLines(null);
|
|
917
|
+
const cells = SetLineMargin(lines, this.status.indentSize, this.options.get('_rtl') ? 'marginRight' : 'marginLeft');
|
|
918
|
+
|
|
919
|
+
// list cells
|
|
920
|
+
if (cells.length > 0) {
|
|
921
|
+
this._applyNestedList(cells, true);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
this.editor.effectNode = null;
|
|
925
|
+
this.selection.setRange(sc, so, ec, eo);
|
|
926
|
+
this.history.push(false);
|
|
927
|
+
},
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* @description Indent less the selected lines.
|
|
931
|
+
* margin size - "status.indentSize"px
|
|
932
|
+
*/
|
|
933
|
+
outdent() {
|
|
934
|
+
const range = this.selection.getRange();
|
|
935
|
+
const sc = range.startContainer;
|
|
936
|
+
const ec = range.endContainer;
|
|
937
|
+
const so = range.startOffset;
|
|
938
|
+
const eo = range.endOffset;
|
|
939
|
+
|
|
940
|
+
const lines = this.getLines(null);
|
|
941
|
+
const cells = SetLineMargin(lines, this.status.indentSize * -1, this.options.get('_rtl') ? 'marginRight' : 'marginLeft');
|
|
942
|
+
|
|
943
|
+
// list cells
|
|
944
|
+
if (cells.length > 0) {
|
|
945
|
+
this._applyNestedList(cells, false);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
this.editor.effectNode = null;
|
|
949
|
+
this.selection.setRange(sc, so, ec, eo);
|
|
950
|
+
this.history.push(false);
|
|
951
|
+
},
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* @description Add, update, and delete style node from selected text. (a, span, strong, ect.)
|
|
955
|
+
* 1. If there is a node in the "styleNode" argument, a node with the same tags and attributes as "styleNode" is added to the selection text.
|
|
956
|
+
* 2. If it is in the same tag, only the tag's attributes are changed without adding a tag.
|
|
957
|
+
* 3. If the "styleNode" argument is null, the node of the selection is update or remove without adding a new node.
|
|
958
|
+
* 4. The same style as the style attribute of the "styleArray" argument is deleted.
|
|
959
|
+
* (Styles should be put with attribute names from css. ["background-color"])
|
|
960
|
+
* 5. The same class name as the class attribute of the "styleArray" argument is deleted.
|
|
961
|
+
* (The class name is preceded by "." [".className"])
|
|
962
|
+
* 6. Use a list of styles and classes of "styleNode" in "styleArray" to avoid duplicate property values.
|
|
963
|
+
* 7. If a node with all styles and classes removed has the same tag name as "styleNode" or "removeNodeArray", or "styleNode" is null, that node is deleted.
|
|
964
|
+
* 8. Regardless of the style and class of the node, the tag with the same name as the "removeNodeArray" argument value is deleted.
|
|
965
|
+
* 9. If the "strictRemove" argument is true, only nodes with all styles and classes removed from the nodes of "removeNodeArray" are removed.
|
|
966
|
+
*10. It won't work if the parent node has the same class and same value style.
|
|
967
|
+
* However, if there is a value in "removeNodeArray", it works and the text node is separated even if there is no node to replace.
|
|
968
|
+
* @param {Element|null} styleNode The element to be added to the selection. If it is null, only delete the node.
|
|
969
|
+
* @param {Array|null} styleArray The style or className attribute name Array to check (['font-size'], ['.className'], ['font-family', 'color', '.className']...])
|
|
970
|
+
* @param {Array|null} removeNodeArray An array of node names to remove types from, remove all formats when "styleNode" is null and there is an empty array or null value. (['span'], ['strong', 'em'] ...])
|
|
971
|
+
* @param {Boolean|null} strictRemove If true, only nodes with all styles and classes removed from the nodes of "removeNodeArray" are removed.
|
|
972
|
+
*/
|
|
973
|
+
applyTextStyle(styleNode, styleArray, removeNodeArray, strictRemove) {
|
|
974
|
+
if (domUtils.getParentElement(this.selection.getNode(), domUtils.isNonEditable)) return;
|
|
975
|
+
|
|
976
|
+
this.selection._resetRangeToTextNode();
|
|
977
|
+
let range = this.selection.getRangeAndAddLine(this.selection.getRange(), null);
|
|
978
|
+
styleArray = styleArray?.length > 0 ? styleArray : false;
|
|
979
|
+
removeNodeArray = removeNodeArray?.length > 0 ? removeNodeArray : false;
|
|
980
|
+
|
|
981
|
+
const isRemoveNode = !styleNode;
|
|
982
|
+
const isRemoveFormat = isRemoveNode && !removeNodeArray && !styleArray;
|
|
983
|
+
let startCon = range.startContainer;
|
|
984
|
+
let startOff = range.startOffset;
|
|
985
|
+
let endCon = range.endContainer;
|
|
986
|
+
let endOff = range.endOffset;
|
|
987
|
+
|
|
988
|
+
if ((isRemoveFormat && range.collapsed && this.isLine(startCon.parentNode) && this.isLine(endCon.parentNode)) || (startCon === endCon && startCon.nodeType === 1 && domUtils.isNonEditable(startCon))) {
|
|
989
|
+
const format = startCon.parentNode;
|
|
990
|
+
if (
|
|
991
|
+
!domUtils.isListCell(format) ||
|
|
992
|
+
!env.getValues(format.style).some((k) => {
|
|
993
|
+
return this._listKebab.includes(k);
|
|
994
|
+
})
|
|
995
|
+
)
|
|
996
|
+
return;
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (range.collapsed && !isRemoveFormat) {
|
|
1001
|
+
if (startCon.nodeType === 1 && !domUtils.isBreak(startCon) && !this.component.is(startCon)) {
|
|
1002
|
+
let afterNode = null;
|
|
1003
|
+
const focusNode = startCon.childNodes[startOff];
|
|
1004
|
+
|
|
1005
|
+
if (focusNode) {
|
|
1006
|
+
if (!focusNode.nextSibling) {
|
|
1007
|
+
afterNode = null;
|
|
1008
|
+
} else {
|
|
1009
|
+
afterNode = domUtils.isBreak(focusNode) ? focusNode : focusNode.nextSibling;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
|
|
1014
|
+
startCon.insertBefore(zeroWidth, afterNode);
|
|
1015
|
+
this.selection.setRange(zeroWidth, 1, zeroWidth, 1);
|
|
1016
|
+
|
|
1017
|
+
range = this.selection.getRange();
|
|
1018
|
+
startCon = range.startContainer;
|
|
1019
|
+
startOff = range.startOffset;
|
|
1020
|
+
endCon = range.endContainer;
|
|
1021
|
+
endOff = range.endOffset;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (this.isLine(startCon)) {
|
|
1026
|
+
startCon = startCon.childNodes[startOff] || startCon.firstChild;
|
|
1027
|
+
startOff = 0;
|
|
1028
|
+
}
|
|
1029
|
+
if (this.isLine(endCon)) {
|
|
1030
|
+
endCon = endCon.childNodes[endOff] || endCon.lastChild;
|
|
1031
|
+
endOff = endCon.textContent.length;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (isRemoveNode) {
|
|
1035
|
+
styleNode = domUtils.createElement('DIV');
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const wRegExp = RegExp;
|
|
1039
|
+
const newNodeName = styleNode.nodeName;
|
|
1040
|
+
|
|
1041
|
+
/* checked same style property */
|
|
1042
|
+
if (!isRemoveFormat && startCon === endCon && !removeNodeArray && styleNode) {
|
|
1043
|
+
let sNode = startCon;
|
|
1044
|
+
let checkCnt = 0;
|
|
1045
|
+
const checkAttrs = [];
|
|
1046
|
+
|
|
1047
|
+
const checkStyles = styleNode.style;
|
|
1048
|
+
for (let i = 0, len = checkStyles.length; i < len; i++) {
|
|
1049
|
+
checkAttrs.push(checkStyles[i]);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const ckeckClasses = styleNode.classList;
|
|
1053
|
+
for (let i = 0, len = ckeckClasses.length; i < len; i++) {
|
|
1054
|
+
checkAttrs.push('.' + ckeckClasses[i]);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
if (checkAttrs.length > 0) {
|
|
1058
|
+
while (!this.isLine(sNode) && !domUtils.isWysiwygFrame(sNode)) {
|
|
1059
|
+
for (let i = 0; i < checkAttrs.length; i++) {
|
|
1060
|
+
if (sNode.nodeType === 1) {
|
|
1061
|
+
const s = checkAttrs[i];
|
|
1062
|
+
const classReg = /^\./.test(s) ? new wRegExp('\\s*' + s.replace(/^\./, '') + '(\\s+|$)', 'ig') : false;
|
|
1063
|
+
|
|
1064
|
+
const styleCheck = isRemoveNode ? !!sNode.style[s] : !!sNode.style[s] && !!styleNode.style[s] && sNode.style[s] === styleNode.style[s];
|
|
1065
|
+
const classCheck = classReg === false ? false : isRemoveNode ? !!sNode.className.match(classReg) : !!sNode.className.match(classReg) && !!styleNode.className.match(classReg);
|
|
1066
|
+
if (styleCheck || classCheck) {
|
|
1067
|
+
checkCnt++;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
sNode = sNode.parentNode;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (checkCnt >= checkAttrs.length) return;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
let start = {},
|
|
1079
|
+
end = {};
|
|
1080
|
+
let newNode,
|
|
1081
|
+
styleRegExp = '',
|
|
1082
|
+
classRegExp = '',
|
|
1083
|
+
removeNodeRegExp = null;
|
|
1084
|
+
|
|
1085
|
+
if (styleArray) {
|
|
1086
|
+
for (let i = 0, len = styleArray.length, s; i < len; i++) {
|
|
1087
|
+
s = styleArray[i];
|
|
1088
|
+
if (/^\./.test(s)) {
|
|
1089
|
+
classRegExp += (classRegExp ? '|' : '\\s*(?:') + s.replace(/^\./, '');
|
|
1090
|
+
} else {
|
|
1091
|
+
styleRegExp += (styleRegExp ? '|' : '(?:;|^|\\s)(?:') + s;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (styleRegExp) {
|
|
1096
|
+
styleRegExp += ')\\s*:[^;]*\\s*(?:;|$)';
|
|
1097
|
+
styleRegExp = new wRegExp(styleRegExp, 'ig');
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if (classRegExp) {
|
|
1101
|
+
classRegExp += ')(?=\\s+|$)';
|
|
1102
|
+
classRegExp = new wRegExp(classRegExp, 'ig');
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (removeNodeArray) {
|
|
1107
|
+
removeNodeRegExp = '^(?:' + removeNodeArray[0];
|
|
1108
|
+
for (let i = 1; i < removeNodeArray.length; i++) {
|
|
1109
|
+
removeNodeRegExp += '|' + removeNodeArray[i];
|
|
1110
|
+
}
|
|
1111
|
+
removeNodeRegExp += ')$';
|
|
1112
|
+
removeNodeRegExp = new wRegExp(removeNodeRegExp, 'i');
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/** validation check function*/
|
|
1116
|
+
const wBoolean = Boolean;
|
|
1117
|
+
const _removeCheck = {
|
|
1118
|
+
v: false
|
|
1119
|
+
};
|
|
1120
|
+
const validation = function (checkNode) {
|
|
1121
|
+
const vNode = checkNode.cloneNode(false);
|
|
1122
|
+
|
|
1123
|
+
// all path
|
|
1124
|
+
if (vNode.nodeType === 3 || domUtils.isBreak(vNode)) return vNode;
|
|
1125
|
+
// all remove
|
|
1126
|
+
if (isRemoveFormat) return null;
|
|
1127
|
+
|
|
1128
|
+
// remove node check
|
|
1129
|
+
const tagRemove = (!removeNodeRegExp && isRemoveNode) || removeNodeRegExp?.test(vNode.nodeName);
|
|
1130
|
+
|
|
1131
|
+
// tag remove
|
|
1132
|
+
if (tagRemove && !strictRemove) {
|
|
1133
|
+
_removeCheck.v = true;
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// style regexp
|
|
1138
|
+
const originStyle = vNode.style.cssText;
|
|
1139
|
+
let style = '';
|
|
1140
|
+
if (styleRegExp && originStyle.length > 0) {
|
|
1141
|
+
style = originStyle.replace(styleRegExp, '').trim();
|
|
1142
|
+
if (style !== originStyle) _removeCheck.v = true;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// class check
|
|
1146
|
+
const originClasses = vNode.className;
|
|
1147
|
+
let classes = '';
|
|
1148
|
+
if (classRegExp && originClasses.length > 0) {
|
|
1149
|
+
classes = originClasses.replace(classRegExp, '').trim();
|
|
1150
|
+
if (classes !== originClasses) _removeCheck.v = true;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// remove only
|
|
1154
|
+
if (isRemoveNode) {
|
|
1155
|
+
if ((classRegExp || !originClasses) && (styleRegExp || !originStyle) && !style && !classes && tagRemove) {
|
|
1156
|
+
_removeCheck.v = true;
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// change
|
|
1162
|
+
if (style || classes || vNode.nodeName !== newNodeName || wBoolean(styleRegExp) !== wBoolean(originStyle) || wBoolean(classRegExp) !== wBoolean(originClasses)) {
|
|
1163
|
+
if (styleRegExp && originStyle.length > 0) vNode.style.cssText = style;
|
|
1164
|
+
if (!vNode.style.cssText) {
|
|
1165
|
+
vNode.removeAttribute('style');
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
if (classRegExp && originClasses.length > 0) vNode.className = classes.trim();
|
|
1169
|
+
if (!vNode.className.trim()) {
|
|
1170
|
+
vNode.removeAttribute('class');
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (!vNode.style.cssText && !vNode.className && (vNode.nodeName === newNodeName || tagRemove)) {
|
|
1174
|
+
_removeCheck.v = true;
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
return vNode;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
_removeCheck.v = true;
|
|
1182
|
+
return null;
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
// get line nodes
|
|
1186
|
+
const lineNodes = this.getLines(null);
|
|
1187
|
+
range = this.selection.getRange();
|
|
1188
|
+
startCon = range.startContainer;
|
|
1189
|
+
startOff = range.startOffset;
|
|
1190
|
+
endCon = range.endContainer;
|
|
1191
|
+
endOff = range.endOffset;
|
|
1192
|
+
|
|
1193
|
+
if (!this.getLine(startCon, null)) {
|
|
1194
|
+
startCon = domUtils.getEdgeChild(
|
|
1195
|
+
lineNodes[0],
|
|
1196
|
+
function (current) {
|
|
1197
|
+
return current.nodeType === 3;
|
|
1198
|
+
},
|
|
1199
|
+
false
|
|
1200
|
+
);
|
|
1201
|
+
startOff = 0;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
if (!this.getLine(endCon, null)) {
|
|
1205
|
+
endCon = domUtils.getEdgeChild(
|
|
1206
|
+
lineNodes[lineNodes.length - 1],
|
|
1207
|
+
function (current) {
|
|
1208
|
+
return current.nodeType === 3;
|
|
1209
|
+
},
|
|
1210
|
+
false
|
|
1211
|
+
);
|
|
1212
|
+
endOff = endCon.textContent.length;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const oneLine = this.getLine(startCon, null) === this.getLine(endCon, null);
|
|
1216
|
+
const endLength = lineNodes.length - (oneLine ? 0 : 1);
|
|
1217
|
+
|
|
1218
|
+
// node Changes
|
|
1219
|
+
newNode = styleNode.cloneNode(false);
|
|
1220
|
+
|
|
1221
|
+
const isRemoveAnchor =
|
|
1222
|
+
isRemoveFormat ||
|
|
1223
|
+
(isRemoveNode &&
|
|
1224
|
+
(function (inst, arr) {
|
|
1225
|
+
for (let n = 0, len = arr.length; n < len; n++) {
|
|
1226
|
+
if (inst._isNonSplitNode(arr[n]) || inst._sn_isSizeNode(arr[n])) return true;
|
|
1227
|
+
}
|
|
1228
|
+
return false;
|
|
1229
|
+
})(this, removeNodeArray));
|
|
1230
|
+
|
|
1231
|
+
const isSizeNode = isRemoveNode || this._sn_isSizeNode(newNode);
|
|
1232
|
+
const _getMaintainedNode = this._sn_getMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
|
|
1233
|
+
const _isMaintainedNode = this._sn_isMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
|
|
1234
|
+
|
|
1235
|
+
// one line
|
|
1236
|
+
if (oneLine) {
|
|
1237
|
+
if (this._sn_resetCommonListCell(lineNodes[0], styleArray)) range = this.selection.setRange(startCon, startOff, endCon, endOff);
|
|
1238
|
+
|
|
1239
|
+
const newRange = this._setNode_oneLine(lineNodes[0], newNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, range.collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode);
|
|
1240
|
+
start.container = newRange.startContainer;
|
|
1241
|
+
start.offset = newRange.startOffset;
|
|
1242
|
+
end.container = newRange.endContainer;
|
|
1243
|
+
end.offset = newRange.endOffset;
|
|
1244
|
+
|
|
1245
|
+
if (start.container === end.container && domUtils.isZeroWith(start.container)) {
|
|
1246
|
+
start.offset = end.offset = 1;
|
|
1247
|
+
}
|
|
1248
|
+
this._sn_setCommonListStyle(newRange.ancestor, null);
|
|
1249
|
+
} else {
|
|
1250
|
+
// multi line
|
|
1251
|
+
let appliedCommonList = false;
|
|
1252
|
+
if (endLength > 0 && this._sn_resetCommonListCell(lineNodes[endLength], styleArray)) appliedCommonList = true;
|
|
1253
|
+
if (this._sn_resetCommonListCell(lineNodes[0], styleArray)) appliedCommonList = true;
|
|
1254
|
+
if (appliedCommonList) this.selection.setRange(startCon, startOff, endCon, endOff);
|
|
1255
|
+
|
|
1256
|
+
// end
|
|
1257
|
+
if (endLength > 0) {
|
|
1258
|
+
newNode = styleNode.cloneNode(false);
|
|
1259
|
+
end = this._setNode_endLine(lineNodes[endLength], newNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// mid
|
|
1263
|
+
for (let i = endLength - 1, newRange; i > 0; i--) {
|
|
1264
|
+
this._sn_resetCommonListCell(lineNodes[i], styleArray);
|
|
1265
|
+
newNode = styleNode.cloneNode(false);
|
|
1266
|
+
newRange = this._setNode_middleLine(lineNodes[i], newNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, end.container);
|
|
1267
|
+
if (newRange.endContainer && newRange.ancestor.contains(newRange.endContainer)) {
|
|
1268
|
+
end.ancestor = null;
|
|
1269
|
+
end.container = newRange.endContainer;
|
|
1270
|
+
}
|
|
1271
|
+
this._sn_setCommonListStyle(newRange.ancestor, null);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// start
|
|
1275
|
+
newNode = styleNode.cloneNode(false);
|
|
1276
|
+
start = this._setNode_startLine(lineNodes[0], newNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, end.container);
|
|
1277
|
+
|
|
1278
|
+
if (start.endContainer) {
|
|
1279
|
+
end.ancestor = null;
|
|
1280
|
+
end.container = start.endContainer;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
if (endLength <= 0) {
|
|
1284
|
+
end = start;
|
|
1285
|
+
} else if (!end.container) {
|
|
1286
|
+
end.ancestor = null;
|
|
1287
|
+
end.container = start.container;
|
|
1288
|
+
end.offset = start.container.textContent.length;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
this._sn_setCommonListStyle(start.ancestor, null);
|
|
1292
|
+
this._sn_setCommonListStyle(end.ancestor || this.getLine(end.container), null);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// set range
|
|
1296
|
+
this.editor._offCurrentController();
|
|
1297
|
+
this.selection.setRange(start.container, start.offset, end.container, end.offset);
|
|
1298
|
+
this.history.push(false);
|
|
1299
|
+
},
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* @description Remove format of the currently selected text.
|
|
1303
|
+
*/
|
|
1304
|
+
removeTextStyle() {
|
|
1305
|
+
this.applyTextStyle(null, null, null, null);
|
|
1306
|
+
},
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* @description Check if the container and offset values are the edges of the "line"
|
|
1310
|
+
* @param {Node} container The node of the selection object. (range.startContainer..)
|
|
1311
|
+
* @param {number} offset The offset of the selection object. (selection.getRange().startOffset...)
|
|
1312
|
+
* @param {string} dir Select check point - "front": Front edge, "end": End edge, undefined: Both edge.
|
|
1313
|
+
* @returns {boolean}
|
|
1314
|
+
*/
|
|
1315
|
+
isEdgeLine(node, offset, dir) {
|
|
1316
|
+
if (!domUtils.isEdgePoint(node, offset, dir)) return false;
|
|
1317
|
+
|
|
1318
|
+
const result = [];
|
|
1319
|
+
dir = dir === 'front' ? 'previousSibling' : 'nextSibling';
|
|
1320
|
+
while (node && !this.isLine(node) && !domUtils.isWysiwygFrame(node)) {
|
|
1321
|
+
if (!node[dir] || (domUtils.isBreak(node[dir]) && !node[dir][dir])) {
|
|
1322
|
+
if (node.nodeType === 1) result.push(node.cloneNode(false));
|
|
1323
|
+
node = node.parentNode;
|
|
1324
|
+
} else {
|
|
1325
|
+
return null;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
return result;
|
|
1330
|
+
},
|
|
1331
|
+
|
|
1332
|
+
/**
|
|
1333
|
+
* @description It is judged whether it is a node related to the text style.
|
|
1334
|
+
* @param {Node|string} element The node to check
|
|
1335
|
+
* @returns {boolean}
|
|
1336
|
+
*/
|
|
1337
|
+
isTextStyleNode(element) {
|
|
1338
|
+
return typeof element === 'string' ? this._textStyleTagsCheck.test(element) : element && element.nodeType === 1 && this._textStyleTagsCheck.test(element.nodeName);
|
|
1339
|
+
},
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* @description It is judged whether it is the format element (P, DIV, H[1-6], PRE, LI | class="__se__format__line_xxx")
|
|
1343
|
+
* Format element also contain "free format Element"
|
|
1344
|
+
* @param {Node|string} element The node to check
|
|
1345
|
+
* @returns {boolean}
|
|
1346
|
+
*/
|
|
1347
|
+
isLine(element) {
|
|
1348
|
+
return typeof element === 'string'
|
|
1349
|
+
? this._formatLineCheck.test(element)
|
|
1350
|
+
: element && element.nodeType === 1 && (this._formatLineCheck.test(element.nodeName) || domUtils.hasClass(element, '__se__format__line_.+|__se__format__br_line_.+')) && !this._nonFormat(element);
|
|
1351
|
+
},
|
|
1352
|
+
|
|
1353
|
+
/**
|
|
1354
|
+
* @description It is judged whether it is the only format element, not block (td, th, li)
|
|
1355
|
+
* Format element also contain "free format Element"
|
|
1356
|
+
* @param {Node|string} element The node to check
|
|
1357
|
+
* @returns {boolean}
|
|
1358
|
+
*/
|
|
1359
|
+
isLineOnly(element) {
|
|
1360
|
+
return this._formatLineCheck.test(element) && !this._formatBlockCheck.test(element);
|
|
1361
|
+
},
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* @description It is judged whether it is the free format element. (PRE | class="__se__format__br_line_xxx")
|
|
1365
|
+
* Free format elements is included in the format element.
|
|
1366
|
+
* Free format elements's line break is "BR" tag.
|
|
1367
|
+
* ※ Entering the Enter key in the space on the last line ends "Free Format" and appends "Format".
|
|
1368
|
+
* @param {Node|string} element The node to check
|
|
1369
|
+
* @returns {boolean}
|
|
1370
|
+
*/
|
|
1371
|
+
isBrLine(element) {
|
|
1372
|
+
return typeof element === 'string'
|
|
1373
|
+
? this._formatBrLineCheck.test(element)
|
|
1374
|
+
: element && element.nodeType === 1 && (this._formatBrLineCheck.test(element.nodeName) || domUtils.hasClass(element, '__se__format__br_line_.+')) && !this._nonFormat(element);
|
|
1375
|
+
},
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* @description It is judged whether it is the range format element. (BLOCKQUOTE, OL, UL, FIGCAPTION, TABLE, THEAD, TBODY, TR, TH, TD | class="__se__format__block_xxx")
|
|
1379
|
+
* Range format element is wrap the "format element" and "component"
|
|
1380
|
+
* @param {Node|string} element The node to check
|
|
1381
|
+
* @returns {boolean}
|
|
1382
|
+
*/
|
|
1383
|
+
isBlock(element) {
|
|
1384
|
+
return typeof element === 'string'
|
|
1385
|
+
? this._formatBlockCheck.test(element)
|
|
1386
|
+
: element && element.nodeType === 1 && (this._formatBlockCheck.test(element.nodeName) || domUtils.hasClass(element, '__se__format__block_.+')) && !this._nonFormat(element);
|
|
1387
|
+
},
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* @description It is judged whether it is the closure range format element. (TH, TD | class="__se__format__block_closure_xxx")
|
|
1391
|
+
* Closure range format elements is included in the range format element.
|
|
1392
|
+
* - Closure range format element is wrap the "format element" and "component"
|
|
1393
|
+
* ※ You cannot exit this format with the Enter key or Backspace key.
|
|
1394
|
+
* ※ Use it only in special cases. ([ex] format of table cells)
|
|
1395
|
+
* @param {Node|string} element The node to check
|
|
1396
|
+
* @returns {boolean}
|
|
1397
|
+
*/
|
|
1398
|
+
isClosureBlock(element) {
|
|
1399
|
+
return typeof element === 'string'
|
|
1400
|
+
? this._formatClosureBlockCheck.test(element)
|
|
1401
|
+
: element && element.nodeType === 1 && (this._formatClosureBlockCheck.test(element.nodeName) || domUtils.hasClass(element, '__se__format__block_closure_.+')) && !this._nonFormat(element);
|
|
1402
|
+
},
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* @description It is judged whether it is the closure free format element. (class="__se__format__br_line__closure_xxx")
|
|
1406
|
+
* Closure free format elements is included in the free format element.
|
|
1407
|
+
* - Closure free format elements's line break is "BR" tag.
|
|
1408
|
+
* ※ You cannot exit this format with the Enter key or Backspace key.
|
|
1409
|
+
* ※ Use it only in special cases. ([ex] format of table cells)
|
|
1410
|
+
* @param {Node|string} element The node to check
|
|
1411
|
+
* @returns {boolean}
|
|
1412
|
+
*/
|
|
1413
|
+
isClosureBrLine(element) {
|
|
1414
|
+
return typeof element === 'string'
|
|
1415
|
+
? this._formatClosureBrLineCheck.test(element)
|
|
1416
|
+
: element && element.nodeType === 1 && (this._formatClosureBrLineCheck.test(element.nodeName) || domUtils.hasClass(element, '__se__format__br_line__closure_.+')) && !this._nonFormat(element);
|
|
1417
|
+
},
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* @description Returns a "line" array from selected range.
|
|
1421
|
+
* @param {Function|null} validation The validation function. (Replaces the default validation format.isLine(current))
|
|
1422
|
+
* @returns {Array}
|
|
1423
|
+
*/
|
|
1424
|
+
getLines(validation) {
|
|
1425
|
+
if (!this.selection._resetRangeToTextNode()) return [];
|
|
1426
|
+
let range = this.selection.getRange();
|
|
1427
|
+
|
|
1428
|
+
if (domUtils.isWysiwygFrame(range.startContainer)) {
|
|
1429
|
+
const children = this.editor.frameContext.get('wysiwyg').children;
|
|
1430
|
+
if (children.length === 0) return [];
|
|
1431
|
+
|
|
1432
|
+
this.selection.setRange(children[0], 0, children[children.length - 1], children[children.length - 1].textContent.trim().length);
|
|
1433
|
+
range = this.selection.getRange();
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
const startCon = range.startContainer;
|
|
1437
|
+
const endCon = range.endContainer;
|
|
1438
|
+
const commonCon = range.commonAncestorContainer;
|
|
1439
|
+
|
|
1440
|
+
// get line nodes
|
|
1441
|
+
const lineNodes = domUtils.getListChildren(commonCon, (current) => {
|
|
1442
|
+
return validation ? validation(current) : this.isLine(current);
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
if (!domUtils.isWysiwygFrame(commonCon) && !this.isBlock(commonCon)) lineNodes.unshift(this.getLine(commonCon, null));
|
|
1446
|
+
if (startCon === endCon || lineNodes.length === 1) return lineNodes;
|
|
1447
|
+
|
|
1448
|
+
const startLine = this.getLine(startCon, null);
|
|
1449
|
+
const endLine = this.getLine(endCon, null);
|
|
1450
|
+
let startIdx = null;
|
|
1451
|
+
let endIdx = null;
|
|
1452
|
+
|
|
1453
|
+
const onlyTable = function (current) {
|
|
1454
|
+
return domUtils.isTableElements(current) ? /^TABLE$/i.test(current.nodeName) : true;
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1457
|
+
let startRangeEl = this.getBlock(startLine, onlyTable);
|
|
1458
|
+
let endRangeEl = this.getBlock(endLine, onlyTable);
|
|
1459
|
+
if (domUtils.isTableElements(startRangeEl) && domUtils.isListCell(startRangeEl.parentNode)) startRangeEl = startRangeEl.parentNode;
|
|
1460
|
+
if (domUtils.isTableElements(endRangeEl) && domUtils.isListCell(endRangeEl.parentNode)) endRangeEl = endRangeEl.parentNode;
|
|
1461
|
+
|
|
1462
|
+
const sameRange = startRangeEl === endRangeEl;
|
|
1463
|
+
for (let i = 0, len = lineNodes.length, line; i < len; i++) {
|
|
1464
|
+
line = lineNodes[i];
|
|
1465
|
+
|
|
1466
|
+
if (startLine === line || (!sameRange && line === startRangeEl)) {
|
|
1467
|
+
startIdx = i;
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (endLine === line || (!sameRange && line === endRangeEl)) {
|
|
1472
|
+
endIdx = i;
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if (startIdx === null) startIdx = 0;
|
|
1478
|
+
if (endIdx === null) endIdx = lineNodes.length - 1;
|
|
1479
|
+
|
|
1480
|
+
return lineNodes.slice(startIdx, endIdx + 1);
|
|
1481
|
+
},
|
|
1482
|
+
|
|
1483
|
+
/**
|
|
1484
|
+
* @description Get lines and components from the selected range. (P, DIV, H[1-6], OL, UL, TABLE..)
|
|
1485
|
+
* If some of the component are included in the selection, get the entire that component.
|
|
1486
|
+
* @param {boolean} removeDuplicate If true, if there is a parent and child tag among the selected elements, the child tag is excluded.
|
|
1487
|
+
* @returns {Array}
|
|
1488
|
+
*/
|
|
1489
|
+
getLinesAndComponents(removeDuplicate) {
|
|
1490
|
+
const commonCon = this.selection.getRange().commonAncestorContainer;
|
|
1491
|
+
const myComponent = domUtils.getParentElement(commonCon, this.component.is.bind(this.component));
|
|
1492
|
+
const selectedLines = domUtils.isTableElements(commonCon)
|
|
1493
|
+
? this.getLines(null)
|
|
1494
|
+
: this.getLines((current) => {
|
|
1495
|
+
const component = domUtils.getParentElement(current, this.component.is.bind(this.component));
|
|
1496
|
+
return (this.isLine(current) && (!component || component === myComponent)) || (this.component.is(current) && !this.getLine(current));
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
if (removeDuplicate) {
|
|
1500
|
+
for (let i = 0, len = selectedLines.length; i < len; i++) {
|
|
1501
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1502
|
+
if (selectedLines[j].contains(selectedLines[i])) {
|
|
1503
|
+
selectedLines.splice(i, 1);
|
|
1504
|
+
i--;
|
|
1505
|
+
len--;
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
return selectedLines;
|
|
1513
|
+
},
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* @description A function that distinguishes areas where "selection" should not be placed
|
|
1517
|
+
* @param {Element} element Element
|
|
1518
|
+
* @returns {boolean}
|
|
1519
|
+
* @private
|
|
1520
|
+
*/
|
|
1521
|
+
_isExcludeSelectionElement(element) {
|
|
1522
|
+
return !/FIGCAPTION/i.test(element.nodeName) && (this.component.is(element) || /FIGURE/i.test(element.nodeName));
|
|
1523
|
+
},
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* @description A function that distinguishes non-formatting HTML elements or tags from formatting ones.
|
|
1527
|
+
* @param {Element} element Element
|
|
1528
|
+
* @returns {boolean}
|
|
1529
|
+
* @private
|
|
1530
|
+
*/
|
|
1531
|
+
_nonFormat(element) {
|
|
1532
|
+
return domUtils.isExcludeFormat(element) || this.component.is(element) || domUtils.isWysiwygFrame(element);
|
|
1533
|
+
},
|
|
1534
|
+
|
|
1535
|
+
/**
|
|
1536
|
+
* @description Nodes that must remain undetached when changing text nodes (A, Label, Code, Span:font-size)
|
|
1537
|
+
* @param {Node|String} element Element to check
|
|
1538
|
+
* @returns {boolean}
|
|
1539
|
+
* @private
|
|
1540
|
+
*/
|
|
1541
|
+
_isNonSplitNode(element) {
|
|
1542
|
+
return element && element.nodeType !== 3 && /^(a|label|code|summary)$/i.test(typeof element === 'string' ? element : element.nodeName);
|
|
1543
|
+
},
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* @description Nodes without text
|
|
1547
|
+
* @param {Node} element Element to check
|
|
1548
|
+
* @returns {boolean}
|
|
1549
|
+
* @private
|
|
1550
|
+
*/
|
|
1551
|
+
_notTextNode(element) {
|
|
1552
|
+
return element && element.nodeType !== 3 && (this.component.is(element) || /^(br|input|select|canvas|img|iframe|audio|video)$/i.test(typeof element === 'string' ? element : element.nodeName));
|
|
1553
|
+
},
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* @description Nodes that need to be added without modification when changing text nodes
|
|
1557
|
+
* @param {Node} element Element to check
|
|
1558
|
+
* @returns {boolean}
|
|
1559
|
+
* @private
|
|
1560
|
+
*/
|
|
1561
|
+
_isIgnoreNodeChange(element) {
|
|
1562
|
+
return element && element.nodeType !== 3 && (domUtils.isNonEditable(element) || !this.isTextStyleNode(element));
|
|
1563
|
+
},
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
* @description Get current selected lines and selected node info.
|
|
1567
|
+
* @returns { lines: Array.<Element>, firstNode: Node, lastNode: Node, firstPath: Array.<number>, lastPath: Array.<number>, startOffset: number, endOffset: number }
|
|
1568
|
+
* @private
|
|
1569
|
+
*/
|
|
1570
|
+
_lineWork() {
|
|
1571
|
+
let range = this.selection.getRange();
|
|
1572
|
+
let selectedFormsts = this.getLinesAndComponents(false);
|
|
1573
|
+
|
|
1574
|
+
if (selectedFormsts.length === 0) {
|
|
1575
|
+
range = this.selection.getRangeAndAddLine(range, null);
|
|
1576
|
+
selectedFormsts = this.getLinesAndComponents(false);
|
|
1577
|
+
if (selectedFormsts.length === 0) return;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const startOffset = range.startOffset;
|
|
1581
|
+
const endOffset = range.endOffset;
|
|
1582
|
+
|
|
1583
|
+
let first = selectedFormsts[0];
|
|
1584
|
+
let last = selectedFormsts[selectedFormsts.length - 1];
|
|
1585
|
+
const firstPath = domUtils.getNodePath(range.startContainer, first, null, null);
|
|
1586
|
+
const lastPath = domUtils.getNodePath(range.endContainer, last, null, null);
|
|
1587
|
+
|
|
1588
|
+
// remove selected list
|
|
1589
|
+
const rlist = this.removeList(selectedFormsts, false);
|
|
1590
|
+
if (rlist.sc) first = rlist.sc;
|
|
1591
|
+
if (rlist.ec) last = rlist.ec;
|
|
1592
|
+
|
|
1593
|
+
// change format tag
|
|
1594
|
+
this.selection.setRange(domUtils.getNodeFromPath(firstPath, first), startOffset, domUtils.getNodeFromPath(lastPath, last), endOffset);
|
|
1595
|
+
|
|
1596
|
+
return {
|
|
1597
|
+
lines: this.getLinesAndComponents(false),
|
|
1598
|
+
firstNode: first,
|
|
1599
|
+
lastNode: last,
|
|
1600
|
+
firstPath: firstPath,
|
|
1601
|
+
lastPath: lastPath,
|
|
1602
|
+
startOffset: startOffset,
|
|
1603
|
+
endOffset: endOffset
|
|
1604
|
+
};
|
|
1605
|
+
},
|
|
1606
|
+
|
|
1607
|
+
_attachNested(originList, innerList, prev, next, nodePath) {
|
|
1608
|
+
let insertPrev = false;
|
|
1609
|
+
|
|
1610
|
+
if (innerList.tagName === prev?.tagName) {
|
|
1611
|
+
const children = innerList.children;
|
|
1612
|
+
while (children[0]) {
|
|
1613
|
+
prev.appendChild(children[0]);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
innerList = prev;
|
|
1617
|
+
insertPrev = true;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
if (innerList.tagName === next?.tagName) {
|
|
1621
|
+
const children = next.children;
|
|
1622
|
+
while (children[0]) {
|
|
1623
|
+
innerList.appendChild(children[0]);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
const temp = next.nextElementSibling;
|
|
1627
|
+
next.parentNode.removeChild(next);
|
|
1628
|
+
next = temp;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
if (!insertPrev) {
|
|
1632
|
+
if (domUtils.isListCell(prev)) {
|
|
1633
|
+
originList = prev;
|
|
1634
|
+
next = null;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
originList.insertBefore(innerList, next);
|
|
1638
|
+
|
|
1639
|
+
if (!nodePath.s) {
|
|
1640
|
+
nodePath.s = domUtils.getNodePath(innerList.firstElementChild.firstChild, originList, null);
|
|
1641
|
+
nodePath.sl = originList;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const slPath = originList.contains(nodePath.sl) ? domUtils.getNodePath(nodePath.sl, originList) : null;
|
|
1645
|
+
nodePath.e = domUtils.getNodePath(innerList.lastElementChild.firstChild, originList, null);
|
|
1646
|
+
nodePath.el = originList;
|
|
1647
|
+
|
|
1648
|
+
this.nodeTransform.mergeSameTags(originList, [nodePath.s, nodePath.e, slPath], false);
|
|
1649
|
+
this.nodeTransform.mergeNestedTags(originList);
|
|
1650
|
+
if (slPath) nodePath.sl = domUtils.getNodeFromPath(slPath, originList);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
return innerList;
|
|
1654
|
+
},
|
|
1655
|
+
|
|
1656
|
+
_detachNested(cells) {
|
|
1657
|
+
const first = cells[0];
|
|
1658
|
+
const last = cells[cells.length - 1];
|
|
1659
|
+
const next = last.nextElementSibling;
|
|
1660
|
+
const originList = first.parentNode;
|
|
1661
|
+
const sibling = originList.parentNode.nextElementSibling;
|
|
1662
|
+
const parentNode = originList.parentNode.parentNode;
|
|
1663
|
+
|
|
1664
|
+
for (let c = 0, cLen = cells.length; c < cLen; c++) {
|
|
1665
|
+
parentNode.insertBefore(cells[c], sibling);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
if (next && originList.children.length > 0) {
|
|
1669
|
+
const newList = originList.cloneNode(false);
|
|
1670
|
+
const children = originList.childNodes;
|
|
1671
|
+
const index = domUtils.getPositionIndex(next);
|
|
1672
|
+
while (children[index]) {
|
|
1673
|
+
newList.appendChild(children[index]);
|
|
1674
|
+
}
|
|
1675
|
+
last.appendChild(newList);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
if (originList.children.length === 0) domUtils.removeItem(originList);
|
|
1679
|
+
this.nodeTransform.mergeSameTags(parentNode);
|
|
1680
|
+
|
|
1681
|
+
const edge = domUtils.getEdgeChildNodes(first, last);
|
|
1682
|
+
|
|
1683
|
+
return {
|
|
1684
|
+
cc: first.parentNode,
|
|
1685
|
+
sc: edge.sc,
|
|
1686
|
+
ec: edge.ec
|
|
1687
|
+
};
|
|
1688
|
+
},
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* @description Nest list cells or cancel nested cells.
|
|
1692
|
+
* @param selectedCells List cells.
|
|
1693
|
+
* @param nested Nested or cancel nested.
|
|
1694
|
+
* @private
|
|
1695
|
+
*/
|
|
1696
|
+
_applyNestedList(selectedCells, nested) {
|
|
1697
|
+
selectedCells = !selectedCells
|
|
1698
|
+
? this.getLines().filter(function (el) {
|
|
1699
|
+
return domUtils.isListCell(el);
|
|
1700
|
+
})
|
|
1701
|
+
: selectedCells;
|
|
1702
|
+
const cellsLen = selectedCells.length;
|
|
1703
|
+
if (cellsLen === 0 || (!nested && !domUtils.isListCell(selectedCells[0].previousElementSibling) && !domUtils.isListCell(selectedCells[cellsLen - 1].nextElementSibling))) {
|
|
1704
|
+
return {
|
|
1705
|
+
sc: selectedCells[0],
|
|
1706
|
+
so: 0,
|
|
1707
|
+
ec: selectedCells[cellsLen - 1],
|
|
1708
|
+
eo: 1
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
let originList = selectedCells[0].parentNode;
|
|
1713
|
+
let lastCell = selectedCells[cellsLen - 1];
|
|
1714
|
+
let range = null;
|
|
1715
|
+
|
|
1716
|
+
if (nested) {
|
|
1717
|
+
if (originList !== lastCell.parentNode && domUtils.isList(lastCell.parentNode.parentNode) && lastCell.nextElementSibling) {
|
|
1718
|
+
lastCell = lastCell.nextElementSibling;
|
|
1719
|
+
while (lastCell) {
|
|
1720
|
+
selectedCells.push(lastCell);
|
|
1721
|
+
lastCell = lastCell.nextElementSibling;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
range = this.applyList(originList.nodeName + ':' + originList.style.listStyleType, selectedCells, true);
|
|
1725
|
+
} else {
|
|
1726
|
+
let innerList = domUtils.createElement(originList.nodeName);
|
|
1727
|
+
let prev = selectedCells[0].previousElementSibling;
|
|
1728
|
+
let next = lastCell.nextElementSibling;
|
|
1729
|
+
const nodePath = {
|
|
1730
|
+
s: null,
|
|
1731
|
+
e: null,
|
|
1732
|
+
sl: originList,
|
|
1733
|
+
el: originList
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
for (let i = 0, len = cellsLen, c; i < len; i++) {
|
|
1737
|
+
c = selectedCells[i];
|
|
1738
|
+
if (c.parentNode !== originList) {
|
|
1739
|
+
this._attachNested(originList, innerList, prev, next, nodePath);
|
|
1740
|
+
originList = c.parentNode;
|
|
1741
|
+
innerList = domUtils.createElement(originList.nodeName);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
prev = c.previousElementSibling;
|
|
1745
|
+
next = c.nextElementSibling;
|
|
1746
|
+
innerList.appendChild(c);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
this._attachNested(originList, innerList, prev, next, nodePath);
|
|
1750
|
+
|
|
1751
|
+
const sc = domUtils.getNodeFromPath(nodePath.s, nodePath.sl);
|
|
1752
|
+
const ec = domUtils.getNodeFromPath(nodePath.e, nodePath.el);
|
|
1753
|
+
range = {
|
|
1754
|
+
sc: sc,
|
|
1755
|
+
so: 0,
|
|
1756
|
+
ec: ec,
|
|
1757
|
+
eo: ec.textContent.length
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
return range;
|
|
1762
|
+
},
|
|
1763
|
+
|
|
1764
|
+
/**
|
|
1765
|
+
* @description Detach Nested all nested lists under the "baseNode".
|
|
1766
|
+
* Returns a list with nested removed.
|
|
1767
|
+
* @param {Node} baseNode Element on which to base.
|
|
1768
|
+
* @param {boolean} all If true, it also detach all nested lists of a returned list.
|
|
1769
|
+
* @returns {Element}
|
|
1770
|
+
* @private
|
|
1771
|
+
*/
|
|
1772
|
+
_removeNestedList(baseNode, all) {
|
|
1773
|
+
const rNode = DeleteNestedList(baseNode);
|
|
1774
|
+
let rangeElement, cNodes;
|
|
1775
|
+
|
|
1776
|
+
if (rNode) {
|
|
1777
|
+
rangeElement = rNode.cloneNode(false);
|
|
1778
|
+
cNodes = rNode.childNodes;
|
|
1779
|
+
const index = domUtils.getPositionIndex(baseNode);
|
|
1780
|
+
while (cNodes[index]) {
|
|
1781
|
+
rangeElement.appendChild(cNodes[index]);
|
|
1782
|
+
}
|
|
1783
|
+
} else {
|
|
1784
|
+
rangeElement = baseNode;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
let rChildren;
|
|
1788
|
+
if (!all) {
|
|
1789
|
+
const depth = domUtils.getNodeDepth(baseNode) + 2;
|
|
1790
|
+
rChildren = domUtils.getListChildren(baseNode, (current) => {
|
|
1791
|
+
return domUtils.isListCell(current) && !current.previousElementSibling && domUtils.getNodeDepth(current) === depth;
|
|
1792
|
+
});
|
|
1793
|
+
} else {
|
|
1794
|
+
rChildren = domUtils.getListChildren(rangeElement, (current) => {
|
|
1795
|
+
return domUtils.isListCell(current) && !current.previousElementSibling;
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
for (let i = 0, len = rChildren.length; i < len; i++) {
|
|
1800
|
+
DeleteNestedList(rChildren[i]);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
if (rNode) {
|
|
1804
|
+
rNode.parentNode.insertBefore(rangeElement, rNode.nextSibling);
|
|
1805
|
+
if (cNodes?.length === 0) domUtils.removeItem(rNode);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
return rangeElement === baseNode ? rangeElement.parentNode : rangeElement;
|
|
1809
|
+
},
|
|
1810
|
+
|
|
1811
|
+
/**
|
|
1812
|
+
* @description wraps text nodes of line selected text.
|
|
1813
|
+
* @param {Element} element The node of the line that contains the selected text node.
|
|
1814
|
+
* @param {Element} newInnerNode The dom that will wrap the selected text area
|
|
1815
|
+
* @param {Function} validation Check if the node should be stripped.
|
|
1816
|
+
* @param {Node} startCon The startContainer property of the selection object.
|
|
1817
|
+
* @param {number} startOff The startOffset property of the selection object.
|
|
1818
|
+
* @param {Node} endCon The endContainer property of the selection object.
|
|
1819
|
+
* @param {number} endOff The endOffset property of the selection object.
|
|
1820
|
+
* @param {boolean} isRemoveFormat Is the remove all formats command?
|
|
1821
|
+
* @param {boolean} isRemoveNode "newInnerNode" is remove node?
|
|
1822
|
+
* @param {boolean} collapsed range.collapsed
|
|
1823
|
+
* @returns {{ancestor: *, startContainer: *, startOffset: *, endContainer: *, endOffset: *}}
|
|
1824
|
+
* @private
|
|
1825
|
+
*/
|
|
1826
|
+
_setNode_oneLine(element, newInnerNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
|
|
1827
|
+
// not add tag
|
|
1828
|
+
let parentCon = startCon.parentNode;
|
|
1829
|
+
while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !domUtils.isWysiwygFrame(parentCon.parentNode)) {
|
|
1830
|
+
if (parentCon.nodeName === newInnerNode.nodeName) break;
|
|
1831
|
+
parentCon = parentCon.parentNode;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
if (!isRemoveNode && parentCon === endCon.parentNode && parentCon.nodeName === newInnerNode.nodeName) {
|
|
1835
|
+
if (domUtils.isZeroWith(startCon.textContent.slice(0, startOff)) && domUtils.isZeroWith(endCon.textContent.slice(endOff))) {
|
|
1836
|
+
const children = parentCon.childNodes;
|
|
1837
|
+
let sameTag = false;
|
|
1838
|
+
|
|
1839
|
+
for (let i = 0, len = children.length, c, s, e, z; i < len; i++) {
|
|
1840
|
+
c = children[i];
|
|
1841
|
+
z = !domUtils.isZeroWith(c);
|
|
1842
|
+
if (c === startCon) {
|
|
1843
|
+
s = true;
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (c === endCon) {
|
|
1847
|
+
e = true;
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
if ((!s && z) || (s && e && z)) {
|
|
1851
|
+
sameTag = false;
|
|
1852
|
+
break;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
if (sameTag) {
|
|
1857
|
+
domUtils.copyTagAttributes(parentCon, newInnerNode);
|
|
1858
|
+
|
|
1859
|
+
return {
|
|
1860
|
+
ancestor: element,
|
|
1861
|
+
startContainer: startCon,
|
|
1862
|
+
startOffset: startOff,
|
|
1863
|
+
endContainer: endCon,
|
|
1864
|
+
endOffset: endOff
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// add tag
|
|
1871
|
+
_removeCheck.v = false;
|
|
1872
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1873
|
+
const inst = this;
|
|
1874
|
+
const el = element;
|
|
1875
|
+
const nNodeArray = [newInnerNode];
|
|
1876
|
+
const pNode = element.cloneNode(false);
|
|
1877
|
+
const isSameNode = startCon === endCon;
|
|
1878
|
+
let startContainer = startCon;
|
|
1879
|
+
let startOffset = startOff;
|
|
1880
|
+
let endContainer = endCon;
|
|
1881
|
+
let endOffset = endOff;
|
|
1882
|
+
let startPass = false;
|
|
1883
|
+
let endPass = false;
|
|
1884
|
+
let pCurrent, newNode, appendNode, cssText, anchorNode;
|
|
1885
|
+
|
|
1886
|
+
const wRegExp = RegExp;
|
|
1887
|
+
function checkCss(vNode) {
|
|
1888
|
+
const regExp = new wRegExp('(?:;|^|\\s)(?:' + cssText + 'null)\\s*:[^;]*\\s*(?:;|$)', 'ig');
|
|
1889
|
+
let style = '';
|
|
1890
|
+
|
|
1891
|
+
if (regExp && vNode.style.cssText.length > 0) {
|
|
1892
|
+
style = regExp.test(vNode.style.cssText);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
return !style;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
(function recursionFunc(current, ancestor) {
|
|
1899
|
+
const childNodes = current.childNodes;
|
|
1900
|
+
|
|
1901
|
+
for (let i = 0, len = childNodes.length, vNode; i < len; i++) {
|
|
1902
|
+
const child = childNodes[i];
|
|
1903
|
+
if (!child) continue;
|
|
1904
|
+
let coverNode = ancestor;
|
|
1905
|
+
let cloneNode;
|
|
1906
|
+
|
|
1907
|
+
// startContainer
|
|
1908
|
+
if (!startPass && child === startContainer) {
|
|
1909
|
+
let line = pNode;
|
|
1910
|
+
anchorNode = _getMaintainedNode(child);
|
|
1911
|
+
const prevNode = domUtils.createTextNode(startContainer.nodeType === 1 ? '' : startContainer.substringData(0, startOffset));
|
|
1912
|
+
const textNode = domUtils.createTextNode(
|
|
1913
|
+
startContainer.nodeType === 1
|
|
1914
|
+
? ''
|
|
1915
|
+
: startContainer.substringData(startOffset, isSameNode ? (endOffset >= startOffset ? endOffset - startOffset : startContainer.data.length - startOffset) : startContainer.data.length - startOffset)
|
|
1916
|
+
);
|
|
1917
|
+
|
|
1918
|
+
if (anchorNode) {
|
|
1919
|
+
const a = _getMaintainedNode(ancestor);
|
|
1920
|
+
if (a.parentNode !== line) {
|
|
1921
|
+
let m = a;
|
|
1922
|
+
let p = null;
|
|
1923
|
+
while (m.parentNode !== line) {
|
|
1924
|
+
ancestor = p = m.parentNode.cloneNode(false);
|
|
1925
|
+
while (m.childNodes[0]) {
|
|
1926
|
+
p.appendChild(m.childNodes[0]);
|
|
1927
|
+
}
|
|
1928
|
+
m.appendChild(p);
|
|
1929
|
+
m = m.parentNode;
|
|
1930
|
+
}
|
|
1931
|
+
m.parentNode.appendChild(a);
|
|
1932
|
+
}
|
|
1933
|
+
anchorNode = anchorNode.cloneNode(false);
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
if (!domUtils.isZeroWith(prevNode)) {
|
|
1937
|
+
ancestor.appendChild(prevNode);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const prevAnchorNode = _getMaintainedNode(ancestor);
|
|
1941
|
+
if (prevAnchorNode) anchorNode = prevAnchorNode;
|
|
1942
|
+
if (anchorNode) line = anchorNode;
|
|
1943
|
+
|
|
1944
|
+
newNode = child;
|
|
1945
|
+
pCurrent = [];
|
|
1946
|
+
cssText = '';
|
|
1947
|
+
while (newNode !== line && newNode !== el && newNode !== null) {
|
|
1948
|
+
vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
|
|
1949
|
+
if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
|
|
1950
|
+
pCurrent.push(vNode);
|
|
1951
|
+
cssText += newNode.style.cssText.substr(0, newNode.style.cssText.indexOf(':')) + '|';
|
|
1952
|
+
}
|
|
1953
|
+
newNode = newNode.parentNode;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
const childNode = pCurrent.pop() || textNode;
|
|
1957
|
+
appendNode = newNode = childNode;
|
|
1958
|
+
while (pCurrent.length > 0) {
|
|
1959
|
+
newNode = pCurrent.pop();
|
|
1960
|
+
appendNode.appendChild(newNode);
|
|
1961
|
+
appendNode = newNode;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
newInnerNode.appendChild(childNode);
|
|
1965
|
+
line.appendChild(newInnerNode);
|
|
1966
|
+
|
|
1967
|
+
if (anchorNode && !_getMaintainedNode(endContainer)) {
|
|
1968
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
1969
|
+
pNode.appendChild(newInnerNode);
|
|
1970
|
+
nNodeArray.push(newInnerNode);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
startContainer = textNode;
|
|
1974
|
+
startOffset = 0;
|
|
1975
|
+
startPass = true;
|
|
1976
|
+
|
|
1977
|
+
if (newNode !== textNode) newNode.appendChild(startContainer);
|
|
1978
|
+
if (!isSameNode) continue;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// endContainer
|
|
1982
|
+
if (!endPass && child === endContainer) {
|
|
1983
|
+
anchorNode = _getMaintainedNode(child);
|
|
1984
|
+
const afterNode = domUtils.createTextNode(endContainer.nodeType === 1 ? '' : endContainer.substringData(endOffset, endContainer.length - endOffset));
|
|
1985
|
+
const textNode = domUtils.createTextNode(isSameNode || endContainer.nodeType === 1 ? '' : endContainer.substringData(0, endOffset));
|
|
1986
|
+
|
|
1987
|
+
if (anchorNode) {
|
|
1988
|
+
anchorNode = anchorNode.cloneNode(false);
|
|
1989
|
+
} else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
|
|
1990
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
1991
|
+
pNode.appendChild(newInnerNode);
|
|
1992
|
+
nNodeArray.push(newInnerNode);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
if (!domUtils.isZeroWith(afterNode)) {
|
|
1996
|
+
newNode = child;
|
|
1997
|
+
cssText = '';
|
|
1998
|
+
pCurrent = [];
|
|
1999
|
+
const anchors = [];
|
|
2000
|
+
while (newNode !== pNode && newNode !== el && newNode !== null) {
|
|
2001
|
+
if (newNode.nodeType === 1 && checkCss(newNode)) {
|
|
2002
|
+
if (_isMaintainedNode(newNode)) anchors.push(newNode.cloneNode(false));
|
|
2003
|
+
else pCurrent.push(newNode.cloneNode(false));
|
|
2004
|
+
cssText += newNode.style.cssText.substr(0, newNode.style.cssText.indexOf(':')) + '|';
|
|
2005
|
+
}
|
|
2006
|
+
newNode = newNode.parentNode;
|
|
2007
|
+
}
|
|
2008
|
+
pCurrent = pCurrent.concat(anchors);
|
|
2009
|
+
|
|
2010
|
+
cloneNode = appendNode = newNode = pCurrent.pop() || afterNode;
|
|
2011
|
+
while (pCurrent.length > 0) {
|
|
2012
|
+
newNode = pCurrent.pop();
|
|
2013
|
+
appendNode.appendChild(newNode);
|
|
2014
|
+
appendNode = newNode;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
pNode.appendChild(cloneNode);
|
|
2018
|
+
newNode.textContent = afterNode.data;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
if (anchorNode && cloneNode) {
|
|
2022
|
+
const afterAnchorNode = _getMaintainedNode(cloneNode);
|
|
2023
|
+
if (afterAnchorNode) {
|
|
2024
|
+
anchorNode = afterAnchorNode;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
newNode = child;
|
|
2029
|
+
pCurrent = [];
|
|
2030
|
+
cssText = '';
|
|
2031
|
+
while (newNode !== pNode && newNode !== el && newNode !== null) {
|
|
2032
|
+
vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
|
|
2033
|
+
if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
|
|
2034
|
+
pCurrent.push(vNode);
|
|
2035
|
+
cssText += newNode.style.cssText.substr(0, newNode.style.cssText.indexOf(':')) + '|';
|
|
2036
|
+
}
|
|
2037
|
+
newNode = newNode.parentNode;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
const childNode = pCurrent.pop() || textNode;
|
|
2041
|
+
appendNode = newNode = childNode;
|
|
2042
|
+
while (pCurrent.length > 0) {
|
|
2043
|
+
newNode = pCurrent.pop();
|
|
2044
|
+
appendNode.appendChild(newNode);
|
|
2045
|
+
appendNode = newNode;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
if (anchorNode) {
|
|
2049
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2050
|
+
newInnerNode.appendChild(childNode);
|
|
2051
|
+
anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
|
|
2052
|
+
pNode.appendChild(anchorNode);
|
|
2053
|
+
nNodeArray.push(newInnerNode);
|
|
2054
|
+
anchorNode = null;
|
|
2055
|
+
} else {
|
|
2056
|
+
newInnerNode.appendChild(childNode);
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
endContainer = textNode;
|
|
2060
|
+
endOffset = textNode.data.length;
|
|
2061
|
+
endPass = true;
|
|
2062
|
+
|
|
2063
|
+
if (!isRemoveFormat && collapsed) {
|
|
2064
|
+
newInnerNode = textNode;
|
|
2065
|
+
textNode.textContent = unicode.zeroWidthSpace;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
if (newNode !== textNode) newNode.appendChild(endContainer);
|
|
2069
|
+
continue;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
// other
|
|
2073
|
+
if (startPass) {
|
|
2074
|
+
if (child.nodeType === 1 && !domUtils.isBreak(child)) {
|
|
2075
|
+
if (inst._isIgnoreNodeChange(child)) {
|
|
2076
|
+
pNode.appendChild(child.cloneNode(true));
|
|
2077
|
+
if (!collapsed) {
|
|
2078
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2079
|
+
pNode.appendChild(newInnerNode);
|
|
2080
|
+
nNodeArray.push(newInnerNode);
|
|
2081
|
+
}
|
|
2082
|
+
} else {
|
|
2083
|
+
recursionFunc(child, child);
|
|
2084
|
+
}
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
newNode = child;
|
|
2089
|
+
pCurrent = [];
|
|
2090
|
+
cssText = '';
|
|
2091
|
+
const anchors = [];
|
|
2092
|
+
while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
|
|
2093
|
+
vNode = endPass ? newNode.cloneNode(false) : validation(newNode);
|
|
2094
|
+
if (newNode.nodeType === 1 && !domUtils.isBreak(child) && vNode && checkCss(newNode)) {
|
|
2095
|
+
if (_isMaintainedNode(newNode)) {
|
|
2096
|
+
if (!anchorNode) anchors.push(vNode);
|
|
2097
|
+
} else {
|
|
2098
|
+
pCurrent.push(vNode);
|
|
2099
|
+
}
|
|
2100
|
+
cssText += newNode.style.cssText.substr(0, newNode.style.cssText.indexOf(':')) + '|';
|
|
2101
|
+
}
|
|
2102
|
+
newNode = newNode.parentNode;
|
|
2103
|
+
}
|
|
2104
|
+
pCurrent = pCurrent.concat(anchors);
|
|
2105
|
+
|
|
2106
|
+
const childNode = pCurrent.pop() || child;
|
|
2107
|
+
appendNode = newNode = childNode;
|
|
2108
|
+
while (pCurrent.length > 0) {
|
|
2109
|
+
newNode = pCurrent.pop();
|
|
2110
|
+
appendNode.appendChild(newNode);
|
|
2111
|
+
appendNode = newNode;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode) && !domUtils.isZeroWith(newInnerNode)) {
|
|
2115
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2116
|
+
pNode.appendChild(newInnerNode);
|
|
2117
|
+
nNodeArray.push(newInnerNode);
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
if (!endPass && !anchorNode && _isMaintainedNode(childNode)) {
|
|
2121
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2122
|
+
const aChildren = childNode.childNodes;
|
|
2123
|
+
for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
|
|
2124
|
+
newInnerNode.appendChild(aChildren[a]);
|
|
2125
|
+
}
|
|
2126
|
+
childNode.appendChild(newInnerNode);
|
|
2127
|
+
pNode.appendChild(childNode);
|
|
2128
|
+
nNodeArray.push(newInnerNode);
|
|
2129
|
+
if (newInnerNode.children.length > 0) ancestor = newNode;
|
|
2130
|
+
else ancestor = newInnerNode;
|
|
2131
|
+
} else if (childNode === child) {
|
|
2132
|
+
if (!endPass) ancestor = newInnerNode;
|
|
2133
|
+
else ancestor = pNode;
|
|
2134
|
+
} else if (endPass) {
|
|
2135
|
+
pNode.appendChild(childNode);
|
|
2136
|
+
ancestor = newNode;
|
|
2137
|
+
} else {
|
|
2138
|
+
newInnerNode.appendChild(childNode);
|
|
2139
|
+
ancestor = newNode;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
if (anchorNode && child.nodeType === 3) {
|
|
2143
|
+
if (_getMaintainedNode(child)) {
|
|
2144
|
+
const ancestorAnchorNode = domUtils.getParentElement(ancestor, (c) => {
|
|
2145
|
+
return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
|
|
2146
|
+
});
|
|
2147
|
+
anchorNode.appendChild(ancestorAnchorNode);
|
|
2148
|
+
newInnerNode = ancestorAnchorNode.cloneNode(false);
|
|
2149
|
+
nNodeArray.push(newInnerNode);
|
|
2150
|
+
pNode.appendChild(newInnerNode);
|
|
2151
|
+
} else {
|
|
2152
|
+
anchorNode = null;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
cloneNode = child.cloneNode(false);
|
|
2158
|
+
ancestor.appendChild(cloneNode);
|
|
2159
|
+
if (child.nodeType === 1 && !domUtils.isBreak(child)) coverNode = cloneNode;
|
|
2160
|
+
|
|
2161
|
+
recursionFunc(child, coverNode);
|
|
2162
|
+
}
|
|
2163
|
+
})(element, pNode);
|
|
2164
|
+
|
|
2165
|
+
// not remove tag
|
|
2166
|
+
if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
|
|
2167
|
+
return {
|
|
2168
|
+
ancestor: element,
|
|
2169
|
+
startContainer: startCon,
|
|
2170
|
+
startOffset: startOff,
|
|
2171
|
+
endContainer: endCon,
|
|
2172
|
+
endOffset: endOff
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
isRemoveFormat = isRemoveFormat && isRemoveNode;
|
|
2177
|
+
|
|
2178
|
+
if (isRemoveFormat) {
|
|
2179
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2180
|
+
const removeNode = nNodeArray[i];
|
|
2181
|
+
let textNode, textNode_s, textNode_e;
|
|
2182
|
+
|
|
2183
|
+
if (collapsed) {
|
|
2184
|
+
textNode = domUtils.createTextNode(unicode.zeroWidthSpace);
|
|
2185
|
+
pNode.replaceChild(textNode, removeNode);
|
|
2186
|
+
} else {
|
|
2187
|
+
const rChildren = removeNode.childNodes;
|
|
2188
|
+
textNode_s = rChildren[0];
|
|
2189
|
+
while (rChildren[0]) {
|
|
2190
|
+
textNode_e = rChildren[0];
|
|
2191
|
+
pNode.insertBefore(textNode_e, removeNode);
|
|
2192
|
+
}
|
|
2193
|
+
domUtils.removeItem(removeNode);
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
if (i === 0) {
|
|
2197
|
+
if (collapsed) {
|
|
2198
|
+
startContainer = endContainer = textNode;
|
|
2199
|
+
} else {
|
|
2200
|
+
startContainer = textNode_s;
|
|
2201
|
+
endContainer = textNode_e;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
} else {
|
|
2206
|
+
if (isRemoveNode) {
|
|
2207
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2208
|
+
SN_StripRemoveNode(nNodeArray[i]);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
if (collapsed) {
|
|
2213
|
+
startContainer = endContainer = newInnerNode;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
|
|
2218
|
+
|
|
2219
|
+
if (collapsed) {
|
|
2220
|
+
startOffset = startContainer.textContent.length;
|
|
2221
|
+
endOffset = endContainer.textContent.length;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// endContainer reset
|
|
2225
|
+
const endConReset = isRemoveFormat || endContainer.textContent.length === 0;
|
|
2226
|
+
|
|
2227
|
+
if (!domUtils.isBreak(endContainer) && endContainer.textContent.length === 0) {
|
|
2228
|
+
domUtils.removeItem(endContainer);
|
|
2229
|
+
endContainer = startContainer;
|
|
2230
|
+
}
|
|
2231
|
+
endOffset = endConReset ? endContainer.textContent.length : endOffset;
|
|
2232
|
+
|
|
2233
|
+
// node change
|
|
2234
|
+
const newStartOffset = {
|
|
2235
|
+
s: 0,
|
|
2236
|
+
e: 0
|
|
2237
|
+
};
|
|
2238
|
+
const startPath = domUtils.getNodePath(startContainer, pNode, newStartOffset);
|
|
2239
|
+
|
|
2240
|
+
const mergeEndCon = !endContainer.parentNode;
|
|
2241
|
+
if (mergeEndCon) endContainer = startContainer;
|
|
2242
|
+
const newEndOffset = {
|
|
2243
|
+
s: 0,
|
|
2244
|
+
e: 0
|
|
2245
|
+
};
|
|
2246
|
+
const endPath = domUtils.getNodePath(endContainer, pNode, !mergeEndCon && !endConReset ? newEndOffset : null);
|
|
2247
|
+
|
|
2248
|
+
startOffset += newStartOffset.s;
|
|
2249
|
+
endOffset = collapsed ? startOffset : mergeEndCon ? startContainer.textContent.length : endConReset ? endOffset + newStartOffset.s : endOffset + newEndOffset.s;
|
|
2250
|
+
|
|
2251
|
+
// tag merge
|
|
2252
|
+
const newOffsets = this.nodeTransform.mergeSameTags(pNode, [startPath, endPath], true);
|
|
2253
|
+
|
|
2254
|
+
element.parentNode.replaceChild(pNode, element);
|
|
2255
|
+
|
|
2256
|
+
startContainer = domUtils.getNodeFromPath(startPath, pNode);
|
|
2257
|
+
endContainer = domUtils.getNodeFromPath(endPath, pNode);
|
|
2258
|
+
|
|
2259
|
+
return {
|
|
2260
|
+
ancestor: pNode,
|
|
2261
|
+
startContainer: startContainer,
|
|
2262
|
+
startOffset: startOffset + newOffsets[0],
|
|
2263
|
+
endContainer: endContainer,
|
|
2264
|
+
endOffset: endOffset + newOffsets[1]
|
|
2265
|
+
};
|
|
2266
|
+
},
|
|
2267
|
+
|
|
2268
|
+
/**
|
|
2269
|
+
* @description wraps first line selected text.
|
|
2270
|
+
* @param {Element} element The node of the line that contains the selected text node.
|
|
2271
|
+
* @param {Element} newInnerNode The dom that will wrap the selected text area
|
|
2272
|
+
* @param {Function} validation Check if the node should be stripped.
|
|
2273
|
+
* @param {Node} startCon The startContainer property of the selection object.
|
|
2274
|
+
* @param {number} startOff The startOffset property of the selection object.
|
|
2275
|
+
* @param {boolean} isRemoveFormat Is the remove all formats command?
|
|
2276
|
+
* @param {boolean} isRemoveNode "newInnerNode" is remove node?
|
|
2277
|
+
* @returns {null|Node} If end container is renewed, returned renewed node
|
|
2278
|
+
* @returns {Object} { ancestor, container, offset, endContainer }
|
|
2279
|
+
* @private
|
|
2280
|
+
*/
|
|
2281
|
+
_setNode_startLine(element, newInnerNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, _endContainer) {
|
|
2282
|
+
// not add tag
|
|
2283
|
+
let parentCon = startCon.parentNode;
|
|
2284
|
+
while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !domUtils.isWysiwygFrame(parentCon.parentNode)) {
|
|
2285
|
+
if (parentCon.nodeName === newInnerNode.nodeName) break;
|
|
2286
|
+
parentCon = parentCon.parentNode;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.nextSibling && domUtils.isZeroWith(startCon.textContent.slice(0, startOff))) {
|
|
2290
|
+
let sameTag = false;
|
|
2291
|
+
let s = startCon.previousSibling;
|
|
2292
|
+
while (s) {
|
|
2293
|
+
if (!domUtils.isZeroWith(s)) {
|
|
2294
|
+
sameTag = false;
|
|
2295
|
+
break;
|
|
2296
|
+
}
|
|
2297
|
+
s = s.previousSibling;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
if (sameTag) {
|
|
2301
|
+
domUtils.copyTagAttributes(parentCon, newInnerNode);
|
|
2302
|
+
|
|
2303
|
+
return {
|
|
2304
|
+
ancestor: element,
|
|
2305
|
+
container: startCon,
|
|
2306
|
+
offset: startOff
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
// add tag
|
|
2312
|
+
_removeCheck.v = false;
|
|
2313
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2314
|
+
const inst = this;
|
|
2315
|
+
const el = element;
|
|
2316
|
+
const nNodeArray = [newInnerNode];
|
|
2317
|
+
const pNode = element.cloneNode(false);
|
|
2318
|
+
|
|
2319
|
+
let container = startCon;
|
|
2320
|
+
let offset = startOff;
|
|
2321
|
+
let passNode = false;
|
|
2322
|
+
let pCurrent, newNode, appendNode, anchorNode;
|
|
2323
|
+
|
|
2324
|
+
(function recursionFunc(current, ancestor) {
|
|
2325
|
+
const childNodes = current.childNodes;
|
|
2326
|
+
|
|
2327
|
+
for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
|
|
2328
|
+
const child = childNodes[i];
|
|
2329
|
+
if (!child) continue;
|
|
2330
|
+
let coverNode = ancestor;
|
|
2331
|
+
|
|
2332
|
+
if (passNode && !domUtils.isBreak(child)) {
|
|
2333
|
+
if (child.nodeType === 1) {
|
|
2334
|
+
if (inst._isIgnoreNodeChange(child)) {
|
|
2335
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2336
|
+
cloneChild = child.cloneNode(true);
|
|
2337
|
+
pNode.appendChild(cloneChild);
|
|
2338
|
+
pNode.appendChild(newInnerNode);
|
|
2339
|
+
nNodeArray.push(newInnerNode);
|
|
2340
|
+
|
|
2341
|
+
// end container
|
|
2342
|
+
if (_endContainer && child.contains(_endContainer)) {
|
|
2343
|
+
const endPath = domUtils.getNodePath(_endContainer, child);
|
|
2344
|
+
_endContainer = domUtils.getNodeFromPath(endPath, cloneChild);
|
|
2345
|
+
}
|
|
2346
|
+
} else {
|
|
2347
|
+
recursionFunc(child, child);
|
|
2348
|
+
}
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
newNode = child;
|
|
2353
|
+
pCurrent = [];
|
|
2354
|
+
const anchors = [];
|
|
2355
|
+
while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
|
|
2356
|
+
vNode = validation(newNode);
|
|
2357
|
+
if (newNode.nodeType === 1 && vNode) {
|
|
2358
|
+
if (_isMaintainedNode(newNode)) {
|
|
2359
|
+
if (!anchorNode) anchors.push(vNode);
|
|
2360
|
+
} else {
|
|
2361
|
+
pCurrent.push(vNode);
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
newNode = newNode.parentNode;
|
|
2365
|
+
}
|
|
2366
|
+
pCurrent = pCurrent.concat(anchors);
|
|
2367
|
+
|
|
2368
|
+
const isTopNode = pCurrent.length > 0;
|
|
2369
|
+
const childNode = pCurrent.pop() || child;
|
|
2370
|
+
appendNode = newNode = childNode;
|
|
2371
|
+
while (pCurrent.length > 0) {
|
|
2372
|
+
newNode = pCurrent.pop();
|
|
2373
|
+
appendNode.appendChild(newNode);
|
|
2374
|
+
appendNode = newNode;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
|
|
2378
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2379
|
+
pNode.appendChild(newInnerNode);
|
|
2380
|
+
nNodeArray.push(newInnerNode);
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
if (!anchorNode && _isMaintainedNode(childNode)) {
|
|
2384
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2385
|
+
const aChildren = childNode.childNodes;
|
|
2386
|
+
for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
|
|
2387
|
+
newInnerNode.appendChild(aChildren[a]);
|
|
2388
|
+
}
|
|
2389
|
+
childNode.appendChild(newInnerNode);
|
|
2390
|
+
pNode.appendChild(childNode);
|
|
2391
|
+
ancestor = !_isMaintainedNode(newNode) ? newNode : newInnerNode;
|
|
2392
|
+
nNodeArray.push(newInnerNode);
|
|
2393
|
+
} else if (isTopNode) {
|
|
2394
|
+
newInnerNode.appendChild(childNode);
|
|
2395
|
+
ancestor = newNode;
|
|
2396
|
+
} else {
|
|
2397
|
+
ancestor = newInnerNode;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
if (anchorNode && child.nodeType === 3) {
|
|
2401
|
+
if (_getMaintainedNode(child)) {
|
|
2402
|
+
const ancestorAnchorNode = domUtils.getParentElement(ancestor, (c) => {
|
|
2403
|
+
return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
|
|
2404
|
+
});
|
|
2405
|
+
anchorNode.appendChild(ancestorAnchorNode);
|
|
2406
|
+
newInnerNode = ancestorAnchorNode.cloneNode(false);
|
|
2407
|
+
nNodeArray.push(newInnerNode);
|
|
2408
|
+
pNode.appendChild(newInnerNode);
|
|
2409
|
+
} else {
|
|
2410
|
+
anchorNode = null;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
// startContainer
|
|
2416
|
+
if (!passNode && child === container) {
|
|
2417
|
+
let line = pNode;
|
|
2418
|
+
anchorNode = _getMaintainedNode(child);
|
|
2419
|
+
const prevNode = domUtils.createTextNode(container.nodeType === 1 ? '' : container.substringData(0, offset));
|
|
2420
|
+
const textNode = domUtils.createTextNode(container.nodeType === 1 ? '' : container.substringData(offset, container.length - offset));
|
|
2421
|
+
|
|
2422
|
+
if (anchorNode) {
|
|
2423
|
+
const a = _getMaintainedNode(ancestor);
|
|
2424
|
+
if (a && a.parentNode !== line) {
|
|
2425
|
+
let m = a;
|
|
2426
|
+
let p = null;
|
|
2427
|
+
while (m.parentNode !== line) {
|
|
2428
|
+
ancestor = p = m.parentNode.cloneNode(false);
|
|
2429
|
+
while (m.childNodes[0]) {
|
|
2430
|
+
p.appendChild(m.childNodes[0]);
|
|
2431
|
+
}
|
|
2432
|
+
m.appendChild(p);
|
|
2433
|
+
m = m.parentNode;
|
|
2434
|
+
}
|
|
2435
|
+
m.parentNode.appendChild(a);
|
|
2436
|
+
}
|
|
2437
|
+
anchorNode = anchorNode.cloneNode(false);
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
if (!domUtils.isZeroWith(prevNode)) {
|
|
2441
|
+
ancestor.appendChild(prevNode);
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
const prevAnchorNode = _getMaintainedNode(ancestor);
|
|
2445
|
+
if (prevAnchorNode) anchorNode = prevAnchorNode;
|
|
2446
|
+
if (anchorNode) line = anchorNode;
|
|
2447
|
+
|
|
2448
|
+
newNode = ancestor;
|
|
2449
|
+
pCurrent = [];
|
|
2450
|
+
while (newNode !== line && newNode !== null) {
|
|
2451
|
+
vNode = validation(newNode);
|
|
2452
|
+
if (newNode.nodeType === 1 && vNode) {
|
|
2453
|
+
pCurrent.push(vNode);
|
|
2454
|
+
}
|
|
2455
|
+
newNode = newNode.parentNode;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
const childNode = pCurrent.pop() || ancestor;
|
|
2459
|
+
appendNode = newNode = childNode;
|
|
2460
|
+
while (pCurrent.length > 0) {
|
|
2461
|
+
newNode = pCurrent.pop();
|
|
2462
|
+
appendNode.appendChild(newNode);
|
|
2463
|
+
appendNode = newNode;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
if (childNode !== ancestor) {
|
|
2467
|
+
newInnerNode.appendChild(childNode);
|
|
2468
|
+
ancestor = newNode;
|
|
2469
|
+
} else {
|
|
2470
|
+
ancestor = newInnerNode;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
if (domUtils.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
|
|
2474
|
+
line.appendChild(newInnerNode);
|
|
2475
|
+
|
|
2476
|
+
container = textNode;
|
|
2477
|
+
offset = 0;
|
|
2478
|
+
passNode = true;
|
|
2479
|
+
|
|
2480
|
+
ancestor.appendChild(container);
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
vNode = !passNode ? child.cloneNode(false) : validation(child);
|
|
2485
|
+
if (vNode) {
|
|
2486
|
+
ancestor.appendChild(vNode);
|
|
2487
|
+
if (child.nodeType === 1 && !domUtils.isBreak(child)) coverNode = vNode;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
recursionFunc(child, coverNode);
|
|
2491
|
+
}
|
|
2492
|
+
})(element, pNode);
|
|
2493
|
+
|
|
2494
|
+
// not remove tag
|
|
2495
|
+
if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
|
|
2496
|
+
return {
|
|
2497
|
+
ancestor: element,
|
|
2498
|
+
container: startCon,
|
|
2499
|
+
offset: startOff,
|
|
2500
|
+
endContainer: _endContainer
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
isRemoveFormat = isRemoveFormat && isRemoveNode;
|
|
2505
|
+
|
|
2506
|
+
if (isRemoveFormat) {
|
|
2507
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2508
|
+
const removeNode = nNodeArray[i];
|
|
2509
|
+
|
|
2510
|
+
const rChildren = removeNode.childNodes;
|
|
2511
|
+
const textNode = rChildren[0];
|
|
2512
|
+
while (rChildren[0]) {
|
|
2513
|
+
pNode.insertBefore(rChildren[0], removeNode);
|
|
2514
|
+
}
|
|
2515
|
+
domUtils.removeItem(removeNode);
|
|
2516
|
+
|
|
2517
|
+
if (i === 0) container = textNode;
|
|
2518
|
+
}
|
|
2519
|
+
} else if (isRemoveNode) {
|
|
2520
|
+
newInnerNode = newInnerNode.firstChild;
|
|
2521
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2522
|
+
SN_StripRemoveNode(nNodeArray[i]);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
if (!isRemoveFormat && pNode.childNodes.length === 0) {
|
|
2527
|
+
if (element.childNodes) {
|
|
2528
|
+
container = element.childNodes[0];
|
|
2529
|
+
} else {
|
|
2530
|
+
container = domUtils.createTextNode(unicode.zeroWidthSpace);
|
|
2531
|
+
element.appendChild(container);
|
|
2532
|
+
}
|
|
2533
|
+
} else {
|
|
2534
|
+
this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
|
|
2535
|
+
|
|
2536
|
+
if (domUtils.isZeroWith(pNode.textContent)) {
|
|
2537
|
+
container = pNode.firstChild;
|
|
2538
|
+
offset = 0;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// node change
|
|
2542
|
+
const offsets = {
|
|
2543
|
+
s: 0,
|
|
2544
|
+
e: 0
|
|
2545
|
+
};
|
|
2546
|
+
const path = domUtils.getNodePath(container, pNode, offsets);
|
|
2547
|
+
offset += offsets.s;
|
|
2548
|
+
|
|
2549
|
+
// tag merge
|
|
2550
|
+
const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
|
|
2551
|
+
|
|
2552
|
+
element.parentNode.replaceChild(pNode, element);
|
|
2553
|
+
|
|
2554
|
+
container = domUtils.getNodeFromPath(path, pNode);
|
|
2555
|
+
offset += newOffsets[0];
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
return {
|
|
2559
|
+
ancestor: pNode,
|
|
2560
|
+
container: container,
|
|
2561
|
+
offset: offset,
|
|
2562
|
+
endContainer: _endContainer
|
|
2563
|
+
};
|
|
2564
|
+
},
|
|
2565
|
+
|
|
2566
|
+
/**
|
|
2567
|
+
* @description wraps mid lines selected text.
|
|
2568
|
+
* @param {Element} element The node of the line that contains the selected text node.
|
|
2569
|
+
* @param {Element} newInnerNode The dom that will wrap the selected text area
|
|
2570
|
+
* @param {Function} validation Check if the node should be stripped.
|
|
2571
|
+
* @param {boolean} isRemoveFormat Is the remove all formats command?
|
|
2572
|
+
* @param {boolean} isRemoveNode "newInnerNode" is remove node?
|
|
2573
|
+
* @param {Node} _endContainer Offset node of last line already modified (end.container)
|
|
2574
|
+
* @returns {Object} { ancestor, endContainer: "If end container is renewed, returned renewed node" }
|
|
2575
|
+
* @private
|
|
2576
|
+
*/
|
|
2577
|
+
_setNode_middleLine(element, newInnerNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, _endContainer) {
|
|
2578
|
+
// not add tag
|
|
2579
|
+
if (!isRemoveNode) {
|
|
2580
|
+
// end container path
|
|
2581
|
+
let endPath = null;
|
|
2582
|
+
if (_endContainer && element.contains(_endContainer)) endPath = domUtils.getNodePath(_endContainer, element);
|
|
2583
|
+
|
|
2584
|
+
const tempNode = element.cloneNode(true);
|
|
2585
|
+
const newNodeName = newInnerNode.nodeName;
|
|
2586
|
+
const newCssText = newInnerNode.style.cssText;
|
|
2587
|
+
const newClass = newInnerNode.className;
|
|
2588
|
+
|
|
2589
|
+
let children = tempNode.childNodes;
|
|
2590
|
+
let i = 0,
|
|
2591
|
+
len = children.length;
|
|
2592
|
+
for (let child; i < len; i++) {
|
|
2593
|
+
child = children[i];
|
|
2594
|
+
if (child.nodeType === 3) break;
|
|
2595
|
+
if (child.nodeName === newNodeName) {
|
|
2596
|
+
child.style.cssText += newCssText;
|
|
2597
|
+
domUtils.addClass(child, newClass);
|
|
2598
|
+
} else if (!domUtils.isBreak(child) && this._isIgnoreNodeChange(child)) {
|
|
2599
|
+
continue;
|
|
2600
|
+
} else if (len === 1) {
|
|
2601
|
+
children = child.childNodes;
|
|
2602
|
+
len = children.length;
|
|
2603
|
+
i = -1;
|
|
2604
|
+
continue;
|
|
2605
|
+
} else {
|
|
2606
|
+
break;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
if (len > 0 && i === len) {
|
|
2611
|
+
element.innerHTML = tempNode.innerHTML;
|
|
2612
|
+
return {
|
|
2613
|
+
ancestor: element,
|
|
2614
|
+
endContainer: endPath ? domUtils.getNodeFromPath(endPath, element) : null
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
// add tag
|
|
2620
|
+
_removeCheck.v = false;
|
|
2621
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2622
|
+
const inst = this;
|
|
2623
|
+
const pNode = element.cloneNode(false);
|
|
2624
|
+
const nNodeArray = [newInnerNode];
|
|
2625
|
+
let noneChange = true;
|
|
2626
|
+
|
|
2627
|
+
(function recursionFunc(current, ancestor) {
|
|
2628
|
+
const childNodes = current.childNodes;
|
|
2629
|
+
|
|
2630
|
+
for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
|
|
2631
|
+
const child = childNodes[i];
|
|
2632
|
+
if (!child) continue;
|
|
2633
|
+
let coverNode = ancestor;
|
|
2634
|
+
|
|
2635
|
+
if (!domUtils.isBreak(child) && inst._isIgnoreNodeChange(child)) {
|
|
2636
|
+
if (newInnerNode.childNodes.length > 0) {
|
|
2637
|
+
pNode.appendChild(newInnerNode);
|
|
2638
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
cloneChild = child.cloneNode(true);
|
|
2642
|
+
pNode.appendChild(cloneChild);
|
|
2643
|
+
pNode.appendChild(newInnerNode);
|
|
2644
|
+
nNodeArray.push(newInnerNode);
|
|
2645
|
+
ancestor = newInnerNode;
|
|
2646
|
+
|
|
2647
|
+
// end container
|
|
2648
|
+
if (_endContainer && child.contains(_endContainer)) {
|
|
2649
|
+
const endPath = domUtils.getNodePath(_endContainer, child);
|
|
2650
|
+
_endContainer = domUtils.getNodeFromPath(endPath, cloneChild);
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
continue;
|
|
2654
|
+
} else {
|
|
2655
|
+
vNode = validation(child);
|
|
2656
|
+
if (vNode) {
|
|
2657
|
+
noneChange = false;
|
|
2658
|
+
ancestor.appendChild(vNode);
|
|
2659
|
+
if (child.nodeType === 1) coverNode = vNode;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
if (!domUtils.isBreak(child)) recursionFunc(child, coverNode);
|
|
2664
|
+
}
|
|
2665
|
+
})(element, newInnerNode);
|
|
2666
|
+
|
|
2667
|
+
// not remove tag
|
|
2668
|
+
if (noneChange || (isRemoveNode && !isRemoveFormat && !_removeCheck.v))
|
|
2669
|
+
return {
|
|
2670
|
+
ancestor: element,
|
|
2671
|
+
endContainer: _endContainer
|
|
2672
|
+
};
|
|
2673
|
+
|
|
2674
|
+
pNode.appendChild(newInnerNode);
|
|
2675
|
+
|
|
2676
|
+
if (isRemoveFormat && isRemoveNode) {
|
|
2677
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2678
|
+
const removeNode = nNodeArray[i];
|
|
2679
|
+
|
|
2680
|
+
const rChildren = removeNode.childNodes;
|
|
2681
|
+
while (rChildren[0]) {
|
|
2682
|
+
pNode.insertBefore(rChildren[0], removeNode);
|
|
2683
|
+
}
|
|
2684
|
+
domUtils.removeItem(removeNode);
|
|
2685
|
+
}
|
|
2686
|
+
} else if (isRemoveNode) {
|
|
2687
|
+
newInnerNode = newInnerNode.firstChild;
|
|
2688
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2689
|
+
SN_StripRemoveNode(nNodeArray[i]);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
|
|
2694
|
+
this.nodeTransform.mergeSameTags(pNode, null, true);
|
|
2695
|
+
|
|
2696
|
+
// node change
|
|
2697
|
+
element.parentNode.replaceChild(pNode, element);
|
|
2698
|
+
return {
|
|
2699
|
+
ancestor: pNode,
|
|
2700
|
+
endContainer: _endContainer
|
|
2701
|
+
};
|
|
2702
|
+
},
|
|
2703
|
+
|
|
2704
|
+
/**
|
|
2705
|
+
* @description wraps last line selected text.
|
|
2706
|
+
* @param {Element} element The node of the line that contains the selected text node.
|
|
2707
|
+
* @param {Element} newInnerNode The dom that will wrap the selected text area
|
|
2708
|
+
* @param {Function} validation Check if the node should be stripped.
|
|
2709
|
+
* @param {Node} endCon The endContainer property of the selection object.
|
|
2710
|
+
* @param {number} endOff The endOffset property of the selection object.
|
|
2711
|
+
* @param {boolean} isRemoveFormat Is the remove all formats command?
|
|
2712
|
+
* @param {boolean} isRemoveNode "newInnerNode" is remove node?
|
|
2713
|
+
* @returns {Object} { ancestor, container, offset }
|
|
2714
|
+
* @private
|
|
2715
|
+
*/
|
|
2716
|
+
_setNode_endLine(element, newInnerNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
|
|
2717
|
+
// not add tag
|
|
2718
|
+
let parentCon = endCon.parentNode;
|
|
2719
|
+
while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !domUtils.isWysiwygFrame(parentCon.parentNode)) {
|
|
2720
|
+
if (parentCon.nodeName === newInnerNode.nodeName) break;
|
|
2721
|
+
parentCon = parentCon.parentNode;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.previousSibling && domUtils.isZeroWith(endCon.textContent.slice(endOff))) {
|
|
2725
|
+
let sameTag = false;
|
|
2726
|
+
let e = endCon.nextSibling;
|
|
2727
|
+
while (e) {
|
|
2728
|
+
if (!domUtils.isZeroWith(e)) {
|
|
2729
|
+
sameTag = false;
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2732
|
+
e = e.nextSibling;
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
if (sameTag) {
|
|
2736
|
+
domUtils.copyTagAttributes(parentCon, newInnerNode);
|
|
2737
|
+
|
|
2738
|
+
return {
|
|
2739
|
+
ancestor: element,
|
|
2740
|
+
container: endCon,
|
|
2741
|
+
offset: endOff
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
// add tag
|
|
2747
|
+
_removeCheck.v = false;
|
|
2748
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2749
|
+
const inst = this;
|
|
2750
|
+
const el = element;
|
|
2751
|
+
const nNodeArray = [newInnerNode];
|
|
2752
|
+
const pNode = element.cloneNode(false);
|
|
2753
|
+
|
|
2754
|
+
let container = endCon;
|
|
2755
|
+
let offset = endOff;
|
|
2756
|
+
let passNode = false;
|
|
2757
|
+
let pCurrent, newNode, appendNode, anchorNode;
|
|
2758
|
+
|
|
2759
|
+
(function recursionFunc(current, ancestor) {
|
|
2760
|
+
const childNodes = current.childNodes;
|
|
2761
|
+
|
|
2762
|
+
for (let i = childNodes.length - 1, vNode; 0 <= i; i--) {
|
|
2763
|
+
const child = childNodes[i];
|
|
2764
|
+
if (!child) continue;
|
|
2765
|
+
let coverNode = ancestor;
|
|
2766
|
+
|
|
2767
|
+
if (passNode && !domUtils.isBreak(child)) {
|
|
2768
|
+
if (child.nodeType === 1) {
|
|
2769
|
+
if (inst._isIgnoreNodeChange(child)) {
|
|
2770
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2771
|
+
const cloneChild = child.cloneNode(true);
|
|
2772
|
+
pNode.insertBefore(cloneChild, ancestor);
|
|
2773
|
+
pNode.insertBefore(newInnerNode, cloneChild);
|
|
2774
|
+
nNodeArray.push(newInnerNode);
|
|
2775
|
+
} else {
|
|
2776
|
+
recursionFunc(child, child);
|
|
2777
|
+
}
|
|
2778
|
+
continue;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
newNode = child;
|
|
2782
|
+
pCurrent = [];
|
|
2783
|
+
const anchors = [];
|
|
2784
|
+
while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
|
|
2785
|
+
vNode = validation(newNode);
|
|
2786
|
+
if (vNode && newNode.nodeType === 1) {
|
|
2787
|
+
if (_isMaintainedNode(newNode)) {
|
|
2788
|
+
if (!anchorNode) anchors.push(vNode);
|
|
2789
|
+
} else {
|
|
2790
|
+
pCurrent.push(vNode);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
newNode = newNode.parentNode;
|
|
2794
|
+
}
|
|
2795
|
+
pCurrent = pCurrent.concat(anchors);
|
|
2796
|
+
|
|
2797
|
+
const isTopNode = pCurrent.length > 0;
|
|
2798
|
+
const childNode = pCurrent.pop() || child;
|
|
2799
|
+
appendNode = newNode = childNode;
|
|
2800
|
+
while (pCurrent.length > 0) {
|
|
2801
|
+
newNode = pCurrent.pop();
|
|
2802
|
+
appendNode.appendChild(newNode);
|
|
2803
|
+
appendNode = newNode;
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
|
|
2807
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2808
|
+
pNode.insertBefore(newInnerNode, pNode.firstChild);
|
|
2809
|
+
nNodeArray.push(newInnerNode);
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
if (!anchorNode && _isMaintainedNode(childNode)) {
|
|
2813
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2814
|
+
const aChildren = childNode.childNodes;
|
|
2815
|
+
for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
|
|
2816
|
+
newInnerNode.appendChild(aChildren[a]);
|
|
2817
|
+
}
|
|
2818
|
+
childNode.appendChild(newInnerNode);
|
|
2819
|
+
pNode.insertBefore(childNode, pNode.firstChild);
|
|
2820
|
+
nNodeArray.push(newInnerNode);
|
|
2821
|
+
if (newInnerNode.children.length > 0) ancestor = newNode;
|
|
2822
|
+
else ancestor = newInnerNode;
|
|
2823
|
+
} else if (isTopNode) {
|
|
2824
|
+
newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
|
|
2825
|
+
ancestor = newNode;
|
|
2826
|
+
} else {
|
|
2827
|
+
ancestor = newInnerNode;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
if (anchorNode && child.nodeType === 3) {
|
|
2831
|
+
if (_getMaintainedNode(child)) {
|
|
2832
|
+
const ancestorAnchorNode = domUtils.getParentElement(ancestor, (c) => {
|
|
2833
|
+
return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
|
|
2834
|
+
});
|
|
2835
|
+
anchorNode.appendChild(ancestorAnchorNode);
|
|
2836
|
+
newInnerNode = ancestorAnchorNode.cloneNode(false);
|
|
2837
|
+
nNodeArray.push(newInnerNode);
|
|
2838
|
+
pNode.insertBefore(newInnerNode, pNode.firstChild);
|
|
2839
|
+
} else {
|
|
2840
|
+
anchorNode = null;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
// endContainer
|
|
2846
|
+
if (!passNode && child === container) {
|
|
2847
|
+
anchorNode = _getMaintainedNode(child);
|
|
2848
|
+
const afterNode = domUtils.createTextNode(container.nodeType === 1 ? '' : container.substringData(offset, container.length - offset));
|
|
2849
|
+
const textNode = domUtils.createTextNode(container.nodeType === 1 ? '' : container.substringData(0, offset));
|
|
2850
|
+
|
|
2851
|
+
if (anchorNode) {
|
|
2852
|
+
anchorNode = anchorNode.cloneNode(false);
|
|
2853
|
+
const a = _getMaintainedNode(ancestor);
|
|
2854
|
+
if (a.parentNode !== pNode) {
|
|
2855
|
+
let m = a;
|
|
2856
|
+
let p = null;
|
|
2857
|
+
while (m.parentNode !== pNode) {
|
|
2858
|
+
ancestor = p = m.parentNode.cloneNode(false);
|
|
2859
|
+
while (m.childNodes[0]) {
|
|
2860
|
+
p.appendChild(m.childNodes[0]);
|
|
2861
|
+
}
|
|
2862
|
+
m.appendChild(p);
|
|
2863
|
+
m = m.parentNode;
|
|
2864
|
+
}
|
|
2865
|
+
m.parentNode.insertBefore(a, m.parentNode.firstChild);
|
|
2866
|
+
}
|
|
2867
|
+
anchorNode = anchorNode.cloneNode(false);
|
|
2868
|
+
} else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
|
|
2869
|
+
newInnerNode = newInnerNode.cloneNode(false);
|
|
2870
|
+
pNode.appendChild(newInnerNode);
|
|
2871
|
+
nNodeArray.push(newInnerNode);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
if (!domUtils.isZeroWith(afterNode)) {
|
|
2875
|
+
ancestor.insertBefore(afterNode, ancestor.firstChild);
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
newNode = ancestor;
|
|
2879
|
+
pCurrent = [];
|
|
2880
|
+
while (newNode !== pNode && newNode !== null) {
|
|
2881
|
+
vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
|
|
2882
|
+
if (vNode && newNode.nodeType === 1) {
|
|
2883
|
+
pCurrent.push(vNode);
|
|
2884
|
+
}
|
|
2885
|
+
newNode = newNode.parentNode;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
const childNode = pCurrent.pop() || ancestor;
|
|
2889
|
+
appendNode = newNode = childNode;
|
|
2890
|
+
while (pCurrent.length > 0) {
|
|
2891
|
+
newNode = pCurrent.pop();
|
|
2892
|
+
appendNode.appendChild(newNode);
|
|
2893
|
+
appendNode = newNode;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
if (childNode !== ancestor) {
|
|
2897
|
+
newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
|
|
2898
|
+
ancestor = newNode;
|
|
2899
|
+
} else {
|
|
2900
|
+
ancestor = newInnerNode;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
if (domUtils.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
|
|
2904
|
+
|
|
2905
|
+
if (anchorNode) {
|
|
2906
|
+
anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
|
|
2907
|
+
pNode.insertBefore(anchorNode, pNode.firstChild);
|
|
2908
|
+
anchorNode = null;
|
|
2909
|
+
} else {
|
|
2910
|
+
pNode.insertBefore(newInnerNode, pNode.firstChild);
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
container = textNode;
|
|
2914
|
+
offset = textNode.data.length;
|
|
2915
|
+
passNode = true;
|
|
2916
|
+
|
|
2917
|
+
ancestor.insertBefore(container, ancestor.firstChild);
|
|
2918
|
+
continue;
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
vNode = !passNode ? child.cloneNode(false) : validation(child);
|
|
2922
|
+
if (vNode) {
|
|
2923
|
+
ancestor.insertBefore(vNode, ancestor.firstChild);
|
|
2924
|
+
if (child.nodeType === 1 && !domUtils.isBreak(child)) coverNode = vNode;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
recursionFunc(child, coverNode);
|
|
2928
|
+
}
|
|
2929
|
+
})(element, pNode);
|
|
2930
|
+
|
|
2931
|
+
// not remove tag
|
|
2932
|
+
if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
|
|
2933
|
+
return {
|
|
2934
|
+
ancestor: element,
|
|
2935
|
+
container: endCon,
|
|
2936
|
+
offset: endOff
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
isRemoveFormat = isRemoveFormat && isRemoveNode;
|
|
2941
|
+
|
|
2942
|
+
if (isRemoveFormat) {
|
|
2943
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2944
|
+
const removeNode = nNodeArray[i];
|
|
2945
|
+
|
|
2946
|
+
const rChildren = removeNode.childNodes;
|
|
2947
|
+
let textNode = null;
|
|
2948
|
+
while (rChildren[0]) {
|
|
2949
|
+
textNode = rChildren[0];
|
|
2950
|
+
pNode.insertBefore(textNode, removeNode);
|
|
2951
|
+
}
|
|
2952
|
+
domUtils.removeItem(removeNode);
|
|
2953
|
+
|
|
2954
|
+
if (i === nNodeArray.length - 1) {
|
|
2955
|
+
container = textNode;
|
|
2956
|
+
offset = textNode.textContent.length;
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
} else if (isRemoveNode) {
|
|
2960
|
+
newInnerNode = newInnerNode.firstChild;
|
|
2961
|
+
for (let i = 0; i < nNodeArray.length; i++) {
|
|
2962
|
+
SN_StripRemoveNode(nNodeArray[i]);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
if (!isRemoveFormat && pNode.childNodes.length === 0) {
|
|
2967
|
+
if (element.childNodes) {
|
|
2968
|
+
container = element.childNodes[0];
|
|
2969
|
+
} else {
|
|
2970
|
+
container = domUtils.createTextNode(unicode.zeroWidthSpace);
|
|
2971
|
+
element.appendChild(container);
|
|
2972
|
+
}
|
|
2973
|
+
} else {
|
|
2974
|
+
if (!isRemoveNode && newInnerNode.textContent.length === 0) {
|
|
2975
|
+
this.nodeTransform.removeEmptyNode(pNode, null, false);
|
|
2976
|
+
return {
|
|
2977
|
+
ancestor: null,
|
|
2978
|
+
container: null,
|
|
2979
|
+
offset: 0
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
|
|
2984
|
+
|
|
2985
|
+
if (domUtils.isZeroWith(pNode.textContent)) {
|
|
2986
|
+
container = pNode.firstChild;
|
|
2987
|
+
offset = container.textContent.length;
|
|
2988
|
+
} else if (domUtils.isZeroWith(container)) {
|
|
2989
|
+
container = newInnerNode;
|
|
2990
|
+
offset = 1;
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
// node change
|
|
2994
|
+
const offsets = {
|
|
2995
|
+
s: 0,
|
|
2996
|
+
e: 0
|
|
2997
|
+
};
|
|
2998
|
+
const path = domUtils.getNodePath(container, pNode, offsets);
|
|
2999
|
+
offset += offsets.s;
|
|
3000
|
+
|
|
3001
|
+
// tag merge
|
|
3002
|
+
const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
|
|
3003
|
+
|
|
3004
|
+
element.parentNode.replaceChild(pNode, element);
|
|
3005
|
+
|
|
3006
|
+
container = domUtils.getNodeFromPath(path, pNode);
|
|
3007
|
+
offset += newOffsets[0];
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
return {
|
|
3011
|
+
ancestor: pNode,
|
|
3012
|
+
container: container,
|
|
3013
|
+
offset: container.nodeType === 1 && offset === 1 ? container.childNodes.length : offset
|
|
3014
|
+
};
|
|
3015
|
+
},
|
|
3016
|
+
|
|
3017
|
+
/**
|
|
3018
|
+
* @description Node with font-size style
|
|
3019
|
+
* @param {Node} element Element to check
|
|
3020
|
+
* @returns {boolean}
|
|
3021
|
+
* @private
|
|
3022
|
+
*/
|
|
3023
|
+
_sn_isSizeNode(element) {
|
|
3024
|
+
return element && typeof element !== 'string' && element.nodeType !== 3 && this.isTextStyleNode(element) && element.style.fontSize;
|
|
3025
|
+
},
|
|
3026
|
+
|
|
3027
|
+
/**
|
|
3028
|
+
* @description Return the parent maintained tag. (bind and use a util object)
|
|
3029
|
+
* @param {Element} element Element
|
|
3030
|
+
* @returns {Element}
|
|
3031
|
+
* @private
|
|
3032
|
+
*/
|
|
3033
|
+
_sn_getMaintainedNode(_isRemove, _isSizeNode, element) {
|
|
3034
|
+
if (!element || _isRemove) return null;
|
|
3035
|
+
return domUtils.getParentElement(element, this._isNonSplitNode) || (!_isSizeNode ? domUtils.getParentElement(element, this._sn_isSizeNode.bind(this)) : null);
|
|
3036
|
+
},
|
|
3037
|
+
|
|
3038
|
+
/**
|
|
3039
|
+
* @description Check if element is a tag that should be persisted. (bind and use a util object)
|
|
3040
|
+
* @param {Element} element Element
|
|
3041
|
+
* @returns {Element}
|
|
3042
|
+
* @private
|
|
3043
|
+
*/
|
|
3044
|
+
_sn_isMaintainedNode(_isRemove, _isSizeNode, element) {
|
|
3045
|
+
if (!element || _isRemove || element.nodeType !== 1) return false;
|
|
3046
|
+
const anchor = this._isNonSplitNode(element);
|
|
3047
|
+
return domUtils.getParentElement(element, this._isNonSplitNode) ? anchor : anchor || (!_isSizeNode ? this._sn_isSizeNode(element) : false);
|
|
3048
|
+
},
|
|
3049
|
+
|
|
3050
|
+
/**
|
|
3051
|
+
* @description If certain styles are applied to all child nodes of the list cell, the style of the list cell is also changed. (bold, color, size)
|
|
3052
|
+
* @param {Element} el List cell element. <li>
|
|
3053
|
+
* @param {Element|null} child Variable for recursive call. ("null" on the first call)
|
|
3054
|
+
* @private
|
|
3055
|
+
*/
|
|
3056
|
+
_sn_setCommonListStyle(el, child) {
|
|
3057
|
+
if (!domUtils.isListCell(el)) return;
|
|
3058
|
+
|
|
3059
|
+
const children = domUtils.getArrayItem(
|
|
3060
|
+
(child || el).childNodes,
|
|
3061
|
+
function (current) {
|
|
3062
|
+
return !domUtils.isBreak(current);
|
|
3063
|
+
},
|
|
3064
|
+
true
|
|
3065
|
+
);
|
|
3066
|
+
child = children[0];
|
|
3067
|
+
|
|
3068
|
+
if (!child || children.length > 1 || child.nodeType !== 1) return;
|
|
3069
|
+
|
|
3070
|
+
// set cell style---
|
|
3071
|
+
const childStyle = child.style;
|
|
3072
|
+
const elStyle = el.style;
|
|
3073
|
+
const nodeName = child.nodeName.toLowerCase();
|
|
3074
|
+
let appliedEl = false;
|
|
3075
|
+
|
|
3076
|
+
// bold, italic
|
|
3077
|
+
if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').bold.toLowerCase()) elStyle.fontWeight = 'bold';
|
|
3078
|
+
if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').italic.toLowerCase()) elStyle.fontStyle = 'italic';
|
|
3079
|
+
|
|
3080
|
+
// styles
|
|
3081
|
+
const cKeys = env.getValues(childStyle);
|
|
3082
|
+
if (cKeys.length > 0) {
|
|
3083
|
+
for (let i = 0, len = this._listCamel.length; i < len; i++) {
|
|
3084
|
+
if (cKeys.includes(this._listKebab[i])) {
|
|
3085
|
+
elStyle[this._listCamel[i]] = childStyle[this._listCamel[i]];
|
|
3086
|
+
childStyle.removeProperty(this._listKebab[i]);
|
|
3087
|
+
appliedEl = true;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
this._sn_setCommonListStyle(el, child);
|
|
3093
|
+
if (!appliedEl) return;
|
|
3094
|
+
|
|
3095
|
+
// common style
|
|
3096
|
+
if (!childStyle.length) {
|
|
3097
|
+
const ch = child.childNodes;
|
|
3098
|
+
const p = child.parentNode;
|
|
3099
|
+
const n = child.nextSibling;
|
|
3100
|
+
while (ch.length > 0) {
|
|
3101
|
+
p.insertBefore(ch[0], n);
|
|
3102
|
+
}
|
|
3103
|
+
domUtils.removeItem(child);
|
|
3104
|
+
}
|
|
3105
|
+
},
|
|
3106
|
+
|
|
3107
|
+
/**
|
|
3108
|
+
* @description Watch the applied text nodes and adjust the common styles of the list.
|
|
3109
|
+
* @param {Element} el "LI" element
|
|
3110
|
+
* @param {Array|null} styleArray Refer style array
|
|
3111
|
+
* @private
|
|
3112
|
+
*/
|
|
3113
|
+
_sn_resetCommonListCell(el, styleArray) {
|
|
3114
|
+
if (!domUtils.isListCell(el)) return;
|
|
3115
|
+
if (!styleArray) styleArray = this._listKebab;
|
|
3116
|
+
|
|
3117
|
+
const children = domUtils.getArrayItem(
|
|
3118
|
+
el.childNodes,
|
|
3119
|
+
function (current) {
|
|
3120
|
+
return !domUtils.isBreak(current);
|
|
3121
|
+
},
|
|
3122
|
+
true
|
|
3123
|
+
);
|
|
3124
|
+
const elStyles = el.style;
|
|
3125
|
+
|
|
3126
|
+
const ec = [],
|
|
3127
|
+
ek = [],
|
|
3128
|
+
elKeys = env.getValues(elStyles);
|
|
3129
|
+
for (let i = 0, len = this._listKebab.length; i < len; i++) {
|
|
3130
|
+
if (elKeys.includes(this._listKebab[i]) && styleArray.includes(this._listKebab[i])) {
|
|
3131
|
+
ec.push(this._listCamel[i]);
|
|
3132
|
+
ek.push(this._listKebab[i]);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
if (!ec.length) return;
|
|
3137
|
+
|
|
3138
|
+
// reset cell style---
|
|
3139
|
+
const refer = domUtils.createElement('SPAN');
|
|
3140
|
+
for (let i = 0, len = ec.length; i < len; i++) {
|
|
3141
|
+
refer.style[ec[i]] = elStyles[ek[i]];
|
|
3142
|
+
elStyles.removeProperty(ek[i]);
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
let sel = refer.cloneNode(false);
|
|
3146
|
+
let r = null,
|
|
3147
|
+
appliedEl = false;
|
|
3148
|
+
for (let i = 0, len = children.length, c, s; i < len; i++) {
|
|
3149
|
+
c = children[i];
|
|
3150
|
+
if (this.options.get('_defaultStyleTagMap')[c.nodeName.toLowerCase()]) continue;
|
|
3151
|
+
|
|
3152
|
+
s = env.getValues(c.style);
|
|
3153
|
+
if (
|
|
3154
|
+
s.length === 0 ||
|
|
3155
|
+
(ec.some(function (k) {
|
|
3156
|
+
return !s.includes(k);
|
|
3157
|
+
}) &&
|
|
3158
|
+
s.some(function (k) {
|
|
3159
|
+
ec.includes(k);
|
|
3160
|
+
}))
|
|
3161
|
+
) {
|
|
3162
|
+
r = c.nextSibling;
|
|
3163
|
+
sel.appendChild(c);
|
|
3164
|
+
} else if (sel.childNodes.length > 0) {
|
|
3165
|
+
el.insertBefore(sel, r);
|
|
3166
|
+
sel = refer.cloneNode(false);
|
|
3167
|
+
r = null;
|
|
3168
|
+
appliedEl = true;
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
if (sel.childNodes.length > 0) {
|
|
3173
|
+
el.insertBefore(sel, r);
|
|
3174
|
+
appliedEl = true;
|
|
3175
|
+
}
|
|
3176
|
+
if (!elStyles.length) {
|
|
3177
|
+
el.removeAttribute('style');
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
return appliedEl;
|
|
3181
|
+
},
|
|
3182
|
+
|
|
3183
|
+
constructor: Format
|
|
3184
|
+
};
|
|
3185
|
+
|
|
3186
|
+
function DeleteNestedList(baseNode) {
|
|
3187
|
+
const baseParent = baseNode.parentNode;
|
|
3188
|
+
let sibling = baseParent;
|
|
3189
|
+
let parent = sibling.parentNode;
|
|
3190
|
+
let liSibling, liParent, child, index, c;
|
|
3191
|
+
|
|
3192
|
+
while (domUtils.isListCell(parent)) {
|
|
3193
|
+
index = domUtils.getPositionIndex(baseNode);
|
|
3194
|
+
liSibling = parent.nextElementSibling;
|
|
3195
|
+
liParent = parent.parentNode;
|
|
3196
|
+
child = sibling;
|
|
3197
|
+
while (child) {
|
|
3198
|
+
sibling = sibling.nextSibling;
|
|
3199
|
+
if (domUtils.isList(child)) {
|
|
3200
|
+
c = child.childNodes;
|
|
3201
|
+
while (c[index]) {
|
|
3202
|
+
liParent.insertBefore(c[index], liSibling);
|
|
3203
|
+
}
|
|
3204
|
+
if (c.length === 0) domUtils.removeItem(child);
|
|
3205
|
+
} else {
|
|
3206
|
+
liParent.appendChild(child);
|
|
3207
|
+
}
|
|
3208
|
+
child = sibling;
|
|
3209
|
+
}
|
|
3210
|
+
sibling = liParent;
|
|
3211
|
+
parent = liParent.parentNode;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
if (baseParent.children.length === 0) domUtils.removeItem(baseParent);
|
|
3215
|
+
|
|
3216
|
+
return liParent;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
function SetLineMargin(lines, size, dir) {
|
|
3220
|
+
const cells = [];
|
|
3221
|
+
|
|
3222
|
+
for (let i = 0, len = lines.length, f, margin; i < len; i++) {
|
|
3223
|
+
f = lines[i];
|
|
3224
|
+
if (!domUtils.isListCell(f)) {
|
|
3225
|
+
margin = /\d+/.test(f.style[dir]) ? numbers.get(f.style[dir], 0) : 0;
|
|
3226
|
+
margin += size;
|
|
3227
|
+
domUtils.setStyle(f, dir, margin <= 0 ? '' : margin + 'px');
|
|
3228
|
+
} else {
|
|
3229
|
+
if (size < 0 || f.previousElementSibling) {
|
|
3230
|
+
cells.push(f);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
return cells;
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
/**
|
|
3239
|
+
* @description Strip remove node
|
|
3240
|
+
* @param {Node} removeNode The remove node
|
|
3241
|
+
* @private
|
|
3242
|
+
*/
|
|
3243
|
+
function SN_StripRemoveNode(removeNode) {
|
|
3244
|
+
const element = removeNode.parentNode;
|
|
3245
|
+
if (!removeNode || removeNode.nodeType === 3 || !element) return;
|
|
3246
|
+
|
|
3247
|
+
const children = removeNode.childNodes;
|
|
3248
|
+
while (children[0]) {
|
|
3249
|
+
element.insertBefore(children[0], removeNode);
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
element.removeChild(removeNode);
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
export default Format;
|