@qti-components/base 2.0.1 → 2.1.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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/register.js +4 -0
- package/dist/register.js.map +1 -1
- package/dist/utils/custom-state-set.d.ts +56 -0
- package/dist/utils/custom-state-set.js +130 -0
- package/dist/utils/custom-state-set.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,5 +23,6 @@ export * from './lib/expression-result';
|
|
|
23
23
|
export * from './lib/interaction.interface';
|
|
24
24
|
export * from './lib/variables';
|
|
25
25
|
export * from './lib/qti-rule-base';
|
|
26
|
+
export * from './utils/custom-state-set';
|
|
26
27
|
export * from './utils/scoring-helper';
|
|
27
28
|
export * from './utils/utils';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export * from './lib/expression-result';
|
|
|
23
23
|
export * from './lib/interaction.interface';
|
|
24
24
|
export * from './lib/variables';
|
|
25
25
|
export * from './lib/qti-rule-base';
|
|
26
|
+
export * from './utils/custom-state-set';
|
|
26
27
|
export * from './utils/scoring-helper';
|
|
27
28
|
export * from './utils/utils';
|
|
28
29
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC","sourcesContent":["import './register';\n\nexport * from './context/computed-item.context';\nexport * from './context/types/computed-item.types';\nexport * from './context/computed.context';\nexport * from './context/types/computed.types';\nexport * from './context/config.context';\nexport * from './context/interaction.context';\nexport * from './context/item.context';\nexport * from './context/types/item.types';\nexport * from './context/qti.context';\nexport * from './context/session.context';\nexport * from './context/test.context';\n\nexport * from './styles/box-sizing.styles';\nexport * from './styles/drop-region.styles';\nexport * from './styles/validation-message.styles';\n\nexport * from './abstract/interaction';\nexport * from './abstract/qti-variable-declaration';\nexport * from './abstract/qti-condition-expression';\nexport * from './abstract/qti-feedback';\nexport * from './abstract/qti-expression';\n\nexport * from './lib/response';\nexport * from './lib/expression-result';\nexport * from './lib/interaction.interface';\nexport * from './lib/variables';\nexport * from './lib/qti-rule-base';\n\nexport * from './utils/scoring-helper';\nexport * from './utils/utils';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC","sourcesContent":["import './register';\n\nexport * from './context/computed-item.context';\nexport * from './context/types/computed-item.types';\nexport * from './context/computed.context';\nexport * from './context/types/computed.types';\nexport * from './context/config.context';\nexport * from './context/interaction.context';\nexport * from './context/item.context';\nexport * from './context/types/item.types';\nexport * from './context/qti.context';\nexport * from './context/session.context';\nexport * from './context/test.context';\n\nexport * from './styles/box-sizing.styles';\nexport * from './styles/drop-region.styles';\nexport * from './styles/validation-message.styles';\n\nexport * from './abstract/interaction';\nexport * from './abstract/qti-variable-declaration';\nexport * from './abstract/qti-condition-expression';\nexport * from './abstract/qti-feedback';\nexport * from './abstract/qti-expression';\n\nexport * from './lib/response';\nexport * from './lib/expression-result';\nexport * from './lib/interaction.interface';\nexport * from './lib/variables';\nexport * from './lib/qti-rule-base';\n\nexport * from './utils/custom-state-set';\nexport * from './utils/scoring-helper';\nexport * from './utils/utils';\n"]}
|
package/dist/register.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { qtiBaseElements } from './elements';
|
|
2
|
+
import { installCustomStateSetFallback } from './utils/custom-state-set';
|
|
3
|
+
// Before any element is defined, so the patched `attachInternals` is in place by
|
|
4
|
+
// the time the first interaction upgrades. No-op where `states` works natively.
|
|
5
|
+
installCustomStateSetFallback();
|
|
2
6
|
for (const { tag, ctor } of qtiBaseElements) {
|
|
3
7
|
if (!customElements.get(tag)) {
|
|
4
8
|
customElements.define(tag, ctor);
|
package/dist/register.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAEzE,iFAAiF;AACjF,gFAAgF;AAChF,6BAA6B,EAAE,CAAC;AAEhC,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,eAAe,EAAE,CAAC;IAC5C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;AACH,CAAC","sourcesContent":["import { qtiBaseElements } from './elements';\nimport { installCustomStateSetFallback } from './utils/custom-state-set';\n\n// Before any element is defined, so the patched `attachInternals` is in place by\n// the time the first interaction upgrades. No-op where `states` works natively.\ninstallCustomStateSetFallback();\n\nfor (const { tag, ctor } of qtiBaseElements) {\n if (!customElements.get(tag)) {\n customElements.define(tag, ctor);\n }\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom-state support shim for browsers this library cannot use natively.
|
|
3
|
+
*
|
|
4
|
+
* Interactions track selection and correct/incorrect marking through
|
|
5
|
+
* `internals.states`, with bare state names (`checked`, `radio`,
|
|
6
|
+
* `correct-response`, …). Two browser profiles cannot service that:
|
|
7
|
+
*
|
|
8
|
+
* - Safari 16.4–17.3 implement `ElementInternals` but not `CustomStateSet`, so
|
|
9
|
+
* `internals.states` is `undefined` and reading it throws — "undefined is not
|
|
10
|
+
* an object (evaluating 'internals.states.has')".
|
|
11
|
+
* - Chrome and Edge before the CSS custom-state spec change expose `states` but
|
|
12
|
+
* reject names that do not start with `--`, so `states.add('radio')` throws
|
|
13
|
+
* "The specified value 'radio' must start with '--'".
|
|
14
|
+
*
|
|
15
|
+
* On both, picking a choice throws rather than registering. `attachInternals`
|
|
16
|
+
* itself is unguarded throughout the interaction base classes, which puts the
|
|
17
|
+
* hard support floor at Safari 16.4 — inside the range that breaks.
|
|
18
|
+
*
|
|
19
|
+
* The shim replaces `states` with a permissive `Set` and mirrors its contents to
|
|
20
|
+
* a space-separated `data-state` attribute on the host, because these browsers'
|
|
21
|
+
* CSS parsers also drop any selector list containing `:state()`. The theme's
|
|
22
|
+
* built stylesheets pair every `:state(x)` with a `[data-state~='x']` arm (see
|
|
23
|
+
* `tools/postcss/custom-state-fallback.mjs`), so styling survives too.
|
|
24
|
+
*
|
|
25
|
+
* Support is classified by behaviour rather than by browser version, and the
|
|
26
|
+
* shim is not installed at all where `states` works.
|
|
27
|
+
*/
|
|
28
|
+
/** A permissive `CustomStateSet` that mirrors its contents to `data-state`. */
|
|
29
|
+
export declare class CustomStateSetShim extends Set<string> {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(host: HTMLElement);
|
|
32
|
+
add(state: string): this;
|
|
33
|
+
delete(state: string): boolean;
|
|
34
|
+
clear(): void;
|
|
35
|
+
}
|
|
36
|
+
export type CustomStateSupport = 'missing' | 'legacy' | 'modern';
|
|
37
|
+
/**
|
|
38
|
+
* Classify native custom-state support by behaviour:
|
|
39
|
+
*
|
|
40
|
+
* - `missing`: `ElementInternals` carries no `states` at all.
|
|
41
|
+
* - `legacy`: `states` exists but rejects a bare name.
|
|
42
|
+
* - `modern`: `states` exists and accepts a bare name — no shim wanted.
|
|
43
|
+
*
|
|
44
|
+
* `attachInternals` only works on a custom element, so the probe registers a
|
|
45
|
+
* throwaway one. That happens solely when `states` is present, so the `missing`
|
|
46
|
+
* path never defines anything.
|
|
47
|
+
*/
|
|
48
|
+
export declare function classifyCustomStateSupport(attachInternals: (this: HTMLElement) => ElementInternals): CustomStateSupport;
|
|
49
|
+
/**
|
|
50
|
+
* Patch `HTMLElement.prototype.attachInternals` so every element's `states` is
|
|
51
|
+
* the shim, on the two profiles that need it. Idempotent, and a no-op where
|
|
52
|
+
* native support is usable.
|
|
53
|
+
*
|
|
54
|
+
* Returns the classification that was acted on, which is what the tests assert.
|
|
55
|
+
*/
|
|
56
|
+
export declare function installCustomStateSetFallback(): CustomStateSupport | 'already-installed';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom-state support shim for browsers this library cannot use natively.
|
|
3
|
+
*
|
|
4
|
+
* Interactions track selection and correct/incorrect marking through
|
|
5
|
+
* `internals.states`, with bare state names (`checked`, `radio`,
|
|
6
|
+
* `correct-response`, …). Two browser profiles cannot service that:
|
|
7
|
+
*
|
|
8
|
+
* - Safari 16.4–17.3 implement `ElementInternals` but not `CustomStateSet`, so
|
|
9
|
+
* `internals.states` is `undefined` and reading it throws — "undefined is not
|
|
10
|
+
* an object (evaluating 'internals.states.has')".
|
|
11
|
+
* - Chrome and Edge before the CSS custom-state spec change expose `states` but
|
|
12
|
+
* reject names that do not start with `--`, so `states.add('radio')` throws
|
|
13
|
+
* "The specified value 'radio' must start with '--'".
|
|
14
|
+
*
|
|
15
|
+
* On both, picking a choice throws rather than registering. `attachInternals`
|
|
16
|
+
* itself is unguarded throughout the interaction base classes, which puts the
|
|
17
|
+
* hard support floor at Safari 16.4 — inside the range that breaks.
|
|
18
|
+
*
|
|
19
|
+
* The shim replaces `states` with a permissive `Set` and mirrors its contents to
|
|
20
|
+
* a space-separated `data-state` attribute on the host, because these browsers'
|
|
21
|
+
* CSS parsers also drop any selector list containing `:state()`. The theme's
|
|
22
|
+
* built stylesheets pair every `:state(x)` with a `[data-state~='x']` arm (see
|
|
23
|
+
* `tools/postcss/custom-state-fallback.mjs`), so styling survives too.
|
|
24
|
+
*
|
|
25
|
+
* Support is classified by behaviour rather than by browser version, and the
|
|
26
|
+
* shim is not installed at all where `states` works.
|
|
27
|
+
*/
|
|
28
|
+
/** A permissive `CustomStateSet` that mirrors its contents to `data-state`. */
|
|
29
|
+
export class CustomStateSetShim extends Set {
|
|
30
|
+
#host;
|
|
31
|
+
constructor(host) {
|
|
32
|
+
super();
|
|
33
|
+
this.#host = host;
|
|
34
|
+
}
|
|
35
|
+
add(state) {
|
|
36
|
+
super.add(state);
|
|
37
|
+
this.#reflect();
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
delete(state) {
|
|
41
|
+
const deleted = super.delete(state);
|
|
42
|
+
this.#reflect();
|
|
43
|
+
return deleted;
|
|
44
|
+
}
|
|
45
|
+
clear() {
|
|
46
|
+
super.clear();
|
|
47
|
+
this.#reflect();
|
|
48
|
+
}
|
|
49
|
+
#reflect() {
|
|
50
|
+
// A custom element must not gain attributes while it is upgrading, so defer
|
|
51
|
+
// the write until the element is connected.
|
|
52
|
+
if (this.#host.isConnected)
|
|
53
|
+
this.#write();
|
|
54
|
+
else
|
|
55
|
+
queueMicrotask(() => this.#write());
|
|
56
|
+
}
|
|
57
|
+
#write() {
|
|
58
|
+
if (this.size === 0)
|
|
59
|
+
this.#host.removeAttribute('data-state');
|
|
60
|
+
else
|
|
61
|
+
this.#host.setAttribute('data-state', Array.from(this).join(' '));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const PROBE_TAG = 'custom-state-support-probe';
|
|
65
|
+
/**
|
|
66
|
+
* Classify native custom-state support by behaviour:
|
|
67
|
+
*
|
|
68
|
+
* - `missing`: `ElementInternals` carries no `states` at all.
|
|
69
|
+
* - `legacy`: `states` exists but rejects a bare name.
|
|
70
|
+
* - `modern`: `states` exists and accepts a bare name — no shim wanted.
|
|
71
|
+
*
|
|
72
|
+
* `attachInternals` only works on a custom element, so the probe registers a
|
|
73
|
+
* throwaway one. That happens solely when `states` is present, so the `missing`
|
|
74
|
+
* path never defines anything.
|
|
75
|
+
*/
|
|
76
|
+
export function classifyCustomStateSupport(attachInternals) {
|
|
77
|
+
if (!('states' in ElementInternals.prototype))
|
|
78
|
+
return 'missing';
|
|
79
|
+
if (typeof customElements === 'undefined')
|
|
80
|
+
return 'modern';
|
|
81
|
+
try {
|
|
82
|
+
if (!customElements.get(PROBE_TAG))
|
|
83
|
+
customElements.define(PROBE_TAG, class extends HTMLElement {
|
|
84
|
+
});
|
|
85
|
+
const states = attachInternals.call(document.createElement(PROBE_TAG)).states;
|
|
86
|
+
if (!states)
|
|
87
|
+
return 'missing';
|
|
88
|
+
// A bare name throws SyntaxError under the legacy spec and never does under
|
|
89
|
+
// the current one, so classify on the throw alone — not on whether the value
|
|
90
|
+
// sticks, which some environments do not honour on a detached element.
|
|
91
|
+
states.add('probe');
|
|
92
|
+
states.delete('probe');
|
|
93
|
+
return 'modern';
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return 'legacy';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let installed = false;
|
|
100
|
+
/**
|
|
101
|
+
* Patch `HTMLElement.prototype.attachInternals` so every element's `states` is
|
|
102
|
+
* the shim, on the two profiles that need it. Idempotent, and a no-op where
|
|
103
|
+
* native support is usable.
|
|
104
|
+
*
|
|
105
|
+
* Returns the classification that was acted on, which is what the tests assert.
|
|
106
|
+
*/
|
|
107
|
+
export function installCustomStateSetFallback() {
|
|
108
|
+
if (installed)
|
|
109
|
+
return 'already-installed';
|
|
110
|
+
if (typeof HTMLElement === 'undefined' || typeof ElementInternals === 'undefined')
|
|
111
|
+
return 'modern';
|
|
112
|
+
const native = HTMLElement.prototype.attachInternals;
|
|
113
|
+
if (!native)
|
|
114
|
+
return 'modern';
|
|
115
|
+
const support = classifyCustomStateSupport(native);
|
|
116
|
+
if (support === 'modern')
|
|
117
|
+
return support;
|
|
118
|
+
installed = true;
|
|
119
|
+
HTMLElement.prototype.attachInternals = function attachInternals() {
|
|
120
|
+
const internals = native.call(this);
|
|
121
|
+
Object.defineProperty(internals, 'states', {
|
|
122
|
+
configurable: true,
|
|
123
|
+
enumerable: true,
|
|
124
|
+
value: new CustomStateSetShim(this)
|
|
125
|
+
});
|
|
126
|
+
return internals;
|
|
127
|
+
};
|
|
128
|
+
return support;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=custom-state-set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-state-set.js","sourceRoot":"","sources":["../../src/utils/custom-state-set.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom-state support shim for browsers this library cannot use natively.\n *\n * Interactions track selection and correct/incorrect marking through\n * `internals.states`, with bare state names (`checked`, `radio`,\n * `correct-response`, …). Two browser profiles cannot service that:\n *\n * - Safari 16.4–17.3 implement `ElementInternals` but not `CustomStateSet`, so\n * `internals.states` is `undefined` and reading it throws — \"undefined is not\n * an object (evaluating 'internals.states.has')\".\n * - Chrome and Edge before the CSS custom-state spec change expose `states` but\n * reject names that do not start with `--`, so `states.add('radio')` throws\n * \"The specified value 'radio' must start with '--'\".\n *\n * On both, picking a choice throws rather than registering. `attachInternals`\n * itself is unguarded throughout the interaction base classes, which puts the\n * hard support floor at Safari 16.4 — inside the range that breaks.\n *\n * The shim replaces `states` with a permissive `Set` and mirrors its contents to\n * a space-separated `data-state` attribute on the host, because these browsers'\n * CSS parsers also drop any selector list containing `:state()`. The theme's\n * built stylesheets pair every `:state(x)` with a `[data-state~='x']` arm (see\n * `tools/postcss/custom-state-fallback.mjs`), so styling survives too.\n *\n * Support is classified by behaviour rather than by browser version, and the\n * shim is not installed at all where `states` works.\n */\n\n/** A permissive `CustomStateSet` that mirrors its contents to `data-state`. */\nexport class CustomStateSetShim extends Set<string> {\n readonly #host: HTMLElement;\n\n constructor(host: HTMLElement) {\n super();\n this.#host = host;\n }\n\n override add(state: string): this {\n super.add(state);\n this.#reflect();\n return this;\n }\n\n override delete(state: string): boolean {\n const deleted = super.delete(state);\n this.#reflect();\n return deleted;\n }\n\n override clear(): void {\n super.clear();\n this.#reflect();\n }\n\n #reflect(): void {\n // A custom element must not gain attributes while it is upgrading, so defer\n // the write until the element is connected.\n if (this.#host.isConnected) this.#write();\n else queueMicrotask(() => this.#write());\n }\n\n #write(): void {\n if (this.size === 0) this.#host.removeAttribute('data-state');\n else this.#host.setAttribute('data-state', Array.from(this).join(' '));\n }\n}\n\nexport type CustomStateSupport = 'missing' | 'legacy' | 'modern';\n\nconst PROBE_TAG = 'custom-state-support-probe';\n\n/**\n * Classify native custom-state support by behaviour:\n *\n * - `missing`: `ElementInternals` carries no `states` at all.\n * - `legacy`: `states` exists but rejects a bare name.\n * - `modern`: `states` exists and accepts a bare name — no shim wanted.\n *\n * `attachInternals` only works on a custom element, so the probe registers a\n * throwaway one. That happens solely when `states` is present, so the `missing`\n * path never defines anything.\n */\nexport function classifyCustomStateSupport(\n attachInternals: (this: HTMLElement) => ElementInternals\n): CustomStateSupport {\n if (!('states' in ElementInternals.prototype)) return 'missing';\n if (typeof customElements === 'undefined') return 'modern';\n\n try {\n if (!customElements.get(PROBE_TAG)) customElements.define(PROBE_TAG, class extends HTMLElement {});\n const states = attachInternals.call(document.createElement(PROBE_TAG)).states;\n if (!states) return 'missing';\n // A bare name throws SyntaxError under the legacy spec and never does under\n // the current one, so classify on the throw alone — not on whether the value\n // sticks, which some environments do not honour on a detached element.\n states.add('probe');\n states.delete('probe');\n return 'modern';\n } catch {\n return 'legacy';\n }\n}\n\nlet installed = false;\n\n/**\n * Patch `HTMLElement.prototype.attachInternals` so every element's `states` is\n * the shim, on the two profiles that need it. Idempotent, and a no-op where\n * native support is usable.\n *\n * Returns the classification that was acted on, which is what the tests assert.\n */\nexport function installCustomStateSetFallback(): CustomStateSupport | 'already-installed' {\n if (installed) return 'already-installed';\n if (typeof HTMLElement === 'undefined' || typeof ElementInternals === 'undefined') return 'modern';\n\n const native = HTMLElement.prototype.attachInternals;\n if (!native) return 'modern';\n\n const support = classifyCustomStateSupport(native);\n if (support === 'modern') return support;\n\n installed = true;\n HTMLElement.prototype.attachInternals = function attachInternals(this: HTMLElement): ElementInternals {\n const internals = native.call(this);\n Object.defineProperty(internals, 'states', {\n configurable: true,\n enumerable: true,\n value: new CustomStateSetShim(this)\n });\n return internals;\n };\n\n return support;\n}\n"]}
|