jodit 4.1.16 → 4.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +22 -5
  2. package/es2015/jodit.css +82 -31
  3. package/es2015/jodit.fat.min.css +1 -1
  4. package/es2015/jodit.fat.min.js +2 -2
  5. package/es2015/jodit.js +1378 -546
  6. package/es2015/jodit.min.css +1 -1
  7. package/es2015/jodit.min.js +2 -2
  8. package/es2015/plugins/debug/debug.js +1 -1
  9. package/es2015/plugins/debug/debug.min.js +1 -1
  10. package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
  11. package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
  12. package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
  13. package/es2018/jodit.css +82 -31
  14. package/es2018/jodit.fat.min.css +1 -1
  15. package/es2018/jodit.fat.min.js +2 -2
  16. package/es2018/jodit.js +1364 -546
  17. package/es2018/jodit.min.css +1 -1
  18. package/es2018/jodit.min.js +2 -2
  19. package/es2018/plugins/debug/debug.js +1 -1
  20. package/es2018/plugins/debug/debug.min.js +1 -1
  21. package/es2018/plugins/speech-recognize/speech-recognize.css +1 -1
  22. package/es2018/plugins/speech-recognize/speech-recognize.js +1 -1
  23. package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
  24. package/es2021/jodit.css +82 -31
  25. package/es2021/jodit.fat.min.css +1 -1
  26. package/es2021/jodit.fat.min.js +2 -2
  27. package/es2021/jodit.js +1361 -546
  28. package/es2021/jodit.min.css +1 -1
  29. package/es2021/jodit.min.js +2 -2
  30. package/es2021/plugins/debug/debug.js +1 -1
  31. package/es2021/plugins/debug/debug.min.js +1 -1
  32. package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
  33. package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
  34. package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
  35. package/es2021.en/jodit.css +82 -31
  36. package/es2021.en/jodit.fat.min.css +1 -1
  37. package/es2021.en/jodit.fat.min.js +2 -2
  38. package/es2021.en/jodit.js +1361 -546
  39. package/es2021.en/jodit.min.css +1 -1
  40. package/es2021.en/jodit.min.js +2 -2
  41. package/es2021.en/plugins/debug/debug.js +1 -1
  42. package/es2021.en/plugins/debug/debug.min.js +1 -1
  43. package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
  44. package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
  45. package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
  46. package/es5/jodit.css +131 -35
  47. package/es5/jodit.fat.min.css +1 -1
  48. package/es5/jodit.fat.min.js +2 -2
  49. package/es5/jodit.js +1402 -482
  50. package/es5/jodit.min.css +3 -3
  51. package/es5/jodit.min.js +2 -2
  52. package/es5/plugins/debug/debug.js +1 -1
  53. package/es5/plugins/debug/debug.min.js +1 -1
  54. package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
  55. package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
  56. package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
  57. package/esm/config.d.ts +22 -0
  58. package/esm/config.js +22 -0
  59. package/esm/core/async/async.js +7 -1
  60. package/esm/core/constants.js +1 -1
  61. package/esm/core/helpers/size/position.js +2 -2
  62. package/esm/core/helpers/utils/selector.d.ts +3 -1
  63. package/esm/core/helpers/utils/selector.js +3 -3
  64. package/esm/core/ui/button/tooltip/tooltip.js +2 -1
  65. package/esm/core/ui/popup/popup.js +1 -1
  66. package/esm/modules/dialog/dialog.js +1 -0
  67. package/esm/modules/file-browser/data-provider.js +4 -1
  68. package/esm/modules/file-browser/file-browser.js +9 -2
  69. package/esm/modules/image-editor/templates/form.js +6 -5
  70. package/esm/modules/toolbar/button/button.d.ts +1 -1
  71. package/esm/modules/widget/tabs/tabs.d.ts +4 -4
  72. package/esm/modules/widget/tabs/tabs.js +9 -7
  73. package/esm/plugins/ai-assistant/ai-assistant.d.ts +1 -1
  74. package/esm/plugins/ai-assistant/ai-assistant.js +3 -3
  75. package/esm/plugins/image-properties/config.d.ts +5 -83
  76. package/esm/plugins/image-properties/config.js +0 -3
  77. package/esm/plugins/image-properties/image-properties.d.ts +22 -32
  78. package/esm/plugins/image-properties/image-properties.js +133 -402
  79. package/{types/plugins/image-properties/templates/form.d.ts → esm/plugins/image-properties/readers/align.d.ts} +3 -5
  80. package/esm/plugins/image-properties/readers/align.js +24 -0
  81. package/esm/plugins/image-properties/readers/index.d.ts +12 -0
  82. package/esm/plugins/image-properties/readers/index.js +38 -0
  83. package/esm/plugins/image-properties/readers/link.d.ts +9 -0
  84. package/esm/plugins/image-properties/readers/link.js +19 -0
  85. package/esm/plugins/image-properties/readers/margin.d.ts +8 -0
  86. package/esm/plugins/image-properties/readers/margin.js +28 -0
  87. package/esm/plugins/image-properties/readers/size.d.ts +8 -0
  88. package/esm/plugins/image-properties/readers/size.js +36 -0
  89. package/esm/plugins/image-properties/ui/ui-image-form.d.ts +29 -0
  90. package/esm/plugins/image-properties/ui/ui-image-form.js +171 -0
  91. package/esm/plugins/image-properties/ui/ui-image-main-tab.d.ts +36 -0
  92. package/esm/plugins/image-properties/ui/ui-image-main-tab.js +179 -0
  93. package/esm/plugins/image-properties/ui/ui-image-position-tab.d.ts +36 -0
  94. package/esm/plugins/image-properties/ui/ui-image-position-tab.js +261 -0
  95. package/esm/plugins/image-properties/{templates/main-tab.d.ts → utils/open-image-editor.d.ts} +5 -3
  96. package/esm/plugins/image-properties/utils/open-image-editor.js +52 -0
  97. package/esm/plugins/image-properties/utils/open-image-popup.d.ts +9 -0
  98. package/esm/plugins/image-properties/utils/open-image-popup.js +34 -0
  99. package/esm/plugins/image-properties/utils/utils.d.ts +9 -0
  100. package/esm/plugins/image-properties/utils/utils.js +21 -0
  101. package/esm/plugins/image-properties/writers/index.d.ts +12 -0
  102. package/esm/plugins/image-properties/writers/index.js +53 -0
  103. package/esm/plugins/image-properties/{templates/form.d.ts → writers/link.d.ts} +2 -4
  104. package/esm/plugins/image-properties/writers/link.js +24 -0
  105. package/esm/plugins/image-properties/writers/margin.d.ts +8 -0
  106. package/esm/plugins/image-properties/writers/margin.js +33 -0
  107. package/esm/plugins/image-properties/writers/size.d.ts +7 -0
  108. package/esm/plugins/image-properties/writers/size.js +28 -0
  109. package/esm/plugins/select/config.d.ts +7 -0
  110. package/esm/plugins/select/config.js +2 -1
  111. package/esm/plugins/select/select.d.ts +7 -3
  112. package/esm/plugins/select/select.js +21 -4
  113. package/esm/types/ui.d.ts +1 -1
  114. package/package.json +1 -1
  115. package/types/config.d.ts +22 -0
  116. package/types/core/helpers/utils/selector.d.ts +3 -1
  117. package/types/modules/toolbar/button/button.d.ts +1 -1
  118. package/types/modules/widget/tabs/tabs.d.ts +4 -4
  119. package/types/plugins/ai-assistant/ai-assistant.d.ts +1 -1
  120. package/types/plugins/image-properties/config.d.ts +5 -83
  121. package/types/plugins/image-properties/image-properties.d.ts +22 -32
  122. package/types/plugins/image-properties/{templates/main-tab.d.ts → readers/align.d.ts} +3 -5
  123. package/types/plugins/image-properties/readers/index.d.ts +12 -0
  124. package/types/plugins/image-properties/readers/link.d.ts +9 -0
  125. package/types/plugins/image-properties/readers/margin.d.ts +8 -0
  126. package/types/plugins/image-properties/readers/size.d.ts +8 -0
  127. package/types/plugins/image-properties/ui/ui-image-form.d.ts +29 -0
  128. package/types/plugins/image-properties/ui/ui-image-main-tab.d.ts +36 -0
  129. package/types/plugins/image-properties/ui/ui-image-position-tab.d.ts +36 -0
  130. package/types/plugins/image-properties/utils/open-image-editor.d.ts +12 -0
  131. package/types/plugins/image-properties/utils/open-image-popup.d.ts +9 -0
  132. package/types/plugins/image-properties/utils/utils.d.ts +9 -0
  133. package/types/plugins/image-properties/writers/index.d.ts +12 -0
  134. package/{esm/plugins/image-properties/templates/position-tab.d.ts → types/plugins/image-properties/writers/link.d.ts} +2 -4
  135. package/types/plugins/image-properties/writers/margin.d.ts +8 -0
  136. package/types/plugins/image-properties/writers/size.d.ts +7 -0
  137. package/types/plugins/select/config.d.ts +7 -0
  138. package/types/plugins/select/select.d.ts +7 -3
  139. package/types/types/ui.d.ts +1 -1
  140. package/esm/plugins/image-properties/templates/form.js +0 -26
  141. package/esm/plugins/image-properties/templates/main-tab.js +0 -47
  142. package/esm/plugins/image-properties/templates/position-tab.js +0 -66
  143. package/types/plugins/image-properties/templates/position-tab.d.ts +0 -10
