suneditor 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suneditor",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "description": "Vanilla JavaScript based WYSIWYG web editor",
5
5
  "author": "Yi JiHong",
6
6
  "license": "MIT",
@@ -300,7 +300,7 @@ class Viewer {
300
300
  this.#originCssText = topArea.style.cssText;
301
301
  this.#editorAreaOriginCssText = editorArea.style.cssText;
302
302
  this.#wysiwygOriginCssText = wysiwygFrame.style.cssText;
303
- this.#codeWrapperOriginCssText = codeWrapper.style.cssText;
303
+ this.#codeWrapperOriginCssText = codeWrapper?.style.cssText;
304
304
  this.#codeOriginCssText = code.style.cssText;
305
305
  this.#codeNumberOriginCssText = codeNumbers?.style.cssText;
306
306
  this.#markdownWrapperOriginCssText = markdownWrapper?.style.cssText;
@@ -344,9 +344,11 @@ class Viewer {
344
344
  wysiwygFrame.style.cssText = (wysiwygFrame.style.cssText.match(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/) || [''])[0] + this.#frameOptions.get('_defaultStyles').editor + (isCodeView || isMarkdownView ? 'display: none;' : '');
345
345
 
346
346
  // code wrapper
347
- codeWrapper.style.cssText = (codeWrapper.style.cssText.match(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/) || [''])[0] + `display: ${!isCodeView ? 'none' : 'flex'} !important;`;
348
- codeWrapper.style.overflow = 'auto';
349
- codeWrapper.style.height = '100%';
347
+ if (codeWrapper) {
348
+ codeWrapper.style.cssText = (codeWrapper.style.cssText.match(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/) || [''])[0] + `display: ${!isCodeView ? 'none' : 'flex'} !important;`;
349
+ codeWrapper.style.overflow = 'auto';
350
+ codeWrapper.style.height = '100%';
351
+ }
350
352
 
351
353
  // markdown wrapper
352
354
  if (markdownWrapper) {
@@ -384,7 +386,9 @@ class Viewer {
384
386
  wysiwygFrame.style.cssText = this.#wysiwygOriginCssText.replace(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/, '') + (isCodeView || isMarkdownView ? 'display: none;' : '');
385
387
 
386
388
  // code wrapper
387
- codeWrapper.style.cssText = this.#codeWrapperOriginCssText.replace(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/, '') + `display: ${!isCodeView ? 'none' : 'flex'} !important;`;
389
+ if (codeWrapper) {
390
+ codeWrapper.style.cssText = this.#codeWrapperOriginCssText.replace(/\s?display(\s+)?:(\s+)?[a-zA-Z]+;/, '') + `display: ${!isCodeView ? 'none' : 'flex'} !important;`;
391
+ }
388
392
 
389
393
  // code
390
394
  code.style.cssText = this.#codeOriginCssText;
@@ -1,6 +1,21 @@
1
1
  /**
2
2
  * @fileoverview Markdown converter module
3
3
  * - Supports GitHub Flavored Markdown (GFM) syntax
4
+ *
5
+ * @description Limitations — Style loss during roundtrip
6
+ *
7
+ * Markdown syntax cannot represent HTML inline styles, classes, or data attributes.
8
+ * When switching between WYSIWYG and Markdown view, the following behavior applies:
9
+ *
10
+ * | Content type | Behavior | Example |
11
+ * |-------------------------------------|---------------------------------------------|------------------------------------------------------|
12
+ * | Media components (`div.se-component`) | Converted to markdown — styles are lost | Image alignment, data-se-* attrs |
13
+ * | Styled `<span>` elements | Preserved as raw HTML in markdown | Font color, background, custom classes |
14
+ * | Tables, figures | Converted to markdown — styles are lost | Table cell styles, figure width, colgroup widths |
15
+ * | General elements (p, h1, blockquote) | Converted to markdown — styles are lost | text-align, color, font-size on paragraphs/headings |
16
+ *
17
+ * This is a fundamental limitation of the Markdown format.
18
+ * To preserve all HTML attributes without loss, use Code View instead.
4
19
  */
5
20
 
6
21
  const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i;
@@ -439,16 +454,16 @@ function nodeToMarkdown(node, indent, isBlock) {
439
454
 
440
455
  // Span - pass through as inline (may contain styles)
441
456
  if (tag === 'span') {
442
- // Check if it has meaningful attributes that need HTML fallback
443
457
  if (attributes.style || attributes.class) {
444
458
  return nodeToHtmlFallback(node);
445
459
  }
446
460
  return childrenToInline(children);
447
461
  }
448
462
 
449
- // Figure - process children (usually contains img or other media)
463
+ // Figure - process children (table, media)
450
464
  if (tag === 'figure') {
451
- return children.map((c) => nodeToMarkdown(c, indent, true)).join('');
465
+ const inner = children.map((c) => nodeToMarkdown(c, indent, true)).join('');
466
+ return /\n$/.test(inner) ? inner : inner + '\n\n';
452
467
  }
453
468
  if (tag === 'figcaption') {
454
469
  const content = childrenToInline(children).trim();
@@ -377,14 +377,13 @@ class Controller {
377
377
  if (!passive) {
378
378
  this.#$.ui.onControllerContext();
379
379
  this.#$.store.set('controlActive', true);
380
+ this.#$.store.set('_preventBlur', true);
380
381
  }
381
382
 
382
383
  if (!this.isOpen) {
383
384
  this.#$.ui.opendControllers.push(info);
384
385
  }
385
386
 
386
- this.#$.store.set('_preventBlur', true);
387
-
388
387
  this.isOpen = true;
389
388
 
390
389
  this.host.controllerOn?.(form, target);
@@ -904,7 +904,7 @@ class Figure {
904
904
  retainFigureFormat(container, originEl, anchorCover, fileManagerInst) {
905
905
  const isInline = this.#$.component.isInline(container);
906
906
  const originParent = originEl.parentNode;
907
- let existElement = this.#$.format.isBlock(originParent) || dom.check.isWysiwygFrame(originParent) ? originEl : Figure.GetContainer(originEl)?.container || originParent || originEl;
907
+ let existElement = this.#$.format.isBlock(originParent) || dom.check.isWysiwygFrame(originParent) || originParent.nodeType >= 9 ? originEl : Figure.GetContainer(originEl)?.container || originParent || originEl;
908
908
 
909
909
  if (dom.query.getParentElement(originEl, dom.check.isExcludeFormat)) {
910
910
  existElement = anchorCover && anchorCover !== originEl ? anchorCover : originEl;
@@ -66,7 +66,7 @@ class CodeBlock extends PluginCommand {
66
66
  // ───────────────── [[toolbar dropdown type]] ─────────────────
67
67
  this.afterItem = dom.utils.createElement(
68
68
  'button',
69
- { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': CodeBlock.key, 'data-type': 'dropdown' },
69
+ { class: 'se-btn se-tooltip se-sub-arrow-btn', type: 'button', 'data-command': CodeBlock.key, 'data-type': 'dropdown' },
70
70
  `${this.$.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.codeLanguage || 'Language'}</span></span>`,
71
71
  );
72
72
 
@@ -27,7 +27,7 @@ class List_bulleted extends PluginCommand {
27
27
  this.icon = 'list_bulleted';
28
28
  this.afterItem = dom.utils.createElement(
29
29
  'button',
30
- { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': List_bulleted.key, 'data-type': 'dropdown' },
30
+ { class: 'se-btn se-tooltip se-sub-arrow-btn', type: 'button', 'data-command': List_bulleted.key, 'data-type': 'dropdown' },
31
31
  `${this.$.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.bulletedList}</span></span>`,
32
32
  );
33
33
 
@@ -27,7 +27,7 @@ class List_numbered extends PluginCommand {
27
27
  this.icon = 'list_numbered';
28
28
  this.afterItem = dom.utils.createElement(
29
29
  'button',
30
- { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': List_numbered.key, 'data-type': 'dropdown' },
30
+ { class: 'se-btn se-tooltip se-sub-arrow-btn', type: 'button', 'data-command': List_numbered.key, 'data-type': 'dropdown' },
31
31
  `${this.$.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.numberedList}</span></span>`,
32
32
  );
33
33
 
@@ -152,25 +152,25 @@ class FontSize extends PluginInput {
152
152
  if (showIncDec) {
153
153
  this.beforeItem = dom.utils.createElement(
154
154
  'button',
155
- { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'dec' },
155
+ { class: 'se-btn se-tooltip se-sub-btn', type: 'button', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'dec' },
156
156
  `${this.$.icons.minus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.decrease}</span></span>`,
157
157
  );
158
158
  this.afterItem = dom.utils.createElement(
159
159
  'button',
160
- { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'inc' },
160
+ { class: 'se-btn se-tooltip se-sub-btn', type: 'button', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'inc' },
161
161
  `${this.$.icons.plus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.increase}</span></span>`,
162
162
  );
163
163
  } else if (!disableInput) {
164
164
  this.afterItem = dom.utils.createElement(
165
165
  'button',
166
- { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': FontSize.key, 'data-type': 'dropdown' },
166
+ { class: 'se-btn se-tooltip se-sub-arrow-btn', type: 'button', 'data-command': FontSize.key, 'data-type': 'dropdown' },
167
167
  `${this.$.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.fontSize}</span></span>`,
168
168
  );
169
169
  this.$.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);
170
170
  } else if (disableInput && !showIncDec) {
171
171
  this.replaceButton = dom.utils.createElement(
172
172
  'button',
173
- { class: 'se-btn se-tooltip se-btn-select se-btn-tool-font-size', 'data-command': FontSize.key, 'data-type': 'dropdown' },
173
+ { class: 'se-btn se-tooltip se-btn-select se-btn-tool-font-size', type: 'button', 'data-command': FontSize.key, 'data-type': 'dropdown' },
174
174
  `<span class="se-txt __se__font_size">${this.$.lang.fontSize}</span>${this.$.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.$.lang.fontSize}</span></span>`,
175
175
  );
176
176
  this.$.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);