@oscarpalmer/toretto 0.28.0 → 0.30.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/index.js +13 -1
- package/dist/html/index.js +63 -0
- package/dist/html/sanitize.js +40 -0
- package/dist/index.js +3 -6
- package/dist/internal/attribute.js +52 -25
- package/dist/toretto.full.js +760 -1026
- package/package.json +12 -12
- package/src/attribute/index.ts +87 -6
- package/src/{html.ts → html/index.ts} +52 -37
- package/src/html/sanitize.ts +83 -0
- package/src/index.ts +7 -2
- package/src/internal/attribute.ts +111 -121
- package/types/attribute/index.d.ts +57 -1
- package/types/{html.d.ts → html/index.d.ts} +4 -5
- package/types/html/sanitize.d.ts +2 -0
- package/types/index.d.ts +2 -2
- package/types/internal/attribute.d.ts +4 -55
- package/dist/html.js +0 -55
- package/dist/internal/sanitize.js +0 -30
- package/src/internal/sanitize.ts +0 -67
- package/types/internal/sanitize.d.ts +0 -13
package/dist/toretto.full.js
CHANGED
|
@@ -1,48 +1,34 @@
|
|
|
1
|
-
//
|
|
2
1
|
function getSupport() {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
if (typeof navigator.maxTouchPoints === 'number' && navigator.maxTouchPoints > 0) {
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
if (typeof navigator.msMaxTouchPoints === 'number' &&
|
|
19
|
-
navigator.msMaxTouchPoints > 0) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
//
|
|
2
|
+
if (window == null || navigator == null) return false;
|
|
3
|
+
if ("matchMedia" in window) {
|
|
4
|
+
const media = matchMedia?.("(pointer: coarse)");
|
|
5
|
+
if (typeof media?.matches === "boolean" && media.matches) return true;
|
|
6
|
+
}
|
|
7
|
+
if ("ontouchstart" in window) return true;
|
|
8
|
+
if (typeof navigator.maxTouchPoints === "number" && navigator.maxTouchPoints > 0) return true;
|
|
9
|
+
if (typeof navigator.msMaxTouchPoints === "number" && navigator.msMaxTouchPoints > 0) return true;
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
25
12
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
* Does the device support touch events?
|
|
14
|
+
*/
|
|
28
15
|
const supportsTouch = (() => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
return instance;
|
|
16
|
+
let support = getSupport();
|
|
17
|
+
const instance = Object.create({
|
|
18
|
+
get() {
|
|
19
|
+
return support;
|
|
20
|
+
},
|
|
21
|
+
update() {
|
|
22
|
+
support = getSupport();
|
|
23
|
+
return support;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(instance, "value", { get() {
|
|
27
|
+
return support;
|
|
28
|
+
} });
|
|
29
|
+
return instance;
|
|
45
30
|
})();
|
|
31
|
+
var touch_default = supportsTouch;
|
|
46
32
|
|
|
47
33
|
function isPlainObject(value) {
|
|
48
34
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -50,14 +36,28 @@ function isPlainObject(value) {
|
|
|
50
36
|
const prototype = Object.getPrototypeOf(value);
|
|
51
37
|
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
52
38
|
}
|
|
39
|
+
var TYPED_ARRAYS = new Set([
|
|
40
|
+
Int8Array,
|
|
41
|
+
Uint8Array,
|
|
42
|
+
Uint8ClampedArray,
|
|
43
|
+
Int16Array,
|
|
44
|
+
Uint16Array,
|
|
45
|
+
Int32Array,
|
|
46
|
+
Uint32Array,
|
|
47
|
+
Float32Array,
|
|
48
|
+
Float64Array,
|
|
49
|
+
BigInt64Array,
|
|
50
|
+
BigUint64Array
|
|
51
|
+
]);
|
|
53
52
|
|
|
54
53
|
function compact(array, strict) {
|
|
55
54
|
if (!Array.isArray(array)) return [];
|
|
56
55
|
const { length } = array;
|
|
56
|
+
const isStrict = strict ?? false;
|
|
57
57
|
const compacted = [];
|
|
58
58
|
for (let index = 0; index < length; index += 1) {
|
|
59
59
|
const item = array[index];
|
|
60
|
-
if (item != null) compacted.push(item);
|
|
60
|
+
if (isStrict && !!item || !isStrict && item != null) compacted.push(item);
|
|
61
61
|
}
|
|
62
62
|
return compacted;
|
|
63
63
|
}
|
|
@@ -79,19 +79,19 @@ function words(value) {
|
|
|
79
79
|
var EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
|
|
80
80
|
|
|
81
81
|
function isNullableOrWhitespace(value) {
|
|
82
|
-
return value == null || EXPRESSION_WHITESPACE.test(getString(value));
|
|
82
|
+
return value == null || EXPRESSION_WHITESPACE$1.test(getString(value));
|
|
83
83
|
}
|
|
84
|
-
var EXPRESSION_WHITESPACE = /^\s*$/;
|
|
84
|
+
var EXPRESSION_WHITESPACE$1 = /^\s*$/;
|
|
85
85
|
|
|
86
86
|
function camelCase(value) {
|
|
87
|
-
return toCase(value, "", true);
|
|
87
|
+
return toCase(value, "", true, false);
|
|
88
88
|
}
|
|
89
89
|
function capitalize(value) {
|
|
90
90
|
if (typeof value !== "string" || value.length === 0) return "";
|
|
91
91
|
return value.length === 1 ? value.toLocaleUpperCase() : `${value.charAt(0).toLocaleUpperCase()}${value.slice(1).toLocaleLowerCase()}`;
|
|
92
92
|
}
|
|
93
93
|
function kebabCase(value) {
|
|
94
|
-
return toCase(value, "-", false);
|
|
94
|
+
return toCase(value, "-", false, false);
|
|
95
95
|
}
|
|
96
96
|
function toCase(value, delimiter, capitalizeAny, capitalizeFirst) {
|
|
97
97
|
if (typeof value !== "string") return "";
|
|
@@ -107,7 +107,7 @@ function toCase(value, delimiter, capitalizeAny, capitalizeFirst) {
|
|
|
107
107
|
for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
|
|
108
108
|
const item = items[itemIndex];
|
|
109
109
|
if (item.length === 0) continue;
|
|
110
|
-
if (!capitalizeAny || itemCount === 0 && partIndex === 0 &&
|
|
110
|
+
if (!capitalizeAny || itemCount === 0 && partIndex === 0 && !capitalizeFirst) partResult.push(item.toLocaleLowerCase());
|
|
111
111
|
else partResult.push(capitalize(item));
|
|
112
112
|
itemCount += 1;
|
|
113
113
|
}
|
|
@@ -127,298 +127,198 @@ function parse(value, reviver) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
* Is the value an event target?
|
|
131
|
+
* @param value Value to check
|
|
132
|
+
* @returns `true` if it's an event target, otherwise `false`
|
|
133
|
+
*/
|
|
134
134
|
function isEventTarget(value) {
|
|
135
|
-
|
|
136
|
-
value != null &&
|
|
137
|
-
typeof value.addEventListener === 'function' &&
|
|
138
|
-
typeof value.removeEventListener === 'function' &&
|
|
139
|
-
typeof value.dispatchEvent === 'function');
|
|
135
|
+
return typeof value === "object" && value != null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function" && typeof value.dispatchEvent === "function";
|
|
140
136
|
}
|
|
141
137
|
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
* Is the value an HTML or SVG element?
|
|
139
|
+
* @param value Value to check
|
|
140
|
+
* @returns `true` if it's an HTML or SVG element, otherwise `false`
|
|
141
|
+
*/
|
|
146
142
|
function isHTMLOrSVGElement(value) {
|
|
147
|
-
|
|
143
|
+
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
148
144
|
}
|
|
149
145
|
|
|
146
|
+
function badAttributeHandler(name, value) {
|
|
147
|
+
if (name == null || value == null) return true;
|
|
148
|
+
if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
|
|
149
|
+
if (EXPRESSION_SKIP_NAME.test(name) || EXPRESSION_URI_VALUE.test(value) || isValidSourceAttribute(name, value)) return false;
|
|
150
|
+
return EXPRESSION_DATA_OR_SCRIPT.test(value);
|
|
151
|
+
}
|
|
152
|
+
function booleanAttributeHandler(name, value) {
|
|
153
|
+
if (name == null || value == null) return true;
|
|
154
|
+
if (!booleanAttributesSet.has(name)) return false;
|
|
155
|
+
const normalized = value.toLowerCase().trim();
|
|
156
|
+
return !(normalized.length === 0 || normalized === name);
|
|
157
|
+
}
|
|
158
|
+
function decodeAttribute(value) {
|
|
159
|
+
textArea ??= document.createElement("textarea");
|
|
160
|
+
textArea.innerHTML = value;
|
|
161
|
+
return decodeURIComponent(textArea.value);
|
|
162
|
+
}
|
|
163
|
+
function handleAttribute(callback, decode, first, second) {
|
|
164
|
+
let name;
|
|
165
|
+
let value;
|
|
166
|
+
if (isAttribute(first)) {
|
|
167
|
+
name = first.name;
|
|
168
|
+
value = String(first.value);
|
|
169
|
+
} else if (typeof first === "string" && typeof second === "string") {
|
|
170
|
+
name = first;
|
|
171
|
+
value = second;
|
|
172
|
+
}
|
|
173
|
+
if (decode && value != null) value = decodeAttribute(value);
|
|
174
|
+
return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
|
|
175
|
+
}
|
|
150
176
|
function isAttribute(value) {
|
|
151
|
-
|
|
152
|
-
(isPlainObject(value) &&
|
|
153
|
-
typeof value.name === 'string' &&
|
|
154
|
-
typeof value.value === 'string'));
|
|
177
|
+
return value instanceof Attr || isPlainObject(value) && typeof value.name === "string" && typeof value.value === "string";
|
|
155
178
|
}
|
|
156
|
-
function isBadAttribute(first, second) {
|
|
157
|
-
|
|
158
|
-
EXPRESSION_ON_PREFIX.test(attribute.name) ||
|
|
159
|
-
(EXPRESSION_SOURCE_PREFIX.test(attribute.name) &&
|
|
160
|
-
EXPRESSION_VALUE_PREFIX.test(String(attribute.value))), first, second);
|
|
179
|
+
function isBadAttribute$1(first, second, decode) {
|
|
180
|
+
return handleAttribute(badAttributeHandler, decode, first, second);
|
|
161
181
|
}
|
|
162
|
-
function isBooleanAttribute(
|
|
163
|
-
|
|
182
|
+
function isBooleanAttribute$1(first, decode) {
|
|
183
|
+
return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
|
|
164
184
|
}
|
|
165
|
-
function isEmptyNonBooleanAttribute(first, second) {
|
|
166
|
-
|
|
167
|
-
!booleanAttributes.includes(attribute.name) &&
|
|
168
|
-
String(attribute.value).trim().length === 0, first, second);
|
|
185
|
+
function isEmptyNonBooleanAttribute$1(first, second, decode) {
|
|
186
|
+
return handleAttribute((name, value) => name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0, decode, first, second);
|
|
169
187
|
}
|
|
170
|
-
function isInvalidBooleanAttribute(first, second) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
function isValidAttribute(callback, first, second) {
|
|
186
|
-
let attribute;
|
|
187
|
-
if (isAttribute(first)) {
|
|
188
|
-
attribute = first;
|
|
189
|
-
}
|
|
190
|
-
else if (typeof first === 'string' && typeof second === 'string') {
|
|
191
|
-
attribute = { name: first, value: second };
|
|
192
|
-
}
|
|
193
|
-
return callback(attribute);
|
|
194
|
-
}
|
|
195
|
-
function updateAttribute(element, name, value) {
|
|
196
|
-
const isBoolean = booleanAttributes.includes(name.toLowerCase());
|
|
197
|
-
if (isBoolean) {
|
|
198
|
-
updateProperty(element, name, value);
|
|
199
|
-
}
|
|
200
|
-
if (isBoolean ? value !== true : value == null) {
|
|
201
|
-
element.removeAttribute(name);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
element.setAttribute(name, isBoolean ? '' : getString(value));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
function updateProperty(element, name, value) {
|
|
208
|
-
const actual = name.toLowerCase();
|
|
209
|
-
element[actual] =
|
|
210
|
-
value === '' || (typeof value === 'string' && value.toLowerCase() === actual) || value === true;
|
|
211
|
-
}
|
|
212
|
-
function updateValue(element, first, second) {
|
|
213
|
-
if (!isHTMLOrSVGElement(element)) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (isProperty(first)) {
|
|
217
|
-
updateAttribute(element, first.name, first.value);
|
|
218
|
-
}
|
|
219
|
-
else if (typeof first === 'string') {
|
|
220
|
-
updateAttribute(element, first, second);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
function updateValues(element, values) {
|
|
224
|
-
if (!isHTMLOrSVGElement(element)) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const isArray = Array.isArray(values);
|
|
228
|
-
const entries = Object.entries(values);
|
|
229
|
-
const { length } = entries;
|
|
230
|
-
for (let index = 0; index < length; index += 1) {
|
|
231
|
-
const entry = entries[index];
|
|
232
|
-
if (isArray) {
|
|
233
|
-
updateAttribute(element, entry[1].name, entry[1].value);
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
updateAttribute(element, entry[0], entry[1]);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
//
|
|
241
|
-
const EXPRESSION_ON_PREFIX = /^on/i;
|
|
242
|
-
const EXPRESSION_SOURCE_PREFIX = /^(href|src|xlink:href)$/i;
|
|
243
|
-
const EXPRESSION_VALUE_PREFIX = /(data:text\/html|javascript:)/i;
|
|
188
|
+
function isInvalidBooleanAttribute$1(first, second, decode) {
|
|
189
|
+
return handleAttribute(booleanAttributeHandler, decode, first, second);
|
|
190
|
+
}
|
|
191
|
+
function isValidSourceAttribute(name, value) {
|
|
192
|
+
return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
|
|
193
|
+
}
|
|
194
|
+
const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
|
|
195
|
+
const EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
|
|
196
|
+
const EXPRESSION_EVENT_NAME = /^on/i;
|
|
197
|
+
const EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
|
|
198
|
+
const EXPRESSION_SOURCE_NAME = /^src$/i;
|
|
199
|
+
const EXPRESSION_SOURCE_VALUE = /^data:/i;
|
|
200
|
+
const EXPRESSION_URI_VALUE = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
201
|
+
const EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
244
202
|
/**
|
|
245
|
-
|
|
246
|
-
|
|
203
|
+
* List of boolean attributes
|
|
204
|
+
*/
|
|
247
205
|
const booleanAttributes = Object.freeze([
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
206
|
+
"async",
|
|
207
|
+
"autofocus",
|
|
208
|
+
"autoplay",
|
|
209
|
+
"checked",
|
|
210
|
+
"controls",
|
|
211
|
+
"default",
|
|
212
|
+
"defer",
|
|
213
|
+
"disabled",
|
|
214
|
+
"formnovalidate",
|
|
215
|
+
"hidden",
|
|
216
|
+
"inert",
|
|
217
|
+
"ismap",
|
|
218
|
+
"itemscope",
|
|
219
|
+
"loop",
|
|
220
|
+
"multiple",
|
|
221
|
+
"muted",
|
|
222
|
+
"nomodule",
|
|
223
|
+
"novalidate",
|
|
224
|
+
"open",
|
|
225
|
+
"playsinline",
|
|
226
|
+
"readonly",
|
|
227
|
+
"required",
|
|
228
|
+
"reversed",
|
|
229
|
+
"selected"
|
|
272
230
|
]);
|
|
231
|
+
const booleanAttributesSet = new Set(booleanAttributes);
|
|
232
|
+
const formElement = document.createElement("form");
|
|
233
|
+
let textArea;
|
|
273
234
|
|
|
274
235
|
function getBoolean(value, defaultValue) {
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
function getAttributeValue(element, name, parseValue) {
|
|
278
|
-
const normalized = kebabCase(name);
|
|
279
|
-
const attribute = element.attributes[normalized];
|
|
280
|
-
const value = attribute instanceof Attr ? attribute.value : undefined;
|
|
281
|
-
return EXPRESSION_DATA_PREFIX.test(normalized) && typeof value === 'string' && parseValue
|
|
282
|
-
? (parse(value) ?? value)
|
|
283
|
-
: value;
|
|
236
|
+
return typeof value === "boolean" ? value : defaultValue ?? false;
|
|
284
237
|
}
|
|
285
238
|
function getStyleValue(element, property, computed) {
|
|
286
|
-
|
|
287
|
-
|
|
239
|
+
const name = camelCase(property);
|
|
240
|
+
return computed ? getComputedStyle(element)[name] : element.style[name];
|
|
288
241
|
}
|
|
289
242
|
const EXPRESSION_DATA_PREFIX = /^data-/i;
|
|
290
243
|
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
return getAttributeValue(element, name, parseValues !== false);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Get specific attributes from an element
|
|
298
|
-
* @param element Element to get attributes from
|
|
299
|
-
* @param names Attribute names
|
|
300
|
-
* @param parseData Parse data values? _(defaults to `true`)_
|
|
301
|
-
* @returns Object of named attributes
|
|
302
|
-
*/
|
|
303
|
-
function getAttributes(element, names, parseData) {
|
|
304
|
-
const attributes = {};
|
|
305
|
-
if (!(isHTMLOrSVGElement(element) && Array.isArray(names))) {
|
|
306
|
-
return attributes;
|
|
307
|
-
}
|
|
308
|
-
const shouldParse = parseData !== false;
|
|
309
|
-
const { length } = names;
|
|
310
|
-
for (let index = 0; index < length; index += 1) {
|
|
311
|
-
const name = names[index];
|
|
312
|
-
if (typeof name === 'string') {
|
|
313
|
-
attributes[name] = getAttributeValue(element, name, shouldParse);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return attributes;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function setAttribute(element, first, second) {
|
|
320
|
-
updateValue(element, first, second);
|
|
244
|
+
function isBadAttribute(first, second) {
|
|
245
|
+
return isBadAttribute$1(first, second, true);
|
|
321
246
|
}
|
|
322
|
-
function
|
|
323
|
-
|
|
247
|
+
function isBooleanAttribute(first) {
|
|
248
|
+
return isBooleanAttribute$1(first, true);
|
|
324
249
|
}
|
|
325
|
-
function
|
|
326
|
-
|
|
250
|
+
function isEmptyNonBooleanAttribute(first, second) {
|
|
251
|
+
return isEmptyNonBooleanAttribute$1(first, second, true);
|
|
327
252
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
253
|
+
function isInvalidBooleanAttribute(first, second) {
|
|
254
|
+
return isInvalidBooleanAttribute$1(first, second, true);
|
|
330
255
|
}
|
|
331
256
|
|
|
332
257
|
/**
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
258
|
+
* Is the value a child node?
|
|
259
|
+
* @param value Value to check
|
|
260
|
+
* @returns `true` if it's a child node, otherwise `false`
|
|
261
|
+
*/
|
|
337
262
|
function isChildNode(value) {
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
function isInDocument(node, document) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return node.ownerDocument?.contains(node) ?? true;
|
|
346
|
-
}
|
|
347
|
-
return node.ownerDocument == null
|
|
348
|
-
? node === document
|
|
349
|
-
: node.ownerDocument === document && document.contains(node);
|
|
350
|
-
}
|
|
351
|
-
//
|
|
263
|
+
return value instanceof Node && CHILD_NODE_TYPES.has(value.nodeType);
|
|
264
|
+
}
|
|
265
|
+
function isInDocument(node, document$1) {
|
|
266
|
+
if (!(node instanceof Node)) return false;
|
|
267
|
+
if (!(document$1 instanceof Document)) return node.ownerDocument?.contains(node) ?? true;
|
|
268
|
+
return node.ownerDocument == null ? node === document$1 : node.ownerDocument === document$1 && document$1.contains(node);
|
|
269
|
+
}
|
|
352
270
|
const CHILD_NODE_TYPES = new Set([
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
271
|
+
Node.ELEMENT_NODE,
|
|
272
|
+
Node.TEXT_NODE,
|
|
273
|
+
Node.PROCESSING_INSTRUCTION_NODE,
|
|
274
|
+
Node.COMMENT_NODE,
|
|
275
|
+
Node.DOCUMENT_TYPE_NODE
|
|
358
276
|
]);
|
|
359
277
|
|
|
360
278
|
function setElementValues(element, first, second, callback) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
else if (typeof first === 'string') {
|
|
373
|
-
callback(element, first, second);
|
|
374
|
-
}
|
|
279
|
+
if (!isHTMLOrSVGElement(element)) return;
|
|
280
|
+
if (isPlainObject(first)) {
|
|
281
|
+
const entries = Object.entries(first);
|
|
282
|
+
const { length } = entries;
|
|
283
|
+
for (let index = 0; index < length; index += 1) {
|
|
284
|
+
const [key, value] = entries[index];
|
|
285
|
+
callback(element, key, value);
|
|
286
|
+
}
|
|
287
|
+
} else if (typeof first === "string") callback(element, first, second);
|
|
375
288
|
}
|
|
376
289
|
function updateElementValue(element, key, value, set, remove, json) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
382
|
-
}
|
|
290
|
+
if (isNullableOrWhitespace(value)) remove.call(element, key);
|
|
291
|
+
else set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
383
292
|
}
|
|
384
293
|
|
|
385
294
|
function getData(element, keys, parseValues) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (value == null) {
|
|
403
|
-
data[key] = undefined;
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
data[key] = shouldParse ? parse(value) : value;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return data;
|
|
295
|
+
if (!isHTMLOrSVGElement(element)) return;
|
|
296
|
+
const shouldParse = parseValues !== false;
|
|
297
|
+
if (typeof keys === "string") {
|
|
298
|
+
const value = element.dataset[keys];
|
|
299
|
+
if (value === void 0) return;
|
|
300
|
+
return shouldParse ? parse(value) : value;
|
|
301
|
+
}
|
|
302
|
+
const { length } = keys;
|
|
303
|
+
const data = {};
|
|
304
|
+
for (let index = 0; index < length; index += 1) {
|
|
305
|
+
const key = keys[index];
|
|
306
|
+
const value = element.dataset[key];
|
|
307
|
+
if (value == null) data[key] = void 0;
|
|
308
|
+
else data[key] = shouldParse ? parse(value) : value;
|
|
309
|
+
}
|
|
310
|
+
return data;
|
|
410
311
|
}
|
|
411
312
|
function getName(original) {
|
|
412
|
-
|
|
313
|
+
return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original).replace(EXPRESSION_DATA_PREFIX, "")}`;
|
|
413
314
|
}
|
|
414
315
|
function setData(element, first, second) {
|
|
415
|
-
|
|
316
|
+
setElementValues(element, first, second, updateDataAttribute);
|
|
416
317
|
}
|
|
417
318
|
function updateDataAttribute(element, key, value) {
|
|
418
|
-
|
|
319
|
+
updateElementValue(element, getName(key), value, element.setAttribute, element.removeAttribute, true);
|
|
419
320
|
}
|
|
420
|
-
|
|
421
|
-
const ATTRIBUTE_DATA_PREFIX = 'data-';
|
|
321
|
+
const ATTRIBUTE_DATA_PREFIX = "data-";
|
|
422
322
|
|
|
423
323
|
function calculate() {
|
|
424
324
|
return new Promise((resolve) => {
|
|
@@ -436,881 +336,715 @@ function calculate() {
|
|
|
436
336
|
function noop() {}
|
|
437
337
|
var CALCULATION_TOTAL = 10;
|
|
438
338
|
var CALCULATION_TRIM = 4;
|
|
339
|
+
let milliseconds = 1e3 / 60;
|
|
439
340
|
calculate().then((value) => {
|
|
341
|
+
milliseconds = value;
|
|
440
342
|
});
|
|
441
343
|
|
|
442
|
-
function addDelegatedHandler(document, type, name, passive) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
passive,
|
|
451
|
-
});
|
|
344
|
+
function addDelegatedHandler(document$1, type, name, passive) {
|
|
345
|
+
const count = `${name}${COUNT_SUFFIX}`;
|
|
346
|
+
if (document$1[count] != null) {
|
|
347
|
+
document$1[count] += 1;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
document$1[count] = 1;
|
|
351
|
+
document$1.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, { passive });
|
|
452
352
|
}
|
|
453
353
|
function addDelegatedListener(target, type, name, listener, passive) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
354
|
+
target[name] ??= /* @__PURE__ */ new Set();
|
|
355
|
+
target[name]?.add(listener);
|
|
356
|
+
addDelegatedHandler(document, type, name, passive);
|
|
357
|
+
return () => {
|
|
358
|
+
removeDelegatedListener(target, type, name, listener, passive);
|
|
359
|
+
};
|
|
460
360
|
}
|
|
461
361
|
function delegatedEventHandler(event) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
362
|
+
const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
363
|
+
const items = event.composedPath();
|
|
364
|
+
const { length } = items;
|
|
365
|
+
Object.defineProperty(event, "target", {
|
|
366
|
+
configurable: true,
|
|
367
|
+
value: items[0]
|
|
368
|
+
});
|
|
369
|
+
for (let index = 0; index < length; index += 1) {
|
|
370
|
+
const item = items[index];
|
|
371
|
+
const listeners = item[key];
|
|
372
|
+
if (item.disabled || listeners == null) continue;
|
|
373
|
+
Object.defineProperty(event, "currentTarget", {
|
|
374
|
+
configurable: true,
|
|
375
|
+
value: item
|
|
376
|
+
});
|
|
377
|
+
for (const listener of listeners) {
|
|
378
|
+
listener.call(item, event);
|
|
379
|
+
if (event.cancelBubble) return;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
486
382
|
}
|
|
487
383
|
function getDelegatedName(target, type, options) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const count = `${name}${COUNT_SUFFIX}`;
|
|
498
|
-
document[count] -= 1;
|
|
499
|
-
if (document[count] < 1) {
|
|
500
|
-
document[count] = undefined;
|
|
501
|
-
document.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
|
|
502
|
-
}
|
|
384
|
+
if (isEventTarget(target) && EVENT_TYPES.has(type) && !options.capture && !options.once && options.signal == null) return `${EVENT_PREFIX}${type}${options.passive ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
385
|
+
}
|
|
386
|
+
function removeDelegatedHandler(document$1, type, name, passive) {
|
|
387
|
+
const count = `${name}${COUNT_SUFFIX}`;
|
|
388
|
+
document$1[count] -= 1;
|
|
389
|
+
if (document$1[count] < 1) {
|
|
390
|
+
document$1[count] = void 0;
|
|
391
|
+
document$1.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
|
|
392
|
+
}
|
|
503
393
|
}
|
|
504
394
|
function removeDelegatedListener(target, type, name, listener, passive) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
//
|
|
517
|
-
const COUNT_SUFFIX = '.count';
|
|
518
|
-
const EVENT_PREFIX = '@';
|
|
519
|
-
const EVENT_SUFFIX_ACTIVE = ':active';
|
|
520
|
-
const EVENT_SUFFIX_PASSIVE = ':passive';
|
|
395
|
+
const handlers = target[name];
|
|
396
|
+
if (handlers == null || !handlers.has(listener)) return false;
|
|
397
|
+
handlers.delete(listener);
|
|
398
|
+
if (handlers.size === 0) target[name] = void 0;
|
|
399
|
+
removeDelegatedHandler(document, type, name, passive);
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
const COUNT_SUFFIX = ".count";
|
|
403
|
+
const EVENT_PREFIX = "@";
|
|
404
|
+
const EVENT_SUFFIX_ACTIVE = ":active";
|
|
405
|
+
const EVENT_SUFFIX_PASSIVE = ":passive";
|
|
521
406
|
const EVENT_TYPES = new Set([
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
407
|
+
"beforeinput",
|
|
408
|
+
"click",
|
|
409
|
+
"dblclick",
|
|
410
|
+
"contextmenu",
|
|
411
|
+
"focusin",
|
|
412
|
+
"focusout",
|
|
413
|
+
"input",
|
|
414
|
+
"keydown",
|
|
415
|
+
"keyup",
|
|
416
|
+
"mousedown",
|
|
417
|
+
"mousemove",
|
|
418
|
+
"mouseout",
|
|
419
|
+
"mouseover",
|
|
420
|
+
"mouseup",
|
|
421
|
+
"pointerdown",
|
|
422
|
+
"pointermove",
|
|
423
|
+
"pointerout",
|
|
424
|
+
"pointerover",
|
|
425
|
+
"pointerup",
|
|
426
|
+
"touchend",
|
|
427
|
+
"touchmove",
|
|
428
|
+
"touchstart"
|
|
544
429
|
]);
|
|
545
430
|
const HANDLER_ACTIVE = delegatedEventHandler.bind(false);
|
|
546
431
|
const HANDLER_PASSIVE = delegatedEventHandler.bind(true);
|
|
547
432
|
|
|
548
|
-
//
|
|
549
433
|
function createDispatchOptions(options) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
434
|
+
return {
|
|
435
|
+
bubbles: getBoolean(options?.bubbles, true),
|
|
436
|
+
cancelable: getBoolean(options?.cancelable, true),
|
|
437
|
+
composed: getBoolean(options?.composed)
|
|
438
|
+
};
|
|
555
439
|
}
|
|
556
440
|
function createEvent(type, options) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
return new Event(type, createDispatchOptions(hasOptions ? options : {}));
|
|
441
|
+
const hasOptions = isPlainObject(options);
|
|
442
|
+
if (hasOptions && PROPERTY_DETAIL in options) return new CustomEvent(type, {
|
|
443
|
+
...createDispatchOptions(options),
|
|
444
|
+
detail: options?.detail
|
|
445
|
+
});
|
|
446
|
+
return new Event(type, createDispatchOptions(hasOptions ? options : {}));
|
|
565
447
|
}
|
|
566
448
|
function createEventOptions(options) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
449
|
+
return {
|
|
450
|
+
capture: getBoolean(options?.capture),
|
|
451
|
+
once: getBoolean(options?.once),
|
|
452
|
+
passive: getBoolean(options?.passive, true),
|
|
453
|
+
signal: options?.signal instanceof AbortSignal ? options.signal : void 0
|
|
454
|
+
};
|
|
573
455
|
}
|
|
574
456
|
function dispatch(target, type, options) {
|
|
575
|
-
|
|
576
|
-
target.dispatchEvent(createEvent(type, options));
|
|
577
|
-
}
|
|
457
|
+
if (isEventTarget(target) && typeof type === "string") target.dispatchEvent(createEvent(type, options));
|
|
578
458
|
}
|
|
579
459
|
/**
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
460
|
+
* Get the X- and Y-coordinates from a pointer event
|
|
461
|
+
* @param event Pointer event
|
|
462
|
+
* @returns X- and Y-coordinates
|
|
463
|
+
*/
|
|
584
464
|
function getPosition(event) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
465
|
+
let x;
|
|
466
|
+
let y;
|
|
467
|
+
if (event instanceof MouseEvent) {
|
|
468
|
+
x = event.clientX;
|
|
469
|
+
y = event.clientY;
|
|
470
|
+
} else if (event instanceof TouchEvent) {
|
|
471
|
+
x = event.touches[0]?.clientX;
|
|
472
|
+
y = event.touches[0]?.clientY;
|
|
473
|
+
}
|
|
474
|
+
return typeof x === "number" && typeof y === "number" ? {
|
|
475
|
+
x,
|
|
476
|
+
y
|
|
477
|
+
} : void 0;
|
|
596
478
|
}
|
|
597
479
|
/**
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
480
|
+
* Remove an event listener
|
|
481
|
+
* @param target Event target
|
|
482
|
+
* @param type Type of event
|
|
483
|
+
* @param listener Event listener
|
|
484
|
+
* @param options Options for event
|
|
485
|
+
*/
|
|
604
486
|
function off(target, type, listener, options) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const delegated = getDelegatedName(target, type, extended);
|
|
610
|
-
if (delegated == null ||
|
|
611
|
-
!removeDelegatedListener(target, type, delegated, listener, extended.passive)) {
|
|
612
|
-
target.removeEventListener(type, listener, extended);
|
|
613
|
-
}
|
|
487
|
+
if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return;
|
|
488
|
+
const extended = createEventOptions(options);
|
|
489
|
+
const delegated = getDelegatedName(target, type, extended);
|
|
490
|
+
if (delegated == null || !removeDelegatedListener(target, type, delegated, listener, extended.passive)) target.removeEventListener(type, listener, extended);
|
|
614
491
|
}
|
|
615
492
|
function on(target, type, listener, options) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
return () => {
|
|
629
|
-
target.removeEventListener(type, listener, extended);
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
//
|
|
633
|
-
const PROPERTY_DETAIL = 'detail';
|
|
493
|
+
if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
|
|
494
|
+
const extended = createEventOptions(options);
|
|
495
|
+
const delegated = getDelegatedName(target, type, extended);
|
|
496
|
+
if (delegated != null) return addDelegatedListener(target, type, delegated, listener, extended.passive);
|
|
497
|
+
target.addEventListener(type, listener, extended);
|
|
498
|
+
if (extended.once) return noop;
|
|
499
|
+
return () => {
|
|
500
|
+
target.removeEventListener(type, listener, extended);
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const PROPERTY_DETAIL = "detail";
|
|
634
504
|
|
|
635
505
|
/**
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
506
|
+
* - Get the distance between two elements _(i.e., the amount of nodes of between them)_
|
|
507
|
+
* - If the distance cannot be calculated, `-1` is returned
|
|
508
|
+
*/
|
|
639
509
|
function getDistanceBetweenElements(origin, target) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
return Math.abs(children.indexOf(origin) - children.indexOf(target));
|
|
647
|
-
}
|
|
648
|
-
const beforeOrInside = !!(comparison & 2 || comparison & 8);
|
|
649
|
-
if (beforeOrInside || !!(comparison & 4 || comparison & 16)) {
|
|
650
|
-
return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
|
|
651
|
-
}
|
|
510
|
+
if (origin === target || origin.parentElement === target) return 0;
|
|
511
|
+
const comparison = origin.compareDocumentPosition(target);
|
|
512
|
+
const children = [...origin.parentElement?.children ?? []];
|
|
513
|
+
if (children.includes(target)) return Math.abs(children.indexOf(origin) - children.indexOf(target));
|
|
514
|
+
const beforeOrInside = !!(comparison & 2 || comparison & 8);
|
|
515
|
+
if (beforeOrInside || !!(comparison & 4 || comparison & 16)) return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
|
|
652
516
|
}
|
|
653
517
|
/**
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
518
|
+
* Find the closest ancestor element that matches the selector _(string or callback)_
|
|
519
|
+
*
|
|
520
|
+
* - If no match is found, `null` is returned
|
|
521
|
+
* - _(If you want to search upwards, downwards, and sideways, use {@link findRelatives})_
|
|
522
|
+
* @param origin Element to start from
|
|
523
|
+
* @param selector Selector to match
|
|
524
|
+
* @returns Found ancestor or `null`
|
|
525
|
+
*/
|
|
662
526
|
function findAncestor(origin, selector) {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
return origin;
|
|
677
|
-
}
|
|
678
|
-
let parent = origin.parentElement;
|
|
679
|
-
while (parent != null && !selector(parent)) {
|
|
680
|
-
if (parent === document.body) {
|
|
681
|
-
return null;
|
|
682
|
-
}
|
|
683
|
-
parent = parent.parentElement;
|
|
684
|
-
}
|
|
685
|
-
return parent;
|
|
527
|
+
if (!(origin instanceof Element) || selector == null) return null;
|
|
528
|
+
if (typeof selector === "string") {
|
|
529
|
+
if (origin.matches?.(selector)) return origin;
|
|
530
|
+
return origin.closest(selector);
|
|
531
|
+
}
|
|
532
|
+
if (typeof selector !== "function") return null;
|
|
533
|
+
if (selector(origin)) return origin;
|
|
534
|
+
let parent = origin.parentElement;
|
|
535
|
+
while (parent != null && !selector(parent)) {
|
|
536
|
+
if (parent === document.body) return null;
|
|
537
|
+
parent = parent.parentElement;
|
|
538
|
+
}
|
|
539
|
+
return parent;
|
|
686
540
|
}
|
|
687
541
|
/**
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
542
|
+
* Finds the closest elements to the origin element that matches the selector
|
|
543
|
+
*
|
|
544
|
+
* - Traverses up, down, and sideways in the _DOM_-tree
|
|
545
|
+
* - _(If you only want to traverse up, use {@link findAncestor})_
|
|
546
|
+
* @param origin Element to start from
|
|
547
|
+
* @param selector Selector to match
|
|
548
|
+
* @param context Context to search within
|
|
549
|
+
* @returns Found elements
|
|
550
|
+
*/
|
|
697
551
|
function findRelatives(origin, selector, context) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
for (let index = 0; index < length; index += 1) {
|
|
719
|
-
const element = elements[index];
|
|
720
|
-
const distance = getDistanceBetweenElements(origin, element) ?? -1;
|
|
721
|
-
if (distance > -1) {
|
|
722
|
-
if (minimum == null || distance < minimum) {
|
|
723
|
-
minimum = distance;
|
|
724
|
-
}
|
|
725
|
-
distances.push({
|
|
726
|
-
distance,
|
|
727
|
-
element,
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
return minimum == null
|
|
732
|
-
? []
|
|
733
|
-
: distances.filter(found => found.distance === minimum).map(found => found.element);
|
|
552
|
+
if (!(origin instanceof Element) || typeof selector !== "string") return [];
|
|
553
|
+
if (origin.matches(selector)) return [origin];
|
|
554
|
+
const elements = [...(context instanceof Document || context instanceof Element ? context : document).querySelectorAll(selector)];
|
|
555
|
+
const { length } = elements;
|
|
556
|
+
if (length === 0) return [];
|
|
557
|
+
if (length === 1) return [elements[0]];
|
|
558
|
+
const distances = [];
|
|
559
|
+
let minimum;
|
|
560
|
+
for (let index = 0; index < length; index += 1) {
|
|
561
|
+
const element = elements[index];
|
|
562
|
+
const distance = getDistanceBetweenElements(origin, element) ?? -1;
|
|
563
|
+
if (distance > -1) {
|
|
564
|
+
if (minimum == null || distance < minimum) minimum = distance;
|
|
565
|
+
distances.push({
|
|
566
|
+
distance,
|
|
567
|
+
element
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return minimum == null ? [] : distances.filter((found) => found.distance === minimum).map((found) => found.element);
|
|
734
572
|
}
|
|
735
573
|
function traverse(from, to) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
return traversed === -1
|
|
755
|
-
? -1
|
|
756
|
-
: distance + Math.abs(index - children.indexOf(current)) + traversed;
|
|
757
|
-
}
|
|
758
|
-
current = parent;
|
|
759
|
-
distance += 1;
|
|
760
|
-
parent = parent.parentElement;
|
|
761
|
-
}
|
|
574
|
+
let index = [...to.children].indexOf(from);
|
|
575
|
+
if (index > -1) return index + 1;
|
|
576
|
+
let current = from;
|
|
577
|
+
let distance = 0;
|
|
578
|
+
let parent = from.parentElement;
|
|
579
|
+
while (parent != null) {
|
|
580
|
+
if (parent === to) return distance + 1;
|
|
581
|
+
const children = [...parent.children ?? []];
|
|
582
|
+
if (children.includes(to)) return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
|
|
583
|
+
index = children.findIndex((child) => child.contains(to));
|
|
584
|
+
if (index > -1) {
|
|
585
|
+
const traversed = traverse(current, children[index]) ?? -1;
|
|
586
|
+
return traversed === -1 ? -1 : distance + Math.abs(index - children.indexOf(current)) + traversed;
|
|
587
|
+
}
|
|
588
|
+
current = parent;
|
|
589
|
+
distance += 1;
|
|
590
|
+
parent = parent.parentElement;
|
|
591
|
+
}
|
|
762
592
|
}
|
|
763
593
|
|
|
764
594
|
/**
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
595
|
+
* Find the first element that matches the selector
|
|
596
|
+
* @param selector Selector to find element for
|
|
597
|
+
* @param context Context to search within _(defaults to `document`)_
|
|
598
|
+
* @returns Found element or `null`
|
|
599
|
+
*/
|
|
770
600
|
function findElement(selector, context) {
|
|
771
|
-
|
|
601
|
+
return findElementOrElements(selector, context, true);
|
|
772
602
|
}
|
|
773
603
|
function findElementOrElements(selector, context, single) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
let array;
|
|
782
|
-
if (Array.isArray(selector)) {
|
|
783
|
-
array = selector;
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
array = selector instanceof Node ? [selector] : [...selector];
|
|
787
|
-
}
|
|
788
|
-
return findElementOrElementsFromNodes(array, context, contexts);
|
|
604
|
+
const callback = single ? QUERY_SELECTOR_SINGLE : QUERY_SELECTOR_ALL;
|
|
605
|
+
const contexts = context == null ? [document] : findElementOrElements(context, void 0, false).filter(isContext);
|
|
606
|
+
if (typeof selector === "string") return findElementOrElementsForSelector(selector, contexts, callback, single);
|
|
607
|
+
let array;
|
|
608
|
+
if (Array.isArray(selector)) array = selector;
|
|
609
|
+
else array = selector instanceof Node ? [selector] : [...selector];
|
|
610
|
+
return findElementOrElementsFromNodes(array, context, contexts);
|
|
789
611
|
}
|
|
790
612
|
function findElementOrElementsForSelector(selector, contexts, callback, single) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
}
|
|
803
|
-
return single ? null : result.filter((value, index, array) => array.indexOf(value) === index);
|
|
613
|
+
const { length } = contexts;
|
|
614
|
+
const result = [];
|
|
615
|
+
for (let index = 0; index < length; index += 1) {
|
|
616
|
+
const value = contexts[index][callback](selector);
|
|
617
|
+
if (single) {
|
|
618
|
+
if (value == null) continue;
|
|
619
|
+
return value;
|
|
620
|
+
}
|
|
621
|
+
result.push(...Array.from(value));
|
|
622
|
+
}
|
|
623
|
+
return single ? null : result.filter((value, index, array) => array.indexOf(value) === index);
|
|
804
624
|
}
|
|
805
625
|
function findElementOrElementsFromNodes(array, context, contexts) {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
if (element != null &&
|
|
819
|
-
(context == null ||
|
|
820
|
-
contexts.length === 0 ||
|
|
821
|
-
contexts.some(context => context === element || context.contains(element))) &&
|
|
822
|
-
!result.includes(element)) {
|
|
823
|
-
result.push(element);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
return result;
|
|
626
|
+
const result = [];
|
|
627
|
+
const nodes = array.filter((node) => node instanceof Node);
|
|
628
|
+
const { length } = nodes;
|
|
629
|
+
for (let index = 0; index < length; index += 1) {
|
|
630
|
+
const node = nodes[index];
|
|
631
|
+
let element;
|
|
632
|
+
if (node instanceof Document) element = node.body;
|
|
633
|
+
else element = node instanceof Element ? node : void 0;
|
|
634
|
+
if (element != null && (context == null || contexts.length === 0 || contexts.some((context$1) => context$1 === element || context$1.contains(element))) && !result.includes(element)) result.push(element);
|
|
635
|
+
}
|
|
636
|
+
return result;
|
|
827
637
|
}
|
|
828
638
|
/**
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
639
|
+
* Find elements that match the selector
|
|
640
|
+
* @param selector Selector to find elements for
|
|
641
|
+
* @param context Context to search within _(defaults to `document`)_
|
|
642
|
+
* @returns Found elements
|
|
643
|
+
*/
|
|
834
644
|
function findElements(selector, context) {
|
|
835
|
-
|
|
645
|
+
return findElementOrElements(selector, context, false);
|
|
836
646
|
}
|
|
837
647
|
/**
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
648
|
+
* Get the most specific element under the pointer
|
|
649
|
+
*
|
|
650
|
+
* - Ignores elements with `pointer-events: none` and `visibility: hidden`
|
|
651
|
+
* - _(If `skipIgnore` is `true`, no elements are ignored)_
|
|
652
|
+
* @param skipIgnore Skip ignored elements?
|
|
653
|
+
* @returns Found element or `null`
|
|
654
|
+
*/
|
|
845
655
|
function getElementUnderPointer(skipIgnore) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
(style.pointerEvents !== STYLE_NONE$1 && style.visibility !== STYLE_HIDDEN$1)) {
|
|
857
|
-
returned.push(element);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return returned.at(-1) ?? null;
|
|
656
|
+
const elements = [...document.querySelectorAll(SUFFIX_HOVER)];
|
|
657
|
+
const { length } = elements;
|
|
658
|
+
const returned = [];
|
|
659
|
+
for (let index = 0; index < length; index += 1) {
|
|
660
|
+
const element = elements[index];
|
|
661
|
+
if (element.tagName === TAG_HEAD) continue;
|
|
662
|
+
const style = getComputedStyle(element);
|
|
663
|
+
if (skipIgnore === true || style.pointerEvents !== STYLE_NONE$1 && style.visibility !== STYLE_HIDDEN$1) returned.push(element);
|
|
664
|
+
}
|
|
665
|
+
return returned.at(-1) ?? null;
|
|
861
666
|
}
|
|
862
667
|
function isContext(value) {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
const
|
|
869
|
-
const
|
|
870
|
-
const
|
|
871
|
-
const SUFFIX_HOVER = ':hover';
|
|
872
|
-
const TAG_HEAD = 'HEAD';
|
|
668
|
+
return typeof value?.querySelector === "function" && typeof value?.querySelectorAll === "function";
|
|
669
|
+
}
|
|
670
|
+
const QUERY_SELECTOR_ALL = "querySelectorAll";
|
|
671
|
+
const QUERY_SELECTOR_SINGLE = "querySelector";
|
|
672
|
+
const STYLE_HIDDEN$1 = "hidden";
|
|
673
|
+
const STYLE_NONE$1 = "none";
|
|
674
|
+
const SUFFIX_HOVER = ":hover";
|
|
675
|
+
const TAG_HEAD = "HEAD";
|
|
873
676
|
|
|
874
|
-
// Based on https://github.com/focus-trap/tabbable :-)
|
|
875
|
-
//
|
|
876
677
|
/**
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
678
|
+
* Get a list of focusable elements within a parent element
|
|
679
|
+
* @param parent Parent element
|
|
680
|
+
* @returns Focusable elements
|
|
681
|
+
*/
|
|
881
682
|
function getFocusable(parent) {
|
|
882
|
-
|
|
683
|
+
return getValidElements(parent, FILTERS_FOCUSABLE, false);
|
|
883
684
|
}
|
|
884
685
|
function getItem(element, tabbable) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
686
|
+
return {
|
|
687
|
+
element,
|
|
688
|
+
tabIndex: tabbable ? getTabIndex(element) : TABINDEX_DEFAULT
|
|
689
|
+
};
|
|
889
690
|
}
|
|
890
691
|
/**
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
692
|
+
* Get a list of tabbable elements within a parent element
|
|
693
|
+
* @param parent Parent element
|
|
694
|
+
* @returns Tabbable elements
|
|
695
|
+
*/
|
|
895
696
|
function getTabbable(parent) {
|
|
896
|
-
|
|
697
|
+
return getValidElements(parent, FILTERS_TABBABLE, true);
|
|
897
698
|
}
|
|
898
699
|
function getTabIndex(element) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
!hasTabIndex(element)) {
|
|
903
|
-
return TABINDEX_BASE;
|
|
904
|
-
}
|
|
905
|
-
return tabIndex;
|
|
700
|
+
const tabIndex = element?.tabIndex ?? TABINDEX_DEFAULT;
|
|
701
|
+
if (tabIndex < TABINDEX_BASE && (EXPRESSION_SPECIAL_TABINDEX.test(element.tagName) || isEditable(element)) && !hasTabIndex(element)) return TABINDEX_BASE;
|
|
702
|
+
return tabIndex;
|
|
906
703
|
}
|
|
907
704
|
function getValidElements(parent, filters, tabbable) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
for (let index = 0; index < length; index += 1) {
|
|
927
|
-
const item = items[index];
|
|
928
|
-
if (item.tabIndex === TABINDEX_BASE) {
|
|
929
|
-
zeroed.push(item.element);
|
|
930
|
-
}
|
|
931
|
-
else {
|
|
932
|
-
indiced[item.tabIndex] = [...(indiced[item.tabIndex] ?? []), item.element];
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
return [...indiced.flat(), ...zeroed];
|
|
705
|
+
if (!(parent instanceof Element)) return [];
|
|
706
|
+
const elements = [...parent.querySelectorAll(SELECTOR_FULL)];
|
|
707
|
+
const items = [];
|
|
708
|
+
let { length } = elements;
|
|
709
|
+
for (let index = 0; index < length; index += 1) {
|
|
710
|
+
const item = getItem(elements[index], tabbable);
|
|
711
|
+
if (!filters.some((filter) => filter(item))) items.push(item);
|
|
712
|
+
}
|
|
713
|
+
if (!tabbable) return items.map((item) => item.element);
|
|
714
|
+
const indiced = [];
|
|
715
|
+
const zeroed = [];
|
|
716
|
+
length = items.length;
|
|
717
|
+
for (let index = 0; index < length; index += 1) {
|
|
718
|
+
const item = items[index];
|
|
719
|
+
if (item.tabIndex === TABINDEX_BASE) zeroed.push(item.element);
|
|
720
|
+
else indiced[item.tabIndex] = [...indiced[item.tabIndex] ?? [], item.element];
|
|
721
|
+
}
|
|
722
|
+
return [...indiced.flat(), ...zeroed];
|
|
936
723
|
}
|
|
937
724
|
function hasTabIndex(element) {
|
|
938
|
-
|
|
725
|
+
return !Number.isNaN(Number.parseInt(element.getAttribute(ATTRIBUTE_TABINDEX), 10));
|
|
939
726
|
}
|
|
940
727
|
function isDisabled(item) {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
}
|
|
944
|
-
return item.element.disabled ?? false;
|
|
728
|
+
if (EXPRESSION_DISABLEABLE.test(item.element.tagName) && isDisabledFromFieldset(item.element)) return true;
|
|
729
|
+
return item.element.disabled ?? false;
|
|
945
730
|
}
|
|
946
731
|
function isDisabledFromFieldset(element) {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
return false;
|
|
732
|
+
let parent = element.parentElement;
|
|
733
|
+
while (parent != null) {
|
|
734
|
+
if (parent instanceof HTMLFieldSetElement && parent.disabled) {
|
|
735
|
+
const children = Array.from(parent.children);
|
|
736
|
+
const { length } = children;
|
|
737
|
+
for (let index = 0; index < length; index += 1) {
|
|
738
|
+
const child = children[index];
|
|
739
|
+
if (child instanceof HTMLLegendElement) return parent.matches(SELECTOR_FIELDSET_DISABLED) || !child.contains(element);
|
|
740
|
+
}
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
parent = parent.parentElement;
|
|
744
|
+
}
|
|
745
|
+
return false;
|
|
963
746
|
}
|
|
964
747
|
function isEditable(element) {
|
|
965
|
-
|
|
748
|
+
return EXPRESSION_TRUEISH.test(element.getAttribute(ATTRIBUTE_CONTENTEDITABLE));
|
|
966
749
|
}
|
|
967
750
|
/**
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
751
|
+
* Is the element focusable?
|
|
752
|
+
* @param element Element to check
|
|
753
|
+
* @returns `true` if focusable, otherwise `false`
|
|
754
|
+
*/
|
|
972
755
|
function isFocusable(element) {
|
|
973
|
-
|
|
756
|
+
return element instanceof Element ? isValidElement(element, FILTERS_FOCUSABLE, false) : false;
|
|
974
757
|
}
|
|
975
758
|
function isHidden(item) {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
if (nodeUnderDetails?.matches(SELECTOR_DETAILS_CLOSED_CHILDREN) ?? false) {
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
const style = getComputedStyle(item.element);
|
|
986
|
-
if (style.display === STYLE_NONE || style.visibility === STYLE_HIDDEN) {
|
|
987
|
-
return true;
|
|
988
|
-
}
|
|
989
|
-
const { height, width } = item.element.getBoundingClientRect();
|
|
990
|
-
return height === 0 && width === 0;
|
|
759
|
+
if ((item.element.hidden ?? false) || item.element instanceof HTMLInputElement && item.element.type === STYLE_HIDDEN) return true;
|
|
760
|
+
if ((item.element.matches(SELECTOR_SUMMARY_FIRST) ? item.element.parentElement : item.element)?.matches(SELECTOR_DETAILS_CLOSED_CHILDREN) ?? false) return true;
|
|
761
|
+
const style = getComputedStyle(item.element);
|
|
762
|
+
if (style.display === STYLE_NONE || style.visibility === STYLE_HIDDEN) return true;
|
|
763
|
+
const { height, width } = item.element.getBoundingClientRect();
|
|
764
|
+
return height === 0 && width === 0;
|
|
991
765
|
}
|
|
992
766
|
function isInert(item) {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
element: item.element.parentElement})));
|
|
767
|
+
return (item.element.inert ?? false) || EXPRESSION_TRUEISH.test(item.element.getAttribute(ATTRIBUTE_INERT)) || item.element.parentElement != null && isInert({
|
|
768
|
+
element: item.element.parentElement,
|
|
769
|
+
tabIndex: TABINDEX_DEFAULT
|
|
770
|
+
});
|
|
998
771
|
}
|
|
999
772
|
function isNotTabbable(item) {
|
|
1000
|
-
|
|
773
|
+
return (item.tabIndex ?? TABINDEX_DEFAULT) < TABINDEX_BASE;
|
|
1001
774
|
}
|
|
1002
775
|
function isNotTabbableRadio(item) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
const parent = item.element.form ?? item.element.getRootNode?.() ?? item.element.ownerDocument;
|
|
1010
|
-
const realName = CSS?.escape?.(item.element.name) ?? item.element.name;
|
|
1011
|
-
const radios = [
|
|
1012
|
-
...parent.querySelectorAll(`${SELECTOR_RADIO_PREFIX}${realName}${SELECTOR_RADIO_SUFFIX}`),
|
|
1013
|
-
];
|
|
1014
|
-
const checked = radios.find(radio => radio.checked);
|
|
1015
|
-
return checked != null && checked !== item.element;
|
|
776
|
+
if (!(item.element instanceof HTMLInputElement) || item.element.type !== TYPE_RADIO || !item.element.name || item.element.checked) return false;
|
|
777
|
+
const parent = item.element.form ?? item.element.getRootNode?.() ?? item.element.ownerDocument;
|
|
778
|
+
const realName = CSS?.escape?.(item.element.name) ?? item.element.name;
|
|
779
|
+
const checked = [...parent.querySelectorAll(`${SELECTOR_RADIO_PREFIX}${realName}${SELECTOR_RADIO_SUFFIX}`)].find((radio) => radio.checked);
|
|
780
|
+
return checked != null && checked !== item.element;
|
|
1016
781
|
}
|
|
1017
782
|
function isSummarised(item) {
|
|
1018
|
-
|
|
1019
|
-
[...item.element.children].some(child => EXPRESSION_SUMMARY.test(child.tagName)));
|
|
783
|
+
return item.element instanceof HTMLDetailsElement && [...item.element.children].some((child) => EXPRESSION_SUMMARY.test(child.tagName));
|
|
1020
784
|
}
|
|
1021
785
|
/**
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
786
|
+
* Is the element tabbable?
|
|
787
|
+
* @param element Element to check
|
|
788
|
+
* @returns `true` if tabbable, otherwise `false`
|
|
789
|
+
*/
|
|
1026
790
|
function isTabbable(element) {
|
|
1027
|
-
|
|
791
|
+
return element instanceof Element ? isValidElement(element, FILTERS_TABBABLE, true) : false;
|
|
1028
792
|
}
|
|
1029
793
|
function isValidElement(element, filters, tabbable) {
|
|
1030
|
-
|
|
1031
|
-
|
|
794
|
+
const item = getItem(element, tabbable);
|
|
795
|
+
return !filters.some((filter) => filter(item));
|
|
1032
796
|
}
|
|
1033
|
-
|
|
1034
|
-
const
|
|
1035
|
-
const
|
|
1036
|
-
const ATTRIBUTE_TABINDEX = 'tabindex';
|
|
797
|
+
const ATTRIBUTE_CONTENTEDITABLE = "contenteditable";
|
|
798
|
+
const ATTRIBUTE_INERT = "inert";
|
|
799
|
+
const ATTRIBUTE_TABINDEX = "tabindex";
|
|
1037
800
|
const EXPRESSION_DISABLEABLE = /^(button|input|select|textarea)$/i;
|
|
1038
801
|
const EXPRESSION_SPECIAL_TABINDEX = /^(audio|details|video)$/i;
|
|
1039
802
|
const EXPRESSION_SUMMARY = /^summary$/i;
|
|
1040
803
|
const EXPRESSION_TRUEISH = /^(|true)$/i;
|
|
1041
804
|
const FILTERS_FOCUSABLE = [
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
805
|
+
isDisabled,
|
|
806
|
+
isInert,
|
|
807
|
+
isHidden,
|
|
808
|
+
isSummarised
|
|
1046
809
|
];
|
|
1047
810
|
const FILTERS_TABBABLE = [
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
811
|
+
isNotTabbable,
|
|
812
|
+
isNotTabbableRadio,
|
|
813
|
+
...FILTERS_FOCUSABLE
|
|
1051
814
|
];
|
|
1052
|
-
const SELECTOR_DETAILS_CLOSED_CHILDREN =
|
|
1053
|
-
const SELECTOR_FIELDSET_DISABLED =
|
|
1054
|
-
const SELECTOR_SUMMARY_FIRST =
|
|
1055
|
-
const SELECTOR_RADIO_PREFIX =
|
|
1056
|
-
const SELECTOR_RADIO_SUFFIX =
|
|
815
|
+
const SELECTOR_DETAILS_CLOSED_CHILDREN = "details:not([open]) *";
|
|
816
|
+
const SELECTOR_FIELDSET_DISABLED = "fieldset[disabled] *";
|
|
817
|
+
const SELECTOR_SUMMARY_FIRST = "details > summary:first-of-type";
|
|
818
|
+
const SELECTOR_RADIO_PREFIX = "input[type=\"radio\"][name=\"";
|
|
819
|
+
const SELECTOR_RADIO_SUFFIX = "\"]";
|
|
1057
820
|
const SELECTOR_FULL = [
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
]
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const STYLE_HIDDEN = 'hidden';
|
|
1073
|
-
const STYLE_NONE = 'none';
|
|
821
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
822
|
+
"[tabindex]:not(slot)",
|
|
823
|
+
"a[href]",
|
|
824
|
+
"audio[controls]",
|
|
825
|
+
"button",
|
|
826
|
+
"details",
|
|
827
|
+
SELECTOR_SUMMARY_FIRST,
|
|
828
|
+
"input",
|
|
829
|
+
"select",
|
|
830
|
+
"textarea",
|
|
831
|
+
"video[controls]"
|
|
832
|
+
].map((selector) => `${selector}:not([inert])`).join(",");
|
|
833
|
+
const STYLE_HIDDEN = "hidden";
|
|
834
|
+
const STYLE_NONE = "none";
|
|
1074
835
|
const TABINDEX_BASE = 0;
|
|
1075
836
|
const TABINDEX_DEFAULT = -1;
|
|
1076
|
-
const TYPE_RADIO =
|
|
837
|
+
const TYPE_RADIO = "radio";
|
|
1077
838
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
839
|
+
function handleElement(element, depth) {
|
|
840
|
+
if (isClobbered(element)) {
|
|
841
|
+
element.remove();
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
if (depth === 0) {
|
|
845
|
+
const scripts = element.querySelectorAll("script");
|
|
846
|
+
for (const script of scripts) script.remove();
|
|
847
|
+
}
|
|
848
|
+
sanitizeAttributes(element, [...element.attributes]);
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Is the element clobbered?
|
|
853
|
+
*
|
|
854
|
+
* Thanks, DOMPurify _(https://github.com/cure53/DOMPurify)_
|
|
855
|
+
*/
|
|
856
|
+
function isClobbered(element) {
|
|
857
|
+
return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function");
|
|
858
|
+
}
|
|
859
|
+
function sanitizeAttributes(element, attributes) {
|
|
860
|
+
const { length } = attributes;
|
|
861
|
+
for (let index = 0; index < length; index += 1) {
|
|
862
|
+
const { name, value } = attributes[index];
|
|
863
|
+
if (isBadAttribute$1(name, value, false) || isEmptyNonBooleanAttribute$1(name, value, false)) element.removeAttribute(name);
|
|
864
|
+
else if (isInvalidBooleanAttribute$1(name, value, false)) element.setAttribute(name, "");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
function sanitizeNodes(nodes, depth) {
|
|
868
|
+
const actual = nodes.filter((node) => node instanceof Node);
|
|
869
|
+
let { length } = nodes;
|
|
870
|
+
for (let index = 0; index < length; index += 1) {
|
|
871
|
+
const node = actual[index];
|
|
872
|
+
if (node instanceof Element && handleElement(node, depth)) {
|
|
873
|
+
actual.splice(index, 1);
|
|
874
|
+
length -= 1;
|
|
875
|
+
index -= 1;
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], depth + 1);
|
|
879
|
+
}
|
|
880
|
+
return nodes;
|
|
1116
881
|
}
|
|
1117
882
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
const cloned = template.content.cloneNode(true);
|
|
1136
|
-
const scripts = cloned.querySelectorAll('script');
|
|
1137
|
-
for (const script of scripts) {
|
|
1138
|
-
script.remove();
|
|
1139
|
-
}
|
|
1140
|
-
cloned.normalize();
|
|
1141
|
-
return sanitizeNodes([...cloned.childNodes], options);
|
|
883
|
+
function createHtml(value) {
|
|
884
|
+
const html$1 = getParser().parseFromString(typeof value === "string" ? value : value.innerHTML, HTML_PARSE_TYPE);
|
|
885
|
+
html$1.body.normalize();
|
|
886
|
+
sanitizeNodes([html$1.body], 0);
|
|
887
|
+
return html$1.body.innerHTML;
|
|
888
|
+
}
|
|
889
|
+
function createTemplate(value, options) {
|
|
890
|
+
const template = document.createElement(TEMPLATE_TAG);
|
|
891
|
+
template.innerHTML = createHtml(value);
|
|
892
|
+
if (typeof value === "string" && options.cache) templates[value] = template;
|
|
893
|
+
return template;
|
|
894
|
+
}
|
|
895
|
+
function getNodes(value, options) {
|
|
896
|
+
if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
|
|
897
|
+
const template = getTemplate(value, options);
|
|
898
|
+
return template == null ? [] : [...template.content.cloneNode(true).childNodes];
|
|
1142
899
|
}
|
|
1143
900
|
function getOptions(input) {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
}
|
|
1152
|
-
function getTemplate(value,
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
|
|
1161
|
-
template = element instanceof HTMLTemplateElement ? element : createTemplate(value, ignore);
|
|
1162
|
-
return template;
|
|
901
|
+
const options = isPlainObject(input) ? input : {};
|
|
902
|
+
options.cache = typeof options.cache === "boolean" ? options.cache : true;
|
|
903
|
+
return options;
|
|
904
|
+
}
|
|
905
|
+
function getParser() {
|
|
906
|
+
parser ??= new DOMParser();
|
|
907
|
+
return parser;
|
|
908
|
+
}
|
|
909
|
+
function getTemplate(value, options) {
|
|
910
|
+
if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
|
|
911
|
+
if (typeof value !== "string" || value.trim().length === 0) return;
|
|
912
|
+
let template = templates[value];
|
|
913
|
+
if (template != null) return template;
|
|
914
|
+
const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
|
|
915
|
+
template = element instanceof HTMLTemplateElement ? element : createTemplate(value, options);
|
|
916
|
+
return template;
|
|
1163
917
|
}
|
|
1164
918
|
const html = ((value, options) => {
|
|
1165
|
-
|
|
919
|
+
return getNodes(value, getOptions(options));
|
|
1166
920
|
});
|
|
1167
921
|
html.clear = () => {
|
|
1168
|
-
|
|
922
|
+
templates = {};
|
|
1169
923
|
};
|
|
1170
924
|
html.remove = (template) => {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
updated[key] = templates[key];
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
templates = updated;
|
|
925
|
+
if (typeof template !== "string" || templates[template] == null) return;
|
|
926
|
+
const keys = Object.keys(templates);
|
|
927
|
+
const { length } = keys;
|
|
928
|
+
const updated = {};
|
|
929
|
+
for (let index = 0; index < length; index += 1) {
|
|
930
|
+
const key = keys[index];
|
|
931
|
+
if (key !== template) updated[key] = templates[key];
|
|
932
|
+
}
|
|
933
|
+
templates = updated;
|
|
1184
934
|
};
|
|
1185
935
|
/**
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
function sanitize(value
|
|
1192
|
-
|
|
1193
|
-
}
|
|
1194
|
-
//
|
|
936
|
+
* Sanitize one or more nodes, recursively
|
|
937
|
+
* @param value Node or nodes to sanitize
|
|
938
|
+
* @param options Sanitization options
|
|
939
|
+
* @returns Sanitized nodes
|
|
940
|
+
*/
|
|
941
|
+
function sanitize(value) {
|
|
942
|
+
return sanitizeNodes(Array.isArray(value) ? value : [value], 0);
|
|
943
|
+
}
|
|
1195
944
|
const EXPRESSION_ID = /^[a-z][\w-]*$/i;
|
|
945
|
+
const HTML_PARSE_TYPE = "text/html";
|
|
946
|
+
const TEMPLATE_TAG = "template";
|
|
947
|
+
let parser;
|
|
1196
948
|
let templates = {};
|
|
1197
949
|
|
|
1198
950
|
/**
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
951
|
+
* Get a style from an element
|
|
952
|
+
* @param element Element to get the style from
|
|
953
|
+
* @param property Style name
|
|
954
|
+
* @param computed Get the computed style? _(defaults to `false`)_
|
|
955
|
+
* @returns Style value
|
|
956
|
+
*/
|
|
1205
957
|
function getStyle(element, property, computed) {
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
}
|
|
1209
|
-
return getStyleValue(element, property, computed === true);
|
|
958
|
+
if (!isHTMLOrSVGElement(element) || typeof property !== "string") return;
|
|
959
|
+
return getStyleValue(element, property, computed === true);
|
|
1210
960
|
}
|
|
1211
961
|
/**
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
962
|
+
* Get styles from an element
|
|
963
|
+
* @param element Element to get the styles from
|
|
964
|
+
* @param properties Styles to get
|
|
965
|
+
* @param computed Get the computed styles? _(defaults to `false`)_
|
|
966
|
+
* @returns Style values
|
|
967
|
+
*/
|
|
1218
968
|
function getStyles(element, properties, computed) {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
styles[property] = getStyleValue(element, property, computed === true);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
return styles;
|
|
969
|
+
const styles = {};
|
|
970
|
+
if (!(isHTMLOrSVGElement(element) && Array.isArray(properties))) return styles;
|
|
971
|
+
const { length } = properties;
|
|
972
|
+
for (let index = 0; index < length; index += 1) {
|
|
973
|
+
const property = properties[index];
|
|
974
|
+
if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
|
|
975
|
+
}
|
|
976
|
+
return styles;
|
|
1231
977
|
}
|
|
1232
978
|
/**
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
979
|
+
* Get the text direction of an element
|
|
980
|
+
* @param element Element to get the text direction from
|
|
981
|
+
* @param computed Get the computed text direction? _(defaults to `false`)_
|
|
982
|
+
* @returns Text direction
|
|
983
|
+
*/
|
|
1238
984
|
function getTextDirection(element, computed) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return direction.toLowerCase();
|
|
1245
|
-
}
|
|
1246
|
-
const value = getStyleValue(element, 'direction', computed === true);
|
|
1247
|
-
return value === 'rtl' ? value : 'ltr';
|
|
985
|
+
if (!(element instanceof Element)) return;
|
|
986
|
+
const direction = element.getAttribute(ATTRIBUTE_DIRECTION);
|
|
987
|
+
if (direction != null && EXPRESSION_DIRECTION.test(direction)) return direction.toLowerCase();
|
|
988
|
+
const value = getStyleValue(element, "direction", computed === true);
|
|
989
|
+
return value === "rtl" ? value : "ltr";
|
|
1248
990
|
}
|
|
1249
991
|
/**
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
992
|
+
* Set a style on an element
|
|
993
|
+
* @param element Element to set the style on
|
|
994
|
+
* @param property Style name
|
|
995
|
+
* @param value Style value
|
|
996
|
+
*/
|
|
1255
997
|
function setStyle(element, property, value) {
|
|
1256
|
-
|
|
998
|
+
setElementValues(element, property, value, updateStyleProperty);
|
|
1257
999
|
}
|
|
1258
1000
|
/**
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1001
|
+
* Set styles on an element
|
|
1002
|
+
* @param element Element to set the styles on
|
|
1003
|
+
* @param styles Styles to set
|
|
1004
|
+
*/
|
|
1263
1005
|
function setStyles(element, styles) {
|
|
1264
|
-
|
|
1006
|
+
setElementValues(element, styles, null, updateStyleProperty);
|
|
1265
1007
|
}
|
|
1266
1008
|
/**
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1009
|
+
* Toggle styles for an element
|
|
1010
|
+
* @param element Element to style
|
|
1011
|
+
* @param styles Styles to be set or removed
|
|
1012
|
+
* @returns Style toggler
|
|
1013
|
+
*/
|
|
1272
1014
|
function toggleStyles(element, styles) {
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
},
|
|
1298
|
-
remove() {
|
|
1299
|
-
if (hasSet) {
|
|
1300
|
-
toggle(false);
|
|
1301
|
-
}
|
|
1302
|
-
},
|
|
1303
|
-
};
|
|
1015
|
+
function toggle(set) {
|
|
1016
|
+
hasSet = set;
|
|
1017
|
+
let next;
|
|
1018
|
+
if (set) {
|
|
1019
|
+
values = getStyles(element, keys);
|
|
1020
|
+
next = styles;
|
|
1021
|
+
} else {
|
|
1022
|
+
next = { ...values };
|
|
1023
|
+
values = {};
|
|
1024
|
+
for (const key of keys) values[key] = void 0;
|
|
1025
|
+
}
|
|
1026
|
+
setStyles(element, next);
|
|
1027
|
+
}
|
|
1028
|
+
const keys = Object.keys(styles);
|
|
1029
|
+
let hasSet = false;
|
|
1030
|
+
let values = {};
|
|
1031
|
+
return {
|
|
1032
|
+
set() {
|
|
1033
|
+
if (!hasSet) toggle(true);
|
|
1034
|
+
},
|
|
1035
|
+
remove() {
|
|
1036
|
+
if (hasSet) toggle(false);
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1304
1039
|
}
|
|
1305
1040
|
function updateStyleProperty(element, key, value) {
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
const ATTRIBUTE_DIRECTION = 'dir';
|
|
1041
|
+
updateElementValue(element, key, value, function(property, value$1) {
|
|
1042
|
+
this.style[property] = value$1;
|
|
1043
|
+
}, function(property) {
|
|
1044
|
+
this.style[property] = "";
|
|
1045
|
+
}, false);
|
|
1046
|
+
}
|
|
1047
|
+
const ATTRIBUTE_DIRECTION = "dir";
|
|
1314
1048
|
const EXPRESSION_DIRECTION = /^(ltr|rtl)$/i;
|
|
1315
1049
|
|
|
1316
|
-
export { findElement as $, findElements as $$,
|
|
1050
|
+
export { findElement as $, findElement, findElements as $$, findElements, dispatch, findAncestor, findRelatives, getData, getElementUnderPointer, getFocusable, getPosition, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEmptyNonBooleanAttribute, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setData, setStyle, setStyles, touch_default as supportsTouch, toggleStyles };
|