suneditor 3.0.0-beta.2 → 3.0.0-beta.20
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/CONTRIBUTING.md +186 -184
- package/LICENSE +21 -21
- package/README.md +157 -180
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +126 -123
- package/src/assets/design/color.css +131 -121
- package/src/assets/design/index.css +3 -3
- package/src/assets/design/size.css +37 -35
- package/src/assets/design/typography.css +37 -37
- package/src/assets/icons/defaultIcons.js +247 -232
- package/src/assets/suneditor-contents.css +779 -778
- package/src/assets/suneditor.css +43 -35
- package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
- package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
- package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
- package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
- package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
- package/src/core/base/eventManager.js +1550 -1484
- package/src/core/base/history.js +355 -355
- package/src/core/class/char.js +163 -162
- package/src/core/class/component.js +856 -842
- package/src/core/class/format.js +3433 -3422
- package/src/core/class/html.js +1927 -1890
- package/src/core/class/menu.js +357 -346
- package/src/core/class/nodeTransform.js +424 -424
- package/src/core/class/offset.js +858 -891
- package/src/core/class/selection.js +710 -620
- package/src/core/class/shortcuts.js +98 -98
- package/src/core/class/toolbar.js +438 -430
- package/src/core/class/ui.js +424 -422
- package/src/core/class/viewer.js +750 -750
- package/src/core/editor.js +1810 -1708
- package/src/core/section/actives.js +268 -241
- package/src/core/section/constructor.js +1348 -1661
- package/src/core/section/context.js +102 -102
- package/src/core/section/documentType.js +582 -561
- package/src/core/section/options.js +367 -0
- package/src/core/util/instanceCheck.js +59 -0
- package/src/editorInjector/_classes.js +36 -36
- package/src/editorInjector/_core.js +92 -92
- package/src/editorInjector/index.js +75 -75
- package/src/events.js +634 -622
- package/src/helper/clipboard.js +59 -59
- package/src/helper/converter.js +586 -564
- package/src/helper/dom/domCheck.js +304 -304
- package/src/helper/dom/domQuery.js +677 -669
- package/src/helper/dom/domUtils.js +618 -557
- package/src/helper/dom/index.js +12 -12
- package/src/helper/env.js +249 -240
- package/src/helper/index.js +25 -25
- package/src/helper/keyCodeMap.js +183 -183
- package/src/helper/numbers.js +72 -72
- package/src/helper/unicode.js +47 -47
- package/src/langs/ckb.js +231 -231
- package/src/langs/cs.js +231 -231
- package/src/langs/da.js +231 -231
- package/src/langs/de.js +231 -231
- package/src/langs/en.js +230 -230
- package/src/langs/es.js +231 -231
- package/src/langs/fa.js +231 -231
- package/src/langs/fr.js +231 -231
- package/src/langs/he.js +231 -231
- package/src/langs/hu.js +230 -230
- package/src/langs/index.js +28 -28
- package/src/langs/it.js +231 -231
- package/src/langs/ja.js +230 -230
- package/src/langs/km.js +230 -230
- package/src/langs/ko.js +230 -230
- package/src/langs/lv.js +231 -231
- package/src/langs/nl.js +231 -231
- package/src/langs/pl.js +231 -231
- package/src/langs/pt_br.js +231 -231
- package/src/langs/ro.js +231 -231
- package/src/langs/ru.js +231 -231
- package/src/langs/se.js +231 -231
- package/src/langs/tr.js +231 -231
- package/src/langs/uk.js +231 -231
- package/src/langs/ur.js +231 -231
- package/src/langs/zh_cn.js +231 -231
- package/src/modules/ApiManager.js +191 -191
- package/src/modules/Browser.js +669 -667
- package/src/modules/ColorPicker.js +364 -362
- package/src/modules/Controller.js +474 -454
- package/src/modules/Figure.js +1620 -1617
- package/src/modules/FileManager.js +359 -359
- package/src/modules/HueSlider.js +577 -565
- package/src/modules/Modal.js +346 -346
- package/src/modules/ModalAnchorEditor.js +643 -643
- package/src/modules/SelectMenu.js +549 -549
- package/src/modules/_DragHandle.js +17 -17
- package/src/modules/index.js +14 -14
- package/src/plugins/browser/audioGallery.js +83 -83
- package/src/plugins/browser/fileBrowser.js +103 -103
- package/src/plugins/browser/fileGallery.js +83 -83
- package/src/plugins/browser/imageGallery.js +81 -81
- package/src/plugins/browser/videoGallery.js +103 -103
- package/src/plugins/command/blockquote.js +61 -60
- package/src/plugins/command/exportPDF.js +134 -134
- package/src/plugins/command/fileUpload.js +456 -456
- package/src/plugins/command/list_bulleted.js +149 -148
- package/src/plugins/command/list_numbered.js +152 -151
- package/src/plugins/dropdown/align.js +157 -155
- package/src/plugins/dropdown/backgroundColor.js +108 -104
- package/src/plugins/dropdown/font.js +141 -137
- package/src/plugins/dropdown/fontColor.js +109 -105
- package/src/plugins/dropdown/formatBlock.js +170 -178
- package/src/plugins/dropdown/hr.js +152 -152
- package/src/plugins/dropdown/layout.js +83 -83
- package/src/plugins/dropdown/lineHeight.js +131 -130
- package/src/plugins/dropdown/list.js +123 -122
- package/src/plugins/dropdown/paragraphStyle.js +138 -138
- package/src/plugins/dropdown/table.js +4110 -4000
- package/src/plugins/dropdown/template.js +83 -83
- package/src/plugins/dropdown/textStyle.js +149 -149
- package/src/plugins/field/mention.js +242 -242
- package/src/plugins/index.js +120 -120
- package/src/plugins/input/fontSize.js +414 -410
- package/src/plugins/input/pageNavigator.js +71 -70
- package/src/plugins/modal/audio.js +677 -677
- package/src/plugins/modal/drawing.js +537 -531
- package/src/plugins/modal/embed.js +886 -886
- package/src/plugins/modal/image.js +1377 -1376
- package/src/plugins/modal/link.js +248 -240
- package/src/plugins/modal/math.js +563 -563
- package/src/plugins/modal/video.js +1226 -1226
- package/src/plugins/popup/anchor.js +224 -222
- package/src/suneditor.js +114 -107
- package/src/themes/dark.css +132 -122
- package/src/typedef.js +132 -130
- package/types/assets/icons/defaultIcons.d.ts +8 -0
- package/types/core/base/eventManager.d.ts +29 -4
- package/types/core/class/char.d.ts +2 -1
- package/types/core/class/component.d.ts +1 -2
- package/types/core/class/format.d.ts +8 -1
- package/types/core/class/html.d.ts +8 -0
- package/types/core/class/menu.d.ts +8 -0
- package/types/core/class/offset.d.ts +24 -26
- package/types/core/class/selection.d.ts +2 -0
- package/types/core/class/toolbar.d.ts +6 -0
- package/types/core/class/ui.d.ts +1 -1
- package/types/core/editor.d.ts +34 -12
- package/types/core/section/constructor.d.ts +5 -638
- package/types/core/section/documentType.d.ts +12 -2
- package/types/core/section/options.d.ts +740 -0
- package/types/core/util/instanceCheck.d.ts +50 -0
- package/types/editorInjector/_core.d.ts +5 -5
- package/types/editorInjector/index.d.ts +2 -2
- package/types/events.d.ts +2 -0
- package/types/helper/converter.d.ts +9 -0
- package/types/helper/dom/domQuery.d.ts +5 -5
- package/types/helper/dom/domUtils.d.ts +8 -0
- package/types/helper/env.d.ts +6 -1
- package/types/helper/index.d.ts +4 -1
- package/types/index.d.ts +122 -120
- package/types/langs/_Lang.d.ts +194 -194
- package/types/modules/ColorPicker.d.ts +5 -1
- package/types/modules/Controller.d.ts +8 -4
- package/types/modules/Figure.d.ts +2 -1
- package/types/modules/HueSlider.d.ts +4 -1
- package/types/modules/SelectMenu.d.ts +1 -1
- package/types/plugins/command/blockquote.d.ts +1 -0
- package/types/plugins/command/list_bulleted.d.ts +1 -0
- package/types/plugins/command/list_numbered.d.ts +1 -0
- package/types/plugins/dropdown/align.d.ts +1 -0
- package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
- package/types/plugins/dropdown/font.d.ts +1 -0
- package/types/plugins/dropdown/fontColor.d.ts +1 -0
- package/types/plugins/dropdown/formatBlock.d.ts +3 -2
- package/types/plugins/dropdown/lineHeight.d.ts +1 -0
- package/types/plugins/dropdown/list.d.ts +1 -0
- package/types/plugins/dropdown/table.d.ts +6 -0
- package/types/plugins/input/fontSize.d.ts +1 -0
- package/types/plugins/modal/drawing.d.ts +4 -0
- package/types/plugins/modal/link.d.ts +32 -15
- package/types/suneditor.d.ts +13 -9
- package/types/typedef.d.ts +8 -0
|
@@ -1,424 +1,424 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Node util class
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
-
import { dom, unicode, numbers } from '../../helper';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Omit<NodeTransform & Partial<__se__EditorInjector>, 'nodeTransform'>} NodeTransformThis
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @constructor
|
|
14
|
-
* @this {NodeTransformThis}
|
|
15
|
-
* @description Node utility class. split, merge, etc.
|
|
16
|
-
* @param {__se__EditorCore} editor - The root editor instance
|
|
17
|
-
*/
|
|
18
|
-
function NodeTransform(editor) {
|
|
19
|
-
CoreInjector.call(this, editor);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
NodeTransform.prototype = {
|
|
23
|
-
/**
|
|
24
|
-
* @this {NodeTransformThis}
|
|
25
|
-
* @template {HTMLElement} T
|
|
26
|
-
* @description Split all tags based on "baseNode"
|
|
27
|
-
* @param {Node} baseNode Element or text node on which to base
|
|
28
|
-
* @param {?number|Node} offset Text offset of "baseNode" (Only valid when "baseNode" is a text node)
|
|
29
|
-
* @param {number} [depth=0] The nesting depth of the element being split. (default: 0)
|
|
30
|
-
* @returns {T} The last element of the splited tag.
|
|
31
|
-
*/
|
|
32
|
-
split(baseNode, offset, depth) {
|
|
33
|
-
if (dom.check.isWysiwygFrame(baseNode) || this.component.is(baseNode) || !baseNode) return /** @type {T} */ (baseNode);
|
|
34
|
-
|
|
35
|
-
if (offset && !numbers.is(offset)) {
|
|
36
|
-
const children = baseNode.childNodes;
|
|
37
|
-
let index = dom.query.getPositionIndex(/** @type {Node} */ (offset));
|
|
38
|
-
const prev = baseNode.cloneNode(false);
|
|
39
|
-
const next = baseNode.cloneNode(false);
|
|
40
|
-
for (let i = 0, len = children.length; i < len; i++) {
|
|
41
|
-
if (i < index) prev.appendChild(children[i]);
|
|
42
|
-
else if (i > index) next.appendChild(children[i]);
|
|
43
|
-
else continue;
|
|
44
|
-
i--;
|
|
45
|
-
len--;
|
|
46
|
-
index--;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (prev.childNodes.length > 0) baseNode.parentNode.insertBefore(prev, baseNode);
|
|
50
|
-
if (next.childNodes.length > 0) baseNode.parentNode.insertBefore(next, /** @type {HTMLElement|Text} */ (baseNode).nextElementSibling);
|
|
51
|
-
|
|
52
|
-
return /** @type {T} */ (baseNode);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const bp = baseNode.parentNode;
|
|
56
|
-
let index = 0;
|
|
57
|
-
let suffixIndex = 1;
|
|
58
|
-
let next = true;
|
|
59
|
-
let newEl, children, temp;
|
|
60
|
-
if (!depth || depth < 0) depth = 0;
|
|
61
|
-
|
|
62
|
-
if (dom.check.isText(baseNode)) {
|
|
63
|
-
index = dom.query.getPositionIndex(baseNode);
|
|
64
|
-
offset = Number(offset);
|
|
65
|
-
if (offset >= 0 && baseNode.length !== offset) {
|
|
66
|
-
baseNode.splitText(offset);
|
|
67
|
-
const after = /** @type {Text} */ (dom.query.getNodeFromPath([index + 1], bp));
|
|
68
|
-
if (dom.check.isZeroWidth(after)) after.data = unicode.zeroWidthSpace;
|
|
69
|
-
}
|
|
70
|
-
} else if (baseNode.nodeType === 1) {
|
|
71
|
-
if (offset === 0) {
|
|
72
|
-
while (baseNode.firstChild) {
|
|
73
|
-
baseNode = baseNode.firstChild;
|
|
74
|
-
}
|
|
75
|
-
if (baseNode.nodeType === 3) {
|
|
76
|
-
const after = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
77
|
-
baseNode.parentNode.insertBefore(after, baseNode);
|
|
78
|
-
baseNode = after;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!baseNode.previousSibling) {
|
|
83
|
-
if (dom.query.getNodeDepth(baseNode) === depth) next = false;
|
|
84
|
-
} else {
|
|
85
|
-
baseNode = baseNode.previousSibling;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (baseNode.nodeType === 1) suffixIndex = 0;
|
|
90
|
-
let depthEl = baseNode;
|
|
91
|
-
while (dom.query.getNodeDepth(depthEl) > depth) {
|
|
92
|
-
index = dom.query.getPositionIndex(depthEl) + suffixIndex;
|
|
93
|
-
depthEl = depthEl.parentNode;
|
|
94
|
-
|
|
95
|
-
temp = newEl;
|
|
96
|
-
newEl = depthEl.cloneNode(false);
|
|
97
|
-
children = depthEl.childNodes;
|
|
98
|
-
|
|
99
|
-
if (temp) {
|
|
100
|
-
if (dom.check.isListCell(newEl) && dom.check.isList(temp) && temp.firstElementChild) {
|
|
101
|
-
newEl.innerHTML = temp.firstElementChild.innerHTML;
|
|
102
|
-
dom.utils.removeItem(temp.firstElementChild);
|
|
103
|
-
if (temp.children.length > 0) newEl.appendChild(temp);
|
|
104
|
-
} else {
|
|
105
|
-
newEl.appendChild(temp);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
while (children[index]) {
|
|
110
|
-
newEl.appendChild(children[index]);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (depthEl.childNodes.length <= 1 && (!depthEl.firstChild || depthEl.firstChild.textContent.length === 0)) /** @type {HTMLElement} */ (depthEl).innerHTML = '<br>';
|
|
115
|
-
|
|
116
|
-
const pElement = depthEl.parentNode;
|
|
117
|
-
if (next) depthEl = depthEl.nextSibling;
|
|
118
|
-
if (!newEl) return /** @type {T} */ (depthEl);
|
|
119
|
-
|
|
120
|
-
this.mergeSameTags(newEl, null, false);
|
|
121
|
-
this.mergeNestedTags(newEl, dom.check.isList);
|
|
122
|
-
|
|
123
|
-
if (newEl.childNodes.length > 0) pElement.insertBefore(newEl, depthEl);
|
|
124
|
-
else newEl = depthEl;
|
|
125
|
-
|
|
126
|
-
if (dom.check.isListCell(newEl) && newEl.children && dom.check.isList(newEl.children[0])) {
|
|
127
|
-
newEl.insertBefore(dom.utils.createElement('BR'), newEl.children[0]);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (bp.childNodes.length === 0) dom.utils.removeItem(bp);
|
|
131
|
-
|
|
132
|
-
return /** @type {T} */ (newEl);
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* @this {NodeTransformThis}
|
|
137
|
-
* @description Use with "npdePath (dom-query-GetNodePath)" to merge the same attributes and tags if they are present and modify the nodepath.
|
|
138
|
-
* - If "offset" has been changed, it will return as much "offset" as it has been modified.
|
|
139
|
-
* - An array containing change offsets is returned in the order of the "nodePathArray" array.
|
|
140
|
-
* @param {Node} element Element
|
|
141
|
-
* @param {?number[][]=} nodePathArray Array of NodePath object ([dom-query-GetNodePath(), ..])
|
|
142
|
-
* @param {?boolean=} onlyText If true, non-text nodes like 'span', 'strong'.. are ignored.
|
|
143
|
-
* @returns {Array<number>} [offset, ..]
|
|
144
|
-
*/
|
|
145
|
-
mergeSameTags(element, nodePathArray, onlyText) {
|
|
146
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
147
|
-
const inst = this;
|
|
148
|
-
const nodePathLen = nodePathArray ? nodePathArray.length : 0;
|
|
149
|
-
let offsets = null;
|
|
150
|
-
|
|
151
|
-
if (nodePathLen) {
|
|
152
|
-
offsets = Array.apply(null, new Array(nodePathLen)).map(Number.prototype.valueOf, 0);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
(function recursionFunc(current, depth, depthIndex) {
|
|
156
|
-
const children = current.childNodes;
|
|
157
|
-
|
|
158
|
-
for (let i = 0, len = children.length, child, next; i < len; i++) {
|
|
159
|
-
child = /** @type {HTMLElement} */ (children[i]);
|
|
160
|
-
next = /** @type {HTMLElement} */ (children[i + 1]);
|
|
161
|
-
if (!child) break;
|
|
162
|
-
if (dom.check.isBreak(child) || dom.check.isMedia(child) || dom.check.isInputElement(child)) continue;
|
|
163
|
-
if ((onlyText && inst.format._isIgnoreNodeChange(child)) || (!onlyText && (dom.check.isTableElements(child) || dom.check.isListCell(child) || (inst.format.isLine(child) && !inst.format.isBrLine(child))))) {
|
|
164
|
-
if (dom.check.isTableElements(child) || dom.check.isListCell(child)) {
|
|
165
|
-
recursionFunc(child, depth + 1, i);
|
|
166
|
-
}
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (len === 1 && current.nodeName === child.nodeName && current.parentNode) {
|
|
170
|
-
// update nodePath
|
|
171
|
-
if (nodePathLen) {
|
|
172
|
-
let path, c, p, cDepth, spliceDepth;
|
|
173
|
-
for (let n = 0; n < nodePathLen; n++) {
|
|
174
|
-
path = nodePathArray[n];
|
|
175
|
-
if (path && path[depth] === i) {
|
|
176
|
-
c = child;
|
|
177
|
-
p = current;
|
|
178
|
-
cDepth = depth;
|
|
179
|
-
spliceDepth = true;
|
|
180
|
-
while (cDepth >= 0) {
|
|
181
|
-
if (dom.utils.getArrayIndex(p.childNodes, c) !== path[cDepth]) {
|
|
182
|
-
spliceDepth = false;
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
c = child.parentNode;
|
|
186
|
-
p = c.parentNode;
|
|
187
|
-
cDepth--;
|
|
188
|
-
}
|
|
189
|
-
if (spliceDepth) {
|
|
190
|
-
path.splice(depth, 1);
|
|
191
|
-
path[depth] = i;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// merge tag
|
|
198
|
-
dom.utils.copyTagAttributes(child, current);
|
|
199
|
-
current.parentNode.insertBefore(child, current);
|
|
200
|
-
dom.utils.removeItem(current);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (!next) {
|
|
204
|
-
if (child.nodeType === 1) recursionFunc(child, depth + 1, i);
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (child.nodeName === next.nodeName && dom.check.isSameAttributes(child, next) && child.getAttribute?.('href') === next.getAttribute?.('href')) {
|
|
209
|
-
const childs = child.childNodes;
|
|
210
|
-
let childLength = 0;
|
|
211
|
-
for (let n = 0, nLen = childs.length; n < nLen; n++) {
|
|
212
|
-
if (childs[n].textContent.length > 0) childLength++;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const l = child.lastChild;
|
|
216
|
-
const r = next.firstChild;
|
|
217
|
-
let addOffset = 0;
|
|
218
|
-
if (l && r) {
|
|
219
|
-
const textOffset = l.nodeType === 3 && r.nodeType === 3;
|
|
220
|
-
addOffset = l.textContent.length;
|
|
221
|
-
let tempL = l.previousSibling;
|
|
222
|
-
while (tempL && tempL.nodeType === 3) {
|
|
223
|
-
addOffset += tempL.textContent.length;
|
|
224
|
-
tempL = tempL.previousSibling;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (childLength > 0 && l.nodeType === 3 && r.nodeType === 3 && (l.textContent.length > 0 || r.textContent.length > 0)) childLength--;
|
|
228
|
-
|
|
229
|
-
if (nodePathLen) {
|
|
230
|
-
let path = null;
|
|
231
|
-
for (let n = 0; n < nodePathLen; n++) {
|
|
232
|
-
path = nodePathArray[n];
|
|
233
|
-
if (path && path[depth] > i) {
|
|
234
|
-
if (depth > 0 && path[depth - 1] !== depthIndex) continue;
|
|
235
|
-
|
|
236
|
-
path[depth] -= 1;
|
|
237
|
-
if (path[depth + 1] >= 0 && path[depth] === i) {
|
|
238
|
-
path[depth + 1] += childLength;
|
|
239
|
-
if (textOffset) {
|
|
240
|
-
if (l && l.nodeType === 3 && r && r.nodeType === 3) {
|
|
241
|
-
offsets[n] += addOffset;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (child.nodeType === 3) {
|
|
251
|
-
addOffset = child.textContent.length;
|
|
252
|
-
child.textContent += next.textContent;
|
|
253
|
-
if (nodePathLen) {
|
|
254
|
-
let path = null;
|
|
255
|
-
for (let n = 0; n < nodePathLen; n++) {
|
|
256
|
-
path = nodePathArray[n];
|
|
257
|
-
if (path && path[depth] > i) {
|
|
258
|
-
if (depth > 0 && path[depth - 1] !== depthIndex) continue;
|
|
259
|
-
|
|
260
|
-
path[depth] -= 1;
|
|
261
|
-
if (path[depth + 1] >= 0 && path[depth] === i) {
|
|
262
|
-
path[depth + 1] += childLength;
|
|
263
|
-
offsets[n] += addOffset;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
child.innerHTML += next.innerHTML;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
dom.utils.removeItem(next);
|
|
273
|
-
i--;
|
|
274
|
-
} else if (child.nodeType === 1) {
|
|
275
|
-
recursionFunc(child, depth + 1, i);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
})(element, 0, 0);
|
|
279
|
-
|
|
280
|
-
return offsets;
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* @this {NodeTransformThis}
|
|
285
|
-
* @description Remove nested tags without other child nodes.
|
|
286
|
-
* @param {Node} element Element object
|
|
287
|
-
* @param {?(current: Node) => boolean|string=} validation Validation function / String("tag1|tag2..") / If null, all tags are applicable.
|
|
288
|
-
*/
|
|
289
|
-
mergeNestedTags(element, validation) {
|
|
290
|
-
if (typeof validation === 'string') {
|
|
291
|
-
const tagRegExp = new RegExp(`^(${validation ? validation : '.+'})$`, 'i');
|
|
292
|
-
validation = (current) => tagRegExp.test(current.nodeName);
|
|
293
|
-
} else if (typeof validation !== 'function') {
|
|
294
|
-
validation = () => true;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
(function recursionFunc(current) {
|
|
298
|
-
let children = current.children;
|
|
299
|
-
if (children.length === 1 && children[0].nodeName === current.nodeName && validation(current)) {
|
|
300
|
-
const temp = children[0];
|
|
301
|
-
children = temp.children;
|
|
302
|
-
while (children[0]) {
|
|
303
|
-
current.appendChild(children[0]);
|
|
304
|
-
}
|
|
305
|
-
current.removeChild(temp);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
for (let i = 0, len = current.children.length; i < len; i++) {
|
|
309
|
-
recursionFunc(current.children[i]);
|
|
310
|
-
}
|
|
311
|
-
})(/** @type {Element} */ (element));
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* @this {NodeTransformThis}
|
|
316
|
-
* @description Delete itself and all parent nodes that match the condition.
|
|
317
|
-
* - Returns an {sc: previousSibling, ec: nextSibling}(the deleted node reference) or null.
|
|
318
|
-
* @param {Node} item Node to be remove
|
|
319
|
-
* @param {?(current: Node) => boolean=} validation Validation function. default(Deleted if it only have breakLine and blanks)
|
|
320
|
-
* @param {?Node=} stopParent Stop when the parent node reaches stopParent
|
|
321
|
-
* @returns {{sc: Node|null, ec: Node|null}|null} {sc: previousSibling, ec: nextSibling} (the deleted node reference) or null.
|
|
322
|
-
*/
|
|
323
|
-
removeAllParents(item, validation, stopParent) {
|
|
324
|
-
if (!item) return null;
|
|
325
|
-
let cc = null;
|
|
326
|
-
if (!validation) {
|
|
327
|
-
validation = (current) => {
|
|
328
|
-
if (current === stopParent || this.component.is(current)) return false;
|
|
329
|
-
const text = current.textContent.trim();
|
|
330
|
-
return text.length === 0 || /^(\n|\u200B)+$/.test(text);
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
(function recursionFunc(element) {
|
|
335
|
-
if (!dom.check.isWysiwygFrame(element)) {
|
|
336
|
-
const parent = element.parentNode;
|
|
337
|
-
if (parent && validation(element)) {
|
|
338
|
-
cc = {
|
|
339
|
-
sc: element.previousElementSibling,
|
|
340
|
-
ec: element.nextElementSibling
|
|
341
|
-
};
|
|
342
|
-
dom.utils.removeItem(element);
|
|
343
|
-
recursionFunc(/** @type {Element} */ (parent));
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
})(/** @type {Element} */ (item));
|
|
347
|
-
|
|
348
|
-
return cc;
|
|
349
|
-
},
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* @this {NodeTransformThis}
|
|
353
|
-
* @description Delete a empty child node of argument element
|
|
354
|
-
* @param {Node} element Element node
|
|
355
|
-
* @param {?Node} notRemoveNode Do not remove node
|
|
356
|
-
* @param {boolean} forceDelete When all child nodes are deleted, the parent node is also deleted.
|
|
357
|
-
*/
|
|
358
|
-
removeEmptyNode(element, notRemoveNode, forceDelete) {
|
|
359
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
360
|
-
const inst = this;
|
|
361
|
-
const allowedEmptyTags = this.options.get('allowedEmptyTags');
|
|
362
|
-
|
|
363
|
-
if (notRemoveNode) {
|
|
364
|
-
notRemoveNode = dom.query.getParentElement(notRemoveNode, (current) => element === current.parentElement);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
(function recursionFunc(current) {
|
|
368
|
-
if (inst.format._notTextNode(current) || current === notRemoveNode || dom.check.isNonEditable(current)) return 0;
|
|
369
|
-
if (current !== element && dom.check.isZeroWidth(current.textContent) && (!current.firstChild || !dom.check.isBreak(current.firstChild)) && !current.querySelector(allowedEmptyTags)) {
|
|
370
|
-
if (current.parentNode) {
|
|
371
|
-
current.parentNode.removeChild(current);
|
|
372
|
-
return -1;
|
|
373
|
-
}
|
|
374
|
-
} else {
|
|
375
|
-
const children = current.children;
|
|
376
|
-
for (let i = 0, len = children.length, r = 0; i < len; i++) {
|
|
377
|
-
if (!children[i + r] || inst.component.is(children[i + r])) continue;
|
|
378
|
-
r += recursionFunc(children[i + r]);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return 0;
|
|
383
|
-
})(/** @type {Element} */ (element));
|
|
384
|
-
|
|
385
|
-
if (element.childNodes.length === 0) {
|
|
386
|
-
if (forceDelete) {
|
|
387
|
-
dom.utils.removeItem(element);
|
|
388
|
-
} else {
|
|
389
|
-
/** @type {HTMLElement} */ (element).innerHTML = '<br>';
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* @this {NodeTransformThis}
|
|
396
|
-
* @description Creates a nested node structure from the given array of nodes.
|
|
397
|
-
* @param {__se__NodeCollection} nodeArray An array of nodes to clone. The first node in the array will be the top-level parent.
|
|
398
|
-
* @param {?(current: Node) => boolean=} validate A validate function.
|
|
399
|
-
* @returns {{ parent: Node, inner: Node }} An object containing the top-level parent node and the innermost child node.
|
|
400
|
-
*/
|
|
401
|
-
createNestedNode(nodeArray, validate) {
|
|
402
|
-
if (typeof validate !== 'function') validate = () => true;
|
|
403
|
-
|
|
404
|
-
const el = /** @type {HTMLElement} */ (nodeArray[0].cloneNode(false));
|
|
405
|
-
let n = el;
|
|
406
|
-
for (let i = 1, len = nodeArray.length, t; i < len; i++) {
|
|
407
|
-
if (!validate(nodeArray[i])) continue;
|
|
408
|
-
t = /** @type {HTMLElement} */ (nodeArray[i].cloneNode(false));
|
|
409
|
-
n.appendChild(t);
|
|
410
|
-
n = t;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
n.innerHTML = '';
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
parent: el,
|
|
417
|
-
inner: n
|
|
418
|
-
};
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
constructor: NodeTransform
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
export default NodeTransform;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Node util class
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
+
import { dom, unicode, numbers } from '../../helper';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Omit<NodeTransform & Partial<__se__EditorInjector>, 'nodeTransform'>} NodeTransformThis
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @constructor
|
|
14
|
+
* @this {NodeTransformThis}
|
|
15
|
+
* @description Node utility class. split, merge, etc.
|
|
16
|
+
* @param {__se__EditorCore} editor - The root editor instance
|
|
17
|
+
*/
|
|
18
|
+
function NodeTransform(editor) {
|
|
19
|
+
CoreInjector.call(this, editor);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
NodeTransform.prototype = {
|
|
23
|
+
/**
|
|
24
|
+
* @this {NodeTransformThis}
|
|
25
|
+
* @template {HTMLElement} T
|
|
26
|
+
* @description Split all tags based on "baseNode"
|
|
27
|
+
* @param {Node} baseNode Element or text node on which to base
|
|
28
|
+
* @param {?number|Node} offset Text offset of "baseNode" (Only valid when "baseNode" is a text node)
|
|
29
|
+
* @param {number} [depth=0] The nesting depth of the element being split. (default: 0)
|
|
30
|
+
* @returns {T} The last element of the splited tag.
|
|
31
|
+
*/
|
|
32
|
+
split(baseNode, offset, depth) {
|
|
33
|
+
if (dom.check.isWysiwygFrame(baseNode) || this.component.is(baseNode) || !baseNode) return /** @type {T} */ (baseNode);
|
|
34
|
+
|
|
35
|
+
if (offset && !numbers.is(offset)) {
|
|
36
|
+
const children = baseNode.childNodes;
|
|
37
|
+
let index = dom.query.getPositionIndex(/** @type {Node} */ (offset));
|
|
38
|
+
const prev = baseNode.cloneNode(false);
|
|
39
|
+
const next = baseNode.cloneNode(false);
|
|
40
|
+
for (let i = 0, len = children.length; i < len; i++) {
|
|
41
|
+
if (i < index) prev.appendChild(children[i]);
|
|
42
|
+
else if (i > index) next.appendChild(children[i]);
|
|
43
|
+
else continue;
|
|
44
|
+
i--;
|
|
45
|
+
len--;
|
|
46
|
+
index--;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (prev.childNodes.length > 0) baseNode.parentNode.insertBefore(prev, baseNode);
|
|
50
|
+
if (next.childNodes.length > 0) baseNode.parentNode.insertBefore(next, /** @type {HTMLElement|Text} */ (baseNode).nextElementSibling);
|
|
51
|
+
|
|
52
|
+
return /** @type {T} */ (baseNode);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const bp = baseNode.parentNode;
|
|
56
|
+
let index = 0;
|
|
57
|
+
let suffixIndex = 1;
|
|
58
|
+
let next = true;
|
|
59
|
+
let newEl, children, temp;
|
|
60
|
+
if (!depth || depth < 0) depth = 0;
|
|
61
|
+
|
|
62
|
+
if (dom.check.isText(baseNode)) {
|
|
63
|
+
index = dom.query.getPositionIndex(baseNode);
|
|
64
|
+
offset = Number(offset);
|
|
65
|
+
if (offset >= 0 && baseNode.length !== offset) {
|
|
66
|
+
baseNode.splitText(offset);
|
|
67
|
+
const after = /** @type {Text} */ (dom.query.getNodeFromPath([index + 1], bp));
|
|
68
|
+
if (dom.check.isZeroWidth(after)) after.data = unicode.zeroWidthSpace;
|
|
69
|
+
}
|
|
70
|
+
} else if (baseNode.nodeType === 1) {
|
|
71
|
+
if (offset === 0) {
|
|
72
|
+
while (baseNode.firstChild) {
|
|
73
|
+
baseNode = baseNode.firstChild;
|
|
74
|
+
}
|
|
75
|
+
if (baseNode.nodeType === 3) {
|
|
76
|
+
const after = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
77
|
+
baseNode.parentNode.insertBefore(after, baseNode);
|
|
78
|
+
baseNode = after;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!baseNode.previousSibling) {
|
|
83
|
+
if (dom.query.getNodeDepth(baseNode) === depth) next = false;
|
|
84
|
+
} else {
|
|
85
|
+
baseNode = baseNode.previousSibling;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (baseNode.nodeType === 1) suffixIndex = 0;
|
|
90
|
+
let depthEl = baseNode;
|
|
91
|
+
while (dom.query.getNodeDepth(depthEl) > depth) {
|
|
92
|
+
index = dom.query.getPositionIndex(depthEl) + suffixIndex;
|
|
93
|
+
depthEl = depthEl.parentNode;
|
|
94
|
+
|
|
95
|
+
temp = newEl;
|
|
96
|
+
newEl = depthEl.cloneNode(false);
|
|
97
|
+
children = depthEl.childNodes;
|
|
98
|
+
|
|
99
|
+
if (temp) {
|
|
100
|
+
if (dom.check.isListCell(newEl) && dom.check.isList(temp) && temp.firstElementChild) {
|
|
101
|
+
newEl.innerHTML = temp.firstElementChild.innerHTML;
|
|
102
|
+
dom.utils.removeItem(temp.firstElementChild);
|
|
103
|
+
if (temp.children.length > 0) newEl.appendChild(temp);
|
|
104
|
+
} else {
|
|
105
|
+
newEl.appendChild(temp);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
while (children[index]) {
|
|
110
|
+
newEl.appendChild(children[index]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (depthEl.childNodes.length <= 1 && (!depthEl.firstChild || depthEl.firstChild.textContent.length === 0)) /** @type {HTMLElement} */ (depthEl).innerHTML = '<br>';
|
|
115
|
+
|
|
116
|
+
const pElement = depthEl.parentNode;
|
|
117
|
+
if (next) depthEl = depthEl.nextSibling;
|
|
118
|
+
if (!newEl) return /** @type {T} */ (depthEl);
|
|
119
|
+
|
|
120
|
+
this.mergeSameTags(newEl, null, false);
|
|
121
|
+
this.mergeNestedTags(newEl, dom.check.isList);
|
|
122
|
+
|
|
123
|
+
if (newEl.childNodes.length > 0) pElement.insertBefore(newEl, depthEl);
|
|
124
|
+
else newEl = depthEl;
|
|
125
|
+
|
|
126
|
+
if (dom.check.isListCell(newEl) && newEl.children && dom.check.isList(newEl.children[0])) {
|
|
127
|
+
newEl.insertBefore(dom.utils.createElement('BR'), newEl.children[0]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (bp.childNodes.length === 0) dom.utils.removeItem(bp);
|
|
131
|
+
|
|
132
|
+
return /** @type {T} */ (newEl);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @this {NodeTransformThis}
|
|
137
|
+
* @description Use with "npdePath (dom-query-GetNodePath)" to merge the same attributes and tags if they are present and modify the nodepath.
|
|
138
|
+
* - If "offset" has been changed, it will return as much "offset" as it has been modified.
|
|
139
|
+
* - An array containing change offsets is returned in the order of the "nodePathArray" array.
|
|
140
|
+
* @param {Node} element Element
|
|
141
|
+
* @param {?number[][]=} nodePathArray Array of NodePath object ([dom-query-GetNodePath(), ..])
|
|
142
|
+
* @param {?boolean=} onlyText If true, non-text nodes like 'span', 'strong'.. are ignored.
|
|
143
|
+
* @returns {Array<number>} [offset, ..]
|
|
144
|
+
*/
|
|
145
|
+
mergeSameTags(element, nodePathArray, onlyText) {
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
147
|
+
const inst = this;
|
|
148
|
+
const nodePathLen = nodePathArray ? nodePathArray.length : 0;
|
|
149
|
+
let offsets = null;
|
|
150
|
+
|
|
151
|
+
if (nodePathLen) {
|
|
152
|
+
offsets = Array.apply(null, new Array(nodePathLen)).map(Number.prototype.valueOf, 0);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
(function recursionFunc(current, depth, depthIndex) {
|
|
156
|
+
const children = current.childNodes;
|
|
157
|
+
|
|
158
|
+
for (let i = 0, len = children.length, child, next; i < len; i++) {
|
|
159
|
+
child = /** @type {HTMLElement} */ (children[i]);
|
|
160
|
+
next = /** @type {HTMLElement} */ (children[i + 1]);
|
|
161
|
+
if (!child) break;
|
|
162
|
+
if (dom.check.isBreak(child) || dom.check.isMedia(child) || dom.check.isInputElement(child)) continue;
|
|
163
|
+
if ((onlyText && inst.format._isIgnoreNodeChange(child)) || (!onlyText && (dom.check.isTableElements(child) || dom.check.isListCell(child) || (inst.format.isLine(child) && !inst.format.isBrLine(child))))) {
|
|
164
|
+
if (dom.check.isTableElements(child) || dom.check.isListCell(child)) {
|
|
165
|
+
recursionFunc(child, depth + 1, i);
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (len === 1 && current.nodeName === child.nodeName && current.parentNode) {
|
|
170
|
+
// update nodePath
|
|
171
|
+
if (nodePathLen) {
|
|
172
|
+
let path, c, p, cDepth, spliceDepth;
|
|
173
|
+
for (let n = 0; n < nodePathLen; n++) {
|
|
174
|
+
path = nodePathArray[n];
|
|
175
|
+
if (path && path[depth] === i) {
|
|
176
|
+
c = child;
|
|
177
|
+
p = current;
|
|
178
|
+
cDepth = depth;
|
|
179
|
+
spliceDepth = true;
|
|
180
|
+
while (cDepth >= 0) {
|
|
181
|
+
if (dom.utils.getArrayIndex(p.childNodes, c) !== path[cDepth]) {
|
|
182
|
+
spliceDepth = false;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
c = child.parentNode;
|
|
186
|
+
p = c.parentNode;
|
|
187
|
+
cDepth--;
|
|
188
|
+
}
|
|
189
|
+
if (spliceDepth) {
|
|
190
|
+
path.splice(depth, 1);
|
|
191
|
+
path[depth] = i;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// merge tag
|
|
198
|
+
dom.utils.copyTagAttributes(child, current);
|
|
199
|
+
current.parentNode.insertBefore(child, current);
|
|
200
|
+
dom.utils.removeItem(current);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!next) {
|
|
204
|
+
if (child.nodeType === 1) recursionFunc(child, depth + 1, i);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (child.nodeName === next.nodeName && dom.check.isSameAttributes(child, next) && child.getAttribute?.('href') === next.getAttribute?.('href')) {
|
|
209
|
+
const childs = child.childNodes;
|
|
210
|
+
let childLength = 0;
|
|
211
|
+
for (let n = 0, nLen = childs.length; n < nLen; n++) {
|
|
212
|
+
if (childs[n].textContent.length > 0) childLength++;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const l = child.lastChild;
|
|
216
|
+
const r = next.firstChild;
|
|
217
|
+
let addOffset = 0;
|
|
218
|
+
if (l && r) {
|
|
219
|
+
const textOffset = l.nodeType === 3 && r.nodeType === 3;
|
|
220
|
+
addOffset = l.textContent.length;
|
|
221
|
+
let tempL = l.previousSibling;
|
|
222
|
+
while (tempL && tempL.nodeType === 3) {
|
|
223
|
+
addOffset += tempL.textContent.length;
|
|
224
|
+
tempL = tempL.previousSibling;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (childLength > 0 && l.nodeType === 3 && r.nodeType === 3 && (l.textContent.length > 0 || r.textContent.length > 0)) childLength--;
|
|
228
|
+
|
|
229
|
+
if (nodePathLen) {
|
|
230
|
+
let path = null;
|
|
231
|
+
for (let n = 0; n < nodePathLen; n++) {
|
|
232
|
+
path = nodePathArray[n];
|
|
233
|
+
if (path && path[depth] > i) {
|
|
234
|
+
if (depth > 0 && path[depth - 1] !== depthIndex) continue;
|
|
235
|
+
|
|
236
|
+
path[depth] -= 1;
|
|
237
|
+
if (path[depth + 1] >= 0 && path[depth] === i) {
|
|
238
|
+
path[depth + 1] += childLength;
|
|
239
|
+
if (textOffset) {
|
|
240
|
+
if (l && l.nodeType === 3 && r && r.nodeType === 3) {
|
|
241
|
+
offsets[n] += addOffset;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (child.nodeType === 3) {
|
|
251
|
+
addOffset = child.textContent.length;
|
|
252
|
+
child.textContent += next.textContent;
|
|
253
|
+
if (nodePathLen) {
|
|
254
|
+
let path = null;
|
|
255
|
+
for (let n = 0; n < nodePathLen; n++) {
|
|
256
|
+
path = nodePathArray[n];
|
|
257
|
+
if (path && path[depth] > i) {
|
|
258
|
+
if (depth > 0 && path[depth - 1] !== depthIndex) continue;
|
|
259
|
+
|
|
260
|
+
path[depth] -= 1;
|
|
261
|
+
if (path[depth + 1] >= 0 && path[depth] === i) {
|
|
262
|
+
path[depth + 1] += childLength;
|
|
263
|
+
offsets[n] += addOffset;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
child.innerHTML += next.innerHTML;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
dom.utils.removeItem(next);
|
|
273
|
+
i--;
|
|
274
|
+
} else if (child.nodeType === 1) {
|
|
275
|
+
recursionFunc(child, depth + 1, i);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
})(element, 0, 0);
|
|
279
|
+
|
|
280
|
+
return offsets;
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @this {NodeTransformThis}
|
|
285
|
+
* @description Remove nested tags without other child nodes.
|
|
286
|
+
* @param {Node} element Element object
|
|
287
|
+
* @param {?(current: Node) => boolean|string=} validation Validation function / String("tag1|tag2..") / If null, all tags are applicable.
|
|
288
|
+
*/
|
|
289
|
+
mergeNestedTags(element, validation) {
|
|
290
|
+
if (typeof validation === 'string') {
|
|
291
|
+
const tagRegExp = new RegExp(`^(${validation ? validation : '.+'})$`, 'i');
|
|
292
|
+
validation = (current) => tagRegExp.test(current.nodeName);
|
|
293
|
+
} else if (typeof validation !== 'function') {
|
|
294
|
+
validation = () => true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
(function recursionFunc(current) {
|
|
298
|
+
let children = current.children;
|
|
299
|
+
if (children.length === 1 && children[0].nodeName === current.nodeName && validation(current)) {
|
|
300
|
+
const temp = children[0];
|
|
301
|
+
children = temp.children;
|
|
302
|
+
while (children[0]) {
|
|
303
|
+
current.appendChild(children[0]);
|
|
304
|
+
}
|
|
305
|
+
current.removeChild(temp);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for (let i = 0, len = current.children.length; i < len; i++) {
|
|
309
|
+
recursionFunc(current.children[i]);
|
|
310
|
+
}
|
|
311
|
+
})(/** @type {Element} */ (element));
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @this {NodeTransformThis}
|
|
316
|
+
* @description Delete itself and all parent nodes that match the condition.
|
|
317
|
+
* - Returns an {sc: previousSibling, ec: nextSibling}(the deleted node reference) or null.
|
|
318
|
+
* @param {Node} item Node to be remove
|
|
319
|
+
* @param {?(current: Node) => boolean=} validation Validation function. default(Deleted if it only have breakLine and blanks)
|
|
320
|
+
* @param {?Node=} stopParent Stop when the parent node reaches stopParent
|
|
321
|
+
* @returns {{sc: Node|null, ec: Node|null}|null} {sc: previousSibling, ec: nextSibling} (the deleted node reference) or null.
|
|
322
|
+
*/
|
|
323
|
+
removeAllParents(item, validation, stopParent) {
|
|
324
|
+
if (!item) return null;
|
|
325
|
+
let cc = null;
|
|
326
|
+
if (!validation) {
|
|
327
|
+
validation = (current) => {
|
|
328
|
+
if (current === stopParent || this.component.is(current)) return false;
|
|
329
|
+
const text = current.textContent.trim();
|
|
330
|
+
return text.length === 0 || /^(\n|\u200B)+$/.test(text);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
(function recursionFunc(element) {
|
|
335
|
+
if (!dom.check.isWysiwygFrame(element)) {
|
|
336
|
+
const parent = element.parentNode;
|
|
337
|
+
if (parent && validation(element)) {
|
|
338
|
+
cc = {
|
|
339
|
+
sc: element.previousElementSibling,
|
|
340
|
+
ec: element.nextElementSibling
|
|
341
|
+
};
|
|
342
|
+
dom.utils.removeItem(element);
|
|
343
|
+
recursionFunc(/** @type {Element} */ (parent));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
})(/** @type {Element} */ (item));
|
|
347
|
+
|
|
348
|
+
return cc;
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @this {NodeTransformThis}
|
|
353
|
+
* @description Delete a empty child node of argument element
|
|
354
|
+
* @param {Node} element Element node
|
|
355
|
+
* @param {?Node} notRemoveNode Do not remove node
|
|
356
|
+
* @param {boolean} forceDelete When all child nodes are deleted, the parent node is also deleted.
|
|
357
|
+
*/
|
|
358
|
+
removeEmptyNode(element, notRemoveNode, forceDelete) {
|
|
359
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
360
|
+
const inst = this;
|
|
361
|
+
const allowedEmptyTags = this.options.get('allowedEmptyTags');
|
|
362
|
+
|
|
363
|
+
if (notRemoveNode) {
|
|
364
|
+
notRemoveNode = dom.query.getParentElement(notRemoveNode, (current) => element === current.parentElement);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
(function recursionFunc(current) {
|
|
368
|
+
if (inst.format._notTextNode(current) || current === notRemoveNode || dom.check.isNonEditable(current)) return 0;
|
|
369
|
+
if (current !== element && dom.check.isZeroWidth(current.textContent) && (!current.firstChild || !dom.check.isBreak(current.firstChild)) && !current.querySelector(allowedEmptyTags)) {
|
|
370
|
+
if (current.parentNode) {
|
|
371
|
+
current.parentNode.removeChild(current);
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
const children = current.children;
|
|
376
|
+
for (let i = 0, len = children.length, r = 0; i < len; i++) {
|
|
377
|
+
if (!children[i + r] || inst.component.is(children[i + r])) continue;
|
|
378
|
+
r += recursionFunc(children[i + r]);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return 0;
|
|
383
|
+
})(/** @type {Element} */ (element));
|
|
384
|
+
|
|
385
|
+
if (element.childNodes.length === 0) {
|
|
386
|
+
if (forceDelete) {
|
|
387
|
+
dom.utils.removeItem(element);
|
|
388
|
+
} else {
|
|
389
|
+
/** @type {HTMLElement} */ (element).innerHTML = '<br>';
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* @this {NodeTransformThis}
|
|
396
|
+
* @description Creates a nested node structure from the given array of nodes.
|
|
397
|
+
* @param {__se__NodeCollection} nodeArray An array of nodes to clone. The first node in the array will be the top-level parent.
|
|
398
|
+
* @param {?(current: Node) => boolean=} validate A validate function.
|
|
399
|
+
* @returns {{ parent: Node, inner: Node }} An object containing the top-level parent node and the innermost child node.
|
|
400
|
+
*/
|
|
401
|
+
createNestedNode(nodeArray, validate) {
|
|
402
|
+
if (typeof validate !== 'function') validate = () => true;
|
|
403
|
+
|
|
404
|
+
const el = /** @type {HTMLElement} */ (nodeArray[0].cloneNode(false));
|
|
405
|
+
let n = el;
|
|
406
|
+
for (let i = 1, len = nodeArray.length, t; i < len; i++) {
|
|
407
|
+
if (!validate(nodeArray[i])) continue;
|
|
408
|
+
t = /** @type {HTMLElement} */ (nodeArray[i].cloneNode(false));
|
|
409
|
+
n.appendChild(t);
|
|
410
|
+
n = t;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
n.innerHTML = '';
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
parent: el,
|
|
417
|
+
inner: n
|
|
418
|
+
};
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
constructor: NodeTransform
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
export default NodeTransform;
|