suneditor 3.1.4 → 3.2.1

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 (217) hide show
  1. package/dist/suneditor-contents.min.css +1 -1
  2. package/dist/suneditor.min.css +28 -1
  3. package/dist/suneditor.min.js +1 -1
  4. package/package.json +2 -1
  5. package/src/assets/design/color.css +12 -0
  6. package/src/assets/design/size.css +9 -2
  7. package/src/assets/icons/defaultIcons.js +96 -47
  8. package/src/assets/suneditor-contents.css +3 -2
  9. package/src/assets/suneditor.css +481 -31
  10. package/src/core/config/contextProvider.js +2 -1
  11. package/src/core/config/eventManager.js +41 -2
  12. package/src/core/config/optionProvider.js +23 -2
  13. package/src/core/editor.js +32 -14
  14. package/src/core/event/actions/index.js +76 -9
  15. package/src/core/event/effects/common.registry.js +2 -1
  16. package/src/core/event/effects/keydown.registry.js +169 -33
  17. package/src/core/event/effects/ruleHelpers.js +20 -6
  18. package/src/core/event/eventOrchestrator.js +162 -30
  19. package/src/core/event/handlers/handler_toolbar.js +4 -2
  20. package/src/core/event/handlers/handler_ww_dragDrop.js +10 -0
  21. package/src/core/event/handlers/handler_ww_input.js +84 -2
  22. package/src/core/event/handlers/handler_ww_key.js +82 -21
  23. package/src/core/event/handlers/handler_ww_mouse.js +45 -8
  24. package/src/core/event/ports.js +27 -13
  25. package/src/core/event/reducers/keydown.reducer.js +33 -5
  26. package/src/core/event/rules/keydown.rule.arrow.js +12 -2
  27. package/src/core/event/rules/keydown.rule.backspace.js +110 -15
  28. package/src/core/event/rules/keydown.rule.delete.js +62 -7
  29. package/src/core/event/rules/keydown.rule.enter.js +62 -31
  30. package/src/core/event/support/defaultLineManager.js +9 -2
  31. package/src/core/event/support/selectionState.js +13 -3
  32. package/src/core/kernel/coreKernel.js +1 -0
  33. package/src/core/logic/dom/char.js +13 -3
  34. package/src/core/logic/dom/format.js +171 -39
  35. package/src/core/logic/dom/html.js +350 -72
  36. package/src/core/logic/dom/inline.js +229 -34
  37. package/src/core/logic/dom/listFormat.js +111 -20
  38. package/src/core/logic/dom/nodeTransform.js +36 -11
  39. package/src/core/logic/dom/offset.js +116 -31
  40. package/src/core/logic/dom/selection.js +147 -29
  41. package/src/core/logic/panel/blockHandle.js +785 -0
  42. package/src/core/logic/panel/blockResolver.js +278 -0
  43. package/src/core/logic/panel/finder.js +16 -6
  44. package/src/core/logic/panel/menu.js +119 -18
  45. package/src/core/logic/panel/toolbar.js +75 -19
  46. package/src/core/logic/panel/viewer.js +76 -20
  47. package/src/core/logic/shell/_commandExecutor.js +44 -9
  48. package/src/core/logic/shell/commandDispatcher.js +46 -4
  49. package/src/core/logic/shell/component.js +102 -25
  50. package/src/core/logic/shell/focusManager.js +13 -3
  51. package/src/core/logic/shell/history.js +21 -6
  52. package/src/core/logic/shell/pluginManager.js +21 -13
  53. package/src/core/logic/shell/shortcuts.js +9 -1
  54. package/src/core/logic/shell/ui.js +139 -37
  55. package/src/core/schema/frameContext.js +34 -3
  56. package/src/core/schema/options.js +46 -4
  57. package/src/core/section/constructor.js +537 -106
  58. package/src/core/section/documentType.js +35 -6
  59. package/src/helper/converter.js +24 -7
  60. package/src/helper/dom/domCheck.js +25 -5
  61. package/src/helper/dom/domQuery.js +2 -1
  62. package/src/helper/dom/domUtils.js +17 -4
  63. package/src/helper/env.js +11 -2
  64. package/src/helper/keyCodeMap.js +6 -2
  65. package/src/helper/markdown.js +24 -5
  66. package/src/helper/msOffice.js +9 -2
  67. package/src/interfaces/plugins.js +1 -1
  68. package/src/langs/ckb.js +0 -1
  69. package/src/langs/cs.js +0 -1
  70. package/src/langs/da.js +0 -1
  71. package/src/langs/de.js +0 -1
  72. package/src/langs/en.js +0 -1
  73. package/src/langs/es.js +0 -1
  74. package/src/langs/fa.js +0 -1
  75. package/src/langs/fr.js +0 -1
  76. package/src/langs/he.js +0 -1
  77. package/src/langs/hu.js +0 -1
  78. package/src/langs/index.js +27 -1
  79. package/src/langs/it.js +0 -1
  80. package/src/langs/ja.js +0 -1
  81. package/src/langs/km.js +0 -1
  82. package/src/langs/ko.js +0 -1
  83. package/src/langs/lv.js +0 -1
  84. package/src/langs/nl.js +0 -1
  85. package/src/langs/pl.js +0 -1
  86. package/src/langs/pt_br.js +0 -1
  87. package/src/langs/ro.js +0 -1
  88. package/src/langs/ru.js +0 -1
  89. package/src/langs/se.js +0 -1
  90. package/src/langs/tr.js +0 -1
  91. package/src/langs/uk.js +0 -1
  92. package/src/langs/ur.js +0 -1
  93. package/src/langs/zh_cn.js +0 -1
  94. package/src/modules/contract/Browser.js +58 -14
  95. package/src/modules/contract/ColorPicker.js +12 -4
  96. package/src/modules/contract/Controller.js +37 -7
  97. package/src/modules/contract/Figure.js +156 -36
  98. package/src/modules/contract/HueSlider.js +27 -4
  99. package/src/modules/contract/Modal.js +50 -10
  100. package/src/modules/manager/FileManager.js +50 -8
  101. package/src/modules/ui/CommandMenu.js +597 -0
  102. package/src/modules/ui/ModalAnchorEditor.js +33 -6
  103. package/src/modules/ui/SelectMenu.js +481 -36
  104. package/src/modules/ui/index.js +1 -0
  105. package/src/plugins/browser/audioGallery.js +2 -1
  106. package/src/plugins/browser/fileBrowser.js +9 -2
  107. package/src/plugins/browser/fileGallery.js +2 -1
  108. package/src/plugins/browser/imageGallery.js +18 -3
  109. package/src/plugins/browser/videoGallery.js +13 -4
  110. package/src/plugins/command/blockquote.js +6 -1
  111. package/src/plugins/command/codeBlock.js +43 -5
  112. package/src/plugins/command/exportPDF.js +20 -4
  113. package/src/plugins/command/fileUpload.js +46 -11
  114. package/src/plugins/command/list_bulleted.js +6 -1
  115. package/src/plugins/command/list_numbered.js +6 -1
  116. package/src/plugins/dropdown/align.js +21 -33
  117. package/src/plugins/dropdown/backgroundColor.js +10 -2
  118. package/src/plugins/dropdown/blockStyle.js +17 -22
  119. package/src/plugins/dropdown/font.js +34 -34
  120. package/src/plugins/dropdown/hr.js +15 -43
  121. package/src/plugins/dropdown/layout.js +10 -27
  122. package/src/plugins/dropdown/lineHeight.js +10 -33
  123. package/src/plugins/dropdown/list.js +19 -25
  124. package/src/plugins/dropdown/paragraphStyle.js +16 -24
  125. package/src/plugins/dropdown/table/index.js +72 -17
  126. package/src/plugins/dropdown/table/render/table.html.js +3 -1
  127. package/src/plugins/dropdown/table/services/table.cell.js +33 -7
  128. package/src/plugins/dropdown/table/services/table.clipboard.js +5 -1
  129. package/src/plugins/dropdown/table/services/table.grid.js +40 -9
  130. package/src/plugins/dropdown/table/services/table.resize.js +55 -11
  131. package/src/plugins/dropdown/table/services/table.selection.js +27 -7
  132. package/src/plugins/dropdown/table/services/table.style.js +120 -20
  133. package/src/plugins/dropdown/template.js +13 -29
  134. package/src/plugins/dropdown/textStyle.js +22 -35
  135. package/src/plugins/field/autocomplete.js +8 -4
  136. package/src/plugins/field/slashCommand.js +297 -0
  137. package/src/plugins/index.js +3 -0
  138. package/src/plugins/input/fontSize.js +37 -8
  139. package/src/plugins/input/pageNavigator.js +7 -1
  140. package/src/plugins/modal/audio.js +71 -17
  141. package/src/plugins/modal/drawing.js +41 -11
  142. package/src/plugins/modal/embed.js +92 -22
  143. package/src/plugins/modal/image/index.js +114 -28
  144. package/src/plugins/modal/image/services/image.size.js +9 -2
  145. package/src/plugins/modal/image/services/image.upload.js +38 -4
  146. package/src/plugins/modal/link.js +9 -2
  147. package/src/plugins/modal/math.js +10 -2
  148. package/src/plugins/modal/video/index.js +112 -26
  149. package/src/plugins/modal/video/render/video.html.js +4 -1
  150. package/src/plugins/modal/video/services/video.size.js +15 -4
  151. package/src/plugins/modal/video/services/video.upload.js +10 -2
  152. package/src/plugins/popup/anchor.js +14 -2
  153. package/src/suneditor.js +8 -2
  154. package/src/themes/cobalt.css +12 -0
  155. package/src/themes/cream.css +12 -0
  156. package/src/themes/dark.css +12 -0
  157. package/src/themes/midnight.css +12 -0
  158. package/src/typedef.js +32 -21
  159. package/types/assets/icons/defaultIcons.d.ts +1 -0
  160. package/types/core/config/contextProvider.d.ts +7 -1
  161. package/types/core/config/eventManager.d.ts +37 -4
  162. package/types/core/config/optionProvider.d.ts +5 -1
  163. package/types/core/event/actions/index.d.ts +44 -6
  164. package/types/core/event/effects/keydown.registry.d.ts +37 -7
  165. package/types/core/event/effects/ruleHelpers.d.ts +6 -1
  166. package/types/core/event/eventOrchestrator.d.ts +13 -3
  167. package/types/core/event/handlers/handler_ww_clipboard.d.ts +15 -3
  168. package/types/core/event/handlers/handler_ww_dragDrop.d.ts +14 -2
  169. package/types/core/event/handlers/handler_ww_input.d.ts +10 -2
  170. package/types/core/event/handlers/handler_ww_key.d.ts +10 -2
  171. package/types/core/event/handlers/handler_ww_mouse.d.ts +25 -5
  172. package/types/core/event/ports.d.ts +11 -4
  173. package/types/core/event/reducers/keydown.reducer.d.ts +12 -3
  174. package/types/core/event/rules/keydown.rule.backspace.d.ts +6 -1
  175. package/types/core/kernel/store.d.ts +4 -1
  176. package/types/core/logic/dom/format.d.ts +32 -14
  177. package/types/core/logic/dom/html.d.ts +11 -3
  178. package/types/core/logic/dom/nodeTransform.d.ts +6 -6
  179. package/types/core/logic/panel/blockHandle.d.ts +64 -0
  180. package/types/core/logic/panel/blockResolver.d.ts +50 -0
  181. package/types/core/logic/panel/menu.d.ts +72 -3
  182. package/types/core/logic/shell/commandDispatcher.d.ts +8 -0
  183. package/types/core/logic/shell/component.d.ts +5 -3
  184. package/types/core/logic/shell/shortcuts.d.ts +10 -1
  185. package/types/core/logic/shell/ui.d.ts +7 -1
  186. package/types/core/schema/context.d.ts +7 -1
  187. package/types/core/schema/frameContext.d.ts +36 -4
  188. package/types/core/schema/options.d.ts +83 -2
  189. package/types/core/section/constructor.d.ts +38 -1
  190. package/types/events.d.ts +208 -32
  191. package/types/helper/converter.d.ts +4 -2
  192. package/types/helper/dom/domCheck.d.ts +12 -2
  193. package/types/helper/dom/domQuery.d.ts +35 -7
  194. package/types/helper/dom/domUtils.d.ts +4 -1
  195. package/types/interfaces/plugins.d.ts +2 -2
  196. package/types/langs/_Lang.d.ts +0 -1
  197. package/types/modules/contract/ColorPicker.d.ts +2 -2
  198. package/types/modules/contract/Controller.d.ts +7 -0
  199. package/types/modules/contract/Figure.d.ts +31 -4
  200. package/types/modules/ui/CommandMenu.d.ts +262 -0
  201. package/types/modules/ui/SelectMenu.d.ts +56 -3
  202. package/types/modules/ui/index.d.ts +1 -0
  203. package/types/plugins/browser/audioGallery.d.ts +1 -1
  204. package/types/plugins/browser/fileBrowser.d.ts +1 -1
  205. package/types/plugins/browser/fileGallery.d.ts +1 -1
  206. package/types/plugins/browser/imageGallery.d.ts +1 -1
  207. package/types/plugins/browser/videoGallery.d.ts +1 -1
  208. package/types/plugins/dropdown/align.d.ts +1 -1
  209. package/types/plugins/dropdown/blockStyle.d.ts +3 -1
  210. package/types/plugins/dropdown/table/index.d.ts +4 -1
  211. package/types/plugins/dropdown/table/services/table.clipboard.d.ts +5 -1
  212. package/types/plugins/dropdown/table/services/table.grid.d.ts +10 -2
  213. package/types/plugins/field/autocomplete.d.ts +0 -1
  214. package/types/plugins/field/slashCommand.d.ts +165 -0
  215. package/types/plugins/index.d.ts +3 -0
  216. package/types/plugins/modal/link.d.ts +4 -1
  217. package/types/typedef.d.ts +48 -23
