suneditor 3.0.0-beta.3 → 3.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/CONTRIBUTING.md +8 -8
  2. package/README.md +44 -49
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +95 -53
  6. package/src/assets/design/color.css +2 -2
  7. package/src/assets/design/size.css +2 -0
  8. package/src/assets/icons/defaultIcons.js +16 -1
  9. package/src/assets/suneditor-contents.css +9 -8
  10. package/src/assets/suneditor.css +29 -26
  11. package/src/core/{section → base}/actives.js +20 -12
  12. package/src/core/base/history.js +4 -4
  13. package/src/core/class/char.js +10 -10
  14. package/src/core/class/component.js +146 -57
  15. package/src/core/class/format.js +94 -2458
  16. package/src/core/class/html.js +187 -129
  17. package/src/core/class/inline.js +1853 -0
  18. package/src/core/class/listFormat.js +582 -0
  19. package/src/core/class/menu.js +14 -3
  20. package/src/core/class/nodeTransform.js +9 -14
  21. package/src/core/class/offset.js +162 -197
  22. package/src/core/class/selection.js +137 -34
  23. package/src/core/class/toolbar.js +73 -52
  24. package/src/core/class/ui.js +11 -11
  25. package/src/core/class/viewer.js +56 -55
  26. package/src/core/config/context.js +122 -0
  27. package/src/core/config/frameContext.js +204 -0
  28. package/src/core/config/options.js +639 -0
  29. package/src/core/editor.js +181 -108
  30. package/src/core/event/actions/index.js +229 -0
  31. package/src/core/event/effects/common.registry.js +60 -0
  32. package/src/core/event/effects/keydown.registry.js +551 -0
  33. package/src/core/event/effects/ruleHelpers.js +145 -0
  34. package/src/core/{base → event}/eventManager.js +119 -201
  35. package/src/core/event/executor.js +21 -0
  36. package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +4 -4
  37. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +2 -2
  38. package/src/core/event/handlers/handler_ww_input.js +77 -0
  39. package/src/core/event/handlers/handler_ww_key.js +228 -0
  40. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.js +3 -3
  41. package/src/core/event/ports.js +211 -0
  42. package/src/core/event/reducers/keydown.reducer.js +89 -0
  43. package/src/core/event/rules/keydown.rule.arrow.js +54 -0
  44. package/src/core/event/rules/keydown.rule.backspace.js +202 -0
  45. package/src/core/event/rules/keydown.rule.delete.js +126 -0
  46. package/src/core/event/rules/keydown.rule.enter.js +144 -0
  47. package/src/core/event/rules/keydown.rule.tab.js +29 -0
  48. package/src/core/section/constructor.js +79 -388
  49. package/src/core/section/documentType.js +47 -26
  50. package/src/core/util/instanceCheck.js +59 -0
  51. package/src/editorInjector/_classes.js +4 -0
  52. package/src/editorInjector/_core.js +17 -7
  53. package/src/editorInjector/index.js +10 -2
  54. package/src/events.js +6 -0
  55. package/src/helper/clipboard.js +24 -10
  56. package/src/helper/converter.js +17 -12
  57. package/src/helper/dom/domCheck.js +22 -3
  58. package/src/helper/dom/domQuery.js +91 -45
  59. package/src/helper/dom/domUtils.js +93 -19
  60. package/src/helper/dom/index.js +4 -0
  61. package/src/helper/env.js +11 -7
  62. package/src/helper/keyCodeMap.js +4 -3
  63. package/src/langs/ckb.js +1 -1
  64. package/src/langs/cs.js +1 -1
  65. package/src/langs/da.js +1 -1
  66. package/src/langs/de.js +1 -1
  67. package/src/langs/en.js +1 -1
  68. package/src/langs/es.js +1 -1
  69. package/src/langs/fa.js +1 -1
  70. package/src/langs/fr.js +1 -1
  71. package/src/langs/he.js +1 -1
  72. package/src/langs/hu.js +1 -1
  73. package/src/langs/it.js +1 -1
  74. package/src/langs/ja.js +1 -1
  75. package/src/langs/km.js +1 -1
  76. package/src/langs/ko.js +1 -1
  77. package/src/langs/lv.js +1 -1
  78. package/src/langs/nl.js +1 -1
  79. package/src/langs/pl.js +1 -1
  80. package/src/langs/pt_br.js +10 -10
  81. package/src/langs/ro.js +1 -1
  82. package/src/langs/ru.js +1 -1
  83. package/src/langs/se.js +1 -1
  84. package/src/langs/tr.js +1 -1
  85. package/src/langs/uk.js +1 -1
  86. package/src/langs/ur.js +1 -1
  87. package/src/langs/zh_cn.js +1 -1
  88. package/src/modules/ApiManager.js +25 -18
  89. package/src/modules/Browser.js +52 -61
  90. package/src/modules/ColorPicker.js +37 -38
  91. package/src/modules/Controller.js +85 -79
  92. package/src/modules/Figure.js +275 -187
  93. package/src/modules/FileManager.js +86 -92
  94. package/src/modules/HueSlider.js +67 -35
  95. package/src/modules/Modal.js +84 -77
  96. package/src/modules/ModalAnchorEditor.js +62 -79
  97. package/src/modules/SelectMenu.js +89 -86
  98. package/src/plugins/browser/audioGallery.js +9 -5
  99. package/src/plugins/browser/fileBrowser.js +10 -6
  100. package/src/plugins/browser/fileGallery.js +9 -5
  101. package/src/plugins/browser/imageGallery.js +9 -5
  102. package/src/plugins/browser/videoGallery.js +11 -6
  103. package/src/plugins/command/blockquote.js +1 -0
  104. package/src/plugins/command/exportPDF.js +11 -8
  105. package/src/plugins/command/fileUpload.js +41 -29
  106. package/src/plugins/command/list_bulleted.js +2 -1
  107. package/src/plugins/command/list_numbered.js +2 -1
  108. package/src/plugins/dropdown/align.js +8 -2
  109. package/src/plugins/dropdown/backgroundColor.js +19 -11
  110. package/src/plugins/dropdown/font.js +15 -9
  111. package/src/plugins/dropdown/fontColor.js +19 -11
  112. package/src/plugins/dropdown/formatBlock.js +7 -2
  113. package/src/plugins/dropdown/hr.js +7 -3
  114. package/src/plugins/dropdown/layout.js +6 -2
  115. package/src/plugins/dropdown/lineHeight.js +8 -3
  116. package/src/plugins/dropdown/list.js +2 -1
  117. package/src/plugins/dropdown/paragraphStyle.js +15 -11
  118. package/src/plugins/dropdown/{table.js → table/index.js} +514 -362
  119. package/src/plugins/dropdown/template.js +6 -2
  120. package/src/plugins/dropdown/textStyle.js +7 -3
  121. package/src/plugins/field/mention.js +33 -27
  122. package/src/plugins/input/fontSize.js +44 -37
  123. package/src/plugins/input/pageNavigator.js +3 -2
  124. package/src/plugins/modal/audio.js +90 -85
  125. package/src/plugins/modal/drawing.js +58 -66
  126. package/src/plugins/modal/embed.js +193 -180
  127. package/src/plugins/modal/image.js +441 -439
  128. package/src/plugins/modal/link.js +31 -8
  129. package/src/plugins/modal/math.js +23 -22
  130. package/src/plugins/modal/video.js +233 -230
  131. package/src/plugins/popup/anchor.js +24 -18
  132. package/src/suneditor.js +69 -24
  133. package/src/typedef.js +42 -19
  134. package/types/assets/icons/defaultIcons.d.ts +8 -0
  135. package/types/core/class/char.d.ts +1 -1
  136. package/types/core/class/component.d.ts +29 -7
  137. package/types/core/class/format.d.ts +4 -354
  138. package/types/core/class/html.d.ts +13 -4
  139. package/types/core/class/inline.d.ts +263 -0
  140. package/types/core/class/listFormat.d.ts +135 -0
  141. package/types/core/class/menu.d.ts +10 -2
  142. package/types/core/class/offset.d.ts +24 -26
  143. package/types/core/class/selection.d.ts +2 -0
  144. package/types/core/class/toolbar.d.ts +24 -11
  145. package/types/core/class/ui.d.ts +1 -1
  146. package/types/core/class/viewer.d.ts +1 -1
  147. package/types/core/config/context.d.ts +157 -0
  148. package/types/core/config/frameContext.d.ts +367 -0
  149. package/types/core/config/options.d.ts +1119 -0
  150. package/types/core/editor.d.ts +101 -66
  151. package/types/core/event/actions/index.d.ts +47 -0
  152. package/types/core/event/effects/common.registry.d.ts +50 -0
  153. package/types/core/event/effects/keydown.registry.d.ts +73 -0
  154. package/types/core/event/effects/ruleHelpers.d.ts +31 -0
  155. package/types/core/{base → event}/eventManager.d.ts +15 -46
  156. package/types/core/event/executor.d.ts +6 -0
  157. package/types/core/event/handlers/handler_ww_input.d.ts +41 -0
  158. package/types/core/{base/eventHandlers/handler_ww_key_input.d.ts → event/handlers/handler_ww_key.d.ts} +4 -6
  159. package/types/core/event/ports.d.ts +255 -0
  160. package/types/core/event/reducers/keydown.reducer.d.ts +75 -0
  161. package/types/core/event/rules/keydown.rule.arrow.d.ts +8 -0
  162. package/types/core/event/rules/keydown.rule.backspace.d.ts +9 -0
  163. package/types/core/event/rules/keydown.rule.delete.d.ts +9 -0
  164. package/types/core/event/rules/keydown.rule.enter.d.ts +9 -0
  165. package/types/core/event/rules/keydown.rule.tab.d.ts +9 -0
  166. package/types/core/section/constructor.d.ts +101 -631
  167. package/types/core/section/documentType.d.ts +14 -4
  168. package/types/core/util/instanceCheck.d.ts +50 -0
  169. package/types/editorInjector/_classes.d.ts +4 -0
  170. package/types/editorInjector/_core.d.ts +17 -7
  171. package/types/editorInjector/index.d.ts +10 -2
  172. package/types/events.d.ts +1 -0
  173. package/types/helper/clipboard.d.ts +2 -2
  174. package/types/helper/converter.d.ts +6 -9
  175. package/types/helper/dom/domCheck.d.ts +7 -0
  176. package/types/helper/dom/domQuery.d.ts +19 -8
  177. package/types/helper/dom/domUtils.d.ts +24 -2
  178. package/types/helper/dom/index.d.ts +86 -1
  179. package/types/helper/env.d.ts +6 -1
  180. package/types/helper/index.d.ts +7 -1
  181. package/types/helper/keyCodeMap.d.ts +3 -3
  182. package/types/index.d.ts +23 -117
  183. package/types/langs/index.d.ts +2 -2
  184. package/types/modules/ApiManager.d.ts +1 -8
  185. package/types/modules/Browser.d.ts +4 -62
  186. package/types/modules/ColorPicker.d.ts +4 -21
  187. package/types/modules/Controller.d.ts +8 -64
  188. package/types/modules/Figure.d.ts +54 -50
  189. package/types/modules/FileManager.d.ts +1 -13
  190. package/types/modules/HueSlider.d.ts +13 -3
  191. package/types/modules/Modal.d.ts +0 -43
  192. package/types/modules/ModalAnchorEditor.d.ts +0 -73
  193. package/types/modules/SelectMenu.d.ts +0 -85
  194. package/types/modules/index.d.ts +3 -3
  195. package/types/plugins/browser/audioGallery.d.ts +29 -18
  196. package/types/plugins/browser/fileBrowser.d.ts +38 -27
  197. package/types/plugins/browser/fileGallery.d.ts +29 -18
  198. package/types/plugins/browser/imageGallery.d.ts +24 -16
  199. package/types/plugins/browser/videoGallery.d.ts +29 -18
  200. package/types/plugins/command/blockquote.d.ts +1 -0
  201. package/types/plugins/command/exportPDF.d.ts +18 -18
  202. package/types/plugins/command/fileUpload.d.ts +65 -45
  203. package/types/plugins/command/list_bulleted.d.ts +1 -0
  204. package/types/plugins/command/list_numbered.d.ts +1 -0
  205. package/types/plugins/dropdown/align.d.ts +13 -8
  206. package/types/plugins/dropdown/backgroundColor.d.ts +30 -19
  207. package/types/plugins/dropdown/font.d.ts +13 -12
  208. package/types/plugins/dropdown/fontColor.d.ts +30 -19
  209. package/types/plugins/dropdown/formatBlock.d.ts +13 -8
  210. package/types/plugins/dropdown/hr.d.ts +15 -11
  211. package/types/plugins/dropdown/layout.d.ts +15 -11
  212. package/types/plugins/dropdown/lineHeight.d.ts +16 -11
  213. package/types/plugins/dropdown/list.d.ts +1 -0
  214. package/types/plugins/dropdown/paragraphStyle.d.ts +31 -27
  215. package/types/plugins/dropdown/table/index.d.ts +582 -0
  216. package/types/plugins/dropdown/table.d.ts +41 -86
  217. package/types/plugins/dropdown/template.d.ts +15 -11
  218. package/types/plugins/dropdown/textStyle.d.ts +19 -11
  219. package/types/plugins/field/mention.d.ts +58 -56
  220. package/types/plugins/index.d.ts +38 -38
  221. package/types/plugins/input/fontSize.d.ts +46 -50
  222. package/types/plugins/modal/audio.d.ts +26 -56
  223. package/types/plugins/modal/drawing.d.ts +0 -85
  224. package/types/plugins/modal/embed.d.ts +15 -79
  225. package/types/plugins/modal/image.d.ts +24 -136
  226. package/types/plugins/modal/link.d.ts +34 -15
  227. package/types/plugins/modal/math.d.ts +0 -16
  228. package/types/plugins/modal/video.d.ts +17 -86
  229. package/types/plugins/popup/anchor.d.ts +1 -8
  230. package/types/suneditor.d.ts +70 -19
  231. package/types/typedef.d.ts +60 -46
  232. package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
  233. package/src/core/section/context.js +0 -102
  234. package/types/core/section/context.d.ts +0 -45
  235. package/types/langs/_Lang.d.ts +0 -194
  236. /package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +0 -0
  237. /package/types/core/{section → base}/actives.d.ts +0 -0
  238. /package/types/core/{base/eventHandlers → event/handlers}/handler_toolbar.d.ts +0 -0
  239. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.d.ts +0 -0
  240. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.d.ts +0 -0
  241. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.d.ts +0 -0
