suneditor 3.0.0-beta.2 → 3.0.0-beta.4

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 (37) hide show
  1. package/dist/suneditor.min.css +1 -1
  2. package/dist/suneditor.min.js +1 -1
  3. package/package.json +1 -1
  4. package/src/assets/design/color.css +11 -1
  5. package/src/assets/suneditor.css +27 -24
  6. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +4 -2
  7. package/src/core/base/eventHandlers/handler_ww_key_input.js +1 -1
  8. package/src/core/base/eventManager.js +61 -30
  9. package/src/core/class/char.js +3 -2
  10. package/src/core/class/component.js +17 -3
  11. package/src/core/class/format.js +13 -2
  12. package/src/core/class/html.js +58 -26
  13. package/src/core/class/selection.js +1 -8
  14. package/src/core/class/toolbar.js +2 -1
  15. package/src/core/class/ui.js +3 -1
  16. package/src/core/class/viewer.js +1 -1
  17. package/src/core/editor.js +124 -58
  18. package/src/core/section/actives.js +48 -23
  19. package/src/core/section/constructor.js +92 -78
  20. package/src/events.js +12 -0
  21. package/src/helper/converter.js +23 -1
  22. package/src/helper/dom/domCheck.js +2 -2
  23. package/src/modules/Controller.js +25 -5
  24. package/src/plugins/dropdown/formatBlock.js +4 -13
  25. package/src/themes/dark.css +11 -1
  26. package/types/core/base/eventManager.d.ts +16 -0
  27. package/types/core/class/char.d.ts +2 -1
  28. package/types/core/class/component.d.ts +1 -0
  29. package/types/core/class/format.d.ts +8 -1
  30. package/types/core/class/html.d.ts +8 -0
  31. package/types/core/class/ui.d.ts +1 -1
  32. package/types/core/editor.d.ts +7 -2
  33. package/types/core/section/constructor.d.ts +7 -0
  34. package/types/events.d.ts +2 -0
  35. package/types/helper/converter.d.ts +9 -0
  36. package/types/helper/index.d.ts +1 -0
  37. package/types/plugins/dropdown/formatBlock.d.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suneditor",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.4",
4
4
  "description": "Vanilla javascript based WYSIWYG web editor",
5
5
  "author": "Yi JiHong",
6
6
  "license": "MIT",
@@ -37,8 +37,18 @@
37
37
  --se-statusbar-font-color: #666;
38
38
  --se-overlay-background-color: #222;
39
39
 
40
- /* hover, active */
40
+ /* hover */
41
+ --se-hover-color: #000;
42
+ --se-hover-dark-color: #cccccc;
43
+ --se-hover-dark2-color: #bfbfbf;
44
+ --se-hover-dark3-color: #b0b0b0;
45
+ --se-hover-light-color: #e1e1e1;
46
+ --se-hover-light2-color: #e6e6e6;
47
+ --se-hover-light3-color: #d9d9d9;
48
+
49
+ /* active */
41
50
  --se-active-color: #5cd2e6;
51
+ --se-active-hover-color: #2964b7;
42
52
  --se-active-dark-color: #80bdff;
43
53
  --se-active-dark2-color: #407dd1;
44
54
  --se-active-dark3-color: #4592ff;
@@ -201,9 +201,13 @@
201
201
  /* se-type-document - placeholder position set */
202
202
  .sun-editor .se-wrapper.se-type-document .se-placeholder {
203
203
  top: clamp(var(--se-doc-min-padding));
204
+ left: clamp(var(--se-doc-min-padding));
205
+ right: clamp(var(--se-doc-min-padding));
206
+ padding: var(--se-edit-inner-padding-doc-type);
207
+ }
208
+ .sun-editor .se-wrapper.se-type-document.se-type-document-header .se-placeholder {
204
209
  left: var(--se-doc-info-width);
205
210
  right: var(--se-doc-info-width);
206
- padding: var(--se-edit-inner-padding-doc-type);
207
211
  }
208
212
  /** --- se-type-document end ---------------------------------------------------------- */
209
213
 
@@ -253,16 +257,17 @@
253
257
 
254
258
  .sun-editor .se-btn:not(.on):not(.active):enabled:hover,
255
259
  .sun-editor .se-btn:not(.on):not(.active):enabled:focus {
256
- background-color: var(--se-main-divider-color);
257
- border-color: var(--se-main-border-color);
260
+ color: var(--se-hover-color);
261
+ background-color: var(--se-hover-light-color);
262
+ border-color: var(--se-hover-dark-color);
258
263
  }