@@ -89,7 +89,10 @@ class HTML {
89
89
  // extra tags
90
90
  const allowedExtraTags = options.get('_allowedExtraTag');
91
91
  const disallowedExtraTags = options.get('_disallowedExtraTag');
92
- this.#disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
92
+ this.#disallowedTagsRegExp = new RegExp(
93
+ `<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`,
94
+ 'gi',
95
+ );
93
96
  this.#disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
94
97
  this.#allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
95
98
 
@@ -104,18 +107,29 @@ class HTML {
104
107
  for (let i = 0; i < allowStyleNodes.length; i++) {
105
108
  disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
106
109
  }
107
- this.#disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
110
+ this.#disallowedStyleNodesRegExp =
111
+ disallowStyleNodes.length === 0
112
+ ? null
113
+ : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
108
114
 
109
115
  // whitelist
110
116
  // tags
111
117
  const defaultAttr = options.get('__defaultAttributeWhitelist');
112
- this.#allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
118
+ this.#allowHTMLComment =
119
+ options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
113
120
  // html check
114
- this.#htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
121
+ this.#htmlCheckWhitelistRegExp = new RegExp(
122
+ '^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$',
123
+ 'i',
124
+ );
115
125
  this.#htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
116
126
  // elements
117
- this.#elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
118
- this.#elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
127
+ this.#elementWhitelistRegExp = converter.createElementWhitelist(
128
+ GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''),
129
+ );
130
+ this.#elementBlacklistRegExp = converter.createElementBlacklist(
131
+ options.get('elementBlacklist').replace('|//', '|<!--|-->'),
132
+ );
119
133
  // attributes
