suneditor 3.0.0-beta.3 → 3.0.0-beta.30

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.
Files changed (241) hide show
  1. package/CONTRIBUTING.md +8 -8
  2. package/README.md +44 -49
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +95 -53
  6. package/src/assets/design/color.css +2 -2
  7. package/src/assets/design/size.css +2 -0
  8. package/src/assets/icons/defaultIcons.js +16 -1
  9. package/src/assets/suneditor-contents.css +9 -8
  10. package/src/assets/suneditor.css +29 -26
  11. package/src/core/{section → base}/actives.js +20 -12
  12. package/src/core/base/history.js +4 -4
  13. package/src/core/class/char.js +10 -10
  14. package/src/core/class/component.js +146 -57
  15. package/src/core/class/format.js +94 -2458
  16. package/src/core/class/html.js +187 -129
  17. package/src/core/class/inline.js +1853 -0
  18. package/src/core/class/listFormat.js +582 -0
  19. package/src/core/class/menu.js +14 -3
  20. package/src/core/class/nodeTransform.js +9 -14
  21. package/src/core/class/offset.js +162 -197
  22. package/src/core/class/selection.js +137 -34
  23. package/src/core/class/toolbar.js +73 -52
  24. package/src/core/class/ui.js +11 -11
  25. package/src/core/class/viewer.js +56 -55
  26. package/src/core/config/context.js +122 -0
  27. package/src/core/config/frameContext.js +204 -0
  28. package/src/core/config/options.js +639 -0
  29. package/src/core/editor.js +181 -108
  30. package/src/core/event/actions/index.js +229 -0
  31. package/src/core/event/effects/common.registry.js +60 -0
  32. package/src/core/event/effects/keydown.registry.js +551 -0
  33. package/src/core/event/effects/ruleHelpers.js +145 -0
  34. package/src/core/{base → event}/eventManager.js +119 -201
  35. package/src/core/event/executor.js +21 -0
  36. package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +4 -4
  37. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +2 -2
  38. package/src/core/event/handlers/handler_ww_input.js +77 -0
  39. package/src/core/event/handlers/handler_ww_key.js +228 -0
  40. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.js +3 -3
  41. package/src/core/event/ports.js +211 -0
  42. package/src/core/event/reducers/keydown.reducer.js +89 -0
  43. package/src/core/event/rules/keydown.rule.arrow.js +54 -0
  44. package/src/core/event/rules/keydown.rule.backspace.js +202 -0
  45. package/src/core/event/rules/keydown.rule.delete.js +126 -0
  46. package/src/core/event/rules/keydown.rule.enter.js +144 -0
  47. package/src/core/event/rules/keydown.rule.tab.js +29 -0
  48. package/src/core/section/constructor.js +79 -388
  49. package/src/core/section/documentType.js +47 -26
  50. package/src/core/util/instanceCheck.js +59 -0
  51. package/src/editorInjector/_classes.js +4 -0
  52. package/src/editorInjector/_core.js +17 -7
  53. package/src/editorInjector/index.js +10 -2
  54. package/src/events.js +6 -0
  55. package/src/helper/clipboard.js +24 -10
  56. package/src/helper/converter.js +17 -12
  57. package/src/helper/dom/domCheck.js +22 -3
  58. package/src/helper/dom/domQuery.js +91 -45
  59. package/src/helper/dom/domUtils.js +93 -19
  60. package/src/helper/dom/index.js +4 -0
  61. package/src/helper/env.js +11 -7
  62. package/src/helper/keyCodeMap.js +4 -3
  63. package/src/langs/ckb.js +1 -1
  64. package/src/langs/cs.js +1 -1
  65. package/src/langs/da.js +1 -1
  66. package/src/langs/de.js +1 -1
  67. package/src/langs/en.js +1 -1
  68. package/src/langs/es.js +1 -1
  69. package/src/langs/fa.js +1 -1
  70. package/src/langs/fr.js +1 -1
  71. package/src/langs/he.js +1 -1
  72. package/src/langs/hu.js +1 -1
  73. package/src/langs/it.js +1 -1
  74. package/src/langs/ja.js +1 -1
  75. package/src/langs/km.js +1 -1
  76. package/src/langs/ko.js +1 -1
  77. package/src/langs/lv.js +1 -1
  78. package/src/langs/nl.js +1 -1
  79. package/src/langs/pl.js +1 -1
  80. package/src/langs/pt_br.js +10 -10
  81. package/src/langs/ro.js +1 -1
  82. package/src/langs/ru.js +1 -1
  83. package/src/langs/se.js +1 -1
  84. package/src/langs/tr.js +1 -1
  85. package/src/langs/uk.js +1 -1
  86. package/src/langs/ur.js +1 -1
  87. package/src/langs/zh_cn.js +1 -1
  88. package/src/modules/ApiManager.js +25 -18
  89. package/src/modules/Browser.js +52 -61
  90. package/src/modules/ColorPicker.js +37 -38
  91. package/src/modules/Controller.js +85 -79
  92. package/src/modules/Figure.js +275 -187
  93. package/src/modules/FileManager.js +86 -92
  94. package/src/modules/HueSlider.js +67 -35
  95. package/src/modules/Modal.js +84 -77
  96. package/src/modules/ModalAnchorEditor.js +62 -79
  97. package/src/modules/SelectMenu.js +89 -86
  98. package/src/plugins/browser/audioGallery.js +9 -5
  99. package/src/plugins/browser/fileBrowser.js +10 -6
  100. package/src/plugins/browser/fileGallery.js +9 -5
  101. package/src/plugins/browser/imageGallery.js +9 -5
  102. package/src/plugins/browser/videoGallery.js +11 -6
  103. package/src/plugins/command/blockquote.js +1 -0
  104. package/src/plugins/command/exportPDF.js +11 -8
  105. package/src/plugins/command/fileUpload.js +41 -29
  106. package/src/plugins/command/list_bulleted.js +2 -1
  107. package/src/plugins/command/list_numbered.js +2 -1
  108. package/src/plugins/dropdown/align.js +8 -2
  109. package/src/plugins/dropdown/backgroundColor.js +19 -11
  110. package/src/plugins/dropdown/font.js +15 -9
  111. package/src/plugins/dropdown/fontColor.js +19 -11
  112. package/src/plugins/dropdown/formatBlock.js +7 -2
  113. package/src/plugins/dropdown/hr.js +7 -3
  114. package/src/plugins/dropdown/layout.js +6 -2
  115. package/src/plugins/dropdown/lineHeight.js +8 -3
  116. package/src/plugins/dropdown/list.js +2 -1
  117. package/src/plugins/dropdown/paragraphStyle.js +15 -11
  118. package/src/plugins/dropdown/{table.js → table/index.js} +514 -362
  119. package/src/plugins/dropdown/template.js +6 -2
  120. package/src/plugins/dropdown/textStyle.js +7 -3
  121. package/src/plugins/field/mention.js +33 -27
  122. package/src/plugins/input/fontSize.js +44 -37
  123. package/src/plugins/input/pageNavigator.js +3 -2
  124. package/src/plugins/modal/audio.js +90 -85
  125. package/src/plugins/modal/drawing.js +58 -66
  126. package/src/plugins/modal/embed.js +193 -180
  127. package/src/plugins/modal/image.js +441 -439
  128. package/src/plugins/modal/link.js +31 -8
  129. package/src/plugins/modal/math.js +23 -22
  130. package/src/plugins/modal/video.js +233 -230
  131. package/src/plugins/popup/anchor.js +24 -18
  132. package/src/suneditor.js +69 -24
  133. package/src/typedef.js +42 -19
  134. package/types/assets/icons/defaultIcons.d.ts +8 -0
  135. package/types/core/class/char.d.ts +1 -1
  136. package/types/core/class/component.d.ts +29 -7
  137. package/types/core/class/format.d.ts +4 -354
  138. package/types/core/class/html.d.ts +13 -4
  139. package/types/core/class/inline.d.ts +263 -0
  140. package/types/core/class/listFormat.d.ts +135 -0
  141. package/types/core/class/menu.d.ts +10 -2
  142. package/types/core/class/offset.d.ts +24 -26
  143. package/types/core/class/selection.d.ts +2 -0
  144. package/types/core/class/toolbar.d.ts +24 -11
  145. package/types/core/class/ui.d.ts +1 -1
  146. package/types/core/class/viewer.d.ts +1 -1
  147. package/types/core/config/context.d.ts +157 -0
  148. package/types/core/config/frameContext.d.ts +367 -0
  149. package/types/core/config/options.d.ts +1119 -0
  150. package/types/core/editor.d.ts +101 -66
  151. package/types/core/event/actions/index.d.ts +47 -0
  152. package/types/core/event/effects/common.registry.d.ts +50 -0
  153. package/types/core/event/effects/keydown.registry.d.ts +73 -0
  154. package/types/core/event/effects/ruleHelpers.d.ts +31 -0
  155. package/types/core/{base → event}/eventManager.d.ts +15 -46
  156. package/types/core/event/executor.d.ts +6 -0
  157. package/types/core/event/handlers/handler_ww_input.d.ts +41 -0
  158. package/types/core/{base/eventHandlers/handler_ww_key_input.d.ts → event/handlers/handler_ww_key.d.ts} +4 -6
  159. package/types/core/event/ports.d.ts +255 -0
  160. package/types/core/event/reducers/keydown.reducer.d.ts +75 -0
  161. package/types/core/event/rules/keydown.rule.arrow.d.ts +8 -0
  162. package/types/core/event/rules/keydown.rule.backspace.d.ts +9 -0
  163. package/types/core/event/rules/keydown.rule.delete.d.ts +9 -0
  164. package/types/core/event/rules/keydown.rule.enter.d.ts +9 -0
  165. package/types/core/event/rules/keydown.rule.tab.d.ts +9 -0
  166. package/types/core/section/constructor.d.ts +101 -631
  167. package/types/core/section/documentType.d.ts +14 -4
  168. package/types/core/util/instanceCheck.d.ts +50 -0
  169. package/types/editorInjector/_classes.d.ts +4 -0
  170. package/types/editorInjector/_core.d.ts +17 -7
  171. package/types/editorInjector/index.d.ts +10 -2
  172. package/types/events.d.ts +1 -0
  173. package/types/helper/clipboard.d.ts +2 -2
  174. package/types/helper/converter.d.ts +6 -9
  175. package/types/helper/dom/domCheck.d.ts +7 -0
  176. package/types/helper/dom/domQuery.d.ts +19 -8
  177. package/types/helper/dom/domUtils.d.ts +24 -2
  178. package/types/helper/dom/index.d.ts +86 -1
  179. package/types/helper/env.d.ts +6 -1
  180. package/types/helper/index.d.ts +7 -1
  181. package/types/helper/keyCodeMap.d.ts +3 -3
  182. package/types/index.d.ts +23 -117
  183. package/types/langs/index.d.ts +2 -2
  184. package/types/modules/ApiManager.d.ts +1 -8
  185. package/types/modules/Browser.d.ts +4 -62
  186. package/types/modules/ColorPicker.d.ts +4 -21
  187. package/types/modules/Controller.d.ts +8 -64
  188. package/types/modules/Figure.d.ts +54 -50
  189. package/types/modules/FileManager.d.ts +1 -13
  190. package/types/modules/HueSlider.d.ts +13 -3
  191. package/types/modules/Modal.d.ts +0 -43
  192. package/types/modules/ModalAnchorEditor.d.ts +0 -73
  193. package/types/modules/SelectMenu.d.ts +0 -85
  194. package/types/modules/index.d.ts +3 -3
  195. package/types/plugins/browser/audioGallery.d.ts +29 -18
  196. package/types/plugins/browser/fileBrowser.d.ts +38 -27
  197. package/types/plugins/browser/fileGallery.d.ts +29 -18
  198. package/types/plugins/browser/imageGallery.d.ts +24 -16
  199. package/types/plugins/browser/videoGallery.d.ts +29 -18
  200. package/types/plugins/command/blockquote.d.ts +1 -0
  201. package/types/plugins/command/exportPDF.d.ts +18 -18
  202. package/types/plugins/command/fileUpload.d.ts +65 -45
  203. package/types/plugins/command/list_bulleted.d.ts +1 -0
  204. package/types/plugins/command/list_numbered.d.ts +1 -0
  205. package/types/plugins/dropdown/align.d.ts +13 -8
  206. package/types/plugins/dropdown/backgroundColor.d.ts +30 -19
  207. package/types/plugins/dropdown/font.d.ts +13 -12
  208. package/types/plugins/dropdown/fontColor.d.ts +30 -19
  209. package/types/plugins/dropdown/formatBlock.d.ts +13 -8
  210. package/types/plugins/dropdown/hr.d.ts +15 -11
  211. package/types/plugins/dropdown/layout.d.ts +15 -11
  212. package/types/plugins/dropdown/lineHeight.d.ts +16 -11
  213. package/types/plugins/dropdown/list.d.ts +1 -0
  214. package/types/plugins/dropdown/paragraphStyle.d.ts +31 -27
  215. package/types/plugins/dropdown/table/index.d.ts +582 -0
  216. package/types/plugins/dropdown/table.d.ts +41 -86
  217. package/types/plugins/dropdown/template.d.ts +15 -11
  218. package/types/plugins/dropdown/textStyle.d.ts +19 -11
  219. package/types/plugins/field/mention.d.ts +58 -56
  220. package/types/plugins/index.d.ts +38 -38
  221. package/types/plugins/input/fontSize.d.ts +46 -50
  222. package/types/plugins/modal/audio.d.ts +26 -56
  223. package/types/plugins/modal/drawing.d.ts +0 -85
  224. package/types/plugins/modal/embed.d.ts +15 -79
  225. package/types/plugins/modal/image.d.ts +24 -136
  226. package/types/plugins/modal/link.d.ts +34 -15
  227. package/types/plugins/modal/math.d.ts +0 -16
  228. package/types/plugins/modal/video.d.ts +17 -86
  229. package/types/plugins/popup/anchor.d.ts +1 -8
  230. package/types/suneditor.d.ts +70 -19
  231. package/types/typedef.d.ts +60 -46
  232. package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
  233. package/src/core/section/context.js +0 -102
  234. package/types/core/section/context.d.ts +0 -45
  235. package/types/langs/_Lang.d.ts +0 -194
  236. /package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +0 -0
  237. /package/types/core/{section → base}/actives.d.ts +0 -0
  238. /package/types/core/{base/eventHandlers → event/handlers}/handler_toolbar.d.ts +0 -0
  239. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.d.ts +0 -0
  240. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.d.ts +0 -0
  241. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.d.ts +0 -0
