@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,351 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ await (async function ()
3
+ {
4
+ await import(`${$UI.getScriptResourcePath('Base.Bindary.Core')}`);
5
+ const core = componyx.bindary_modules.core;
6
+
7
+ const dataUpdater = componyx.bindary_modules.dataUpdater =
8
+ {
9
+ initUpdateRules(rules, orgRules)
10
+ {
11
+ rules = (rules) ? ($lib.isArray(rules)) ? rules : [rules] : [{}];
12
+
13
+ if (orgRules && !$lib.isEmpty(orgRules[0]))
14
+ rules = orgRules.concat(rules);
15
+
16
+ return rules;
17
+ },
18
+
19
+ update(dataMsg, mustUpdateView)
20
+ {
21
+ if (dataMsg)
22
+ {
23
+ if (core.fireEvent('preDataUpdate', [dataMsg]) === false) return;
24
+
25
+ let update = dataUpdater.updateData.bind(dataUpdater, dataMsg);
26
+
27
+ core.autoUpdateView = false;
28
+
29
+ // data update
30
+ if (core.dataLocks['#'])
31
+ {
32
+ core.dataLocks['#'].push(update); // store in cache to postpone data-update until unlock is called
33
+ }
34
+ else
35
+ {
36
+ update();
37
+ core.fireEvent('postDataUpdate');
38
+ }
39
+
40
+ core.autoUpdateView = true;
41
+
42
+ // detect error and fire possible callbacks
43
+ if (!dataUpdater.advance(dataMsg))
44
+ {
45
+ if (core.routeCallbackId)
46
+ core.postRender(); // load route cancelled, but call postRender for possible event listeners
47
+
48
+ return;
49
+ }
50
+ }
51
+
52
+ if (mustUpdateView !== false) $bindary.updateView(false);
53
+ },
54
+
55
+ advance(dataMsg)
56
+ {
57
+ let advance = true, callbackResult, msg;
58
+
59
+ if (!$lib.isArray(dataMsg)) dataMsg = [dataMsg];
60
+
61
+ for (let index = 0; index < dataMsg.length; ++index)
62
+ {
63
+ msg = dataMsg[index];
64
+
65
+ if (msg.isError)
66
+ {
67
+ if (core.fireEvent('dataError', [msg]) === false)
68
+ advance = false;
69
+
70
+ core.clearPendingUpdate();
71
+ }
72
+
73
+ if (msg.callbackId && msg.callbackId !== core.routeCallbackId)
74
+ {
75
+ if (core.callbacks[msg.callbackId])
76
+ {
77
+ callbackResult = core.callbacks[msg.callbackId](msg);
78
+ delete core.callbacks[msg.callbackId];
79
+
80
+ if (callbackResult === false) advance = false;
81
+ } else advance = false; // callback does not exist anymore
82
+ }
83
+ }
84
+
85
+ if (core.dataLocks['#']) advance = false;
86
+
87
+ return advance;
88
+ },
89
+
90
+ updateData(dataMsg)
91
+ {
92
+ if (!$lib.isArray(dataMsg)) dataMsg = [dataMsg];
93
+
94
+ for (let index = 0; index < dataMsg.length; ++index)
95
+ {
96
+ let data = dataMsg[index].data;
97
+
98
+ if (data)
99
+ {
100
+ dataUpdater.prepareContainer(core.dataLabel[0], data);
101
+
102
+ if ($lib.isEmpty(dataMsg[index].routeIndex) || dataMsg[index].routeIndex === $bindary.routeIndex)
103
+ {
104
+ dataUpdater.prepareContainer(core.dataLabel[1], data);
105
+ core.cache.routeData[$bindary.routeIndex] = $bindary.routeData;
106
+ }
107
+
108
+ if ($lib.isEmpty(dataMsg[index].routePath) || dataMsg[index].routePath === $bindary.routePath)
109
+ {
110
+ dataUpdater.prepareContainer(core.dataLabel[2], data);
111
+ core.cache.viewData[$bindary.routePath] = $bindary.viewData;
112
+ }
113
+ }
114
+ }
115
+ },
116
+
117
+ prepareContainer(name, data)
118
+ {
119
+ let container = data[name];
120
+ if (!container) return;
121
+
122
+ let update = dataUpdater.updateContainer.bind(dataUpdater, name, container, data[name + 'Rules']);
123
+
124
+ if (core.dataLocks[name])
125
+ {
126
+ core.dataLocks[name].push(update);
127
+ }
128
+ else
129
+ {
130
+ update();
131
+ }
132
+ },
133
+
134
+ updateContainer(name, sourceContainer, rulesContainer)
135
+ {
136
+ let target, source, rules, path, curTargetContainer, update,
137
+ targetContainer = $bindary[name];
138
+
139
+ for (let key in sourceContainer)
140
+ {
141
+ curTargetContainer = targetContainer;
142
+ source = sourceContainer[key];
143
+ rules = rulesContainer ? rulesContainer[key] : null;
144
+ path = rules ? rules.path : null;
145
+
146
+ if (rules)
147
+ {
148
+ if (!$lib.isEmpty(rules.routePath) && name === core.dataLabel[2])
149
+ {
150
+ curTargetContainer = core.cache.viewData[rules.routePath];
151
+ }
152
+ else if (!$lib.isEmpty(rules.routeIndex) && name === core.dataLabel[1])
153
+ {
154
+ curTargetContainer = core.cache.routeData[rules.routeIndex];
155
+ }
156
+ }
157
+
158
+ target = path ? dataUpdater.getTargetModel(path, curTargetContainer) : { key, obj: curTargetContainer };
159
+ path = name + core.dksc + (path || key);
160
+
161
+ update = dataUpdater.updateModel.bind(dataUpdater, target.key, target.obj, source, rules, path);
162
+
163
+ if (core.dataLocks[path])
164
+ {
165
+ core.dataLocks[path].push(update);
166
+ }
167
+ else
168
+ {
169
+ update();
170
+ }
171
+ }
172
+ },
173
+
174
+ updateModel(key, target, source, rules, path)
175
+ {
176
+ let isArray = $lib.isArray(source),
177
+ defaultUpdateRule = $bindary.defaultUpdateRule;
178
+
179
+ core.observed = false;
180
+
181
+ if (!(source == null && rules && rules.ignoreNullValues))
182
+ {
183
+ if (!rules)
184
+ rules = { update: defaultUpdateRule };
185
+ else if ($lib.isEmpty(rules.update))
186
+ rules.update = defaultUpdateRule;
187
+
188
+ const equalityCheck = (rules && typeof rules.equalityCheck === 'boolean') ? rules.equalityCheck : $bindary.equalityCheck;
189
+
190
+ if (equalityCheck && dataUpdater.deepEqual(target[key], source))
191
+ return;
192
+
193
+ if ($lib.isEmpty(target[key]) || rules.update === false || !(isArray || $lib.isPlainObject(source)))
194
+ {
195
+ target[key] = source;
196
+ }
197
+ else if (isArray)
198
+ {
199
+ dataUpdater.updateArray(target[key], source, rules);
200
+ }
201
+ else
202
+ {
203
+ $lib.clone(target[key], source, true, true, true, null, null, null, rules.omitKeys);
204
+ }
205
+
206
+ if (!core.observed)
207
+ dataUpdater.dataChange(path, rules, isArray);
208
+ }
209
+ },
210
+
211
+ deepEqual(a, b, seen = new WeakMap())
212
+ {
213
+ if (a === b) return true;
214
+
215
+ if (a instanceof Date && b instanceof Date)
216
+ return a.getTime() === b.getTime();
217
+
218
+ if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null)
219
+ return false;
220
+
221
+ if (seen.has(a))
222
+ {
223
+ if (seen.get(a).has(b))
224
+ {
225
+ return true; // already compared these objects
226
+ }
227
+ }
228
+ else
229
+ {
230
+ seen.set(a, new WeakSet());
231
+ }
232
+ seen.get(a).add(b);
233
+
234
+ let keysA = Object.keys(a);
235
+ let keysB = Object.keys(b);
236
+
237
+ if (keysA.length !== keysB.length) return false;
238
+
239
+ for (let key of keysA)
240
+ {
241
+ if (!keysB.includes(key)) return false;
242
+ if (!dataUpdater.deepEqual(a[key], b[key], seen)) return false;
243
+ }
244
+
245
+ return true;
246
+ },
247
+
248
+ getTargetModel(path, target)
249
+ {
250
+ let pathKey, index;
251
+
252
+ while (path)
253
+ {
254
+ index = path.indexOf(core.dksc);
255
+ pathKey = index > -1 ? path.substr(0, index) : path;
256
+
257
+ if (index > -1)
258
+ {
259
+ target = target[pathKey] = target[pathKey] || {};
260
+ path = index > -1 ? path.substr(index + core.dksc.length) : null;
261
+ } else
262
+ {
263
+ path = null;
264
+ }
265
+ }
266
+
267
+ return { key: pathKey, obj: target };
268
+ },
269
+
270
+ dataChange(path, rules, isArray)
271
+ {
272
+ if (!core.dataChanges[path]) core.dataChanges[path] = [];
273
+ if (rules && isArray) core.dataChanges[path].push(rules);
274
+ },
275
+
276
+ updateArray(target, source, rules)
277
+ {
278
+ // the rules are required for updating repeat-elements within the view
279
+ // removeIndex, addIndex and addCount must be updated in the rules because an array can be updated multiple times (same target, different source array)
280
+
281
+ if (!rules)
282
+ rules = {};
283
+
284
+ if ($lib.isEmpty(rules.removeIndex) && rules.removeCount > 0)
285
+ rules.removeIndex = target.length - rules.removeCount; // set correct index when removeCount is specified
286
+
287
+ if (rules.removeCount > 0)
288
+ target.splice(rules.removeIndex, rules.removeCount);
289
+
290
+ if ($lib.isEmpty(rules.addCount))
291
+ rules.addCount = 0;
292
+
293
+ if (rules.updateKey)
294
+ {
295
+ let updateKey = rules.updateKey,
296
+ add = [];
297
+
298
+ rules.keys = [];
299
+
300
+ if (rules.removeByKeyValues)
301
+ {
302
+ $lib.each(rules.removeByKeyValues, function (value)
303
+ {
304
+ let index = $lib.indexOf(target, function (i) { return (i[updateKey] == value); });
305
+
306
+ if (index > -1)
307
+ {
308
+ rules.keys.push(index.toString());
309
+ target.splice(index, 1);
310
+ }
311
+ });
312
+ }
313
+
314
+ $lib.each(source, function (srcItem)
315
+ {
316
+ let index = $lib.indexOf(target, function (i) { return (i[updateKey] === srcItem[updateKey]); });
317
+
318
+ if (index > -1)
319
+ {
320
+ rules.keys.push(index.toString());
321
+ target[index] = srcItem;
322
+ }
323
+ else
324
+ add.push(srcItem);
325
+ });
326
+
327
+ if (add.length)
328
+ dataUpdater.extendTargetArray(target, add, rules);
329
+ }
330
+ else
331
+ dataUpdater.extendTargetArray(target, source, rules);
332
+ },
333
+
334
+ extendTargetArray(target, source, rules)
335
+ {
336
+ rules.addCount = source.length;
337
+
338
+ if (rules.addCount > 0 && $lib.isEmpty(rules.addIndex))
339
+ target.push.apply(target, source);
340
+ else if (rules.addCount > 0)
341
+ target.splice.apply(target, [rules.addIndex, 0].concat(source));
342
+
343
+ if ($lib.isEmpty(rules.addIndex) && rules.addCount > 0)
344
+ rules.addIndex = target.length - rules.addCount;
345
+ }
346
+ };
347
+ })();
348
+
349
+ var DataUpdater = componyx.bindary_modules.dataUpdater;
350
+
351
+ export { DataUpdater as default };
@@ -0,0 +1,347 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ await (async function ()
3
+ {
4
+ await import(`${$UI.getScriptResourcePath('Base.Bindary.Core')}`);
5
+ componyx.bindary_modules.core;
6
+
7
+ componyx.bindary_modules = componyx.bindary_modules || {};
8
+ componyx.bindary_modules.ExpressionEngine = class ExpressionEngine
9
+ {
10
+ constructor()
11
+ {
12
+ this.resolveRegex = /^[\$A-Za-z_][\w.@\[\]]*(\(\))?/;
13
+ this.allowedCharsRegex = /^[|&+\-*/().,\s<>=!?:@_\$[\]{}_A-Za-z0-9'".]+$/;
14
+ this.allowedMathFunctions = ['abs', 'ceil', 'floor', 'round', 'max', 'min', 'trunc', 'sqrt', 'pow', 'log', 'exp', 'random'];
15
+ this.allowedStringFunctions = ['toLowerCase', 'toUpperCase', 'trim', 'replace', 'includes', 'indexOf', 'startsWith', 'endsWith', 'slice', 'substring'];
16
+ }
17
+
18
+ /**
19
+ * Compiles an expression string containing `{{ }}` placeholders into a function.
20
+ * Supports:
21
+ * - Multiple `{{ ... }}` placeholders within the string.
22
+ * - Expressions inside `{{ }}`, including basic operators, custom resolver tokens, and a whitelist of Math and String methods.
23
+ * - Literal text outside placeholders is preserved as string literals.
24
+ *
25
+ * Supported Math methods: 'abs', 'ceil', 'floor', 'round', 'max', 'min', 'trunc', 'sqrt', 'pow', 'log', 'exp', 'random'
26
+ * Supported String methods: 'toLowerCase', 'toUpperCase', 'trim', 'replace', 'includes', 'indexOf', 'startsWith', 'endsWith', 'slice', 'substring'
27
+ * @private
28
+ * @param {string} expr - The expression string to compile.
29
+ * @param {function(string, number): any} tokenHandler - Invoked at compile time for each custom token found.
30
+ * Receives the token string as first argument and the start index of the token in the source string as second argument.
31
+ * @returns {function(function(string): any): any} A function that takes a resolver callback to provide token values at runtime.
32
+ * @example
33
+ * const exprFn = evaluate("Hello {{ String.toUpperCase(name) + '!' }} Your high score is {{ Math.max(a, b) + ' points' }}.");
34
+ * const result = exprFn(token => {
35
+ * const values = { name: "Joe", a: 5, b: 10 };
36
+ * return values[token];
37
+ * });
38
+ * console.log(result); // "Hello JOE! Your high score is 10 points."
39
+ */
40
+ evaluate(expr, tokenHandler)
41
+ {
42
+ const allowedCharsRegex = this.allowedCharsRegex,
43
+ allowedMathFunctions = this.allowedMathFunctions,
44
+ allowedStringFunctions = this.allowedStringFunctions;
45
+
46
+ // --------------------------------------------------------------
47
+ // Extract balanced parentheses content, used for Math/Strings
48
+ // --------------------------------------------------------------
49
+ const extractParentheses = (text, startIndex) =>
50
+ {
51
+ if (text[startIndex] !== '(') return null;
52
+
53
+ let depth = 1;
54
+ let index = startIndex + 1;
55
+
56
+ while (index < text.length && depth > 0)
57
+ {
58
+ if (text[index] === '(') depth++;
59
+ else if (text[index] === ')') depth--;
60
+ index++;
61
+ }
62
+
63
+ if (depth !== 0) return null;
64
+ return { content: text.slice(startIndex + 1, index - 1), endIndex: index - 1 };
65
+ };
66
+
67
+ // --------------------------------------------------------------
68
+ // Inner expression processor:
69
+ // Tokenizes Math/Strings/DataKeys/Text recursively.
70
+ // --------------------------------------------------------------
71
+ const processExpression = (text, exprStartIndex) =>
72
+ {
73
+ text = text.trim();
74
+ if (!text) return null;
75
+
76
+ // Full whitelist check
77
+ if (!allowedCharsRegex.test(text))
78
+ return null;
79
+
80
+ let index = 0;
81
+ let out = '';
82
+
83
+ while (index < text.length)
84
+ {
85
+ let ch = text[index];
86
+
87
+ // ----------------------------------------
88
+ // Whitespace - skip it
89
+ // ----------------------------------------
90
+ if (/\s/.test(ch))
91
+ {
92
+ index++;
93
+ continue;
94
+ }
95
+
96
+ // ----------------------------------------
97
+ // Numeric literals
98
+ // ----------------------------------------
99
+ if (/\d/.test(ch))
100
+ {
101
+ out += ch;
102
+ index++;
103
+ continue;
104
+ }
105
+
106
+ // ----------------------------------------
107
+ // Quoted string literal
108
+ // ----------------------------------------
109
+ if (ch === '"' || ch === "'")
110
+ {
111
+ const quote = ch;
112
+ let start = index++;
113
+
114
+ while (index < text.length)
115
+ {
116
+ if (text[index] === '\\')
117
+ {
118
+ index += 2;
119
+ continue;
120
+ }
121
+ if (text[index] === quote)
122
+ break;
123
+ index++;
124
+ }
125
+
126
+ if (index >= text.length) return null;
127
+
128
+ out += text.slice(start, ++index);
129
+ continue;
130
+ }
131
+
132
+ // ----------------------------------------
133
+ // Math or String function call
134
+ // ----------------------------------------
135
+ let fnMatch = text.slice(index).match(/^(Math|String)\.([A-Za-z_]\w*)/);
136
+ if (fnMatch)
137
+ {
138
+ const type = fnMatch[1],
139
+ fnName = fnMatch[2],
140
+ allowedFns = type === 'Math' ? allowedMathFunctions : allowedStringFunctions;
141
+
142
+ if (!allowedFns.includes(fnName)) return null;
143
+
144
+ out += (type === 'Math') ? `${type}.${fnName}` : `String.prototype.${fnName}.call`;
145
+ index += fnMatch[0].length;
146
+
147
+ if (text[index] !== '(') return null;
148
+
149
+ const paren = extractParentheses(text, index);
150
+ if (!paren) return null;
151
+
152
+ const inner = processExpression(paren.content, exprStartIndex + index + 1);
153
+ if (inner === null) return null;
154
+
155
+ if (type === 'Math')
156
+ out += `(${inner})`;
157
+ else
158
+ out += `( ${inner} )`;
159
+
160
+ index = paren.endIndex + 1;
161
+ continue;
162
+ }
163
+
164
+ // ----------------------------------------
165
+ // Booleans
166
+ // ----------------------------------------
167
+ const afterTrue = text[index + 4];
168
+
169
+ if (text.startsWith("true", index) && (afterTrue === undefined || !/[\w]/.test(afterTrue)))
170
+ {
171
+ out += "true";
172
+ index += 4;
173
+ continue;
174
+ }
175
+
176
+ const afterFalse = text[index + 5];
177
+
178
+ if (text.startsWith("false", index) && (afterFalse === undefined || !/[\w]/.test(afterFalse)))
179
+ {
180
+ out += "false";
181
+ index += 5;
182
+ continue;
183
+ }
184
+
185
+ // ----------------------------------------
186
+ // null
187
+ // ----------------------------------------
188
+ if (text.startsWith("null", index) && !/\w/.test(text[index + 4] ?? ''))
189
+ {
190
+ out += "null";
191
+ index += 4;
192
+ continue;
193
+ }
194
+
195
+ // ----------------------------------------
196
+ // Empty Object
197
+ // ----------------------------------------
198
+ if (ch === '{')
199
+ {
200
+ if (text[index + 1] === '}')
201
+ {
202
+ out += '{}';
203
+ index += 2;
204
+ continue;
205
+ }
206
+ return null;
207
+ }
208
+
209
+ // ----------------------------------------
210
+ // Empty Array
211
+ // ----------------------------------------
212
+ if (ch === '[')
213
+ {
214
+ if (text[index + 1] === ']')
215
+ {
216
+ out += '[]';
217
+ index += 2;
218
+ continue;
219
+ }
220
+ return null;
221
+ }
222
+
223
+ // ----------------------------------------
224
+ // Custom Resolver match
225
+ // ----------------------------------------
226
+ let resolverMatch = text.slice(index).match(this.resolveRegex);
227
+ if (resolverMatch)
228
+ {
229
+ const token = resolverMatch[0];
230
+
231
+ if (tokenHandler)
232
+ tokenHandler(token, exprStartIndex + index);
233
+
234
+ out += `resolver(${JSON.stringify(token)})`;
235
+ index += token.length;
236
+ continue;
237
+ }
238
+
239
+ // ----------------------------------------
240
+ // Operators
241
+ // ----------------------------------------
242
+ if ("&|+-*/%()<>!=?:@,.".includes(ch))
243
+ {
244
+ out += ch;
245
+ index++;
246
+ continue;
247
+ }
248
+
249
+
250
+ return null;
251
+ }
252
+
253
+ return out;
254
+ };
255
+
256
+ // --------------------------------------------------------------
257
+ // MAIN PROCESS splits literal text and {{ expressions }}
258
+ // --------------------------------------------------------------
259
+ function process(text, index = 0, inPlaceholder = false)
260
+ {
261
+ let output = "";
262
+ let inString = null;
263
+ let escape = false;
264
+
265
+ while (index < text.length)
266
+ {
267
+ let ch = text[index];
268
+
269
+ // Handle escapes
270
+ if (ch === '\\' && !escape)
271
+ {
272
+ escape = true;
273
+ output += JSON.stringify(ch) + " + "; // output the backslash as a literal
274
+ index++;
275
+ continue;
276
+ }
277
+
278
+ if (escape)
279
+ {
280
+ escape = false;
281
+ output += JSON.stringify(ch) + " + "; // output escaped char as literal, but skip string boundary detection
282
+ index++;
283
+ continue;
284
+ }
285
+
286
+ // Detect string boundaries
287
+ if (!escape && (ch === '"' || ch === "'"))
288
+ {
289
+ if (inString === null) inString = ch;
290
+ else if (inString === ch) inString = null;
291
+ }
292
+
293
+ if (inPlaceholder)
294
+ {
295
+ if (inString === null && ch === '}' && text[index + 1] === '}') // Find end of placeholder
296
+ {
297
+ return { value: output, index: index + 2 }; // Return processed content and new index after }}
298
+ }
299
+ // Otherwise accumulate inside placeholder
300
+ output += ch;
301
+ index++;
302
+ }
303
+ else if (ch === '{' && text[index + 1] === '{') // Outside placeholder: detect {{ to enter placeholder mode
304
+ {
305
+ // Recursive call to process inside placeholder
306
+ const innerResult = process(text, index + 2, true);
307
+ const exprStartIndex = index + 2;
308
+ if (!innerResult) return null; // invalid
309
+
310
+ const exprContent = innerResult.value;
311
+ index = innerResult.index;
312
+
313
+ const processed = processExpression(exprContent, exprStartIndex);
314
+ if (processed === null) return null;
315
+
316
+ output += "(" + processed + ")" + " + ";
317
+ continue;
318
+ }
319
+ else
320
+ {
321
+ output += JSON.stringify(ch) + " + "; // Default: literal text outside placeholders
322
+ index++;
323
+ }
324
+ }
325
+
326
+ // If we are inside a placeholder but no closing }}, invalid expression
327
+ if (inPlaceholder) return null;
328
+
329
+ // Return accumulated string and index (end of text)
330
+ return { value: output, index: index };
331
+ }
332
+
333
+ if (!expr)
334
+ return null;
335
+
336
+ const result = process(expr, 0, false);
337
+ if (!result) return null;
338
+
339
+ let output = result.value.replace(/\s*\+\s*$/, ""); // Trim trailing " + "
340
+ return Function('resolver', `"use strict"; return (${output})`); // Run JS-safe parsed code and accept resolver fn
341
+ }
342
+ };
343
+ })();
344
+
345
+ var ExpressionEngine = componyx.bindary_modules.expressionEngine;
346
+
347
+ export { ExpressionEngine as default };