@componyx/ui 0.0.0-stage → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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/DataObserver.js +717 -0
  85. package/Form/Modules/Databinder.js +602 -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,950 @@
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
+ * Format module for text formatting operations
6
+ * @class Format
7
+ * @memberof componyx.UI.editor_modules
8
+ * @ignore
9
+ */
10
+ componyx.UI.editor_modules.Format = class Format
11
+ {
12
+ constructor(editor)
13
+ {
14
+ this.editor = editor;
15
+ this.instance = editor;
16
+ this.utility = editor.utility;
17
+ this.cloneStamp = null;
18
+ }
19
+
20
+ /**
21
+ * Sets a CSS block-level style on all root block nodes in the selection.
22
+ * @param {string} property - CSS style property (camelCase, e.g. "text-align")
23
+ * @param {string} value - CSS value
24
+ */
25
+ setBlockStyle(property, value)
26
+ {
27
+ const editor = this.editor;
28
+
29
+ editor.activateDocument();
30
+
31
+ const selection = editor.selectionRange,
32
+ history = editor.history;
33
+
34
+ let range = selection.getRange();
35
+
36
+ if (!range)
37
+ return;
38
+
39
+ let nodes = selection.getRootNodesInRange(range);
40
+
41
+ if (!nodes || !nodes.length)
42
+ return;
43
+
44
+ nodes.forEach(node =>
45
+ {
46
+ if (value == null || value === '')
47
+ node.style.removeProperty(property);
48
+ else
49
+ node.style.setProperty(property, value);
50
+ });
51
+
52
+ history.addItem();
53
+ }
54
+
55
+ /**
56
+ * Toggles the layout node on/off.
57
+ * @param {Object} settings
58
+ */
59
+ toggleLayoutNode(settings)
60
+ {
61
+ this.editor.activateDocument();
62
+
63
+ let range = this.editor.selectionRange.getRange();
64
+
65
+ if (!this.allowContent(range))
66
+ return;
67
+
68
+ range = this.editor.selectionRange.ensureTextRange(range);
69
+
70
+ let nodes = this.editor.selectionRange.getRootNodesInRange(range),
71
+ length = nodes.length,
72
+ rootNode = nodes[0],
73
+ nodeName = settings.tag,
74
+ styles = settings.styles,
75
+ clearStyle = this.isClearStyle(styles),
76
+ removable = (!styles || !$lib.isEmpty(clearStyle)),
77
+ layoutNode = this.editor.nodeManager.getLayoutNode(nodeName, range.startContainer, nodes[0]),
78
+ marker, end;
79
+
80
+ if (this.editor.history.historyStack.get(this.editor.getEditorElement()).items.length == 0)
81
+ range = this.editor.history.addItem(range);
82
+
83
+ if (range.collapsed)
84
+ {
85
+ if (this.isWordSelection(range, rootNode))
86
+ {
87
+ if (layoutNode && styles && this.canUpdateStyleNode(layoutNode, rootNode, clearStyle))
88
+ {
89
+ this.updateNodeStyles(layoutNode, styles);
90
+ this.editor.contentManager.cleanUp(rootNode, null, range);
91
+ }
92
+ else
93
+ {
94
+ if (layoutNode && removable)
95
+ this.removeWordLayoutNode(nodeName, rootNode, range, clearStyle);
96
+ else if (!clearStyle)
97
+ this.addWordLayoutNode(nodeName, styles, rootNode, range);
98
+ }
99
+ }
100
+ else
101
+ {
102
+ if (layoutNode && removable)
103
+ this.removeLayoutNode(nodeName, rootNode, range, clearStyle);
104
+ else if (!clearStyle)
105
+ this.addLayoutNode(this.createLayoutNode(nodeName, styles), range);
106
+
107
+ this.editor.contentManager.cleanUp(rootNode, null, range);
108
+ }
109
+ }
110
+ else
111
+ {
112
+ marker = this.editor.selectionRange.createRangeMarker(range);
113
+ range = this.editor.selectionRange.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
114
+
115
+ for (var index = 0; index < length; ++index)
116
+ {
117
+ let mustRestore = true;
118
+ rootNode = nodes[index];
119
+ end = (index == (length - 1));
120
+
121
+ if ('UL OL TABLE TR'.split(' ').indexOf(rootNode.nodeName) > -1)
122
+ continue;
123
+
124
+ if (layoutNode && removable)
125
+ {
126
+ this.removeNodesFromRange(rootNode, nodeName, range, !index, end, clearStyle);
127
+ }
128
+ else if (!clearStyle)
129
+ {
130
+ let node = this.createLayoutNode(nodeName, styles);
131
+ mustRestore = this.addNodeToRange(rootNode, node, range, !index, end);
132
+
133
+ if (index === 0)
134
+ node.insertBefore($lib('#' + marker.startId), node.firstChild);
135
+ }
136
+
137
+ if (mustRestore)
138
+ {
139
+ range = this.editor.selectionRange.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
140
+ this.editor.contentManager.cleanUp(rootNode, null, range, marker);
141
+ }
142
+ }
143
+ }
144
+
145
+ this.editor.history.addItem(this.editor.selectionRange.getRange(), marker);
146
+ }
147
+
148
+ /**
149
+ * Copies the formatting.
150
+ */
151
+ copyFormat()
152
+ {
153
+ this.editor.activateDocument();
154
+ let range = this.editor.selectionRange.getRange(),
155
+ rootNode = this.editor.nodeManager.getRootNode(range.startContainer);
156
+
157
+ this.cloneStamp = this.editor.nodeManager.getLayoutNodes(range.startContainer, rootNode, true);
158
+
159
+ if ($lib.isEmpty(this.cloneStamp))
160
+ this.cloneStamp = null;
161
+
162
+ return this.cloneStamp;
163
+ }
164
+
165
+ /**
166
+ * Pastes the formatting to the current range.
167
+ * @param {Node[]} format
168
+ * @param {boolean} clear
169
+ */
170
+ pasteFormat(format, clear = true)
171
+ {
172
+ this.editor.activateDocument();
173
+
174
+ format = format || this.cloneStamp;
175
+
176
+ if (format)
177
+ {
178
+ if (clear)
179
+ this.editor.history.noHistory(() => this.clearFormat());
180
+
181
+ let selection = this.editor.selectionRange,
182
+ range = selection.ensureTextRange(selection.getRange()),
183
+ marker = selection.createRangeMarker(range),
184
+ nodes = selection.getRootNodesInRange(range),
185
+ length = nodes.length,
186
+ rootNode = nodes[0],
187
+ wordRange, end;
188
+
189
+ if (range.collapsed && !this.isWordSelection(range, rootNode))
190
+ this.addLayoutNode(this.editor.nodeManager.buildLayoutTree(format), range);
191
+ else
192
+ {
193
+ if (range.collapsed)
194
+ wordRange = this.createWordRange(rootNode, marker);
195
+
196
+ range = (wordRange) ? wordRange.range : range;
197
+
198
+ $lib.each(nodes, (rootNode, index) =>
199
+ {
200
+ end = (index == (length - 1));
201
+
202
+ if (this.addNodeToRange(rootNode, this.editor.nodeManager.buildLayoutTree(format), range, !index, end))
203
+ {
204
+ range = this.editor.selectionRange.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
205
+ this.editor.contentManager.cleanUp(rootNode, null, range, marker);
206
+ }
207
+ });
208
+
209
+ if (wordRange)
210
+ wordRange.clear();
211
+ }
212
+
213
+ this.editor.history.addItem(null, marker);
214
+
215
+ if (!arguments.length)
216
+ this.cloneStamp = null;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Clears the formatting on the current range.
222
+ */
223
+ clearFormat()
224
+ {
225
+ this.editor.activateDocument();
226
+
227
+ let selection = this.editor.selectionRange,
228
+ nodeName = '*',
229
+ range = selection.ensureTextRange(this.editor.selectionRange.getRange()),
230
+ nodes = selection.getRootNodesInRange(range),
231
+ length = nodes.length,
232
+ rootNode = nodes[0], end;
233
+
234
+ if (range.collapsed)
235
+ {
236
+ if (this.isWordSelection(range, rootNode))
237
+ this.removeWordLayoutNode(nodeName, rootNode, range);
238
+ else
239
+ this.removeLayoutNode(nodeName, rootNode, range);
240
+ }
241
+ else
242
+ {
243
+ let marker = selection.createRangeMarker(range);
244
+
245
+ range = selection.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
246
+
247
+ $lib.each(nodes, (node, index) =>
248
+ {
249
+ rootNode = nodes[index];
250
+ end = (index == (length - 1));
251
+
252
+ this.removeNodesFromRange(rootNode, nodeName, range, !index, end);
253
+ range = selection.restoreRangeToMarker(marker, this.editor.getDoc().createRange());
254
+ this.editor.contentManager.cleanUp(rootNode, null, range, marker);
255
+ });
256
+
257
+ selection.removeRangeMarker(marker);
258
+ }
259
+
260
+ this.editor.history.addItem();
261
+ }
262
+
263
+ /**
264
+ * Adds layout node
265
+ */
266
+ addLayoutNode(layoutNode, range)
267
+ {
268
+ $lib.insertNode(this.editor.nodeManager.getRootNode(layoutNode), true, true, range);
269
+ range.setStart(this.editor.nodeManager.createTextNodePlaceHolder(layoutNode, null, this.utility.zeroWidthChar), 0);
270
+ range.collapse(true);
271
+
272
+ let next = layoutNode.nextSibling;
273
+
274
+ if (next && next.textContent == this.utility.zeroWidthChar)
275
+ next.remove();
276
+ }
277
+
278
+ /**
279
+ * Removes layout node
280
+ */
281
+ removeLayoutNode(nodeName, rootNode, range, clearStyle)
282
+ {
283
+ this.createEmptyWord(range);
284
+ this.removeWordLayoutNode(nodeName, rootNode, range, clearStyle);
285
+ }
286
+
287
+ /**
288
+ * Adds layout node to word
289
+ */
290
+ addWordLayoutNode(nodeName, styles, rootNode, range)
291
+ {
292
+ let marker = this.editor.selectionRange.createRangeMarker(range),
293
+ node = this.createLayoutNode(nodeName, styles).outerHTML,
294
+ suffix = `</${nodeName}>`,
295
+ prefix = node.substr(0, node.length - suffix.length);
296
+
297
+ this.surroundWord(rootNode, marker, prefix, suffix);
298
+ this.editor.selectionRange.restoreRangeToMarker(marker, range);
299
+
300
+ this.editor.contentManager.cleanUp(rootNode, null, range, marker);
301
+ this.editor.selectionRange.removeRangeMarker(marker);
302
+ range.commonAncestorContainer.normalize();
303
+ }
304
+
305
+ /**
306
+ * Removes layout node for word
307
+ */
308
+ removeWordLayoutNode(nodeName, rootNode, range, clearStyle)
309
+ {
310
+ let marker = this.editor.selectionRange.createRangeMarker(range),
311
+ wordRange = this.createWordRange(rootNode, marker, true);
312
+
313
+ this.removeNodesFromRange(rootNode, nodeName, wordRange.range, true, true, clearStyle);
314
+ wordRange.clear();
315
+
316
+ this.editor.selectionRange.restoreRangeToMarker(marker, range);
317
+ this.editor.contentManager.cleanUp(rootNode, null, range, marker);
318
+ this.editor.selectionRange.removeRangeMarker(marker);
319
+ }
320
+
321
+ /**
322
+ * Creates an empty word
323
+ */
324
+ createEmptyWord(range)
325
+ {
326
+ let textNode = this.editor.getDoc().createTextNode(this.utility.zeroWidthChar + this.utility.zeroWidthChar);
327
+ $lib.insertNode(textNode, true, true, range);
328
+ range.setStart(textNode, 1);
329
+ range.collapse(true);
330
+ }
331
+
332
+ /**
333
+ * Creates a word range
334
+ */
335
+ createWordRange(rootNode, marker, includeSpaces)
336
+ {
337
+ let prefixId = this.utility.newGuid(),
338
+ suffixId = this.utility.newGuid(),
339
+ prefix = '<span id="' + prefixId + '"></span>',
340
+ suffix = '<span id="' + suffixId + '"></span>',
341
+ range;
342
+
343
+ this.surroundWord(rootNode, marker, prefix, suffix, includeSpaces);
344
+ range = this.editor.getDoc().createRange();
345
+ range.setStartAfter($lib('#' + prefixId));
346
+ range.setEndBefore($lib('#' + suffixId));
347
+
348
+ return {
349
+ range: this.editor.selectionRange.ensureTextRange(range),
350
+ clear: function ()
351
+ {
352
+ rootNode.querySelectorAll('#' + prefixId).forEach(el => el.remove());
353
+ rootNode.querySelectorAll('#' + suffixId).forEach(el => el.remove());
354
+ }
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Surrounds a word with the specified prefix and suffix
360
+ */
361
+ surroundWord(rootNode, marker, prefix, suffix, includeSpaces)
362
+ {
363
+ let markerEl = $lib('#' + marker.startId),
364
+ startChar = '\u200c',
365
+ endChar = '\u200d',
366
+ previous = this.getTextNodeWithSpace(markerEl, rootNode, true),
367
+ next = this.getTextNodeWithSpace(markerEl, rootNode);
368
+
369
+ if (!previous.textContent.length)
370
+ previous.textContent = this.utility.zeroWidthChar;
371
+
372
+ if (!next.textContent.length)
373
+ next.textContent = this.utility.zeroWidthChar;
374
+
375
+ previous.textContent = previous.textContent.replace(this.utility.prevWordRegEx, (includeSpaces) ? startChar + '$1$2' : '$1' + startChar + '$2');
376
+ next.textContent = next.textContent.replace(this.utility.nextWordRegEx, (includeSpaces) ? '$1$2' + endChar : '$1' + endChar + '$2');
377
+
378
+ let html = rootNode.innerHTML;
379
+ html = html.replace(/[\u200c]/gm, prefix).replace(/[\u200d]/gm, suffix);
380
+
381
+ rootNode.innerHTML = html;
382
+ }
383
+
384
+ /**
385
+ * Gets the text node with spaces
386
+ */
387
+ getTextNodeWithSpace(node, rootNode, previous)
388
+ {
389
+ let next;
390
+
391
+ node = this.editor.nodeManager.getFirstTextNode(node, rootNode, previous, true);
392
+
393
+ while (!node.textContent.match(/[\s]/gm))
394
+ {
395
+ next = this.editor.nodeManager.getFirstTextNode(this.editor.nodeManager.sibling(node, rootNode, previous, null, true), rootNode, previous, true);
396
+
397
+ if (next && next != node)
398
+ {
399
+ // Stop if a <br> exists between current node and next
400
+ const range = this.editor.getDoc().createRange();
401
+
402
+ if (previous)
403
+ {
404
+ range.setStartAfter(next);
405
+ range.setEndBefore(node);
406
+ }
407
+ else
408
+ {
409
+ range.setStartAfter(node);
410
+ range.setEndBefore(next);
411
+ }
412
+
413
+ if (range.cloneContents().querySelector('br'))
414
+ break;
415
+
416
+ node = next;
417
+ }
418
+ else
419
+ break;
420
+ }
421
+
422
+ return node;
423
+ }
424
+
425
+ /**
426
+ * Surrounds the rootNode range with the specified layout node
427
+ */
428
+ addNodeToRange(rootNode, layoutNode, range, start, end)
429
+ {
430
+ this.editor.selectionRange.splitRange(range, start, end);
431
+
432
+ let innerNodes = this.editor.nodeManager.getElementsByTagName(rootNode, '*'),
433
+ innerRootNodes = innerNodes.filter((node) =>
434
+ {
435
+ return this.editor.nodeManager.isBlock(node) || this.editor.nodeManager.isLayoutRoot(node);
436
+ }),
437
+ siblings = [];
438
+
439
+ if (innerRootNodes.length)
440
+ {
441
+ if (!start && !end)
442
+ {
443
+ siblings = this.editor.nodeManager.getSiblings(rootNode, rootNode.lastChild);
444
+ siblings.push(rootNode.lastChild);
445
+ }
446
+ else
447
+ siblings = this.editor.nodeManager.getSiblings(rootNode, (end) ? range.endContainer.nextSibling : null, (start) ? range.startContainer.previousSibling : null);
448
+
449
+ $lib.each(innerRootNodes, (node) =>
450
+ {
451
+ let index = siblings.indexOf(node);
452
+
453
+ if (index > -1)
454
+ siblings.splice(index, 1);
455
+ });
456
+
457
+ if (siblings.length)
458
+ {
459
+ let lineRange = this.editor.getDoc().createRange();
460
+ lineRange.setStartBefore(siblings[0]);
461
+ lineRange.setEndAfter(siblings[siblings.length - 1]);
462
+
463
+ if (lineRange.toString().length)
464
+ this.editor.selectionRange.surroundRange(lineRange, layoutNode);
465
+ else
466
+ return false;
467
+ }
468
+ }
469
+ else
470
+ {
471
+ let lineRange = this.editor.getDoc().createRange();
472
+
473
+ lineRange.selectNodeContents(rootNode);
474
+
475
+ if (start)
476
+ lineRange.setStart(range.startContainer, range.startOffset);
477
+
478
+ if (end)
479
+ lineRange.setEnd(range.endContainer, range.endOffset);
480
+
481
+ if (lineRange.toString().length || (rootNode.nodeName == 'FIGURE' && layoutNode.nodeName == 'A' && !layoutNode.parentNode))
482
+ this.editor.selectionRange.surroundRange(lineRange, layoutNode);
483
+ else
484
+ return false;
485
+ }
486
+
487
+ return true;
488
+ }
489
+
490
+ /**
491
+ * Remvoes the layout nodes from the range.
492
+ * @param {any} rootNode
493
+ * @param {any} nodeName
494
+ * @param {any} range
495
+ * @param {any} start
496
+ * @param {any} end
497
+ * @param {any} clearStyle
498
+ */
499
+ removeNodesFromRange(rootNode, nodeName, range, start, end, clearStyle)
500
+ {
501
+ let lineRange = this.editor.getDoc().createRange(),
502
+ startContainer = range.startContainer,
503
+ endContainer = range.endContainer,
504
+ startOffset,
505
+ textLength = endContainer.textContent.length,
506
+ layoutNodes = [],
507
+ siblings = new Map(),
508
+ fragment, parent;
509
+
510
+ nodeName = nodeName.toUpperCase();
511
+
512
+ this.editor.selectionRange.splitRange(range, start, end);
513
+ startContainer = range.startContainer;
514
+ endContainer = range.endContainer;
515
+ startOffset = range.startOffset;
516
+ textLength = endContainer.textContent.length;
517
+
518
+ if (start)
519
+ {
520
+ if (nodeName == '*' || clearStyle)
521
+ {
522
+ layoutNodes = this.editor.nodeManager.getLayoutNodes(range.startContainer, rootNode, null, (clearStyle) ? 2 : null);
523
+ parent = layoutNodes[layoutNodes.length - 1];
524
+ }
525
+ else
526
+ {
527
+ parent = this.editor.nodeManager.getLayoutNode(nodeName, range.startContainer, rootNode);
528
+ layoutNodes[0] = parent;
529
+ }
530
+
531
+ if (parent)
532
+ {
533
+ $lib.each(layoutNodes, (node, index) =>
534
+ {
535
+ siblings.set(node.cloneNode(false), this.editor.nodeManager.getSiblings(node, range.startContainer, range.endContainer));
536
+
537
+ this.removeLayout(node, clearStyle);
538
+
539
+ range.setStart(startContainer, startOffset);
540
+ if (end)
541
+ range.setEnd(endContainer, textLength);
542
+ });
543
+ }
544
+ }
545
+
546
+ lineRange.selectNodeContents(rootNode);
547
+
548
+ if (start)
549
+ lineRange.setStart(range.startContainer, range.startOffset);
550
+
551
+ if (end)
552
+ lineRange.setEnd(range.endContainer, range.endContainer.textContent.length);
553
+
554
+ fragment = lineRange.extractContents();
555
+ let temp = $lib.element('', '', '');
556
+ temp.appendChild(fragment);
557
+ this.removeNodes(this.editor.nodeManager.getElementsByTagName(temp, nodeName), nodeName, clearStyle);
558
+
559
+ lineRange.insertNode($lib.extract(temp));
560
+ this.restoreSiblings(siblings);
561
+ }
562
+
563
+ /**
564
+ * Removes the layout nodes.
565
+ * @param {any} nodes
566
+ * @param {any} nodeName
567
+ * @param {any} clearStyle
568
+ */
569
+ removeNodes(nodes, nodeName, clearStyle)
570
+ {
571
+ $lib.each(nodes, (node) =>
572
+ {
573
+ if ((this.editor.nodeManager.isTextDecorationNode(node) || this.editor.nodeManager.isStyleNode(node)) && (nodeName == '*' || node.nodeName == nodeName))
574
+ {
575
+ this.removeLayout(node, clearStyle);
576
+ }
577
+ });
578
+ }
579
+
580
+ /**
581
+ * Removes the layout from the node.
582
+ * @param {any} node
583
+ * @param {any} clearStyle
584
+ */
585
+ removeLayout(node, clearStyle)
586
+ {
587
+ if (clearStyle)
588
+ {
589
+ const hasClearStyle = this.hasStyleKey(node, clearStyle);
590
+
591
+ if (hasClearStyle && this.hasOtherStyleKeys(node, clearStyle))
592
+ node.style.removeProperty(clearStyle);
593
+ else if (hasClearStyle)
594
+ $lib.unsurround(node);
595
+ }
596
+ else
597
+ {
598
+ $lib.unsurround(node);
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Restored nodes that where inside parent layout-node
604
+ */
605
+ restoreSiblings(siblings)
606
+ {
607
+ for (let [layoutNode, nodeList] of siblings)
608
+ {
609
+ $lib.each(nodeList, (node) =>
610
+ {
611
+ $lib.surround(layoutNode.cloneNode(false), node);
612
+ });
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Restores the style node on the specified children
618
+ */
619
+ restoreOriginalStyleNode(node, child)
620
+ {
621
+ let next = child.nextSibling;
622
+ $lib.surround(node, child);
623
+
624
+ while (next)
625
+ {
626
+ child = next;
627
+ next = child.nextSibling;
628
+ node.appendChild(child);
629
+ }
630
+ }
631
+
632
+ /**
633
+ * Returns a value indicating if the style node can be updated
634
+ */
635
+ canUpdateStyleNode(layoutNode, rootNode, clearStyle)
636
+ {
637
+ if (!clearStyle)
638
+ return true;
639
+
640
+ let parentLayoutNodes = this.editor.nodeManager.getLayoutNodes(layoutNode, rootNode, null, 2),
641
+ updateStyleNode = true;
642
+
643
+ $lib.each(parentLayoutNodes, (n) =>
644
+ {
645
+ return (updateStyleNode = !this.hasStyleKey(n, clearStyle));
646
+ });
647
+
648
+ return updateStyleNode;
649
+ }
650
+
651
+ /**
652
+ * Creates layout node.
653
+ * @param {string} nodeName
654
+ * @param {Object} styles
655
+ * @param {*} content
656
+ * @param {Object} attributes
657
+ * @param {Object} properties
658
+ */
659
+ createLayoutNode(nodeName, styles, content, attributes, properties)
660
+ {
661
+ return this.updateNodeStyles($lib.element('', '', nodeName, content, attributes, properties), styles);
662
+ }
663
+
664
+ /**
665
+ * Updates the node styles.
666
+ * @param {Node} node
667
+ * @param {Object} styles
668
+ * @param {boolean} clear
669
+ */
670
+ updateNodeStyles(node, styles, clear)
671
+ {
672
+ if (!$lib.isEmpty(styles))
673
+ node.setAttribute(this.utility.attrPrefix + 'style', '');
674
+
675
+ $lib.each(styles, (value, key) =>
676
+ {
677
+ if (clear)
678
+ node.style.removeProperty(key);
679
+ else
680
+ node.style.setProperty(key, value);
681
+ });
682
+
683
+ return node;
684
+ }
685
+
686
+ /**
687
+ * Gets the styles specified on the node and parent nodes.
688
+ * Uses a cache (Map) to avoid redundant computation.
689
+ * @param {Node} node
690
+ * @param {Map<Node, Object>} [styleCache] Optional cache for memoization
691
+ */
692
+ getLayoutTreeStyles(node, styleCache = new Map())
693
+ {
694
+ if (!node)
695
+ return {};
696
+
697
+ if (styleCache.has(node))
698
+ return styleCache.get(node);
699
+
700
+ let styles = {};
701
+
702
+ let current = node;
703
+ while (current)
704
+ {
705
+ if (this.editor.nodeManager.isStyleNode(current))
706
+ {
707
+ this.iterateStyle(current, (name, value) =>
708
+ {
709
+ if (!(name in styles))
710
+ styles[name] = value;
711
+ });
712
+ }
713
+ current = current.parentNode;
714
+ }
715
+
716
+ styleCache.set(node, styles);
717
+ return styles;
718
+ }
719
+
720
+ /**
721
+ * Updates nodes with the specified style.
722
+ * @param {Node} layoutNode
723
+ * @param {Object} styles
724
+ */
725
+ updateDeeperStyleNodes(layoutNode, styles)
726
+ {
727
+ if ($lib.isEmpty(styles))
728
+ return;
729
+
730
+ let nodes = this.editor.nodeManager.getElementsByTagName(layoutNode, 'span');
731
+
732
+ $lib.each(nodes, (node) =>
733
+ {
734
+ if (this.editor.nodeManager.isStyleNode(node))
735
+ {
736
+ if (this.hasDifferentStyleProp(layoutNode, node))
737
+ this.updateNodeStyles(node, styles, true);
738
+ else
739
+ $lib.unsurround(node);
740
+ }
741
+ });
742
+ }
743
+
744
+ /**
745
+ * Returns a value indicating if the parent and child have equal style properties.
746
+ * @param {Node} parent
747
+ * @param {Node} child
748
+ */
749
+ hasEqualStyles(parent, child)
750
+ {
751
+ if (parent.style.cssText == child.style.cssText)
752
+ return true;
753
+
754
+ let param = { has: false };
755
+
756
+ this.iterateStyle(child, function (param, name, value)
757
+ {
758
+ return (param.has = parent.style[name] == value);
759
+ }.bind(this, param));
760
+
761
+ return param.has;
762
+ }
763
+
764
+ /**
765
+ * Returns a value indicating if the child has different style properties defined than the parent.
766
+ * @param {Node} parent
767
+ * @param {Node} child
768
+ */
769
+ hasDifferentStyleProp(parent, child)
770
+ {
771
+ let param = { has: false };
772
+
773
+ this.iterateStyle(child, function (param, name, value)
774
+ {
775
+ return !(param.has = $lib.isEmpty(parent.style[name]))
776
+ }.bind(this, param));
777
+
778
+ return param.has;
779
+ }
780
+
781
+ /**
782
+ * Returns a value indicating if the node has other style keys than the one specified.
783
+ * @param {Node} node
784
+ * @param {string} key
785
+ */
786
+ hasOtherStyleKeys(node, key)
787
+ {
788
+ let param = { has: false };
789
+
790
+ this.iterateStyle(node, function (param, name, value)
791
+ {
792
+ return !(param.has = name != key);
793
+ }.bind(this, param));
794
+
795
+ return param.has;
796
+ }
797
+
798
+ /**
799
+ * Returns a value indicating if the node has the specified style key.
800
+ * @param {Node} node
801
+ * @param {string} key
802
+ */
803
+ hasStyleKey(node, key)
804
+ {
805
+ let param = { has: false };
806
+
807
+ this.iterateStyle(node, function (param, name, value)
808
+ {
809
+ return !(param.has = name == key);
810
+ }.bind(this, param));
811
+
812
+ return param.has;
813
+ }
814
+
815
+ /**
816
+ * Copies all the styles.
817
+ * @param {Node} from
818
+ * @param {Node} to
819
+ */
820
+ copyStyles(from, to)
821
+ {
822
+ this.iterateStyle(from, (name, value) => { to.style[name] = value; });
823
+ }
824
+
825
+ /**
826
+ * Iterates all style properties and executes the specified function on each property.
827
+ * @param {Node} node
828
+ * @param {Function} fn
829
+ */
830
+ iterateStyle(node, fn)
831
+ {
832
+ if (!node.style)
833
+ return;
834
+
835
+ let props = node.style.cssText.split(';'), prop, name;
836
+
837
+ $lib.each(props, (style) =>
838
+ {
839
+ prop = style.split(/:(.+)/);
840
+ name = $lib.trim(prop[0]);
841
+
842
+ if (!$lib.isEmpty(name))
843
+ return fn(name, $lib.trim(prop[1]));
844
+ });
845
+ }
846
+
847
+ /**
848
+ * Returns a value indicating if the nodes can be merged.
849
+ * @param {Node} sibling
850
+ * @param {Node} node
851
+ */
852
+ canMerge(sibling, node)
853
+ {
854
+ if (!sibling)
855
+ return false;
856
+
857
+ if (this.editor.nodeManager.isStyleNode(sibling) && this.editor.nodeManager.isStyleNode(node))
858
+ return (sibling.style.cssText == node.style.cssText);
859
+ else
860
+ return ((sibling.nodeType != 1 || !sibling.hasAttributes()) && sibling.nodeName == node.nodeName);
861
+ }
862
+
863
+ /**
864
+ * Returns a value indicating if the style property must be cleared.
865
+ * @param {Object} styles
866
+ */
867
+ isClearStyle(styles)
868
+ {
869
+ let styleName = null;
870
+ $lib.each(styles, (v, k) =>
871
+ {
872
+ if (v == null)
873
+ styleName = k;
874
+
875
+ return !styleName;
876
+ });
877
+
878
+ return styleName;
879
+ }
880
+
881
+ /**
882
+ * Returns a value indicating if the range allows content.
883
+ * @param {Range} range
884
+ */
885
+ allowContent(range)
886
+ {
887
+ return (range.startContainer.nodeType != 1 || range.startContainer.contentEditable != 'false');
888
+ }
889
+
890
+ /**
891
+ * Returns a value indicating if the range caret is placed inside a word.
892
+ * @param {Range} range
893
+ * @param {Node} rootNode
894
+ */
895
+ isWordSelection(range, rootNode)
896
+ {
897
+ let startContainer = range.startContainer,
898
+ container = this.editor.nodeManager.getTextContainer(startContainer, rootNode),
899
+ prevChar = this.getPreviousChar(startContainer, container, range),
900
+ nextChar = this.getNextChar(startContainer, container, range);
901
+
902
+ return (!$lib.isEmpty(prevChar) && prevChar.match(/[^\s]/) && !$lib.isEmpty(nextChar) && nextChar.match(/[^\s]/));
903
+ }
904
+
905
+ /**
906
+ * Gets the previous character.
907
+ * @param {Node} textNode
908
+ * @param {Node} parent
909
+ * @param {Range} range
910
+ */
911
+ getPreviousChar(textNode, parent, range)
912
+ {
913
+ let prevNode, prevChar = '';
914
+
915
+ if (range.startOffset > 0)
916
+ prevChar = textNode.textContent[range.startOffset - 1];
917
+ else
918
+ {
919
+ prevNode = this.editor.nodeManager.sibling(textNode, parent, true, null, true);
920
+ prevChar = (prevNode) ? prevNode.textContent[prevNode.textContent.length - 1] : '';
921
+ }
922
+
923
+ return prevChar;
924
+ }
925
+
926
+ /**
927
+ * Gets the next character.
928
+ * @param {Node} textNode
929
+ * @param {Node} parent
930
+ * @param {Range} range
931
+ */
932
+ getNextChar(textNode, parent, range)
933
+ {
934
+ let nextNode, nextChar = '';
935
+
936
+ if (textNode.textContent.length > range.startOffset)
937
+ nextChar = textNode.textContent[range.startOffset];
938
+ else
939
+ {
940
+ nextNode = this.editor.nodeManager.sibling(textNode, parent, false, null, true);
941
+ nextChar = (nextNode) ? nextNode.textContent[0] : '';
942
+ }
943
+
944
+ return nextChar;
945
+ }
946
+ };
947
+
948
+ var Format = componyx.UI.editor_modules.FormatManager;
949
+
950
+ export { Format as default };