@@ -0,0 +1,1853 @@
1
+ /**
2
+ * @fileoverview Inline class
3
+ */
4
+
5
+ import CoreInjector from '../../editorInjector/_core';
6
+ import { dom, unicode, converter } from '../../helper';
7
+
8
+ /**
9
+ * @typedef {Omit<Inline & Partial<__se__EditorInjector>, 'inline'>} InlineThis
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} NodeStyleContainerType
14
+ * @property {?Node=} ancestor
15
+ * @property {?number=} offset
16
+ * @property {?Node=} container
17
+ * @property {?Node=} endContainer
18
+ */
19
+
20
+ /**
21
+ * @constructor
22
+ * @this {InlineThis}
23
+ * @description Classes related to editor inline formats such as style node like strong, span, etc.
24
+ * @param {__se__EditorCore} editor - The root editor instance
25
+ */
26
+ function Inline(editor) {
27
+ CoreInjector.call(this, editor);
28
+
29
+ // members
30
+ this._listCamel = this.options.get('__listCommonStyle');
31
+ this._listKebab = converter.camelToKebabCase(this.options.get('__listCommonStyle'));
32
+ }
33
+
34
+ Inline.prototype = {
35
+ /**
36
+ * @this {InlineThis}
37
+ * @description Adds, updates, or deletes style nodes from selected text (a, span, strong, etc.).
38
+ * @param {?Node} styleNode The element to be added to the selection. If null, only existing nodes are modified or removed.
39
+ * @param {Object} [options] Options
40
+ * @param {Array<string>} [options.stylesToModify=null] Array of style or class names to check and modify.
41
+ * (e.g., ['font-size'], ['.className'], ['font-family', 'color', '.className'])
42
+ * @param {Array<string>} [options.nodesToRemove=null] Array of node names to remove.
43
+ * If empty array or null when styleNode is null, all formats are removed.
44
+ * (e.g., ['span'], ['strong', 'em'])
45
+ * @param {boolean} [options.strictRemove=false] If true, only removes nodes from nodesToRemove if all styles and classes are removed.
46
+ * @returns {HTMLElement} The element that was added to or modified in the selection.
47
+ *
48
+ * @details
49
+ * 1. If styleNode is provided, a node with the same tags and attributes is added to the selected text.
50
+ * 2. If the same tag already exists, only its attributes are updated.
51
+ * 3. If styleNode is null, existing nodes are updated or removed without adding new ones.
52
+ * 4. Styles matching those in stylesToModify are removed. (Use CSS attribute names, e.g., "background-color")
53
+ * 5. Classes matching those in stylesToModify (prefixed with ".") are removed.
54
+ * 6. stylesToModify is used to avoid duplicate property values from styleNode.
55
+ * 7. Nodes with all styles and classes removed are deleted if they match styleNode, are in nodesToRemove, or if styleNode is null.
56
+ * 8. Tags matching names in nodesToRemove are deleted regardless of their style and class.
57
+ * 9. If strictRemove is true, nodes in nodesToRemove are only removed if all their styles and classes are removed.
58
+ * 10. The function won't modify nodes if the parent has the same class and style values.
59
+ * - However, if nodesToRemove has values, it will work and separate text nodes even if there's no node to replace.
60
+ */
61
+ apply(styleNode, { stylesToModify, nodesToRemove, strictRemove } = {}) {
62
+ if (dom.query.getParentElement(this.selection.getNode(), dom.check.isNonEditable)) return;
63
+
64
+ this.selection._resetRangeToTextNode();
65
+ let range = this.selection.getRangeAndAddLine(this.selection.getRange(), null);
66
+ stylesToModify = stylesToModify?.length > 0 ? stylesToModify : null;
67
+ nodesToRemove = nodesToRemove?.length > 0 ? nodesToRemove : null;
68
+
69
+ const isRemoveNode = !styleNode;
70
+ const isRemoveFormat = isRemoveNode && !nodesToRemove && !stylesToModify;
71
+ let startCon = range.startContainer;
72
+ let startOff = range.startOffset;
73
+ let endCon = range.endContainer;
74
+ let endOff = range.endOffset;
75
+
76
+ if ((isRemoveFormat && range.collapsed && this.format.isLine(startCon.parentNode) && this.format.isLine(endCon.parentNode)) || (startCon === endCon && startCon.nodeType === 1 && dom.check.isNonEditable(startCon))) {
77
+ const format = startCon.parentNode;
78
+ if (!dom.check.isListCell(format) || !converter.getValues(format.style).some((k) => this._listKebab.includes(k))) {
79
+ return;
80
+ }
81
+ }
82
+
83
+ if (range.collapsed && !isRemoveFormat) {
84
+ if (startCon.nodeType === 1 && !dom.check.isBreak(startCon) && !this.component.is(startCon)) {
85
+ let afterNode = null;
86
+ const focusNode = startCon.childNodes[startOff];
87
+
88
+ if (focusNode) {
89
+ if (!focusNode.nextSibling) {
90
+ afterNode = null;
91
+ } else {
92
+ afterNode = dom.check.isBreak(focusNode) ? focusNode : focusNode.nextSibling;
93
+ }
94
+ }
95
+
96
+ const zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
97
+ startCon.insertBefore(zeroWidth, afterNode);
98
+ this.selection.setRange(zeroWidth, 1, zeroWidth, 1);
99
+
100
+ range = this.selection.getRange();
101
+ startCon = range.startContainer;
102
+ startOff = range.startOffset;
103
+ endCon = range.endContainer;
104
+ endOff = range.endOffset;
105
+ }
106
+ }
107
+
108
+ if (this.format.isLine(startCon)) {
109
+ startCon = startCon.childNodes[startOff] || startCon.firstChild;
110
+ startOff = 0;
111
+ }
112
+ if (this.format.isLine(endCon)) {
113
+ endCon = endCon.childNodes[endOff] || endCon.lastChild;
114
+ endOff = endCon.textContent.length;
115
+ }
116
+
117
+ if (isRemoveNode) {
118
+ styleNode = dom.utils.createElement('DIV');
119
+ }
120
+
121
+ const wRegExp = RegExp;
122
+ const newNodeName = styleNode.nodeName;
123
+
124
+ /* checked same style property */
125
+ if (!isRemoveFormat && startCon === endCon && !nodesToRemove && styleNode) {
126
+ let sNode = startCon;
127
+ let checkCnt = 0;
128
+ const checkAttrs = [];
129
+
130
+ const checkStyles = /** @type {HTMLElement} */ (styleNode).style;
131
+ for (let i = 0, len = checkStyles.length; i < len; i++) {
132
+ checkAttrs.push(checkStyles[i]);
133
+ }
134
+
135
+ const checkClassName = /** @type {HTMLElement} */ (styleNode).className;
136
+ const ckeckClasses = /** @type {HTMLElement} */ (styleNode).classList;
137
+ for (let i = 0, len = ckeckClasses.length; i < len; i++) {
138
+ checkAttrs.push('.' + ckeckClasses[i]);
139
+ }
140
+
141
+ if (checkAttrs.length > 0) {
142
+ while (!this.format.isLine(sNode) && !dom.check.isWysiwygFrame(sNode)) {
143
+ for (let i = 0; i < checkAttrs.length; i++) {
144
+ if (sNode.nodeType === 1) {
145
+ const s = checkAttrs[i];
146
+ const classReg = /^\./.test(s) ? new wRegExp('\\s*' + s.replace(/^\./, '') + '(\\s+|$)', 'ig') : false;
147
+ const sNodeStyle = /** @type {HTMLElement} */ (sNode).style;
148
+ const sNodeClassName = /** @type {HTMLElement} */ (sNode).className;
149
+
150
+ const styleCheck = isRemoveNode ? !!sNodeStyle[s] : !!sNodeStyle[s] && !!checkStyles[s] && sNodeStyle[s] === checkStyles[s];
151
+ const classCheck = classReg === false ? false : isRemoveNode ? !!sNodeClassName.match(classReg) : !!sNodeClassName.match(classReg) && !!checkClassName.match(classReg);
152
+ if (styleCheck || classCheck) {
153
+ checkCnt++;
154
+ }
155
+ }
156
+ }
157
+ sNode = sNode.parentNode;
158
+ }
159
+
160
+ if (checkCnt >= checkAttrs.length) return;
161
+ }
162
+ }
163
+
164
+ let newNode;
165
+ /** @type {NodeStyleContainerType} */
166
+ let start = {};
167
+ /** @type {NodeStyleContainerType} */
168
+ let end = {};
169
+
170
+ /** @type {string|RegExp} */
171
+ let styleRegExp = '';
172
+ /** @type {string|RegExp} */
173
+ let classRegExp = '';
174
+ /** @type {string|RegExp} */
175
+ let removeNodeRegExp;
176
+
177
+ if (stylesToModify) {
178
+ for (let i = 0, len = stylesToModify.length, s; i < len; i++) {
179
+ s = stylesToModify[i];
180
+ if (/^\./.test(s)) {
181
+ classRegExp += (classRegExp ? '|' : '\\s*(?:') + s.replace(/^\./, '');
182
+ } else {
183
+ styleRegExp += (styleRegExp ? '|' : '(?:;|^|\\s)(?:') + s;
184
+ }
185
+ }
186
+
187
+ if (styleRegExp) {
188
+ styleRegExp += ')\\s*:[^;]*\\s*(?:;|$)';
189
+ styleRegExp = new wRegExp(styleRegExp, 'ig');
190
+ }
191
+
192
+ if (classRegExp) {
193
+ classRegExp += ')(?=\\s+|$)';
194
+ classRegExp = new wRegExp(classRegExp, 'ig');
195
+ }
196
+ }
197
+
198
+ if (nodesToRemove) {
199
+ removeNodeRegExp = '^(?:' + nodesToRemove[0];
200
+ for (let i = 1; i < nodesToRemove.length; i++) {
201
+ removeNodeRegExp += '|' + nodesToRemove[i];
202
+ }
203
+ removeNodeRegExp += ')$';
204
+ removeNodeRegExp = new wRegExp(removeNodeRegExp, 'i');
205
+ }
206
+
207
+ /** validation check function*/
208
+ const _removeCheck = {
209
+ v: false
210
+ };
211
+ const validation = function (checkNode) {
212
+ const vNode = checkNode.cloneNode(false);
213
+
214
+ // all path
215
+ if (vNode.nodeType === 3 || dom.check.isBreak(vNode)) return vNode;
216
+ // all remove
217
+ if (isRemoveFormat) return null;
218
+
219
+ // remove node check
220
+ const tagRemove = (!removeNodeRegExp && isRemoveNode) || /** @type {RegExp} */ (removeNodeRegExp)?.test(vNode.nodeName);
221
+
222
+ // tag remove
223
+ if (tagRemove && !strictRemove) {
224
+ _removeCheck.v = true;
225
+ return null;
226
+ }
227
+
228
+ // style regexp
229
+ const originStyle = vNode.style.cssText;
230
+ let style = '';
231
+ if (styleRegExp && originStyle.length > 0) {
232
+ style = originStyle.replace(styleRegExp, '').trim();
233
+ if (style !== originStyle) _removeCheck.v = true;
234
+ }
235
+
236
+ // class check
237
+ const originClasses = vNode.className;
238
+ let classes = '';
239
+ if (classRegExp && originClasses.length > 0) {
240
+ classes = originClasses.replace(classRegExp, '').trim();
241
+ if (classes !== originClasses) _removeCheck.v = true;
242
+ }
243
+
244
+ // remove only
245
+ if (isRemoveNode) {
246
+ if ((classRegExp || !originClasses) && (styleRegExp || !originStyle) && !style && !classes && tagRemove) {
247
+ _removeCheck.v = true;
248
+ return null;
249
+ }
250
+ }
251
+
252
+ // change
253
+ if (style || classes || vNode.nodeName !== newNodeName || Boolean(styleRegExp) !== Boolean(originStyle) || Boolean(classRegExp) !== Boolean(originClasses)) {
254
+ if (styleRegExp && originStyle.length > 0) vNode.style.cssText = style;
255
+ if (!vNode.style.cssText) {
256
+ vNode.removeAttribute('style');
257
+ }
258
+
259
+ if (classRegExp && originClasses.length > 0) vNode.className = classes.trim();
260
+ if (!vNode.className.trim()) {
261
+ vNode.removeAttribute('class');
262
+ }
263
+
264
+ if (!vNode.style.cssText && !vNode.className && (vNode.nodeName === newNodeName || tagRemove)) {
265
+ _removeCheck.v = true;
266
+ return null;
267
+ }
268
+
269
+ return vNode;
270
+ }
271
+
272
+ _removeCheck.v = true;
273
+ return null;
274
+ };
275
+
276
+ // get line nodes
277
+ const lineNodes = this.format.getLines(null);
278
+ if (lineNodes.length === 0) {
279
+ console.warn('[SUNEDITOR.inline.apply.warn] There is no line to apply.');
280
+ return;
281
+ }
282
+
283
+ range = this.selection.getRange();
284
+ startCon = range.startContainer;
285
+ startOff = range.startOffset;
286
+ endCon = range.endContainer;
287
+ endOff = range.endOffset;
288
+
289
+ if (!this.format.getLine(startCon, null)) {
290
+ startCon = dom.query.getEdgeChild(
291
+ lineNodes[0],
292
+ function (current) {
293
+ return current.nodeType === 3;
294
+ },
295
+ false
296
+ );
297
+ startOff = 0;
298
+ }
299
+
300
+ if (!this.format.getLine(endCon, null)) {
301
+ endCon = dom.query.getEdgeChild(
302
+ lineNodes.at(-1),
303
+ function (current) {
304
+ return current.nodeType === 3;
305
+ },
306
+ false
307
+ );
308
+ endOff = endCon.textContent.length;
309
+ }
310
+
311
+ const oneLine = this.format.getLine(startCon, null) === this.format.getLine(endCon, null);
312
+ const endLength = lineNodes.length - (oneLine ? 0 : 1);
313
+
314
+ // node Changes
315
+ newNode = styleNode.cloneNode(false);
316
+
317
+ const isRemoveAnchor =
318
+ isRemoveFormat ||
319
+ (isRemoveNode &&
320
+ (function (inst, arr) {
321
+ for (let n = 0, len = arr.length; n < len; n++) {
322
+ if (inst._isNonSplitNode(arr[n])) return true;
323
+ }
324
+ return false;
325
+ })(this, nodesToRemove));
326
+
327
+ const isSizeNode = isRemoveNode || this._sn_isSizeNode(newNode);
328
+ const _getMaintainedNode = this._sn_getMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
329
+ const _isMaintainedNode = this._sn_isMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
330
+
331
+ // one line
332
+ if (oneLine) {
333
+ if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) range = this.selection.setRange(startCon, startOff, endCon, endOff);
334
+
335
+ const newRange = this._setNode_oneLine(lineNodes[0], newNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, range.collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode);
336
+ start.container = newRange.startContainer;
337
+ start.offset = newRange.startOffset;
338
+ end.container = newRange.endContainer;
339
+ end.offset = newRange.endOffset;
340
+
341
+ if (start.container === end.container && dom.check.isZeroWidth(start.container)) {
342
+ start.offset = end.offset = 1;
343
+ }
344
+ this._sn_setCommonListStyle(newRange.ancestor, null);
345
+ } else {
346
+ // multi line
347
+ let appliedCommonList = false;
348
+ if (endLength > 0 && this._sn_resetCommonListCell(lineNodes[endLength], stylesToModify)) appliedCommonList = true;
349
+ if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) appliedCommonList = true;
350
+ if (appliedCommonList) this.selection.setRange(startCon, startOff, endCon, endOff);
351
+
352
+ // end
353
+ if (endLength > 0) {
354
+ newNode = styleNode.cloneNode(false);
355
+ end = this._setNode_endLine(lineNodes[endLength], newNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode);
356
+ }
357
+
358
+ // mid
359
+ for (let i = endLength - 1, newRange; i > 0; i--) {
360
+ this._sn_resetCommonListCell(lineNodes[i], stylesToModify);
361
+ newNode = styleNode.cloneNode(false);
362
+ newRange = this._setNode_middleLine(lineNodes[i], newNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, end.container);
363
+ if (newRange.endContainer && newRange.ancestor.contains(newRange.endContainer)) {
364
+ end.ancestor = null;
365
+ end.container = newRange.endContainer;
366
+ }
367
+ this._sn_setCommonListStyle(newRange.ancestor, null);
368
+ }
369
+
370
+ // start
371
+ newNode = styleNode.cloneNode(false);
372
+ start = this._setNode_startLine(lineNodes[0], newNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, end.container);
373
+
374
+ if (start.endContainer) {
375
+ end.ancestor = null;
376
+ end.container = start.endContainer;
377
+ }
378
+
379
+ if (endLength <= 0) {
380
+ end = start;
381
+ } else if (!end.container) {
382
+ end.ancestor = null;
383
+ end.container = start.container;
384
+ end.offset = start.container.textContent.length;
385
+ }
386
+
387
+ this._sn_setCommonListStyle(start.ancestor, null);
388
+ this._sn_setCommonListStyle(end.ancestor || this.format.getLine(end.container), null);
389
+ }
390
+
391
+ // set range
392
+ this.ui._offCurrentController();
393
+ this.selection.setRange(start.container, start.offset, end.container, end.offset);
394
+ this.history.push(false);
395
+
396
+ return /** @type {HTMLElement} */ (newNode);
397
+ },
398
+
399
+ /**
400
+ * @this {InlineThis}
401
+ * @description Remove format of the currently selected text.
402
+ */
403
+ remove() {
404
+ this.apply(null, { stylesToModify: null, nodesToRemove: null, strictRemove: null });
405
+ },
406
+
407
+ /**
408
+ * @private
409
+ * @this {InlineThis}
410
+ * @description Nodes that must remain undetached when changing text nodes (A, Label, Code, Span:font-size)
411
+ * @param {Node|string} element Element to check
412
+ * @returns {boolean}
413
+ */
414
+ _isNonSplitNode(element) {
415
+ if (!element) return false;
416
+ const checkRegExp = /^(a|label|code|summary)$/i;
417
+ if (typeof element === 'string') return checkRegExp.test(element);
418
+ return element.nodeType === 1 && checkRegExp.test(element.nodeName);
419
+ },
420
+
421
+ /**
422
+ * @private
423
+ * @this {InlineThis}
424
+ * @description Nodes that need to be added without modification when changing text nodes
425
+ * @param {Node} element Element to check
426
+ * @returns {boolean}
427
+ */
428
+ _isIgnoreNodeChange(element) {
429
+ return element && element.nodeType === 1 && (dom.check.isNonEditable(element) || !this.format.isTextStyleNode(element) || this.component.is(element));
430
+ },
431
+
432
+ /**
433
+ * @private
434
+ * @this {InlineThis}
435
+ * @description wraps text nodes of line selected text.
436
+ * @param {Node} element The node of the line that contains the selected text node.
437
+ * @param {Node} newInnerNode The dom that will wrap the selected text area
438
+ * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
439
+ * @param {Node} startCon The startContainer property of the selection object.
440
+ * @param {number} startOff The startOffset property of the selection object.
441
+ * @param {Node} endCon The endContainer property of the selection object.
442
+ * @param {number} endOff The endOffset property of the selection object.
443
+ * @param {boolean} isRemoveFormat Is the remove all formats command?
444
+ * @param {boolean} isRemoveNode "newInnerNode" is remove node?
445
+ * @param {boolean} collapsed range.collapsed
446
+ * @returns {{ancestor: *, startContainer: *, startOffset: *, endContainer: *, endOffset: *}}
447
+ */
448
+ _setNode_oneLine(element, newInnerNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
449
+ // not add tag
450
+ let parentCon = startCon.parentNode;
451
+ while (!parentCon.nextSibling && !parentCon.previousSibling && !this.format.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
452
+ if (parentCon.nodeName === newInnerNode.nodeName) break;
453
+ parentCon = parentCon.parentNode;
454
+ }
455
+
456
+ if (!isRemoveNode && parentCon === endCon.parentNode && parentCon.nodeName === newInnerNode.nodeName) {
457
+ if (dom.check.isZeroWidth(startCon.textContent.slice(0, startOff)) && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
458
+ const children = parentCon.childNodes;
459
+ let sameTag = false;
460
+
461
+ for (let i = 0, len = children.length, c, s, e, z; i < len; i++) {
462
+ c = children[i];
463
+ z = !dom.check.isZeroWidth(c);
464
+ if (c === startCon) {
465
+ s = true;
466
+ continue;
467
+ }
468
+ if (c === endCon) {
469
+ e = true;
470
+ continue;
471
+ }
472
+ if ((!s && z) || (s && e && z)) {
473
+ sameTag = false;
474
+ break;
475
+ }
476
+ }
477
+
478
+ if (sameTag) {
479
+ dom.utils.copyTagAttributes(parentCon, newInnerNode);
480
+
481
+ return {
482
+ ancestor: element,
483
+ startContainer: startCon,
484
+ startOffset: startOff,
485
+ endContainer: endCon,
486
+ endOffset: endOff
487
+ };
488
+ }
489
+ }
490
+ }
491
+
492
+ // add tag
493
+ _removeCheck.v = false;
494
+ const inst = this;
495
+ const el = element;
496
+ const nNodeArray = [newInnerNode];
497
+ const pNode = element.cloneNode(false);
498
+ const isSameNode = startCon === endCon;
499
+ let startContainer = startCon;
500
+ let startOffset = startOff;
501
+ let endContainer = endCon;
502
+ let endOffset = endOff;
503
+ let startPass = false;
504
+ let endPass = false;
505
+ let pCurrent, newNode, appendNode, cssText, anchorNode;
506
+
507
+ const wRegExp = RegExp;
508
+ function checkCss(vNode) {
509
+ const regExp = new wRegExp('(?:;|^|\\s)(?:' + cssText + 'null)\\s*:[^;]*\\s*(?:;|$)', 'ig');
510
+ let style = false;
511
+
512
+ if (regExp && vNode.style.cssText.length > 0) {
513
+ style = regExp.test(vNode.style.cssText);
514
+ }
515
+
516
+ return !style;
517
+ }
518
+
519
+ (function recursionFunc(current, ancestor) {
520
+ const childNodes = current.childNodes;
521
+
522
+ for (let i = 0, len = childNodes.length, vNode; i < len; i++) {
523
+ const child = childNodes[i];
524
+ if (!child) continue;
525
+ let coverNode = ancestor;
526
+ let cloneNode;
527
+
528
+ // startContainer
529
+ if (!startPass && child === startContainer) {
530
+ let line = pNode;
531
+ anchorNode = _getMaintainedNode(child);
532
+
533
+ let _prevText = '';
534
+ let _nextText = '';
535
+ if (startContainer.nodeType === 3) {
536
+ const sText = /** @type {Text} */ (startContainer);
537
+ _prevText = sText.substringData(0, startOffset);
538
+ _nextText = sText.substringData(startOffset, isSameNode ? (endOffset >= startOffset ? endOffset - startOffset : sText.data.length - startOffset) : sText.data.length - startOffset);
539
+ }
540
+
541
+ const prevNode = dom.utils.createTextNode(_prevText);
542
+ const textNode = dom.utils.createTextNode(_nextText);
543
+
544
+ if (anchorNode) {
545
+ const a = _getMaintainedNode(ancestor);
546
+ if (a.parentNode !== line) {
547
+ let m = a;
548
+ let p = null;
549
+ while (m.parentNode !== line) {
550
+ ancestor = p = m.parentNode.cloneNode(false);
551
+ while (m.childNodes[0]) {
552
+ p.appendChild(m.childNodes[0]);
553
+ }
554
+ m.appendChild(p);
555
+ m = m.parentNode;
556
+ }
557
+ m.parentNode.appendChild(a);
558
+ }
559
+ anchorNode = anchorNode.cloneNode(false);
560
+ }
561
+
562
+ if (!dom.check.isZeroWidth(prevNode)) {
563
+ ancestor.appendChild(prevNode);
564
+ }
565
+
566
+ const prevAnchorNode = _getMaintainedNode(ancestor);
567
+ if (prevAnchorNode) anchorNode = prevAnchorNode;
568
+ if (anchorNode) line = anchorNode;
569
+
570
+ newNode = /** @type {HTMLElement} */ (child);
571
+ pCurrent = [];
572
+ cssText = '';
573
+ while (newNode !== line && newNode !== el && newNode !== null) {
574
+ vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
575
+ if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
576
+ pCurrent.push(vNode);
577
+ cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
578
+ }
579
+ newNode = newNode.parentElement;
580
+ }
581
+
582
+ const childNode = pCurrent.pop() || textNode;
583
+ appendNode = newNode = childNode;
584
+ while (pCurrent.length > 0) {
585
+ newNode = pCurrent.pop();
586
+ appendNode.appendChild(newNode);
587
+ appendNode = newNode;
588
+ }
589
+
590
+ newInnerNode.appendChild(childNode);
591
+ line.appendChild(newInnerNode);
592
+
593
+ if (anchorNode && !_getMaintainedNode(endContainer)) {
594
+ newInnerNode = newInnerNode.cloneNode(false);
595
+ pNode.appendChild(newInnerNode);
596
+ nNodeArray.push(newInnerNode);
597
+ }
598
+
599
+ startContainer = textNode;
600
+ startOffset = 0;
601
+ startPass = true;
602
+
603
+ if (newNode !== textNode) newNode.appendChild(startContainer);
604
+ if (!isSameNode) continue;
605
+ }
606
+
607
+ // endContainer
608
+ if (!endPass && child === endContainer) {
609
+ anchorNode = _getMaintainedNode(child);
610
+
611
+ let _prevText = '';
612
+ let _nextText = '';
613
+ if (endContainer.nodeType === 3) {
614
+ const eText = /** @type {Text} */ (endContainer);
615
+ _prevText = eText.substringData(endOffset, eText.length - endOffset);
616
+ _nextText = isSameNode ? '' : eText.substringData(0, endOffset);
617
+ }
618
+
619
+ const afterNode = dom.utils.createTextNode(_prevText);
620
+ const textNode = dom.utils.createTextNode(_nextText);
621
+
622
+ if (anchorNode) {
623
+ anchorNode = anchorNode.cloneNode(false);
624
+ } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
625
+ newInnerNode = newInnerNode.cloneNode(false);
626
+ pNode.appendChild(newInnerNode);
627
+ nNodeArray.push(newInnerNode);
628
+ }
629
+
630
+ if (!dom.check.isZeroWidth(afterNode)) {
631
+ newNode = /** @type {HTMLElement} */ (child);
632
+ cssText = '';
633
+ pCurrent = [];
634
+ const anchors = [];
635
+ while (newNode !== pNode && newNode !== el && newNode !== null) {
636
+ if (newNode.nodeType === 1 && checkCss(newNode)) {
637
+ if (_isMaintainedNode(newNode)) anchors.push(newNode.cloneNode(false));
638
+ else pCurrent.push(newNode.cloneNode(false));
639
+ cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
640
+ }
641
+ newNode = newNode.parentElement;
642
+ }
643
+ pCurrent = pCurrent.concat(anchors);
644
+
645
+ cloneNode = appendNode = newNode = pCurrent.pop() || afterNode;
646
+ while (pCurrent.length > 0) {
647
+ newNode = pCurrent.pop();
648
+ appendNode.appendChild(newNode);
649
+ appendNode = newNode;
650
+ }
651
+
652
+ pNode.appendChild(cloneNode);
653
+ newNode.textContent = afterNode.data;
654
+ }
655
+
656
+ if (anchorNode && cloneNode) {
657
+ const afterAnchorNode = _getMaintainedNode(cloneNode);
658
+ if (afterAnchorNode) {
659
+ anchorNode = afterAnchorNode;
660
+ }
661
+ }
662
+
663
+ newNode = /** @type {HTMLElement} */ (child);
664
+ pCurrent = [];
665
+ cssText = '';
666
+ while (newNode !== pNode && newNode !== el && newNode !== null) {
667
+ vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
668
+ if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
669
+ pCurrent.push(vNode);
670
+ cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
671
+ }
672
+ newNode = newNode.parentElement;
673
+ }
674
+
675
+ const childNode = pCurrent.pop() || textNode;
676
+ appendNode = newNode = childNode;
677
+ while (pCurrent.length > 0) {
678
+ newNode = pCurrent.pop();
679
+ appendNode.appendChild(newNode);
680
+ appendNode = newNode;
681
+ }
682
+
683
+ if (anchorNode) {
684
+ newInnerNode = newInnerNode.cloneNode(false);
685
+ newInnerNode.appendChild(childNode);
686
+ anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
687
+ pNode.appendChild(anchorNode);
688
+ nNodeArray.push(newInnerNode);
689
+ anchorNode = null;
690
+ } else {
691
+ newInnerNode.appendChild(childNode);
692
+ }
693
+
694
+ endContainer = textNode;
695
+ endOffset = textNode.data.length;
696
+ endPass = true;
697
+
698
+ if (!isRemoveFormat && collapsed) {
699
+ newInnerNode = textNode;
700
+ textNode.textContent = unicode.zeroWidthSpace;
701
+ }
702
+
703
+ if (newNode !== textNode) newNode.appendChild(endContainer);
704
+ continue;
705
+ }
706
+
707
+ // other
708
+ if (startPass) {
709
+ if (child.nodeType === 1 && !dom.check.isBreak(child)) {
710
+ if (inst._isIgnoreNodeChange(child)) {
711
+ pNode.appendChild(child.cloneNode(true));
712
+ if (!collapsed) {
713
+ newInnerNode = newInnerNode.cloneNode(false);
714
+ pNode.appendChild(newInnerNode);
715
+ nNodeArray.push(newInnerNode);
716
+ }
717
+ } else {
718
+ recursionFunc(child, child);
719
+ }
720
+ continue;
721
+ }
722
+
723
+ newNode = /** @type {HTMLElement} */ (child);
724
+ pCurrent = [];
725
+ cssText = '';
726
+ const anchors = [];
727
+ while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
728
+ vNode = endPass ? newNode.cloneNode(false) : validation(newNode);
729
+ if (newNode.nodeType === 1 && !dom.check.isBreak(child) && vNode && checkCss(newNode)) {
730
+ if (_isMaintainedNode(newNode)) {
731
+ if (!anchorNode) anchors.push(vNode);
732
+ } else {
733
+ pCurrent.push(vNode);
734
+ }
735
+ cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
736
+ }
737
+ newNode = newNode.parentElement;
738
+ }
739
+ pCurrent = pCurrent.concat(anchors);
740
+
741
+ const childNode = pCurrent.pop() || child;
742
+ appendNode = newNode = childNode;
743
+ while (pCurrent.length > 0) {
744
+ newNode = pCurrent.pop();
745
+ appendNode.appendChild(newNode);
746
+ appendNode = newNode;
747
+ }
748
+
749
+ if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode) && !dom.check.isZeroWidth(newInnerNode)) {
750
+ newInnerNode = newInnerNode.cloneNode(false);
751
+ pNode.appendChild(newInnerNode);
752
+ nNodeArray.push(newInnerNode);
753
+ }
754
+
755
+ if (!endPass && !anchorNode && _isMaintainedNode(childNode)) {
756
+ newInnerNode = newInnerNode.cloneNode(false);
757
+ const aChildren = childNode.childNodes;
758
+ for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
759
+ newInnerNode.appendChild(aChildren[a]);
760
+ }
761
+ childNode.appendChild(newInnerNode);
762
+ pNode.appendChild(childNode);
763
+ nNodeArray.push(newInnerNode);
764
+ if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
765
+ else ancestor = newInnerNode;
766
+ } else if (childNode === child) {
767
+ if (!endPass) ancestor = newInnerNode;
768
+ else ancestor = pNode;
769
+ } else if (endPass) {
770
+ pNode.appendChild(childNode);
771
+ ancestor = newNode;
772
+ } else {
773
+ newInnerNode.appendChild(childNode);
774
+ ancestor = newNode;
775
+ }
776
+
777
+ if (anchorNode && child.nodeType === 3) {
778
+ if (_getMaintainedNode(child)) {
779
+ const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
780
+ return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
781
+ });
782
+ anchorNode.appendChild(ancestorAnchorNode);
783
+ newInnerNode = ancestorAnchorNode.cloneNode(false);
784
+ nNodeArray.push(newInnerNode);
785
+ pNode.appendChild(newInnerNode);
786
+ } else {
787
+ anchorNode = null;
788
+ }
789
+ }
790
+ }
791
+
792
+ cloneNode = child.cloneNode(false);
793
+ ancestor.appendChild(cloneNode);
794
+ if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = cloneNode;
795
+
796
+ recursionFunc(child, coverNode);
797
+ }
798
+ })(element, pNode);
799
+
800
+ // not remove tag
801
+ if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
802
+ return {
803
+ ancestor: element,
804
+ startContainer: startCon,
805
+ startOffset: startOff,
806
+ endContainer: endCon,
807
+ endOffset: endOff
808
+ };
809
+ }
810
+
811
+ isRemoveFormat &&= isRemoveNode;
812
+
813
+ if (isRemoveFormat) {
814
+ for (let i = 0; i < nNodeArray.length; i++) {
815
+ const removeNode = nNodeArray[i];
816
+ let textNode, textNode_s, textNode_e;
817
+
818
+ if (collapsed) {
819
+ textNode = dom.utils.createTextNode(unicode.zeroWidthSpace);
820
+ pNode.replaceChild(textNode, removeNode);
821
+ } else {
822
+ const rChildren = removeNode.childNodes;
823
+ textNode_s = rChildren[0];
824
+ while (rChildren[0]) {
825
+ textNode_e = rChildren[0];
826
+ pNode.insertBefore(textNode_e, removeNode);
827
+ }
828
+ dom.utils.removeItem(removeNode);
829
+ }
830
+
831
+ if (i === 0) {
832
+ if (collapsed) {
833
+ startContainer = endContainer = textNode;
834
+ } else {
835
+ startContainer = textNode_s;
836
+ endContainer = textNode_e;
837
+ }
838
+ }
839
+ }
840
+ } else {
841
+ if (isRemoveNode) {
842
+ for (let i = 0; i < nNodeArray.length; i++) {
843
+ SN_StripRemoveNode(nNodeArray[i]);
844
+ }
845
+ }
846
+
847
+ if (collapsed) {
848
+ startContainer = endContainer = newInnerNode;
849
+ }
850
+ }
851
+
852
+ this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
853
+
854
+ if (collapsed) {
855
+ startOffset = startContainer.textContent.length;
856
+ endOffset = endContainer.textContent.length;
857
+ }
858
+
859
+ // endContainer reset
860
+ const endConReset = isRemoveFormat || endContainer.textContent.length === 0;
861
+
862
+ if (!dom.check.isBreak(endContainer) && endContainer.textContent.length === 0) {
863
+ dom.utils.removeItem(endContainer);
864
+ endContainer = startContainer;
865
+ }
866
+ endOffset = endConReset ? endContainer.textContent.length : endOffset;
867
+
868
+ // node change
869
+ const newStartOffset = {
870
+ s: 0,
871
+ e: 0
872
+ };
873
+ const startPath = dom.query.getNodePath(startContainer, pNode, newStartOffset);
874
+
875
+ const mergeEndCon = !endContainer.parentNode;
876
+ if (mergeEndCon) endContainer = startContainer;
877
+ const newEndOffset = {
878
+ s: 0,
879
+ e: 0
880
+ };
881
+ const endPath = dom.query.getNodePath(endContainer, pNode, !mergeEndCon && !endConReset ? newEndOffset : null);
882
+
883
+ startOffset += newStartOffset.s;
884
+ endOffset = collapsed ? startOffset : mergeEndCon ? startContainer.textContent.length : endConReset ? endOffset + newStartOffset.s : endOffset + newEndOffset.s;
885
+
886
+ // tag merge
887
+ const newOffsets = this.nodeTransform.mergeSameTags(pNode, [startPath, endPath], true);
888
+
889
+ element.parentNode.replaceChild(pNode, element);
890
+
891
+ startContainer = dom.query.getNodeFromPath(startPath, pNode);
892
+ endContainer = dom.query.getNodeFromPath(endPath, pNode);
893
+
894
+ return {
895
+ ancestor: pNode,
896
+ startContainer: startContainer,
897
+ startOffset: startOffset + newOffsets[0],
898
+ endContainer: endContainer,
899
+ endOffset: endOffset + newOffsets[1]
900
+ };
901
+ },
902
+
903
+ /**
904
+ * @private
905
+ * @this {InlineThis}
906
+ * @description wraps first line selected text.
907
+ * @param {Node} element The node of the line that contains the selected text node.
908
+ * @param {Node} newInnerNode The dom that will wrap the selected text area
909
+ * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
910
+ * @param {Node} startCon The startContainer property of the selection object.
911
+ * @param {number} startOff The startOffset property of the selection object.
912
+ * @param {boolean} isRemoveFormat Is the remove all formats command?
913
+ * @param {boolean} isRemoveNode "newInnerNode" is remove node?
914
+ * @returns {NodeStyleContainerType} { ancestor, container, offset, endContainer }
915
+ */
916
+ _setNode_startLine(element, newInnerNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, _endContainer) {
917
+ // not add tag
918
+ let parentCon = startCon.parentNode;
919
+ while (!parentCon.nextSibling && !parentCon.previousSibling && !this.format.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
920
+ if (parentCon.nodeName === newInnerNode.nodeName) break;
921
+ parentCon = parentCon.parentNode;
922
+ }
923
+
924
+ if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.format.isLine(parentCon) && !parentCon.nextSibling && dom.check.isZeroWidth(startCon.textContent.slice(0, startOff))) {
925
+ let sameTag = false;
926
+ let s = startCon.previousSibling;
927
+ while (s) {
928
+ if (!dom.check.isZeroWidth(s)) {
929
+ sameTag = false;
930
+ break;
931
+ }
932
+ s = s.previousSibling;
933
+ }
934
+
935
+ if (sameTag) {
936
+ dom.utils.copyTagAttributes(parentCon, newInnerNode);
937
+
938
+ return {
939
+ ancestor: element,
940
+ container: startCon,
941
+ offset: startOff
942
+ };
943
+ }
944
+ }
945
+
946
+ // add tag
947
+ _removeCheck.v = false;
948
+ const inst = this;
949
+ const el = element;
950
+ const nNodeArray = [newInnerNode];
951
+ const pNode = element.cloneNode(false);
952
+
953
+ let container = startCon;
954
+ let offset = startOff;
955
+ let passNode = false;
956
+ let pCurrent, newNode, appendNode, anchorNode;
957
+
958
+ (function recursionFunc(current, ancestor) {
959
+ const childNodes = current.childNodes;
960
+
961
+ for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
962
+ const child = /** @type {HTMLElement} */ (childNodes[i]);
963
+ if (!child) continue;
964
+ let coverNode = ancestor;
965
+
966
+ if (passNode && !dom.check.isBreak(child)) {
967
+ if (child.nodeType === 1) {
968
+ if (inst._isIgnoreNodeChange(child)) {
969
+ newInnerNode = newInnerNode.cloneNode(false);
970
+ cloneChild = child.cloneNode(true);
971
+ pNode.appendChild(cloneChild);
972
+ pNode.appendChild(newInnerNode);
973
+ nNodeArray.push(newInnerNode);
974
+
975
+ // end container
976
+ if (_endContainer && child.contains(_endContainer)) {
977
+ const endPath = dom.query.getNodePath(_endContainer, child);
978
+ _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
979
+ }
980
+ } else {
981
+ recursionFunc(child, child);
982
+ }
983
+ continue;
984
+ }
985
+
986
+ newNode = child;
987
+ pCurrent = [];
988
+ const anchors = [];
989
+ while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
990
+ vNode = validation(newNode);
991
+ if (newNode.nodeType === 1 && vNode) {
992
+ if (_isMaintainedNode(newNode)) {
993
+ if (!anchorNode) anchors.push(vNode);
994
+ } else {
995
+ pCurrent.push(vNode);
996
+ }
997
+ }
998
+ newNode = newNode.parentNode;
999
+ }
1000
+ pCurrent = pCurrent.concat(anchors);
1001
+
1002
+ const isTopNode = pCurrent.length > 0;
1003
+ const childNode = pCurrent.pop() || child;
1004
+ appendNode = newNode = childNode;
1005
+ while (pCurrent.length > 0) {
1006
+ newNode = pCurrent.pop();
1007
+ appendNode.appendChild(newNode);
1008
+ appendNode = newNode;
1009
+ }
1010
+
1011
+ if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
1012
+ newInnerNode = newInnerNode.cloneNode(false);
1013
+ pNode.appendChild(newInnerNode);
1014
+ nNodeArray.push(newInnerNode);
1015
+ }
1016
+
1017
+ if (!anchorNode && _isMaintainedNode(childNode)) {
1018
+ newInnerNode = newInnerNode.cloneNode(false);
1019
+ const aChildren = childNode.childNodes;
1020
+ for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
1021
+ newInnerNode.appendChild(aChildren[a]);
1022
+ }
1023
+ childNode.appendChild(newInnerNode);
1024
+ pNode.appendChild(childNode);
1025
+ ancestor = !_isMaintainedNode(newNode) ? newNode : newInnerNode;
1026
+ nNodeArray.push(newInnerNode);
1027
+ } else if (isTopNode) {
1028
+ newInnerNode.appendChild(childNode);
1029
+ ancestor = newNode;
1030
+ } else {
1031
+ ancestor = newInnerNode;
1032
+ }
1033
+
1034
+ if (anchorNode && child.nodeType === 3) {
1035
+ if (_getMaintainedNode(child)) {
1036
+ const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
1037
+ return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
1038
+ });
1039
+ anchorNode.appendChild(ancestorAnchorNode);
1040
+ newInnerNode = ancestorAnchorNode.cloneNode(false);
1041
+ nNodeArray.push(newInnerNode);
1042
+ pNode.appendChild(newInnerNode);
1043
+ } else {
1044
+ anchorNode = null;
1045
+ }
1046
+ }
1047
+ }
1048
+
1049
+ // startContainer
1050
+ if (!passNode && child === container) {
1051
+ let line = pNode;
1052
+ anchorNode = _getMaintainedNode(child);
1053
+
1054
+ let _prevText = '';
1055
+ let _nextText = '';
1056
+ if (container.nodeType === 3) {
1057
+ const cText = /** @type {Text} */ (container);
1058
+ _prevText = cText.substringData(0, offset);
1059
+ _nextText = cText.substringData(offset, cText.length - offset);
1060
+ }
1061
+
1062
+ const prevNode = dom.utils.createTextNode(_prevText);
1063
+ const textNode = dom.utils.createTextNode(_nextText);
1064
+
1065
+ if (anchorNode) {
1066
+ const a = _getMaintainedNode(ancestor);
1067
+ if (a && a.parentNode !== line) {
1068
+ let m = a;
1069
+ let p = null;
1070
+ while (m.parentNode !== line) {
1071
+ ancestor = p = m.parentNode.cloneNode(false);
1072
+ while (m.childNodes[0]) {
1073
+ p.appendChild(m.childNodes[0]);
1074
+ }
1075
+ m.appendChild(p);
1076
+ m = m.parentNode;
1077
+ }
1078
+ m.parentNode.appendChild(a);
1079
+ }
1080
+ anchorNode = anchorNode.cloneNode(false);
1081
+ }
1082
+
1083
+ if (!dom.check.isZeroWidth(prevNode)) {
1084
+ ancestor.appendChild(prevNode);
1085
+ }
1086
+
1087
+ const prevAnchorNode = _getMaintainedNode(ancestor);
1088
+ if (prevAnchorNode) anchorNode = prevAnchorNode;
1089
+ if (anchorNode) line = anchorNode;
1090
+
1091
+ newNode = ancestor;
1092
+ pCurrent = [];
1093
+ while (newNode !== line && newNode !== null) {
1094
+ vNode = validation(newNode);
1095
+ if (newNode.nodeType === 1 && vNode) {
1096
+ pCurrent.push(vNode);
1097
+ }
1098
+ newNode = newNode.parentNode;
1099
+ }
1100
+
1101
+ const childNode = pCurrent.pop() || ancestor;
1102
+ appendNode = newNode = childNode;
1103
+ while (pCurrent.length > 0) {
1104
+ newNode = pCurrent.pop();
1105
+ appendNode.appendChild(newNode);
1106
+ appendNode = newNode;
1107
+ }
1108
+
1109
+ if (childNode !== ancestor) {
1110
+ newInnerNode.appendChild(childNode);
1111
+ ancestor = newNode;
1112
+ } else {
1113
+ ancestor = newInnerNode;
1114
+ }
1115
+
1116
+ if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
1117
+ line.appendChild(newInnerNode);
1118
+
1119
+ container = textNode;
1120
+ offset = 0;
1121
+ passNode = true;
1122
+
1123
+ ancestor.appendChild(container);
1124
+ continue;
1125
+ }
1126
+
1127
+ vNode = !passNode ? child.cloneNode(false) : validation(child);
1128
+ if (vNode) {
1129
+ ancestor.appendChild(vNode);
1130
+ if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
1131
+ }
1132
+
1133
+ recursionFunc(child, coverNode);
1134
+ }
1135
+ })(element, pNode);
1136
+
1137
+ // not remove tag
1138
+ if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
1139
+ return {
1140
+ ancestor: element,
1141
+ container: startCon,
1142
+ offset: startOff,
1143
+ endContainer: _endContainer
1144
+ };
1145
+ }
1146
+
1147
+ isRemoveFormat &&= isRemoveNode;
1148
+
1149
+ if (isRemoveFormat) {
1150
+ for (let i = 0; i < nNodeArray.length; i++) {
1151
+ const removeNode = nNodeArray[i];
1152
+
1153
+ const rChildren = removeNode.childNodes;
1154
+ const textNode = rChildren[0];
1155
+ while (rChildren[0]) {
1156
+ pNode.insertBefore(rChildren[0], removeNode);
1157
+ }
1158
+ dom.utils.removeItem(removeNode);
1159
+
1160
+ if (i === 0) container = textNode;
1161
+ }
1162
+ } else if (isRemoveNode) {
1163
+ newInnerNode = newInnerNode.firstChild;
1164
+ for (let i = 0; i < nNodeArray.length; i++) {
1165
+ SN_StripRemoveNode(nNodeArray[i]);
1166
+ }
1167
+ }
1168
+
1169
+ if (!isRemoveFormat && pNode.childNodes.length === 0) {
1170
+ if (element.childNodes) {
1171
+ container = element.childNodes[0];
1172
+ } else {
1173
+ container = dom.utils.createTextNode(unicode.zeroWidthSpace);
1174
+ element.appendChild(container);
1175
+ }
1176
+ } else {
1177
+ this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
1178
+
1179
+ if (dom.check.isZeroWidth(pNode.textContent)) {
1180
+ container = pNode.firstChild;
1181
+ offset = 0;
1182
+ }
1183
+
1184
+ // node change
1185
+ const offsets = {
1186
+ s: 0,
1187
+ e: 0
1188
+ };
1189
+ const path = dom.query.getNodePath(container, pNode, offsets);
1190
+ offset += offsets.s;
1191
+
1192
+ // tag merge
1193
+ const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
1194
+
1195
+ element.parentNode.replaceChild(pNode, element);
1196
+
1197
+ container = dom.query.getNodeFromPath(path, pNode);
1198
+ offset += newOffsets[0];
1199
+ }
1200
+
1201
+ return {
1202
+ ancestor: pNode,
1203
+ container: container,
1204
+ offset: offset,
1205
+ endContainer: _endContainer
1206
+ };
1207
+ },
1208
+
1209
+ /**
1210
+ * @private
1211
+ * @this {InlineThis}
1212
+ * @description wraps mid lines selected text.
1213
+ * @param {HTMLElement} element The node of the line that contains the selected text node.
1214
+ * @param {Node} newInnerNode The dom that will wrap the selected text area
1215
+ * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
1216
+ * @param {boolean} isRemoveFormat Is the remove all formats command?
1217
+ * @param {boolean} isRemoveNode "newInnerNode" is remove node?
1218
+ * @param {Node} _endContainer Offset node of last line already modified (end.container)
1219
+ * @returns {NodeStyleContainerType} { ancestor, endContainer: "If end container is renewed, returned renewed node" }
1220
+ */
1221
+ _setNode_middleLine(element, newInnerNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, _endContainer) {
1222
+ // not add tag
1223
+ if (!isRemoveNode) {
1224
+ // end container path
1225
+ let endPath = null;
1226
+ if (_endContainer && element.contains(_endContainer)) endPath = dom.query.getNodePath(_endContainer, element);
1227
+
1228
+ const tempNode = element.cloneNode(true);
1229
+ const newNodeName = /** @type {HTMLElement} */ (newInnerNode).nodeName;
1230
+ const newCssText = /** @type {HTMLElement} */ (newInnerNode).style.cssText;
1231
+ const newClass = /** @type {HTMLElement} */ (newInnerNode).className;
1232
+
1233
+ let children = tempNode.childNodes;
1234
+ let i = 0,
1235
+ len = children.length;
1236
+ for (let child; i < len; i++) {
1237
+ child = /** @type {HTMLElement} */ (children[i]);
1238
+ if (child.nodeType === 3) break;
1239
+ if (child.nodeName === newNodeName) {
1240
+ child.style.cssText += newCssText;
1241
+ dom.utils.addClass(child, newClass);
1242
+ } else if (!dom.check.isBreak(child) && this._isIgnoreNodeChange(child)) {
1243
+ continue;
1244
+ } else if (len === 1) {
1245
+ children = child.childNodes;
1246
+ len = children.length;
1247
+ i = -1;
1248
+ continue;
1249
+ } else {
1250
+ break;
1251
+ }
1252
+ }
1253
+
1254
+ if (len > 0 && i === len) {
1255
+ element.innerHTML = /** @type {HTMLElement} */ (tempNode).innerHTML;
1256
+ return {
1257
+ ancestor: element,
1258
+ endContainer: endPath ? dom.query.getNodeFromPath(endPath, element) : null
1259
+ };
1260
+ }
1261
+ }
1262
+
1263
+ // add tag
1264
+ _removeCheck.v = false;
1265
+ const inst = this;
1266
+ const pNode = element.cloneNode(false);
1267
+ const nNodeArray = [newInnerNode];
1268
+ let noneChange = true;
1269
+
1270
+ (function recursionFunc(current, ancestor) {
1271
+ const childNodes = current.childNodes;
1272
+
1273
+ for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
1274
+ const child = /** @type {HTMLElement} */ (childNodes[i]);
1275
+ if (!child) continue;
1276
+ let coverNode = ancestor;
1277
+
1278
+ if (!dom.check.isBreak(child) && inst._isIgnoreNodeChange(child)) {
1279
+ if (newInnerNode.childNodes.length > 0) {
1280
+ pNode.appendChild(newInnerNode);
1281
+ newInnerNode = newInnerNode.cloneNode(false);
1282
+ }
1283
+
1284
+ cloneChild = child.cloneNode(true);
1285
+ pNode.appendChild(cloneChild);
1286
+ pNode.appendChild(newInnerNode);
1287
+ nNodeArray.push(newInnerNode);
1288
+ ancestor = newInnerNode;
1289
+
1290
+ // end container
1291
+ if (_endContainer && child.contains(_endContainer)) {
1292
+ const endPath = dom.query.getNodePath(_endContainer, child);
1293
+ _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
1294
+ }
1295
+
1296
+ continue;
1297
+ } else {
1298
+ vNode = validation(child);
1299
+ if (vNode) {
1300
+ noneChange = false;
1301
+ ancestor.appendChild(vNode);
1302
+ if (child.nodeType === 1) coverNode = vNode;
1303
+ }
1304
+ }
1305
+
1306
+ if (!dom.check.isBreak(child)) recursionFunc(child, coverNode);
1307
+ }
1308
+ })(element, newInnerNode);
1309
+
1310
+ // not remove tag
1311
+ if (noneChange || (isRemoveNode && !isRemoveFormat && !_removeCheck.v))
1312
+ return {
1313
+ ancestor: element,
1314
+ endContainer: _endContainer
1315
+ };
1316
+
1317
+ pNode.appendChild(newInnerNode);
1318
+
1319
+ if (isRemoveFormat && isRemoveNode) {
1320
+ for (let i = 0; i < nNodeArray.length; i++) {
1321
+ const removeNode = nNodeArray[i];
1322
+
1323
+ const rChildren = removeNode.childNodes;
1324
+ while (rChildren[0]) {
1325
+ pNode.insertBefore(rChildren[0], removeNode);
1326
+ }
1327
+ dom.utils.removeItem(removeNode);
1328
+ }
1329
+ } else if (isRemoveNode) {
1330
+ newInnerNode = newInnerNode.firstChild;
1331
+ for (let i = 0; i < nNodeArray.length; i++) {
1332
+ SN_StripRemoveNode(nNodeArray[i]);
1333
+ }
1334
+ }
1335
+
1336
+ this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
1337
+ this.nodeTransform.mergeSameTags(pNode, null, true);
1338
+
1339
+ // node change
1340
+ element.parentNode.replaceChild(pNode, element);
1341
+ return {
1342
+ ancestor: pNode,
1343
+ endContainer: _endContainer
1344
+ };
1345
+ },
1346
+
1347
+ /**
1348
+ * @private
1349
+ * @this {InlineThis}
1350
+ * @description wraps last line selected text.
1351
+ * @param {Node} element The node of the line that contains the selected text node.
1352
+ * @param {Node} newInnerNode The dom that will wrap the selected text area
1353
+ * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
1354
+ * @param {Node} endCon The endContainer property of the selection object.
1355
+ * @param {number} endOff The endOffset property of the selection object.
1356
+ * @param {boolean} isRemoveFormat Is the remove all formats command?
1357
+ * @param {boolean} isRemoveNode "newInnerNode" is remove node?
1358
+ * @returns {NodeStyleContainerType} { ancestor, container, offset }
1359
+ */
1360
+ _setNode_endLine(element, newInnerNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
1361
+ // not add tag
1362
+ let parentCon = endCon.parentNode;
1363
+ while (!parentCon.nextSibling && !parentCon.previousSibling && !this.format.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
1364
+ if (parentCon.nodeName === newInnerNode.nodeName) break;
1365
+ parentCon = parentCon.parentNode;
1366
+ }
1367
+
1368
+ if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.format.isLine(parentCon) && !parentCon.previousSibling && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
1369
+ let sameTag = false;
1370
+ let e = endCon.nextSibling;
1371
+ while (e) {
1372
+ if (!dom.check.isZeroWidth(e)) {
1373
+ sameTag = false;
1374
+ break;
1375
+ }
1376
+ e = e.nextSibling;
1377
+ }
1378
+
1379
+ if (sameTag) {
1380
+ dom.utils.copyTagAttributes(parentCon, newInnerNode);
1381
+
1382
+ return {
1383
+ ancestor: element,
1384
+ container: endCon,
1385
+ offset: endOff
1386
+ };
1387
+ }
1388
+ }
1389
+
1390
+ // add tag
1391
+ _removeCheck.v = false;
1392
+ const inst = this;
1393
+ const el = element;
1394
+ const nNodeArray = [newInnerNode];
1395
+ const pNode = element.cloneNode(false);
1396
+
1397
+ let container = endCon;
1398
+ let offset = endOff;
1399
+ let passNode = false;
1400
+ let pCurrent, newNode, appendNode, anchorNode;
1401
+
1402
+ (function recursionFunc(current, ancestor) {
1403
+ const childNodes = current.childNodes;
1404
+
1405
+ for (let i = childNodes.length - 1, vNode; 0 <= i; i--) {
1406
+ const child = childNodes[i];
1407
+ if (!child) continue;
1408
+ let coverNode = ancestor;
1409
+
1410
+ if (passNode && !dom.check.isBreak(child)) {
1411
+ if (child.nodeType === 1) {
1412
+ if (inst._isIgnoreNodeChange(child)) {
1413
+ newInnerNode = newInnerNode.cloneNode(false);
1414
+ const cloneChild = child.cloneNode(true);
1415
+ pNode.insertBefore(cloneChild, ancestor);
1416
+ pNode.insertBefore(newInnerNode, cloneChild);
1417
+ nNodeArray.push(newInnerNode);
1418
+ } else {
1419
+ recursionFunc(child, child);
1420
+ }
1421
+ continue;
1422
+ }
1423
+
1424
+ newNode = child;
1425
+ pCurrent = [];
1426
+ const anchors = [];
1427
+ while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
1428
+ vNode = validation(newNode);
1429
+ if (vNode && newNode.nodeType === 1) {
1430
+ if (_isMaintainedNode(newNode)) {
1431
+ if (!anchorNode) anchors.push(vNode);
1432
+ } else {
1433
+ pCurrent.push(vNode);
1434
+ }
1435
+ }
1436
+ newNode = newNode.parentNode;
1437
+ }
1438
+ pCurrent = pCurrent.concat(anchors);
1439
+
1440
+ const isTopNode = pCurrent.length > 0;
1441
+ const childNode = pCurrent.pop() || child;
1442
+ appendNode = newNode = childNode;
1443
+ while (pCurrent.length > 0) {
1444
+ newNode = pCurrent.pop();
1445
+ appendNode.appendChild(newNode);
1446
+ appendNode = newNode;
1447
+ }
1448
+
1449
+ if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
1450
+ newInnerNode = newInnerNode.cloneNode(false);
1451
+ pNode.insertBefore(newInnerNode, pNode.firstChild);
1452
+ nNodeArray.push(newInnerNode);
1453
+ }
1454
+
1455
+ if (!anchorNode && _isMaintainedNode(childNode)) {
1456
+ newInnerNode = newInnerNode.cloneNode(false);
1457
+ const aChildren = childNode.childNodes;
1458
+ for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
1459
+ newInnerNode.appendChild(aChildren[a]);
1460
+ }
1461
+ childNode.appendChild(newInnerNode);
1462
+ pNode.insertBefore(childNode, pNode.firstChild);
1463
+ nNodeArray.push(newInnerNode);
1464
+ if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
1465
+ else ancestor = newInnerNode;
1466
+ } else if (isTopNode) {
1467
+ newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
1468
+ ancestor = newNode;
1469
+ } else {
1470
+ ancestor = newInnerNode;
1471
+ }
1472
+
1473
+ if (anchorNode && child.nodeType === 3) {
1474
+ if (_getMaintainedNode(child)) {
1475
+ const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
1476
+ return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
1477
+ });
1478
+ anchorNode.appendChild(ancestorAnchorNode);
1479
+ newInnerNode = ancestorAnchorNode.cloneNode(false);
1480
+ nNodeArray.push(newInnerNode);
1481
+ pNode.insertBefore(newInnerNode, pNode.firstChild);
1482
+ } else {
1483
+ anchorNode = null;
1484
+ }
1485
+ }
1486
+ }
1487
+
1488
+ // endContainer
1489
+ if (!passNode && child === container) {
1490
+ anchorNode = _getMaintainedNode(child);
1491
+
1492
+ let _prevText = '';
1493
+ let _nextText = '';
1494
+ if (container.nodeType === 3) {
1495
+ const cText = /** @type {Text} */ (container);
1496
+ _prevText = cText.substringData(offset, cText.length - offset);
1497
+ _nextText = cText.substringData(0, offset);
1498
+ }
1499
+
1500
+ const afterNode = dom.utils.createTextNode(_prevText);
1501
+ const textNode = dom.utils.createTextNode(_nextText);
1502
+
1503
+ if (anchorNode) {
1504
+ anchorNode = anchorNode.cloneNode(false);
1505
+ const a = _getMaintainedNode(ancestor);
1506
+ if (a.parentNode !== pNode) {
1507
+ let m = a;
1508
+ let p = null;
1509
+ while (m.parentNode !== pNode) {
1510
+ ancestor = p = m.parentNode.cloneNode(false);
1511
+ while (m.childNodes[0]) {
1512
+ p.appendChild(m.childNodes[0]);
1513
+ }
1514
+ m.appendChild(p);
1515
+ m = m.parentNode;
1516
+ }
1517
+ m.parentNode.insertBefore(a, m.parentNode.firstChild);
1518
+ }
1519
+ anchorNode = anchorNode.cloneNode(false);
1520
+ } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
1521
+ newInnerNode = newInnerNode.cloneNode(false);
1522
+ pNode.appendChild(newInnerNode);
1523
+ nNodeArray.push(newInnerNode);
1524
+ }
1525
+
1526
+ if (!dom.check.isZeroWidth(afterNode)) {
1527
+ ancestor.insertBefore(afterNode, ancestor.firstChild);
1528
+ }
1529
+
1530
+ newNode = ancestor;
1531
+ pCurrent = [];
1532
+ while (newNode !== pNode && newNode !== null) {
1533
+ vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
1534
+ if (vNode && newNode.nodeType === 1) {
1535
+ pCurrent.push(vNode);
1536
+ }
1537
+ newNode = newNode.parentNode;
1538
+ }
1539
+
1540
+ const childNode = pCurrent.pop() || ancestor;
1541
+ appendNode = newNode = childNode;
1542
+ while (pCurrent.length > 0) {
1543
+ newNode = pCurrent.pop();
1544
+ appendNode.appendChild(newNode);
1545
+ appendNode = newNode;
1546
+ }
1547
+
1548
+ if (childNode !== ancestor) {
1549
+ newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
1550
+ ancestor = newNode;
1551
+ } else {
1552
+ ancestor = newInnerNode;
1553
+ }
1554
+
1555
+ if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
1556
+
1557
+ if (anchorNode) {
1558
+ anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
1559
+ pNode.insertBefore(anchorNode, pNode.firstChild);
1560
+ anchorNode = null;
1561
+ } else {
1562
+ pNode.insertBefore(newInnerNode, pNode.firstChild);
1563
+ }
1564
+
1565
+ container = textNode;
1566
+ offset = textNode.data.length;
1567
+ passNode = true;
1568
+
1569
+ ancestor.insertBefore(container, ancestor.firstChild);
1570
+ continue;
1571
+ }
1572
+
1573
+ vNode = !passNode ? child.cloneNode(false) : validation(child);
1574
+ if (vNode) {
1575
+ ancestor.insertBefore(vNode, ancestor.firstChild);
1576
+ if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
1577
+ }
1578
+
1579
+ recursionFunc(child, coverNode);
1580
+ }
1581
+ })(element, pNode);
1582
+
1583
+ // not remove tag
1584
+ if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
1585
+ return {
1586
+ ancestor: element,
1587
+ container: endCon,
1588
+ offset: endOff
1589
+ };
1590
+ }
1591
+
1592
+ isRemoveFormat &&= isRemoveNode;
1593
+
1594
+ if (isRemoveFormat) {
1595
+ for (let i = 0; i < nNodeArray.length; i++) {
1596
+ const removeNode = nNodeArray[i];
1597
+
1598
+ const rChildren = removeNode.childNodes;
1599
+ let textNode = null;
1600
+ while (rChildren[0]) {
1601
+ textNode = rChildren[0];
1602
+ pNode.insertBefore(textNode, removeNode);
1603
+ }
1604
+ dom.utils.removeItem(removeNode);
1605
+
1606
+ if (i === nNodeArray.length - 1) {
1607
+ container = textNode;
1608
+ offset = textNode.textContent.length;
1609
+ }
1610
+ }
1611
+ } else if (isRemoveNode) {
1612
+ newInnerNode = newInnerNode.firstChild;
1613
+ for (let i = 0; i < nNodeArray.length; i++) {
1614
+ SN_StripRemoveNode(nNodeArray[i]);
1615
+ }
1616
+ }
1617
+
1618
+ if (!isRemoveFormat && pNode.childNodes.length === 0) {
1619
+ if (element.childNodes) {
1620
+ container = element.childNodes[0];
1621
+ } else {
1622
+ container = dom.utils.createTextNode(unicode.zeroWidthSpace);
1623
+ element.appendChild(container);
1624
+ }
1625
+ } else {
1626
+ if (!isRemoveNode && newInnerNode.textContent.length === 0) {
1627
+ this.nodeTransform.removeEmptyNode(pNode, null, false);
1628
+ return {
1629
+ ancestor: null,
1630
+ container: null,
1631
+ offset: 0
1632
+ };
1633
+ }
1634
+
1635
+ this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
1636
+
1637
+ if (dom.check.isZeroWidth(pNode.textContent)) {
1638
+ container = pNode.firstChild;
1639
+ offset = container.textContent.length;
1640
+ } else if (dom.check.isZeroWidth(container)) {
1641
+ container = newInnerNode;
1642
+ offset = 1;
1643
+ }
1644
+
1645
+ // node change
1646
+ const offsets = {
1647
+ s: 0,
1648
+ e: 0
1649
+ };
1650
+ const path = dom.query.getNodePath(container, pNode, offsets);
1651
+ offset += offsets.s;
1652
+
1653
+ // tag merge
1654
+ const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
1655
+
1656
+ element.parentNode.replaceChild(pNode, element);
1657
+
1658
+ container = dom.query.getNodeFromPath(path, pNode);
1659
+ offset += newOffsets[0];
1660
+ }
1661
+
1662
+ return {
1663
+ ancestor: pNode,
1664
+ container: container,
1665
+ offset: container.nodeType === 1 && offset === 1 ? container.childNodes.length : offset
1666
+ };
1667
+ },
1668
+
1669
+ /**
1670
+ * @private
1671
+ * @this {InlineThis}
1672
+ * @description Node with font-size style
1673
+ * @param {Node} element Element to check
1674
+ * @returns {boolean}
1675
+ */
1676
+ _sn_isSizeNode(element) {
1677
+ return element && typeof element !== 'string' && element.nodeType !== 3 && this.format.isTextStyleNode(element) && !!element.style.fontSize;
1678
+ },
1679
+
1680
+ /**
1681
+ * @private
1682
+ * @this {InlineThis}
1683
+ * @description Return the parent maintained tag. (bind and use a util object)
1684
+ * @param {boolean} _isRemove is remove anchor
1685
+ * @param {boolean} _isSizeNode is size span node
1686
+ * @param {Node} element Element
1687
+ * @returns {Node|null}
1688
+ */
1689
+ _sn_getMaintainedNode(_isRemove, _isSizeNode, element) {
1690
+ if (!element || _isRemove) return null;
1691
+ return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) || (!_isSizeNode ? dom.query.getParentElement(element, this._sn_isSizeNode.bind(this)) : null);
1692
+ },
1693
+
1694
+ /**
1695
+ * @private
1696
+ * @this {InlineThis}
1697
+ * @description Check if element is a tag that should be persisted. (bind and use a util object)
1698
+ * @param {boolean} _isRemove is remove anchor
1699
+ * @param {boolean} _isSizeNode is size span node
1700
+ * @param {Node} element Element
1701
+ * @returns {boolean}
1702
+ */
1703
+ _sn_isMaintainedNode(_isRemove, _isSizeNode, element) {
1704
+ if (!element || _isRemove || element.nodeType !== 1) return false;
1705
+ const anchor = this._isNonSplitNode(element);
1706
+ return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) ? anchor : anchor || (!_isSizeNode ? this._sn_isSizeNode(element) : false);
1707
+ },
1708
+
1709
+ /**
1710
+ * @private
1711
+ * @this {InlineThis}
1712
+ * @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)
1713
+ * @param {Node} el List cell element. <li>
1714
+ * @param {?Node} child Variable for recursive call. ("null" on the first call)
1715
+ */
1716
+ _sn_setCommonListStyle(el, child) {
1717
+ if (!dom.check.isListCell(el)) return;
1718
+
1719
+ const children = dom.utils.arrayFilter((child || el).childNodes, (current) => !dom.check.isBreak(current));
1720
+ child = children[0];
1721
+
1722
+ if (!dom.check.isElement(child) || children.length > 1) return;
1723
+
1724
+ // set cell style---
1725
+ const childStyle = child.style;
1726
+ const elStyle = el.style;
1727
+ const nodeName = child.nodeName.toLowerCase();
1728
+ let appliedEl = false;
1729
+
1730
+ // bold, italic
1731
+ if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').bold.toLowerCase()) elStyle.fontWeight = 'bold';
1732
+ if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').italic.toLowerCase()) elStyle.fontStyle = 'italic';
1733
+
1734
+ // styles
1735
+ const cKeys = converter.getValues(childStyle);
1736
+ if (cKeys.length > 0) {
1737
+ for (let i = 0, len = this._listCamel.length; i < len; i++) {
1738
+ if (cKeys.includes(this._listKebab[i])) {
1739
+ elStyle[this._listCamel[i]] = childStyle[this._listCamel[i]];
1740
+ childStyle.removeProperty(this._listKebab[i]);
1741
+ appliedEl = true;
1742
+ }
1743
+ }
1744
+ }
1745
+
1746
+ this._sn_setCommonListStyle(el, child);
1747
+ if (!appliedEl) return;
1748
+
1749
+ // common style
1750
+ if (childStyle.length === 0) {
1751
+ const ch = child.childNodes;
1752
+ const p = child.parentNode;
1753
+ const n = child.nextSibling;
1754
+ while (ch.length > 0) {
1755
+ p.insertBefore(ch[0], n);
1756
+ }
1757
+ dom.utils.removeItem(child);
1758
+ }
1759
+ },
1760
+
1761
+ /**
1762
+ * @private
1763
+ * @this {InlineThis}
1764
+ * @description Watch the applied text nodes and adjust the common styles of the list.
1765
+ * @param {Node} el "LI" element
1766
+ * @param {Array|null} styleArray Refer style array
1767
+ */
1768
+ _sn_resetCommonListCell(el, styleArray) {
1769
+ if (!dom.check.isListCell(el)) return;
1770
+ styleArray ||= this._listKebab;
1771
+
1772
+ const children = dom.utils.arrayFilter(el.childNodes, (current) => !dom.check.isBreak(current));
1773
+ const elStyles = el.style;
1774
+
1775
+ const ec = [],
1776
+ ek = [],
1777
+ elKeys = converter.getValues(elStyles);
1778
+ for (let i = 0, len = this._listKebab.length; i < len; i++) {
1779
+ if (elKeys.includes(this._listKebab[i]) && styleArray.includes(this._listKebab[i])) {
1780
+ ec.push(this._listCamel[i]);
1781
+ ek.push(this._listKebab[i]);
1782
+ }
1783
+ }
1784
+
1785
+ if (ec.length === 0) return;
1786
+
1787
+ // reset cell style---
1788
+ const refer = dom.utils.createElement('SPAN');
1789
+ for (let i = 0, len = ec.length; i < len; i++) {
1790
+ refer.style[ec[i]] = elStyles[ek[i]];
1791
+ elStyles.removeProperty(ek[i]);
1792
+ }
1793
+
1794
+ let sel = refer.cloneNode(false);
1795
+ let r = null,
1796
+ appliedEl = false;
1797
+ for (let i = 0, len = children.length, c, s; i < len; i++) {
1798
+ c = /** @type {HTMLElement} */ (children[i]);
1799
+ if (this.options.get('_defaultStyleTagMap')[c.nodeName.toLowerCase()]) continue;
1800
+
1801
+ s = converter.getValues(c.style);
1802
+ if (
1803
+ s.length === 0 ||
1804
+ (ec.some(function (k) {
1805
+ return !s.includes(k);
1806
+ }) &&
1807
+ s.some(function (k) {
1808
+ ec.includes(k);
1809
+ }))
1810
+ ) {
1811
+ r = c.nextSibling;
1812
+ sel.appendChild(c);
1813
+ } else if (sel.childNodes.length > 0) {
1814
+ el.insertBefore(sel, r);
1815
+ sel = refer.cloneNode(false);
1816
+ r = null;
1817
+ appliedEl = true;
1818
+ }
1819
+ }
1820
+
1821
+ if (sel.childNodes.length > 0) {
1822
+ el.insertBefore(sel, r);
1823
+ appliedEl = true;
1824
+ }
1825
+ if (elStyles.length === 0) {
1826
+ el.removeAttribute('style');
1827
+ }
1828
+
1829
+ return appliedEl;
1830
+ },
1831
+
1832
+ constructor: Inline
1833
+ };
1834
+
1835
+ /**
1836
+ * @private
1837
+ * @description Strip remove node
1838
+ * @param {Node} removeNode The remove node
1839
+ * @private
1840
+ */
1841
+ function SN_StripRemoveNode(removeNode) {
1842
+ const element = removeNode.parentNode;
1843
+ if (!removeNode || removeNode.nodeType === 3 || !element) return;
1844
+
1845
+ const children = removeNode.childNodes;
1846
+ while (children[0]) {
1847
+ element.insertBefore(children[0], removeNode);
1848
+ }
1849
+
1850
+ element.removeChild(removeNode);
1851
+ }
1852
+
1853
+ export default Inline;