259
264
 
260
265
  .sun-editor .se-btn:not(.on):not(.active):enabled:active,
261
266
  .sun-editor .se-btn:not(.on):not(.active):enabled.__se__active {
262
- background-color: var(--se-main-shadow-color);
263
- border-color: var(--se-main-shadow-color) !important;
264
- outline: 1px solid var(--se-main-outline-color) !important;
265
- box-shadow: 0 0 0 0.3rem var(--se-main-shadow-color);
267
+ background-color: var(--se-hover-light2-color);
268
+ border-color: var(--se-hover-dark2-color) !important;
269
+ outline: 1px solid var(--se-hover-dark3-color) !important;
270
+ box-shadow: 0 0 0 0.3rem var(--se-hover-light2-color);
266
271
  transition: box-shadow 0.1s ease-in-out;
267
272
  }
268
273
 
@@ -274,7 +279,7 @@
274
279
  .sun-editor .se-menu-list li:hover .se-btn-input input:enabled {
275
280
  border-width: 1px;
276
281
  border-style: solid;
277
- border-color: var(--se-main-divider-color);
282
+ border-color: var(--se-hover-light3-color);
278
283
  }
279
284
 
280
285
  /* after, before button - on, active */
@@ -324,7 +329,7 @@
324
329
  .sun-editor .se-btn:enabled.on:hover,
325
330
  .sun-editor .se-btn:enabled.on:focus {
326
331
  background-color: var(--se-active-light3-color);
327
- color: var(--se-active-dark2-color);
332
+ color: var(--se-active-hover-color);
328
333
  border-color: var(--se-active-light5-color);
329
334
  }
330
335
 
@@ -349,14 +354,14 @@
349
354
  .sun-editor .se-btn:enabled.active:hover,
350
355
  .sun-editor .se-btn:enabled.active:focus {
351
356
  background-color: var(--se-active-light5-color);
352
- color: var(--se-active-dark5-color);
357
+ color: var(--se-active-hover-color);
353
358
  border-color: var(--se-active-dark4-color);
354
359
  }
355
360
 
356
361
  .sun-editor .se-btn:enabled.active:active,
357
362
  .sun-editor .se-btn:enabled.active.__se__active {
358
363
  background-color: var(--se-active-light5-color);
359
- color: var(--se-active-dark5-color);
364
+ color: var(--se-active-hover-color);
360
365
  border-color: var(--se-active-dark5-color) !important;
361
366
  outline: 1px solid var(--se-active-dark5-color) !important;
362
367
  box-shadow: 0 0 0 0.3rem var(--se-active-light5-color);
@@ -773,9 +778,7 @@
773
778
  height: 100%;
774
779
  top: 0;
775
780
  left: 0;
776
- background-color: rgba(0, 0, 0, 0.35);
777
- opacity: 0.7;
778
- filter: alpha(opacity=70);
781
+ background-color: transparent;
779
782
  z-index: 2147483647;
780
783
  }
781
784
 
@@ -811,7 +814,7 @@
811
814
  opacity: 0.95;
812
815
  border-radius: var(--se-border-radius);
813
816
  cursor: pointer;
814
- z-index: 2147483647;
817
+ z-index: 2147483646;
815
818
  transition: opacity 0.3s ease, color 0.3s ease, background-color 0.3s ease;
816
819
  }
817
820
  .sun-editor .se-line-breaker-component.se-on-selected {
@@ -868,7 +871,7 @@
868
871
  .sun-editor .se-toolbar.se-toolbar-balloon {
869
872
  display: none;
870
873
  position: absolute;
871
- z-index: 2147483647;
874
+ z-index: 2147483646;
872
875
  width: auto;
873
876
  box-shadow: 0 3px 9px var(--se-shadow-layer-color);
874
877
  -webkit-box-shadow: 0 3px 9px var(--se-shadow-layer-color);
@@ -1057,7 +1060,7 @@
1057
1060
  left: 0px;
1058
1061
  width: 100%;
1059
1062
  height: 0px;
1060
- z-index: 2147483647;
1063
+ z-index: 2147483646;
1061
1064
  }
1062
1065
 
1063
1066
  /** --- dropdown layer ---------------------------------------------------------- */
@@ -1733,7 +1736,7 @@
1733
1736
  -khtml-user-select: none;
1734
1737
  -webkit-user-select: none;
1735
1738
  -ms-user-select: none;
1736
- z-index: 2147483647;
1739
+ z-index: 2147483646;
1737
1740
  background-color: transparent;
1738
1741
  }
