@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,748 @@
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
+ * ContentManager module — manages content management actions.
6
+ *
7
+ * @class
8
+ * @memberof componyx.UI.editor_modules
9
+ * @ignore
10
+ */
11
+ componyx.UI.editor_modules.ContentManager = class ContentManager
12
+ {
13
+ constructor(editor)
14
+ {
15
+ this.editor = editor;
16
+ this.utility = editor.utility;
17
+ }
18
+
19
+
20
+ /**
21
+ * Gets the editor content.
22
+ * @param {HTMLElement|null} [editableElement] The specific editable element to get content from. Only applicable if this editor instance uses multiple editable elements.
23
+ * @returns {String} The editor HTML content.
24
+ *
25
+ */
26
+ getContent(editableElement)
27
+ {
28
+ let content = $lib.element();
29
+
30
+ editableElement = editableElement || this.editor.getEditorElement();
31
+
32
+ this.editor.resizer.clear(true); // clear resizer to avoid including resizer elements in the source
33
+ content.innerHTML = editableElement.innerHTML;
34
+ this.cleanSource(content);
35
+ this.editor.table.clearAll(content, true);
36
+
37
+ return content.innerHTML;
38
+ }
39
+
40
+ /**
41
+ * Corrects the specified source document.
42
+ * @param {HTMLElement} source
43
+ * @param {Range} range
44
+ * @param {Object} marker
45
+ *
46
+ */
47
+ ensureDocStructure(source, range, marker)
48
+ {
49
+ this.editor.activateDocument();
50
+
51
+ if (!this.hasContent())
52
+ this.insertParagraph();
53
+
54
+ const selection = this.editor.selectionRange;
55
+ range = range || selection.ensureTextRange(selection.getRange());
56
+ marker = marker || selection.createRangeMarker(range);
57
+
58
+ this.editor.resizer.clear(true);
59
+ this.editor.paragraphButtons.destroy(source);
60
+ this.ensureWrappedNodes(source);
61
+ this.ensureTableCellStructure(source);
62
+ this.restoreEditorAttrs(source);
63
+
64
+ for (const figure of source.querySelectorAll('figure:not([contenteditable="false"]):not(:has(table)')) // Figure left after table got removed
65
+ {
66
+ figure.remove();
67
+ }
68
+
69
+ let rootNodes = this.ensureBlockNodes(source);
70
+
71
+ selection.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
72
+ this.createSelectables(source);
73
+
74
+ for (const n of rootNodes)
75
+ {
76
+ this.cleanUp(n, null, range, marker);
77
+
78
+ if (!n.childNodes.length)
79
+ n.remove();
80
+ }
81
+
82
+ if (marker)
83
+ {
84
+ selection.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
85
+ selection.removeRangeMarker(marker);
86
+ }
87
+
88
+ this.editor.history.addItem();
89
+ }
90
+
91
+ /**
92
+ * Ensures that root nodes are placed inside a block-node if this is not the case.
93
+ * @param {HTMLElement} source
94
+ * @returns {Set} A set of blocknodes.
95
+ *
96
+ */
97
+ ensureBlockNodes(source)
98
+ {
99
+ let rootNodes = new Set(),
100
+ blockEl;
101
+
102
+ const childNodes = Array.from(source.childNodes);
103
+
104
+ $lib.each(childNodes, (n) =>
105
+ {
106
+ let isBlock = this.editor.nodeManager.isBlock(n);
107
+
108
+ if (n.nodeName === 'DIV' && n.contentEditable !== 'false')
109
+ {
110
+ const hasBlockChild = Array.from(n.childNodes).some(child => this.editor.nodeManager.isBlock(child));
111
+
112
+ if (!hasBlockChild)
113
+ {
114
+ let p = $lib.element('', '', 'p');
115
+ $lib.surround(p, n); // wraps div inside p
116
+ $lib.unsurround(n); // removes div, keeps its children inside p
117
+ blockEl = p;
118
+ }
119
+ else
120
+ blockEl = n;
121
+ }
122
+ else if (!isBlock && !blockEl)
123
+ blockEl = $lib.surround($lib.element('', '', 'p'), n);
124
+ else if (!isBlock)
125
+ blockEl.appendChild(n);
126
+ else
127
+ blockEl = n;
128
+
129
+ rootNodes.add(blockEl);
130
+ });
131
+
132
+ return rootNodes;
133
+ }
134
+
135
+ ensureWrappedNodes(source)
136
+ {
137
+ $lib.each(this.editor.nodeManager.getElementsByTagName(source, 'HR IMG VIDEO IFRAME OBJECT EMBED TABLE SVG'), (n) =>
138
+ {
139
+ let name = n.nodeName,
140
+ parentNode = n.parentNode,
141
+ isTable = name == 'TABLE',
142
+ isFigure = name.match(this.utility.figureNodesRegEx),
143
+ existingFigure = n.closest('figure');
144
+
145
+ if (existingFigure)
146
+ {
147
+ n = existingFigure;
148
+ }
149
+ else if (n.nodeName == 'IMG' && !this.editor.nodeManager.isBlock(n.parentNode, true)) // inline image
150
+ {
151
+ if (n.parentNode.childNodes.length > 1) // parent has other content beside the img, wrap img in its own span
152
+ n = $lib.surround($lib.element('', '', 'span'), n);
153
+ else
154
+ n = n.parentNode;
155
+
156
+ n.style.display = 'inline-block';
157
+ n.style.width = 'fit-content';
158
+ }
159
+ else if (parentNode == source || (parentNode.nodeName == 'DIV' && isFigure) || (' DIV FIGURE '.indexOf(' ' + parentNode.nodeName + ' ') == -1))
160
+ {
161
+ n = $lib.surround($lib.element('', '', (isFigure) ? 'figure' : 'div', '', ''), n);
162
+ if (n.parentNode != source && n.parentNode.nodeName == 'DIV') // remove parent when parent div node has been enclosed by a figure node
163
+ $lib.unsurround(n.parentNode);
164
+ }
165
+ else
166
+ n = parentNode;
167
+
168
+ if (!isTable)
169
+ n.contentEditable = false;
170
+
171
+ if (n.nodeName == 'FIGURE' && isTable)
172
+ {
173
+ n.classList.add(this.editor.getCssClass(this.editor.classOption.TABLE_WRAPPER));
174
+ }
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Cleans up the root node by clearing and merging double layout nodes.
180
+ * @param {Node} rootNode
181
+ * @param {string} nodeName
182
+ * @param {Range} range
183
+ * @param {Object} marker
184
+ *
185
+ */
186
+ cleanUp(rootNode, nodeName, range, marker)
187
+ {
188
+ let removeMarker = $lib.isEmpty(marker);
189
+ const selection = this.editor.selectionRange;
190
+
191
+ marker = marker || selection.createRangeMarker(range || selection.getRange());
192
+
193
+ this.replaceBoldItalic(rootNode);
194
+ this.clearDoubles(rootNode, nodeName, marker);
195
+ rootNode.normalize();
196
+ this.mergeNodes(rootNode, nodeName, marker);
197
+ this.removeRedundantStyleNodes(rootNode);
198
+
199
+ selection.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
200
+
201
+ if (removeMarker)
202
+ selection.removeRangeMarker(marker);
203
+ }
204
+
205
+ /**
206
+ * Replaces bold <b> and italic <i> tags for strong <strong> and emphasized <em>.
207
+ * @param {Node} rootNode
208
+ *
209
+ */
210
+ replaceBoldItalic(rootNode)
211
+ {
212
+ let nodes = this.editor.nodeManager.getElementsByTagName(rootNode, 'b i');
213
+
214
+ $lib.each(nodes, (node) =>
215
+ {
216
+ $lib.surround($lib.element('', '', (node.nodeName == 'B') ? 'strong' : 'em'), node);
217
+ $lib.unsurround(node);
218
+ });
219
+ }
220
+
221
+ /**
222
+ * Clears double layout nodes when possible.
223
+ * @param {Node} element
224
+ * @param {string} nodeName
225
+ * @param {Object} marker
226
+ *
227
+ */
228
+ clearDoubles(element, nodeName, marker)
229
+ {
230
+ let nm = this.editor.nodeManager,
231
+ nodes = nm.getElementsByTagName(element, nodeName || this.utility.layoutNodeNames.join(' ')),
232
+ childNodes, childIndex, childNode, isStyle, textContent;
233
+
234
+ $lib.each(nodes, (node) =>
235
+ {
236
+ if (!nm.contains(element, node))
237
+ return;
238
+
239
+ isStyle = nm.isStyleNode(node);
240
+ textContent = node.textContent;
241
+
242
+ if (nm.canRemove(node))
243
+ {
244
+ nm.removeNodeAndKeepMarkers(node, marker);
245
+ }
246
+ else
247
+ {
248
+ childNodes = nm.getElementsByTagName(node, node.nodeName.toUpperCase());
249
+
250
+ for (childIndex = 0; childIndex < childNodes.length; ++childIndex)
251
+ {
252
+ childNode = childNodes[childIndex];
253
+
254
+ if (nm.canRemove(node))
255
+ nm.removeNodeAndKeepMarkers(node, marker);
256
+ else if (!isStyle)
257
+ $lib.unsurround(childNode);
258
+ else if (nm.isStyleNode(childNode))
259
+ {
260
+ if (childNode.textContent == textContent)
261
+ {
262
+ this.editor.format.copyStyles(childNode, node);
263
+ $lib.unsurround(childNode);
264
+ }
265
+ else
266
+ {
267
+ let equal = true, sNode = childNode;
268
+
269
+ while (sNode && sNode != node)
270
+ {
271
+ if (nm.isStyleNode(sNode))
272
+ equal = this.editor.format.hasEqualStyles(node, sNode);
273
+
274
+ sNode = (equal) ? sNode.parentNode : null;
275
+ }
276
+
277
+ if (equal)
278
+ $lib.unsurround(childNode);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Merges two of the same nodes when possible.
288
+ * @param {Node} rootNode
289
+ * @param {string} nodeName
290
+ * @param {Object} marker
291
+ *
292
+ */
293
+ mergeNodes(rootNode, nodeName, marker)
294
+ {
295
+ let nodes = this.editor.nodeManager.getElementsByTagName(rootNode, nodeName || this.utility.layoutNodeNames.join(' ')),
296
+ startMarker = $lib('#' + marker.startId),
297
+ endMarker = (marker.endId) ? $lib('#' + marker.endId) : null,
298
+ node, index, prev, next;
299
+
300
+ for (index = 0; index < nodes.length; ++index)
301
+ {
302
+ node = nodes[index];
303
+ prev = node.previousSibling;
304
+ next = node.nextSibling;
305
+
306
+ if ((startMarker && prev == startMarker) || (endMarker && prev == endMarker))
307
+ prev = prev.previousSibling;
308
+
309
+ if ((startMarker && next == startMarker) || (endMarker && next == endMarker))
310
+ next = next.nextSibling;
311
+
312
+ if (this.editor.nodeManager.isStyleNode(node) || (node.nodeType == 1 && !node.hasAttributes()))
313
+ {
314
+ if (this.editor.format.canMerge(prev, node))
315
+ {
316
+ if (prev.nextSibling && prev.nextSibling == startMarker)
317
+ node.insertBefore(startMarker, node.firstChild);
318
+ else if (prev.nextSibling && prev.nextSibling == endMarker)
319
+ node.insertBefore(endMarker, node.firstChild);
320
+
321
+ node.insertBefore($lib.extract(prev), node.firstChild);
322
+ $lib.remove(prev);
323
+ }
324
+
325
+ if (this.editor.format.canMerge(next, node))
326
+ {
327
+ if (next.previousSibling && next.previousSibling == startMarker)
328
+ node.appendChild(startMarker);
329
+ else if (next.previousSibling && next.previousSibling == endMarker)
330
+ node.appendChild(endMarker);
331
+
332
+ node.appendChild($lib.extract(next));
333
+ $lib.remove(next);
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Removes possible default style node when set as root layout-node.
341
+ * @param {Node} rootNode
342
+ *
343
+ */
344
+ removeRedundantStyleNodes(rootNode)
345
+ {
346
+ const nodes = this.editor.nodeManager.getElementsByTagName(rootNode, 'SPAN'),
347
+ styleCache = new Map();
348
+
349
+ $lib.each(nodes, node =>
350
+ {
351
+ if (this.editor.nodeManager.isStyleNode(node) && this.isRedundantStyleNode(node, styleCache))
352
+ {
353
+ $lib.unsurround(node);
354
+ }
355
+ });
356
+ }
357
+
358
+ /**
359
+ * Checks if style node is redundant.
360
+ * @param {Node} node
361
+ * @param {Map} styleCache
362
+ * @returns {boolean} A value indicating if the style node can be removed.
363
+ *
364
+ */
365
+ isRedundantStyleNode(node, styleCache)
366
+ {
367
+ const inheritedStyles = this.editor.format.getLayoutTreeStyles(node.parentNode, styleCache);
368
+ let redundant = true;
369
+
370
+ this.editor.format.iterateStyle(node, (name, value) =>
371
+ {
372
+ const isDefaultFontFamily = (name === 'font-family' && value === this.editor.defaultFontFamily),
373
+ isDefaultFontSize = (name === 'font-size' && value === this.editor.defaultFontSize);
374
+
375
+ if (inheritedStyles[name] === undefined)
376
+ {
377
+ if (isDefaultFontFamily || isDefaultFontSize)
378
+ {
379
+ return;
380
+ }
381
+ else
382
+ redundant = false;
383
+ }
384
+ else if (inheritedStyles[name] !== value)
385
+ {
386
+ return (redundant = false);
387
+ }
388
+ });
389
+
390
+ return redundant;
391
+ }
392
+
393
+ /**
394
+ * Cleans the HTML source.
395
+ * @param {HTMLElement|null} source The specific source element to clean. If null, the active editor content will be cleaned directly.
396
+ * @param {boolean} clearZeroWidthCharacters A value indicating whether zero-width characters should be cleared. Default is true.
397
+ */
398
+ cleanSource(source = null, clearZeroWidthCharacters = true)
399
+ {
400
+ const contentEl = source || this.editor.getEditorElement();
401
+
402
+ this.clearEditorAttributes(contentEl);
403
+ this.clearContentEditableChildren(contentEl);
404
+
405
+ if (clearZeroWidthCharacters)
406
+ this.clearZeroWidth(contentEl);
407
+
408
+ this.clearEmptyParagraph(contentEl);
409
+ this.editor.table.clearAll(contentEl);
410
+ this.editor.paragraphButtons.destroy(contentEl);
411
+
412
+ if (!source)
413
+ this.editor.resizer.clear(true);
414
+
415
+ return contentEl.innerHTML;
416
+ }
417
+
418
+ ensureTableCellStructure(source)
419
+ {
420
+ source.querySelectorAll('td, th').forEach(cell =>
421
+ {
422
+ if (!cell.firstChild)
423
+ {
424
+ const p = $lib.element({ tag: 'p' });
425
+ p.appendChild($lib.element({ tag: 'br' }));
426
+ cell.appendChild(p);
427
+ return;
428
+ }
429
+
430
+ this.ensureBlockNodes(cell);
431
+ });
432
+ }
433
+
434
+ /**
435
+ * Esnures that specific nodes get the correct editor attribute.
436
+ * @param {any} element
437
+ *
438
+ */
439
+ restoreEditorAttrs(element)
440
+ {
441
+ const prefix = this.utility.attrPrefix,
442
+ nm = this.editor.nodeManager;
443
+
444
+ $lib.each(element.querySelectorAll('a, hr'), (node) =>
445
+ {
446
+ if (node.nodeName == 'A' && nm.isBookmark(node))
447
+ node.setAttribute(prefix + 'bookmark', '');
448
+ else if (node.nodeName == 'HR' && nm.isPageBreak(node))
449
+ node.setAttribute(prefix + 'pagebreak', '');
450
+ });
451
+ }
452
+
453
+ /**
454
+ * Removes the editor attributes that are only required inside the editor.
455
+ * @param {HTMLElement} editor
456
+ *
457
+ */
458
+ clearEditorAttributes(element)
459
+ {
460
+ element = element || this.editor.getEditorElement();
461
+ const prefix = this.utility.attrPrefix;
462
+ const keep = [
463
+ prefix + 'media',
464
+ prefix + 'embed',
465
+ prefix + 'styleid'
466
+ ];
467
+
468
+ $lib.each(element.getElementsByTagName('*'), (node) =>
469
+ {
470
+ if (node.hasAttributes())
471
+ {
472
+ Array.from(node.attributes)
473
+ .filter(attr => attr.name.startsWith(prefix) && !keep.includes(attr.name))
474
+ .forEach(attr => node.removeAttribute(attr.name));
475
+ }
476
+ });
477
+ }
478
+
479
+ /**
480
+ * Removes zero-width characters from the source HTML.
481
+ * @param {HTMLElement} editor
482
+ *
483
+ */
484
+ clearZeroWidth(editor)
485
+ {
486
+ editor = editor || this.editor.getEditorElement();
487
+
488
+ let nodes = editor.ownerDocument.createTreeWalker(editor, NodeFilter.SHOW_TEXT, null, null),
489
+ node, clear = [];
490
+
491
+ while (node = nodes.nextNode())
492
+ {
493
+ if (node.nodeValue.match(this.utility.zeroWidthRegEx))
494
+ node.nodeValue = node.nodeValue.replace(this.utility.zeroWidthRegEx, '');
495
+
496
+ if (node.nodeValue.length == 0)
497
+ clear.push(node);
498
+ }
499
+
500
+ clear.forEach((n) => { n.remove(); });
501
+ }
502
+
503
+ /**
504
+ * Removes content editable child elements.
505
+ * @param {HTMLElement} editor
506
+ *
507
+ */
508
+ clearContentEditableChildren(editor)
509
+ {
510
+ editor = editor || this.editor.getEditorElement();
511
+ editor.querySelectorAll('[contenteditable]').forEach(el => el.removeAttribute('contenteditable'));
512
+ }
513
+
514
+ /**
515
+ * Clears the editor if it has an empty paragraph.
516
+ * @param {HTMLElement} editor
517
+ *
518
+ */
519
+ clearEmptyParagraph(editor)
520
+ {
521
+ editor = editor || this.editor.getEditorElement();
522
+
523
+ if (editor && editor.innerHTML == '<p><br></p>')
524
+ {
525
+ while (editor.firstChild)
526
+ {
527
+ editor.firstChild.remove();
528
+ }
529
+ }
530
+ }
531
+
532
+ /** Inserts a paragraph into the current Range.
533
+ * *
534
+ */
535
+ insertParagraph()
536
+ {
537
+ const selection = this.editor.selectionRange,
538
+ isAutoRecovery = !this.hasContent();
539
+
540
+ let range = selection.getRange(),
541
+ nodes = selection.getRootNodesInRange(range),
542
+ lastNode = nodes[nodes.length - 1],
543
+ p = this.editor.getDoc().createElement('p'),
544
+ br = $lib.element('', '', 'br');
545
+
546
+ p.appendChild(br);
547
+
548
+ if (lastNode && lastNode.nodeName !== 'TD' && lastNode.nodeName !== 'TH')
549
+ range = selection.collapseRangeToNode(lastNode);
550
+
551
+ range.deleteContents();
552
+ selection.insertIntoRange(range, p, br);
553
+ selection.restoreRange(range);
554
+
555
+ this.setBlockAttr(p);
556
+
557
+ if (!isAutoRecovery)
558
+ this.editor.history.addItem(range);
559
+ }
560
+
561
+ /**
562
+ * Inserts a BR tag into the range.
563
+ * @param {Range} range
564
+ *
565
+ */
566
+ insertBR(range)
567
+ {
568
+ let br = $lib.element('', '', 'br'),
569
+ sc = range.startContainer,
570
+ n = this.editor.getDoc().createTextNode(this.utility.zeroWidthChar);
571
+
572
+ if (sc.nodeType === 3 && sc.nodeValue.match(this.utility.zeroWidthRegEx))
573
+ {
574
+ range.setStart(sc, sc.length);
575
+ range.collapse(true);
576
+ }
577
+
578
+ this.editor.selectionRange.insertIntoRange(range, br, br, true);
579
+ this.editor.selectionRange.insertIntoRange(range, n, n, false);
580
+ }
581
+
582
+ /**
583
+ * Inserts HTML or a node into the current selection.
584
+ * @param {Object} settings
585
+ * @returns {Node}
586
+ *
587
+ */
588
+ insertHTML(settings)
589
+ {
590
+ this.editor.activateDocument();
591
+
592
+ if (!this.hasContent())
593
+ this.insertParagraph();
594
+
595
+ const selection = this.editor.selectionRange,
596
+ nm = this.editor.nodeManager,
597
+ history = this.editor.history;
598
+
599
+ let node = settings.node || nm.createNode(settings),
600
+ range = selection.ensureTextRange(selection.getRange()),
601
+ marker = selection.createRangeMarker(range);
602
+
603
+ if (!range.collapsed)
604
+ {
605
+ range = selection.restoreRangeToMarker(marker, range);
606
+ range.deleteContents();
607
+ range = selection.restoreRangeToMarker(marker, range);
608
+ }
609
+
610
+ let markerEl = $lib('#' + marker.startId) || $lib('#' + marker.endId),
611
+ rootNode = nm.getRootNode(markerEl),
612
+ beforeNode;
613
+
614
+ if (settings.tag && settings.isPhrasingContent == undefined && this.editor.nodeManager.isBlock($lib.element('', '', settings.tag), true)) // Safety: force block behaviour for known block elements
615
+ settings.isPhrasingContent = false;
616
+
617
+ if (settings.isPhrasingContent === false)
618
+ {
619
+ let rightSiblings = nm.getSiblings(rootNode, null, markerEl);
620
+
621
+ if (rightSiblings.length > 1 || (rightSiblings.length === 1 && rightSiblings[0].nodeName !== 'BR'))
622
+ {
623
+ let p = $lib.element(rootNode.parentElement, rootNode.nextSibling, 'p', rightSiblings);
624
+
625
+ if (!nm.nodeHasContent(rootNode))
626
+ rootNode.remove();
627
+
628
+ rootNode = beforeNode = p;
629
+ }
630
+ else
631
+ {
632
+ beforeNode = rootNode.nextSibling;
633
+ }
634
+ }
635
+
636
+ if (settings.isPhrasingContent !== false)
637
+ {
638
+ $lib.insertNode(node, true, true, range);
639
+
640
+ range.setStartAfter(node);
641
+ range.setEndAfter(node);
642
+ range.collapse(false);
643
+
644
+ let textNode = this.editor.getDoc().createTextNode(this.utility.zeroWidthChar);
645
+
646
+ $lib.insertNode(textNode, true, true, range);
647
+ range.collapse(true);
648
+ }
649
+ else
650
+ {
651
+ if (rootNode.nodeName.match(this.utility.blockNodeRegEx))
652
+ {
653
+ if (!nm.nodeHasContent(rootNode))
654
+ rootNode.parentElement.replaceChild(node, rootNode);
655
+ else
656
+ rootNode.parentElement.insertBefore(node, beforeNode);
657
+
658
+ if (!node.nextSibling)
659
+ {
660
+ range.selectNodeContents(node);
661
+ range.collapse(false);
662
+
663
+ history.noHistory(() => this.insertParagraph());
664
+ }
665
+ }
666
+ else
667
+ {
668
+ rootNode.insertBefore(node, markerEl.nextElementSibling || markerEl);
669
+ }
670
+
671
+ if (node.nextSibling)
672
+ {
673
+ range.selectNodeContents(node.nextSibling);
674
+ range.collapse(true);
675
+ }
676
+ }
677
+
678
+ selection.removeRangeMarker(marker);
679
+
680
+ if (settings.select)
681
+ this.editor.selectNode.bind(node)();
682
+
683
+ history.addItem();
684
+
685
+ return node;
686
+ }
687
+
688
+ /**
689
+ * Returns a value indicating if the editor body has content.
690
+ *
691
+ */
692
+ hasContent()
693
+ {
694
+ const editor = this.editor.getEditorElement();
695
+
696
+ if (!editor.textContent.length && editor.firstChild && editor.firstChild.nodeType != 1)
697
+ editor.firstChild.remove();
698
+
699
+ return editor.childNodes.length != 0;
700
+ }
701
+
702
+ /**
703
+ * Sets the block attribute if block view is enabled
704
+ * @param {Node} el
705
+ *
706
+ */
707
+ setBlockAttr(el)
708
+ {
709
+ if (this.editor.isBlockViewSelected())
710
+ el.setAttribute(this.utility.attrPrefix + 'blockview', el.nodeName);
711
+ }
712
+
713
+ /**
714
+ * Sets or removes the block view attributes.
715
+ * @param {boolean} on
716
+ * @param {HTMLElement} editor
717
+ *
718
+ */
719
+ setBlockAttrAll(on = true, editor)
720
+ {
721
+ editor = editor || this.editor.getEditorElement();
722
+ let attr = this.utility.attrPrefix + 'blockview';
723
+
724
+ $lib.each(editor.childNodes, (el) =>
725
+ {
726
+ if (on)
727
+ el.setAttribute(attr, el.nodeName);
728
+ else if (!on)
729
+ el.removeAttribute(attr);
730
+ });
731
+ }
732
+
733
+ /**
734
+ * Creates selectable elements for the specified source document.
735
+ * @param {HTMLElement} source
736
+ *
737
+ */
738
+ createSelectables(source)
739
+ {
740
+ new Set(source.querySelectorAll(this.editor.selectableNodes.join(','))).forEach(node =>
741
+ this.editor.bindNodeEvents(node)
742
+ );
743
+ }
744
+ };
745
+
746
+ var ContentManager = componyx.UI.editor_modules.ContentManager;
747
+
748
+ export { ContentManager as default };