@cocreate/element-prototype 1.29.0 → 1.29.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/docs/index.html +221 -221
- package/package.json +5 -9
- package/src/getAttribute.js +28 -4
- package/src/getValue.js +535 -255
- package/src/index.js +1 -1
- package/src/{utility.js → operators.js} +62 -7
- package/src/queryElements.js +15 -2
- package/src/setValue.js +1 -1
- package/webpack.config.js +65 -90
package/src/getValue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { processOperators } from "./
|
|
1
|
+
import { processOperators } from "./operators";
|
|
2
2
|
|
|
3
3
|
const storage = new Map();
|
|
4
4
|
|
|
@@ -7,188 +7,385 @@ HTMLElement.prototype.getValue = function () {
|
|
|
7
7
|
return value;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
// TODO:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// TODO: return blobs for element.src and for link.href (Not addressed)
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieves and processes the value from an HTML element based on its type and additional attributes.
|
|
14
|
+
*
|
|
15
|
+
* @param {HTMLElement} element - The HTML element to process for its value.
|
|
16
|
+
* @param {string} valueType - Optional. The expected type that defines how the value should be processed.
|
|
17
|
+
* @returns {any} - The processed value based on the element's type and attributes.
|
|
18
|
+
*/
|
|
19
|
+
const getValue = (element, valueType) => {
|
|
20
|
+
// If no valueType is given, attempt to retrieve it from the element's attributes
|
|
21
|
+
if (!valueType) {
|
|
22
|
+
valueType = element.getAttribute("value-type") || "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Initialize value with element's value property or its 'value' attribute, defaulting to an empty string
|
|
14
26
|
let value = element.value || element.getAttribute("value") || "";
|
|
27
|
+
|
|
28
|
+
// Handle specific cases for elements considered components, plugins, or file inputs
|
|
15
29
|
if (
|
|
16
30
|
element.hasAttribute("component") ||
|
|
17
31
|
element.hasAttribute("plugin") ||
|
|
18
32
|
element.type === "file" ||
|
|
19
33
|
element.getAttribute("type") === "file"
|
|
20
34
|
) {
|
|
35
|
+
// Retrieve and delete value from storage for secure data handling
|
|
21
36
|
value = storage.get(element);
|
|
22
37
|
storage.delete(element);
|
|
23
38
|
return value;
|
|
24
39
|
}
|
|
25
40
|
|
|
41
|
+
// Retrieve prefix and suffix from attributes for later use
|
|
26
42
|
let prefix = element.getAttribute("value-prefix") || "";
|
|
27
43
|
let suffix = element.getAttribute("value-suffix") || "";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
];
|
|
110
|
-
value = days[value.getDay()];
|
|
111
|
-
break;
|
|
112
|
-
case "getMonthName":
|
|
113
|
-
const months = [
|
|
114
|
-
"January",
|
|
115
|
-
"February",
|
|
116
|
-
"March",
|
|
117
|
-
"April",
|
|
118
|
-
"May",
|
|
119
|
-
"June",
|
|
120
|
-
"July",
|
|
121
|
-
"August",
|
|
122
|
-
"September",
|
|
123
|
-
"October",
|
|
124
|
-
"November",
|
|
125
|
-
"December"
|
|
126
|
-
];
|
|
127
|
-
value = months[value.getMonth()];
|
|
128
|
-
break;
|
|
129
|
-
case "toUnixTimestamp":
|
|
130
|
-
value = Math.floor(value.getTime() / 1000);
|
|
131
|
-
break;
|
|
132
|
-
case "toLocaleString":
|
|
133
|
-
let locale = element.getAttribute("locale") || "en-US";
|
|
134
|
-
value = value[valueType](locale);
|
|
135
|
-
break;
|
|
136
|
-
default:
|
|
137
|
-
if (typeof value[valueType] === "function") {
|
|
138
|
-
value = value[valueType]();
|
|
139
|
-
} else {
|
|
140
|
-
console.warn(
|
|
141
|
-
`The method ${valueType} is not a function of Date object.`
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
44
|
+
|
|
45
|
+
// Determine elementType using type first, fallback to tagName (both lowercase and uppercase respectively)
|
|
46
|
+
const elementType = element.type ? element.type.toLowerCase() : null;
|
|
47
|
+
const tagName = element.tagName.toUpperCase();
|
|
48
|
+
|
|
49
|
+
// Switch statement to handle different element types and tagNames
|
|
50
|
+
switch (elementType) {
|
|
51
|
+
case "checkbox":
|
|
52
|
+
// Handles multiple checkboxes in a group using a key and applies prefix/suffix if needed
|
|
53
|
+
value = handleCheckbox(element, prefix, suffix);
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case "radio":
|
|
57
|
+
const key = element.getAttribute("key");
|
|
58
|
+
// Handles radio inputs by selecting the checked radio's value in the group with the same key
|
|
59
|
+
value = document.querySelector(`input[key="${key}"]:checked`).value;
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case "number":
|
|
63
|
+
// Converts the value to a number for inputs of type number
|
|
64
|
+
value = Number(value);
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case "range":
|
|
68
|
+
// If a minimum is specified, returns a range array, otherwise a single number
|
|
69
|
+
value = element.min
|
|
70
|
+
? [Number(element.min), Number(element.value)]
|
|
71
|
+
: Number(element.value);
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case "password":
|
|
75
|
+
// Encodes the value in Base64 format for password inputs for secure representation
|
|
76
|
+
value = btoa(value || "");
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case "email":
|
|
80
|
+
// Converts the email to lowercase to maintain a standardized format
|
|
81
|
+
value = value.toLowerCase();
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case "url":
|
|
85
|
+
// TODO: Implement logic to return URL parts instead of the complete URL
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case "time":
|
|
89
|
+
case "date":
|
|
90
|
+
case "datetime":
|
|
91
|
+
case "datetime-local":
|
|
92
|
+
// Processes datetime-related inputs with custom logic
|
|
93
|
+
value = handleDateTime(element, value, valueType);
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
switch (tagName) {
|
|
97
|
+
case "INPUT":
|
|
98
|
+
// For generic input types, use the element's value
|
|
99
|
+
value = element.value;
|
|
100
|
+
break;
|
|
101
|
+
|
|
102
|
+
case "SELECT":
|
|
103
|
+
// Handles multiple selection in select elements, applying prefix/suffix if required
|
|
104
|
+
value = element.hasAttribute("multiple")
|
|
105
|
+
? handleMultipleSelect(element, prefix, suffix)
|
|
106
|
+
: element.value;
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case "TEXTAREA":
|
|
110
|
+
// For textarea elements, preference is given to the 'value' attribute if it exists
|
|
111
|
+
value = element.hasAttribute("value")
|
|
112
|
+
? element.getAttribute("value")
|
|
113
|
+
: element.value;
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case "IFRAME":
|
|
117
|
+
// For iframes, return the document source
|
|
118
|
+
value = element.srcdoc;
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
default:
|
|
122
|
+
// Handles cases not explicitly matched by type or tagName
|
|
123
|
+
value = handleElement(element, valueType);
|
|
124
|
+
break;
|
|
145
125
|
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// If the desired valueType is boolean, convert the value accordingly
|
|
130
|
+
if (valueType === "boolean") {
|
|
131
|
+
return value && value !== "false";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Apply additional processing through a series of transformation functions
|
|
135
|
+
if (value) {
|
|
136
|
+
value = processOperators(element, value, ["$value"]);
|
|
137
|
+
value = caseHandler(element, value);
|
|
138
|
+
value = regex(element, value);
|
|
139
|
+
value = encodeValue(value, element.getAttribute("value-encode") || "");
|
|
140
|
+
value = decodeValue(value, element.getAttribute("value-decode") || "");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Append prefix and suffix to value if applicable, before JSON parsing
|
|
144
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
145
|
+
if (prefix || suffix) {
|
|
146
|
+
value = prefix + value + suffix;
|
|
146
147
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Parse the value as JSON, if possible, and convert to an array if needed
|
|
151
|
+
value = parseJson(value, valueType);
|
|
152
|
+
value = toArray(value, valueType);
|
|
153
|
+
|
|
154
|
+
return value; // Return the final processed value
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Processes a checkbox or a group of checkboxes to determine their checked values.
|
|
159
|
+
* For multiple checkboxes with the same key, their checked values are collected into an array.
|
|
160
|
+
*
|
|
161
|
+
* @param {HTMLInputElement} element - The input element of type checkbox.
|
|
162
|
+
* @param {string} prefix - A string to prepend to each checked value.
|
|
163
|
+
* @param {string} suffix - A string to append to each checked value.
|
|
164
|
+
* @returns {string|boolean|Array} - The value(s) of checked checkbox(es), or `false` if none are checked.
|
|
165
|
+
*/
|
|
166
|
+
const handleCheckbox = (element, prefix = "", suffix = "") => {
|
|
167
|
+
// Retrieve all checkboxes with the same key, else just the single element
|
|
168
|
+
const inputs = element.getAttribute("key")
|
|
169
|
+
? document.querySelectorAll(
|
|
170
|
+
`input[type="${element.type}"][key="${element.getAttribute(
|
|
171
|
+
"key"
|
|
172
|
+
)}"]`
|
|
173
|
+
)
|
|
174
|
+
: [element];
|
|
175
|
+
|
|
176
|
+
if (inputs.length === 1) {
|
|
177
|
+
// If only one checkbox, return its value or true/false depending on its checked state
|
|
178
|
+
return element.checked ? element.value || true : false;
|
|
157
179
|
} else {
|
|
158
|
-
|
|
180
|
+
// For multiple checkboxes, collect their checked values into an array
|
|
181
|
+
return Array.from(inputs)
|
|
182
|
+
.filter((el) => el.checked) // Filter only checked elements
|
|
183
|
+
.map((el) => `${prefix}${el.value}${suffix}`); // Apply prefix/suffix and collect values
|
|
184
|
+
}
|
|
185
|
+
};
|
|
159
186
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Handles and transforms a date/time value based on the specified valueType.
|
|
189
|
+
* Supports operations like converting to ISO string, extracting day/month names, converting to Unix timestamp, and more.
|
|
190
|
+
*
|
|
191
|
+
* @param {HTMLElement} element - The DOM element containing the date/time value.
|
|
192
|
+
* @param {string} value - The initial value which may represent a date/time.
|
|
193
|
+
* @param {string} valueType - Specifies the type of transformation to apply to the date/time value.
|
|
194
|
+
* @returns {any} - The transformed or processed date/time value.
|
|
195
|
+
*/
|
|
196
|
+
const handleDateTime = (element, value, valueType) => {
|
|
197
|
+
// Convert special string '$now' to current date
|
|
198
|
+
if (value === "$now") {
|
|
199
|
+
value = new Date();
|
|
200
|
+
} else if (value) {
|
|
201
|
+
// Initialize a new Date from the string or object
|
|
202
|
+
value = new Date(value);
|
|
203
|
+
}
|
|
164
204
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
205
|
+
// Check if value is a valid date
|
|
206
|
+
if (value instanceof Date && !isNaN(value.getTime())) {
|
|
207
|
+
// Default behavior if no specific valueType provided
|
|
208
|
+
if (!valueType) {
|
|
209
|
+
value = value.toISOString();
|
|
169
210
|
}
|
|
170
211
|
|
|
171
|
-
//
|
|
172
|
-
if (
|
|
173
|
-
value =
|
|
174
|
-
} else if (valueType === "outerHTML") {
|
|
175
|
-
value = targetElement.outerHTML;
|
|
176
|
-
} else if (valueType === "element" || valueType === "node") {
|
|
177
|
-
value = targetElement.outerHTML;
|
|
178
|
-
} else {
|
|
179
|
-
value = targetElement.innerHTML;
|
|
212
|
+
// Format for 'time' type elements
|
|
213
|
+
if (element.type === "time" && !valueType) {
|
|
214
|
+
value = value.substring(11, 19) + "Z";
|
|
180
215
|
}
|
|
216
|
+
|
|
217
|
+
if (valueType) {
|
|
218
|
+
switch (valueType) {
|
|
219
|
+
case "getDayName":
|
|
220
|
+
const days = [
|
|
221
|
+
"Sunday",
|
|
222
|
+
"Monday",
|
|
223
|
+
"Tuesday",
|
|
224
|
+
"Wednesday",
|
|
225
|
+
"Thursday",
|
|
226
|
+
"Friday",
|
|
227
|
+
"Saturday"
|
|
228
|
+
];
|
|
229
|
+
value = days[value.getDay()];
|
|
230
|
+
break;
|
|
231
|
+
case "getMonthName":
|
|
232
|
+
const months = [
|
|
233
|
+
"January",
|
|
234
|
+
"February",
|
|
235
|
+
"March",
|
|
236
|
+
"April",
|
|
237
|
+
"May",
|
|
238
|
+
"June",
|
|
239
|
+
"July",
|
|
240
|
+
"August",
|
|
241
|
+
"September",
|
|
242
|
+
"October",
|
|
243
|
+
"November",
|
|
244
|
+
"December"
|
|
245
|
+
];
|
|
246
|
+
value = months[value.getMonth()];
|
|
247
|
+
break;
|
|
248
|
+
case "toUnixTimestamp":
|
|
249
|
+
value = Math.floor(value.getTime() / 1000);
|
|
250
|
+
break;
|
|
251
|
+
case "toLocaleString":
|
|
252
|
+
let locale = element.getAttribute("locale") || "en-US";
|
|
253
|
+
value = value.toLocaleString(locale);
|
|
254
|
+
break;
|
|
255
|
+
default:
|
|
256
|
+
if (typeof value[valueType] === "function") {
|
|
257
|
+
value = value[valueType]();
|
|
258
|
+
} else {
|
|
259
|
+
console.warn(
|
|
260
|
+
`The method ${valueType} is not a function of Date object.`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
console.warn("Provided date is invalid or could not be parsed:", value);
|
|
181
268
|
}
|
|
269
|
+
return value;
|
|
270
|
+
};
|
|
182
271
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Processes a <select> HTML element with the "multiple" attribute to retrieve an array of selected option values.
|
|
274
|
+
* Each selected value can have a prefix or suffix added to it if specified.
|
|
275
|
+
*
|
|
276
|
+
* @param {HTMLSelectElement} element - The select element with multiple options that are possibly selected.
|
|
277
|
+
* @param {string} prefix - A string to prepend to each selected option value.
|
|
278
|
+
* @param {string} suffix - A string to append to each selected option value.
|
|
279
|
+
* @returns {string[]} - An array of selected option values, each optionally prefixed and suffixed.
|
|
280
|
+
*/
|
|
281
|
+
const handleMultipleSelect = (element, prefix, suffix) => {
|
|
282
|
+
// Initialize an empty array to hold the values of selected options.
|
|
283
|
+
let value = [];
|
|
284
|
+
|
|
285
|
+
// Retrieve all selected options from the select element.
|
|
286
|
+
let options = element.selectedOptions;
|
|
287
|
+
|
|
288
|
+
// Iterate over each selected option.
|
|
289
|
+
for (let i = 0; i < options.length; i++) {
|
|
290
|
+
// Retrieve the value of the current option.
|
|
291
|
+
let optionValue = options[i].value;
|
|
292
|
+
|
|
293
|
+
// If a prefix or suffix is provided, modify the option value accordingly.
|
|
294
|
+
if (prefix || suffix) {
|
|
295
|
+
optionValue = prefix + optionValue + suffix;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Add the processed option value to the array.
|
|
299
|
+
value.push(optionValue);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Return the array of processed option values.
|
|
303
|
+
return value;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Retrieves a specific value from an HTML element based on the specified valueType.
|
|
308
|
+
* If the element has a "value" attribute, it returns its value. Otherwise, it processes
|
|
309
|
+
* the element to extract or format the desired information based on the valueType.
|
|
310
|
+
*
|
|
311
|
+
* @param {HTMLElement} element - The DOM element to process.
|
|
312
|
+
* @param {string} valueType - The type of value to retrieve (e.g., "text", "outerHTML").
|
|
313
|
+
* @returns {string} - The extracted value based on the specified valueType.
|
|
314
|
+
*/
|
|
315
|
+
const handleElement = (element, valueType) => {
|
|
316
|
+
// Check if the element has a "value" attribute; if so, return its value.
|
|
317
|
+
if (element.hasAttribute("value")) {
|
|
318
|
+
return element.getAttribute("value");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Use the original element as the target by default.
|
|
322
|
+
let targetElement = element;
|
|
323
|
+
// Retrieve the "value-remove-query" attribute used to specify selectors that should be removed.
|
|
324
|
+
const excludeSelector = element.getAttribute("value-remove-query");
|
|
325
|
+
|
|
326
|
+
// If there is an excludeSelector, clone the element and remove elements matching the selector.
|
|
327
|
+
if (excludeSelector) {
|
|
328
|
+
// Clone the element to avoid modifying the original DOM.
|
|
329
|
+
targetElement = element.cloneNode(true);
|
|
330
|
+
// Find and remove all elements that match the excludeSelector from the cloned element.
|
|
331
|
+
targetElement
|
|
332
|
+
.querySelectorAll(excludeSelector)
|
|
333
|
+
.forEach((el) => el.remove());
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Determine the value to return based on the valueType.
|
|
337
|
+
switch (valueType) {
|
|
338
|
+
case "text":
|
|
339
|
+
// Return the text content of the target element.
|
|
340
|
+
return targetElement.innerText;
|
|
341
|
+
case "outerHTML":
|
|
342
|
+
case "element":
|
|
343
|
+
case "node":
|
|
344
|
+
// For these cases, return the outer HTML of the target element.
|
|
345
|
+
return targetElement.outerHTML;
|
|
346
|
+
default:
|
|
347
|
+
// By default, return the inner HTML of the target element.
|
|
348
|
+
return targetElement.innerHTML;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Modifies the case of `value` based on specific attributes in `element`.
|
|
354
|
+
* Converts to lowercase or uppercase if attributes are present and not "false".
|
|
355
|
+
*
|
|
356
|
+
* @param {HTMLElement} element - The DOM element to check for attributes.
|
|
357
|
+
* @param {string} value - The value to adjust based on the attributes.
|
|
358
|
+
* @returns {string} - The adjusted value.
|
|
359
|
+
*/
|
|
360
|
+
function caseHandler(element, value) {
|
|
361
|
+
// Convert to lowercase if "value-lowercase" is set and not "false"
|
|
362
|
+
const lowercase = element.getAttribute("value-lowercase");
|
|
363
|
+
if (lowercase !== null && lowercase !== "false") {
|
|
364
|
+
value = value.toLowerCase();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Convert to uppercase if "value-uppercase" is set and not "false"
|
|
368
|
+
const uppercase = element.getAttribute("value-uppercase");
|
|
369
|
+
if (uppercase !== null && uppercase !== "false") {
|
|
370
|
+
value = value.toUpperCase();
|
|
186
371
|
}
|
|
187
372
|
|
|
188
|
-
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
189
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Processes the value by applying regex-based transformations specified by certain attributes on an element.
|
|
378
|
+
* Attributes dictate regex operations such as replace, match, split, etc.
|
|
379
|
+
*
|
|
380
|
+
* @param {HTMLElement} element - The DOM element whose attributes specify regex operations.
|
|
381
|
+
* @param {string} value - The value to be transformed based on regex operations.
|
|
382
|
+
* @returns {string} - The transformed value, or the original value if an error occurs.
|
|
383
|
+
*/
|
|
384
|
+
function regex(element, value) {
|
|
190
385
|
try {
|
|
191
|
-
const attributes = element.attributes; //
|
|
386
|
+
const attributes = element.attributes; // Retrieve all attributes of the element
|
|
387
|
+
|
|
388
|
+
// Define a list of attributes that specify regex operations
|
|
192
389
|
const regexAttribute = [
|
|
193
390
|
"value-replace",
|
|
194
391
|
"value-replaceall",
|
|
@@ -199,195 +396,194 @@ const getValue = (element) => {
|
|
|
199
396
|
"value-search",
|
|
200
397
|
"value-exec"
|
|
201
398
|
];
|
|
202
|
-
|
|
399
|
+
|
|
400
|
+
// Iterate over element attributes and process ones related to regex operations
|
|
203
401
|
for (let i = 0; i < attributes.length; i++) {
|
|
402
|
+
// Stop processing if value is null or undefined
|
|
204
403
|
if (value === null || value === undefined) break;
|
|
205
404
|
|
|
405
|
+
// Skip attributes that do not relate to regex operations
|
|
206
406
|
if (!regexAttribute.includes(attributes[i].name)) continue;
|
|
207
407
|
|
|
208
|
-
let regexAttributeValue = attributes[i].value;
|
|
408
|
+
let regexAttributeValue = attributes[i].value; // The attribute value containing regex pattern
|
|
209
409
|
|
|
410
|
+
// Skip processing for empty regex attributes
|
|
210
411
|
if (!regexAttributeValue) continue;
|
|
211
412
|
|
|
413
|
+
// Parse the regex pattern and replacement from the attribute value
|
|
212
414
|
let { regex, replacement } = regexParser(regexAttributeValue);
|
|
213
415
|
|
|
416
|
+
// Use parsed regex if available
|
|
214
417
|
if (regex) regexAttributeValue = regex;
|
|
215
418
|
|
|
419
|
+
// Default to an empty string for replacement if not specified
|
|
216
420
|
replacement =
|
|
217
421
|
replacement || element.getAttribute("value-replacement") || "";
|
|
218
422
|
|
|
423
|
+
// Execute the determined regex operation
|
|
219
424
|
switch (attributes[i].name) {
|
|
220
425
|
case "value-replace":
|
|
221
426
|
value = value.replace(regexAttributeValue, replacement);
|
|
222
427
|
break;
|
|
223
|
-
|
|
224
428
|
case "value-replaceall":
|
|
225
429
|
value = value.replaceAll(regexAttributeValue, replacement);
|
|
226
430
|
break;
|
|
227
|
-
|
|
228
431
|
case "value-test":
|
|
229
432
|
value = regex.test(value);
|
|
230
433
|
break;
|
|
231
|
-
|
|
232
434
|
case "value-match":
|
|
233
435
|
const matches = value.match(regexAttributeValue);
|
|
234
436
|
if (matches) {
|
|
235
|
-
value = matches[1] || matches[0]; //
|
|
437
|
+
value = matches[1] || matches[0]; // Use capturing group if available
|
|
236
438
|
}
|
|
237
439
|
break;
|
|
238
|
-
|
|
239
440
|
case "value-split":
|
|
240
441
|
value = value.split(regexAttributeValue);
|
|
241
442
|
break;
|
|
242
|
-
|
|
243
443
|
case "value-lastindex":
|
|
244
|
-
regex.lastIndex = 0; // Ensure starting index is
|
|
444
|
+
regex.lastIndex = 0; // Ensure the starting index is reset
|
|
245
445
|
regex.test(value);
|
|
246
446
|
value = regex.lastIndex;
|
|
247
447
|
break;
|
|
248
|
-
|
|
249
448
|
case "value-search":
|
|
250
449
|
value = value.search(regexAttributeValue);
|
|
251
450
|
break;
|
|
252
|
-
|
|
253
451
|
case "value-exec":
|
|
254
452
|
const execResult = regex.exec(value);
|
|
255
453
|
if (execResult) {
|
|
256
|
-
value = execResult[1] || execResult[2] || execResult[0]; // Prioritize capturing
|
|
257
|
-
} else {
|
|
258
|
-
// value = null;
|
|
454
|
+
value = execResult[1] || execResult[2] || execResult[0]; // Prioritize capturing groups
|
|
259
455
|
}
|
|
260
456
|
break;
|
|
261
|
-
|
|
262
457
|
default:
|
|
263
|
-
//
|
|
458
|
+
// Unknown attribute, ignored
|
|
264
459
|
break;
|
|
265
460
|
}
|
|
266
461
|
}
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error("getValue() error:", error, element);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// TODO: encode and decode needs a method to prevent multiple encode of an already encoded value
|
|
272
|
-
let encode = element.getAttribute("value-encode");
|
|
273
|
-
if (encode) value = encodeValue(value, encode);
|
|
274
|
-
|
|
275
|
-
let decode = element.getAttribute("value-decode");
|
|
276
|
-
if (decode) value = decodeValue(value, decode);
|
|
277
|
-
|
|
278
|
-
let lowercase = element.getAttribute("value-lowercase");
|
|
279
|
-
if (lowercase || lowercase === "") value = value.toLowerCase();
|
|
280
|
-
let uppercase = element.getAttribute("value-uppercase");
|
|
281
|
-
if (uppercase || uppercase === "") value = value.toUpperCase();
|
|
282
|
-
|
|
283
|
-
// Apply prefix and suffix first, before JSON parsing
|
|
284
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
285
|
-
if (prefix || suffix) {
|
|
286
|
-
value = prefix + value + suffix;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Handle JSON parsing for objects, arrays, or when valueType starts with 'array'
|
|
291
|
-
if (
|
|
292
|
-
value &&
|
|
293
|
-
(valueType === "object" ||
|
|
294
|
-
valueType === "json" ||
|
|
295
|
-
valueType.startsWith("array"))
|
|
296
|
-
) {
|
|
297
|
-
try {
|
|
298
|
-
value = JSON.parse(value);
|
|
299
|
-
} catch (error) {
|
|
300
|
-
const jsonRegex = /(\{[\s\S]*\}|\[[\s\S]*\])/;
|
|
301
|
-
const match = value.match(jsonRegex);
|
|
302
|
-
|
|
303
|
-
if (match) {
|
|
304
|
-
try {
|
|
305
|
-
value = JSON.parse(match[0]);
|
|
306
|
-
} catch (e) {
|
|
307
|
-
console.error(
|
|
308
|
-
"Failed to parse JSON after regex extraction:",
|
|
309
|
-
e
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
} else {
|
|
313
|
-
console.error("No valid JSON structure found in the string.");
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
462
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
value = Object.entries(value);
|
|
329
|
-
} else {
|
|
330
|
-
// Default behavior: wrap the object in an array
|
|
331
|
-
value = [value];
|
|
332
|
-
}
|
|
333
|
-
} else {
|
|
334
|
-
// If it's not an object (i.e., a primitive), wrap the value in an array
|
|
335
|
-
value = [value];
|
|
336
|
-
}
|
|
337
|
-
}
|
|
463
|
+
// Return the transformed value
|
|
464
|
+
return value;
|
|
465
|
+
} catch (error) {
|
|
466
|
+
// Log a warning since the transformation error is non-critical, but should be noted
|
|
467
|
+
console.warn(
|
|
468
|
+
"Warning: Transformation error during regex operation:",
|
|
469
|
+
error,
|
|
470
|
+
element
|
|
471
|
+
);
|
|
472
|
+
return value; // Return the original value to maintain application flow
|
|
338
473
|
}
|
|
474
|
+
}
|
|
339
475
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Parses a string to extract a regular expression and a replacement string.
|
|
478
|
+
* Assumes the input string may contain a regex pattern in the form "/pattern/flags"
|
|
479
|
+
* and an optional replacement string separated by a comma.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} string - The input string potentially containing a regex pattern and a replacement.
|
|
482
|
+
* @returns {Object} An object containing the `regex` (RegExp object) and `replacement` (string).
|
|
483
|
+
*/
|
|
343
484
|
function regexParser(string) {
|
|
344
485
|
let regex, replacement;
|
|
345
|
-
// Match a regex pattern enclosed by delimiters or a bare regex string
|
|
346
|
-
// let regexMatch = string.match(/^\/(.+)\/([gimuy]*)$/) || [null, string, ""];
|
|
347
486
|
|
|
487
|
+
// Match a regex pattern defined as /pattern/flags within the input string.
|
|
488
|
+
// It captures the pattern and the flags (e.g., 'g' for global, 'i' for case-insensitive).
|
|
348
489
|
let regexMatch = string.match(/\/(.+)\/([gimuy]*)/);
|
|
490
|
+
|
|
491
|
+
// If a regex pattern is found in the string, proceed to create the RegExp object
|
|
349
492
|
if (regexMatch) {
|
|
493
|
+
// Create a new RegExp object using the captured pattern and flags
|
|
350
494
|
regex = new RegExp(regexMatch[1], regexMatch[2]);
|
|
495
|
+
|
|
496
|
+
// Split the input string on ", " to separate the pattern from a replacement string
|
|
351
497
|
const splitReplace = string.split(", ");
|
|
498
|
+
|
|
499
|
+
// Check if there's a replacement string provided. If so, trim the surrounding spaces.
|
|
352
500
|
replacement =
|
|
353
501
|
splitReplace.length > 1 ? splitReplace[1].slice(1, -1) : "";
|
|
354
502
|
}
|
|
355
503
|
|
|
504
|
+
// Return an object containing the parsed regex and replacement string
|
|
356
505
|
return { regex, replacement };
|
|
357
506
|
}
|
|
358
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Encodes the given value using the specified encoding type.
|
|
510
|
+
*
|
|
511
|
+
* @param {string} value - The value to be encoded.
|
|
512
|
+
* @param {string} encodingType - The type of encoding to apply. Accepted values include "url", "uri-component", "base64", "html-entities", and "json".
|
|
513
|
+
* @returns {string} - The encoded value or the original value if encoding type is unsupported.
|
|
514
|
+
*/
|
|
359
515
|
function encodeValue(value, encodingType) {
|
|
516
|
+
if (!encodingType) {
|
|
517
|
+
return value;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Determine the encoding method based on the case-insensitive encodingType
|
|
360
521
|
switch (encodingType.toLowerCase()) {
|
|
361
522
|
case "url":
|
|
362
523
|
case "uri":
|
|
524
|
+
// Encode spaces as "%20" explicitly and use encodeURI for overall URL encoding (excluding special characters like '?', '&', '/')
|
|
363
525
|
return encodeURI(value.replace(/ /g, "%20"));
|
|
526
|
+
|
|
364
527
|
case "uri-component":
|
|
528
|
+
// Encode spaces as "%20" explicitly and use encodeURIComponent for more thorough encoding, including special characters
|
|
365
529
|
return encodeURIComponent(value.replace(/ /g, "%20"));
|
|
530
|
+
|
|
366
531
|
case "base64":
|
|
367
532
|
case "atob":
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
533
|
+
try {
|
|
534
|
+
// Create a TextEncoder to convert the value into a Uint8Array, then encode it in base64
|
|
535
|
+
const encoder = new TextEncoder();
|
|
536
|
+
const uint8Array = encoder.encode(value);
|
|
537
|
+
return btoa(String.fromCharCode(...uint8Array));
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.warn(`Failed to encode as Base64: ${error.message}`);
|
|
540
|
+
return value; // Return the original value as a fallback
|
|
541
|
+
}
|
|
542
|
+
|
|
371
543
|
case "html-entities":
|
|
544
|
+
// Replace specific HTML entities with their corresponding character codes using regex
|
|
372
545
|
return value.replace(/[\u00A0-\u9999<>\&]/g, (i) => {
|
|
373
546
|
return `&#${i.charCodeAt(0)};`;
|
|
374
547
|
});
|
|
548
|
+
|
|
375
549
|
case "json":
|
|
376
|
-
|
|
550
|
+
try {
|
|
551
|
+
// Convert the value to a JSON string representation
|
|
552
|
+
return JSON.stringify(value);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
console.warn(`Failed to convert to JSON: ${error.message}`);
|
|
555
|
+
return value; // Return the original value as a fallback
|
|
556
|
+
}
|
|
557
|
+
|
|
377
558
|
default:
|
|
378
|
-
|
|
559
|
+
// Log a warning for unsupported encoding types and return the original value
|
|
560
|
+
console.warn(`Unsupported encoding type: ${encodingType}`);
|
|
561
|
+
return value;
|
|
379
562
|
}
|
|
380
563
|
}
|
|
381
564
|
|
|
565
|
+
/**
|
|
566
|
+
* Decodes the given value using the specified decoding type.
|
|
567
|
+
*
|
|
568
|
+
* @param {string} value - The value to be decoded.
|
|
569
|
+
* @param {string} decodingType - The type of decoding to apply. Accepted values include "url", "uri-component", "base64", "html-entities", and "json".
|
|
570
|
+
* @returns {string} - The decoded value or the original value if decoding failed.
|
|
571
|
+
*/
|
|
382
572
|
function decodeValue(value, decodingType) {
|
|
573
|
+
if (!decodingType) {
|
|
574
|
+
return value;
|
|
575
|
+
}
|
|
576
|
+
|
|
383
577
|
switch (decodingType.toLowerCase()) {
|
|
384
578
|
case "url":
|
|
385
579
|
case "uri":
|
|
386
580
|
return decodeURI(value);
|
|
581
|
+
|
|
387
582
|
case "uri-component":
|
|
388
583
|
return decodeURIComponent(value);
|
|
584
|
+
|
|
389
585
|
case "base64":
|
|
390
|
-
case "btoa": //
|
|
586
|
+
case "btoa": // Alias for Base64 decoding
|
|
391
587
|
try {
|
|
392
588
|
const decodedArray = Uint8Array.from(atob(value), (c) =>
|
|
393
589
|
c.charCodeAt(0)
|
|
@@ -395,21 +591,105 @@ function decodeValue(value, decodingType) {
|
|
|
395
591
|
const decoder = new TextDecoder();
|
|
396
592
|
return decoder.decode(decodedArray);
|
|
397
593
|
} catch (error) {
|
|
398
|
-
|
|
594
|
+
console.warn(`Failed to decode Base64: ${error.message}`);
|
|
595
|
+
return value; // Return the original value as a fallback
|
|
399
596
|
}
|
|
597
|
+
|
|
400
598
|
case "html-entities":
|
|
401
599
|
const tempElement = document.createElement("div");
|
|
402
600
|
tempElement.innerHTML = value;
|
|
403
601
|
return tempElement.textContent;
|
|
602
|
+
|
|
404
603
|
case "json":
|
|
405
604
|
try {
|
|
406
605
|
return JSON.parse(value);
|
|
407
606
|
} catch (error) {
|
|
408
|
-
|
|
607
|
+
console.warn(`Failed to parse JSON: ${error.message}`);
|
|
608
|
+
return value; // Return the original value as a fallback
|
|
409
609
|
}
|
|
610
|
+
|
|
410
611
|
default:
|
|
411
|
-
|
|
612
|
+
console.warn(`Unsupported decoding type: ${decodingType}`);
|
|
613
|
+
return value; // Return the original value as a fallback
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Parses a string into a JSON object if the provided valueType suggests it might be JSON.
|
|
619
|
+
* Handles objects, arrays, or types starting with 'array'. If parsing fails,
|
|
620
|
+
* attempts to extract a JSON structure using regex and tries again.
|
|
621
|
+
*
|
|
622
|
+
* @param {string} value - The string value that might contain JSON data.
|
|
623
|
+
* @param {string} valueType - A string indicating the expected type of the value (e.g., "object", "json", "array").
|
|
624
|
+
* @returns {any} The parsed JSON object/array or the original value if parsing fails.
|
|
625
|
+
*/
|
|
626
|
+
function parseJson(value, valueType) {
|
|
627
|
+
// Check if the value is present and if the valueType suggests it could be a JSON structure
|
|
628
|
+
if (
|
|
629
|
+
value && // Ensure there is a value to parse
|
|
630
|
+
(valueType === "object" || // Check if the type is explicitly an object
|
|
631
|
+
valueType === "json" || // Check if the type is explicitly JSON
|
|
632
|
+
valueType.startsWith("array")) // Check if the type indicates an array or complex array structure
|
|
633
|
+
) {
|
|
634
|
+
try {
|
|
635
|
+
// Attempt to parse the value directly as JSON
|
|
636
|
+
value = JSON.parse(value);
|
|
637
|
+
} catch {
|
|
638
|
+
// If direct JSON parsing fails, attempt to extract a potential JSON structure using a regex
|
|
639
|
+
const jsonRegex = /(\{[\s\S]*}|\[[\s\S]*\])/; // Regex to find JSON objects or arrays
|
|
640
|
+
const match = value.match(jsonRegex); // Search the value for JSON-like patterns
|
|
641
|
+
|
|
642
|
+
if (match) {
|
|
643
|
+
try {
|
|
644
|
+
// If a pattern is found, attempt to parse the extracted potential JSON
|
|
645
|
+
value = JSON.parse(match[0]);
|
|
646
|
+
} catch {
|
|
647
|
+
// Warn if parsing still fails after extracting potential JSON
|
|
648
|
+
console.warn(
|
|
649
|
+
"Warning: Failed to parse JSON after extraction. Returning original value."
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
// Warn if no valid JSON structure is found in the string
|
|
654
|
+
console.warn(
|
|
655
|
+
"Warning: No valid JSON structure found in the string. Returning original value."
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
412
659
|
}
|
|
660
|
+
return value; // Return the transformed value, or the original if no transformation occurs
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Processes a value by converting it to an array or extracting specific array-related elements.
|
|
665
|
+
* @param {any} value - The value to be processed, potentially an object or primitive.
|
|
666
|
+
* @param {string} valueType - A string indicating the type of conversion or extraction to perform.
|
|
667
|
+
* @returns {Array} - The processed value as an array, or a transformed array-like structure.
|
|
668
|
+
*/
|
|
669
|
+
function toArray(value, valueType) {
|
|
670
|
+
// Now handle array-specific logic if valueType starts with 'array'
|
|
671
|
+
if (valueType.startsWith("array")) {
|
|
672
|
+
// If the value isn't already an array, convert it accordingly
|
|
673
|
+
if (!Array.isArray(value)) {
|
|
674
|
+
// If the parsed value is an object, apply array conversion based on operators
|
|
675
|
+
if (typeof value === "object") {
|
|
676
|
+
if (valueType === "array.$keys") {
|
|
677
|
+
value = Object.keys(value); // Extracts keys
|
|
678
|
+
} else if (valueType === "array.$values") {
|
|
679
|
+
value = Object.values(value); // Extracts values
|
|
680
|
+
} else if (valueType === "array.$entries") {
|
|
681
|
+
value = Object.entries(value); // Extracts entries as [key, value]
|
|
682
|
+
} else {
|
|
683
|
+
// Default behavior: wrap the object in an array
|
|
684
|
+
value = [value];
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
// If it's not an object (i.e., a primitive), wrap the value in an array
|
|
688
|
+
value = [value];
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return value;
|
|
413
693
|
}
|
|
414
694
|
|
|
415
695
|
export { getValue, storage };
|