1739
1742
 
@@ -1783,7 +1786,7 @@
1783
1786
  left: 0;
1784
1787
  width: 100%;
1785
1788
  height: 100%;
1786
- z-index: 2147483647;
1789
+ z-index: 2147483646;
1787
1790
  }
1788
1791
  .sun-editor .se-modal-area.se-backdrop-show {
1789
1792
  display: block;
@@ -2774,7 +2777,7 @@
2774
2777
  min-width: auto;
2775
2778
  top: 0px;
2776
2779
  left: 0px;
2777
- z-index: 2147483647;
2780
+ z-index: 2147483646;
2778
2781
  display: none;
2779
2782
  width: -webkit-max-content;
2780
2783
  width: -moz-max-content;
@@ -2845,7 +2848,7 @@
2845
2848
  background-color: var(--se-active-light4-color);
2846
2849
  border-color: var(--se-active-light4-color) !important;
2847
2850
  outline: 1px solid var(--se-active-color) !important;
2848
- box-shadow: inset 2px 2px 5px var(--se-active-light6-color), inset -2px -2px 5px var(--se-active-light-color);
2851
+ box-shadow: none;
2849
2852
  transition: background-color 0.1s ease-in-out, box-shadow 0.1s ease-in-out;
2850
2853
  }
2851
2854
 
@@ -2866,7 +2869,7 @@
2866
2869
  left: 0;
2867
2870
  width: 100%;
2868
2871
  height: 100%;
2869
- z-index: 2147483647;
2872
+ z-index: 2147483646;
2870
2873
  }
2871
2874
 
2872
2875
  .sun-editor .se-browser label,
@@ -3313,7 +3316,7 @@
3313
3316
  left: 0;
3314
3317
  width: 100%;
3315
3318
  height: 100%;
3316
- z-index: 2147483647;
3319
+ z-index: 2147483646;
3317
3320
  pointer-events: none;
3318
3321
  }
3319
3322
 
@@ -4031,7 +4034,7 @@
4031
4034
  pointer-events: none;
4032
4035
  width: 2px;
4033
4036
  background-color: var(--se-active-dark5-color);
4034
- z-index: 2147483647;
4037
+ z-index: 2147483646;
4035
4038
  }
