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
@@ -3,32 +3,22 @@
3
3
  */
4
4
 
5
5
  import CoreInjector from '../../editorInjector/_core';
6
- import { dom, unicode, numbers, converter } from '../../helper';
6
+ import { dom, unicode, numbers } from '../../helper';
7
7
 
8
8
  /**
9
9
  * @typedef {Omit<Format & Partial<__se__EditorInjector>, 'format'>} FormatThis
10
10
  */
11
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
12
  /**
21
13
  * @constructor
22
14
  * @this {FormatThis}
23
- * @description Classes related to editor formats such as line creation, line retrieval from selected range, etc.
15
+ * @description Classes related to editor formats such as "line" and "block".
24
16
  * @param {__se__EditorCore} editor - The root editor instance
25
17
  */
26
18
  function Format(editor) {
27
19
  CoreInjector.call(this, editor);
28
20
 
29
21
  // members
30
- this._listCamel = this.options.get('__listCommonStyle');
31
- this._listKebab = converter.camelToKebabCase(this.options.get('__listCommonStyle'));
32
22
  this._formatLineCheck = this.options.get('formatLine').reg;
33
23
  this._formatBrLineCheck = this.options.get('formatBrLine').reg;
34
24
  this._formatBlockCheck = this.options.get('formatBlock').reg;
@@ -78,8 +68,8 @@ Format.prototype = {
78
68
  this.history.push(false);
79
69
 
80
70
  // document type
81
- if (this.editor.frameContext.has('documentType-use-header')) {
82
- this.editor.frameContext.get('documentType').reHeader();
71
+ if (this.frameContext.has('documentType_use_header')) {
72
+ this.frameContext.get('documentType').reHeader();
83
73
  }
84
74
  },
85
75
 
@@ -92,16 +82,24 @@ Format.prototype = {
92
82
  */
93
83
  getLine(node, validation) {
94
84
  if (!node) return null;
95
- if (!validation) {
96
- validation = function () {
97
- return true;
98
- };
99
- }
85
+
86
+ validation ||= () => true;
100
87
 
101
88
  while (node) {
102
89
  if (dom.check.isWysiwygFrame(node)) return null;
103
- if (this.isBlock(node)) return /** @type {HTMLElement} */ (node.firstElementChild);
104
- if (this.isLine(node) && validation(node)) return /** @type {HTMLElement} */ (node);
90
+
91
+ if (this.isBlock(node)) {
92
+ if (this.isLine(node.firstElementChild)) {
93
+ return /** @type {HTMLElement} */ (node.firstElementChild);
94
+ }
95
+ if (this.isLine(node)) {
96
+ return /** @type {HTMLElement} */ (node);
97
+ }
98
+ }
99
+
100
+ if (this.isLine(node) && validation(node)) {
101
+ return /** @type {HTMLElement} */ (node);
102
+ }
105
103
 
106
104
  node = node.parentNode;
107
105
  }
@@ -187,11 +185,8 @@ Format.prototype = {
187
185
  */
188
186
  getBrLine(element, validation) {
189
187
  if (!element) return null;
190
- if (!validation) {
191
- validation = function () {
192
- return true;
193
- };
194
- }
188
+
189
+ validation ||= () => true;
195
190
 
196
191
  while (element) {
197
192
  if (dom.check.isWysiwygFrame(element)) return null;
@@ -220,7 +215,7 @@ Format.prototype = {
220
215
  if (!this.isBrLine(element) && this.isBrLine(currentFormatEl || element.parentNode) && !this.component.is(element)) {
221
216
  oFormat = dom.utils.createElement('BR');
222
217
  } else {
223
- const oFormatName = lineNode ? (typeof lineNode === 'string' ? lineNode : lineNode.nodeName) : this.isLineOnly(currentFormatEl) ? currentFormatEl.nodeName : this.options.get('defaultLine');
218
+ const oFormatName = lineNode ? (typeof lineNode === 'string' ? lineNode : lineNode.nodeName) : this.isNormalLine(currentFormatEl) ? currentFormatEl.nodeName : this.options.get('defaultLine');
224
219
  oFormat = dom.utils.createElement(oFormatName, null, '<br>');
225
220
  if ((lineNode && typeof lineNode !== 'string') || (!lineNode && this.isLine(currentFormatEl))) {
226
221
  dom.utils.copyTagAttributes(oFormat, /** @type {Node} */ (lineNode || currentFormatEl), ['id']);
@@ -242,11 +237,8 @@ Format.prototype = {
242
237
  */
243
238
  getBlock(element, validation) {
244
239
  if (!element) return null;
245
- if (!validation) {
246
- validation = function () {
247
- return true;
248
- };
249
- }
240
+
241
+ validation ||= () => true;
250
242
 
251
243
  while (element) {
252
244
  if (dom.check.isWysiwygFrame(element)) return null;
@@ -296,7 +288,7 @@ Format.prototype = {
296
288
  }
297
289
  }
298
290
 
299
- const last = rangeLines[rangeLines.length - 1];
291
+ const last = rangeLines.at(-1);
300
292
  let standTag, beforeTag, pElement;
301
293
 
302
294
  if (this.isBlock(last) || this.isLine(last)) {
@@ -385,7 +377,7 @@ Format.prototype = {
385
377
  }
386
378
 
387
379
  if (!nested) block.appendChild(listParent);
388
- if (nextList) edge.removeArray[edge.removeArray.length - 1].appendChild(nextList);
380
+ if (nextList) edge.removeArray.at(-1).appendChild(nextList);
389
381
  listParent = null;
390
382
  nested = false;
391
383
  }
@@ -454,7 +446,7 @@ Format.prototype = {
454
446
  let so = range.startOffset;
455
447
  let eo = range.endOffset;
456
448
 
457
- let children = dom.query.getListChildNodes(blockElement, (current) => current.parentNode === blockElement);
449
+ let children = dom.query.getListChildNodes(blockElement, null, 1);
458
450
  let parent = blockElement.parentNode;
459
451
  let firstNode = null;
460
452
  let lastNode = null;
@@ -467,7 +459,7 @@ Format.prototype = {
467
459
  let moveComplete = false;
468
460
 
469
461
  const appendNode = (parentEl, insNode, sibling, originNode) => {
470
- if (dom.check.isZeroWidth(insNode)) {
462
+ if (insNode.childNodes.length === 1 && dom.check.isZeroWidth(insNode)) {
471
463
  insNode.innerHTML = unicode.zeroWidthSpace;
472
464
  so = eo = 1;
473
465
  }
@@ -486,12 +478,12 @@ Format.prototype = {
486
478
  c = insChildren[0];
487
479
  if (this._notTextNode(c) && !dom.check.isBreak(c) && !dom.check.isListCell(format)) {
488
480
  if (format.childNodes.length > 0) {
489
- if (!first) first = format;
481
+ first ||= format;
490
482
  parentEl.insertBefore(format, sibling);
491
483
  format = insNode.cloneNode(false);
492
484
  }
493
485
  parentEl.insertBefore(c, sibling);
494
- if (!first) first = c;
486
+ first ||= c;
495
487
  } else {
496
488
  format.appendChild(c);
497
489
  }
@@ -508,7 +500,7 @@ Format.prototype = {
508
500
  parentEl.parentNode.insertBefore(format, parentEl.nextElementSibling);
509
501
  } else {
510
502
  const originNext = originNode.nextElementSibling;
511
- const detachRange = this._removeNestedList(originNode, false);
503
+ const detachRange = this.listFormat.removeNested(originNode, false);
512
504
  if (blockElement !== detachRange || originNext !== originNode.nextElementSibling) {
513
505
  const fChildren = format.childNodes;
514
506
  while (fChildren[0]) {
@@ -523,7 +515,7 @@ Format.prototype = {
523
515
  parentEl.insertBefore(format, sibling);
524
516
  }
525
517
 
526
- if (!first) first = format;
518
+ first ||= format;
527
519
  }
528
520
 
529
521
  return first;
@@ -545,7 +537,7 @@ Format.prototype = {
545
537
 
546
538
  if (selectedFormats) lineIndex = selectedFormats.indexOf(insNode);
547
539
  if (selectedFormats && lineIndex === -1) {
548
- if (!rangeEl) rangeEl = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
540
+ rangeEl ||= /** @type {HTMLElement} */ (blockElement.cloneNode(false));
549
541
  rangeEl.appendChild(insNode);
550
542
  } else {
551
543
  if (selectedFormats) next = selectedFormats[lineIndex + 1];
@@ -557,7 +549,7 @@ Format.prototype = {
557
549
  if (!newList && dom.check.isListCell(insNode)) {
558
550
  if (next && dom.query.getNodeDepth(insNode) !== dom.query.getNodeDepth(next) && (dom.check.isListCell(parent) || dom.utils.arrayFind(insNode.children, dom.check.isList))) {
559
551
  const insNext = insNode.nextElementSibling;
560
- const detachRange = this._removeNestedList(insNode, false);
552
+ const detachRange = this.listFormat.removeNested(insNode, false);
561
553
  if (blockElement !== detachRange || insNext !== insNode.nextElementSibling) {
562
554
  blockElement = detachRange;
563
555
  reset = true;
@@ -568,10 +560,10 @@ Format.prototype = {
568
560
  shouldDelete
569
561
  ? inner.nodeName
570
562
  : dom.check.isList(blockElement.parentNode) || dom.check.isListCell(blockElement.parentNode)
571
- ? 'LI'
572
- : dom.check.isTableCell(blockElement.parentNode)
573
- ? 'DIV'
574
- : this.options.get('defaultLine')
563
+ ? 'LI'
564
+ : dom.check.isTableCell(blockElement.parentNode)
565
+ ? 'DIV'
566
+ : this.options.get('defaultLine')
575
567
  );
576
568
  const isCell = dom.check.isListCell(insNode);
577
569
  const innerChildren = inner.childNodes;
@@ -601,9 +593,7 @@ Format.prototype = {
601
593
  if (!reset) {
602
594
  if (selectedFormats) {
603
595
  lastNode = insNode;
604
- if (!firstNode) {
605
- firstNode = insNode;
606
- }
596
+ firstNode ||= insNode;
607
597
  } else if (!firstNode) {
608
598
  firstNode = lastNode = insNode;
609
599
  }
@@ -615,7 +605,7 @@ Format.prototype = {
615
605
 
616
606
  if (reset) {
617
607
  reset = moveComplete = false;
618
- children = dom.query.getListChildNodes(blockElement, (current) => current.parentNode === blockElement);
608
+ children = dom.query.getListChildNodes(blockElement, null, 1);
619
609
  rangeEl = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
620
610
  parent = blockElement.parentNode;
621
611
  i = -1;
@@ -633,7 +623,7 @@ Format.prototype = {
633
623
  }
634
624
 
635
625
  if (newBlockElement) firstNode = newBlockElement.previousSibling;
636
- else if (!firstNode) firstNode = blockElement.previousSibling;
626
+ else firstNode ||= blockElement.previousSibling;
637
627
  rangeRight = blockElement.nextSibling !== rangeEl ? blockElement.nextSibling : rangeEl ? rangeEl.nextSibling : null;
638
628
 
639
629
  if (/** @type {HTMLElement} */ (blockElement).children.length === 0 || blockElement.textContent.length === 0) {
@@ -643,6 +633,8 @@ Format.prototype = {
643
633
  }
644
634
 
645
635
  let edge = null;
636
+ this.editor.effectNode = null;
637
+
646
638
  if (shouldDelete) {
647
639
  edge = {
648
640
  cc: rangeParent,
@@ -653,20 +645,23 @@ Format.prototype = {
653
645
  removeArray: removeArray
654
646
  };
655
647
  } else {
656
- if (!firstNode) firstNode = lastNode;
657
- if (!lastNode) lastNode = firstNode;
658
- const childEdge = dom.query.getEdgeChildNodes(firstNode, lastNode.parentNode ? firstNode : lastNode);
659
- edge = {
660
- cc: (childEdge.sc || childEdge.ec).parentNode,
661
- sc: childEdge.sc,
662
- so: so,
663
- ec: childEdge.ec,
664
- eo: eo,
665
- removeArray: null
666
- };
648
+ firstNode ||= lastNode;
649
+ lastNode ||= firstNode;
650
+ const childEdge = dom.query.getEdgeChildNodes(firstNode, lastNode?.parentNode ? firstNode : lastNode);
651
+ if (!childEdge) {
652
+ this.editor.focus();
653
+ } else {
654
+ edge = {
655
+ cc: (childEdge.sc || childEdge.ec).parentNode,
656
+ sc: childEdge.sc,
657
+ so: so,
658
+ ec: childEdge.ec,
659
+ eo: eo,
660
+ removeArray: null
661
+ };
662
+ }
667
663
  }
668
664
 
669
- this.editor.effectNode = null;
670
665
  if (skipHistory) return edge;
671
666
 
672
667
  if (!shouldDelete && edge) {
@@ -680,273 +675,6 @@ Format.prototype = {
680
675
  this.history.push(false);
681
676
  },
682
677
 
683
- /**
684
- * @this {FormatThis}
685
- * @description Append all selected "line" element to the list and insert.
686
- * @param {string} type List type. (ol | ul):[listStyleType]
687
- * @param {Array<Node>} selectedCells "line" elements or list cells.
688
- * @param {boolean} nested If true, indenting existing list cells.
689
- */
690
- applyList(type, selectedCells, nested) {
691
- const listTag = (type.split(':')[0] || 'ol').toUpperCase();
692
- const listStyle = type.split(':')[1] || '';
693
-
694
- let range = this.selection.getRange();
695
- let selectedFormats = /** @type {Array<HTMLElement>} */ (!selectedCells ? this.getLinesAndComponents(false) : selectedCells);
696
-
697
- if (selectedFormats.length === 0) {
698
- if (selectedCells) return;
699
- range = this.selection.getRangeAndAddLine(range, null);
700
- selectedFormats = this.getLinesAndComponents(false);
701
- if (selectedFormats.length === 0) return;
702
- }
703
-
704
- dom.query.sortNodeByDepth(selectedFormats, true);
705
-
706
- // merge
707
- const firstSel = selectedFormats[0];
708
- const lastSel = selectedFormats[selectedFormats.length - 1];
709
- let topEl = (dom.check.isListCell(firstSel) || this.component.is(firstSel)) && !firstSel.previousElementSibling ? firstSel.parentElement.previousElementSibling : firstSel.previousElementSibling;
710
- let bottomEl = (dom.check.isListCell(lastSel) || this.component.is(lastSel)) && !lastSel.nextElementSibling ? lastSel.parentElement.nextElementSibling : lastSel.nextElementSibling;
711
-
712
- const isCollapsed = range.collapsed;
713
- const originRange = {
714
- sc: range.startContainer,
715
- so: range.startContainer === range.endContainer && dom.check.isZeroWidth(range.startContainer) && range.startOffset === 0 && range.endOffset === 1 ? range.endOffset : range.startOffset,
716
- ec: range.endContainer,
717
- eo: range.endOffset
718
- };
719
- let afterRange = null;
720
- let isRemove = true;
721
-
722
- for (let i = 0, len = selectedFormats.length; i < len; i++) {
723
- if (!dom.check.isList(this.getBlock(selectedFormats[i], (current) => this.getBlock(current) && current !== selectedFormats[i]))) {
724
- isRemove = false;
725
- break;
726
- }
727
- }
728
-
729
- if (isRemove && (!topEl || firstSel.tagName !== topEl.tagName || listTag !== topEl.tagName.toUpperCase()) && (!bottomEl || lastSel.tagName !== bottomEl.tagName || listTag !== bottomEl.tagName.toUpperCase())) {
730
- if (nested) {
731
- for (let i = 0, len = selectedFormats.length; i < len; i++) {
732
- for (let j = i - 1; j >= 0; j--) {
733
- if (selectedFormats[j].contains(selectedFormats[i])) {
734
- selectedFormats.splice(i, 1);
735
- i--;
736
- len--;
737
- break;
738
- }
739
- }
740
- }
741
- }
742
-
743
- const currentFormat = this.getBlock(firstSel);
744
- const cancel = currentFormat?.tagName === listTag;
745
- let rangeArr, tempList;
746
- const passComponent = (current) => {
747
- return !this.component.is(current);
748
- };
749
-
750
- if (!cancel) {
751
- tempList = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
752
- }
753
-
754
- for (let i = 0, len = selectedFormats.length, r, o; i < len; i++) {
755
- o = this.getBlock(selectedFormats[i], passComponent);
756
- if (!o || !dom.check.isList(o)) continue;
757
-
758
- if (!r) {
759
- r = o;
760
- rangeArr = {
761
- r: r,
762
- f: [dom.query.getParentElement(selectedFormats[i], 'LI')]
763
- };
764
- } else {
765
- if (r !== o) {
766
- if (nested && dom.check.isListCell(o.parentNode)) {
767
- this._detachNested(rangeArr.f);
768
- } else {
769
- afterRange = this.removeBlock(rangeArr.f[0].parentElement, { selectedFormats: rangeArr.f, newBlockElement: tempList, shouldDelete: false, skipHistory: true });
770
- }
771
-
772
- o = selectedFormats[i].parentNode;
773
- if (!cancel) {
774
- tempList = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
775
- }
776
-
777
- r = o;
778
- rangeArr = {
779
- r: r,
780
- f: [dom.query.getParentElement(selectedFormats[i], 'LI')]
781
- };
782
- } else {
783
- rangeArr.f.push(dom.query.getParentElement(selectedFormats[i], 'LI'));
784
- }
785
- }
786
-
787
- if (i === len - 1) {
788
- if (nested && dom.check.isListCell(o.parentNode)) {
789
- this._detachNested(rangeArr.f);
790
- } else {
791
- afterRange = this.removeBlock(rangeArr.f[0].parentElement, { selectedFormats: rangeArr.f, newBlockElement: tempList, shouldDelete: false, skipHistory: true });
792
- }
793
- }
794
- }
795
- } else {
796
- const topElParent = topEl ? topEl.parentNode : topEl;
797
- const bottomElParent = bottomEl ? bottomEl.parentNode : bottomEl;
798
- topEl = /** @type {HTMLElement} */ (topElParent && !dom.check.isWysiwygFrame(topElParent) && topElParent.nodeName === listTag ? topElParent : topEl);
799
- bottomEl = /** @type {HTMLElement} */ (bottomElParent && !dom.check.isWysiwygFrame(bottomElParent) && bottomElParent.nodeName === listTag ? bottomElParent : bottomEl);
800
-
801
- const mergeTop = topEl?.tagName === listTag;
802
- const mergeBottom = bottomEl?.tagName === listTag;
803
-
804
- let list = mergeTop ? topEl : dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
805
- let firstList = null;
806
- let topNumber = null;
807
- // let lastList = null;
808
- // let bottomNumber = null;
809
-
810
- const passComponent = (current) => {
811
- return !this.component.is(current) && !dom.check.isList(current);
812
- };
813
-
814
- for (let i = 0, len = selectedFormats.length, newCell, fTag, isCell, next, originParent, nextParent, parentTag, siblingTag, rangeTag; i < len; i++) {
815
- fTag = selectedFormats[i];
816
- if (fTag.childNodes.length === 0 && !this._isIgnoreNodeChange(fTag)) {
817
- dom.utils.removeItem(fTag);
818
- continue;
819
- }
820
- next = selectedFormats[i + 1];
821
- originParent = fTag.parentNode;
822
- nextParent = next ? next.parentNode : null;
823
- isCell = dom.check.isListCell(fTag);
824
- rangeTag = this.isBlock(originParent) ? originParent : null;
825
- parentTag = isCell && !dom.check.isWysiwygFrame(originParent) ? originParent.parentNode : originParent;
826
- siblingTag = isCell && !dom.check.isWysiwygFrame(originParent) ? (!next || dom.check.isListCell(parentTag) ? originParent : originParent.nextSibling) : fTag.nextSibling;
827
-
828
- newCell = dom.utils.createElement('LI');
829
- dom.utils.copyFormatAttributes(newCell, fTag);
830
- if (this.component.is(fTag)) {
831
- const isHR = /^HR$/i.test(fTag.nodeName);
832
- if (!isHR) newCell.innerHTML = '<br>';
833
- newCell.innerHTML += fTag.outerHTML;
834
- if (isHR) newCell.innerHTML += '<br>';
835
- } else {
836
- const fChildren = fTag.childNodes;
837
- while (fChildren[0]) {
838
- newCell.appendChild(fChildren[0]);
839
- }
840
- }
841
- list.appendChild(newCell);
842
-
843
- // if (!next) lastList = list;
844
- if (!next || parentTag !== nextParent || this.isBlock(siblingTag)) {
845
- if (!firstList) firstList = list;
846
- if ((!mergeTop || !next || parentTag !== nextParent) && !(next && dom.check.isList(nextParent) && nextParent === originParent)) {
847
- if (list.parentNode !== parentTag) parentTag.insertBefore(list, siblingTag);
848
- }
849
- }
850
-
851
- dom.utils.removeItem(fTag);
852
- if (mergeTop && topNumber === null) topNumber = list.children.length - 1;
853
- if (
854
- next &&
855
- (this.getBlock(nextParent, passComponent) !== this.getBlock(originParent, passComponent) ||
856
- (dom.check.isList(nextParent) && dom.check.isList(originParent) && dom.query.getNodeDepth(nextParent) !== dom.query.getNodeDepth(originParent)))
857
- ) {
858
- list = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
859
- }
860
-
861
- if (rangeTag?.children.length === 0) dom.utils.removeItem(rangeTag);
862
- }
863
-
864
- if (topNumber) {
865
- firstList = firstList.children[topNumber];
866
- }
867
-
868
- if (mergeBottom) {
869
- // bottomNumber = list.children.length - 1;
870
- list.innerHTML += bottomEl.innerHTML;
871
- // lastList = list.children[bottomNumber] || lastList;
872
- dom.utils.removeItem(bottomEl);
873
- }
874
- }
875
-
876
- this.editor.effectNode = null;
877
- return !isRemove || !isCollapsed ? originRange : afterRange || originRange;
878
- },
879
-
880
- /**
881
- * @this {FormatThis}
882
- * @description "selectedCells" array are detached from the list element.
883
- * - The return value is applied when the first and last lines of "selectedFormats" are "LI" respectively.
884
- * @param {Array<Node>} selectedCells Array of ["line", li] elements(LI, P...) to remove.
885
- * @param {boolean} shouldDelete If true, It does not just remove the list, it deletes the content.
886
- * @returns {{sc: Node, ec: Node}} Node information after deletion
887
- * - sc: Start container node
888
- * - ec: End container node
889
- */
890
- removeList(selectedCells, shouldDelete) {
891
- let rangeArr = {};
892
- let listFirst = false;
893
- let listLast = false;
894
- let first = null;
895
- let last = null;
896
- const passComponent = (current) => {
897
- return !this.component.is(current);
898
- };
899
-
900
- for (let i = 0, len = selectedCells.length, r, o, lastIndex, isList; i < len; i++) {
901
- lastIndex = i === len - 1;
902
- o = this.getBlock(selectedCells[i], passComponent);
903
- isList = dom.check.isList(o);
904
- if (!r && isList) {
905
- r = o;
906
- rangeArr = {
907
- r: r,
908
- f: [dom.query.getParentElement(selectedCells[i], 'LI')]
909
- };
910
- if (i === 0) listFirst = true;
911
- } else if (r && isList) {
912
- if (r !== o) {
913
- const edge = this.removeBlock(rangeArr.f[0].parentNode, { selectedFormats: rangeArr.f, newBlockElement: null, shouldDelete, skipHistory: true });
914
- o = selectedCells[i].parentNode;
915
- if (listFirst) {
916
- first = edge.sc;
917
- listFirst = false;
918
- }
919
- if (lastIndex) last = edge.ec;
920
-
921
- if (isList) {
922
- r = o;
923
- rangeArr = {
924
- r: r,
925
- f: [dom.query.getParentElement(selectedCells[i], 'LI')]
926
- };
927
- if (lastIndex) listLast = true;
928
- } else {
929
- r = null;
930
- }
931
- } else {
932
- rangeArr.f.push(dom.query.getParentElement(selectedCells[i], 'LI'));
933
- if (lastIndex) listLast = true;
934
- }
935
- }
936
-
937
- if (lastIndex && dom.check.isList(r)) {
938
- const edge = this.removeBlock(rangeArr.f[0].parentNode, { selectedFormats: rangeArr.f, newBlockElement: null, shouldDelete, skipHistory: true });
939
- if (listLast || len === 1) last = edge.ec;
940
- if (listFirst) first = edge.sc || last;
941
- }
942
- }
943
-
944
- return {
945
- sc: first,
946
- ec: last
947
- };
948
- },
949
-
950
678
  /**
951
679
  * @this {FormatThis}
952
680
  * @description Indent more the selected lines.
@@ -964,7 +692,7 @@ Format.prototype = {
964
692
 
965
693
  // list cells
966
694
  if (cells.length > 0) {
967
- this._applyNestedList(cells, true);
695
+ this.listFormat.applyNested(cells, false);
968
696
  }
969
697
 
970
698
  this.editor.effectNode = null;
@@ -989,7 +717,7 @@ Format.prototype = {
989
717
 
990
718
  // list cells
991
719
  if (cells.length > 0) {
992
- this._applyNestedList(cells, false);
720
+ this.listFormat.applyNested(cells, true);
993
721
  }
994
722
 
995
723
  this.editor.effectNode = null;
@@ -997,378 +725,6 @@ Format.prototype = {
997
725
  this.history.push(false);
998
726
  },
999
727
 
1000
- /**
1001
- * @this {FormatThis}
1002
- * @description Adds, updates, or deletes style nodes from selected text (a, span, strong, etc.).
1003
- * @param {?Node} styleNode The element to be added to the selection. If null, only existing nodes are modified or removed.
1004
- * @param {Object} [options] Options
1005
- * @param {Array<string>} [options.stylesToModify=null] Array of style or class names to check and modify.
1006
- * (e.g., ['font-size'], ['.className'], ['font-family', 'color', '.className'])
1007
- * @param {Array<string>} [options.nodesToRemove=null] Array of node names to remove.
1008
- * If empty array or null when styleNode is null, all formats are removed.
1009
- * (e.g., ['span'], ['strong', 'em'])
1010
- * @param {boolean} [options.strictRemove=false] If true, only removes nodes from nodesToRemove if all styles and classes are removed.
1011
- * @returns {HTMLElement} The element that was added to or modified in the selection.
1012
- *
1013
- * @details
1014
- * 1. If styleNode is provided, a node with the same tags and attributes is added to the selected text.
1015
- * 2. If the same tag already exists, only its attributes are updated.
1016
- * 3. If styleNode is null, existing nodes are updated or removed without adding new ones.
1017
- * 4. Styles matching those in stylesToModify are removed. (Use CSS attribute names, e.g., "background-color")
1018
- * 5. Classes matching those in stylesToModify (prefixed with ".") are removed.
1019
- * 6. stylesToModify is used to avoid duplicate property values from styleNode.
1020
- * 7. Nodes with all styles and classes removed are deleted if they match styleNode, are in nodesToRemove, or if styleNode is null.
1021
- * 8. Tags matching names in nodesToRemove are deleted regardless of their style and class.
1022
- * 9. If strictRemove is true, nodes in nodesToRemove are only removed if all their styles and classes are removed.
1023
- * 10. The function won't modify nodes if the parent has the same class and style values.
1024
- * - However, if nodesToRemove has values, it will work and separate text nodes even if there's no node to replace.
1025
- */
1026
- applyInlineElement(styleNode, { stylesToModify, nodesToRemove, strictRemove } = {}) {
1027
- if (dom.query.getParentElement(this.selection.getNode(), dom.check.isNonEditable)) return;
1028
-
1029
- this.selection._resetRangeToTextNode();
1030
- let range = this.selection.getRangeAndAddLine(this.selection.getRange(), null);
1031
- stylesToModify = stylesToModify?.length > 0 ? stylesToModify : null;
1032
- nodesToRemove = nodesToRemove?.length > 0 ? nodesToRemove : null;
1033
-
1034
- const isRemoveNode = !styleNode;
1035
- const isRemoveFormat = isRemoveNode && !nodesToRemove && !stylesToModify;
1036
- let startCon = range.startContainer;
1037
- let startOff = range.startOffset;
1038
- let endCon = range.endContainer;
1039
- let endOff = range.endOffset;
1040
-
1041
- if ((isRemoveFormat && range.collapsed && this.isLine(startCon.parentNode) && this.isLine(endCon.parentNode)) || (startCon === endCon && startCon.nodeType === 1 && dom.check.isNonEditable(startCon))) {
1042
- const format = startCon.parentNode;
1043
- if (
1044
- !dom.check.isListCell(format) ||
1045
- !converter.getValues(format.style).some((k) => {
1046
- return this._listKebab.includes(k);
1047
- })
1048
- )
1049
- return;
1050
- return;
1051
- }
1052
-
1053
- if (range.collapsed && !isRemoveFormat) {
1054
- if (startCon.nodeType === 1 && !dom.check.isBreak(startCon) && !this.component.is(startCon)) {
1055
- let afterNode = null;
1056
- const focusNode = startCon.childNodes[startOff];
1057
-
1058
- if (focusNode) {
1059
- if (!focusNode.nextSibling) {
1060
- afterNode = null;
1061
- } else {
1062
- afterNode = dom.check.isBreak(focusNode) ? focusNode : focusNode.nextSibling;
1063
- }
1064
- }
1065
-
1066
- const zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
1067
- startCon.insertBefore(zeroWidth, afterNode);
1068
- this.selection.setRange(zeroWidth, 1, zeroWidth, 1);
1069
-
1070
- range = this.selection.getRange();
1071
- startCon = range.startContainer;
1072
- startOff = range.startOffset;
1073
- endCon = range.endContainer;
1074
- endOff = range.endOffset;
1075
- }
1076
- }
1077
-
1078
- if (this.isLine(startCon)) {
1079
- startCon = startCon.childNodes[startOff] || startCon.firstChild;
1080
- startOff = 0;
1081
- }
1082
- if (this.isLine(endCon)) {
1083
- endCon = endCon.childNodes[endOff] || endCon.lastChild;
1084
- endOff = endCon.textContent.length;
1085
- }
1086
-
1087
- if (isRemoveNode) {
1088
- styleNode = dom.utils.createElement('DIV');
1089
- }
1090
-
1091
- const wRegExp = RegExp;
1092
- const newNodeName = styleNode.nodeName;
1093
-
1094
- /* checked same style property */
1095
- if (!isRemoveFormat && startCon === endCon && !nodesToRemove && styleNode) {
1096
- let sNode = startCon;
1097
- let checkCnt = 0;
1098
- const checkAttrs = [];
1099
-
1100
- const checkStyles = /** @type {HTMLElement} */ (styleNode).style;
1101
- for (let i = 0, len = checkStyles.length; i < len; i++) {
1102
- checkAttrs.push(checkStyles[i]);
1103
- }
1104
-
1105
- const checkClassName = /** @type {HTMLElement} */ (styleNode).className;
1106
- const ckeckClasses = /** @type {HTMLElement} */ (styleNode).classList;
1107
- for (let i = 0, len = ckeckClasses.length; i < len; i++) {
1108
- checkAttrs.push('.' + ckeckClasses[i]);
1109
- }
1110
-
1111
- if (checkAttrs.length > 0) {
1112
- while (!this.isLine(sNode) && !dom.check.isWysiwygFrame(sNode)) {
1113
- for (let i = 0; i < checkAttrs.length; i++) {
1114
- if (sNode.nodeType === 1) {
1115
- const s = checkAttrs[i];
1116
- const classReg = /^\./.test(s) ? new wRegExp('\\s*' + s.replace(/^\./, '') + '(\\s+|$)', 'ig') : false;
1117
- const sNodeStyle = /** @type {HTMLElement} */ (sNode).style;
1118
- const sNodeClassName = /** @type {HTMLElement} */ (sNode).className;
1119
-
1120
- const styleCheck = isRemoveNode ? !!sNodeStyle[s] : !!sNodeStyle[s] && !!checkStyles[s] && sNodeStyle[s] === checkStyles[s];
1121
- const classCheck = classReg === false ? false : isRemoveNode ? !!sNodeClassName.match(classReg) : !!sNodeClassName.match(classReg) && !!checkClassName.match(classReg);
1122
- if (styleCheck || classCheck) {
1123
- checkCnt++;
1124
- }
1125
- }
1126
- }
1127
- sNode = sNode.parentNode;
1128
- }
1129
-
1130
- if (checkCnt >= checkAttrs.length) return;
1131
- }
1132
- }
1133
-
1134
- let newNode;
1135
- /** @type {NodeStyleContainerType} */
1136
- let start = {};
1137
- /** @type {NodeStyleContainerType} */
1138
- let end = {};
1139
-
1140
- /** @type {string|RegExp} */
1141
- let styleRegExp;
1142
- /** @type {string|RegExp} */
1143
- let classRegExp;
1144
- /** @type {string|RegExp} */
1145
- let removeNodeRegExp;
1146
-
1147
- if (stylesToModify) {
1148
- for (let i = 0, len = stylesToModify.length, s; i < len; i++) {
1149
- s = stylesToModify[i];
1150
- if (/^\./.test(s)) {
1151
- classRegExp += (classRegExp ? '|' : '\\s*(?:') + s.replace(/^\./, '');
1152
- } else {
1153
- styleRegExp += (styleRegExp ? '|' : '(?:;|^|\\s)(?:') + s;
1154
- }
1155
- }
1156
-
1157
- if (styleRegExp) {
1158
- styleRegExp += ')\\s*:[^;]*\\s*(?:;|$)';
1159
- styleRegExp = new wRegExp(styleRegExp, 'ig');
1160
- }
1161
-
1162
- if (classRegExp) {
1163
- classRegExp += ')(?=\\s+|$)';
1164
- classRegExp = new wRegExp(classRegExp, 'ig');
1165
- }
1166
- }
1167
-
1168
- if (nodesToRemove) {
1169
- removeNodeRegExp = '^(?:' + nodesToRemove[0];
1170
- for (let i = 1; i < nodesToRemove.length; i++) {
1171
- removeNodeRegExp += '|' + nodesToRemove[i];
1172
- }
1173
- removeNodeRegExp += ')$';
1174
- removeNodeRegExp = new wRegExp(removeNodeRegExp, 'i');
1175
- }
1176
-
1177
- /** validation check function*/
1178
- const _removeCheck = {
1179
- v: false
1180
- };
1181
- const validation = function (checkNode) {
1182
- const vNode = checkNode.cloneNode(false);
1183
-
1184
- // all path
1185
- if (vNode.nodeType === 3 || dom.check.isBreak(vNode)) return vNode;
1186
- // all remove
1187
- if (isRemoveFormat) return null;
1188
-
1189
- // remove node check
1190
- const tagRemove = (!removeNodeRegExp && isRemoveNode) || /** @type {RegExp} */ (removeNodeRegExp)?.test(vNode.nodeName);
1191
-
1192
- // tag remove
1193
- if (tagRemove && !strictRemove) {
1194
- _removeCheck.v = true;
1195
- return null;
1196
- }
1197
-
1198
- // style regexp
1199
- const originStyle = vNode.style.cssText;
1200
- let style = '';
1201
- if (styleRegExp && originStyle.length > 0) {
1202
- style = originStyle.replace(styleRegExp, '').trim();
1203
- if (style !== originStyle) _removeCheck.v = true;
1204
- }
1205
-
1206
- // class check
1207
- const originClasses = vNode.className;
1208
- let classes = '';
1209
- if (classRegExp && originClasses.length > 0) {
1210
- classes = originClasses.replace(classRegExp, '').trim();
1211
- if (classes !== originClasses) _removeCheck.v = true;
1212
- }
1213
-
1214
- // remove only
1215
- if (isRemoveNode) {
1216
- if ((classRegExp || !originClasses) && (styleRegExp || !originStyle) && !style && !classes && tagRemove) {
1217
- _removeCheck.v = true;
1218
- return null;
1219
- }
1220
- }
1221
-
1222
- // change
1223
- if (style || classes || vNode.nodeName !== newNodeName || Boolean(styleRegExp) !== Boolean(originStyle) || Boolean(classRegExp) !== Boolean(originClasses)) {
1224
- if (styleRegExp && originStyle.length > 0) vNode.style.cssText = style;
1225
- if (!vNode.style.cssText) {
1226
- vNode.removeAttribute('style');
1227
- }
1228
-
1229
- if (classRegExp && originClasses.length > 0) vNode.className = classes.trim();
1230
- if (!vNode.className.trim()) {
1231
- vNode.removeAttribute('class');
1232
- }
1233
-
1234
- if (!vNode.style.cssText && !vNode.className && (vNode.nodeName === newNodeName || tagRemove)) {
1235
- _removeCheck.v = true;
1236
- return null;
1237
- }
1238
-
1239
- return vNode;
1240
- }
1241
-
1242
- _removeCheck.v = true;
1243
- return null;
1244
- };
1245
-
1246
- // get line nodes
1247
- const lineNodes = this.getLines(null);
1248
- range = this.selection.getRange();
1249
- startCon = range.startContainer;
1250
- startOff = range.startOffset;
1251
- endCon = range.endContainer;
1252
- endOff = range.endOffset;
1253
-
1254
- if (!this.getLine(startCon, null)) {
1255
- startCon = dom.query.getEdgeChild(
1256
- lineNodes[0],
1257
- function (current) {
1258
- return current.nodeType === 3;
1259
- },
1260
- false
1261
- );
1262
- startOff = 0;
1263
- }
1264
-
1265
- if (!this.getLine(endCon, null)) {
1266
- endCon = dom.query.getEdgeChild(
1267
- lineNodes[lineNodes.length - 1],
1268
- function (current) {
1269
- return current.nodeType === 3;
1270
- },
1271
- false
1272
- );
1273
- endOff = endCon.textContent.length;
1274
- }
1275
-
1276
- const oneLine = this.getLine(startCon, null) === this.getLine(endCon, null);
1277
- const endLength = lineNodes.length - (oneLine ? 0 : 1);
1278
-
1279
- // node Changes
1280
- newNode = styleNode.cloneNode(false);
1281
-
1282
- const isRemoveAnchor =
1283
- isRemoveFormat ||
1284
- (isRemoveNode &&
1285
- (function (inst, arr) {
1286
- for (let n = 0, len = arr.length; n < len; n++) {
1287
- if (inst._isNonSplitNode(arr[n])) return true;
1288
- }
1289
- return false;
1290
- })(this, nodesToRemove));
1291
-
1292
- const isSizeNode = isRemoveNode || this._sn_isSizeNode(newNode);
1293
- const _getMaintainedNode = this._sn_getMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
1294
- const _isMaintainedNode = this._sn_isMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
1295
-
1296
- // one line
1297
- if (oneLine) {
1298
- if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) range = this.selection.setRange(startCon, startOff, endCon, endOff);
1299
-
1300
- const newRange = this._setNode_oneLine(lineNodes[0], newNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, range.collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode);
1301
- start.container = newRange.startContainer;
1302
- start.offset = newRange.startOffset;
1303
- end.container = newRange.endContainer;
1304
- end.offset = newRange.endOffset;
1305
-
1306
- if (start.container === end.container && dom.check.isZeroWidth(start.container)) {
1307
- start.offset = end.offset = 1;
1308
- }
1309
- this._sn_setCommonListStyle(newRange.ancestor, null);
1310
- } else {
1311
- // multi line
1312
- let appliedCommonList = false;
1313
- if (endLength > 0 && this._sn_resetCommonListCell(lineNodes[endLength], stylesToModify)) appliedCommonList = true;
1314
- if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) appliedCommonList = true;
1315
- if (appliedCommonList) this.selection.setRange(startCon, startOff, endCon, endOff);
1316
-
1317
- // end
1318
- if (endLength > 0) {
1319
- newNode = styleNode.cloneNode(false);
1320
- end = this._setNode_endLine(lineNodes[endLength], newNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode);
1321
- }
1322
-
1323
- // mid
1324
- for (let i = endLength - 1, newRange; i > 0; i--) {
1325
- this._sn_resetCommonListCell(lineNodes[i], stylesToModify);
1326
- newNode = styleNode.cloneNode(false);
1327
- newRange = this._setNode_middleLine(lineNodes[i], newNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, end.container);
1328
- if (newRange.endContainer && newRange.ancestor.contains(newRange.endContainer)) {
1329
- end.ancestor = null;
1330
- end.container = newRange.endContainer;
1331
- }
1332
- this._sn_setCommonListStyle(newRange.ancestor, null);
1333
- }
1334
-
1335
- // start
1336
- newNode = styleNode.cloneNode(false);
1337
- start = this._setNode_startLine(lineNodes[0], newNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, end.container);
1338
-
1339
- if (start.endContainer) {
1340
- end.ancestor = null;
1341
- end.container = start.endContainer;
1342
- }
1343
-
1344
- if (endLength <= 0) {
1345
- end = start;
1346
- } else if (!end.container) {
1347
- end.ancestor = null;
1348
- end.container = start.container;
1349
- end.offset = start.container.textContent.length;
1350
- }
1351
-
1352
- this._sn_setCommonListStyle(start.ancestor, null);
1353
- this._sn_setCommonListStyle(end.ancestor || this.getLine(end.container), null);
1354
- }
1355
-
1356
- // set range
1357
- this.ui._offCurrentController();
1358
- this.selection.setRange(start.container, start.offset, end.container, end.offset);
1359
- this.history.push(false);
1360
-
1361
- return /** @type {HTMLElement} */ (newNode);
1362
- },
1363
-
1364
- /**
1365
- * @this {FormatThis}
1366
- * @description Remove format of the currently selected text.
1367
- */
1368
- removeInlineElement() {
1369
- this.applyInlineElement(null, { stylesToModify: null, nodesToRemove: null, strictRemove: null });
1370
- },
1371
-
1372
728
  /**
1373
729
  * @this {FormatThis}
1374
730
  * @description Check if the container and offset values are the edges of the "line"
@@ -1401,7 +757,7 @@ Format.prototype = {
1401
757
  * @returns {element is HTMLElement}
1402
758
  */
1403
759
  isTextStyleNode(element) {
1404
- return typeof element === 'string' ? this._textStyleTagsCheck.test(element) : element && element.nodeType === 1 && this._textStyleTagsCheck.test(element.nodeName);
760
+ return typeof element === 'string' ? this._textStyleTagsCheck.test(element) : element?.nodeType === 1 && this._textStyleTagsCheck.test(element.nodeName);
1405
761
  },
1406
762
 
1407
763
  /**
@@ -1413,20 +769,19 @@ Format.prototype = {
1413
769
  * @returns {element is HTMLElement}
1414
770
  */
1415
771
  isLine(element) {
1416
- if (this.isBlock(element)) return false;
1417
772
  return typeof element === 'string'
1418
773
  ? this._formatLineCheck.test(element)
1419
- : element && element.nodeType === 1 && (this._formatLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__line_.+|__se__format__br_line_.+')) && !this._nonFormat(element);
774
+ : element?.nodeType === 1 && (this._formatLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__line_.+|__se__format__br_line_.+')) && !this._nonFormat(element);
1420
775
  },
1421
776
 
1422
777
  /**
1423
778
  * @this {FormatThis}
1424
- * @description It is judged whether it is the only "line" element, not "brLine".
779
+ * @description It is judged whether it is the only "line" element.
1425
780
  * @param {Node|string} element The node to check
1426
781
  * @returns {element is HTMLElement}
1427
782
  */
1428
- isLineOnly(element) {
1429
- return this.isLine(element) && (this._brLineBreak || !this.isBrLine(element));
783
+ isNormalLine(element) {
784
+ return this.isLine(element) && (this._brLineBreak || !this.isBrLine(element)) && !this.isBlock(element);
1430
785
  },
1431
786
 
1432
787
  /**
@@ -1444,7 +799,7 @@ Format.prototype = {
1444
799
  (this._brLineBreak && this.isLine(element)) ||
1445
800
  (typeof element === 'string'
1446
801
  ? this._formatBrLineCheck.test(element)
1447
- : element && element.nodeType === 1 && (this._formatBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line_.+')) && !this._nonFormat(element))
802
+ : element?.nodeType === 1 && (this._formatBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line_.+')) && !this._nonFormat(element))
1448
803
  );
1449
804
  },
1450
805
 
@@ -1459,7 +814,7 @@ Format.prototype = {
1459
814
  isBlock(element) {
1460
815
  return typeof element === 'string'
1461
816
  ? this._formatBlockCheck.test(element)
1462
- : element && element.nodeType === 1 && (this._formatBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_.+')) && !this._nonFormat(element);
817
+ : element?.nodeType === 1 && (this._formatBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_.+')) && !this._nonFormat(element);
1463
818
  },
1464
819
 
1465
820
  /**
@@ -1476,7 +831,7 @@ Format.prototype = {
1476
831
  isClosureBlock(element) {
1477
832
  return typeof element === 'string'
1478
833
  ? this._formatClosureBlockCheck.test(element)
1479
- : element && element.nodeType === 1 && (this._formatClosureBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_closure_.+')) && !this._nonFormat(element);
834
+ : element?.nodeType === 1 && (this._formatClosureBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_closure_.+')) && !this._nonFormat(element);
1480
835
  },
1481
836
 
1482
837
  /**
@@ -1493,7 +848,7 @@ Format.prototype = {
1493
848
  isClosureBrLine(element) {
1494
849
  return typeof element === 'string'
1495
850
  ? this._formatClosureBrLineCheck.test(element)
1496
- : element && element.nodeType === 1 && (this._formatClosureBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line__closure_.+')) && !this._nonFormat(element);
851
+ : element?.nodeType === 1 && (this._formatClosureBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line__closure_.+')) && !this._nonFormat(element);
1497
852
  },
1498
853
 
1499
854
  /**
@@ -1507,10 +862,10 @@ Format.prototype = {
1507
862
  let range = this.selection.getRange();
1508
863
 
1509
864
  if (dom.check.isWysiwygFrame(range.startContainer)) {
1510
- const children = this.editor.frameContext.get('wysiwyg').children;
865
+ const children = this.frameContext.get('wysiwyg').children;
1511
866
  if (children.length === 0) return [];
1512
867
 
1513
- this.selection.setRange(children[0], 0, children[children.length - 1], children[children.length - 1].textContent.trim().length);
868
+ this.selection.setRange(children[0], 0, children.at(-1), children.at(-1).textContent.trim().length);
1514
869
  range = this.selection.getRange();
1515
870
  }
1516
871
 
@@ -1519,11 +874,10 @@ Format.prototype = {
1519
874
  const commonCon = range.commonAncestorContainer;
1520
875
 
1521
876
  // get line nodes
1522
- const lineNodes = dom.query.getListChildren(commonCon, (current) => {
1523
- return validation ? validation(current) : this.isLine(current);
1524
- });
877
+ validation ||= this.isLine.bind(this);
878
+ const lineNodes = dom.query.getListChildren(commonCon, (current) => validation(current), null);
1525
879
 
1526
- if (!dom.check.isWysiwygFrame(commonCon) && !this.isBlock(commonCon)) lineNodes.unshift(this.getLine(commonCon, null));
880
+ if (commonCon.nodeType === 3 || (!dom.check.isWysiwygFrame(commonCon) && !this.isBlock(commonCon))) lineNodes.unshift(this.getLine(commonCon, null));
1527
881
  if (startCon === endCon || lineNodes.length === 1) return lineNodes;
1528
882
 
1529
883
  const startLine = this.getLine(startCon, null);
@@ -1575,8 +929,8 @@ Format.prototype = {
1575
929
  ? this.getLines(null)
1576
930
  : this.getLines((current) => {
1577
931
  const component = dom.query.getParentElement(current, this.component.is.bind(this.component));
1578
- return (this.isLine(current) && (!component || component === myComponent)) || (this.component.is(current) && !this.getLine(current));
1579
- });
932
+ return (this.isLine(current) && (!component || component === myComponent)) || (dom.check.isComponentContainer(current) && !this.getLine(current));
933
+ });
1580
934
 
1581
935
  if (removeDuplicate) {
1582
936
  for (let i = 0, len = selectedLines.length; i < len; i++) {
@@ -1616,20 +970,6 @@ Format.prototype = {
1616
970
  return dom.check.isExcludeFormat(element) || this.component.is(element) || dom.check.isWysiwygFrame(element);
1617
971
  },
1618
972
 
1619
- /**
1620
- * @private
1621
- * @this {FormatThis}
1622
- * @description Nodes that must remain undetached when changing text nodes (A, Label, Code, Span:font-size)
1623
- * @param {Node|string} element Element to check
1624
- * @returns {boolean}
1625
- */
1626
- _isNonSplitNode(element) {
1627
- if (!element) return false;
1628
- const checkRegExp = /^(a|label|code|summary)$/i;
1629
- if (typeof element === 'string') return checkRegExp.test(element);
1630
- return element.nodeType === 1 && checkRegExp.test(element.nodeName);
1631
- },
1632
-
1633
973
  /**
1634
974
  * @private
1635
975
  * @this {FormatThis}
@@ -1644,17 +984,6 @@ Format.prototype = {
1644
984
  return element.nodeType === 1 && (this.component.is(element) || checkRegExp.test(element.nodeName));
1645
985
  },
1646
986
 
1647
- /**
1648
- * @private
1649
- * @this {FormatThis}
1650
- * @description Nodes that need to be added without modification when changing text nodes
1651
- * @param {Node} element Element to check
1652
- * @returns {boolean}
1653
- */
1654
- _isIgnoreNodeChange(element) {
1655
- return element && element.nodeType === 1 && (dom.check.isNonEditable(element) || !this.isTextStyleNode(element) || this.component.is(element));
1656
- },
1657
-
1658
987
  /**
1659
988
  * @private
1660
989
  * @this {FormatThis}
@@ -1675,12 +1004,12 @@ Format.prototype = {
1675
1004
  const endOffset = range.endOffset;
1676
1005
 
1677
1006
  let first = /** @type {Node} */ (selectedFormsts[0]);
1678
- let last = /** @type {Node} */ (selectedFormsts[selectedFormsts.length - 1]);
1007
+ let last = /** @type {Node} */ (selectedFormsts.at(-1));
1679
1008
  const firstPath = dom.query.getNodePath(range.startContainer, first, null);
1680
1009
  const lastPath = dom.query.getNodePath(range.endContainer, last, null);
1681
1010
 
1682
1011
  // remove selected list
1683
- const rlist = this.removeList(selectedFormsts, false);
1012
+ const rlist = this.listFormat.remove(selectedFormsts, false);
1684
1013
  if (rlist.sc) first = rlist.sc;
1685
1014
  if (rlist.ec) last = rlist.ec;
1686
1015
 
@@ -1701,1700 +1030,25 @@ Format.prototype = {
1701
1030
  /**
1702
1031
  * @private
1703
1032
  * @this {FormatThis}
1704
- * @description Attaches a nested list structure by merging adjacent lists if applicable.
1705
- * - Ensures that the nested list is placed correctly in the document structure.
1706
- * @param {Element} originList The original list element where the nested list is inserted.
1707
- * @param {Element} innerList The nested list element.
1708
- * @param {Element} prev The previous sibling element.
1709
- * @param {Element} next The next sibling element.
1710
- * @param {{s: Array<number> | null, e: Array<number> | null, sl: Node | null, el: Node | null}} nodePath Object storing the start and end node paths.
1711
- * - s : Start node path.
1712
- * - e : End node path.
1713
- * - sl : Start node's parent element.
1714
- * - el : End node's parent element.
1715
- * @returns {Node} The attached inner list.
1033
+ * @description Reset the line break format.
1034
+ * @param {"line"|"br"} breakFormat options.get('defaultLineBreakFormat')
1716
1035
  */
1717
- _attachNested(originList, innerList, prev, next, nodePath) {
1718
- let insertPrev = false;
1719
-
1720
- if (innerList.tagName === prev?.tagName) {
1721
- const children = innerList.children;
1722
- while (children[0]) {
1723
- prev.appendChild(children[0]);
1724
- }
1036
+ __resetBrLineBreak(breakFormat) {
1037
+ this._brLineBreak = breakFormat === 'br';
1038
+ },
1725
1039
 
1726
- innerList = prev;
1727
- insertPrev = true;
1728
- }
1040
+ constructor: Format
1041
+ };
1729
1042
 
1730
- if (innerList.tagName === next?.tagName) {
1731
- const children = next.children;
1732
- while (children[0]) {
1733
- innerList.appendChild(children[0]);
1734
- }
1735
-
1736
- const temp = next.nextElementSibling;
1737
- next.parentNode.removeChild(next);
1738
- next = temp;
1739
- }
1740
-
1741
- if (!insertPrev) {
1742
- if (dom.check.isListCell(prev)) {
1743
- originList = prev;
1744
- next = null;
1745
- }
1746
-
1747
- originList.insertBefore(innerList, next);
1748
-
1749
- if (!nodePath.s) {
1750
- nodePath.s = dom.query.getNodePath(innerList.firstElementChild.firstChild, originList, null);
1751
- nodePath.sl = originList;
1752
- }
1753
-
1754
- const slPath = originList.contains(nodePath.sl) ? dom.query.getNodePath(nodePath.sl, originList) : null;
1755
- nodePath.e = dom.query.getNodePath(innerList.lastElementChild.firstChild, originList, null);
1756
- nodePath.el = originList;
1757
-
1758
- this.nodeTransform.mergeSameTags(originList, [nodePath.s, nodePath.e, slPath], false);
1759
- this.nodeTransform.mergeNestedTags(originList);
1760
- if (slPath) nodePath.sl = dom.query.getNodeFromPath(slPath, originList);
1761
- }
1762
-
1763
- return innerList;
1764
- },
1765
-
1766
- /**
1767
- * @private
1768
- * @this {FormatThis}
1769
- * @description Detaches a nested list structure by extracting list items from their parent list.
1770
- * - Ensures proper restructuring of the list elements.
1771
- * @param {Array<HTMLElement>} cells The list items to be detached.
1772
- * @returns {{cc: Node, sc: Node, ec: Node}} An object containing reference nodes for repositioning.
1773
- * - cc : The parent node of the first list item.
1774
- * - sc : The first list item.
1775
- * - ec : The last list item.
1776
- */
1777
- _detachNested(cells) {
1778
- const first = cells[0];
1779
- const last = cells[cells.length - 1];
1780
- const next = last.nextElementSibling;
1781
- const originList = first.parentElement;
1782
- const sibling = originList.parentElement.nextElementSibling;
1783
- const parentNode = originList.parentElement.parentElement;
1784
-
1785
- for (let c = 0, cLen = cells.length; c < cLen; c++) {
1786
- parentNode.insertBefore(cells[c], sibling);
1787
- }
1788
-
1789
- if (next && originList.children.length > 0) {
1790
- const newList = originList.cloneNode(false);
1791
- const children = originList.childNodes;
1792
- const index = dom.query.getPositionIndex(next);
1793
- while (children[index]) {
1794
- newList.appendChild(children[index]);
1795
- }
1796
- last.appendChild(newList);
1797
- }
1798
-
1799
- if (originList.children.length === 0) dom.utils.removeItem(originList);
1800
- this.nodeTransform.mergeSameTags(parentNode);
1801
-
1802
- const edge = dom.query.getEdgeChildNodes(first, last);
1803
-
1804
- return {
1805
- cc: first.parentNode,
1806
- sc: edge.sc,
1807
- ec: edge.ec
1808
- };
1809
- },
1810
-
1811
- /**
1812
- * @private
1813
- * @this {FormatThis}
1814
- * @description Nest list cells or cancel nested cells.
1815
- * @param selectedCells List cells.
1816
- * @param nested Nested or cancel nested.
1817
- */
1818
- _applyNestedList(selectedCells, nested) {
1819
- selectedCells = !selectedCells
1820
- ? this.getLines().filter(function (el) {
1821
- return dom.check.isListCell(el);
1822
- })
1823
- : selectedCells;
1824
- const cellsLen = selectedCells.length;
1825
- if (cellsLen === 0 || (!nested && !dom.check.isListCell(selectedCells[0].previousElementSibling) && !dom.check.isListCell(selectedCells[cellsLen - 1].nextElementSibling))) {
1826
- return {
1827
- sc: selectedCells[0],
1828
- so: 0,
1829
- ec: selectedCells[cellsLen - 1],
1830
- eo: 1
1831
- };
1832
- }
1833
-
1834
- let originList = selectedCells[0].parentNode;
1835
- let lastCell = selectedCells[cellsLen - 1];
1836
- let range = null;
1837
-
1838
- if (nested) {
1839
- if (originList !== lastCell.parentNode && dom.check.isList(lastCell.parentNode.parentNode) && lastCell.nextElementSibling) {
1840
- lastCell = lastCell.nextElementSibling;
1841
- while (lastCell) {
1842
- selectedCells.push(lastCell);
1843
- lastCell = lastCell.nextElementSibling;
1844
- }
1845
- }
1846
- range = this.applyList(originList.nodeName + ':' + originList.style.listStyleType, selectedCells, true);
1847
- } else {
1848
- let innerList = dom.utils.createElement(originList.nodeName);
1849
- let prev = selectedCells[0].previousElementSibling;
1850
- let next = lastCell.nextElementSibling;
1851
- const nodePath = {
1852
- s: null,
1853
- e: null,
1854
- sl: originList,
1855
- el: originList
1856
- };
1857
-
1858
- for (let i = 0, len = cellsLen, c; i < len; i++) {
1859
- c = selectedCells[i];
1860
- if (c.parentNode !== originList) {
1861
- this._attachNested(originList, innerList, prev, next, nodePath);
1862
- originList = c.parentNode;
1863
- innerList = dom.utils.createElement(originList.nodeName);
1864
- }
1865
-
1866
- prev = c.previousElementSibling;
1867
- next = c.nextElementSibling;
1868
- innerList.appendChild(c);
1869
- }
1870
-
1871
- this._attachNested(originList, innerList, prev, next, nodePath);
1872
-
1873
- const sc = dom.query.getNodeFromPath(nodePath.s, nodePath.sl);
1874
- const ec = dom.query.getNodeFromPath(nodePath.e, nodePath.el);
1875
- range = {
1876
- sc: sc,
1877
- so: 0,
1878
- ec: ec,
1879
- eo: ec.textContent.length
1880
- };
1881
- }
1882
-
1883
- return range;
1884
- },
1885
-
1886
- /**
1887
- * @private
1888
- * @this {FormatThis}
1889
- * @description Detach Nested all nested lists under the "baseNode".
1890
- * - Returns a list with nested removed.
1891
- * @param {HTMLElement} baseNode Element on which to base.
1892
- * @param {boolean} all If true, it also detach all nested lists of a returned list.
1893
- * @returns {Node} Result element
1894
- */
1895
- _removeNestedList(baseNode, all) {
1896
- const rNode = DeleteNestedList(baseNode);
1897
- let rangeElement, cNodes;
1898
-
1899
- if (rNode) {
1900
- rangeElement = rNode.cloneNode(false);
1901
- cNodes = rNode.childNodes;
1902
- const index = dom.query.getPositionIndex(baseNode);
1903
- while (cNodes[index]) {
1904
- rangeElement.appendChild(cNodes[index]);
1905
- }
1906
- } else {
1907
- rangeElement = baseNode;
1908
- }
1909
-
1910
- let rChildren;
1911
- if (!all) {
1912
- const depth = dom.query.getNodeDepth(baseNode) + 2;
1913
- rChildren = dom.query.getListChildren(baseNode, (current) => {
1914
- return dom.check.isListCell(current) && !current.previousElementSibling && dom.query.getNodeDepth(current) === depth;
1915
- });
1916
- } else {
1917
- rChildren = dom.query.getListChildren(rangeElement, (current) => {
1918
- return dom.check.isListCell(current) && !current.previousElementSibling;
1919
- });
1920
- }
1921
-
1922
- for (let i = 0, len = rChildren.length; i < len; i++) {
1923
- DeleteNestedList(rChildren[i]);
1924
- }
1925
-
1926
- if (rNode) {
1927
- rNode.parentNode.insertBefore(rangeElement, rNode.nextSibling);
1928
- if (cNodes?.length === 0) dom.utils.removeItem(rNode);
1929
- }
1930
-
1931
- return rangeElement === baseNode ? rangeElement.parentNode : rangeElement;
1932
- },
1933
-
1934
- /**
1935
- * @private
1936
- * @this {FormatThis}
1937
- * @description wraps text nodes of line selected text.
1938
- * @param {Node} element The node of the line that contains the selected text node.
1939
- * @param {Node} newInnerNode The dom that will wrap the selected text area
1940
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
1941
- * @param {Node} startCon The startContainer property of the selection object.
1942
- * @param {number} startOff The startOffset property of the selection object.
1943
- * @param {Node} endCon The endContainer property of the selection object.
1944
- * @param {number} endOff The endOffset property of the selection object.
1945
- * @param {boolean} isRemoveFormat Is the remove all formats command?
1946
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
1947
- * @param {boolean} collapsed range.collapsed
1948
- * @returns {{ancestor: *, startContainer: *, startOffset: *, endContainer: *, endOffset: *}}
1949
- */
1950
- _setNode_oneLine(element, newInnerNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
1951
- // not add tag
1952
- let parentCon = startCon.parentNode;
1953
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
1954
- if (parentCon.nodeName === newInnerNode.nodeName) break;
1955
- parentCon = parentCon.parentNode;
1956
- }
1957
-
1958
- if (!isRemoveNode && parentCon === endCon.parentNode && parentCon.nodeName === newInnerNode.nodeName) {
1959
- if (dom.check.isZeroWidth(startCon.textContent.slice(0, startOff)) && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
1960
- const children = parentCon.childNodes;
1961
- let sameTag = false;
1962
-
1963
- for (let i = 0, len = children.length, c, s, e, z; i < len; i++) {
1964
- c = children[i];
1965
- z = !dom.check.isZeroWidth(c);
1966
- if (c === startCon) {
1967
- s = true;
1968
- continue;
1969
- }
1970
- if (c === endCon) {
1971
- e = true;
1972
- continue;
1973
- }
1974
- if ((!s && z) || (s && e && z)) {
1975
- sameTag = false;
1976
- break;
1977
- }
1978
- }
1979
-
1980
- if (sameTag) {
1981
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
1982
-
1983
- return {
1984
- ancestor: element,
1985
- startContainer: startCon,
1986
- startOffset: startOff,
1987
- endContainer: endCon,
1988
- endOffset: endOff
1989
- };
1990
- }
1991
- }
1992
- }
1993
-
1994
- // add tag
1995
- _removeCheck.v = false;
1996
- // eslint-disable-next-line @typescript-eslint/no-this-alias
1997
- const inst = this;
1998
- const el = element;
1999
- const nNodeArray = [newInnerNode];
2000
- const pNode = element.cloneNode(false);
2001
- const isSameNode = startCon === endCon;
2002
- let startContainer = startCon;
2003
- let startOffset = startOff;
2004
- let endContainer = endCon;
2005
- let endOffset = endOff;
2006
- let startPass = false;
2007
- let endPass = false;
2008
- let pCurrent, newNode, appendNode, cssText, anchorNode;
2009
-
2010
- const wRegExp = RegExp;
2011
- function checkCss(vNode) {
2012
- const regExp = new wRegExp('(?:;|^|\\s)(?:' + cssText + 'null)\\s*:[^;]*\\s*(?:;|$)', 'ig');
2013
- let style = false;
2014
-
2015
- if (regExp && vNode.style.cssText.length > 0) {
2016
- style = regExp.test(vNode.style.cssText);
2017
- }
2018
-
2019
- return !style;
2020
- }
2021
-
2022
- (function recursionFunc(current, ancestor) {
2023
- const childNodes = current.childNodes;
2024
-
2025
- for (let i = 0, len = childNodes.length, vNode; i < len; i++) {
2026
- const child = childNodes[i];
2027
- if (!child) continue;
2028
- let coverNode = ancestor;
2029
- let cloneNode;
2030
-
2031
- // startContainer
2032
- if (!startPass && child === startContainer) {
2033
- let line = pNode;
2034
- anchorNode = _getMaintainedNode(child);
2035
-
2036
- let _prevText = '';
2037
- let _nextText = '';
2038
- if (startContainer.nodeType === 3) {
2039
- const sText = /** @type {Text} */ (startContainer);
2040
- _prevText = sText.substringData(0, startOffset);
2041
- _nextText = sText.substringData(startOffset, isSameNode ? (endOffset >= startOffset ? endOffset - startOffset : sText.data.length - startOffset) : sText.data.length - startOffset);
2042
- }
2043
-
2044
- const prevNode = dom.utils.createTextNode(_prevText);
2045
- const textNode = dom.utils.createTextNode(_nextText);
2046
-
2047
- if (anchorNode) {
2048
- const a = _getMaintainedNode(ancestor);
2049
- if (a.parentNode !== line) {
2050
- let m = a;
2051
- let p = null;
2052
- while (m.parentNode !== line) {
2053
- ancestor = p = m.parentNode.cloneNode(false);
2054
- while (m.childNodes[0]) {
2055
- p.appendChild(m.childNodes[0]);
2056
- }
2057
- m.appendChild(p);
2058
- m = m.parentNode;
2059
- }
2060
- m.parentNode.appendChild(a);
2061
- }
2062
- anchorNode = anchorNode.cloneNode(false);
2063
- }
2064
-
2065
- if (!dom.check.isZeroWidth(prevNode)) {
2066
- ancestor.appendChild(prevNode);
2067
- }
2068
-
2069
- const prevAnchorNode = _getMaintainedNode(ancestor);
2070
- if (prevAnchorNode) anchorNode = prevAnchorNode;
2071
- if (anchorNode) line = anchorNode;
2072
-
2073
- newNode = /** @type {HTMLElement} */ (child);
2074
- pCurrent = [];
2075
- cssText = '';
2076
- while (newNode !== line && newNode !== el && newNode !== null) {
2077
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
2078
- if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
2079
- pCurrent.push(vNode);
2080
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2081
- }
2082
- newNode = newNode.parentElement;
2083
- }
2084
-
2085
- const childNode = pCurrent.pop() || textNode;
2086
- appendNode = newNode = childNode;
2087
- while (pCurrent.length > 0) {
2088
- newNode = pCurrent.pop();
2089
- appendNode.appendChild(newNode);
2090
- appendNode = newNode;
2091
- }
2092
-
2093
- newInnerNode.appendChild(childNode);
2094
- line.appendChild(newInnerNode);
2095
-
2096
- if (anchorNode && !_getMaintainedNode(endContainer)) {
2097
- newInnerNode = newInnerNode.cloneNode(false);
2098
- pNode.appendChild(newInnerNode);
2099
- nNodeArray.push(newInnerNode);
2100
- }
2101
-
2102
- startContainer = textNode;
2103
- startOffset = 0;
2104
- startPass = true;
2105
-
2106
- if (newNode !== textNode) newNode.appendChild(startContainer);
2107
- if (!isSameNode) continue;
2108
- }
2109
-
2110
- // endContainer
2111
- if (!endPass && child === endContainer) {
2112
- anchorNode = _getMaintainedNode(child);
2113
-
2114
- let _prevText = '';
2115
- let _nextText = '';
2116
- if (endContainer.nodeType === 3) {
2117
- const eText = /** @type {Text} */ (endContainer);
2118
- _prevText = eText.substringData(endOffset, eText.length - endOffset);
2119
- _nextText = isSameNode ? '' : eText.substringData(0, endOffset);
2120
- }
2121
-
2122
- const afterNode = dom.utils.createTextNode(_prevText);
2123
- const textNode = dom.utils.createTextNode(_nextText);
2124
-
2125
- if (anchorNode) {
2126
- anchorNode = anchorNode.cloneNode(false);
2127
- } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
2128
- newInnerNode = newInnerNode.cloneNode(false);
2129
- pNode.appendChild(newInnerNode);
2130
- nNodeArray.push(newInnerNode);
2131
- }
2132
-
2133
- if (!dom.check.isZeroWidth(afterNode)) {
2134
- newNode = /** @type {HTMLElement} */ (child);
2135
- cssText = '';
2136
- pCurrent = [];
2137
- const anchors = [];
2138
- while (newNode !== pNode && newNode !== el && newNode !== null) {
2139
- if (newNode.nodeType === 1 && checkCss(newNode)) {
2140
- if (_isMaintainedNode(newNode)) anchors.push(newNode.cloneNode(false));
2141
- else pCurrent.push(newNode.cloneNode(false));
2142
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2143
- }
2144
- newNode = newNode.parentElement;
2145
- }
2146
- pCurrent = pCurrent.concat(anchors);
2147
-
2148
- cloneNode = appendNode = newNode = pCurrent.pop() || afterNode;
2149
- while (pCurrent.length > 0) {
2150
- newNode = pCurrent.pop();
2151
- appendNode.appendChild(newNode);
2152
- appendNode = newNode;
2153
- }
2154
-
2155
- pNode.appendChild(cloneNode);
2156
- newNode.textContent = afterNode.data;
2157
- }
2158
-
2159
- if (anchorNode && cloneNode) {
2160
- const afterAnchorNode = _getMaintainedNode(cloneNode);
2161
- if (afterAnchorNode) {
2162
- anchorNode = afterAnchorNode;
2163
- }
2164
- }
2165
-
2166
- newNode = /** @type {HTMLElement} */ (child);
2167
- pCurrent = [];
2168
- cssText = '';
2169
- while (newNode !== pNode && newNode !== el && newNode !== null) {
2170
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
2171
- if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
2172
- pCurrent.push(vNode);
2173
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2174
- }
2175
- newNode = newNode.parentElement;
2176
- }
2177
-
2178
- const childNode = pCurrent.pop() || textNode;
2179
- appendNode = newNode = childNode;
2180
- while (pCurrent.length > 0) {
2181
- newNode = pCurrent.pop();
2182
- appendNode.appendChild(newNode);
2183
- appendNode = newNode;
2184
- }
2185
-
2186
- if (anchorNode) {
2187
- newInnerNode = newInnerNode.cloneNode(false);
2188
- newInnerNode.appendChild(childNode);
2189
- anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
2190
- pNode.appendChild(anchorNode);
2191
- nNodeArray.push(newInnerNode);
2192
- anchorNode = null;
2193
- } else {
2194
- newInnerNode.appendChild(childNode);
2195
- }
2196
-
2197
- endContainer = textNode;
2198
- endOffset = textNode.data.length;
2199
- endPass = true;
2200
-
2201
- if (!isRemoveFormat && collapsed) {
2202
- newInnerNode = textNode;
2203
- textNode.textContent = unicode.zeroWidthSpace;
2204
- }
2205
-
2206
- if (newNode !== textNode) newNode.appendChild(endContainer);
2207
- continue;
2208
- }
2209
-
2210
- // other
2211
- if (startPass) {
2212
- if (child.nodeType === 1 && !dom.check.isBreak(child)) {
2213
- if (inst._isIgnoreNodeChange(child)) {
2214
- pNode.appendChild(child.cloneNode(true));
2215
- if (!collapsed) {
2216
- newInnerNode = newInnerNode.cloneNode(false);
2217
- pNode.appendChild(newInnerNode);
2218
- nNodeArray.push(newInnerNode);
2219
- }
2220
- } else {
2221
- recursionFunc(child, child);
2222
- }
2223
- continue;
2224
- }
2225
-
2226
- newNode = /** @type {HTMLElement} */ (child);
2227
- pCurrent = [];
2228
- cssText = '';
2229
- const anchors = [];
2230
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2231
- vNode = endPass ? newNode.cloneNode(false) : validation(newNode);
2232
- if (newNode.nodeType === 1 && !dom.check.isBreak(child) && vNode && checkCss(newNode)) {
2233
- if (_isMaintainedNode(newNode)) {
2234
- if (!anchorNode) anchors.push(vNode);
2235
- } else {
2236
- pCurrent.push(vNode);
2237
- }
2238
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2239
- }
2240
- newNode = newNode.parentElement;
2241
- }
2242
- pCurrent = pCurrent.concat(anchors);
2243
-
2244
- const childNode = pCurrent.pop() || child;
2245
- appendNode = newNode = childNode;
2246
- while (pCurrent.length > 0) {
2247
- newNode = pCurrent.pop();
2248
- appendNode.appendChild(newNode);
2249
- appendNode = newNode;
2250
- }
2251
-
2252
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode) && !dom.check.isZeroWidth(newInnerNode)) {
2253
- newInnerNode = newInnerNode.cloneNode(false);
2254
- pNode.appendChild(newInnerNode);
2255
- nNodeArray.push(newInnerNode);
2256
- }
2257
-
2258
- if (!endPass && !anchorNode && _isMaintainedNode(childNode)) {
2259
- newInnerNode = newInnerNode.cloneNode(false);
2260
- const aChildren = childNode.childNodes;
2261
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2262
- newInnerNode.appendChild(aChildren[a]);
2263
- }
2264
- childNode.appendChild(newInnerNode);
2265
- pNode.appendChild(childNode);
2266
- nNodeArray.push(newInnerNode);
2267
- if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
2268
- else ancestor = newInnerNode;
2269
- } else if (childNode === child) {
2270
- if (!endPass) ancestor = newInnerNode;
2271
- else ancestor = pNode;
2272
- } else if (endPass) {
2273
- pNode.appendChild(childNode);
2274
- ancestor = newNode;
2275
- } else {
2276
- newInnerNode.appendChild(childNode);
2277
- ancestor = newNode;
2278
- }
2279
-
2280
- if (anchorNode && child.nodeType === 3) {
2281
- if (_getMaintainedNode(child)) {
2282
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2283
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2284
- });
2285
- anchorNode.appendChild(ancestorAnchorNode);
2286
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2287
- nNodeArray.push(newInnerNode);
2288
- pNode.appendChild(newInnerNode);
2289
- } else {
2290
- anchorNode = null;
2291
- }
2292
- }
2293
- }
2294
-
2295
- cloneNode = child.cloneNode(false);
2296
- ancestor.appendChild(cloneNode);
2297
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = cloneNode;
2298
-
2299
- recursionFunc(child, coverNode);
2300
- }
2301
- })(element, pNode);
2302
-
2303
- // not remove tag
2304
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
2305
- return {
2306
- ancestor: element,
2307
- startContainer: startCon,
2308
- startOffset: startOff,
2309
- endContainer: endCon,
2310
- endOffset: endOff
2311
- };
2312
- }
2313
-
2314
- isRemoveFormat = isRemoveFormat && isRemoveNode;
2315
-
2316
- if (isRemoveFormat) {
2317
- for (let i = 0; i < nNodeArray.length; i++) {
2318
- const removeNode = nNodeArray[i];
2319
- let textNode, textNode_s, textNode_e;
2320
-
2321
- if (collapsed) {
2322
- textNode = dom.utils.createTextNode(unicode.zeroWidthSpace);
2323
- pNode.replaceChild(textNode, removeNode);
2324
- } else {
2325
- const rChildren = removeNode.childNodes;
2326
- textNode_s = rChildren[0];
2327
- while (rChildren[0]) {
2328
- textNode_e = rChildren[0];
2329
- pNode.insertBefore(textNode_e, removeNode);
2330
- }
2331
- dom.utils.removeItem(removeNode);
2332
- }
2333
-
2334
- if (i === 0) {
2335
- if (collapsed) {
2336
- startContainer = endContainer = textNode;
2337
- } else {
2338
- startContainer = textNode_s;
2339
- endContainer = textNode_e;
2340
- }
2341
- }
2342
- }
2343
- } else {
2344
- if (isRemoveNode) {
2345
- for (let i = 0; i < nNodeArray.length; i++) {
2346
- SN_StripRemoveNode(nNodeArray[i]);
2347
- }
2348
- }
2349
-
2350
- if (collapsed) {
2351
- startContainer = endContainer = newInnerNode;
2352
- }
2353
- }
2354
-
2355
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2356
-
2357
- if (collapsed) {
2358
- startOffset = startContainer.textContent.length;
2359
- endOffset = endContainer.textContent.length;
2360
- }
2361
-
2362
- // endContainer reset
2363
- const endConReset = isRemoveFormat || endContainer.textContent.length === 0;
2364
-
2365
- if (!dom.check.isBreak(endContainer) && endContainer.textContent.length === 0) {
2366
- dom.utils.removeItem(endContainer);
2367
- endContainer = startContainer;
2368
- }
2369
- endOffset = endConReset ? endContainer.textContent.length : endOffset;
2370
-
2371
- // node change
2372
- const newStartOffset = {
2373
- s: 0,
2374
- e: 0
2375
- };
2376
- const startPath = dom.query.getNodePath(startContainer, pNode, newStartOffset);
2377
-
2378
- const mergeEndCon = !endContainer.parentNode;
2379
- if (mergeEndCon) endContainer = startContainer;
2380
- const newEndOffset = {
2381
- s: 0,
2382
- e: 0
2383
- };
2384
- const endPath = dom.query.getNodePath(endContainer, pNode, !mergeEndCon && !endConReset ? newEndOffset : null);
2385
-
2386
- startOffset += newStartOffset.s;
2387
- endOffset = collapsed ? startOffset : mergeEndCon ? startContainer.textContent.length : endConReset ? endOffset + newStartOffset.s : endOffset + newEndOffset.s;
2388
-
2389
- // tag merge
2390
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [startPath, endPath], true);
2391
-
2392
- element.parentNode.replaceChild(pNode, element);
2393
-
2394
- startContainer = dom.query.getNodeFromPath(startPath, pNode);
2395
- endContainer = dom.query.getNodeFromPath(endPath, pNode);
2396
-
2397
- return {
2398
- ancestor: pNode,
2399
- startContainer: startContainer,
2400
- startOffset: startOffset + newOffsets[0],
2401
- endContainer: endContainer,
2402
- endOffset: endOffset + newOffsets[1]
2403
- };
2404
- },
2405
-
2406
- /**
2407
- * @private
2408
- * @this {FormatThis}
2409
- * @description wraps first line selected text.
2410
- * @param {Node} element The node of the line that contains the selected text node.
2411
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2412
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2413
- * @param {Node} startCon The startContainer property of the selection object.
2414
- * @param {number} startOff The startOffset property of the selection object.
2415
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2416
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2417
- * @returns {NodeStyleContainerType} { ancestor, container, offset, endContainer }
2418
- */
2419
- _setNode_startLine(element, newInnerNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, _endContainer) {
2420
- // not add tag
2421
- let parentCon = startCon.parentNode;
2422
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
2423
- if (parentCon.nodeName === newInnerNode.nodeName) break;
2424
- parentCon = parentCon.parentNode;
2425
- }
2426
-
2427
- if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.nextSibling && dom.check.isZeroWidth(startCon.textContent.slice(0, startOff))) {
2428
- let sameTag = false;
2429
- let s = startCon.previousSibling;
2430
- while (s) {
2431
- if (!dom.check.isZeroWidth(s)) {
2432
- sameTag = false;
2433
- break;
2434
- }
2435
- s = s.previousSibling;
2436
- }
2437
-
2438
- if (sameTag) {
2439
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
2440
-
2441
- return {
2442
- ancestor: element,
2443
- container: startCon,
2444
- offset: startOff
2445
- };
2446
- }
2447
- }
2448
-
2449
- // add tag
2450
- _removeCheck.v = false;
2451
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2452
- const inst = this;
2453
- const el = element;
2454
- const nNodeArray = [newInnerNode];
2455
- const pNode = element.cloneNode(false);
2456
-
2457
- let container = startCon;
2458
- let offset = startOff;
2459
- let passNode = false;
2460
- let pCurrent, newNode, appendNode, anchorNode;
2461
-
2462
- (function recursionFunc(current, ancestor) {
2463
- const childNodes = current.childNodes;
2464
-
2465
- for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
2466
- const child = /** @type {HTMLElement} */ (childNodes[i]);
2467
- if (!child) continue;
2468
- let coverNode = ancestor;
2469
-
2470
- if (passNode && !dom.check.isBreak(child)) {
2471
- if (child.nodeType === 1) {
2472
- if (inst._isIgnoreNodeChange(child)) {
2473
- newInnerNode = newInnerNode.cloneNode(false);
2474
- cloneChild = child.cloneNode(true);
2475
- pNode.appendChild(cloneChild);
2476
- pNode.appendChild(newInnerNode);
2477
- nNodeArray.push(newInnerNode);
2478
-
2479
- // end container
2480
- if (_endContainer && child.contains(_endContainer)) {
2481
- const endPath = dom.query.getNodePath(_endContainer, child);
2482
- _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
2483
- }
2484
- } else {
2485
- recursionFunc(child, child);
2486
- }
2487
- continue;
2488
- }
2489
-
2490
- newNode = child;
2491
- pCurrent = [];
2492
- const anchors = [];
2493
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2494
- vNode = validation(newNode);
2495
- if (newNode.nodeType === 1 && vNode) {
2496
- if (_isMaintainedNode(newNode)) {
2497
- if (!anchorNode) anchors.push(vNode);
2498
- } else {
2499
- pCurrent.push(vNode);
2500
- }
2501
- }
2502
- newNode = newNode.parentNode;
2503
- }
2504
- pCurrent = pCurrent.concat(anchors);
2505
-
2506
- const isTopNode = pCurrent.length > 0;
2507
- const childNode = pCurrent.pop() || child;
2508
- appendNode = newNode = childNode;
2509
- while (pCurrent.length > 0) {
2510
- newNode = pCurrent.pop();
2511
- appendNode.appendChild(newNode);
2512
- appendNode = newNode;
2513
- }
2514
-
2515
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
2516
- newInnerNode = newInnerNode.cloneNode(false);
2517
- pNode.appendChild(newInnerNode);
2518
- nNodeArray.push(newInnerNode);
2519
- }
2520
-
2521
- if (!anchorNode && _isMaintainedNode(childNode)) {
2522
- newInnerNode = newInnerNode.cloneNode(false);
2523
- const aChildren = childNode.childNodes;
2524
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2525
- newInnerNode.appendChild(aChildren[a]);
2526
- }
2527
- childNode.appendChild(newInnerNode);
2528
- pNode.appendChild(childNode);
2529
- ancestor = !_isMaintainedNode(newNode) ? newNode : newInnerNode;
2530
- nNodeArray.push(newInnerNode);
2531
- } else if (isTopNode) {
2532
- newInnerNode.appendChild(childNode);
2533
- ancestor = newNode;
2534
- } else {
2535
- ancestor = newInnerNode;
2536
- }
2537
-
2538
- if (anchorNode && child.nodeType === 3) {
2539
- if (_getMaintainedNode(child)) {
2540
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2541
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2542
- });
2543
- anchorNode.appendChild(ancestorAnchorNode);
2544
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2545
- nNodeArray.push(newInnerNode);
2546
- pNode.appendChild(newInnerNode);
2547
- } else {
2548
- anchorNode = null;
2549
- }
2550
- }
2551
- }
2552
-
2553
- // startContainer
2554
- if (!passNode && child === container) {
2555
- let line = pNode;
2556
- anchorNode = _getMaintainedNode(child);
2557
-
2558
- let _prevText = '';
2559
- let _nextText = '';
2560
- if (container.nodeType === 3) {
2561
- const cText = /** @type {Text} */ (container);
2562
- _prevText = cText.substringData(0, offset);
2563
- _nextText = cText.substringData(offset, cText.length - offset);
2564
- }
2565
-
2566
- const prevNode = dom.utils.createTextNode(_prevText);
2567
- const textNode = dom.utils.createTextNode(_nextText);
2568
-
2569
- if (anchorNode) {
2570
- const a = _getMaintainedNode(ancestor);
2571
- if (a && a.parentNode !== line) {
2572
- let m = a;
2573
- let p = null;
2574
- while (m.parentNode !== line) {
2575
- ancestor = p = m.parentNode.cloneNode(false);
2576
- while (m.childNodes[0]) {
2577
- p.appendChild(m.childNodes[0]);
2578
- }
2579
- m.appendChild(p);
2580
- m = m.parentNode;
2581
- }
2582
- m.parentNode.appendChild(a);
2583
- }
2584
- anchorNode = anchorNode.cloneNode(false);
2585
- }
2586
-
2587
- if (!dom.check.isZeroWidth(prevNode)) {
2588
- ancestor.appendChild(prevNode);
2589
- }
2590
-
2591
- const prevAnchorNode = _getMaintainedNode(ancestor);
2592
- if (prevAnchorNode) anchorNode = prevAnchorNode;
2593
- if (anchorNode) line = anchorNode;
2594
-
2595
- newNode = ancestor;
2596
- pCurrent = [];
2597
- while (newNode !== line && newNode !== null) {
2598
- vNode = validation(newNode);
2599
- if (newNode.nodeType === 1 && vNode) {
2600
- pCurrent.push(vNode);
2601
- }
2602
- newNode = newNode.parentNode;
2603
- }
2604
-
2605
- const childNode = pCurrent.pop() || ancestor;
2606
- appendNode = newNode = childNode;
2607
- while (pCurrent.length > 0) {
2608
- newNode = pCurrent.pop();
2609
- appendNode.appendChild(newNode);
2610
- appendNode = newNode;
2611
- }
2612
-
2613
- if (childNode !== ancestor) {
2614
- newInnerNode.appendChild(childNode);
2615
- ancestor = newNode;
2616
- } else {
2617
- ancestor = newInnerNode;
2618
- }
2619
-
2620
- if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
2621
- line.appendChild(newInnerNode);
2622
-
2623
- container = textNode;
2624
- offset = 0;
2625
- passNode = true;
2626
-
2627
- ancestor.appendChild(container);
2628
- continue;
2629
- }
2630
-
2631
- vNode = !passNode ? child.cloneNode(false) : validation(child);
2632
- if (vNode) {
2633
- ancestor.appendChild(vNode);
2634
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
2635
- }
2636
-
2637
- recursionFunc(child, coverNode);
2638
- }
2639
- })(element, pNode);
2640
-
2641
- // not remove tag
2642
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
2643
- return {
2644
- ancestor: element,
2645
- container: startCon,
2646
- offset: startOff,
2647
- endContainer: _endContainer
2648
- };
2649
- }
2650
-
2651
- isRemoveFormat = isRemoveFormat && isRemoveNode;
2652
-
2653
- if (isRemoveFormat) {
2654
- for (let i = 0; i < nNodeArray.length; i++) {
2655
- const removeNode = nNodeArray[i];
2656
-
2657
- const rChildren = removeNode.childNodes;
2658
- const textNode = rChildren[0];
2659
- while (rChildren[0]) {
2660
- pNode.insertBefore(rChildren[0], removeNode);
2661
- }
2662
- dom.utils.removeItem(removeNode);
2663
-
2664
- if (i === 0) container = textNode;
2665
- }
2666
- } else if (isRemoveNode) {
2667
- newInnerNode = newInnerNode.firstChild;
2668
- for (let i = 0; i < nNodeArray.length; i++) {
2669
- SN_StripRemoveNode(nNodeArray[i]);
2670
- }
2671
- }
2672
-
2673
- if (!isRemoveFormat && pNode.childNodes.length === 0) {
2674
- if (element.childNodes) {
2675
- container = element.childNodes[0];
2676
- } else {
2677
- container = dom.utils.createTextNode(unicode.zeroWidthSpace);
2678
- element.appendChild(container);
2679
- }
2680
- } else {
2681
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2682
-
2683
- if (dom.check.isZeroWidth(pNode.textContent)) {
2684
- container = pNode.firstChild;
2685
- offset = 0;
2686
- }
2687
-
2688
- // node change
2689
- const offsets = {
2690
- s: 0,
2691
- e: 0
2692
- };
2693
- const path = dom.query.getNodePath(container, pNode, offsets);
2694
- offset += offsets.s;
2695
-
2696
- // tag merge
2697
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
2698
-
2699
- element.parentNode.replaceChild(pNode, element);
2700
-
2701
- container = dom.query.getNodeFromPath(path, pNode);
2702
- offset += newOffsets[0];
2703
- }
2704
-
2705
- return {
2706
- ancestor: pNode,
2707
- container: container,
2708
- offset: offset,
2709
- endContainer: _endContainer
2710
- };
2711
- },
2712
-
2713
- /**
2714
- * @private
2715
- * @this {FormatThis}
2716
- * @description wraps mid lines selected text.
2717
- * @param {HTMLElement} element The node of the line that contains the selected text node.
2718
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2719
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2720
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2721
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2722
- * @param {Node} _endContainer Offset node of last line already modified (end.container)
2723
- * @returns {NodeStyleContainerType} { ancestor, endContainer: "If end container is renewed, returned renewed node" }
2724
- */
2725
- _setNode_middleLine(element, newInnerNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, _endContainer) {
2726
- // not add tag
2727
- if (!isRemoveNode) {
2728
- // end container path
2729
- let endPath = null;
2730
- if (_endContainer && element.contains(_endContainer)) endPath = dom.query.getNodePath(_endContainer, element);
2731
-
2732
- const tempNode = element.cloneNode(true);
2733
- const newNodeName = /** @type {HTMLElement} */ (newInnerNode).nodeName;
2734
- const newCssText = /** @type {HTMLElement} */ (newInnerNode).style.cssText;
2735
- const newClass = /** @type {HTMLElement} */ (newInnerNode).className;
2736
-
2737
- let children = tempNode.childNodes;
2738
- let i = 0,
2739
- len = children.length;
2740
- for (let child; i < len; i++) {
2741
- child = /** @type {HTMLElement} */ (children[i]);
2742
- if (child.nodeType === 3) break;
2743
- if (child.nodeName === newNodeName) {
2744
- child.style.cssText += newCssText;
2745
- dom.utils.addClass(child, newClass);
2746
- } else if (!dom.check.isBreak(child) && this._isIgnoreNodeChange(child)) {
2747
- continue;
2748
- } else if (len === 1) {
2749
- children = child.childNodes;
2750
- len = children.length;
2751
- i = -1;
2752
- continue;
2753
- } else {
2754
- break;
2755
- }
2756
- }
2757
-
2758
- if (len > 0 && i === len) {
2759
- element.innerHTML = /** @type {HTMLElement} */ (tempNode).innerHTML;
2760
- return {
2761
- ancestor: element,
2762
- endContainer: endPath ? dom.query.getNodeFromPath(endPath, element) : null
2763
- };
2764
- }
2765
- }
2766
-
2767
- // add tag
2768
- _removeCheck.v = false;
2769
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2770
- const inst = this;
2771
- const pNode = element.cloneNode(false);
2772
- const nNodeArray = [newInnerNode];
2773
- let noneChange = true;
2774
-
2775
- (function recursionFunc(current, ancestor) {
2776
- const childNodes = current.childNodes;
2777
-
2778
- for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
2779
- const child = /** @type {HTMLElement} */ (childNodes[i]);
2780
- if (!child) continue;
2781
- let coverNode = ancestor;
2782
-
2783
- if (!dom.check.isBreak(child) && inst._isIgnoreNodeChange(child)) {
2784
- if (newInnerNode.childNodes.length > 0) {
2785
- pNode.appendChild(newInnerNode);
2786
- newInnerNode = newInnerNode.cloneNode(false);
2787
- }
2788
-
2789
- cloneChild = child.cloneNode(true);
2790
- pNode.appendChild(cloneChild);
2791
- pNode.appendChild(newInnerNode);
2792
- nNodeArray.push(newInnerNode);
2793
- ancestor = newInnerNode;
2794
-
2795
- // end container
2796
- if (_endContainer && child.contains(_endContainer)) {
2797
- const endPath = dom.query.getNodePath(_endContainer, child);
2798
- _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
2799
- }
2800
-
2801
- continue;
2802
- } else {
2803
- vNode = validation(child);
2804
- if (vNode) {
2805
- noneChange = false;
2806
- ancestor.appendChild(vNode);
2807
- if (child.nodeType === 1) coverNode = vNode;
2808
- }
2809
- }
2810
-
2811
- if (!dom.check.isBreak(child)) recursionFunc(child, coverNode);
2812
- }
2813
- })(element, newInnerNode);
2814
-
2815
- // not remove tag
2816
- if (noneChange || (isRemoveNode && !isRemoveFormat && !_removeCheck.v))
2817
- return {
2818
- ancestor: element,
2819
- endContainer: _endContainer
2820
- };
2821
-
2822
- pNode.appendChild(newInnerNode);
2823
-
2824
- if (isRemoveFormat && isRemoveNode) {
2825
- for (let i = 0; i < nNodeArray.length; i++) {
2826
- const removeNode = nNodeArray[i];
2827
-
2828
- const rChildren = removeNode.childNodes;
2829
- while (rChildren[0]) {
2830
- pNode.insertBefore(rChildren[0], removeNode);
2831
- }
2832
- dom.utils.removeItem(removeNode);
2833
- }
2834
- } else if (isRemoveNode) {
2835
- newInnerNode = newInnerNode.firstChild;
2836
- for (let i = 0; i < nNodeArray.length; i++) {
2837
- SN_StripRemoveNode(nNodeArray[i]);
2838
- }
2839
- }
2840
-
2841
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2842
- this.nodeTransform.mergeSameTags(pNode, null, true);
2843
-
2844
- // node change
2845
- element.parentNode.replaceChild(pNode, element);
2846
- return {
2847
- ancestor: pNode,
2848
- endContainer: _endContainer
2849
- };
2850
- },
2851
-
2852
- /**
2853
- * @private
2854
- * @this {FormatThis}
2855
- * @description wraps last line selected text.
2856
- * @param {Node} element The node of the line that contains the selected text node.
2857
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2858
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2859
- * @param {Node} endCon The endContainer property of the selection object.
2860
- * @param {number} endOff The endOffset property of the selection object.
2861
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2862
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2863
- * @returns {NodeStyleContainerType} { ancestor, container, offset }
2864
- */
2865
- _setNode_endLine(element, newInnerNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
2866
- // not add tag
2867
- let parentCon = endCon.parentNode;
2868
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
2869
- if (parentCon.nodeName === newInnerNode.nodeName) break;
2870
- parentCon = parentCon.parentNode;
2871
- }
2872
-
2873
- if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.previousSibling && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
2874
- let sameTag = false;
2875
- let e = endCon.nextSibling;
2876
- while (e) {
2877
- if (!dom.check.isZeroWidth(e)) {
2878
- sameTag = false;
2879
- break;
2880
- }
2881
- e = e.nextSibling;
2882
- }
2883
-
2884
- if (sameTag) {
2885
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
2886
-
2887
- return {
2888
- ancestor: element,
2889
- container: endCon,
2890
- offset: endOff
2891
- };
2892
- }
2893
- }
2894
-
2895
- // add tag
2896
- _removeCheck.v = false;
2897
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2898
- const inst = this;
2899
- const el = element;
2900
- const nNodeArray = [newInnerNode];
2901
- const pNode = element.cloneNode(false);
2902
-
2903
- let container = endCon;
2904
- let offset = endOff;
2905
- let passNode = false;
2906
- let pCurrent, newNode, appendNode, anchorNode;
2907
-
2908
- (function recursionFunc(current, ancestor) {
2909
- const childNodes = current.childNodes;
2910
-
2911
- for (let i = childNodes.length - 1, vNode; 0 <= i; i--) {
2912
- const child = childNodes[i];
2913
- if (!child) continue;
2914
- let coverNode = ancestor;
2915
-
2916
- if (passNode && !dom.check.isBreak(child)) {
2917
- if (child.nodeType === 1) {
2918
- if (inst._isIgnoreNodeChange(child)) {
2919
- newInnerNode = newInnerNode.cloneNode(false);
2920
- const cloneChild = child.cloneNode(true);
2921
- pNode.insertBefore(cloneChild, ancestor);
2922
- pNode.insertBefore(newInnerNode, cloneChild);
2923
- nNodeArray.push(newInnerNode);
2924
- } else {
2925
- recursionFunc(child, child);
2926
- }
2927
- continue;
2928
- }
2929
-
2930
- newNode = child;
2931
- pCurrent = [];
2932
- const anchors = [];
2933
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2934
- vNode = validation(newNode);
2935
- if (vNode && newNode.nodeType === 1) {
2936
- if (_isMaintainedNode(newNode)) {
2937
- if (!anchorNode) anchors.push(vNode);
2938
- } else {
2939
- pCurrent.push(vNode);
2940
- }
2941
- }
2942
- newNode = newNode.parentNode;
2943
- }
2944
- pCurrent = pCurrent.concat(anchors);
2945
-
2946
- const isTopNode = pCurrent.length > 0;
2947
- const childNode = pCurrent.pop() || child;
2948
- appendNode = newNode = childNode;
2949
- while (pCurrent.length > 0) {
2950
- newNode = pCurrent.pop();
2951
- appendNode.appendChild(newNode);
2952
- appendNode = newNode;
2953
- }
2954
-
2955
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
2956
- newInnerNode = newInnerNode.cloneNode(false);
2957
- pNode.insertBefore(newInnerNode, pNode.firstChild);
2958
- nNodeArray.push(newInnerNode);
2959
- }
2960
-
2961
- if (!anchorNode && _isMaintainedNode(childNode)) {
2962
- newInnerNode = newInnerNode.cloneNode(false);
2963
- const aChildren = childNode.childNodes;
2964
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2965
- newInnerNode.appendChild(aChildren[a]);
2966
- }
2967
- childNode.appendChild(newInnerNode);
2968
- pNode.insertBefore(childNode, pNode.firstChild);
2969
- nNodeArray.push(newInnerNode);
2970
- if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
2971
- else ancestor = newInnerNode;
2972
- } else if (isTopNode) {
2973
- newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
2974
- ancestor = newNode;
2975
- } else {
2976
- ancestor = newInnerNode;
2977
- }
2978
-
2979
- if (anchorNode && child.nodeType === 3) {
2980
- if (_getMaintainedNode(child)) {
2981
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2982
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2983
- });
2984
- anchorNode.appendChild(ancestorAnchorNode);
2985
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2986
- nNodeArray.push(newInnerNode);
2987
- pNode.insertBefore(newInnerNode, pNode.firstChild);
2988
- } else {
2989
- anchorNode = null;
2990
- }
2991
- }
2992
- }
2993
-
2994
- // endContainer
2995
- if (!passNode && child === container) {
2996
- anchorNode = _getMaintainedNode(child);
2997
-
2998
- let _prevText = '';
2999
- let _nextText = '';
3000
- if (container.nodeType === 3) {
3001
- const cText = /** @type {Text} */ (container);
3002
- _prevText = cText.substringData(offset, cText.length - offset);
3003
- _nextText = cText.substringData(0, offset);
3004
- }
3005
-
3006
- const afterNode = dom.utils.createTextNode(_prevText);
3007
- const textNode = dom.utils.createTextNode(_nextText);
3008
-
3009
- if (anchorNode) {
3010
- anchorNode = anchorNode.cloneNode(false);
3011
- const a = _getMaintainedNode(ancestor);
3012
- if (a.parentNode !== pNode) {
3013
- let m = a;
3014
- let p = null;
3015
- while (m.parentNode !== pNode) {
3016
- ancestor = p = m.parentNode.cloneNode(false);
3017
- while (m.childNodes[0]) {
3018
- p.appendChild(m.childNodes[0]);
3019
- }
3020
- m.appendChild(p);
3021
- m = m.parentNode;
3022
- }
3023
- m.parentNode.insertBefore(a, m.parentNode.firstChild);
3024
- }
3025
- anchorNode = anchorNode.cloneNode(false);
3026
- } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
3027
- newInnerNode = newInnerNode.cloneNode(false);
3028
- pNode.appendChild(newInnerNode);
3029
- nNodeArray.push(newInnerNode);
3030
- }
3031
-
3032
- if (!dom.check.isZeroWidth(afterNode)) {
3033
- ancestor.insertBefore(afterNode, ancestor.firstChild);
3034
- }
3035
-
3036
- newNode = ancestor;
3037
- pCurrent = [];
3038
- while (newNode !== pNode && newNode !== null) {
3039
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
3040
- if (vNode && newNode.nodeType === 1) {
3041
- pCurrent.push(vNode);
3042
- }
3043
- newNode = newNode.parentNode;
3044
- }
3045
-
3046
- const childNode = pCurrent.pop() || ancestor;
3047
- appendNode = newNode = childNode;
3048
- while (pCurrent.length > 0) {
3049
- newNode = pCurrent.pop();
3050
- appendNode.appendChild(newNode);
3051
- appendNode = newNode;
3052
- }
3053
-
3054
- if (childNode !== ancestor) {
3055
- newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
3056
- ancestor = newNode;
3057
- } else {
3058
- ancestor = newInnerNode;
3059
- }
3060
-
3061
- if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
3062
-
3063
- if (anchorNode) {
3064
- anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
3065
- pNode.insertBefore(anchorNode, pNode.firstChild);
3066
- anchorNode = null;
3067
- } else {
3068
- pNode.insertBefore(newInnerNode, pNode.firstChild);
3069
- }
3070
-
3071
- container = textNode;
3072
- offset = textNode.data.length;
3073
- passNode = true;
3074
-
3075
- ancestor.insertBefore(container, ancestor.firstChild);
3076
- continue;
3077
- }
3078
-
3079
- vNode = !passNode ? child.cloneNode(false) : validation(child);
3080
- if (vNode) {
3081
- ancestor.insertBefore(vNode, ancestor.firstChild);
3082
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
3083
- }
3084
-
3085
- recursionFunc(child, coverNode);
3086
- }
3087
- })(element, pNode);
3088
-
3089
- // not remove tag
3090
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
3091
- return {
3092
- ancestor: element,
3093
- container: endCon,
3094
- offset: endOff
3095
- };
3096
- }
3097
-
3098
- isRemoveFormat = isRemoveFormat && isRemoveNode;
3099
-
3100
- if (isRemoveFormat) {
3101
- for (let i = 0; i < nNodeArray.length; i++) {
3102
- const removeNode = nNodeArray[i];
3103
-
3104
- const rChildren = removeNode.childNodes;
3105
- let textNode = null;
3106
- while (rChildren[0]) {
3107
- textNode = rChildren[0];
3108
- pNode.insertBefore(textNode, removeNode);
3109
- }
3110
- dom.utils.removeItem(removeNode);
3111
-
3112
- if (i === nNodeArray.length - 1) {
3113
- container = textNode;
3114
- offset = textNode.textContent.length;
3115
- }
3116
- }
3117
- } else if (isRemoveNode) {
3118
- newInnerNode = newInnerNode.firstChild;
3119
- for (let i = 0; i < nNodeArray.length; i++) {
3120
- SN_StripRemoveNode(nNodeArray[i]);
3121
- }
3122
- }
3123
-
3124
- if (!isRemoveFormat && pNode.childNodes.length === 0) {
3125
- if (element.childNodes) {
3126
- container = element.childNodes[0];
3127
- } else {
3128
- container = dom.utils.createTextNode(unicode.zeroWidthSpace);
3129
- element.appendChild(container);
3130
- }
3131
- } else {
3132
- if (!isRemoveNode && newInnerNode.textContent.length === 0) {
3133
- this.nodeTransform.removeEmptyNode(pNode, null, false);
3134
- return {
3135
- ancestor: null,
3136
- container: null,
3137
- offset: 0
3138
- };
3139
- }
3140
-
3141
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
3142
-
3143
- if (dom.check.isZeroWidth(pNode.textContent)) {
3144
- container = pNode.firstChild;
3145
- offset = container.textContent.length;
3146
- } else if (dom.check.isZeroWidth(container)) {
3147
- container = newInnerNode;
3148
- offset = 1;
3149
- }
3150
-
3151
- // node change
3152
- const offsets = {
3153
- s: 0,
3154
- e: 0
3155
- };
3156
- const path = dom.query.getNodePath(container, pNode, offsets);
3157
- offset += offsets.s;
3158
-
3159
- // tag merge
3160
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
3161
-
3162
- element.parentNode.replaceChild(pNode, element);
3163
-
3164
- container = dom.query.getNodeFromPath(path, pNode);
3165
- offset += newOffsets[0];
3166
- }
3167
-
3168
- return {
3169
- ancestor: pNode,
3170
- container: container,
3171
- offset: container.nodeType === 1 && offset === 1 ? container.childNodes.length : offset
3172
- };
3173
- },
3174
-
3175
- /**
3176
- * @private
3177
- * @this {FormatThis}
3178
- * @description Node with font-size style
3179
- * @param {Node} element Element to check
3180
- * @returns {boolean}
3181
- */
3182
- _sn_isSizeNode(element) {
3183
- return element && typeof element !== 'string' && element.nodeType !== 3 && this.isTextStyleNode(element) && !!element.style.fontSize;
3184
- },
3185
-
3186
- /**
3187
- * @private
3188
- * @this {FormatThis}
3189
- * @description Return the parent maintained tag. (bind and use a util object)
3190
- * @param {boolean} _isRemove is remove anchor
3191
- * @param {boolean} _isSizeNode is size span node
3192
- * @param {Node} element Element
3193
- * @returns {Node|null}
3194
- */
3195
- _sn_getMaintainedNode(_isRemove, _isSizeNode, element) {
3196
- if (!element || _isRemove) return null;
3197
- return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) || (!_isSizeNode ? dom.query.getParentElement(element, this._sn_isSizeNode.bind(this)) : null);
3198
- },
3199
-
3200
- /**
3201
- * @private
3202
- * @this {FormatThis}
3203
- * @description Check if element is a tag that should be persisted. (bind and use a util object)
3204
- * @param {boolean} _isRemove is remove anchor
3205
- * @param {boolean} _isSizeNode is size span node
3206
- * @param {Node} element Element
3207
- * @returns {boolean}
3208
- */
3209
- _sn_isMaintainedNode(_isRemove, _isSizeNode, element) {
3210
- if (!element || _isRemove || element.nodeType !== 1) return false;
3211
- const anchor = this._isNonSplitNode(element);
3212
- return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) ? anchor : anchor || (!_isSizeNode ? this._sn_isSizeNode(element) : false);
3213
- },
3214
-
3215
- /**
3216
- * @private
3217
- * @this {FormatThis}
3218
- * @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)
3219
- * @param {Node} el List cell element. <li>
3220
- * @param {?Node} child Variable for recursive call. ("null" on the first call)
3221
- */
3222
- _sn_setCommonListStyle(el, child) {
3223
- if (!dom.check.isListCell(el)) return;
3224
-
3225
- const children = dom.utils.arrayFilter((child || el).childNodes, (current) => !dom.check.isBreak(current));
3226
- child = children[0];
3227
-
3228
- if (!dom.check.isElement(child) || children.length > 1) return;
3229
-
3230
- // set cell style---
3231
- const childStyle = child.style;
3232
- const elStyle = el.style;
3233
- const nodeName = child.nodeName.toLowerCase();
3234
- let appliedEl = false;
3235
-
3236
- // bold, italic
3237
- if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').bold.toLowerCase()) elStyle.fontWeight = 'bold';
3238
- if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').italic.toLowerCase()) elStyle.fontStyle = 'italic';
3239
-
3240
- // styles
3241
- const cKeys = converter.getValues(childStyle);
3242
- if (cKeys.length > 0) {
3243
- for (let i = 0, len = this._listCamel.length; i < len; i++) {
3244
- if (cKeys.includes(this._listKebab[i])) {
3245
- elStyle[this._listCamel[i]] = childStyle[this._listCamel[i]];
3246
- childStyle.removeProperty(this._listKebab[i]);
3247
- appliedEl = true;
3248
- }
3249
- }
3250
- }
3251
-
3252
- this._sn_setCommonListStyle(el, child);
3253
- if (!appliedEl) return;
3254
-
3255
- // common style
3256
- if (!childStyle.length) {
3257
- const ch = child.childNodes;
3258
- const p = child.parentNode;
3259
- const n = child.nextSibling;
3260
- while (ch.length > 0) {
3261
- p.insertBefore(ch[0], n);
3262
- }
3263
- dom.utils.removeItem(child);
3264
- }
3265
- },
3266
-
3267
- /**
3268
- * @private
3269
- * @this {FormatThis}
3270
- * @description Watch the applied text nodes and adjust the common styles of the list.
3271
- * @param {Node} el "LI" element
3272
- * @param {Array|null} styleArray Refer style array
3273
- */
3274
- _sn_resetCommonListCell(el, styleArray) {
3275
- if (!dom.check.isListCell(el)) return;
3276
- if (!styleArray) styleArray = this._listKebab;
3277
-
3278
- const children = dom.utils.arrayFilter(el.childNodes, (current) => !dom.check.isBreak(current));
3279
- const elStyles = el.style;
3280
-
3281
- const ec = [],
3282
- ek = [],
3283
- elKeys = converter.getValues(elStyles);
3284
- for (let i = 0, len = this._listKebab.length; i < len; i++) {
3285
- if (elKeys.includes(this._listKebab[i]) && styleArray.includes(this._listKebab[i])) {
3286
- ec.push(this._listCamel[i]);
3287
- ek.push(this._listKebab[i]);
3288
- }
3289
- }
3290
-
3291
- if (!ec.length) return;
3292
-
3293
- // reset cell style---
3294
- const refer = dom.utils.createElement('SPAN');
3295
- for (let i = 0, len = ec.length; i < len; i++) {
3296
- refer.style[ec[i]] = elStyles[ek[i]];
3297
- elStyles.removeProperty(ek[i]);
3298
- }
3299
-
3300
- let sel = refer.cloneNode(false);
3301
- let r = null,
3302
- appliedEl = false;
3303
- for (let i = 0, len = children.length, c, s; i < len; i++) {
3304
- c = /** @type {HTMLElement} */ (children[i]);
3305
- if (this.options.get('_defaultStyleTagMap')[c.nodeName.toLowerCase()]) continue;
3306
-
3307
- s = converter.getValues(c.style);
3308
- if (
3309
- s.length === 0 ||
3310
- (ec.some(function (k) {
3311
- return !s.includes(k);
3312
- }) &&
3313
- s.some(function (k) {
3314
- ec.includes(k);
3315
- }))
3316
- ) {
3317
- r = c.nextSibling;
3318
- sel.appendChild(c);
3319
- } else if (sel.childNodes.length > 0) {
3320
- el.insertBefore(sel, r);
3321
- sel = refer.cloneNode(false);
3322
- r = null;
3323
- appliedEl = true;
3324
- }
3325
- }
3326
-
3327
- if (sel.childNodes.length > 0) {
3328
- el.insertBefore(sel, r);
3329
- appliedEl = true;
3330
- }
3331
- if (!elStyles.length) {
3332
- el.removeAttribute('style');
3333
- }
3334
-
3335
- return appliedEl;
3336
- },
3337
-
3338
- /**
3339
- * @private
3340
- * @this {FormatThis}
3341
- * @description Reset the line break format.
3342
- * @param {"line"|"br"} breakFormat options.get('defaultLineBreakFormat')
3343
- */
3344
- __resetBrLineBreak(breakFormat) {
3345
- this._brLineBreak = breakFormat === 'br';
3346
- },
3347
-
3348
- constructor: Format
3349
- };
3350
-
3351
- /**
3352
- * @private
3353
- * @param {Node} baseNode Node
3354
- */
3355
- function DeleteNestedList(baseNode) {
3356
- const baseParent = baseNode.parentNode;
3357
- let parent = baseParent.parentNode;
3358
- let siblingNode = /** @type {*} */ (baseParent);
3359
- let liSibling, liParent, child, index, c;
3360
-
3361
- while (dom.check.isListCell(parent)) {
3362
- index = dom.query.getPositionIndex(baseNode);
3363
- liSibling = parent.nextElementSibling;
3364
- liParent = parent.parentNode;
3365
- child = siblingNode;
3366
-
3367
- while (child) {
3368
- siblingNode = siblingNode.nextSibling;
3369
- if (dom.check.isList(child)) {
3370
- c = child.childNodes;
3371
- while (c[index]) {
3372
- liParent.insertBefore(c[index], liSibling);
3373
- }
3374
- if (c.length === 0) dom.utils.removeItem(child);
3375
- } else {
3376
- liParent.appendChild(child);
3377
- }
3378
- child = siblingNode;
3379
- }
3380
-
3381
- parent = liParent.parentNode;
3382
- }
3383
-
3384
- if (baseParent.children.length === 0) dom.utils.removeItem(baseParent);
3385
-
3386
- return liParent;
3387
- }
3388
-
3389
- /**
3390
- * @private
3391
- * @param {Array<HTMLElement>} lines - Line elements
3392
- * @param {number} size - Margin size
3393
- * @param {string} dir - Direction
3394
- * @returns
3395
- */
3396
- function SetLineMargin(lines, size, dir) {
3397
- const cells = [];
1043
+ /**
1044
+ * @private
1045
+ * @param {Array<HTMLElement>} lines - Line elements
1046
+ * @param {number} size - Margin size
1047
+ * @param {string} dir - Direction
1048
+ * @returns
1049
+ */
1050
+ function SetLineMargin(lines, size, dir) {
1051
+ const cells = [];
3398
1052
 
3399
1053
  for (let i = 0, len = lines.length, f, margin; i < len; i++) {
3400
1054
  f = lines[i];
@@ -3412,22 +1066,4 @@ function SetLineMargin(lines, size, dir) {
3412
1066
  return cells;
3413
1067
  }
3414
1068
 
3415
- /**
3416
- * @private
3417
- * @description Strip remove node
3418
- * @param {Node} removeNode The remove node
3419
- * @private
3420
- */
3421
- function SN_StripRemoveNode(removeNode) {
3422
- const element = removeNode.parentNode;
3423
- if (!removeNode || removeNode.nodeType === 3 || !element) return;
3424
-
3425
- const children = removeNode.childNodes;
3426
- while (children[0]) {
3427
- element.insertBefore(children[0], removeNode);
3428
- }
3429
-
3430
- element.removeChild(removeNode);
3431
- }
3432
-
3433
1069
  export default Format;