@shgysk8zer0/polyfills 0.0.2

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +2 -0
  4. package/abort.js +194 -0
  5. package/all.js +30 -0
  6. package/animation.js +33 -0
  7. package/aom.js +43 -0
  8. package/appBadge.js +27 -0
  9. package/array.js +258 -0
  10. package/assets/CookieStore.js +230 -0
  11. package/assets/Lock.js +31 -0
  12. package/assets/LockManager.js +278 -0
  13. package/assets/Sanitizer.js +59 -0
  14. package/assets/SanitizerConfig.js +348 -0
  15. package/assets/SanitizerConfigBase.js +18 -0
  16. package/assets/SanitizerConfigEdge.js +335 -0
  17. package/assets/SanitizerConfigW3C.js +766 -0
  18. package/assets/Scheduler.js +124 -0
  19. package/assets/TextDecoder.js +83 -0
  20. package/assets/TextEncoder.js +41 -0
  21. package/assets/TrustedTypes.js +595 -0
  22. package/assets/attributes.js +15 -0
  23. package/assets/csp.js +29 -0
  24. package/assets/error.js +8 -0
  25. package/assets/sanitizerUtils.js +270 -0
  26. package/assets/trust.js +190 -0
  27. package/assets/utility.js +101 -0
  28. package/cookieStore.js +2 -0
  29. package/crypto.js +8 -0
  30. package/dialog.js +63 -0
  31. package/element.js +171 -0
  32. package/elementInternals.js +616 -0
  33. package/errors.js +21 -0
  34. package/function.js +31 -0
  35. package/globalThis.js +36 -0
  36. package/iterator.js +197 -0
  37. package/legacy/array.js +15 -0
  38. package/legacy/element.js +140 -0
  39. package/legacy/map.js +168 -0
  40. package/legacy/object.js +42 -0
  41. package/legacy.js +9 -0
  42. package/locks.js +33 -0
  43. package/map.js +32 -0
  44. package/match-media.js +58 -0
  45. package/math.js +69 -0
  46. package/navigator.js +55 -0
  47. package/number.js +14 -0
  48. package/package.json +52 -0
  49. package/performance.js +36 -0
  50. package/promise.js +70 -0
  51. package/sanitizer.js +3 -0
  52. package/scheduler.js +5 -0
  53. package/secure-context.js +31 -0
  54. package/set.js +73 -0
  55. package/share.js +17 -0
  56. package/symbols.js +9 -0
  57. package/textEncoder.js +16 -0
  58. package/trustedTypes.js +3 -0
  59. package/weakMap.js +28 -0
  60. package/window.js +85 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * @copyright 2023 Chris Zuber <admin@kernvalley.us>
