@shgysk8zer0/polyfills 0.4.5 → 0.4.7
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/CHANGELOG.md +13 -0
- package/ShadowRoot.js +2 -16
- package/all.min.js +11 -11
- package/all.min.js.map +1 -1
- package/browser.js +1 -0
- package/browser.min.js +11 -11
- package/browser.min.js.map +1 -1
- package/close-watcher.js +41 -0
- package/element.js +60 -8
- package/methods/dom.js +84 -4
- package/package.json +2 -2
package/close-watcher.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
if (! (globalThis.CloseWatcher instanceof Function)) {
|
|
2
|
+
globalThis.CloseWatcher = class CloseWatcher extends EventTarget {
|
|
3
|
+
#controller = new AbortController();
|
|
4
|
+
#cancelable = true;
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
|
|
9
|
+
document.addEventListener('keydown', event => {
|
|
10
|
+
if (event.key === 'Escape') {
|
|
11
|
+
this.requestClose();
|
|
12
|
+
}
|
|
13
|
+
}, { signal: this.#controller.signal });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
requestClose() {
|
|
17
|
+
if (! this.#controller.signal.aborted) {
|
|
18
|
+
const event = new Event('cancel', { cancelable: this.#cancelable });
|
|
19
|
+
this.dispatchEvent(event);
|
|
20
|
+
this.#cancelable = false;
|
|
21
|
+
|
|
22
|
+
if (! event.defaultPrevented) {
|
|
23
|
+
this.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
close() {
|
|
29
|
+
if (! this.#controller.signal.aborted) {
|
|
30
|
+
this.dispatchEvent(new Event('close'));
|
|
31
|
+
this.destroy();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
destroy() {
|
|
36
|
+
if (! this.#controller.signal.aborted) {
|
|
37
|
+
this.#controller.abort();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
package/element.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { aria } from './aom.js';
|
|
2
2
|
import { polyfillGetterSetter, polyfillMethod, overwriteMethod } from './utils.js';
|
|
3
|
-
import { setHTMLUnsafe } from './methods/dom.js';
|
|
3
|
+
import { getHTML, setHTMLUnsafe } from './methods/dom.js';
|
|
4
4
|
import './sanitizer.js';
|
|
5
5
|
|
|
6
6
|
polyfillMethod(Element.prototype, 'setHTMLUnsafe', setHTMLUnsafe);
|
|
7
|
+
polyfillMethod(Element.prototype, 'getHTML', getHTML);
|
|
7
8
|
|
|
8
9
|
if ('CustomElementRegistry' in globalThis && ! (CustomElementRegistry.prototype.getName instanceof Function)) {
|
|
9
10
|
const registry = new Map();
|
|
@@ -47,7 +48,7 @@ export function initPopover(target = document.body) {
|
|
|
47
48
|
.forEach(el => el.addEventListener('click', handlePopover));
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
if ((globalThis.ToggleEvent instanceof Function)) {
|
|
51
|
+
if (! (globalThis.ToggleEvent instanceof Function)) {
|
|
51
52
|
class ToggleEvent extends Event {
|
|
52
53
|
#newState;
|
|
53
54
|
#oldState;
|
|
@@ -268,26 +269,77 @@ if (! (HTMLImageElement.prototype.decode instanceof Function)) {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
if (! HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode')) {
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
const attachedShadows = new WeakMap();
|
|
273
|
+
|
|
274
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootMode', {
|
|
275
|
+
get() {
|
|
273
276
|
return this.getAttribute('shadowrootmode');
|
|
274
277
|
},
|
|
275
|
-
set
|
|
278
|
+
set(val) {
|
|
276
279
|
this.setAttribute('shadowrootmode', val);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootDelegatesFocus', {
|
|
284
|
+
get() {
|
|
285
|
+
return this.hasAttribute('shadowrootdelegatesfocus');
|
|
286
|
+
},
|
|
287
|
+
set(val) {
|
|
288
|
+
this.toggleAttribute('shadowrootdelegatesfocus', val);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootClonable', {
|
|
293
|
+
get() {
|
|
294
|
+
return this.hasAttribute('shadowrootclonable');
|
|
295
|
+
},
|
|
296
|
+
set(val) {
|
|
297
|
+
this.toggleAttribute('shadowrootclonable', val);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootSerializable', {
|
|
302
|
+
get() {
|
|
303
|
+
return this.hasAttribute('shadowrootserializable');
|
|
277
304
|
},
|
|
278
|
-
|
|
279
|
-
|
|
305
|
+
set(val) {
|
|
306
|
+
this.toggleAttribute('shadowrootserializable', val);
|
|
307
|
+
}
|
|
280
308
|
});
|
|
281
309
|
|
|
282
310
|
const attachShadows = (base = document) => {
|
|
283
311
|
base.querySelectorAll('template[shadowrootmode]').forEach(tmp => {
|
|
284
|
-
const shadow = tmp.parentElement.attachShadow({
|
|
312
|
+
const shadow = tmp.parentElement.attachShadow({
|
|
313
|
+
mode: tmp.shadowRootMode,
|
|
314
|
+
clonable: tmp.shadowRootClonable,
|
|
315
|
+
delegatesFocus: tmp.shadowRootDelegatesFocus,
|
|
316
|
+
serializable: tmp.shadowRootSerializable,
|
|
317
|
+
});
|
|
318
|
+
|
|
285
319
|
shadow.append(tmp.content);
|
|
286
320
|
tmp.remove();
|
|
287
321
|
attachShadows(shadow);
|
|
322
|
+
attachedShadows.set(shadow.host, shadow);
|
|
288
323
|
});
|
|
289
324
|
};
|
|
290
325
|
|
|
326
|
+
overwriteMethod(HTMLElement.prototype, 'attachShadow', function(attach) {
|
|
327
|
+
return ({ mode, clonable = false, delegatesFocus = false, serializable = false, slotAssignment = 'auto' }) => {
|
|
328
|
+
if (! attachedShadows.has(this)) {
|
|
329
|
+
return attach.call(this, { mode, clonable, delegatesFocus, serializable, slotAssignment });
|
|
330
|
+
} else {
|
|
331
|
+
const shadow = attachedShadows.get(this);
|
|
332
|
+
|
|
333
|
+
if (mode === shadow.shadowRootMode) {
|
|
334
|
+
attachShadows.remove(this);
|
|
335
|
+
return shadow;
|
|
336
|
+
} else {
|
|
337
|
+
throw new DOMException('Element.attachShadow: Unable to re-attach to existing ShadowDOM');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
|
|
291
343
|
if (document.readyState === 'loading') {
|
|
292
344
|
document.addEventListener('readystatechange', () => attachShadows(document), { once: true });
|
|
293
345
|
} else {
|
package/methods/dom.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
const HOSTS_SYMBOL = Symbol('shadow:hosts');
|
|
2
|
+
|
|
3
|
+
function getTagHTML(el) {
|
|
4
|
+
const outerHTML = el.cloneNode().outerHTML;
|
|
5
|
+
const closeStart = outerHTML.indexOf('/') - 1;
|
|
6
|
+
return closeStart > 0 ? [outerHTML.substring(0, closeStart), outerHTML.substring(closeStart)] : [outerHTML, ''];
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
function attachShadow(template){
|
|
2
10
|
if (template instanceof HTMLTemplateElement && template.parentElement instanceof HTMLElement) {
|
|
3
11
|
const shadow = template.parentElement.attachShadow({
|
|
@@ -12,6 +20,45 @@ function attachShadow(template){
|
|
|
12
20
|
}
|
|
13
21
|
}
|
|
14
22
|
|
|
23
|
+
function serializeChildNodes(node, { serializableShadowRoots = false, shadowRoots = [], [HOSTS_SYMBOL]: hosts = [] } = {}) {
|
|
24
|
+
return Array.from(
|
|
25
|
+
node.childNodes,
|
|
26
|
+
child => {
|
|
27
|
+
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
28
|
+
const [open, close] = getTagHTML(child);
|
|
29
|
+
return open + child.getHTML({ serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts }) + close;
|
|
30
|
+
} else {
|
|
31
|
+
return child.textContent;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
).join('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function serializeShadowRoot(shadow, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts = [] } = {}) {
|
|
38
|
+
if (shadow.serializable) {
|
|
39
|
+
const openTag = `<template shadowrootmode="${shadow.mode}"`;
|
|
40
|
+
const attrs = [];
|
|
41
|
+
|
|
42
|
+
if (shadow.clonable) {
|
|
43
|
+
attrs.push(' shadowrootclonable=""');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (shadow.serializable) {
|
|
47
|
+
attrs.push(' shadowrootserializable=""');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (shadow.delegatesFocus) {
|
|
51
|
+
attrs.push(' shadowrootdelegatesfocus=""');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const tag = attrs.length === 0 ? openTag + '>' : openTag + ' ' + attrs.join('') + '>';
|
|
55
|
+
|
|
56
|
+
return tag + serializeChildNodes(shadow, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts }) + '</template>';
|
|
57
|
+
} else {
|
|
58
|
+
return serializeChildNodes(shadow, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
15
62
|
export function parseHTMLUnsafe(input){
|
|
16
63
|
const parser = new DOMParser();
|
|
17
64
|
// Ensures `URL` is "about:blank"
|
|
@@ -20,7 +67,7 @@ export function parseHTMLUnsafe(input){
|
|
|
20
67
|
const parsed = parser.parseFromString(input, 'text/html');
|
|
21
68
|
doc.head.append(...parsed.head.childNodes);
|
|
22
69
|
doc.body.append(...parsed.body.childNodes);
|
|
23
|
-
doc.querySelectorAll('template[shadowrootmode]').forEach(attachShadow);
|
|
70
|
+
doc.querySelectorAll('* > template[shadowrootmode]').forEach(attachShadow);
|
|
24
71
|
return doc;
|
|
25
72
|
}
|
|
26
73
|
|
|
@@ -30,10 +77,43 @@ export function setHTMLUnsafe(input) {
|
|
|
30
77
|
const parsed = parser.parseFromString(input, 'text/html');
|
|
31
78
|
const frag = document.createDocumentFragment();
|
|
32
79
|
frag.append(...parsed.body.childNodes);
|
|
33
|
-
|
|
80
|
+
frag.querySelectorAll('* > template[shadowrootmode]').forEach(attachShadow);
|
|
34
81
|
this.replaceChildren(frag);
|
|
35
82
|
}
|
|
36
83
|
|
|
37
|
-
export function getHTML() {
|
|
38
|
-
|
|
84
|
+
export function getHTML({ serializableShadowRoots = false, shadowRoots = [], [HOSTS_SYMBOL]: hosts = [] } = {}) {
|
|
85
|
+
if (serializableShadowRoots && hosts.length === 0 && shadowRoots.length !== 0) {
|
|
86
|
+
hosts.push(...shadowRoots.map(shadow => shadow.host));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
switch(this.nodeType) {
|
|
90
|
+
case Node.ELEMENT_NODE:
|
|
91
|
+
if (
|
|
92
|
+
serializableShadowRoots
|
|
93
|
+
&& this.shadowRoot instanceof ShadowRoot
|
|
94
|
+
&& (this.shadowRoot.serializable || shadowRoots.includes(this.shadowRoot))
|
|
95
|
+
) {
|
|
96
|
+
return this.shadowRoot.getHTML({ serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts })
|
|
97
|
+
+ serializeChildNodes(this,{ serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts } );
|
|
98
|
+
} else if (serializableShadowRoots && hosts.includes(this)) {
|
|
99
|
+
const shadow = shadowRoots.find(shadow => shadow.host.isSameNode(this));
|
|
100
|
+
|
|
101
|
+
return serializeShadowRoot(shadow, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts })
|
|
102
|
+
+ serializeChildNodes(this, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts } );
|
|
103
|
+
} else {
|
|
104
|
+
return this.innerHTML;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
case Node.DOCUMENT_FRAGMENT_NODE:
|
|
108
|
+
if (! (this instanceof ShadowRoot)) {
|
|
109
|
+
return '';
|
|
110
|
+
} else if (serializableShadowRoots && (this.serializable || shadowRoots.includes(this))) {
|
|
111
|
+
return serializeShadowRoot(this, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts });
|
|
112
|
+
} else {
|
|
113
|
+
return serializeChildNodes(this, { serializableShadowRoots, shadowRoots, [HOSTS_SYMBOL]: hosts });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
default:
|
|
117
|
+
throw new TypeError('\'getHTML\' called on an object that does not implement interface Element.');
|
|
118
|
+
}
|
|
39
119
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgysk8zer0/polyfills",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A collection of JavaScript polyfills",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"urlpattern-polyfill": "^10.0.0"
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
|
-
"@aegisjsproject/sanitizer": "^0.1.
|
|
95
|
+
"@aegisjsproject/sanitizer": "^0.1.3",
|
|
96
96
|
"@aegisjsproject/trusted-types": "^1.0.1"
|
|
97
97
|
}
|
|
98
98
|
}
|