@cocreate/element-prototype 1.29.0 → 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/src/getValue.js CHANGED
@@ -1,4 +1,4 @@
1
- import { processOperators } from "./utility";
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: check if using a a switch case will provide better performance
11
- // return blobs for element.src and for link.href
12
- // pass value type as a param
13
- const getValue = (element) => {
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
- let valueType = element.getAttribute("value-type") || "";
29
-
30
- if (element.type === "checkbox") {
31
- let inputs = [element];
32
- let key = element.getAttribute("key");
33
- if (key)
34
- inputs = document.querySelectorAll(
35
- `input[type="${element.type}"][key="${key}"]`
36
- );
37
-
38
- if (inputs.length > 1) {
39
- value = [];
40
- inputs.forEach((el) => {
41
- if (el.checked) {
42
- let checkedValue = el.value;
43
- if (prefix || suffix)
44
- checkedValue = prefix + checkedValue + suffix;
45
-
46
- value.push(checkedValue);
47
- }
48
- });
49
- } else {
50
- if (element.checked) {
51
- if (element.hasAttribute("value"))
52
- value = element.value || true;
53
- else value = true;
54
- } else value = false;
55
- }
56
- } else if (element.type === "radio") {
57
- let key = element.getAttribute("key");
58
- value = document.querySelector(`input[key="${key}"]:checked`).value;
59
- } else if (element.type === "number") {
60
- value = Number(value);
61
- } else if (element.type === "range") {
62
- if (Number(element.min))
63
- value = [Number(element.min), Number(element.value)];
64
- else value = Number(element.value);
65
- } else if (element.type === "password") {
66
- value = btoa(value || "");
67
- } else if (element.type === "email") {
68
- value = value.toLowerCase();
69
- } else if (element.type === "url") {
70
- // TODO: define attributes to return url parts
71
- // return as a string or an object of url parts
72
- } else if (
73
- element.tagName == "SELECT" &&
74
- element.hasAttribute("multiple")
75
- ) {
76
- let options = element.selectedOptions;
77
- value = [];
78
- for (let i = 0; i < options.length; i++) {
79
- let optionValue = options[i].value;
80
- if (prefix || suffix) optionValue = prefix + optionValue + suffix;
81
- value.push(optionValue);
82
- }
83
- } else if (
84
- ["time", "date", "datetime", "datetime-local"].includes(
85
- element.getAttribute("type")
86
- )
87
- ) {
88
- if (value === "$now") value = new Date();
89
- else if (value) value = new Date(value);
90
-
91
- if (value) {
92
- if (!valueType) value = value.toISOString();
93
-
94
- if (element.type === "time")
95
- // value = value.substring(11, 8) + 'Z';
96
- value = value.substring(11, 19) + "Z";
97
-
98
- if (valueType) {
99
- switch (valueType) {
100
- case "getDayName":
101
- const days = [
102
- "Sunday",
103
- "Monday",
104
- "Tuesday",
105
- "Wednesday",
106
- "Thursday",
107
- "Friday",
108
- "Saturday"
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
- } else if (element.tagName == "INPUT" || element.tagName == "SELECT") {
148
- value = element.value;
149
- } else if (element.tagName == "TEXTAREA") {
150
- if (element.hasAttribute("value"))
151
- value = element.getAttribute("value");
152
- else value = element.value;
153
- } else if (element.tagName === "IFRAME") {
154
- value = element.srcdoc;
155
- } else if (element.hasAttribute("value")) {
156
- value = element.getAttribute("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);
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
- let targetElement = element;
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
- // If value-exclude-selector exists, clone the element and remove the specified selectors
161
- const excludeSelector = element.getAttribute("value-remove-selector");
162
- if (excludeSelector) {
163
- targetElement = element.cloneNode(true);
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
- // Remove matching elements from the cloned element
166
- targetElement
167
- .querySelectorAll(excludeSelector)
168
- .forEach((el) => el.remove());
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
- // Determine whether to use outerHTML, innerHTML, or innerText based on valueType
172
- if (valueType === "text") {
173
- value = targetElement.innerText;
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
- if (valueType === "boolean") {
184
- if (!value || value === "fasle") return false;
185
- else return true;
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
- value = processOperators(element, value, ["$value"]);
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; // Get all attributes of the element
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
- // Process each attribute in order
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]; // Prioritize capturing group if available
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 0
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 group if available
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
- // Ignore other attributes
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
- // Now handle array-specific logic if valueType starts with 'array'
319
- if (valueType.startsWith("array")) {
320
- if (!Array.isArray(value)) {
321
- // If the parsed value is an object, apply array conversion based on operators
322
- if (typeof value === "object") {
323
- if (valueType === "array.$keys") {
324
- value = Object.keys(value);
325
- } else if (valueType === "array.$values") {
326
- value = Object.values(value);
327
- } else if (valueType === "array.$entries") {
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
- return value;
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
- const encoder = new TextEncoder();
369
- const uint8Array = encoder.encode(value);
370
- return btoa(String.fromCharCode(...uint8Array));
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
- return JSON.stringify(value);
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
- throw new Error(`Unsupported encoding type: ${encodingType}`);
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": // New case for Base64 decoding (alias for 'base64')
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
- throw new Error(`Invalid Base64 string: ${error.message}`);
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
- throw new Error(`Invalid JSON string: ${error.message}`);
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
- throw new Error(`Unsupported decoding type: ${decodingType}`);
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 };