@componyx/ui 0.0.0-stage → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/AppSkin/AppSkin.css +1268 -0
  2. package/AppSkin/AppSkin.js +2748 -0
  3. package/AppSkin/Themes/Default.css +241 -0
  4. package/Base/Base.js +3407 -0
  5. package/Base/ComponentIcons.css +66 -0
  6. package/Base/Font/ComponentIcons.woff +0 -0
  7. package/Base/Font/UI.woff +0 -0
  8. package/Base/Library.js +8432 -0
  9. package/Base/NavigationManager.js +462 -0
  10. package/Base/ResponsiveTemplates.js +371 -0
  11. package/Base/Sanitizer.js +288 -0
  12. package/Base/Themes/Default.css +255 -0
  13. package/Base/UI.css +618 -0
  14. package/Base/UIResourcePath.js +6 -0
  15. package/Bindary/Bindary.js +2011 -0
  16. package/Bindary/Modules/Connection.js +216 -0
  17. package/Bindary/Modules/Core.js +854 -0
  18. package/Bindary/Modules/DataUpdater.js +351 -0
  19. package/Bindary/Modules/ExpressionEngine.js +347 -0
  20. package/Bindary/Modules/ProxyManager.js +482 -0
  21. package/Bindary/Modules/Renderer.js +605 -0
  22. package/Bindary/Modules/Router.js +291 -0
  23. package/Bindary/Modules/Scroller.js +89 -0
  24. package/Bindary/Modules/Template.js +1273 -0
  25. package/BookmarkSpy/BookmarkSpy.js +430 -0
  26. package/Box/Box.css +36 -0
  27. package/Box/Box.js +1685 -0
  28. package/Button/Button.css +470 -0
  29. package/Button/Button.js +1398 -0
  30. package/Button/Themes/Default.css +200 -0
  31. package/Carousel/Carousel.css +273 -0
  32. package/Carousel/Carousel.js +607 -0
  33. package/ColorButton/ColorButton.css +39 -0
  34. package/ColorButton/ColorButton.js +336 -0
  35. package/ColorPicker/ColorPicker.css +359 -0
  36. package/ColorPicker/ColorPicker.js +972 -0
  37. package/ComboBox/ComboBox.css +357 -0
  38. package/ComboBox/ComboBox.js +2342 -0
  39. package/ComboBox/Themes/Default.css +23 -0
  40. package/DataPager/DataPager.css +72 -0
  41. package/DataPager/DataPager.js +674 -0
  42. package/DatePicker/DatePicker.css +227 -0
  43. package/DatePicker/DatePicker.js +1531 -0
  44. package/DatePicker/Themes/Default.css +26 -0
  45. package/Dialog/Dialog.css +106 -0
  46. package/Dialog/Dialog.js +572 -0
  47. package/Dialog/Themes/Default.css +10 -0
  48. package/Editor/Data/Emoji.json +8285 -0
  49. package/Editor/Data/Special.json +1042 -0
  50. package/Editor/Editor.css +1562 -0
  51. package/Editor/Editor.js +4058 -0
  52. package/Editor/Font/Editor.woff +0 -0
  53. package/Editor/Modules/ComponentFactory.js +565 -0
  54. package/Editor/Modules/ContentManager.js +748 -0
  55. package/Editor/Modules/DialogManager.js +1234 -0
  56. package/Editor/Modules/EventManager.js +458 -0
  57. package/Editor/Modules/Format.js +950 -0
  58. package/Editor/Modules/History.js +191 -0
  59. package/Editor/Modules/LayoutState.js +379 -0
  60. package/Editor/Modules/ListManager.js +296 -0
  61. package/Editor/Modules/MenuManager.js +388 -0
  62. package/Editor/Modules/NodeManager.js +681 -0
  63. package/Editor/Modules/ParagraphButtons.js +122 -0
  64. package/Editor/Modules/Resizer.js +176 -0
  65. package/Editor/Modules/SelectionRange.js +564 -0
  66. package/Editor/Modules/SourceViewBuilder.js +212 -0
  67. package/Editor/Modules/SymbolPicker.js +208 -0
  68. package/Editor/Modules/Table.js +2327 -0
  69. package/Editor/Modules/TableDialog.js +757 -0
  70. package/Editor/Modules/Toolbar.js +375 -0
  71. package/Editor/Modules/TooltipMenu.js +125 -0
  72. package/Editor/Modules/Utility.js +131 -0
  73. package/Editor/Themes/Default.css +204 -0
  74. package/FileUpload/FileUpload.css +156 -0
  75. package/FileUpload/FileUpload.js +898 -0
  76. package/FileUpload/Themes/Default.css +17 -0
  77. package/Form/Form.css +1934 -0
  78. package/Form/Form.js +4943 -0
  79. package/Form/Modules/ActionManager.js +368 -0
  80. package/Form/Modules/BuildPanelManager.js +319 -0
  81. package/Form/Modules/ComponentFactory.js +468 -0
  82. package/Form/Modules/ComponentPanel.js +265 -0
  83. package/Form/Modules/ConfigPanelManager.js +685 -0
  84. package/Form/Modules/DataBinder.js +602 -0
  85. package/Form/Modules/DataObserver.js +717 -0
  86. package/Form/Modules/Draggable.js +756 -0
  87. package/Form/Modules/OptionsPanel.js +342 -0
  88. package/Form/Modules/Renderer.js +1889 -0
  89. package/Form/Modules/RuleEngine.js +585 -0
  90. package/Form/Modules/RulesPanel.js +1158 -0
  91. package/Form/Modules/Types.js +1709 -0
  92. package/Form/Modules/ValidationManager.js +194 -0
  93. package/Form/Themes/Default.css +216 -0
  94. package/FormField/FormField.css +468 -0
  95. package/FormField/FormField.js +576 -0
  96. package/FormField/Themes/Default.css +99 -0
  97. package/Grid/Grid.css +544 -0
  98. package/Grid/Grid.js +3761 -0
  99. package/Grid/Themes/Default.css +43 -0
  100. package/LICENSE +126 -0
  101. package/MaskedTextBox/MaskedTextBox.css +28 -0
  102. package/MaskedTextBox/MaskedTextBox.js +642 -0
  103. package/MaskedTextBox/Themes/Default.css +6 -0
  104. package/Menu/Menu.css +328 -0
  105. package/Menu/Menu.js +2386 -0
  106. package/Menu/Themes/Default.css +24 -0
  107. package/NumericBox/NumericBox.css +120 -0
  108. package/NumericBox/NumericBox.js +984 -0
  109. package/PanelBar/PanelBar.css +123 -0
  110. package/PanelBar/PanelBar.js +505 -0
  111. package/PanelBar/Themes/Default.css +51 -0
  112. package/PieChart/PieChart.css +95 -0
  113. package/PieChart/PieChart.js +820 -0
  114. package/PieChart/Themes/Default.css +9 -0
  115. package/README.md +178 -3
  116. package/Slider/Slider.css +348 -0
  117. package/Slider/Slider.js +1306 -0
  118. package/Slider/Themes/Default.css +44 -0
  119. package/TabStrip/TabStrip.css +52 -0
  120. package/TabStrip/TabStrip.js +362 -0
  121. package/TabStrip/Themes/Default.css +35 -0
  122. package/TimePicker/Themes/Default.css +30 -0
  123. package/TimePicker/TimePicker.css +253 -0
  124. package/TimePicker/TimePicker.js +1485 -0
  125. package/TooltipManager/Themes/Default.css +21 -0
  126. package/TooltipManager/TooltipManager.css +227 -0
  127. package/TooltipManager/TooltipManager.js +791 -0
  128. package/Validator/Validator.css +54 -0
  129. package/Validator/Validator.js +2508 -0
  130. package/componyx.html-data.json +619 -0
  131. package/index.js +27 -0
  132. package/package.json +43 -4
  133. package/types/AppSkin.d.ts +842 -0
  134. package/types/Base.d.ts +1034 -0
  135. package/types/Bindary.d.ts +1196 -0
  136. package/types/BookmarkSpy.d.ts +112 -0
  137. package/types/Box.d.ts +374 -0
  138. package/types/Button.d.ts +455 -0
  139. package/types/Carousel.d.ts +136 -0
  140. package/types/ColorButton.d.ts +120 -0
  141. package/types/ColorPicker.d.ts +225 -0
  142. package/types/ComboBox.d.ts +507 -0
  143. package/types/DataPager.d.ts +152 -0
  144. package/types/DatePicker.d.ts +354 -0
  145. package/types/Dialog.d.ts +160 -0
  146. package/types/Editor.d.ts +1289 -0
  147. package/types/FileUpload.d.ts +206 -0
  148. package/types/Form.d.ts +3078 -0
  149. package/types/FormField.d.ts +204 -0
  150. package/types/Grid.d.ts +911 -0
  151. package/types/Library.d.ts +1899 -0
  152. package/types/MaskedTextBox.d.ts +91 -0
  153. package/types/Menu.d.ts +577 -0
  154. package/types/NumericBox.d.ts +310 -0
  155. package/types/PanelBar.d.ts +223 -0
  156. package/types/PieChart.d.ts +224 -0
  157. package/types/ResponsiveTemplates.d.ts +108 -0
  158. package/types/Slider.d.ts +227 -0
  159. package/types/TabStrip.d.ts +185 -0
  160. package/types/TimePicker.d.ts +265 -0
  161. package/types/TooltipManager.d.ts +178 -0
  162. package/types/UI.d.ts +29 -0
  163. package/types/Validator.d.ts +653 -0
  164. package/types/index.d.ts +59 -0
  165. package/ui.css +11783 -0
  166. package/ui.js +79477 -0
  167. package/ui.min.css +138 -0
  168. package/ui.min.js +264 -0
  169. package/ui.min.js.map +1 -0
