@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,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExpressionParser - Parse and compile expressions
|
|
3
|
+
*
|
|
4
|
+
* Converts string expressions into safe, compiled functions
|
|
5
|
+
* Supports field references, operators, functions, and conditionals
|
|
6
|
+
* @private
|
|
7
|
+
*/
|
|
8
|
+
var ExpressionParser = /** @class */ (function () {
|
|
9
|
+
function ExpressionParser() {
|
|
10
|
+
this.supportedFunctions = [
|
|
11
|
+
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
12
|
+
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
13
|
+
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
14
|
+
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS', 'INCLUDES', 'CONTAINS', 'ISNAN', 'SUPPORTEDVALUESOF', 'TOSTRING', 'DATE', 'GETTIME',
|
|
15
|
+
'EVERY', 'SOME'
|
|
16
|
+
];
|
|
17
|
+
this.availableFields = new Map();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initialize parser with available fields for type validation
|
|
21
|
+
*/
|
|
22
|
+
ExpressionParser.prototype.setAvailableFields = function (fields) {
|
|
23
|
+
var _this = this;
|
|
24
|
+
this.availableFields.clear();
|
|
25
|
+
fields.forEach(function (field) {
|
|
26
|
+
_this.availableFields.set(field.name, field);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
ExpressionParser.prototype.validateFieldOperations = function (expression) {
|
|
30
|
+
var errors = [];
|
|
31
|
+
// Skip if no fields available
|
|
32
|
+
if (this.availableFields.size === 0) {
|
|
33
|
+
return errors;
|
|
34
|
+
}
|
|
35
|
+
// Invalid operators for string fields (allow only + for concatenation)
|
|
36
|
+
var stringInvalidOps = ['-', '*', '/', '%', '^'];
|
|
37
|
+
// All operators invalid for date fields
|
|
38
|
+
var dateInvalidOps = ['-', '*', '/', '%', '^', '+'];
|
|
39
|
+
// Track reported operator positions to avoid duplicates
|
|
40
|
+
var reportedOperators = new Set();
|
|
41
|
+
// Extract all field references and their surrounding operators
|
|
42
|
+
var fieldRefPattern = /(\{[^}]+\})/g;
|
|
43
|
+
var match;
|
|
44
|
+
while ((match = fieldRefPattern.exec(expression)) !== null) {
|
|
45
|
+
var fieldRef = match[0];
|
|
46
|
+
var fieldName = fieldRef.slice(1, -1); // Remove { }
|
|
47
|
+
var field = this.availableFields.get(fieldName);
|
|
48
|
+
if (!field)
|
|
49
|
+
continue; // Field not found in available fields, skip validation
|
|
50
|
+
var fieldIndex = match.index;
|
|
51
|
+
var fieldEndIndex = fieldIndex + fieldRef.length;
|
|
52
|
+
// Get context around field reference (including spaces)
|
|
53
|
+
var textBefore = expression.substring(Math.max(0, fieldIndex - 10), fieldIndex);
|
|
54
|
+
var textAfter = expression.substring(fieldEndIndex, Math.min(expression.length, fieldEndIndex + 10));
|
|
55
|
+
// Extract the operator if present (skip spaces)
|
|
56
|
+
var operatorBefore = textBefore.replace(/\s+$/, '').slice(-1);
|
|
57
|
+
var operatorAfter = textAfter.replace(/^\s+/, '').charAt(0);
|
|
58
|
+
// Calculate actual position of operators in the original expression
|
|
59
|
+
var textBeforeTrimmed = textBefore.replace(/\s+$/, '');
|
|
60
|
+
var textAfterTrimmed = textAfter.replace(/^\s+/, '');
|
|
61
|
+
var operatorBeforePos = fieldIndex - (textBefore.length - textBeforeTrimmed.length) - 1;
|
|
62
|
+
var operatorAfterPos = fieldEndIndex + (textAfter.length - textAfterTrimmed.length);
|
|
63
|
+
// For STRING fields: reject all operators except +
|
|
64
|
+
if (field.type === 'string' && !field.format) {
|
|
65
|
+
if (operatorBefore && stringInvalidOps.indexOf(operatorBefore) !== -1) {
|
|
66
|
+
var key = operatorBeforePos + ":" + operatorBefore;
|
|
67
|
+
if (!reportedOperators.has(key)) {
|
|
68
|
+
errors.push("Cannot perform '" + operatorBefore + "' operation on string field '{" + fieldName + "}'");
|
|
69
|
+
reportedOperators.add(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (operatorAfter && stringInvalidOps.indexOf(operatorAfter) !== -1) {
|
|
73
|
+
var key = operatorAfterPos + ":" + operatorAfter;
|
|
74
|
+
if (!reportedOperators.has(key)) {
|
|
75
|
+
errors.push("Cannot perform '" + operatorAfter + "' operation on string field '{" + fieldName + "}'");
|
|
76
|
+
reportedOperators.add(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// For DATE fields: reject ALL operators including +
|
|
81
|
+
if (field.format === 'date' || field.format === 'date-time' || field.format === 'time') {
|
|
82
|
+
if (operatorBefore && dateInvalidOps.indexOf(operatorBefore) !== -1) {
|
|
83
|
+
var key = operatorBeforePos + ":" + operatorBefore;
|
|
84
|
+
if (!reportedOperators.has(key)) {
|
|
85
|
+
errors.push("Cannot perform '" + operatorBefore + "' operation on date field '{" + fieldName + "}'");
|
|
86
|
+
reportedOperators.add(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (operatorAfter && dateInvalidOps.indexOf(operatorAfter) !== -1) {
|
|
90
|
+
var key = operatorAfterPos + ":" + operatorAfter;
|
|
91
|
+
if (!reportedOperators.has(key)) {
|
|
92
|
+
errors.push("Cannot perform '" + operatorAfter + "' operation on date field '{" + fieldName + "}'");
|
|
93
|
+
reportedOperators.add(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return errors;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Validate expression syntax
|
|
102
|
+
*/
|
|
103
|
+
ExpressionParser.prototype.validate = function (expression) {
|
|
104
|
+
var errors = [];
|
|
105
|
+
if (!expression || expression.trim() === '') {
|
|
106
|
+
return { valid: true, errors: [] };
|
|
107
|
+
}
|
|
108
|
+
// Check for unbalanced braces
|
|
109
|
+
var openBraces = (expression.match(/\{/g) || []).length;
|
|
110
|
+
var closeBraces = (expression.match(/\}/g) || []).length;
|
|
111
|
+
if (openBraces !== closeBraces) {
|
|
112
|
+
errors.push('Unbalanced braces in expression');
|
|
113
|
+
}
|
|
114
|
+
// Check for unbalanced parentheses
|
|
115
|
+
var openParens = (expression.match(/\(/g) || []).length;
|
|
116
|
+
var closeParens = (expression.match(/\)/g) || []).length;
|
|
117
|
+
if (openParens !== closeParens) {
|
|
118
|
+
errors.push('Unbalanced parentheses in expression');
|
|
119
|
+
}
|
|
120
|
+
// Check for invalid function calls
|
|
121
|
+
var funcPattern = /(\w+)\s*\(/g;
|
|
122
|
+
var match;
|
|
123
|
+
while ((match = funcPattern.exec(expression)) !== null) {
|
|
124
|
+
var funcName = match[1].toUpperCase();
|
|
125
|
+
// Only validate uppercase-named functions
|
|
126
|
+
if (funcName === funcName.toUpperCase() && this.supportedFunctions.indexOf(funcName) === -1) {
|
|
127
|
+
errors.push("Unknown function: " + funcName);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Validate field operations based on field types
|
|
131
|
+
var fieldOpErrors = this.validateFieldOperations(expression);
|
|
132
|
+
errors.push.apply(errors, fieldOpErrors);
|
|
133
|
+
return {
|
|
134
|
+
valid: errors.length === 0,
|
|
135
|
+
errors: errors
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Compile expression to executable function
|
|
140
|
+
*/
|
|
141
|
+
ExpressionParser.prototype.compile = function (expression, options) {
|
|
142
|
+
if (options === void 0) { options = {}; }
|
|
143
|
+
try {
|
|
144
|
+
// Set available fields for validation if provided
|
|
145
|
+
if (options.availableFields && Array.isArray(options.availableFields)) {
|
|
146
|
+
this.setAvailableFields(options.availableFields);
|
|
147
|
+
}
|
|
148
|
+
// Validate expression before compiling
|
|
149
|
+
var validation = this.validate(expression);
|
|
150
|
+
if (!validation.valid) {
|
|
151
|
+
return function () { return null; };
|
|
152
|
+
}
|
|
153
|
+
// Replace field references with context lookups
|
|
154
|
+
var compiled_1 = expression;
|
|
155
|
+
// Handle field references FIRST (this includes refs inside ${...})
|
|
156
|
+
compiled_1 = this.compileFieldReferences(compiled_1);
|
|
157
|
+
compiled_1 = options.isCustomValidation ? this.compileInputReferences(compiled_1) : compiled_1;
|
|
158
|
+
// Handle bare function calls SECOND (e.g., SUM([...]) without braces)
|
|
159
|
+
compiled_1 = this.compileBareFunctionCalls(compiled_1);
|
|
160
|
+
// Handle math expressions THIRD (now ${} and {} contain already-replaced refs)
|
|
161
|
+
compiled_1 = this.compileMatrixExpressions(compiled_1);
|
|
162
|
+
return function (context) {
|
|
163
|
+
try {
|
|
164
|
+
// This uses Function constructor with controlled context binding
|
|
165
|
+
// which is safer than eval() but still allows expressions
|
|
166
|
+
// Note: compiled expression already uses context['field'] syntax
|
|
167
|
+
var func = new Function('context', "\n try {\n return " + compiled_1 + ";\n } catch (e) {\n return null;\n }\n ");
|
|
168
|
+
return func(context);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return function () { return null; };
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
// Private helpers
|
|
180
|
+
ExpressionParser.prototype.compileMatrixExpressions = function (expression) {
|
|
181
|
+
var compiled = expression;
|
|
182
|
+
var constants = ['PI', 'TRUE', 'FALSE', 'NULL', 'UNDEFINED', 'Math'];
|
|
183
|
+
var functions = [
|
|
184
|
+
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
185
|
+
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
186
|
+
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
187
|
+
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
188
|
+
];
|
|
189
|
+
// Create fresh regex each time to avoid lastIndex issues with g flag
|
|
190
|
+
var mathExprPattern = /\$?\{([^}]+)\}/g;
|
|
191
|
+
// Process ${...} and {...} blocks: replace bare identifiers with context lookups
|
|
192
|
+
compiled = compiled.replace(mathExprPattern, function (_match, content) {
|
|
193
|
+
var transformed = content;
|
|
194
|
+
// STEP 1: Handle function calls first
|
|
195
|
+
// Match function names followed by ( and prefix with context.
|
|
196
|
+
// Use negative lookbehind to avoid double-replacing (e.g., context.SUM)
|
|
197
|
+
functions.forEach(function (fn) {
|
|
198
|
+
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
199
|
+
var funcCallRegex = new RegExp("(?<!context\\.)\\b" + fn + "\\b(?=\\s*\\()", 'g');
|
|
200
|
+
transformed = transformed.replace(funcCallRegex, "context." + fn);
|
|
201
|
+
});
|
|
202
|
+
constants.forEach(function (constant) {
|
|
203
|
+
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
204
|
+
var constantRegex = new RegExp("\\b" + constant + "\\b", 'g');
|
|
205
|
+
transformed = transformed.replace(constantRegex, "context." + constant);
|
|
206
|
+
});
|
|
207
|
+
// Return as parenthesized expression
|
|
208
|
+
return "(" + transformed + ")";
|
|
209
|
+
});
|
|
210
|
+
return compiled;
|
|
211
|
+
};
|
|
212
|
+
ExpressionParser.prototype.compileBareFunctionCalls = function (expression) {
|
|
213
|
+
var compiled = expression;
|
|
214
|
+
var functions = [
|
|
215
|
+
'SUM', 'AVG', 'MIN', 'MAX', 'COUNT',
|
|
216
|
+
'ROUND', 'FLOOR', 'CEIL', 'ABS', 'SQRT',
|
|
217
|
+
'UPPER', 'LOWER', 'CONCAT', 'LENGTH', 'TRIM', 'SUBSTRING',
|
|
218
|
+
'TODAY', 'AGE', 'DATEFORMAT', 'DATEDIFF', 'ADDDAYS', 'SUBDAYS'
|
|
219
|
+
];
|
|
220
|
+
// Handle bare function calls like SUM([...]) that are not inside ${...} or {...}
|
|
221
|
+
// Use negative lookbehind to avoid matching if already preceded by context.
|
|
222
|
+
functions.forEach(function (fn) {
|
|
223
|
+
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
224
|
+
var funcCallRegex = new RegExp("(?<!context\\.)\\b" + fn + "\\b(?=\\s*\\()", 'g');
|
|
225
|
+
compiled = compiled.replace(funcCallRegex, "context." + fn);
|
|
226
|
+
});
|
|
227
|
+
return compiled;
|
|
228
|
+
};
|
|
229
|
+
ExpressionParser.prototype.compileFieldReferences = function (expression) {
|
|
230
|
+
var compiled = expression;
|
|
231
|
+
// Create fresh regex each time to avoid lastIndex issues with g flag
|
|
232
|
+
var fieldRefPattern = /\{([^{}]+)\}/g;
|
|
233
|
+
// Replace {fieldName} with context['fieldName'] or field access
|
|
234
|
+
compiled = compiled.replace(fieldRefPattern, function (match, ref) {
|
|
235
|
+
// Handle escaped braces
|
|
236
|
+
if (ref.startsWith('\\') && ref.endsWith('\\')) {
|
|
237
|
+
return match; // Keep as is
|
|
238
|
+
}
|
|
239
|
+
// Handle nested paths like {customer.name}
|
|
240
|
+
return "context['" + ref.replace(/\./g, '\'].[') + "']";
|
|
241
|
+
});
|
|
242
|
+
return compiled;
|
|
243
|
+
};
|
|
244
|
+
ExpressionParser.prototype.compileInputReferences = function (expression) {
|
|
245
|
+
var compiled = expression;
|
|
246
|
+
// First, protect string literals to avoid replacing 'input' inside strings
|
|
247
|
+
var strings = [];
|
|
248
|
+
var stringProtected = compiled;
|
|
249
|
+
// eslint-disable-next-line security/detect-unsafe-regex
|
|
250
|
+
stringProtected = stringProtected.replace(/(['"`])(?:(?=(\\?))\2.)*?\1/g, function (match) {
|
|
251
|
+
strings.push(match);
|
|
252
|
+
return "__STRING_" + (strings.length - 1) + "__";
|
|
253
|
+
});
|
|
254
|
+
// Case 1: Replace input.property with context['input'].property
|
|
255
|
+
var inputPropertyPattern = /(?<!context\[')\binput\./g;
|
|
256
|
+
stringProtected = stringProtected.replace(inputPropertyPattern, 'context[\'input\'].');
|
|
257
|
+
// Case 2: Replace bare 'input' with context['input']
|
|
258
|
+
// Use negative lookbehind to avoid replacing if already preceded by context.'
|
|
259
|
+
var inputPattern = /(?<!context\[')\binput\b(?!\s*[(\.])/g;
|
|
260
|
+
stringProtected = stringProtected.replace(inputPattern, 'context[\'input\']');
|
|
261
|
+
// Restore string literals
|
|
262
|
+
var result = stringProtected;
|
|
263
|
+
strings.forEach(function (str, idx) {
|
|
264
|
+
result = result.replace("__STRING_" + idx + "__", str);
|
|
265
|
+
});
|
|
266
|
+
return result;
|
|
267
|
+
};
|
|
268
|
+
return ExpressionParser;
|
|
269
|
+
}());
|
|
270
|
+
export { ExpressionParser };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ExpressionEngine } from './expression-engine';
|
|
2
|
+
/**
|
|
3
|
+
* @private
|
|
4
|
+
*/
|
|
5
|
+
export declare class GridExpressions {
|
|
6
|
+
private engine;
|
|
7
|
+
constructor();
|
|
8
|
+
/**
|
|
9
|
+
* Convert a column value to the proper JS type for expression evaluation.
|
|
10
|
+
* Form inputs return strings; numeric/boolean values must be coerced.
|
|
11
|
+
*/
|
|
12
|
+
parseColumnValue(value: any, columnType?: string): any;
|
|
13
|
+
/**
|
|
14
|
+
* Evaluate all expression-bearing columns for a single row.
|
|
15
|
+
* Returns a new row object with calculated values merged in.
|
|
16
|
+
*/
|
|
17
|
+
evaluateRowExpressions(row: Record<string, any>, columns: any[]): Record<string, any>;
|
|
18
|
+
/**
|
|
19
|
+
* Extract the subset of columns that carry a non-empty `expression`.
|
|
20
|
+
*/
|
|
21
|
+
extractCalculatedColumns(columns: any[]): any[];
|
|
22
|
+
getEngine(): ExpressionEngine;
|
|
23
|
+
}
|
|
24
|
+
export default GridExpressions;
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { ExpressionEngine } from './expression-engine';
|
|
13
|
+
/**
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
var GridExpressions = /** @class */ (function () {
|
|
17
|
+
function GridExpressions() {
|
|
18
|
+
this.engine = new ExpressionEngine();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Convert a column value to the proper JS type for expression evaluation.
|
|
22
|
+
* Form inputs return strings; numeric/boolean values must be coerced.
|
|
23
|
+
*/
|
|
24
|
+
GridExpressions.prototype.parseColumnValue = function (value, columnType) {
|
|
25
|
+
// Handle null/undefined/empty
|
|
26
|
+
if (value === null || value === undefined || value === '') {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
// Parse number types
|
|
30
|
+
if (columnType === 'number') {
|
|
31
|
+
var parsed = parseFloat(String(value));
|
|
32
|
+
return isNaN(parsed) ? 0 : parsed;
|
|
33
|
+
}
|
|
34
|
+
// Parse boolean types
|
|
35
|
+
if (columnType === 'boolean') {
|
|
36
|
+
if (typeof value === 'boolean') {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
return value === 'true' || value === '1' || value === 1 || value === true;
|
|
40
|
+
}
|
|
41
|
+
// Return as-is for string and other types
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate all expression-bearing columns for a single row.
|
|
46
|
+
* Returns a new row object with calculated values merged in.
|
|
47
|
+
*/
|
|
48
|
+
GridExpressions.prototype.evaluateRowExpressions = function (row, columns) {
|
|
49
|
+
var _this = this;
|
|
50
|
+
var calculatedRow = __assign({}, row);
|
|
51
|
+
// Find all columns with expressions
|
|
52
|
+
var calculatedColumns = columns.filter(function (col) { return col.expression && col.expression.toString().trim(); });
|
|
53
|
+
var _loop_1 = function (column) {
|
|
54
|
+
if (!column.expression) {
|
|
55
|
+
return "continue";
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
// Prepare row values with proper type conversion
|
|
59
|
+
var typedRow_1 = {};
|
|
60
|
+
columns.forEach(function (col) {
|
|
61
|
+
var value = calculatedRow[col.field];
|
|
62
|
+
typedRow_1[col.field] = _this.parseColumnValue(value, col.type);
|
|
63
|
+
});
|
|
64
|
+
// Evaluate expression with current row as context
|
|
65
|
+
var result = this_1.engine.evaluate(column.expression, typedRow_1, {
|
|
66
|
+
throwOnError: false
|
|
67
|
+
});
|
|
68
|
+
// Update row with calculated value
|
|
69
|
+
calculatedRow[column.field] = result;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
calculatedRow[column.field] = null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var this_1 = this;
|
|
76
|
+
for (var _i = 0, calculatedColumns_1 = calculatedColumns; _i < calculatedColumns_1.length; _i++) {
|
|
77
|
+
var column = calculatedColumns_1[_i];
|
|
78
|
+
_loop_1(column);
|
|
79
|
+
}
|
|
80
|
+
return calculatedRow;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Extract the subset of columns that carry a non-empty `expression`.
|
|
84
|
+
*/
|
|
85
|
+
GridExpressions.prototype.extractCalculatedColumns = function (columns) {
|
|
86
|
+
return columns.filter(function (col) { return col.expression && col.expression.toString().trim(); });
|
|
87
|
+
};
|
|
88
|
+
GridExpressions.prototype.getEngine = function () { return this.engine; };
|
|
89
|
+
return GridExpressions;
|
|
90
|
+
}());
|
|
91
|
+
export { GridExpressions };
|
|
92
|
+
export default GridExpressions;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './checkbox-group';
|
|
2
|
+
export * from './conditional-rule-engine';
|
|
3
|
+
export * from './custom-validation-engine';
|
|
4
|
+
export * from './datagrid-component';
|
|
5
|
+
export * from './json-converter';
|
|
6
|
+
export * from './minmax-rule-engine';
|
|
7
|
+
export * from './option-fetcher';
|
|
8
|
+
export * from './parser';
|
|
9
|
+
export * from './static-html-renderer';
|
|
10
|
+
export * from './utils';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './checkbox-group';
|
|
2
|
+
export * from './conditional-rule-engine';
|
|
3
|
+
export * from './custom-validation-engine';
|
|
4
|
+
export * from './datagrid-component';
|
|
5
|
+
export * from './json-converter';
|
|
6
|
+
export * from './minmax-rule-engine';
|
|
7
|
+
export * from './option-fetcher';
|
|
8
|
+
export * from './parser';
|
|
9
|
+
export * from './static-html-renderer';
|
|
10
|
+
export * from './utils';
|