120
134
  const regEndStr = '\\s*=\\s*(")[^"]*\\1';
121
135
  const _wAttr = options.get('attributeWhitelist');
@@ -134,7 +148,16 @@ class HTML {
134
148
  }
135
149
  }
136
150
 
137
- this.#attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
151
+ this.#attributeWhitelistRegExp = new RegExp(
152
+ '\\s(?:' +
153
+ (allAttr || defaultAttr) +
154
+ '|' +
155
+ REQUIRED_DATA_ATTRS +
156
+ (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') +
157
+ ')' +
158
+ regEndStr,
159
+ 'ig',
160
+ );
138
161
  this.#attributeWhitelist = tagsAttr;
139
162
 
140
163
  // blacklist
@@ -166,7 +189,7 @@ class HTML {
166
189
  * @param {Object} params - Filtering parameters.
167
190
  * @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by `'|'`. (e.g. `"div|p|span"`).
168
191
  * @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by `'|'`. (e.g. `"script|iframe"`).
169
- * @param {(node: Node) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
192
+ * @param {(node: *) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
170
193
  * - Return `null` to remove the node.
171
194
  * - Return a `Node` to replace the current node.
172
195
  * - Return a `string` to replace the node's `outerHTML`.
@@ -244,14 +267,18 @@ class HTML {
244
267
  * });
245
268
  */
246
269
  clean(html, { forceFormat, whitelist, blacklist, _freeCodeViewMode } = {}) {
247
- const { tagFilter, formatFilter, classFilter, textStyleTagFilter, attrFilter, styleFilter } = this.#options.get('strictMode');
270
+ const { tagFilter, formatFilter, classFilter, textStyleTagFilter, attrFilter, styleFilter } =
271
+ this.#options.get('strictMode');
248
272
  let cleanData = '';
249
273
 
250
274
  html = this.compress(html);
251
275
 
252
276
  if (tagFilter) {
253
277
  html = html.replace(this.#disallowedTagsRegExp, '');
254
- html = this.#deleteDisallowedTags(html, this.#elementWhitelistRegExp, this.#elementBlacklistRegExp).replace(/<br\/?>$/i, '');
278
+ html = this.#deleteDisallowedTags(html, this.#elementWhitelistRegExp, this.#elementBlacklistRegExp).replace(
279
+ /<br\/?>$/i,
280
+ '',
281
+ );
255
282
  }
256
283
 
257
284
  if (this.#autoStyleify) {
@@ -269,7 +296,15 @@ class HTML {
269
296
 
270
297
  if (tagFilter) {
271
298
  try {
272
- this.#consistencyCheckOfHTML(domParser, this.#htmlCheckWhitelistRegExp, this.#htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
299
+ this.#consistencyCheckOfHTML(
300
+ domParser,
301
+ this.#htmlCheckWhitelistRegExp,
302
+ this.#htmlCheckBlacklistRegExp,
303
+ tagFilter,
304
+ formatFilter,
305
+ classFilter,
306
+ _freeCodeViewMode,
307
+ );
273
308
  } catch (error) {
274
309
  console.warn('[SUNEDITOR.html.clean.fail]', error.message);
275
310
  }
@@ -314,8 +349,16 @@ class HTML {
314
349
 
315
350
  // whitelist, blacklist
316
351
  if (tagFilter) {
317
- if (whitelist) cleanData = cleanData.replace(typeof whitelist === 'string' ? converter.createElementWhitelist(whitelist) : whitelist, '');
318
- if (blacklist) cleanData = cleanData.replace(typeof blacklist === 'string' ? converter.createElementBlacklist(blacklist) : blacklist, '');
352
+ if (whitelist)
353
+ cleanData = cleanData.replace(
354
+ typeof whitelist === 'string' ? converter.createElementWhitelist(whitelist) : whitelist,
355
+ '',
356
+ );
357
+ if (blacklist)
358
+ cleanData = cleanData.replace(
359
+ typeof blacklist === 'string' ? converter.createElementBlacklist(blacklist) : blacklist,
360
+ '',
361
+ );
319
362
  }
320
363
 
321
364
  if (textStyleTagFilter) {
@@ -345,7 +388,8 @@ class HTML {
345
388
  * editor.html.insert('<div class="custom">Content</div>', { skipCleaning: true });
346
389
  */
347
390
  insert(html, { selectInserted, skipCharCount, skipCleaning } = {}) {
348
- if (!this.#frameContext.get('wysiwyg').contains(this.#$.selection.get().focusNode)) this.#$.focusManager.focus();
391
+ if (!this.#frameContext.get('wysiwyg').contains(this.#$.selection.get().focusNode))
392
+ this.#$.focusManager.focus();
349
393
 
350
394
  this.remove();
351
395
  this.#$.focusManager.focus();
@@ -364,7 +408,8 @@ class HTML {
364
408
  const domTree = domParser.childNodes;
365
409
 
366
410
  if (!skipCharCount) {
367
- const type = this.#frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
411
+ const type =
412
+ this.#frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
368
413
  let checkHTML = '';
369
414
  for (let i = 0, len = domTree.length; i < len; i++) {
370
415
  checkHTML += domTree[i][type];
@@ -462,13 +507,18 @@ class HTML {
462
507
  let range = null;
463
508
 
464
509
  if (afterNode) {
465
- const afterNewLine = this.#$.format.isLine(afterNode) || this.#$.format.isBlock(afterNode) || this.#$.component.is(afterNode) ? this.#$.format.addLine(afterNode, null) : afterNode;
510
+ const afterNewLine =
511
+ this.#$.format.isLine(afterNode) || this.#$.format.isBlock(afterNode) || this.#$.component.is(afterNode)
512
+ ? this.#$.format.addLine(afterNode, null)
513
+ : afterNode;
466
514
  range = this.#$.selection.setRange(afterNewLine, 1, afterNewLine, 1);
467
515
  } else {
468
516
  range = this.#$.selection.getRange();
469
517
  }
470
518
 
471
- let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.#$.format.getLine(this.#$.selection.getNode(), null);
519
+ let line = dom.check.isListCell(range.commonAncestorContainer)
520
+ ? range.commonAncestorContainer
521
+ : this.#$.format.getLine(this.#$.selection.getNode(), null);
472
522
  let insertListCell = dom.check.isListCell(line) && (dom.check.isListCell(oNode) || dom.check.isList(oNode));
473
523
 
474
524
  let parentNode,
@@ -476,7 +526,8 @@ class HTML {
476
526
  tempAfterNode,
477
527
  tempParentNode = null;
478
528
  const freeFormat = this.#$.format.isBrLine(line);
479
- const isFormats = this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || this.#$.component.isBasic(oNode);
529
+ const isFormats =
530
+ this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || this.#$.component.isBasic(oNode);
480
531
 
481
532
  if (insertListCell) {
482
533
  tempAfterNode = afterNode || dom.check.isList(oNode) ? line.lastChild : line.nextElementSibling;
@@ -511,7 +562,11 @@ class HTML {
511
562
  const depthFormat = dom.query.getParentElement(container, (current) => {
512
563
  return this.#$.format.isBlock(current) || dom.check.isListCell(current);
513
564
  });
514
- afterNode = this.#$.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
565
+ afterNode = this.#$.nodeTransform.split(
566
+ container,
567
+ r.offset,
568
+ !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1,
569
+ );
515
570
  if (!afterNode) {
516
571
  if (!dom.check.isListCell(line)) {
517
572
  tempAfterNode = afterNode = line;
@@ -522,7 +577,11 @@ class HTML {
522
577
  let newCell = null;
523
578
  if (!isEdge) {
524
579
  newCell = line.cloneNode(false);
525
- newCell.appendChild(afterNode.textContent.trim() ? afterNode : dom.utils.createTextNode(unicode.zeroWidthSpace));
580
+ newCell.appendChild(
581
+ afterNode.textContent.trim()
582
+ ? afterNode
583
+ : dom.utils.createTextNode(unicode.zeroWidthSpace),
584
+ );
526
585
  }
527
586
  if (subList) {
528
587
  if (!newCell) {
@@ -542,13 +601,22 @@ class HTML {
542
601
  }
543
602
  }
544
603
 
545
- range = !afterNode && !isFormats ? this.#$.selection.getRangeAndAddLine(this.#$.selection.getRange(), null) : this.#$.selection.getRange();
604
+ range =
605
+ !afterNode && !isFormats
606
+ ? this.#$.selection.getRangeAndAddLine(this.#$.selection.getRange(), null)
607
+ : this.#$.selection.getRange();
546
608
  const commonCon = range.commonAncestorContainer;
547
609
  const startOff = range.startOffset;
548
610
  const endOff = range.endOffset;
549
611
  const formatRange = range.startContainer === commonCon && this.#$.format.isLine(commonCon);
550
- const startCon = formatRange ? commonCon.childNodes[startOff] || commonCon.childNodes[0] || range.startContainer : range.startContainer;
551
- const endCon = formatRange ? commonCon.childNodes[endOff] || commonCon.childNodes[commonCon.childNodes.length - 1] || range.endContainer : range.endContainer;
612
+ const startCon = formatRange
613
+ ? commonCon.childNodes[startOff] || commonCon.childNodes[0] || range.startContainer
614
+ : range.startContainer;
615
+ const endCon = formatRange
616
+ ? commonCon.childNodes[endOff] ||
617
+ commonCon.childNodes[commonCon.childNodes.length - 1] ||
618
+ range.endContainer
619
+ : range.endContainer;
552
620
 
553
621
  if (!insertListCell) {
554
622
  if (!afterNode) {
@@ -560,18 +628,25 @@ class HTML {
560
628
  /** No Select range node */
561
629
  if (range.collapsed) {
562
630
  if (commonCon.nodeType === 3) {
563
- if (commonCon.textContent.length > endOff) afterNode = /** @type {Text} */ (commonCon).splitText(endOff);
631
+ if (commonCon.textContent.length > endOff)
632
+ afterNode = /** @type {Text} */ (commonCon).splitText(endOff);
564
633
  else afterNode = commonCon.nextSibling;
565
634
  } else {
566
635
  if (!dom.check.isBreak(parentNode)) {
567
636
  const c = parentNode.childNodes[startOff];
568
- const focusNode = c?.nodeType === 3 && dom.check.isZeroWidth(c) && dom.check.isBreak(c.nextSibling) ? c.nextSibling : c;
637
+ const focusNode =
638
+ c?.nodeType === 3 && dom.check.isZeroWidth(c) && dom.check.isBreak(c.nextSibling)
639
+ ? c.nextSibling
640
+ : c;
569
641
  if (focusNode) {
570
642
  if (!focusNode.nextSibling && dom.check.isBreak(focusNode)) {
571
643
  parentNode.removeChild(focusNode);
572
644
  afterNode = null;
573
645
  } else {
574
- afterNode = dom.check.isBreak(focusNode) && !dom.check.isBreak(oNode) ? focusNode : focusNode.nextSibling;
646
+ afterNode =
647
+ dom.check.isBreak(focusNode) && !dom.check.isBreak(oNode)
648
+ ? focusNode
649
+ : focusNode.nextSibling;
575
650
  }
576
651
  } else {
577
652
  afterNode = null;
@@ -590,7 +665,8 @@ class HTML {
590
665
  else afterNode = /** @type {Text} */ (endCon).splitText(endOff);
591
666
 
592
667
  let removeNode = startCon;
593
- if (!dom.check.isEdgePoint(startCon, startOff)) removeNode = /** @type {Text} */ (startCon).splitText(startOff);
668
+ if (!dom.check.isEdgePoint(startCon, startOff))
669
+ removeNode = /** @type {Text} */ (startCon).splitText(startOff);
594
670
 
595
671
  parentNode.removeChild(removeNode);
596
672
  if (parentNode.childNodes.length === 0 && isFormats) {
@@ -605,7 +681,12 @@ class HTML {
605
681
  if (this.#$.format.isLine(container)) {
606
682
  container.innerHTML = '<br>';
607
683
  } else if (this.#$.format.isBlock(container)) {
608
- container.innerHTML = '<' + this.#options.get('defaultLine') + '><br></' + this.#options.get('defaultLine') + '>';
684
+ container.innerHTML =
685
+ '<' +
686
+ this.#options.get('defaultLine') +
687
+ '><br></' +
688
+ this.#options.get('defaultLine') +
689
+ '>';
609
690
  }
610
691
  }
611
692
 
@@ -626,10 +707,16 @@ class HTML {
626
707
  afterNode = null;
627
708
  }
628
709
  } else if (dom.check.isWysiwygFrame(container) && !this.#$.format.isLine(oNode)) {
629
- parentNode = container.appendChild(dom.utils.createElement(this.#options.get('defaultLine')));
710
+ parentNode = container.appendChild(
711
+ dom.utils.createElement(this.#options.get('defaultLine')),
712
+ );
630
713
  afterNode = null;
631
714
  } else {
632
- afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
715
+ afterNode = isFormats
716
+ ? endCon
717
+ : container === prevContainer
718
+ ? container.nextSibling
719
+ : container;
633
720
  parentNode = !afterNode || !afterNode.parentNode ? commonCon : afterNode.parentNode;
634
721
  }
635
722
 
@@ -655,13 +742,22 @@ class HTML {
655
742
  afterNode = null;
656
743
  }
657
744
 
658
- if (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.#$.component.isBasic(oNode))) {
745
+ if (
746
+ this.#$.format.isLine(oNode) ||
747
+ this.#$.format.isBlock(oNode) ||
748
+ (!dom.check.isListCell(parentNode) && this.#$.component.isBasic(oNode))
749
+ ) {
659
750
  const oldParent = parentNode;
660
751
  if (dom.check.isListCell(afterNode)) {
661
752
  parentNode = afterNode.previousElementSibling || afterNode;
662
753
  } else if (!originAfter && !afterNode) {
663
754
  const r = this.remove();
664
- const container = r.container.nodeType === 3 ? (dom.check.isListCell(this.#$.format.getLine(r.container, null)) ? r.container : this.#$.format.getLine(r.container, null) || r.container.parentNode) : r.container;
755
+ const container =
756
+ r.container.nodeType === 3
757
+ ? dom.check.isListCell(this.#$.format.getLine(r.container, null))
758
+ ? r.container
759
+ : this.#$.format.getLine(r.container, null) || r.container.parentNode
760
+ : r.container;
665
761
  const rangeCon = dom.check.isWysiwygFrame(container) || this.#$.format.isBlock(container);
666
762
  parentNode = rangeCon ? container : container.parentNode;
667
763
  afterNode = rangeCon ? null : container.nextSibling;
@@ -670,7 +766,13 @@ class HTML {
670
766
  if (oldParent.childNodes.length === 0 && parentNode !== oldParent) dom.utils.removeItem(oldParent);
671
767
  }
672
768
 
673
- if (isFormats && !freeFormat && !this.#$.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
769
+ if (
770
+ isFormats &&
771
+ !freeFormat &&
772
+ !this.#$.format.isBlock(parentNode) &&
773
+ !dom.check.isListCell(parentNode) &&
774
+ !dom.check.isWysiwygFrame(parentNode)
775
+ ) {
674
776
  afterNode = /** @type {HTMLElement} */ (parentNode).nextElementSibling;
675
777
  parentNode = parentNode.parentNode;
676
778
  }
@@ -762,7 +864,11 @@ class HTML {
762
864
  }
763
865
  }
764
866
 
765
- if (freeFormat && !dom.check.isList(oNode) && (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode))) {
867
+ if (
868
+ freeFormat &&
869
+ !dom.check.isList(oNode) &&
870
+ (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode))
871
+ ) {
766
872
  oNode = this.#setIntoFreeFormat(oNode);
767
873
  }
768
874
 
@@ -771,7 +877,11 @@ class HTML {
771
877
  if (oNode.nodeType === 3) {
772
878
  offset = oNode.textContent.length;
773
879
  this.#$.selection.setRange(oNode, offset, oNode, offset);
774
- } else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.#$.format.isLine(parentNode)) {
880
+ } else if (
881
+ !dom.check.isBreak(oNode) &&
882
+ !dom.check.isListCell(oNode) &&
883
+ this.#$.format.isLine(parentNode)
884
+ ) {
775
885
  let zeroWidth = null;
776
886
  if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
777
887
  zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
@@ -831,7 +941,12 @@ class HTML {
831
941
  let endCon = range.endContainer;
832
942
  let startOff = range.startOffset;
833
943
  let endOff = range.endOffset;
834
- const commonCon = /** @type {HTMLElement} */ (range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer);
944
+ const commonCon = /** @type {HTMLElement} */ (
945
+ range.commonAncestorContainer.nodeType === 3 &&
946
+ range.commonAncestorContainer.parentNode === startCon.parentNode
947
+ ? startCon.parentNode
948
+ : range.commonAncestorContainer
949
+ );
835
950
 
836
951
  if (dom.check.isWysiwygFrame(startCon) && dom.check.isWysiwygFrame(endCon)) {
837
952
  this.set('');
@@ -852,7 +967,8 @@ class HTML {
852
967
  const next = compContainer.nextSibling || compContainer.previousSibling;
853
968
  const nextOffset = next === compContainer.previousSibling ? next?.textContent?.length || 1 : 0;
854
969
  const parentNext = parent.nextElementSibling || parent.previousElementSibling;
855
- const parentNextOffset = parentNext === parent.previousElementSibling ? parentNext?.textContent?.length || 1 : 0;
970
+ const parentNextOffset =
971
+ parentNext === parent.previousElementSibling ? parentNext?.textContent?.length || 1 : 0;
856
972
 
857
973
  dom.utils.removeItem(compContainer);
858
974
 
@@ -879,7 +995,10 @@ class HTML {
879
995
  };
880
996
  }
881
997
  } else {
882
- if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
998
+ if (
999
+ (commonCon.nodeType === 1 && startOff === 0 && endOff === 1) ||
1000
+ (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)
1001
+ ) {
883
1002
  const nextEl = dom.query.getNextDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
884
1003
  const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
885
1004
  const line = this.#$.format.getLine(commonCon);
@@ -938,7 +1057,11 @@ class HTML {
938
1057
 
939
1058
  if (childNodes.length > 0 && startIndex > -1 && endIndex > -1) {
940
1059
  for (let i = startIndex + 1, startNode = startCon; i >= 0; i--) {
941
- if (childNodes[i] === startNode.parentNode && childNodes[i].firstChild === startNode && startOff === 0) {
1060
+ if (
1061
+ childNodes[i] === startNode.parentNode &&
1062
+ childNodes[i].firstChild === startNode &&
1063
+ startOff === 0
1064
+ ) {
942
1065
  startIndex = i;
943
1066
  startNode = startNode.parentNode;
944
1067
  }
@@ -953,7 +1076,13 @@ class HTML {
953
1076
  }
954
1077
  } else {
955
1078
  if (childNodes.length === 0) {
956
- if (this.#$.format.isLine(commonCon) || this.#$.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
1079
+ if (
1080
+ this.#$.format.isLine(commonCon) ||
1081
+ this.#$.format.isBlock(commonCon) ||
1082
+ dom.check.isWysiwygFrame(commonCon) ||
1083
+ dom.check.isBreak(commonCon) ||
1084
+ dom.check.isMedia(commonCon)
1085
+ ) {
957
1086
  return {
958
1087
  container: commonCon,
959
1088
  offset: 0,
@@ -1002,7 +1131,9 @@ class HTML {
1002
1131
  const sc = /** @type {Text} */ (startCon);
1003
1132
  const ec = /** @type {Text} */ (endCon);
1004
1133
  if (item === endCon) {
1005
- beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff) + ec.substringData(endOff, ec.length - endOff));
1134
+ beforeNode = dom.utils.createTextNode(
1135
+ sc.substringData(0, startOff) + ec.substringData(endOff, ec.length - endOff),
1136
+ );
1006
1137
  offset = startOff;
1007
1138
  } else {
1008
1139
  beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff));
@@ -1045,7 +1176,12 @@ class HTML {
1045
1176
  container = endUl.previousSibling;
1046
1177
  offset = container.textContent.length;
1047
1178
  } else {
1048
- container = endCon && endCon.parentNode ? endCon : startCon && startCon.parentNode ? startCon : range.endContainer || range.startContainer;
1179
+ container =
1180
+ endCon && endCon.parentNode
1181
+ ? endCon
1182
+ : startCon && startCon.parentNode
1183
+ ? startCon
1184
+ : range.endContainer || range.startContainer;
1049
1185
  if (isStartEdge || isEndEdge) {
1050
1186
  if (isEndEdge) {
1051
1187
  if (container.nodeType === 1 && container.childNodes.length === 0) {
@@ -1075,7 +1211,10 @@ class HTML {
1075
1211
  if (rc) container = rc.sc || rc.ec || this.#frameContext.get('wysiwyg');
1076
1212
  }
1077
1213
 
1078
- if (!container || (container.nodeType === 1 && !this.#$.format.isLine(container) && !dom.check.isBreak(container))) {
1214
+ if (
1215
+ !container ||
1216
+ (container.nodeType === 1 && !this.#$.format.isLine(container) && !dom.check.isBreak(container))
1217
+ ) {
1079
1218
  container = nextFocusNodes?.sc || nextFocusNodes?.ec;
1080
1219
  offset = container?.nodeType === 3 ? container.textContent.length : 1;
1081
1220
  }
@@ -1112,7 +1251,18 @@ class HTML {
1112
1251
  for (let i = 0, len = rootKey.length, r; i < len; i++) {
1113
1252
  this.#$.facade.changeFrameContext(rootKey[i]);
1114
1253
 
1115
- const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(this.#frameContext.get('wysiwyg'), true));
1254
+ const renderHTML = dom.utils.createElement(
1255
+ 'DIV',
1256
+ null,
1257
+ this._convertToCode(this.#frameContext.get('wysiwyg'), true),
1258
+ );
1259
+
1260
+ const phLines = renderHTML.querySelectorAll('.se-placeholder-line');
1261
+ for (let j = 0, jlen = phLines.length; j < jlen; j++) {
1262
+ phLines[j].classList.remove('se-placeholder-line');
1263
+ phLines[j].removeAttribute('data-se-placeholder-line');
1264
+ if (!phLines[j].getAttribute('class')) phLines[j].removeAttribute('class');
1265
+ }
1116
1266
 
1117
1267
  const isTableCell = dom.check.isTableCell;
1118
1268
  const isEmptyLine = dom.check.isEmptyLine;
@@ -1147,13 +1297,17 @@ class HTML {
1147
1297
  const content = renderHTML.innerHTML;
1148
1298
  if (this.#frameOptions.get('iframe_fullPage')) {
1149
1299
  if (includeFullPage) {
1150
- const attrs = dom.utils.getAttributesToString(this.#frameContext.get('_wd').body, ['contenteditable']);
1300
+ const attrs = dom.utils.getAttributesToString(this.#frameContext.get('_wd').body, [
1301
+ 'contenteditable',
1302
+ ]);
1151
1303
  r = `<!DOCTYPE html><html>${this.#frameContext.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
1152
1304
  } else {
1153
1305
  r = content;
1154
1306
  }
1155
1307
  } else {
1156
- r = withFrame ? `<div class="${this.#options.get('_editableClass') + '' + (this.#options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
1308
+ r = withFrame
1309
+ ? `<div class="${this.#options.get('_editableClass') + '' + (this.#options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>`
1310
+ : renderHTML.innerHTML;
1157
1311
  }
1158
1312
 
1159
1313
  resultValue[rootKey[i]] = r;
@@ -1175,7 +1329,10 @@ class HTML {
1175
1329
  set(html, { rootKey } = {}) {
1176
1330
  this.#$.ui.offCurrentController();
1177
1331
  this.#$.selection.removeRange();
1178
- const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
1332
+ const convertValue =
1333
+ html === null || html === undefined
1334
+ ? ''
1335
+ : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
1179
1336
 
1180
1337
  if (!rootKey) rootKey = [this.#store.get('rootKey')];
1181
1338
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
@@ -1225,7 +1382,9 @@ class HTML {
1225
1382
  this.#$.history.push(false, rootKey[i]);
1226
1383
  this.#$.selection.scrollTo(children.at(-1));
1227
1384
  } else {
1228
- this.#$.viewer._setCodeView(this.#$.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false));
1385
+ this.#$.viewer._setCodeView(
1386
+ this.#$.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false),
1387
+ );
1229
1388
  }
1230
1389
  }
1231
1390
  }
@@ -1268,10 +1427,15 @@ class HTML {
1268
1427
  */
1269
1428
  async copy(content) {
1270
1429
  try {
1271
- if (typeof content !== 'string' && !dom.check.isElement(content) && !dom.check.isText(content)) return false;
1430
+ if (typeof content !== 'string' && !dom.check.isElement(content) && !dom.check.isText(content))
1431
+ return false;
1272
1432
 
1273
1433
  if ((await clipboard.write(content)) === false) {
1274
- this.#$.ui.showToast(this.#$.lang.message_copy_fail, this.#options.get('toastMessageTime').copy, 'error');
1434
+ this.#$.ui.showToast(
1435
+ this.#$.lang.message_copy_fail,
1436
+ this.#options.get('toastMessageTime').copy,
1437
+ 'error',
1438
+ );
1275
1439
  return false;
1276
1440
  }
1277
1441
  this.#$.ui.showToast(this.#$.lang.message_copy_success, this.#options.get('toastMessageTime').copy);
@@ -1297,8 +1461,14 @@ class HTML {
1297
1461
 
1298
1462
  for (let i = 0; i < rootKey.length; i++) {
1299
1463
  this.#$.facade.changeFrameContext(rootKey[i]);
1300
- if (ctx.head) this.#frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.#disallowedTagsRegExp, '');
1301
- if (ctx.body) this.#frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
1464
+ if (ctx.head)
1465
+ this.#frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.#disallowedTagsRegExp, '');
1466
+ if (ctx.body)
1467
+ this.#frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, {
1468
+ forceFormat: true,
1469
+ whitelist: null,
1470
+ blacklist: null,
1471
+ });
1302
1472
  this.#$.pluginManager.resetFileInfo();
1303
1473
  }
1304
1474
  }
@@ -1322,7 +1492,10 @@ class HTML {
1322
1492
  _convertToCode(html, comp) {
1323
1493
  let returnHTML = '';
1324
1494
  const wRegExp = RegExp;
1325
- const brReg = new wRegExp('^(BLOCKQUOTE|PRE|TABLE|THEAD|TBODY|TR|TH|TD|OL|UL|IMG|IFRAME|VIDEO|AUDIO|FIGURE|FIGCAPTION|HR|BR|CANVAS|SELECT)$', 'i');
1495
+ const brReg = new wRegExp(
1496
+ '^(BLOCKQUOTE|PRE|TABLE|THEAD|TBODY|TR|TH|TD|OL|UL|IMG|IFRAME|VIDEO|AUDIO|FIGURE|FIGCAPTION|HR|BR|CANVAS|SELECT)$',
1497
+ 'i',
1498
+ );
1326
1499
  const wDoc = typeof html === 'string' ? _d.createRange().createContextualFragment(html) : html;
1327
1500
  const isFormat = (current) => {
1328
1501
  return this.#$.format.isLine(current) || this.#$.component.is(current);
@@ -1350,11 +1523,20 @@ class HTML {
1350
1523
  continue;
1351
1524
  }
1352
1525
  if (node.nodeType === 3) {
1353
- if (!dom.check.isList(node.parentElement)) returnHTML += converter.htmlToEntity(/^\n+$/.test(/** @type {Text} */ (node).data) ? '' : /** @type {Text} */ (node).data);
1526
+ if (!dom.check.isList(node.parentElement))
1527
+ returnHTML += converter.htmlToEntity(
1528
+ /^\n+$/.test(/** @type {Text} */ (node).data) ? '' : /** @type {Text} */ (node).data,
1529
+ );
1354
1530
  continue;
1355
1531
  }
1356
1532
  if (node.childNodes.length === 0) {
1357
- returnHTML += (/^HR$/i.test(node.nodeName) ? brChar : '') + (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName) ? '' : elementIndent) + /** @type {HTMLElement} */ (node).outerHTML + br;
1533
+ returnHTML +=
1534
+ (/^HR$/i.test(node.nodeName) ? brChar : '') +
1535
+ (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName)
1536
+ ? ''
1537
+ : elementIndent) +
1538
+ /** @type {HTMLElement} */ (node).outerHTML +
1539
+ br;
1358
1540
  continue;
1359
1541
  }
1360
1542
 
@@ -1363,9 +1545,18 @@ class HTML {
1363
1545
  } else {
1364
1546
  tag = node.nodeName.toLowerCase();
1365
1547
  tagIndent = elementIndent || nodeRegTest ? indent : '';
1366
- returnHTML += (lineBR || (elementRegTest ? '' : br)) + tagIndent + /** @type {HTMLElement} */ (node).outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] + br;
1548
+ returnHTML +=
1549
+ (lineBR || (elementRegTest ? '' : br)) +
1550
+ tagIndent +
1551
+ /** @type {HTMLElement} */ (node).outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] +
1552
+ br;
1367
1553
  recursionFunc(node, indent + indentSize + '');
1368
- returnHTML += (/\n$/.test(returnHTML) ? tagIndent : '') + '</' + tag + '>' + (lineBR || br || elementRegTest ? brChar : /^(TH|TD)$/i.test(node.nodeName) ? brChar : '');
1554
+ returnHTML +=
1555
+ (/\n$/.test(returnHTML) ? tagIndent : '') +
1556
+ '</' +
1557
+ tag +
1558
+ '>' +
1559
+ (lineBR || br || elementRegTest ? brChar : /^(TH|TD)$/i.test(node.nodeName) ? brChar : '');
1369
1560
  }
1370
1561
  }
1371
1562
  })(wDoc, '');
@@ -1443,7 +1634,10 @@ class HTML {
1443
1634
  dom.query.getListChildNodes(
1444
1635
  node,
1445
1636
  (current) => {
1446
- return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1637
+ return (
1638
+ dom.check.isSpanWithoutAttr(current) &&
1639
+ !dom.query.getParentElement(current, dom.check.isExcludeFormat)
1640
+ );
1447
1641
  },
1448
1642
  null,
1449
1643
  ) || [];
@@ -1465,7 +1659,15 @@ class HTML {
1465
1659
  return dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML;
1466
1660
  } else {
1467
1661
  const n = /** @type {HTMLElement} */ (node);
1468
- return '<' + defaultLine + '>' + (dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML) + '</' + defaultLine + '>';
1662
+ return (
1663
+ '<' +
1664
+ defaultLine +
1665
+ '>' +
1666
+ (dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML) +
1667
+ '</' +
1668
+ defaultLine +
1669
+ '>'
1670
+ );
1469
1671
  }
1470
1672
  }
1471
1673
  // text
@@ -1475,7 +1677,8 @@ class HTML {
1475
1677
  let html = '';
1476
1678
  for (let i = 0, tLen = textArray.length, text; i < tLen; i++) {
1477
1679
  text = textArray[i].trim();
1478
- if (text.length > 0) html += '<' + defaultLine + '>' + converter.htmlToEntity(text) + '</' + defaultLine + '>';
1680
+ if (text.length > 0)
1681
+ html += '<' + defaultLine + '>' + converter.htmlToEntity(text) + '</' + defaultLine + '>';
1479
1682
  }
1480
1683
  return html;
1481
1684
  }
@@ -1487,6 +1690,23 @@ class HTML {
1487
1690
  return '';
1488
1691
  }
1489
1692
 
1693
+ /**
1694
+ * @description Checks whether a node is a block-level container in which whitespace-only text
1695
+ * children are insignificant formatting whitespace (safe to drop), as opposed to an inline/line
1696
+ * @param {?Node} node Parent node of the whitespace text node (may be a `DocumentFragment`).
1697
+ * @returns {boolean} `true` if the node is a block-level container.
1698
+ */
1699
+ #isBlockLevelWhitespaceContext(node) {
1700
+ return (
1701
+ !node ||
1702
+ node.nodeType === 11 ||
1703
+ dom.check.isWysiwygFrame(node) ||
1704
+ dom.check.isList(node) ||
1705
+ dom.check.isTableElements(node) ||
1706
+ this.#$.format.isBlock(node)
1707
+ );
1708
+ }
1709
+
1490
1710
  /**
1491
1711
  * @description Fix tags that do not fit the editor format.
1492
1712
  * @param {DocumentFragment} documentFragment Document fragment `DOCUMENT_FRAGMENT_NODE` (nodeType === 11)
@@ -1497,7 +1717,15 @@ class HTML {
1497
1717
  * @param {boolean} classFilter Class name filter option
1498
1718
  * @param {boolean} _freeCodeViewMode Enforces strict HTML validation based on the editor`s policy
1499
1719
  */
1500
- #consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
1720
+ #consistencyCheckOfHTML(
1721
+ documentFragment,
1722
+ htmlCheckWhitelistRegExp,
1723
+ htmlCheckBlacklistRegExp,
1724
+ tagFilter,
1725
+ formatFilter,
1726
+ classFilter,
1727
+ _freeCodeViewMode,
1728
+ ) {
1501
1729
  const removeTags = [],
1502
1730
  emptyTags = [],
1503
1731
  wrongList = [],
@@ -1509,14 +1737,29 @@ class HTML {
1509
1737
  (current) => {
1510
1738
  if (current.nodeType !== 1) {
1511
1739
  if (formatFilter && dom.check.isList(current.parentElement)) removeTags.push(current);
1512
- if (current.nodeType === 3 && !current.textContent.trim()) removeTags.push(current);
1740
+ if (current.nodeType === 3) {
1741
+ // Remove empty text nodes, and collapsible whitespace-only nodes that render nothing
1742
+ if (
1743
+ !current.textContent ||
1744
+ (_RE_INSIGNIFICANT_WS.test(current.textContent) &&
1745
+ (this.#isBlockLevelWhitespaceContext(current.parentNode) ||
1746
+ !current.previousSibling !== !current.nextSibling))
1747
+ ) {
1748
+ removeTags.push(current);
1749
+ }
1750
+ }
1513
1751
  return false;
1514
1752
  }
1515
1753
 
1516
1754
  // tag filter
1517
1755
  if (tagFilter) {
1518
1756
  // white list
1519
- if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
1757
+ if (
1758
+ htmlCheckBlacklistRegExp.test(current.nodeName) ||
1759
+ (!htmlCheckWhitelistRegExp.test(current.nodeName) &&
1760
+ current.childNodes.length === 0 &&
1761
+ dom.check.isExcludeFormat(current))
1762
+ ) {
1520
1763
  removeTags.push(current);
1521
1764
  return false;
1522
1765
  }
@@ -1534,7 +1777,9 @@ class HTML {
1534
1777
  !dom.check.isTableElements(current) &&
1535
1778
  !dom.check.isListCell(current) &&
1536
1779
  !dom.check.isAnchor(current) &&
1537
- (this.#$.format.isLine(current) || this.#$.format.isBlock(current) || this.#$.format.isTextStyleNode(current)) &&
1780
+ (this.#$.format.isLine(current) ||
1781
+ this.#$.format.isBlock(current) ||
1782
+ this.#$.format.isTextStyleNode(current)) &&
1538
1783
  current.childNodes.length === 0 &&
1539
1784
  nrtag
1540
1785
  ) {
@@ -1543,7 +1788,11 @@ class HTML {
1543
1788
  }
1544
1789
 
1545
1790
  // wrong list
1546
- if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
1791
+ if (
1792
+ dom.check.isList(current.parentNode) &&
1793
+ !dom.check.isList(current) &&
1794
+ !dom.check.isListCell(current)
1795
+ ) {
1547
1796
  wrongList.push(current);
1548
1797
  return false;
1549
1798
  }
@@ -1577,7 +1826,9 @@ class HTML {
1577
1826
  current.parentNode !== documentFragment &&
1578
1827
  nrtag &&
1579
1828
  ((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
1580
- ((this.#$.format.isLine(current) || this.#$.component.is(current)) && !this.#$.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.#$.component.is.bind(this.#$.component))));
1829
+ ((this.#$.format.isLine(current) || this.#$.component.is(current)) &&
1830
+ !this.#$.format.isBlock(current.parentNode) &&
1831
+ !dom.query.getParentElement(current, this.#$.component.is.bind(this.#$.component))));
1581
1832
 
1582
1833
  return result;
1583
1834
  },
@@ -1701,7 +1952,13 @@ class HTML {
1701
1952
 
1702
1953
  if (n.nodeType === 8) {
1703
1954
  value += '<!-- ' + n.textContent + ' -->';
1704
- } else if (!/meta/i.test(n.nodeName) && !this.#$.format.isLine(n) && !this.#$.format.isBlock(n) && !this.#$.component.is(n) && !dom.check.isExcludeFormat(n)) {
1955
+ } else if (
1956
+ !/meta/i.test(n.nodeName) &&
1957
+ !this.#$.format.isLine(n) &&
1958
+ !this.#$.format.isBlock(n) &&
1959
+ !this.#$.component.is(n) &&
1960
+ !dom.check.isExcludeFormat(n)
1961
+ ) {
1705
1962
  f ||= dom.utils.createElement(this.#options.get('defaultLine'));
1706
1963
  if (this.#$.format.isTextStyleNode(n)) {
1707
1964
  /** @type {HTMLElement} */
@@ -1766,7 +2023,12 @@ class HTML {
1766
2023
 
1767
2024
  for (let i = 0, len = domTree.length, t; i < len; i++) {
1768
2025
  t = domTree[i];
1769
- if (t.nodeType === 1 && !this.#$.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.#disallowedTagNameRegExp.test(t.nodeName)) {
2026
+ if (
2027
+ t.nodeType === 1 &&
2028
+ !this.#$.format.isTextStyleNode(t) &&
2029
+ !dom.check.isBreak(t) &&
2030
+ !this.#disallowedTagNameRegExp.test(t.nodeName)
2031
+ ) {
1770
2032
  requireFormat = true;
1771
2033
  break;
1772
2034
  }
@@ -1790,7 +2052,12 @@ class HTML {
1790
2052
  const face = (m.match(_RE_FACE_ATTR) || [])[1];
1791
2053
  const color = (m.match(_RE_COLOR_ATTR) || [])[1];
1792
2054
  if (size || face || color) {
1793
- sv = 'style="' + (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
2055
+ sv =
2056
+ 'style="' +
2057
+ (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') +
2058
+ (face ? 'font-family:' + face + ';' : '') +
2059
+ (color ? 'color:' + color + ';' : '') +
2060
+ '"';
1794
2061
  }
1795
2062
  }
1796
2063
 
@@ -1823,7 +2090,10 @@ class HTML {
1823
2090
  case 'fontSize':
1824
2091
  if (!this.#$.plugins.fontSize) continue;
1825
2092
  if (!this.#fontSizeUnitRegExp.test(r[0])) {
1826
- r[0] = r[0].replace((r[0].match(_RE_CSS_VALUE) || [])[1], converter.toFontUnit.bind(null, this.#options.get('fontSizeUnits')[0]));
2093
+ r[0] = r[0].replace(
2094
+ (r[0].match(_RE_CSS_VALUE) || [])[1],
2095
+ converter.toFontUnit.bind(null, this.#options.get('fontSizeUnits')[0]),
2096
+ );
1827
2097
  }
1828
2098
  break;
1829
2099
  case 'color':
@@ -2003,10 +2273,16 @@ class HTML {
2003
2273
  styleToTag.italic = { regex: /font-style\s*:\s*italic/i, tag: convertTextTags.italic };
2004
2274
  break;
2005
2275
  case 'underline':
2006
- styleToTag.underline = { regex: /text-decoration\s*:\s*underline/i, tag: convertTextTags.underline };
2276
+ styleToTag.underline = {
2277
+ regex: /text-decoration\s*:\s*underline/i,
2278
+ tag: convertTextTags.underline,
2279
+ };
2007
2280
  break;
2008
2281
  case 'strike':
2009
- styleToTag.strike = { regex: /text-decoration\s*:\s*line-through/i, tag: convertTextTags.strike };
2282
+ styleToTag.strike = {
2283
+ regex: /text-decoration\s*:\s*line-through/i,
2284
+ tag: convertTextTags.strike,
2285
+ };
2010
2286
  break;
2011
2287
  }
2012
2288
  });
@@ -2084,6 +2360,8 @@ const _RE_LEADING_SPACE = /^\s/;
2084
2360
 
2085
2361
  // #cleanStyle
2086
2362
  const _RE_TAG_ATTRS = /<[^\s]+\s(.+)/;
2363
+ // #consistencyCheckOfHTML: collapsible ASCII whitespace only (excludes &nbsp;/zero-width, which are meaningful content)
2364
+ const _RE_INSIGNIFICANT_WS = /^[ \t\n\r\f\v]+$/;
2087
2365
  const _RE_SIZE_ATTR = /\ssize="([^"]+)"/i;
2088
2366
  const _RE_FACE_ATTR = /\sface="([^"]+)"/i;
2089
2367
  const _RE_COLOR_ATTR = /\scolor="([^"]+)"/i;