@@ -0,0 +1,1234 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ componyx.UI.editor_modules = componyx.UI.editor_modules || {};
3
+
4
+ /**
5
+ * Dialog Manager — manages editor dialogs.
6
+ * @class DialogManager
7
+ * @memberof componyx.UI.editor_modules
8
+ * @ignore
9
+ */
10
+ componyx.UI.editor_modules.DialogManager = class DialogManager
11
+ {
12
+ #activeDialog = null;
13
+ #sourceDialog;
14
+ #bookmarkDialog;
15
+ #linkDialog;
16
+ #imageDialog;
17
+ #mediaDialog;
18
+ #tableDialog;
19
+
20
+ constructor(editor)
21
+ {
22
+ this.editor = editor;
23
+ this.cf = editor.componentFactory;
24
+ this.utility = editor.utility;
25
+ this.validator = this.cf.createValidator();
26
+
27
+ const getId = this.#getId.bind(this);
28
+ const shared = {
29
+ validator: this.validator,
30
+ addValidatorRules: (dialog, rules) => this.#addDialogValidatorRules(dialog, rules)
31
+ };
32
+
33
+ this.#sourceDialog = new SourceDialog(editor, this.cf, getId);
34
+ this.#bookmarkDialog = new BookmarkDialog(editor, this.cf, getId, shared);
35
+ this.#linkDialog = new LinkDialog(editor, this.cf, getId, shared);
36
+ this.#imageDialog = new ImageDialog(editor, this.cf, this.utility, getId, shared);
37
+ this.#mediaDialog = new MediaDialog(editor, this.cf, getId, shared);
38
+ this.#tableDialog = new componyx.UI.editor_modules.TableDialog(editor);
39
+ this.createTableDialog();
40
+ }
41
+
42
+ get activeDialog() { return this.#activeDialog; }
43
+ set activeDialog(dialog) { this.#activeDialog = dialog; }
44
+
45
+ get sourceDialog() { return this.#sourceDialog.dialog; }
46
+ get bookmarkDialog() { return this.#bookmarkDialog.dialog; }
47
+ get linkDialog() { return this.#linkDialog.dialog; }
48
+ get imageDialog() { return this.#imageDialog.dialog; }
49
+ get mediaDialog() { return this.#mediaDialog.dialog; }
50
+ get tableDialog() { return this.#tableDialog; }
51
+
52
+ createSourceDialog() { this.#sourceDialog.create(); }
53
+ createBookmarkDialog() { this.#bookmarkDialog.create(); }
54
+ createLinkDialog() { this.#linkDialog.create(); }
55
+ createImageDialog() { this.#imageDialog.create(); }
56
+ createMediaDialog() { this.#mediaDialog.create(); }
57
+ createTableDialog() { this.#tableDialog.create(); }
58
+
59
+ #addDialogValidatorRules(dialog, rules)
60
+ {
61
+ this.#activeDialog = dialog;
62
+ this.validator.clear();
63
+
64
+ $lib.each(rules, (rule) =>
65
+ {
66
+ const name = rule.name;
67
+ const dataType = rule.dataType;
68
+
69
+ this.validator.addRule(name, { live: true }, 0);
70
+
71
+ if (dataType)
72
+ this.validator.addRule(name, { dataType: dataType, live: true }, 1);
73
+ });
74
+
75
+ this.validator.validate();
76
+ }
77
+
78
+ #getId(id)
79
+ {
80
+ return this.utility.getId(id);
81
+ }
82
+ };
83
+
84
+ class SourceDialog
85
+ {
86
+ #dialog = null;
87
+ #editor;
88
+ #cf;
89
+ #getId;
90
+
91
+ constructor(editor, cf, getId)
92
+ {
93
+ this.#editor = editor;
94
+ this.#cf = cf;
95
+ this.#getId = getId;
96
+ }
97
+
98
+ get dialog() { return this.#dialog; }
99
+
100
+ create()
101
+ {
102
+ const editor = this.#editor,
103
+ cf = this.#cf,
104
+ getId = this.#getId,
105
+ header = $lib.element('', '', '', editor.labels.sourceViewDialogHeader),
106
+ content = $lib.element('', '', '', '', {
107
+ class: editor.getCssClass(editor.classOption.SOURCE_VIEW)
108
+ });
109
+
110
+ this.#dialog = cf.createDialog('SourceDialog', editor.sourceDialogId, {
111
+ cssClass: editor.getCssClass(editor.classOption.SOURCE_DIALOG),
112
+ header: header,
113
+ content: content,
114
+ onShowComplete: () =>
115
+ {
116
+ editor.createSourceViewBuilder(editor.getEditorElement(), content);
117
+ },
118
+ onConfirm: () =>
119
+ {
120
+ editor.setSourceViewBuilderHTML(editor.getEditorElement());
121
+ },
122
+ onHide: () =>
123
+ {
124
+ editor.getSourceViewStore().delete(editor.getEditorElement());
125
+ $UI.store[getId('source')].deselect();
126
+ }
127
+ });
128
+ }
129
+
130
+ show() { this.#dialog?.show(); }
131
+ }
132
+
133
+ class BookmarkDialog
134
+ {
135
+ #dialog = null;
136
+ #editor;
137
+ #cf;
138
+ #getId;
139
+ #shared;
140
+
141
+ constructor(editor, cf, getId, shared)
142
+ {
143
+ this.#editor = editor;
144
+ this.#cf = cf;
145
+ this.#getId = getId;
146
+ this.#shared = shared;
147
+ }
148
+
149
+ get dialog() { return this.#dialog; }
150
+
151
+ create()
152
+ {
153
+ const editor = this.#editor,
154
+ cf = this.#cf,
155
+ getId = this.#getId,
156
+ header = $lib.element('', '', '', editor.labels.bookmarkDialogHeader),
157
+ input = $lib.element('', '', 'input', '', {
158
+ type: "text",
159
+ name: getId('bookmark'),
160
+ placeholder: editor.labels.bookmarkInputPlaceholder
161
+ }),
162
+ content = $lib.element('', '', '', input);
163
+
164
+ this.#dialog = cf.createDialog('BookmarkDialog', editor.bookmarkDialogId, {
165
+ cssClass: editor.getCssClass(editor.classOption.BOOKMARK_DIALOG),
166
+ header: header,
167
+ content: content,
168
+ onShowComplete: () =>
169
+ {
170
+ input.value = editor.layoutState.activeLayout.bookmark
171
+ ? editor.layoutState.activeLayout.bookmark.id
172
+ : '';
173
+ this.#shared.addValidatorRules(this.#dialog, [{ name: input.name }]);
174
+ },
175
+ onConfirm: () =>
176
+ {
177
+ if (editor.layoutState.activeLayout.bookmark)
178
+ {
179
+ editor.layoutState.activeLayout.bookmark.id = input.value;
180
+ }
181
+ else
182
+ {
183
+ editor.contentManager.insertHTML({
184
+ tag: 'a',
185
+ selectable: true,
186
+ allowContent: false,
187
+ attributes: {
188
+ id: input.value,
189
+ [editor.utility.attrPrefix + 'bookmark']: ''
190
+ }
191
+ });
192
+ }
193
+ },
194
+ onHide: () =>
195
+ {
196
+ input.value = '';
197
+ }
198
+ });
199
+ }
200
+
201
+ show() { this.#dialog?.show(); }
202
+ }
203
+
204
+ class LinkDialog
205
+ {
206
+ #dialog = null;
207
+ #editor;
208
+ #cf;
209
+ #getId;
210
+ #shared;
211
+
212
+ constructor(editor, cf, getId, shared)
213
+ {
214
+ this.#editor = editor;
215
+ this.#cf = cf;
216
+ this.#getId = getId;
217
+ this.#shared = shared;
218
+ }
219
+
220
+ get dialog() { return this.#dialog; }
221
+
222
+ create()
223
+ {
224
+ const editor = this.#editor,
225
+ cf = this.#cf,
226
+ getId = this.#getId,
227
+ header = $lib.element('', '', '', editor.labels.linkDialogHeader),
228
+ footer = $lib.element(),
229
+ content = $lib.element(),
230
+ temp = $lib.element(),
231
+ hrefContainer = $lib.element(),
232
+ targetContainer = $lib.element(),
233
+ downloadableCheckbox = $lib.element('', '', 'input', '', { id: getId('linkDownloadable'), type: 'checkbox' }),
234
+ text = $lib.element('', '', 'input', '', { id: getId('linkText'), name: getId('linkText') }),
235
+ title = $lib.element('', '', 'input', '', { id: getId('linkTitle') }),
236
+ href = cf.createComboBox('linkHref', editor.linkDialogHrefComboBoxId, hrefContainer,
237
+ {
238
+ allowInput: true,
239
+ allowCustomValue: true,
240
+ noResultTemplate: editor.labels.comboBoxNoResult,
241
+ onItemSelect: () => this.#shared.validator.validate(),
242
+ onClear: () => this.#shared.validator.validate()
243
+ }
244
+ ),
245
+ target = cf.createComboBox('linkTarget', editor.linkDialogTargetComboBoxId, targetContainer,
246
+ {
247
+ allowInput: false
248
+ }
249
+ ),
250
+ removeButton = cf.createButton('removeLink', editor.linkDialogRemoveButtonId, footer,
251
+ {
252
+ transparent: false,
253
+ transparentBorder: false,
254
+ text: editor.labels.removeLink,
255
+ command: () => editor.removeLink(),
256
+ cssClass: this.#editor.getCssClass(this.#editor.classOption.REMOVE)
257
+ }
258
+ );
259
+
260
+ const fields = {};
261
+ fields.href = cf.createFormField('fldHref', editor.linkDialogHrefFormFieldId, temp, { label: editor.labels.linkHref, field: hrefContainer.firstElementChild }).element;
262
+ fields.text = cf.createFormField('fldText', editor.linkDialogTextFormFieldId, temp, { label: editor.labels.linkText, field: text }).element;
263
+ fields.title = cf.createFormField('fldTitle', editor.linkDialogTitleFormFieldId, temp, { label: editor.labels.linkTitle, field: title }).element;
264
+ fields.target = cf.createFormField('fldTarget', editor.linkDialogTargetFormFieldId, temp, { label: editor.labels.linkTarget, field: targetContainer.firstElementChild }).element;
265
+
266
+ removeButton.render();
267
+
268
+ cf.createFormField('fldDownloadable', editor.linkDialogDownloadableFormFieldId, footer, { label: editor.labels.linkDownloadable, field: downloadableCheckbox, labelDisplay: 5, switch: true });
269
+
270
+ let defaultTemplate = '{href}{text}{title}{target}',
271
+ templateId = 'LinkDialogContent',
272
+ defaultTemplateId = 'Default' + templateId,
273
+ hasCustomTemplate = editor.hasTemplate(templateId);
274
+
275
+ if (!editor.hasTemplate(templateId))
276
+ {
277
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
278
+ templateId = defaultTemplateId;
279
+ }
280
+
281
+ editor.applyTemplate(content, templateId, fields);
282
+
283
+ if (!content.querySelector(`#${getId('fldHref')}`))
284
+ {
285
+ fields.href.style.display = 'none';
286
+ content.appendChild(fields.href);
287
+ }
288
+
289
+ this.#dialog = cf.createDialog('LinkDialog', editor.linkDialogId, {
290
+ cssClass: editor.getCssClass(editor.classOption.LINK_DIALOG),
291
+ header: header,
292
+ footer: footer,
293
+ content: content,
294
+ onShowComplete: () =>
295
+ {
296
+ const input = href.element.querySelector('input[type=text]');
297
+ const range = editor.selectionRange.getRange();
298
+ const link = editor.layoutState.activeLayout.link;
299
+
300
+ this.#loadComboBox(this.#fillHrefComboBox(href));
301
+ this.#loadComboBox(this.#fillTargetComboBox(target));
302
+
303
+ text.disabled = !range.collapsed || editor.layoutState.activeLayout.link;
304
+ title.value = '';
305
+ input.name = getId('linkHref');
306
+ downloadableCheckbox.checked = false;
307
+ removeButton.hide();
308
+
309
+ if (link)
310
+ {
311
+ text.value = link.textContent;
312
+ title.value = link.getAttribute('title');
313
+ href.setValue(link.getAttribute('href'));
314
+ target.setValue(link.target);
315
+
316
+ if (link.hasAttribute('download'))
317
+ downloadableCheckbox.checked = true;
318
+
319
+ removeButton.show();
320
+ }
321
+ else if (!range.collapsed)
322
+ {
323
+ text.value = range.toString();
324
+ }
325
+
326
+ this.#setConfirmState(hasCustomTemplate, href.getValue(), [{ name: input.name, dataType: 4 }]);
327
+
328
+ const activeItem = link ? {
329
+ href: link.getAttribute('href'),
330
+ text: link.textContent,
331
+ title: link.getAttribute('title'),
332
+ target: link.target,
333
+ downloadable: link.hasAttribute('download')
334
+ } : null;
335
+
336
+ editor.events.onLinkDialogShow.fire(editor,
337
+ {
338
+ container: content,
339
+ activeItem,
340
+ confirmSelection: (url, linkText, linkTitle) => this.#confirmSelection(url, linkText, linkTitle, href, text, title)
341
+ });
342
+ },
343
+ onConfirm: () =>
344
+ {
345
+ this.#confirmDialog(href, text, title, target, downloadableCheckbox);
346
+ },
347
+ onHide: () =>
348
+ {
349
+ text.value = '';
350
+ }
351
+ });
352
+ }
353
+
354
+ show() { this.#dialog?.show(); }
355
+
356
+ #setConfirmState(hasCustomTemplate, fieldValue, validatorRules)
357
+ {
358
+ this.#addValidatorRules(validatorRules);
359
+
360
+ if (hasCustomTemplate)
361
+ {
362
+ const confirmButton = $UI.store[this.#dialog.id + '_Confirm'];
363
+ if (fieldValue)
364
+ confirmButton.enable();
365
+ else
366
+ confirmButton.disable();
367
+ }
368
+ }
369
+
370
+ #addValidatorRules(rules)
371
+ {
372
+ const getId = this.#getId;
373
+ const linkHref = getId('linkHref');
374
+ const validator = this.#shared.validator;
375
+
376
+ validator.clear();
377
+
378
+ $lib.each(rules, (rule) =>
379
+ {
380
+ const name = rule.name;
381
+ const dataType = rule.dataType;
382
+
383
+ validator.addRule(name, { live: true }, 0);
384
+
385
+ if (name == linkHref)
386
+ {
387
+ validator.addRule(name, {
388
+ onValidation: (v, args) =>
389
+ {
390
+ const valid = validator.validateType({ dataType: dataType }, args.fieldValue);
391
+ const combo = $UI.store[linkHref];
392
+ if (valid) return true;
393
+ return $lib.indexOf(combo.itemList, item => item.text === args.fieldValue) > -1;
394
+ },
395
+ live: true,
396
+ alwaysCheck: true
397
+ }, 6);
398
+ }
399
+ else if (dataType)
400
+ {
401
+ validator.addRule(name, { dataType: dataType, live: true }, 1);
402
+ }
403
+ });
404
+
405
+ validator.validate();
406
+ }
407
+
408
+ #confirmSelection(url, linkText, linkTitle, href, text, title)
409
+ {
410
+ href.setValue(url);
411
+ if (linkText) text.value = linkText;
412
+ if (linkTitle) title.value = linkTitle;
413
+ $UI.store[this.#dialog.id + '_Confirm'].enable();
414
+ }
415
+
416
+ #confirmDialog(href, text, title, target, downloadableCheckbox)
417
+ {
418
+ const editor = this.#editor;
419
+ const hrefValue = href.getValue();
420
+ const targetValue = target.getValue();
421
+
422
+ if (editor.layoutState.activeLayout.link)
423
+ {
424
+ const linkEl = editor.layoutState.activeLayout.link;
425
+ linkEl.href = hrefValue;
426
+ linkEl.target = targetValue;
427
+ linkEl.title = title.value;
428
+
429
+ if (downloadableCheckbox.checked)
430
+ linkEl.setAttribute('download', '');
431
+ else
432
+ linkEl.removeAttribute('download');
433
+
434
+ if (!text.disabled)
435
+ linkEl.textContent = text.value || hrefValue;
436
+
437
+ editor.history.addItem();
438
+ }
439
+ else
440
+ {
441
+ const range = editor.selectionRange.getRange();
442
+ const settings = {
443
+ tag: 'a',
444
+ attributes: {
445
+ href: hrefValue,
446
+ title: title.value,
447
+ target: targetValue,
448
+ rel: "noreferer",
449
+ download: downloadableCheckbox.checked ? '' : undefined
450
+ },
451
+ selectable: true,
452
+ allowContent: true,
453
+ textContent: range.collapsed ? (text.value || hrefValue) : null
454
+ };
455
+
456
+ if (range.collapsed)
457
+ editor.contentManager.insertHTML(settings);
458
+ else
459
+ editor.format.pasteFormat([editor.nodeManager.createNode(settings)], false);
460
+ }
461
+ }
462
+
463
+ #fillHrefComboBox(combo)
464
+ {
465
+ const links = this.#editor.getEditorElement().getElementsByTagName('a');
466
+ this.#createComboBoxItemList(combo);
467
+
468
+ $lib.each(links, link =>
469
+ {
470
+ if (link.hasAttribute('href') && $lib.startsWith(link.getAttribute('href'), '#'))
471
+ return;
472
+
473
+ const href = link.getAttribute('href');
474
+ let item;
475
+
476
+ if (href)
477
+ item = { id: href, text: href };
478
+ else if (link.id)
479
+ item = { id: '#' + link.id, text: this.#editor.labels.bookmarkPrefix + ': ' + link.id };
480
+
481
+ if (item)
482
+ combo.itemList.push(item);
483
+ });
484
+
485
+ return combo;
486
+ }
487
+
488
+ #fillTargetComboBox(combo)
489
+ {
490
+ const targets = this.#editor.getDoc().getElementsByTagName('iframe');
491
+ const labels = this.#editor.labels;
492
+
493
+ this.#createComboBoxItemList(combo);
494
+ combo.itemList.push({ id: '_self', text: labels.linkTargetCurrent, selected: true });
495
+ combo.itemList.push({ id: '_blank', text: labels.linkTargetNew });
496
+
497
+ $lib.each(targets, target =>
498
+ {
499
+ if (target.name)
500
+ combo.itemList.push({ text: target.name });
501
+ });
502
+
503
+ return combo;
504
+ }
505
+
506
+ #createComboBoxItemList(combo)
507
+ {
508
+ if (combo.itemList && combo.itemList.length)
509
+ combo.itemList.length = 0;
510
+ else
511
+ combo.itemList = [];
512
+
513
+ if (combo.renderState == 2)
514
+ combo.clear();
515
+ }
516
+
517
+ #loadComboBox(combo)
518
+ {
519
+ if (combo.renderState == 2)
520
+ combo.load(false, null, false);
521
+ }
522
+ }
523
+
524
+ class ImageDialog
525
+ {
526
+ #dialog = null;
527
+ #editor;
528
+ #cf;
529
+ #utility;
530
+ #getId;
531
+ #shared;
532
+
533
+ constructor(editor, cf, utility, getId, shared)
534
+ {
535
+ this.#editor = editor;
536
+ this.#cf = cf;
537
+ this.#utility = utility;
538
+ this.#getId = getId;
539
+ this.#shared = shared;
540
+ }
541
+
542
+ get dialog() { return this.#dialog; }
543
+
544
+ create()
545
+ {
546
+ const editor = this.#editor,
547
+ cf = this.#cf,
548
+ getId = this.#getId,
549
+ cssClassHalf = editor.getCssClass(editor.classOption.FIELD_HALF),
550
+ imgId = getId('imgSrc'),
551
+ header = $lib.element('', '', '', editor.labels.imageDialogHeader),
552
+ footer = $lib.element(),
553
+ content = $lib.element(),
554
+ temp = $lib.element(),
555
+ srcContainer = $lib.element(),
556
+ heightContainer = $lib.element(),
557
+ events = this.#createEvents(() =>
558
+ {
559
+ $UI.store[getId('ImageDialog_Confirm')].enable();
560
+ }),
561
+ radio = this.#createRadioButtons(),
562
+ alt = $lib.element('', '', 'input', '', { id: getId('imgAlt') }),
563
+ caption = $lib.element('', '', 'input', '', { id: getId('imgCaption') }),
564
+ width = $lib.element('', '', 'input', '', { id: getId('imgWidth') }),
565
+ height = $lib.element(heightContainer, '', 'input', '', { id: getId('imgHeight') }),
566
+ src = $lib.element(srcContainer, '', 'input', '', { id: imgId, name: imgId }, { readOnly: !editor.allowImageUrlInput }),
567
+ inlineCheckbox = $lib.element('', '', 'input', '', {
568
+ id: getId('imgInline'),
569
+ type: 'checkbox'
570
+ }, {
571
+ onclick: function ()
572
+ {
573
+ caption.disabled = this.checked;
574
+ radio.hideAll();
575
+ if (this.checked)
576
+ radio.toggle([0, 1, 2, 3, 4], true);
577
+ else
578
+ radio.toggle([5, 6, 7], true);
579
+ }
580
+ });
581
+
582
+ if (editor.allowImageFileSelect)
583
+ this.#createFileButton(srcContainer, events);
584
+
585
+ this.#createUnlockButton(heightContainer);
586
+
587
+ [width, height].forEach(el => $lib.on(el, 'input', events.constrainProportions));
588
+ $lib.on(src, 'change', events.urlChange);
589
+
590
+ if (!editor.allowImageUrlInput)
591
+ {
592
+ $lib.on(src, 'click', () =>
593
+ {
594
+ srcContainer.querySelector('input[type="file"]').click();
595
+ });
596
+ }
597
+
598
+ const fields = {};
599
+ fields.source = cf.createFormField('fldImgSrc', editor.imageDialogSourceFormFieldId, temp, { label: editor.labels.imageSource, field: srcContainer }).element;
600
+ fields.alt = cf.createFormField('fldImgAlt', editor.imageDialogAltFormFieldId, temp, { label: editor.labels.imageAlt, field: alt }).element;
601
+ fields.width = cf.createFormField('fldImgWidth', editor.imageDialogWidthFormFieldId, temp, { label: editor.labels.imageWidth, field: width, inline: true, cssClass: cssClassHalf }).element;
602
+ fields.height = cf.createFormField('fldImgHeight', editor.imageDialogHeightFormFieldId, temp, { label: editor.labels.imageHeight, field: heightContainer, inline: true, cssClass: cssClassHalf }).element;
603
+ fields.caption = cf.createFormField('fldImgCaption', editor.imageDialogCaptionFormFieldId, temp, { label: editor.labels.caption, field: caption }).element;
604
+ fields.alignment = cf.createFormField('fldImgAlignment', editor.imageDialogAlignmentFormFieldId, temp, { label: editor.labels.imageAlignment, field: radio.content }).element;
605
+ fields.inline = cf.createFormField('fldImgInline', editor.imageDialogInlineFormFieldId, footer, { label: editor.labels.imageInline, field: inlineCheckbox, switch: true, labelDisplay: 5 }).element;
606
+
607
+ let defaultTemplate = '{source}{alt}{width}{height}{caption}{alignment}',
608
+ templateId = 'ImageDialogContent',
609
+ hasCustomTemplate = editor.hasTemplate(templateId),
610
+ defaultTemplateId = 'Default' + templateId;
611
+
612
+ if (!editor.hasTemplate(templateId))
613
+ {
614
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
615
+ templateId = defaultTemplateId;
616
+ }
617
+
618
+ editor.applyTemplate(content, templateId, fields);
619
+
620
+ if (!content.querySelector(`#${getId('fldImgSrc')}`))
621
+ {
622
+ fields.source.style.display = 'none';
623
+ content.appendChild(fields.source);
624
+ }
625
+
626
+ this.#dialog = cf.createDialog('ImageDialog', editor.imageDialogId, {
627
+ cssClass: editor.classOption.IMAGE_DIALOG,
628
+ header: header,
629
+ footer: footer,
630
+ content: content,
631
+ onShowComplete: () =>
632
+ {
633
+ const img = editor.layoutState.activeLayout.image,
634
+ range = editor.selectionRange.getRange(),
635
+ rootNode = editor.nodeManager.getRootNode(range.startContainer);
636
+
637
+ radio.hideAll();
638
+
639
+ if (img)
640
+ {
641
+ const figure = img.parentElement;
642
+ const captionEl = figure.querySelector('figcaption');
643
+
644
+ src.value = img.getAttribute('src'); alt.value = img.alt;
645
+ width.value = img.width;
646
+ height.value = img.height;
647
+ events.widthRatio = img.__widthRatio || (img.width / img.height);
648
+ caption.value = captionEl ? captionEl.textContent : '';
649
+
650
+ if (figure.nodeName == 'SPAN')
651
+ {
652
+ caption.disabled = inlineCheckbox.checked = true;
653
+ radio.toggle([0, 1, 2, 3, 4], true);
654
+ }
655
+ else
656
+ {
657
+ caption.disabled = inlineCheckbox.checked = false;
658
+ radio.toggle([5, 6, 7], true);
659
+ }
660
+
661
+ radio.select(figure);
662
+ }
663
+ else
664
+ {
665
+ src.value = alt.value = width.value = height.value = caption.value = '';
666
+
667
+ if (rootNode && rootNode.textContent.length)
668
+ {
669
+ inlineCheckbox.checked = caption.disabled = true;
670
+ radio.toggle([0, 1, 2, 3, 4], true);
671
+ }
672
+ else
673
+ {
674
+ inlineCheckbox.checked = caption.disabled = false;
675
+ radio.toggle([5, 6, 7], true);
676
+ }
677
+ }
678
+
679
+ this.#setConfirmState(hasCustomTemplate, src.value, [{ name: src.name, dataType: 5 }]);
680
+
681
+ const activeItem = img ? {
682
+ src: img.getAttribute('src'),
683
+ alt: img.alt,
684
+ width: img.width,
685
+ height: img.height,
686
+ caption: caption.value
687
+ } : null;
688
+
689
+ editor.events.onImageDialogShow.fire(editor,
690
+ {
691
+ container: content,
692
+ activeItem,
693
+ confirmSelection: (url, altText, captionText) => this.#confirmSelection(url, altText, captionText, src, alt, caption, events)
694
+ });
695
+ },
696
+ onConfirm: () =>
697
+ {
698
+ this.#confirmDialog(src, alt, width, height, caption, inlineCheckbox, radio, events);
699
+ }
700
+ });
701
+ }
702
+
703
+ show() { this.#dialog?.show(); }
704
+
705
+ #setConfirmState(hasCustomTemplate, fieldValue, validatorRules)
706
+ {
707
+ if (this.#editor.allowImageUrlInput)
708
+ {
709
+ this.#shared.addValidatorRules(this.#dialog, validatorRules);
710
+ }
711
+ else
712
+ {
713
+ const confirmButton = $UI.store[this.#dialog.id + '_Confirm'];
714
+ if (fieldValue)
715
+ confirmButton.enable();
716
+ else
717
+ confirmButton.disable();
718
+ }
719
+ }
720
+
721
+ #confirmSelection(url, altText, captionText, src, alt, caption, events)
722
+ {
723
+ src.value = url;
724
+
725
+ if (altText && alt) alt.value = altText;
726
+ if (captionText && caption) caption.value = captionText;
727
+
728
+ const img = new Image();
729
+ img.onload = events.onLoad.bind(img);
730
+ img.src = url;
731
+
732
+ $UI.store[this.#dialog.id + '_Confirm'].enable();
733
+ }
734
+
735
+ #confirmDialog(src, alt, width, height, caption, inlineCheckbox, radio, events)
736
+ {
737
+ const editor = this.#editor;
738
+ let img,
739
+ captionHTML = '',
740
+ figure = editor.layoutState.activeLayout.image ? editor.layoutState.activeLayout.image.parentNode : null;
741
+
742
+ if (editor.layoutState.activeLayout.image &&
743
+ ((figure.nodeName == 'FIGURE' && !inlineCheckbox.checked) ||
744
+ (figure.nodeName == 'SPAN' && inlineCheckbox.checked)))
745
+ {
746
+ img = editor.layoutState.activeLayout.image;
747
+
748
+ this.#configureImage(img, { src, alt, width, height, events });
749
+
750
+ let captionEl = img.parentElement.querySelector('figcaption');
751
+
752
+ if (!captionEl && caption.value)
753
+ {
754
+ captionEl = $lib.element('', '', 'figcaption', caption.value);
755
+ img.parentElement.appendChild(captionEl);
756
+ }
757
+ else if (captionEl)
758
+ {
759
+ if (caption.value)
760
+ captionEl.textContent = caption.value;
761
+ else
762
+ captionEl.remove();
763
+ }
764
+ }
765
+ else
766
+ {
767
+ if (figure)
768
+ figure.remove();
769
+
770
+ img = this.#configureImage($lib.element('', '', 'img'), { src, alt, width, height, events });
771
+
772
+ if (!caption.disabled && caption.value)
773
+ captionHTML = $lib.element('', '', 'figcaption', caption.value).outerHTML;
774
+
775
+ figure = editor.history.noHistory(() =>
776
+ editor.contentManager.insertHTML({
777
+ tag: inlineCheckbox.checked ? 'span' : 'figure',
778
+ styles: !inlineCheckbox.checked ? { width: 'fit-content' } : { display: 'inline-block', width: 'fit-content' },
779
+ isPhrasingContent: inlineCheckbox.checked,
780
+ selectable: true,
781
+ select: true,
782
+ allowContent: false,
783
+ innerHTML: img.outerHTML + captionHTML,
784
+ })
785
+ );
786
+
787
+ figure.firstElementChild.__widthRatio = events.widthRatio;
788
+ }
789
+
790
+ if (inlineCheckbox.checked)
791
+ {
792
+ const alignClass = radio.selected.cssClass.replace('button ', '').trim();
793
+ this.#applyImageAlignStyle(figure, alignClass);
794
+ }
795
+ else
796
+ {
797
+ const alignClass = radio.selected.cssClass.replace('button ', '').trim();
798
+ this.#applyImageAlignStyle(figure, alignClass);
799
+ }
800
+
801
+ editor.history.addItem();
802
+ }
803
+
804
+ #applyImageAlignStyle(figure, alignClass)
805
+ {
806
+ figure.style.removeProperty('float');
807
+ figure.style.removeProperty('vertical-align');
808
+ figure.style.removeProperty('margin-left');
809
+ figure.style.removeProperty('margin-right');
810
+
811
+ switch (alignClass)
812
+ {
813
+ case 'img-inline-align-default-top': figure.style.verticalAlign = 'top'; break;
814
+ case 'img-inline-align-default-center': figure.style.verticalAlign = 'middle'; break;
815
+ case 'img-inline-align-default-bottom': figure.style.verticalAlign = 'bottom'; break;
816
+ case 'img-inline-align-left': figure.style.float = 'left'; break;
817
+ case 'img-inline-align-right': figure.style.float = 'right'; break;
818
+ case 'img-align-center': figure.style.marginLeft = 'auto'; figure.style.marginRight = 'auto'; break;
819
+ case 'img-align-right': figure.style.marginLeft = 'auto'; break;
820
+ }
821
+ }
822
+
823
+ #configureImage(img, settings)
824
+ {
825
+ img.contentEditable = false;
826
+ img.src = settings.src.value;
827
+ img.alt = settings.alt.value;
828
+ img.width = settings.width.value;
829
+ img.height = settings.height.value;
830
+ img.__widthRatio = settings.events.widthRatio;
831
+ img.style.removeProperty('width');
832
+ img.style.removeProperty('height');
833
+ return img;
834
+ }
835
+
836
+ #createFileButton(container, events)
837
+ {
838
+ const getId = this.#getId;
839
+ const guid = getId('fileButtonInput');
840
+ const label = $lib.element(container, '', 'label', '');
841
+ const button = this.#cf.createButton('imgFileButton', this.#editor.imageDialogFileButtonId, label,
842
+ {
843
+
844
+ cssClass: this.#editor.getCssClass(this.#editor.classOption.FILE_SELECT),
845
+ cssClassIcon: 'ico-upload',
846
+ tooltip: this.#editor.labels.imageFileButtonTooltip
847
+ }
848
+ );
849
+
850
+ label.htmlFor = guid;
851
+ button.render();
852
+ $lib.element(label, '', 'input', '', '', {
853
+ id: guid,
854
+ type: 'file',
855
+ accept: 'image/*',
856
+ hidden: true,
857
+ onchange: events.fileChange
858
+ });
859
+
860
+ return button;
861
+ }
862
+
863
+ #createUnlockButton(container)
864
+ {
865
+ const button = this.#cf.createButton('imgUnlockButton', this.#editor.imageDialogConstrainButtonId, container,
866
+ {
867
+ cssClass: this.#editor.getCssClass(this.#editor.classOption.LOCK),
868
+ cssClassIcon: this.#utility.iconPrefix + 'lock',
869
+ type: 1,
870
+ tooltip: this.#editor.labels.imageConstrainButtonTooltip
871
+ }
872
+ );
873
+
874
+ button.render();
875
+ return button;
876
+ }
877
+
878
+ #createEvents(fileSelected)
879
+ {
880
+ const getId = this.#getId;
881
+ const editor = this.#editor;
882
+ this.#shared.validator;
883
+
884
+ const events = {
885
+ widthRatio: null,
886
+
887
+ urlChange: function (e)
888
+ {
889
+ const img = new Image();
890
+ img.onload = events.onLoad.bind(img);
891
+ img.src = this.value;
892
+ },
893
+
894
+ fileChange: function (e)
895
+ {
896
+ const file = this.files[0],
897
+ url = window.URL.createObjectURL(file),
898
+ img = new Image();
899
+
900
+ $lib('#' + getId('imgSrc')).value = url;
901
+ img.onload = events.onLoad.bind(img, file);
902
+ img.src = url;
903
+ editor.events.onImageSelect.fire(editor, { imageElement: this, file: file || null });
904
+
905
+ this.value = '';
906
+ fileSelected();
907
+ },
908
+
909
+ onLoad: function (file)
910
+ {
911
+ $lib('#' + getId('imgWidth')).value = this.width;
912
+ $lib('#' + getId('imgHeight')).value = this.height;
913
+ events.widthRatio = this.width / this.height;
914
+ editor.events.onImageLoad.fire(editor, { imageElement: this, file: file || null });
915
+ },
916
+
917
+ constrainProportions: function (e)
918
+ {
919
+ const el = this,
920
+ isWidthEl = el.id.indexOf('Width') > -1,
921
+ linkedEl = isWidthEl ? $lib('#' + getId('imgHeight')) : $lib('#' + getId('imgWidth')),
922
+ value = parseFloat(el.value),
923
+ ratio = events.widthRatio;
924
+
925
+ if (isNaN(value) || $lib('#' + getId('imgSrc')).value == '' || $UI.store[getId('imgUnlockButton')].selected)
926
+ return;
927
+
928
+ linkedEl.value = Math.round(isWidthEl ? value * (1 / ratio) : value * ratio);
929
+ }
930
+ };
931
+
932
+ return events;
933
+ }
934
+
935
+ #createRadioButtons()
936
+ {
937
+ const cf = this.#cf,
938
+ editor = this.#editor,
939
+ utility = this.#utility,
940
+ radio = {
941
+ buttons: [],
942
+ content: $lib.element(),
943
+ selected: null,
944
+
945
+ create: function (settings)
946
+ {
947
+ settings.onSelect = function (button) { this.selected = button; }.bind(this);
948
+ this.buttons.push(cf.createButton(settings.id,
949
+ editor.imageDialogAlignButtonId,
950
+ this.content,
951
+ settings
952
+ ));
953
+ },
954
+
955
+ hideAll: function ()
956
+ {
957
+ $lib.each(this.buttons, f => { f.deselect(); f.hide(); });
958
+ },
959
+
960
+ toggle: function (buttons, show)
961
+ {
962
+ $lib.each(buttons, (f, i) =>
963
+ {
964
+ const button = this.buttons[f];
965
+ if (!i) button.select();
966
+ show ? button.show() : button.hide();
967
+ });
968
+ },
969
+
970
+ select: function (figure)
971
+ {
972
+ const style = figure.style;
973
+ let alignClass = '';
974
+
975
+ if (style.float == 'left') alignClass = 'img-inline-align-left';
976
+ else if (style.float == 'right') alignClass = 'img-inline-align-right';
977
+ else if (style.verticalAlign == 'top') alignClass = 'img-inline-align-default-top';
978
+ else if (style.verticalAlign == 'middle') alignClass = 'img-inline-align-default-center';
979
+ else if (style.verticalAlign == 'bottom') alignClass = 'img-inline-align-default-bottom';
980
+ else if (style.marginLeft == 'auto' && style.marginRight == 'auto') alignClass = 'img-align-center';
981
+ else if (style.marginLeft == 'auto') alignClass = 'img-align-right';
982
+
983
+ $lib.each(this.buttons, button =>
984
+ {
985
+ if (button.cssClass.includes(alignClass))
986
+ {
987
+ button.select();
988
+ return false;
989
+ }
990
+ });
991
+ }
992
+ };
993
+
994
+ const labels = editor.labels,
995
+ classOption = editor.classOption;
996
+
997
+ radio.create({ id: 'imgInlineAlignDefaultBottom', text: labels.alignBottom, cssClass: editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_BOTTOM), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_BOTTOM), radioGroupId: 'imageAlign1' });
998
+ radio.create({ id: 'imgInlineAlignDefaultCenter', text: labels.alignMiddle, cssClass: editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_CENTER), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_CENTER), radioGroupId: 'imageAlign1' });
999
+ radio.create({ id: 'imgInlineAlignDefaultTop', text: labels.alignTop, cssClass: editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_TOP), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_INLINE_ALIGN_DEFAULT_TOP), radioGroupId: 'imageAlign1' });
1000
+ radio.create({ id: 'imgInlineAlignLeft', text: labels.alignLeft, cssClass: editor.getCssClass(classOption.IMG_INLINE_ALIGN_LEFT), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_INLINE_ALIGN_LEFT), radioGroupId: 'imageAlign1' });
1001
+ radio.create({ id: 'imgInlineAlignRight', text: labels.alignRight, cssClass: editor.getCssClass(classOption.IMG_INLINE_ALIGN_RIGHT), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_INLINE_ALIGN_RIGHT), radioGroupId: 'imageAlign1' });
1002
+ radio.create({ id: 'imgAlignLeft', text: labels.alignLeft, cssClass: editor.getCssClass(classOption.IMG_ALIGN_LEFT), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_ALIGN_LEFT), radioGroupId: 'imageAlign2' });
1003
+ radio.create({ id: 'imgAlignCenter', text: labels.alignCenter, cssClass: editor.getCssClass(classOption.IMG_ALIGN_CENTER), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_ALIGN_CENTER), radioGroupId: 'imageAlign2' });
1004
+ radio.create({ id: 'imgAlignRight', text: labels.alignRight, cssClass: editor.getCssClass(classOption.IMG_ALIGN_RIGHT), cssClassIcon: utility.iconPrefix + editor.getCssClass(classOption.IMG_ALIGN_RIGHT), radioGroupId: 'imageAlign2' });
1005
+
1006
+ return radio;
1007
+ }
1008
+ }
1009
+
1010
+ class MediaDialog
1011
+ {
1012
+ #dialog = null;
1013
+ #editor;
1014
+ #cf;
1015
+ #getId;
1016
+ #shared;
1017
+
1018
+ constructor(editor, cf, getId, shared)
1019
+ {
1020
+ this.#editor = editor;
1021
+ this.#cf = cf;
1022
+ this.#getId = getId;
1023
+ this.#shared = shared;
1024
+ }
1025
+
1026
+ get dialog() { return this.#dialog; }
1027
+
1028
+ create()
1029
+ {
1030
+ const editor = this.#editor,
1031
+ cf = this.#cf,
1032
+ getId = this.#getId,
1033
+ mediaId = getId('mediaSrc'),
1034
+ mediaEmbedId = getId('mediaEmbed'),
1035
+ embedAttr = editor.utility.attrPrefix + 'embed',
1036
+ header = $lib.element('', '', '', editor.labels.mediaDialogHeader),
1037
+ content = $lib.element(),
1038
+ temp = $lib.element(),
1039
+ src = $lib.element('', '', 'input', '', { id: mediaId, name: mediaId }),
1040
+ embed = $lib.element('', '', 'textarea', '', { id: mediaEmbedId, name: mediaEmbedId }),
1041
+ caption = $lib.element('', '', 'input', '', { id: getId('mediaCaption') }),
1042
+ width = $lib.element('', '', 'input', '', { id: getId('mediaWidth') });
1043
+
1044
+ const events = {
1045
+ inputChanged: function (e)
1046
+ {
1047
+ const el = e.target;
1048
+ const elToDisable = (el == src) ? embed : src;
1049
+ elToDisable.disabled = (el.value != '');
1050
+
1051
+ if (el == src)
1052
+ this.#shared.addValidatorRules(this.#dialog, [{ name: src.name, dataType: 4 }]);
1053
+ else
1054
+ this.#shared.addValidatorRules(this.#dialog, [{ name: embed.name }]);
1055
+ }.bind(this)
1056
+ };
1057
+
1058
+ [src, embed].forEach(node => $lib.on(node, 'input', events.inputChanged));
1059
+
1060
+ const fields = {};
1061
+ fields.source = cf.createFormField('fldMediaSrc', editor.mediaDialogSourceFormFieldId, temp, { label: editor.labels.mediaSource, field: src }).element;
1062
+ fields.embed = cf.createFormField('fldMediaEmbed', editor.mediaDialogEmbedFormFieldId, temp, { label: editor.labels.mediaEmbed, field: embed }).element;
1063
+ fields.width = cf.createFormField('fldMediaWidth', editor.mediaDialogWidthFormFieldId, temp, { label: editor.labels.mediaWidth, field: width }).element;
1064
+ fields.caption = cf.createFormField('fldCaption', editor.mediaDialogCaptionFormFieldId, temp, { label: editor.labels.caption, field: caption }).element;
1065
+
1066
+ let defaultTemplate = '{source}{embed}{caption}{width}',
1067
+ templateId = 'MediaDialogContent',
1068
+ defaultTemplateId = 'Default' + templateId,
1069
+ hasCustomTemplate = editor.hasTemplate(templateId);
1070
+
1071
+ if (!editor.hasTemplate(templateId))
1072
+ {
1073
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
1074
+ templateId = defaultTemplateId;
1075
+ }
1076
+
1077
+ editor.applyTemplate(content, templateId, fields);
1078
+
1079
+ if (!content.querySelector(`#${getId('fldMediaSrc')}`))
1080
+ {
1081
+ fields.source.style.display = 'none';
1082
+ content.appendChild(fields.source);
1083
+ }
1084
+
1085
+ if (!content.querySelector(`#${getId('fldMediaEmbed')}`))
1086
+ {
1087
+ fields.embed.style.display = 'none';
1088
+ content.appendChild(fields.embed);
1089
+ }
1090
+
1091
+ this.#dialog = cf.createDialog('MediaDialog', editor.mediaDialogId, {
1092
+ cssClass: editor.getCssClass(editor.classOption.MEDIA_DIALOG),
1093
+ header: header,
1094
+ content: content,
1095
+ onShowComplete: () =>
1096
+ {
1097
+ const media = editor.layoutState.activeLayout.media;
1098
+
1099
+ src.value = embed.value = width.value = '';
1100
+ src.disabled = embed.disabled = false;
1101
+
1102
+ if (media)
1103
+ {
1104
+ if (media.hasAttribute(embedAttr))
1105
+ {
1106
+ embed.value = media.firstElementChild.innerHTML;
1107
+ src.disabled = true;
1108
+ }
1109
+ else
1110
+ {
1111
+ src.value = media.firstElementChild.firstElementChild.src;
1112
+ embed.disabled = true;
1113
+ }
1114
+
1115
+ width.value = media.style.width;
1116
+ }
1117
+
1118
+ this.#setConfirmState(hasCustomTemplate, src.value, embed.value ? [{ name: embed.name }] : [{ name: src.name, dataType: 4 }]);
1119
+
1120
+ const activeItem = media ? {
1121
+ src: media.hasAttribute(embedAttr) ? null : src.value,
1122
+ embed: media.hasAttribute(embedAttr) ? embed.value : null,
1123
+ width: width.value,
1124
+ caption: media.querySelector('figcaption')?.textContent || ''
1125
+ } : null;
1126
+
1127
+ editor.events.onMediaDialogShow.fire(editor,
1128
+ {
1129
+ container: content,
1130
+ activeItem,
1131
+ confirmSelection: (url, captionText) => this.#confirmSelection(url, captionText, src, caption)
1132
+ });
1133
+ },
1134
+ onConfirm: () =>
1135
+ {
1136
+ this.#confirmDialog(src, embed, caption, width, embedAttr);
1137
+ }
1138
+ });
1139
+ }
1140
+
1141
+ show() { this.#dialog?.show(); }
1142
+
1143
+ #setConfirmState(hasCustomTemplate, fieldValue, validatorRules)
1144
+ {
1145
+ this.#shared.addValidatorRules(this.#dialog, validatorRules);
1146
+
1147
+ if (hasCustomTemplate)
1148
+ {
1149
+ const confirmButton = $UI.store[this.#dialog.id + '_Confirm'];
1150
+ if (fieldValue)
1151
+ confirmButton.enable();
1152
+ else
1153
+ confirmButton.disable();
1154
+ }
1155
+ }
1156
+
1157
+ #confirmSelection(url, captionText, src, caption)
1158
+ {
1159
+ src.value = url;
1160
+ if (captionText) caption.value = captionText;
1161
+ $UI.store[this.#dialog.id + '_Confirm'].enable();
1162
+ }
1163
+
1164
+ #confirmDialog(src, embed, caption, width, embedAttr)
1165
+ {
1166
+ const editor = this.#editor;
1167
+
1168
+ $lib.each(editor.mediaURLReplacement, item =>
1169
+ {
1170
+ if (src.value.match(item.match))
1171
+ src.value = src.value.replace(item.match, item.replace);
1172
+ });
1173
+
1174
+ let media = editor.layoutState.activeLayout.media,
1175
+ mediaRatio = $lib.element(),
1176
+ captionHTML = '',
1177
+ mediaContent = embed.value
1178
+ ? embed.value
1179
+ : $lib.element('', '', 'iframe', '', {
1180
+ allow: 'encrypted-media',
1181
+ allowfullscreen: 'true',
1182
+ contenteditable: 'false'
1183
+ }, { src: src.value }).outerHTML;
1184
+
1185
+ mediaRatio.setAttribute('style', 'position: relative; padding-bottom: 56.25%;');
1186
+ mediaRatio.innerHTML = mediaContent;
1187
+
1188
+ const iframe = mediaRatio.querySelector('iframe');
1189
+
1190
+ if (iframe)
1191
+ iframe.setAttribute('style', 'position: absolute; left: 0; top: 0; width: 100%; height: 100%; border: 0;');
1192
+
1193
+ mediaContent = mediaRatio.outerHTML;
1194
+
1195
+ if (media)
1196
+ {
1197
+ media.removeAttribute(embedAttr);
1198
+ media.style.width = width.value ? $lib.unit(width.value) : null;
1199
+ media.innerHTML = mediaContent;
1200
+
1201
+ if (caption.value)
1202
+ media.appendChild($lib.element('', '', 'figcaption', caption.value));
1203
+
1204
+ editor.history.addItem();
1205
+ }
1206
+ else
1207
+ {
1208
+ if (caption.value)
1209
+ captionHTML = $lib.element('', '', 'figcaption', caption.value).outerHTML;
1210
+
1211
+ media = editor.history.noHistory(() =>
1212
+ editor.contentManager.insertHTML({
1213
+ tag: 'figure',
1214
+ styles: { 'width': width.value ? $lib.unit(width.value) : null },
1215
+ attributes: { [editor.utility.attrPrefix + 'media']: '' },
1216
+ selectable: true,
1217
+ select: true,
1218
+ allowContent: false,
1219
+ isPhrasingContent: false,
1220
+ innerHTML: mediaContent + captionHTML,
1221
+ })
1222
+ );
1223
+ }
1224
+
1225
+ if (embed.value)
1226
+ media.setAttribute(embedAttr, '');
1227
+
1228
+ editor.history.addItem();
1229
+ }
1230
+ }
1231
+
1232
+ var DialogManager = componyx.UI.editor_modules.DialogManager;
1233
+
1234
+ export { DialogManager as default };