suneditor 3.1.1 → 3.1.3
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/README.md +1 -1
- package/dist/suneditor-contents.min.css +1 -1
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +10 -7
- package/src/assets/design/size.css +2 -1
- package/src/assets/suneditor.css +75 -23
- package/src/core/editor.js +1 -0
- package/src/core/event/actions/index.js +2 -1
- package/src/core/event/effects/keydown.registry.js +3 -1
- package/src/core/event/effects/ruleHelpers.js +30 -1
- package/src/core/event/handlers/handler_ww_dragDrop.js +20 -0
- package/src/core/event/rules/keydown.rule.arrow.js +22 -16
- package/src/core/event/rules/keydown.rule.backspace.js +18 -5
- package/src/core/event/rules/keydown.rule.delete.js +7 -5
- package/src/core/event/rules/keydown.rule.enter.js +33 -3
- package/src/core/logic/panel/menu.js +4 -0
- package/src/core/logic/shell/ui.js +52 -3
- package/src/core/schema/options.js +1 -1
- package/src/core/section/constructor.js +44 -13
- package/src/core/section/documentType.js +55 -29
- package/src/modules/contract/Browser.js +3 -0
- package/src/modules/contract/Controller.js +3 -0
- package/src/modules/contract/Figure.js +23 -4
- package/src/modules/contract/Modal.js +2 -0
- package/src/plugins/dropdown/table/services/table.style.js +21 -12
- package/types/core/event/actions/index.d.ts +1 -1
- package/types/core/event/effects/keydown.registry.d.ts +1 -1
- package/types/core/event/effects/ruleHelpers.d.ts +12 -0
- package/types/core/logic/shell/ui.d.ts +2 -2
- package/types/core/schema/options.d.ts +2 -2
- package/types/core/section/documentType.d.ts +17 -1
|
@@ -89,6 +89,7 @@ class UIManager {
|
|
|
89
89
|
|
|
90
90
|
// toast
|
|
91
91
|
const toastPopup = CreateToastHTML();
|
|
92
|
+
toastPopup.setAttribute('popover', 'manual');
|
|
92
93
|
this.toastPopup = toastPopup;
|
|
93
94
|
this.toastContainer = toastPopup.querySelector('.se-toast-container');
|
|
94
95
|
this.toastMessage = toastPopup.querySelector('span');
|
|
@@ -200,14 +201,27 @@ class UIManager {
|
|
|
200
201
|
|
|
201
202
|
/**
|
|
202
203
|
* @description Set direction to `rtl` or `ltr`.
|
|
203
|
-
* @param {
|
|
204
|
+
* @param {"rtl"|"ltr"} dir `rtl` or `ltr`
|
|
204
205
|
*/
|
|
205
206
|
setDir(dir) {
|
|
206
207
|
const rtl = dir === 'rtl';
|
|
207
208
|
if (this.#options.get('_rtl') === rtl) return;
|
|
208
209
|
|
|
210
|
+
const prevDir = this.#options.get('textDirection');
|
|
211
|
+
const prevEditableClass = this.#options.get('_editableClass');
|
|
212
|
+
const prevPrintClass = this.#options.get('printClass');
|
|
213
|
+
|
|
209
214
|
try {
|
|
210
215
|
this.#options.set('_rtl', rtl);
|
|
216
|
+
this.#options.set('textDirection', dir);
|
|
217
|
+
|
|
218
|
+
// update _editableClass / printClass
|
|
219
|
+
const editableClass = rtl ? this.#options.get('_editableClass').replace(/\s*se-rtl/, '') + ' se-rtl' : this.#options.get('_editableClass').replace(/\s*se-rtl/, '');
|
|
220
|
+
this.#options.set('_editableClass', editableClass);
|
|
221
|
+
if (this.#options.get('printClass')) {
|
|
222
|
+
this.#options.set('printClass', rtl ? this.#options.get('printClass').replace(/\s*se-rtl/, '') + ' se-rtl' : this.#options.get('printClass').replace(/\s*se-rtl/, ''));
|
|
223
|
+
}
|
|
224
|
+
|
|
211
225
|
this.offCurrentController();
|
|
212
226
|
|
|
213
227
|
const fc = this.#frameContext;
|
|
@@ -221,11 +235,13 @@ class UIManager {
|
|
|
221
235
|
if (rtl) {
|
|
222
236
|
this.#contextProvider.applyToRoots((e) => {
|
|
223
237
|
dom.utils.addClass([e.get('topArea'), e.get('wysiwyg'), e.get('documentTypePageMirror')], 'se-rtl');
|
|
238
|
+
e.get('wysiwyg').dir = 'rtl';
|
|
224
239
|
});
|
|
225
240
|
dom.utils.addClass([this.#carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
|
|
226
241
|
} else {
|
|
227
242
|
this.#contextProvider.applyToRoots((e) => {
|
|
228
243
|
dom.utils.removeClass([e.get('topArea'), e.get('wysiwyg'), e.get('documentTypePageMirror')], 'se-rtl');
|
|
244
|
+
e.get('wysiwyg').removeAttribute('dir');
|
|
229
245
|
});
|
|
230
246
|
dom.utils.removeClass([this.#carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
|
|
231
247
|
}
|
|
@@ -255,6 +271,12 @@ class UIManager {
|
|
|
255
271
|
|
|
256
272
|
this.#activeDirBtn(rtl);
|
|
257
273
|
|
|
274
|
+
// reverse toolbar buttons
|
|
275
|
+
this.#reverseToolbarButtons(this.#context.get('toolbar_buttonTray'));
|
|
276
|
+
if (this.#context.has('toolbar_sub_buttonTray')) {
|
|
277
|
+
this.#reverseToolbarButtons(this.#context.get('toolbar_sub_buttonTray'));
|
|
278
|
+
}
|
|
279
|
+
|
|
258
280
|
// document type
|
|
259
281
|
if (fc.has('documentType_use_header')) {
|
|
260
282
|
if (rtl) fc.get('wrapper').appendChild(fc.get('documentTypeInner'));
|
|
@@ -269,6 +291,9 @@ class UIManager {
|
|
|
269
291
|
else if (this.#store.mode.isSubBalloon) this.#$.subToolbar._showBalloon();
|
|
270
292
|
} catch (e) {
|
|
271
293
|
this.#options.set('_rtl', !rtl);
|
|
294
|
+
this.#options.set('textDirection', prevDir);
|
|
295
|
+
this.#options.set('_editableClass', prevEditableClass);
|
|
296
|
+
if (prevPrintClass !== null) this.#options.set('printClass', prevPrintClass);
|
|
272
297
|
console.warn(`[SUNEDITOR.ui.setDir.fail] ${e.toString()}`);
|
|
273
298
|
}
|
|
274
299
|
|
|
@@ -372,7 +397,9 @@ class UIManager {
|
|
|
372
397
|
* @param {string} [rootKey] Root key
|
|
373
398
|
*/
|
|
374
399
|
showLoading(rootKey) {
|
|
375
|
-
/** @type {HTMLElement} */ ((rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector('.se-loading-box'))
|
|
400
|
+
const el = /** @type {HTMLElement} */ ((rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector('.se-loading-box'));
|
|
401
|
+
el.style.display = 'block';
|
|
402
|
+
el.showPopover?.();
|
|
376
403
|
}
|
|
377
404
|
|
|
378
405
|
/**
|
|
@@ -380,7 +407,9 @@ class UIManager {
|
|
|
380
407
|
* @param {string} [rootKey] Root key
|
|
381
408
|
*/
|
|
382
409
|
hideLoading(rootKey) {
|
|
383
|
-
/** @type {HTMLElement} */ ((rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector('.se-loading-box'))
|
|
410
|
+
const el = /** @type {HTMLElement} */ ((rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector('.se-loading-box'));
|
|
411
|
+
el.hidePopover?.();
|
|
412
|
+
el.style.display = 'none';
|
|
384
413
|
}
|
|
385
414
|
|
|
386
415
|
/**
|
|
@@ -399,6 +428,7 @@ class UIManager {
|
|
|
399
428
|
this.#bindClose = this.#eventManager.addGlobalEvent('keydown', this.#closeListener[0]);
|
|
400
429
|
|
|
401
430
|
this.#alertArea.style.display = 'block';
|
|
431
|
+
this.#alertArea.showPopover?.();
|
|
402
432
|
dom.utils.addClass(this.alertModal, 'se-modal-show');
|
|
403
433
|
}
|
|
404
434
|
|
|
@@ -408,6 +438,7 @@ class UIManager {
|
|
|
408
438
|
alertClose() {
|
|
409
439
|
dom.utils.removeClass(this.alertModal, 'se-modal-show');
|
|
410
440
|
dom.utils.removeClass(this.alertModal, 'se-alert-*');
|
|
441
|
+
this.#alertArea.hidePopover?.();
|
|
411
442
|
this.#alertArea.style.display = 'none';
|
|
412
443
|
this.#bindAlertClick &&= this.#eventManager.removeEvent(this.#bindAlertClick);
|
|
413
444
|
this.#bindClose &&= this.#eventManager.removeGlobalEvent(this.#bindClose);
|
|
@@ -428,6 +459,7 @@ class UIManager {
|
|
|
428
459
|
if (type) dom.utils.addClass(this.toastPopup, `se-toast-${type}`);
|
|
429
460
|
|
|
430
461
|
this.toastPopup.style.display = 'block';
|
|
462
|
+
this.toastPopup.showPopover?.();
|
|
431
463
|
this.toastMessage.textContent = message;
|
|
432
464
|
dom.utils.addClass(this.toastContainer, 'se-toast-show');
|
|
433
465
|
|
|
@@ -444,6 +476,7 @@ class UIManager {
|
|
|
444
476
|
if (this.#toastToggle) _w.clearTimeout(this.#toastToggle);
|
|
445
477
|
this.#toastToggle = null;
|
|
446
478
|
dom.utils.removeClass(this.toastContainer, 'se-toast-show');
|
|
479
|
+
this.toastPopup.hidePopover?.();
|
|
447
480
|
this.toastPopup.style.display = 'none';
|
|
448
481
|
}
|
|
449
482
|
|
|
@@ -485,12 +518,14 @@ class UIManager {
|
|
|
485
518
|
enableBackWrapper(cursor) {
|
|
486
519
|
this.#backWrapper.style.cursor = cursor;
|
|
487
520
|
this.#backWrapper.style.display = 'block';
|
|
521
|
+
this.#backWrapper.showPopover?.();
|
|
488
522
|
}
|
|
489
523
|
|
|
490
524
|
/**
|
|
491
525
|
* @description Disabled background `div`
|
|
492
526
|
*/
|
|
493
527
|
disableBackWrapper() {
|
|
528
|
+
this.#backWrapper.hidePopover?.();
|
|
494
529
|
this.#backWrapper.style.display = 'none';
|
|
495
530
|
this.#backWrapper.style.cursor = 'default';
|
|
496
531
|
}
|
|
@@ -702,6 +737,20 @@ class UIManager {
|
|
|
702
737
|
}
|
|
703
738
|
}
|
|
704
739
|
|
|
740
|
+
/**
|
|
741
|
+
* @description Reverse the order of toolbar button groups (excluding the more-layer).
|
|
742
|
+
* @param {HTMLElement} buttonTray - The `.se-btn-tray` element.
|
|
743
|
+
*/
|
|
744
|
+
#reverseToolbarButtons(buttonTray) {
|
|
745
|
+
if (!buttonTray) return;
|
|
746
|
+
const moreLayer = buttonTray.querySelector('.se-toolbar-more-layer');
|
|
747
|
+
const children = Array.from(buttonTray.children).filter((c) => c !== moreLayer);
|
|
748
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
749
|
+
buttonTray.appendChild(children[i]);
|
|
750
|
+
}
|
|
751
|
+
if (moreLayer) buttonTray.appendChild(moreLayer);
|
|
752
|
+
}
|
|
753
|
+
|
|
705
754
|
/**
|
|
706
755
|
* @internal
|
|
707
756
|
* @description Set the disabled button list
|
|
@@ -6,6 +6,7 @@ import { dom, numbers, converter, env } from '../../helper';
|
|
|
6
6
|
import { DEFAULTS } from '../schema/options';
|
|
7
7
|
|
|
8
8
|
const _d = env._d;
|
|
9
|
+
let editorInstanceId = 0;
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {import('../schema/options').AllBaseOptions} AllBaseOptions_constructor
|
|
@@ -57,16 +58,21 @@ function Constructor(editorTargets, options) {
|
|
|
57
58
|
const icons = optionMap.i;
|
|
58
59
|
const lang = optionMap.l;
|
|
59
60
|
const loadingBox = dom.utils.createElement('DIV', { class: 'se-loading-box sun-editor-common' }, '<div class="se-loading-effect"></div>');
|
|
61
|
+
const editorFormFieldPrefix = 'suneditor-' + ++editorInstanceId;
|
|
60
62
|
|
|
61
63
|
/** --- carrier wrapper --------------------------------------------------------------- */
|
|
62
64
|
const editor_carrier_wrapper = dom.utils.createElement('DIV', { class: 'sun-editor sun-editor-carrier-wrapper sun-editor-common' + o.get('_themeClass') + (o.get('_rtl') ? ' se-rtl' : '') });
|
|
63
65
|
// menuTray
|
|
64
|
-
const menuTray = dom.utils.createElement('DIV', { class: 'se-menu-tray' });
|
|
66
|
+
const menuTray = dom.utils.createElement('DIV', { class: 'se-menu-tray', popover: 'manual' });
|
|
65
67
|
editor_carrier_wrapper.appendChild(menuTray);
|
|
66
68
|
// focus temp element
|
|
67
69
|
const focusTemp = /** @type {HTMLInputElement} */ (
|
|
68
70
|
dom.utils.createElement('INPUT', {
|
|
71
|
+
type: 'text',
|
|
72
|
+
id: editorFormFieldPrefix + '-focus-temp',
|
|
69
73
|
class: '__se__focus__temp__',
|
|
74
|
+
autocomplete: 'off',
|
|
75
|
+
'aria-hidden': 'true',
|
|
70
76
|
style: 'position: fixed !important; top: -10000px !important; left: -10000px !important; display: block !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important;',
|
|
71
77
|
})
|
|
72
78
|
);
|
|
@@ -74,7 +80,7 @@ function Constructor(editorTargets, options) {
|
|
|
74
80
|
editor_carrier_wrapper.appendChild(focusTemp);
|
|
75
81
|
|
|
76
82
|
// modal
|
|
77
|
-
const modal = dom.utils.createElement('DIV', { class: 'se-modal se-modal-area sun-editor-common' });
|
|
83
|
+
const modal = dom.utils.createElement('DIV', { class: 'se-modal se-modal-area sun-editor-common', popover: 'manual' });
|
|
78
84
|
const modal_back = dom.utils.createElement('DIV', { class: 'se-modal-back' });
|
|
79
85
|
const modal_inner = dom.utils.createElement('DIV', { class: 'se-modal-inner' });
|
|
80
86
|
modal.appendChild(modal_back);
|
|
@@ -82,7 +88,7 @@ function Constructor(editorTargets, options) {
|
|
|
82
88
|
editor_carrier_wrapper.appendChild(modal);
|
|
83
89
|
|
|
84
90
|
// alert
|
|
85
|
-
const alert = dom.utils.createElement('DIV', { class: 'se-alert se-modal-area sun-editor-common', style: 'display: none;' });
|
|
91
|
+
const alert = dom.utils.createElement('DIV', { class: 'se-alert se-modal-area sun-editor-common', style: 'display: none;', popover: 'manual' });
|
|
86
92
|
const alert_back = dom.utils.createElement('DIV', { class: 'se-modal-back' });
|
|
87
93
|
const alert_inner = dom.utils.createElement('DIV', { class: 'se-modal-inner' });
|
|
88
94
|
alert.appendChild(alert_back);
|
|
@@ -90,11 +96,13 @@ function Constructor(editorTargets, options) {
|
|
|
90
96
|
editor_carrier_wrapper.appendChild(alert);
|
|
91
97
|
|
|
92
98
|
// loding box, resizing back
|
|
93
|
-
editor_carrier_wrapper.appendChild(dom.utils.createElement('DIV', { class: 'se-back-wrapper' }));
|
|
94
|
-
|
|
99
|
+
editor_carrier_wrapper.appendChild(dom.utils.createElement('DIV', { class: 'se-back-wrapper', popover: 'manual' }));
|
|
100
|
+
const loadingBoxEl = /** @type {HTMLElement} */ (loadingBox.cloneNode(true));
|
|
101
|
+
loadingBoxEl.setAttribute('popover', 'manual');
|
|
102
|
+
editor_carrier_wrapper.appendChild(loadingBoxEl);
|
|
95
103
|
|
|
96
104
|
// drag cursor
|
|
97
|
-
const dragCursor = dom.utils.createElement('DIV', { class: 'se-drag-cursor' });
|
|
105
|
+
const dragCursor = dom.utils.createElement('DIV', { class: 'se-drag-cursor', popover: 'manual' });
|
|
98
106
|
editor_carrier_wrapper.appendChild(dragCursor);
|
|
99
107
|
|
|
100
108
|
// set carrier wrapper
|
|
@@ -146,7 +154,7 @@ function Constructor(editorTargets, options) {
|
|
|
146
154
|
container.appendChild(dom.utils.createElement('DIV', { class: 'se-toolbar-shadow' }));
|
|
147
155
|
|
|
148
156
|
// init element
|
|
149
|
-
const initElements = _initTargetElements(editTarget.key, o, top_div, to);
|
|
157
|
+
const initElements = _initTargetElements(editTarget.key, o, top_div, to, editorFormFieldPrefix);
|
|
150
158
|
const bottomBar = initElements.bottomBar;
|
|
151
159
|
const statusbar = bottomBar.statusbar;
|
|
152
160
|
const wysiwyg_div = initElements.wysiwygFrame;
|
|
@@ -195,7 +203,11 @@ function Constructor(editorTargets, options) {
|
|
|
195
203
|
// not used code mirror
|
|
196
204
|
if (textarea === codeMirrorEl) {
|
|
197
205
|
// add line nubers
|
|
198
|
-
const codeNumbers = dom.utils.createElement(
|
|
206
|
+
const codeNumbers = dom.utils.createElement(
|
|
207
|
+
'TEXTAREA',
|
|
208
|
+
{ id: editorFormFieldPrefix + '-code-view-line-' + (key || 'default'), class: 'se-code-view-line', readonly: 'true', autocomplete: 'off', 'aria-hidden': 'true', tabindex: '-1' },
|
|
209
|
+
null,
|
|
210
|
+
);
|
|
199
211
|
codeWrapper.insertBefore(codeNumbers, textarea);
|
|
200
212
|
} else {
|
|
201
213
|
textarea = codeMirrorEl;
|
|
@@ -207,7 +219,11 @@ function Constructor(editorTargets, options) {
|
|
|
207
219
|
let markdownTextarea = null;
|
|
208
220
|
if (o.get('buttons').has('markdownView')) {
|
|
209
221
|
markdownTextarea = initElements.markdownView;
|
|
210
|
-
const markdownNumbers = dom.utils.createElement(
|
|
222
|
+
const markdownNumbers = dom.utils.createElement(
|
|
223
|
+
'TEXTAREA',
|
|
224
|
+
{ id: editorFormFieldPrefix + '-markdown-view-line-' + (key || 'default'), class: 'se-markdown-view-line', readonly: 'true', autocomplete: 'off', 'aria-hidden': 'true', tabindex: '-1' },
|
|
225
|
+
null,
|
|
226
|
+
);
|
|
211
227
|
markdownWrapper = dom.utils.createElement('DIV', { class: 'se-markdown-wrapper' });
|
|
212
228
|
markdownWrapper.appendChild(markdownNumbers);
|
|
213
229
|
markdownWrapper.appendChild(markdownTextarea);
|
|
@@ -361,8 +377,21 @@ export function CreateShortcuts(command, button, values, keyMap, rc, reverseKeys
|
|
|
361
377
|
}
|
|
362
378
|
}
|
|
363
379
|
|
|
380
|
+
/**
|
|
381
|
+
* @description Append tooltip span
|
|
382
|
+
* @param {Element} tooptipBtn
|
|
383
|
+
* @param {boolean} shift
|
|
384
|
+
* @param {string} shortcut
|
|
385
|
+
*/
|
|
364
386
|
function _addTooltip(tooptipBtn, shift, shortcut) {
|
|
365
|
-
|
|
387
|
+
const tooltip = dom.utils.createElement('SPAN', { class: 'se-shortcut' }, env.cmdIcon + (shift ? env.shiftIcon : '') + '+<span class="se-shortcut-key">' + shortcut + '</span>');
|
|
388
|
+
const prevTooltip = tooptipBtn.querySelector('.se-shortcut');
|
|
389
|
+
|
|
390
|
+
if (prevTooltip) {
|
|
391
|
+
tooptipBtn.replaceChild(tooltip, prevTooltip);
|
|
392
|
+
} else {
|
|
393
|
+
tooptipBtn.appendChild(tooltip);
|
|
394
|
+
}
|
|
366
395
|
}
|
|
367
396
|
|
|
368
397
|
/**
|
|
@@ -921,9 +950,10 @@ function InitFrameOptions(o, origin) {
|
|
|
921
950
|
* @param {Map<string, *>} options - Options
|
|
922
951
|
* @param {HTMLElement} topDiv - Top div
|
|
923
952
|
* @param {SunEditor.FrameOptions} targetOptions - `editor.frameOptions`
|
|
953
|
+
* @param {string} formFieldPrefix - Prefix for generated form field ids
|
|
924
954
|
* @returns {{bottomBar: ReturnType<CreateStatusbar>, wysiwygFrame: HTMLElement, codeView: HTMLElement, markdownView: HTMLElement, placeholder: HTMLElement}}
|
|
925
955
|
*/
|
|
926
|
-
function _initTargetElements(key, options, topDiv, targetOptions) {
|
|
956
|
+
function _initTargetElements(key, options, topDiv, targetOptions, formFieldPrefix) {
|
|
927
957
|
const editorStyles = targetOptions.get('_defaultStyles');
|
|
928
958
|
/** top div */
|
|
929
959
|
topDiv.style.cssText = editorStyles.top;
|
|
@@ -937,6 +967,7 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
|
|
|
937
967
|
|
|
938
968
|
if (!targetOptions.get('iframe')) {
|
|
939
969
|
wysiwygDiv.setAttribute('contenteditable', 'true');
|
|
970
|
+
if (options.get('_rtl')) wysiwygDiv.dir = 'rtl';
|
|
940
971
|
wysiwygDiv.className += ' ' + options.get('_editableClass');
|
|
941
972
|
wysiwygDiv.style.cssText = editorStyles.frame + editorStyles.editor;
|
|
942
973
|
} else {
|
|
@@ -976,10 +1007,10 @@ function _initTargetElements(key, options, topDiv, targetOptions) {
|
|
|
976
1007
|
}
|
|
977
1008
|
|
|
978
1009
|
// textarea for code view
|
|
979
|
-
const textarea = dom.utils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-code-viewer', style: editorStyles.frame });
|
|
1010
|
+
const textarea = dom.utils.createElement('TEXTAREA', { id: formFieldPrefix + '-code-viewer-' + (key || 'default'), class: 'se-wrapper-inner se-code-viewer', style: editorStyles.frame, autocomplete: 'off' });
|
|
980
1011
|
|
|
981
1012
|
// textarea for markdown view
|
|
982
|
-
const markdownTextarea = dom.utils.createElement('TEXTAREA', { class: 'se-wrapper-inner se-markdown-viewer', style: editorStyles.frame });
|
|
1013
|
+
const markdownTextarea = dom.utils.createElement('TEXTAREA', { id: formFieldPrefix + '-markdown-viewer-' + (key || 'default'), class: 'se-wrapper-inner se-markdown-viewer', style: editorStyles.frame, autocomplete: 'off' });
|
|
983
1014
|
|
|
984
1015
|
const placeholder = dom.utils.createElement('SPAN', { class: 'se-placeholder' });
|
|
985
1016
|
if (targetOptions.get('placeholder')) {
|
|
@@ -162,28 +162,19 @@ class DocumentType {
|
|
|
162
162
|
|
|
163
163
|
// page break
|
|
164
164
|
let pageBreakHeight = 0;
|
|
165
|
-
|
|
166
|
-
let additionalPages = 0;
|
|
165
|
+
const breakPoints = [];
|
|
167
166
|
if (pageBreaks.length > 0) {
|
|
168
167
|
pageBreakHeight = pageBreaks[0].offsetHeight;
|
|
169
168
|
for (let i = 0; i < pageBreaks.length; i++) {
|
|
170
|
-
|
|
171
|
-
const sectionHeight = breakPosition - lastBreakPosition;
|
|
172
|
-
if (sectionHeight % A4_PAGE_HEIGHT !== 0) additionalPages++;
|
|
173
|
-
lastBreakPosition = breakPosition;
|
|
169
|
+
breakPoints.push({ top: pageBreaks[i].offsetTop, end: pageBreaks[i].offsetTop + pageBreakHeight / 2 });
|
|
174
170
|
}
|
|
175
|
-
|
|
176
|
-
const lastSectionHeight = mirrorHeight - lastBreakPosition;
|
|
177
|
-
if (lastSectionHeight > 0 && lastSectionHeight % A4_PAGE_HEIGHT !== 0) additionalPages++;
|
|
178
171
|
}
|
|
179
172
|
|
|
180
173
|
const scrollTop = !this.#isScrollable(this.#fc) ? 0 : this._getWWScrollTop();
|
|
181
|
-
const
|
|
182
|
-
const wwWidth = this.#wwFrame.offsetWidth + 1;
|
|
183
|
-
const pages = [];
|
|
174
|
+
const pages = [{ number: 0, top: 0 }];
|
|
184
175
|
|
|
185
|
-
for (let i = 0; i <
|
|
186
|
-
pages.push({ number:
|
|
176
|
+
for (let i = 0; i < breakPoints.length; i++) {
|
|
177
|
+
pages.push({ number: 0, top: breakPoints[i].top + pageBreakHeight / 2, isBreak: true });
|
|
187
178
|
}
|
|
188
179
|
|
|
189
180
|
this.#mirrorCache = 0;
|
|
@@ -191,14 +182,27 @@ class DocumentType {
|
|
|
191
182
|
const mChr = this.#mirror.children;
|
|
192
183
|
this._initializeCache(mChr);
|
|
193
184
|
|
|
194
|
-
|
|
185
|
+
// Calculate page positions per section (between page breaks)
|
|
186
|
+
const sectionStarts = [0, ...breakPoints.map((b) => b.end)];
|
|
187
|
+
const sectionEnds = [...breakPoints.map((b) => b.top), mirrorHeight];
|
|
188
|
+
|
|
189
|
+
for (let s = 0; s < sectionStarts.length; s++) {
|
|
190
|
+
const sStart = sectionStarts[s];
|
|
191
|
+
const sEnd = sectionEnds[s];
|
|
192
|
+
let t = sStart;
|
|
193
|
+
let isFirst = s === 0;
|
|
195
194
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (
|
|
201
|
-
|
|
195
|
+
while (true) {
|
|
196
|
+
t += A4_PAGE_HEIGHT + (isFirst ? this.#paddingTop + this.#paddingBottom : this.#paddingTop);
|
|
197
|
+
isFirst = false;
|
|
198
|
+
|
|
199
|
+
if (t >= sEnd) break;
|
|
200
|
+
|
|
201
|
+
if (!pages.some((p) => Math.abs(p.top - t) < 3)) {
|
|
202
|
+
const top = this._calcPageBreakTop(t, chr, mChr, pages);
|
|
203
|
+
if (top === null) break;
|
|
204
|
+
pages.push({ number: 0, top });
|
|
205
|
+
}
|
|
202
206
|
}
|
|
203
207
|
}
|
|
204
208
|
|
|
@@ -214,8 +218,8 @@ class DocumentType {
|
|
|
214
218
|
this.#page.innerHTML = '';
|
|
215
219
|
this.#pages = [];
|
|
216
220
|
|
|
217
|
-
|
|
218
|
-
|
|
221
|
+
const wwWidth = this.#wwFrame.offsetWidth + 1;
|
|
222
|
+
for (let i = 0, t; i < pages.length; i++) {
|
|
219
223
|
t = pages[i].top;
|
|
220
224
|
if (mirrorHeight < t) break;
|
|
221
225
|
|
|
@@ -249,10 +253,11 @@ class DocumentType {
|
|
|
249
253
|
* @param {number} t - The initial top position value to be adjusted.
|
|
250
254
|
* @param {HTMLCollection} chr - The elements array in the current (main) page.
|
|
251
255
|
* @param {HTMLCollection} mChr - The elements array in the mirrored page.
|
|
256
|
+
* @param {Array.<{number: number, top: number, isBreak?: boolean}>} [pages] - The pages array containing page break info.
|
|
252
257
|
* @returns {number|null} The adjusted top value.
|
|
253
258
|
*/
|
|
254
|
-
_calcPageBreakTop(t, chr, mChr) {
|
|
255
|
-
const { ci } = this._getElementAtPosition(t, mChr);
|
|
259
|
+
_calcPageBreakTop(t, chr, mChr, pages) {
|
|
260
|
+
const { ci } = this._getElementAtPosition(t, mChr, pages);
|
|
256
261
|
const mel = /** @type {HTMLElement} */ (mChr[ci]);
|
|
257
262
|
const el = /** @type {HTMLElement} */ (chr[ci]);
|
|
258
263
|
if (!mel || !el) return null;
|
|
@@ -290,22 +295,39 @@ class DocumentType {
|
|
|
290
295
|
* @description Retrieves the element at a given position.
|
|
291
296
|
* @param {number} pageTop - The vertical position to check.
|
|
292
297
|
* @param {HTMLCollection} mChr - List of mirrored elements.
|
|
298
|
+
* @param {Array.<{number: number, top: number, isBreak?: boolean}>} [pages] - The pages array containing page break info for skipping break elements.
|
|
293
299
|
* @returns {{ci: number, cm: number, ch: number}} The closest element and its related data.
|
|
294
300
|
* - ci: The index of the closest element.
|
|
295
301
|
* - cm: The distance between the top of the closest element and the given position.
|
|
296
302
|
* - ch: The height of the closest element.
|
|
297
303
|
*/
|
|
298
|
-
_getElementAtPosition(pageTop, mChr) {
|
|
304
|
+
_getElementAtPosition(pageTop, mChr, pages) {
|
|
299
305
|
let start = this.#mirrorCache;
|
|
300
306
|
let end = mChr.length - 1;
|
|
301
307
|
|
|
308
|
+
// Reset cache if target position is before cached position (crossing section boundaries)
|
|
309
|
+
if (start > 0) {
|
|
310
|
+
const cachedPos = this.#positionCache.get(start);
|
|
311
|
+
if (cachedPos && pageTop < cachedPos.top) {
|
|
312
|
+
start = 0;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
302
316
|
while (start <= end) {
|
|
303
317
|
const mid = Math.floor((start + end) / 2);
|
|
304
318
|
const { top, height, bottom } = this.#positionCache.get(mid);
|
|
305
319
|
|
|
306
320
|
if (pageTop >= top && pageTop <= bottom) {
|
|
307
|
-
|
|
308
|
-
|
|
321
|
+
let ci = mid;
|
|
322
|
+
// Skip page break elements — use adjacent content element
|
|
323
|
+
if (pages && dom.utils.hasClass(mChr[ci], 'se-page-break')) {
|
|
324
|
+
ci = ci + 1 < mChr.length ? ci + 1 : Math.max(0, ci - 1);
|
|
325
|
+
const adjPos = this.#positionCache.get(ci);
|
|
326
|
+
this.#mirrorCache = ci;
|
|
327
|
+
return { ci, cm: pageTop - adjPos.bottom, ch: adjPos.height };
|
|
328
|
+
}
|
|
329
|
+
this.#mirrorCache = ci;
|
|
330
|
+
return { ci, cm: pageTop - bottom, ch: height };
|
|
309
331
|
}
|
|
310
332
|
|
|
311
333
|
if (pageTop < top) {
|
|
@@ -315,7 +337,11 @@ class DocumentType {
|
|
|
315
337
|
}
|
|
316
338
|
}
|
|
317
339
|
|
|
318
|
-
|
|
340
|
+
let closestIndex = mChr[start] ? start : end;
|
|
341
|
+
// Skip page break elements for closest match
|
|
342
|
+
if (pages && dom.utils.hasClass(mChr[closestIndex], 'se-page-break')) {
|
|
343
|
+
closestIndex = closestIndex + 1 < mChr.length ? closestIndex + 1 : Math.max(0, closestIndex - 1);
|
|
344
|
+
}
|
|
319
345
|
this.#mirrorCache = closestIndex;
|
|
320
346
|
const iElement = this.#positionCache.get(closestIndex);
|
|
321
347
|
return { ci: closestIndex, cm: pageTop - iElement.bottom, ch: iElement.height };
|
|
@@ -151,6 +151,7 @@ class Browser {
|
|
|
151
151
|
// init
|
|
152
152
|
browserFrame.appendChild(dom.utils.createElement('DIV', { class: 'se-browser-back' }));
|
|
153
153
|
browserFrame.appendChild(content);
|
|
154
|
+
browserFrame.setAttribute('popover', 'manual');
|
|
154
155
|
this.#$.contextProvider.carrierWrapper.appendChild(browserFrame);
|
|
155
156
|
|
|
156
157
|
this.#$.eventManager.addEvent(this.tagArea, 'click', this.#OnClickTag.bind(this));
|
|
@@ -197,6 +198,7 @@ class Browser {
|
|
|
197
198
|
|
|
198
199
|
this.titleArea.textContent = params.title || this.title;
|
|
199
200
|
this.area.style.display = 'block';
|
|
201
|
+
this.area.showPopover?.();
|
|
200
202
|
this.#$.ui.opendBrowser = this;
|
|
201
203
|
this.closeArrow = this.#$.options.get('_rtl') ? this.#$.icons.menu_arrow_left : this.#$.icons.menu_arrow_right;
|
|
202
204
|
|
|
@@ -217,6 +219,7 @@ class Browser {
|
|
|
217
219
|
this.#removeGlobalEvent();
|
|
218
220
|
this.apiManager.cancel();
|
|
219
221
|
|
|
222
|
+
this.area.hidePopover?.();
|
|
220
223
|
this.area.style.display = 'none';
|
|
221
224
|
this.selectedTags = [];
|
|
222
225
|
this.items = [];
|
|
@@ -110,6 +110,7 @@ class Controller {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// add element
|
|
113
|
+
this.form.setAttribute('popover', 'manual');
|
|
113
114
|
this.#$.contextProvider.carrierWrapper.appendChild(element);
|
|
114
115
|
|
|
115
116
|
// init
|
|
@@ -394,6 +395,7 @@ class Controller {
|
|
|
394
395
|
* @description Hide controller at editor area (link button, image resize button..)
|
|
395
396
|
*/
|
|
396
397
|
#controllerOff() {
|
|
398
|
+
this.form.hidePopover?.();
|
|
397
399
|
this.form.style.display = 'none';
|
|
398
400
|
this.#$.ui.opendControllers = this.#$.ui.opendControllers.filter((v) => v.form !== this.form);
|
|
399
401
|
if (this.#$.ui.currentControllerName !== this.kind && this.#$.ui.opendControllers.length > 0) return;
|
|
@@ -464,6 +466,7 @@ class Controller {
|
|
|
464
466
|
|
|
465
467
|
controller.style.zIndex = this.toTop ? INDEX_0 : this.#reserveIndex ? INDEX_S_1 : INDEX_1;
|
|
466
468
|
controller.style.visibility = '';
|
|
469
|
+
controller.showPopover?.();
|
|
467
470
|
return true;
|
|
468
471
|
}
|
|
469
472
|
|
|
@@ -282,10 +282,18 @@ class Figure {
|
|
|
282
282
|
const cover = dom.query.getParentElement(element, 'FIGURE', 2);
|
|
283
283
|
const inlineCover = dom.query.getParentElement(element, 'SPAN', 2);
|
|
284
284
|
const anyCover = cover || inlineCover;
|
|
285
|
-
|
|
285
|
+
let target = dom.query.getParentElement(element, (current) => current.parentElement === anyCover, 0) || element;
|
|
286
|
+
|
|
287
|
+
// When image is wrapped by anchor, target becomes <a> instead of <img>
|
|
288
|
+
if (dom.check.isAnchor(target)) {
|
|
289
|
+
const imgEl = target.querySelector(':scope > img');
|
|
290
|
+
if (imgEl) {
|
|
291
|
+
target = imgEl;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
286
294
|
|
|
287
295
|
return {
|
|
288
|
-
target,
|
|
296
|
+
target: /** @type {HTMLElement} */ (target),
|
|
289
297
|
container: dom.query.getParentElement(target, Figure.is, 3) || cover,
|
|
290
298
|
cover: cover,
|
|
291
299
|
inlineCover: dom.utils.hasClass(inlineCover, 'se-inline-component') ? /** @type {HTMLElement} */ (inlineCover) : null,
|
|
@@ -734,12 +742,23 @@ class Figure {
|
|
|
734
742
|
const { container, inlineCover, target } = Figure.GetContainer(targetNode);
|
|
735
743
|
const { w, h } = this.getSize(target);
|
|
736
744
|
|
|
745
|
+
// Check if target is wrapped by an anchor
|
|
746
|
+
const anchorEl = dom.check.isAnchor(target.parentNode) ? target.parentNode : null;
|
|
747
|
+
|
|
737
748
|
const newTarget = /** @type {HTMLElement} */ (target.cloneNode(false));
|
|
738
749
|
newTarget.style.width = '';
|
|
739
750
|
newTarget.style.height = '';
|
|
740
751
|
newTarget.removeAttribute('width');
|
|
741
752
|
newTarget.removeAttribute('height');
|
|
742
753
|
|
|
754
|
+
// Preserve anchor wrapper if exists
|
|
755
|
+
let elementToInsert = newTarget;
|
|
756
|
+
if (anchorEl) {
|
|
757
|
+
const newAnchor = /** @type {HTMLElement} */ (anchorEl.cloneNode(false));
|
|
758
|
+
newAnchor.appendChild(newTarget);
|
|
759
|
+
elementToInsert = newAnchor;
|
|
760
|
+
}
|
|
761
|
+
|
|
743
762
|
switch (formatStyle) {
|
|
744
763
|
case 'inline': {
|
|
745
764
|
if (inlineCover) break;
|
|
@@ -748,7 +767,7 @@ class Figure {
|
|
|
748
767
|
const next = container.nextElementSibling;
|
|
749
768
|
const parent = container.parentElement;
|
|
750
769
|
|
|
751
|
-
const figure = Figure.CreateInlineContainer(
|
|
770
|
+
const figure = Figure.CreateInlineContainer(elementToInsert);
|
|
752
771
|
dom.utils.addClass(
|
|
753
772
|
figure.container,
|
|
754
773
|
container.className
|
|
@@ -777,7 +796,7 @@ class Figure {
|
|
|
777
796
|
dom.utils.removeItem(s.previousElementSibling);
|
|
778
797
|
}
|
|
779
798
|
|
|
780
|
-
const figure = Figure.CreateContainer(
|
|
799
|
+
const figure = Figure.CreateContainer(elementToInsert);
|
|
781
800
|
dom.utils.addClass(
|
|
782
801
|
figure.container,
|
|
783
802
|
container.className
|
|
@@ -162,6 +162,7 @@ class Modal {
|
|
|
162
162
|
|
|
163
163
|
dom.utils.addClass(this.#modalArea, 'se-backdrop-show');
|
|
164
164
|
dom.utils.addClass(this.form, 'se-modal-show');
|
|
165
|
+
this.#modalArea.showPopover?.();
|
|
165
166
|
|
|
166
167
|
if (this.#resizeBody) {
|
|
167
168
|
const offset = this.#saveOffset();
|
|
@@ -197,6 +198,7 @@ class Modal {
|
|
|
197
198
|
this.#bindClose &&= this.#$.eventManager.removeGlobalEvent(this.#bindClose);
|
|
198
199
|
|
|
199
200
|
// close
|
|
201
|
+
this.#modalArea.hidePopover?.();
|
|
200
202
|
dom.utils.removeClass(this.#modalArea, 'se-backdrop-show');
|
|
201
203
|
dom.utils.removeClass(this.form, 'se-modal-show');
|
|
202
204
|
|
|
@@ -598,16 +598,25 @@ export class TableStyleService {
|
|
|
598
598
|
this._propsCache = [];
|
|
599
599
|
|
|
600
600
|
for (let i = 0, t, isBreak; (t = targets[i]); i++) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
601
|
+
const {
|
|
602
|
+
cssText: t_cssText,
|
|
603
|
+
border: t_border,
|
|
604
|
+
backgroundColor: t_backgroundColor,
|
|
605
|
+
color: t_color,
|
|
606
|
+
textAlign: t_textAlign,
|
|
607
|
+
verticalAlign: t_verticalAlign,
|
|
608
|
+
fontWeight: t_fontWeight,
|
|
609
|
+
textDecoration: t_textDecoration,
|
|
610
|
+
fontStyle: t_fontStyle,
|
|
611
|
+
} = t.style;
|
|
612
|
+
this._propsCache.push([t, t_cssText]);
|
|
604
613
|
if (isBreak) continue;
|
|
605
614
|
|
|
606
|
-
const { c, s, w } = this.#getBorderStyle(
|
|
615
|
+
const { c, s, w } = this.#getBorderStyle(t_border);
|
|
607
616
|
|
|
608
617
|
// use getComputedStyle to normalize any CSS color format to rgb
|
|
609
|
-
let hexBackColor =
|
|
610
|
-
let hexColor =
|
|
618
|
+
let hexBackColor = t_backgroundColor;
|
|
619
|
+
let hexColor = t_color;
|
|
611
620
|
if (hexBackColor || hexColor) {
|
|
612
621
|
const computed = _w.getComputedStyle(t);
|
|
613
622
|
if (hexBackColor) hexBackColor = computed.backgroundColor;
|
|
@@ -619,12 +628,12 @@ export class TableStyleService {
|
|
|
619
628
|
if (b_width && cellBorder.w !== w) b_width = '';
|
|
620
629
|
if (backColor !== converter.rgb2hex(hexBackColor)) backColor = '';
|
|
621
630
|
if (fontColor !== converter.rgb2hex(hexColor)) fontColor = '';
|
|
622
|
-
if (align !== (isTable ? this.#state.figureElement?.style.float :
|
|
623
|
-
if (align_v && align_v !==
|
|
624
|
-
if (bold && bold !== /.+/.test(
|
|
625
|
-
if (underline && underline !== /underline/i.test(
|
|
626
|
-
if (strike && strike !== /line-through/i.test(
|
|
627
|
-
if (italic && italic !== /italic/i.test(
|
|
631
|
+
if (align !== (isTable ? this.#state.figureElement?.style.float : t_textAlign)) align = '';
|
|
632
|
+
if (align_v && align_v !== t_verticalAlign) align_v = '';
|
|
633
|
+
if (bold && bold !== /.+/.test(t_fontWeight)) bold = false;
|
|
634
|
+
if (underline && underline !== /underline/i.test(t_textDecoration)) underline = false;
|
|
635
|
+
if (strike && strike !== /line-through/i.test(t_textDecoration)) strike = false;
|
|
636
|
+
if (italic && italic !== /italic/i.test(t_fontStyle)) italic = false;
|
|
628
637
|
if (!b_color || !b_style || !b_width || !backColor || !fontColor) {
|
|
629
638
|
isBreak = true;
|
|
630
639
|
}
|
|
@@ -31,7 +31,7 @@ export namespace A {
|
|
|
31
31
|
function enterFormatCleanBrAndZWS(selectionNode: Node, selectionFormat: boolean, brBlock: Element, children: NodeList, offset: number): Action;
|
|
32
32
|
function enterFormatInsertBrHtml(brBlock: Element, range: Range, wSelection: Selection, offset: number): Action;
|
|
33
33
|
function enterFormatInsertBrNode(wSelection: Selection): Action;
|
|
34
|
-
function enterFormatBreakAtEdge(formatEl: Element, selectionNode: Node, formatStartEdge: boolean, formatEndEdge: boolean): Action;
|
|
34
|
+
function enterFormatBreakAtEdge(formatEl: Element, selectionNode: Node, formatStartEdge: boolean, formatEndEdge: boolean, bidiSwapped?: boolean): Action;
|
|
35
35
|
function enterFormatBreakWithSelection(formatEl: Element, range: Range, formatStartEdge: boolean, formatEndEdge: boolean): Action;
|
|
36
36
|
function enterFormatBreakAtCursor(formatEl: Element, range: Range): Action;
|
|
37
37
|
function enterFigcaptionExitInList(formatEl: Element): Action;
|