4036
4039
  .sun-editor.sun-editor-carrier-wrapper .se-drag-cursor::before {
4037
4040
  content: '';
@@ -15,9 +15,10 @@ import { _DragHandle } from '../../../modules';
15
15
  * @param {DragEvent} e - Event object
16
16
  */
17
17
  export function OnDragOver_wysiwyg(fc, dragCursor, _iframeTopArea, _innerToolbar, e) {
18
- e.preventDefault();
19
-
20
18
  const { sc, so, ec, eo } = this.selection.getDragEventLocationRange(e);
19
+ if (!sc) return;
20
+
21
+ e.preventDefault();
21
22
 
22
23
  const cursorRange = fc.get('_wd').createRange();
23
24
  cursorRange.setStart(sc, so);
@@ -77,6 +78,7 @@ export function OnDrop_wysiwyg(fc, dragCursor, e) {
77
78
  if (!dataTransfer) return true;
78
79
 
79
80
  const { sc, so, ec, eo } = this.selection.getDragEventLocationRange(e);
81
+ if (!sc) return;
80
82
 
81
83
  if (dom.query.getParentElement(sc, '.se-disable-pointer')) {
82
84
  e.preventDefault();
@@ -632,7 +632,7 @@ export async function OnKeyDown_wysiwyg(fc, e) {
632
632
  const baseIndex = dom.query.findTextIndexOnLine(formatEl, range.startContainer, range.startOffset, this.component.is.bind(this.component));
633
633
  const prevTabEndIndex = this.format.isLine(formatEl.previousElementSibling) ? dom.query.findTabEndIndex(formatEl.previousElementSibling, baseIndex, 2) : 0;
634
634
  if (prevTabEndIndex > baseIndex) {
635
- tabSize = prevTabEndIndex - baseIndex + 1;
635
+ tabSize = prevTabEndIndex - baseIndex;
636
636
  }
637
637
  }
638
638
 
@@ -500,13 +500,13 @@ EventManager.prototype = {
500
500
  const sIsCell = dom.check.isTableCell(sCell);
501
501
  const eIsCell = dom.check.isTableCell(eCell);
502
502
  if (((sIsCell && !sCell.previousElementSibling && !sCell.parentElement.previousElementSibling) || (eIsCell && !eCell.nextElementSibling && !eCell.parentElement.nextElementSibling)) && sCell !== eCell) {
503
- const ancestor = dom.query.getParentElement(range.commonAncestorContainer, dom.check.isFigure) || range.commonAncestorContainer;
503
+ const ancestor = dom.query.getParentElement(range.commonAncestorContainer, dom.check.isFigure)?.parentElement || range.commonAncestorContainer;
504
504
  if (!sIsCell) {
505
- dom.utils.removeItem(dom.query.getParentElement(eCell, (current) => ancestor === current));
505
+ dom.utils.removeItem(dom.query.getParentElement(eCell, (current) => ancestor === current.parentNode));
506
506
  } else if (!eIsCell) {
507
- dom.utils.removeItem(dom.query.getParentElement(sCell, (current) => ancestor === current));
507
+ dom.utils.removeItem(dom.query.getParentElement(sCell, (current) => ancestor === current.parentNode));
508
508
  } else {
509
- dom.utils.removeItem(dom.query.getParentElement(sCell, (current) => ancestor === current));
509
+ dom.utils.removeItem(dom.query.getParentElement(sCell, (current) => ancestor === current.parentNode));
510
510
  this.editor._nativeFocus();
511
511
  return true;
512
512
  }
@@ -881,14 +881,14 @@ EventManager.prototype = {
881
881
 
882
882
  /** line breaker */
883
883
  const lineBreakEventName = isMobile ? 'touchstart' : 'mousedown';
884
- this.addEvent(
885
- [fc.get('lineBreaker_t'), fc.get('lineBreaker_b')],
886
- lineBreakEventName,
887
- (e) => {
888
- e.preventDefault();
889
- },
890
- false
891
- );
884
+ // this.addEvent(
885
+ // [fc.get('lineBreaker_t'), fc.get('lineBreaker_b')],
886
+ // lineBreakEventName,
887
+ // (e) => {
888
+ // e.preventDefault();
889
+ // },
890
+ // false
891
+ // );
892
892
  this.addEvent(fc.get('lineBreaker_t'), lineBreakEventName, DisplayLineBreak.bind(this, 't'), false);
893
893
  this.addEvent(fc.get('lineBreaker_b'), lineBreakEventName, DisplayLineBreak.bind(this, 'b'), false);
894
894
 
@@ -1222,6 +1222,40 @@ EventManager.prototype = {
1222
1222
  if (!env.isMobile) this.editor.selection.scrollTo(range);
1223
1223
  },
1224
1224
 
1225
+ /**
1226
+ * @private
1227
+ * @description Focus Event Postprocessing
1228
+ * @this {EventManagerThis}
1229
+ * @param {__se__FrameContext} frameContext - frame context object
1230
+ * @param {Event} event - Event object
1231
+ */
1232
+ __postFocusEvent(frameContext, event) {
1233
+ if (this.editor.isInline || this.editor.isBalloonAlways) this.toolbar.show();
1234
+ if (this.editor.isSubBalloonAlways) this.subToolbar.show();
1235
+
1236
+ // user event
1237
+ this.triggerEvent('onFocus', { frameContext, event });
1238
+ // plugin event
1239
+ this._callPluginEvent('onFocus', { frameContext, event });
1240
+ },
1241
+
1242
+ /**
1243
+ * @private
1244
+ * @description Blur Event Postprocessing
1245
+ * @this {EventManagerThis}
1246
+ * @param {__se__FrameContext} frameContext - frame context object
1247
+ * @param {Event} event - Event object
1248
+ */
1249
+ __postBlurEvent(frameContext, event) {
1250
+ if (this.editor.isInline || this.editor.isBalloon) this._hideToolbar();
1251
+ if (this.editor.isSubBalloon) this._hideToolbar_sub();
1252
+
1253
+ // user event
1254
+ this.triggerEvent('onBlur', { frameContext, event });
1255
+ // plugin event
1256
+ this._callPluginEvent('onBlur', { frameContext, event });
1257
+ },
1258
+
1225
1259
  constructor: EventManager
1226
1260
  };
1227
1261
 
@@ -1258,6 +1292,10 @@ function OnFocus_wysiwyg(frameContext, e) {
1258
1292
  return false;
1259
1293
  }
1260
1294
 
1295
+ this.status.hasFocus = true;
1296
+ this.component.__prevent = false;
1297
+ this.triggerEvent('onNativeFocus', { frameContext, event: e });
1298
+
1261
1299
  const rootKey = frameContext.get('key');
1262
1300
 
1263
1301
  if (this._inputFocus) {
@@ -1269,11 +1307,11 @@ function OnFocus_wysiwyg(frameContext, e) {
1269
1307
  return;
1270
1308
  }
1271
1309
 
1272
- if (this.status.rootKey === rootKey && this.editor._preventBlur) return;
1310
+ if ((this.status.rootKey === rootKey && this.editor._preventBlur) || this.editor._preventFocus) return;
1311
+ this.editor._preventFocus = true;
1273
1312
 
1274
1313
  const onSelected = this.editor.status.onSelected || this.editor.opendModal;
1275
1314
  this.ui._offCurrentController();
1276
- this.status.hasFocus = true;
1277
1315
 
1278
1316
  dom.utils.removeClass(this.editor.commandTargets.get('codeView'), 'active');
1279
1317
  dom.utils.setDisabled(this.editor._codeViewDisabledButtons, false);
@@ -1286,12 +1324,7 @@ function OnFocus_wysiwyg(frameContext, e) {
1286
1324
  }
1287
1325
 
1288
1326
  this._w.setTimeout(() => {
1289
- if (this.editor.isInline) this.toolbar._showInline();
1290
-
1291
- // user event
1292
- this.triggerEvent('onFocus', { frameContext, event: e });
1293
- // plugin event
1294
- this._callPluginEvent('onFocus', { frameContext, event: e });
1327
+ this.__postFocusEvent(frameContext, e);
1295
1328
  }, 0);
1296
1329
  }
1297
1330
 
@@ -1301,12 +1334,14 @@ function OnFocus_wysiwyg(frameContext, e) {
1301
1334
  * @param {Event} e - Event object
1302
1335
  */
1303
1336
  function OnBlur_wysiwyg(frameContext, e) {
1304
- if (this._inputFocus || this.editor._preventBlur || frameContext.get('isCodeView') || frameContext.get('isReadOnly') || frameContext.get('isDisabled')) return;
1337
+ if (frameContext.get('isCodeView') || frameContext.get('isReadOnly') || frameContext.get('isDisabled')) return;
1305
1338
 
1306
1339
  this.status.hasFocus = false;
1307
1340
  this.editor.effectNode = null;
1308
- if (this.editor.isInline || this.editor.isBalloon) this._hideToolbar();
1309
- if (this.editor.isSubBalloon) this._hideToolbar_sub();
1341
+ this.triggerEvent('onNativeBlur', { frameContext, event: e });
1342
+
1343
+ if (this._inputFocus || this.editor._preventBlur) return;
1344
+ this.editor._preventFocus = false;
1310
1345
 
1311
1346
  this._setKeyEffect([]);
1312
1347
 
@@ -1319,10 +1354,7 @@ function OnBlur_wysiwyg(frameContext, e) {
1319
1354
 
1320
1355
  this.history.check(frameContext.get('key'), this.status._range);
1321
1356
 
1322
- // user event
1323
- this.triggerEvent('onBlur', { frameContext, event: e });
1324
- // plugin event
1325
- this._callPluginEvent('onBlur', { frameContext, event: e });
1357
+ this.__postBlurEvent(frameContext, e);
1326
1358
  }
1327
1359
 
1328
1360
  /**
@@ -1380,12 +1412,11 @@ function DisplayLineBreak(dir, e) {
1380
1412
  this.component.deselect();
1381
1413
 
1382
1414
  try {
1383
- this.editor._preventBlur = true;
1384
1415
  const focusEl = isList ? format : format.firstChild;
1385
1416
  this.selection.setRange(focusEl, 1, focusEl, 1);
1386
1417
  this.history.push(false);
1387
- } finally {
1388
- this.editor._preventBlur = false;
1418
+ } catch (err) {
1419
+ console.warn('[SUNEDITOR.lineBreaker.error]', err);
1389
1420
  }
1390
1421
  }
1391
1422
 
@@ -88,9 +88,10 @@ Char.prototype = {
88
88
  /**
89
89
  * @this {CharThis}
90
90
  * @description Set the char count to charCounter element textContent.
91
+ * @param {?__se__FrameContext=} fc Frame context
91
92
  */
92
- display() {
93
- const charCounter = this.editor.frameContext.get('charCounter');
93
+ display(fc) {
94
+ const charCounter = (fc || this.editor.frameContext).get('charCounter');
94
95
  if (charCounter) {
95
96
  _w.setTimeout(() => {
96
97
  charCounter.textContent = this.getLength();
@@ -7,8 +7,8 @@ import { dom, env, numbers, unicode, keyCodeMap, converter } from '../../helper'
7
7
  import { Figure, _DragHandle } from '../../modules';
8
8
 
9
9
  const { _w, ON_OVER_COMPONENT, isMobile } = env;
10
- const DIR_KEYCODE = /^(3[7-9]|40)$/;
11
- const DIR_UP_KEYCODE = /^3[7-8]$/;
10
+ const DIR_KEYCODE = /^Arrow(Left|Up|Right|Down)$/;
11
+ const DIR_UP_KEYCODE = /^Arrow(Left|Up)$/;
12
12
 
13
13
  /**
14
14
  * @typedef {Omit<Component & Partial<__se__EditorInjector>, 'component'>} ComponentThis
@@ -79,6 +79,8 @@ function Component(editor) {
79
79
  /** @type {boolean} */
80
80
  this.__selectionSelected = false;
81
81
 
82
+ this.__prevent = false;
83
+
82
84
  this.editor.applyFrameRoots((e) => {
83
85
  // drag
84
86
  const dragHandle = dom.utils.createElement('DIV', { class: 'se-drag-handle', draggable: 'true' }, this.icons.selection);
@@ -230,7 +232,8 @@ Component.prototype = {
230
232
  const plugin = info.launcher || this.plugins[pluginName];
231
233
  if (!plugin) return;
232
234
 
233
- if (!isInput && _DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
235
+ const notOver = _DragHandle.get('__overInfo') !== ON_OVER_COMPONENT;
236
+ if (!isInput && notOver) {
234
237
  if (this.editor.status._onMousedown) return;
235
238
 
236
239
  this.editor._preventBlur = true;
@@ -245,6 +248,7 @@ Component.prototype = {
245
248
  }
246
249
 
247
250
  this.isSelected = true;
251
+ this.__prevent = true;
248
252
 
249
253
  let isNonFigureComponent;
250
254
  if (typeof plugin.select === 'function') isNonFigureComponent = plugin.select(element);
@@ -270,6 +274,11 @@ Component.prototype = {
270
274
  dom.utils.addClass(info.container, 'se-component-selected');
271
275
  }, 0)();
272
276
 
277
+ if (notOver && !this.status.hasFocus && !this.editor._preventFocus) {
278
+ this.eventManager.__postFocusEvent(this.editor.frameContext, null);
279
+ this.editor._preventFocus = true;
280
+ }
281
+
273
282
  if (!isBreakComponent && __overInfo !== ON_OVER_COMPONENT) {
274
283
  // set zero width space
275
284
  if (!this.isInline(info.container)) return;
@@ -321,6 +330,11 @@ Component.prototype = {
321
330
  }, 0);
322
331
  this.__deselect();
323
332
  this.ui.setControllerOnDisabledButtons(false);
333
+
334
+ if (this.editor._preventFocus && !this.status.hasFocus && !this.__prevent) {
335
+ this.eventManager.__postBlurEvent(this.editor.frameContext, null);
336
+ this.editor._preventFocus = false;
337
+ }
324
338
  },
325
339
 
326
340
  /**
@@ -35,8 +35,9 @@ function Format(editor) {
35
35
  this._formatClosureBlockCheck = this.options.get('formatClosureBlock').reg;
36
36
  this._formatClosureBrLineCheck = this.options.get('formatClosureBrLine').reg;
37
37
  this._textStyleTagsCheck = new RegExp('^(' + this.options.get('textStyleTags') + ')$', 'i');
38
- // members - _defaultBrLineBreak
39
- this._brLineBreak = this.options.get('_defaultBrLineBreak');
38
+
39
+ this._brLineBreak = null;
40
+ this.__resetBrLineBreak(this.options.get('defaultLineBreakFormat'));
40
41
  }
41
42
 
42
43
  Format.prototype = {
@@ -3334,6 +3335,16 @@ Format.prototype = {
3334
3335
  return appliedEl;
3335
3336
  },
3336
3337
 
3338
+ /**
3339
+ * @private
3340
+ * @this {FormatThis}
3341
+ * @description Reset the line break format.
3342
+ * @param {"line"|"br"} breakFormat options.get('defaultLineBreakFormat')
3343
+ */
3344
+ __resetBrLineBreak(breakFormat) {
3345
+ this._brLineBreak = breakFormat === 'br';
3346
+ },
3347
+
3337
3348
  constructor: Format
3338
3349
  };
3339
3350
 
@@ -152,27 +152,7 @@ function HTML(editor) {
152
152
  this._attributeBlacklist = tagsAttr;
153
153
 
154
154
  // autoStyleify
155
- if (options.get('autoStyleify').length > 0) {
156
- const convertTextTags = options.get('convertTextTags');
157
- const styleToTag = {};
158
- options.get('autoStyleify').forEach((style) => {
159
- switch (style) {
160
- case 'bold':
161
- styleToTag.bold = { regex: /font-weight\s*:\s*bold/i, tag: convertTextTags.bold };
162
- break;
163
- case 'italic':
164
- styleToTag.italic = { regex: /font-style\s*:\s*italic/i, tag: convertTextTags.italic };
165
- break;
166
- case 'underline':
167
- styleToTag.underline = { regex: /text-decoration\s*:\s*underline/i, tag: convertTextTags.underline };
168
- break;
169
- case 'strike':
170
- styleToTag.strike = { regex: /text-decoration\s*:\s*line-through/i, tag: convertTextTags.strike };
171
- break;
172
- }
173
- });
174
- this._autoStyleify = styleToTag;
175
- }
155
+ this.__resetAutoStyleify(options.get('autoStyleify'));
176
156
  }
177
157
 
178
158
  HTML.prototype = {
@@ -1059,11 +1039,29 @@ HTML.prototype = {
1059
1039
 
1060
1040
  const fc = this.editor.frameContext;
1061
1041
  const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
1062
- const editableEls = dom.query.getListChildren(renderHTML, (current) => current.hasAttribute('contenteditable'));
1042
+
1043
+ const isTableCell = dom.check.isTableCell;
1044
+ const isEmptyLine = dom.check.isEmptyLine;
1045
+ const editableEls = [];
1046
+ const emptyCells = [];
1047
+ dom.query.getListChildren(renderHTML, (current) => {
1048
+ if (current.hasAttribute('contenteditable')) {
1049
+ editableEls.push(current);
1050
+ }
1051
+
1052
+ const parent = current.parentElement;
1053
+ if (isTableCell(parent) && parent.children.length <= 1 && isEmptyLine(current)) {
1054
+ emptyCells.push(parent);
1055
+ }
1056
+ return false;
1057
+ });
1063
1058
 
1064
1059
  for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
1065
1060
  editableEls[j].removeAttribute('contenteditable');
1066
1061
  }
1062
+ for (let j = 0, jlen = emptyCells.length; j < jlen; j++) {
1063
+ emptyCells[j].innerHTML = '<br>';
1064
+ }
1067
1065
 
1068
1066
  const content = renderHTML.innerHTML;
1069
1067
  if (this.editor.frameOptions.get('iframe_fullPage')) {
@@ -1212,10 +1210,7 @@ HTML.prototype = {
1212
1210
  * @returns {string} HTML string
1213
1211
  */
1214
1212
  compress(html) {
1215
- return html
1216
- .replace(/\n/g, '')
1217
- .replace(/(>)(?:\s+)(<)/g, '$1$2')
1218
- .trim();
1213
+ return html.replace(/>\s+</g, '> <').replace(/\n/g, '').trim();
1219
1214
  },
1220
1215
 
1221
1216
  /**
@@ -1582,6 +1577,10 @@ HTML.prototype = {
1582
1577
  value += '<!-- ' + n.textContent + ' -->';
1583
1578
  } else if (!/meta/i.test(n.nodeName) && !this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) && !dom.check.isExcludeFormat(n)) {
1584
1579
  if (!f) f = dom.utils.createElement(this.options.get('defaultLine'));
1580
+ if (this.format.isTextStyleNode(n)) {
1581
+ /** @type {HTMLElement} */
1582
+ (n).removeAttribute('style');
1583
+ }
1585
1584
  f.appendChild(n);
1586
1585
  i--;
1587
1586
  len--;
@@ -1811,6 +1810,39 @@ HTML.prototype = {
1811
1810
  return oNode;
1812
1811
  },
1813
1812
 
1813
+ /**
1814
+ * @private
1815
+ * @this {HTMLThis}
1816
+ * @description Reset autoStyleify options.
1817
+ * @param {Array.<string>} autoStyleify Styles applied automatically on text input.
1818
+ * - ex ["bold", "underline", "italic", "strike"]
1819
+ */
1820
+ __resetAutoStyleify(autoStyleify) {
1821
+ if (autoStyleify.length > 0) {
1822
+ const convertTextTags = this.options.get('convertTextTags');
1823
+ const styleToTag = {};
1824
+ autoStyleify.forEach((style) => {
1825
+ switch (style) {
1826
+ case 'bold':
1827
+ styleToTag.bold = { regex: /font-weight\s*:\s*bold/i, tag: convertTextTags.bold };
1828
+ break;
1829
+ case 'italic':
1830
+ styleToTag.italic = { regex: /font-style\s*:\s*italic/i, tag: convertTextTags.italic };
1831
+ break;
1832
+ case 'underline':
1833
+ styleToTag.underline = { regex: /text-decoration\s*:\s*underline/i, tag: convertTextTags.underline };
1834
+ break;
1835
+ case 'strike':
1836
+ styleToTag.strike = { regex: /text-decoration\s*:\s*line-through/i, tag: convertTextTags.strike };
1837
+ break;
1838
+ }
1839
+ });
1840
+ this._autoStyleify = styleToTag;
1841
+ } else {
1842
+ this._autoStyleify = null;
1843
+ }
1844
+ },
1845
+
1814
1846
  constructor: HTML
1815
1847
  };
1816
1848
 
@@ -149,6 +149,7 @@ Selection_.prototype = {
149
149
  try {
150
150
  range.setStart(sc, so);
151
151
  range.setEnd(ec, eo);
152
+ this.status.hasFocus = true;
152
153
  } catch (error) {
153
154
  console.warn('[SUNEDITOR.selection.focus.warn]', error.message);
154
155
  this.editor._nativeFocus();
@@ -332,14 +333,6 @@ Selection_.prototype = {
332
333
  eo = r.endOffset;
333
334
  }
334
335
 
335
- if (!r) {
336
- r = this.getRange();
337
- sc = r.startContainer;
338
- so = r.startOffset;
339
- ec = r.endContainer;
340
- eo = r.endOffset;
341
- }
342
-
343
336
  return {
344
337
  sc,
345
338
  so,
@@ -167,6 +167,7 @@ Toolbar.prototype = {
167
167
  this.editor.commandTargets = new Map();
168
168
  this.editor.shortcutsKeyMap = new Map();
169
169
  this.editor.__saveCommandButtons(cmdButtons, newToolbar.buttonTray);
170
+ this.editor.__cachingShortcuts();
170
171
 
171
172
  this.context.get(this.keyName + '.main').replaceChild(newToolbar.buttonTray, this.context.get(this.keyName + '.buttonTray'));
172
173
  this.context.set(this.keyName + '.buttonTray', newToolbar.buttonTray);
@@ -305,7 +306,7 @@ Toolbar.prototype = {
305
306
  * @param {?Range=} rangeObj - Selection range
306
307
  */
307
308
  _showBalloon(rangeObj) {
308
- if (!this._isBalloon || this.editor.opendControllers.length > 0) {
309
+ if (!this._isBalloon) {
309
310
  return;
310
311
  }
311
312
  if (this.isSub) this.resetResponsiveToolbar();
@@ -48,7 +48,7 @@ function UI(editor) {
48
48
  UI.prototype = {
49
49
  /**
50
50
  * @this {UIThis}
51
- * @description Set "options.get('editorStyle')" style.
51
+ * @description set editor frame styles.
52
52
  * - Define the style of the edit area
53
53
  * - It can also be defined with the "setOptions" method, but the "setEditorStyle" method does not render the editor again.
54
54
  * @param {string} style Style string
@@ -56,7 +56,9 @@ UI.prototype = {
56
56
  */
57
57
  setEditorStyle(style, fc) {
58
58
  fc = fc || this.editor.frameContext;
59
+
59
60
  const fo = fc.get('options');
61
+ fo.set('editorStyle', style);
60
62
 
61
63
  const newStyles = converter._setDefaultOptionStyle(fo, style);
62
64
  fo.set('_defaultStyles', newStyles);