@@ -13,17 +13,18 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
13
13
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
14
14
  return c > 3 && r && Object.defineProperty(target, key, r), r;
15
15
  };
16
- import { autobind, watch } from "jodit/esm/core/decorators/index.js";
16
+ import { cache, cached, watch } from "jodit/esm/core/decorators/index.js";
17
+ import { Dom } from "jodit/esm/core/dom/dom.js";
17
18
  import { pluginSystem } from "jodit/esm/core/global.js";
18
- import { attr, css, hAlignElement, isArray, isNumeric, isString, kebabCase, markOwner, position, refs, trim } from "jodit/esm/core/helpers/index.js";
19
+ import { isAbortError, isNumeric, markOwner } from "jodit/esm/core/helpers/index.js";
20
+ import { Plugin } from "jodit/esm/core/plugin/plugin.js";
19
21
  import { Button } from "jodit/esm/core/ui/button/index.js";
20
- import { Dom, Icon, Plugin, Popup } from "jodit/esm/modules/index.js";
21
- import { openImageEditor } from "jodit/esm/modules/image-editor/image-editor.js";
22
- import { FileSelectorWidget, TabsWidget } from "jodit/esm/modules/widget/index.js";
23
22
  import "./config";
24
- import { form } from "./templates/form";
25
- import { mainTab } from "./templates/main-tab";
26
- import { positionTab } from "./templates/position-tab";
23
+ import { UIImagePropertiesForm } from "./ui/ui-image-form";
24
+ import { openImageEditorDialog } from "./utils/open-image-editor";
25
+ import { openImagePopup } from "./utils/open-image-popup";
26
+ import { readValuesFromImage } from "./readers";
27
+ import { applyValuesToImage } from "./writers";
27
28
  /**
28
29
  * Plug-in for image editing window
29
30
  *
@@ -37,15 +38,6 @@ import { positionTab } from "./templates/position-tab";
37
38
  * });
38
39
  * ```
39
40
  */
