@cocreate/element-prototype 1.34.0 → 1.38.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 +22 -20
- package/CONTRIBUTING.md +1 -1
- package/README.md +58 -0
- package/package.json +5 -5
- package/src/getValue.js +305 -550
- package/src/setValue.js +283 -252
- package/test/password-value.test.cjs +48 -0
- package/test/value-handler.test.cjs +275 -0
package/src/getValue.js
CHANGED
|
@@ -17,21 +17,22 @@
|
|
|
17
17
|
********************************************************************************/
|
|
18
18
|
|
|
19
19
|
import { astSync, astAsync } from "@cocreate/utils";
|
|
20
|
+
import { getValueAttribute, transformValue, parseJson, toBoolean, toArray } from "@cocreate/utils";
|
|
20
21
|
|
|
21
22
|
const storage = new Map();
|
|
22
23
|
|
|
23
24
|
HTMLElement.prototype.getValue = function () {
|
|
24
|
-
|
|
25
|
+
return getValue(this);
|
|
25
26
|
};
|
|
26
27
|
SVGElement.prototype.getValue = function () {
|
|
27
|
-
|
|
28
|
+
return getValue(this);
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
HTMLElement.prototype.getValueAsync = async function () {
|
|
31
|
-
|
|
32
|
+
return await getValueAsync(this);
|
|
32
33
|
};
|
|
33
34
|
SVGElement.prototype.getValueAsync = async function () {
|
|
34
|
-
|
|
35
|
+
return await getValueAsync(this);
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -42,121 +43,128 @@ SVGElement.prototype.getValueAsync = async function () {
|
|
|
42
43
|
* @returns {any} - The processed value.
|
|
43
44
|
*/
|
|
44
45
|
const getValue = (element, valueType) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
46
|
+
if (!valueType) {
|
|
47
|
+
valueType = getValueAttribute(element, "type", "get-value") || "";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const evaluateValue = getValueAttribute(element, "evaluate", "get-value", true) !== "false";
|
|
51
|
+
const readValueAttribute = () => evaluateValue
|
|
52
|
+
? element.getAttribute("value")
|
|
53
|
+
: element.attributes.getNamedItem("value")?.value;
|
|
54
|
+
let value = element.value || readValueAttribute() || "";
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
element.hasAttribute("component") ||
|
|
58
|
+
element.type === "file" ||
|
|
59
|
+
element.getAttribute("type") === "file"
|
|
60
|
+
) {
|
|
61
|
+
value = storage.get(element);
|
|
62
|
+
// storage.delete(element);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let prefix = getValueAttribute(element, "prefix", "get-value") || "";
|
|
67
|
+
let suffix = getValueAttribute(element, "suffix", "get-value") || "";
|
|
68
|
+
|
|
69
|
+
const elementType = element.getAttribute("type") || element.type;
|
|
70
|
+
const tagName = element.tagName.toUpperCase();
|
|
71
|
+
|
|
72
|
+
switch (elementType) {
|
|
73
|
+
case "checkbox":
|
|
74
|
+
value = handleCheckbox(element, prefix, suffix, valueType);
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case "radio": {
|
|
78
|
+
const key = element.getAttribute("key");
|
|
79
|
+
let el = document.querySelector(`input[key="${key}"]:checked`);
|
|
80
|
+
if (el) {
|
|
81
|
+
value = el.value;
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case "number":
|
|
87
|
+
value = value === "" ? "" : Number(value);
|
|
88
|
+
break;
|
|
89
|
+
case "range":
|
|
90
|
+
value = element.min
|
|
91
|
+
? [Number(element.min), Number(element.value)]
|
|
92
|
+
: Number(element.value);
|
|
93
|
+
break;
|
|
94
|
+
|
|
95
|
+
case "password":
|
|
96
|
+
value = btoa(value || "");
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case "email":
|
|
100
|
+
value = value.toLowerCase();
|
|
101
|
+
break;
|
|
102
|
+
|
|
103
|
+
case "url":
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case "time":
|
|
107
|
+
case "date":
|
|
108
|
+
case "datetime":
|
|
109
|
+
case "datetime-local":
|
|
110
|
+
value = handleDateTime(element, value, valueType);
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
default:
|
|
114
|
+
switch (tagName) {
|
|
115
|
+
case "INPUT":
|
|
116
|
+
case "OPTION":
|
|
117
|
+
value = element.value;
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
case "SELECT":
|
|
121
|
+
value = handleSelectedValues(element,
|
|
122
|
+
Array.from(element.selectedOptions, option => getValue(option)),
|
|
123
|
+
prefix, suffix);
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case "TEXTAREA":
|
|
127
|
+
value = element.hasAttribute("value")
|
|
128
|
+
? readValueAttribute()
|
|
129
|
+
: element.value;
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case "IFRAME":
|
|
133
|
+
value = element.srcdoc;
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case "IMG":
|
|
137
|
+
value = element.src;
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
default:
|
|
141
|
+
value = handleElement(element, valueType, evaluateValue);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (valueType === "boolean") {
|
|
148
|
+
return toBoolean(value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (value) {
|
|
152
|
+
if (evaluateValue) {
|
|
153
|
+
value = astSync({ executeAST: value, node: element, exclude: ["$value"] });
|
|
154
|
+
}
|
|
155
|
+
value = transformValue(element, value, "get-value");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
159
|
+
if (prefix || suffix) {
|
|
160
|
+
value = prefix + value + suffix;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
value = parseJson(value, valueType);
|
|
165
|
+
value = toArray(value, valueType);
|
|
166
|
+
|
|
167
|
+
return value;
|
|
160
168
|
};
|
|
161
169
|
|
|
162
170
|
/**
|
|
@@ -167,151 +175,158 @@ const getValue = (element, valueType) => {
|
|
|
167
175
|
* @returns {Promise<any>} - A promise that resolves to the processed value.
|
|
168
176
|
*/
|
|
169
177
|
const getValueAsync = async (element, valueType) => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
178
|
+
if (!valueType) {
|
|
179
|
+
valueType = getValueAttribute(element, "type", "get-value") || "";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const evaluateValue = getValueAttribute(element, "evaluate", "get-value", true) !== "false";
|
|
183
|
+
const readValueAttribute = () => evaluateValue
|
|
184
|
+
? element.getAttribute("value")
|
|
185
|
+
: element.attributes.getNamedItem("value")?.value;
|
|
186
|
+
let value = element.value || readValueAttribute() || "";
|
|
187
|
+
|
|
188
|
+
if (
|
|
189
|
+
element.hasAttribute("component") ||
|
|
190
|
+
element.type === "file" ||
|
|
191
|
+
element.getAttribute("type") === "file"
|
|
192
|
+
) {
|
|
193
|
+
value = storage.get(element);
|
|
194
|
+
storage.delete(element);
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let prefix = getValueAttribute(element, "prefix", "get-value") || "";
|
|
199
|
+
let suffix = getValueAttribute(element, "suffix", "get-value") || "";
|
|
200
|
+
|
|
201
|
+
const elementType = element.getAttribute("type") || element.type;
|
|
202
|
+
const tagName = element.tagName.toUpperCase();
|
|
203
|
+
|
|
204
|
+
switch (elementType) {
|
|
205
|
+
case "checkbox":
|
|
206
|
+
value = handleCheckbox(element, prefix, suffix, valueType);
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case "radio": {
|
|
210
|
+
const key = element.getAttribute("key");
|
|
211
|
+
let el = document.querySelector(`input[key="${key}"]:checked`);
|
|
212
|
+
if (el) {
|
|
213
|
+
value = el.value;
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
case "number":
|
|
219
|
+
value = Number(value);
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case "range":
|
|
223
|
+
value = element.min
|
|
224
|
+
? [Number(element.min), Number(element.value)]
|
|
225
|
+
: Number(element.value);
|
|
226
|
+
break;
|
|
227
|
+
|
|
228
|
+
case "password":
|
|
229
|
+
value = btoa(value || "");
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
case "email":
|
|
233
|
+
value = value.toLowerCase();
|
|
234
|
+
break;
|
|
235
|
+
|
|
236
|
+
case "url":
|
|
237
|
+
break;
|
|
238
|
+
|
|
239
|
+
case "time":
|
|
240
|
+
case "date":
|
|
241
|
+
case "datetime":
|
|
242
|
+
case "datetime-local":
|
|
243
|
+
value = handleDateTime(element, value, valueType);
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
default:
|
|
247
|
+
switch (tagName) {
|
|
248
|
+
case "INPUT":
|
|
249
|
+
case "OPTION":
|
|
250
|
+
value = element.value;
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case "SELECT":
|
|
254
|
+
value = handleSelectedValues(element,
|
|
255
|
+
await Promise.all(Array.from(element.selectedOptions, option => getValueAsync(option))),
|
|
256
|
+
prefix, suffix);
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
case "TEXTAREA":
|
|
260
|
+
value = element.hasAttribute("value")
|
|
261
|
+
? readValueAttribute()
|
|
262
|
+
: element.value;
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
case "IFRAME":
|
|
266
|
+
value = element.srcdoc;
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
default:
|
|
270
|
+
value = handleElement(element, valueType, evaluateValue);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (valueType === "boolean") {
|
|
277
|
+
return toBoolean(value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (value) {
|
|
281
|
+
if (evaluateValue) {
|
|
282
|
+
value = await astAsync({ executeAST: value, node: element, exclude: ["$value"] });
|
|
283
|
+
}
|
|
284
|
+
value = transformValue(element, value, "get-value");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
288
|
+
if (prefix || suffix) {
|
|
289
|
+
value = prefix + value + suffix;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
value = parseJson(value, valueType);
|
|
294
|
+
value = toArray(value, valueType);
|
|
295
|
+
|
|
296
|
+
return value;
|
|
285
297
|
};
|
|
286
298
|
|
|
287
299
|
/**
|
|
288
300
|
* Processes a checkbox or a group of checkboxes to determine their checked values.
|
|
289
301
|
*/
|
|
290
|
-
const handleCheckbox = (element, prefix = "", suffix = "") => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
302
|
+
const handleCheckbox = (element, prefix = "", suffix = "", valueType = "") => {
|
|
303
|
+
const inputs = element.getAttribute("key")
|
|
304
|
+
? document.querySelectorAll(
|
|
305
|
+
`input[type="${element.type}"][key="${element.getAttribute(
|
|
306
|
+
"key"
|
|
307
|
+
)}"]`
|
|
308
|
+
)
|
|
309
|
+
: [element];
|
|
310
|
+
|
|
311
|
+
if (inputs.length === 1) {
|
|
312
|
+
if (valueType.startsWith("array"))
|
|
313
|
+
return element.checked ? [element.value || true] : [];
|
|
314
|
+
return element.checked ? element.value || true : false;
|
|
315
|
+
} else {
|
|
316
|
+
return Array.from(inputs)
|
|
317
|
+
.filter((el) => el.checked)
|
|
318
|
+
.map((el) => `${prefix}${el.value}${suffix}`);
|
|
319
|
+
}
|
|
306
320
|
};
|
|
307
321
|
|
|
308
322
|
/**
|
|
309
323
|
* Handles and transforms a date/time value based on the specified valueType.
|
|
310
324
|
*/
|
|
311
325
|
const handleDateTime = (element, value, valueType) => {
|
|
326
|
+
if ((value === "" || value === null || value === undefined) && !element.hasAttribute("now")) return "";
|
|
312
327
|
const inputType = (element.getAttribute("type") || element.type || "").toLowerCase();
|
|
313
328
|
let date;
|
|
314
|
-
|
|
329
|
+
|
|
315
330
|
if (value === "$now" || element.hasAttribute("now")) {
|
|
316
331
|
date = new Date();
|
|
317
332
|
} else if (value instanceof Date) {
|
|
@@ -464,7 +479,7 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
464
479
|
case "timeAgo": {
|
|
465
480
|
const diffMs = Date.now() - date.getTime();
|
|
466
481
|
const absSecs = Math.floor(Math.abs(diffMs) / 1000);
|
|
467
|
-
|
|
482
|
+
|
|
468
483
|
const limitAttr = element.getAttribute("time-ago-limit");
|
|
469
484
|
if (limitAttr) {
|
|
470
485
|
const match = limitAttr.match(/^(\d+)([a-z]+)$/i);
|
|
@@ -472,7 +487,7 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
472
487
|
const amount = parseInt(match[1], 10);
|
|
473
488
|
const limitUnit = match[2].toLowerCase();
|
|
474
489
|
let multiplier = 0;
|
|
475
|
-
|
|
490
|
+
|
|
476
491
|
switch (limitUnit) {
|
|
477
492
|
case 's': multiplier = 1; break;
|
|
478
493
|
case 'm': multiplier = 60; break;
|
|
@@ -482,9 +497,9 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
482
497
|
case 'mo': multiplier = 2592000; break;
|
|
483
498
|
case 'y': multiplier = 31536000; break;
|
|
484
499
|
}
|
|
485
|
-
|
|
500
|
+
|
|
486
501
|
const limitInSeconds = amount * multiplier;
|
|
487
|
-
|
|
502
|
+
|
|
488
503
|
if (absSecs > limitInSeconds) {
|
|
489
504
|
const locale = element.getAttribute("locale") || "en-US";
|
|
490
505
|
return date.toLocaleString(locale);
|
|
@@ -532,308 +547,48 @@ const handleDateTime = (element, value, valueType) => {
|
|
|
532
547
|
};
|
|
533
548
|
|
|
534
549
|
/**
|
|
535
|
-
*
|
|
550
|
+
* Collects processed option values, retaining select-level multi-value affixes.
|
|
536
551
|
*/
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
let optionValue = options[i].value;
|
|
543
|
-
if (prefix || suffix) {
|
|
544
|
-
optionValue = prefix + optionValue + suffix;
|
|
545
|
-
}
|
|
546
|
-
value.push(optionValue);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return value;
|
|
552
|
+
const handleSelectedValues = (element, values, prefix, suffix) => {
|
|
553
|
+
if (!element.hasAttribute("multiple")) return values.length ? values[0] : "";
|
|
554
|
+
return prefix || suffix
|
|
555
|
+
? values.map(value => prefix + value + suffix)
|
|
556
|
+
: values;
|
|
550
557
|
};
|
|
551
558
|
|
|
552
559
|
/**
|
|
553
560
|
* Retrieves a value from a standard element.
|
|
554
561
|
*/
|
|
555
|
-
const handleElement = (element, valueType) => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const excludeSelector = element.getAttribute("value-remove-query");
|
|
562
|
-
|
|
563
|
-
if (excludeSelector) {
|
|
564
|
-
targetElement = element.cloneNode(true);
|
|
565
|
-
targetElement
|
|
566
|
-
.querySelectorAll(excludeSelector)
|
|
567
|
-
.forEach((el) => el.remove());
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
switch (valueType) {
|
|
571
|
-
case "text":
|
|
572
|
-
return targetElement.innerText;
|
|
573
|
-
case "outerHTML":
|
|
574
|
-
case "element":
|
|
575
|
-
case "node":
|
|
576
|
-
return targetElement.outerHTML;
|
|
577
|
-
default:
|
|
578
|
-
return targetElement.innerHTML;
|
|
579
|
-
}
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
/**
|
|
583
|
-
* Adjusts casing transformations on attributes.
|
|
584
|
-
*/
|
|
585
|
-
function caseHandler(element, value) {
|
|
586
|
-
const lowercase = element.getAttribute("value-lowercase");
|
|
587
|
-
if (lowercase !== null && lowercase !== "false") {
|
|
588
|
-
value = value.toLowerCase();
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
const uppercase = element.getAttribute("value-uppercase");
|
|
592
|
-
if (uppercase !== null && uppercase !== "false") {
|
|
593
|
-
value = value.toUpperCase();
|
|
594
|
-
}
|
|
562
|
+
const handleElement = (element, valueType, evaluateValue) => {
|
|
563
|
+
if (element.hasAttribute("value")) {
|
|
564
|
+
return evaluateValue
|
|
565
|
+
? element.getAttribute("value")
|
|
566
|
+
: element.attributes.getNamedItem("value")?.value;
|
|
567
|
+
}
|
|
595
568
|
|
|
596
|
-
|
|
597
|
-
|
|
569
|
+
let targetElement = element;
|
|
570
|
+
const excludeSelector = getValueAttribute(element, "remove-query", "get-value");
|
|
598
571
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const regexAttribute = [
|
|
606
|
-
"value-replace",
|
|
607
|
-
"value-replaceall",
|
|
608
|
-
"value-test",
|
|
609
|
-
"value-match",
|
|
610
|
-
"value-split",
|
|
611
|
-
"value-lastindex",
|
|
612
|
-
"value-search",
|
|
613
|
-
"value-exec"
|
|
614
|
-
];
|
|
615
|
-
|
|
616
|
-
for (let i = 0; i < attributes.length; i++) {
|
|
617
|
-
if (value === null || value === undefined) break;
|
|
618
|
-
if (!regexAttribute.includes(attributes[i].name)) continue;
|
|
619
|
-
|
|
620
|
-
let regexAttributeValue = attributes[i].value;
|
|
621
|
-
if (!regexAttributeValue) continue;
|
|
622
|
-
|
|
623
|
-
let { regex, replacement } = regexParser(regexAttributeValue);
|
|
624
|
-
if (regex) regexAttributeValue = regex;
|
|
625
|
-
|
|
626
|
-
replacement =
|
|
627
|
-
replacement || element.getAttribute("value-replacement") || "";
|
|
628
|
-
|
|
629
|
-
switch (attributes[i].name) {
|
|
630
|
-
case "value-replace":
|
|
631
|
-
value = value.replace(regexAttributeValue, replacement);
|
|
632
|
-
break;
|
|
633
|
-
case "value-replaceall":
|
|
634
|
-
value = value.replaceAll(regexAttributeValue, replacement);
|
|
635
|
-
break;
|
|
636
|
-
case "value-test":
|
|
637
|
-
value = regex.test(value);
|
|
638
|
-
break;
|
|
639
|
-
case "value-match": {
|
|
640
|
-
const matches = value.match(regexAttributeValue);
|
|
641
|
-
if (matches) {
|
|
642
|
-
value = matches[1] || matches[0];
|
|
643
|
-
}
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
case "value-split":
|
|
647
|
-
value = value.split(regexAttributeValue);
|
|
648
|
-
break;
|
|
649
|
-
case "value-lastindex":
|
|
650
|
-
regex.lastIndex = 0;
|
|
651
|
-
regex.test(value);
|
|
652
|
-
value = regex.lastIndex;
|
|
653
|
-
break;
|
|
654
|
-
case "value-search":
|
|
655
|
-
value = value.search(regexAttributeValue);
|
|
656
|
-
break;
|
|
657
|
-
case "value-exec": {
|
|
658
|
-
const execResult = regex.exec(value);
|
|
659
|
-
if (execResult) {
|
|
660
|
-
value = execResult[1] || execResult[2] || execResult[0];
|
|
661
|
-
}
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
return value;
|
|
668
|
-
} catch (error) {
|
|
669
|
-
console.warn(
|
|
670
|
-
"Warning: Transformation error during regex operation:",
|
|
671
|
-
error,
|
|
672
|
-
element
|
|
673
|
-
);
|
|
674
|
-
return value;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function regexParser(string) {
|
|
679
|
-
let regex, replacement;
|
|
680
|
-
let regexMatch = string.match(/\/(.+)\/([gimuy]*)/);
|
|
681
|
-
|
|
682
|
-
if (regexMatch) {
|
|
683
|
-
regex = new RegExp(regexMatch[1], regexMatch[2]);
|
|
684
|
-
const splitReplace = string.split(", ");
|
|
685
|
-
replacement =
|
|
686
|
-
splitReplace.length > 1 ? splitReplace[1].slice(1, -1) : "";
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return { regex, replacement };
|
|
690
|
-
}
|
|
572
|
+
if (excludeSelector) {
|
|
573
|
+
targetElement = element.cloneNode(true);
|
|
574
|
+
targetElement
|
|
575
|
+
.querySelectorAll(excludeSelector)
|
|
576
|
+
.forEach((el) => el.remove());
|
|
577
|
+
}
|
|
691
578
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return encodeURIComponent(value.replace(/ /g, "%20"));
|
|
707
|
-
|
|
708
|
-
case "base64":
|
|
709
|
-
case "atob":
|
|
710
|
-
try {
|
|
711
|
-
const encoder = new TextEncoder();
|
|
712
|
-
const uint8Array = encoder.encode(value);
|
|
713
|
-
return btoa(String.fromCharCode(...uint8Array));
|
|
714
|
-
} catch (error) {
|
|
715
|
-
console.warn(`Failed to encode as Base64: ${error.message}`);
|
|
716
|
-
return value;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
case "html-entities":
|
|
720
|
-
return value.replace(/[\u00A0-\u9999<>\&]/g, (i) => {
|
|
721
|
-
return `&#${i.charCodeAt(0)};`;
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
case "json":
|
|
725
|
-
try {
|
|
726
|
-
return JSON.stringify(value);
|
|
727
|
-
} catch (error) {
|
|
728
|
-
console.warn(`Failed to convert to JSON: ${error.message}`);
|
|
729
|
-
return value;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
default:
|
|
733
|
-
console.warn(`Unsupported encoding type: ${encodingType}`);
|
|
734
|
-
return value;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
579
|
+
switch (valueType) {
|
|
580
|
+
case "text":
|
|
581
|
+
case "innerText":
|
|
582
|
+
return targetElement.innerText;
|
|
583
|
+
case "textContent":
|
|
584
|
+
return targetElement.textContent;
|
|
585
|
+
case "outerHTML":
|
|
586
|
+
case "element":
|
|
587
|
+
case "node":
|
|
588
|
+
return targetElement.outerHTML;
|
|
589
|
+
default:
|
|
590
|
+
return targetElement.innerHTML;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
737
593
|
|
|
738
|
-
|
|
739
|
-
* Decodes calculated structures securely.
|
|
740
|
-
*/
|
|
741
|
-
function decodeValue(value, decodingType) {
|
|
742
|
-
if (!decodingType) {
|
|
743
|
-
return value;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
switch (decodingType.toLowerCase()) {
|
|
747
|
-
case "url":
|
|
748
|
-
case "uri":
|
|
749
|
-
return decodeURI(value);
|
|
750
|
-
|
|
751
|
-
case "uri-component":
|
|
752
|
-
return decodeURIComponent(value);
|
|
753
|
-
|
|
754
|
-
case "base64":
|
|
755
|
-
case "btoa":
|
|
756
|
-
try {
|
|
757
|
-
const decodedArray = Uint8Array.from(atob(value), (c) =>
|
|
758
|
-
c.charCodeAt(0)
|
|
759
|
-
);
|
|
760
|
-
const decoder = new TextDecoder();
|
|
761
|
-
return decoder.decode(decodedArray);
|
|
762
|
-
} catch (error) {
|
|
763
|
-
console.warn(`Failed to decode Base64: ${error.message}`);
|
|
764
|
-
return value;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
case "html-entities": {
|
|
768
|
-
const tempElement = document.createElement("div");
|
|
769
|
-
tempElement.innerHTML = value;
|
|
770
|
-
return tempElement.textContent;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
case "json":
|
|
774
|
-
try {
|
|
775
|
-
return JSON.parse(value);
|
|
776
|
-
} catch (error) {
|
|
777
|
-
console.warn(`Failed to parse JSON: ${error.message}`);
|
|
778
|
-
return value;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
default:
|
|
782
|
-
console.warn(`Unsupported decoding type: ${decodingType}`);
|
|
783
|
-
return value;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
function parseJson(value, valueType) {
|
|
788
|
-
if (
|
|
789
|
-
value &&
|
|
790
|
-
(valueType === "object" ||
|
|
791
|
-
valueType === "json" ||
|
|
792
|
-
valueType.startsWith("array"))
|
|
793
|
-
) {
|
|
794
|
-
try {
|
|
795
|
-
value = JSON.parse(value);
|
|
796
|
-
} catch {
|
|
797
|
-
const jsonRegex = /(\{[\s\S]*}|\[[\s\S]*\])/;
|
|
798
|
-
const match = value.match(jsonRegex);
|
|
799
|
-
|
|
800
|
-
if (match) {
|
|
801
|
-
try {
|
|
802
|
-
value = JSON.parse(match[0]);
|
|
803
|
-
} catch {
|
|
804
|
-
console.warn(
|
|
805
|
-
"Warning: Failed to parse JSON after extraction. Returning original value."
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
} else {
|
|
809
|
-
console.warn(
|
|
810
|
-
"Warning: No valid JSON structure found in the string. Returning original value."
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
return value;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
function toArray(value, valueType) {
|
|
819
|
-
if (valueType.startsWith("array")) {
|
|
820
|
-
if (!Array.isArray(value)) {
|
|
821
|
-
if (typeof value === "object" && value !== null) {
|
|
822
|
-
if (valueType === "array.$keys") {
|
|
823
|
-
value = Object.keys(value);
|
|
824
|
-
} else if (valueType === "array.$values") {
|
|
825
|
-
value = Object.values(value);
|
|
826
|
-
} else if (valueType === "array.$entries") {
|
|
827
|
-
value = Object.entries(value);
|
|
828
|
-
} else {
|
|
829
|
-
value = [value];
|
|
830
|
-
}
|
|
831
|
-
} else {
|
|
832
|
-
value = [value];
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
return value;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
export { getValue, getValueAsync, storage };
|
|
594
|
+
export { getValue, getValueAsync, storage };
|