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.
- package/CONTRIBUTING.md +8 -8
- package/README.md +44 -49
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +95 -53
- package/src/assets/design/color.css +2 -2
- package/src/assets/design/size.css +2 -0
- package/src/assets/icons/defaultIcons.js +16 -1
- package/src/assets/suneditor-contents.css +9 -8
- package/src/assets/suneditor.css +29 -26
- package/src/core/{section → base}/actives.js +20 -12
- package/src/core/base/history.js +4 -4
- package/src/core/class/char.js +10 -10
- package/src/core/class/component.js +146 -57
- package/src/core/class/format.js +94 -2458
- package/src/core/class/html.js +187 -129
- package/src/core/class/inline.js +1853 -0
- package/src/core/class/listFormat.js +582 -0
- package/src/core/class/menu.js +14 -3
- package/src/core/class/nodeTransform.js +9 -14
- package/src/core/class/offset.js +162 -197
- package/src/core/class/selection.js +137 -34
- package/src/core/class/toolbar.js +73 -52
- package/src/core/class/ui.js +11 -11
- package/src/core/class/viewer.js +56 -55
- package/src/core/config/context.js +122 -0
- package/src/core/config/frameContext.js +204 -0
- package/src/core/config/options.js +639 -0
- package/src/core/editor.js +181 -108
- package/src/core/event/actions/index.js +229 -0
- package/src/core/event/effects/common.registry.js +60 -0
- package/src/core/event/effects/keydown.registry.js +551 -0
- package/src/core/event/effects/ruleHelpers.js +145 -0
- package/src/core/{base → event}/eventManager.js +119 -201
- package/src/core/event/executor.js +21 -0
- package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +4 -4
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +2 -2
- package/src/core/event/handlers/handler_ww_input.js +77 -0
- package/src/core/event/handlers/handler_ww_key.js +228 -0
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.js +3 -3
- package/src/core/event/ports.js +211 -0
- package/src/core/event/reducers/keydown.reducer.js +89 -0
- package/src/core/event/rules/keydown.rule.arrow.js +54 -0
- package/src/core/event/rules/keydown.rule.backspace.js +202 -0
- package/src/core/event/rules/keydown.rule.delete.js +126 -0
- package/src/core/event/rules/keydown.rule.enter.js +144 -0
- package/src/core/event/rules/keydown.rule.tab.js +29 -0
- package/src/core/section/constructor.js +79 -388
- package/src/core/section/documentType.js +47 -26
- package/src/core/util/instanceCheck.js +59 -0
- package/src/editorInjector/_classes.js +4 -0
- package/src/editorInjector/_core.js +17 -7
- package/src/editorInjector/index.js +10 -2
- package/src/events.js +6 -0
- package/src/helper/clipboard.js +24 -10
- package/src/helper/converter.js +17 -12
- package/src/helper/dom/domCheck.js +22 -3
- package/src/helper/dom/domQuery.js +91 -45
- package/src/helper/dom/domUtils.js +93 -19
- package/src/helper/dom/index.js +4 -0
- package/src/helper/env.js +11 -7
- package/src/helper/keyCodeMap.js +4 -3
- package/src/langs/ckb.js +1 -1
- package/src/langs/cs.js +1 -1
- package/src/langs/da.js +1 -1
- package/src/langs/de.js +1 -1
- package/src/langs/en.js +1 -1
- package/src/langs/es.js +1 -1
- package/src/langs/fa.js +1 -1
- package/src/langs/fr.js +1 -1
- package/src/langs/he.js +1 -1
- package/src/langs/hu.js +1 -1
- package/src/langs/it.js +1 -1
- package/src/langs/ja.js +1 -1
- package/src/langs/km.js +1 -1
- package/src/langs/ko.js +1 -1
- package/src/langs/lv.js +1 -1
- package/src/langs/nl.js +1 -1
- package/src/langs/pl.js +1 -1
- package/src/langs/pt_br.js +10 -10
- package/src/langs/ro.js +1 -1
- package/src/langs/ru.js +1 -1
- package/src/langs/se.js +1 -1
- package/src/langs/tr.js +1 -1
- package/src/langs/uk.js +1 -1
- package/src/langs/ur.js +1 -1
- package/src/langs/zh_cn.js +1 -1
- package/src/modules/ApiManager.js +25 -18
- package/src/modules/Browser.js +52 -61
- package/src/modules/ColorPicker.js +37 -38
- package/src/modules/Controller.js +85 -79
- package/src/modules/Figure.js +275 -187
- package/src/modules/FileManager.js +86 -92
- package/src/modules/HueSlider.js +67 -35
- package/src/modules/Modal.js +84 -77
- package/src/modules/ModalAnchorEditor.js +62 -79
- package/src/modules/SelectMenu.js +89 -86
- package/src/plugins/browser/audioGallery.js +9 -5
- package/src/plugins/browser/fileBrowser.js +10 -6
- package/src/plugins/browser/fileGallery.js +9 -5
- package/src/plugins/browser/imageGallery.js +9 -5
- package/src/plugins/browser/videoGallery.js +11 -6
- package/src/plugins/command/blockquote.js +1 -0
- package/src/plugins/command/exportPDF.js +11 -8
- package/src/plugins/command/fileUpload.js +41 -29
- package/src/plugins/command/list_bulleted.js +2 -1
- package/src/plugins/command/list_numbered.js +2 -1
- package/src/plugins/dropdown/align.js +8 -2
- package/src/plugins/dropdown/backgroundColor.js +19 -11
- package/src/plugins/dropdown/font.js +15 -9
- package/src/plugins/dropdown/fontColor.js +19 -11
- package/src/plugins/dropdown/formatBlock.js +7 -2
- package/src/plugins/dropdown/hr.js +7 -3
- package/src/plugins/dropdown/layout.js +6 -2
- package/src/plugins/dropdown/lineHeight.js +8 -3
- package/src/plugins/dropdown/list.js +2 -1
- package/src/plugins/dropdown/paragraphStyle.js +15 -11
- package/src/plugins/dropdown/{table.js → table/index.js} +514 -362
- package/src/plugins/dropdown/template.js +6 -2
- package/src/plugins/dropdown/textStyle.js +7 -3
- package/src/plugins/field/mention.js +33 -27
- package/src/plugins/input/fontSize.js +44 -37
- package/src/plugins/input/pageNavigator.js +3 -2
- package/src/plugins/modal/audio.js +90 -85
- package/src/plugins/modal/drawing.js +58 -66
- package/src/plugins/modal/embed.js +193 -180
- package/src/plugins/modal/image.js +441 -439
- package/src/plugins/modal/link.js +31 -8
- package/src/plugins/modal/math.js +23 -22
- package/src/plugins/modal/video.js +233 -230
- package/src/plugins/popup/anchor.js +24 -18
- package/src/suneditor.js +69 -24
- package/src/typedef.js +42 -19
- package/types/assets/icons/defaultIcons.d.ts +8 -0
- package/types/core/class/char.d.ts +1 -1
- package/types/core/class/component.d.ts +29 -7
- package/types/core/class/format.d.ts +4 -354
- package/types/core/class/html.d.ts +13 -4
- package/types/core/class/inline.d.ts +263 -0
- package/types/core/class/listFormat.d.ts +135 -0
- package/types/core/class/menu.d.ts +10 -2
- package/types/core/class/offset.d.ts +24 -26
- package/types/core/class/selection.d.ts +2 -0
- package/types/core/class/toolbar.d.ts +24 -11
- package/types/core/class/ui.d.ts +1 -1
- package/types/core/class/viewer.d.ts +1 -1
- package/types/core/config/context.d.ts +157 -0
- package/types/core/config/frameContext.d.ts +367 -0
- package/types/core/config/options.d.ts +1119 -0
- package/types/core/editor.d.ts +101 -66
- package/types/core/event/actions/index.d.ts +47 -0
- package/types/core/event/effects/common.registry.d.ts +50 -0
- package/types/core/event/effects/keydown.registry.d.ts +73 -0
- package/types/core/event/effects/ruleHelpers.d.ts +31 -0
- package/types/core/{base → event}/eventManager.d.ts +15 -46
- package/types/core/event/executor.d.ts +6 -0
- package/types/core/event/handlers/handler_ww_input.d.ts +41 -0
- package/types/core/{base/eventHandlers/handler_ww_key_input.d.ts → event/handlers/handler_ww_key.d.ts} +4 -6
- package/types/core/event/ports.d.ts +255 -0
- package/types/core/event/reducers/keydown.reducer.d.ts +75 -0
- package/types/core/event/rules/keydown.rule.arrow.d.ts +8 -0
- package/types/core/event/rules/keydown.rule.backspace.d.ts +9 -0
- package/types/core/event/rules/keydown.rule.delete.d.ts +9 -0
- package/types/core/event/rules/keydown.rule.enter.d.ts +9 -0
- package/types/core/event/rules/keydown.rule.tab.d.ts +9 -0
- package/types/core/section/constructor.d.ts +101 -631
- package/types/core/section/documentType.d.ts +14 -4
- package/types/core/util/instanceCheck.d.ts +50 -0
- package/types/editorInjector/_classes.d.ts +4 -0
- package/types/editorInjector/_core.d.ts +17 -7
- package/types/editorInjector/index.d.ts +10 -2
- package/types/events.d.ts +1 -0
- package/types/helper/clipboard.d.ts +2 -2
- package/types/helper/converter.d.ts +6 -9
- package/types/helper/dom/domCheck.d.ts +7 -0
- package/types/helper/dom/domQuery.d.ts +19 -8
- package/types/helper/dom/domUtils.d.ts +24 -2
- package/types/helper/dom/index.d.ts +86 -1
- package/types/helper/env.d.ts +6 -1
- package/types/helper/index.d.ts +7 -1
- package/types/helper/keyCodeMap.d.ts +3 -3
- package/types/index.d.ts +23 -117
- package/types/langs/index.d.ts +2 -2
- package/types/modules/ApiManager.d.ts +1 -8
- package/types/modules/Browser.d.ts +4 -62
- package/types/modules/ColorPicker.d.ts +4 -21
- package/types/modules/Controller.d.ts +8 -64
- package/types/modules/Figure.d.ts +54 -50
- package/types/modules/FileManager.d.ts +1 -13
- package/types/modules/HueSlider.d.ts +13 -3
- package/types/modules/Modal.d.ts +0 -43
- package/types/modules/ModalAnchorEditor.d.ts +0 -73
- package/types/modules/SelectMenu.d.ts +0 -85
- package/types/modules/index.d.ts +3 -3
- package/types/plugins/browser/audioGallery.d.ts +29 -18
- package/types/plugins/browser/fileBrowser.d.ts +38 -27
- package/types/plugins/browser/fileGallery.d.ts +29 -18
- package/types/plugins/browser/imageGallery.d.ts +24 -16
- package/types/plugins/browser/videoGallery.d.ts +29 -18
- package/types/plugins/command/blockquote.d.ts +1 -0
- package/types/plugins/command/exportPDF.d.ts +18 -18
- package/types/plugins/command/fileUpload.d.ts +65 -45
- package/types/plugins/command/list_bulleted.d.ts +1 -0
- package/types/plugins/command/list_numbered.d.ts +1 -0
- package/types/plugins/dropdown/align.d.ts +13 -8
- package/types/plugins/dropdown/backgroundColor.d.ts +30 -19
- package/types/plugins/dropdown/font.d.ts +13 -12
- package/types/plugins/dropdown/fontColor.d.ts +30 -19
- package/types/plugins/dropdown/formatBlock.d.ts +13 -8
- package/types/plugins/dropdown/hr.d.ts +15 -11
- package/types/plugins/dropdown/layout.d.ts +15 -11
- package/types/plugins/dropdown/lineHeight.d.ts +16 -11
- package/types/plugins/dropdown/list.d.ts +1 -0
- package/types/plugins/dropdown/paragraphStyle.d.ts +31 -27
- package/types/plugins/dropdown/table/index.d.ts +582 -0
- package/types/plugins/dropdown/table.d.ts +41 -86
- package/types/plugins/dropdown/template.d.ts +15 -11
- package/types/plugins/dropdown/textStyle.d.ts +19 -11
- package/types/plugins/field/mention.d.ts +58 -56
- package/types/plugins/index.d.ts +38 -38
- package/types/plugins/input/fontSize.d.ts +46 -50
- package/types/plugins/modal/audio.d.ts +26 -56
- package/types/plugins/modal/drawing.d.ts +0 -85
- package/types/plugins/modal/embed.d.ts +15 -79
- package/types/plugins/modal/image.d.ts +24 -136
- package/types/plugins/modal/link.d.ts +34 -15
- package/types/plugins/modal/math.d.ts +0 -16
- package/types/plugins/modal/video.d.ts +17 -86
- package/types/plugins/popup/anchor.d.ts +1 -8
- package/types/suneditor.d.ts +70 -19
- package/types/typedef.d.ts +60 -46
- package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
- package/src/core/section/context.js +0 -102
- package/types/core/section/context.d.ts +0 -45
- package/types/langs/_Lang.d.ts +0 -194
- /package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +0 -0
- /package/types/core/{section → base}/actives.d.ts +0 -0
- /package/types/core/{base/eventHandlers → event/handlers}/handler_toolbar.d.ts +0 -0
- /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.d.ts +0 -0
- /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.d.ts +0 -0
- /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.d.ts +0 -0
|
@@ -31,6 +31,13 @@ const { NO_EVENT } = env;
|
|
|
31
31
|
* @property {boolean} [keepFormatType=false] - Whether to retain the chosen format type after image insertion.
|
|
32
32
|
* @property {boolean} [linkEnableFileUpload] - Whether to enable file uploads for linked images.
|
|
33
33
|
* @property {FigureControls_image} [controls] - Figure controls.
|
|
34
|
+
* @property {__se__ComponentInsertBehaviorType} [insertBehavior] - Component insertion behavior for selection and cursor placement. [default: options.get('componentInsertBehavior')]
|
|
35
|
+
* - For inline components: places the cursor near the inserted component or selects it if no nearby range is available.
|
|
36
|
+
* - For block components: executes behavior based on `selectMode`:
|
|
37
|
+
* - `auto`: Move cursor to the next line if possible, otherwise select the component.
|
|
38
|
+
* - `select`: Always select the inserted component.
|
|
39
|
+
* - `line`: Move cursor to the next line if possible, or create a new line and move there.
|
|
40
|
+
* - `none`: Do nothing.
|
|
34
41
|
*/
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -48,10 +55,26 @@ class Image_ extends EditorInjector {
|
|
|
48
55
|
* @returns {Element|null} Returns a node if the node is a valid component.
|
|
49
56
|
*/
|
|
50
57
|
static component(node) {
|
|
51
|
-
const compNode = dom.check.isFigure(node) || (/^span$/i.test(node.nodeName) && dom.
|
|
58
|
+
const compNode = dom.check.isFigure(node) || (/^span$/i.test(node.nodeName) && dom.check.isComponentContainer(node)) ? node.firstElementChild : node;
|
|
52
59
|
return /^IMG$/i.test(compNode?.nodeName) ? compNode : dom.check.isAnchor(compNode) && /^IMG$/i.test(compNode?.firstElementChild?.nodeName) ? compNode?.firstElementChild : null;
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
#produceIndex;
|
|
63
|
+
#linkElement;
|
|
64
|
+
#linkValue;
|
|
65
|
+
#align;
|
|
66
|
+
#svgDefaultSize;
|
|
67
|
+
#element;
|
|
68
|
+
#cover;
|
|
69
|
+
#container;
|
|
70
|
+
#caption;
|
|
71
|
+
#ratio;
|
|
72
|
+
#origin_w;
|
|
73
|
+
#origin_h;
|
|
74
|
+
#resizing;
|
|
75
|
+
#onlyPercentage;
|
|
76
|
+
#nonResizing;
|
|
77
|
+
|
|
55
78
|
/**
|
|
56
79
|
* @constructor
|
|
57
80
|
* @param {__se__EditorCore} editor - The root editor instance
|
|
@@ -79,7 +102,8 @@ class Image_ extends EditorInjector {
|
|
|
79
102
|
acceptedFormats: typeof pluginOptions.acceptedFormats !== 'string' || pluginOptions.acceptedFormats.trim() === '*' ? 'image/*' : pluginOptions.acceptedFormats.trim() || 'image/*',
|
|
80
103
|
useFormatType: pluginOptions.useFormatType ?? true,
|
|
81
104
|
defaultFormatType: ['block', 'inline'].includes(pluginOptions.defaultFormatType) ? pluginOptions.defaultFormatType : 'block',
|
|
82
|
-
keepFormatType: pluginOptions.keepFormatType ?? false
|
|
105
|
+
keepFormatType: pluginOptions.keepFormatType ?? false,
|
|
106
|
+
insertBehavior: pluginOptions.insertBehavior
|
|
83
107
|
};
|
|
84
108
|
|
|
85
109
|
// create HTML
|
|
@@ -87,12 +111,13 @@ class Image_ extends EditorInjector {
|
|
|
87
111
|
const modalEl = CreateHTML_modal(editor, this.pluginOptions);
|
|
88
112
|
const ctrlAs = this.pluginOptions.useFormatType ? 'as' : '';
|
|
89
113
|
const figureControls =
|
|
90
|
-
pluginOptions.controls ||
|
|
114
|
+
pluginOptions.controls ||
|
|
115
|
+
(!this.pluginOptions.canResize
|
|
91
116
|
? [[ctrlAs, 'mirror_h', 'mirror_v', 'align', 'caption', 'edit', 'revert', 'copy', 'remove']]
|
|
92
117
|
: [
|
|
93
118
|
[ctrlAs, 'resize_auto,100,75,50', 'rotate_l', 'rotate_r', 'mirror_h', 'mirror_v'],
|
|
94
|
-
['
|
|
95
|
-
|
|
119
|
+
['edit', 'align', 'caption', 'revert', 'copy', 'remove']
|
|
120
|
+
]);
|
|
96
121
|
|
|
97
122
|
// show align
|
|
98
123
|
this.alignForm = modalEl.alignForm;
|
|
@@ -101,13 +126,9 @@ class Image_ extends EditorInjector {
|
|
|
101
126
|
// modules
|
|
102
127
|
const Link = this.plugins.link ? this.plugins.link.pluginOptions : {};
|
|
103
128
|
this.anchor = new ModalAnchorEditor(this, modalEl.html, {
|
|
129
|
+
...Link,
|
|
104
130
|
textToDisplay: false,
|
|
105
|
-
title: true
|
|
106
|
-
openNewWindow: Link.openNewWindow,
|
|
107
|
-
relList: Link.relList,
|
|
108
|
-
defaultRel: Link.defaultRel,
|
|
109
|
-
noAutoPrefix: Link.noAutoPrefix,
|
|
110
|
-
enableFileUpload: pluginOptions.linkEnableFileUpload
|
|
131
|
+
title: true
|
|
111
132
|
});
|
|
112
133
|
this.modal = new Modal(this, modalEl.html);
|
|
113
134
|
this.figure = new Figure(this, figureControls, {
|
|
@@ -133,24 +154,23 @@ class Image_ extends EditorInjector {
|
|
|
133
154
|
this.proportion = null;
|
|
134
155
|
this.inputX = null;
|
|
135
156
|
this.inputY = null;
|
|
136
|
-
this._linkElement = null;
|
|
137
|
-
this._linkValue = '';
|
|
138
|
-
this._align = 'none';
|
|
139
|
-
this._svgDefaultSize = '30%';
|
|
140
157
|
this._base64RenderIndex = 0;
|
|
141
|
-
|
|
142
|
-
this
|
|
143
|
-
this
|
|
144
|
-
this
|
|
145
|
-
this
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this
|
|
150
|
-
this
|
|
151
|
-
this
|
|
152
|
-
this
|
|
153
|
-
this
|
|
158
|
+
|
|
159
|
+
this.#produceIndex = 0;
|
|
160
|
+
this.#linkElement = null;
|
|
161
|
+
this.#linkValue = '';
|
|
162
|
+
this.#align = 'none';
|
|
163
|
+
this.#svgDefaultSize = '30%';
|
|
164
|
+
this.#element = null;
|
|
165
|
+
this.#cover = null;
|
|
166
|
+
this.#container = null;
|
|
167
|
+
this.#caption = null;
|
|
168
|
+
this.#ratio = { w: 0, h: 0 };
|
|
169
|
+
this.#origin_w = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
|
|
170
|
+
this.#origin_h = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
|
|
171
|
+
this.#resizing = this.pluginOptions.canResize;
|
|
172
|
+
this.#onlyPercentage = this.pluginOptions.percentageOnlySize;
|
|
173
|
+
this.#nonResizing = !this.#resizing || !this.pluginOptions.showHeightInput || this.#onlyPercentage;
|
|
154
174
|
|
|
155
175
|
// init
|
|
156
176
|
this.eventManager.addEvent(modalEl.tabs, 'click', this.#OpenTab.bind(this));
|
|
@@ -161,7 +181,7 @@ class Image_ extends EditorInjector {
|
|
|
161
181
|
const galleryButton = modalEl.galleryButton;
|
|
162
182
|
if (galleryButton) this.eventManager.addEvent(galleryButton, 'click', this.#OpenGallery.bind(this));
|
|
163
183
|
|
|
164
|
-
if (this
|
|
184
|
+
if (this.#resizing) {
|
|
165
185
|
this.proportion = modalEl.proportion;
|
|
166
186
|
this.inputX = modalEl.inputX;
|
|
167
187
|
this.inputY = modalEl.inputY;
|
|
@@ -190,6 +210,7 @@ class Image_ extends EditorInjector {
|
|
|
190
210
|
* @description Executes the method that is called when a "Modal" module's is opened.
|
|
191
211
|
*/
|
|
192
212
|
open() {
|
|
213
|
+
this.#produceIndex = 0;
|
|
193
214
|
this.modal.open();
|
|
194
215
|
}
|
|
195
216
|
|
|
@@ -208,9 +229,9 @@ class Image_ extends EditorInjector {
|
|
|
208
229
|
*/
|
|
209
230
|
on(isUpdate) {
|
|
210
231
|
if (!isUpdate) {
|
|
211
|
-
if (this
|
|
212
|
-
this.inputX.value = this
|
|
213
|
-
this.inputY.value = this
|
|
232
|
+
if (this.#resizing) {
|
|
233
|
+
this.inputX.value = this.#origin_w = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
|
|
234
|
+
this.inputY.value = this.#origin_h = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
|
|
214
235
|
}
|
|
215
236
|
if (this.imgInputFile && this.pluginOptions.allowMultiple) this.imgInputFile.setAttribute('multiple', 'multiple');
|
|
216
237
|
} else {
|
|
@@ -244,17 +265,17 @@ class Image_ extends EditorInjector {
|
|
|
244
265
|
* @returns {Promise<boolean>} Success or failure
|
|
245
266
|
*/
|
|
246
267
|
async modalAction() {
|
|
247
|
-
this
|
|
268
|
+
this.#align = /** @type {HTMLInputElement} */ (this.modal.form.querySelector('input[name="suneditor_image_radio"]:checked')).value;
|
|
248
269
|
|
|
249
270
|
if (this.modal.isUpdate) {
|
|
250
|
-
this
|
|
271
|
+
this.#fixTagStructure(this.inputX?.value, this.inputY?.value);
|
|
251
272
|
this.history.push(false);
|
|
252
273
|
}
|
|
253
274
|
|
|
254
275
|
if (this.imgInputFile && this.imgInputFile.files.length > 0) {
|
|
255
276
|
return await this.submitFile(this.imgInputFile.files);
|
|
256
|
-
} else if (this.imgUrlFile && this.
|
|
257
|
-
return await this.submitURL(this
|
|
277
|
+
} else if (this.imgUrlFile && this.#linkValue.length > 0) {
|
|
278
|
+
return await this.submitURL(this.#linkValue);
|
|
258
279
|
}
|
|
259
280
|
|
|
260
281
|
return false;
|
|
@@ -276,10 +297,10 @@ class Image_ extends EditorInjector {
|
|
|
276
297
|
query: 'img',
|
|
277
298
|
method: (element) => {
|
|
278
299
|
const figureInfo = Figure.GetContainer(element);
|
|
279
|
-
if (figureInfo && figureInfo.container && figureInfo.cover) return;
|
|
300
|
+
if (figureInfo && figureInfo.container && (figureInfo.cover || figureInfo.inlineCover)) return;
|
|
280
301
|
|
|
281
|
-
this
|
|
282
|
-
this
|
|
302
|
+
const { w, h } = this.#ready(element, true);
|
|
303
|
+
this.#fileCheck(w, h);
|
|
283
304
|
}
|
|
284
305
|
};
|
|
285
306
|
}
|
|
@@ -291,7 +312,7 @@ class Image_ extends EditorInjector {
|
|
|
291
312
|
init() {
|
|
292
313
|
Modal.OnChangeFile(this.fileModalWrapper, []);
|
|
293
314
|
if (this.imgInputFile) this.imgInputFile.value = '';
|
|
294
|
-
if (this.imgUrlFile) this
|
|
315
|
+
if (this.imgUrlFile) this.#linkValue = this.previewSrc.textContent = this.imgUrlFile.value = '';
|
|
295
316
|
if (this.imgInputFile && this.imgUrlFile) {
|
|
296
317
|
this.imgUrlFile.disabled = false;
|
|
297
318
|
this.previewSrc.style.textDecoration = '';
|
|
@@ -300,21 +321,21 @@ class Image_ extends EditorInjector {
|
|
|
300
321
|
this.altText.value = '';
|
|
301
322
|
/** @type {HTMLInputElement} */ (this.modal.form.querySelector('input[name="suneditor_image_radio"][value="none"]')).checked = true;
|
|
302
323
|
this.captionCheckEl.checked = false;
|
|
303
|
-
this
|
|
304
|
-
this
|
|
305
|
-
w:
|
|
306
|
-
h:
|
|
324
|
+
this.#element = null;
|
|
325
|
+
this.#ratio = {
|
|
326
|
+
w: 0,
|
|
327
|
+
h: 0
|
|
307
328
|
};
|
|
308
329
|
this.#OpenTab('init');
|
|
309
330
|
|
|
310
|
-
if (this
|
|
331
|
+
if (this.#resizing) {
|
|
311
332
|
this.inputX.value = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
|
|
312
333
|
this.inputY.value = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
|
|
313
334
|
this.proportion.checked = true;
|
|
314
335
|
}
|
|
315
336
|
|
|
316
337
|
if (this.pluginOptions.useFormatType) {
|
|
317
|
-
this
|
|
338
|
+
this.#activeAsInline((this.pluginOptions.keepFormatType ? this.as : this.pluginOptions.defaultFormatType) === 'inline');
|
|
318
339
|
}
|
|
319
340
|
|
|
320
341
|
this.anchor.init();
|
|
@@ -326,70 +347,7 @@ class Image_ extends EditorInjector {
|
|
|
326
347
|
* @param {HTMLElement} target Target component element
|
|
327
348
|
*/
|
|
328
349
|
select(target) {
|
|
329
|
-
this
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* @private
|
|
334
|
-
* @description Prepares the component for selection.
|
|
335
|
-
* - Ensures that the controller is properly positioned and initialized.
|
|
336
|
-
* - Prevents duplicate event handling if the component is already selected.
|
|
337
|
-
* @param {HTMLElement} target - The selected element.
|
|
338
|
-
*/
|
|
339
|
-
_ready(target) {
|
|
340
|
-
if (!target) return;
|
|
341
|
-
const figureInfo = this.figure.open(target, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: false });
|
|
342
|
-
this.anchor.set(dom.check.isAnchor(target.parentNode) ? target.parentNode : null);
|
|
343
|
-
|
|
344
|
-
this._linkElement = this.anchor.currentTarget;
|
|
345
|
-
this._element = target;
|
|
346
|
-
this._cover = figureInfo.cover;
|
|
347
|
-
this._container = figureInfo.container;
|
|
348
|
-
this._caption = figureInfo.caption;
|
|
349
|
-
this._align = figureInfo.align;
|
|
350
|
-
target.style.float = '';
|
|
351
|
-
|
|
352
|
-
this._origin_w = String(figureInfo.originWidth || figureInfo.w || '');
|
|
353
|
-
this._origin_h = String(figureInfo.originHeight || figureInfo.h || '');
|
|
354
|
-
this.altText.value = this._element.alt;
|
|
355
|
-
|
|
356
|
-
if (this.imgUrlFile) this._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = this._element.src;
|
|
357
|
-
|
|
358
|
-
/** @type {HTMLInputElement} */
|
|
359
|
-
const activeAlign = this.modal.form.querySelector('input[name="suneditor_image_radio"][value="' + this._align + '"]') || this.modal.form.querySelector('input[name="suneditor_image_radio"][value="none"]');
|
|
360
|
-
activeAlign.checked = true;
|
|
361
|
-
this.captionCheckEl.checked = !!this._caption;
|
|
362
|
-
|
|
363
|
-
if (!this._resizing) return;
|
|
364
|
-
|
|
365
|
-
const percentageRotation = this._onlyPercentage && this.figure.isVertical;
|
|
366
|
-
let w = percentageRotation ? '' : figureInfo.width;
|
|
367
|
-
if (this._onlyPercentage) {
|
|
368
|
-
w = numbers.get(w, 2);
|
|
369
|
-
if (w > 100) w = 100;
|
|
370
|
-
}
|
|
371
|
-
this.inputX.value = String(w === 'auto' ? '' : w);
|
|
372
|
-
|
|
373
|
-
if (!this._onlyPercentage) {
|
|
374
|
-
const h = percentageRotation ? '' : figureInfo.height;
|
|
375
|
-
this.inputY.value = String(h === 'auto' ? '' : h);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
this.proportion.checked = true;
|
|
379
|
-
this.inputX.disabled = percentageRotation ? true : false;
|
|
380
|
-
this.inputY.disabled = percentageRotation ? true : false;
|
|
381
|
-
this.proportion.disabled = percentageRotation ? true : false;
|
|
382
|
-
|
|
383
|
-
this._ratio = this.proportion.checked
|
|
384
|
-
? figureInfo.ratio
|
|
385
|
-
: {
|
|
386
|
-
w: 1,
|
|
387
|
-
h: 1
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
if (this.pluginOptions.useFormatType) {
|
|
391
|
-
this._activeAsInline(this.component.isInline(figureInfo.container));
|
|
392
|
-
}
|
|
350
|
+
this.#ready(target);
|
|
393
351
|
}
|
|
394
352
|
|
|
395
353
|
/**
|
|
@@ -399,18 +357,18 @@ class Image_ extends EditorInjector {
|
|
|
399
357
|
* @returns {Promise<void>}
|
|
400
358
|
*/
|
|
401
359
|
async destroy(target) {
|
|
402
|
-
const targetEl = target || this
|
|
360
|
+
const targetEl = target || this.#element;
|
|
403
361
|
const container = dom.query.getParentElement(targetEl, Figure.is) || targetEl;
|
|
404
362
|
const focusEl = container.previousElementSibling || container.nextElementSibling;
|
|
405
363
|
const emptyDiv = container.parentNode;
|
|
406
364
|
|
|
407
|
-
const message = await this.triggerEvent('onImageDeleteBefore', { element: targetEl, container, align: this
|
|
365
|
+
const message = await this.triggerEvent('onImageDeleteBefore', { element: targetEl, container, align: this.#align, alt: this.altText.value, url: this.#linkValue });
|
|
408
366
|
if (message === false) return;
|
|
409
367
|
|
|
410
368
|
dom.utils.removeItem(container);
|
|
411
369
|
this.init();
|
|
412
370
|
|
|
413
|
-
if (emptyDiv !== this.
|
|
371
|
+
if (emptyDiv !== this.frameContext.get('wysiwyg')) {
|
|
414
372
|
this.nodeTransform.removeAllParents(
|
|
415
373
|
emptyDiv,
|
|
416
374
|
function (current) {
|
|
@@ -425,48 +383,6 @@ class Image_ extends EditorInjector {
|
|
|
425
383
|
this.history.push(false);
|
|
426
384
|
}
|
|
427
385
|
|
|
428
|
-
/**
|
|
429
|
-
* @private
|
|
430
|
-
* @description Retrieves the current image information.
|
|
431
|
-
* @returns {*} - The image data.
|
|
432
|
-
*/
|
|
433
|
-
_getInfo() {
|
|
434
|
-
return {
|
|
435
|
-
element: this._element,
|
|
436
|
-
anchor: this.anchor.create(true),
|
|
437
|
-
inputWidth: this.inputX?.value || '',
|
|
438
|
-
inputHeight: this.inputY?.value || '',
|
|
439
|
-
align: this._align,
|
|
440
|
-
isUpdate: this.modal.isUpdate,
|
|
441
|
-
alt: this.altText.value
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* @private
|
|
447
|
-
* @description Toggles between block and inline image format.
|
|
448
|
-
* @param {boolean} isInline - Whether the image should be inline.
|
|
449
|
-
*/
|
|
450
|
-
_activeAsInline(isInline) {
|
|
451
|
-
if (isInline) {
|
|
452
|
-
dom.utils.addClass(this.asInline, 'on');
|
|
453
|
-
dom.utils.removeClass(this.asBlock, 'on');
|
|
454
|
-
this.as = 'inline';
|
|
455
|
-
// buttns
|
|
456
|
-
if (this.alignForm) this.alignForm.style.display = 'none';
|
|
457
|
-
// caption
|
|
458
|
-
if (this.captionEl) this.captionEl.style.display = 'none';
|
|
459
|
-
} else {
|
|
460
|
-
dom.utils.addClass(this.asBlock, 'on');
|
|
461
|
-
dom.utils.removeClass(this.asInline, 'on');
|
|
462
|
-
this.as = 'block';
|
|
463
|
-
// buttns
|
|
464
|
-
if (this.alignForm) this.alignForm.style.display = '';
|
|
465
|
-
// caption
|
|
466
|
-
if (this.captionEl) this.captionEl.style.display = '';
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
386
|
/**
|
|
471
387
|
* @description Create an "image" component using the provided files.
|
|
472
388
|
* @param {FileList|File[]} fileList File object list
|
|
@@ -483,7 +399,7 @@ class Image_ extends EditorInjector {
|
|
|
483
399
|
if (!/image/i.test(f.type)) continue;
|
|
484
400
|
|
|
485
401
|
s = f.size;
|
|
486
|
-
if (slngleSizeLimit &&
|
|
402
|
+
if (slngleSizeLimit > 0 && s > slngleSizeLimit) {
|
|
487
403
|
const err = '[SUNEDITOR.imageUpload.fail] Size of uploadable single file: ' + slngleSizeLimit / 1000 + 'KB';
|
|
488
404
|
const message = await this.triggerEvent('onImageUploadError', {
|
|
489
405
|
error: err,
|
|
@@ -517,13 +433,11 @@ class Image_ extends EditorInjector {
|
|
|
517
433
|
return false;
|
|
518
434
|
}
|
|
519
435
|
|
|
520
|
-
const imgInfo = { files, ...this
|
|
521
|
-
const handler = function (infos, newInfos) {
|
|
436
|
+
const imgInfo = { files, ...this.#getInfo() };
|
|
437
|
+
const handler = function (uploadCallback, infos, newInfos) {
|
|
522
438
|
infos = newInfos || infos;
|
|
523
|
-
|
|
524
|
-
}.bind(this, imgInfo);
|
|
525
|
-
// se-ts-ignore
|
|
526
|
-
this._serverUpload;
|
|
439
|
+
uploadCallback(infos, infos.files);
|
|
440
|
+
}.bind(this, this.#serverUpload.bind(this), imgInfo);
|
|
527
441
|
|
|
528
442
|
const result = await this.triggerEvent('onImageUploadBefore', {
|
|
529
443
|
info: imgInfo,
|
|
@@ -543,22 +457,19 @@ class Image_ extends EditorInjector {
|
|
|
543
457
|
* @returns {Promise<boolean>} If return false, the file upload will be canceled
|
|
544
458
|
*/
|
|
545
459
|
async submitURL(url) {
|
|
546
|
-
if (!url)
|
|
547
|
-
if (!url) return false;
|
|
460
|
+
if (!(url ||= this.#linkValue)) return false;
|
|
548
461
|
|
|
549
462
|
const file = { name: url.split('/').pop(), size: 0 };
|
|
550
463
|
const imgInfo = {
|
|
551
464
|
url,
|
|
552
465
|
files: file,
|
|
553
|
-
...this
|
|
466
|
+
...this.#getInfo()
|
|
554
467
|
};
|
|
555
468
|
|
|
556
|
-
const handler = function (infos, newInfos) {
|
|
469
|
+
const handler = function (uploadCallback, infos, newInfos) {
|
|
557
470
|
infos = newInfos || infos;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
else this._produce(infoUrl, infos.anchor, infos.inputWidth, infos.inputHeight, infos.align, infos.files, infos.alt);
|
|
561
|
-
}.bind(this, imgInfo);
|
|
471
|
+
uploadCallback(infos);
|
|
472
|
+
}.bind(this, this.#urlUpload.bind(this), imgInfo);
|
|
562
473
|
|
|
563
474
|
const result = await this.triggerEvent('onImageUploadBefore', {
|
|
564
475
|
info: imgInfo,
|
|
@@ -575,18 +486,204 @@ class Image_ extends EditorInjector {
|
|
|
575
486
|
}
|
|
576
487
|
|
|
577
488
|
/**
|
|
578
|
-
* @
|
|
489
|
+
* @description Creates a new image component, wraps it in a figure container with an optional anchor,
|
|
490
|
+
* - applies size and alignment settings, and inserts it into the editor.
|
|
491
|
+
* @param {string} src - The URL of the image to be inserted.
|
|
492
|
+
* @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
|
|
493
|
+
* @param {string} width - The width value to be applied to the image.
|
|
494
|
+
* @param {string} height - The height value to be applied to the image.
|
|
495
|
+
* @param {string} align - The alignment setting for the image (e.g., 'left', 'center', 'right').
|
|
496
|
+
* @param {{name: string, size: number}} file - File metadata associated with the image
|
|
497
|
+
* @param {string} alt - The alternative text for the image.
|
|
498
|
+
* @param {boolean} isLast - Indicates whether this is the last file in the batch (used for scroll and insert actions).
|
|
499
|
+
*/
|
|
500
|
+
create(src, anchor, width, height, align, file, alt, isLast) {
|
|
501
|
+
/** @type {HTMLImageElement} */
|
|
502
|
+
const oImg = dom.utils.createElement('IMG');
|
|
503
|
+
oImg.src = src;
|
|
504
|
+
oImg.alt = alt;
|
|
505
|
+
anchor = this.#setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
|
|
506
|
+
|
|
507
|
+
const figureInfo = Figure.CreateContainer(anchor, 'se-image-container');
|
|
508
|
+
const cover = figureInfo.cover;
|
|
509
|
+
const container = figureInfo.container;
|
|
510
|
+
|
|
511
|
+
// caption
|
|
512
|
+
if (this.captionCheckEl.checked) {
|
|
513
|
+
this.#caption = Figure.CreateCaption(cover, this.lang.caption);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.#element = oImg;
|
|
517
|
+
this.#cover = cover;
|
|
518
|
+
this.#container = container;
|
|
519
|
+
this.figure.open(oImg, { nonResizing: this.#nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, infoOnly: true });
|
|
520
|
+
|
|
521
|
+
// set size
|
|
522
|
+
this.#applySize(width, height);
|
|
523
|
+
|
|
524
|
+
// align
|
|
525
|
+
this.figure.setAlign(oImg, align);
|
|
526
|
+
|
|
527
|
+
this.fileManager.setFileData(oImg, file);
|
|
528
|
+
|
|
529
|
+
this.#produceIndex++;
|
|
530
|
+
oImg.onload = this.#OnloadImg.bind(this, oImg, this.#svgDefaultSize, container);
|
|
531
|
+
this.component.insert(container, { scrollTo: isLast ? true : false, insertBehavior: isLast ? null : 'line' });
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* @description Creates a new inline image component, wraps it in an inline figure container with an optional anchor,
|
|
536
|
+
* - applies size settings, and inserts it into the editor.
|
|
537
|
+
* @param {string} src - The URL of the image to be inserted.
|
|
538
|
+
* @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
|
|
539
|
+
* @param {string} width - The width value to be applied to the image.
|
|
540
|
+
* @param {string} height - The height value to be applied to the image.
|
|
541
|
+
* @param {{name: string, size: number}} file - File metadata associated with the image
|
|
542
|
+
* @param {string} alt - The alternative text for the image.
|
|
543
|
+
* @param {boolean} isLast - Indicates whether this is the last file in the batch (used for scroll and insert actions).
|
|
544
|
+
*/
|
|
545
|
+
createInline(src, anchor, width, height, file, alt, isLast) {
|
|
546
|
+
/** @type {HTMLImageElement} */
|
|
547
|
+
const oImg = dom.utils.createElement('IMG');
|
|
548
|
+
oImg.src = src;
|
|
549
|
+
oImg.alt = alt;
|
|
550
|
+
anchor = this.#setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
|
|
551
|
+
|
|
552
|
+
const figureInfo = Figure.CreateInlineContainer(anchor, 'se-image-container');
|
|
553
|
+
const container = figureInfo.container;
|
|
554
|
+
|
|
555
|
+
this.#element = oImg;
|
|
556
|
+
this.#container = container;
|
|
557
|
+
this.figure.open(oImg, { nonResizing: this.#nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, infoOnly: true });
|
|
558
|
+
|
|
559
|
+
// set size
|
|
560
|
+
this.#applySize(width, height);
|
|
561
|
+
|
|
562
|
+
this.fileManager.setFileData(oImg, file);
|
|
563
|
+
|
|
564
|
+
this.#produceIndex++;
|
|
565
|
+
oImg.onload = this.#OnloadImg.bind(this, oImg, this.#svgDefaultSize, container);
|
|
566
|
+
this.component.insert(container, { scrollTo: isLast ? true : false, insertBehavior: isLast ? null : 'line' });
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* @description Prepares the component for selection.
|
|
571
|
+
* - Ensures that the controller is properly positioned and initialized.
|
|
572
|
+
* - Prevents duplicate event handling if the component is already selected.
|
|
573
|
+
* @param {HTMLElement} target - The selected element.
|
|
574
|
+
* @param {boolean} [infoOnly=false] - If true, only retrieves information without opening the controller.
|
|
575
|
+
* @returns {{w: string, h: string}} - The width and height of the component.
|
|
576
|
+
*/
|
|
577
|
+
#ready(target, infoOnly = false) {
|
|
578
|
+
if (!target) return;
|
|
579
|
+
const figureInfo = this.figure.open(target, { nonResizing: this.#nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, infoOnly });
|
|
580
|
+
this.anchor.set(dom.check.isAnchor(target.parentNode) ? target.parentNode : null);
|
|
581
|
+
|
|
582
|
+
this.#linkElement = this.anchor.currentTarget;
|
|
583
|
+
this.#element = target;
|
|
584
|
+
this.#cover = figureInfo.cover;
|
|
585
|
+
this.#container = figureInfo.container;
|
|
586
|
+
this.#caption = figureInfo.caption;
|
|
587
|
+
this.#align = figureInfo.align;
|
|
588
|
+
target.style.float = '';
|
|
589
|
+
|
|
590
|
+
this.#origin_w = String(figureInfo.originWidth || figureInfo.w || '');
|
|
591
|
+
this.#origin_h = String(figureInfo.originHeight || figureInfo.h || '');
|
|
592
|
+
this.altText.value = this.#element.alt;
|
|
593
|
+
|
|
594
|
+
if (this.imgUrlFile) this.#linkValue = this.previewSrc.textContent = this.imgUrlFile.value = this.#element.src;
|
|
595
|
+
|
|
596
|
+
/** @type {HTMLInputElement} */
|
|
597
|
+
const activeAlign = this.modal.form.querySelector('input[name="suneditor_image_radio"][value="' + this.#align + '"]') || this.modal.form.querySelector('input[name="suneditor_image_radio"][value="none"]');
|
|
598
|
+
activeAlign.checked = true;
|
|
599
|
+
this.captionCheckEl.checked = !!this.#caption;
|
|
600
|
+
|
|
601
|
+
const { dw, dh } = this.figure.getSize(target);
|
|
602
|
+
|
|
603
|
+
if (!this.#resizing) return { w: dw, h: dh };
|
|
604
|
+
|
|
605
|
+
this.inputX.value = dw === 'auto' ? '' : dw;
|
|
606
|
+
this.inputY.value = dh === 'auto' ? '' : dh;
|
|
607
|
+
|
|
608
|
+
const percentageRotation = this.#onlyPercentage && this.figure.isVertical;
|
|
609
|
+
this.proportion.checked = true;
|
|
610
|
+
this.inputX.disabled = percentageRotation ? true : false;
|
|
611
|
+
this.inputY.disabled = percentageRotation ? true : false;
|
|
612
|
+
this.proportion.disabled = percentageRotation ? true : false;
|
|
613
|
+
|
|
614
|
+
this.#ratio = this.proportion.checked
|
|
615
|
+
? figureInfo.ratio
|
|
616
|
+
: {
|
|
617
|
+
w: 0,
|
|
618
|
+
h: 0
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
if (this.pluginOptions.useFormatType) {
|
|
622
|
+
this.#activeAsInline(this.component.isInline(figureInfo.container));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return { w: dw, h: dh };
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* @description Retrieves the current image information.
|
|
630
|
+
* @returns {*} - The image data.
|
|
631
|
+
*/
|
|
632
|
+
#getInfo() {
|
|
633
|
+
return {
|
|
634
|
+
element: this.#element,
|
|
635
|
+
anchor: this.anchor.create(true),
|
|
636
|
+
inputWidth: this.inputX?.value || '',
|
|
637
|
+
inputHeight: this.inputY?.value || '',
|
|
638
|
+
align: this.#align,
|
|
639
|
+
isUpdate: this.modal.isUpdate,
|
|
640
|
+
alt: this.altText.value
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* @description Toggles between block and inline image format.
|
|
646
|
+
* @param {boolean} isInline - Whether the image should be inline.
|
|
647
|
+
*/
|
|
648
|
+
#activeAsInline(isInline) {
|
|
649
|
+
if (isInline) {
|
|
650
|
+
dom.utils.addClass(this.asInline, 'on');
|
|
651
|
+
dom.utils.removeClass(this.asBlock, 'on');
|
|
652
|
+
this.as = 'inline';
|
|
653
|
+
// buttns
|
|
654
|
+
if (this.alignForm) this.alignForm.style.display = 'none';
|
|
655
|
+
// caption
|
|
656
|
+
if (this.captionEl) this.captionEl.style.display = 'none';
|
|
657
|
+
} else {
|
|
658
|
+
dom.utils.addClass(this.asBlock, 'on');
|
|
659
|
+
dom.utils.removeClass(this.asInline, 'on');
|
|
660
|
+
this.as = 'block';
|
|
661
|
+
// buttns
|
|
662
|
+
if (this.alignForm) this.alignForm.style.display = '';
|
|
663
|
+
// caption
|
|
664
|
+
if (this.captionEl) this.captionEl.style.display = '';
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
579
669
|
* @description Updates the selected image size, alt text, and caption.
|
|
580
670
|
* @param {string} width - New image width.
|
|
581
671
|
* @param {string} height - New image height.
|
|
582
672
|
*/
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
673
|
+
#fixTagStructure(width, height) {
|
|
674
|
+
width ||= this.inputX?.value || 'auto';
|
|
675
|
+
height ||= this.inputY?.value || 'auto';
|
|
676
|
+
|
|
677
|
+
let imageEl = this.#element;
|
|
678
|
+
|
|
679
|
+
// as (block | inline)
|
|
680
|
+
if ((this.as === 'block' && !this.#cover) || (this.as === 'inline' && this.#cover)) {
|
|
681
|
+
imageEl = this.figure.convertAsFormat(imageEl, this.as);
|
|
682
|
+
}
|
|
586
683
|
|
|
587
|
-
|
|
588
|
-
const cover = this
|
|
589
|
-
const container = this
|
|
684
|
+
// --- update image ---
|
|
685
|
+
const cover = this.#cover;
|
|
686
|
+
const container = this.#container === this.#cover ? null : this.#container;
|
|
590
687
|
|
|
591
688
|
// check size
|
|
592
689
|
let changeSize;
|
|
@@ -604,14 +701,14 @@ class Image_ extends EditorInjector {
|
|
|
604
701
|
// caption
|
|
605
702
|
let modifiedCaption = false;
|
|
606
703
|
if (this.captionCheckEl.checked) {
|
|
607
|
-
if (!this
|
|
608
|
-
this
|
|
704
|
+
if (!this.#caption) {
|
|
705
|
+
this.#caption = Figure.CreateCaption(cover, this.lang.caption);
|
|
609
706
|
modifiedCaption = true;
|
|
610
707
|
}
|
|
611
708
|
} else {
|
|
612
|
-
if (this
|
|
613
|
-
dom.utils.removeItem(this
|
|
614
|
-
this
|
|
709
|
+
if (this.#caption) {
|
|
710
|
+
dom.utils.removeItem(this.#caption);
|
|
711
|
+
this.#caption = null;
|
|
615
712
|
modifiedCaption = true;
|
|
616
713
|
}
|
|
617
714
|
}
|
|
@@ -620,42 +717,42 @@ class Image_ extends EditorInjector {
|
|
|
620
717
|
let isNewAnchor = false;
|
|
621
718
|
const anchor = this.anchor.create(true);
|
|
622
719
|
if (anchor) {
|
|
623
|
-
if (this
|
|
624
|
-
this
|
|
625
|
-
cover.insertBefore(this
|
|
720
|
+
if (this.#linkElement !== anchor || !container.contains(anchor)) {
|
|
721
|
+
this.#linkElement = anchor.cloneNode(false);
|
|
722
|
+
cover.insertBefore(this.#setAnchor(imageEl, this.#linkElement), this.#caption);
|
|
626
723
|
isNewAnchor = true;
|
|
627
724
|
}
|
|
628
|
-
} else if (this
|
|
629
|
-
if (cover.contains(this
|
|
725
|
+
} else if (this.#linkElement !== null) {
|
|
726
|
+
if (cover.contains(this.#linkElement)) {
|
|
630
727
|
const newEl = imageEl.cloneNode(true);
|
|
631
|
-
cover.removeChild(this
|
|
632
|
-
cover.insertBefore(newEl, this
|
|
728
|
+
cover.removeChild(this.#linkElement);
|
|
729
|
+
cover.insertBefore(newEl, this.#caption);
|
|
633
730
|
imageEl = newEl;
|
|
634
731
|
}
|
|
635
732
|
}
|
|
636
733
|
|
|
637
|
-
// size
|
|
638
|
-
if (this._resizing && changeSize) {
|
|
639
|
-
this._applySize(width, height);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
734
|
if (isNewAnchor) {
|
|
643
735
|
dom.utils.removeItem(anchor);
|
|
644
736
|
}
|
|
645
737
|
|
|
738
|
+
// size
|
|
739
|
+
if (this.#resizing && changeSize) {
|
|
740
|
+
this.#applySize(width, height);
|
|
741
|
+
}
|
|
742
|
+
|
|
646
743
|
// transform
|
|
647
|
-
if (modifiedCaption || (!this
|
|
744
|
+
if (modifiedCaption || (!this.#onlyPercentage && changeSize)) {
|
|
648
745
|
if (/\d+/.test(imageEl.style.height) || (this.figure.isVertical && this.captionCheckEl.checked)) {
|
|
649
746
|
if (/auto|%$/.test(width) || /auto|%$/.test(height)) {
|
|
650
747
|
this.figure.deleteTransform(imageEl);
|
|
651
|
-
} else {
|
|
748
|
+
} else if (!this.#resizing || !changeSize || !this.figure.isVertical) {
|
|
652
749
|
this.figure.setTransform(imageEl, width, height, 0);
|
|
653
750
|
}
|
|
654
751
|
}
|
|
655
752
|
}
|
|
656
753
|
|
|
657
754
|
// align
|
|
658
|
-
this.figure.setAlign(imageEl, this
|
|
755
|
+
this.figure.setAlign(imageEl, this.#align);
|
|
659
756
|
|
|
660
757
|
// select
|
|
661
758
|
imageEl.onload = () => {
|
|
@@ -664,42 +761,31 @@ class Image_ extends EditorInjector {
|
|
|
664
761
|
}
|
|
665
762
|
|
|
666
763
|
/**
|
|
667
|
-
* @private
|
|
668
764
|
* @description Validates the image size and applies necessary transformations.
|
|
669
765
|
* @param {string} width - The width of the image.
|
|
670
766
|
* @param {string} height - The height of the image.
|
|
671
767
|
*/
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
768
|
+
#fileCheck(width, height) {
|
|
769
|
+
width ||= this.inputX?.value || 'auto';
|
|
770
|
+
height ||= this.inputY?.value || 'auto';
|
|
675
771
|
|
|
676
|
-
let imageEl = this
|
|
677
|
-
let cover = this
|
|
772
|
+
let imageEl = this.#element;
|
|
773
|
+
let cover = this.#cover;
|
|
678
774
|
let inlineCover = null;
|
|
679
|
-
let container = this
|
|
775
|
+
let container = this.#container === this.#cover ? null : this.#container;
|
|
680
776
|
let isNewContainer = false;
|
|
681
777
|
|
|
682
778
|
if (!cover || !container) {
|
|
683
779
|
isNewContainer = true;
|
|
684
|
-
imageEl = this.
|
|
780
|
+
imageEl = this.#element.cloneNode(true);
|
|
685
781
|
const figureInfo =
|
|
686
|
-
this.pluginOptions.useFormatType && width !== 'auto' && (/^span$/i.test(this.
|
|
782
|
+
this.pluginOptions.useFormatType && width !== 'auto' && (/^span$/i.test(this.#element.parentElement?.nodeName) || this.format.isLine(this.#element.parentElement))
|
|
687
783
|
? Figure.CreateInlineContainer(imageEl, 'se-image-container')
|
|
688
784
|
: Figure.CreateContainer(imageEl, 'se-image-container');
|
|
689
785
|
cover = figureInfo.cover;
|
|
690
786
|
container = figureInfo.container;
|
|
691
787
|
inlineCover = figureInfo.inlineCover;
|
|
692
|
-
this.figure.open(imageEl, { nonResizing: true, nonSizeInfo: false, nonBorder: false, figureTarget: false,
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// check size
|
|
696
|
-
let changeSize;
|
|
697
|
-
const x = numbers.is(width) ? width + this.sizeUnit : width;
|
|
698
|
-
const y = numbers.is(height) ? height + this.sizeUnit : height;
|
|
699
|
-
if (/%$/.test(imageEl.style.width)) {
|
|
700
|
-
changeSize = x !== container.style.width || y !== container.style.height;
|
|
701
|
-
} else {
|
|
702
|
-
changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
|
|
788
|
+
this.figure.open(imageEl, { nonResizing: true, nonSizeInfo: false, nonBorder: false, figureTarget: false, infoOnly: true });
|
|
703
789
|
}
|
|
704
790
|
|
|
705
791
|
// alt
|
|
@@ -709,14 +795,14 @@ class Image_ extends EditorInjector {
|
|
|
709
795
|
let modifiedCaption = false;
|
|
710
796
|
if (!inlineCover) {
|
|
711
797
|
if (this.captionCheckEl.checked) {
|
|
712
|
-
if (!this
|
|
713
|
-
this
|
|
798
|
+
if (!this.#caption || isNewContainer) {
|
|
799
|
+
this.#caption = Figure.CreateCaption(cover, this.lang.caption);
|
|
714
800
|
modifiedCaption = true;
|
|
715
801
|
}
|
|
716
802
|
} else {
|
|
717
|
-
if (this
|
|
718
|
-
dom.utils.removeItem(this
|
|
719
|
-
this
|
|
803
|
+
if (this.#caption) {
|
|
804
|
+
dom.utils.removeItem(this.#caption);
|
|
805
|
+
this.#caption = null;
|
|
720
806
|
modifiedCaption = true;
|
|
721
807
|
}
|
|
722
808
|
}
|
|
@@ -726,46 +812,48 @@ class Image_ extends EditorInjector {
|
|
|
726
812
|
let isNewAnchor = null;
|
|
727
813
|
const anchor = this.anchor.create(true);
|
|
728
814
|
if (anchor) {
|
|
729
|
-
if (this
|
|
730
|
-
this
|
|
731
|
-
cover.insertBefore(this
|
|
732
|
-
isNewAnchor = this
|
|
815
|
+
if (this.#linkElement !== anchor || (isNewContainer && !container.contains(anchor))) {
|
|
816
|
+
this.#linkElement = anchor.cloneNode(false);
|
|
817
|
+
cover.insertBefore(this.#setAnchor(imageEl, this.#linkElement), this.#caption);
|
|
818
|
+
isNewAnchor = this.#element;
|
|
733
819
|
}
|
|
734
|
-
} else if (this
|
|
735
|
-
if (cover.contains(this
|
|
820
|
+
} else if (this.#linkElement !== null) {
|
|
821
|
+
if (cover.contains(this.#linkElement)) {
|
|
736
822
|
const newEl = imageEl.cloneNode(true);
|
|
737
|
-
cover.removeChild(this
|
|
738
|
-
cover.insertBefore(newEl, this
|
|
823
|
+
cover.removeChild(this.#linkElement);
|
|
824
|
+
cover.insertBefore(newEl, this.#caption);
|
|
739
825
|
imageEl = newEl;
|
|
740
826
|
}
|
|
741
827
|
}
|
|
742
828
|
|
|
743
829
|
if (isNewContainer) {
|
|
744
|
-
imageEl = this
|
|
745
|
-
this.figure.retainFigureFormat(container, this
|
|
746
|
-
this
|
|
747
|
-
this
|
|
748
|
-
this
|
|
830
|
+
imageEl = this.#element;
|
|
831
|
+
this.figure.retainFigureFormat(container, this.#element, isNewAnchor ? anchor : null, this.fileManager);
|
|
832
|
+
this.#element = imageEl = container.querySelector('img');
|
|
833
|
+
this.#cover = cover;
|
|
834
|
+
this.#container = container;
|
|
749
835
|
}
|
|
750
836
|
|
|
751
837
|
// size
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
838
|
+
imageEl.style.width = '';
|
|
839
|
+
imageEl.style.height = '';
|
|
840
|
+
imageEl.removeAttribute('width');
|
|
841
|
+
imageEl.removeAttribute('height');
|
|
842
|
+
this.#applySize(width, height);
|
|
755
843
|
|
|
756
844
|
if (isNewAnchor) {
|
|
757
845
|
if (!isNewContainer) {
|
|
758
846
|
dom.utils.removeItem(anchor);
|
|
759
847
|
} else {
|
|
760
848
|
dom.utils.removeItem(isNewAnchor);
|
|
761
|
-
if (dom.query.getListChildren(anchor, (current) => /IMG/i.test(current.tagName)).length === 0) {
|
|
849
|
+
if (dom.query.getListChildren(anchor, (current) => /IMG/i.test(current.tagName), null).length === 0) {
|
|
762
850
|
dom.utils.removeItem(anchor);
|
|
763
851
|
}
|
|
764
852
|
}
|
|
765
853
|
}
|
|
766
854
|
|
|
767
855
|
// transform
|
|
768
|
-
if (modifiedCaption ||
|
|
856
|
+
if (modifiedCaption || !this.#onlyPercentage) {
|
|
769
857
|
if (/\d+/.test(imageEl.style.height) || (this.figure.isVertical && this.captionCheckEl.checked)) {
|
|
770
858
|
if (/auto|%$/.test(width) || /auto|%$/.test(height)) {
|
|
771
859
|
this.figure.deleteTransform(imageEl);
|
|
@@ -776,56 +864,10 @@ class Image_ extends EditorInjector {
|
|
|
776
864
|
}
|
|
777
865
|
|
|
778
866
|
// align
|
|
779
|
-
this.figure.setAlign(imageEl, this
|
|
867
|
+
this.figure.setAlign(imageEl, this.#align);
|
|
780
868
|
}
|
|
781
869
|
|
|
782
870
|
/**
|
|
783
|
-
* @description Opens a specific tab inside the modal.
|
|
784
|
-
* @param {MouseEvent|string} e - The event object or tab name.
|
|
785
|
-
* @returns {boolean} - Whether the tab was successfully opened.
|
|
786
|
-
*/
|
|
787
|
-
#OpenTab(e) {
|
|
788
|
-
const modalForm = this.modal.form;
|
|
789
|
-
const targetElement = typeof e === 'string' ? modalForm.querySelector('._se_tab_link') : dom.query.getEventTarget(e);
|
|
790
|
-
|
|
791
|
-
if (!/^BUTTON$/i.test(targetElement.tagName)) {
|
|
792
|
-
return false;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// Declare all variables
|
|
796
|
-
const tabName = targetElement.getAttribute('data-tab-link');
|
|
797
|
-
let i;
|
|
798
|
-
|
|
799
|
-
// Get all elements with class="tabcontent" and hide them
|
|
800
|
-
const tabContent = /** @type {HTMLCollectionOf<HTMLElement>}*/ (modalForm.getElementsByClassName('_se_tab_content'));
|
|
801
|
-
for (i = 0; i < tabContent.length; i++) {
|
|
802
|
-
tabContent[i].style.display = 'none';
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// Get all elements with class="tablinks" and remove the class "active"
|
|
806
|
-
const tabLinks = modalForm.getElementsByClassName('_se_tab_link');
|
|
807
|
-
for (i = 0; i < tabLinks.length; i++) {
|
|
808
|
-
dom.utils.removeClass(tabLinks[i], 'active');
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
// Show the current tab, and add an "active" class to the button that opened the tab
|
|
812
|
-
/** @type {HTMLElement}*/ (modalForm.querySelector('._se_tab_content_' + tabName)).style.display = 'block';
|
|
813
|
-
dom.utils.addClass(targetElement, 'active');
|
|
814
|
-
|
|
815
|
-
// focus
|
|
816
|
-
if (e !== 'init') {
|
|
817
|
-
if (tabName === 'image') {
|
|
818
|
-
this.focusElement.focus();
|
|
819
|
-
} else if (tabName === 'url') {
|
|
820
|
-
this.anchor.urlInput.focus();
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
return false;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
* @private
|
|
829
871
|
* @description Creates a new image component based on provided parameters.
|
|
830
872
|
* @param {string} src - The image source URL.
|
|
831
873
|
* @param {?Node} anchor - Optional anchor wrapping the image.
|
|
@@ -834,25 +876,26 @@ class Image_ extends EditorInjector {
|
|
|
834
876
|
* @param {string} align - Image alignment.
|
|
835
877
|
* @param {{name: string, size: number}} file - File metadata.
|
|
836
878
|
* @param {string} alt - Alternative text.
|
|
879
|
+
* @param {boolean} isLast - Indicates if this is the last image in a batch (for scroll and insert behavior).
|
|
837
880
|
*/
|
|
838
|
-
|
|
881
|
+
#produce(src, anchor, width, height, align, file, alt, isLast) {
|
|
839
882
|
if (this.as !== 'inline') {
|
|
840
|
-
this.create(src, anchor, width, height, align, file, alt);
|
|
883
|
+
this.create(src, anchor, width, height, align, file, alt, isLast);
|
|
841
884
|
} else {
|
|
842
|
-
this.createInline(src, anchor, width, height, file, alt);
|
|
885
|
+
this.createInline(src, anchor, width, height, file, alt, isLast);
|
|
843
886
|
}
|
|
844
887
|
}
|
|
845
888
|
|
|
846
889
|
/**
|
|
847
|
-
* @private
|
|
848
890
|
* @description Applies the specified width and height to the image.
|
|
849
891
|
* @param {string} w - Image width.
|
|
850
892
|
* @param {string} h - Image height.
|
|
851
893
|
*/
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
894
|
+
#applySize(w, h) {
|
|
895
|
+
w ||= this.inputX?.value || this.pluginOptions.defaultWidth;
|
|
896
|
+
h ||= this.inputY?.value || this.pluginOptions.defaultHeight;
|
|
897
|
+
|
|
898
|
+
if (this.#onlyPercentage) {
|
|
856
899
|
if (!w) w = '100%';
|
|
857
900
|
else if (/%$/.test(w)) w += '%';
|
|
858
901
|
}
|
|
@@ -860,102 +903,24 @@ class Image_ extends EditorInjector {
|
|
|
860
903
|
}
|
|
861
904
|
|
|
862
905
|
/**
|
|
863
|
-
* @description Creates a new image component, wraps it in a figure container with an optional anchor,
|
|
864
|
-
* - applies size and alignment settings, and inserts it into the editor.
|
|
865
|
-
* @param {string} src - The URL of the image to be inserted.
|
|
866
|
-
* @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
|
|
867
|
-
* @param {string} width - The width value to be applied to the image.
|
|
868
|
-
* @param {string} height - The height value to be applied to the image.
|
|
869
|
-
* @param {string} align - The alignment setting for the image (e.g., 'left', 'center', 'right').
|
|
870
|
-
* @param {{name: string, size: number}} file - File metadata associated with the image
|
|
871
|
-
* @param {string} alt - The alternative text for the image.
|
|
872
|
-
*/
|
|
873
|
-
create(src, anchor, width, height, align, file, alt) {
|
|
874
|
-
/** @type {HTMLImageElement} */
|
|
875
|
-
const oImg = dom.utils.createElement('IMG');
|
|
876
|
-
oImg.src = src;
|
|
877
|
-
oImg.alt = alt;
|
|
878
|
-
anchor = this._setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
|
|
879
|
-
|
|
880
|
-
const figureInfo = Figure.CreateContainer(anchor, 'se-image-container');
|
|
881
|
-
const cover = figureInfo.cover;
|
|
882
|
-
const container = figureInfo.container;
|
|
883
|
-
|
|
884
|
-
// caption
|
|
885
|
-
if (this.captionCheckEl.checked) {
|
|
886
|
-
this._caption = Figure.CreateCaption(cover, this.lang.caption);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
this._element = oImg;
|
|
890
|
-
this._cover = cover;
|
|
891
|
-
this._container = container;
|
|
892
|
-
this.figure.open(oImg, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
|
|
893
|
-
|
|
894
|
-
// set size
|
|
895
|
-
this._applySize(width, height);
|
|
896
|
-
|
|
897
|
-
// align
|
|
898
|
-
this.figure.setAlign(oImg, align);
|
|
899
|
-
|
|
900
|
-
this.fileManager.setFileData(oImg, file);
|
|
901
|
-
|
|
902
|
-
oImg.onload = this.#OnloadImg.bind(this, oImg, this._svgDefaultSize, container);
|
|
903
|
-
this.component.insert(container, { skipCharCount: false, skipSelection: !this.options.get('componentAutoSelect'), skipHistory: false });
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* @description Creates a new inline image component, wraps it in an inline figure container with an optional anchor,
|
|
908
|
-
* - applies size settings, and inserts it into the editor.
|
|
909
|
-
* @param {string} src - The URL of the image to be inserted.
|
|
910
|
-
* @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
|
|
911
|
-
* @param {string} width - The width value to be applied to the image.
|
|
912
|
-
* @param {string} height - The height value to be applied to the image.
|
|
913
|
-
* @param {{name: string, size: number}} file - File metadata associated with the image
|
|
914
|
-
* @param {string} alt - The alternative text for the image.
|
|
915
|
-
*/
|
|
916
|
-
createInline(src, anchor, width, height, file, alt) {
|
|
917
|
-
/** @type {HTMLImageElement} */
|
|
918
|
-
const oImg = dom.utils.createElement('IMG');
|
|
919
|
-
oImg.src = src;
|
|
920
|
-
oImg.alt = alt;
|
|
921
|
-
anchor = this._setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
|
|
922
|
-
|
|
923
|
-
const figureInfo = Figure.CreateInlineContainer(anchor, 'se-image-container');
|
|
924
|
-
const container = figureInfo.container;
|
|
925
|
-
|
|
926
|
-
this._element = oImg;
|
|
927
|
-
this._container = container;
|
|
928
|
-
this.figure.open(oImg, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
|
|
929
|
-
|
|
930
|
-
// set size
|
|
931
|
-
this._applySize(width, height);
|
|
932
|
-
|
|
933
|
-
this.fileManager.setFileData(oImg, file);
|
|
934
|
-
|
|
935
|
-
oImg.onload = this.#OnloadImg.bind(this, oImg, this._svgDefaultSize, container);
|
|
936
|
-
this.component.insert(container, { skipCharCount: false, skipSelection: true, skipHistory: false });
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
/**
|
|
940
|
-
* @private
|
|
941
906
|
* @description Updates the image source URL.
|
|
942
907
|
* @param {string} src - The new image source.
|
|
943
908
|
* @param {HTMLImageElement} element - The image element.
|
|
944
909
|
* @param {{ name: string, size: number }} file - File metadata.
|
|
945
910
|
*/
|
|
946
|
-
|
|
911
|
+
#updateSrc(src, element, file) {
|
|
947
912
|
element.src = src;
|
|
948
913
|
this.fileManager.setFileData(element, file);
|
|
949
914
|
this.component.select(element, Image_.key);
|
|
950
915
|
}
|
|
951
916
|
|
|
952
917
|
/**
|
|
953
|
-
* @private
|
|
954
918
|
* @description Registers the uploaded image and inserts it into the editor.
|
|
955
919
|
* @param {ImageInfo_image} info - Image info.
|
|
956
920
|
* @param {Object<string, *>} response - Server response data.
|
|
957
921
|
*/
|
|
958
|
-
|
|
922
|
+
#register(info, response) {
|
|
923
|
+
this.#produceIndex = 0;
|
|
959
924
|
const fileList = response.result;
|
|
960
925
|
|
|
961
926
|
for (let i = 0, len = fileList.length, file; i < len; i++) {
|
|
@@ -964,34 +929,44 @@ class Image_ extends EditorInjector {
|
|
|
964
929
|
size: fileList[i].size
|
|
965
930
|
};
|
|
966
931
|
if (info.isUpdate) {
|
|
967
|
-
this
|
|
932
|
+
this.#updateSrc(fileList[i].url, info.element, file);
|
|
968
933
|
break;
|
|
969
934
|
} else {
|
|
970
|
-
this
|
|
935
|
+
this.#produce(fileList[i].url, info.anchor, info.inputWidth, info.inputHeight, info.align, file, info.alt, i === len - 1);
|
|
971
936
|
}
|
|
972
937
|
}
|
|
973
938
|
}
|
|
974
939
|
|
|
975
940
|
/**
|
|
976
|
-
* @private
|
|
977
941
|
* @description Uploads the image to the server.
|
|
978
942
|
* @param {ImageInfo_image} info - Image upload info.
|
|
979
943
|
* @param {FileList} files - List of image files.
|
|
980
944
|
*/
|
|
981
|
-
|
|
945
|
+
#serverUpload(info, files) {
|
|
982
946
|
if (!files) return;
|
|
983
947
|
|
|
984
948
|
// server upload
|
|
985
949
|
const imageUploadUrl = this.pluginOptions.uploadUrl;
|
|
986
950
|
if (typeof imageUploadUrl === 'string' && imageUploadUrl.length > 0) {
|
|
987
|
-
this.fileManager.upload(imageUploadUrl, this.pluginOptions.uploadHeaders, files, this.#UploadCallBack.bind(this, info), this.
|
|
951
|
+
this.fileManager.upload(imageUploadUrl, this.pluginOptions.uploadHeaders, files, this.#UploadCallBack.bind(this, info), this.#error.bind(this));
|
|
988
952
|
} else {
|
|
989
|
-
this
|
|
953
|
+
this.#setBase64(files, info.anchor, info.inputWidth, info.inputHeight, info.align, info.alt, info.isUpdate);
|
|
990
954
|
}
|
|
991
955
|
}
|
|
992
956
|
|
|
993
957
|
/**
|
|
994
|
-
* @
|
|
958
|
+
* @description Handles image upload via URL.
|
|
959
|
+
* @param {*} info - Image information.
|
|
960
|
+
*/
|
|
961
|
+
#urlUpload(info) {
|
|
962
|
+
this.#produceIndex = 0;
|
|
963
|
+
const infoUrl = info.url;
|
|
964
|
+
|
|
965
|
+
if (this.modal.isUpdate) this.#updateSrc(infoUrl, info.element, info.files);
|
|
966
|
+
else this.#produce(infoUrl, info.anchor, info.inputWidth, info.inputHeight, info.align, info.files, info.alt, true);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
995
970
|
* @description Converts an image file to Base64 and inserts it into the editor.
|
|
996
971
|
* @param {FileList|File[]} files - List of image files.
|
|
997
972
|
* @param {?Node} anchor - Optional anchor wrapping the image.
|
|
@@ -1001,7 +976,7 @@ class Image_ extends EditorInjector {
|
|
|
1001
976
|
* @param {string} alt - Alternative text.
|
|
1002
977
|
* @param {boolean} isUpdate - Whether the image is being updated.
|
|
1003
978
|
*/
|
|
1004
|
-
|
|
979
|
+
#setBase64(files, anchor, width, height, align, alt, isUpdate) {
|
|
1005
980
|
try {
|
|
1006
981
|
const filesLen = this.modal.isUpdate ? 1 : files.length;
|
|
1007
982
|
|
|
@@ -1014,28 +989,26 @@ class Image_ extends EditorInjector {
|
|
|
1014
989
|
this._base64RenderIndex = filesLen;
|
|
1015
990
|
const filesStack = new Array(filesLen);
|
|
1016
991
|
|
|
1017
|
-
if (this
|
|
992
|
+
if (this.#resizing) {
|
|
1018
993
|
this.inputX.value = width;
|
|
1019
994
|
this.inputY.value = height;
|
|
1020
995
|
}
|
|
1021
996
|
|
|
1022
|
-
for (let i = 0, reader, file; i < filesLen; i++) {
|
|
997
|
+
for (let i = 0, renderFunc = this.#onRenderBase64.bind(this), reader, file; i < filesLen; i++) {
|
|
1023
998
|
reader = new FileReader();
|
|
1024
999
|
file = files[i];
|
|
1025
1000
|
|
|
1026
|
-
reader.onload = function (on_reader, update, updateElement, on_file, index) {
|
|
1001
|
+
reader.onload = function (loadCallback, on_reader, update, updateElement, on_file, index) {
|
|
1027
1002
|
filesStack[index] = {
|
|
1028
1003
|
result: on_reader.result,
|
|
1029
1004
|
file: on_file
|
|
1030
1005
|
};
|
|
1031
1006
|
|
|
1032
1007
|
if (--this._base64RenderIndex === 0) {
|
|
1033
|
-
|
|
1008
|
+
loadCallback(update, filesStack, updateElement, anchor, width, height, align, alt);
|
|
1034
1009
|
this.ui.hideLoading();
|
|
1035
1010
|
}
|
|
1036
|
-
}.bind(this, reader, isUpdate, this
|
|
1037
|
-
// se-ts-ignore
|
|
1038
|
-
this._onRenderBase64;
|
|
1011
|
+
}.bind(this, renderFunc, reader, isUpdate, this.#element, file, i);
|
|
1039
1012
|
|
|
1040
1013
|
reader.readAsDataURL(file);
|
|
1041
1014
|
}
|
|
@@ -1046,7 +1019,6 @@ class Image_ extends EditorInjector {
|
|
|
1046
1019
|
}
|
|
1047
1020
|
|
|
1048
1021
|
/**
|
|
1049
|
-
* @private
|
|
1050
1022
|
* @description Inserts an image using a Base64-encoded string.
|
|
1051
1023
|
* @param {boolean} update - Whether the image is being updated.
|
|
1052
1024
|
* @param {Array<{result: string, file: { name: string, size: number }}>} filesStack - Stack of Base64-encoded files.
|
|
@@ -1059,24 +1031,25 @@ class Image_ extends EditorInjector {
|
|
|
1059
1031
|
* @param {string} align - Image alignment.
|
|
1060
1032
|
* @param {string} alt - Alternative text.
|
|
1061
1033
|
*/
|
|
1062
|
-
|
|
1034
|
+
#onRenderBase64(update, filesStack, updateElement, anchor, width, height, align, alt) {
|
|
1035
|
+
this.#produceIndex = 0;
|
|
1036
|
+
|
|
1063
1037
|
for (let i = 0, len = filesStack.length; i < len; i++) {
|
|
1064
1038
|
if (update) {
|
|
1065
|
-
this
|
|
1039
|
+
this.#updateSrc(filesStack[i].result, updateElement, filesStack[i].file);
|
|
1066
1040
|
} else {
|
|
1067
|
-
this
|
|
1041
|
+
this.#produce(filesStack[i].result, anchor, width, height, align, filesStack[i].file, alt, i === len - 1);
|
|
1068
1042
|
}
|
|
1069
1043
|
}
|
|
1070
1044
|
}
|
|
1071
1045
|
|
|
1072
1046
|
/**
|
|
1073
|
-
* @private
|
|
1074
1047
|
* @description Wraps an image element with an anchor if provided.
|
|
1075
1048
|
* @param {Node} imgTag - The image element to be wrapped.
|
|
1076
1049
|
* @param {?Node} anchor - The anchor element to wrap around the image. If null, returns the image itself.
|
|
1077
1050
|
* @returns {Node} - The wrapped image inside the anchor or the original image element.
|
|
1078
1051
|
*/
|
|
1079
|
-
|
|
1052
|
+
#setAnchor(imgTag, anchor) {
|
|
1080
1053
|
if (anchor) {
|
|
1081
1054
|
anchor.appendChild(imgTag);
|
|
1082
1055
|
return anchor;
|
|
@@ -1086,12 +1059,11 @@ class Image_ extends EditorInjector {
|
|
|
1086
1059
|
}
|
|
1087
1060
|
|
|
1088
1061
|
/**
|
|
1089
|
-
* @private
|
|
1090
1062
|
* @description Handles errors during image upload and displays appropriate messages.
|
|
1091
1063
|
* @param {Object<string, *>} response - The error response from the server.
|
|
1092
1064
|
* @returns {Promise<void>}
|
|
1093
1065
|
*/
|
|
1094
|
-
async
|
|
1066
|
+
async #error(response) {
|
|
1095
1067
|
const message = await this.triggerEvent('onImageUploadError', { error: response });
|
|
1096
1068
|
const err = message === NO_EVENT ? response.errorMessage : message || response.errorMessage;
|
|
1097
1069
|
this.ui.alertOpen(err, 'error');
|
|
@@ -1107,13 +1079,58 @@ class Image_ extends EditorInjector {
|
|
|
1107
1079
|
if ((await this.triggerEvent('imageUploadHandler', { xmlHttp, info })) === NO_EVENT) {
|
|
1108
1080
|
const response = JSON.parse(xmlHttp.responseText);
|
|
1109
1081
|
if (response.errorMessage) {
|
|
1110
|
-
this
|
|
1082
|
+
this.#error(response);
|
|
1111
1083
|
} else {
|
|
1112
|
-
this
|
|
1084
|
+
this.#register(info, response);
|
|
1113
1085
|
}
|
|
1114
1086
|
}
|
|
1115
1087
|
}
|
|
1116
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* @description Opens a specific tab inside the modal.
|
|
1091
|
+
* @param {MouseEvent|string} e - The event object or tab name.
|
|
1092
|
+
* @returns {boolean} - Whether the tab was successfully opened.
|
|
1093
|
+
*/
|
|
1094
|
+
#OpenTab(e) {
|
|
1095
|
+
const modalForm = this.modal.form;
|
|
1096
|
+
const targetElement = typeof e === 'string' ? modalForm.querySelector('._se_tab_link') : dom.query.getEventTarget(e);
|
|
1097
|
+
|
|
1098
|
+
if (!/^BUTTON$/i.test(targetElement.tagName)) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Declare all variables
|
|
1103
|
+
const tabName = targetElement.getAttribute('data-tab-link');
|
|
1104
|
+
let i;
|
|
1105
|
+
|
|
1106
|
+
// Get all elements with class="tabcontent" and hide them
|
|
1107
|
+
const tabContent = /** @type {HTMLCollectionOf<HTMLElement>}*/ (modalForm.getElementsByClassName('_se_tab_content'));
|
|
1108
|
+
for (i = 0; i < tabContent.length; i++) {
|
|
1109
|
+
tabContent[i].style.display = 'none';
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Get all elements with class="tablinks" and remove the class "active"
|
|
1113
|
+
const tabLinks = modalForm.getElementsByClassName('_se_tab_link');
|
|
1114
|
+
for (i = 0; i < tabLinks.length; i++) {
|
|
1115
|
+
dom.utils.removeClass(tabLinks[i], 'active');
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// Show the current tab, and add an "active" class to the button that opened the tab
|
|
1119
|
+
/** @type {HTMLElement}*/ (modalForm.querySelector('._se_tab_content_' + tabName)).style.display = 'block';
|
|
1120
|
+
dom.utils.addClass(targetElement, 'active');
|
|
1121
|
+
|
|
1122
|
+
// focus
|
|
1123
|
+
if (e !== 'init') {
|
|
1124
|
+
if (tabName === 'image') {
|
|
1125
|
+
this.focusElement.focus();
|
|
1126
|
+
} else if (tabName === 'url') {
|
|
1127
|
+
this.anchor.urlInput.focus();
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1117
1134
|
#RemoveSelectedFiles() {
|
|
1118
1135
|
this.imgInputFile.value = '';
|
|
1119
1136
|
if (this.imgUrlFile) {
|
|
@@ -1131,10 +1148,10 @@ class Image_ extends EditorInjector {
|
|
|
1131
1148
|
return;
|
|
1132
1149
|
}
|
|
1133
1150
|
|
|
1134
|
-
if (xy === 'x' && this
|
|
1151
|
+
if (xy === 'x' && this.#onlyPercentage && e.target.value > 100) {
|
|
1135
1152
|
e.target.value = 100;
|
|
1136
1153
|
} else if (this.proportion.checked) {
|
|
1137
|
-
const ratioSize = Figure.CalcRatio(this.inputX.value, this.inputY.value, this.sizeUnit, this
|
|
1154
|
+
const ratioSize = Figure.CalcRatio(this.inputX.value, this.inputY.value, this.sizeUnit, this.#ratio);
|
|
1138
1155
|
if (xy === 'x') {
|
|
1139
1156
|
this.inputY.value = String(ratioSize.h);
|
|
1140
1157
|
} else {
|
|
@@ -1144,36 +1161,31 @@ class Image_ extends EditorInjector {
|
|
|
1144
1161
|
}
|
|
1145
1162
|
|
|
1146
1163
|
#OnChangeRatio() {
|
|
1147
|
-
this
|
|
1148
|
-
? Figure.GetRatio(this.inputX.value, this.inputY.value, this.sizeUnit)
|
|
1149
|
-
: {
|
|
1150
|
-
w: 1,
|
|
1151
|
-
h: 1
|
|
1152
|
-
};
|
|
1164
|
+
this.#ratio = this.proportion.checked ? Figure.GetRatio(this.inputX.value, this.inputY.value, this.sizeUnit) : { w: 0, h: 0 };
|
|
1153
1165
|
}
|
|
1154
1166
|
|
|
1155
1167
|
#OnClickRevert() {
|
|
1156
|
-
if (this
|
|
1157
|
-
this.inputX.value = Number(this
|
|
1168
|
+
if (this.#onlyPercentage) {
|
|
1169
|
+
this.inputX.value = Number(this.#origin_w) > 100 ? '100' : this.#origin_w;
|
|
1158
1170
|
} else {
|
|
1159
|
-
this.inputX.value = this
|
|
1160
|
-
this.inputY.value = this
|
|
1171
|
+
this.inputX.value = this.#origin_w;
|
|
1172
|
+
this.inputY.value = this.#origin_h;
|
|
1161
1173
|
}
|
|
1162
1174
|
}
|
|
1163
1175
|
|
|
1164
1176
|
#OnClickAsButton({ target }) {
|
|
1165
|
-
this
|
|
1177
|
+
this.#activeAsInline(target.getAttribute('data-command') === 'asInline');
|
|
1166
1178
|
}
|
|
1167
1179
|
|
|
1168
1180
|
#OnLinkPreview(e) {
|
|
1169
1181
|
const value = e.target.value.trim();
|
|
1170
|
-
this
|
|
1182
|
+
this.#linkValue = this.previewSrc.textContent = !value
|
|
1171
1183
|
? ''
|
|
1172
1184
|
: this.options.get('defaultUrlProtocol') && !value.includes('://') && value.indexOf('#') !== 0
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1185
|
+
? this.options.get('defaultUrlProtocol') + value
|
|
1186
|
+
: !value.includes('://')
|
|
1187
|
+
? '/' + value
|
|
1188
|
+
: value;
|
|
1177
1189
|
}
|
|
1178
1190
|
|
|
1179
1191
|
#OnfileInputChange({ target }) {
|
|
@@ -1195,33 +1207,23 @@ class Image_ extends EditorInjector {
|
|
|
1195
1207
|
|
|
1196
1208
|
#SetUrlInput(target) {
|
|
1197
1209
|
this.altText.value = target.getAttribute('data-value') || target.alt;
|
|
1198
|
-
this
|
|
1210
|
+
this.#linkValue = this.previewSrc.textContent = this.imgUrlFile.value = target.getAttribute('data-command') || target.src;
|
|
1199
1211
|
this.imgUrlFile.focus();
|
|
1200
1212
|
}
|
|
1201
1213
|
|
|
1202
1214
|
#OnloadImg(oImg, _svgDefaultSize, container) {
|
|
1215
|
+
this.#produceIndex--;
|
|
1216
|
+
delete oImg.onload;
|
|
1217
|
+
|
|
1203
1218
|
// svg exception handling
|
|
1204
|
-
if (oImg.offsetWidth === 0) this
|
|
1205
|
-
if (this.options.get('componentAutoSelect')) {
|
|
1206
|
-
this.component.select(oImg, Image_.key);
|
|
1207
|
-
} else {
|
|
1208
|
-
if (!this.component.isInline(container)) {
|
|
1209
|
-
const line = this.format.addLine(container, null);
|
|
1210
|
-
if (line) this.selection.setRange(line, 0, line, 0);
|
|
1211
|
-
} else {
|
|
1212
|
-
const r = this.selection.getNearRange(container);
|
|
1213
|
-
if (r) {
|
|
1214
|
-
this.selection.setRange(r.container, r.offset, r.container, r.offset);
|
|
1215
|
-
} else {
|
|
1216
|
-
this.component.select(oImg, Image_.key);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1219
|
+
if (oImg.offsetWidth === 0) this.#applySize(_svgDefaultSize, '');
|
|
1220
1220
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1221
|
+
if (this.#produceIndex === 0) {
|
|
1222
|
+
this.component.applyInsertBehavior(container, null, this.pluginOptions.insertBehavior || this.options.get('componentInsertBehavior'));
|
|
1223
1223
|
|
|
1224
|
-
|
|
1224
|
+
this.editor._iframeAutoHeight(this.frameContext);
|
|
1225
|
+
this.history.push(false);
|
|
1226
|
+
}
|
|
1225
1227
|
}
|
|
1226
1228
|
}
|
|
1227
1229
|
|