@oscarpalmer/toretto 0.41.0 → 0.43.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/dist/attribute/{get.d.mts → get.attribute.d.mts} +3 -2
- package/dist/attribute/{get.mjs → get.attribute.mjs} +4 -3
- package/dist/attribute/index.d.mts +3 -16
- package/dist/attribute/index.mjs +4 -7
- package/dist/attribute/{set.d.mts → set.attribute.d.mts} +4 -4
- package/dist/attribute/{set.mjs → set.attribute.mjs} +2 -2
- package/dist/create.d.mts +25 -0
- package/dist/create.mjs +17 -0
- package/dist/data.mjs +7 -7
- package/dist/event/delegation.mjs +8 -1
- package/dist/html/index.d.mts +23 -26
- package/dist/html/index.mjs +85 -18
- package/dist/html/sanitize.mjs +6 -5
- package/dist/index.d.mts +117 -54
- package/dist/index.mjs +541 -380
- package/dist/internal/attribute.d.mts +4 -3
- package/dist/internal/attribute.mjs +13 -23
- package/dist/internal/element-value.d.mts +2 -2
- package/dist/internal/element-value.mjs +12 -6
- package/dist/internal/get-value.mjs +3 -1
- package/dist/internal/property.d.mts +4 -0
- package/dist/internal/property.mjs +24 -0
- package/dist/property/get.property.d.mts +20 -0
- package/dist/property/get.property.mjs +35 -0
- package/dist/property/index.d.mts +3 -0
- package/dist/property/index.mjs +3 -0
- package/dist/property/set.property.d.mts +32 -0
- package/dist/property/set.property.mjs +32 -0
- package/dist/style.d.mts +16 -9
- package/dist/style.mjs +22 -21
- package/package.json +14 -6
- package/src/attribute/{get.ts → get.attribute.ts} +14 -3
- package/src/attribute/index.ts +10 -22
- package/src/attribute/{set.ts → set.attribute.ts} +9 -5
- package/src/create.ts +81 -0
- package/src/data.ts +16 -8
- package/src/event/delegation.ts +24 -3
- package/src/event/index.ts +9 -3
- package/src/find/index.ts +11 -3
- package/src/find/relative.ts +4 -0
- package/src/focusable.ts +10 -2
- package/src/html/index.ts +166 -58
- package/src/html/sanitize.ts +14 -11
- package/src/index.ts +2 -1
- package/src/internal/attribute.ts +23 -42
- package/src/internal/element-value.ts +25 -6
- package/src/internal/get-value.ts +14 -0
- package/src/internal/is.ts +4 -0
- package/src/internal/property.ts +42 -0
- package/src/is.ts +10 -2
- package/src/property/get.property.ts +73 -0
- package/src/property/index.ts +2 -0
- package/src/property/set.property.ts +103 -0
- package/src/style.ts +81 -36
- package/src/touch.ts +14 -2
package/dist/index.mjs
CHANGED
|
@@ -51,19 +51,6 @@ function isPlainObject(value) {
|
|
|
51
51
|
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
52
52
|
}
|
|
53
53
|
//#endregion
|
|
54
|
-
//#region node_modules/@oscarpalmer/atoms/dist/internal/array/compact.mjs
|
|
55
|
-
function compact(array, strict) {
|
|
56
|
-
if (!Array.isArray(array)) return [];
|
|
57
|
-
if (strict === true) return array.filter(Boolean);
|
|
58
|
-
const { length } = array;
|
|
59
|
-
const compacted = [];
|
|
60
|
-
for (let index = 0; index < length; index += 1) {
|
|
61
|
-
const item = array[index];
|
|
62
|
-
if (item != null) compacted.push(item);
|
|
63
|
-
}
|
|
64
|
-
return compacted;
|
|
65
|
-
}
|
|
66
|
-
//#endregion
|
|
67
54
|
//#region node_modules/@oscarpalmer/atoms/dist/internal/string.mjs
|
|
68
55
|
/**
|
|
69
56
|
* Get the string value from any value
|
|
@@ -79,13 +66,23 @@ function getString(value) {
|
|
|
79
66
|
return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
|
|
80
67
|
}
|
|
81
68
|
/**
|
|
82
|
-
* Join an array of values into a string
|
|
83
|
-
*
|
|
69
|
+
* Join an array of values into a string _(while ignoring empty values)_
|
|
70
|
+
*
|
|
71
|
+
* _(`null`, `undefined`, and any values that become whitespace-only strings are considered empty)_
|
|
72
|
+
* @param array Array of values
|
|
84
73
|
* @param delimiter Delimiter to use between values
|
|
85
74
|
* @returns Joined string
|
|
86
75
|
*/
|
|
87
|
-
function join(
|
|
88
|
-
|
|
76
|
+
function join(array, delimiter) {
|
|
77
|
+
if (!Array.isArray(array)) return "";
|
|
78
|
+
const { length } = array;
|
|
79
|
+
if (length === 0) return "";
|
|
80
|
+
const values = [];
|
|
81
|
+
for (let index = 0; index < length; index += 1) {
|
|
82
|
+
const item = getString(array[index]);
|
|
83
|
+
if (item.trim().length > 0) values.push(item);
|
|
84
|
+
}
|
|
85
|
+
return values.join(typeof delimiter === "string" ? delimiter : "");
|
|
89
86
|
}
|
|
90
87
|
/**
|
|
91
88
|
* Split a string into words _(and other readable parts)_
|
|
@@ -99,212 +96,15 @@ const EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
|
|
|
99
96
|
//#endregion
|
|
100
97
|
//#region node_modules/@oscarpalmer/atoms/dist/is.mjs
|
|
101
98
|
/**
|
|
102
|
-
* Is the value `undefined`, `null`, or a whitespace-only string?
|
|
99
|
+
* Is the value `undefined`, `null`, or stringified as a whitespace-only string?
|
|
103
100
|
* @param value Value to check
|
|
104
|
-
* @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
|
|
101
|
+
* @returns `true` if the value is nullable or matches a whitespace-only string, otherwise `false`
|
|
105
102
|
*/
|
|
106
103
|
function isNullableOrWhitespace(value) {
|
|
107
104
|
return value == null || EXPRESSION_WHITESPACE$1.test(getString(value));
|
|
108
105
|
}
|
|
109
106
|
const EXPRESSION_WHITESPACE$1 = /^\s*$/;
|
|
110
107
|
//#endregion
|
|
111
|
-
//#region src/internal/is.ts
|
|
112
|
-
/**
|
|
113
|
-
* Is the value an event target?
|
|
114
|
-
* @param value Value to check
|
|
115
|
-
* @returns `true` if it's an event target, otherwise `false`
|
|
116
|
-
*/
|
|
117
|
-
function isEventTarget(value) {
|
|
118
|
-
return typeof value === "object" && value != null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function" && typeof value.dispatchEvent === "function";
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Is the value an HTML or SVG element?
|
|
122
|
-
* @param value Value to check
|
|
123
|
-
* @returns `true` if it's an HTML or SVG element, otherwise `false`
|
|
124
|
-
*/
|
|
125
|
-
function isHTMLOrSVGElement(value) {
|
|
126
|
-
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
127
|
-
}
|
|
128
|
-
//#endregion
|
|
129
|
-
//#region src/is.ts
|
|
130
|
-
/**
|
|
131
|
-
* Is the value a child node?
|
|
132
|
-
* @param value Value to check
|
|
133
|
-
* @returns `true` if it's a child node, otherwise `false`
|
|
134
|
-
*/
|
|
135
|
-
function isChildNode(value) {
|
|
136
|
-
return value instanceof Node && CHILD_NODE_TYPES.has(value.nodeType);
|
|
137
|
-
}
|
|
138
|
-
function isInDocument(node, doc) {
|
|
139
|
-
if (!(node instanceof Node)) return false;
|
|
140
|
-
if (!(doc instanceof Document)) return node.ownerDocument?.contains(node) ?? true;
|
|
141
|
-
return node.ownerDocument == null ? node === doc : node.ownerDocument === doc && doc.contains(node);
|
|
142
|
-
}
|
|
143
|
-
const CHILD_NODE_TYPES = new Set([
|
|
144
|
-
Node.ELEMENT_NODE,
|
|
145
|
-
Node.TEXT_NODE,
|
|
146
|
-
Node.PROCESSING_INSTRUCTION_NODE,
|
|
147
|
-
Node.COMMENT_NODE,
|
|
148
|
-
Node.DOCUMENT_TYPE_NODE
|
|
149
|
-
]);
|
|
150
|
-
//#endregion
|
|
151
|
-
//#region src/internal/element-value.ts
|
|
152
|
-
function setElementValue(element, first, second, third, callback) {
|
|
153
|
-
if (!isHTMLOrSVGElement(element)) return;
|
|
154
|
-
if (typeof first === "string") setElementValues(element, first, second, third, callback);
|
|
155
|
-
else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
|
|
156
|
-
}
|
|
157
|
-
function setElementValues(element, first, second, third, callback) {
|
|
158
|
-
if (!isHTMLOrSVGElement(element)) return;
|
|
159
|
-
if (typeof first === "string") {
|
|
160
|
-
callback(element, first, second, third);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const isArray = Array.isArray(first);
|
|
164
|
-
if (!isArray && !(typeof first === "object" && first !== null)) return;
|
|
165
|
-
const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({
|
|
166
|
-
name,
|
|
167
|
-
value
|
|
168
|
-
}));
|
|
169
|
-
const { length } = entries;
|
|
170
|
-
for (let index = 0; index < length; index += 1) {
|
|
171
|
-
const entry = entries[index];
|
|
172
|
-
if (typeof entry === "object" && typeof entry?.name === "string") callback(element, entry.name, entry.value, third);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function updateElementValue(element, key, value, set, remove, isBoolean, json) {
|
|
176
|
-
if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
|
|
177
|
-
else set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/internal/attribute.ts
|
|
181
|
-
function badAttributeHandler(name, value) {
|
|
182
|
-
if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
|
|
183
|
-
if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
|
|
184
|
-
if (EXPRESSION_SKIP_NAME.test(name) || EXPRESSION_URI_VALUE.test(value) || isValidSourceAttribute(name, value)) return false;
|
|
185
|
-
return EXPRESSION_DATA_OR_SCRIPT.test(value);
|
|
186
|
-
}
|
|
187
|
-
function booleanAttributeHandler(name, value) {
|
|
188
|
-
if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
|
|
189
|
-
const normalizedName = name.toLowerCase();
|
|
190
|
-
if (!booleanAttributesSet.has(normalizedName)) return false;
|
|
191
|
-
const normalized = value.toLowerCase();
|
|
192
|
-
return !(normalized.length === 0 || normalized === normalizedName);
|
|
193
|
-
}
|
|
194
|
-
function decodeAttribute(value) {
|
|
195
|
-
textArea ??= document.createElement("textarea");
|
|
196
|
-
textArea.innerHTML = value;
|
|
197
|
-
return decodeURIComponent(textArea.value);
|
|
198
|
-
}
|
|
199
|
-
function handleAttribute(callback, decode, first, second) {
|
|
200
|
-
let name;
|
|
201
|
-
let value;
|
|
202
|
-
if (isAttribute(first)) {
|
|
203
|
-
name = first.name;
|
|
204
|
-
value = String(first.value);
|
|
205
|
-
} else if (typeof first === "string" && typeof second === "string") {
|
|
206
|
-
name = first;
|
|
207
|
-
value = second;
|
|
208
|
-
}
|
|
209
|
-
if (decode && value != null) value = decodeAttribute(value);
|
|
210
|
-
return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
|
|
211
|
-
}
|
|
212
|
-
function isAttribute(value) {
|
|
213
|
-
return value instanceof Attr || isPlainObject(value) && typeof value.name === "string" && "value" in value;
|
|
214
|
-
}
|
|
215
|
-
function _isBadAttribute(first, second, decode) {
|
|
216
|
-
return handleAttribute(badAttributeHandler, decode, first, second);
|
|
217
|
-
}
|
|
218
|
-
function _isBooleanAttribute(first, decode) {
|
|
219
|
-
return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
|
|
220
|
-
}
|
|
221
|
-
function _isEmptyNonBooleanAttribute(first, second, decode) {
|
|
222
|
-
return handleAttribute((name, value) => name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0, decode, first, second);
|
|
223
|
-
}
|
|
224
|
-
function _isInvalidBooleanAttribute(first, second, decode) {
|
|
225
|
-
return handleAttribute(booleanAttributeHandler, decode, first, second);
|
|
226
|
-
}
|
|
227
|
-
function isValidSourceAttribute(name, value) {
|
|
228
|
-
return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
|
|
229
|
-
}
|
|
230
|
-
function updateAttribute(element, name, value, dispatch) {
|
|
231
|
-
const normalizedName = name.toLowerCase();
|
|
232
|
-
const isBoolean = booleanAttributesSet.has(normalizedName);
|
|
233
|
-
const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === normalizedName) : value == null ? "" : value;
|
|
234
|
-
if (name in element) updateProperty(element, normalizedName, next, dispatch);
|
|
235
|
-
updateElementValue(element, name, isBoolean ? next ? "" : null : value, element.setAttribute, element.removeAttribute, isBoolean, false);
|
|
236
|
-
}
|
|
237
|
-
function updateProperty(element, name, value, dispatch) {
|
|
238
|
-
if (Object.is(element[name], value)) return;
|
|
239
|
-
element[name] = value;
|
|
240
|
-
const event = dispatch !== false && elementEvents[element.tagName]?.[name];
|
|
241
|
-
if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
|
|
242
|
-
}
|
|
243
|
-
const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
|
|
244
|
-
const EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
|
|
245
|
-
const EXPRESSION_EVENT_NAME = /^on/i;
|
|
246
|
-
const EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
|
|
247
|
-
const EXPRESSION_SOURCE_NAME = /^src$/i;
|
|
248
|
-
const EXPRESSION_SOURCE_VALUE = /^data:/i;
|
|
249
|
-
const EXPRESSION_URI_VALUE = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
250
|
-
const EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
251
|
-
/**
|
|
252
|
-
* List of boolean attributes
|
|
253
|
-
*/
|
|
254
|
-
const booleanAttributes = Object.freeze([
|
|
255
|
-
"async",
|
|
256
|
-
"autofocus",
|
|
257
|
-
"autoplay",
|
|
258
|
-
"checked",
|
|
259
|
-
"controls",
|
|
260
|
-
"default",
|
|
261
|
-
"defer",
|
|
262
|
-
"disabled",
|
|
263
|
-
"formnovalidate",
|
|
264
|
-
"hidden",
|
|
265
|
-
"inert",
|
|
266
|
-
"ismap",
|
|
267
|
-
"itemscope",
|
|
268
|
-
"loop",
|
|
269
|
-
"multiple",
|
|
270
|
-
"muted",
|
|
271
|
-
"nomodule",
|
|
272
|
-
"novalidate",
|
|
273
|
-
"open",
|
|
274
|
-
"playsinline",
|
|
275
|
-
"readonly",
|
|
276
|
-
"required",
|
|
277
|
-
"reversed",
|
|
278
|
-
"selected"
|
|
279
|
-
]);
|
|
280
|
-
const booleanAttributesSet = new Set(booleanAttributes);
|
|
281
|
-
const elementEvents = {
|
|
282
|
-
DETAILS: { open: "toggle" },
|
|
283
|
-
INPUT: {
|
|
284
|
-
checked: "change",
|
|
285
|
-
value: "input"
|
|
286
|
-
},
|
|
287
|
-
SELECT: { value: "change" },
|
|
288
|
-
TEXTAREA: { value: "input" }
|
|
289
|
-
};
|
|
290
|
-
const formElement = document.createElement("form");
|
|
291
|
-
let textArea;
|
|
292
|
-
//#endregion
|
|
293
|
-
//#region node_modules/@oscarpalmer/atoms/dist/string/index.mjs
|
|
294
|
-
/**
|
|
295
|
-
* Parse a JSON string into its proper value _(or `undefined` if it fails)_
|
|
296
|
-
* @param value JSON string to parse
|
|
297
|
-
* @param reviver Reviver function to transform the parsed values
|
|
298
|
-
* @returns Parsed value or `undefined` if parsing fails
|
|
299
|
-
*/
|
|
300
|
-
function parse(value, reviver) {
|
|
301
|
-
try {
|
|
302
|
-
return JSON.parse(value, reviver);
|
|
303
|
-
} catch {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
//#endregion
|
|
308
108
|
//#region node_modules/@oscarpalmer/atoms/dist/internal/number.mjs
|
|
309
109
|
/**
|
|
310
110
|
* Clamp a number between a minimum and maximum value
|
|
@@ -349,7 +149,7 @@ var SizedMap = class extends Map {
|
|
|
349
149
|
* Is the Map full?
|
|
350
150
|
*/
|
|
351
151
|
get full() {
|
|
352
|
-
return
|
|
152
|
+
return super.size >= this.#maximumSize;
|
|
353
153
|
}
|
|
354
154
|
get maximum() {
|
|
355
155
|
return this.#maximumSize;
|
|
@@ -368,21 +168,30 @@ var SizedMap = class extends Map {
|
|
|
368
168
|
* @inheritdoc
|
|
369
169
|
*/
|
|
370
170
|
get(key) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
171
|
+
if (super.has(key)) {
|
|
172
|
+
const value = super.get(key);
|
|
173
|
+
this.#setValue(key, value, true);
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
374
176
|
}
|
|
375
177
|
/**
|
|
376
178
|
* @inheritdoc
|
|
377
179
|
*/
|
|
378
180
|
set(key, value) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
181
|
+
return this.#setValue(key, value, super.has(key));
|
|
182
|
+
}
|
|
183
|
+
#setValue(key, value, has) {
|
|
184
|
+
if (has) super.delete(key);
|
|
185
|
+
else if (super.size >= this.#maximumSize) super.delete(super.keys().next().value);
|
|
186
|
+
super.set(key, value);
|
|
187
|
+
return this;
|
|
382
188
|
}
|
|
383
189
|
};
|
|
384
190
|
//#endregion
|
|
385
191
|
//#region node_modules/@oscarpalmer/atoms/dist/function/memoize.mjs
|
|
192
|
+
/**
|
|
193
|
+
* A memoized function, caching and retrieving results based on the its parameters _(or a custom cache key)_
|
|
194
|
+
*/
|
|
386
195
|
var Memoized = class {
|
|
387
196
|
#state;
|
|
388
197
|
/**
|
|
@@ -534,27 +343,239 @@ function toCaseCallback(value) {
|
|
|
534
343
|
}
|
|
535
344
|
cased.push(join(partResult, delimiters[type]));
|
|
536
345
|
}
|
|
537
|
-
return join(cased, delimiters[type]);
|
|
346
|
+
return join(cased, delimiters[type]);
|
|
347
|
+
}
|
|
348
|
+
const CASE_CAMEL = "camel";
|
|
349
|
+
const CASE_KEBAB = "kebab";
|
|
350
|
+
const CASE_PASCAL = "pascal";
|
|
351
|
+
const CASE_SNAKE = "snake";
|
|
352
|
+
const DELIMTER_EMPTY = "";
|
|
353
|
+
const DELIMITER_HYPHEN = "-";
|
|
354
|
+
const DELIMITER_UNDERSCORE = "_";
|
|
355
|
+
const EXPRESSION_CAMEL_CASE = /(\p{Ll})(\p{Lu})/gu;
|
|
356
|
+
const EXPRESSION_ACRONYM = /(\p{Lu}*)(\p{Lu})(\p{Ll}+)/gu;
|
|
357
|
+
const REPLACEMENT_CAMEL_CASE = "$1-$2";
|
|
358
|
+
const S = "s";
|
|
359
|
+
const caseMemoizers = {};
|
|
360
|
+
const delimiters = {
|
|
361
|
+
[CASE_CAMEL]: DELIMTER_EMPTY,
|
|
362
|
+
[CASE_KEBAB]: DELIMITER_HYPHEN,
|
|
363
|
+
[CASE_PASCAL]: DELIMTER_EMPTY,
|
|
364
|
+
[CASE_SNAKE]: DELIMITER_UNDERSCORE
|
|
365
|
+
};
|
|
366
|
+
let memoizedCapitalize;
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/internal/is.ts
|
|
369
|
+
/**
|
|
370
|
+
* Is the value an event target?
|
|
371
|
+
* @param value Value to check
|
|
372
|
+
* @returns `true` if it's an event target, otherwise `false`
|
|
373
|
+
*/
|
|
374
|
+
function isEventTarget(value) {
|
|
375
|
+
return typeof value === "object" && value != null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function" && typeof value.dispatchEvent === "function";
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Is the value an HTML or SVG element?
|
|
379
|
+
* @param value Value to check
|
|
380
|
+
* @returns `true` if it's an HTML or SVG element, otherwise `false`
|
|
381
|
+
*/
|
|
382
|
+
function isHTMLOrSVGElement(value) {
|
|
383
|
+
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
384
|
+
}
|
|
385
|
+
//#endregion
|
|
386
|
+
//#region src/is.ts
|
|
387
|
+
/**
|
|
388
|
+
* Is the value a child node?
|
|
389
|
+
* @param value Value to check
|
|
390
|
+
* @returns `true` if it's a child node, otherwise `false`
|
|
391
|
+
*/
|
|
392
|
+
function isChildNode(value) {
|
|
393
|
+
return value instanceof Node && CHILD_NODE_TYPES.has(value.nodeType);
|
|
394
|
+
}
|
|
395
|
+
function isInDocument(node, doc) {
|
|
396
|
+
if (!(node instanceof Node)) return false;
|
|
397
|
+
if (!(doc instanceof Document)) return node.ownerDocument?.contains(node) ?? true;
|
|
398
|
+
return node.ownerDocument == null ? node === doc : node.ownerDocument === doc && doc.contains(node);
|
|
399
|
+
}
|
|
400
|
+
const CHILD_NODE_TYPES = new Set([
|
|
401
|
+
Node.ELEMENT_NODE,
|
|
402
|
+
Node.TEXT_NODE,
|
|
403
|
+
Node.PROCESSING_INSTRUCTION_NODE,
|
|
404
|
+
Node.COMMENT_NODE,
|
|
405
|
+
Node.DOCUMENT_TYPE_NODE
|
|
406
|
+
]);
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/internal/element-value.ts
|
|
409
|
+
function normalizeKey(key, style) {
|
|
410
|
+
return style && key.startsWith(CSS_VARIABLE_PREFIX$1) ? key : kebabCase(key);
|
|
411
|
+
}
|
|
412
|
+
function setElementValue(element, first, second, third, callback, style) {
|
|
413
|
+
if (!isHTMLOrSVGElement(element)) return;
|
|
414
|
+
if (typeof first === "string") setElementValues(element, first, second, third, callback, style);
|
|
415
|
+
else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback, style);
|
|
416
|
+
}
|
|
417
|
+
function setElementValues(element, first, second, third, callback, style) {
|
|
418
|
+
if (!isHTMLOrSVGElement(element)) return;
|
|
419
|
+
const dispatch = third !== false;
|
|
420
|
+
if (typeof first === "string") {
|
|
421
|
+
callback(element, normalizeKey(first, style), second, dispatch);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const isArray = Array.isArray(first);
|
|
425
|
+
if (!isArray && !(typeof first === "object" && first !== null)) return;
|
|
426
|
+
const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({
|
|
427
|
+
name,
|
|
428
|
+
value
|
|
429
|
+
}));
|
|
430
|
+
const { length } = entries;
|
|
431
|
+
for (let index = 0; index < length; index += 1) {
|
|
432
|
+
const entry = entries[index];
|
|
433
|
+
if (typeof entry === "object" && typeof entry?.name === "string") callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function updateElementValue(element, key, value, set, remove, isBoolean, json) {
|
|
437
|
+
if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
|
|
438
|
+
else set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
439
|
+
}
|
|
440
|
+
const CSS_VARIABLE_PREFIX$1 = "--";
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/internal/property.ts
|
|
443
|
+
function updateProperty(element, name, value, dispatch) {
|
|
444
|
+
let property = name;
|
|
445
|
+
if (!(property in element)) property = camelCase(name);
|
|
446
|
+
if (!(property in element) || Object.is(element[property], value)) return;
|
|
447
|
+
element[property] = value;
|
|
448
|
+
const event = dispatch && elementEvents[element.tagName]?.[property];
|
|
449
|
+
if (typeof event === "string") element.dispatchEvent(new Event(event, {
|
|
450
|
+
bubbles: true,
|
|
451
|
+
cancelable: true
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
const elementEvents = {
|
|
455
|
+
DETAILS: { open: "toggle" },
|
|
456
|
+
INPUT: {
|
|
457
|
+
checked: "change",
|
|
458
|
+
value: "input"
|
|
459
|
+
},
|
|
460
|
+
SELECT: { value: "change" },
|
|
461
|
+
TEXTAREA: { value: "input" }
|
|
462
|
+
};
|
|
463
|
+
//#endregion
|
|
464
|
+
//#region src/internal/attribute.ts
|
|
465
|
+
function badAttributeHandler(name, value) {
|
|
466
|
+
if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
|
|
467
|
+
if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
|
|
468
|
+
if (EXPRESSION_SKIP_NAME.test(name) || EXPRESSION_URI_VALUE.test(value) || isValidSourceAttribute(name, value)) return false;
|
|
469
|
+
return EXPRESSION_DATA_OR_SCRIPT.test(value);
|
|
470
|
+
}
|
|
471
|
+
function booleanAttributeHandler(name, value) {
|
|
472
|
+
if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
|
|
473
|
+
const normalizedName = name.toLowerCase();
|
|
474
|
+
if (!booleanAttributesSet.has(normalizedName)) return false;
|
|
475
|
+
const normalized = value.toLowerCase();
|
|
476
|
+
return !(normalized.length === 0 || normalized === normalizedName);
|
|
477
|
+
}
|
|
478
|
+
function decodeAttribute(value) {
|
|
479
|
+
textArea ??= document.createElement("textarea");
|
|
480
|
+
textArea.innerHTML = value;
|
|
481
|
+
return decodeURIComponent(textArea.value);
|
|
482
|
+
}
|
|
483
|
+
function handleAttribute(callback, decode, first, second) {
|
|
484
|
+
let name;
|
|
485
|
+
let value;
|
|
486
|
+
if (isAttribute(first)) {
|
|
487
|
+
name = first.name;
|
|
488
|
+
value = String(first.value);
|
|
489
|
+
} else if (typeof first === "string" && typeof second === "string") {
|
|
490
|
+
name = first;
|
|
491
|
+
value = second;
|
|
492
|
+
}
|
|
493
|
+
if (name != null) name = kebabCase(name);
|
|
494
|
+
if (decode && value != null) value = decodeAttribute(value);
|
|
495
|
+
return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
|
|
496
|
+
}
|
|
497
|
+
function isAttribute(value) {
|
|
498
|
+
return value instanceof Attr || isPlainObject(value) && typeof value.name === "string" && "value" in value;
|
|
499
|
+
}
|
|
500
|
+
function _isBadAttribute(first, second, decode) {
|
|
501
|
+
return handleAttribute(badAttributeHandler, decode, first, second);
|
|
502
|
+
}
|
|
503
|
+
function _isBooleanAttribute(first, decode) {
|
|
504
|
+
return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
|
|
505
|
+
}
|
|
506
|
+
function _isInvalidBooleanAttribute(first, second, decode) {
|
|
507
|
+
return handleAttribute(booleanAttributeHandler, decode, first, second);
|
|
508
|
+
}
|
|
509
|
+
function isValidSourceAttribute(name, value) {
|
|
510
|
+
return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
|
|
511
|
+
}
|
|
512
|
+
function updateAttribute(element, name, value, dispatch) {
|
|
513
|
+
const lowerCaseName = name.toLowerCase();
|
|
514
|
+
const isBoolean = booleanAttributesSet.has(lowerCaseName);
|
|
515
|
+
const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === lowerCaseName) : value == null ? "" : value;
|
|
516
|
+
if (isBoolean || dispatchedAttributes.has(name)) updateProperty(element, name, next, dispatch);
|
|
517
|
+
updateElementValue(element, name, isBoolean ? next ? "" : null : value, element.setAttribute, element.removeAttribute, isBoolean, false);
|
|
518
|
+
}
|
|
519
|
+
const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
|
|
520
|
+
const EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
|
|
521
|
+
const EXPRESSION_EVENT_NAME = /^on/i;
|
|
522
|
+
const EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
|
|
523
|
+
const EXPRESSION_SOURCE_NAME = /^src$/i;
|
|
524
|
+
const EXPRESSION_SOURCE_VALUE = /^data:/i;
|
|
525
|
+
const EXPRESSION_URI_VALUE = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
526
|
+
const EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
527
|
+
/**
|
|
528
|
+
* List of boolean attributes
|
|
529
|
+
*/
|
|
530
|
+
const booleanAttributes = Object.freeze([
|
|
531
|
+
"async",
|
|
532
|
+
"autofocus",
|
|
533
|
+
"autoplay",
|
|
534
|
+
"checked",
|
|
535
|
+
"controls",
|
|
536
|
+
"default",
|
|
537
|
+
"defer",
|
|
538
|
+
"disabled",
|
|
539
|
+
"formnovalidate",
|
|
540
|
+
"hidden",
|
|
541
|
+
"inert",
|
|
542
|
+
"ismap",
|
|
543
|
+
"itemscope",
|
|
544
|
+
"loop",
|
|
545
|
+
"multiple",
|
|
546
|
+
"muted",
|
|
547
|
+
"nomodule",
|
|
548
|
+
"novalidate",
|
|
549
|
+
"open",
|
|
550
|
+
"playsinline",
|
|
551
|
+
"readonly",
|
|
552
|
+
"required",
|
|
553
|
+
"reversed",
|
|
554
|
+
"selected"
|
|
555
|
+
]);
|
|
556
|
+
const booleanAttributesSet = new Set(booleanAttributes);
|
|
557
|
+
const dispatchedAttributes = new Set([
|
|
558
|
+
"checked",
|
|
559
|
+
"open",
|
|
560
|
+
"value"
|
|
561
|
+
]);
|
|
562
|
+
const formElement = document.createElement("form");
|
|
563
|
+
let textArea;
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region node_modules/@oscarpalmer/atoms/dist/string/index.mjs
|
|
566
|
+
/**
|
|
567
|
+
* Parse a JSON string into its proper value _(or `undefined` if it fails)_
|
|
568
|
+
* @param value JSON string to parse
|
|
569
|
+
* @param reviver Reviver function to transform the parsed values
|
|
570
|
+
* @returns Parsed value or `undefined` if parsing fails
|
|
571
|
+
*/
|
|
572
|
+
function parse(value, reviver) {
|
|
573
|
+
try {
|
|
574
|
+
return JSON.parse(value, reviver);
|
|
575
|
+
} catch {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
538
578
|
}
|
|
539
|
-
const CASE_CAMEL = "camel";
|
|
540
|
-
const CASE_KEBAB = "kebab";
|
|
541
|
-
const CASE_PASCAL = "pascal";
|
|
542
|
-
const CASE_SNAKE = "snake";
|
|
543
|
-
const DELIMTER_EMPTY = "";
|
|
544
|
-
const DELIMITER_HYPHEN = "-";
|
|
545
|
-
const DELIMITER_UNDERSCORE = "_";
|
|
546
|
-
const EXPRESSION_CAMEL_CASE = /(\p{Ll})(\p{Lu})/gu;
|
|
547
|
-
const EXPRESSION_ACRONYM = /(\p{Lu}*)(\p{Lu})(\p{Ll}+)/gu;
|
|
548
|
-
const REPLACEMENT_CAMEL_CASE = "$1-$2";
|
|
549
|
-
const S = "s";
|
|
550
|
-
const caseMemoizers = {};
|
|
551
|
-
const delimiters = {
|
|
552
|
-
[CASE_CAMEL]: DELIMTER_EMPTY,
|
|
553
|
-
[CASE_KEBAB]: DELIMITER_HYPHEN,
|
|
554
|
-
[CASE_PASCAL]: DELIMTER_EMPTY,
|
|
555
|
-
[CASE_SNAKE]: DELIMITER_UNDERSCORE
|
|
556
|
-
};
|
|
557
|
-
let memoizedCapitalize;
|
|
558
579
|
//#endregion
|
|
559
580
|
//#region src/internal/get-value.ts
|
|
560
581
|
function getBoolean(value, defaultValue) {
|
|
@@ -567,14 +588,16 @@ function getAttributeValue(element, name, parseValue) {
|
|
|
567
588
|
return EXPRESSION_DATA_PREFIX.test(normalized) && typeof value === "string" && parseValue ? parse(value) ?? value : value;
|
|
568
589
|
}
|
|
569
590
|
function getStyleValue(element, property, computed) {
|
|
591
|
+
if (property.startsWith(CSS_VARIABLE_PREFIX)) return element.style.getPropertyValue(property);
|
|
570
592
|
const name = camelCase(property);
|
|
571
593
|
return computed ? getComputedStyle(element)[name] : element.style[name];
|
|
572
594
|
}
|
|
573
595
|
const EXPRESSION_DATA_PREFIX = /^data-/i;
|
|
596
|
+
const CSS_VARIABLE_PREFIX = "--";
|
|
574
597
|
//#endregion
|
|
575
|
-
//#region src/attribute/get.ts
|
|
598
|
+
//#region src/attribute/get.attribute.ts
|
|
576
599
|
function getAttribute(element, name, parseValues) {
|
|
577
|
-
if (isHTMLOrSVGElement(element) && typeof name === "string") return getAttributeValue(element, name, parseValues !== false);
|
|
600
|
+
if (isHTMLOrSVGElement(element) && typeof name === "string") return getAttributeValue(element, kebabCase(name), parseValues !== false);
|
|
578
601
|
}
|
|
579
602
|
/**
|
|
580
603
|
* Get specific attributes from an element
|
|
@@ -590,12 +613,12 @@ function getAttributes(element, names, parseData) {
|
|
|
590
613
|
const { length } = names;
|
|
591
614
|
for (let index = 0; index < length; index += 1) {
|
|
592
615
|
const name = names[index];
|
|
593
|
-
if (typeof name === "string") attributes[name] = getAttributeValue(element, name, shouldParse);
|
|
616
|
+
if (typeof name === "string") attributes[name] = getAttributeValue(element, kebabCase(name), shouldParse);
|
|
594
617
|
}
|
|
595
618
|
return attributes;
|
|
596
619
|
}
|
|
597
620
|
//#endregion
|
|
598
|
-
//#region src/attribute/set.ts
|
|
621
|
+
//#region src/attribute/set.attribute.ts
|
|
599
622
|
function setAttribute(element, first, second, third) {
|
|
600
623
|
setElementValue(element, first, second, third, updateAttribute);
|
|
601
624
|
}
|
|
@@ -610,34 +633,200 @@ function isBadAttribute(first, second) {
|
|
|
610
633
|
function isBooleanAttribute(first) {
|
|
611
634
|
return _isBooleanAttribute(first, true);
|
|
612
635
|
}
|
|
613
|
-
function isEmptyNonBooleanAttribute(first, second) {
|
|
614
|
-
return _isEmptyNonBooleanAttribute(first, second, true);
|
|
615
|
-
}
|
|
616
636
|
function isInvalidBooleanAttribute(first, second) {
|
|
617
637
|
return _isInvalidBooleanAttribute(first, second, true);
|
|
618
638
|
}
|
|
619
639
|
//#endregion
|
|
640
|
+
//#region src/style.ts
|
|
641
|
+
/**
|
|
642
|
+
* Get a style from an element
|
|
643
|
+
* @param element Element to get the style from
|
|
644
|
+
* @param property Style name
|
|
645
|
+
* @param computed Get the computed style? _(defaults to `false`)_
|
|
646
|
+
* @returns Style value
|
|
647
|
+
*/
|
|
648
|
+
function getStyle(element, property, computed) {
|
|
649
|
+
if (isHTMLOrSVGElement(element) && typeof property === "string") return getStyleValue(element, property, computed === true);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Get styles from an element
|
|
653
|
+
* @param element Element to get the styles from
|
|
654
|
+
* @param properties Styles to get
|
|
655
|
+
* @param computed Get the computed styles? _(defaults to `false`)_
|
|
656
|
+
* @returns Style values
|
|
657
|
+
*/
|
|
658
|
+
function getStyles(element, properties, computed) {
|
|
659
|
+
const styles = {};
|
|
660
|
+
if (!(isHTMLOrSVGElement(element) && Array.isArray(properties))) return styles;
|
|
661
|
+
const { length } = properties;
|
|
662
|
+
for (let index = 0; index < length; index += 1) {
|
|
663
|
+
const property = properties[index];
|
|
664
|
+
if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
|
|
665
|
+
}
|
|
666
|
+
return styles;
|
|
667
|
+
}
|
|
668
|
+
function getTextDirection(node) {
|
|
669
|
+
let target;
|
|
670
|
+
if (isHTMLOrSVGElement(node)) target = node;
|
|
671
|
+
else target = node instanceof Node ? node.ownerDocument?.documentElement ?? document.documentElement : document.documentElement;
|
|
672
|
+
let { direction } = target.style;
|
|
673
|
+
if (direction === "") direction = getStyleValue(target, PROPERTY_DIRECTION, true);
|
|
674
|
+
return direction === DIRECTION_RTL ? DIRECTION_RTL : DIRECTION_LTR;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Set a style on an element
|
|
678
|
+
* @param element Element to set the style on
|
|
679
|
+
* @param property Style name
|
|
680
|
+
* @param value Style value
|
|
681
|
+
*/
|
|
682
|
+
function setStyle(element, property, value) {
|
|
683
|
+
setElementValues(element, property, value, null, updateStyleProperty, true);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Set styles on an element
|
|
687
|
+
* @param element Element to set the styles on
|
|
688
|
+
* @param styles Styles to set
|
|
689
|
+
*/
|
|
690
|
+
function setStyles(element, styles) {
|
|
691
|
+
setElementValues(element, styles, null, null, updateStyleProperty, true);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Toggle styles for an element
|
|
695
|
+
* @param element Element to style
|
|
696
|
+
* @param styles Styles to be set or removed
|
|
697
|
+
* @returns Style toggler
|
|
698
|
+
*/
|
|
699
|
+
function toggleStyles(element, styles) {
|
|
700
|
+
function toggle(set) {
|
|
701
|
+
hasSet = set;
|
|
702
|
+
let next;
|
|
703
|
+
if (set) {
|
|
704
|
+
values = getStyles(element, keys);
|
|
705
|
+
next = styles;
|
|
706
|
+
} else {
|
|
707
|
+
next = { ...values };
|
|
708
|
+
values = {};
|
|
709
|
+
for (let index = 0; index < length; index += 1) values[keys[index]] = void 0;
|
|
710
|
+
}
|
|
711
|
+
setStyles(element, next);
|
|
712
|
+
}
|
|
713
|
+
const keys = Object.keys(styles);
|
|
714
|
+
const { length } = keys;
|
|
715
|
+
let hasSet = false;
|
|
716
|
+
let values = {};
|
|
717
|
+
return {
|
|
718
|
+
set() {
|
|
719
|
+
if (!hasSet) toggle(true);
|
|
720
|
+
},
|
|
721
|
+
remove() {
|
|
722
|
+
if (hasSet) toggle(false);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function updateStyleProperty(element, key, value) {
|
|
727
|
+
updateElementValue(element, key, value, function(property, style) {
|
|
728
|
+
if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, String(style));
|
|
729
|
+
else this.style[property] = String(style);
|
|
730
|
+
}, function(property) {
|
|
731
|
+
if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
|
|
732
|
+
else this.style[property] = "";
|
|
733
|
+
if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
|
|
734
|
+
}, false, false);
|
|
735
|
+
}
|
|
736
|
+
const ATTRIBUTE_STYLE = "style";
|
|
737
|
+
const DIRECTION_LTR = "ltr";
|
|
738
|
+
const DIRECTION_RTL = "rtl";
|
|
739
|
+
const PROPERTY_DIRECTION = "direction";
|
|
740
|
+
const VARIABLE_PREFIX = "--";
|
|
741
|
+
//#endregion
|
|
742
|
+
//#region src/property/get.property.ts
|
|
743
|
+
/**
|
|
744
|
+
* Get the values of one or more properties on an element
|
|
745
|
+
* @param target Target element
|
|
746
|
+
* @param properties Properties to get
|
|
747
|
+
* @returns Property values
|
|
748
|
+
*/
|
|
749
|
+
function getProperties(target, properties) {
|
|
750
|
+
const values = {};
|
|
751
|
+
if (!isHTMLOrSVGElement(target) || !Array.isArray(properties)) return values;
|
|
752
|
+
const { length } = properties;
|
|
753
|
+
for (let index = 0; index < length; index += 1) {
|
|
754
|
+
const property = properties[index];
|
|
755
|
+
if (typeof property === "string") values[property] = getPropertyValue(target, property);
|
|
756
|
+
}
|
|
757
|
+
return values;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Get the value of a property on an element
|
|
761
|
+
* @param target Target element
|
|
762
|
+
* @param property Property to get
|
|
763
|
+
* @returns Property value
|
|
764
|
+
*/
|
|
765
|
+
function getProperty(target, property) {
|
|
766
|
+
if (isHTMLOrSVGElement(target) && typeof property === "string") return getPropertyValue(target, property);
|
|
767
|
+
}
|
|
768
|
+
function getPropertyValue(element, property) {
|
|
769
|
+
let actual = property;
|
|
770
|
+
if (!(actual in element)) actual = camelCase(actual);
|
|
771
|
+
if (actual in element) return element[actual];
|
|
772
|
+
}
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region src/property/set.property.ts
|
|
775
|
+
/**
|
|
776
|
+
* Set the values of one or more properties on an element
|
|
777
|
+
*
|
|
778
|
+
* Also updates attributes for boolean/dispatchable properties, and if `dispatch` is `true`, will dispatch events for dispatchable properties
|
|
779
|
+
* @param target Target element
|
|
780
|
+
* @param properties Properties to set
|
|
781
|
+
* @param dispatch Dispatch events for properties? _(defaults to `true`)_
|
|
782
|
+
*/
|
|
783
|
+
function setProperties(target, properties, dispatch) {
|
|
784
|
+
if (!isHTMLOrSVGElement(target) || !isPlainObject(properties)) return;
|
|
785
|
+
const shouldDispatch = dispatch !== false;
|
|
786
|
+
const keys = Object.keys(properties);
|
|
787
|
+
const { length } = keys;
|
|
788
|
+
for (let index = 0; index < length; index += 1) setPropertyValue(target, keys[index], properties[keys[index]], shouldDispatch);
|
|
789
|
+
}
|
|
790
|
+
function setProperty(target, property, value, dispatch) {
|
|
791
|
+
if (isHTMLOrSVGElement(target) && typeof property === "string") setPropertyValue(target, property, value, dispatch !== false);
|
|
792
|
+
}
|
|
793
|
+
function setPropertyValue(element, property, value, dispatch) {
|
|
794
|
+
if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) setAttribute(element, property, value, dispatch);
|
|
795
|
+
else updateProperty(element, property, value, dispatch);
|
|
796
|
+
}
|
|
797
|
+
//#endregion
|
|
798
|
+
//#region src/create.ts
|
|
799
|
+
function createElement(tag, properties, attributes, styles) {
|
|
800
|
+
if (typeof tag !== "string") throw new TypeError(MESSAGE);
|
|
801
|
+
const element = document.createElement(tag);
|
|
802
|
+
if (properties != null) setProperties(element, properties);
|
|
803
|
+
if (attributes != null) setAttributes(element, attributes);
|
|
804
|
+
if (styles != null) setStyles(element, styles);
|
|
805
|
+
return element;
|
|
806
|
+
}
|
|
807
|
+
const MESSAGE = "Tag name must be a string";
|
|
808
|
+
//#endregion
|
|
620
809
|
//#region src/data.ts
|
|
621
810
|
function getData(element, keys, parseValues) {
|
|
622
811
|
if (!isHTMLOrSVGElement(element)) return;
|
|
623
|
-
const
|
|
812
|
+
const noParse = parseValues === false;
|
|
624
813
|
if (typeof keys === "string") {
|
|
625
|
-
const value = element.dataset[keys];
|
|
814
|
+
const value = element.dataset[camelCase(keys)];
|
|
626
815
|
if (value === void 0) return;
|
|
627
|
-
return
|
|
816
|
+
return noParse ? value : parse(value);
|
|
628
817
|
}
|
|
629
818
|
const { length } = keys;
|
|
630
819
|
const data = {};
|
|
631
820
|
for (let index = 0; index < length; index += 1) {
|
|
632
821
|
const key = keys[index];
|
|
633
|
-
const value = element.dataset[key];
|
|
822
|
+
const value = element.dataset[camelCase(key)];
|
|
634
823
|
if (value == null) data[key] = void 0;
|
|
635
|
-
else data[key] =
|
|
824
|
+
else data[key] = noParse ? value : parse(value);
|
|
636
825
|
}
|
|
637
826
|
return data;
|
|
638
827
|
}
|
|
639
828
|
function getName(original) {
|
|
640
|
-
return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original
|
|
829
|
+
return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original.replace(EXPRESSION_DATA_PREFIX, ""))}`;
|
|
641
830
|
}
|
|
642
831
|
function setData(element, first, second) {
|
|
643
832
|
setElementValues(element, first, second, null, updateDataAttribute);
|
|
@@ -671,7 +860,14 @@ function delegatedEventHandler(event) {
|
|
|
671
860
|
const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
672
861
|
const items = event.composedPath();
|
|
673
862
|
const { length } = items;
|
|
863
|
+
let cancelled = false;
|
|
674
864
|
let target = items[0];
|
|
865
|
+
const originalStopPropagation = event.stopPropagation;
|
|
866
|
+
event.stopPropagation = function() {
|
|
867
|
+
cancelled = true;
|
|
868
|
+
originalStopPropagation.call(event);
|
|
869
|
+
};
|
|
870
|
+
event.stopImmediatePropagation = event.stopPropagation.bind(event);
|
|
675
871
|
Object.defineProperties(event, {
|
|
676
872
|
currentTarget: {
|
|
677
873
|
configurable: true,
|
|
@@ -691,7 +887,7 @@ function delegatedEventHandler(event) {
|
|
|
691
887
|
target = item;
|
|
692
888
|
for (const listener of listeners) {
|
|
693
889
|
listener.call(item, event);
|
|
694
|
-
if (
|
|
890
|
+
if (cancelled) return;
|
|
695
891
|
}
|
|
696
892
|
}
|
|
697
893
|
}
|
|
@@ -1125,8 +1321,9 @@ const TYPE_RADIO = "radio";
|
|
|
1125
1321
|
//#region src/html/sanitize.ts
|
|
1126
1322
|
function handleElement(element, depth) {
|
|
1127
1323
|
if (depth === 0) {
|
|
1128
|
-
const removable = element.querySelectorAll(REMOVE_SELECTOR);
|
|
1129
|
-
|
|
1324
|
+
const removable = [...element.querySelectorAll(REMOVE_SELECTOR)];
|
|
1325
|
+
const { length } = removable;
|
|
1326
|
+
for (let index = 0; index < length; index += 1) removable[index].remove();
|
|
1130
1327
|
}
|
|
1131
1328
|
sanitizeAttributes(element, [...element.attributes]);
|
|
1132
1329
|
}
|
|
@@ -1145,7 +1342,7 @@ function sanitizeAttributes(element, attributes) {
|
|
|
1145
1342
|
const { length } = attributes;
|
|
1146
1343
|
for (let index = 0; index < length; index += 1) {
|
|
1147
1344
|
const { name, value } = attributes[index];
|
|
1148
|
-
if (_isBadAttribute(name, value, false)
|
|
1345
|
+
if (_isBadAttribute(name, value, false)) element.removeAttribute(name);
|
|
1149
1346
|
else if (_isInvalidBooleanAttribute(name, value, false)) setAttribute(element, name, true);
|
|
1150
1347
|
}
|
|
1151
1348
|
}
|
|
@@ -1191,16 +1388,22 @@ function createHtml(value) {
|
|
|
1191
1388
|
function createTemplate(value, options) {
|
|
1192
1389
|
const template = document.createElement(TEMPLATE_TAG);
|
|
1193
1390
|
template.innerHTML = createHtml(value);
|
|
1194
|
-
if (typeof value === "string" && options.cache) templates
|
|
1391
|
+
if (typeof value === "string" && options.cache) templates.set(value, template);
|
|
1195
1392
|
return template;
|
|
1196
1393
|
}
|
|
1394
|
+
function getComment(index) {
|
|
1395
|
+
return COMMENT_TEMPLATE.replace(COMMENT_INDEX, String(index));
|
|
1396
|
+
}
|
|
1197
1397
|
function getHtml(value) {
|
|
1198
1398
|
return `${TEMPORARY_ELEMENT}${typeof value === "string" ? value : value.innerHTML}${TEMPORARY_ELEMENT}`;
|
|
1199
1399
|
}
|
|
1200
|
-
function getNodes(value, options) {
|
|
1400
|
+
function getNodes(value, options, nodes) {
|
|
1201
1401
|
if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
|
|
1202
1402
|
const template = getTemplate(value, options);
|
|
1203
|
-
|
|
1403
|
+
if (template == null) return [];
|
|
1404
|
+
const cloned = [...template.content.cloneNode(true).childNodes];
|
|
1405
|
+
if (nodes != null) replaceComments(cloned, nodes);
|
|
1406
|
+
return cloned;
|
|
1204
1407
|
}
|
|
1205
1408
|
function getOptions(input) {
|
|
1206
1409
|
const options = isPlainObject(input) ? input : {};
|
|
@@ -1211,31 +1414,86 @@ function getParser() {
|
|
|
1211
1414
|
parser ??= new DOMParser();
|
|
1212
1415
|
return parser;
|
|
1213
1416
|
}
|
|
1417
|
+
function getTagged(strings, values) {
|
|
1418
|
+
const tagged = {
|
|
1419
|
+
nodes: [],
|
|
1420
|
+
template: ""
|
|
1421
|
+
};
|
|
1422
|
+
const stringsLength = strings.length;
|
|
1423
|
+
let nodeIndex = 0;
|
|
1424
|
+
for (let stringIndex = 0; stringIndex < stringsLength; stringIndex += 1) {
|
|
1425
|
+
const value = values[stringIndex];
|
|
1426
|
+
tagged.template += strings[stringIndex];
|
|
1427
|
+
if (value instanceof Node) {
|
|
1428
|
+
tagged.nodes.push(value);
|
|
1429
|
+
tagged.template += getComment(nodeIndex);
|
|
1430
|
+
nodeIndex += 1;
|
|
1431
|
+
} else if (hasNodes(value)) {
|
|
1432
|
+
const items = [...value];
|
|
1433
|
+
const itemsLength = items.length;
|
|
1434
|
+
for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
|
|
1435
|
+
const item = items[itemIndex];
|
|
1436
|
+
if (item instanceof Node) {
|
|
1437
|
+
tagged.nodes.push(item);
|
|
1438
|
+
tagged.template += getComment(nodeIndex);
|
|
1439
|
+
nodeIndex += 1;
|
|
1440
|
+
} else tagged.template += getString(item);
|
|
1441
|
+
}
|
|
1442
|
+
} else if (Array.isArray(value)) {
|
|
1443
|
+
const valueLength = value.length;
|
|
1444
|
+
for (let valueIndex = 0; valueIndex < valueLength; valueIndex += 1) tagged.template += getString(value[valueIndex]);
|
|
1445
|
+
} else tagged.template += getString(value);
|
|
1446
|
+
}
|
|
1447
|
+
return tagged;
|
|
1448
|
+
}
|
|
1214
1449
|
function getTemplate(value, options) {
|
|
1215
1450
|
if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
|
|
1216
1451
|
if (value.trim().length === 0) return;
|
|
1217
|
-
let template = templates
|
|
1452
|
+
let template = templates.get(value);
|
|
1218
1453
|
if (template != null) return template;
|
|
1219
1454
|
const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
|
|
1220
1455
|
return createTemplate(element instanceof HTMLTemplateElement ? element : value, options);
|
|
1221
1456
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1457
|
+
function hasNodes(value) {
|
|
1458
|
+
if (value instanceof HTMLCollection || value instanceof NodeList) return true;
|
|
1459
|
+
return Array.isArray(value) && value.some((item) => item instanceof Node);
|
|
1460
|
+
}
|
|
1461
|
+
function html(first, ...second) {
|
|
1462
|
+
if (isTagged(first)) {
|
|
1463
|
+
const tagged = getTagged(first, second);
|
|
1464
|
+
return getNodes(tagged.template, getOptions(), tagged.nodes);
|
|
1465
|
+
}
|
|
1466
|
+
return getNodes(first, getOptions(second[0]));
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Clear cache of template elements
|
|
1470
|
+
*/
|
|
1225
1471
|
html.clear = () => {
|
|
1226
|
-
templates
|
|
1472
|
+
templates.clear();
|
|
1227
1473
|
};
|
|
1474
|
+
/**
|
|
1475
|
+
* Remove cached template element for an HTML string or id
|
|
1476
|
+
* @param template HTML string or id for a template element
|
|
1477
|
+
*/
|
|
1228
1478
|
html.remove = (template) => {
|
|
1229
|
-
|
|
1230
|
-
const keys = Object.keys(templates);
|
|
1231
|
-
const { length } = keys;
|
|
1232
|
-
const updated = {};
|
|
1233
|
-
for (let index = 0; index < length; index += 1) {
|
|
1234
|
-
const key = keys[index];
|
|
1235
|
-
if (key !== template) updated[key] = templates[key];
|
|
1236
|
-
}
|
|
1237
|
-
templates = updated;
|
|
1479
|
+
templates.delete(template);
|
|
1238
1480
|
};
|
|
1481
|
+
function isTagged(value) {
|
|
1482
|
+
return Array.isArray(value) && Array.isArray(value.raw);
|
|
1483
|
+
}
|
|
1484
|
+
function replaceComments(origin, replacements) {
|
|
1485
|
+
const nodes = [...origin];
|
|
1486
|
+
const { length } = nodes;
|
|
1487
|
+
for (let nodeIndex = 0; nodeIndex < length; nodeIndex += 1) {
|
|
1488
|
+
const node = nodes[nodeIndex];
|
|
1489
|
+
if (node instanceof Comment) {
|
|
1490
|
+
const [, index] = EXPRESSION_COMMENT.exec(node.textContent) ?? [];
|
|
1491
|
+
if (index != null) node.replaceWith(replacements[Number(index)]);
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
if (node.hasChildNodes()) replaceComments(node.childNodes, replacements);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1239
1497
|
/**
|
|
1240
1498
|
* Sanitize one or more nodes, recursively
|
|
1241
1499
|
* @param value Node or nodes to sanitize
|
|
@@ -1245,112 +1503,15 @@ html.remove = (template) => {
|
|
|
1245
1503
|
function sanitize(value) {
|
|
1246
1504
|
return sanitizeNodes(Array.isArray(value) ? value : [value], 0);
|
|
1247
1505
|
}
|
|
1506
|
+
const COMMENT_INDEX = "<index>";
|
|
1507
|
+
const COMMENT_TEMPLATE = `<!--toretto.node:${COMMENT_INDEX}-->`;
|
|
1508
|
+
const EXPRESSION_COMMENT = /^toretto\.node:(\d+)$/;
|
|
1248
1509
|
const EXPRESSION_ID = /^[a-z][\w-]*$/i;
|
|
1249
1510
|
const PARSE_TYPE_HTML = "text/html";
|
|
1250
1511
|
const TEMPLATE_TAG = "template";
|
|
1251
1512
|
const TEMPORARY_ELEMENT = "<toretto-temporary></toretto-temporary>";
|
|
1513
|
+
const templates = new SizedMap(128);
|
|
1252
1514
|
let parser;
|
|
1253
|
-
|
|
1254
|
-
//#endregion
|
|
1255
|
-
//#region src/style.ts
|
|
1256
|
-
/**
|
|
1257
|
-
* Get a style from an element
|
|
1258
|
-
* @param element Element to get the style from
|
|
1259
|
-
* @param property Style name
|
|
1260
|
-
* @param computed Get the computed style? _(defaults to `false`)_
|
|
1261
|
-
* @returns Style value
|
|
1262
|
-
*/
|
|
1263
|
-
function getStyle(element, property, computed) {
|
|
1264
|
-
if (!isHTMLOrSVGElement(element) || typeof property !== "string") return;
|
|
1265
|
-
return getStyleValue(element, property, computed === true);
|
|
1266
|
-
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Get styles from an element
|
|
1269
|
-
* @param element Element to get the styles from
|
|
1270
|
-
* @param properties Styles to get
|
|
1271
|
-
* @param computed Get the computed styles? _(defaults to `false`)_
|
|
1272
|
-
* @returns Style values
|
|
1273
|
-
*/
|
|
1274
|
-
function getStyles(element, properties, computed) {
|
|
1275
|
-
const styles = {};
|
|
1276
|
-
if (!(isHTMLOrSVGElement(element) && Array.isArray(properties))) return styles;
|
|
1277
|
-
const { length } = properties;
|
|
1278
|
-
for (let index = 0; index < length; index += 1) {
|
|
1279
|
-
const property = properties[index];
|
|
1280
|
-
if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
|
|
1281
|
-
}
|
|
1282
|
-
return styles;
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Get the text direction of an element
|
|
1286
|
-
* @param element Element to get the text direction from
|
|
1287
|
-
* @param computed Get the computed text direction? _(defaults to `false`)_
|
|
1288
|
-
* @returns Text direction
|
|
1289
|
-
*/
|
|
1290
|
-
function getTextDirection(element, computed) {
|
|
1291
|
-
if (!(element instanceof Element)) return;
|
|
1292
|
-
const direction = element.getAttribute(ATTRIBUTE_DIRECTION);
|
|
1293
|
-
if (direction != null && EXPRESSION_DIRECTION.test(direction)) return direction.toLowerCase();
|
|
1294
|
-
const value = getStyleValue(element, "direction", computed === true);
|
|
1295
|
-
return value === "rtl" ? value : "ltr";
|
|
1296
|
-
}
|
|
1297
|
-
/**
|
|
1298
|
-
* Set a style on an element
|
|
1299
|
-
* @param element Element to set the style on
|
|
1300
|
-
* @param property Style name
|
|
1301
|
-
* @param value Style value
|
|
1302
|
-
*/
|
|
1303
|
-
function setStyle(element, property, value) {
|
|
1304
|
-
setElementValues(element, property, value, null, updateStyleProperty);
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Set styles on an element
|
|
1308
|
-
* @param element Element to set the styles on
|
|
1309
|
-
* @param styles Styles to set
|
|
1310
|
-
*/
|
|
1311
|
-
function setStyles(element, styles) {
|
|
1312
|
-
setElementValues(element, styles, null, null, updateStyleProperty);
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Toggle styles for an element
|
|
1316
|
-
* @param element Element to style
|
|
1317
|
-
* @param styles Styles to be set or removed
|
|
1318
|
-
* @returns Style toggler
|
|
1319
|
-
*/
|
|
1320
|
-
function toggleStyles(element, styles) {
|
|
1321
|
-
function toggle(set) {
|
|
1322
|
-
hasSet = set;
|
|
1323
|
-
let next;
|
|
1324
|
-
if (set) {
|
|
1325
|
-
values = getStyles(element, keys);
|
|
1326
|
-
next = styles;
|
|
1327
|
-
} else {
|
|
1328
|
-
next = { ...values };
|
|
1329
|
-
values = {};
|
|
1330
|
-
for (const key of keys) values[key] = void 0;
|
|
1331
|
-
}
|
|
1332
|
-
setStyles(element, next);
|
|
1333
|
-
}
|
|
1334
|
-
const keys = Object.keys(styles);
|
|
1335
|
-
let hasSet = false;
|
|
1336
|
-
let values = {};
|
|
1337
|
-
return {
|
|
1338
|
-
set() {
|
|
1339
|
-
if (!hasSet) toggle(true);
|
|
1340
|
-
},
|
|
1341
|
-
remove() {
|
|
1342
|
-
if (hasSet) toggle(false);
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
function updateStyleProperty(element, key, value) {
|
|
1347
|
-
updateElementValue(element, key, value, function(property, style) {
|
|
1348
|
-
this.style[property] = style;
|
|
1349
|
-
}, function(property) {
|
|
1350
|
-
this.style[property] = "";
|
|
1351
|
-
}, false, false);
|
|
1352
|
-
}
|
|
1353
|
-
const ATTRIBUTE_DIRECTION = "dir";
|
|
1354
|
-
const EXPRESSION_DIRECTION = /^(ltr|rtl)$/i;
|
|
1515
|
+
window.templates = templates;
|
|
1355
1516
|
//#endregion
|
|
1356
|
-
export { findElement as $, findElement, findElements as $$, findElements, booleanAttributes, dispatch, findAncestor, findRelatives, getAttribute, getAttributes, getData, getDistance, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode,
|
|
1517
|
+
export { findElement as $, findElement, findElements as $$, findElements, booleanAttributes, createElement, dispatch, findAncestor, findRelatives, getAttribute, getAttributes, getData, getDistance, getElementUnderPointer, getFocusable, getPosition, getProperties, getProperty, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setAttribute, setAttributes, setData, setProperties, setProperty, setStyle, setStyles, supportsTouch, toggleStyles };
|