@shgysk8zer0/polyfills 0.4.4 → 0.4.6
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 +83 -9
- package/methods/dom.js +84 -4
- package/package.json +1 -1
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,32 @@
|
|
|
1
1
|
import { aria } from './aom.js';
|
|
2
|
-
import { polyfillGetterSetter, polyfillMethod } from './utils.js';
|
|
3
|
-
import { setHTMLUnsafe } from './methods/dom.js';
|
|
2
|
+
import { polyfillGetterSetter, polyfillMethod, overwriteMethod } from './utils.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);
|
|
8
|
+
|
|
9
|
+
if ('CustomElementRegistry' in globalThis && ! (CustomElementRegistry.prototype.getName instanceof Function)) {
|
|
10
|
+
const registry = new Map();
|
|
11
|
+
|
|
12
|
+
overwriteMethod(CustomElementRegistry.prototype, 'define', original => {
|
|
13
|
+
return (tag, proto, opts) => {
|
|
14
|
+
original.call(customElements, tag,proto, opts);
|
|
15
|
+
registry.set(proto, typeof opts.extends === 'string' ? opts.extends : tag);
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
polyfillMethod(CustomElementRegistry.prototype, 'getName', proto => {
|
|
20
|
+
if (typeof proto === 'function') {
|
|
21
|
+
return registry.get(proto) ?? null;
|
|
22
|
+
} else if (typeof proto === 'object' && proto !== null) {
|
|
23
|
+
// Unnecessary, but just to be sure the same errors are thrown.
|
|
24
|
+
throw new TypeError('CustomElementRegistry.getName: Argument 1 is not callable.');
|
|
25
|
+
} else {
|
|
26
|
+
throw new TypeError('CustomElementRegistry.getName: Argument 1 is not an object.');
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
7
30
|
|
|
8
31
|
function handlePopover({ currentTarget }) {
|
|
9
32
|
switch(currentTarget.popoverTargetAction) {
|
|
@@ -25,7 +48,7 @@ export function initPopover(target = document.body) {
|
|
|
25
48
|
.forEach(el => el.addEventListener('click', handlePopover));
|
|
26
49
|
}
|
|
27
50
|
|
|
28
|
-
if ((globalThis.ToggleEvent instanceof Function)) {
|
|
51
|
+
if (! (globalThis.ToggleEvent instanceof Function)) {
|
|
29
52
|
class ToggleEvent extends Event {
|
|
30
53
|
#newState;
|
|
31
54
|
#oldState;
|
|
@@ -246,26 +269,77 @@ if (! (HTMLImageElement.prototype.decode instanceof Function)) {
|
|
|
246
269
|
}
|
|
247
270
|
|
|
248
271
|
if (! HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode')) {
|
|
249
|
-
|
|
250
|
-
|
|
272
|
+
const attachedShadows = new WeakMap();
|
|
273
|
+
|
|
274
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootMode', {
|
|
275
|
+
get() {
|
|
251
276
|
return this.getAttribute('shadowrootmode');
|
|
252
277
|
},
|
|
253
|
-
set
|
|
278
|
+
set(val) {
|
|
254
279
|
this.setAttribute('shadowrootmode', val);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
polyfillGetterSetter(HTMLTemplateElement.prototype, 'shadowRootDelegatesFocus', {
|
|
284
|
+
get() {
|
|
285
|
+
return this.hasAttribute('shadowrootdelegatesfocus');
|
|
255
286
|
},
|
|
256
|
-
|
|
257
|
-
|
|
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');
|
|
304
|
+
},
|
|
305
|
+
set(val) {
|
|
306
|
+
this.toggleAttribute('shadowrootserializable', val);
|
|
307
|
+
}
|
|
258
308
|
});
|
|
259
309
|
|
|
260
310
|
const attachShadows = (base = document) => {
|
|
261
311
|
base.querySelectorAll('template[shadowrootmode]').forEach(tmp => {
|
|
262
|
-
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
|
+
|
|
263
319
|
shadow.append(tmp.content);
|
|
264
320
|
tmp.remove();
|
|
265
321
|
attachShadows(shadow);
|
|
322
|
+
attachedShadows.set(shadow.host, shadow);
|
|
266
323
|
});
|
|
267
324
|
};
|
|
268
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
|
+
|
|
269
343
|
if (document.readyState === 'loading') {
|
|
270
344
|
document.addEventListener('readystatechange', () => attachShadows(document), { once: true });
|
|
271
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
|
}
|