@syncfusion/ej2-form-renderer 34.2.2
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.
- package/README.md +60 -0
- package/common.d.ts +4 -0
- package/common.js +4 -0
- package/dist/ej2-form-renderer.umd.min.js +11 -0
- package/dist/ej2-form-renderer.umd.min.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es2015.js +6787 -0
- package/dist/es6/ej2-form-renderer.es2015.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es5.js +7055 -0
- package/dist/es6/ej2-form-renderer.es5.js.map +1 -0
- package/dist/global/ej2-form-renderer.min.js +11 -0
- package/dist/global/ej2-form-renderer.min.js.map +1 -0
- package/dist/global/index.d.ts +14 -0
- package/form-renderer.d.ts +4 -0
- package/form-renderer.js +4 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/license +10 -0
- package/package.json +41 -0
- package/src/common/checkbox-group.d.ts +55 -0
- package/src/common/checkbox-group.js +241 -0
- package/src/common/conditional-rule-engine.d.ts +241 -0
- package/src/common/conditional-rule-engine.js +505 -0
- package/src/common/custom-validation-engine.d.ts +57 -0
- package/src/common/custom-validation-engine.js +152 -0
- package/src/common/datagrid-component.d.ts +156 -0
- package/src/common/datagrid-component.js +410 -0
- package/src/common/expressions/dependency-tracker.d.ts +29 -0
- package/src/common/expressions/dependency-tracker.js +137 -0
- package/src/common/expressions/expression-engine.d.ts +94 -0
- package/src/common/expressions/expression-engine.js +514 -0
- package/src/common/expressions/expression-parser.d.ts +40 -0
- package/src/common/expressions/expression-parser.js +270 -0
- package/src/common/expressions/grid-expressions.d.ts +24 -0
- package/src/common/expressions/grid-expressions.js +92 -0
- package/src/common/expressions/index.d.ts +4 -0
- package/src/common/expressions/index.js +4 -0
- package/src/common/index.d.ts +10 -0
- package/src/common/index.js +10 -0
- package/src/common/json-converter.d.ts +945 -0
- package/src/common/json-converter.js +1110 -0
- package/src/common/minmax-rule-engine.d.ts +105 -0
- package/src/common/minmax-rule-engine.js +380 -0
- package/src/common/option-fetcher.d.ts +30 -0
- package/src/common/option-fetcher.js +139 -0
- package/src/common/parser.d.ts +98 -0
- package/src/common/parser.js +788 -0
- package/src/common/static-html-renderer.d.ts +45 -0
- package/src/common/static-html-renderer.js +77 -0
- package/src/common/utils.d.ts +116 -0
- package/src/common/utils.js +484 -0
- package/src/form-renderer/form-renderer-model.d.ts +65 -0
- package/src/form-renderer/form-renderer.d.ts +425 -0
- package/src/form-renderer/form-renderer.js +2277 -0
- package/src/form-renderer/index.d.ts +6 -0
- package/src/form-renderer/index.js +5 -0
- package/src/form-renderer/render-form-field.d.ts +31 -0
- package/src/form-renderer/render-form-field.js +816 -0
- package/src/form-renderer/types/form-schema.d.ts +452 -0
- package/src/form-renderer/types/form-schema.js +32 -0
- package/src/global.js +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/styles/bds-lite.css +599 -0
- package/styles/bds-lite.scss +2 -0
- package/styles/bds.css +599 -0
- package/styles/bds.scss +2 -0
- package/styles/bootstrap-dark-lite.css +608 -0
- package/styles/bootstrap-dark-lite.scss +2 -0
- package/styles/bootstrap-dark.css +608 -0
- package/styles/bootstrap-dark.scss +2 -0
- package/styles/bootstrap-lite.css +595 -0
- package/styles/bootstrap-lite.scss +2 -0
- package/styles/bootstrap.css +595 -0
- package/styles/bootstrap.scss +2 -0
- package/styles/bootstrap4-lite.css +595 -0
- package/styles/bootstrap4-lite.scss +2 -0
- package/styles/bootstrap4.css +595 -0
- package/styles/bootstrap4.scss +2 -0
- package/styles/bootstrap5-dark-lite.css +595 -0
- package/styles/bootstrap5-dark-lite.scss +2 -0
- package/styles/bootstrap5-dark.css +595 -0
- package/styles/bootstrap5-dark.scss +2 -0
- package/styles/bootstrap5-lite.css +595 -0
- package/styles/bootstrap5-lite.scss +2 -0
- package/styles/bootstrap5.3-lite.css +679 -0
- package/styles/bootstrap5.3-lite.scss +2 -0
- package/styles/bootstrap5.3.css +679 -0
- package/styles/bootstrap5.3.scss +2 -0
- package/styles/bootstrap5.css +595 -0
- package/styles/bootstrap5.scss +2 -0
- package/styles/fabric-dark-lite.css +608 -0
- package/styles/fabric-dark-lite.scss +2 -0
- package/styles/fabric-dark.css +608 -0
- package/styles/fabric-dark.scss +2 -0
- package/styles/fabric-lite.css +595 -0
- package/styles/fabric-lite.scss +2 -0
- package/styles/fabric.css +595 -0
- package/styles/fabric.scss +2 -0
- package/styles/fluent-dark-lite.css +595 -0
- package/styles/fluent-dark-lite.scss +2 -0
- package/styles/fluent-dark.css +595 -0
- package/styles/fluent-dark.scss +2 -0
- package/styles/fluent-lite.css +595 -0
- package/styles/fluent-lite.scss +2 -0
- package/styles/fluent.css +595 -0
- package/styles/fluent.scss +2 -0
- package/styles/fluent2-lite.css +681 -0
- package/styles/fluent2-lite.scss +2 -0
- package/styles/fluent2.css +681 -0
- package/styles/fluent2.scss +2 -0
- package/styles/form-renderer/_all.scss +2 -0
- package/styles/form-renderer/_bds-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap4-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5.3-definition.scss +8 -0
- package/styles/form-renderer/_fabric-dark-definition.scss +8 -0
- package/styles/form-renderer/_fabric-definition.scss +8 -0
- package/styles/form-renderer/_fluent-dark-definition.scss +8 -0
- package/styles/form-renderer/_fluent-definition.scss +8 -0
- package/styles/form-renderer/_fluent2-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-light-definition.scss +8 -0
- package/styles/form-renderer/_layout.scss +480 -0
- package/styles/form-renderer/_material-dark-definition.scss +8 -0
- package/styles/form-renderer/_material-definition.scss +8 -0
- package/styles/form-renderer/_material3-dark-definition.scss +8 -0
- package/styles/form-renderer/_material3-definition.scss +8 -0
- package/styles/form-renderer/_mixin.scss +26 -0
- package/styles/form-renderer/_tailwind-dark-definition.scss +8 -0
- package/styles/form-renderer/_tailwind-definition.scss +8 -0
- package/styles/form-renderer/_tailwind3-definition.scss +8 -0
- package/styles/form-renderer/_theme.scss +69 -0
- package/styles/form-renderer/_variables.scss +2 -0
- package/styles/form-renderer/bds.css +599 -0
- package/styles/form-renderer/bds.scss +2 -0
- package/styles/form-renderer/bootstrap-dark.css +608 -0
- package/styles/form-renderer/bootstrap-dark.scss +2 -0
- package/styles/form-renderer/bootstrap.css +595 -0
- package/styles/form-renderer/bootstrap.scss +2 -0
- package/styles/form-renderer/bootstrap4.css +595 -0
- package/styles/form-renderer/bootstrap4.scss +2 -0
- package/styles/form-renderer/bootstrap5-dark.css +595 -0
- package/styles/form-renderer/bootstrap5-dark.scss +2 -0
- package/styles/form-renderer/bootstrap5.3.css +679 -0
- package/styles/form-renderer/bootstrap5.3.scss +2 -0
- package/styles/form-renderer/bootstrap5.css +595 -0
- package/styles/form-renderer/bootstrap5.scss +2 -0
- package/styles/form-renderer/fabric-dark.css +608 -0
- package/styles/form-renderer/fabric-dark.scss +2 -0
- package/styles/form-renderer/fabric.css +595 -0
- package/styles/form-renderer/fabric.scss +2 -0
- package/styles/form-renderer/fluent-dark.css +595 -0
- package/styles/form-renderer/fluent-dark.scss +2 -0
- package/styles/form-renderer/fluent.css +595 -0
- package/styles/form-renderer/fluent.scss +2 -0
- package/styles/form-renderer/fluent2.css +681 -0
- package/styles/form-renderer/fluent2.scss +2 -0
- package/styles/form-renderer/highcontrast-light.css +595 -0
- package/styles/form-renderer/highcontrast-light.scss +2 -0
- package/styles/form-renderer/highcontrast.css +608 -0
- package/styles/form-renderer/highcontrast.scss +2 -0
- package/styles/form-renderer/material-dark.css +608 -0
- package/styles/form-renderer/material-dark.scss +2 -0
- package/styles/form-renderer/material.css +595 -0
- package/styles/form-renderer/material.scss +2 -0
- package/styles/form-renderer/material3-dark.css +677 -0
- package/styles/form-renderer/material3-dark.scss +2 -0
- package/styles/form-renderer/material3.css +679 -0
- package/styles/form-renderer/material3.scss +2 -0
- package/styles/form-renderer/tailwind-dark.css +595 -0
- package/styles/form-renderer/tailwind-dark.scss +2 -0
- package/styles/form-renderer/tailwind.css +595 -0
- package/styles/form-renderer/tailwind.scss +2 -0
- package/styles/form-renderer/tailwind3.css +669 -0
- package/styles/form-renderer/tailwind3.scss +2 -0
- package/styles/highcontrast-light-lite.css +595 -0
- package/styles/highcontrast-light-lite.scss +2 -0
- package/styles/highcontrast-light.css +595 -0
- package/styles/highcontrast-light.scss +2 -0
- package/styles/highcontrast-lite.css +608 -0
- package/styles/highcontrast-lite.scss +2 -0
- package/styles/highcontrast.css +608 -0
- package/styles/highcontrast.scss +2 -0
- package/styles/material-dark-lite.css +608 -0
- package/styles/material-dark-lite.scss +2 -0
- package/styles/material-dark.css +608 -0
- package/styles/material-dark.scss +2 -0
- package/styles/material-lite.css +595 -0
- package/styles/material-lite.scss +2 -0
- package/styles/material.css +595 -0
- package/styles/material.scss +2 -0
- package/styles/material3-dark-lite.css +677 -0
- package/styles/material3-dark-lite.scss +2 -0
- package/styles/material3-dark.css +677 -0
- package/styles/material3-dark.scss +2 -0
- package/styles/material3-lite.css +679 -0
- package/styles/material3-lite.scss +2 -0
- package/styles/material3.css +679 -0
- package/styles/material3.scss +2 -0
- package/styles/tailwind-dark-lite.css +595 -0
- package/styles/tailwind-dark-lite.scss +2 -0
- package/styles/tailwind-dark.css +595 -0
- package/styles/tailwind-dark.scss +2 -0
- package/styles/tailwind-lite.css +595 -0
- package/styles/tailwind-lite.scss +2 -0
- package/styles/tailwind.css +595 -0
- package/styles/tailwind.scss +2 -0
- package/styles/tailwind3-lite.css +669 -0
- package/styles/tailwind3-lite.scss +2 -0
- package/styles/tailwind3.css +669 -0
- package/styles/tailwind3.scss +2 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { ExpressionParser } from './expression-parser';
|
|
13
|
+
import { DependencyTracker } from './dependency-tracker';
|
|
14
|
+
import { Internationalization } from '@syncfusion/ej2-base';
|
|
15
|
+
/**
|
|
16
|
+
* ExpressionEngine - Core evaluation engine
|
|
17
|
+
*
|
|
18
|
+
* Safely evaluates user-defined expressions against form state
|
|
19
|
+
* Supports field references, math operations, functions, and conditionals
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
var ExpressionEngine = /** @class */ (function () {
|
|
23
|
+
function ExpressionEngine() {
|
|
24
|
+
this.evaluationCache = new Map();
|
|
25
|
+
this.compiledFunctions = new Map();
|
|
26
|
+
this.parser = new ExpressionParser();
|
|
27
|
+
this.dependencyTracker = new DependencyTracker();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Evaluate expression against form values
|
|
31
|
+
*/
|
|
32
|
+
ExpressionEngine.prototype.evaluate = function (expression, formValues, options, isCustomValidation) {
|
|
33
|
+
if (options === void 0) { options = {}; }
|
|
34
|
+
if (isCustomValidation === void 0) { isCustomValidation = false; }
|
|
35
|
+
try {
|
|
36
|
+
if (!expression || expression.trim() === '') {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
var _a = options.timeout, timeout = _a === void 0 ? 5000 : _a, _b = options.maxDepth, maxDepth = _b === void 0 ? 10 : _b, _c = options.locale, locale = _c === void 0 ? 'en-US' : _c, _d = options.throwOnError, throwOnError = _d === void 0 ? false : _d;
|
|
40
|
+
// Check cache
|
|
41
|
+
var cacheKey = this.getCacheKey(expression, formValues);
|
|
42
|
+
if (!isCustomValidation && this.evaluationCache.has(cacheKey)) {
|
|
43
|
+
return this.evaluationCache.get(cacheKey);
|
|
44
|
+
}
|
|
45
|
+
// Get or compile function
|
|
46
|
+
var fn = this.compiledFunctions.get(expression);
|
|
47
|
+
if (!fn) {
|
|
48
|
+
fn = this.parser.compile(expression, {
|
|
49
|
+
timeout: timeout,
|
|
50
|
+
maxDepth: maxDepth,
|
|
51
|
+
locale: locale,
|
|
52
|
+
isCustomValidation: isCustomValidation
|
|
53
|
+
});
|
|
54
|
+
this.compiledFunctions.set(expression, fn);
|
|
55
|
+
}
|
|
56
|
+
// Execute with context
|
|
57
|
+
var context = this.buildContext(formValues);
|
|
58
|
+
var startTime = Date.now();
|
|
59
|
+
var result = fn(context);
|
|
60
|
+
// Validate timeout
|
|
61
|
+
if (Date.now() - startTime > timeout) {
|
|
62
|
+
var error = new Error("Expression evaluation exceeded timeout of " + timeout + "ms");
|
|
63
|
+
if (throwOnError)
|
|
64
|
+
throw error;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// Cache result
|
|
68
|
+
this.evaluationCache.set(cacheKey, result);
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (options.throwOnError) {
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Extract field names referenced in expression
|
|
80
|
+
*/
|
|
81
|
+
ExpressionEngine.prototype.extractDependencies = function (expression) {
|
|
82
|
+
if (!expression)
|
|
83
|
+
return [];
|
|
84
|
+
return this.dependencyTracker.extractFieldReferences(expression);
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Build dependency graph for all expressions
|
|
88
|
+
*/
|
|
89
|
+
ExpressionEngine.prototype.buildDependencyGraph = function (components) {
|
|
90
|
+
var _this = this;
|
|
91
|
+
var graph = {};
|
|
92
|
+
components.forEach(function (comp) {
|
|
93
|
+
if (comp.expressionValue) {
|
|
94
|
+
var deps = _this.extractDependencies(comp.expressionValue);
|
|
95
|
+
graph[comp.name] = deps;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return graph;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Detect circular dependencies
|
|
102
|
+
*/
|
|
103
|
+
ExpressionEngine.prototype.detectCircularDependencies = function (graph) {
|
|
104
|
+
var visited = new Set();
|
|
105
|
+
var recursionStack = new Set();
|
|
106
|
+
var cycles = [];
|
|
107
|
+
var visit = function (node, path) {
|
|
108
|
+
visited.add(node);
|
|
109
|
+
recursionStack.add(node);
|
|
110
|
+
path.push(node);
|
|
111
|
+
var deps = graph[node] || [];
|
|
112
|
+
for (var _i = 0, deps_1 = deps; _i < deps_1.length; _i++) {
|
|
113
|
+
var dep = deps_1[_i];
|
|
114
|
+
if (!visited.has(dep)) {
|
|
115
|
+
visit(dep, path.slice());
|
|
116
|
+
}
|
|
117
|
+
else if (recursionStack.has(dep)) {
|
|
118
|
+
var cycleStart = path.indexOf(dep);
|
|
119
|
+
cycles.push(path.slice(cycleStart).concat([dep]));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
recursionStack.delete(node);
|
|
123
|
+
};
|
|
124
|
+
for (var _i = 0, _a = Object.keys(graph); _i < _a.length; _i++) {
|
|
125
|
+
var node = _a[_i];
|
|
126
|
+
if (!visited.has(node)) {
|
|
127
|
+
visit(node, []);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return cycles;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Get topological order for evaluation
|
|
134
|
+
*/
|
|
135
|
+
ExpressionEngine.prototype.getTopologicalOrder = function (graph) {
|
|
136
|
+
var visited = new Set();
|
|
137
|
+
var order = [];
|
|
138
|
+
var visit = function (node) {
|
|
139
|
+
if (visited.has(node))
|
|
140
|
+
return;
|
|
141
|
+
visited.add(node);
|
|
142
|
+
var deps = graph[node] || [];
|
|
143
|
+
for (var _i = 0, deps_2 = deps; _i < deps_2.length; _i++) {
|
|
144
|
+
var dep = deps_2[_i];
|
|
145
|
+
visit(dep);
|
|
146
|
+
}
|
|
147
|
+
order.push(node);
|
|
148
|
+
};
|
|
149
|
+
for (var _i = 0, _a = Object.keys(graph); _i < _a.length; _i++) {
|
|
150
|
+
var node = _a[_i];
|
|
151
|
+
visit(node);
|
|
152
|
+
}
|
|
153
|
+
return order;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Clear internal caches
|
|
157
|
+
*/
|
|
158
|
+
ExpressionEngine.prototype.clearCache = function () {
|
|
159
|
+
this.evaluationCache.clear();
|
|
160
|
+
this.compiledFunctions.clear();
|
|
161
|
+
};
|
|
162
|
+
// Private helper methods
|
|
163
|
+
ExpressionEngine.prototype.getCacheKey = function (expression, formValues) {
|
|
164
|
+
// Simple cache key (in production, use better hashing)
|
|
165
|
+
return expression + ":" + JSON.stringify(formValues);
|
|
166
|
+
};
|
|
167
|
+
ExpressionEngine.prototype.buildContext = function (formValues) {
|
|
168
|
+
var intl = new Internationalization();
|
|
169
|
+
var dFormatter = intl.getDateFormat({
|
|
170
|
+
format: 'MM/dd/yyyy',
|
|
171
|
+
type: 'date'
|
|
172
|
+
});
|
|
173
|
+
return __assign({}, formValues, {
|
|
174
|
+
// Constants
|
|
175
|
+
PI: Math.PI, TRUE: true, FALSE: false, NULL: null, UNDEFINED: undefined, Math: Math,
|
|
176
|
+
// Aggregate Functions
|
|
177
|
+
SUM: function (values) { return Array.isArray(values) ? values.reduce(function (a, b) { return Number(a) + Number(b); }, 0) : 0; }, AVG: function (values) {
|
|
178
|
+
if (!Array.isArray(values) || values.length === 0)
|
|
179
|
+
return 0;
|
|
180
|
+
var sum = values.reduce(function (a, b) { return Number(a) + Number(b); }, 0);
|
|
181
|
+
return sum / values.length;
|
|
182
|
+
}, MIN: function (values) { return Array.isArray(values) && values.length > 0 ? Math.min.apply(Math, values.map(Number)) : 0; }, MAX: function (values) { return Array.isArray(values) && values.length > 0 ? Math.max.apply(Math, values.map(Number)) : 0; }, COUNT: function (values) { return Array.isArray(values) ? values.length : 0; },
|
|
183
|
+
// Math Functions
|
|
184
|
+
ROUND: function (value, decimals) {
|
|
185
|
+
if (decimals === void 0) { decimals = 0; }
|
|
186
|
+
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
|
187
|
+
}, FLOOR: Math.floor, CEIL: Math.ceil, ABS: Math.abs, SQRT: Math.sqrt,
|
|
188
|
+
// String Functions
|
|
189
|
+
UPPER: function (text) { return String(text).toUpperCase(); }, LOWER: function (text) { return String(text).toLowerCase(); }, CONCAT: function () {
|
|
190
|
+
var strings = [];
|
|
191
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
192
|
+
strings[_i] = arguments[_i];
|
|
193
|
+
}
|
|
194
|
+
return strings.map(String).join('');
|
|
195
|
+
}, LENGTH: function (text) { return String(text).length; }, TRIM: function (text) { return String(text).trim(); }, SUBSTRING: function (text, start, length) {
|
|
196
|
+
return length !== undefined
|
|
197
|
+
? String(text).substr(start, length)
|
|
198
|
+
: String(text).substr(start);
|
|
199
|
+
},
|
|
200
|
+
// Date Functions
|
|
201
|
+
TODAY: function () {
|
|
202
|
+
var value = dFormatter(new Date());
|
|
203
|
+
return value;
|
|
204
|
+
}, AGE: function (birthDate) {
|
|
205
|
+
var birth = typeof birthDate === 'string' ? new Date(birthDate) : birthDate;
|
|
206
|
+
var today = new Date();
|
|
207
|
+
var age = today.getFullYear() - birth.getFullYear();
|
|
208
|
+
var monthDiff = today.getMonth() - birth.getMonth();
|
|
209
|
+
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
|
210
|
+
age--;
|
|
211
|
+
}
|
|
212
|
+
return age;
|
|
213
|
+
}, DATEFORMAT: function (date, format) {
|
|
214
|
+
var d = typeof date === 'string' ? new Date(date) : date;
|
|
215
|
+
var formatter = intl.getDateFormat({
|
|
216
|
+
format: format,
|
|
217
|
+
type: 'date'
|
|
218
|
+
});
|
|
219
|
+
// Simple date format implementation
|
|
220
|
+
return formatter(d);
|
|
221
|
+
}, DATEDIFF: function (date1, date2) {
|
|
222
|
+
var d1 = typeof date1 === 'string' ? new Date(date1) : date1;
|
|
223
|
+
var d2 = typeof date2 === 'string' ? new Date(date2) : date2;
|
|
224
|
+
return Math.floor((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
|
|
225
|
+
},
|
|
226
|
+
// Add days to a date (for date arithmetic like {date} + 5)
|
|
227
|
+
ADDDAYS: function (date, days) {
|
|
228
|
+
var d = typeof date === 'string' ? new Date(date) : new Date(date);
|
|
229
|
+
d.setDate(d.getDate() + Number(days));
|
|
230
|
+
return dFormatter(d);
|
|
231
|
+
},
|
|
232
|
+
// Add days to a date (for date arithmetic like {date} + 5)
|
|
233
|
+
SUBDAYS: function (date, days) {
|
|
234
|
+
var d = typeof date === 'string' ? new Date(date) : new Date(date);
|
|
235
|
+
d.setDate(d.getDate() - Number(days));
|
|
236
|
+
return dFormatter(d);
|
|
237
|
+
} });
|
|
238
|
+
};
|
|
239
|
+
return ExpressionEngine;
|
|
240
|
+
}());
|
|
241
|
+
export { ExpressionEngine };
|
|
242
|
+
/**
|
|
243
|
+
* ExpressionValidator - Validates expressions before runtime
|
|
244
|
+
*
|
|
245
|
+
* Checks syntax, validates field references, and detects circular dependencies
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
var ExpressionValidator = /** @class */ (function () {
|
|
249
|
+
function ExpressionValidator() {
|
|
250
|
+
this.supportedFunctions = [
|
|
251
|
+
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
252
|
+
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
253
|
+
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
254
|
+
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
255
|
+
];
|
|
256
|
+
this.keywords = ['PI', 'TRUE', 'FALSE', 'NULL', 'UNDEFINED', 'and', 'or', 'in'];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Validate expression against available fields
|
|
260
|
+
*/
|
|
261
|
+
ExpressionValidator.prototype.validate = function (expression, availableFields) {
|
|
262
|
+
var errors = [];
|
|
263
|
+
if (!expression || expression.trim() === '') {
|
|
264
|
+
return errors; // Empty expression is valid
|
|
265
|
+
}
|
|
266
|
+
// Check syntax
|
|
267
|
+
var syntaxErrors = this.validateSyntax(expression);
|
|
268
|
+
errors.push.apply(errors, syntaxErrors);
|
|
269
|
+
// Check field references
|
|
270
|
+
var fieldErrors = this.validateFieldReferences(expression, availableFields);
|
|
271
|
+
errors.push.apply(errors, fieldErrors);
|
|
272
|
+
// Check function calls
|
|
273
|
+
var funcErrors = this.validateFunctionCalls(expression);
|
|
274
|
+
errors.push.apply(errors, funcErrors);
|
|
275
|
+
// Check field operations based on types
|
|
276
|
+
var opErrors = this.validateFieldOperations(expression, availableFields);
|
|
277
|
+
errors.push.apply(errors, opErrors);
|
|
278
|
+
return errors;
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Validate field operations - check for invalid operators on string/date types
|
|
282
|
+
*/
|
|
283
|
+
ExpressionValidator.prototype.validateFieldOperations = function (expression, availableFields) {
|
|
284
|
+
var errors = [];
|
|
285
|
+
// Skip if not field objects with type info
|
|
286
|
+
if (!Array.isArray(availableFields) || availableFields.length === 0) {
|
|
287
|
+
return errors;
|
|
288
|
+
}
|
|
289
|
+
var firstField = availableFields[0];
|
|
290
|
+
if (typeof firstField === 'string') {
|
|
291
|
+
return errors; // No type information available
|
|
292
|
+
}
|
|
293
|
+
// Build field map from objects
|
|
294
|
+
var fieldMap = new Map();
|
|
295
|
+
availableFields.forEach(function (field) {
|
|
296
|
+
if (field && typeof field === 'object' && field.name) {
|
|
297
|
+
fieldMap.set(field.name, field);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
if (fieldMap.size === 0)
|
|
301
|
+
return errors;
|
|
302
|
+
// Track reported operator positions to avoid duplicates
|
|
303
|
+
var reportedOperators = new Set();
|
|
304
|
+
// Check each field reference for invalid operations
|
|
305
|
+
var fieldRefPattern = /(\{[^}]+\})/g;
|
|
306
|
+
var match;
|
|
307
|
+
// Invalid operators for string fields (allow only + for concatenation)
|
|
308
|
+
var stringInvalidOps = ['-', '*', '/', '%', '^'];
|
|
309
|
+
// Operators invalid for date fields (allow + for date arithmetic with numbers)
|
|
310
|
+
var dateInvalidOps = ['-', '*', '/', '%', '^', '+'];
|
|
311
|
+
while ((match = fieldRefPattern.exec(expression)) !== null) {
|
|
312
|
+
var fieldRef = match[0];
|
|
313
|
+
var fieldName = fieldRef.slice(1, -1); // Remove { }
|
|
314
|
+
var field = fieldMap.get(fieldName);
|
|
315
|
+
if (!field)
|
|
316
|
+
continue;
|
|
317
|
+
var fieldIndex = match.index;
|
|
318
|
+
var fieldEndIndex = fieldIndex + fieldRef.length;
|
|
319
|
+
// Get context around field reference (including spaces)
|
|
320
|
+
var textBefore = expression.substring(Math.max(0, fieldIndex - 10), fieldIndex);
|
|
321
|
+
var textAfter = expression.substring(fieldEndIndex, Math.min(expression.length, fieldEndIndex + 10));
|
|
322
|
+
// Extract the operator if present (skip spaces)
|
|
323
|
+
var operatorBefore = textBefore.replace(/\s+$/, '').slice(-1);
|
|
324
|
+
var operatorAfter = textAfter.replace(/^\s+/, '').charAt(0);
|
|
325
|
+
// Calculate actual position of operators in the original expression
|
|
326
|
+
var textBeforeTrimmed = textBefore.replace(/\s+$/, '');
|
|
327
|
+
var textAfterTrimmed = textAfter.replace(/^\s+/, '');
|
|
328
|
+
var operatorBeforePos = fieldIndex - (textBefore.length - textBeforeTrimmed.length) - 1;
|
|
329
|
+
var operatorAfterPos = fieldEndIndex + (textAfter.length - textAfterTrimmed.length);
|
|
330
|
+
// For STRING fields (without format)
|
|
331
|
+
if (field.type === 'string' && !field.format) {
|
|
332
|
+
if (operatorBefore && stringInvalidOps.indexOf(operatorBefore) !== -1) {
|
|
333
|
+
var key = operatorBeforePos + ":" + operatorBefore;
|
|
334
|
+
if (!reportedOperators.has(key)) {
|
|
335
|
+
errors.push({
|
|
336
|
+
type: 'InvalidOperation',
|
|
337
|
+
message: "Cannot perform '" + operatorBefore + "' operation on string field '{" + fieldName + "}'"
|
|
338
|
+
});
|
|
339
|
+
reportedOperators.add(key);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (operatorAfter && stringInvalidOps.indexOf(operatorAfter) !== -1) {
|
|
343
|
+
var key = operatorAfterPos + ":" + operatorAfter;
|
|
344
|
+
if (!reportedOperators.has(key)) {
|
|
345
|
+
errors.push({
|
|
346
|
+
type: 'InvalidOperation',
|
|
347
|
+
message: "Cannot perform '" + operatorAfter + "' operation on string field '{" + fieldName + "}'"
|
|
348
|
+
});
|
|
349
|
+
reportedOperators.add(key);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// For DATE fields - allow + with numbers, reject other operators
|
|
354
|
+
if (field.format === 'date' || field.format === 'date-time' || field.format === 'time') {
|
|
355
|
+
if (operatorBefore && dateInvalidOps.indexOf(operatorBefore) !== -1) {
|
|
356
|
+
var key = operatorBeforePos + ":" + operatorBefore;
|
|
357
|
+
if (!reportedOperators.has(key)) {
|
|
358
|
+
errors.push({
|
|
359
|
+
type: 'InvalidOperation',
|
|
360
|
+
message: "Cannot perform '" + operatorBefore + "' operation on date field '{" + fieldName + "}'"
|
|
361
|
+
});
|
|
362
|
+
reportedOperators.add(key);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (operatorAfter && dateInvalidOps.indexOf(operatorAfter) !== -1) {
|
|
366
|
+
var key = operatorAfterPos + ":" + operatorAfter;
|
|
367
|
+
if (!reportedOperators.has(key)) {
|
|
368
|
+
errors.push({
|
|
369
|
+
type: 'InvalidOperation',
|
|
370
|
+
message: "Cannot perform '" + operatorAfter + "' operation on date field '{" + fieldName + "}'"
|
|
371
|
+
});
|
|
372
|
+
reportedOperators.add(key);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Allow + only when combined with numbers or number fields
|
|
376
|
+
if (operatorAfter === '+') {
|
|
377
|
+
var operandAfter = textAfter.replace(/^\s+/, '').substring(1).trim();
|
|
378
|
+
var isNumberField = /^\{[^}]+\}/.test(operandAfter) && fieldMap.get(operandAfter.slice(1, -1)) && fieldMap.get(operandAfter.slice(1, -1)).type === 'number';
|
|
379
|
+
var isNumberLiteral = /^\d+/.test(operandAfter);
|
|
380
|
+
if (!isNumberField && !isNumberLiteral) {
|
|
381
|
+
var key = operatorAfterPos + ":" + operatorAfter;
|
|
382
|
+
if (!reportedOperators.has(key)) {
|
|
383
|
+
errors.push({
|
|
384
|
+
type: 'InvalidOperation',
|
|
385
|
+
message: "Date field '{" + fieldName + "}' can only be used with +number or +{numberField}, not '" + operatorAfter + operandAfter.substring(0, 10) + "'"
|
|
386
|
+
});
|
|
387
|
+
reportedOperators.add(key);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (operatorBefore === '+') {
|
|
392
|
+
var trimmedBefore = textBefore.replace(/\s+$/, '');
|
|
393
|
+
var operandBefore = trimmedBefore.substring(0, trimmedBefore.length - 1).trim();
|
|
394
|
+
var isNumberField = /\{[^}]+\}$/.test(operandBefore) && fieldMap.get(operandBefore.slice(operandBefore.lastIndexOf('{') + 1, -1)) && fieldMap.get(operandBefore.slice(operandBefore.lastIndexOf('{') + 1, -1)).type === 'number';
|
|
395
|
+
var isNumberLiteral = /\d+$/.test(operandBefore);
|
|
396
|
+
if (!isNumberField && !isNumberLiteral) {
|
|
397
|
+
var key = operatorBeforePos + ":" + operatorBefore;
|
|
398
|
+
if (!reportedOperators.has(key)) {
|
|
399
|
+
errors.push({
|
|
400
|
+
type: 'InvalidOperation',
|
|
401
|
+
message: "Date field '{" + fieldName + "}' can only be used with +number or +{numberField}"
|
|
402
|
+
});
|
|
403
|
+
reportedOperators.add(key);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return errors;
|
|
410
|
+
};
|
|
411
|
+
ExpressionValidator.prototype.validateSyntax = function (expression) {
|
|
412
|
+
var errors = [];
|
|
413
|
+
// Check balanced braces
|
|
414
|
+
var openBraces = (expression.match(/\{/g) || []).length;
|
|
415
|
+
var closeBraces = (expression.match(/\}/g) || []).length;
|
|
416
|
+
if (openBraces !== closeBraces) {
|
|
417
|
+
errors.push({
|
|
418
|
+
type: 'SyntaxError',
|
|
419
|
+
message: 'Unbalanced braces in expression'
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
// Check balanced parentheses
|
|
423
|
+
var openParens = (expression.match(/\(/g) || []).length;
|
|
424
|
+
var closeParens = (expression.match(/\)/g) || []).length;
|
|
425
|
+
if (openParens !== closeParens) {
|
|
426
|
+
errors.push({
|
|
427
|
+
type: 'SyntaxError',
|
|
428
|
+
message: 'Unbalanced parentheses in expression'
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
// Check balanced $ brackets
|
|
432
|
+
var openDollar = (expression.match(/\$\{/g) || []).length;
|
|
433
|
+
var closeDollar = (expression.match(/\}/g) || []).length;
|
|
434
|
+
if (openDollar > closeDollar) {
|
|
435
|
+
errors.push({
|
|
436
|
+
type: 'SyntaxError',
|
|
437
|
+
message: 'Unbalanced ${ } in expression'
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
return errors;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* @private
|
|
444
|
+
*/
|
|
445
|
+
ExpressionValidator.prototype.validateFieldReferences = function (expression, availableFields) {
|
|
446
|
+
var errors = [];
|
|
447
|
+
// Skip field validation if availableFields is not properly populated
|
|
448
|
+
// This allows expressions to work at runtime even if we can't validate field names at editor time
|
|
449
|
+
if (!availableFields || availableFields.length === 0) {
|
|
450
|
+
return errors;
|
|
451
|
+
}
|
|
452
|
+
var fieldRefPattern = /\{([^{}]+)\}/g;
|
|
453
|
+
var match;
|
|
454
|
+
// Handle both string[] and AvailableField[] formats
|
|
455
|
+
var availableFieldsSet;
|
|
456
|
+
if (typeof availableFields[0] === 'string') {
|
|
457
|
+
availableFieldsSet = new Set(availableFields);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
availableFieldsSet = new Set(availableFields.map(function (f) { return f.name; }));
|
|
461
|
+
}
|
|
462
|
+
while ((match = fieldRefPattern.exec(expression)) !== null) {
|
|
463
|
+
var ref = match[1].trim();
|
|
464
|
+
// Skip if it's a function call
|
|
465
|
+
if (this.isFunctionCall(ref))
|
|
466
|
+
continue;
|
|
467
|
+
// Skip if it's a math expression (contains operators or numbers)
|
|
468
|
+
// This distinguishes between {fieldName} and ${5 + 3}
|
|
469
|
+
if (this.isMathExpression(ref))
|
|
470
|
+
continue;
|
|
471
|
+
// Extract field name from nested paths (e.g., "customer.name" -> "customer")
|
|
472
|
+
var fieldName = ref.split(/[.\[\]]/)[0].trim();
|
|
473
|
+
// Check if field exists
|
|
474
|
+
if (fieldName && this.keywords.indexOf(fieldName.toUpperCase()) === -1) {
|
|
475
|
+
if (!availableFieldsSet.has(fieldName)) {
|
|
476
|
+
errors.push({
|
|
477
|
+
type: 'UndefinedField',
|
|
478
|
+
message: "Field '" + fieldName + "' is not defined in the form",
|
|
479
|
+
field: fieldName
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return errors;
|
|
485
|
+
};
|
|
486
|
+
ExpressionValidator.prototype.validateFunctionCalls = function (expression) {
|
|
487
|
+
var errors = [];
|
|
488
|
+
var funcPattern = /(\w+)\s*\(/g;
|
|
489
|
+
var match;
|
|
490
|
+
while ((match = funcPattern.exec(expression)) !== null) {
|
|
491
|
+
var funcName = match[1];
|
|
492
|
+
// Only validate uppercase functions (user-defined lowercase functions might be allowed)
|
|
493
|
+
if (funcName === funcName.toUpperCase() && this.supportedFunctions.indexOf(funcName) === -1) {
|
|
494
|
+
errors.push({
|
|
495
|
+
type: 'InvalidFunction',
|
|
496
|
+
message: "Unknown function: '" + funcName + "'. Supported functions: " + this.supportedFunctions.join(', ')
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return errors;
|
|
501
|
+
};
|
|
502
|
+
ExpressionValidator.prototype.isFunctionCall = function (ref) {
|
|
503
|
+
return /^\w+\s*\(/.test(ref);
|
|
504
|
+
};
|
|
505
|
+
ExpressionValidator.prototype.isMathExpression = function (ref) {
|
|
506
|
+
// Check if the expression contains math operators, numbers, or other non-field-name patterns
|
|
507
|
+
// Math expressions like: "5 + 3", "qty * 2", "age > 18", "sum([1,2,3])"
|
|
508
|
+
// should be skipped, while field references like "fieldName" or "customer.name" should be validated
|
|
509
|
+
var mathOperatorPattern = /[\+\-\*\/\%\=\<\>\!\&\|\^\~\s\d]/;
|
|
510
|
+
return mathOperatorPattern.test(ref);
|
|
511
|
+
};
|
|
512
|
+
return ExpressionValidator;
|
|
513
|
+
}());
|
|
514
|
+
export { ExpressionValidator };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field type information for validation
|
|
3
|
+
* @private
|
|
4
|
+
*/
|
|
5
|
+
export interface AvailableField {
|
|
6
|
+
name: string;
|
|
7
|
+
type: string;
|
|
8
|
+
format?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* ExpressionParser - Parse and compile expressions
|
|
12
|
+
*
|
|
13
|
+
* Converts string expressions into safe, compiled functions
|
|
14
|
+
* Supports field references, operators, functions, and conditionals
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
export declare class ExpressionParser {
|
|
18
|
+
private supportedFunctions;
|
|
19
|
+
private availableFields;
|
|
20
|
+
/**
|
|
21
|
+
* Initialize parser with available fields for type validation
|
|
22
|
+
*/
|
|
23
|
+
setAvailableFields(fields: AvailableField[]): void;
|
|
24
|
+
private validateFieldOperations;
|
|
25
|
+
/**
|
|
26
|
+
* Validate expression syntax
|
|
27
|
+
*/
|
|
28
|
+
validate(expression: string): {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
errors: string[];
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Compile expression to executable function
|
|
34
|
+
*/
|
|
35
|
+
compile(expression: string, options?: any): any;
|
|
36
|
+
private compileMatrixExpressions;
|
|
37
|
+
private compileBareFunctionCalls;
|
|
38
|
+
private compileFieldReferences;
|
|
39
|
+
private compileInputReferences;
|
|
40
|
+
}
|