@@ -3,7 +3,8 @@
3
3
  */
4
4
 
5
5
  import CoreInjector from '../../editorInjector/_core';
6
- import { dom, unicode, env } from '../../helper';
6
+ import { dom, unicode, env, numbers } from '../../helper';
7
+ const { _w, isTouchDevice } = env;
7
8
 
8
9
  /**
9
10
  * @typedef {Omit<Selection_ & Partial<__se__EditorInjector>, 'selection'>} SelectionThis
@@ -28,6 +29,13 @@ function Selection_(editor) {
28
29
  /** @type {HTMLElement|Text} */
29
30
  this.selectionNode = null;
30
31
  this.__iframeFocus = false;
32
+ this.__hasScrollParents = false;
33
+ this._scrollMargin = 0;
34
+
35
+ _w.setTimeout(() => {
36
+ this.__hasScrollParents = this.eventManager?.scrollparents.length > 0;
37
+ this._scrollMargin = !this.frameContext ? 40 : (numbers.get(_w.getComputedStyle(this.frameContext.get('wysiwyg')).scrollMargin, 0) || 40) + numbers.get(_w.getComputedStyle(this.frameContext.get('wrapper')).paddingBottom, 0);
38
+ }, 1000);
31
39
  }
32
40
 
33
41
  Selection_.prototype = {
@@ -37,19 +45,16 @@ Selection_.prototype = {
37
45
  * @returns {Selection}
38
46
  */
39
47
  get() {
40
- const wSelection = this.editor.frameContext.get('_ww').getSelection();
41
48
  let selection = null;
42
49
 
43
- if (this._shadowRoot) {
44
- selection = wSelection.getComposedRanges?.();
45
- }
46
-
47
- if (!selection) {
48
- selection = wSelection;
50
+ if (typeof this.editor._shadowRoot?.getSelection === 'function') {
51
+ selection = this.editor._shadowRoot.getSelection();
52
+ } else {
53
+ selection = this.frameContext.get('_ww').getSelection();
49
54
  }
50
55
 
51
56
  if (!selection) return null;
52
- if (!this.status._range && !this.editor.frameContext.get('wysiwyg').contains(selection.focusNode)) {
57
+ if (!this.status._range && !this.frameContext.get('wysiwyg').contains(selection.focusNode)) {
53
58
  selection.removeAllRanges();
54
59
  selection.addRange(this._createDefaultRange());
55
60
  }
@@ -64,7 +69,7 @@ Selection_.prototype = {
64
69
  */
65
70
  isRange(range) {
66
71
  // return /Range/.test(Object.prototype.toString.call(range?.__proto__));
67
- return range instanceof Range;
72
+ return this.instanceCheck.isRange(range);
68
73
  },
69
74
 
70
75
  /**
@@ -75,7 +80,7 @@ Selection_.prototype = {
75
80
  getRange() {
76
81
  const range = this.status._range || this._createDefaultRange();
77
82
  const selection = this.get();
78
- if (range.collapsed === selection.isCollapsed || !this.editor.frameContext.get('wysiwyg').contains(selection.focusNode)) {
83
+ if (range.collapsed === selection.isCollapsed || !this.frameContext.get('wysiwyg').contains(selection.focusNode)) {
79
84
  if (this.component.is(range.startContainer)) {
80
85
  const compInfo = this.component.get(range.startContainer);
81
86
  const container = compInfo?.container;
@@ -144,9 +149,11 @@ Selection_.prototype = {
144
149
  eo = eo > 0 ? (ec.nodeType === 1 && !dom.check.isBreak(ec) ? 1 : ec.textContent ? ec.textContent.length : 0) : 0;
145
150
  }
146
151
 
147
- const range = this.editor.frameContext.get('_wd').createRange();
152
+ const range = this.frameContext.get('_wd').createRange();
148
153
 
149
154
  try {
155
+ so = Math.min(so, sc.textContent?.length || 0);
156
+ eo = Math.min(eo, ec.textContent?.length || 0);
150
157
  range.setStart(sc, so);
151
158
  range.setEnd(ec, eo);
152
159
  this.status.hasFocus = true;
@@ -166,7 +173,7 @@ Selection_.prototype = {
166
173
  this.status._range = range;
167
174
  this._rangeInfo(range, this.get());
168
175
 
169
- if (this.editor.frameOptions.get('iframe')) this.__focus();
176
+ if (this.frameOptions.get('iframe')) this.__focus();
170
177
 
171
178
  return range;
172
179
  },
@@ -218,7 +225,7 @@ Selection_.prototype = {
218
225
  */
219
226
  getRangeAndAddLine(range, container) {
220
227
  if (this._isNone(range)) {
221
- const parent = container?.parentElement || this.editor.frameContext.get('wysiwyg');
228
+ const parent = container?.parentElement || this.frameContext.get('wysiwyg');
222
229
  const op = dom.utils.createElement(this.options.get('defaultLine'), null, '<br>');
223
230
  parent.insertBefore(op, container && container !== parent ? (!(/** @type {HTMLElement} */ (container).previousElementSibling) ? container : /** @type {HTMLElement} */ (container).nextElementSibling) : parent.firstElementChild);
224
231
  this.setRange(op.firstElementChild, 0, op.firstElementChild, 1);
@@ -233,9 +240,9 @@ Selection_.prototype = {
233
240
  * @returns {HTMLElement|Text}
234
241
  */
235
242
  getNode() {
236
- if (!this.editor.frameContext.get('wysiwyg').contains(this.selectionNode)) this._init();
243
+ if (!this.frameContext.get('wysiwyg').contains(this.selectionNode)) this._init();
237
244
  if (!this.selectionNode) {
238
- const selectionNode = /** @type {HTMLElement|Text} */ (dom.query.getEdgeChild(this.editor.frameContext.get('wysiwyg').firstChild, (current) => current.childNodes.length === 0 || current.nodeType === 3, false));
245
+ const selectionNode = /** @type {HTMLElement|Text} */ (dom.query.getEdgeChild(this.frameContext.get('wysiwyg').firstChild, (current) => current.childNodes.length === 0 || current.nodeType === 3, false));
239
246
  if (!selectionNode) {
240
247
  this._init();
241
248
  } else {
@@ -254,7 +261,7 @@ Selection_.prototype = {
254
261
  * @returns {{rects: RectsInfo_selection, position: "start"|"end", scrollLeft: number, scrollTop: number}}
255
262
  */
256
263
  getRects(target, position) {
257
- const targetAbs = dom.check.isElement(/** @type {Node} */ (target)) ? this._w.getComputedStyle(target).position === 'absolute' : false;
264
+ const targetAbs = dom.check.isElement(/** @type {Node} */ (target)) ? _w.getComputedStyle(target).position === 'absolute' : false;
258
265
  target = /** @type {Range} */ (!target || dom.check.isText(/** @type {Node} */ (target)) ? this.getRange() : target);
259
266
  const globalScroll = this.offset.getGlobalScroll();
260
267
  let isStartPosition = position === 'start';
@@ -291,7 +298,7 @@ Selection_.prototype = {
291
298
  isStartPosition = true;
292
299
  }
293
300
 
294
- const iframeRects = /^iframe$/i.test(this.editor.frameContext.get('wysiwygFrame').nodeName) ? this.editor.frameContext.get('wysiwygFrame').getClientRects()[0] : null;
301
+ const iframeRects = /^iframe$/i.test(this.frameContext.get('wysiwygFrame').nodeName) ? this.frameContext.get('wysiwygFrame').getClientRects()[0] : null;
295
302
  if (!targetAbs && iframeRects) {
296
303
  rects = {
297
304
  left: rects.left + iframeRects.left,
@@ -316,7 +323,7 @@ Selection_.prototype = {
316
323
  * @returns {{sc: Node, so: number, ec: Node, eo: number}} {sc: startContainer, so: startOffset, ec: endContainer, eo: endOffset}
317
324
  */
318
325
  getDragEventLocationRange(e) {
319
- const wd = this.editor.frameContext.get('_wd');
326
+ const wd = this.frameContext.get('_wd');
320
327
  let r, sc, so, ec, eo;
321
328
 
322
329
  if (wd.caretPositionFromPoint) {
@@ -348,21 +355,117 @@ Selection_.prototype = {
348
355
  * @param {?Object<string, *>=} scrollOption option of scrollTo
349
356
  */
350
357
  scrollTo(ref, scrollOption) {
351
- if (ref instanceof Selection) {
358
+ if (this.instanceCheck.isSelection(ref)) {
352
359
  ref = ref.getRangeAt(0);
353
- } else if (ref instanceof Node) {
360
+ } else if (this.instanceCheck.isNode(ref)) {
354
361
  ref = this.setRange(ref, 1, ref, 1);
355
362
  } else if (typeof ref?.startContainer === 'undefined') {
356
363
  console.warn('[SUNEDITOR.html.scrollTo.warn] "selectionRange" must be Selection or Range or Node object.', ref);
357
364
  }
358
365
 
359
- const rect = ref.getBoundingClientRect();
360
- const isVisible = rect.top >= 0 && rect.bottom <= this.editor.frameContext.get('wysiwygFrame').innerHeight;
366
+ scrollOption = { behavior: 'smooth', block: 'nearest', inline: 'nearest', ...scrollOption };
367
+ const el = dom.query.getParentElement(ref.startContainer, (current) => current.nodeType === 1);
368
+
369
+ const { frameContext, frameOptions } = this.editor;
370
+ const ww = frameContext.get('_ww');
371
+ const wwFrame = frameContext.get('wysiwygFrame');
372
+ const isIframe = frameOptions.get('iframe');
373
+ const isAutoHeight = frameOptions.get('height') === 'auto';
374
+ const initViewportHeight = this.status.initViewportHeight;
375
+ const viewportHeight = this.status.currentViewportHeight;
376
+ const scrollY = isAutoHeight ? _w.scrollY : isIframe ? ww.scrollY : wwFrame.scrollTop;
377
+ const realToolbarHeight = this.context.get('toolbar_main').offsetHeight;
378
+ const toolbarHeight = this.toolbar._sticky ? realToolbarHeight : 0;
379
+ const statusbarHeight = frameContext.get('statusbar')?.offsetHeight || 0;
380
+
381
+ if (this.__hasScrollParents || (!isIframe && (!isTouchDevice || initViewportHeight - viewportHeight < 150))) {
382
+ el?.scrollIntoView(scrollOption);
383
+
384
+ if (scrollOption?.behavior === 'auto') {
385
+ if (toolbarHeight && scrollY > _w.scrollY) {
386
+ _w.scrollBy(0, -toolbarHeight);
387
+ } else if (isAutoHeight) {
388
+ _w.scrollBy(0, statusbarHeight);
389
+ }
390
+ }
361
391
 
362
- if (isVisible) return;
392
+ return;
393
+ }
363
394
 
364
- const el = dom.query.getParentElement(ref.startContainer, (current) => current.nodeType === 1);
365
- el?.scrollIntoView?.(scrollOption || this.options.get('scrollToOptions'));
395
+ // --- When there is no upper scroll and it is an iframe ---
396
+ const PADDING = this._scrollMargin;
397
+ const viewHeight = isAutoHeight ? viewportHeight : wwFrame.offsetHeight;
398
+ const elH = el.offsetHeight || 0;
399
+
400
+ const behavior = scrollOption?.behavior;
401
+ if (isAutoHeight) {
402
+ if (isIframe) {
403
+ const rect = this.getRects(ref, 'end').rects;
404
+ const topMargin = rect.top + elH - toolbarHeight;
405
+ const bottomMargin = viewHeight - PADDING - (rect.top + elH);
406
+ if (topMargin >= 0 && bottomMargin >= 0) return;
407
+
408
+ const newScrollTop = scrollY - (topMargin < 0 ? -(topMargin - PADDING) : bottomMargin);
409
+ _w.scrollTo({
410
+ top: newScrollTop,
411
+ behavior
412
+ });
413
+ } else {
414
+ const rect = this.offset.getGlobal(el);
415
+ const scrollMargin = viewHeight + scrollY - rect.top - elH;
416
+
417
+ if (scrollMargin - PADDING > 0 && viewHeight > scrollMargin + PADDING + toolbarHeight) return;
418
+
419
+ const newScrollTop = scrollMargin <= PADDING ? scrollY - scrollMargin + PADDING + statusbarHeight : scrollY - scrollMargin + (viewHeight - elH - PADDING);
420
+ _w.scrollTo({
421
+ top: newScrollTop,
422
+ behavior
423
+ });
424
+ }
425
+ } else {
426
+ // local scroll
427
+ const { top } = this.offset.getLocal(el);
428
+
429
+ const keepLocalScroll = top - PADDING > 0 && top + PADDING <= viewHeight;
430
+ const rectScroll = top - PADDING > 0 ? top + PADDING - viewHeight : top - (toolbarHeight + elH);
431
+ let newScrollTop = scrollY + rectScroll;
432
+
433
+ // frame scroll
434
+ const gy = _w.scrollY;
435
+ const globalRect = this.offset.getGlobal();
436
+ const topMargin = gy - globalRect.top + realToolbarHeight;
437
+ const bottomMargin = globalRect.top + globalRect.height - (gy + viewportHeight) + realToolbarHeight;
438
+
439
+ // set frame scroll
440
+ if (topMargin > 0) {
441
+ const newFrameY = (keepLocalScroll ? top : top + scrollY - newScrollTop) - elH - PADDING - topMargin;
442
+ if (newFrameY < 0) {
443
+ newScrollTop += realToolbarHeight;
444
+ _w.scrollTo({
445
+ top: gy + newFrameY,
446
+ behavior: 'smooth'
447
+ });
448
+ }
449
+ }
450
+ if (bottomMargin > 0) {
451
+ const newFrameY = (keepLocalScroll ? top : top + scrollY - newScrollTop) + elH + PADDING - (globalRect.height - bottomMargin);
452
+ if (newFrameY > 0) {
453
+ newScrollTop += statusbarHeight;
454
+ _w.scrollTo({
455
+ top: gy + newFrameY,
456
+ behavior: 'smooth'
457
+ });
458
+ }
459
+ }
460
+
461
+ // set local scroll
462
+ if (!keepLocalScroll) {
463
+ (isIframe ? ww : wwFrame).scrollTo({
464
+ top: newScrollTop,
465
+ behavior
466
+ });
467
+ }
468
+ }
366
469
  },
367
470
 
368
471
  /**
@@ -389,8 +492,8 @@ Selection_.prototype = {
389
492
  * @returns {Range}
390
493
  */
391
494
  _createDefaultRange() {
392
- const wysiwyg = this.editor.frameContext.get('wysiwyg');
393
- const range = this.editor.frameContext.get('_wd').createRange();
495
+ const wysiwyg = this.frameContext.get('wysiwyg');
496
+ const range = this.frameContext.get('_wd').createRange();
394
497
 
395
498
  let firstFormat = wysiwyg.firstElementChild;
396
499
  let focusEl = null;
@@ -439,7 +542,7 @@ Selection_.prototype = {
439
542
  * @description Saving the range object and the currently selected node of editor
440
543
  */
441
544
  _init() {
442
- const activeEl = this.editor.frameContext.get('_wd').activeElement;
545
+ const activeEl = this.frameContext.get('_wd').activeElement;
443
546
  if (dom.check.isInputElement(activeEl)) {
444
547
  this.selectionNode = activeEl;
445
548
  return activeEl;
@@ -471,10 +574,10 @@ Selection_.prototype = {
471
574
  if (caption) {
472
575
  caption.focus();
473
576
  } else {
474
- this.editor.frameContext.get('wysiwyg').focus();
577
+ this.frameContext.get('wysiwyg').focus();
475
578
  }
476
579
  } finally {
477
- env._w.setTimeout(() => (this.__iframeFocus = false), 0);
580
+ _w.setTimeout(() => (this.__iframeFocus = false), 0);
478
581
  }
479
582
  },
480
583
 
@@ -525,7 +628,7 @@ Selection_.prototype = {
525
628
  }
526
629
 
527
630
  // startContainer
528
- tempCon = dom.check.isWysiwygFrame(startCon) ? this.editor.frameContext.get('wysiwyg').firstChild : startCon;
631
+ tempCon = dom.check.isWysiwygFrame(startCon) ? this.frameContext.get('wysiwyg').firstChild : startCon;
529
632
  tempOffset = startOff;
530
633
 
531
634
  if (dom.check.isBreak(tempCon) || (tempCon.nodeType === 1 && tempCon.childNodes.length > 0)) {
@@ -541,7 +644,7 @@ Selection_.prototype = {
541
644
 
542
645
  let format = this.format.getLine(tempCon, null);
543
646
  if (format === this.format.getBlock(format, null)) {
544
- tempCon = tempCon || tempConCache;
647
+ tempCon ||= tempConCache;
545
648
  format = dom.utils.createElement(dom.query.getParentElement(tempCon, dom.check.isTableCell) ? 'DIV' : this.options.get('defaultLine'));
546
649
  tempCon.parentNode.insertBefore(format, tempCon);
547
650
  if (tempCon !== tempConCache) format.appendChild(tempCon);
@@ -566,7 +669,7 @@ Selection_.prototype = {
566
669
  startOff = tempOffset;
567
670
 
568
671
  // endContainer
569
- tempCon = dom.check.isWysiwygFrame(endCon) ? this.editor.frameContext.get('wysiwyg').lastChild : endCon;
672
+ tempCon = dom.check.isWysiwygFrame(endCon) ? this.frameContext.get('wysiwyg').lastChild : endCon;
570
673
  tempOffset = endOff;
571
674
 
572
675
  if (dom.check.isBreak(tempCon) || (tempCon.nodeType === 1 && tempCon.childNodes.length > 0)) {
@@ -18,18 +18,31 @@ const { _w } = env;
18
18
  * @description Toolbar class
19
19
  * @param {__se__EditorCore} editor - The root editor instance
20
20
  * @param {Object} options - toolbar options
21
- * @param {String} options.keyName - toolbar key name
22
- * @param {Boolean} options.balloon - balloon toolbar
23
- * @param {Boolean} options.inline - inline toolbar
24
- * @param {Boolean} options.balloonAlways - balloon toolbar always show
21
+ * @param {"toolbar"|"toolbar_sub"} options.keyName - toolbar key name
22
+ * @param {boolean} options.balloon - balloon toolbar
23
+ * @param {boolean} options.inline - inline toolbar
24
+ * @param {boolean} options.balloonAlways - balloon toolbar always show
25
25
  * @param {Array<Node>} options.res - responsive toolbar button list
26
26
  */
27
27
  function Toolbar(editor, { keyName, balloon, inline, balloonAlways, res }) {
28
28
  CoreInjector.call(this, editor);
29
29
 
30
30
  // members
31
- this.keyName = keyName;
32
- this.isSub = /sub/.test(keyName);
31
+ this.isSub = keyName === 'toolbar_sub';
32
+
33
+ /**
34
+ * @type {Object}
35
+ * @description Key names for the toolbar elements.
36
+ * @property {"toolbar_sub_main"|"toolbar_main"} main - Main toolbar key name
37
+ * @property {"toolbar_sub_buttonTray"|"toolbar_buttonTray"} buttonTray - Button tray key name
38
+ * @property {"toolbar_sub_width"|"toolbar_width"} width - Toolbar width key name
39
+ */
40
+ this.keyName = {
41
+ main: this.isSub ? 'toolbar_sub_main' : 'toolbar_main',
42
+ buttonTray: this.isSub ? 'toolbar_sub_buttonTray' : 'toolbar_buttonTray',
43
+ width: this.isSub ? 'toolbar_sub_width' : 'toolbar_width'
44
+ };
45
+
33
46
  this.currentMoreLayerActiveButton = null;
34
47
  this._isBalloon = balloon;
35
48
  this._isInline = inline;
@@ -64,7 +77,7 @@ Toolbar.prototype = {
64
77
  this._moreLayerOff();
65
78
  this.menu.dropdownOff();
66
79
  this.menu.containerOff();
67
- dom.utils.setDisabled(this.context.get(this.keyName + '.buttonTray').querySelectorAll('.se-menu-list .se-toolbar-btn[data-type]'), true);
80
+ dom.utils.setDisabled(this.context.get(this.keyName.buttonTray).querySelectorAll('.se-menu-list .se-toolbar-btn[data-type]'), true);
68
81
  },
69
82
 
70
83
  /**
@@ -72,7 +85,7 @@ Toolbar.prototype = {
72
85
  * @description Enable the toolbar
73
86
  */
74
87
  enable() {
75
- dom.utils.setDisabled(this.context.get(this.keyName + '.buttonTray').querySelectorAll('.se-menu-list .se-toolbar-btn[data-type]'), false);
88
+ dom.utils.setDisabled(this.context.get(this.keyName.buttonTray).querySelectorAll('.se-menu-list .se-toolbar-btn[data-type]'), false);
76
89
  },
77
90
 
78
91
  /**
@@ -85,8 +98,8 @@ Toolbar.prototype = {
85
98
  } else if (this._isBalloon) {
86
99
  this._showBalloon();
87
100
  } else {
88
- this.context.get(this.keyName + '.main').style.display = '';
89
- if (!this.isSub) this.editor.frameContext.get('_stickyDummy').style.display = '';
101
+ this.context.get(this.keyName.main).style.display = '';
102
+ if (!this.isSub) this.frameContext.get('_stickyDummy').style.display = '';
90
103
  }
91
104
 
92
105
  if (!this.isSub) this.resetResponsiveToolbar();
@@ -98,12 +111,12 @@ Toolbar.prototype = {
98
111
  */
99
112
  hide() {
100
113
  if (this._isInline) {
101
- this.context.get(this.keyName + '.main').style.display = 'none';
102
- this.context.get(this.keyName + '.main').style.top = '0px';
114
+ this.context.get(this.keyName.main).style.display = 'none';
115
+ this.context.get(this.keyName.main).style.top = '0px';
103
116
  this._inlineToolbarAttr.isShow = false;
104
117
  } else {
105
- this.context.get(this.keyName + '.main').style.display = 'none';
106
- if (!this.isSub) this.editor.frameContext.get('_stickyDummy').style.display = 'none';
118
+ this.context.get(this.keyName.main).style.display = 'none';
119
+ if (!this.isSub) this.frameContext.get('_stickyDummy').style.display = 'none';
107
120
  if (this._isBalloon) {
108
121
  this._balloonOffset = {
109
122
  top: 0,
@@ -123,10 +136,10 @@ Toolbar.prototype = {
123
136
  const responsiveSize = this._rButtonsize;
124
137
  if (responsiveSize) {
125
138
  let w = 0;
126
- if (((this._isBalloon || this._isInline) && this.options.get('toolbar_width') === 'auto') || (this.editor.isSubBalloon && this.options.get('toolbar.sub_width') === 'auto')) {
127
- w = this.editor.frameContext.get('topArea').offsetWidth;
139
+ if (((this._isBalloon || this._isInline) && this.options.get('toolbar_width') === 'auto') || (this.editor.isSubBalloon && this.options.get('toolbar_sub_width') === 'auto')) {
140
+ w = this.frameContext.get('topArea').offsetWidth;
128
141
  } else {
129
- w = this.context.get(this.keyName + '.main').offsetWidth;
142
+ w = this.context.get(this.keyName.main).offsetWidth;
130
143
  }
131
144
 
132
145
  let responsiveWidth = 'default';
@@ -156,33 +169,40 @@ Toolbar.prototype = {
156
169
  this.menu.dropdownOff();
157
170
  this.menu.containerOff();
158
171
 
159
- const { options, icons, lang, isSub } = this;
160
- const newToolbar = CreateToolBar(buttonList, this.plugins, options, icons, lang, true);
172
+ const { icons, lang } = this;
173
+ const newToolbar = CreateToolBar(buttonList, this.plugins, this.editor.__options, icons, lang, true);
161
174
 
162
175
  newToolbar.updateButtons.forEach((v) => UpdateButton(v.button, v.plugin, this.icons, this.lang));
163
176
 
164
- let cmdButtons;
165
- if (isSub) cmdButtons = this.editor.subAllCommandButtons = new Map();
166
- else cmdButtons = this.editor.allCommandButtons = new Map();
177
+ this.context.get(this.keyName.main).replaceChild(newToolbar.buttonTray, this.context.get(this.keyName.buttonTray));
178
+ this.context.set(this.keyName.buttonTray, newToolbar.buttonTray);
179
+
180
+ this._resetButtonInfo();
181
+
182
+ this.triggerEvent('onSetToolbarButtons', { buttonTray: newToolbar.buttonTray, frameContext: this.frameContext });
183
+ },
184
+
185
+ /**
186
+ * @private
187
+ * @this {ToolbarThis}
188
+ * @description Reset the common buttons info.
189
+ */
190
+ _resetButtonInfo() {
191
+ this.editor.allCommandButtons = new Map();
192
+ this.editor.subAllCommandButtons = new Map();
167
193
  this.editor.commandTargets = new Map();
168
194
  this.editor.shortcutsKeyMap = new Map();
169
- this.editor.__saveCommandButtons(cmdButtons, newToolbar.buttonTray);
170
- this.editor.__cachingShortcuts();
171
-
172
- this.context.get(this.keyName + '.main').replaceChild(newToolbar.buttonTray, this.context.get(this.keyName + '.buttonTray'));
173
- this.context.set(this.keyName + '.buttonTray', newToolbar.buttonTray);
174
195
 
175
- this.editor.__setDisabledButtons();
196
+ this.editor.__cachingButtons(this.isSub ? 'sub' : 'main');
197
+ this.editor.__cachingShortcuts();
176
198
 
177
- this.history.resetButtons(this.editor.frameContext.get('key'), null);
199
+ this.history.resetButtons(this.frameContext.get('key'), null);
178
200
  this._resetSticky();
179
201
 
180
202
  this.editor.effectNode = null;
181
203
  this.viewer._setButtonsActive();
182
204
  if (this.status.hasFocus) this.eventManager.applyTagEffect();
183
- if (this.editor.frameContext.get('isReadOnly')) this.ui.setControllerOnDisabledButtons(true);
184
-
185
- this.triggerEvent('onSetToolbarButtons', { buttonTray: newToolbar.buttonTray, frameContext: this.editor.frameContext });
205
+ if (this.frameContext.get('isReadOnly')) this.ui.setControllerOnDisabledButtons(true);
186
206
  },
187
207
 
188
208
  /**
@@ -191,17 +211,17 @@ Toolbar.prototype = {
191
211
  * @description Reset the sticky toolbar position based on the editor state.
192
212
  */
193
213
  _resetSticky() {
194
- const wrapper = this.editor.frameContext.get('wrapper');
214
+ const wrapper = this.frameContext.get('wrapper');
195
215
  if (!wrapper) return;
196
216
 
197
- const toolbar = this.context.get(this.keyName + '.main');
198
- if (this.editor.frameContext.get('isFullScreen') || toolbar.offsetWidth === 0 || this.options.get('toolbar_sticky') < 0) return;
217
+ const toolbar = this.context.get(this.keyName.main);
218
+ if (this.frameContext.get('isFullScreen') || toolbar.offsetWidth === 0 || this.options.get('toolbar_sticky') < 0) return;
199
219
 
200
220
  const currentScrollY = this._isViewPortSize ? _w.visualViewport.pageTop : _w.scrollY;
201
221
 
202
- const minHeight = this.editor.frameContext.get('_minHeight');
222
+ const minHeight = this.frameContext.get('_minHeight');
203
223
  const editorHeight = wrapper.offsetHeight;
204
- const editorOffset = this.offset.getGlobal(this.editor.frameContext.get('topArea'));
224
+ const editorOffset = this.offset.getGlobal(this.frameContext.get('topArea'));
205
225
  const y = currentScrollY + this.options.get('toolbar_sticky');
206
226
  const t = (this._isBalloon || this._isInline ? editorOffset.top : this.offset.getGlobal(this.options.get('toolbar_container')).top) - (this._isInline ? toolbar.offsetHeight : 0);
207
227
  const inlineOffset = 1;
@@ -225,10 +245,10 @@ Toolbar.prototype = {
225
245
  * @description Enable sticky toolbar mode and adjust position.
226
246
  */
227
247
  _onSticky(inlineOffset) {
228
- const toolbar = this.context.get(this.keyName + '.main');
248
+ const toolbar = this.context.get(this.keyName.main);
229
249
 
230
250
  if (!this._isInline) {
231
- const stickyDummy = !this.options.get('toolbar_container') ? this.editor.frameContext.get('_stickyDummy') : this.context.get('_stickyDummy');
251
+ const stickyDummy = !this.options.get('toolbar_container') ? this.frameContext.get('_stickyDummy') : this.context.get('_stickyDummy');
232
252
  stickyDummy.style.height = toolbar.offsetHeight + 'px';
233
253
  stickyDummy.style.display = 'block';
234
254
  }
@@ -259,13 +279,13 @@ Toolbar.prototype = {
259
279
  * @description Disable sticky toolbar mode.
260
280
  */
261
281
  _offSticky() {
262
- const stickyDummy = !this.options.get('toolbar_container') ? this.editor.frameContext.get('_stickyDummy') : this.context.get('_stickyDummy');
282
+ const stickyDummy = !this.options.get('toolbar_container') ? this.frameContext.get('_stickyDummy') : this.context.get('_stickyDummy');
263
283
  stickyDummy.style.display = 'none';
264
284
 
265
- const toolbar = this.context.get(this.keyName + '.main');
285
+ const toolbar = this.context.get(this.keyName.main);
266
286
  toolbar.style.top = this._isInline ? this._inlineToolbarAttr.top : '';
267
287
  toolbar.style.width = this._isInline ? this._inlineToolbarAttr.width : '';
268
- this.editor.frameContext.get('wrapper').style.marginTop = '';
288
+ this.frameContext.get('wrapper').style.marginTop = '';
269
289
 
270
290
  dom.utils.removeClass(toolbar, 'se-toolbar-sticky');
271
291
  this._sticky = false;
@@ -312,7 +332,7 @@ Toolbar.prototype = {
312
332
  if (this.isSub) this.resetResponsiveToolbar();
313
333
 
314
334
  const range = rangeObj || this.selection.getRange();
315
- const toolbar = this.context.get(this.keyName + '.main');
335
+ const toolbar = this.context.get(this.keyName.main);
316
336
  const selection = this.selection.get();
317
337
 
318
338
  let isDirTop;
@@ -321,13 +341,13 @@ Toolbar.prototype = {
321
341
  } else if (selection.focusNode === selection.anchorNode) {
322
342
  isDirTop = selection.focusOffset < selection.anchorOffset;
323
343
  } else {
324
- const childNodes = dom.query.getListChildNodes(range.commonAncestorContainer, null);
344
+ const childNodes = dom.query.getListChildNodes(range.commonAncestorContainer, null, null);
325
345
  isDirTop = dom.utils.getArrayIndex(childNodes, selection.focusNode) < dom.utils.getArrayIndex(childNodes, selection.anchorNode);
326
346
  }
327
347
 
328
348
  this._setBalloonOffset(isDirTop, range);
329
349
 
330
- this.triggerEvent('onShowToolbar', { toolbar, mode: 'balloon', frameContext: this.editor.frameContext });
350
+ this.triggerEvent('onShowToolbar', { toolbar, mode: 'balloon', frameContext: this.frameContext });
331
351
  },
332
352
 
333
353
  /**
@@ -338,8 +358,8 @@ Toolbar.prototype = {
338
358
  * @param {Range} [range] - Selection range
339
359
  */
340
360
  _setBalloonOffset(positionTop, range) {
341
- const toolbar = this.context.get(this.keyName + '.main');
342
- const topArea = this.editor.frameContext.get('topArea');
361
+ const toolbar = this.context.get(this.keyName.main);
362
+ const topArea = this.frameContext.get('topArea');
343
363
  const offsets = this.offset.getGlobal(topArea);
344
364
  const stickyTop = offsets.top;
345
365
 
@@ -355,7 +375,7 @@ Toolbar.prototype = {
355
375
  let left = container.offsetLeft;
356
376
  let top = container.offsetTop;
357
377
 
358
- while (!container.parentElement.contains(editorParent) || !/^(BODY|HTML)$/i.test(container.parentElement.nodeName)) {
378
+ while (!container.parentElement.contains(editorParent) && !/^(BODY|HTML)$/i.test(container.parentElement.nodeName)) {
359
379
  container = container.offsetParent;
360
380
  left += container.offsetLeft;
361
381
  top += container.offsetTop;
@@ -381,14 +401,14 @@ Toolbar.prototype = {
381
401
  _showInline() {
382
402
  if (!this._isInline) return;
383
403
 
384
- const toolbar = this.context.get(this.keyName + '.main');
404
+ const toolbar = this.context.get(this.keyName.main);
385
405
  toolbar.style.visibility = 'hidden';
386
406
  this._offSticky();
387
407
 
388
408
  toolbar.style.display = 'block';
389
409
  toolbar.style.top = '0px';
390
- this._inlineToolbarAttr.width = toolbar.style.width = this.options.get(this.keyName + '_width');
391
- this._inlineToolbarAttr.top = toolbar.style.top = -1 + (this.offset.getGlobal(this.editor.frameContext.get('topArea')).top - this.offset.getGlobal(toolbar).top - toolbar.offsetHeight) + 'px';
410
+ this._inlineToolbarAttr.width = toolbar.style.width = this.options.get(this.keyName.width);
411
+ this._inlineToolbarAttr.top = toolbar.style.top = -1 + (this.offset.getGlobal(this.frameContext.get('topArea')).top - this.offset.getGlobal(toolbar).top - toolbar.offsetHeight) + 'px';
392
412
 
393
413
  this._resetSticky();
394
414
  this._inlineToolbarAttr.isShow = true;
@@ -418,7 +438,8 @@ Toolbar.prototype = {
418
438
  */
419
439
  _moreLayerOff() {
420
440
  if (this.currentMoreLayerActiveButton) {
421
- const layer = this.context.get(this.keyName + '.main').querySelector('.' + this.currentMoreLayerActiveButton.getAttribute('data-command'));
441
+ /** @type {HTMLElement} */
442
+ const layer = this.context.get(this.keyName.main).querySelector('.' + this.currentMoreLayerActiveButton.getAttribute('data-command'));
422
443
  layer.style.display = 'none';
423
444
  dom.utils.removeClass(this.currentMoreLayerActiveButton, 'on');
424
445
  this.currentMoreLayerActiveButton = null;