40
- const normalSizeToString = (value) => {
41
- value = trim(value);
42
- return /^[0-9]+$/.test(value) ? value + 'px' : value;
43
- };
44
- const normalSizeFromString = (value) => {
45
- return /^[-+]?[0-9.]+px$/.test(value.toString())
46
- ? parseFloat(value.toString())
47
- : value;
48
- };
49
41
  /**
50
42
  * Show dialog with image's options
51
43
  */
@@ -54,406 +46,123 @@ export class imageProperties extends Plugin {
54
46
  super(...arguments);
55
47
  this.state = {
56
48
  image: new Image(),
49
+ sourceImage: new Image(),
57
50
  get ratio() {
58
- return this.image.naturalWidth / this.image.naturalHeight || 1;
51
+ const { naturalWidth, naturalHeight } = this.image;
52
+ return naturalWidth / naturalHeight || 1;
59
53
  },
60
54
  sizeIsLocked: true,
61
- marginIsLocked: true
55
+ marginIsLocked: true,
56
+ values: {
57
+ style: '',
58
+ imageSrc: '',
59
+ borderRadius: 0,
60
+ imageTitle: '',
61
+ imageAlt: '',
62
+ imageLink: '',
63
+ imageLinkOpenInNewTab: false,
64
+ imageWidth: 0,
65
+ imageHeight: 0,
66
+ marginTop: 0,
67
+ marginRight: 0,
68
+ marginBottom: 0,
69
+ marginLeft: 0,
70
+ classes: '',
71
+ id: '',
72
+ align: ''
73
+ }
62
74
  };
63
75
  this.activeTabState = {
64
- __activeTab: 'Image'
76
+ activeTab: 'Image'
65
77
  };
66
78
  }
