@cocreate/element-prototype 1.33.1 → 1.34.0
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/.github/workflows/automated.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/package.json +4 -4
- package/release.config.js +1 -1
- package/src/getAttribute.js +74 -38
- package/src/getValue.js +220 -251
- package/src/queryElements.js +15 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.33.2](https://github.com/CoCreate-app/CoCreate-element-prototype/compare/v1.33.1...v1.33.2) (2026-07-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* set default branch to main ([f58a47d](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/f58a47deb32726011de8ad248f7f34b1197e0c35))
|
|
7
|
+
|
|
1
8
|
## [1.33.1](https://github.com/CoCreate-app/CoCreate-element-prototype/compare/v1.33.0...v1.33.1) (2026-07-17)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/element-prototype",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "A simple element-prototype component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"element-prototype",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
},
|
|
37
37
|
"main": "./src/index.js",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cocreate/utils": "^1.44.
|
|
39
|
+
"@cocreate/utils": "^1.44.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@cocreate/webpack": "^1.6.
|
|
42
|
+
"@cocreate/webpack": "^1.6.2"
|
|
43
43
|
},
|
|
44
44
|
"allowScripts": {
|
|
45
45
|
"@cocreate/utils@1.42.2": true,
|
|
46
46
|
"@cocreate/webpack@1.4.3": true
|
|
47
47
|
}
|
|
48
|
-
}
|
|
48
|
+
}
|
package/release.config.js
CHANGED
package/src/getAttribute.js
CHANGED
|
@@ -1,69 +1,105 @@
|
|
|
1
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
2
18
|
|
|
3
|
-
|
|
19
|
+
import { astSync, astAsync } from "@cocreate/utils";
|
|
20
|
+
|
|
21
|
+
// Store a reference to the original native getAttribute function
|
|
4
22
|
const originalGetAttribute = Element.prototype.getAttribute;
|
|
5
23
|
|
|
6
|
-
// Map to store attribute details
|
|
24
|
+
// Map to store attribute binding details for dynamic storage syncs
|
|
7
25
|
const attributes = new Map();
|
|
8
26
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
if (typeof window !== "undefined") {
|
|
28
|
+
// Add an event listener for storage changes to update local attributes dynamically
|
|
29
|
+
window.addEventListener("storage", updateAttributes);
|
|
30
|
+
|
|
31
|
+
// Custom event bridge to update attributes from script environments
|
|
32
|
+
window.addEventListener("updateAttributes", function (e) {
|
|
33
|
+
updateAttributes(e.detail);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
15
36
|
|
|
16
37
|
/**
|
|
17
|
-
* Function to update attributes based on specific storage keys
|
|
18
|
-
* @param {Object} e - The event object containing key and newValue
|
|
38
|
+
* Function to update attributes on registered elements based on specific storage keys
|
|
39
|
+
* @param {Object} e - The event object containing changed key and newValue
|
|
19
40
|
*/
|
|
20
41
|
function updateAttributes(e) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
if (!e) return;
|
|
43
|
+
const keys = ["organization_id", "user_id", "clientId", "session_id"];
|
|
44
|
+
if (keys.includes(e.key)) {
|
|
45
|
+
let attr = attributes.get(e.key) || [];
|
|
46
|
+
for (let attribute of attr) {
|
|
47
|
+
if (attribute && attribute.element) {
|
|
48
|
+
attribute.element.setAttribute(attribute.name, e.newValue);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
28
52
|
}
|
|
29
53
|
|
|
30
54
|
/**
|
|
31
|
-
* Custom getAttribute function that processes the attribute value
|
|
55
|
+
* Custom getAttribute function that processes the attribute value synchronously
|
|
32
56
|
* @param {Element} element - The element from which to get the attribute
|
|
33
57
|
* @param {string} name - The attribute name
|
|
34
|
-
* @returns {string} - The processed attribute value
|
|
58
|
+
* @returns {string|any} - The processed attribute value
|
|
35
59
|
*/
|
|
36
60
|
function getAttribute(element, name) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
if (!(element instanceof Element)) {
|
|
62
|
+
throw new Error("First argument must be an Element");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const value = originalGetAttribute.call(element, name);
|
|
66
|
+
if (!value || typeof value !== "string") {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Evaluate formulas or step paths synchronously via the unified AST Engine
|
|
71
|
+
return astSync({ executeAST: value, node: element });
|
|
42
72
|
}
|
|
43
73
|
|
|
44
74
|
/**
|
|
45
|
-
* Asynchronously gets an attribute and processes its value
|
|
75
|
+
* Asynchronously gets an attribute and processes its value through our async AST pipelines.
|
|
46
76
|
* @param {Element} element - The element from which to get the attribute.
|
|
47
77
|
* @param {string} name - The attribute name.
|
|
48
|
-
* @returns {Promise<string|
|
|
78
|
+
* @returns {Promise<string|any>} - A promise that resolves to the processed attribute value.
|
|
49
79
|
*/
|
|
50
80
|
async function getAttributeAsync(element, name) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
if (!(element instanceof Element)) {
|
|
82
|
+
throw new Error("First argument must be an Element");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const value = originalGetAttribute.call(element, name);
|
|
86
|
+
if (!value || typeof value !== "string") {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Evaluate async formulas, database lookups, or template blocks asynchronously
|
|
91
|
+
return await astAsync({ executeAST: value, node: element });
|
|
56
92
|
}
|
|
57
93
|
|
|
58
|
-
// Override the getAttribute method on Element prototype
|
|
94
|
+
// Override the native getAttribute method on the global Element prototype
|
|
59
95
|
Element.prototype.getAttribute = function (name) {
|
|
60
|
-
|
|
96
|
+
return getAttribute(this, name);
|
|
61
97
|
};
|
|
62
98
|
|
|
63
|
-
// Add getAttributeAsync to the Element prototype
|
|
99
|
+
// Add getAttributeAsync directly to the global Element prototype
|
|
64
100
|
Element.prototype.getAttributeAsync = function (name) {
|
|
65
|
-
|
|
101
|
+
return getAttributeAsync(this, name);
|
|
66
102
|
};
|
|
67
103
|
|
|
68
|
-
// Export the
|
|
69
|
-
export { getAttribute, getAttributeAsync };
|
|
104
|
+
// Export the customized attribute getters
|
|
105
|
+
export { getAttribute, getAttributeAsync };
|
package/src/getValue.js
CHANGED
|
@@ -1,178 +1,293 @@
|
|
|
1
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
18
|
+
|
|
19
|
+
import { astSync, astAsync } from "@cocreate/utils";
|
|
2
20
|
|
|
3
21
|
const storage = new Map();
|
|
4
22
|
|
|
5
23
|
HTMLElement.prototype.getValue = function () {
|
|
6
|
-
|
|
7
|
-
return value;
|
|
24
|
+
return getValue(this);
|
|
8
25
|
};
|
|
9
26
|
SVGElement.prototype.getValue = function () {
|
|
10
|
-
|
|
11
|
-
return value;
|
|
27
|
+
return getValue(this);
|
|
12
28
|
};
|
|
13
29
|
|
|
14
|
-
|
|
30
|
+
HTMLElement.prototype.getValueAsync = async function () {
|
|
31
|
+
return await getValueAsync(this);
|
|
32
|
+
};
|
|
33
|
+
SVGElement.prototype.getValueAsync = async function () {
|
|
34
|
+
return await getValueAsync(this);
|
|
35
|
+
};
|
|
15
36
|
|
|
16
37
|
/**
|
|
17
|
-
* Retrieves and processes the value from an HTML element
|
|
38
|
+
* Retrieves and processes the value from an HTML element synchronously.
|
|
18
39
|
*
|
|
19
40
|
* @param {HTMLElement} element - The HTML element to process for its value.
|
|
20
41
|
* @param {string} valueType - Optional. The expected type that defines how the value should be processed.
|
|
21
|
-
* @returns {any} - The processed value
|
|
42
|
+
* @returns {any} - The processed value.
|
|
22
43
|
*/
|
|
23
44
|
const getValue = (element, valueType) => {
|
|
24
|
-
// If no valueType is given, attempt to retrieve it from the element's attributes
|
|
25
45
|
if (!valueType) {
|
|
26
46
|
valueType = element.getAttribute("value-type") || "";
|
|
27
47
|
}
|
|
28
48
|
|
|
29
|
-
// Initialize value with element's value property or its 'value' attribute, defaulting to an empty string
|
|
30
49
|
let value = element.value || element.getAttribute("value") || "";
|
|
31
50
|
|
|
32
|
-
// Handle specific cases for elements considered components, plugins, or file inputs
|
|
33
51
|
if (
|
|
34
52
|
element.hasAttribute("component") ||
|
|
35
|
-
// element.hasAttribute("plugin") ||
|
|
36
53
|
element.type === "file" ||
|
|
37
54
|
element.getAttribute("type") === "file"
|
|
38
55
|
) {
|
|
39
|
-
// Retrieve and delete value from storage for secure data handling
|
|
40
56
|
value = storage.get(element);
|
|
41
57
|
storage.delete(element);
|
|
42
58
|
return value;
|
|
43
59
|
}
|
|
44
60
|
|
|
45
|
-
// Retrieve prefix and suffix from attributes for later use
|
|
46
61
|
let prefix = element.getAttribute("value-prefix") || "";
|
|
47
62
|
let suffix = element.getAttribute("value-suffix") || "";
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
// const elementType = element.type ? element.type.toLowerCase() : null;
|
|
51
|
-
const elementType = element.getAttribute("type") || element.type
|
|
64
|
+
const elementType = element.getAttribute("type") || element.type;
|
|
52
65
|
const tagName = element.tagName.toUpperCase();
|
|
53
66
|
|
|
54
|
-
// Switch statement to handle different element types and tagNames
|
|
55
67
|
switch (elementType) {
|
|
56
68
|
case "checkbox":
|
|
57
|
-
// Handles multiple checkboxes in a group using a key and applies prefix/suffix if needed
|
|
58
69
|
value = handleCheckbox(element, prefix, suffix);
|
|
59
70
|
break;
|
|
60
71
|
|
|
61
|
-
case "radio":
|
|
72
|
+
case "radio": {
|
|
62
73
|
const key = element.getAttribute("key");
|
|
63
|
-
// Handles radio inputs by selecting the checked radio's value in the group with the same key
|
|
64
74
|
let el = document.querySelector(`input[key="${key}"]:checked`);
|
|
65
75
|
if (el) {
|
|
66
76
|
value = el.value;
|
|
67
77
|
}
|
|
68
78
|
break;
|
|
79
|
+
}
|
|
69
80
|
|
|
70
81
|
case "number":
|
|
71
|
-
// Converts the value to a number for inputs of type number
|
|
72
82
|
value = Number(value);
|
|
73
83
|
break;
|
|
74
84
|
|
|
75
85
|
case "range":
|
|
76
|
-
// If a minimum is specified, returns a range array, otherwise a single number
|
|
77
86
|
value = element.min
|
|
78
87
|
? [Number(element.min), Number(element.value)]
|
|
79
88
|
: Number(element.value);
|
|
80
89
|
break;
|
|
81
90
|
|
|
82
91
|
case "password":
|
|
83
|
-
// Encodes the value in Base64 format for password inputs for secure representation
|
|
84
92
|
value = btoa(value || "");
|
|
85
93
|
break;
|
|
86
94
|
|
|
87
95
|
case "email":
|
|
88
|
-
// Converts the email to lowercase to maintain a standardized format
|
|
89
96
|
value = value.toLowerCase();
|
|
90
97
|
break;
|
|
91
98
|
|
|
92
99
|
case "url":
|
|
93
|
-
// TODO: Implement logic to return URL parts instead of the complete URL
|
|
94
100
|
break;
|
|
95
101
|
|
|
96
102
|
case "time":
|
|
97
103
|
case "date":
|
|
98
104
|
case "datetime":
|
|
99
105
|
case "datetime-local":
|
|
100
|
-
// Processes datetime-related inputs with custom logic
|
|
101
106
|
value = handleDateTime(element, value, valueType);
|
|
102
107
|
break;
|
|
108
|
+
|
|
103
109
|
default:
|
|
104
110
|
switch (tagName) {
|
|
105
111
|
case "INPUT":
|
|
106
|
-
// For generic input types, use the element's value
|
|
107
112
|
value = element.value;
|
|
108
113
|
break;
|
|
109
114
|
|
|
110
115
|
case "SELECT":
|
|
111
|
-
// Handles multiple selection in select elements, applying prefix/suffix if required
|
|
112
116
|
value = element.hasAttribute("multiple")
|
|
113
117
|
? handleMultipleSelect(element, prefix, suffix)
|
|
114
118
|
: element.value;
|
|
115
119
|
break;
|
|
116
120
|
|
|
117
121
|
case "TEXTAREA":
|
|
118
|
-
// For textarea elements, preference is given to the 'value' attribute if it exists
|
|
119
122
|
value = element.hasAttribute("value")
|
|
120
123
|
? element.getAttribute("value")
|
|
121
124
|
: element.value;
|
|
122
125
|
break;
|
|
123
126
|
|
|
124
127
|
case "IFRAME":
|
|
125
|
-
// For iframes, return the document source
|
|
126
128
|
value = element.srcdoc;
|
|
127
129
|
break;
|
|
128
130
|
|
|
129
131
|
default:
|
|
130
|
-
// Handles cases not explicitly matched by type or tagName
|
|
131
132
|
value = handleElement(element, valueType);
|
|
132
133
|
break;
|
|
133
134
|
}
|
|
134
135
|
break;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
// If the desired valueType is boolean, convert the value accordingly
|
|
138
138
|
if (valueType === "boolean") {
|
|
139
139
|
return value && value !== "false";
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
// Apply additional processing through a series of transformation functions
|
|
143
142
|
if (value) {
|
|
144
|
-
value =
|
|
143
|
+
value = astSync({ executeAST: value, node: element, exclude: ["$value"] });
|
|
145
144
|
value = caseHandler(element, value);
|
|
146
145
|
value = regex(element, value);
|
|
147
146
|
value = encodeValue(value, element.getAttribute("value-encode") || "");
|
|
148
147
|
value = decodeValue(value, element.getAttribute("value-decode") || "");
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
// Append prefix and suffix to value if applicable, before JSON parsing
|
|
152
150
|
if (typeof value === "string" || typeof value === "number") {
|
|
153
151
|
if (prefix || suffix) {
|
|
154
152
|
value = prefix + value + suffix;
|
|
155
153
|
}
|
|
156
154
|
}
|
|
157
155
|
|
|
158
|
-
// Parse the value as JSON, if possible, and convert to an array if needed
|
|
159
156
|
value = parseJson(value, valueType);
|
|
160
157
|
value = toArray(value, valueType);
|
|
161
158
|
|
|
162
|
-
return value;
|
|
159
|
+
return value;
|
|
163
160
|
};
|
|
164
161
|
|
|
165
162
|
/**
|
|
166
|
-
*
|
|
167
|
-
* For multiple checkboxes with the same key, their checked values are collected into an array.
|
|
163
|
+
* Retrieves and processes the value from an HTML element asynchronously.
|
|
168
164
|
*
|
|
169
|
-
* @param {
|
|
170
|
-
* @param {string}
|
|
171
|
-
* @
|
|
172
|
-
|
|
165
|
+
* @param {HTMLElement} element - The HTML element to process for its value.
|
|
166
|
+
* @param {string} valueType - Optional. The expected type that defines how the value should be processed.
|
|
167
|
+
* @returns {Promise<any>} - A promise that resolves to the processed value.
|
|
168
|
+
*/
|
|
169
|
+
const getValueAsync = async (element, valueType) => {
|
|
170
|
+
if (!valueType) {
|
|
171
|
+
valueType = element.getAttribute("value-type") || "";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let value = element.value || element.getAttribute("value") || "";
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
element.hasAttribute("component") ||
|
|
178
|
+
element.type === "file" ||
|
|
179
|
+
element.getAttribute("type") === "file"
|
|
180
|
+
) {
|
|
181
|
+
value = storage.get(element);
|
|
182
|
+
storage.delete(element);
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let prefix = element.getAttribute("value-prefix") || "";
|
|
187
|
+
let suffix = element.getAttribute("value-suffix") || "";
|
|
188
|
+
|
|
189
|
+
const elementType = element.getAttribute("type") || element.type;
|
|
190
|
+
const tagName = element.tagName.toUpperCase();
|
|
191
|
+
|
|
192
|
+
switch (elementType) {
|
|
193
|
+
case "checkbox":
|
|
194
|
+
value = handleCheckbox(element, prefix, suffix);
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case "radio": {
|
|
198
|
+
const key = element.getAttribute("key");
|
|
199
|
+
let el = document.querySelector(`input[key="${key}"]:checked`);
|
|
200
|
+
if (el) {
|
|
201
|
+
value = el.value;
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
case "number":
|
|
207
|
+
value = Number(value);
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case "range":
|
|
211
|
+
value = element.min
|
|
212
|
+
? [Number(element.min), Number(element.value)]
|
|
213
|
+
: Number(element.value);
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case "password":
|
|
217
|
+
value = btoa(value || "");
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case "email":
|
|
221
|
+
value = value.toLowerCase();
|
|
222
|
+
break;
|
|
223
|
+
|
|
224
|
+
case "url":
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case "time":
|
|
228
|
+
case "date":
|
|
229
|
+
case "datetime":
|
|
230
|
+
case "datetime-local":
|
|
231
|
+
value = handleDateTime(element, value, valueType);
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
switch (tagName) {
|
|
236
|
+
case "INPUT":
|
|
237
|
+
value = element.value;
|
|
238
|
+
break;
|
|
239
|
+
|
|
240
|
+
case "SELECT":
|
|
241
|
+
value = element.hasAttribute("multiple")
|
|
242
|
+
? handleMultipleSelect(element, prefix, suffix)
|
|
243
|
+
: element.value;
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
case "TEXTAREA":
|
|
247
|
+
value = element.hasAttribute("value")
|
|
248
|
+
? element.getAttribute("value")
|
|
249
|
+
: element.value;
|
|
250
|
+
break;
|
|
251
|
+
|
|
252
|
+
case "IFRAME":
|
|
253
|
+
value = element.srcdoc;
|
|
254
|
+
break;
|
|
255
|
+
|
|
256
|
+
default:
|
|
257
|
+
value = handleElement(element, valueType);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (valueType === "boolean") {
|
|
264
|
+
return value && value !== "false";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (value) {
|
|
268
|
+
value = await astAsync({ executeAST: value, node: element, exclude: ["$value"] });
|
|
269
|
+
value = caseHandler(element, value);
|
|
270
|
+
value = regex(element, value);
|
|
271
|
+
value = encodeValue(value, element.getAttribute("value-encode") || "");
|
|
272
|
+
value = decodeValue(value, element.getAttribute("value-decode") || "");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
276
|
+
if (prefix || suffix) {
|
|
277
|
+
value = prefix + value + suffix;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
value = parseJson(value, valueType);
|
|
282
|
+
value = toArray(value, valueType);
|
|
283
|
+
|
|
284
|
+
return value;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Processes a checkbox or a group of checkboxes to determine their checked values.
|
|
173
289
|
*/
|
|
174
290
|
const handleCheckbox = (element, prefix = "", suffix = "") => {
|
|
175
|
-
// Retrieve all checkboxes with the same key, else just the single element
|
|
176
291
|
const inputs = element.getAttribute("key")
|
|
177
292
|
? document.querySelectorAll(
|
|
178
293
|
`input[type="${element.type}"][key="${element.getAttribute(
|
|
@@ -182,30 +297,16 @@ const handleCheckbox = (element, prefix = "", suffix = "") => {
|
|
|
182
297
|
: [element];
|
|
183
298
|
|
|
184
299
|
if (inputs.length === 1) {
|
|
185
|
-
// If only one checkbox, return its value or true/false depending on its checked state
|
|
186
300
|
return element.checked ? element.value || true : false;
|
|
187
301
|
} else {
|
|
188
|
-
// For multiple checkboxes, collect their checked values into an array
|
|
189
302
|
return Array.from(inputs)
|
|
190
|
-
.filter((el) => el.checked)
|
|
191
|
-
.map((el) => `${prefix}${el.value}${suffix}`);
|
|
303
|
+
.filter((el) => el.checked)
|
|
304
|
+
.map((el) => `${prefix}${el.value}${suffix}`);
|
|
192
305
|
}
|
|
193
306
|
};
|
|
194
307
|
|
|
195
308
|
/**
|
|
196
309
|
* Handles and transforms a date/time value based on the specified valueType.
|
|
197
|
-
* Supports operations like converting to ISO string, extracting day/month names, converting to Unix timestamp, and more.
|
|
198
|
-
*
|
|
199
|
-
* @param {HTMLElement} element - The DOM element containing the date/time value.
|
|
200
|
-
* @param {string} value - The initial value which may represent a date/time.
|
|
201
|
-
* @param {string} valueType - Specifies the type of transformation to apply to the date/time value.
|
|
202
|
-
* @returns {any} - The transformed or processed date/time value.
|
|
203
|
-
*/
|
|
204
|
-
/**
|
|
205
|
-
* Handles and transforms a date/time value with a 3-step pipeline:
|
|
206
|
-
* 1. SNAP: Adjusts date to start/end of periods (Month, Week, Year).
|
|
207
|
-
* 2. MATH: Adds/Subtracts Years, Months, Weeks, Days, Hours, Minutes, Seconds.
|
|
208
|
-
* 3. FORMAT: Returns the final string/number representation.
|
|
209
310
|
*/
|
|
210
311
|
const handleDateTime = (element, value, valueType) => {
|
|
211
312
|
const inputType = (element.getAttribute("type") || element.type || "").toLowerCase();
|
|
@@ -214,7 +315,6 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
214
315
|
if (value === "$now" || element.hasAttribute("now")) {
|
|
215
316
|
date = new Date();
|
|
216
317
|
} else if (value instanceof Date) {
|
|
217
|
-
// If it's already a date object, clone it to avoid mutating references
|
|
218
318
|
date = new Date(value.getTime());
|
|
219
319
|
} else if (value) {
|
|
220
320
|
if (
|
|
@@ -223,7 +323,6 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
223
323
|
value
|
|
224
324
|
)
|
|
225
325
|
) {
|
|
226
|
-
// Normalize datetime-local only when seconds are absent; preserve provided seconds.
|
|
227
326
|
if (inputType === "datetime-local" && /^[0-9T:-]{16}$/.test(value)) {
|
|
228
327
|
value = `${value}:00`;
|
|
229
328
|
}
|
|
@@ -237,12 +336,11 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
237
336
|
}
|
|
238
337
|
|
|
239
338
|
if (date instanceof Date && !isNaN(date.getTime())) {
|
|
240
|
-
// These operations "floor" or "ceil" the date to a specific boundary.
|
|
241
339
|
switch (valueType) {
|
|
242
340
|
case "startOfDay":
|
|
243
341
|
date.setHours(0, 0, 0, 0);
|
|
244
342
|
break;
|
|
245
|
-
case "startOfWeek":
|
|
343
|
+
case "startOfWeek": {
|
|
246
344
|
const startWkOff = parseInt(
|
|
247
345
|
element.getAttribute("week-start-day") || 0,
|
|
248
346
|
10
|
|
@@ -250,7 +348,8 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
250
348
|
date.setDate(date.getDate() - date.getDay() + startWkOff);
|
|
251
349
|
date.setHours(0, 0, 0, 0);
|
|
252
350
|
break;
|
|
253
|
-
|
|
351
|
+
}
|
|
352
|
+
case "endOfWeek": {
|
|
254
353
|
const endWkOff = parseInt(
|
|
255
354
|
element.getAttribute("week-start-day") || 0,
|
|
256
355
|
10
|
|
@@ -258,6 +357,7 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
258
357
|
date.setDate(date.getDate() - date.getDay() + 6 + endWkOff);
|
|
259
358
|
date.setHours(23, 59, 59, 999);
|
|
260
359
|
break;
|
|
360
|
+
}
|
|
261
361
|
case "startOfMonth":
|
|
262
362
|
date.setDate(1);
|
|
263
363
|
date.setHours(0, 0, 0, 0);
|
|
@@ -276,78 +376,68 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
276
376
|
break;
|
|
277
377
|
}
|
|
278
378
|
|
|
279
|
-
// --- PHASE 3: MATH (Modify the Date)
|
|
280
|
-
// Helper to get integer value from attribute safely
|
|
281
379
|
const getVal = (attr) =>
|
|
282
380
|
parseInt(element.getAttribute(attr) || "0", 10);
|
|
283
381
|
|
|
284
|
-
// 1. Years
|
|
285
382
|
const addYears = getVal("add-years") - getVal("subtract-years");
|
|
286
383
|
if (addYears) date.setFullYear(date.getFullYear() + addYears);
|
|
287
384
|
|
|
288
|
-
// 2. Months
|
|
289
385
|
const addMonths = getVal("add-months") - getVal("subtract-months");
|
|
290
386
|
if (addMonths) date.setMonth(date.getMonth() + addMonths);
|
|
291
387
|
|
|
292
|
-
// 3. Weeks
|
|
293
388
|
const addWeeks = getVal("add-weeks") - getVal("subtract-weeks");
|
|
294
389
|
if (addWeeks) date.setDate(date.getDate() + addWeeks * 7);
|
|
295
390
|
|
|
296
|
-
// 4. Days
|
|
297
391
|
const addDays = getVal("add-days") - getVal("subtract-days");
|
|
298
392
|
if (addDays) date.setDate(date.getDate() + addDays);
|
|
299
393
|
|
|
300
|
-
// 5. Hours
|
|
301
394
|
const addHours = getVal("add-hours") - getVal("subtract-hours");
|
|
302
395
|
if (addHours) date.setHours(date.getHours() + addHours);
|
|
303
396
|
|
|
304
|
-
// 6. Minutes
|
|
305
397
|
const addMinutes = getVal("add-minutes") - getVal("subtract-minutes");
|
|
306
398
|
if (addMinutes) date.setMinutes(date.getMinutes() + addMinutes);
|
|
307
399
|
|
|
308
|
-
// 7. Seconds
|
|
309
400
|
const addSeconds = getVal("add-seconds") - getVal("subtract-seconds");
|
|
310
401
|
if (addSeconds) date.setSeconds(date.getSeconds() + addSeconds);
|
|
311
402
|
|
|
312
|
-
|
|
313
|
-
// --- PHASE 4: FORMATTING (Output the Result) ---
|
|
314
403
|
switch (valueType) {
|
|
315
|
-
case "getDayName":
|
|
404
|
+
case "getDayName": {
|
|
316
405
|
const days = [
|
|
317
406
|
"Sunday", "Monday", "Tuesday", "Wednesday",
|
|
318
407
|
"Thursday", "Friday", "Saturday",
|
|
319
408
|
];
|
|
320
409
|
return days[date.getDay()];
|
|
410
|
+
}
|
|
321
411
|
|
|
322
|
-
case "getMonthName":
|
|
412
|
+
case "getMonthName": {
|
|
323
413
|
const months = [
|
|
324
414
|
"January", "February", "March", "April", "May", "June",
|
|
325
415
|
"July", "August", "September", "October", "November", "December",
|
|
326
416
|
];
|
|
327
417
|
return months[date.getMonth()];
|
|
418
|
+
}
|
|
328
419
|
|
|
329
420
|
case "getYear":
|
|
330
421
|
case "getFullYear":
|
|
331
422
|
return date.getFullYear();
|
|
332
423
|
|
|
333
|
-
case "getTime":
|
|
424
|
+
case "getTime": {
|
|
334
425
|
let hours = date.getHours();
|
|
335
426
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
336
427
|
const ampm = hours >= 12 ? "PM" : "AM";
|
|
337
|
-
hours = hours % 12 || 12;
|
|
428
|
+
hours = hours % 12 || 12;
|
|
338
429
|
return `${hours}:${minutes} ${ampm}`;
|
|
430
|
+
}
|
|
339
431
|
|
|
340
432
|
case "timeTo": {
|
|
341
433
|
const diffMs = date.getTime() - Date.now();
|
|
342
434
|
let totalSeconds = Math.floor(Math.abs(diffMs) / 1000);
|
|
343
435
|
|
|
344
|
-
// Fetch requested units, defaulting to all if attribute isn't present
|
|
345
436
|
const unitsAttr = element.getAttribute("time-to-units") || "years,months,days,hours,minutes,seconds";
|
|
346
437
|
const requestedUnits = unitsAttr.split(',').map(u => u.trim().toLowerCase());
|
|
347
438
|
|
|
348
439
|
const result = {};
|
|
349
440
|
|
|
350
|
-
// Using the exact same time approximations as your timeAgo feature
|
|
351
441
|
const unitValues = {
|
|
352
442
|
years: 31536000,
|
|
353
443
|
months: 2592000,
|
|
@@ -358,14 +448,13 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
358
448
|
seconds: 1
|
|
359
449
|
};
|
|
360
450
|
|
|
361
|
-
// The order matters here (largest to smallest) so we "consume" time properly
|
|
362
451
|
const order = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"];
|
|
363
452
|
|
|
364
453
|
for (const unit of order) {
|
|
365
454
|
if (requestedUnits.includes(unit)) {
|
|
366
455
|
const val = Math.floor(totalSeconds / unitValues[unit]);
|
|
367
456
|
result[unit] = val;
|
|
368
|
-
totalSeconds -= val * unitValues[unit];
|
|
457
|
+
totalSeconds -= val * unitValues[unit];
|
|
369
458
|
}
|
|
370
459
|
}
|
|
371
460
|
|
|
@@ -376,7 +465,6 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
376
465
|
const diffMs = Date.now() - date.getTime();
|
|
377
466
|
const absSecs = Math.floor(Math.abs(diffMs) / 1000);
|
|
378
467
|
|
|
379
|
-
// --- LIMIT CHECK ---
|
|
380
468
|
const limitAttr = element.getAttribute("time-ago-limit");
|
|
381
469
|
if (limitAttr) {
|
|
382
470
|
const match = limitAttr.match(/^(\d+)([a-z]+)$/i);
|
|
@@ -386,18 +474,17 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
386
474
|
let multiplier = 0;
|
|
387
475
|
|
|
388
476
|
switch (limitUnit) {
|
|
389
|
-
case 's': multiplier = 1; break;
|
|
390
|
-
case 'm': multiplier = 60; break;
|
|
391
|
-
case 'h': multiplier = 3600; break;
|
|
392
|
-
case 'd': multiplier = 86400; break;
|
|
393
|
-
case 'w': multiplier = 604800; break;
|
|
394
|
-
case 'mo': multiplier = 2592000; break;
|
|
395
|
-
case 'y': multiplier = 31536000; break;
|
|
477
|
+
case 's': multiplier = 1; break;
|
|
478
|
+
case 'm': multiplier = 60; break;
|
|
479
|
+
case 'h': multiplier = 3600; break;
|
|
480
|
+
case 'd': multiplier = 86400; break;
|
|
481
|
+
case 'w': multiplier = 604800; break;
|
|
482
|
+
case 'mo': multiplier = 2592000; break;
|
|
483
|
+
case 'y': multiplier = 31536000; break;
|
|
396
484
|
}
|
|
397
485
|
|
|
398
486
|
const limitInSeconds = amount * multiplier;
|
|
399
487
|
|
|
400
|
-
// If the time elapsed is greater than the limit, fallback to standard date string
|
|
401
488
|
if (absSecs > limitInSeconds) {
|
|
402
489
|
const locale = element.getAttribute("locale") || "en-US";
|
|
403
490
|
return date.toLocaleString(locale);
|
|
@@ -421,19 +508,18 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
421
508
|
case "toUnixTimestamp":
|
|
422
509
|
return Math.floor(date.getTime() / 1000);
|
|
423
510
|
|
|
424
|
-
case "toLocaleString":
|
|
511
|
+
case "toLocaleString": {
|
|
425
512
|
const locale = element.getAttribute("locale") || "en-US";
|
|
426
513
|
return date.toLocaleString(locale);
|
|
514
|
+
}
|
|
427
515
|
|
|
428
516
|
default:
|
|
429
|
-
// Handle generic methods if specified
|
|
430
517
|
if (valueType && typeof date[valueType] === "function") {
|
|
431
518
|
return date[valueType]();
|
|
432
519
|
}
|
|
433
520
|
|
|
434
521
|
const pad = (n) => String(n).padStart(2, "0");
|
|
435
522
|
if (inputType === "datetime-local") {
|
|
436
|
-
// Return a datetime-local compatible string with seconds to avoid invalid values
|
|
437
523
|
return `${date.getFullYear()}-${pad(
|
|
438
524
|
date.getMonth() + 1
|
|
439
525
|
)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(
|
|
@@ -444,102 +530,64 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
444
530
|
}
|
|
445
531
|
}
|
|
446
532
|
};
|
|
533
|
+
|
|
447
534
|
/**
|
|
448
|
-
* Processes a <select>
|
|
449
|
-
* Each selected value can have a prefix or suffix added to it if specified.
|
|
450
|
-
*
|
|
451
|
-
* @param {HTMLSelectElement} element - The select element with multiple options that are possibly selected.
|
|
452
|
-
* @param {string} prefix - A string to prepend to each selected option value.
|
|
453
|
-
* @param {string} suffix - A string to append to each selected option value.
|
|
454
|
-
* @returns {string[]} - An array of selected option values, each optionally prefixed and suffixed.
|
|
535
|
+
* Processes a <select> element with the "multiple" attribute.
|
|
455
536
|
*/
|
|
456
537
|
const handleMultipleSelect = (element, prefix, suffix) => {
|
|
457
|
-
// Initialize an empty array to hold the values of selected options.
|
|
458
538
|
let value = [];
|
|
459
|
-
|
|
460
|
-
// Retrieve all selected options from the select element.
|
|
461
539
|
let options = element.selectedOptions;
|
|
462
540
|
|
|
463
|
-
// Iterate over each selected option.
|
|
464
541
|
for (let i = 0; i < options.length; i++) {
|
|
465
|
-
// Retrieve the value of the current option.
|
|
466
542
|
let optionValue = options[i].value;
|
|
467
|
-
|
|
468
|
-
// If a prefix or suffix is provided, modify the option value accordingly.
|
|
469
543
|
if (prefix || suffix) {
|
|
470
544
|
optionValue = prefix + optionValue + suffix;
|
|
471
545
|
}
|
|
472
|
-
|
|
473
|
-
// Add the processed option value to the array.
|
|
474
546
|
value.push(optionValue);
|
|
475
547
|
}
|
|
476
548
|
|
|
477
|
-
// Return the array of processed option values.
|
|
478
549
|
return value;
|
|
479
550
|
};
|
|
480
551
|
|
|
481
552
|
/**
|
|
482
|
-
* Retrieves a
|
|
483
|
-
* If the element has a "value" attribute, it returns its value. Otherwise, it processes
|
|
484
|
-
* the element to extract or format the desired information based on the valueType.
|
|
485
|
-
*
|
|
486
|
-
* @param {HTMLElement} element - The DOM element to process.
|
|
487
|
-
* @param {string} valueType - The type of value to retrieve (e.g., "text", "outerHTML").
|
|
488
|
-
* @returns {string} - The extracted value based on the specified valueType.
|
|
553
|
+
* Retrieves a value from a standard element.
|
|
489
554
|
*/
|
|
490
555
|
const handleElement = (element, valueType) => {
|
|
491
|
-
// Check if the element has a "value" attribute; if so, return its value.
|
|
492
556
|
if (element.hasAttribute("value")) {
|
|
493
557
|
return element.getAttribute("value");
|
|
494
558
|
}
|
|
495
559
|
|
|
496
|
-
// Use the original element as the target by default.
|
|
497
560
|
let targetElement = element;
|
|
498
|
-
// Retrieve the "value-remove-query" attribute used to specify selectors that should be removed.
|
|
499
561
|
const excludeSelector = element.getAttribute("value-remove-query");
|
|
500
562
|
|
|
501
|
-
// If there is an excludeSelector, clone the element and remove elements matching the selector.
|
|
502
563
|
if (excludeSelector) {
|
|
503
|
-
// Clone the element to avoid modifying the original DOM.
|
|
504
564
|
targetElement = element.cloneNode(true);
|
|
505
|
-
// Find and remove all elements that match the excludeSelector from the cloned element.
|
|
506
565
|
targetElement
|
|
507
566
|
.querySelectorAll(excludeSelector)
|
|
508
567
|
.forEach((el) => el.remove());
|
|
509
568
|
}
|
|
510
569
|
|
|
511
|
-
// Determine the value to return based on the valueType.
|
|
512
570
|
switch (valueType) {
|
|
513
571
|
case "text":
|
|
514
|
-
// Return the text content of the target element.
|
|
515
572
|
return targetElement.innerText;
|
|
516
573
|
case "outerHTML":
|
|
517
574
|
case "element":
|
|
518
575
|
case "node":
|
|
519
|
-
// For these cases, return the outer HTML of the target element.
|
|
520
576
|
return targetElement.outerHTML;
|
|
521
577
|
default:
|
|
522
|
-
// By default, return the inner HTML of the target element.
|
|
523
578
|
return targetElement.innerHTML;
|
|
524
579
|
}
|
|
525
580
|
};
|
|
526
581
|
|
|
527
582
|
/**
|
|
528
|
-
*
|
|
529
|
-
* Converts to lowercase or uppercase if attributes are present and not "false".
|
|
530
|
-
*
|
|
531
|
-
* @param {HTMLElement} element - The DOM element to check for attributes.
|
|
532
|
-
* @param {string} value - The value to adjust based on the attributes.
|
|
533
|
-
* @returns {string} - The adjusted value.
|
|
583
|
+
* Adjusts casing transformations on attributes.
|
|
534
584
|
*/
|
|
535
585
|
function caseHandler(element, value) {
|
|
536
|
-
// Convert to lowercase if "value-lowercase" is set and not "false"
|
|
537
586
|
const lowercase = element.getAttribute("value-lowercase");
|
|
538
587
|
if (lowercase !== null && lowercase !== "false") {
|
|
539
588
|
value = value.toLowerCase();
|
|
540
589
|
}
|
|
541
590
|
|
|
542
|
-
// Convert to uppercase if "value-uppercase" is set and not "false"
|
|
543
591
|
const uppercase = element.getAttribute("value-uppercase");
|
|
544
592
|
if (uppercase !== null && uppercase !== "false") {
|
|
545
593
|
value = value.toUpperCase();
|
|
@@ -549,18 +597,11 @@ function caseHandler(element, value) {
|
|
|
549
597
|
}
|
|
550
598
|
|
|
551
599
|
/**
|
|
552
|
-
*
|
|
553
|
-
* Attributes dictate regex operations such as replace, match, split, etc.
|
|
554
|
-
*
|
|
555
|
-
* @param {HTMLElement} element - The DOM element whose attributes specify regex operations.
|
|
556
|
-
* @param {string} value - The value to be transformed based on regex operations.
|
|
557
|
-
* @returns {string} - The transformed value, or the original value if an error occurs.
|
|
600
|
+
* Transforms output based on Regular Expression definitions.
|
|
558
601
|
*/
|
|
559
602
|
function regex(element, value) {
|
|
560
603
|
try {
|
|
561
|
-
const attributes = element.attributes;
|
|
562
|
-
|
|
563
|
-
// Define a list of attributes that specify regex operations
|
|
604
|
+
const attributes = element.attributes;
|
|
564
605
|
const regexAttribute = [
|
|
565
606
|
"value-replace",
|
|
566
607
|
"value-replaceall",
|
|
@@ -572,30 +613,19 @@ function regex(element, value) {
|
|
|
572
613
|
"value-exec"
|
|
573
614
|
];
|
|
574
615
|
|
|
575
|
-
// Iterate over element attributes and process ones related to regex operations
|
|
576
616
|
for (let i = 0; i < attributes.length; i++) {
|
|
577
|
-
// Stop processing if value is null or undefined
|
|
578
617
|
if (value === null || value === undefined) break;
|
|
579
|
-
|
|
580
|
-
// Skip attributes that do not relate to regex operations
|
|
581
618
|
if (!regexAttribute.includes(attributes[i].name)) continue;
|
|
582
619
|
|
|
583
|
-
let regexAttributeValue = attributes[i].value;
|
|
584
|
-
|
|
585
|
-
// Skip processing for empty regex attributes
|
|
620
|
+
let regexAttributeValue = attributes[i].value;
|
|
586
621
|
if (!regexAttributeValue) continue;
|
|
587
622
|
|
|
588
|
-
// Parse the regex pattern and replacement from the attribute value
|
|
589
623
|
let { regex, replacement } = regexParser(regexAttributeValue);
|
|
590
|
-
|
|
591
|
-
// Use parsed regex if available
|
|
592
624
|
if (regex) regexAttributeValue = regex;
|
|
593
625
|
|
|
594
|
-
// Default to an empty string for replacement if not specified
|
|
595
626
|
replacement =
|
|
596
627
|
replacement || element.getAttribute("value-replacement") || "";
|
|
597
628
|
|
|
598
|
-
// Execute the determined regex operation
|
|
599
629
|
switch (attributes[i].name) {
|
|
600
630
|
case "value-replace":
|
|
601
631
|
value = value.replace(regexAttributeValue, replacement);
|
|
@@ -606,143 +636,107 @@ function regex(element, value) {
|
|
|
606
636
|
case "value-test":
|
|
607
637
|
value = regex.test(value);
|
|
608
638
|
break;
|
|
609
|
-
case "value-match":
|
|
639
|
+
case "value-match": {
|
|
610
640
|
const matches = value.match(regexAttributeValue);
|
|
611
641
|
if (matches) {
|
|
612
|
-
value = matches[1] || matches[0];
|
|
642
|
+
value = matches[1] || matches[0];
|
|
613
643
|
}
|
|
614
644
|
break;
|
|
645
|
+
}
|
|
615
646
|
case "value-split":
|
|
616
647
|
value = value.split(regexAttributeValue);
|
|
617
648
|
break;
|
|
618
649
|
case "value-lastindex":
|
|
619
|
-
regex.lastIndex = 0;
|
|
650
|
+
regex.lastIndex = 0;
|
|
620
651
|
regex.test(value);
|
|
621
652
|
value = regex.lastIndex;
|
|
622
653
|
break;
|
|
623
654
|
case "value-search":
|
|
624
655
|
value = value.search(regexAttributeValue);
|
|
625
656
|
break;
|
|
626
|
-
case "value-exec":
|
|
657
|
+
case "value-exec": {
|
|
627
658
|
const execResult = regex.exec(value);
|
|
628
659
|
if (execResult) {
|
|
629
|
-
value = execResult[1] || execResult[2] || execResult[0];
|
|
660
|
+
value = execResult[1] || execResult[2] || execResult[0];
|
|
630
661
|
}
|
|
631
662
|
break;
|
|
632
|
-
|
|
633
|
-
// Unknown attribute, ignored
|
|
634
|
-
break;
|
|
663
|
+
}
|
|
635
664
|
}
|
|
636
665
|
}
|
|
637
666
|
|
|
638
|
-
// Return the transformed value
|
|
639
667
|
return value;
|
|
640
668
|
} catch (error) {
|
|
641
|
-
// Log a warning since the transformation error is non-critical, but should be noted
|
|
642
669
|
console.warn(
|
|
643
670
|
"Warning: Transformation error during regex operation:",
|
|
644
671
|
error,
|
|
645
672
|
element
|
|
646
673
|
);
|
|
647
|
-
return value;
|
|
674
|
+
return value;
|
|
648
675
|
}
|
|
649
676
|
}
|
|
650
677
|
|
|
651
|
-
/**
|
|
652
|
-
* Parses a string to extract a regular expression and a replacement string.
|
|
653
|
-
* Assumes the input string may contain a regex pattern in the form "/pattern/flags"
|
|
654
|
-
* and an optional replacement string separated by a comma.
|
|
655
|
-
*
|
|
656
|
-
* @param {string} string - The input string potentially containing a regex pattern and a replacement.
|
|
657
|
-
* @returns {Object} An object containing the `regex` (RegExp object) and `replacement` (string).
|
|
658
|
-
*/
|
|
659
678
|
function regexParser(string) {
|
|
660
679
|
let regex, replacement;
|
|
661
|
-
|
|
662
|
-
// Match a regex pattern defined as /pattern/flags within the input string.
|
|
663
|
-
// It captures the pattern and the flags (e.g., 'g' for global, 'i' for case-insensitive).
|
|
664
680
|
let regexMatch = string.match(/\/(.+)\/([gimuy]*)/);
|
|
665
681
|
|
|
666
|
-
// If a regex pattern is found in the string, proceed to create the RegExp object
|
|
667
682
|
if (regexMatch) {
|
|
668
|
-
// Create a new RegExp object using the captured pattern and flags
|
|
669
683
|
regex = new RegExp(regexMatch[1], regexMatch[2]);
|
|
670
|
-
|
|
671
|
-
// Split the input string on ", " to separate the pattern from a replacement string
|
|
672
684
|
const splitReplace = string.split(", ");
|
|
673
|
-
|
|
674
|
-
// Check if there's a replacement string provided. If so, trim the surrounding spaces.
|
|
675
685
|
replacement =
|
|
676
686
|
splitReplace.length > 1 ? splitReplace[1].slice(1, -1) : "";
|
|
677
687
|
}
|
|
678
688
|
|
|
679
|
-
// Return an object containing the parsed regex and replacement string
|
|
680
689
|
return { regex, replacement };
|
|
681
690
|
}
|
|
682
691
|
|
|
683
692
|
/**
|
|
684
|
-
* Encodes
|
|
685
|
-
*
|
|
686
|
-
* @param {string} value - The value to be encoded.
|
|
687
|
-
* @param {string} encodingType - The type of encoding to apply. Accepted values include "url", "uri-component", "base64", "html-entities", and "json".
|
|
688
|
-
* @returns {string} - The encoded value or the original value if encoding type is unsupported.
|
|
693
|
+
* Encodes calculated structures securely.
|
|
689
694
|
*/
|
|
690
695
|
function encodeValue(value, encodingType) {
|
|
691
696
|
if (!encodingType) {
|
|
692
697
|
return value;
|
|
693
698
|
}
|
|
694
699
|
|
|
695
|
-
// Determine the encoding method based on the case-insensitive encodingType
|
|
696
700
|
switch (encodingType.toLowerCase()) {
|
|
697
701
|
case "url":
|
|
698
702
|
case "uri":
|
|
699
|
-
// Encode spaces as "%20" explicitly and use encodeURI for overall URL encoding (excluding special characters like '?', '&', '/')
|
|
700
703
|
return encodeURI(value.replace(/ /g, "%20"));
|
|
701
704
|
|
|
702
705
|
case "uri-component":
|
|
703
|
-
// Encode spaces as "%20" explicitly and use encodeURIComponent for more thorough encoding, including special characters
|
|
704
706
|
return encodeURIComponent(value.replace(/ /g, "%20"));
|
|
705
707
|
|
|
706
708
|
case "base64":
|
|
707
709
|
case "atob":
|
|
708
710
|
try {
|
|
709
|
-
// Create a TextEncoder to convert the value into a Uint8Array, then encode it in base64
|
|
710
711
|
const encoder = new TextEncoder();
|
|
711
712
|
const uint8Array = encoder.encode(value);
|
|
712
713
|
return btoa(String.fromCharCode(...uint8Array));
|
|
713
714
|
} catch (error) {
|
|
714
715
|
console.warn(`Failed to encode as Base64: ${error.message}`);
|
|
715
|
-
return value;
|
|
716
|
+
return value;
|
|
716
717
|
}
|
|
717
718
|
|
|
718
719
|
case "html-entities":
|
|
719
|
-
// Replace specific HTML entities with their corresponding character codes using regex
|
|
720
720
|
return value.replace(/[\u00A0-\u9999<>\&]/g, (i) => {
|
|
721
721
|
return `&#${i.charCodeAt(0)};`;
|
|
722
722
|
});
|
|
723
723
|
|
|
724
724
|
case "json":
|
|
725
725
|
try {
|
|
726
|
-
// Convert the value to a JSON string representation
|
|
727
726
|
return JSON.stringify(value);
|
|
728
727
|
} catch (error) {
|
|
729
728
|
console.warn(`Failed to convert to JSON: ${error.message}`);
|
|
730
|
-
return value;
|
|
729
|
+
return value;
|
|
731
730
|
}
|
|
732
731
|
|
|
733
732
|
default:
|
|
734
|
-
// Log a warning for unsupported encoding types and return the original value
|
|
735
733
|
console.warn(`Unsupported encoding type: ${encodingType}`);
|
|
736
734
|
return value;
|
|
737
735
|
}
|
|
738
736
|
}
|
|
739
737
|
|
|
740
738
|
/**
|
|
741
|
-
* Decodes
|
|
742
|
-
*
|
|
743
|
-
* @param {string} value - The value to be decoded.
|
|
744
|
-
* @param {string} decodingType - The type of decoding to apply. Accepted values include "url", "uri-component", "base64", "html-entities", and "json".
|
|
745
|
-
* @returns {string} - The decoded value or the original value if decoding failed.
|
|
739
|
+
* Decodes calculated structures securely.
|
|
746
740
|
*/
|
|
747
741
|
function decodeValue(value, decodingType) {
|
|
748
742
|
if (!decodingType) {
|
|
@@ -758,7 +752,7 @@ function decodeValue(value, decodingType) {
|
|
|
758
752
|
return decodeURIComponent(value);
|
|
759
753
|
|
|
760
754
|
case "base64":
|
|
761
|
-
case "btoa":
|
|
755
|
+
case "btoa":
|
|
762
756
|
try {
|
|
763
757
|
const decodedArray = Uint8Array.from(atob(value), (c) =>
|
|
764
758
|
c.charCodeAt(0)
|
|
@@ -767,99 +761,74 @@ function decodeValue(value, decodingType) {
|
|
|
767
761
|
return decoder.decode(decodedArray);
|
|
768
762
|
} catch (error) {
|
|
769
763
|
console.warn(`Failed to decode Base64: ${error.message}`);
|
|
770
|
-
return value;
|
|
764
|
+
return value;
|
|
771
765
|
}
|
|
772
766
|
|
|
773
|
-
case "html-entities":
|
|
767
|
+
case "html-entities": {
|
|
774
768
|
const tempElement = document.createElement("div");
|
|
775
769
|
tempElement.innerHTML = value;
|
|
776
770
|
return tempElement.textContent;
|
|
771
|
+
}
|
|
777
772
|
|
|
778
773
|
case "json":
|
|
779
774
|
try {
|
|
780
775
|
return JSON.parse(value);
|
|
781
776
|
} catch (error) {
|
|
782
777
|
console.warn(`Failed to parse JSON: ${error.message}`);
|
|
783
|
-
return value;
|
|
778
|
+
return value;
|
|
784
779
|
}
|
|
785
780
|
|
|
786
781
|
default:
|
|
787
782
|
console.warn(`Unsupported decoding type: ${decodingType}`);
|
|
788
|
-
return value;
|
|
783
|
+
return value;
|
|
789
784
|
}
|
|
790
785
|
}
|
|
791
786
|
|
|
792
|
-
/**
|
|
793
|
-
* Parses a string into a JSON object if the provided valueType suggests it might be JSON.
|
|
794
|
-
* Handles objects, arrays, or types starting with 'array'. If parsing fails,
|
|
795
|
-
* attempts to extract a JSON structure using regex and tries again.
|
|
796
|
-
*
|
|
797
|
-
* @param {string} value - The string value that might contain JSON data.
|
|
798
|
-
* @param {string} valueType - A string indicating the expected type of the value (e.g., "object", "json", "array").
|
|
799
|
-
* @returns {any} The parsed JSON object/array or the original value if parsing fails.
|
|
800
|
-
*/
|
|
801
787
|
function parseJson(value, valueType) {
|
|
802
|
-
// Check if the value is present and if the valueType suggests it could be a JSON structure
|
|
803
788
|
if (
|
|
804
|
-
value &&
|
|
805
|
-
(valueType === "object" ||
|
|
806
|
-
valueType === "json" ||
|
|
807
|
-
valueType.startsWith("array"))
|
|
789
|
+
value &&
|
|
790
|
+
(valueType === "object" ||
|
|
791
|
+
valueType === "json" ||
|
|
792
|
+
valueType.startsWith("array"))
|
|
808
793
|
) {
|
|
809
794
|
try {
|
|
810
|
-
// Attempt to parse the value directly as JSON
|
|
811
795
|
value = JSON.parse(value);
|
|
812
796
|
} catch {
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
const match = value.match(jsonRegex); // Search the value for JSON-like patterns
|
|
797
|
+
const jsonRegex = /(\{[\s\S]*}|\[[\s\S]*\])/;
|
|
798
|
+
const match = value.match(jsonRegex);
|
|
816
799
|
|
|
817
800
|
if (match) {
|
|
818
801
|
try {
|
|
819
|
-
// If a pattern is found, attempt to parse the extracted potential JSON
|
|
820
802
|
value = JSON.parse(match[0]);
|
|
821
803
|
} catch {
|
|
822
|
-
// Warn if parsing still fails after extracting potential JSON
|
|
823
804
|
console.warn(
|
|
824
805
|
"Warning: Failed to parse JSON after extraction. Returning original value."
|
|
825
806
|
);
|
|
826
807
|
}
|
|
827
808
|
} else {
|
|
828
|
-
// Warn if no valid JSON structure is found in the string
|
|
829
809
|
console.warn(
|
|
830
810
|
"Warning: No valid JSON structure found in the string. Returning original value."
|
|
831
811
|
);
|
|
832
812
|
}
|
|
833
813
|
}
|
|
834
814
|
}
|
|
835
|
-
return value;
|
|
815
|
+
return value;
|
|
836
816
|
}
|
|
837
817
|
|
|
838
|
-
/**
|
|
839
|
-
* Processes a value by converting it to an array or extracting specific array-related elements.
|
|
840
|
-
* @param {any} value - The value to be processed, potentially an object or primitive.
|
|
841
|
-
* @param {string} valueType - A string indicating the type of conversion or extraction to perform.
|
|
842
|
-
* @returns {Array} - The processed value as an array, or a transformed array-like structure.
|
|
843
|
-
*/
|
|
844
818
|
function toArray(value, valueType) {
|
|
845
|
-
// Now handle array-specific logic if valueType starts with 'array'
|
|
846
819
|
if (valueType.startsWith("array")) {
|
|
847
|
-
// If the value isn't already an array, convert it accordingly
|
|
848
820
|
if (!Array.isArray(value)) {
|
|
849
|
-
|
|
850
|
-
if (typeof value === "object") {
|
|
821
|
+
if (typeof value === "object" && value !== null) {
|
|
851
822
|
if (valueType === "array.$keys") {
|
|
852
|
-
value = Object.keys(value);
|
|
823
|
+
value = Object.keys(value);
|
|
853
824
|
} else if (valueType === "array.$values") {
|
|
854
|
-
value = Object.values(value);
|
|
825
|
+
value = Object.values(value);
|
|
855
826
|
} else if (valueType === "array.$entries") {
|
|
856
|
-
value = Object.entries(value);
|
|
827
|
+
value = Object.entries(value);
|
|
857
828
|
} else {
|
|
858
|
-
// Default behavior: wrap the object in an array
|
|
859
829
|
value = [value];
|
|
860
830
|
}
|
|
861
831
|
} else {
|
|
862
|
-
// If it's not an object (i.e., a primitive), wrap the value in an array
|
|
863
832
|
value = [value];
|
|
864
833
|
}
|
|
865
834
|
}
|
|
@@ -867,4 +836,4 @@ function toArray(value, valueType) {
|
|
|
867
836
|
return value;
|
|
868
837
|
}
|
|
869
838
|
|
|
870
|
-
export { getValue, storage };
|
|
839
|
+
export { getValue, getValueAsync, storage };
|
package/src/queryElements.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { queryElements as baseQueryElements } from "@cocreate/utils";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Custom method to query elements in the context of a specific DOM element.
|
|
5
5
|
*
|
|
6
|
-
* @
|
|
7
|
-
* @
|
|
6
|
+
* @this {Element} - The DOM element context.
|
|
7
|
+
* @param {Object} [options={}] - Options to customize the query.
|
|
8
|
+
* @returns {NodeListOf<Element>} A list of elements found based on the query criteria.
|
|
8
9
|
*/
|
|
9
|
-
function queryElements(options = {}) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export function queryElements(options = {}) {
|
|
11
|
+
// Ensure 'this' is a valid Element when used as a prototype method
|
|
12
|
+
if (!(this instanceof Element)) {
|
|
13
|
+
throw new TypeError("queryElements must be called on an Element instance");
|
|
14
|
+
}
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
return baseQueryElements({ element: this, ...options });
|
|
17
|
+
}
|
|
16
18
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
+
// Attach the method to Element's prototype for instance method usage
|
|
20
|
+
if (!Element.prototype.queryElements) {
|
|
21
|
+
Element.prototype.queryElements = queryElements;
|
|
22
|
+
}
|