3
+ */
4
+ import { SanitizerConfig as defaultConfig } from './SanitizerConfigW3C.js';
5
+ import { createPolicy } from './trust.js';
6
+ import { isObject, getType, callOnce } from './utility.js';
7
+ import { urls } from './attributes.js';
8
+
9
+ export const supported = () => 'Sanitizer' in globalThis;
10
+ export const nativeSupport = supported();
11
+
12
+ const allowProtocols = ['https:'];
13
+
14
+ if (! allowProtocols.includes(location.protocol)) {
15
+ allowProtocols.push(location.protocol);
16
+ }
17
+ const policyName = 'sanitizer-raw#html';
18
+ const getPolicy = callOnce(() => createPolicy(policyName, { createHTML: input => input }));
19
+ const createHTML = input => getPolicy().createHTML(input);
20
+
21
+ function documentToFragment(doc) {
22
+ const frag = document.createDocumentFragment();
23
+ const clone = doc.cloneNode(true);
24
+ frag.append(...clone.head.childNodes, ...clone.body.childNodes);
25
+ return frag;
26
+ }
27
+
28
+ export function sanitize(input, { config = defaultConfig } = {}) {
29
+ if (! (input instanceof Node)) {
30
+ throw new TypeError('sanitize requires a Document or DocumentFragment');
31
+ } else if (input.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
32
+ return sanitizeNode(input, config);
33
+ } else if (input.nodeType === Node.DOCUMENT_NODE) {
34
+ return sanitizeNode(documentToFragment(input), { config });
35
+ } else {
36
+ throw new TypeError('sanitize requires a Document or DocumentFragment');
37
+ }
38
+ }
39
+
40
+ export function sanitizeFor(tag, content, { config = defaultConfig } = {}) {
41
+ const el = document.createElement(tag);
42
+ const temp = document.createElement('template');
43
+ temp.innerHTML = createHTML(content);
44
+ el.append(sanitize(temp.content, { config }));
45
+ return el;
46
+ }
47
+
48
+ export function sanitizeNode(node, { config = defaultConfig } = {}) {
49
+ try {
50
+ if (! (node instanceof Node)) {
51
+ throw new TypeError(`Expected a Node but got a ${getType(node)}.`);
52
+ } else if (! isObject(config)) {
53
+ throw new TypeError(`Expected config to be an object but got ${getType(config)}.`);
54
+ }
55
+
56
+ const {
57
+ allowElements, allowComments, allowAttributes, allowCustomElements,
58
+ blockElements, dropAttributes, dropElements, allowUnknownMarkup,
59
+ } = config;
60
+
61
+ switch(node.nodeType) {
62
+ case Node.TEXT_NODE:
63
+ break;
64
+
65
+ case Node.ELEMENT_NODE: {
66
+ if (
67
+ ! allowUnknownMarkup
68
+ && ( ! (node instanceof HTMLElement) || node instanceof HTMLUnknownElement)
69
+ ) {
70
+ node.remove();
71
+ break;
72
+ }
73
+
74
+ const tag = node.tagName.toLowerCase();
75
+
76
+ if (Array.isArray(dropElements) && dropElements.includes(tag)) {
77
+ node.remove();
78
+ } else if (Array.isArray(blockElements) && blockElements.includes(tag)) {
79
+ if (node.hasChildNodes()) {
80
+ [...node.childNodes].forEach(node => sanitizeNode(node, config));
81
+ node.replaceWith(...node.childNodes);
82
+ } else {
83
+ node.remove();
84
+ }
85
+ } else if (tag.includes('-') && ! allowCustomElements) {
86
+ node.remove();
87
+ } else if (Array.isArray(allowElements) && ! allowElements.includes(tag)) {
88
+ node.remove();
89
+ } else if (tag === 'template') {
90
+ sanitizeNode(node.content, config);
91
+ } else {
92
+ if (node.hasAttributes()) {
93
+ node.getAttributeNames()
94
+ .forEach(attr => sanitizeNode(node.getAttributeNode(attr), config));
95
+ }
96
+
97
+ if (node.hasChildNodes()) {
98
+ [...node.childNodes].forEach(node => sanitizeNode(node, config));
99
+ }
100
+ }
101
+
102
+ break;
103
+ }
104
+
105
+ case Node.ATTRIBUTE_NODE: {
106
+ const { value, ownerElement } = node;
107
+ const name = node.name.toLowerCase();
108
+ const tag = ownerElement.tagName.toLowerCase();
109
+
110
+ if (
111
+ urls.includes(name)
112
+ && ! allowProtocols.includes(new URL(value, document.baseURI).protocol)
113
+ ) {
114
+ ownerElement.removeAttributeNode(node);
115
+ } else if (isObject(dropAttributes)) {
116
+ if (
117
+ name in dropAttributes
118
+ && ['*', tag].some(sel => dropAttributes[name].includes(sel))
119
+ ) {
120
+ ownerElement.removeAttributeNode(node);
121
+
122
+ if (name.startsWith('on')) {
123
+ delete ownerElement[name];
124
+ }
125
+ }
126
+ } else if (isObject(allowAttributes)) {
127
+ if (
128
+ ! name.startsWith('data-')
129
+ && ! (name in allowAttributes
130
+ && ['*', tag].some(sel => allowAttributes[name].includes(sel)))
131
+ ) {
132
+ ownerElement.removeAttributeNode(node);
133
+
134
+ if (name.startsWith('on')) {
135
+ delete ownerElement[name];
136
+ }
137
+ }
138
+ }
139
+
140
+ break;
141
+ }
142
+
143
+ case Node.COMMENT_NODE: {
144
+ if (! allowComments) {
145
+ node.remove();
146
+ }
147
+
148
+ break;
149
+ }
150
+
151
+ case Node.DOCUMENT_NODE:
152
+ case Node.DOCUMENT_FRAGMENT_NODE: {
153
+ if (node.hasChildNodes()) {
154
+ [...node.childNodes].forEach(node => sanitizeNode(node, config));
155
+ }
156
+
157
+ break;
158
+ }
159
+
160
+ case Node.CDATA_SECTION_NODE:
161
+ case Node.PROCESSING_INSTRUCTION_NODE:
162
+ case Node.DOCUMENT_TYPE_NODE:
163
+ default: {
164
+ node.parentElement.removeChild(node);
165
+ }
166
+ }
167
+ } catch(err) {
168
+ node.parentElement.removeChild(node);
169
+ console.error(err);
170
+ }
171
+
172
+ return node;
173
+ }
174
+
175
+ export function getSantizerUtils(Sanitizer, defaultConfig) {
176
+ const setHTML = function setHTML(el, input, { sanitizer = new Sanitizer() } = {}) {
177
+ const div = sanitizer.sanitizeFor('div', input);
178
+ el.replaceChildren(...div.children);
179
+ };
180
+
181
+ const polyfill = function polyfill() {
182
+ let polyfilled = false;
183
+
184
+ if (! supported()) {
185
+ globalThis.Sanitizer = Sanitizer;
186
+ polyfilled = true;
187
+ } else {
188
+ if (! (globalThis.Sanitizer.getDefaultConfiguration instanceof Function)) {
189
+ globalThis.Sanitizer.getDefaultConfiguration = function() {
190
+ return defaultConfig;
191
+ };
192
+ polyfilled = true;
193
+ }
194
+
195
+ if (! (globalThis.Sanitizer.prototype.getConfiguration instanceof Function)) {
196
+ const configs = new WeakMap();
197
+ const SanitizerNative = globalThis.Sanitizer;
198
+
199
+ globalThis.Sanitizer = class Sanitizer extends SanitizerNative {
200
+ constructor({
201
+ allowAttributes, allowComments, allowElements, allowCustomElements,
202
+ blockElements, dropAttributes, dropElements, allowUnknownMarkup,
203
+ } = SanitizerNative.getDefaultConfiguration()) {
204
+ super({
205
+ allowAttributes, allowComments, allowElements, allowCustomElements,
206
+ blockElements, dropAttributes, dropElements, allowUnknownMarkup,
207
+ });
208
+ configs.set(this, {
209
+ allowAttributes, allowComments, allowElements, allowCustomElements,
210
+ blockElements, dropAttributes, dropElements, allowUnknownMarkup,
211
+ });
212
+ }
213
+
214
+ getConfiguration() {
215
+ return configs.get(this);
216
+ }
217
+ };
218
+ polyfilled = true;
219
+ }
220
+
221
+ if (! (globalThis.Sanitizer.prototype.sanitize instanceof Function)) {
222
+ globalThis.Sanitizer.prototype.sanitize = function(input) {
223
+ if (! (input instanceof Node)) {
224
+ throw new TypeError('`Sanitizer.sanitize()` expects a `Node`');
225
+ } else if (! [Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE].includes(input.nodeType)) {
226
+ throw new TypeError('Expected a Document or DocumentFragment in `Sanitizer.sanitize()`.');
227
+ } else {
228
+ return sanitize(input, { config: this.getConfiguration() });
229
+ }
230
+ };
231
+ polyfilled = true;
232
+ }
233
+
234
+ if (
235
+ ! (globalThis.Sanitizer.prototype.sanitizeFor instanceof Function)
236
+ && Element.prototype.setHTML instanceof Function
237
+ ) {
238
+ globalThis.Sanitizer.prototype.sanitizeFor = function(element, input) {
239
+ const el = document.createElement(element);
240
+ el.setHTML(input, { sanitizer: this });
241
+ return el;
242
+ };
243
+ polyfilled = true;
244
+ } else if (! (globalThis.Sanitizer.prototype.sanitizeFor instanceof Function)) {
245
+ globalThis.Sanitizer.prototype.sanitizeFor = function(element, input) {
246
+ const el = document.createElement(element);
247
+ const tmp = document.createElement('template');
248
+ tmp.innerHTML = createHTML(input);
249
+ el.append(this.sanitize(tmp.content));
250
+ return el;
251
+ };
252
+ polyfilled = true;
253
+ }
254
+
255
+ if (! (Element.prototype.setHTML instanceof Function)) {
256
+ Element.prototype.setHTML = function(input, { sanitizer = new globalThis.Sanitizer() } = {}) {
257
+ const el = sanitizer.sanitizeFor('div', input);
258
+ this.replaceChildren(...el.children);
259
+ };
260
+ polyfilled = true;
261
+ }
262
+ }
263
+
264
+ return polyfilled;
265
+ };
266
+
267
+ return { setHTML, polyfill };
268
+ }
269
+
270
+ export const trustPolicies = [policyName];
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @copyright 2023 Chris Zuber <admin@kernvalley.us>
3
+ */
4
+ import { callOnce } from './utility.js';
5
+
6
+ export function setProp(el, prop, val, {
7
+ policy,
8
+ } = {}) {
9
+ switch(getPropertyType(el.tagName, prop)) {
10
+ case 'TrustedScript':
11
+ el[prop] = createScript(val, { policy });
12
+ break;
13
+
14
+ case 'TrustedScriptURL':
15
+ el[prop] = createScriptURL(val, { policy });
16
+ break;
17
+
18
+ case 'TrustedHTML':
19
+ el[prop] = createHTML(val, { policy });
20
+ break;
21
+
22
+ default:
23
+ el[prop] = val;
24
+ }
25
+ }
26
+
27
+ export function setAttr(el, attr, val, {
28
+ elementNs,
29
+ policy,
30
+ } = {}) {
31
+ switch(getAttributeType(el.tagName, attr, elementNs)) {
32
+ case 'TrustedScriptURL':
33
+ if (typeof elementNs === 'string') {
34
+ el.setAttributeNs(elementNs, attr, createScriptURL(val, { policy }));
35
+ } else {
36
+ el.setAttribute(attr, createScriptURL(val, { policy }));
37
+ }
38
+ break;
39
+
40
+ case 'TrustedScript':
41
+ if (typeof elementNs === 'string') {
42
+ el.setAttributeNS(elementNs, attr, createScript(val, { policy }));
43
+ } else {
44
+ el.setAttribute(attr, createScript(val, { policy }));
45
+ }
46
+ break;
47
+
48
+ case 'TrustedHTML':
49
+ if (typeof elementNs === 'string') {
50
+ el.setAttributeNS(elementNs, attr, createHTML(val, { policy }));
51
+ } else {
52
+ el.setAttribute(attr, createHTML(val, { policy }));
53
+ }
54
+ break;
55
+
56
+ default:
57
+ if (typeof elementNs === 'string') {
58
+ el.setAttributeNS(elementNs, attr, val);
59
+ } else {
60
+ el.setAttribute(attr, val);
61
+ }
62
+ }
63
+ }
64
+
65
+ export function supported() {
66
+ return 'trustedTypes' in globalThis
67
+ && trustedTypes instanceof EventTarget
68
+ && trustedTypes.createPolicy instanceof Function;
69
+ }
70
+
71
+ export function isTrustPolicy(policy) {
72
+ if ('TrustedTypePolicy' in globalThis && policy instanceof TrustedTypePolicy) {
73
+ return true;
74
+ } else {
75
+ return policy != null && policy.createHTML instanceof Function;
76
+ }
77
+ }
78
+
79
+ export function hasDefaultPolicy() {
80
+ return supported() && isTrustPolicy(trustedTypes.defaultPolicy);
81
+ }
82
+
83
+ export function getAttributeType(tagName, attribute, elementNs) {
84
+ if (supported()) {
85
+ return trustedTypes.getAttributeType(tagName.toLowerCase(), attribute, elementNs);
86
+ } else {
87
+ return null;
88
+ }
89
+ }
90
+
91
+ export function getPropertyType(tagName, property) {
92
+ if (supported()) {
93
+ return trustedTypes.getPropertyType(tagName.toLowerCase(), property);
94
+ } else {
95
+ return null;
96
+ }
97
+ }
98
+
99
+ export function isHTML(input) {
100
+ if (supported()) {
101
+ return trustedTypes.isHTML(input);
102
+ } else {
103
+ return typeof input === 'string';
104
+ }
105
+ }
106
+
107
+ export function isScript(input) {
108
+ if (supported()) {
109
+ return trustedTypes.isScript(input);
110
+ } else {
111
+ return typeof input === 'string';
112
+ }
113
+ }
114
+
115
+ export function isScriptURL(input) {
116
+ if (supported()) {
117
+ return trustedTypes.isScriptURL(input);
118
+ } else {
119
+ return typeof input === 'string' || input instanceof URL;
120
+ }
121
+ }
122
+
123
+ export function isTrustedType(input) {
124
+ if (supported()) {
125
+ return trustedTypes.isHTML(input) || trustedTypes.isScript(input) || trustedTypes.isScriptURL(input);
126
+ } else {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ export function createHTML(input, { policy = getDefaultPolicy() } = {}) {
132
+ if (isTrustPolicy(policy) && ! isHTML(input)) {
133
+ return policy.createHTML(input);
134
+ } else {
135
+ return input;
136
+ }
137
+ }
138
+
139
+ export function createScript(input, { policy = getDefaultPolicy() } = {}) {
140
+ if (isTrustPolicy(policy) && ! isScript(input)) {
141
+ return policy.createScript(input);
142
+ } else {
143
+ return input;
144
+ }
145
+ }
146
+
147
+ export function createScriptURL(input, { policy = getDefaultPolicy() } = {}) {
148
+ if (isTrustPolicy(policy) && ! isScriptURL(input)) {
149
+ return policy.createScriptURL(input);
150
+ } else {
151
+ return input;
152
+ }
153
+ }
154
+
155
+ export function createPolicy(name, {
156
+ createHTML = () => {
157
+ throw new TypeError('This policy does not provide `createHTML()`');
158
+ },
159
+ createScript = () => {
160
+ throw new TypeError('This policy does not provide `createScript()`');
161
+ },
162
+ createScriptURL = () => {
163
+ throw new TypeError('This policy does not provide `createScriptURL()`');
164
+ },
165
+ }) {
166
+ if (supported()) {
167
+ return trustedTypes.createPolicy(name, { createHTML, createScript, createScriptURL });
168
+ } else {
169
+ return Object.freeze({
170
+ name,
171
+ createHTML: (input, ...args) => createHTML(input.toString(), ...args),
172
+ createScript: (input, ...args) => createScript(input.toString(), ...args),
173
+ createScriptURL: (input, ...args) => createScriptURL(input.toString(), ...args),
174
+ });
175
+ }
176
+ }
177
+
178
+ export function getDefaultPolicy() {
179
+ if (supported()) {
180
+ return trustedTypes.defaultPolicy;
181
+ } else {
182
+ return null;
183
+ }
184
+ }
185
+
186
+ export const createHTMLPolicyGetter = (name, cb) => callOnce(() => createPolicy(name, { createHTML: cb }));
187
+
188
+ export const createScriptPolicyGetter = (name, cb) => callOnce(() => createPolicy(name, { createScript: cb }));
189
+
190
+ export const createScriptURLPolicyGetter = (name, cb) => callOnce(() => createPolicy(name, { createScriptURL: cb }));
@@ -0,0 +1,101 @@
1
+ const funcs = new WeakMap();
2
+
3
+ export const between = (min, val, max) => typeof val === 'number' && val >= min && val <= max;
4
+
5
+ export function getDeferred() {
6
+ if (Promise.withResolvers instanceof Function) {
7
+ return Promise.withResolvers();
8
+ } else {
9
+ const def = {};
10
+ def.promise = new Promise((resolve, reject) => {
11
+ def.resolve = resolve;
12
+ def.reject = reject;
13
+ });
14
+
15
+ return def;
16
+ }
17
+ }
18
+
19
+ export function isAsyncFunction(what) {
20
+ return what instanceof Function && what.constructor.name === 'AsyncFunction';
21
+ }
22
+
23
+ export function isAsync(what) {
24
+ return isAsyncFunction(what) || what instanceof Promise;
25
+ }
26
+
27
+ export function isObject(thing) {
28
+ return typeof thing === 'object' && ! Object.is(thing, null) && ! Array.isArray(thing);
29
+ }
30
+
31
+ export function getType(thing) {
32
+ switch (typeof thing) {
33
+ case 'undefined':
34
+ return 'Undefined';
35
+
36
+ case 'function':
37
+ if ('prototype' in thing) {
38
+ return getType(thing.prototype);
39
+ } else if ('constructor' in thing) {
40
+ return thing.constructor.name;
41
+ } else {
42
+ return 'Function';
43
+ }
44
+
45
+ case 'object':
46
+ if (Object.is(thing, null)) {
47
+ return 'Null';
48
+ } else if ('constructor' in thing) {
49
+ return thing.constructor.name;
50
+ } else if (Symbol.toStringTag in thing) {
51
+ return thing[Symbol.toStringTag];
52
+ } else if ('prototype' in thing) {
53
+ return getType(thing.prototype);
54
+ } else {
55
+ console.log(thing);
56
+ return 'Unknown Object';
57
+ }
58
+
59
+ case 'string':
60
+ return 'String';
61
+
62
+ case 'number':
63
+ return Number.isNaN(thing) ? 'NaN' : 'Number';
64
+
65
+ case 'bigint':
66
+ return 'BigInt';
67
+
68
+ case 'boolean':
69
+ return 'Boolean';
70
+
71
+ case 'symbol':
72
+ return 'Symbol';
73
+
74
+ default:
75
+ return 'Unknown';
76
+ }
77
+ }
78
+
79
+ export function callOnce(callback, thisArg) {
80
+ if (callback.once instanceof Function) {
81
+ return callback.once(thisArg);
82
+ } else {
83
+ return function(...args) {
84
+ if (funcs.has(callback)) {
85
+ return funcs.get(callback);
86
+ } else if (isAsyncFunction(callback)) {
87
+ const retVal = callback.apply(thisArg || this, args).catch(err => {
88
+ funcs.delete(callback);
89
+ throw err;
90
+ });
91
+
92
+ funcs.set(callback, retVal);
93
+ return retVal;
94
+ } else if (callback instanceof Function) {
95
+ const retVal = callback.apply(thisArg || this, args);
96
+ funcs.set(callback, retVal);
97
+ return retVal;
98
+ }
99
+ };
100
+ }
101
+ }
package/cookieStore.js ADDED
@@ -0,0 +1,2 @@
1
+ import { polyfill } from './assets/CookieStore.js';
2
+ export const polyfilled = polyfill();
package/crypto.js ADDED
@@ -0,0 +1,8 @@
1
+ if (crypto in globalThis && ! (crypto.randomUUID instanceof Function)) {
2
+ crypto.randomUUID = function randomUUID() {
3
+ 'use strict';
4
+ return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
5
+ (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
6
+ );
7
+ };
8
+ }
package/dialog.js ADDED
@@ -0,0 +1,63 @@
1
+ (function() {
2
+ 'use strict';
3
+
4
+ if (document.createElement('dialog') instanceof HTMLUnknownElement && ! HTMLUnknownElement.prototype.hasOwnProperty('open')) {
5
+ HTMLUnknownElement.prototype.show = function() {
6
+ this.open = true;
7
+ };
8
+
9
+ HTMLUnknownElement.prototype.close = function(returnValue = null) {
10
+ this.open = false;
11
+ if (this.tagName === 'DIALOG') {
12
+ const event = new CustomEvent('close');
13
+
14
+ if (typeof returnValue === 'string') {
15
+ event.returnValue = true;
16
+ this.returnValue = returnValue;
17
+ }
18
+ this.dispatchEvent(event);
19
+ delete this.returnValue;
20
+ }
21
+ };
22
+
23
+ Object.defineProperty(HTMLUnknownElement.prototype, 'open', {
24
+ set: function(open) {
25
+ if (this.tagName === 'DETAILS') {
26
+ this.dispatchEvent(new CustomEvent('toggle'));
27
+ this.toggleAttribute('open', open);
28
+ } else if (this.tagName === 'DIALOG') {
29
+ this.toggleAttribute('open', open);
30
+ if (! open) {
31
+ this.classList.remove('modal');
32
+ const next = this.nextElementSibling;
33
+ if (next instanceof HTMLElement && next.matches('.backdrop')) {
34
+ next.remove();
35
+ }
36
+ }
37
+ }
38
+ },
39
+ get: function() {
40
+ return this.hasAttribute('open');
41
+ }
42
+ });
43
+ }
44
+
45
+ if (! (document.createElement('dialog').showModal instanceof Function)) {
46
+ HTMLUnknownElement.prototype.showModal = function() {
47
+ const controller = new AbortController();
48
+ const signal = controller.signal;
49
+ document.addEventListener('keydown', function escapeHandle({ key }) {
50
+ if (key === 'Escape') {
51
+ this.close();
52
+ }
53
+ }, { passive: true, signal });
54
+
55
+ this.addEventListener('close', () => controller.abort(), { once: true, signal });
56
+ this.open = true;
57
+ this.classList.add('modal');
58
+ const backdrop = document.createElement('div');
59
+ backdrop.classList.add('backdrop');
60
+ this.after(backdrop);
61
+ };
62
+ }
63
+ })();