67
- onChangeMarginIsLocked() {
68
- if (!this.form) {
69
- return;
70
- }
71
- const { marginRight, marginBottom, marginLeft, lockMargin } = refs(this.form);
72
- [marginRight, marginBottom, marginLeft].forEach(elm => {
73
- attr(elm, 'disabled', this.state.marginIsLocked || null);
79
+ get form() {
80
+ return new UIImagePropertiesForm(this.j, this.state, this.activeTabState, {
81
+ openImageEditor: () => openImageEditorDialog(this.j, this.state),
82
+ openImagePopup: target => openImagePopup(this.j, this.dialog, this.state, target)
74
83
  });
75
- lockMargin.innerHTML = Icon.get(this.state.marginIsLocked ? 'lock' : 'unlock');
76
- }
77
- onChangeSizeIsLocked() {
78
- if (!this.form) {
79
- return;
80
- }
81
- const { lockSize, imageWidth } = refs(this.form);
82
- lockSize.innerHTML = Icon.get(this.state.sizeIsLocked ? 'lock' : 'unlock');
83
- lockSize.classList.remove('jodit-properties__lock');
84
- lockSize.classList.remove('jodit-properties__unlock');
85
- lockSize.classList.add(this.state.sizeIsLocked
86
- ? 'jodit-properties__lock'
87
- : 'jodit-properties__unlock');
88
- this.j.e.fire(imageWidth, 'change');
89
- }
90
- /**
91
- * Open dialog editing image properties
92
- *
93
- * @example
94
- * ```javascript
95
- * const editor = Jodit.makeJodit('#editor');
96
- * img = editor.createInside.element('img');
97
- *
98
- * img.setAttribute('src', 'images/some-image.png');
99
- * editor.s.insertImage(img);
100
- * // open the properties of the editing window
101
- * editor.events.fire('openImageProperties', img);
102
- * ```
103
- */
104
- open() {
105
- this.makeForm();
106
- this.activeTabState.__activeTab = 'Image';
107
- this.j.e.fire('hidePopup');
108
- markOwner(this.j, this.dialog.container);
109
- this.state.marginIsLocked = true;
110
- this.state.sizeIsLocked = true;
111
- this.onChangeMarginIsLocked();
112
- this.onChangeSizeIsLocked();
113
- this.updateValues();
114
- this.dialog.open().setModal(true).setPosition();
115
- return false;
116
84
  }
117
85
  /**
118
- * Create form for edit image properties
86
+ * Dialog for form
119
87
  */
120
- makeForm() {
121
- if (this.dialog) {
122
- return;
123
- }
124
- this.dialog = this.j.dlg({
88
+ get dialog() {
89
+ const { j } = this;
90
+ const dialog = j.dlg({
125
91
  minWidth: Math.min(400, screen.width),
126
92
  minHeight: 590,
127
93
  buttons: ['fullsize', 'dialog.close']
128
94
  });
129
- const editor = this.j, opt = editor.o, i18n = editor.i18n.bind(editor), buttons = {
130
- check: Button(editor, 'ok', 'Apply', 'primary'),
131
- remove: Button(editor, 'bin', 'Delete')
132
- };
133
- editor.e.on(this.dialog, 'afterClose', () => {
134
- if (this.state.image.parentNode &&
135
- opt.image.selectImageAfterClose) {
136
- editor.s.select(this.state.image);
137
- }
95
+ const buttons = this.__buttons;
96
+ buttons.check.onAction(() => {
97
+ applyValuesToImage(j, this.state, this.state.sourceImage);
98
+ j.synchronizeValues();
99
+ dialog.close();
138
100
  });
139
101
  buttons.remove.onAction(() => {
140
- editor.s.removeNode(this.state.image);
141
- this.dialog.close();
102
+ j.s.removeNode(this.state.sourceImage);
103
+ dialog.close();
142
104
  });
143
- const { dialog } = this;
144
- dialog.setHeader(i18n('Image properties'));
145
- const mainForm = form(editor);
146
- this.form = mainForm;
147
- dialog.setContent(mainForm);
148
- const { tabsBox } = refs(this.form);
149
- if (tabsBox) {
150
- tabsBox.appendChild(TabsWidget(editor, [
151
- { name: 'Image', content: mainTab(editor) },
152
- { name: 'Advanced', content: positionTab(editor) }
153
- ], this.activeTabState));
154
- }
155
- buttons.check.onAction(this.onApply);
156
- const { changeImage, editImage } = refs(this.form);
157
- editor.e.on(changeImage, 'click', this.openImagePopup);
158
- if (opt.image.useImageEditor) {
159
- editor.e.on(editImage, 'click', this.openImageEditor);
160
- }
161
- const { lockSize, lockMargin, imageWidth, imageHeight } = refs(mainForm);
162
- if (lockSize) {
163
- editor.e.on(lockSize, 'click', () => {
164
- this.state.sizeIsLocked = !this.state.sizeIsLocked;
165
- });
166
- }
167
- editor.e.on(lockMargin, 'click', (e) => {
168
- this.state.marginIsLocked = !this.state.marginIsLocked;
169
- e.preventDefault();
105
+ buttons.cancel.onAction(() => {
106
+ dialog.close();
170
107
  });
171
- const changeSizes = (event) => {
172
- if (!isNumeric(imageWidth.value) || !isNumeric(imageHeight.value)) {
173
- return;
174
- }
175
- const w = parseFloat(imageWidth.value), h = parseFloat(imageHeight.value);
176
- if (event.target === imageWidth) {
177
- imageHeight.value = Math.round(w / this.state.ratio).toString();
178
- }
179
- else {
180
- imageWidth.value = Math.round(h * this.state.ratio).toString();
181
- }
182
- };
183
- editor.e.on([imageWidth, imageHeight], 'change keydown mousedown paste', (event) => {
184
- if (!this.state.sizeIsLocked) {
185
- return;
108
+ dialog.setHeader(j.i18n('Image properties'));
109
+ dialog.setContent(this.form);
110
+ dialog.setFooter([[buttons.cancel, buttons.remove], buttons.check]);
111
+ j.e.on(dialog, 'afterClose', () => {
112
+ if (this.state.image.parentNode &&
113
+ j.o.image.selectImageAfterClose) {
114
+ j.s.select(this.state.sourceImage);
186
115
  }
187
- editor.async.setTimeout(changeSizes.bind(this, event), {
188
- timeout: editor.defaultTimeout,
189
- label: 'image-properties-changeSize'
190
- });
191
116
  });
192
- dialog.setFooter([buttons.remove, buttons.check]);
193
- dialog.setSize(this.j.o.image.dialogWidth);
117
+ dialog.setSize(j.o.image.dialogWidth);
118
+ markOwner(j, dialog.container);
119
+ return dialog;
194
120
  }
195
- /**
196
- * Set input values from image
197
- */
198
- updateValues() {
199
- const opt = this.j.o;
200
- const { image } = this.state;
201
- const { marginTop, marginRight, marginBottom, marginLeft, lockMargin, imageSrc, id, classes, align, style, imageTitle, imageAlt, borderRadius, imageLink, imageWidth, imageHeight, imageLinkOpenInNewTab, imageViewSrc, lockSize } = refs(this.form);
202
- const updateLock = () => {
203
- lockMargin.checked = this.state.marginIsLocked;
204
- lockSize.checked = this.state.sizeIsLocked;
205
- }, updateAlign = () => {
206
- if (image.style.cssFloat &&
207
- ['left', 'right'].indexOf(image.style.cssFloat.toLowerCase()) !== -1) {
208
- align.value = css(image, 'float');
209
- }
210
- else {
211
- if (css(image, 'display') === 'block' &&
212
- image.style.marginLeft === 'auto' &&
213
- image.style.marginRight === 'auto') {
214
- align.value = 'center';
215
- }
216
- }
217
- }, updateBorderRadius = () => {
218
- borderRadius.value = (parseInt(image.style.borderRadius || '0', 10) || '0').toString();
219
- }, updateId = () => {
220
- id.value = attr(image, 'id') || '';
221
- }, updateStyle = () => {
222
- style.value = attr(image, 'style') || '';
223
- }, updateClasses = () => {
224
- classes.value = (attr(image, 'class') || '').replace(/jodit_focused_image[\s]*/, '');
225
- }, updateMargins = () => {
226
- if (!opt.image.editMargins) {
227
- return;
228
- }
229
- let equal = true, wasEmptyField = false;
230
- [marginTop, marginRight, marginBottom, marginLeft].forEach(elm => {
231
- const id = attr(elm, 'data-ref') || '';
232
- let value = image.style.getPropertyValue(kebabCase(id));
233
- if (!value) {
234
- wasEmptyField = true;
235
- elm.value = '';
236
- return;
237
- }
238
- if (/^[0-9]+(px)?$/.test(value)) {
239
- value = parseInt(value, 10);
240
- }
241
- elm.value = value.toString() || '';
242
- if ((wasEmptyField && elm.value) ||
243
- (equal &&
244
- id !== 'marginTop' &&
245
- elm.value !== marginTop.value)) {
246
- equal = false;
247
- }
248
- });
249
- this.state.marginIsLocked = equal;
250
- }, updateSizes = () => {
251
- const width = attr(image, 'width') ||
252
- css(image, 'width', true) ||
253
- false, height = attr(image, 'height') ||
254
- css(image, 'height', true) ||
255
- false;
256
- imageWidth.value =
257
- width !== false
258
- ? normalSizeFromString(width).toString()
259
- : image.offsetWidth.toString();
260
- imageHeight.value =
261
- height !== false
262
- ? normalSizeFromString(height).toString()
263
- : image.offsetHeight.toString();
264
- this.state.sizeIsLocked = (() => {
265
- if (!isNumeric(imageWidth.value) ||
266
- !isNumeric(imageHeight.value)) {
267
- return false;
268
- }
269
- const w = parseFloat(imageWidth.value), h = parseFloat(imageHeight.value);
270
- return Math.abs(w - h * this.state.ratio) < 1;
271
- })();
272
- }, updateText = () => {
273
- imageTitle.value = attr(image, 'title') || '';
274
- imageAlt.value = attr(image, 'alt') || '';
275
- const a = Dom.closest(image, 'a', this.j.editor);
276
- if (a) {
277
- imageLink.value = attr(a, 'href') || '';
278
- imageLinkOpenInNewTab.checked =
279
- attr(a, 'target') === '_blank';
280
- }
281
- else {
282
- imageLink.value = '';
283
- imageLinkOpenInNewTab.checked = false;
284
- }
285
- }, updateSrc = () => {
286
- imageSrc.value = attr(image, 'src') || '';
287
- if (imageViewSrc) {
288
- attr(imageViewSrc, 'src', attr(image, 'src') || '');
289
- }
121
+ get __buttons() {
122
+ const { j } = this;
123
+ return {
124
+ check: Button(j, 'ok', 'Apply', 'primary'),
125
+ remove: Button(j, 'bin', 'Delete'),
126
+ cancel: Button(j, 'cancel', 'Cancel')
290
127
  };
291
- updateLock();
292
- updateSrc();
293
- updateText();
294
- updateSizes();
295
- updateMargins();
296
- updateClasses();
297
- updateId();
298
- updateBorderRadius();
299
- updateAlign();
300
- updateStyle();
301
128
  }
302
129
  /**
303
- * Apply form's values to image
304
- */
305
- onApply() {
306
- const { style, imageSrc, borderRadius, imageTitle, imageAlt, imageLink, imageWidth, imageHeight, marginTop, marginRight, marginBottom, marginLeft, imageLinkOpenInNewTab, align, classes, id } = refs(this.form);
307
- const opt = this.j.o;
308
- const { image } = this.state;
309
- // styles
310
- if (opt.image.editStyle) {
311
- attr(image, 'style', style.value || null);
312
- }
313
- // Src
314
- if (imageSrc.value) {
315
- attr(image, 'src', imageSrc.value);
316
- }
317
- else {
318
- Dom.safeRemove(image);
319
- this.dialog.close();
320
- return;
321
- }
322
- // Border radius
323
- if (borderRadius.value !== '0' && /^[0-9]+$/.test(borderRadius.value)) {
324
- image.style.borderRadius = borderRadius.value + 'px';
325
- }
326
- else {
327
- image.style.borderRadius = '';
328
- }
329
- // Title
330
- attr(image, 'title', imageTitle.value || null);
331
- // Alt
332
- attr(image, 'alt', imageAlt.value || null);
333
- // Link
334
- let link = Dom.closest(image, 'a', this.j.editor);
335
- if (imageLink.value) {
336
- if (!link) {
337
- link = Dom.wrap(image, 'a', this.j.createInside);
338
- }
339
- attr(link, 'href', imageLink.value);
340
- attr(link, 'target', imageLinkOpenInNewTab.checked ? '_blank' : null);
341
- }
342
- else {
343
- if (link && link.parentNode) {
344
- link.parentNode.replaceChild(image, link);
345
- }
346
- }
347
- // Size
348
- if (imageWidth.value !== image.offsetWidth.toString() ||
349
- imageHeight.value !== image.offsetHeight.toString()) {
350
- const updatedtWidth = trim(imageWidth.value)
351
- ? normalSizeToString(imageWidth.value)
352
- : null;
353
- const updatedHeight = trim(imageHeight.value)
354
- ? normalSizeToString(imageHeight.value)
355
- : null;
356
- css(image, {
357
- width: updatedtWidth,
358
- height: updatedHeight
359
- });
360
- attr(image, 'width', attr(image, 'width') ? updatedtWidth : null);
361
- attr(image, 'height', attr(image, 'height') ? updatedHeight : null);
362
- }
363
- const margins = [marginTop, marginRight, marginBottom, marginLeft];
364
- if (opt.image.editMargins) {
365
- if (!this.state.marginIsLocked) {
366
- margins.forEach((margin) => {
367
- const side = attr(margin, 'data-ref') || '';
368
- css(image, side, normalSizeToString(margin.value));
369
- });
370
- }
371
- else {
372
- css(image, 'margin', normalSizeToString(marginTop.value));
373
- }
374
- }
375
- if (opt.image.editClass) {
376
- attr(image, 'class', classes.value || null);
377
- }
378
- if (opt.image.editId) {
379
- attr(image, 'id', id.value || null);
380
- }
381
- if (opt.image.editAlign) {
382
- hAlignElement(image, align.value);
383
- }
384
- this.j.synchronizeValues();
385
- this.dialog.close();
386
- }
387
- /**
388
- * Open image editor dialog
130
+ * Open dialog editing image properties
131
+ *
132
+ * @example
133
+ * ```javascript
134
+ * const editor = Jodit.makeJodit('#editor');
135
+ * img = editor.createInside.element('img');
136
+ *
137
+ * img.setAttribute('src', 'images/some-image.png');
138
+ * editor.s.insertImage(img);
139
+ * // open the properties of the editing window
140
+ * editor.events.fire('openImageProperties', img);
141
+ * ```
389
142
  */
390
- openImageEditor() {
391
- const url = attr(this.state.image, 'src') || '', a = this.j.c.element('a'), loadExternal = () => {
392
- if (a.host !== location.host) {
393
- this.j.confirm('You can only edit your own images. Download this image on the host?', yes => {
394
- if (yes && this.j.uploader) {
395
- this.j.uploader.uploadRemoteImage(a.href.toString(), resp => {
396
- this.j.alert('The image has been successfully uploaded to the host!', () => {
397
- if (isString(resp.newfilename)) {
398
- attr(this.state.image, 'src', resp.baseurl +
399
- resp.newfilename);
400
- this.updateValues();
401
- }
402
- });
403
- }, error => {
404
- this.j.alert('There was an error loading %s', error.message);
405
- });
406
- }
407
- });
408
- return;
143
+ open() {
144
+ this.activeTabState.activeTab = 'Image';
145
+ this.__lock();
146
+ this.dialog.open().setModal(true).setPosition();
147
+ this.async
148
+ .promise((resolve, reject) => readValuesFromImage(this.j, this.state).then(resolve, reject))
149
+ .catch((e) => {
150
+ if (!isAbortError(e)) {
151
+ this.dialog.message.error(e.message);
409
152
  }
410
- };
411
- a.href = url;
412
- this.j.filebrowser.dataProvider
413
- .getPathByUrl(a.href.toString())
414
- .then(resp => {
415
- openImageEditor.call(this.j.filebrowser, a.href, resp.name, resp.path, resp.source, () => {
416
- const timestamp = new Date().getTime();
417
- attr(this.state.image, 'src', url +
418
- (url.indexOf('?') !== -1 ? '' : '?') +
419
- '&_tmp=' +
420
- timestamp.toString());
421
- this.updateValues();
422
- }, error => {
423
- this.j.alert(error.message);
424
- });
425
153
  })
426
- .catch(error => {
427
- this.j.alert(error.message, loadExternal);
428
- });
154
+ .finally(() => this.__unlock());
155
+ return false;
429
156
  }
430
- /**
431
- * Open popup with filebrowser/uploader buttons for image
432
- */
433
- openImagePopup(event) {
434
- const popup = new Popup(this.j), { changeImage } = refs(this.form);
435
- popup.setZIndex(this.dialog.getZIndex() + 1);
436
- popup
437
- .setContent(FileSelectorWidget(this.j, {
438
- upload: (data) => {
439
- if (data.files && data.files.length) {
440
- attr(this.state.image, 'src', data.baseurl + data.files[0]);
441
- }
442
- this.updateValues();
443
- popup.close();
444
- },
445
- filebrowser: (data) => {
446
- if (data &&
447
- isArray(data.files) &&
448
- data.files.length) {
449
- attr(this.state.image, 'src', data.files[0]);
450
- popup.close();
451
- this.updateValues();
452
- }
453
- }
454
- }, this.state.image, popup.close))
455
- .open(() => position(changeImage));
456
- event.stopPropagation();
157
+ __lock() {
158
+ this.dialog.lock();
159
+ this.form.setMod('lock', true);
160
+ Object.values(this.__buttons).forEach(b => (b.state.disabled = true));
161
+ }
162
+ __unlock() {
163
+ this.dialog.unlock();
164
+ this.form.setMod('lock', false);
165
+ Object.values(this.__buttons).forEach(b => (b.state.disabled = false));
457
166
  }
458
167
  /** @override **/
459
168
  afterInit(editor) {
@@ -472,7 +181,8 @@ export class imageProperties extends Plugin {
472
181
  false) {
473
182
  return;
474
183
  }
475
- self.state.image = image;
184
+ self.state.sourceImage = image;
185
+ self.state.image = image.cloneNode(true);
476
186
  if (!editor.o.readonly) {
477
187
  e.stopImmediatePropagation();
478
188
  e.preventDefault();
@@ -486,29 +196,50 @@ export class imageProperties extends Plugin {
486
196
  });
487
197
  })
488
198
  .on('openImageProperties.imageproperties', (image) => {
489
- this.state.image = image;
199
+ self.state.sourceImage = image;
200
+ this.state.image = image.cloneNode(true);
490
201
  this.open();
491
202
  });
492
203
  }
204
+ async onStateValuesImageSrcChange() {
205
+ const { image, values } = this.state;
206
+ if (!image.src) {
207
+ return;
208
+ }
209
+ try {
210
+ this.__lock();
211
+ await image.decode();
212
+ if (this.state.sizeIsLocked && isNumeric(values.imageWidth)) {
213
+ const w = parseFloat(values.imageWidth.toString());
214
+ values.imageHeight = Math.round(w / this.state.ratio);
215
+ }
216
+ this.j.e.fire('updateImageProperties.imageproperties', image);
217
+ }
218
+ catch (e) {
219
+ this.j.alert(e.message);
220
+ }
221
+ finally {
222
+ this.__unlock();
223
+ }
224
+ }
493
225
  /** @override */
494
226
  beforeDestruct(editor) {
495
- this.dialog && this.dialog.destruct();
227
+ Object.values(cached(this, '__buttons') ?? {}).forEach(b => b.destruct());
228
+ cached(this, 'dialog')?.destruct();
229
+ cached(this, 'form')?.destruct();
496
230
  editor.e.off(editor.editor, '.imageproperties').off('.imageproperties');
497
231
  }
498
232
  }
499
233
  __decorate([
500
- watch('state.marginIsLocked')
501
- ], imageProperties.prototype, "onChangeMarginIsLocked", null);
502
- __decorate([
503
- watch('state.sizeIsLocked')
504
- ], imageProperties.prototype, "onChangeSizeIsLocked", null);
234
+ cache
235
+ ], imageProperties.prototype, "form", null);
505
236
  __decorate([
506
- autobind
507
- ], imageProperties.prototype, "onApply", null);
237
+ cache
238
+ ], imageProperties.prototype, "dialog", null);
508
239
  __decorate([
509
- autobind
510
- ], imageProperties.prototype, "openImageEditor", null);
240
+ cache
241
+ ], imageProperties.prototype, "__buttons", null);
511
242
  __decorate([
512
- autobind
513
- ], imageProperties.prototype, "openImagePopup", null);
243
+ watch('state.image')
244
+ ], imageProperties.prototype, "onStateValuesImageSrcChange", null);
514
245
  pluginSystem.add('imageProperties', imageProperties);
@@ -3,8 +3,6 @@
3
3
  * Released under MIT see LICENSE.txt in the project root for license information.
4
4
  * Copyright (c) 2013-2024 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
- /**
7
- * @module plugins/image-properties
8
- */
9
- import type { IJodit } from "jodit/esm/types";
10
- export declare function form(editor: IJodit): HTMLElement;
6
+ import type { EditValues } from "../interface";
7
+ /** @private */
8
+ export declare function readAlign(image: HTMLImageElement, values: EditValues): void;
@@ -0,0 +1,24 @@
1
+ /*!
2
+ * Jodit Editor (https://xdsoft.net/jodit/)
3
+ * Released under MIT see LICENSE.txt in the project root for license information.
4
+ * Copyright (c) 2013-2024 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+ import { css } from "jodit/esm/core/helpers/utils/css.js";
7
+ /** @private */
8
+ export function readAlign(image, values) {
9
+ // Align
10
+ if (image.style.cssFloat &&
11
+ ['left', 'right'].indexOf(image.style.cssFloat.toLowerCase()) !== -1) {
12
+ values.align = css(image, 'float');
13
+ }
14
+ else {
15
+ if (css(image, 'display') === 'block' &&
16
+ image.style.marginLeft === 'auto' &&
17
+ image.style.marginRight === 'auto') {
18
+ values.align = 'center';
19
+ }
20
+ else {
21
+ values.align = '';
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,12 @@
1
+ /*!
2
+ * Jodit Editor (https://xdsoft.net/jodit/)
3
+ * Released under MIT see LICENSE.txt in the project root for license information.
4
+ * Copyright (c) 2013-2024 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+ import type { IJodit } from "jodit/esm/types";
7
+ import type { ImagePropertiesState } from "../interface";
8
+ /**
9
+ * Read values from image and set it to state
10
+ * @private
11
+ */
12
+ export declare function readValuesFromImage(j: IJodit, state: ImagePropertiesState): Promise<void>;