suneditor 3.0.0-beta.26 → 3.0.0-beta.28

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