@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/src/getValue.js CHANGED
@@ -1,4 +1,4 @@
1
- import utility from "./utility";
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: 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);
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
- } 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);
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
- if (value) {
92
- if (!valueType) value = value.toISOString();
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
- if (element.type === "time")
95
- // value = value.substring(11, 8) + 'Z';
96
- value = value.substring(11, 19) + "Z";
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[valueType](locale);
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
- let targetElement = element;
157
-
158
- // If value-exclude-selector exists, clone the element and remove the specified selectors
159
- const excludeSelector = element.getAttribute("value-remove-selector");
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
- // Remove matching elements from the cloned element
164
- targetElement
165
- .querySelectorAll(excludeSelector)
166
- .forEach((el) => el.remove());
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
- // Determine whether to use outerHTML, innerHTML, or innerText based on valueType
170
- if (valueType === "text") {
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
- if (valueType === "boolean") {
180
- if (!value || value === "fasle") return false;
181
- else return true;
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 (value === "$organization_id")
185
- value = localStorage.getItem("organization_id");
186
- else if (value === "$user_id") value = localStorage.getItem("user_id");
187
- else if (value === "$clientId") value = localStorage.getItem("clientId");
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; // 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
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
- // Process each attribute in order
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]; // Prioritize capturing group if available
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 0
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 group if available
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
- // Ignore other attributes
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
- // Now handle array-specific logic if valueType starts with 'array'
323
- if (valueType.startsWith("array")) {
324
- if (!Array.isArray(value)) {
325
- // If the parsed value is an object, apply array conversion based on operators
326
- if (typeof value === "object") {
327
- if (valueType === "array.$keys") {
328
- value = Object.keys(value);
329
- } else if (valueType === "array.$values") {
330
- value = Object.values(value);
331
- } else if (valueType === "array.$entries") {
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
- return value;
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
- const encoder = new TextEncoder();
373
- const uint8Array = encoder.encode(value);
374
- 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
+
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
- 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
+
381
558
  default:
382
- 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;
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": // New case for Base64 decoding (alias for 'base64')
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
- 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
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
- 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
413
609
  }
610
+
414
611
  default:
415
- 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
+ }
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 };