@cocreate/element-prototype 1.28.1 → 1.29.1
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 +38 -0
- package/docs/index.html +221 -221
- package/package.json +7 -9
- package/src/getAttribute.js +27 -32
- package/src/getValue.js +488 -212
- package/src/index.js +12 -4
- package/src/operators.js +255 -0
- package/src/queryElements.js +18 -0
- package/src/setValue.js +20 -3
- package/webpack.config.js +65 -90
- package/src/utility.js +0 -80
package/src/getValue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { processOperators } from "./operators";
|
|
2
2
|
|
|
3
3
|
const storage = new Map();
|
|
4
4
|
|
|
@@ -7,94 +7,214 @@ 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
|
-
|
|
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;
|
|
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;
|
|
82
147
|
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
if (value === "$now") value = new Date();
|
|
89
|
-
else if (value) value = new Date(value);
|
|
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);
|
|
90
153
|
|
|
91
|
-
|
|
92
|
-
|
|
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;
|
|
179
|
+
} else {
|
|
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
|
+
};
|
|
186
|
+
|
|
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
|
+
}
|
|
204
|
+
|
|
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();
|
|
210
|
+
}
|
|
93
211
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
212
|
+
// Format for 'time' type elements
|
|
213
|
+
if (element.type === "time" && !valueType) {
|
|
214
|
+
value = value.substring(11, 19) + "Z";
|
|
215
|
+
}
|
|
97
216
|
|
|
217
|
+
if (valueType) {
|
|
98
218
|
switch (valueType) {
|
|
99
219
|
case "getDayName":
|
|
100
220
|
const days = [
|
|
@@ -130,7 +250,7 @@ const getValue = (element) => {
|
|
|
130
250
|
break;
|
|
131
251
|
case "toLocaleString":
|
|
132
252
|
let locale = element.getAttribute("locale") || "en-US";
|
|
133
|
-
value = value
|
|
253
|
+
value = value.toLocaleString(locale);
|
|
134
254
|
break;
|
|
135
255
|
default:
|
|
136
256
|
if (typeof value[valueType] === "function") {
|
|
@@ -140,59 +260,132 @@ const getValue = (element) => {
|
|
|
140
260
|
`The method ${valueType} is not a function of Date object.`
|
|
141
261
|
);
|
|
142
262
|
}
|
|
263
|
+
break;
|
|
143
264
|
}
|
|
144
265
|
}
|
|
145
|
-
} else if (element.tagName == "INPUT" || element.tagName == "SELECT") {
|
|
146
|
-
value = element.value;
|
|
147
|
-
} else if (element.tagName == "TEXTAREA") {
|
|
148
|
-
if (element.hasAttribute("value"))
|
|
149
|
-
value = element.getAttribute("value");
|
|
150
|
-
else value = element.value;
|
|
151
|
-
} else if (element.tagName === "IFRAME") {
|
|
152
|
-
value = element.srcdoc;
|
|
153
|
-
} else if (element.hasAttribute("value")) {
|
|
154
|
-
value = element.getAttribute("value");
|
|
155
266
|
} else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (excludeSelector) {
|
|
161
|
-
targetElement = element.cloneNode(true);
|
|
267
|
+
console.warn("Provided date is invalid or could not be parsed:", value);
|
|
268
|
+
}
|
|
269
|
+
return value;
|
|
270
|
+
};
|
|
162
271
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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;
|
|
167
296
|
}
|
|
168
297
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
value = targetElement.innerText;
|
|
172
|
-
} else if (valueType === "outerHTML") {
|
|
173
|
-
value = targetElement.outerHTML;
|
|
174
|
-
} else {
|
|
175
|
-
value = targetElement.innerHTML;
|
|
176
|
-
}
|
|
298
|
+
// Add the processed option value to the array.
|
|
299
|
+
value.push(optionValue);
|
|
177
300
|
}
|
|
178
301
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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();
|
|
182
365
|
}
|
|
183
366
|
|
|
184
|
-
if
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
else if (value === "$session_id")
|
|
189
|
-
value = localStorage.getItem("session_id");
|
|
190
|
-
else if (typeof value === "string" && value.includes("$")) {
|
|
191
|
-
value = utility.urlOperators(value);
|
|
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();
|
|
192
371
|
}
|
|
193
372
|
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
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) {
|
|
194
385
|
try {
|
|
195
|
-
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
|
|
196
389
|
const regexAttribute = [
|
|
197
390
|
"value-replace",
|
|
198
391
|
"value-replaceall",
|
|
@@ -203,195 +396,194 @@ const getValue = (element) => {
|
|
|
203
396
|
"value-search",
|
|
204
397
|
"value-exec"
|
|
205
398
|
];
|
|
206
|
-
|
|
399
|
+
|
|
400
|
+
// Iterate over element attributes and process ones related to regex operations
|
|
207
401
|
for (let i = 0; i < attributes.length; i++) {
|
|
402
|
+
// Stop processing if value is null or undefined
|
|
208
403
|
if (value === null || value === undefined) break;
|
|
209
404
|
|
|
405
|
+
// Skip attributes that do not relate to regex operations
|
|
210
406
|
if (!regexAttribute.includes(attributes[i].name)) continue;
|
|
211
407
|
|
|
212
|
-
let regexAttributeValue = attributes[i].value;
|
|
408
|
+
let regexAttributeValue = attributes[i].value; // The attribute value containing regex pattern
|
|
213
409
|
|
|
410
|
+
// Skip processing for empty regex attributes
|
|
214
411
|
if (!regexAttributeValue) continue;
|
|
215
412
|
|
|
413
|
+
// Parse the regex pattern and replacement from the attribute value
|
|
216
414
|
let { regex, replacement } = regexParser(regexAttributeValue);
|
|
217
415
|
|
|
416
|
+
// Use parsed regex if available
|
|
218
417
|
if (regex) regexAttributeValue = regex;
|
|
219
418
|
|
|
419
|
+
// Default to an empty string for replacement if not specified
|
|
220
420
|
replacement =
|
|
221
421
|
replacement || element.getAttribute("value-replacement") || "";
|
|
222
422
|
|
|
423
|
+
// Execute the determined regex operation
|
|
223
424
|
switch (attributes[i].name) {
|
|
224
425
|
case "value-replace":
|
|
225
426
|
value = value.replace(regexAttributeValue, replacement);
|
|
226
427
|
break;
|
|
227
|
-
|
|
228
428
|
case "value-replaceall":
|
|
229
429
|
value = value.replaceAll(regexAttributeValue, replacement);
|
|
230
430
|
break;
|
|
231
|
-
|
|
232
431
|
case "value-test":
|
|
233
432
|
value = regex.test(value);
|
|
234
433
|
break;
|
|
235
|
-
|
|
236
434
|
case "value-match":
|
|
237
435
|
const matches = value.match(regexAttributeValue);
|
|
238
436
|
if (matches) {
|
|
239
|
-
value = matches[1] || matches[0]; //
|
|
437
|
+
value = matches[1] || matches[0]; // Use capturing group if available
|
|
240
438
|
}
|
|
241
439
|
break;
|
|
242
|
-
|
|
243
440
|
case "value-split":
|
|
244
441
|
value = value.split(regexAttributeValue);
|
|
245
442
|
break;
|
|
246
|
-
|
|
247
443
|
case "value-lastindex":
|
|
248
|
-
regex.lastIndex = 0; // Ensure starting index is
|
|
444
|
+
regex.lastIndex = 0; // Ensure the starting index is reset
|
|
249
445
|
regex.test(value);
|
|
250
446
|
value = regex.lastIndex;
|
|
251
447
|
break;
|
|
252
|
-
|
|
253
448
|
case "value-search":
|
|
254
449
|
value = value.search(regexAttributeValue);
|
|
255
450
|
break;
|
|
256
|
-
|
|
257
451
|
case "value-exec":
|
|
258
452
|
const execResult = regex.exec(value);
|
|
259
453
|
if (execResult) {
|
|
260
|
-
value = execResult[1] || execResult[2] || execResult[0]; // Prioritize capturing
|
|
261
|
-
} else {
|
|
262
|
-
// value = null;
|
|
454
|
+
value = execResult[1] || execResult[2] || execResult[0]; // Prioritize capturing groups
|
|
263
455
|
}
|
|
264
456
|
break;
|
|
265
|
-
|
|
266
457
|
default:
|
|
267
|
-
//
|
|
458
|
+
// Unknown attribute, ignored
|
|
268
459
|
break;
|
|
269
460
|
}
|
|
270
461
|
}
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.error("getValue() error:", error, element);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// TODO: encode and decode needs a method to prevent multiple encode of an already encoded value
|
|
276
|
-
let encode = element.getAttribute("value-encode");
|
|
277
|
-
if (encode) value = encodeValue(value, encode);
|
|
278
|
-
|
|
279
|
-
let decode = element.getAttribute("value-decode");
|
|
280
|
-
if (decode) value = decodeValue(value, decode);
|
|
281
|
-
|
|
282
|
-
let lowercase = element.getAttribute("value-lowercase");
|
|
283
|
-
if (lowercase || lowercase === "") value = value.toLowerCase();
|
|
284
|
-
let uppercase = element.getAttribute("value-uppercase");
|
|
285
|
-
if (uppercase || uppercase === "") value = value.toUpperCase();
|
|
286
|
-
|
|
287
|
-
// Apply prefix and suffix first, before JSON parsing
|
|
288
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
289
|
-
if (prefix || suffix) {
|
|
290
|
-
value = prefix + value + suffix;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Handle JSON parsing for objects, arrays, or when valueType starts with 'array'
|
|
295
|
-
if (
|
|
296
|
-
value &&
|
|
297
|
-
(valueType === "object" ||
|
|
298
|
-
valueType === "json" ||
|
|
299
|
-
valueType.startsWith("array"))
|
|
300
|
-
) {
|
|
301
|
-
try {
|
|
302
|
-
value = JSON.parse(value);
|
|
303
|
-
} catch (error) {
|
|
304
|
-
const jsonRegex = /(\{[\s\S]*\}|\[[\s\S]*\])/;
|
|
305
|
-
const match = value.match(jsonRegex);
|
|
306
|
-
|
|
307
|
-
if (match) {
|
|
308
|
-
try {
|
|
309
|
-
value = JSON.parse(match[0]);
|
|
310
|
-
} catch (e) {
|
|
311
|
-
console.error(
|
|
312
|
-
"Failed to parse JSON after regex extraction:",
|
|
313
|
-
e
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
console.error("No valid JSON structure found in the string.");
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
462
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
value = Object.entries(value);
|
|
333
|
-
} else {
|
|
334
|
-
// Default behavior: wrap the object in an array
|
|
335
|
-
value = [value];
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
// If it's not an object (i.e., a primitive), wrap the value in an array
|
|
339
|
-
value = [value];
|
|
340
|
-
}
|
|
341
|
-
}
|
|
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
|
|
342
473
|
}
|
|
474
|
+
}
|
|
343
475
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
+
*/
|
|
347
484
|
function regexParser(string) {
|
|
348
485
|
let regex, replacement;
|
|
349
|
-
// Match a regex pattern enclosed by delimiters or a bare regex string
|
|
350
|
-
// let regexMatch = string.match(/^\/(.+)\/([gimuy]*)$/) || [null, string, ""];
|
|
351
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).
|
|
352
489
|
let regexMatch = string.match(/\/(.+)\/([gimuy]*)/);
|
|
490
|
+
|
|
491
|
+
// If a regex pattern is found in the string, proceed to create the RegExp object
|
|
353
492
|
if (regexMatch) {
|
|
493
|
+
// Create a new RegExp object using the captured pattern and flags
|
|
354
494
|
regex = new RegExp(regexMatch[1], regexMatch[2]);
|
|
495
|
+
|
|
496
|
+
// Split the input string on ", " to separate the pattern from a replacement string
|
|
355
497
|
const splitReplace = string.split(", ");
|
|
498
|
+
|
|
499
|
+
// Check if there's a replacement string provided. If so, trim the surrounding spaces.
|
|
356
500
|
replacement =
|
|
357
501
|
splitReplace.length > 1 ? splitReplace[1].slice(1, -1) : "";
|
|
358
502
|
}
|
|
359
503
|
|
|
504
|
+
// Return an object containing the parsed regex and replacement string
|
|
360
505
|
return { regex, replacement };
|
|
361
506
|
}
|
|
362
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
|
+
*/
|
|
363
515
|
function encodeValue(value, encodingType) {
|
|
516
|
+
if (!encodingType) {
|
|
517
|
+
return value;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Determine the encoding method based on the case-insensitive encodingType
|
|
364
521
|
switch (encodingType.toLowerCase()) {
|
|
365
522
|
case "url":
|
|
366
523
|
case "uri":
|
|
524
|
+
// Encode spaces as "%20" explicitly and use encodeURI for overall URL encoding (excluding special characters like '?', '&', '/')
|
|
367
525
|
return encodeURI(value.replace(/ /g, "%20"));
|
|
526
|
+
|
|
368
527
|
case "uri-component":
|
|
528
|
+
// Encode spaces as "%20" explicitly and use encodeURIComponent for more thorough encoding, including special characters
|
|
369
529
|
return encodeURIComponent(value.replace(/ /g, "%20"));
|
|
530
|
+
|
|
370
531
|
case "base64":
|
|
371
532
|
case "atob":
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
+
|
|
375
543
|
case "html-entities":
|
|
544
|
+
// Replace specific HTML entities with their corresponding character codes using regex
|
|
376
545
|
return value.replace(/[\u00A0-\u9999<>\&]/g, (i) => {
|
|
377
546
|
return `&#${i.charCodeAt(0)};`;
|
|
378
547
|
});
|
|
548
|
+
|
|
379
549
|
case "json":
|
|
380
|
-
|
|
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
|
+
|
|
381
558
|
default:
|
|
382
|
-
|
|
559
|
+
// Log a warning for unsupported encoding types and return the original value
|
|
560
|
+
console.warn(`Unsupported encoding type: ${encodingType}`);
|
|
561
|
+
return value;
|
|
383
562
|
}
|
|
384
563
|
}
|
|
385
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
|
+
*/
|
|
386
572
|
function decodeValue(value, decodingType) {
|
|
573
|
+
if (!decodingType) {
|
|
574
|
+
return value;
|
|
575
|
+
}
|
|
576
|
+
|
|
387
577
|
switch (decodingType.toLowerCase()) {
|
|
388
578
|
case "url":
|
|
389
579
|
case "uri":
|
|
390
580
|
return decodeURI(value);
|
|
581
|
+
|
|
391
582
|
case "uri-component":
|
|
392
583
|
return decodeURIComponent(value);
|
|
584
|
+
|
|
393
585
|
case "base64":
|
|
394
|
-
case "btoa": //
|
|
586
|
+
case "btoa": // Alias for Base64 decoding
|
|
395
587
|
try {
|
|
396
588
|
const decodedArray = Uint8Array.from(atob(value), (c) =>
|
|
397
589
|
c.charCodeAt(0)
|
|
@@ -399,21 +591,105 @@ function decodeValue(value, decodingType) {
|
|
|
399
591
|
const decoder = new TextDecoder();
|
|
400
592
|
return decoder.decode(decodedArray);
|
|
401
593
|
} catch (error) {
|
|
402
|
-
|
|
594
|
+
console.warn(`Failed to decode Base64: ${error.message}`);
|
|
595
|
+
return value; // Return the original value as a fallback
|
|
403
596
|
}
|
|
597
|
+
|
|
404
598
|
case "html-entities":
|
|
405
599
|
const tempElement = document.createElement("div");
|
|
406
600
|
tempElement.innerHTML = value;
|
|
407
601
|
return tempElement.textContent;
|
|
602
|
+
|
|
408
603
|
case "json":
|
|
409
604
|
try {
|
|
410
605
|
return JSON.parse(value);
|
|
411
606
|
} catch (error) {
|
|
412
|
-
|
|
607
|
+
console.warn(`Failed to parse JSON: ${error.message}`);
|
|
608
|
+
return value; // Return the original value as a fallback
|
|
413
609
|
}
|
|
610
|
+
|
|
414
611
|
default:
|
|
415
|
-
|
|
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
|
+
}
|
|
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
|
+
}
|
|
416
691
|
}
|
|
692
|
+
return value;
|
|
417
693
|
}
|
|
418
694
|
|
|
419
695
|
export { getValue, storage };
|