@aemforms/af-core 0.22.23 → 0.22.26
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/lib/browser/afb-events.js +151 -0
- package/lib/browser/afb-runtime.js +3620 -0
- package/lib/cjs/index.cjs +1876 -267
- package/lib/esm/BaseNode.js +454 -26
- package/lib/esm/Checkbox.js +1 -37
- package/lib/esm/CheckboxGroup.js +1 -38
- package/lib/esm/Container.js +9 -30
- package/lib/esm/DateField.js +2 -38
- package/lib/esm/Field.d.ts +2 -2
- package/lib/esm/Field.js +26 -62
- package/lib/esm/Fieldset.js +3 -36
- package/lib/esm/FileObject.js +1 -23
- package/lib/esm/FileUpload.js +1 -34
- package/lib/esm/Form.d.ts +1 -1
- package/lib/esm/Form.js +8 -40
- package/lib/esm/FormInstance.js +5 -53
- package/lib/esm/FormMetaData.js +1 -26
- package/lib/esm/InstanceManager.js +8 -35
- package/lib/esm/Node.js +1 -25
- package/lib/esm/Scriptable.js +2 -29
- package/lib/esm/controller/EventQueue.js +1 -23
- package/lib/esm/controller/Events.d.ts +1 -1
- package/lib/esm/controller/Events.js +19 -41
- package/lib/esm/controller/Logger.d.ts +2 -2
- package/lib/esm/controller/Logger.js +1 -23
- package/lib/esm/data/DataGroup.js +1 -24
- package/lib/esm/data/DataValue.js +1 -23
- package/lib/esm/data/EmptyDataValue.js +1 -23
- package/lib/esm/index.js +21 -55
- package/lib/esm/rules/FunctionRuntime.d.ts +3 -3
- package/lib/esm/rules/FunctionRuntime.js +6 -31
- package/lib/esm/rules/RuleEngine.js +1 -30
- package/lib/esm/types/Json.d.ts +16 -16
- package/lib/esm/types/Json.js +2 -24
- package/lib/esm/types/Model.d.ts +4 -4
- package/lib/esm/types/Model.js +1 -23
- package/lib/esm/types/index.js +2 -22
- package/lib/esm/utils/DataRefParser.d.ts +2 -2
- package/lib/esm/utils/DataRefParser.js +6 -31
- package/lib/esm/utils/Fetch.d.ts +1 -1
- package/lib/esm/utils/Fetch.js +2 -24
- package/lib/esm/utils/FormCreationUtils.js +2 -40
- package/lib/esm/utils/FormUtils.js +8 -33
- package/lib/esm/utils/JsonUtils.js +11 -34
- package/lib/esm/utils/LogUtils.js +1 -23
- package/lib/esm/utils/SchemaUtils.js +2 -24
- package/lib/esm/utils/TranslationUtils.d.ts +1 -1
- package/lib/esm/utils/TranslationUtils.js +9 -32
- package/lib/esm/utils/ValidationUtils.d.ts +4 -4
- package/lib/esm/utils/ValidationUtils.js +4 -30
- package/package.json +2 -14
- package/lib/esm/BaseNode-dc59ab07.js +0 -478
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,25 +1,169 @@
|
|
|
1
|
-
/*************************************************************************
|
|
2
|
-
* ADOBE CONFIDENTIAL
|
|
3
|
-
* ___________________
|
|
4
|
-
*
|
|
5
|
-
* Copyright 2022 Adobe
|
|
6
|
-
* All Rights Reserved.
|
|
7
|
-
*
|
|
8
|
-
* NOTICE: All information contained herein is, and remains
|
|
9
|
-
* the property of Adobe and its suppliers, if any. The intellectual
|
|
10
|
-
* and technical concepts contained herein are proprietary to Adobe
|
|
11
|
-
* and its suppliers and are protected by all applicable intellectual
|
|
12
|
-
* property laws, including trade secret and copyright laws.
|
|
13
|
-
* Dissemination of this information or reproduction of this material
|
|
14
|
-
* is strictly forbidden unless prior written permission is obtained
|
|
15
|
-
* from Adobe.
|
|
16
|
-
|
|
17
|
-
* Adobe permits you to use and modify this file solely in accordance with
|
|
18
|
-
* the terms of the Adobe license agreement accompanying it.
|
|
19
|
-
*************************************************************************/
|
|
20
|
-
|
|
21
1
|
'use strict';
|
|
22
2
|
|
|
3
|
+
const translationProps = ['description', 'placeholder', 'enum', 'enumNames', 'label.value', 'constraintMessages.accept',
|
|
4
|
+
'constraintMessages.enum', 'constraintMessages.exclusiveMinimum', 'constraintMessages.exclusiveMaximum', 'constraintMessages.format', 'constraintMessages.maxFileSize', 'constraintMessages.maxLength',
|
|
5
|
+
'constraintMessages.maximum', 'constraintMessages.maxItems', 'constraintMessages.minLength', 'constraintMessages.minimum', 'constraintMessages.minItems', 'constraintMessages.pattern', 'constraintMessages.required',
|
|
6
|
+
'constraintMessages.step', 'constraintMessages.type', 'constraintMessages.validationExpression'];
|
|
7
|
+
const constraintProps = ['accept', 'enum', 'exclusiveMinimum', 'exclusiveMaximum',
|
|
8
|
+
'format', 'maxFileSize', 'maxLength', 'maximum', 'maxItems',
|
|
9
|
+
'minLength', 'minimum', 'minItems', 'pattern', 'required', 'step', 'validationExpression', 'enumNames'];
|
|
10
|
+
|
|
11
|
+
class ValidationError {
|
|
12
|
+
fieldName;
|
|
13
|
+
errorMessages;
|
|
14
|
+
constructor(fieldName = '', errorMessages = []) {
|
|
15
|
+
this.errorMessages = errorMessages;
|
|
16
|
+
this.fieldName = fieldName;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const objToMap = (o) => new Map(Object.entries(o));
|
|
21
|
+
const stringViewTypes = objToMap({ 'date': 'date-input', 'data-url': 'file-input', 'binary': 'file-input' });
|
|
22
|
+
const typeToViewTypes = objToMap({
|
|
23
|
+
'number': 'number-input',
|
|
24
|
+
'boolean': 'checkbox',
|
|
25
|
+
'object': 'panel',
|
|
26
|
+
'array': 'panel',
|
|
27
|
+
'file': 'file-input',
|
|
28
|
+
'file[]': 'file-input'
|
|
29
|
+
});
|
|
30
|
+
const arrayTypes = ['string[]', 'boolean[]', 'number[]', 'array'];
|
|
31
|
+
const defaultFieldTypes = (schema) => {
|
|
32
|
+
const type = schema.type || 'string';
|
|
33
|
+
if ('enum' in schema) {
|
|
34
|
+
const enums = schema.enum;
|
|
35
|
+
if (enums.length > 2 || arrayTypes.indexOf(type) > -1) {
|
|
36
|
+
return 'drop-down';
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return 'checkbox';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (type === 'string' || type === 'string[]') {
|
|
43
|
+
return stringViewTypes.get(schema.format) || 'text-input';
|
|
44
|
+
}
|
|
45
|
+
return typeToViewTypes.get(type) || 'text-input';
|
|
46
|
+
};
|
|
47
|
+
const fieldSchema = (input) => {
|
|
48
|
+
if ('items' in input) {
|
|
49
|
+
const fieldset = input;
|
|
50
|
+
const items = fieldset.items;
|
|
51
|
+
if (fieldset.type === 'array') {
|
|
52
|
+
return {
|
|
53
|
+
type: 'array',
|
|
54
|
+
items: fieldSchema(items[0]),
|
|
55
|
+
minItems: fieldset?.minItems,
|
|
56
|
+
maxItems: fieldset?.maxItems
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const iter = items.filter(x => x.name != null);
|
|
61
|
+
return {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: Object.fromEntries(iter.map(item => [item.name, fieldSchema(item)])),
|
|
64
|
+
required: iter.filter(x => x.required).map(x => x.name)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const field = input;
|
|
70
|
+
const schemaProps = ['type', 'maxLength', 'minLength', 'minimum', 'maximum', 'format', 'pattern', 'step', 'enum'];
|
|
71
|
+
const schema = schemaProps.reduce((acc, prop) => {
|
|
72
|
+
const p = prop;
|
|
73
|
+
if (prop in field && field[p] != undefined) {
|
|
74
|
+
acc[prop] = field[p];
|
|
75
|
+
}
|
|
76
|
+
return acc;
|
|
77
|
+
}, {});
|
|
78
|
+
if (field.dataRef === 'none' || Object.keys(schema).length == 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
title: field.label?.value,
|
|
83
|
+
description: field.description,
|
|
84
|
+
...schema
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const exportDataSchema = (form) => {
|
|
89
|
+
return fieldSchema(form);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getProperty = (data, key, def) => {
|
|
93
|
+
if (key in data) {
|
|
94
|
+
return data[key];
|
|
95
|
+
}
|
|
96
|
+
else if (!key.startsWith(':')) {
|
|
97
|
+
const prefixedKey = `:${key}`;
|
|
98
|
+
if (prefixedKey in data) {
|
|
99
|
+
return data[prefixedKey];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return def;
|
|
103
|
+
};
|
|
104
|
+
const isFile = function (item) {
|
|
105
|
+
return (item?.type === 'file' || item?.type === 'file[]') ||
|
|
106
|
+
((item?.type === 'string' || item?.type === 'string[]') &&
|
|
107
|
+
(item?.format === 'binary' || item?.format === 'data-url'));
|
|
108
|
+
};
|
|
109
|
+
const checkIfConstraintsArePresent = function (item) {
|
|
110
|
+
return constraintProps.some(cp => item[cp] !== undefined);
|
|
111
|
+
};
|
|
112
|
+
const isCheckbox = function (item) {
|
|
113
|
+
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
114
|
+
return fieldType === 'checkbox';
|
|
115
|
+
};
|
|
116
|
+
const isCheckboxGroup = function (item) {
|
|
117
|
+
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
118
|
+
return fieldType === 'checkbox-group';
|
|
119
|
+
};
|
|
120
|
+
const isDateField = function (item) {
|
|
121
|
+
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
122
|
+
return (fieldType === 'text-input' && item?.format === 'date') || fieldType === 'date-input';
|
|
123
|
+
};
|
|
124
|
+
function deepClone(obj, idGenerator) {
|
|
125
|
+
let result;
|
|
126
|
+
if (obj instanceof Array) {
|
|
127
|
+
result = [];
|
|
128
|
+
result = obj.map(x => deepClone(x, idGenerator));
|
|
129
|
+
}
|
|
130
|
+
else if (typeof obj === 'object' && obj !== null) {
|
|
131
|
+
result = {};
|
|
132
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
133
|
+
result[key] = deepClone(value, idGenerator);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
result = obj;
|
|
138
|
+
}
|
|
139
|
+
if (idGenerator && result && result.id) {
|
|
140
|
+
result.id = idGenerator();
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
function checkIfKeyAdded(currentObj, prevObj, objKey) {
|
|
145
|
+
if (currentObj != null && prevObj != null) {
|
|
146
|
+
const newPrvObj = { ...prevObj };
|
|
147
|
+
newPrvObj[objKey] = currentObj[objKey];
|
|
148
|
+
const newJsonStr = jsonString(currentObj).replace(jsonString(newPrvObj), '');
|
|
149
|
+
return newJsonStr === '';
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const jsonString = (obj) => {
|
|
156
|
+
return JSON.stringify(obj, null, 2);
|
|
157
|
+
};
|
|
158
|
+
const isRepeatable = (obj) => {
|
|
159
|
+
return ((obj.repeatable &&
|
|
160
|
+
((obj.minOccur === undefined && obj.maxOccur === undefined) ||
|
|
161
|
+
(obj.minOccur !== undefined && obj.maxOccur !== undefined && obj.maxOccur !== 0) ||
|
|
162
|
+
(obj.minOccur !== undefined && obj.maxOccur !== undefined && obj.minOccur !== 0 && obj.maxOccur !== 0) ||
|
|
163
|
+
(obj.minOccur !== undefined && obj.minOccur >= 0) ||
|
|
164
|
+
(obj.maxOccur !== undefined && obj.maxOccur !== 0))) || false);
|
|
165
|
+
};
|
|
166
|
+
|
|
23
167
|
class ActionImpl {
|
|
24
168
|
_metadata;
|
|
25
169
|
_type;
|
|
@@ -543,12 +687,12 @@ const resolveData = (data, input, create) => {
|
|
|
543
687
|
return result;
|
|
544
688
|
};
|
|
545
689
|
|
|
546
|
-
|
|
690
|
+
var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
547
691
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
548
692
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
549
693
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
550
694
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
551
|
-
}
|
|
695
|
+
};
|
|
552
696
|
const editableProperties = [
|
|
553
697
|
'value',
|
|
554
698
|
'label',
|
|
@@ -979,177 +1123,22 @@ class BaseNode {
|
|
|
979
1123
|
}
|
|
980
1124
|
}
|
|
981
1125
|
}
|
|
982
|
-
__decorate([
|
|
1126
|
+
__decorate$3([
|
|
983
1127
|
dependencyTracked()
|
|
984
1128
|
], BaseNode.prototype, "index", null);
|
|
985
|
-
__decorate([
|
|
1129
|
+
__decorate$3([
|
|
986
1130
|
dependencyTracked()
|
|
987
1131
|
], BaseNode.prototype, "description", null);
|
|
988
|
-
__decorate([
|
|
1132
|
+
__decorate$3([
|
|
989
1133
|
dependencyTracked()
|
|
990
1134
|
], BaseNode.prototype, "visible", null);
|
|
991
|
-
__decorate([
|
|
1135
|
+
__decorate$3([
|
|
992
1136
|
dependencyTracked()
|
|
993
1137
|
], BaseNode.prototype, "label", null);
|
|
994
|
-
__decorate([
|
|
1138
|
+
__decorate$3([
|
|
995
1139
|
dependencyTracked()
|
|
996
1140
|
], BaseNode.prototype, "properties", null);
|
|
997
1141
|
|
|
998
|
-
const translationProps = ['description', 'placeholder', 'enum', 'enumNames', 'label.value', 'constraintMessages.accept',
|
|
999
|
-
'constraintMessages.enum', 'constraintMessages.exclusiveMinimum', 'constraintMessages.exclusiveMaximum', 'constraintMessages.format', 'constraintMessages.maxFileSize', 'constraintMessages.maxLength',
|
|
1000
|
-
'constraintMessages.maximum', 'constraintMessages.maxItems', 'constraintMessages.minLength', 'constraintMessages.minimum', 'constraintMessages.minItems', 'constraintMessages.pattern', 'constraintMessages.required',
|
|
1001
|
-
'constraintMessages.step', 'constraintMessages.type', 'constraintMessages.validationExpression'];
|
|
1002
|
-
const constraintProps = ['accept', 'enum', 'exclusiveMinimum', 'exclusiveMaximum',
|
|
1003
|
-
'format', 'maxFileSize', 'maxLength', 'maximum', 'maxItems',
|
|
1004
|
-
'minLength', 'minimum', 'minItems', 'pattern', 'required', 'step', 'validationExpression', 'enumNames'];
|
|
1005
|
-
|
|
1006
|
-
const objToMap = (o) => new Map(Object.entries(o));
|
|
1007
|
-
const stringViewTypes = objToMap({ 'date': 'date-input', 'data-url': 'file-input', 'binary': 'file-input' });
|
|
1008
|
-
const typeToViewTypes = objToMap({
|
|
1009
|
-
'number': 'number-input',
|
|
1010
|
-
'boolean': 'checkbox',
|
|
1011
|
-
'object': 'panel',
|
|
1012
|
-
'array': 'panel',
|
|
1013
|
-
'file': 'file-input',
|
|
1014
|
-
'file[]': 'file-input'
|
|
1015
|
-
});
|
|
1016
|
-
const arrayTypes = ['string[]', 'boolean[]', 'number[]', 'array'];
|
|
1017
|
-
const defaultFieldTypes = (schema) => {
|
|
1018
|
-
const type = schema.type || 'string';
|
|
1019
|
-
if ('enum' in schema) {
|
|
1020
|
-
const enums = schema.enum;
|
|
1021
|
-
if (enums.length > 2 || arrayTypes.indexOf(type) > -1) {
|
|
1022
|
-
return 'drop-down';
|
|
1023
|
-
}
|
|
1024
|
-
else {
|
|
1025
|
-
return 'checkbox';
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
if (type === 'string' || type === 'string[]') {
|
|
1029
|
-
return stringViewTypes.get(schema.format) || 'text-input';
|
|
1030
|
-
}
|
|
1031
|
-
return typeToViewTypes.get(type) || 'text-input';
|
|
1032
|
-
};
|
|
1033
|
-
const fieldSchema = (input) => {
|
|
1034
|
-
if ('items' in input) {
|
|
1035
|
-
const fieldset = input;
|
|
1036
|
-
const items = fieldset.items;
|
|
1037
|
-
if (fieldset.type === 'array') {
|
|
1038
|
-
return {
|
|
1039
|
-
type: 'array',
|
|
1040
|
-
items: fieldSchema(items[0]),
|
|
1041
|
-
minItems: fieldset?.minItems,
|
|
1042
|
-
maxItems: fieldset?.maxItems
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
else {
|
|
1046
|
-
const iter = items.filter(x => x.name != null);
|
|
1047
|
-
return {
|
|
1048
|
-
type: 'object',
|
|
1049
|
-
properties: Object.fromEntries(iter.map(item => [item.name, fieldSchema(item)])),
|
|
1050
|
-
required: iter.filter(x => x.required).map(x => x.name)
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
else {
|
|
1055
|
-
const field = input;
|
|
1056
|
-
const schemaProps = ['type', 'maxLength', 'minLength', 'minimum', 'maximum', 'format', 'pattern', 'step', 'enum'];
|
|
1057
|
-
const schema = schemaProps.reduce((acc, prop) => {
|
|
1058
|
-
const p = prop;
|
|
1059
|
-
if (prop in field && field[p] != undefined) {
|
|
1060
|
-
acc[prop] = field[p];
|
|
1061
|
-
}
|
|
1062
|
-
return acc;
|
|
1063
|
-
}, {});
|
|
1064
|
-
if (field.dataRef === 'none' || Object.keys(schema).length == 0) {
|
|
1065
|
-
return undefined;
|
|
1066
|
-
}
|
|
1067
|
-
return {
|
|
1068
|
-
title: field.label?.value,
|
|
1069
|
-
description: field.description,
|
|
1070
|
-
...schema
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
};
|
|
1074
|
-
const exportDataSchema = (form) => {
|
|
1075
|
-
return fieldSchema(form);
|
|
1076
|
-
};
|
|
1077
|
-
|
|
1078
|
-
const getProperty = (data, key, def) => {
|
|
1079
|
-
if (key in data) {
|
|
1080
|
-
return data[key];
|
|
1081
|
-
}
|
|
1082
|
-
else if (!key.startsWith(':')) {
|
|
1083
|
-
const prefixedKey = `:${key}`;
|
|
1084
|
-
if (prefixedKey in data) {
|
|
1085
|
-
return data[prefixedKey];
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
return def;
|
|
1089
|
-
};
|
|
1090
|
-
const isFile = function (item) {
|
|
1091
|
-
return (item?.type === 'file' || item?.type === 'file[]') ||
|
|
1092
|
-
((item?.type === 'string' || item?.type === 'string[]') &&
|
|
1093
|
-
(item?.format === 'binary' || item?.format === 'data-url'));
|
|
1094
|
-
};
|
|
1095
|
-
const checkIfConstraintsArePresent = function (item) {
|
|
1096
|
-
return constraintProps.some(cp => item[cp] !== undefined);
|
|
1097
|
-
};
|
|
1098
|
-
const isCheckbox = function (item) {
|
|
1099
|
-
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
1100
|
-
return fieldType === 'checkbox';
|
|
1101
|
-
};
|
|
1102
|
-
const isCheckboxGroup = function (item) {
|
|
1103
|
-
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
1104
|
-
return fieldType === 'checkbox-group';
|
|
1105
|
-
};
|
|
1106
|
-
const isDateField = function (item) {
|
|
1107
|
-
const fieldType = item?.fieldType || defaultFieldTypes(item);
|
|
1108
|
-
return (fieldType === 'text-input' && item?.format === 'date') || fieldType === 'date-input';
|
|
1109
|
-
};
|
|
1110
|
-
function deepClone(obj, idGenerator) {
|
|
1111
|
-
let result;
|
|
1112
|
-
if (obj instanceof Array) {
|
|
1113
|
-
result = [];
|
|
1114
|
-
result = obj.map(x => deepClone(x, idGenerator));
|
|
1115
|
-
}
|
|
1116
|
-
else if (typeof obj === 'object' && obj !== null) {
|
|
1117
|
-
result = {};
|
|
1118
|
-
Object.entries(obj).forEach(([key, value]) => {
|
|
1119
|
-
result[key] = deepClone(value, idGenerator);
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
result = obj;
|
|
1124
|
-
}
|
|
1125
|
-
if (idGenerator && result && result.id) {
|
|
1126
|
-
result.id = idGenerator();
|
|
1127
|
-
}
|
|
1128
|
-
return result;
|
|
1129
|
-
}
|
|
1130
|
-
function checkIfKeyAdded(currentObj, prevObj, objKey) {
|
|
1131
|
-
if (currentObj != null && prevObj != null) {
|
|
1132
|
-
const newPrvObj = { ...prevObj };
|
|
1133
|
-
newPrvObj[objKey] = currentObj[objKey];
|
|
1134
|
-
const newJsonStr = jsonString(currentObj).replace(jsonString(newPrvObj), '');
|
|
1135
|
-
return newJsonStr === '';
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
return false;
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
const jsonString = (obj) => {
|
|
1142
|
-
return JSON.stringify(obj, null, 2);
|
|
1143
|
-
};
|
|
1144
|
-
const isRepeatable = (obj) => {
|
|
1145
|
-
return ((obj.repeatable &&
|
|
1146
|
-
((obj.minOccur === undefined && obj.maxOccur === undefined) ||
|
|
1147
|
-
(obj.minOccur !== undefined && obj.maxOccur !== undefined && obj.maxOccur !== 0) ||
|
|
1148
|
-
(obj.minOccur !== undefined && obj.maxOccur !== undefined && obj.minOccur !== 0 && obj.maxOccur !== 0) ||
|
|
1149
|
-
(obj.minOccur !== undefined && obj.minOccur >= 0) ||
|
|
1150
|
-
(obj.maxOccur !== undefined && obj.maxOccur !== 0))) || false);
|
|
1151
|
-
};
|
|
1152
|
-
|
|
1153
1142
|
class Scriptable extends BaseNode {
|
|
1154
1143
|
_events = {};
|
|
1155
1144
|
_rules = {};
|
|
@@ -1312,6 +1301,12 @@ class Scriptable extends BaseNode {
|
|
|
1312
1301
|
}
|
|
1313
1302
|
}
|
|
1314
1303
|
|
|
1304
|
+
var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1305
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1306
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1307
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1308
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1309
|
+
};
|
|
1315
1310
|
class Container extends Scriptable {
|
|
1316
1311
|
_children = [];
|
|
1317
1312
|
_childrenReference;
|
|
@@ -1581,13 +1576,13 @@ class Container extends Scriptable {
|
|
|
1581
1576
|
}
|
|
1582
1577
|
}
|
|
1583
1578
|
}
|
|
1584
|
-
__decorate([
|
|
1579
|
+
__decorate$2([
|
|
1585
1580
|
dependencyTracked()
|
|
1586
1581
|
], Container.prototype, "maxItems", null);
|
|
1587
|
-
__decorate([
|
|
1582
|
+
__decorate$2([
|
|
1588
1583
|
dependencyTracked()
|
|
1589
1584
|
], Container.prototype, "minItems", null);
|
|
1590
|
-
__decorate([
|
|
1585
|
+
__decorate$2([
|
|
1591
1586
|
dependencyTracked()
|
|
1592
1587
|
], Container.prototype, "activeChild", null);
|
|
1593
1588
|
|
|
@@ -2453,16 +2448,17 @@ class Form extends Container {
|
|
|
2453
2448
|
super.dispatch(action);
|
|
2454
2449
|
}
|
|
2455
2450
|
}
|
|
2451
|
+
executeAction(action) {
|
|
2452
|
+
if ((action.type !== 'submit') || this._invalidFields.length === 0) {
|
|
2453
|
+
super.executeAction(action);
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
2456
|
submit(action, context) {
|
|
2457
2457
|
if (this.validate().length === 0) {
|
|
2458
2458
|
const payload = action?.payload || {};
|
|
2459
2459
|
submit(context, payload?.success, payload?.error, payload?.submit_as, payload?.data);
|
|
2460
2460
|
}
|
|
2461
2461
|
}
|
|
2462
|
-
reset() {
|
|
2463
|
-
super.reset();
|
|
2464
|
-
this._invalidFields = [];
|
|
2465
|
-
}
|
|
2466
2462
|
getElement(id) {
|
|
2467
2463
|
if (id == this.id) {
|
|
2468
2464
|
return this;
|
|
@@ -2489,6 +2485,7 @@ class Form extends Container {
|
|
|
2489
2485
|
}
|
|
2490
2486
|
}
|
|
2491
2487
|
|
|
2488
|
+
// Type constants used to define functions.
|
|
2492
2489
|
var dataTypes = {
|
|
2493
2490
|
TYPE_NUMBER: 0,
|
|
2494
2491
|
TYPE_ANY: 1,
|
|
@@ -2560,9 +2557,11 @@ const {
|
|
|
2560
2557
|
TYPE_CLASS: TYPE_CLASS$1,
|
|
2561
2558
|
TYPE_ARRAY_ARRAY,
|
|
2562
2559
|
} = dataTypes;
|
|
2560
|
+
|
|
2563
2561
|
const {
|
|
2564
2562
|
TOK_EXPREF: TOK_EXPREF$3,
|
|
2565
2563
|
} = tokenDefinitions;
|
|
2564
|
+
|
|
2566
2565
|
const TYPE_NAME_TABLE = {
|
|
2567
2566
|
[TYPE_NUMBER]: 'number',
|
|
2568
2567
|
[TYPE_ANY$1]: 'any',
|
|
@@ -2577,10 +2576,13 @@ const TYPE_NAME_TABLE = {
|
|
|
2577
2576
|
[TYPE_CLASS$1]: 'class',
|
|
2578
2577
|
[TYPE_ARRAY_ARRAY]: 'Array<array>',
|
|
2579
2578
|
};
|
|
2579
|
+
|
|
2580
2580
|
function getTypeName(inputObj, useValueOf = true) {
|
|
2581
2581
|
if (inputObj === null) return TYPE_NULL;
|
|
2582
2582
|
let obj = inputObj;
|
|
2583
2583
|
if (useValueOf) {
|
|
2584
|
+
// check for the case where there's a child named 'valueOf' that's not a function
|
|
2585
|
+
// if so, then it's an object...
|
|
2584
2586
|
if (typeof inputObj.valueOf === 'function') obj = inputObj.valueOf.call(inputObj);
|
|
2585
2587
|
else return TYPE_OBJECT;
|
|
2586
2588
|
}
|
|
@@ -2596,6 +2598,8 @@ function getTypeName(inputObj, useValueOf = true) {
|
|
|
2596
2598
|
case '[object Null]':
|
|
2597
2599
|
return TYPE_NULL;
|
|
2598
2600
|
case '[object Object]':
|
|
2601
|
+
// Check if it's an expref. If it has, it's been
|
|
2602
|
+
// tagged with a jmespathType attr of 'Expref';
|
|
2599
2603
|
if (obj.jmespathType === TOK_EXPREF$3) {
|
|
2600
2604
|
return TYPE_EXPREF;
|
|
2601
2605
|
}
|
|
@@ -2604,17 +2608,23 @@ function getTypeName(inputObj, useValueOf = true) {
|
|
|
2604
2608
|
return TYPE_OBJECT;
|
|
2605
2609
|
}
|
|
2606
2610
|
}
|
|
2611
|
+
|
|
2607
2612
|
function getTypeNames(inputObj) {
|
|
2613
|
+
// return the types with and without using valueOf
|
|
2614
|
+
// needed for the cases where we really need an object passed to a function -- not it's value
|
|
2608
2615
|
const type1 = getTypeName(inputObj);
|
|
2609
2616
|
const type2 = getTypeName(inputObj, false);
|
|
2610
2617
|
return [type1, type2];
|
|
2611
2618
|
}
|
|
2619
|
+
|
|
2612
2620
|
function matchType(actuals, expectedList, argValue, context, toNumber, toString) {
|
|
2613
2621
|
const actual = actuals[0];
|
|
2614
2622
|
if (expectedList.findIndex(
|
|
2615
2623
|
type => type === TYPE_ANY$1 || actual === type,
|
|
2616
2624
|
) !== -1
|
|
2617
2625
|
) return argValue;
|
|
2626
|
+
// Can't coerce Objects to any other type,
|
|
2627
|
+
// and cannot coerce anything to a Class
|
|
2618
2628
|
let wrongType = false;
|
|
2619
2629
|
if (actual === TYPE_OBJECT || (expectedList.length === 1 && expectedList[0] === TYPE_CLASS$1)) {
|
|
2620
2630
|
wrongType = true;
|
|
@@ -2634,9 +2644,11 @@ function matchType(actuals, expectedList, argValue, context, toNumber, toString)
|
|
|
2634
2644
|
if (wrongType) {
|
|
2635
2645
|
throw new Error(`TypeError: ${context} expected argument to be type ${TYPE_NAME_TABLE[expectedList[0]]} but received type ${TYPE_NAME_TABLE[actual]} instead.`);
|
|
2636
2646
|
}
|
|
2647
|
+
// no exact match in the list of possible types, see if we can coerce an array type
|
|
2637
2648
|
let expected = -1;
|
|
2638
2649
|
if (actual === TYPE_ARRAY$1) {
|
|
2639
2650
|
if (expectedList.includes(TYPE_ARRAY_STRING$1) && expectedList.includes(TYPE_ARRAY_NUMBER)) {
|
|
2651
|
+
// choose the array type based on the first element
|
|
2640
2652
|
if (argValue.length > 0 && typeof argValue[0] === 'string') expected = TYPE_ARRAY_STRING$1;
|
|
2641
2653
|
else expected = TYPE_ARRAY_NUMBER;
|
|
2642
2654
|
}
|
|
@@ -2646,6 +2658,7 @@ function matchType(actuals, expectedList, argValue, context, toNumber, toString)
|
|
|
2646
2658
|
e => [TYPE_ARRAY_STRING$1, TYPE_ARRAY_NUMBER, TYPE_ARRAY$1].includes(e),
|
|
2647
2659
|
);
|
|
2648
2660
|
}
|
|
2661
|
+
// no match, just take the first type
|
|
2649
2662
|
if (expected === -1) [expected] = expectedList;
|
|
2650
2663
|
if (expected === TYPE_ANY$1) return argValue;
|
|
2651
2664
|
if (expected === TYPE_ARRAY_STRING$1
|
|
@@ -2655,8 +2668,12 @@ function matchType(actuals, expectedList, argValue, context, toNumber, toString)
|
|
|
2655
2668
|
if (actual === TYPE_ARRAY_NUMBER || actual === TYPE_ARRAY_STRING$1) return argValue;
|
|
2656
2669
|
return argValue === null ? [] : [argValue];
|
|
2657
2670
|
}
|
|
2671
|
+
// The expected type can either just be array,
|
|
2672
|
+
// or it can require a specific subtype (array of numbers).
|
|
2658
2673
|
const subtype = expected === TYPE_ARRAY_NUMBER ? TYPE_NUMBER : TYPE_STRING$1;
|
|
2659
2674
|
if (actual === TYPE_ARRAY$1) {
|
|
2675
|
+
// Otherwise we need to check subtypes.
|
|
2676
|
+
// We're going to modify the array, so take a copy
|
|
2660
2677
|
const returnArray = argValue.slice();
|
|
2661
2678
|
for (let i = 0; i < returnArray.length; i += 1) {
|
|
2662
2679
|
const indexType = getTypeNames(returnArray[i]);
|
|
@@ -2677,6 +2694,7 @@ function matchType(actuals, expectedList, argValue, context, toNumber, toString)
|
|
|
2677
2694
|
} else {
|
|
2678
2695
|
if (expected === TYPE_NUMBER) {
|
|
2679
2696
|
if ([TYPE_STRING$1, TYPE_BOOLEAN, TYPE_NULL].includes(actual)) return toNumber(argValue);
|
|
2697
|
+
/* TYPE_ARRAY, TYPE_EXPREF, TYPE_OBJECT, TYPE_ARRAY, TYPE_ARRAY_NUMBER, TYPE_ARRAY_STRING */
|
|
2680
2698
|
return 0;
|
|
2681
2699
|
}
|
|
2682
2700
|
if (expected === TYPE_STRING$1) {
|
|
@@ -2699,31 +2717,42 @@ function isArray(obj) {
|
|
|
2699
2717
|
}
|
|
2700
2718
|
return false;
|
|
2701
2719
|
}
|
|
2720
|
+
|
|
2702
2721
|
function isObject(obj) {
|
|
2703
2722
|
if (obj !== null) {
|
|
2704
2723
|
return Object.prototype.toString.call(obj) === '[object Object]';
|
|
2705
2724
|
}
|
|
2706
2725
|
return false;
|
|
2707
2726
|
}
|
|
2727
|
+
|
|
2708
2728
|
function getValueOf(a) {
|
|
2709
2729
|
if (a === null || a === undefined) return a;
|
|
2710
2730
|
if (isArray(a)) {
|
|
2711
2731
|
return a.map(i => getValueOf(i));
|
|
2712
2732
|
}
|
|
2733
|
+
// if we have a child named 'valueOf' then we're an object,
|
|
2734
|
+
// and just return the object.
|
|
2713
2735
|
if (typeof (a.valueOf) !== 'function') return a;
|
|
2714
2736
|
return a.valueOf();
|
|
2715
2737
|
}
|
|
2738
|
+
|
|
2716
2739
|
function strictDeepEqual(lhs, rhs) {
|
|
2717
2740
|
const first = getValueOf(lhs);
|
|
2718
2741
|
const second = getValueOf(rhs);
|
|
2742
|
+
// Check the scalar case first.
|
|
2719
2743
|
if (first === second) {
|
|
2720
2744
|
return true;
|
|
2721
2745
|
}
|
|
2746
|
+
|
|
2747
|
+
// Check if they are the same type.
|
|
2722
2748
|
const firstType = Object.prototype.toString.call(first);
|
|
2723
2749
|
if (firstType !== Object.prototype.toString.call(second)) {
|
|
2724
2750
|
return false;
|
|
2725
2751
|
}
|
|
2752
|
+
// We know that first and second have the same type so we can just check the
|
|
2753
|
+
// first type from now on.
|
|
2726
2754
|
if (isArray(first) === true) {
|
|
2755
|
+
// Short circuit if they're not the same length;
|
|
2727
2756
|
if (first.length !== second.length) {
|
|
2728
2757
|
return false;
|
|
2729
2758
|
}
|
|
@@ -2735,7 +2764,9 @@ function strictDeepEqual(lhs, rhs) {
|
|
|
2735
2764
|
return true;
|
|
2736
2765
|
}
|
|
2737
2766
|
if (isObject(first) === true) {
|
|
2767
|
+
// An object is equal if it has the same key/value pairs.
|
|
2738
2768
|
const keysSeen = {};
|
|
2769
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
2739
2770
|
for (const key in first) {
|
|
2740
2771
|
if (hasOwnProperty.call(first, key)) {
|
|
2741
2772
|
if (strictDeepEqual(first[key], second[key]) === false) {
|
|
@@ -2744,6 +2775,9 @@ function strictDeepEqual(lhs, rhs) {
|
|
|
2744
2775
|
keysSeen[key] = true;
|
|
2745
2776
|
}
|
|
2746
2777
|
}
|
|
2778
|
+
// Now check that there aren't any keys in second that weren't
|
|
2779
|
+
// in first.
|
|
2780
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
2747
2781
|
for (const key2 in second) {
|
|
2748
2782
|
if (hasOwnProperty.call(second, key2)) {
|
|
2749
2783
|
if (keysSeen[key2] !== true) {
|
|
@@ -2769,22 +2803,42 @@ const {
|
|
|
2769
2803
|
TOK_NE: TOK_NE$2,
|
|
2770
2804
|
TOK_FLATTEN: TOK_FLATTEN$2,
|
|
2771
2805
|
} = tokenDefinitions;
|
|
2806
|
+
|
|
2772
2807
|
const {
|
|
2773
2808
|
TYPE_STRING,
|
|
2774
2809
|
TYPE_ARRAY_STRING,
|
|
2775
2810
|
TYPE_ARRAY,
|
|
2776
2811
|
} = dataTypes;
|
|
2812
|
+
|
|
2777
2813
|
function isFalse(value) {
|
|
2814
|
+
// From the spec:
|
|
2815
|
+
// A false value corresponds to the following values:
|
|
2816
|
+
// Empty list
|
|
2817
|
+
// Empty object
|
|
2818
|
+
// Empty string
|
|
2819
|
+
// False boolean
|
|
2820
|
+
// null value
|
|
2821
|
+
// (new) use JS truthy evaluation. This changes the spec behavior.
|
|
2822
|
+
// Where in the past a zero (0) would be True, it's now false
|
|
2823
|
+
|
|
2824
|
+
// First check the scalar values.
|
|
2778
2825
|
if (value === null) return true;
|
|
2826
|
+
// in case it's an object with a valueOf defined
|
|
2779
2827
|
const obj = getValueOf(value);
|
|
2780
2828
|
if (obj === '' || obj === false || obj === null) {
|
|
2781
2829
|
return true;
|
|
2782
2830
|
}
|
|
2783
2831
|
if (isArray(obj) && obj.length === 0) {
|
|
2832
|
+
// Check for an empty array.
|
|
2784
2833
|
return true;
|
|
2785
2834
|
}
|
|
2786
2835
|
if (isObject(obj)) {
|
|
2836
|
+
// Check for an empty object.
|
|
2837
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
2787
2838
|
for (const key in obj) {
|
|
2839
|
+
// If there are any keys, then
|
|
2840
|
+
// the object is not empty so the object
|
|
2841
|
+
// is not false.
|
|
2788
2842
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
2789
2843
|
return false;
|
|
2790
2844
|
}
|
|
@@ -2793,9 +2847,11 @@ function isFalse(value) {
|
|
|
2793
2847
|
}
|
|
2794
2848
|
return !obj;
|
|
2795
2849
|
}
|
|
2850
|
+
|
|
2796
2851
|
function objValues(obj) {
|
|
2797
2852
|
return Object.values(obj);
|
|
2798
2853
|
}
|
|
2854
|
+
|
|
2799
2855
|
class TreeInterpreter {
|
|
2800
2856
|
constructor(runtime, globals, toNumber, toString, debug, language) {
|
|
2801
2857
|
this.runtime = runtime;
|
|
@@ -2805,20 +2861,27 @@ class TreeInterpreter {
|
|
|
2805
2861
|
this.debug = debug;
|
|
2806
2862
|
this.language = language;
|
|
2807
2863
|
}
|
|
2864
|
+
|
|
2808
2865
|
search(node, value) {
|
|
2809
2866
|
return this.visit(node, value);
|
|
2810
2867
|
}
|
|
2868
|
+
|
|
2811
2869
|
visit(n, v) {
|
|
2812
2870
|
const visitFunctions = {
|
|
2813
2871
|
Field: (node, value) => {
|
|
2872
|
+
// we used to check isObject(value) here -- but it is possible for an array-based
|
|
2873
|
+
// object to have properties. So we'll allow the child check on objects and arrays.
|
|
2814
2874
|
if (value !== null && (isObject(value) || isArray(value))) {
|
|
2815
2875
|
let field = value[node.name];
|
|
2876
|
+
// fields can be objects with overridden methods. e.g. valueOf
|
|
2877
|
+
// so don't resolve to a function...
|
|
2816
2878
|
if (typeof field === 'function') field = undefined;
|
|
2817
2879
|
if (field === undefined) {
|
|
2818
2880
|
try {
|
|
2819
2881
|
this.debug.push(`Failed to find: '${node.name}'`);
|
|
2820
2882
|
const available = Object.keys(value).map(a => `'${a}'`).toString();
|
|
2821
2883
|
if (available.length) this.debug.push(`Available fields: ${available}`);
|
|
2884
|
+
// eslint-disable-next-line no-empty
|
|
2822
2885
|
} catch (e) {}
|
|
2823
2886
|
return null;
|
|
2824
2887
|
}
|
|
@@ -2826,6 +2889,7 @@ class TreeInterpreter {
|
|
|
2826
2889
|
}
|
|
2827
2890
|
return null;
|
|
2828
2891
|
},
|
|
2892
|
+
|
|
2829
2893
|
Subexpression: (node, value) => {
|
|
2830
2894
|
let result = this.visit(node.children[0], value);
|
|
2831
2895
|
for (let i = 1; i < node.children.length; i += 1) {
|
|
@@ -2834,10 +2898,12 @@ class TreeInterpreter {
|
|
|
2834
2898
|
}
|
|
2835
2899
|
return result;
|
|
2836
2900
|
},
|
|
2901
|
+
|
|
2837
2902
|
IndexExpression: (node, value) => {
|
|
2838
2903
|
const left = this.visit(node.children[0], value);
|
|
2839
2904
|
return this.visit(node.children[1], left);
|
|
2840
2905
|
},
|
|
2906
|
+
|
|
2841
2907
|
Index: (node, value) => {
|
|
2842
2908
|
if (isArray(value)) {
|
|
2843
2909
|
let index = this.toNumber(this.visit(node.value, value));
|
|
@@ -2863,6 +2929,7 @@ class TreeInterpreter {
|
|
|
2863
2929
|
this.debug.push(`left side of index expression ${value} is not an array or object.`);
|
|
2864
2930
|
return null;
|
|
2865
2931
|
},
|
|
2932
|
+
|
|
2866
2933
|
Slice: (node, value) => {
|
|
2867
2934
|
if (!isArray(value)) return null;
|
|
2868
2935
|
const sliceParams = node.children.slice(0).map(
|
|
@@ -2882,7 +2949,9 @@ class TreeInterpreter {
|
|
|
2882
2949
|
}
|
|
2883
2950
|
return result;
|
|
2884
2951
|
},
|
|
2952
|
+
|
|
2885
2953
|
Projection: (node, value) => {
|
|
2954
|
+
// Evaluate left child.
|
|
2886
2955
|
const base = this.visit(node.children[0], value);
|
|
2887
2956
|
if (!isArray(base)) return null;
|
|
2888
2957
|
const collected = [];
|
|
@@ -2894,7 +2963,9 @@ class TreeInterpreter {
|
|
|
2894
2963
|
});
|
|
2895
2964
|
return collected;
|
|
2896
2965
|
},
|
|
2966
|
+
|
|
2897
2967
|
ValueProjection: (node, value) => {
|
|
2968
|
+
// Evaluate left child.
|
|
2898
2969
|
const projection = this.visit(node.children[0], value);
|
|
2899
2970
|
if (!isObject(getValueOf(projection))) return null;
|
|
2900
2971
|
const collected = [];
|
|
@@ -2905,6 +2976,7 @@ class TreeInterpreter {
|
|
|
2905
2976
|
});
|
|
2906
2977
|
return collected;
|
|
2907
2978
|
},
|
|
2979
|
+
|
|
2908
2980
|
FilterProjection: (node, value) => {
|
|
2909
2981
|
const base = this.visit(node.children[0], value);
|
|
2910
2982
|
if (!isArray(base)) return null;
|
|
@@ -2912,6 +2984,7 @@ class TreeInterpreter {
|
|
|
2912
2984
|
const matched = this.visit(node.children[2], b);
|
|
2913
2985
|
return !isFalse(matched);
|
|
2914
2986
|
});
|
|
2987
|
+
|
|
2915
2988
|
const finalResults = [];
|
|
2916
2989
|
filtered.forEach(f => {
|
|
2917
2990
|
const current = this.visit(node.children[1], f);
|
|
@@ -2919,9 +2992,11 @@ class TreeInterpreter {
|
|
|
2919
2992
|
});
|
|
2920
2993
|
return finalResults;
|
|
2921
2994
|
},
|
|
2995
|
+
|
|
2922
2996
|
Comparator: (node, value) => {
|
|
2923
2997
|
const first = this.visit(node.children[0], value);
|
|
2924
2998
|
const second = this.visit(node.children[1], value);
|
|
2999
|
+
|
|
2925
3000
|
if (node.name === TOK_EQ$2) return strictDeepEqual(first, second);
|
|
2926
3001
|
if (node.name === TOK_NE$2) return !strictDeepEqual(first, second);
|
|
2927
3002
|
if (node.name === TOK_GT$2) return first > second;
|
|
@@ -2930,6 +3005,7 @@ class TreeInterpreter {
|
|
|
2930
3005
|
if (node.name === TOK_LTE$2) return first <= second;
|
|
2931
3006
|
throw new Error(`Unknown comparator: ${node.name}`);
|
|
2932
3007
|
},
|
|
3008
|
+
|
|
2933
3009
|
[TOK_FLATTEN$2]: (node, value) => {
|
|
2934
3010
|
const original = this.visit(node.children[0], value);
|
|
2935
3011
|
if (!isArray(original)) return null;
|
|
@@ -2943,11 +3019,14 @@ class TreeInterpreter {
|
|
|
2943
3019
|
});
|
|
2944
3020
|
return merged;
|
|
2945
3021
|
},
|
|
3022
|
+
|
|
2946
3023
|
Identity: (_node, value) => value,
|
|
3024
|
+
|
|
2947
3025
|
MultiSelectList: (node, value) => {
|
|
2948
3026
|
if (value === null) return null;
|
|
2949
3027
|
return node.children.map(child => this.visit(child, value));
|
|
2950
3028
|
},
|
|
3029
|
+
|
|
2951
3030
|
MultiSelectHash: (node, value) => {
|
|
2952
3031
|
if (value === null) return null;
|
|
2953
3032
|
const collected = {};
|
|
@@ -2956,21 +3035,26 @@ class TreeInterpreter {
|
|
|
2956
3035
|
});
|
|
2957
3036
|
return collected;
|
|
2958
3037
|
},
|
|
3038
|
+
|
|
2959
3039
|
OrExpression: (node, value) => {
|
|
2960
3040
|
let matched = this.visit(node.children[0], value);
|
|
2961
3041
|
if (isFalse(matched)) matched = this.visit(node.children[1], value);
|
|
2962
3042
|
return matched;
|
|
2963
3043
|
},
|
|
3044
|
+
|
|
2964
3045
|
AndExpression: (node, value) => {
|
|
2965
3046
|
const first = this.visit(node.children[0], value);
|
|
3047
|
+
|
|
2966
3048
|
if (isFalse(first) === true) return first;
|
|
2967
3049
|
return this.visit(node.children[1], value);
|
|
2968
3050
|
},
|
|
3051
|
+
|
|
2969
3052
|
AddExpression: (node, value) => {
|
|
2970
3053
|
const first = this.visit(node.children[0], value);
|
|
2971
3054
|
const second = this.visit(node.children[1], value);
|
|
2972
3055
|
return this.applyOperator(first, second, '+');
|
|
2973
3056
|
},
|
|
3057
|
+
|
|
2974
3058
|
ConcatenateExpression: (node, value) => {
|
|
2975
3059
|
let first = this.visit(node.children[0], value);
|
|
2976
3060
|
let second = this.visit(node.children[1], value);
|
|
@@ -2978,6 +3062,7 @@ class TreeInterpreter {
|
|
|
2978
3062
|
second = matchType(getTypeNames(second), [TYPE_STRING, TYPE_ARRAY_STRING], second, 'concatenate', this.toNumber, this.toString);
|
|
2979
3063
|
return this.applyOperator(first, second, '&');
|
|
2980
3064
|
},
|
|
3065
|
+
|
|
2981
3066
|
UnionExpression: (node, value) => {
|
|
2982
3067
|
let first = this.visit(node.children[0], value);
|
|
2983
3068
|
let second = this.visit(node.children[1], value);
|
|
@@ -2985,52 +3070,72 @@ class TreeInterpreter {
|
|
|
2985
3070
|
second = matchType(getTypeNames(second), [TYPE_ARRAY], second, 'union', this.toNumber, this.toString);
|
|
2986
3071
|
return first.concat(second);
|
|
2987
3072
|
},
|
|
3073
|
+
|
|
2988
3074
|
SubtractExpression: (node, value) => {
|
|
2989
3075
|
const first = this.visit(node.children[0], value);
|
|
2990
3076
|
const second = this.visit(node.children[1], value);
|
|
2991
3077
|
return this.applyOperator(first, second, '-');
|
|
2992
3078
|
},
|
|
3079
|
+
|
|
2993
3080
|
MultiplyExpression: (node, value) => {
|
|
2994
3081
|
const first = this.visit(node.children[0], value);
|
|
2995
3082
|
const second = this.visit(node.children[1], value);
|
|
2996
3083
|
return this.applyOperator(first, second, '*');
|
|
2997
3084
|
},
|
|
3085
|
+
|
|
2998
3086
|
DivideExpression: (node, value) => {
|
|
2999
3087
|
const first = this.visit(node.children[0], value);
|
|
3000
3088
|
const second = this.visit(node.children[1], value);
|
|
3001
3089
|
return this.applyOperator(first, second, '/');
|
|
3002
3090
|
},
|
|
3091
|
+
|
|
3003
3092
|
PowerExpression: (node, value) => {
|
|
3004
3093
|
const first = this.visit(node.children[0], value);
|
|
3005
3094
|
const second = this.visit(node.children[1], value);
|
|
3006
3095
|
return this.applyOperator(first, second, '^');
|
|
3007
3096
|
},
|
|
3097
|
+
|
|
3008
3098
|
NotExpression: (node, value) => {
|
|
3009
3099
|
const first = this.visit(node.children[0], value);
|
|
3010
3100
|
return isFalse(first);
|
|
3011
3101
|
},
|
|
3102
|
+
|
|
3012
3103
|
UnaryMinusExpression: (node, value) => {
|
|
3013
3104
|
const first = this.visit(node.children[0], value);
|
|
3014
3105
|
return first * -1;
|
|
3015
3106
|
},
|
|
3107
|
+
|
|
3016
3108
|
Literal: node => node.value,
|
|
3109
|
+
|
|
3017
3110
|
Number: node => node.value,
|
|
3111
|
+
|
|
3018
3112
|
[TOK_PIPE$2]: (node, value) => {
|
|
3019
3113
|
const left = this.visit(node.children[0], value);
|
|
3020
3114
|
return this.visit(node.children[1], left);
|
|
3021
3115
|
},
|
|
3116
|
+
|
|
3022
3117
|
[TOK_CURRENT$2]: (_node, value) => value,
|
|
3118
|
+
|
|
3023
3119
|
[TOK_GLOBAL$2]: node => {
|
|
3024
3120
|
const result = this.globals[node.name];
|
|
3025
3121
|
return result === undefined ? null : result;
|
|
3026
3122
|
},
|
|
3123
|
+
|
|
3027
3124
|
Function: (node, value) => {
|
|
3125
|
+
// Special case for if()
|
|
3126
|
+
// we need to make sure the results are called only after the condition is evaluated
|
|
3127
|
+
// Otherwise we end up with both results invoked -- which could include side effects
|
|
3128
|
+
// For "if", the last parameter to callFunction is false (bResolved) to indicate there's
|
|
3129
|
+
// no point in validating the argument type.
|
|
3028
3130
|
if (node.name === 'if') return this.runtime.callFunction(node.name, node.children, value, this, false);
|
|
3029
3131
|
const resolvedArgs = node.children.map(child => this.visit(child, value));
|
|
3030
3132
|
return this.runtime.callFunction(node.name, resolvedArgs, value, this);
|
|
3031
3133
|
},
|
|
3134
|
+
|
|
3032
3135
|
ExpressionReference: node => {
|
|
3033
3136
|
const [refNode] = node.children;
|
|
3137
|
+
// Tag the node with a specific attribute so the type
|
|
3138
|
+
// checker verify the type.
|
|
3034
3139
|
refNode.jmespathType = TOK_EXPREF$2;
|
|
3035
3140
|
return refNode;
|
|
3036
3141
|
},
|
|
@@ -3039,6 +3144,8 @@ class TreeInterpreter {
|
|
|
3039
3144
|
if (!fn) throw new Error(`Unknown/missing node type ${(n && n.type) || ''}`);
|
|
3040
3145
|
return fn(n, v);
|
|
3041
3146
|
}
|
|
3147
|
+
|
|
3148
|
+
// eslint-disable-next-line class-methods-use-this
|
|
3042
3149
|
computeSliceParams(arrayLength, sliceParams) {
|
|
3043
3150
|
function capSliceRange(arrayLen, actual, stp) {
|
|
3044
3151
|
let actualValue = actual;
|
|
@@ -3052,6 +3159,7 @@ class TreeInterpreter {
|
|
|
3052
3159
|
}
|
|
3053
3160
|
return actualValue;
|
|
3054
3161
|
}
|
|
3162
|
+
|
|
3055
3163
|
let [start, stop, step] = sliceParams;
|
|
3056
3164
|
if (step === null) {
|
|
3057
3165
|
step = 1;
|
|
@@ -3061,11 +3169,13 @@ class TreeInterpreter {
|
|
|
3061
3169
|
throw error;
|
|
3062
3170
|
}
|
|
3063
3171
|
const stepValueNegative = step < 0;
|
|
3172
|
+
|
|
3064
3173
|
if (start === null) {
|
|
3065
3174
|
start = stepValueNegative ? arrayLength - 1 : 0;
|
|
3066
3175
|
} else {
|
|
3067
3176
|
start = capSliceRange(arrayLength, start, step);
|
|
3068
3177
|
}
|
|
3178
|
+
|
|
3069
3179
|
if (stop === null) {
|
|
3070
3180
|
stop = stepValueNegative ? -1 : arrayLength;
|
|
3071
3181
|
} else {
|
|
@@ -3073,8 +3183,10 @@ class TreeInterpreter {
|
|
|
3073
3183
|
}
|
|
3074
3184
|
return [start, stop, step];
|
|
3075
3185
|
}
|
|
3186
|
+
|
|
3076
3187
|
applyOperator(first, second, operator) {
|
|
3077
3188
|
if (isArray(first) && isArray(second)) {
|
|
3189
|
+
// balance the size of the arrays
|
|
3078
3190
|
const shorter = first.length < second.length ? first : second;
|
|
3079
3191
|
const diff = Math.abs(first.length - second.length);
|
|
3080
3192
|
shorter.length += diff;
|
|
@@ -3085,8 +3197,10 @@ class TreeInterpreter {
|
|
|
3085
3197
|
}
|
|
3086
3198
|
return result;
|
|
3087
3199
|
}
|
|
3200
|
+
|
|
3088
3201
|
if (isArray(first)) return first.map(a => this.applyOperator(a, second, operator));
|
|
3089
3202
|
if (isArray(second)) return second.map(a => this.applyOperator(first, a, operator));
|
|
3203
|
+
|
|
3090
3204
|
if (operator === '*') return this.toNumber(first) * this.toNumber(second);
|
|
3091
3205
|
if (operator === '&') return first + second;
|
|
3092
3206
|
if (operator === '+') {
|
|
@@ -3104,6 +3218,8 @@ class TreeInterpreter {
|
|
|
3104
3218
|
}
|
|
3105
3219
|
}
|
|
3106
3220
|
|
|
3221
|
+
/* eslint-disable no-underscore-dangle */
|
|
3222
|
+
|
|
3107
3223
|
const {
|
|
3108
3224
|
TOK_UNQUOTEDIDENTIFIER: TOK_UNQUOTEDIDENTIFIER$1,
|
|
3109
3225
|
TOK_QUOTEDIDENTIFIER: TOK_QUOTEDIDENTIFIER$1,
|
|
@@ -3143,8 +3259,16 @@ const {
|
|
|
3143
3259
|
TOK_LPAREN: TOK_LPAREN$1,
|
|
3144
3260
|
TOK_LITERAL: TOK_LITERAL$1,
|
|
3145
3261
|
} = tokenDefinitions;
|
|
3262
|
+
|
|
3263
|
+
// The "&", "[", "<", ">" tokens
|
|
3264
|
+
// are not in basicToken because
|
|
3265
|
+
// there are two token variants
|
|
3266
|
+
// ("&&", "[?", "<=", ">="). This is specially handled
|
|
3267
|
+
// below.
|
|
3268
|
+
|
|
3146
3269
|
const basicTokens = {
|
|
3147
3270
|
'.': TOK_DOT$1,
|
|
3271
|
+
// "*": TOK_STAR,
|
|
3148
3272
|
',': TOK_COMMA$1,
|
|
3149
3273
|
':': TOK_COLON$1,
|
|
3150
3274
|
'{': TOK_LBRACE$1,
|
|
@@ -3154,6 +3278,7 @@ const basicTokens = {
|
|
|
3154
3278
|
')': TOK_RPAREN$1,
|
|
3155
3279
|
'@': TOK_CURRENT$1,
|
|
3156
3280
|
};
|
|
3281
|
+
|
|
3157
3282
|
const globalStartToken = '$';
|
|
3158
3283
|
const operatorStartToken = {
|
|
3159
3284
|
'<': true,
|
|
@@ -3161,34 +3286,42 @@ const operatorStartToken = {
|
|
|
3161
3286
|
'=': true,
|
|
3162
3287
|
'!': true,
|
|
3163
3288
|
};
|
|
3289
|
+
|
|
3164
3290
|
const skipChars = {
|
|
3165
3291
|
' ': true,
|
|
3166
3292
|
'\t': true,
|
|
3167
3293
|
'\n': true,
|
|
3168
3294
|
};
|
|
3295
|
+
|
|
3169
3296
|
function isNum(ch) {
|
|
3170
3297
|
return (ch >= '0' && ch <= '9') || (ch === '.');
|
|
3171
3298
|
}
|
|
3299
|
+
|
|
3172
3300
|
function isAlphaNum(ch) {
|
|
3173
3301
|
return (ch >= 'a' && ch <= 'z')
|
|
3174
3302
|
|| (ch >= 'A' && ch <= 'Z')
|
|
3175
3303
|
|| (ch >= '0' && ch <= '9')
|
|
3176
3304
|
|| ch === '_';
|
|
3177
3305
|
}
|
|
3306
|
+
|
|
3178
3307
|
function isIdentifier(stream, pos) {
|
|
3179
3308
|
const ch = stream[pos];
|
|
3309
|
+
// $ is special -- it's allowed to be part of an identifier if it's the first character
|
|
3180
3310
|
if (ch === '$') {
|
|
3181
3311
|
return stream.length > pos && isAlphaNum(stream[pos + 1]);
|
|
3182
3312
|
}
|
|
3313
|
+
// return whether character 'isAlpha'
|
|
3183
3314
|
return (ch >= 'a' && ch <= 'z')
|
|
3184
3315
|
|| (ch >= 'A' && ch <= 'Z')
|
|
3185
3316
|
|| ch === '_';
|
|
3186
3317
|
}
|
|
3318
|
+
|
|
3187
3319
|
class Lexer {
|
|
3188
3320
|
constructor(allowedGlobalNames = [], debug = []) {
|
|
3189
3321
|
this._allowedGlobalNames = allowedGlobalNames;
|
|
3190
3322
|
this.debug = debug;
|
|
3191
3323
|
}
|
|
3324
|
+
|
|
3192
3325
|
tokenize(stream) {
|
|
3193
3326
|
const tokens = [];
|
|
3194
3327
|
this._current = 0;
|
|
@@ -3197,6 +3330,7 @@ class Lexer {
|
|
|
3197
3330
|
let token;
|
|
3198
3331
|
while (this._current < stream.length) {
|
|
3199
3332
|
const prev = tokens.length ? tokens.slice(-1)[0].type : null;
|
|
3333
|
+
|
|
3200
3334
|
if (this._isGlobal(prev, stream, this._current)) {
|
|
3201
3335
|
tokens.push(this._consumeGlobal(stream));
|
|
3202
3336
|
} else if (isIdentifier(stream, this._current)) {
|
|
@@ -3221,6 +3355,8 @@ class Lexer {
|
|
|
3221
3355
|
token = this._consumeNumber(stream);
|
|
3222
3356
|
tokens.push(token);
|
|
3223
3357
|
} else if (stream[this._current] === '[') {
|
|
3358
|
+
// No need to increment this._current. This happens
|
|
3359
|
+
// in _consumeLBracket
|
|
3224
3360
|
token = this._consumeLBracket(stream);
|
|
3225
3361
|
tokens.push(token);
|
|
3226
3362
|
} else if (stream[this._current] === '"') {
|
|
@@ -3250,6 +3386,7 @@ class Lexer {
|
|
|
3250
3386
|
} else if (operatorStartToken[stream[this._current]] !== undefined) {
|
|
3251
3387
|
tokens.push(this._consumeOperator(stream));
|
|
3252
3388
|
} else if (skipChars[stream[this._current]] !== undefined) {
|
|
3389
|
+
// Ignore whitespace.
|
|
3253
3390
|
this._current += 1;
|
|
3254
3391
|
} else if (stream[this._current] === '&') {
|
|
3255
3392
|
start = this._current;
|
|
@@ -3258,6 +3395,9 @@ class Lexer {
|
|
|
3258
3395
|
this._current += 1;
|
|
3259
3396
|
tokens.push({ type: TOK_AND$1, value: '&&', start });
|
|
3260
3397
|
} else if (prev === TOK_COMMA$1 || prev === TOK_LPAREN$1) {
|
|
3398
|
+
// based on previous token we'll know if this & is a JMESPath expression-type
|
|
3399
|
+
// or if it's a concatenation operator
|
|
3400
|
+
// if we're a function arg then it's an expression-type
|
|
3261
3401
|
tokens.push({ type: TOK_EXPREF$1, value: '&', start });
|
|
3262
3402
|
} else {
|
|
3263
3403
|
tokens.push({ type: TOK_CONCATENATE$1, value: '&', start });
|
|
@@ -3277,6 +3417,8 @@ class Lexer {
|
|
|
3277
3417
|
} else if (stream[this._current] === '*') {
|
|
3278
3418
|
start = this._current;
|
|
3279
3419
|
this._current += 1;
|
|
3420
|
+
// based on previous token we'll know if this asterix is a star -- not a multiply
|
|
3421
|
+
// might be better to list the prev tokens that are valid for multiply?
|
|
3280
3422
|
const prevToken = tokens.length && tokens.slice(-1)[0].type;
|
|
3281
3423
|
if (tokens.length === 0 || [
|
|
3282
3424
|
TOK_LBRACKET$1,
|
|
@@ -3316,6 +3458,7 @@ class Lexer {
|
|
|
3316
3458
|
}
|
|
3317
3459
|
return tokens;
|
|
3318
3460
|
}
|
|
3461
|
+
|
|
3319
3462
|
_consumeUnquotedIdentifier(stream) {
|
|
3320
3463
|
const start = this._current;
|
|
3321
3464
|
this._current += 1;
|
|
@@ -3324,12 +3467,14 @@ class Lexer {
|
|
|
3324
3467
|
}
|
|
3325
3468
|
return stream.slice(start, this._current);
|
|
3326
3469
|
}
|
|
3470
|
+
|
|
3327
3471
|
_consumeQuotedIdentifier(stream) {
|
|
3328
3472
|
const start = this._current;
|
|
3329
3473
|
this._current += 1;
|
|
3330
3474
|
const maxLength = stream.length;
|
|
3331
3475
|
let foundNonAlpha = !isIdentifier(stream, start + 1);
|
|
3332
3476
|
while (stream[this._current] !== '"' && this._current < maxLength) {
|
|
3477
|
+
// You can escape a double quote and you can escape an escape.
|
|
3333
3478
|
let current = this._current;
|
|
3334
3479
|
if (!isAlphaNum(stream[current])) foundNonAlpha = true;
|
|
3335
3480
|
if (stream[current] === '\\' && (stream[current + 1] === '\\'
|
|
@@ -3342,19 +3487,26 @@ class Lexer {
|
|
|
3342
3487
|
}
|
|
3343
3488
|
this._current += 1;
|
|
3344
3489
|
const val = stream.slice(start, this._current);
|
|
3490
|
+
// Check for unnecessary double quotes.
|
|
3491
|
+
// json-formula uses double quotes to escape characters that don't belong in names names.
|
|
3492
|
+
// e.g. "purchase-order".address
|
|
3493
|
+
// If we find a double-quoted entity with spaces or all legal characters, issue a warning
|
|
3345
3494
|
try {
|
|
3346
3495
|
if (!foundNonAlpha || val.includes(' ')) {
|
|
3347
3496
|
this.debug.push(`Suspicious quotes: ${val}`);
|
|
3348
3497
|
this.debug.push(`Did you intend a literal? '${val.replace(/"/g, '')}'?`);
|
|
3349
3498
|
}
|
|
3499
|
+
// eslint-disable-next-line no-empty
|
|
3350
3500
|
} catch (e) {}
|
|
3351
3501
|
return JSON.parse(val);
|
|
3352
3502
|
}
|
|
3503
|
+
|
|
3353
3504
|
_consumeRawStringLiteral(stream) {
|
|
3354
3505
|
const start = this._current;
|
|
3355
3506
|
this._current += 1;
|
|
3356
3507
|
const maxLength = stream.length;
|
|
3357
3508
|
while (stream[this._current] !== "'" && this._current < maxLength) {
|
|
3509
|
+
// You can escape a single quote and you can escape an escape.
|
|
3358
3510
|
let current = this._current;
|
|
3359
3511
|
if (stream[current] === '\\' && (stream[current + 1] === '\\'
|
|
3360
3512
|
|| stream[current + 1] === "'")) {
|
|
@@ -3368,6 +3520,7 @@ class Lexer {
|
|
|
3368
3520
|
const literal = stream.slice(start + 1, this._current - 1);
|
|
3369
3521
|
return literal.replaceAll("\\'", "'");
|
|
3370
3522
|
}
|
|
3523
|
+
|
|
3371
3524
|
_consumeNumber(stream) {
|
|
3372
3525
|
const start = this._current;
|
|
3373
3526
|
this._current += 1;
|
|
@@ -3384,11 +3537,13 @@ class Lexer {
|
|
|
3384
3537
|
}
|
|
3385
3538
|
return { type: TOK_NUMBER$1, value, start };
|
|
3386
3539
|
}
|
|
3540
|
+
|
|
3387
3541
|
_consumeUnaryMinus() {
|
|
3388
3542
|
const start = this._current;
|
|
3389
3543
|
this._current += 1;
|
|
3390
3544
|
return { type: TOK_UNARY_MINUS$1, value: '-', start };
|
|
3391
3545
|
}
|
|
3546
|
+
|
|
3392
3547
|
_consumeLBracket(stream) {
|
|
3393
3548
|
const start = this._current;
|
|
3394
3549
|
this._current += 1;
|
|
@@ -3402,22 +3557,28 @@ class Lexer {
|
|
|
3402
3557
|
}
|
|
3403
3558
|
return { type: TOK_LBRACKET$1, value: '[', start };
|
|
3404
3559
|
}
|
|
3560
|
+
|
|
3405
3561
|
_isGlobal(prev, stream, pos) {
|
|
3562
|
+
// global tokens occur only at the start of an expression
|
|
3406
3563
|
if (prev !== null && prev === TOK_DOT$1) return false;
|
|
3407
3564
|
const ch = stream[pos];
|
|
3408
3565
|
if (ch !== globalStartToken) return false;
|
|
3566
|
+
// $ is special -- it's allowed to be part of an identifier if it's the first character
|
|
3409
3567
|
let i = pos + 1;
|
|
3410
3568
|
while (i < stream.length && isAlphaNum(stream[i])) i += 1;
|
|
3411
3569
|
const global = stream.slice(pos, i);
|
|
3412
3570
|
return this._allowedGlobalNames.includes(global);
|
|
3413
3571
|
}
|
|
3572
|
+
|
|
3414
3573
|
_consumeGlobal(stream) {
|
|
3415
3574
|
const start = this._current;
|
|
3416
3575
|
this._current += 1;
|
|
3417
3576
|
while (this._current < stream.length && isAlphaNum(stream[this._current])) this._current += 1;
|
|
3418
3577
|
const global = stream.slice(start, this._current);
|
|
3578
|
+
|
|
3419
3579
|
return { type: TOK_GLOBAL$1, name: global, start };
|
|
3420
3580
|
}
|
|
3581
|
+
|
|
3421
3582
|
_consumeOperator(stream) {
|
|
3422
3583
|
const start = this._current;
|
|
3423
3584
|
const startingChar = stream[start];
|
|
@@ -3443,17 +3604,20 @@ class Lexer {
|
|
|
3443
3604
|
}
|
|
3444
3605
|
return { type: TOK_GT$1, value: '>', start };
|
|
3445
3606
|
}
|
|
3607
|
+
// startingChar is '='
|
|
3446
3608
|
if (stream[this._current] === '=') {
|
|
3447
3609
|
this._current += 1;
|
|
3448
3610
|
return { type: TOK_EQ$1, value: '==', start };
|
|
3449
3611
|
}
|
|
3450
3612
|
return { type: TOK_EQ$1, value: '=', start };
|
|
3451
3613
|
}
|
|
3614
|
+
|
|
3452
3615
|
_consumeLiteral(stream) {
|
|
3453
3616
|
function _looksLikeJSON(str) {
|
|
3454
3617
|
if (str === '') return false;
|
|
3455
3618
|
if ('[{"'.includes(str[0])) return true;
|
|
3456
3619
|
if (['true', 'false', 'null'].includes(str)) return true;
|
|
3620
|
+
|
|
3457
3621
|
if ('-0123456789'.includes(str[0])) {
|
|
3458
3622
|
try {
|
|
3459
3623
|
JSON.parse(str);
|
|
@@ -3465,6 +3629,7 @@ class Lexer {
|
|
|
3465
3629
|
return false;
|
|
3466
3630
|
}
|
|
3467
3631
|
}
|
|
3632
|
+
|
|
3468
3633
|
this._current += 1;
|
|
3469
3634
|
const start = this._current;
|
|
3470
3635
|
const maxLength = stream.length;
|
|
@@ -3472,12 +3637,14 @@ class Lexer {
|
|
|
3472
3637
|
let inQuotes = false;
|
|
3473
3638
|
while ((inQuotes || stream[this._current] !== '`') && this._current < maxLength) {
|
|
3474
3639
|
let current = this._current;
|
|
3640
|
+
// bypass escaped double quotes when we're inside quotes
|
|
3475
3641
|
if (inQuotes && stream[current] === '\\' && stream[current + 1] === '"') current += 2;
|
|
3476
3642
|
else {
|
|
3477
3643
|
if (stream[current] === '"') inQuotes = !inQuotes;
|
|
3478
3644
|
if (inQuotes && stream[current + 1] === '`') current += 2;
|
|
3479
3645
|
else if (stream[current] === '\\' && (stream[current + 1] === '\\'
|
|
3480
3646
|
|| stream[current + 1] === '`')) {
|
|
3647
|
+
// You can escape a literal char or you can escape the escape.
|
|
3481
3648
|
current += 2;
|
|
3482
3649
|
} else {
|
|
3483
3650
|
current += 1;
|
|
@@ -3490,13 +3657,16 @@ class Lexer {
|
|
|
3490
3657
|
if (_looksLikeJSON(literalString)) {
|
|
3491
3658
|
literal = JSON.parse(literalString);
|
|
3492
3659
|
} else {
|
|
3660
|
+
// Try to JSON parse it as "<literal>"
|
|
3493
3661
|
literal = JSON.parse(`"${literalString}"`);
|
|
3494
3662
|
}
|
|
3663
|
+
// +1 gets us to the ending "`", +1 to move on to the next char.
|
|
3495
3664
|
this._current += 1;
|
|
3496
3665
|
return literal;
|
|
3497
3666
|
}
|
|
3498
3667
|
}
|
|
3499
3668
|
|
|
3669
|
+
/* eslint-disable no-underscore-dangle */
|
|
3500
3670
|
const {
|
|
3501
3671
|
TOK_LITERAL,
|
|
3502
3672
|
TOK_COLON,
|
|
@@ -3538,6 +3708,7 @@ const {
|
|
|
3538
3708
|
TOK_LBRACKET,
|
|
3539
3709
|
TOK_LPAREN,
|
|
3540
3710
|
} = tokenDefinitions;
|
|
3711
|
+
|
|
3541
3712
|
const bindingPower = {
|
|
3542
3713
|
[TOK_EOF]: 0,
|
|
3543
3714
|
[TOK_UNQUOTEDIDENTIFIER]: 0,
|
|
@@ -3577,10 +3748,12 @@ const bindingPower = {
|
|
|
3577
3748
|
[TOK_LBRACKET]: 55,
|
|
3578
3749
|
[TOK_LPAREN]: 60,
|
|
3579
3750
|
};
|
|
3751
|
+
|
|
3580
3752
|
class Parser {
|
|
3581
3753
|
constructor(allowedGlobalNames = []) {
|
|
3582
3754
|
this._allowedGlobalNames = allowedGlobalNames;
|
|
3583
3755
|
}
|
|
3756
|
+
|
|
3584
3757
|
parse(expression, debug) {
|
|
3585
3758
|
this._loadTokens(expression, debug);
|
|
3586
3759
|
this.index = 0;
|
|
@@ -3595,12 +3768,14 @@ class Parser {
|
|
|
3595
3768
|
}
|
|
3596
3769
|
return ast;
|
|
3597
3770
|
}
|
|
3771
|
+
|
|
3598
3772
|
_loadTokens(expression, debug) {
|
|
3599
3773
|
const lexer = new Lexer(this._allowedGlobalNames, debug);
|
|
3600
3774
|
const tokens = lexer.tokenize(expression);
|
|
3601
3775
|
tokens.push({ type: TOK_EOF, value: '', start: expression.length });
|
|
3602
3776
|
this.tokens = tokens;
|
|
3603
3777
|
}
|
|
3778
|
+
|
|
3604
3779
|
expression(rbp) {
|
|
3605
3780
|
const leftToken = this._lookaheadToken(0);
|
|
3606
3781
|
this._advance();
|
|
@@ -3613,21 +3788,28 @@ class Parser {
|
|
|
3613
3788
|
}
|
|
3614
3789
|
return left;
|
|
3615
3790
|
}
|
|
3791
|
+
|
|
3616
3792
|
_lookahead(number) {
|
|
3617
3793
|
return this.tokens[this.index + number].type;
|
|
3618
3794
|
}
|
|
3795
|
+
|
|
3619
3796
|
_lookaheadToken(number) {
|
|
3620
3797
|
return this.tokens[this.index + number];
|
|
3621
3798
|
}
|
|
3799
|
+
|
|
3622
3800
|
_advance() {
|
|
3623
3801
|
this.index += 1;
|
|
3624
3802
|
}
|
|
3803
|
+
|
|
3625
3804
|
_getIndex() {
|
|
3626
3805
|
return this.index;
|
|
3627
3806
|
}
|
|
3807
|
+
|
|
3628
3808
|
_setIndex(index) {
|
|
3629
3809
|
this.index = index;
|
|
3630
3810
|
}
|
|
3811
|
+
|
|
3812
|
+
// eslint-disable-next-line consistent-return
|
|
3631
3813
|
nud(token) {
|
|
3632
3814
|
let left;
|
|
3633
3815
|
let right;
|
|
@@ -3656,6 +3838,8 @@ class Parser {
|
|
|
3656
3838
|
case TOK_STAR:
|
|
3657
3839
|
left = { type: 'Identity' };
|
|
3658
3840
|
if (this._lookahead(0) === TOK_RBRACKET) {
|
|
3841
|
+
// This can happen in a multiselect,
|
|
3842
|
+
// [a, b, *]
|
|
3659
3843
|
right = { type: 'Identity' };
|
|
3660
3844
|
} else {
|
|
3661
3845
|
right = this._parseProjectionRHS(bindingPower.Star);
|
|
@@ -3702,6 +3886,8 @@ class Parser {
|
|
|
3702
3886
|
this._errorToken(token);
|
|
3703
3887
|
}
|
|
3704
3888
|
}
|
|
3889
|
+
|
|
3890
|
+
// eslint-disable-next-line consistent-return
|
|
3705
3891
|
led(tokenName, left) {
|
|
3706
3892
|
let condition;
|
|
3707
3893
|
let right;
|
|
@@ -3722,6 +3908,7 @@ class Parser {
|
|
|
3722
3908
|
right = this._parseDotRHS(rbp);
|
|
3723
3909
|
return { type: 'Subexpression', children: [left, right] };
|
|
3724
3910
|
}
|
|
3911
|
+
// Creating a projection.
|
|
3725
3912
|
this._advance();
|
|
3726
3913
|
right = this._parseProjectionRHS(rbp);
|
|
3727
3914
|
return { type: 'ValueProjection', children: [left, right] };
|
|
@@ -3799,6 +3986,7 @@ class Parser {
|
|
|
3799
3986
|
this._errorToken(this._lookaheadToken(0));
|
|
3800
3987
|
}
|
|
3801
3988
|
}
|
|
3989
|
+
|
|
3802
3990
|
_match(tokenType) {
|
|
3803
3991
|
if (this._lookahead(0) === tokenType) {
|
|
3804
3992
|
this._advance();
|
|
@@ -3809,6 +3997,8 @@ class Parser {
|
|
|
3809
3997
|
throw error;
|
|
3810
3998
|
}
|
|
3811
3999
|
}
|
|
4000
|
+
|
|
4001
|
+
// eslint-disable-next-line class-methods-use-this
|
|
3812
4002
|
_errorToken(token) {
|
|
3813
4003
|
const error = new Error(`Invalid token (${
|
|
3814
4004
|
token.type}): "${
|
|
@@ -3816,14 +4006,17 @@ class Parser {
|
|
|
3816
4006
|
error.name = 'ParserError';
|
|
3817
4007
|
throw error;
|
|
3818
4008
|
}
|
|
4009
|
+
|
|
3819
4010
|
_parseChainedIndexExpression() {
|
|
3820
4011
|
const oldIndex = this._getIndex();
|
|
3821
4012
|
if (this._lookahead(0) === TOK_COLON) {
|
|
3822
4013
|
return this._parseSliceExpression();
|
|
3823
4014
|
}
|
|
4015
|
+
// look ahead of the first expression to determine the type
|
|
3824
4016
|
const first = this.expression(0);
|
|
3825
4017
|
const token = this._lookahead(0);
|
|
3826
4018
|
if (token === TOK_COLON) {
|
|
4019
|
+
// now that we know the type revert back to the old position and parse
|
|
3827
4020
|
this._setIndex(oldIndex);
|
|
3828
4021
|
return this._parseSliceExpression();
|
|
3829
4022
|
}
|
|
@@ -3833,6 +4026,7 @@ class Parser {
|
|
|
3833
4026
|
value: first,
|
|
3834
4027
|
};
|
|
3835
4028
|
}
|
|
4029
|
+
|
|
3836
4030
|
_parseUnchainedIndexExpression() {
|
|
3837
4031
|
const oldIndex = this._getIndex();
|
|
3838
4032
|
const firstToken = this._lookahead(0);
|
|
@@ -3861,6 +4055,7 @@ class Parser {
|
|
|
3861
4055
|
this._setIndex(oldIndex);
|
|
3862
4056
|
return this._parseMultiselectList();
|
|
3863
4057
|
}
|
|
4058
|
+
|
|
3864
4059
|
_projectIfSlice(left, right) {
|
|
3865
4060
|
const indexExpr = { type: 'IndexExpression', children: [left, right] };
|
|
3866
4061
|
if (right.type === 'Slice') {
|
|
@@ -3871,16 +4066,20 @@ class Parser {
|
|
|
3871
4066
|
}
|
|
3872
4067
|
return indexExpr;
|
|
3873
4068
|
}
|
|
4069
|
+
|
|
3874
4070
|
_parseSliceExpression() {
|
|
4071
|
+
// [start:end:step] where each part is optional, as well as the last
|
|
4072
|
+
// colon.
|
|
3875
4073
|
const parts = [null, null, null];
|
|
3876
4074
|
let index = 0;
|
|
3877
4075
|
let currentToken = this._lookahead(0);
|
|
3878
4076
|
while (currentToken !== TOK_RBRACKET && index < 3) {
|
|
3879
|
-
if (currentToken === TOK_COLON && index < 2) {
|
|
4077
|
+
if (currentToken === TOK_COLON && index < 2) { // there can't be more than 2 colons
|
|
3880
4078
|
index += 1;
|
|
3881
4079
|
this._advance();
|
|
3882
4080
|
} else {
|
|
3883
4081
|
parts[index] = this.expression(0);
|
|
4082
|
+
// check next token to be either colon or rbracket
|
|
3884
4083
|
const t = this._lookahead(0);
|
|
3885
4084
|
if (t !== TOK_COLON && t !== TOK_RBRACKET) {
|
|
3886
4085
|
const error = new Error(`Syntax error, unexpected token: ${
|
|
@@ -3897,10 +4096,13 @@ class Parser {
|
|
|
3897
4096
|
children: parts,
|
|
3898
4097
|
};
|
|
3899
4098
|
}
|
|
4099
|
+
|
|
3900
4100
|
_parseComparator(left, comparator) {
|
|
3901
4101
|
const right = this.expression(bindingPower[comparator]);
|
|
3902
4102
|
return { type: 'Comparator', name: comparator, children: [left, right] };
|
|
3903
4103
|
}
|
|
4104
|
+
|
|
4105
|
+
// eslint-disable-next-line consistent-return
|
|
3904
4106
|
_parseDotRHS(rbp) {
|
|
3905
4107
|
const lookahead = this._lookahead(0);
|
|
3906
4108
|
const exprTokens = [TOK_UNQUOTEDIDENTIFIER, TOK_QUOTEDIDENTIFIER, TOK_STAR];
|
|
@@ -3916,6 +4118,7 @@ class Parser {
|
|
|
3916
4118
|
return this._parseMultiselectHash();
|
|
3917
4119
|
}
|
|
3918
4120
|
}
|
|
4121
|
+
|
|
3919
4122
|
_parseProjectionRHS(rbp) {
|
|
3920
4123
|
let right;
|
|
3921
4124
|
if (bindingPower[this._lookahead(0)] < 10) {
|
|
@@ -3936,6 +4139,7 @@ class Parser {
|
|
|
3936
4139
|
}
|
|
3937
4140
|
return right;
|
|
3938
4141
|
}
|
|
4142
|
+
|
|
3939
4143
|
_parseMultiselectList() {
|
|
3940
4144
|
const expressions = [];
|
|
3941
4145
|
while (this._lookahead(0) !== TOK_RBRACKET) {
|
|
@@ -3951,6 +4155,7 @@ class Parser {
|
|
|
3951
4155
|
this._match(TOK_RBRACKET);
|
|
3952
4156
|
return { type: 'MultiSelectList', children: expressions };
|
|
3953
4157
|
}
|
|
4158
|
+
|
|
3954
4159
|
_parseMultiselectHash() {
|
|
3955
4160
|
const pairs = [];
|
|
3956
4161
|
const identifierTypes = [TOK_UNQUOTEDIDENTIFIER, TOK_QUOTEDIDENTIFIER];
|
|
@@ -3983,6 +4188,20 @@ class Parser {
|
|
|
3983
4188
|
}
|
|
3984
4189
|
}
|
|
3985
4190
|
|
|
4191
|
+
/*
|
|
4192
|
+
Copyright 2021 Adobe. All rights reserved.
|
|
4193
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4194
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
4195
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
4196
|
+
|
|
4197
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
4198
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
4199
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
4200
|
+
governing permissions and limitations under the License.
|
|
4201
|
+
*/
|
|
4202
|
+
|
|
4203
|
+
// get the offset in MS, given a date and timezone
|
|
4204
|
+
// timezone is an IANA name. e.g. 'America/New_York'
|
|
3986
4205
|
function offsetMS(dateObj, timeZone) {
|
|
3987
4206
|
const tzOffset = new Intl.DateTimeFormat('en-US', { timeZone, timeZoneName: 'longOffset' }).format(dateObj);
|
|
3988
4207
|
const offset = /GMT([+\-−])?(\d{1,2}):?(\d{0,2})?/.exec(tzOffset);
|
|
@@ -3991,11 +4210,16 @@ function offsetMS(dateObj, timeZone) {
|
|
|
3991
4210
|
const result = (((hours || 0) * 60) + 1 * (minutes || 0)) * 60 * 1000;
|
|
3992
4211
|
return sign === '-' ? result * -1 : result;
|
|
3993
4212
|
}
|
|
4213
|
+
|
|
3994
4214
|
function round(num, digits) {
|
|
3995
4215
|
const precision = 10 ** digits;
|
|
3996
4216
|
return Math.round(num * precision) / precision;
|
|
3997
4217
|
}
|
|
4218
|
+
|
|
3998
4219
|
const MS_IN_DAY = 24 * 60 * 60 * 1000;
|
|
4220
|
+
|
|
4221
|
+
// If we create a non-UTC date, then we need to adjust from the default JavaScript timezone
|
|
4222
|
+
// to the default timezone
|
|
3999
4223
|
function adjustTimeZone(dateObj, timeZone) {
|
|
4000
4224
|
if (dateObj === null) return null;
|
|
4001
4225
|
let baseDate = Date.UTC(
|
|
@@ -4008,10 +4232,32 @@ function adjustTimeZone(dateObj, timeZone) {
|
|
|
4008
4232
|
dateObj.getMilliseconds(),
|
|
4009
4233
|
);
|
|
4010
4234
|
baseDate += offsetMS(dateObj, timeZone);
|
|
4235
|
+
|
|
4236
|
+
// get the offset for the default JS environment
|
|
4237
|
+
// return days since the epoch
|
|
4011
4238
|
return new Date(baseDate);
|
|
4012
4239
|
}
|
|
4240
|
+
|
|
4013
4241
|
function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
4014
4242
|
return {
|
|
4243
|
+
/**
|
|
4244
|
+
* Returns the logical AND result of all parameters.
|
|
4245
|
+
* If the parameters are not boolean they will be cast to boolean as per the following rules
|
|
4246
|
+
* * null -> false
|
|
4247
|
+
* * number -> false if the number is 0, true otherwise
|
|
4248
|
+
* * string -> false if the string is empty, true otherwise. String "false" resolves to true
|
|
4249
|
+
* * array -> true
|
|
4250
|
+
* * object -> true
|
|
4251
|
+
* @param {any} firstOperand logical expression
|
|
4252
|
+
* @param {...any} [additionalOperands] any number of additional expressions
|
|
4253
|
+
* @returns {boolean} The logical result of applying AND to all parameters
|
|
4254
|
+
* @example
|
|
4255
|
+
* and(10 > 8, length('foo') < 5) // returns true
|
|
4256
|
+
* @example
|
|
4257
|
+
* and(`null`, length('foo') < 5) // returns false
|
|
4258
|
+
* @function
|
|
4259
|
+
* @category openFormula
|
|
4260
|
+
*/
|
|
4015
4261
|
and: {
|
|
4016
4262
|
_func: resolvedArgs => {
|
|
4017
4263
|
let result = !!valueOf(resolvedArgs[0]);
|
|
@@ -4022,6 +4268,17 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4022
4268
|
},
|
|
4023
4269
|
_signature: [{ types: [dataTypes.TYPE_ANY], variadic: true }],
|
|
4024
4270
|
},
|
|
4271
|
+
|
|
4272
|
+
/**
|
|
4273
|
+
* Returns a lower-case string of the `input` string using locale-specific mappings.
|
|
4274
|
+
* e.g. Strings with German lowercase letter 'ß' can be compared to 'ss'
|
|
4275
|
+
* @param {string} input string to casefold
|
|
4276
|
+
* @returns {string} A new string converted to lower case
|
|
4277
|
+
* @function casefold
|
|
4278
|
+
* @example
|
|
4279
|
+
* casefold('AbC') // returns 'abc'
|
|
4280
|
+
* @category JSONFormula
|
|
4281
|
+
*/
|
|
4025
4282
|
casefold: {
|
|
4026
4283
|
_func: (args, _data, interpreter) => {
|
|
4027
4284
|
const str = toString(args[0]);
|
|
@@ -4031,6 +4288,34 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4031
4288
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4032
4289
|
],
|
|
4033
4290
|
},
|
|
4291
|
+
|
|
4292
|
+
/**
|
|
4293
|
+
* Return difference between two date values.
|
|
4294
|
+
* @param {number} start_date The starting date.
|
|
4295
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
4296
|
+
* @param {number} end_date The end date -- must be greater or equal to start_date.
|
|
4297
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
4298
|
+
* @param {string} unit One of:
|
|
4299
|
+
* * `y` the number of whole years between start_date and end_date
|
|
4300
|
+
* * `m` the number of whole months between start_date and end_date.
|
|
4301
|
+
* * `d` the number of days between start_date and end_date
|
|
4302
|
+
* * `md` the number of days between start_date and end_date after subtracting whole months.
|
|
4303
|
+
* * `ym` the number of whole months between start_date and end_date
|
|
4304
|
+
* after subtracting whole years.
|
|
4305
|
+
* * `yd` the number of days between start_date and end_date, assuming start_date
|
|
4306
|
+
* and end_date were no more than one year apart
|
|
4307
|
+
* @returns {integer} The number of days/months/years difference
|
|
4308
|
+
* @function
|
|
4309
|
+
* @category openFormula
|
|
4310
|
+
* @example
|
|
4311
|
+
* datedif(datetime(2001, 1, 1), datetime(2003, 1, 1), 'y') // returns 2
|
|
4312
|
+
* @example
|
|
4313
|
+
* datedif(datetime(2001, 6, 1), datetime(2003, 8, 15), 'D') // returns 440
|
|
4314
|
+
* // 440 days between June 1, 2001, and August 15, 2002 (440)
|
|
4315
|
+
* @example
|
|
4316
|
+
* datedif(datetime(2001, 6, 1), datetime(2003, 8, 15), 'YD') // returns 440
|
|
4317
|
+
* // 75 days between June 1 and August 15, ignoring the years of the dates (75)
|
|
4318
|
+
*/
|
|
4034
4319
|
datedif: {
|
|
4035
4320
|
_func: args => {
|
|
4036
4321
|
const d1 = toNumber(args[0]);
|
|
@@ -4044,6 +4329,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4044
4329
|
const yearDiff = date2.getFullYear() - date1.getFullYear();
|
|
4045
4330
|
let monthDiff = date2.getMonth() - date1.getMonth();
|
|
4046
4331
|
const dayDiff = date2.getDate() - date1.getDate();
|
|
4332
|
+
|
|
4047
4333
|
if (unit === 'y') {
|
|
4048
4334
|
let y = yearDiff;
|
|
4049
4335
|
if (monthDiff < 0) y -= 1;
|
|
@@ -4072,6 +4358,32 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4072
4358
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4073
4359
|
],
|
|
4074
4360
|
},
|
|
4361
|
+
|
|
4362
|
+
/**
|
|
4363
|
+
* Return a date/time value.
|
|
4364
|
+
* @param {integer} year Integer value representing the year.
|
|
4365
|
+
* Values from 0 to 99 map to the years 1900 to 1999. All other values are the actual year
|
|
4366
|
+
* @param {integer} month Integer value representing the month, beginning with 1 for
|
|
4367
|
+
* January to 12 for December.
|
|
4368
|
+
* @param {integer} day Integer value representing the day of the month.
|
|
4369
|
+
* @param {integer} [hours] Integer value between 0 and 23 representing the hour of the day.
|
|
4370
|
+
* Defaults to 0.
|
|
4371
|
+
* @param {integer} [minutes] Integer value representing the minute segment of a time.
|
|
4372
|
+
* The default is 0 minutes past the hour.
|
|
4373
|
+
* @param {integer} [seconds] Integer value representing the second segment of a time.
|
|
4374
|
+
* The default is 0 seconds past the minute.
|
|
4375
|
+
* @param {integer} [milliseconds] Integer value representing the millisecond segment of a time.
|
|
4376
|
+
* The default is 0 milliseconds past the second.
|
|
4377
|
+
* @param {string} [timeZoneName] according to IANA time zone names. e.g. "America/Toronto"
|
|
4378
|
+
* @returns {number} A date/time value represented by number of seconds since 1 January 1970.
|
|
4379
|
+
* @kind function
|
|
4380
|
+
* @function
|
|
4381
|
+
* @category JSONFormula
|
|
4382
|
+
* @example
|
|
4383
|
+
* datetime(2010, 10, 10) // returns representation of October 10, 2010
|
|
4384
|
+
* @example
|
|
4385
|
+
* datetime(2010, 2, 28) // returns representation of February 28, 2010
|
|
4386
|
+
*/
|
|
4075
4387
|
datetime: {
|
|
4076
4388
|
_func: args => {
|
|
4077
4389
|
const year = toNumber(args[0]);
|
|
@@ -4082,6 +4394,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4082
4394
|
const seconds = args.length > 5 ? toNumber(args[5]) : 0;
|
|
4083
4395
|
const ms = args.length > 6 ? toNumber(args[6]) : 0;
|
|
4084
4396
|
const tz = args.length > 7 ? toString(args[7]) : null;
|
|
4397
|
+
// javascript months starts from 0
|
|
4085
4398
|
let jsDate = new Date(year, month - 1, day, hours, minutes, seconds, ms);
|
|
4086
4399
|
if (tz) {
|
|
4087
4400
|
jsDate = adjustTimeZone(jsDate, tz);
|
|
@@ -4099,6 +4412,18 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4099
4412
|
{ types: [dataTypes.TYPE_STRING], optional: true },
|
|
4100
4413
|
],
|
|
4101
4414
|
},
|
|
4415
|
+
|
|
4416
|
+
/**
|
|
4417
|
+
* Returns the day of a date, represented by a serial number.
|
|
4418
|
+
* The day is given as an integer ranging from 1 to 31.
|
|
4419
|
+
* @param {number} The date of the day you are trying to find.
|
|
4420
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
4421
|
+
* @return {number}
|
|
4422
|
+
* @function day
|
|
4423
|
+
* @category openFormula
|
|
4424
|
+
* @example
|
|
4425
|
+
* day(datetime(2008,5,23)) //returns 23
|
|
4426
|
+
*/
|
|
4102
4427
|
day: {
|
|
4103
4428
|
_func: args => {
|
|
4104
4429
|
const date = toNumber(args[0]);
|
|
@@ -4109,6 +4434,19 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4109
4434
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4110
4435
|
],
|
|
4111
4436
|
},
|
|
4437
|
+
|
|
4438
|
+
/**
|
|
4439
|
+
* Searches a nested hierarchy of objects to return an array of elements that match a `name`.
|
|
4440
|
+
* The name can be either a key into a map or an array index.
|
|
4441
|
+
* This is similar to the JSONPath deep scan operator (..)
|
|
4442
|
+
* @param {object} object The starting object or array where we start the search
|
|
4443
|
+
* @param {string} name The name (or index position) of the elements to find
|
|
4444
|
+
* @returns {any}
|
|
4445
|
+
* @function
|
|
4446
|
+
* @category JSONFormula
|
|
4447
|
+
* @example
|
|
4448
|
+
* deepScan({a : {b1 : {c : 2}, b2 : {c : 3}}}, 'c') //returns [2, 3]
|
|
4449
|
+
*/
|
|
4112
4450
|
deepScan: {
|
|
4113
4451
|
_func: resolvedArgs => {
|
|
4114
4452
|
const [source, n] = resolvedArgs;
|
|
@@ -4129,6 +4467,16 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4129
4467
|
{ types: [dataTypes.TYPE_STRING, dataTypes.TYPE_NUMBER] },
|
|
4130
4468
|
],
|
|
4131
4469
|
},
|
|
4470
|
+
|
|
4471
|
+
/**
|
|
4472
|
+
* returns an array of a given object's property `[key, value]` pairs.
|
|
4473
|
+
* @param {object} obj Object whose `[key, value]` pairs need to be extracted
|
|
4474
|
+
* @returns {any[]} an array of [key, value] pairs
|
|
4475
|
+
* @function entries
|
|
4476
|
+
* @category JSONFormula
|
|
4477
|
+
* @example
|
|
4478
|
+
* entries({a: 1, b: 2}) //returns [['a', 1], ['b', 2]]
|
|
4479
|
+
*/
|
|
4132
4480
|
entries: {
|
|
4133
4481
|
_func: args => {
|
|
4134
4482
|
const obj = valueOf(args[0]);
|
|
@@ -4146,11 +4494,27 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4146
4494
|
},
|
|
4147
4495
|
],
|
|
4148
4496
|
},
|
|
4497
|
+
|
|
4498
|
+
/**
|
|
4499
|
+
* Returns the serial number of the end of a month, given `startDate` plus `monthAdd` months
|
|
4500
|
+
* @param {number} startDate The base date to start from.
|
|
4501
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
4502
|
+
* @param {integer} monthAdd Number of months to add to start date
|
|
4503
|
+
* @return {integer} the number of days in the computed month
|
|
4504
|
+
* @function
|
|
4505
|
+
* @category openFormula
|
|
4506
|
+
* @example
|
|
4507
|
+
* eomonth(datetime(2011, 1, 1), 1) //returns datetime(2011, 2, 28)
|
|
4508
|
+
* @example
|
|
4509
|
+
* eomonth(datetime(2011, 1, 1), -3) //returns datetime(2010, 10, 31)
|
|
4510
|
+
*/
|
|
4149
4511
|
eomonth: {
|
|
4150
4512
|
_func: args => {
|
|
4151
4513
|
const date = toNumber(args[0]);
|
|
4152
4514
|
const months = toNumber(args[1]);
|
|
4153
4515
|
const jsDate = new Date(date * MS_IN_DAY);
|
|
4516
|
+
// We can give the constructor a month value > 11 and it will increment the years
|
|
4517
|
+
// Since day is 1-based, giving zero will yield the last day of the previous month
|
|
4154
4518
|
const newDate = new Date(jsDate.getFullYear(), jsDate.getMonth() + months + 1, 0);
|
|
4155
4519
|
return newDate.getTime() / MS_IN_DAY;
|
|
4156
4520
|
},
|
|
@@ -4159,6 +4523,16 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4159
4523
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4160
4524
|
],
|
|
4161
4525
|
},
|
|
4526
|
+
|
|
4527
|
+
/**
|
|
4528
|
+
* Returns e (the base of natural logarithms) raised to a power x. (i.e. e<sup>x</sup>)
|
|
4529
|
+
* @param x {number} A numeric expression representing the power of e.
|
|
4530
|
+
* @returns {number} e (the base of natural logarithms) raised to a power x
|
|
4531
|
+
* @function exp
|
|
4532
|
+
* @category openFormula
|
|
4533
|
+
* @example
|
|
4534
|
+
* exp(10) //returns e^10
|
|
4535
|
+
*/
|
|
4162
4536
|
exp: {
|
|
4163
4537
|
_func: args => {
|
|
4164
4538
|
const value = toNumber(args[0]);
|
|
@@ -4168,10 +4542,37 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4168
4542
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4169
4543
|
],
|
|
4170
4544
|
},
|
|
4545
|
+
|
|
4546
|
+
/**
|
|
4547
|
+
* Return constant boolean false value.
|
|
4548
|
+
* Note that expressions may also use the JSON literal false: `` `false` ``
|
|
4549
|
+
* @returns {boolean} constant boolean value `false`
|
|
4550
|
+
* @function
|
|
4551
|
+
* @category openFormula
|
|
4552
|
+
*/
|
|
4171
4553
|
false: {
|
|
4172
4554
|
_func: () => false,
|
|
4173
4555
|
_signature: [],
|
|
4174
4556
|
},
|
|
4557
|
+
|
|
4558
|
+
/**
|
|
4559
|
+
* finds and returns the index of query in text from a start position
|
|
4560
|
+
* @param {string} query string to search
|
|
4561
|
+
* @param {string} text text in which the query has to be searched
|
|
4562
|
+
* @param {number} [start] starting position: defaults to 0
|
|
4563
|
+
* @returns {number|null} the index of the query to be searched in the text. If not found
|
|
4564
|
+
* returns null
|
|
4565
|
+
* @function
|
|
4566
|
+
* @category openFormula
|
|
4567
|
+
* @example
|
|
4568
|
+
* find('m', 'abm') //returns 2
|
|
4569
|
+
* @example
|
|
4570
|
+
* find('M', 'abMcdM', 3) //returns 2
|
|
4571
|
+
* @example
|
|
4572
|
+
* find('M', 'ab') //returns `null`
|
|
4573
|
+
* @example
|
|
4574
|
+
* find('M', 'abMcdM', 2) //returns 2
|
|
4575
|
+
*/
|
|
4175
4576
|
find: {
|
|
4176
4577
|
_func: args => {
|
|
4177
4578
|
const query = toString(args[0]);
|
|
@@ -4189,6 +4590,16 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4189
4590
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4190
4591
|
],
|
|
4191
4592
|
},
|
|
4593
|
+
|
|
4594
|
+
/**
|
|
4595
|
+
* returns an object by transforming a list of key-value `pairs` into an object.
|
|
4596
|
+
* @param {any[]} pairs list of key-value pairs to create the object from
|
|
4597
|
+
* @returns {object}
|
|
4598
|
+
* @category JSONFormula
|
|
4599
|
+
* @function fromEntries
|
|
4600
|
+
* @example
|
|
4601
|
+
* fromEntries([['a', 1], ['b', 2]]) //returns {a: 1, b: 2}
|
|
4602
|
+
*/
|
|
4192
4603
|
fromEntries: {
|
|
4193
4604
|
_func: args => {
|
|
4194
4605
|
const array = args[0];
|
|
@@ -4198,19 +4609,51 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4198
4609
|
{ types: [dataTypes.TYPE_ARRAY_ARRAY] },
|
|
4199
4610
|
],
|
|
4200
4611
|
},
|
|
4612
|
+
|
|
4613
|
+
/**
|
|
4614
|
+
* Extract the hour (0 through 23) from a time/datetime representation
|
|
4615
|
+
* @param {number} The datetime/time for which the hour is to be returned.
|
|
4616
|
+
* Dates should be specified using the [datetime]{@link datetime} or [time]{@link time} function
|
|
4617
|
+
* @return {number}
|
|
4618
|
+
* @function hour
|
|
4619
|
+
* @category openFormula
|
|
4620
|
+
* @example
|
|
4621
|
+
* hour(datetime(2008,5,23,12, 0, 0)) //returns 12
|
|
4622
|
+
* hour(time(12, 0, 0)) //returns 12
|
|
4623
|
+
*/
|
|
4201
4624
|
hour: {
|
|
4202
4625
|
_func: args => {
|
|
4626
|
+
// grab just the fraction part
|
|
4203
4627
|
const time = toNumber(args[0]) % 1;
|
|
4204
4628
|
if (time < 0) {
|
|
4205
4629
|
return null;
|
|
4206
4630
|
}
|
|
4631
|
+
// Normally we'd round to 15 digits, but since we're also multiplying by 24,
|
|
4632
|
+
// a reasonable precision is around 14 digits.
|
|
4633
|
+
|
|
4207
4634
|
const hour = round(time * 24, 14);
|
|
4635
|
+
|
|
4208
4636
|
return Math.floor(hour % 24);
|
|
4209
4637
|
},
|
|
4210
4638
|
_signature: [
|
|
4211
4639
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4212
4640
|
],
|
|
4213
4641
|
},
|
|
4642
|
+
|
|
4643
|
+
/**
|
|
4644
|
+
* Return one of two values `result1` or `result2`, depending on the `condition`
|
|
4645
|
+
* @returns {boolean} True
|
|
4646
|
+
* @param {any} condition logical expression to evaluate
|
|
4647
|
+
* @param {any} result1 if logical condition is true
|
|
4648
|
+
* @param {any} result2 if logical condition is false
|
|
4649
|
+
* @return {any} either result1 or result2
|
|
4650
|
+
* @function
|
|
4651
|
+
* @category openFormula
|
|
4652
|
+
* @example
|
|
4653
|
+
* if(true(), 1, 2) // returns 1
|
|
4654
|
+
* @example
|
|
4655
|
+
* if(false(), 1, 2) // returns 2
|
|
4656
|
+
*/
|
|
4214
4657
|
if: {
|
|
4215
4658
|
_func: (unresolvedArgs, data, interpreter) => {
|
|
4216
4659
|
const conditionNode = unresolvedArgs[0];
|
|
@@ -4227,6 +4670,22 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4227
4670
|
{ types: [dataTypes.TYPE_ANY] },
|
|
4228
4671
|
{ types: [dataTypes.TYPE_ANY] }],
|
|
4229
4672
|
},
|
|
4673
|
+
|
|
4674
|
+
/**
|
|
4675
|
+
* Return a selected number of text characters from the left or
|
|
4676
|
+
* in case of array selected number of elements from the start
|
|
4677
|
+
* @param {string|array} subject The text/array of characters/elements to extract.
|
|
4678
|
+
* @param {number} [elements] number of elements to pick. Defaults to 1
|
|
4679
|
+
* @return {string|array}
|
|
4680
|
+
* @function left
|
|
4681
|
+
* @category openFormula
|
|
4682
|
+
* @example
|
|
4683
|
+
* left('Sale Price', 4) //returns 'Sale'
|
|
4684
|
+
* @example
|
|
4685
|
+
* left('Sweden') // returns 'S'
|
|
4686
|
+
* @example
|
|
4687
|
+
* left([4, 5, 6], 2) // returns [4, 5]
|
|
4688
|
+
*/
|
|
4230
4689
|
left: {
|
|
4231
4690
|
_func: args => {
|
|
4232
4691
|
const numEntries = args.length > 1 ? toNumber(args[1]) : 1;
|
|
@@ -4242,6 +4701,18 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4242
4701
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4243
4702
|
],
|
|
4244
4703
|
},
|
|
4704
|
+
|
|
4705
|
+
/**
|
|
4706
|
+
* Converts all the alphabetic characters in a string to lowercase. If the value
|
|
4707
|
+
* is not a string it will be converted into string
|
|
4708
|
+
* using the default toString method
|
|
4709
|
+
* @param {string} input input string
|
|
4710
|
+
* @returns {string} the lower case value of the input string
|
|
4711
|
+
* @function lower
|
|
4712
|
+
* @category openFormula
|
|
4713
|
+
* @example
|
|
4714
|
+
* lower('E. E. Cummings') //returns e. e. cummings
|
|
4715
|
+
*/
|
|
4245
4716
|
lower: {
|
|
4246
4717
|
_func: args => {
|
|
4247
4718
|
const value = toString(args[0]);
|
|
@@ -4251,6 +4722,27 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4251
4722
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4252
4723
|
],
|
|
4253
4724
|
},
|
|
4725
|
+
|
|
4726
|
+
/**
|
|
4727
|
+
* Returns extracted text, given an original text, starting position, and length.
|
|
4728
|
+
* or in case of array, extracts a subset of the array from start till the length
|
|
4729
|
+
* number of elements.
|
|
4730
|
+
* Returns null if the `startPos` is greater than the length of the array
|
|
4731
|
+
* @param {string|array} subject the text string or array of characters or elements to extract.
|
|
4732
|
+
* @param {number} startPos the position of the first character or element to extract.
|
|
4733
|
+
* The position starts with 0
|
|
4734
|
+
* @param {number} length The number of characters or elements to return from text. If it
|
|
4735
|
+
* is greater then the length of `subject` the argument is set to the length of the subject.
|
|
4736
|
+
* @return {string|array}
|
|
4737
|
+
* @function mid
|
|
4738
|
+
* @category openFormula
|
|
4739
|
+
* @example
|
|
4740
|
+
* mid("Fluid Flow",1,5) //returns 'Fluid'
|
|
4741
|
+
* @example
|
|
4742
|
+
* mid("Fluid Flow",7,20) //returns 'Flow'
|
|
4743
|
+
* @example
|
|
4744
|
+
* mid("Fluid Flow",20,5) //returns `null`
|
|
4745
|
+
*/
|
|
4254
4746
|
mid: {
|
|
4255
4747
|
_func: args => {
|
|
4256
4748
|
const startPos = toNumber(args[1]);
|
|
@@ -4268,12 +4760,27 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4268
4760
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4269
4761
|
],
|
|
4270
4762
|
},
|
|
4763
|
+
|
|
4764
|
+
/**
|
|
4765
|
+
* Extract the minute (0 through 59) from a time/datetime representation
|
|
4766
|
+
* @param {number} The datetime/time for which the minute is to be returned.
|
|
4767
|
+
* Dates should be specified using the [datetime]{@link datetime} or [time]{@link time} function
|
|
4768
|
+
* @return {number}
|
|
4769
|
+
* @function minute
|
|
4770
|
+
* @category openFormula
|
|
4771
|
+
* @example
|
|
4772
|
+
* month(datetime(2008,5,23,12, 10, 0)) //returns 10
|
|
4773
|
+
* month(time(12, 10, 0)) //returns 10
|
|
4774
|
+
*/
|
|
4271
4775
|
minute: {
|
|
4272
4776
|
_func: args => {
|
|
4273
4777
|
const time = toNumber(args[0]) % 1;
|
|
4274
4778
|
if (time < 0) {
|
|
4275
4779
|
return null;
|
|
4276
4780
|
}
|
|
4781
|
+
|
|
4782
|
+
// Normally we'd round to 15 digits, but since we're also multiplying by 1440,
|
|
4783
|
+
// a reasonable precision is around 10 digits.
|
|
4277
4784
|
const minute = Math.round(time * 1440, 10);
|
|
4278
4785
|
return Math.floor(minute % 60);
|
|
4279
4786
|
},
|
|
@@ -4281,6 +4788,20 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4281
4788
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4282
4789
|
],
|
|
4283
4790
|
},
|
|
4791
|
+
|
|
4792
|
+
/**
|
|
4793
|
+
* Return the remainder when one number is divided by another number.
|
|
4794
|
+
* The sign is the same as divisor
|
|
4795
|
+
* @param {number} dividend The number for which to find the remainder.
|
|
4796
|
+
* @param {number} divisor The number by which to divide number.
|
|
4797
|
+
* @return {number} Computes the remainder of `dividend`/`divisor`.
|
|
4798
|
+
* @function mod
|
|
4799
|
+
* @category openFormula
|
|
4800
|
+
* @example
|
|
4801
|
+
* mod(3, 2) //returns 1
|
|
4802
|
+
* @example
|
|
4803
|
+
* mod(-3, 2) //returns 1
|
|
4804
|
+
*/
|
|
4284
4805
|
mod: {
|
|
4285
4806
|
_func: args => {
|
|
4286
4807
|
const p1 = toNumber(args[0]);
|
|
@@ -4292,28 +4813,97 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4292
4813
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4293
4814
|
],
|
|
4294
4815
|
},
|
|
4816
|
+
|
|
4817
|
+
/**
|
|
4818
|
+
* Returns the month of a date represented by a serial number.
|
|
4819
|
+
* The month is given as an integer, ranging from 1 (January) to 12 (December).
|
|
4820
|
+
* @param {number} The date for which the month is to be returned.
|
|
4821
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
4822
|
+
* @return {number}
|
|
4823
|
+
* @function month
|
|
4824
|
+
* @category openFormula
|
|
4825
|
+
* @example
|
|
4826
|
+
* month(datetime(2008,5,23)) //returns 5
|
|
4827
|
+
*/
|
|
4295
4828
|
month: {
|
|
4296
4829
|
_func: args => {
|
|
4297
4830
|
const date = toNumber(args[0]);
|
|
4298
4831
|
const jsDate = new Date(date * MS_IN_DAY);
|
|
4832
|
+
// javascript months start from 0ß
|
|
4299
4833
|
return jsDate.getMonth() + 1;
|
|
4300
4834
|
},
|
|
4301
4835
|
_signature: [
|
|
4302
4836
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4303
4837
|
],
|
|
4304
4838
|
},
|
|
4839
|
+
|
|
4840
|
+
/**
|
|
4841
|
+
* Compute logical NOT of a `value`. If the parameter is not boolean it will be cast to boolean
|
|
4842
|
+
* as per the following rules
|
|
4843
|
+
* * null -> false
|
|
4844
|
+
* * number -> false if the number is 0, true otherwise
|
|
4845
|
+
* * string -> false if the string is empty, true otherwise. String "false" resolves to true
|
|
4846
|
+
* * array -> true
|
|
4847
|
+
* * object -> true
|
|
4848
|
+
* Note that it is also possible to use the logical and operator: `A && B`
|
|
4849
|
+
* @param {any} value - any data type
|
|
4850
|
+
* @returns {boolean} The logical NOT applied to the input parameter
|
|
4851
|
+
* @example
|
|
4852
|
+
* not(length('bar') > 0) // returns false
|
|
4853
|
+
* @example
|
|
4854
|
+
* not(false()) // returns true
|
|
4855
|
+
* @example
|
|
4856
|
+
* not('abcd') // returns false
|
|
4857
|
+
* @example
|
|
4858
|
+
* not('') // returns true
|
|
4859
|
+
* @function
|
|
4860
|
+
* @category openFormula
|
|
4861
|
+
*/
|
|
4305
4862
|
not: {
|
|
4306
4863
|
_func: resolveArgs => !valueOf(resolveArgs[0]),
|
|
4307
4864
|
_signature: [{ types: [dataTypes.TYPE_ANY] }],
|
|
4308
4865
|
},
|
|
4866
|
+
|
|
4867
|
+
/**
|
|
4868
|
+
* returns the time since epoch with days as exponent and time of day as fraction
|
|
4869
|
+
* @return {number} representation of current time as a number
|
|
4870
|
+
* @function now
|
|
4871
|
+
* @category openFormula
|
|
4872
|
+
*/
|
|
4309
4873
|
now: {
|
|
4310
4874
|
_func: () => Date.now() / MS_IN_DAY,
|
|
4311
4875
|
_signature: [],
|
|
4312
4876
|
},
|
|
4877
|
+
|
|
4878
|
+
/**
|
|
4879
|
+
* Return constant null value.
|
|
4880
|
+
* Note that expressions may also use the JSON literal null: `` `null` ``
|
|
4881
|
+
* @returns {boolean} True
|
|
4882
|
+
* @function
|
|
4883
|
+
* @category JSONFormula
|
|
4884
|
+
*/
|
|
4313
4885
|
null: {
|
|
4314
4886
|
_func: () => null,
|
|
4315
4887
|
_signature: [],
|
|
4316
4888
|
},
|
|
4889
|
+
|
|
4890
|
+
/**
|
|
4891
|
+
* Returns the logical OR result of two parameters.
|
|
4892
|
+
* If the parameters are not boolean they will be cast to boolean as per the following rules
|
|
4893
|
+
* * null -> false
|
|
4894
|
+
* * number -> false if the number is 0, true otherwise
|
|
4895
|
+
* * string -> false if the string is empty, true otherwise. String "false" resolves to true
|
|
4896
|
+
* * array -> true
|
|
4897
|
+
* * object -> true
|
|
4898
|
+
* @param {any} first logical expression
|
|
4899
|
+
* @param {...any} [operand] any number of additional expressions
|
|
4900
|
+
* @returns {boolean} The logical result of applying OR to all parameters
|
|
4901
|
+
* @example
|
|
4902
|
+
* or((x / 2) == y, (y * 2) == x)
|
|
4903
|
+
* // true
|
|
4904
|
+
* @function
|
|
4905
|
+
* @category openFormula
|
|
4906
|
+
*/
|
|
4317
4907
|
or: {
|
|
4318
4908
|
_func: resolvedArgs => {
|
|
4319
4909
|
let result = !!valueOf(resolvedArgs[0]);
|
|
@@ -4324,6 +4914,17 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4324
4914
|
},
|
|
4325
4915
|
_signature: [{ types: [dataTypes.TYPE_ANY], variadic: true }],
|
|
4326
4916
|
},
|
|
4917
|
+
|
|
4918
|
+
/**
|
|
4919
|
+
* Computes `a` raised to a power `x`. (a<sup>x</sup>)
|
|
4920
|
+
* @param {number} a The base number. It can be any real number.
|
|
4921
|
+
* @param {number} x The exponent to which the base number is raised.
|
|
4922
|
+
* @return {number}
|
|
4923
|
+
* @function power
|
|
4924
|
+
* @category openFormula
|
|
4925
|
+
* @example
|
|
4926
|
+
* power(10, 2) //returns 100 (10 raised to power 2)
|
|
4927
|
+
*/
|
|
4327
4928
|
power: {
|
|
4328
4929
|
_func: args => {
|
|
4329
4930
|
const base = toNumber(args[0]);
|
|
@@ -4335,6 +4936,21 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4335
4936
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4336
4937
|
],
|
|
4337
4938
|
},
|
|
4939
|
+
|
|
4940
|
+
/**
|
|
4941
|
+
* Return the input string with the first letter of each word converted to an
|
|
4942
|
+
* uppercase letter and the rest of the letters in the word converted to lowercase.
|
|
4943
|
+
* @param {string} text the text to partially capitalize.
|
|
4944
|
+
* @returns {string}
|
|
4945
|
+
* @function proper
|
|
4946
|
+
* @category openFormula
|
|
4947
|
+
* @example
|
|
4948
|
+
* proper('this is a TITLE') //returns 'This Is A Title'
|
|
4949
|
+
* @example
|
|
4950
|
+
* proper('2-way street') //returns '2-Way Street'
|
|
4951
|
+
* @example
|
|
4952
|
+
* proper('76BudGet') //returns '76Budget'
|
|
4953
|
+
*/
|
|
4338
4954
|
proper: {
|
|
4339
4955
|
_func: args => {
|
|
4340
4956
|
const text = toString(args[0]);
|
|
@@ -4347,6 +4963,24 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4347
4963
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4348
4964
|
],
|
|
4349
4965
|
},
|
|
4966
|
+
|
|
4967
|
+
/**
|
|
4968
|
+
* Returns text where an old text is substituted at a given start position and
|
|
4969
|
+
* length, with a new text.
|
|
4970
|
+
* @param {string} text original text
|
|
4971
|
+
* @param {number} start index in the original text from where to begin the replacement.
|
|
4972
|
+
* @param {number} length number of characters to be replaced
|
|
4973
|
+
* @param {string} replacement string to replace at the start index
|
|
4974
|
+
* @returns {string}
|
|
4975
|
+
* @function replace
|
|
4976
|
+
* @category openFormula
|
|
4977
|
+
* @example
|
|
4978
|
+
* replace('abcdefghijk', 6, 5, '*') //returns abcde*k
|
|
4979
|
+
* @example
|
|
4980
|
+
* replace('2009',3,2,'10') //returns 2010
|
|
4981
|
+
* @example
|
|
4982
|
+
* replace('123456',1,3,'@') //returns @456
|
|
4983
|
+
*/
|
|
4350
4984
|
replace: {
|
|
4351
4985
|
_func: args => {
|
|
4352
4986
|
const oldText = toString(args[0]);
|
|
@@ -4356,6 +4990,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4356
4990
|
if (startNum < 0) {
|
|
4357
4991
|
return null;
|
|
4358
4992
|
}
|
|
4993
|
+
|
|
4359
4994
|
const lhs = oldText.substr(0, startNum);
|
|
4360
4995
|
const rhs = oldText.substr(startNum + numChars);
|
|
4361
4996
|
return lhs + newText + rhs;
|
|
@@ -4367,6 +5002,17 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4367
5002
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4368
5003
|
],
|
|
4369
5004
|
},
|
|
5005
|
+
|
|
5006
|
+
/**
|
|
5007
|
+
* Return text repeated Count times.
|
|
5008
|
+
* @param {string} text text to repeat
|
|
5009
|
+
* @param {number} count number of times to repeat the text
|
|
5010
|
+
* @returns {string}
|
|
5011
|
+
* @function rept
|
|
5012
|
+
* @category openFormula
|
|
5013
|
+
* @example
|
|
5014
|
+
* rept('x', 5) //returns 'xxxxx'
|
|
5015
|
+
*/
|
|
4370
5016
|
rept: {
|
|
4371
5017
|
_func: args => {
|
|
4372
5018
|
const text = toString(args[0]);
|
|
@@ -4381,6 +5027,23 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4381
5027
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4382
5028
|
],
|
|
4383
5029
|
},
|
|
5030
|
+
|
|
5031
|
+
/**
|
|
5032
|
+
* Return a selected number of text characters from the right of a `subject` or
|
|
5033
|
+
* in case of array selected number of elements from the end of `subject` array
|
|
5034
|
+
* Returns null if the number of elements is less than 0
|
|
5035
|
+
* @param {string|array} subject The text/array containing the characters/elements to extract.
|
|
5036
|
+
* @param {number} [elements] number of elements to pick. Defaults to 1
|
|
5037
|
+
* @return {string|array}
|
|
5038
|
+
* @function right
|
|
5039
|
+
* @category openFormula
|
|
5040
|
+
* @example
|
|
5041
|
+
* right('Sale Price', 4) //returns 'rice'
|
|
5042
|
+
* @example
|
|
5043
|
+
* left('Sweden') // returns 'n'
|
|
5044
|
+
* @example
|
|
5045
|
+
* left([4, 5, 6], 2) // returns [5, 6]
|
|
5046
|
+
*/
|
|
4384
5047
|
right: {
|
|
4385
5048
|
_func: args => {
|
|
4386
5049
|
const numEntries = args.length > 1 ? toNumber(args[1]) : 1;
|
|
@@ -4398,6 +5061,29 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4398
5061
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4399
5062
|
],
|
|
4400
5063
|
},
|
|
5064
|
+
|
|
5065
|
+
/**
|
|
5066
|
+
* Round a number to a specified `precision`.
|
|
5067
|
+
* ### Remarks
|
|
5068
|
+
* * If `precision` is greater than zero, round to the specified number of decimal places.
|
|
5069
|
+
* * If `precision` is 0, round to the nearest integer.
|
|
5070
|
+
* * If `precision` is less than 0, round to the left of the decimal point.
|
|
5071
|
+
* @param {number} num number to round off
|
|
5072
|
+
* @param {number} precision number is rounded to the specified precision.
|
|
5073
|
+
* @returns {number}
|
|
5074
|
+
* @function round
|
|
5075
|
+
* @category openFormula
|
|
5076
|
+
* @example
|
|
5077
|
+
* round(2.15, 1) //returns 2.2
|
|
5078
|
+
* @example
|
|
5079
|
+
* round(626.3,-3) //returns 1000 (Rounds 626.3 to the nearest multiple of 1000)
|
|
5080
|
+
* @example
|
|
5081
|
+
* round(626.3, 0) //returns 626
|
|
5082
|
+
* @example
|
|
5083
|
+
* round(1.98,-1) //returns 0 (Rounds 1.98 to the nearest multiple of 10)
|
|
5084
|
+
* @example
|
|
5085
|
+
* round(-50.55,-2) // -100 (round -50.55 to the nearest multiple of 100)
|
|
5086
|
+
*/
|
|
4401
5087
|
round: {
|
|
4402
5088
|
_func: args => {
|
|
4403
5089
|
const number = toNumber(args[0]);
|
|
@@ -4409,15 +5095,38 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4409
5095
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4410
5096
|
],
|
|
4411
5097
|
},
|
|
5098
|
+
|
|
5099
|
+
/**
|
|
5100
|
+
* Perform a wildcard search. The search is case-sensitive and supports two forms of wildcards:
|
|
5101
|
+
* "*" finds a a sequence of characters and "?" finds a single character.
|
|
5102
|
+
* To use "*" or "?" as text values, precede them with a tilde ("~") character.
|
|
5103
|
+
* Note that the wildcard search is not greedy.
|
|
5104
|
+
* e.g. search('a*b', 'abb') will return [0, 'ab'] Not [0, 'abb']
|
|
5105
|
+
* @param {string} findText the search string -- which may include wild cards.
|
|
5106
|
+
* @param {string} withinText The string to search.
|
|
5107
|
+
* @param {integer} startPos The zero-based position of withinText to start searching.
|
|
5108
|
+
* Defaults to zero.
|
|
5109
|
+
* @returns {array} returns an array with two values:
|
|
5110
|
+
* The start position of the found text and the text string that was found.
|
|
5111
|
+
* If a match was not found, an empty array is returned.
|
|
5112
|
+
* @function search
|
|
5113
|
+
* @category openFormula
|
|
5114
|
+
* @example
|
|
5115
|
+
* search('a?c', 'acabc') //returns [2, 'abc']
|
|
5116
|
+
*/
|
|
4412
5117
|
search: {
|
|
4413
5118
|
_func: args => {
|
|
4414
5119
|
const findText = toString(args[0]);
|
|
4415
5120
|
const withinText = toString(args[1]);
|
|
4416
5121
|
const startPos = toNumber(args[2]);
|
|
4417
5122
|
if (findText === null || withinText === null || withinText.length === 0) return [];
|
|
5123
|
+
// escape all characters that would otherwise create a regular expression
|
|
4418
5124
|
const reString = findText.replace(/([[.\\^$()+{])/g, '\\$1')
|
|
5125
|
+
// add the single character wildcard
|
|
4419
5126
|
.replace(/~?\?/g, match => match === '~?' ? '\\?' : '.')
|
|
5127
|
+
// add the multi-character wildcard
|
|
4420
5128
|
.replace(/~?\*/g, match => match === '~*' ? '\\*' : '.*?')
|
|
5129
|
+
// get rid of the escape characters
|
|
4421
5130
|
.replace(/~~/g, '~');
|
|
4422
5131
|
const re = new RegExp(reString);
|
|
4423
5132
|
const result = withinText.substring(startPos).match(re);
|
|
@@ -4429,13 +5138,28 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4429
5138
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4430
5139
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4431
5140
|
],
|
|
5141
|
+
|
|
4432
5142
|
},
|
|
5143
|
+
/**
|
|
5144
|
+
* Extract the second (0 through 59) from a time/datetime representation
|
|
5145
|
+
* @param {number} The datetime/time for which the second is to be returned.
|
|
5146
|
+
* Dates should be specified using the [datetime]{@link datetime} or [time]{@link time} function
|
|
5147
|
+
* @return {number}
|
|
5148
|
+
* @function second
|
|
5149
|
+
* @category openFormula
|
|
5150
|
+
* @example
|
|
5151
|
+
* second(datetime(2008,5,23,12, 10, 53)) //returns 53
|
|
5152
|
+
* second(time(12, 10, 53)) //returns 53
|
|
5153
|
+
*/
|
|
4433
5154
|
second: {
|
|
4434
5155
|
_func: args => {
|
|
4435
5156
|
const time = toNumber(args[0]) % 1;
|
|
4436
5157
|
if (time < 0) {
|
|
4437
5158
|
return null;
|
|
4438
5159
|
}
|
|
5160
|
+
|
|
5161
|
+
// Normally we'd round to 15 digits, but since we're also multiplying by 86400,
|
|
5162
|
+
// a reasonable precision is around 10 digits.
|
|
4439
5163
|
const seconds = round(time * 86400, 10);
|
|
4440
5164
|
return Math.floor(seconds % 60);
|
|
4441
5165
|
},
|
|
@@ -4443,6 +5167,19 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4443
5167
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4444
5168
|
],
|
|
4445
5169
|
},
|
|
5170
|
+
|
|
5171
|
+
/**
|
|
5172
|
+
* split a string into an array, given a separator
|
|
5173
|
+
* @param {string} string string to split
|
|
5174
|
+
* @param {string} separator separator where the split should occur
|
|
5175
|
+
* @return {string[]}
|
|
5176
|
+
* @function split
|
|
5177
|
+
* @category openFormula
|
|
5178
|
+
* @example
|
|
5179
|
+
* split('abcdef', '') //returns ['a', 'b', 'c', 'd', 'e', 'f']
|
|
5180
|
+
* @example
|
|
5181
|
+
* split('abcdef', 'e') //returns ['abcd', 'f']
|
|
5182
|
+
*/
|
|
4446
5183
|
split: {
|
|
4447
5184
|
_func: args => {
|
|
4448
5185
|
const str = toString(args[0]);
|
|
@@ -4454,6 +5191,16 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4454
5191
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4455
5192
|
],
|
|
4456
5193
|
},
|
|
5194
|
+
|
|
5195
|
+
/**
|
|
5196
|
+
* Return the square root of a number
|
|
5197
|
+
* @param {number} num number whose square root has to be calculated
|
|
5198
|
+
* @return {number}
|
|
5199
|
+
* @function sqrt
|
|
5200
|
+
* @category openFormula
|
|
5201
|
+
* @example
|
|
5202
|
+
* sqrt(4) //returns 2
|
|
5203
|
+
*/
|
|
4457
5204
|
sqrt: {
|
|
4458
5205
|
_func: args => {
|
|
4459
5206
|
const result = Math.sqrt(toNumber(args[0]));
|
|
@@ -4466,6 +5213,20 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4466
5213
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4467
5214
|
],
|
|
4468
5215
|
},
|
|
5216
|
+
|
|
5217
|
+
/**
|
|
5218
|
+
* Estimates standard deviation based on a sample.
|
|
5219
|
+
* `stdev` assumes that its arguments are a sample of the entire population.
|
|
5220
|
+
* If your data represents a entire population,
|
|
5221
|
+
* then compute the standard deviation using [stdevp]{@link stdevp}.
|
|
5222
|
+
* @param {number[]} numbers The array of numbers comprising the population
|
|
5223
|
+
* @returns {number}
|
|
5224
|
+
* @category openFormula
|
|
5225
|
+
* @function stdev
|
|
5226
|
+
* @example
|
|
5227
|
+
* stdev([1345, 1301, 1368]) //returns 34.044089061098404
|
|
5228
|
+
* stdevp([1345, 1301, 1368]) //returns 27.797
|
|
5229
|
+
*/
|
|
4469
5230
|
stdev: {
|
|
4470
5231
|
_func: args => {
|
|
4471
5232
|
const values = args[0] || [];
|
|
@@ -4477,6 +5238,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4477
5238
|
const sumSquare = coercedValues.reduce((a, b) => a + b * b, 0);
|
|
4478
5239
|
const result = Math.sqrt((sumSquare - values.length * mean * mean) / (values.length - 1));
|
|
4479
5240
|
if (Number.isNaN(result)) {
|
|
5241
|
+
// this would never happen
|
|
4480
5242
|
return null;
|
|
4481
5243
|
}
|
|
4482
5244
|
return result;
|
|
@@ -4485,6 +5247,20 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4485
5247
|
{ types: [dataTypes.TYPE_ARRAY_NUMBER] },
|
|
4486
5248
|
],
|
|
4487
5249
|
},
|
|
5250
|
+
|
|
5251
|
+
/**
|
|
5252
|
+
* Calculates standard deviation based on the entire population given as arguments.
|
|
5253
|
+
* `stdevp` assumes that its arguments are the entire population.
|
|
5254
|
+
* If your data represents a sample of the population,
|
|
5255
|
+
* then compute the standard deviation using [stdev]{@link stdev}.
|
|
5256
|
+
* @param {number[]} numbers The array of numbers comprising the population
|
|
5257
|
+
* @returns {number}
|
|
5258
|
+
* @category openFormula
|
|
5259
|
+
* @function stdevp
|
|
5260
|
+
* @example
|
|
5261
|
+
* stdevp([1345, 1301, 1368]) //returns 27.797
|
|
5262
|
+
* stdev([1345, 1301, 1368]) //returns 34.044
|
|
5263
|
+
*/
|
|
4488
5264
|
stdevp: {
|
|
4489
5265
|
_func: args => {
|
|
4490
5266
|
const values = args[0] || [];
|
|
@@ -4496,6 +5272,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4496
5272
|
const meanSumSquare = coercedValues.reduce((a, b) => a + b * b, 0) / values.length;
|
|
4497
5273
|
const result = Math.sqrt(meanSumSquare - mean * mean);
|
|
4498
5274
|
if (Number.isNaN(result)) {
|
|
5275
|
+
// this would never happen
|
|
4499
5276
|
return null;
|
|
4500
5277
|
}
|
|
4501
5278
|
return result;
|
|
@@ -4504,18 +5281,43 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4504
5281
|
{ types: [dataTypes.TYPE_ARRAY_NUMBER] },
|
|
4505
5282
|
],
|
|
4506
5283
|
},
|
|
5284
|
+
|
|
5285
|
+
/**
|
|
5286
|
+
* Returns input `text`, with text `old` replaced by text `new` (when searching from the left).
|
|
5287
|
+
* If `which` parameter is omitted, every occurrence of `old` is replaced with `new`;
|
|
5288
|
+
* If `which` is provided, only that occurrence of `old` is replaced by `new`
|
|
5289
|
+
* (starting the count from 1).
|
|
5290
|
+
* If there is no match, or if `old` has length 0, `text` is returned unchanged.
|
|
5291
|
+
* Note that `old` and `new` may have different lengths. If `which` < 1, return `text` unchanged
|
|
5292
|
+
* @param {string} text The text for which to substitute characters.
|
|
5293
|
+
* @param {string} old The text to replace.
|
|
5294
|
+
* @param {string} new The text to replace `old` with.
|
|
5295
|
+
* @param {integer} [which] The one-based occurrence of `old` text to replace with `new` text.
|
|
5296
|
+
* @returns {string} replaced string
|
|
5297
|
+
* @function
|
|
5298
|
+
* @category openFormula
|
|
5299
|
+
* @example
|
|
5300
|
+
* substitute('Sales Data', 'Sales', 'Cost') //returns 'Cost Data'
|
|
5301
|
+
* @example
|
|
5302
|
+
* substitute('Quarter 1, 2008', '1', '2', 1) //returns 'Quarter 2, 2008'
|
|
5303
|
+
* @example
|
|
5304
|
+
* substitute('Quarter 1, 1008', '1', '2', 2) //returns 'Quarter 1, 2008'
|
|
5305
|
+
*/
|
|
4507
5306
|
substitute: {
|
|
4508
5307
|
_func: args => {
|
|
4509
5308
|
const src = toString(args[0]);
|
|
4510
5309
|
const old = toString(args[1]);
|
|
4511
5310
|
const replacement = toString(args[2]);
|
|
5311
|
+
// no third parameter? replace all instances
|
|
4512
5312
|
if (args.length <= 3) return src.replaceAll(old, replacement);
|
|
4513
5313
|
const whch = toNumber(args[3]);
|
|
4514
5314
|
if (whch < 1) return src;
|
|
5315
|
+
// find the instance to replace
|
|
4515
5316
|
let pos = -1;
|
|
4516
5317
|
for (let i = 0; i < whch; i += 1) {
|
|
4517
5318
|
pos += 1;
|
|
4518
5319
|
const nextFind = src.slice(pos).indexOf(old);
|
|
5320
|
+
// no instance to match 'Which'
|
|
4519
5321
|
if (nextFind === -1) return src;
|
|
4520
5322
|
pos += nextFind;
|
|
4521
5323
|
}
|
|
@@ -4528,6 +5330,21 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4528
5330
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4529
5331
|
],
|
|
4530
5332
|
},
|
|
5333
|
+
|
|
5334
|
+
/**
|
|
5335
|
+
* Construct and returns time from hours, minutes, and seconds.
|
|
5336
|
+
* @param {integer} hours Integer value between 0 and 23 representing the hour of the day.
|
|
5337
|
+
* Defaults to 0.
|
|
5338
|
+
* @param {integer} minutes Integer value representing the minute segment of a time.
|
|
5339
|
+
* The default is 0 minutes past the hour.
|
|
5340
|
+
* @param {integer} seconds Integer value representing the second segment of a time.
|
|
5341
|
+
* The default is 0 seconds past the minute.
|
|
5342
|
+
* @return {number} Returns the fraction of the day consumed by the given time
|
|
5343
|
+
* @function time
|
|
5344
|
+
* @category openFormula
|
|
5345
|
+
* @example
|
|
5346
|
+
* time(12, 0, 0) //returns 0.5 (half day)
|
|
5347
|
+
*/
|
|
4531
5348
|
time: {
|
|
4532
5349
|
_func: args => {
|
|
4533
5350
|
const hours = toNumber(args[0]);
|
|
@@ -4545,23 +5362,65 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4545
5362
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4546
5363
|
],
|
|
4547
5364
|
},
|
|
5365
|
+
|
|
5366
|
+
/**
|
|
5367
|
+
* returns the number of days since epoch
|
|
5368
|
+
* @return number
|
|
5369
|
+
* @function today
|
|
5370
|
+
* @category openFormula
|
|
5371
|
+
*/
|
|
4548
5372
|
today: {
|
|
4549
5373
|
_func: () => Math.floor(Date.now() / MS_IN_DAY),
|
|
4550
5374
|
_signature: [],
|
|
4551
5375
|
},
|
|
5376
|
+
|
|
5377
|
+
/**
|
|
5378
|
+
* Remove leading and trailing spaces, and replace all internal multiple spaces
|
|
5379
|
+
* with a single space.
|
|
5380
|
+
* @param {string} text string to trim
|
|
5381
|
+
* @return {string} removes all leading and trailing space.
|
|
5382
|
+
* Any other sequence of 2 or more spaces is replaced with a single space.
|
|
5383
|
+
* @function trim
|
|
5384
|
+
* @category openFormula
|
|
5385
|
+
* @example
|
|
5386
|
+
* trim(' ab c ') //returns 'ab c'
|
|
5387
|
+
*/
|
|
4552
5388
|
trim: {
|
|
4553
5389
|
_func: args => {
|
|
4554
5390
|
const text = toString(args[0]);
|
|
5391
|
+
// only removes the space character
|
|
5392
|
+
// other whitespace characters like \t \n left intact
|
|
4555
5393
|
return text.split(' ').filter(x => x).join(' ');
|
|
4556
5394
|
},
|
|
4557
5395
|
_signature: [
|
|
4558
5396
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4559
5397
|
],
|
|
4560
5398
|
},
|
|
5399
|
+
|
|
5400
|
+
/**
|
|
5401
|
+
* Return constant boolean true value.
|
|
5402
|
+
* Note that expressions may also use the JSON literal true: `` `true` ``
|
|
5403
|
+
* @returns {boolean} True
|
|
5404
|
+
* @function
|
|
5405
|
+
* @category openFormula
|
|
5406
|
+
*/
|
|
4561
5407
|
true: {
|
|
4562
5408
|
_func: () => true,
|
|
4563
5409
|
_signature: [],
|
|
4564
5410
|
},
|
|
5411
|
+
|
|
5412
|
+
/**
|
|
5413
|
+
* Truncates a number to an integer by removing the fractional part of the number.
|
|
5414
|
+
* @param {number} numA number to truncate
|
|
5415
|
+
* @param {number} [numB] A number specifying the precision of the truncation. Default is 0
|
|
5416
|
+
* @return {number}
|
|
5417
|
+
* @function trunc
|
|
5418
|
+
* @category openFormula
|
|
5419
|
+
* @example
|
|
5420
|
+
* trunc(8.9) //returns 8
|
|
5421
|
+
* trunc(-8.9) //returns -8
|
|
5422
|
+
* trunc(8.912, 2) //returns 8.91
|
|
5423
|
+
*/
|
|
4565
5424
|
trunc: {
|
|
4566
5425
|
_func: args => {
|
|
4567
5426
|
const number = toNumber(args[0]);
|
|
@@ -4574,8 +5433,20 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4574
5433
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4575
5434
|
],
|
|
4576
5435
|
},
|
|
5436
|
+
|
|
5437
|
+
/**
|
|
5438
|
+
* takes an array and returns unique elements within it
|
|
5439
|
+
* @param {array} input input array
|
|
5440
|
+
* @return {array} array with duplicate elements removed
|
|
5441
|
+
* @function unique
|
|
5442
|
+
* @category JSONFormula
|
|
5443
|
+
* @example
|
|
5444
|
+
* unique([1, 2, 3, 4, 1, 1, 2]) //returns [1, 2, 3, 4]
|
|
5445
|
+
*/
|
|
4577
5446
|
unique: {
|
|
4578
5447
|
_func: args => {
|
|
5448
|
+
// create an array of values for searching. That way if the array elements are
|
|
5449
|
+
// represented by objects with a valueOf(), then we'll locate them in the valueArray
|
|
4579
5450
|
const valueArray = args[0].map(a => valueOf(a));
|
|
4580
5451
|
return args[0].filter((v, index) => valueArray.indexOf(valueOf(v)) === index);
|
|
4581
5452
|
},
|
|
@@ -4583,6 +5454,18 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4583
5454
|
{ types: [dataTypes.TYPE_ARRAY] },
|
|
4584
5455
|
],
|
|
4585
5456
|
},
|
|
5457
|
+
|
|
5458
|
+
/**
|
|
5459
|
+
* Converts all the alphabetic characters in a string to uppercase.
|
|
5460
|
+
* If the value is not a string it will be converted into string
|
|
5461
|
+
* using the default toString method
|
|
5462
|
+
* @param {string} input input string
|
|
5463
|
+
* @returns {string} the upper case value of the input string
|
|
5464
|
+
* @function upper
|
|
5465
|
+
* @category openFormula
|
|
5466
|
+
* @example
|
|
5467
|
+
* upper('abcd') //returns 'ABCD'
|
|
5468
|
+
*/
|
|
4586
5469
|
upper: {
|
|
4587
5470
|
_func: args => {
|
|
4588
5471
|
const value = toString(args[0]);
|
|
@@ -4592,6 +5475,19 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4592
5475
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4593
5476
|
],
|
|
4594
5477
|
},
|
|
5478
|
+
|
|
5479
|
+
/**
|
|
5480
|
+
* Perform an indexed lookup on a map or array
|
|
5481
|
+
* @param {map | array} object on which to perform the lookup
|
|
5482
|
+
* @param {string | integer} index: a named child for a map or an integer offset for an array
|
|
5483
|
+
* @returns {any} the result of the lookup -- or `null` if not found.
|
|
5484
|
+
* @function
|
|
5485
|
+
* @category JSONFormula
|
|
5486
|
+
* @example
|
|
5487
|
+
* value({a: 1, b:2, c:3}, a) //returns 1
|
|
5488
|
+
* @example
|
|
5489
|
+
* value([1, 2, 3, 4], 2) //returns 3
|
|
5490
|
+
*/
|
|
4595
5491
|
value: {
|
|
4596
5492
|
_func: args => {
|
|
4597
5493
|
const obj = args[0] || {};
|
|
@@ -4601,6 +5497,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4601
5497
|
debug.push(`Failed to find: '${index}'`);
|
|
4602
5498
|
const available = Object.keys(obj).map(a => `'${a}'`).toString();
|
|
4603
5499
|
if (available.length) debug.push(`Available fields: ${available}`);
|
|
5500
|
+
|
|
4604
5501
|
return null;
|
|
4605
5502
|
}
|
|
4606
5503
|
return result;
|
|
@@ -4610,18 +5507,43 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4610
5507
|
{ types: [dataTypes.TYPE_STRING, dataTypes.TYPE_NUMBER] },
|
|
4611
5508
|
],
|
|
4612
5509
|
},
|
|
5510
|
+
|
|
5511
|
+
/**
|
|
5512
|
+
* Extract the day of the week from a date; if text, uses current locale to convert to a date.
|
|
5513
|
+
* @param {number} The datetime for which the day of the week is to be returned.
|
|
5514
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
5515
|
+
* @param {number} [returnType] A number that determines the
|
|
5516
|
+
* numeral representation (a number from 0 to 7) of the
|
|
5517
|
+
* day of week. Default is 1. Supports the following values
|
|
5518
|
+
* * 1 : Sunday (1), Monday (2), ..., Saturday (7)
|
|
5519
|
+
* * 2 : Monday (1), Tuesday (2), ..., Sunday(7)
|
|
5520
|
+
* * 3 : Monday (0), Tuesday (2), ...., Sunday(6)
|
|
5521
|
+
* @returns {number} day of the week
|
|
5522
|
+
* @function weekday
|
|
5523
|
+
* @category openFormula
|
|
5524
|
+
* @example
|
|
5525
|
+
* weekday(datetime(2006,5,21)) // 1
|
|
5526
|
+
* @example
|
|
5527
|
+
* weekday(datetime(2006,5,21), 2) // 7
|
|
5528
|
+
* @example
|
|
5529
|
+
* weekday(datetime(2006,5,21), 3) // 6
|
|
5530
|
+
*/
|
|
4613
5531
|
weekday: {
|
|
4614
5532
|
_func: args => {
|
|
4615
5533
|
const date = toNumber(args[0]);
|
|
4616
5534
|
const type = args.length > 1 ? toNumber(args[1]) : 1;
|
|
4617
5535
|
const jsDate = new Date(date * MS_IN_DAY);
|
|
4618
5536
|
const day = jsDate.getDay();
|
|
5537
|
+
// day is in range [0-7) with 0 mapping to sunday
|
|
4619
5538
|
switch (type) {
|
|
4620
5539
|
case 1:
|
|
5540
|
+
// range = [1, 7], sunday = 1
|
|
4621
5541
|
return day + 1;
|
|
4622
5542
|
case 2:
|
|
5543
|
+
// range = [1, 7] sunday = 7
|
|
4623
5544
|
return ((day + 6) % 7) + 1;
|
|
4624
5545
|
case 3:
|
|
5546
|
+
// range = [0, 6] sunday = 6
|
|
4625
5547
|
return (day + 6) % 7;
|
|
4626
5548
|
default:
|
|
4627
5549
|
return null;
|
|
@@ -4632,6 +5554,17 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4632
5554
|
{ types: [dataTypes.TYPE_NUMBER], optional: true },
|
|
4633
5555
|
],
|
|
4634
5556
|
},
|
|
5557
|
+
|
|
5558
|
+
/**
|
|
5559
|
+
* Returns the year of a date represented by a serial number.
|
|
5560
|
+
* @param {number} The date for which the year is to be returned.
|
|
5561
|
+
* Dates should be entered by using the [datetime]{@link datetime} function
|
|
5562
|
+
* @return {number}
|
|
5563
|
+
* @function year
|
|
5564
|
+
* @category openFormula
|
|
5565
|
+
* @example
|
|
5566
|
+
* year(datetime(2008,5,23)) //returns 2008
|
|
5567
|
+
*/
|
|
4635
5568
|
year: {
|
|
4636
5569
|
_func: args => {
|
|
4637
5570
|
const date = toNumber(args[0]);
|
|
@@ -4642,6 +5575,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4642
5575
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4643
5576
|
],
|
|
4644
5577
|
},
|
|
5578
|
+
|
|
4645
5579
|
charCode: {
|
|
4646
5580
|
_func: args => {
|
|
4647
5581
|
const code = toNumber(args[0]);
|
|
@@ -4654,6 +5588,7 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4654
5588
|
{ types: [dataTypes.TYPE_NUMBER] },
|
|
4655
5589
|
],
|
|
4656
5590
|
},
|
|
5591
|
+
|
|
4657
5592
|
codePoint: {
|
|
4658
5593
|
_func: args => {
|
|
4659
5594
|
const text = toString(args[0]);
|
|
@@ -4666,24 +5601,28 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4666
5601
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4667
5602
|
],
|
|
4668
5603
|
},
|
|
5604
|
+
|
|
4669
5605
|
encodeUrlComponent: {
|
|
4670
5606
|
_func: args => encodeURIComponent(args[0]),
|
|
4671
5607
|
_signature: [
|
|
4672
5608
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4673
5609
|
],
|
|
4674
5610
|
},
|
|
5611
|
+
|
|
4675
5612
|
encodeUrl: {
|
|
4676
5613
|
_func: args => encodeURI(args[0]),
|
|
4677
5614
|
_signature: [
|
|
4678
5615
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4679
5616
|
],
|
|
4680
5617
|
},
|
|
5618
|
+
|
|
4681
5619
|
decodeUrlComponent: {
|
|
4682
5620
|
_func: args => decodeURIComponent(args[0]),
|
|
4683
5621
|
_signature: [
|
|
4684
5622
|
{ types: [dataTypes.TYPE_STRING] },
|
|
4685
5623
|
],
|
|
4686
5624
|
},
|
|
5625
|
+
|
|
4687
5626
|
decodeUrl: {
|
|
4688
5627
|
_func: args => decodeURI(args[0]),
|
|
4689
5628
|
_signature: [
|
|
@@ -4693,6 +5632,27 @@ function openFormulaFunctions(valueOf, toString, toNumber, debug = []) {
|
|
|
4693
5632
|
};
|
|
4694
5633
|
}
|
|
4695
5634
|
|
|
5635
|
+
/*
|
|
5636
|
+
Copyright 2014 James Saryerwinnie
|
|
5637
|
+
|
|
5638
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5639
|
+
you may not use this file except in compliance with the License.
|
|
5640
|
+
You may obtain a copy of the License at
|
|
5641
|
+
|
|
5642
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
5643
|
+
|
|
5644
|
+
Unless required by applicable law or agreed to in writing, software
|
|
5645
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
5646
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5647
|
+
See the License for the specific language governing permissions and
|
|
5648
|
+
limitations under the License.
|
|
5649
|
+
|
|
5650
|
+
NOTICE:
|
|
5651
|
+
This file is substantially modified from the original source taken from:
|
|
5652
|
+
https://github.com/jmespath/jmespath.js
|
|
5653
|
+
|
|
5654
|
+
*/
|
|
5655
|
+
|
|
4696
5656
|
function functions(
|
|
4697
5657
|
runtime,
|
|
4698
5658
|
isObject,
|
|
@@ -4715,6 +5675,7 @@ function functions(
|
|
|
4715
5675
|
TYPE_ARRAY_NUMBER,
|
|
4716
5676
|
TYPE_ARRAY_STRING,
|
|
4717
5677
|
} = dataTypes;
|
|
5678
|
+
|
|
4718
5679
|
function createKeyFunction(exprefNode, allowedTypes) {
|
|
4719
5680
|
return x => {
|
|
4720
5681
|
const current = runtime.interpreter.visit(exprefNode, x);
|
|
@@ -4726,11 +5687,46 @@ function functions(
|
|
|
4726
5687
|
return current;
|
|
4727
5688
|
};
|
|
4728
5689
|
}
|
|
5690
|
+
|
|
4729
5691
|
const functionMap = {
|
|
5692
|
+
// name: [function, <signature>]
|
|
5693
|
+
// The <signature> can be:
|
|
5694
|
+
//
|
|
5695
|
+
// {
|
|
5696
|
+
// args: [[type1, type2], [type1, type2]],
|
|
5697
|
+
// variadic: true|false
|
|
5698
|
+
// }
|
|
5699
|
+
//
|
|
5700
|
+
// Each arg in the arg list is a list of valid types
|
|
5701
|
+
// (if the function is overloaded and supports multiple
|
|
5702
|
+
// types. If the type is "any" then no type checking
|
|
5703
|
+
// occurs on the argument. Variadic is optional
|
|
5704
|
+
// and if not provided is assumed to be false.
|
|
5705
|
+
/**
|
|
5706
|
+
* Returns the absolute value of the provided argument `value`.
|
|
5707
|
+
* @param {number} value argument whose absolute value has to be returned
|
|
5708
|
+
* @return {number} returns the absolute value of the `value` argument
|
|
5709
|
+
* @function abs
|
|
5710
|
+
* @example
|
|
5711
|
+
* abs(-1) //returns 1
|
|
5712
|
+
* @category jmespath
|
|
5713
|
+
*/
|
|
4730
5714
|
abs: {
|
|
4731
5715
|
_func: resolvedArgs => Math.abs(resolvedArgs[0]),
|
|
4732
5716
|
_signature: [{ types: [TYPE_NUMBER] }],
|
|
4733
5717
|
},
|
|
5718
|
+
/**
|
|
5719
|
+
* Returns the average of the elements in the provided array.
|
|
5720
|
+
* An empty array will produce a return value of `null`.
|
|
5721
|
+
* @param {number[]} elements array of elements whose average has to be computed
|
|
5722
|
+
* @return {number} average value
|
|
5723
|
+
* @function avg
|
|
5724
|
+
* @example
|
|
5725
|
+
* avg([]) //returns null
|
|
5726
|
+
* @example
|
|
5727
|
+
* avg([1, 2, 3]) //returns 2
|
|
5728
|
+
* @category jmespath
|
|
5729
|
+
*/
|
|
4734
5730
|
avg: {
|
|
4735
5731
|
_func: resolvedArgs => {
|
|
4736
5732
|
let sum = 0;
|
|
@@ -4742,15 +5738,59 @@ function functions(
|
|
|
4742
5738
|
},
|
|
4743
5739
|
_signature: [{ types: [TYPE_ARRAY_NUMBER] }],
|
|
4744
5740
|
},
|
|
5741
|
+
|
|
5742
|
+
/**
|
|
5743
|
+
* Returns the next highest integer value of the argument `num` by rounding up if necessary.
|
|
5744
|
+
* @param {number} num number whose next highest integer value has to be computed
|
|
5745
|
+
* @return {number}
|
|
5746
|
+
* @function ceil
|
|
5747
|
+
* @example
|
|
5748
|
+
* ceil(10) //returns 10
|
|
5749
|
+
* @example
|
|
5750
|
+
* ceil(10.4) //return 11
|
|
5751
|
+
* @category jmespath
|
|
5752
|
+
*/
|
|
4745
5753
|
ceil: {
|
|
4746
5754
|
_func: resolvedArgs => Math.ceil(resolvedArgs[0]),
|
|
4747
5755
|
_signature: [{ types: [TYPE_NUMBER] }],
|
|
4748
5756
|
},
|
|
5757
|
+
/**
|
|
5758
|
+
* Returns true if the given `subject` contains the provided `search` string.
|
|
5759
|
+
* If `subject` is an array, this function returns true if one of the elements
|
|
5760
|
+
* in the array is equal to the provided `search` value. If the provided `subject`
|
|
5761
|
+
* is a string, this function returns true if the string contains the provided
|
|
5762
|
+
* `search` argument.
|
|
5763
|
+
* @param {array|string} subject the subject in which the element has to be searched
|
|
5764
|
+
* @param {string|boolean|number|date} search element to search
|
|
5765
|
+
* @return {boolean}
|
|
5766
|
+
* @function contains
|
|
5767
|
+
* @example
|
|
5768
|
+
* contains([1, 2, 3, 4], 2) //returns true
|
|
5769
|
+
* @example
|
|
5770
|
+
* contains([1, 2, 3, 4], -1) //returns false
|
|
5771
|
+
* @example
|
|
5772
|
+
* contains('Abcd', 'd') //returns true
|
|
5773
|
+
* @example
|
|
5774
|
+
* contains('Abcd', 'x') //returns false
|
|
5775
|
+
* @category jmespath
|
|
5776
|
+
*/
|
|
4749
5777
|
contains: {
|
|
4750
5778
|
_func: resolvedArgs => valueOf(resolvedArgs[0]).indexOf(valueOf(resolvedArgs[1])) >= 0,
|
|
4751
5779
|
_signature: [{ types: [TYPE_STRING, TYPE_ARRAY] },
|
|
4752
5780
|
{ types: [TYPE_ANY] }],
|
|
4753
5781
|
},
|
|
5782
|
+
/**
|
|
5783
|
+
* Returns true if the `subject` ends with the `suffix`, otherwise this function returns false.
|
|
5784
|
+
* @param {string} subject subject in which the `suffix` is being searched for
|
|
5785
|
+
* @param {string} suffix suffix to search in the subject
|
|
5786
|
+
* @return {boolean}
|
|
5787
|
+
* @function endsWith
|
|
5788
|
+
* @example
|
|
5789
|
+
* endsWith('Abcd', 'd') //returns true
|
|
5790
|
+
* @example
|
|
5791
|
+
* endsWith('Abcd', 'A') //returns false
|
|
5792
|
+
* @category jmespath
|
|
5793
|
+
*/
|
|
4754
5794
|
endsWith: {
|
|
4755
5795
|
_func: resolvedArgs => {
|
|
4756
5796
|
const searchStr = valueOf(resolvedArgs[0]);
|
|
@@ -4759,10 +5799,34 @@ function functions(
|
|
|
4759
5799
|
},
|
|
4760
5800
|
_signature: [{ types: [TYPE_STRING] }, { types: [TYPE_STRING] }],
|
|
4761
5801
|
},
|
|
5802
|
+
|
|
5803
|
+
/**
|
|
5804
|
+
* Returns the next lowest integer value of the argument `num` by rounding down if necessary.
|
|
5805
|
+
* @param {number} num number whose next lowest integer value has to be returned
|
|
5806
|
+
* @return {number}
|
|
5807
|
+
* @function floor
|
|
5808
|
+
* @example
|
|
5809
|
+
* floor(10.4) //returns 10
|
|
5810
|
+
* @example
|
|
5811
|
+
* floor(10) //returns 10
|
|
5812
|
+
* @category jmespath
|
|
5813
|
+
*/
|
|
4762
5814
|
floor: {
|
|
4763
5815
|
_func: resolvedArgs => Math.floor(resolvedArgs[0]),
|
|
4764
5816
|
_signature: [{ types: [TYPE_NUMBER] }],
|
|
4765
5817
|
},
|
|
5818
|
+
|
|
5819
|
+
/**
|
|
5820
|
+
* Returns all the elements from the provided `stringsarray`
|
|
5821
|
+
* array joined together using the `glue` argument as a separator between each.
|
|
5822
|
+
* @param {string} glue
|
|
5823
|
+
* @param {string[]} stringsarray
|
|
5824
|
+
* @return {string}
|
|
5825
|
+
* @function join
|
|
5826
|
+
* @example
|
|
5827
|
+
* join(',', ['a', 'b', 'c']) //returns 'a,b,c'
|
|
5828
|
+
* @category jmespath
|
|
5829
|
+
*/
|
|
4766
5830
|
join: {
|
|
4767
5831
|
_func: resolvedArgs => {
|
|
4768
5832
|
const joinChar = resolvedArgs[0];
|
|
@@ -4774,6 +5838,17 @@ function functions(
|
|
|
4774
5838
|
{ types: [TYPE_ARRAY_STRING] },
|
|
4775
5839
|
],
|
|
4776
5840
|
},
|
|
5841
|
+
|
|
5842
|
+
/**
|
|
5843
|
+
* Returns an array containing the keys of the provided object `obj`. If the passed
|
|
5844
|
+
* object is null, the value returned is an empty array
|
|
5845
|
+
* @param {object} obj the object whose keys need to be extracted
|
|
5846
|
+
* @return {array}
|
|
5847
|
+
* @function keys
|
|
5848
|
+
* @example
|
|
5849
|
+
* keys({a : 3, b : 4}) //returns ['a', 'b']
|
|
5850
|
+
* @category jmespath
|
|
5851
|
+
*/
|
|
4777
5852
|
keys: {
|
|
4778
5853
|
_func: resolvedArgs => {
|
|
4779
5854
|
if (resolvedArgs[0] === null) return [];
|
|
@@ -4781,14 +5856,54 @@ function functions(
|
|
|
4781
5856
|
},
|
|
4782
5857
|
_signature: [{ types: [TYPE_ANY] }],
|
|
4783
5858
|
},
|
|
5859
|
+
|
|
5860
|
+
/**
|
|
5861
|
+
* Returns the length of the given argument `subject` using the following types rules:
|
|
5862
|
+
* * string: returns the number of code points in the string
|
|
5863
|
+
* * array: returns the number of elements in the array
|
|
5864
|
+
* * object: returns the number of key-value pairs in the object
|
|
5865
|
+
* @param {string | array | object} subject subject whose length has to be calculated
|
|
5866
|
+
* @return {number}
|
|
5867
|
+
* @function length
|
|
5868
|
+
* @example
|
|
5869
|
+
* length([]) //returns 0
|
|
5870
|
+
* @example
|
|
5871
|
+
* length('') //returns 0
|
|
5872
|
+
* @example
|
|
5873
|
+
* length('abcd') //returns 4
|
|
5874
|
+
* @example
|
|
5875
|
+
* length([1, 2, 3, 4]) //returns 4
|
|
5876
|
+
* @example
|
|
5877
|
+
* length({}) // returns 0
|
|
5878
|
+
* @example
|
|
5879
|
+
* length({a : 3, b : 4}) //returns 2
|
|
5880
|
+
* @category jmespath
|
|
5881
|
+
*/
|
|
4784
5882
|
length: {
|
|
4785
5883
|
_func: resolvedArgs => {
|
|
4786
5884
|
const arg = valueOf(resolvedArgs[0]);
|
|
4787
5885
|
if (isObject(arg)) return Object.keys(arg).length;
|
|
5886
|
+
|
|
4788
5887
|
return isArray(arg) ? arg.length : toString(arg).length;
|
|
4789
5888
|
},
|
|
4790
5889
|
_signature: [{ types: [TYPE_STRING, TYPE_ARRAY, TYPE_OBJECT] }],
|
|
4791
5890
|
},
|
|
5891
|
+
|
|
5892
|
+
/**
|
|
5893
|
+
* Apply the `expr` to every element in the `elements` array and return the array of results.
|
|
5894
|
+
* An elements of length N will produce a return array of length N. Unlike a projection,
|
|
5895
|
+
* `[*].bar`, `map()` will include the result of applying the `expr` for every element
|
|
5896
|
+
* in the elements array, even if the result is `null`.
|
|
5897
|
+
* @param {expression} expr expression to evaluate on each element
|
|
5898
|
+
* @param {array} elements array of elements on which the expression will be evaluated
|
|
5899
|
+
* @return {array}
|
|
5900
|
+
* @function map
|
|
5901
|
+
* @example
|
|
5902
|
+
* map(&(@ + 1), [1, 2, 3, 4]) // returns [2, 3, 4, 5]
|
|
5903
|
+
* @example
|
|
5904
|
+
* map(&length(@), ['doe', 'nick', 'chris']) // returns [3,4, 5]
|
|
5905
|
+
* @category jmespath
|
|
5906
|
+
*/
|
|
4792
5907
|
map: {
|
|
4793
5908
|
_func: resolvedArgs => {
|
|
4794
5909
|
const exprefNode = resolvedArgs[0];
|
|
@@ -4796,15 +5911,35 @@ function functions(
|
|
|
4796
5911
|
},
|
|
4797
5912
|
_signature: [{ types: [TYPE_EXPREF] }, { types: [TYPE_ARRAY] }],
|
|
4798
5913
|
},
|
|
5914
|
+
|
|
5915
|
+
/**
|
|
5916
|
+
* Returns the highest value in the provided `collection` arguments.
|
|
5917
|
+
* If all collections are empty `null` is returned.
|
|
5918
|
+
* max() can work on numbers or strings.
|
|
5919
|
+
* If a mix of numbers and strings are provided, the type of the first value will be used.
|
|
5920
|
+
* @param {number[]|string[]} collection array in which the maximum element is to be calculated
|
|
5921
|
+
* @return {number}
|
|
5922
|
+
* @function max
|
|
5923
|
+
* @example
|
|
5924
|
+
* max([1, 2, 3], [4, 5, 6], 7) //returns 7
|
|
5925
|
+
* @example
|
|
5926
|
+
* max([]) // returns null
|
|
5927
|
+
* @example
|
|
5928
|
+
* max(['a', 'a1', 'b']) // returns 'b'
|
|
5929
|
+
* @category jmespath
|
|
5930
|
+
*/
|
|
4799
5931
|
max: {
|
|
4800
5932
|
_func: args => {
|
|
5933
|
+
// flatten the args into a single array
|
|
4801
5934
|
const array = args.reduce((prev, cur) => {
|
|
4802
5935
|
if (Array.isArray(cur)) prev.push(...cur);
|
|
4803
5936
|
else prev.push(cur);
|
|
4804
5937
|
return prev;
|
|
4805
5938
|
}, []);
|
|
5939
|
+
|
|
4806
5940
|
const first = array.find(r => r !== null);
|
|
4807
5941
|
if (array.length === 0 || first === undefined) return null;
|
|
5942
|
+
// use the first value to determine the comparison type
|
|
4808
5943
|
const isNumber = getTypeName(first, true) === TYPE_NUMBER;
|
|
4809
5944
|
const compare = isNumber
|
|
4810
5945
|
? (prev, cur) => {
|
|
@@ -4815,10 +5950,25 @@ function functions(
|
|
|
4815
5950
|
const current = toString(cur);
|
|
4816
5951
|
return prev.localeCompare(current) === 1 ? prev : current;
|
|
4817
5952
|
};
|
|
5953
|
+
|
|
4818
5954
|
return array.reduce(compare, isNumber ? toNumber(first) : toString(first));
|
|
4819
5955
|
},
|
|
4820
5956
|
_signature: [{ types: [TYPE_ARRAY, TYPE_ARRAY_NUMBER, TYPE_ARRAY_STRING], variadic: true }],
|
|
4821
5957
|
},
|
|
5958
|
+
|
|
5959
|
+
/**
|
|
5960
|
+
* Returns the maximum element in an array using the expression `expr` as the comparison key.
|
|
5961
|
+
* The entire element is returned.
|
|
5962
|
+
* @param {array} elements the array in which the maximum element is to be found
|
|
5963
|
+
* @param {expression} expr the expr to use as the `comparison` key
|
|
5964
|
+
* @return {any}
|
|
5965
|
+
* @function maxBy
|
|
5966
|
+
* @example
|
|
5967
|
+
* maxBy(['abcd', 'e', 'def'], &length(@)) //returns 'abcd'
|
|
5968
|
+
* @example
|
|
5969
|
+
* maxBy([{year: 2010}, {year: 2020}, {year: 1910}], &year) //returns {year: 2020}
|
|
5970
|
+
* @category jmespath
|
|
5971
|
+
*/
|
|
4822
5972
|
maxBy: {
|
|
4823
5973
|
_func: resolvedArgs => {
|
|
4824
5974
|
const exprefNode = resolvedArgs[1];
|
|
@@ -4838,6 +5988,22 @@ function functions(
|
|
|
4838
5988
|
},
|
|
4839
5989
|
_signature: [{ types: [TYPE_ARRAY] }, { types: [TYPE_EXPREF] }],
|
|
4840
5990
|
},
|
|
5991
|
+
|
|
5992
|
+
/**
|
|
5993
|
+
* Accepts 0 or more objects as arguments, and returns a single object with
|
|
5994
|
+
* subsequent objects merged. Each subsequent object’s key/value pairs are
|
|
5995
|
+
* added to the preceding object. This function is used to combine multiple
|
|
5996
|
+
* objects into one. You can think of this as the first object being the base object,
|
|
5997
|
+
* and each subsequent argument being overrides that are applied to the base object.
|
|
5998
|
+
* @param {...object} args
|
|
5999
|
+
* @return {object}
|
|
6000
|
+
* @function merge
|
|
6001
|
+
* @example
|
|
6002
|
+
* merge({a: 1, b: 2}, {c : 3, d: 4}) // returns {a :1, b: 2, c: 3, d: 4}
|
|
6003
|
+
* @example
|
|
6004
|
+
* merge({a: 1, b: 2}, {a : 3, d: 4}) // returns {a :3, b: 2, d: 4}
|
|
6005
|
+
* @category jmespath
|
|
6006
|
+
*/
|
|
4841
6007
|
merge: {
|
|
4842
6008
|
_func: resolvedArgs => {
|
|
4843
6009
|
const merged = {};
|
|
@@ -4850,15 +6016,35 @@ function functions(
|
|
|
4850
6016
|
},
|
|
4851
6017
|
_signature: [{ types: [TYPE_OBJECT], variadic: true }],
|
|
4852
6018
|
},
|
|
6019
|
+
|
|
6020
|
+
/**
|
|
6021
|
+
* Returns the lowest value in the provided `collection` arguments.
|
|
6022
|
+
* If all collections are empty `null` is returned.
|
|
6023
|
+
* min() can work on numbers or strings.
|
|
6024
|
+
* If a mix of numbers and strings are provided, the type of the first value will be used.
|
|
6025
|
+
* @param {number[]|string[]} collection array in which the minimum element is to be calculated
|
|
6026
|
+
* @return {number}
|
|
6027
|
+
* @function min
|
|
6028
|
+
* @example
|
|
6029
|
+
* min([1, 2, 3], [4, 5, 6], 7) //returns 1
|
|
6030
|
+
* @example
|
|
6031
|
+
* min([]) // returns null
|
|
6032
|
+
* @example
|
|
6033
|
+
* min(['a', 'a1', 'b']) // returns 'a'
|
|
6034
|
+
* @category jmespath
|
|
6035
|
+
*/
|
|
4853
6036
|
min: {
|
|
4854
6037
|
_func: args => {
|
|
6038
|
+
// flatten the args into a single array
|
|
4855
6039
|
const array = args.reduce((prev, cur) => {
|
|
4856
6040
|
if (Array.isArray(cur)) prev.push(...cur);
|
|
4857
6041
|
else prev.push(cur);
|
|
4858
6042
|
return prev;
|
|
4859
6043
|
}, []);
|
|
6044
|
+
|
|
4860
6045
|
const first = array.find(r => r !== null);
|
|
4861
6046
|
if (array.length === 0 || first === undefined) return null;
|
|
6047
|
+
// use the first value to determine the comparison type
|
|
4862
6048
|
const isNumber = getTypeName(first, true) === TYPE_NUMBER;
|
|
4863
6049
|
const compare = isNumber
|
|
4864
6050
|
? (prev, cur) => {
|
|
@@ -4869,10 +6055,25 @@ function functions(
|
|
|
4869
6055
|
const current = toString(cur);
|
|
4870
6056
|
return prev.localeCompare(current) === 1 ? current : prev;
|
|
4871
6057
|
};
|
|
6058
|
+
|
|
4872
6059
|
return array.reduce(compare, isNumber ? toNumber(first) : toString(first));
|
|
4873
6060
|
},
|
|
4874
6061
|
_signature: [{ types: [TYPE_ARRAY, TYPE_ARRAY_NUMBER, TYPE_ARRAY_STRING], variadic: true }],
|
|
4875
6062
|
},
|
|
6063
|
+
|
|
6064
|
+
/**
|
|
6065
|
+
* Returns the minimum element in `elements` array using the expression `expr`
|
|
6066
|
+
* as the comparison key.
|
|
6067
|
+
* @param {array} elements
|
|
6068
|
+
* @param {expression} expr expression that returns either a string or a number
|
|
6069
|
+
* @return {any}
|
|
6070
|
+
* @function minBy
|
|
6071
|
+
* @example
|
|
6072
|
+
* minBy(['abcd', 'e', 'def'], &length(@)) //returns 'e'
|
|
6073
|
+
* @example
|
|
6074
|
+
* minBy([{year: 2010}, {year: 2020}, {year: 1910}], &year) //returns {year: 1910}
|
|
6075
|
+
* @category jmespath
|
|
6076
|
+
*/
|
|
4876
6077
|
minBy: {
|
|
4877
6078
|
_func: resolvedArgs => {
|
|
4878
6079
|
const exprefNode = resolvedArgs[1];
|
|
@@ -4892,10 +6093,48 @@ function functions(
|
|
|
4892
6093
|
},
|
|
4893
6094
|
_signature: [{ types: [TYPE_ARRAY] }, { types: [TYPE_EXPREF] }],
|
|
4894
6095
|
},
|
|
6096
|
+
|
|
6097
|
+
/**
|
|
6098
|
+
* Returns the first argument that does not resolve to `null`.
|
|
6099
|
+
* This function accepts one or more arguments, and will evaluate
|
|
6100
|
+
* them in order until a non null argument is encounted. If all
|
|
6101
|
+
* arguments values resolve to null, then a value of null is returned.
|
|
6102
|
+
* @param {...any} argument
|
|
6103
|
+
* @return {any}
|
|
6104
|
+
* @function notNull
|
|
6105
|
+
* @example
|
|
6106
|
+
* notNull(1, 2, 3, 4, `null`) //returns 1
|
|
6107
|
+
* @example
|
|
6108
|
+
* notNull(`null`, 2, 3, 4, `null`) //returns 2
|
|
6109
|
+
* @category jmespath
|
|
6110
|
+
*/
|
|
4895
6111
|
notNull: {
|
|
4896
6112
|
_func: resolvedArgs => resolvedArgs.find(arg => getTypeName(arg) !== TYPE_NULL) || null,
|
|
4897
6113
|
_signature: [{ types: [TYPE_ANY], variadic: true }],
|
|
4898
6114
|
},
|
|
6115
|
+
|
|
6116
|
+
/**
|
|
6117
|
+
* executes a user-supplied reducer expression `expr` on each element of the
|
|
6118
|
+
* array, in order, passing in the return value from the calculation on the preceding element.
|
|
6119
|
+
* The final result of running the reducer across all elements of the `elements` array is a
|
|
6120
|
+
* single value.
|
|
6121
|
+
* The expression can access the following properties
|
|
6122
|
+
* * accumulated: accumulated value based on the previous calculations. Initial value is `null`
|
|
6123
|
+
* * current: current element to process
|
|
6124
|
+
* * index: index of the `current` element in the array
|
|
6125
|
+
* * array: original array
|
|
6126
|
+
* @param {expression} expr reducer expr to be executed on each element
|
|
6127
|
+
* @param {array} elements array of elements on which the expression will be evaluated
|
|
6128
|
+
* @return {any}
|
|
6129
|
+
* @function reduce
|
|
6130
|
+
* @example
|
|
6131
|
+
* reduce(&(accumulated + current), [1, 2, 3]) //returns 6
|
|
6132
|
+
* @example
|
|
6133
|
+
* reduce(&(accumulated - current), [3, 3, 3]) //returns -9
|
|
6134
|
+
* @example
|
|
6135
|
+
* reduce(&if(accumulated == `null`, current, accumulated * current), [3, 3, 3]) //returns 27
|
|
6136
|
+
* @category jmespath
|
|
6137
|
+
*/
|
|
4899
6138
|
reduce: {
|
|
4900
6139
|
_func: resolvedArgs => {
|
|
4901
6140
|
const exprefNode = resolvedArgs[0];
|
|
@@ -4912,10 +6151,23 @@ function functions(
|
|
|
4912
6151
|
{ types: [TYPE_ANY], optional: true },
|
|
4913
6152
|
],
|
|
4914
6153
|
},
|
|
6154
|
+
|
|
6155
|
+
/**
|
|
6156
|
+
* Register a function to allow code re-use. The registered function may take one parameter.
|
|
6157
|
+
* If more parameters are needed, combine them in an array or map.
|
|
6158
|
+
* @param {string} functionName Name of the function to register
|
|
6159
|
+
* @param {expression} expr Expression to execute with this function call
|
|
6160
|
+
* @return {{}} returns an empty object
|
|
6161
|
+
* @function register
|
|
6162
|
+
* @example
|
|
6163
|
+
* register('product', &@[0] * @[1]) // can now call: product([2,21]) => returns 42
|
|
6164
|
+
* @category jmespath
|
|
6165
|
+
*/
|
|
4915
6166
|
register: {
|
|
4916
6167
|
_func: resolvedArgs => {
|
|
4917
6168
|
const functionName = resolvedArgs[0];
|
|
4918
6169
|
const exprefNode = resolvedArgs[1];
|
|
6170
|
+
|
|
4919
6171
|
if (functionMap[functionName]) {
|
|
4920
6172
|
debug.push(`Cannot re-register '${functionName}'`);
|
|
4921
6173
|
return {};
|
|
@@ -4931,6 +6183,15 @@ function functions(
|
|
|
4931
6183
|
{ types: [TYPE_EXPREF] },
|
|
4932
6184
|
],
|
|
4933
6185
|
},
|
|
6186
|
+
/**
|
|
6187
|
+
* Reverses the order of the `argument`.
|
|
6188
|
+
* @param {string|array} argument
|
|
6189
|
+
* @return {array}
|
|
6190
|
+
* @function reverse
|
|
6191
|
+
* @example
|
|
6192
|
+
* reverse(['a', 'b', 'c']) //returns ['c', 'b', 'a']
|
|
6193
|
+
* @category jmespath
|
|
6194
|
+
*/
|
|
4934
6195
|
reverse: {
|
|
4935
6196
|
_func: resolvedArgs => {
|
|
4936
6197
|
const originalStr = valueOf(resolvedArgs[0]);
|
|
@@ -4948,6 +6209,18 @@ function functions(
|
|
|
4948
6209
|
},
|
|
4949
6210
|
_signature: [{ types: [TYPE_STRING, TYPE_ARRAY] }],
|
|
4950
6211
|
},
|
|
6212
|
+
|
|
6213
|
+
/**
|
|
6214
|
+
* This function accepts an array `list` argument and returns the sorted elements of
|
|
6215
|
+
* the `list` as an array. The array must be a list of strings or numbers.
|
|
6216
|
+
* Sorting strings is based on code points. Locale is not taken into account.
|
|
6217
|
+
* @param {number[]|string[]} list
|
|
6218
|
+
* @return {number[]|string[]}
|
|
6219
|
+
* @function sort
|
|
6220
|
+
* @example
|
|
6221
|
+
* sort([1, 2, 4, 3, 1]) // returns [1, 1, 2, 3, 4]
|
|
6222
|
+
* @category jmespath
|
|
6223
|
+
*/
|
|
4951
6224
|
sort: {
|
|
4952
6225
|
_func: resolvedArgs => {
|
|
4953
6226
|
const sortedArray = resolvedArgs[0].slice(0);
|
|
@@ -4965,6 +6238,24 @@ function functions(
|
|
|
4965
6238
|
},
|
|
4966
6239
|
_signature: [{ types: [TYPE_ARRAY, TYPE_ARRAY_STRING, TYPE_ARRAY_NUMBER] }],
|
|
4967
6240
|
},
|
|
6241
|
+
|
|
6242
|
+
/**
|
|
6243
|
+
* Sort an array using an expression `expr` as the sort key. For each element
|
|
6244
|
+
* in the array of elements, the `expr` expression is applied and the resulting
|
|
6245
|
+
* value is used as the key used when sorting the elements. If the result of
|
|
6246
|
+
* evaluating the `expr` against the current array element results in type
|
|
6247
|
+
* other than a number or a string, a type error will occur.
|
|
6248
|
+
* @param {array} elements
|
|
6249
|
+
* @param {expression} expr
|
|
6250
|
+
* @return {array}
|
|
6251
|
+
* @function sortBy
|
|
6252
|
+
* @example
|
|
6253
|
+
* sortBy(['abcd', 'e', 'def'], &length(@)) //returns ['e', 'def', 'abcd']
|
|
6254
|
+
* @example
|
|
6255
|
+
* // returns [{year: 1910}, {year: 2010}, {year: 2020}]
|
|
6256
|
+
* sortBy([{year: 2010}, {year: 2020}, {year: 1910}], &year)
|
|
6257
|
+
* @category jmespath
|
|
6258
|
+
*/
|
|
4968
6259
|
sortBy: {
|
|
4969
6260
|
_func: resolvedArgs => {
|
|
4970
6261
|
const sortedArray = resolvedArgs[0].slice(0);
|
|
@@ -4978,6 +6269,13 @@ function functions(
|
|
|
4978
6269
|
if ([TYPE_NUMBER, TYPE_STRING].indexOf(requiredType) < 0) {
|
|
4979
6270
|
throw new Error('TypeError');
|
|
4980
6271
|
}
|
|
6272
|
+
// In order to get a stable sort out of an unstable
|
|
6273
|
+
// sort algorithm, we decorate/sort/undecorate (DSU)
|
|
6274
|
+
// by creating a new list of [index, element] pairs.
|
|
6275
|
+
// In the cmp function, if the evaluated elements are
|
|
6276
|
+
// equal, then the index will be used as the tiebreaker.
|
|
6277
|
+
// After the decorated list has been sorted, it will be
|
|
6278
|
+
// undecorated to extract the original elements.
|
|
4981
6279
|
const decorated = [];
|
|
4982
6280
|
for (let i = 0; i < sortedArray.length; i += 1) {
|
|
4983
6281
|
decorated.push([i, sortedArray[i]]);
|
|
@@ -5002,8 +6300,12 @@ function functions(
|
|
|
5002
6300
|
if (exprA < exprB) {
|
|
5003
6301
|
return -1;
|
|
5004
6302
|
}
|
|
6303
|
+
// If they're equal compare the items by their
|
|
6304
|
+
// order to maintain relative order of equal keys
|
|
6305
|
+
// (i.e. to get a stable sort).
|
|
5005
6306
|
return a[0] - b[0];
|
|
5006
6307
|
});
|
|
6308
|
+
// Undecorate: extract out the original list elements.
|
|
5007
6309
|
for (let j = 0; j < decorated.length; j += 1) {
|
|
5008
6310
|
[, sortedArray[j]] = decorated[j];
|
|
5009
6311
|
}
|
|
@@ -5011,10 +6313,32 @@ function functions(
|
|
|
5011
6313
|
},
|
|
5012
6314
|
_signature: [{ types: [TYPE_ARRAY] }, { types: [TYPE_EXPREF] }],
|
|
5013
6315
|
},
|
|
6316
|
+
|
|
6317
|
+
/**
|
|
6318
|
+
* Returns true if the `subject` starts with the `prefix`, otherwise returns false.
|
|
6319
|
+
* @param {string} subject subject in which the `prefix` is being searched for
|
|
6320
|
+
* @param {string} prefix prefix to search in the subject
|
|
6321
|
+
* @return {boolean}
|
|
6322
|
+
* @function startsWith
|
|
6323
|
+
* @example
|
|
6324
|
+
* startsWith('jack is at home', 'jack') // returns true
|
|
6325
|
+
* @category jmespath
|
|
6326
|
+
*/
|
|
5014
6327
|
startsWith: {
|
|
5015
6328
|
_func: resolvedArgs => valueOf(resolvedArgs[0]).startsWith(valueOf(resolvedArgs[1])),
|
|
5016
6329
|
_signature: [{ types: [TYPE_STRING] }, { types: [TYPE_STRING] }],
|
|
5017
6330
|
},
|
|
6331
|
+
|
|
6332
|
+
/**
|
|
6333
|
+
* Returns the sum of the provided `collection` array argument.
|
|
6334
|
+
* An empty array will produce a return value of 0.
|
|
6335
|
+
* @param {number[]} collection array whose element's sum has to be computed
|
|
6336
|
+
* @return {number}
|
|
6337
|
+
* @function sum
|
|
6338
|
+
* @example
|
|
6339
|
+
* sum([1, 2, 3]) //returns 6
|
|
6340
|
+
* @category jmespath
|
|
6341
|
+
*/
|
|
5018
6342
|
sum: {
|
|
5019
6343
|
_func: resolvedArgs => {
|
|
5020
6344
|
let sum = 0;
|
|
@@ -5025,6 +6349,20 @@ function functions(
|
|
|
5025
6349
|
},
|
|
5026
6350
|
_signature: [{ types: [TYPE_ARRAY_NUMBER] }],
|
|
5027
6351
|
},
|
|
6352
|
+
|
|
6353
|
+
/**
|
|
6354
|
+
* converts the passed `arg` to an array. The conversion happens as per the following rules
|
|
6355
|
+
* * array - Returns the passed in value.
|
|
6356
|
+
* * number/string/object/boolean - Returns a one element array containing the argument.
|
|
6357
|
+
* @param {any} arg
|
|
6358
|
+
* @return {array}
|
|
6359
|
+
* @function toArray
|
|
6360
|
+
* @example
|
|
6361
|
+
* toArray(1) // returns [1]
|
|
6362
|
+
* @example
|
|
6363
|
+
* toArray(null()) // returns [`null`]
|
|
6364
|
+
* @category jmespath
|
|
6365
|
+
*/
|
|
5028
6366
|
toArray: {
|
|
5029
6367
|
_func: resolvedArgs => {
|
|
5030
6368
|
if (getTypeName(resolvedArgs[0]) === TYPE_ARRAY) {
|
|
@@ -5032,8 +6370,31 @@ function functions(
|
|
|
5032
6370
|
}
|
|
5033
6371
|
return [resolvedArgs[0]];
|
|
5034
6372
|
},
|
|
6373
|
+
|
|
5035
6374
|
_signature: [{ types: [TYPE_ANY] }],
|
|
5036
6375
|
},
|
|
6376
|
+
|
|
6377
|
+
/**
|
|
6378
|
+
* converts the passed arg to a number. The conversion happens as per the following rules
|
|
6379
|
+
* * string - Returns the parsed number.
|
|
6380
|
+
* * number - Returns the passed in value.
|
|
6381
|
+
* * array - null
|
|
6382
|
+
* * object - null
|
|
6383
|
+
* * boolean - null
|
|
6384
|
+
* * null - null
|
|
6385
|
+
* @param {any} arg
|
|
6386
|
+
* @return {number}
|
|
6387
|
+
* @function toNumber
|
|
6388
|
+
* @example
|
|
6389
|
+
* toNumber(1) //returns 1
|
|
6390
|
+
* @example
|
|
6391
|
+
* toNumber('10') //returns 10
|
|
6392
|
+
* @example
|
|
6393
|
+
* toNumber({a: 1}) //returns null
|
|
6394
|
+
* @example
|
|
6395
|
+
* toNumber(true()) //returns null
|
|
6396
|
+
* @category jmespath
|
|
6397
|
+
*/
|
|
5037
6398
|
toNumber: {
|
|
5038
6399
|
_func: resolvedArgs => {
|
|
5039
6400
|
const typeName = getTypeName(resolvedArgs[0]);
|
|
@@ -5047,6 +6408,20 @@ function functions(
|
|
|
5047
6408
|
},
|
|
5048
6409
|
_signature: [{ types: [TYPE_ANY] }],
|
|
5049
6410
|
},
|
|
6411
|
+
|
|
6412
|
+
/**
|
|
6413
|
+
* converts the passed `arg` to a string. The conversion happens as per the following rules
|
|
6414
|
+
* * string - Returns the passed in value.
|
|
6415
|
+
* * number/array/object/boolean - The JSON encoded value of the object.
|
|
6416
|
+
* @param {any} arg
|
|
6417
|
+
* @return {string}
|
|
6418
|
+
* @function toString
|
|
6419
|
+
* @example
|
|
6420
|
+
* toString(1) //returns '1'
|
|
6421
|
+
* @example
|
|
6422
|
+
* toString(true()) //returns 'true'
|
|
6423
|
+
* @category jmespath
|
|
6424
|
+
*/
|
|
5050
6425
|
toString: {
|
|
5051
6426
|
_func: resolvedArgs => {
|
|
5052
6427
|
if (getTypeName(resolvedArgs[0]) === TYPE_STRING) {
|
|
@@ -5054,8 +6429,30 @@ function functions(
|
|
|
5054
6429
|
}
|
|
5055
6430
|
return JSON.stringify(resolvedArgs[0]);
|
|
5056
6431
|
},
|
|
6432
|
+
|
|
5057
6433
|
_signature: [{ types: [TYPE_ANY] }],
|
|
5058
6434
|
},
|
|
6435
|
+
|
|
6436
|
+
/**
|
|
6437
|
+
* Returns the JavaScript type of the given `subject` argument as a string value.
|
|
6438
|
+
*
|
|
6439
|
+
* The return value MUST be one of the following:
|
|
6440
|
+
* * number
|
|
6441
|
+
* * string
|
|
6442
|
+
* * boolean
|
|
6443
|
+
* * array
|
|
6444
|
+
* * object
|
|
6445
|
+
* * null
|
|
6446
|
+
* @param {any} subject
|
|
6447
|
+
* @return {string}
|
|
6448
|
+
*
|
|
6449
|
+
* @function type
|
|
6450
|
+
* @example
|
|
6451
|
+
* type(1) //returns 'number'
|
|
6452
|
+
* @example
|
|
6453
|
+
* type('') //returns 'string'
|
|
6454
|
+
* @category jmespath
|
|
6455
|
+
*/
|
|
5059
6456
|
type: {
|
|
5060
6457
|
_func: resolvedArgs => ({
|
|
5061
6458
|
[TYPE_NUMBER]: 'number',
|
|
@@ -5068,6 +6465,18 @@ function functions(
|
|
|
5068
6465
|
}[getTypeName(resolvedArgs[0])]),
|
|
5069
6466
|
_signature: [{ types: [TYPE_ANY] }],
|
|
5070
6467
|
},
|
|
6468
|
+
|
|
6469
|
+
/**
|
|
6470
|
+
* Returns the values of the provided object `obj`. Note that because JSON hashes are
|
|
6471
|
+
* inherently unordered, the values associated with the provided object obj are
|
|
6472
|
+
* inherently unordered.
|
|
6473
|
+
* @param {object} obj
|
|
6474
|
+
* @return {array}
|
|
6475
|
+
* @function values
|
|
6476
|
+
* @example
|
|
6477
|
+
* values({a : 3, b : 4}) //returns [3, 4]
|
|
6478
|
+
* @category jmespath
|
|
6479
|
+
*/
|
|
5071
6480
|
values: {
|
|
5072
6481
|
_func: resolvedArgs => {
|
|
5073
6482
|
const arg = valueOf(resolvedArgs[0]);
|
|
@@ -5076,6 +6485,19 @@ function functions(
|
|
|
5076
6485
|
},
|
|
5077
6486
|
_signature: [{ types: [TYPE_ANY] }],
|
|
5078
6487
|
},
|
|
6488
|
+
|
|
6489
|
+
/**
|
|
6490
|
+
* Returns a convolved (zipped) array containing grouped arrays of values from
|
|
6491
|
+
* the array arguments from index 0, 1, 2, etc.
|
|
6492
|
+
* This function accepts a variable number of arguments.
|
|
6493
|
+
* The length of the returned array is equal to the length of the shortest array.
|
|
6494
|
+
* @param {...array} arrays array of arrays to zip together
|
|
6495
|
+
* @return {array} An array of arrays with elements zipped together
|
|
6496
|
+
* @function zip
|
|
6497
|
+
* @example
|
|
6498
|
+
* zip([1, 2, 3], [4, 5, 6]) //returns [[1, 4], [2, 5], [3, 6]]
|
|
6499
|
+
* @category jmespath
|
|
6500
|
+
*/
|
|
5079
6501
|
zip: {
|
|
5080
6502
|
_func: args => {
|
|
5081
6503
|
const count = args.reduce((min, current) => Math.min(min, current.length), args[0].length);
|
|
@@ -5094,13 +6516,17 @@ function functions(
|
|
|
5094
6516
|
return functionMap;
|
|
5095
6517
|
}
|
|
5096
6518
|
|
|
6519
|
+
/* eslint-disable max-classes-per-file */
|
|
6520
|
+
|
|
6521
|
+
// Type constants used to define functions.
|
|
5097
6522
|
const {
|
|
5098
6523
|
TYPE_CLASS,
|
|
5099
6524
|
TYPE_ANY,
|
|
5100
6525
|
} = dataTypes;
|
|
6526
|
+
|
|
5101
6527
|
function getToNumber(stringToNumber, debug = []) {
|
|
5102
6528
|
return value => {
|
|
5103
|
-
const n = getValueOf(value);
|
|
6529
|
+
const n = getValueOf(value); // in case it's an object that implements valueOf()
|
|
5104
6530
|
if (n === null) return null;
|
|
5105
6531
|
if (n instanceof Array) {
|
|
5106
6532
|
debug.push('Converted array to zero');
|
|
@@ -5116,20 +6542,26 @@ function getToNumber(stringToNumber, debug = []) {
|
|
|
5116
6542
|
}
|
|
5117
6543
|
function toString(a) {
|
|
5118
6544
|
if (a === null || a === undefined) return '';
|
|
6545
|
+
// don't call a 'toString' method, since we could have a child named 'toString()'
|
|
5119
6546
|
return a.toString();
|
|
5120
6547
|
}
|
|
6548
|
+
|
|
5121
6549
|
const defaultStringToNumber = (str => {
|
|
5122
6550
|
const n = +str;
|
|
5123
6551
|
return Number.isNaN(n) ? 0 : n;
|
|
5124
6552
|
});
|
|
6553
|
+
|
|
5125
6554
|
function isClass(obj) {
|
|
5126
6555
|
if (obj === null) return false;
|
|
5127
6556
|
if (Array.isArray(obj)) return false;
|
|
5128
6557
|
return obj.constructor.name !== 'Object';
|
|
5129
6558
|
}
|
|
6559
|
+
|
|
5130
6560
|
function matchClass(arg, expectedList) {
|
|
6561
|
+
// checking isClass() generates a dependency -- so call it only if necessary
|
|
5131
6562
|
return expectedList.includes(TYPE_CLASS) && isClass(arg);
|
|
5132
6563
|
}
|
|
6564
|
+
|
|
5133
6565
|
class Runtime {
|
|
5134
6566
|
constructor(debug, toNumber, customFunctions = {}) {
|
|
5135
6567
|
this.strictDeepEqual = strictDeepEqual;
|
|
@@ -5144,16 +6576,25 @@ class Runtime {
|
|
|
5144
6576
|
toString,
|
|
5145
6577
|
debug,
|
|
5146
6578
|
);
|
|
6579
|
+
|
|
5147
6580
|
Object.entries(
|
|
5148
6581
|
openFormulaFunctions(getValueOf, toString, toNumber, debug),
|
|
5149
6582
|
).forEach(([fname, func]) => {
|
|
5150
6583
|
this.functionTable[fname] = func;
|
|
5151
6584
|
});
|
|
6585
|
+
|
|
5152
6586
|
Object.entries(customFunctions).forEach(([fname, func]) => {
|
|
5153
6587
|
this.functionTable[fname] = func;
|
|
5154
6588
|
});
|
|
5155
6589
|
}
|
|
6590
|
+
|
|
6591
|
+
// eslint-disable-next-line class-methods-use-this
|
|
5156
6592
|
_validateArgs(argName, args, signature, bResolved) {
|
|
6593
|
+
// Validating the args requires validating
|
|
6594
|
+
// the correct arity and the correct type of each arg.
|
|
6595
|
+
// If the last argument is declared as variadic, then we need
|
|
6596
|
+
// a minimum number of args to be required. Otherwise it has to
|
|
6597
|
+
// be an exact amount.
|
|
5157
6598
|
if (signature.length === 0) {
|
|
5158
6599
|
return;
|
|
5159
6600
|
}
|
|
@@ -5172,31 +6613,39 @@ class Runtime {
|
|
|
5172
6613
|
+ `takes ${signature.length}${pluralized
|
|
5173
6614
|
} but received ${args.length}`);
|
|
5174
6615
|
}
|
|
6616
|
+
// if the arguments are unresolved, there's no point in validating types
|
|
5175
6617
|
if (!bResolved) return;
|
|
5176
6618
|
let currentSpec;
|
|
5177
6619
|
let actualType;
|
|
5178
6620
|
const limit = Math.min(signature.length, args.length);
|
|
5179
6621
|
for (let i = 0; i < limit; i += 1) {
|
|
5180
6622
|
currentSpec = signature[i].types;
|
|
6623
|
+
// Try to avoid checks that will introspect the object and generate dependencies
|
|
5181
6624
|
if (!matchClass(args[i], currentSpec) && !currentSpec.includes(TYPE_ANY)) {
|
|
5182
6625
|
actualType = getTypeNames(args[i]);
|
|
6626
|
+
// eslint-disable-next-line no-param-reassign
|
|
5183
6627
|
args[i] = matchType(actualType, currentSpec, args[i], argName, this.toNumber, toString);
|
|
5184
6628
|
}
|
|
5185
6629
|
}
|
|
5186
6630
|
}
|
|
6631
|
+
|
|
5187
6632
|
callFunction(name, resolvedArgs, data, interpreter, bResolved = true) {
|
|
6633
|
+
// this check will weed out 'valueOf', 'toString' etc
|
|
5188
6634
|
if (!Object.prototype.hasOwnProperty.call(this.functionTable, name)) throw new Error(`Unknown function: ${name}()`);
|
|
6635
|
+
|
|
5189
6636
|
const functionEntry = this.functionTable[name];
|
|
5190
6637
|
this._validateArgs(name, resolvedArgs, functionEntry._signature, bResolved);
|
|
5191
6638
|
return functionEntry._func.call(this, resolvedArgs, data, interpreter);
|
|
5192
6639
|
}
|
|
5193
6640
|
}
|
|
6641
|
+
|
|
5194
6642
|
class Formula {
|
|
5195
6643
|
constructor(debug, customFunctions, stringToNumberFn) {
|
|
5196
6644
|
this.debug = debug;
|
|
5197
6645
|
this.toNumber = getToNumber(stringToNumberFn || defaultStringToNumber, debug);
|
|
5198
6646
|
this.runtime = new Runtime(debug, this.toNumber, customFunctions);
|
|
5199
6647
|
}
|
|
6648
|
+
|
|
5200
6649
|
compile(stream, allowedGlobalNames = []) {
|
|
5201
6650
|
let ast;
|
|
5202
6651
|
try {
|
|
@@ -5208,7 +6657,11 @@ class Formula {
|
|
|
5208
6657
|
}
|
|
5209
6658
|
return ast;
|
|
5210
6659
|
}
|
|
6660
|
+
|
|
5211
6661
|
search(node, data, globals = {}, language = 'en-US') {
|
|
6662
|
+
// This needs to be improved. Both the interpreter and runtime depend on
|
|
6663
|
+
// each other. The runtime needs the interpreter to support exprefs.
|
|
6664
|
+
// There's likely a clean way to avoid the cyclic dependency.
|
|
5212
6665
|
this.runtime.interpreter = new TreeInterpreter(
|
|
5213
6666
|
this.runtime,
|
|
5214
6667
|
globals,
|
|
@@ -5217,6 +6670,7 @@ class Formula {
|
|
|
5217
6670
|
this.debug,
|
|
5218
6671
|
language,
|
|
5219
6672
|
);
|
|
6673
|
+
|
|
5220
6674
|
try {
|
|
5221
6675
|
return this.runtime.interpreter.search(node, data);
|
|
5222
6676
|
} catch (e) {
|
|
@@ -5226,7 +6680,33 @@ class Formula {
|
|
|
5226
6680
|
}
|
|
5227
6681
|
}
|
|
5228
6682
|
|
|
6683
|
+
/*
|
|
6684
|
+
Copyright 2021 Adobe. All rights reserved.
|
|
6685
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
6686
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
6687
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6688
|
+
|
|
6689
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
6690
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
6691
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
6692
|
+
governing permissions and limitations under the License.
|
|
6693
|
+
*/
|
|
6694
|
+
|
|
6695
|
+
/**
|
|
6696
|
+
* Returns an instance of JSON JsonFormula Expression that can be executed later on with
|
|
6697
|
+
* multiple instances of JSON Data. The instance of the class has a single search
|
|
6698
|
+
* function that can be used to evaluate the expression on a json payload. The advantage
|
|
6699
|
+
* of using this over {jsonJsonFormula} function is that it can be performant if a single expression
|
|
6700
|
+
* has to be used for multiple json data instances.
|
|
6701
|
+
*/
|
|
5229
6702
|
class JsonFormula {
|
|
6703
|
+
/**
|
|
6704
|
+
* @param customFunctions {*} custom functions needed by a hosting application.
|
|
6705
|
+
* @param stringToNumber {function} A function that converts string values to numbers.
|
|
6706
|
+
* Can be used to convert currencies/dates to numbers
|
|
6707
|
+
* @param language
|
|
6708
|
+
* @param debug {array} will be populated with any errors/warnings
|
|
6709
|
+
*/
|
|
5230
6710
|
constructor(
|
|
5231
6711
|
customFunctions = {},
|
|
5232
6712
|
stringToNumber = null,
|
|
@@ -5237,10 +6717,25 @@ class JsonFormula {
|
|
|
5237
6717
|
this.debug = debug;
|
|
5238
6718
|
this.formula = new Formula(debug, customFunctions, stringToNumber);
|
|
5239
6719
|
}
|
|
6720
|
+
|
|
6721
|
+
/**
|
|
6722
|
+
* Evaluates the JsonFormula on a particular json payload and return the result
|
|
6723
|
+
* @param json {object} the json data on which the expression needs to be evaluated
|
|
6724
|
+
* @param globals {*} global objects that can be accessed via custom functions.
|
|
6725
|
+
* @returns {*} the result of the expression being evaluated
|
|
6726
|
+
*/
|
|
5240
6727
|
search(expression, json, globals = {}, language = 'en-US') {
|
|
5241
6728
|
const ast = this.compile(expression, Object.keys(globals));
|
|
5242
6729
|
return this.run(ast, json, language, globals);
|
|
5243
6730
|
}
|
|
6731
|
+
|
|
6732
|
+
/**
|
|
6733
|
+
* Execute a previously compiled expression against a json object and return the result
|
|
6734
|
+
* @param ast {object} The abstract syntax tree returned from compile()
|
|
6735
|
+
* @param json {object} the json data on which the expression needs to be evaluated
|
|
6736
|
+
* @param globals {*} set of objects available in global scope
|
|
6737
|
+
* @returns {*} the result of the expression being evaluated
|
|
6738
|
+
*/
|
|
5244
6739
|
run(ast, json, language, globals) {
|
|
5245
6740
|
return this.formula.search(
|
|
5246
6741
|
ast,
|
|
@@ -5249,6 +6744,14 @@ class JsonFormula {
|
|
|
5249
6744
|
language,
|
|
5250
6745
|
);
|
|
5251
6746
|
}
|
|
6747
|
+
|
|
6748
|
+
/*
|
|
6749
|
+
* Creates a compiled expression that can be executed later on with some data.
|
|
6750
|
+
* @param expression {string} the expression to evaluate
|
|
6751
|
+
* @param allowedGlobalNames {string[]} A list of names of the global variables
|
|
6752
|
+
* being used in the expression.
|
|
6753
|
+
* @param debug {array} will be populated with any errors/warnings
|
|
6754
|
+
*/
|
|
5252
6755
|
compile(expression, allowedGlobalNames = []) {
|
|
5253
6756
|
this.debug.length = 0;
|
|
5254
6757
|
return this.formula.compile(expression, allowedGlobalNames);
|
|
@@ -5301,8 +6804,7 @@ class RuleEngine {
|
|
|
5301
6804
|
}
|
|
5302
6805
|
|
|
5303
6806
|
const defaults = {
|
|
5304
|
-
visible: true
|
|
5305
|
-
enabled: true
|
|
6807
|
+
visible: true
|
|
5306
6808
|
};
|
|
5307
6809
|
class Fieldset extends Container {
|
|
5308
6810
|
constructor(params, _options) {
|
|
@@ -5345,6 +6847,12 @@ class Fieldset extends Container {
|
|
|
5345
6847
|
}
|
|
5346
6848
|
}
|
|
5347
6849
|
|
|
6850
|
+
var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
6851
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6852
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
6853
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6854
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6855
|
+
};
|
|
5348
6856
|
class InstanceManager extends Fieldset {
|
|
5349
6857
|
get maxOccur() {
|
|
5350
6858
|
return this._jsonModel.maxItems;
|
|
@@ -5362,22 +6870,13 @@ class InstanceManager extends Fieldset {
|
|
|
5362
6870
|
return this.removeItem(action);
|
|
5363
6871
|
}
|
|
5364
6872
|
}
|
|
5365
|
-
__decorate([
|
|
6873
|
+
__decorate$1([
|
|
5366
6874
|
dependencyTracked()
|
|
5367
6875
|
], InstanceManager.prototype, "maxOccur", null);
|
|
5368
|
-
__decorate([
|
|
6876
|
+
__decorate$1([
|
|
5369
6877
|
dependencyTracked()
|
|
5370
6878
|
], InstanceManager.prototype, "minOccur", null);
|
|
5371
6879
|
|
|
5372
|
-
class ValidationError {
|
|
5373
|
-
fieldName;
|
|
5374
|
-
errorMessages;
|
|
5375
|
-
constructor(fieldName = '', errorMessages = []) {
|
|
5376
|
-
this.errorMessages = errorMessages;
|
|
5377
|
-
this.fieldName = fieldName;
|
|
5378
|
-
}
|
|
5379
|
-
}
|
|
5380
|
-
|
|
5381
6880
|
const dateRegex = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
|
|
5382
6881
|
const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
5383
6882
|
const daysInMonth = (leapYear, month) => {
|
|
@@ -5651,9 +7150,33 @@ const Constraints = {
|
|
|
5651
7150
|
}
|
|
5652
7151
|
};
|
|
5653
7152
|
|
|
7153
|
+
/*************************************************************************
|
|
7154
|
+
* ADOBE CONFIDENTIAL
|
|
7155
|
+
* ___________________
|
|
7156
|
+
*
|
|
7157
|
+
* Copyright 2022 Adobe
|
|
7158
|
+
* All Rights Reserved.
|
|
7159
|
+
*
|
|
7160
|
+
* NOTICE: All information contained herein is, and remains
|
|
7161
|
+
* the property of Adobe and its suppliers, if any. The intellectual
|
|
7162
|
+
* and technical concepts contained herein are proprietary to Adobe
|
|
7163
|
+
* and its suppliers and are protected by all applicable intellectual
|
|
7164
|
+
* property laws, including trade secret and copyright laws.
|
|
7165
|
+
* Dissemination of this information or reproduction of this material
|
|
7166
|
+
* is strictly forbidden unless prior written permission is obtained
|
|
7167
|
+
* from Adobe.
|
|
7168
|
+
**************************************************************************/
|
|
7169
|
+
/**
|
|
7170
|
+
* https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
|
7171
|
+
* Credit: https://git.corp.adobe.com/dc/dfl/blob/master/src/patterns/parseDateTimeSkeleton.js
|
|
7172
|
+
* Created a separate library to be used elsewhere as well.
|
|
7173
|
+
*/
|
|
5654
7174
|
const DATE_TIME_REGEX =
|
|
7175
|
+
// eslint-disable-next-line max-len
|
|
5655
7176
|
/(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvV]{1,5}|[zZOvVxX]{1,3}|S{1,3}|'(?:[^']|'')*')|[^a-zA-Z']+/g;
|
|
7177
|
+
|
|
5656
7178
|
const ShorthandStyles$1 = ["full", "long", "medium", "short"];
|
|
7179
|
+
|
|
5657
7180
|
function getSkeleton(skeleton, language) {
|
|
5658
7181
|
if (ShorthandStyles$1.find(type => skeleton.includes(type))) {
|
|
5659
7182
|
const parsed = parseDateStyle(skeleton, language);
|
|
@@ -5674,13 +7197,24 @@ function getSkeleton(skeleton, language) {
|
|
|
5674
7197
|
}
|
|
5675
7198
|
return skeleton;
|
|
5676
7199
|
}
|
|
7200
|
+
|
|
7201
|
+
/**
|
|
7202
|
+
*
|
|
7203
|
+
* @param skeleton shorthand style for the date concatenated with shorthand style of time. The
|
|
7204
|
+
* Shorthand style for both date and time is one of ['full', 'long', 'medium', 'short'].
|
|
7205
|
+
* @param language {string} language to parse the date shorthand style
|
|
7206
|
+
* @returns {[*,string][]}
|
|
7207
|
+
*/
|
|
5677
7208
|
function parseDateStyle(skeleton, language) {
|
|
5678
7209
|
const options = {};
|
|
7210
|
+
// the skeleton could have two keywords -- one for date, one for time
|
|
5679
7211
|
const styles = skeleton.split(/\s/).filter(s => s.length);
|
|
5680
7212
|
options.dateStyle = styles[0];
|
|
5681
7213
|
if (styles.length > 1) options.timeStyle = styles[1];
|
|
7214
|
+
|
|
5682
7215
|
const testDate = new Date(2000, 2, 1, 2, 3, 4);
|
|
5683
7216
|
const parts = new Intl.DateTimeFormat(language, options).formatToParts(testDate);
|
|
7217
|
+
// oddly, the formatted month name can be different from the standalone month name
|
|
5684
7218
|
const formattedMarch = parts.find(p => p.type === 'month').value;
|
|
5685
7219
|
const longMarch = new Intl.DateTimeFormat(language, {month: 'long'}).formatToParts(testDate)[0].value;
|
|
5686
7220
|
const shortMarch = new Intl.DateTimeFormat(language, {month: 'short'}).formatToParts(testDate)[0].value;
|
|
@@ -5704,6 +7238,11 @@ function parseDateStyle(skeleton, language) {
|
|
|
5704
7238
|
});
|
|
5705
7239
|
return result;
|
|
5706
7240
|
}
|
|
7241
|
+
|
|
7242
|
+
/**
|
|
7243
|
+
* Parse Date time skeleton into Intl.DateTimeFormatOptions parts
|
|
7244
|
+
* Ref: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
|
7245
|
+
*/
|
|
5707
7246
|
function parseDateTimeSkeleton(skeleton, language) {
|
|
5708
7247
|
if (ShorthandStyles$1.find(type => skeleton.includes(type))) {
|
|
5709
7248
|
return parseDateStyle(skeleton, language);
|
|
@@ -5712,9 +7251,11 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5712
7251
|
skeleton.replace(DATE_TIME_REGEX, match => {
|
|
5713
7252
|
const len = match.length;
|
|
5714
7253
|
switch (match[0]) {
|
|
7254
|
+
// Era
|
|
5715
7255
|
case 'G':
|
|
5716
7256
|
result.push(['era', len === 4 ? 'long' : len === 5 ? 'narrow' : 'short', len]);
|
|
5717
7257
|
break;
|
|
7258
|
+
// Year
|
|
5718
7259
|
case 'y':
|
|
5719
7260
|
result.push(['year', len === 2 ? '2-digit' : 'numeric', len]);
|
|
5720
7261
|
break;
|
|
@@ -5725,13 +7266,16 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5725
7266
|
throw new RangeError(
|
|
5726
7267
|
'`Y/u/U/r` (year) patterns are not supported, use `y` instead'
|
|
5727
7268
|
);
|
|
7269
|
+
// Quarter
|
|
5728
7270
|
case 'q':
|
|
5729
7271
|
case 'Q':
|
|
5730
7272
|
throw new RangeError('`q/Q` (quarter) patterns are not supported');
|
|
7273
|
+
// Month
|
|
5731
7274
|
case 'M':
|
|
5732
7275
|
case 'L':
|
|
5733
7276
|
result.push(['month', ['numeric', '2-digit', 'short', 'long', 'narrow'][len - 1], len]);
|
|
5734
7277
|
break;
|
|
7278
|
+
// Week
|
|
5735
7279
|
case 'w':
|
|
5736
7280
|
case 'W':
|
|
5737
7281
|
throw new RangeError('`w/W` (week) patterns are not supported');
|
|
@@ -5744,6 +7288,7 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5744
7288
|
throw new RangeError(
|
|
5745
7289
|
'`D/F/g` (day) patterns are not supported, use `d` instead'
|
|
5746
7290
|
);
|
|
7291
|
+
// Weekday
|
|
5747
7292
|
case 'E':
|
|
5748
7293
|
result.push(['weekday', ['short', 'short', 'short', 'long', 'narrow', 'narrow'][len - 1], len]);
|
|
5749
7294
|
break;
|
|
@@ -5759,14 +7304,16 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5759
7304
|
}
|
|
5760
7305
|
result.push(['weekday', ['short', 'long', 'narrow', 'short'][len - 3], len]);
|
|
5761
7306
|
break;
|
|
5762
|
-
|
|
7307
|
+
// Period
|
|
7308
|
+
case 'a': // AM, PM
|
|
5763
7309
|
result.push(['hour12', true, 1]);
|
|
5764
7310
|
break;
|
|
5765
|
-
case 'b':
|
|
5766
|
-
case 'B':
|
|
7311
|
+
case 'b': // am, pm, noon, midnight
|
|
7312
|
+
case 'B': // flexible day periods
|
|
5767
7313
|
throw new RangeError(
|
|
5768
7314
|
'`b/B` (period) patterns are not supported, use `a` instead'
|
|
5769
7315
|
);
|
|
7316
|
+
// Hour
|
|
5770
7317
|
case 'h':
|
|
5771
7318
|
result.push(['hourCycle', 'h12']);
|
|
5772
7319
|
result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
|
|
@@ -5789,9 +7336,11 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5789
7336
|
throw new RangeError(
|
|
5790
7337
|
'`j/J/C` (hour) patterns are not supported, use `h/H/K/k` instead'
|
|
5791
7338
|
);
|
|
7339
|
+
// Minute
|
|
5792
7340
|
case 'm':
|
|
5793
7341
|
result.push(['minute', ['numeric', '2-digit'][len - 1], len]);
|
|
5794
7342
|
break;
|
|
7343
|
+
// Second
|
|
5795
7344
|
case 's':
|
|
5796
7345
|
result.push(['second', ['numeric', '2-digit'][len - 1], len]);
|
|
5797
7346
|
break;
|
|
@@ -5802,19 +7351,23 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5802
7351
|
throw new RangeError(
|
|
5803
7352
|
'`S/A` (millisecond) patterns are not supported, use `s` instead'
|
|
5804
7353
|
);
|
|
5805
|
-
|
|
7354
|
+
// Zone
|
|
7355
|
+
case 'O': // timeZone GMT-8 or GMT-08:00
|
|
5806
7356
|
result.push(['timeZoneName', len < 4 ? 'shortOffset' : 'longOffset', len]);
|
|
5807
7357
|
result.push(['x-timeZoneName', len < 4 ? 'O' : 'OOOO', len]);
|
|
5808
7358
|
break;
|
|
5809
|
-
case 'X':
|
|
5810
|
-
case 'x':
|
|
5811
|
-
case 'Z':
|
|
7359
|
+
case 'X': // 1, 2, 3, 4: The ISO8601 varios formats
|
|
7360
|
+
case 'x': // 1, 2, 3, 4: The ISO8601 varios formats
|
|
7361
|
+
case 'Z': // 1..3, 4, 5: The ISO8601 varios formats
|
|
7362
|
+
// Z, ZZ, ZZZ should produce -0800
|
|
7363
|
+
// ZZZZ should produce GMT-08:00
|
|
7364
|
+
// ZZZZZ should produce -8:00 or -07:52:58
|
|
5812
7365
|
result.push(['timeZoneName', 'longOffset', 1]);
|
|
5813
7366
|
result.push(['x-timeZoneName', match, 1]);
|
|
5814
7367
|
break;
|
|
5815
|
-
case 'z':
|
|
5816
|
-
case 'v':
|
|
5817
|
-
case 'V':
|
|
7368
|
+
case 'z': // 1..3, 4: specific non-location format
|
|
7369
|
+
case 'v': // 1, 4: generic non-location format
|
|
7370
|
+
case 'V': // 1, 2, 3, 4: time zone ID or city
|
|
5818
7371
|
throw new RangeError(
|
|
5819
7372
|
'z/v/V` (timeZone) patterns are not supported, use `X/x/Z/O` instead'
|
|
5820
7373
|
);
|
|
@@ -5829,6 +7382,29 @@ function parseDateTimeSkeleton(skeleton, language) {
|
|
|
5829
7382
|
return result;
|
|
5830
7383
|
}
|
|
5831
7384
|
|
|
7385
|
+
/*************************************************************************
|
|
7386
|
+
* ADOBE CONFIDENTIAL
|
|
7387
|
+
* ___________________
|
|
7388
|
+
*
|
|
7389
|
+
* Copyright 2022 Adobe
|
|
7390
|
+
* All Rights Reserved.
|
|
7391
|
+
*
|
|
7392
|
+
* NOTICE: All information contained herein is, and remains
|
|
7393
|
+
* the property of Adobe and its suppliers, if any. The intellectual
|
|
7394
|
+
* and technical concepts contained herein are proprietary to Adobe
|
|
7395
|
+
* and its suppliers and are protected by all applicable intellectual
|
|
7396
|
+
* property laws, including trade secret and copyright laws.
|
|
7397
|
+
* Dissemination of this information or reproduction of this material
|
|
7398
|
+
* is strictly forbidden unless prior written permission is obtained
|
|
7399
|
+
* from Adobe.
|
|
7400
|
+
**************************************************************************/
|
|
7401
|
+
|
|
7402
|
+
/**
|
|
7403
|
+
* in some cases, DateTimeFormat doesn't respect the 'numeric' vs. '2-digit' setting
|
|
7404
|
+
* for time values. The function corrects that
|
|
7405
|
+
* @param formattedParts instance of Intl.DateTimeFormatPart[]
|
|
7406
|
+
* @param parsed
|
|
7407
|
+
*/
|
|
5832
7408
|
function fixDigits(formattedParts, parsed) {
|
|
5833
7409
|
['hour', 'minute', 'second'].forEach(type => {
|
|
5834
7410
|
const defn = formattedParts.find(f => f.type === type);
|
|
@@ -5838,29 +7414,55 @@ function fixDigits(formattedParts, parsed) {
|
|
|
5838
7414
|
if (fmt === 'numeric' && defn.value.length === 2 && defn.value.charAt(0) === '0') defn.value = defn.value.slice(1);
|
|
5839
7415
|
});
|
|
5840
7416
|
}
|
|
7417
|
+
|
|
5841
7418
|
function fixYear(formattedParts, parsed) {
|
|
7419
|
+
// two digit years are handled differently in DateTimeFormat. 00 becomes 1900
|
|
7420
|
+
// providing a two digit year 0010 gets formatted to 10 and when parsed becomes 1910
|
|
7421
|
+
// Hence we need to pad the year with 0 as required by the skeleton and mentioned in
|
|
7422
|
+
// unicode. https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-year
|
|
5842
7423
|
const defn = formattedParts.find(f => f.type === 'year');
|
|
5843
7424
|
if (!defn) return;
|
|
7425
|
+
// eslint-disable-next-line no-unused-vars
|
|
5844
7426
|
const chars = parsed.find(pair => pair[0] === 'year')[2];
|
|
5845
7427
|
while(defn.value.length < chars) {
|
|
5846
7428
|
defn.value = `0${defn.value}`;
|
|
5847
7429
|
}
|
|
5848
7430
|
}
|
|
7431
|
+
|
|
7432
|
+
/**
|
|
7433
|
+
*
|
|
7434
|
+
* @param dateValue {Date}
|
|
7435
|
+
* @param language {string}
|
|
7436
|
+
* @param skeleton {string}
|
|
7437
|
+
* @param timeZone {string}
|
|
7438
|
+
* @returns {T}
|
|
7439
|
+
*/
|
|
5849
7440
|
function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
7441
|
+
// DateTimeFormat renames some of the options in its formatted output
|
|
7442
|
+
//@ts-ignore
|
|
5850
7443
|
const mappings = key => ({
|
|
5851
7444
|
hour12: 'dayPeriod',
|
|
5852
7445
|
fractionalSecondDigits: 'fractionalSecond',
|
|
5853
7446
|
})[key] || key;
|
|
7447
|
+
|
|
7448
|
+
// produces an array of name/value pairs of skeleton parts
|
|
5854
7449
|
const allParameters = parseDateTimeSkeleton(skeleton, language);
|
|
5855
7450
|
allParameters.push(['timeZone', timeZone]);
|
|
7451
|
+
|
|
5856
7452
|
const parsed = allParameters.filter(p => !p[0].startsWith('x-'));
|
|
5857
7453
|
const nonStandard = allParameters.filter(p => p[0].startsWith('x-'));
|
|
7454
|
+
// reduce to a set of options that can be used to format
|
|
5858
7455
|
const options = Object.fromEntries(parsed);
|
|
5859
7456
|
delete options.literal;
|
|
7457
|
+
|
|
5860
7458
|
const df = new Intl.DateTimeFormat(language, options);
|
|
7459
|
+
// formattedParts will have all the pieces we need for our date -- but not in the correct order
|
|
5861
7460
|
const formattedParts = df.formatToParts(dateValue);
|
|
7461
|
+
|
|
5862
7462
|
fixDigits(formattedParts, allParameters);
|
|
5863
7463
|
fixYear(formattedParts, parsed);
|
|
7464
|
+
// iterate through the original parsed components and use its ordering and literals,
|
|
7465
|
+
// and add the formatted pieces
|
|
5864
7466
|
return parsed.reduce((result, cur) => {
|
|
5865
7467
|
if (cur[0] === 'literal') result.push(cur);
|
|
5866
7468
|
else {
|
|
@@ -5870,25 +7472,37 @@ function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
|
5870
7472
|
const category = tz[0];
|
|
5871
7473
|
if (category === 'Z') {
|
|
5872
7474
|
if (tz.length < 4) {
|
|
7475
|
+
// handle 'Z', 'ZZ', 'ZZZ' Time Zone: ISO8601 basic hms? / RFC 822
|
|
5873
7476
|
v.value = v.value.replace(/(GMT|:)/g, '');
|
|
5874
7477
|
if (v.value === '') v.value = '+0000';
|
|
5875
7478
|
} else if (tz.length === 5) {
|
|
7479
|
+
// 'ZZZZZ' Time Zone: ISO8601 extended hms?
|
|
5876
7480
|
if (v.value === 'GMT') v.value = 'Z';
|
|
5877
7481
|
else v.value = v.value.replace(/GMT/, '');
|
|
5878
7482
|
}
|
|
5879
7483
|
}
|
|
5880
7484
|
if (category === 'X' || category === 'x') {
|
|
5881
7485
|
if (tz.length === 1) {
|
|
7486
|
+
// 'X' ISO8601 basic hm?, with Z for 0
|
|
7487
|
+
// -08, +0530, Z
|
|
7488
|
+
// 'x' ISO8601 basic hm?, without Z for 0
|
|
5882
7489
|
v.value = v.value.replace(/(GMT|:(00)?)/g, '');
|
|
5883
7490
|
}
|
|
5884
7491
|
if (tz.length === 2) {
|
|
7492
|
+
// 'XX' ISO8601 basic hm, with Z
|
|
7493
|
+
// -0800, Z
|
|
7494
|
+
// 'xx' ISO8601 basic hm, without Z
|
|
5885
7495
|
v.value = v.value.replace(/(GMT|:)/g, '');
|
|
5886
7496
|
}
|
|
5887
7497
|
if (tz.length === 3) {
|
|
7498
|
+
// 'XXX' ISO8601 extended hm, with Z
|
|
7499
|
+
// -08:00, Z
|
|
7500
|
+
// 'xxx' ISO8601 extended hm, without Z
|
|
5888
7501
|
v.value = v.value.replace(/GMT/g, '');
|
|
5889
7502
|
}
|
|
5890
7503
|
if (category === 'X' && v.value === '') v.value = 'Z';
|
|
5891
7504
|
} else if (tz === 'O') {
|
|
7505
|
+
// eliminate 'GMT', leading and trailing zeros
|
|
5892
7506
|
v.value = v.value.replace(/GMT/g, '').replace(/0(\d+):/, '$1:').replace(/:00/, '');
|
|
5893
7507
|
if (v.value === '') v.value = '+0';
|
|
5894
7508
|
}
|
|
@@ -5898,12 +7512,18 @@ function formatDateToParts(dateValue, language, skeleton, timeZone) {
|
|
|
5898
7512
|
return result;
|
|
5899
7513
|
}, []);
|
|
5900
7514
|
}
|
|
7515
|
+
|
|
7516
|
+
/**
|
|
7517
|
+
*
|
|
7518
|
+
* @param dateValue {Date}
|
|
7519
|
+
* @param language {string}
|
|
7520
|
+
* @param skeleton {string}
|
|
7521
|
+
* @param timeZone {string}
|
|
7522
|
+
*/
|
|
5901
7523
|
function formatDate(dateValue, language, skeleton, timeZone) {
|
|
5902
|
-
if (skeleton.startsWith('date|')) {
|
|
5903
|
-
skeleton = skeleton.split('|')[1];
|
|
5904
|
-
}
|
|
5905
7524
|
if (ShorthandStyles$1.find(type => skeleton.includes(type))) {
|
|
5906
7525
|
const options = {timeZone};
|
|
7526
|
+
// the skeleton could have two keywords -- one for date, one for time
|
|
5907
7527
|
const parts = skeleton.split(/\s/).filter(s => s.length);
|
|
5908
7528
|
if (ShorthandStyles$1.indexOf(parts[0]) > -1) {
|
|
5909
7529
|
options.dateStyle = parts[0];
|
|
@@ -5939,7 +7559,9 @@ const currencies = {
|
|
|
5939
7559
|
'ru-RU': 'RUB',
|
|
5940
7560
|
'tr-TR': 'TRY'
|
|
5941
7561
|
};
|
|
7562
|
+
|
|
5942
7563
|
const locales = Object.keys(currencies);
|
|
7564
|
+
|
|
5943
7565
|
const getCurrency = function (locale) {
|
|
5944
7566
|
if (locales.indexOf(locale) > -1) {
|
|
5945
7567
|
return currencies[locale]
|
|
@@ -5953,7 +7575,8 @@ const getCurrency = function (locale) {
|
|
|
5953
7575
|
};
|
|
5954
7576
|
|
|
5955
7577
|
const NUMBER_REGEX =
|
|
5956
|
-
|
|
7578
|
+
// eslint-disable-next-line max-len
|
|
7579
|
+
/(?:[#]+|[@]+(?:#+)?|[0]+|[,]|[.]|[-]|[+]|[%]|[¤]{1,4}(?:\/([a-zA-Z]{3}))?|[;]|[K]{1,2}|E{1,2}[+]?|'(?:[^']|'')*')|[^a-zA-Z']+/g;
|
|
5957
7580
|
const supportedUnits = ['acre', 'bit', 'byte', 'celsius', 'centimeter', 'day',
|
|
5958
7581
|
'degree', 'fahrenheit', 'fluid-ounce', 'foot', 'gallon', 'gigabit',
|
|
5959
7582
|
'gigabyte', 'gram', 'hectare', 'hour', 'inch', 'kilobit', 'kilobyte',
|
|
@@ -5961,6 +7584,8 @@ const supportedUnits = ['acre', 'bit', 'byte', 'celsius', 'centimeter', 'day',
|
|
|
5961
7584
|
'mile-scandinavian', 'milliliter', 'millimeter', 'millisecond', 'minute', 'month',
|
|
5962
7585
|
'ounce', 'percent', 'petabyte', 'pound', 'second', 'stone', 'terabit', 'terabyte', 'week', 'yard', 'year'].join('|');
|
|
5963
7586
|
const ShorthandStyles = [/^currency(?:\/([a-zA-Z]{3}))?$/, /^decimal$/, /^integer$/, /^percent$/, new RegExp(`^unit\/(${supportedUnits})$`)];
|
|
7587
|
+
|
|
7588
|
+
|
|
5964
7589
|
function parseNumberSkeleton(skeleton, language) {
|
|
5965
7590
|
const options = {};
|
|
5966
7591
|
const order = [];
|
|
@@ -5989,7 +7614,6 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
5989
7614
|
break;
|
|
5990
7615
|
case 4:
|
|
5991
7616
|
options.style = 'percent';
|
|
5992
|
-
options.maximumFractionDigits = 2;
|
|
5993
7617
|
break;
|
|
5994
7618
|
case 5:
|
|
5995
7619
|
options.style = "unit";
|
|
@@ -6006,7 +7630,7 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
6006
7630
|
options.minimumIntegerDigits = 1;
|
|
6007
7631
|
options.maximumFractionDigits = 0;
|
|
6008
7632
|
options.minimumFractionDigits = 0;
|
|
6009
|
-
skeleton.replace(NUMBER_REGEX, (match,
|
|
7633
|
+
skeleton.replace(NUMBER_REGEX, (match, offset) => {
|
|
6010
7634
|
const len = match.length;
|
|
6011
7635
|
switch(match[0]) {
|
|
6012
7636
|
case '#':
|
|
@@ -6019,10 +7643,10 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
6019
7643
|
if (options?.minimumSignificantDigits) {
|
|
6020
7644
|
throw "@ symbol should occur together"
|
|
6021
7645
|
}
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
options.maximumSignificantDigits = len;
|
|
7646
|
+
order.push(['@', len]);
|
|
7647
|
+
options.minimumSignificantDigits = len;
|
|
7648
|
+
const hashes = match.match(/#+/) || "";
|
|
7649
|
+
options.maximumSignificantDigits = len + hashes.length;
|
|
6026
7650
|
order.push(['digit', hashes.length]);
|
|
6027
7651
|
break;
|
|
6028
7652
|
case ',':
|
|
@@ -6047,9 +7671,6 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
6047
7671
|
}
|
|
6048
7672
|
if (options?.decimal === true) {
|
|
6049
7673
|
options.minimumFractionDigits = len;
|
|
6050
|
-
if (!options.maximumFractionDigits) {
|
|
6051
|
-
options.maximumFractionDigits = len;
|
|
6052
|
-
}
|
|
6053
7674
|
} else {
|
|
6054
7675
|
options.minimumIntegerDigits = len;
|
|
6055
7676
|
}
|
|
@@ -6073,20 +7694,18 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
6073
7694
|
case '¤':
|
|
6074
7695
|
if (offset !== 0 && offset !== skeleton.length - 1) {
|
|
6075
7696
|
console.error("currency display should be either in the beginning or at the end");
|
|
6076
|
-
} else {
|
|
6077
|
-
options.style = 'currency';
|
|
6078
|
-
options.currencyDisplay = ['symbol', 'code', 'name', 'narrowSymbol'][len - 1];
|
|
6079
|
-
options.currency = currencySymbol || getCurrency(language);
|
|
6080
|
-
order.push(['currency', len]);
|
|
6081
7697
|
}
|
|
7698
|
+
options.style = 'currency';
|
|
7699
|
+
options.currencyDisplay = ['symbol', 'code', 'name', 'narrowSymbol'][len -1];
|
|
7700
|
+
options.currency = getCurrency(language);
|
|
7701
|
+
order.push(['currency', len]);
|
|
6082
7702
|
break;
|
|
6083
7703
|
case '%':
|
|
6084
7704
|
if (offset !== 0 && offset !== skeleton.length - 1) {
|
|
6085
7705
|
console.error("percent display should be either in the beginning or at the end");
|
|
6086
|
-
} else {
|
|
6087
|
-
order.push(['%', 1]);
|
|
6088
|
-
options.style = 'percent';
|
|
6089
7706
|
}
|
|
7707
|
+
order.push(['%', 1]);
|
|
7708
|
+
options.style = 'percent';
|
|
6090
7709
|
break;
|
|
6091
7710
|
case 'E':
|
|
6092
7711
|
order.push(['E', len]);
|
|
@@ -6103,9 +7722,6 @@ function parseNumberSkeleton(skeleton, language) {
|
|
|
6103
7722
|
}
|
|
6104
7723
|
|
|
6105
7724
|
function formatNumber(numberValue, language, skeletn) {
|
|
6106
|
-
if (skeletn.startsWith('num|')) {
|
|
6107
|
-
skeletn = skel.split('|')[1];
|
|
6108
|
-
}
|
|
6109
7725
|
if (!skeletn) return numberValue
|
|
6110
7726
|
language = language || "en";
|
|
6111
7727
|
const {options, order} = parseNumberSkeleton(skeletn, language);
|
|
@@ -6116,6 +7732,7 @@ const getCategory = function (skeleton) {
|
|
|
6116
7732
|
const chkCategory = skeleton?.match(/^(?:(num|date)\|)?(.+)/);
|
|
6117
7733
|
return [chkCategory?.[1], chkCategory?.[2]]
|
|
6118
7734
|
};
|
|
7735
|
+
|
|
6119
7736
|
const format = function (value, locale, skeleton, timezone) {
|
|
6120
7737
|
const [category, skelton] = getCategory(skeleton);
|
|
6121
7738
|
switch (category) {
|
|
@@ -6131,6 +7748,12 @@ const format = function (value, locale, skeleton, timezone) {
|
|
|
6131
7748
|
}
|
|
6132
7749
|
};
|
|
6133
7750
|
|
|
7751
|
+
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
7752
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
7753
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
7754
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
7755
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7756
|
+
};
|
|
6134
7757
|
const validTypes = ['string', 'number', 'boolean', 'file', 'string[]', 'number[]', 'boolean[]', 'file[]', 'array', 'object'];
|
|
6135
7758
|
class Field extends Scriptable {
|
|
6136
7759
|
constructor(params, _options) {
|
|
@@ -6279,10 +7902,10 @@ class Field extends Scriptable {
|
|
|
6279
7902
|
}
|
|
6280
7903
|
}
|
|
6281
7904
|
get editFormat() {
|
|
6282
|
-
return this.
|
|
7905
|
+
return this._jsonModel.editFormat;
|
|
6283
7906
|
}
|
|
6284
7907
|
get displayFormat() {
|
|
6285
|
-
return this.
|
|
7908
|
+
return this._jsonModel.displayFormat;
|
|
6286
7909
|
}
|
|
6287
7910
|
get placeholder() {
|
|
6288
7911
|
return this._jsonModel.placeholder;
|
|
@@ -6355,43 +7978,31 @@ class Field extends Scriptable {
|
|
|
6355
7978
|
return this._jsonModel.value === undefined || this._jsonModel.value === null || this._jsonModel.value === '';
|
|
6356
7979
|
}
|
|
6357
7980
|
withCategory(df) {
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
if (
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
df = `num|${df}`;
|
|
6366
|
-
}
|
|
6367
|
-
return df;
|
|
7981
|
+
const hasCategory = df?.match(/^(?:date|num)\|/);
|
|
7982
|
+
if (hasCategory == null) {
|
|
7983
|
+
if (this.format === 'date') {
|
|
7984
|
+
df = `date|${df}`;
|
|
7985
|
+
}
|
|
7986
|
+
else if (this.type === 'number') {
|
|
7987
|
+
df = `num|${df}`;
|
|
6368
7988
|
}
|
|
7989
|
+
return df;
|
|
6369
7990
|
}
|
|
6370
7991
|
return df;
|
|
6371
7992
|
}
|
|
6372
7993
|
get editValue() {
|
|
6373
|
-
const df = this.editFormat;
|
|
7994
|
+
const df = this.withCategory(this.editFormat);
|
|
6374
7995
|
if (df && this.isNotEmpty(this.value) && this.valid !== false) {
|
|
6375
|
-
|
|
6376
|
-
return format(this.value, this.language, df);
|
|
6377
|
-
}
|
|
6378
|
-
catch (e) {
|
|
6379
|
-
return this.value;
|
|
6380
|
-
}
|
|
7996
|
+
return format(this.value, this.language, df);
|
|
6381
7997
|
}
|
|
6382
7998
|
else {
|
|
6383
7999
|
return this.value;
|
|
6384
8000
|
}
|
|
6385
8001
|
}
|
|
6386
8002
|
get displayValue() {
|
|
6387
|
-
const df = this.displayFormat;
|
|
8003
|
+
const df = this.withCategory(this.displayFormat);
|
|
6388
8004
|
if (df && this.isNotEmpty(this.value) && this.valid !== false) {
|
|
6389
|
-
|
|
6390
|
-
return format(this.value, this.language, df);
|
|
6391
|
-
}
|
|
6392
|
-
catch (e) {
|
|
6393
|
-
return this.value;
|
|
6394
|
-
}
|
|
8005
|
+
return format(this.value, this.language, df);
|
|
6395
8006
|
}
|
|
6396
8007
|
else {
|
|
6397
8008
|
return this.value;
|
|
@@ -6531,7 +8142,7 @@ class Field extends Scriptable {
|
|
|
6531
8142
|
const iv = this._jsonModel.minimum || this._jsonModel.default || 0;
|
|
6532
8143
|
const fIVal = iv * factor;
|
|
6533
8144
|
const qt = (fVal - fIVal) / fStep;
|
|
6534
|
-
const valid =
|
|
8145
|
+
const valid = (fVal - fIVal) % fStep < .001;
|
|
6535
8146
|
let next, prev;
|
|
6536
8147
|
if (!valid) {
|
|
6537
8148
|
next = (Math.ceil(qt) * fStep + fIVal) / factor;
|
|
@@ -6734,8 +8345,6 @@ class Field extends Scriptable {
|
|
|
6734
8345
|
getState() {
|
|
6735
8346
|
return {
|
|
6736
8347
|
...super.getState(),
|
|
6737
|
-
editFormat: this.editFormat,
|
|
6738
|
-
displayFormat: this.displayFormat,
|
|
6739
8348
|
editValue: this.editValue,
|
|
6740
8349
|
displayValue: this.displayValue
|
|
6741
8350
|
};
|