interestfor 1.0.10 → 1.0.12

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/README.md CHANGED
@@ -1,11 +1,15 @@
1
- # `interestfor` Polyfill
2
-
3
- A polyfill for the `interestfor` attribute. This is a new API for "interest
4
- interactions" that is in the process of being implemented in browsers. It [shipped](https://chromestatus.com/feature/4530756656562176)
5
- in Chromium browsers (e.g. Chrome, Edge) in version 142 (October 2025).
6
- See the MDN documentation for more details:
7
-
8
- https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using_interest_invokers
1
+ # The Interest Invokers Polyfill
2
+
3
+ A polyfill for the [Interest Invokers
4
+ API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using_interest_invokers),
5
+ including the `interestfor` attribute, the `:interest-source` and
6
+ `:interest-target` pseudo classes, the `::interest-button` pseudo element, and
7
+ the `interest-delay` CSS properties. All of the API except the
8
+ `::interest-button` pseudo element have been
9
+ [shipped](https://chromestatus.com/feature/4530756656562176) in Chromium
10
+ browsers (e.g. Chrome, Edge), since version 142 (October 2025). The
11
+ `::interest-button` pseudo element is planned to be shipped soon in Chromium
12
+ also.
9
13
 
10
14
  ## Usage
11
15
  To use this polyfill, simply load it:
@@ -17,6 +21,12 @@ To use this polyfill, simply load it:
17
21
  <div popover id=foo>Popover</div>
18
22
  ```
19
23
 
24
+ ## Browser Compatibility
25
+
26
+ This polyfill uses all standard APIs, and is therefore compatible with all
27
+ modern browsers, including Safari and Firefox. It supports mobile touchscreen
28
+ operation also, as much as [is possible](#touchscreen-support).
29
+
20
30
  ## Behavior Summary
21
31
 
22
32
  Adding the `interestfor` attribute to a button or link (the "interest
@@ -46,14 +56,16 @@ A few more details:
46
56
  - The `interestForElement` property is available on `HTMLButtonElement`,
47
57
  `HTMLAnchorElement`, `HTMLAreaElement`, and `SVGAElement`. It reflects the
48
58
  `interestfor` attribute as an element reference.
59
+ - The `:interest-source` and `:interest-target` pseudo classes match the
60
+ interest invoker and its target element while interest is shown.
61
+ - The `interest-delay`, `interest-delay-start`, and `interest-delay-end` CSS
62
+ properties control the delays before showing and losing interest.
49
63
  - The optional `::interest-button` pseudo element provides a button that shows
50
64
  interest immediately when clicked or tapped. See below.
51
65
 
52
66
  Again, this is a quick summary. For the complete details, please [read the
53
- OpenUI explainer](https://open-ui.org/components/interest-invokers.explainer),
54
- the [HTML spec PR](https://github.com/whatwg/html/pull/11006), and the CSSWG
55
- specs for [the pseudo classes](https://drafts.csswg.org/selectors-5/#interest-pseudos)
56
- and [CSS properties](https://drafts.csswg.org/css-ui-4/#interest).
67
+ OpenUI explainer](https://open-ui.org/components/interest-invokers.explainer) or
68
+ the [specifications](#standards).
57
69
 
58
70
  ## The `::interest-button` pseudo element
59
71
 
@@ -202,21 +214,45 @@ native feature:
202
214
  - The polyfill installs a `MutationObserver` on the document to keep interest
203
215
  buttons up to date, and prepends a `<style>` element to `<head>` containing
204
216
  the default styles for interest invokers and interest buttons.
217
+ - Accessibility mappings (`aria-labelledby`, `aria-describedby`, `aria-details`,
218
+ `aria-expanded`, and `role="tooltip"`) are set up on a best-effort basis by
219
+ inspecting the DOM. Because JavaScript does not have access to the browser's
220
+ accessibility tree or computed roles/names, the polyfill cannot match the
221
+ native feature in all cases (for example, when accessible names depend on CSS,
222
+ shadow DOM boundaries, or targets without an `id`, or when plain vs. rich
223
+ hint classification depends on computed roles). Native browser support handles
224
+ these accessibility mappings directly in the accessibility tree without
225
+ mutating DOM attributes.
226
+ - On iOS (detected via user-agent sniffing for `iPad`, `iPhone`, or `iPod`),
227
+ long-pressing a `<button interestfor>` shows interest when the finger is
228
+ released (`touchend`) after holding for at least 500ms, rather than while the
229
+ finger is still held down. Unlike Android (which fires `pointercancel` and
230
+ `touchcancel` after a ~500ms hold), iOS WebKit does not cancel the touch
231
+ stream and fires `pointerup` on finger release, which would otherwise trigger
232
+ native popover light-dismiss on a popover opened after `pointerdown`.
205
233
 
206
234
 
207
235
  ## Tests
208
236
 
209
- The `test/test.html` file exercises the various parts of this feature and tests
210
- for correctness. This is a manual test, since hovering, keyboard-focusing, etc.
211
- cannot be done programmatically. The test is capable of testing either the
212
- polyfill (optionally minified) or the native feature. You can run tests directly
213
- from this repo,
237
+ The `test/test.html` file contains an automated test suite that runs on page
238
+ load and tests the polyfill (either `src/interestfor.js` or
239
+ `src/interestfor.min.js`). You can run the tests directly from this repo,
214
240
  [here](https://mfreed7.github.io/interestfor/test/test.html).
215
241
 
216
- ## Browser Compatibility
217
-
218
- This polyfill uses all standard APIs, and is therefore compatible with all
219
- modern browsers, including Safari and Firefox.
242
+ ## Standards
243
+
244
+ This is a recently-developed API, and the specs are in various states of
245
+ standardization:
246
+ - The `::interest-button` CSS pseudo-element is defined here:
247
+ https://drafts.csswg.org/css-pseudo-4/#selectordef-interest-button
248
+ - The `:interest-source` and `:interest-target` CSS pseudo-classes are defined
249
+ here:
250
+ https://drafts.csswg.org/selectors-5/#interest-pseudos
251
+ - The `interest-delay-*` CSS properties are defined here:
252
+ https://drafts.csswg.org/css-ui-4/#interest
253
+ - The HTML spec PR is located here:
254
+ https://github.com/whatwg/html/pull/11006
255
+ (Add a Thumbs Up 👍 to support landing it!)
220
256
 
221
257
  ## Improvements / Bugs
222
258
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "interestfor",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "A polyfill for the `interestfor` API",
5
5
  "keywords": "interestfor, interesttarget, polyfill, hover-triggering",
6
6
  "homepage": "https://github.com/mfreed7/interestfor#readme",
@@ -59,8 +59,7 @@
59
59
  capture: true,
60
60
  });
61
61
  }
62
-
63
- if(!window.InterestEvent) {
62
+ if (!window.InterestEvent || window.interestForUsePolyfillAlways) {
64
63
  window.InterestEvent = class InterestEvent extends Event {
65
64
  #source;
66
65
  constructor(type, eventInit = {}) {
@@ -561,12 +560,111 @@
561
560
  return true;
562
561
  }
563
562
 
563
+ // Best-effort approximation of the Accessible Name Computation algorithm.
564
+ // Because JavaScript cannot query the browser's actual accessibility tree,
565
+ // this only inspects common DOM attributes (`aria-labelledby`, `aria-label`,
566
+ // `alt`, `title`), `<label>` elements, and subtree text nodes. It will miss
567
+ // names or visibility derived from CSS (e.g. `display: none` or `::before`
568
+ // `content`), `ElementInternals`, `ariaLabelledByElements`, or other
569
+ // browser-internal accessibility heuristics.
570
+ function normalizeText(str) {
571
+ return str ? str.replace(/\s+/g, " ").trim() : "";
572
+ }
573
+
574
+ function getSubtreeText(node, isRoot = false) {
575
+ if (node.nodeType === Node.TEXT_NODE) {
576
+ return node.data;
577
+ }
578
+ if (node.nodeType !== Node.ELEMENT_NODE) {
579
+ return "";
580
+ }
581
+ if (
582
+ !isRoot &&
583
+ (node.hasAttribute("hidden") ||
584
+ node.getAttribute("aria-hidden") === "true" ||
585
+ node.localName === "script" ||
586
+ node.localName === "style")
587
+ ) {
588
+ return "";
589
+ }
590
+ const ariaLabel = node.getAttribute("aria-label");
591
+ if (ariaLabel) {
592
+ return ariaLabel;
593
+ }
594
+ if (node.localName === "img" || node.localName === "area") {
595
+ return node.getAttribute("alt") || "";
596
+ }
597
+ let text = "";
598
+ for (const child of node.childNodes) {
599
+ text += getSubtreeText(child);
600
+ }
601
+ return text;
602
+ }
603
+
604
+ function getAccessibleName(el) {
605
+ const labelledBy = el.getAttribute("aria-labelledby");
606
+ if (labelledBy) {
607
+ const root = getRootNode(el);
608
+ if (typeof root.getElementById === "function") {
609
+ const parts = [];
610
+ for (const id of labelledBy.trim().split(/\s+/)) {
611
+ const ref = id ? root.getElementById(id) : null;
612
+ if (ref) {
613
+ parts.push(normalizeText(getSubtreeText(ref, true)));
614
+ }
615
+ }
616
+ const text = normalizeText(parts.join(" "));
617
+ if (text) {
618
+ return text;
619
+ }
620
+ }
621
+ }
622
+ const ariaLabel = normalizeText(el.getAttribute("aria-label"));
623
+ if (ariaLabel) {
624
+ return ariaLabel;
625
+ }
626
+ if (el.labels?.length) {
627
+ const labelText = normalizeText(
628
+ [...el.labels].map((l) => getSubtreeText(l, true)).join(" ")
629
+ );
630
+ if (labelText) {
631
+ return labelText;
632
+ }
633
+ }
634
+ const subtreeText = normalizeText(getSubtreeText(el, true));
635
+ if (subtreeText) {
636
+ return subtreeText;
637
+ }
638
+ return normalizeText(el.getAttribute("title"));
639
+ }
640
+
564
641
  function setupAccessibility(invoker, target) {
642
+ if (!target.id) {
643
+ return;
644
+ }
565
645
  if (isPlainHint(target)) {
566
- invoker.setAttribute("aria-describedby", target.id);
646
+ const labelledBy = invoker.getAttribute("aria-labelledby") || "";
647
+ if (labelledBy.trim().split(/\s+/).includes(target.id)) {
648
+ return;
649
+ }
650
+ const name = getAccessibleName(invoker);
651
+ const hintText = normalizeText(getSubtreeText(target, true));
652
+ if (!name) {
653
+ if (hintText) {
654
+ invoker.setAttribute("aria-labelledby", target.id);
655
+ }
656
+ } else if (
657
+ hintText &&
658
+ hintText !== name &&
659
+ !invoker.hasAttribute("aria-describedby")
660
+ ) {
661
+ invoker.setAttribute("aria-describedby", target.id);
662
+ }
567
663
  } else {
568
664
  // Rich hint
569
- invoker.setAttribute("aria-details", target.id);
665
+ if (!invoker.hasAttribute("aria-details")) {
666
+ invoker.setAttribute("aria-details", target.id);
667
+ }
570
668
  invoker.setAttribute("aria-expanded", "false");
571
669
  if (!target.hasAttribute("role")) {
572
670
  target.setAttribute("role", "tooltip");
@@ -575,23 +673,26 @@
575
673
  }
576
674
 
577
675
  function initializeDataField(el) {
578
- if (el[dataField]) return;
579
- el[dataField] = {
580
- state: InterestState.NoInterest,
581
- target: null,
582
- gainedTimer: null,
583
- lostTimer: null,
584
- longPressTimer: null,
585
- suppressNextFocusInterest: false,
586
- clearGainedTask() {
587
- clearTimeout(this.gainedTimer);
588
- },
589
- clearLostTask() {
590
- clearTimeout(this.lostTimer);
591
- },
592
- };
676
+ if (!el[dataField]) {
677
+ el[dataField] = {
678
+ state: InterestState.NoInterest,
679
+ target: null,
680
+ a11yTarget: null,
681
+ gainedTimer: null,
682
+ lostTimer: null,
683
+ longPressTimer: null,
684
+ suppressNextFocusInterest: false,
685
+ clearGainedTask() {
686
+ clearTimeout(this.gainedTimer);
687
+ },
688
+ clearLostTask() {
689
+ clearTimeout(this.lostTimer);
690
+ },
691
+ };
692
+ }
593
693
  const target = GetInterestForTarget(el);
594
- if (target) {
694
+ if (target && el[dataField].a11yTarget !== target) {
695
+ el[dataField].a11yTarget = target;
595
696
  setupAccessibility(el, target);
596
697
  }
597
698
  }
@@ -764,20 +865,24 @@
764
865
  const updates = [];
765
866
  for (const invoker of invokers) {
766
867
  let content = null;
767
- if (invoker.isConnected && invoker.hasAttribute(attributeName)) {
868
+ const target = GetInterestForTarget(invoker);
869
+ if (target) {
870
+ invokersMissingTarget.delete(invoker);
768
871
  if (wantsInterestButton(invoker)) {
769
- invokersMissingTarget.delete(invoker);
770
872
  content = getInterestButtonContent(invoker);
771
- } else {
772
- // The target might be added (or become valid) later.
773
- invokersMissingTarget.add(invoker);
774
873
  }
874
+ } else if (invoker.isConnected && invoker.hasAttribute(attributeName)) {
875
+ // The target might be added (or become valid) later.
876
+ invokersMissingTarget.add(invoker);
775
877
  } else {
776
878
  invokersMissingTarget.delete(invoker);
777
879
  }
778
- updates.push([invoker, content]);
880
+ updates.push([invoker, target, content]);
779
881
  }
780
- for (const [invoker, content] of updates) {
882
+ for (const [invoker, target, content] of updates) {
883
+ if (target) {
884
+ initializeDataField(invoker);
885
+ }
781
886
  setInterestButton(invoker, content);
782
887
  }
783
888
  }
@@ -879,11 +984,19 @@
879
984
  HandleInterestHoverOrFocus(el, source);
880
985
  }
881
986
  };
987
+ const clearTouch = (e) => {
988
+ if (e.pointerType !== "touch") {
989
+ touchInProgress = false;
990
+ }
991
+ };
992
+ document.body.addEventListener("pointerover", clearTouch);
993
+ document.body.addEventListener("pointerdown", clearTouch);
882
994
  document.body.addEventListener("mouseover", (e) => handler(e, Source.Hover));
883
995
  document.body.addEventListener("mouseout", (e) => handler(e, Source.DeHover));
884
996
  document.body.addEventListener("focusin", (e) => handler(e, Source.Focus));
885
997
  document.body.addEventListener("focusout", (e) => handler(e, Source.Blur));
886
998
  document.body.addEventListener("keydown", (e) => {
999
+ touchInProgress = false;
887
1000
  if (e.key === "Escape" && !e.defaultPrevented) {
888
1001
  // Lose interest in all invokers, most recent first. These
889
1002
  // `loseinterest` events are not cancelable.
@@ -899,20 +1012,37 @@
899
1012
  });
900
1013
  // Touch support
901
1014
  const longPressTime = 500;
1015
+ // WebKit (iOS) long-press workaround:
1016
+ // On Android, holding for ~500ms fires `pointercancel` and `touchcancel`,
1017
+ // so the popover can open immediately when `longPressTimer` fires while the
1018
+ // finger is still down. On iOS WebKit, holding a button with
1019
+ // `user-select: none` does not cancel the touch stream; when the finger is
1020
+ // later lifted, iOS fires `pointerup` (which triggers WebKit's native
1021
+ // popover light-dismiss because the popover was not open at `pointerdown`)
1022
+ // followed by `touchend` and synthesized mouse/`click` events.
1023
+ // On iOS only, record that the 500ms hold succeeded (`longPressActive`),
1024
+ // and on `touchend` (which fires after `pointerup`), call `preventDefault()`
1025
+ // to suppress the synthesized `click` and open the popover then.
1026
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
902
1027
  document.body.addEventListener("touchstart", (e) => {
903
1028
  touchInProgress = true;
904
1029
  const invoker = e.target.closest("button[interestfor]");
905
1030
  if (invoker) {
906
1031
  initializeDataField(invoker);
1032
+ invoker[dataField].longPressActive = false;
907
1033
  invoker[dataField].longPressTimer = setTimeout(() => {
908
1034
  // Long-press shows interest immediately, and that interest is not
909
1035
  // lost by de-hovering or blurring the invoker.
910
1036
  if (invoker[dataField].state === InterestState.NoInterest) {
911
- GainOrLoseInterest(
912
- invoker,
913
- GetInterestForTarget(invoker),
914
- InterestState.ExplicitInterest
915
- );
1037
+ if (isIOS) {
1038
+ invoker[dataField].longPressActive = true;
1039
+ } else {
1040
+ GainOrLoseInterest(
1041
+ invoker,
1042
+ GetInterestForTarget(invoker),
1043
+ InterestState.ExplicitInterest
1044
+ );
1045
+ }
916
1046
  }
917
1047
  invoker[dataField].longPressTimer = null;
918
1048
  }, longPressTime);
@@ -920,16 +1050,34 @@
920
1050
  });
921
1051
  const cancelLongPress = (e) => {
922
1052
  const invoker = e.target.closest("button[interestfor]");
923
- if (invoker && invoker[dataField]?.longPressTimer) {
1053
+ if (invoker && invoker[dataField]) {
924
1054
  clearTimeout(invoker[dataField].longPressTimer);
925
1055
  invoker[dataField].longPressTimer = null;
1056
+ invoker[dataField].longPressActive = false;
926
1057
  }
927
1058
  };
928
- document.body.addEventListener("touchend", (e) => {
929
- cancelLongPress(e);
930
- touchInProgress = false;
931
- });
932
1059
  document.body.addEventListener("touchmove", cancelLongPress);
1060
+ document.body.addEventListener(
1061
+ "touchend",
1062
+ (e) => {
1063
+ const invoker = e.target.closest("button[interestfor]");
1064
+ const longPressActive = invoker?.[dataField]?.longPressActive;
1065
+ cancelLongPress(e);
1066
+ if (longPressActive) {
1067
+ if (e.cancelable) {
1068
+ e.preventDefault();
1069
+ }
1070
+ if (invoker[dataField].state === InterestState.NoInterest) {
1071
+ GainOrLoseInterest(
1072
+ invoker,
1073
+ GetInterestForTarget(invoker),
1074
+ InterestState.ExplicitInterest
1075
+ );
1076
+ }
1077
+ }
1078
+ },
1079
+ { passive: false }
1080
+ );
933
1081
  }
934
1082
 
935
1083
  // CSS registration
@@ -1010,7 +1158,8 @@
1010
1158
  ].forEach((iface) => {
1011
1159
  if (
1012
1160
  iface &&
1013
- !Object.prototype.hasOwnProperty.call(iface.prototype, "interestForElement")
1161
+ (!Object.prototype.hasOwnProperty.call(iface.prototype, "interestForElement") ||
1162
+ window.interestForUsePolyfillAlways)
1014
1163
  ) {
1015
1164
  Object.defineProperty(iface.prototype, "interestForElement", descriptor);
1016
1165
  }
@@ -5,4 +5,4 @@
5
5
  // LICENSE file in the root directory of this source tree.
6
6
 
7
7
 
8
- !function(){const e="interestfor",t="interest",n="loseinterest",r="--interest-delay-start",o="--interest-delay-end",s="--interest-delay",i="--interest-button-content",a="--interest-button-polyfill-content",l="interest-button",c="__interestForData",u="__interestForTargetData",d="__interestForButton",f="__interestForButtonInvoker",m="__interestForExplicitTarget",p="http://www.w3.org/1999/xlink",h=new Set,b=new Set,y=new Set;let g=!1;if(window.interestForPolyfillInstalled)return;window.interestForPolyfillInstalled=!0;const w=HTMLButtonElement.prototype.hasOwnProperty("interestForElement");if(w&&!window.interestForUsePolyfillAlways)return;if(w){const e=e=>{e.isTrusted&&(e.preventDefault(),e.stopImmediatePropagation())};document.body.addEventListener(t,e,{capture:!0}),document.body.addEventListener(n,e,{capture:!0})}window.InterestEvent||(window.InterestEvent=class extends Event{#e;constructor(e,t={}){super(e,t);const{source:n}=t;if(null!=n&&!(n instanceof Element))throw new TypeError("source must be an element");this.#e=n}get[Symbol.toStringTag](){return"InterestEvent"}get source(){const e=this.#e;if(!e)return null;const t=S(e);return t!==S(this.target||document)?t.host:e}});const E="none",v="full",T="explicit",L="hover",A="dehover",x="focus",k="blur";function S(e){return e&&"function"==typeof e.getRootNode?e.getRootNode():e&&e.parentNode?S(e.parentNode):e}function N(e,r,o,s={}){if(!e||!r)return!1;if(!e.isConnected||I(e)!==r||o===E&&F(r)!==e)return!1;if(o!==E){q(e),e[c].state!==E&&e[c].target!==r&&H(e);const n=F(r);if(n){if(n===e)return o===T&&(n[c].state=T),n[c].clearLostTask(),!1;if(!N(n,r,E))return!1;if(!e.isConnected||I(e)!==r)return!1}return function(e,n,r){const o=e[c];switch(r){case v:case T:if(o.state!==E)throw new Error("Invalid state");if(!n.dispatchEvent(new InterestEvent(t,{source:e,cancelable:!0})))return!1;if(o.state=r,o.target=n,o.clearGainedTask(),n[u]||(n[u]={}),n[u].invoker=e,n.hasAttribute("popover")&&(n.addEventListener("beforetoggle",M),n[u].shownByInterest=!1,!$(n))){try{n.showPopover({source:e})}catch{}n[u].shownByInterest=$(n)}if(h.add(e),e.classList.add("interest-source"),n.classList.add("interest-target"),G(n)||e.setAttribute("aria-expanded","true"),"none"===getComputedStyle(e).anchorName&&"auto"===getComputedStyle(n).positionAnchor){const t=`--interest-anchor-${Math.random().toString(36).substring(2)}`;e.style.anchorName=t,n.style.positionAnchor=t,o.anchorName=t}break;default:throw new Error("Invalid state")}return!0}(e,r,o)}return function(e,{force:t=!1,closePopovers:r=!0}={}){const o=e[c];if(clearTimeout(o.gainedTimer),clearTimeout(o.lostTimer),o.state===E)return!0;const s=o.target,i=s.dispatchEvent(new InterestEvent(n,{source:e,cancelable:!t}));if(!i)return!1;const a=s[u],l=r&&a?.invoker===e&&a.shownByInterest;H(e),l&&$(s)&&function(e,t){const n=e[c],r=t.contains(document.activeElement);r&&(n.suppressNextFocusInterest=!0);try{t.hidePopover()}catch{}r&&(n.suppressNextFocusInterest=!1)}(e,s);return!0}(e,s)}function P(e){const t=1e3*V(e,o);if(!_(t))return;const n=e[c];n.clearLostTask();const r=n.target;n.lostTimer=setTimeout(()=>{N(e,r,E)},t)}function F(e){const t=e[u]?.invoker||null;if(!t)return null;const n=t[c];return n&&n.state!==E&&n.target===e&&I(t)===e?t:null}function I(t){if(!t.hasAttribute(e)||!t.isConnected||!function(e){if(e instanceof HTMLButtonElement)return!e.matches(":disabled");if(e instanceof HTMLAnchorElement||e instanceof HTMLAreaElement)return e.hasAttribute("href");if(window.SVGAElement&&e instanceof SVGAElement)return e.hasAttribute("href")||e.hasAttributeNS(p,"href");return!1}(t))return null;const n=C(t);return n&&n.isConnected?n:null}function C(t){const n=t.getAttribute(e);if(null===n)return null;const r=t[m];if(r&&""===n)return function(e,t){const n=S(e);let r=S(t);for(;r;){if(r===n)return!0;r=r instanceof ShadowRoot?S(r.host):null}return!1}(r,t)?r:null;if(""===n)return null;const o=S(t);return"function"==typeof o.getElementById?o.getElementById(n):null}function $(e){try{return e.matches(":popover-open")}catch{return!1}}function M(e){if("closed"!==e.newState)return;const t=e.currentTarget,n=F(t);if(n){if(t.contains(document.activeElement)){const e=n[c];e.suppressNextFocusInterest=!0,setTimeout(()=>{e.suppressNextFocusInterest=!1},0)}N(n,t,E,{closePopovers:!1})}}function _(e){return e>=0&&e<=2147483647}function B(e){const t=String(e).trim(),n=t.match(/^([\d.]+(?:e[+-]?\d+)?)s$/i);if(n)return parseFloat(n[1]);const r=t.match(/^([\d.]+(?:e[+-]?\d+)?)ms$/i);return r?parseFloat(r[1])/1e3:/\b(infinity|nan)\b/i.test(t)?1/0:parseFloat(t)||0}function V(e,t){const n=getComputedStyle(e),o=n.getPropertyValue(t).trim();if("normal"!==o.toLowerCase())return B(o);const i=n.getPropertyValue(s).trim();if(i&&"normal"!==i.toLowerCase()){const e=i.split(/\s+/).filter(e=>e.length>0);if(e.length>0){const n=e[0],o=e.length>1?e[1]:n;let s;if(s=t===r?n:o,"normal"!==s.toLowerCase())return B(s)}}return t===r?.25:.15}function H(e){const t=e[c];if(!t)return;if(clearTimeout(t.gainedTimer),clearTimeout(t.lostTimer),t.state===E)return;const n=t.target;h.delete(e),e.classList.remove("interest-source"),n&&(n[u]?.invoker===e&&(n.removeEventListener("beforetoggle",M),n[u]=null,n.classList.remove("interest-target")),G(n)||e.setAttribute("aria-expanded","false")),t.anchorName&&(e.style.anchorName="",n&&(n.style.positionAnchor=""),t.anchorName=null),t.state=E,t.target=null}const O=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex="-1"])'].join(",");function G(e){if("hint"!==e.getAttribute("popover")?.toLowerCase())return!1;if(e.querySelector(O))return!1;if(e.querySelector("h1,h2,h3,h4,h5,h6,ul,ol,li,table,nav,header,footer,main,aside,article,section,form,blockquote,details,summary,dialog"))return!1;const t=e.querySelectorAll("[role]");for(const e of t){const t=e.getAttribute("role").toLowerCase();if(!["presentation","none","generic","image"].includes(t))return!1}return!0}function q(e){if(e[c])return;e[c]={state:E,target:null,gainedTimer:null,lostTimer:null,longPressTimer:null,suppressNextFocusInterest:!1,clearGainedTask(){clearTimeout(this.gainedTimer)},clearLostTask(){clearTimeout(this.lostTimer)}};const t=I(e);t&&function(e,t){G(t)?e.setAttribute("aria-describedby",t.id):(e.setAttribute("aria-details",t.id),e.setAttribute("aria-expanded","false"),t.hasAttribute("role")||t.setAttribute("role","tooltip"))}(e,t)}function D(e){if(e[f])return e[f];if(e.assignedSlot)return e.assignedSlot;if(e.parentElement)return e.parentElement;const t=e.parentNode;return t instanceof ShadowRoot?t.host:null}function z(e){const t=new Set;if(0===h.size)return t;const n=e=>{const r=F(e);r&&!t.has(r)&&(t.add(r),n(r));const o=D(e);o&&!t.has(o)&&n(o)};return n(e),t}function j(e,t){if(g)return;if(!e.isConnected)return;const n=I(e);n&&q(e);const o=e[c];if(t===L||t===x){let s=!1;o&&(o.clearLostTask(),s=t===x&&o.suppressNextFocusInterest,o.suppressNextFocusInterest=!1);for(const t of z(e))t[c].clearLostTask();n&&!s&&o.state===E&&function(e,t){const n=I(e);if(!n)return;const o=1e3*V(e,r);_(o)&&(e[c].clearGainedTask(),e[c].gainedTimer=setTimeout(()=>{N(e,n,t)},o))}(e,v)}else if(o&&(o.clearGainedTask(),o.state===v&&P(e)),t===k||!e.matches(":hover"))for(const t of z(e))P(t)}function R(e){return e instanceof HTMLElement&&"area"!==e.localName&&!!e.parentNode&&!!I(e)}function U(e){const t=getComputedStyle(e).getPropertyValue(i).trim();return t&&"none"!==t&&"normal"!==t?t:null}function J(){const e=this[f];e&&N(e,I(e),T)}function K(e,t){let n=e[d];t?(n||(n=e.ownerDocument.createElement("span"),n.className=l,n.setAttribute("aria-hidden","true"),n[f]=e,n.addEventListener("click",J),e[d]=n,b.add(e)),n.style.getPropertyValue(a)!==t&&n.style.setProperty(a,t),e.nextSibling!==n&&e.after(n)):n&&(n.remove(),e[d]=null,b.delete(e))}function Q(t){const n=[];for(const r of t){let t=null;r.isConnected&&r.hasAttribute(e)?R(r)?(y.delete(r),t=U(r)):y.add(r):y.delete(r),n.push([r,t])}for(const[e,t]of n)K(e,t)}function W(t){const n=new Set;let r=!1;const o=t=>{if(t.nodeType===Node.ELEMENT_NODE&&!t[f]){t.hasAttribute(e)&&n.add(t);for(const r of t.querySelectorAll(`[${e}]`))n.add(r)}};for(const s of t){if("childList"===s.type){for(const e of s.addedNodes)e.nodeType!==Node.ELEMENT_NODE||e[f]||(r=!0,o(e));continue}const t=s.target;switch(s.attributeName){case e:null!==s.oldValue&&H(t),""!==t.getAttribute(e)&&(t[m]=null),n.add(t);break;case"id":r=!0;break;default:o(t)}}for(const e of[...h]){const t=e[c].target;e.isConnected&&t&&t.isConnected||H(e)}if(r)for(const e of y)n.add(e);for(const e of[...b])n.has(e)||(R(e)?e.nextSibling!==e[d]&&e.after(e[d]):K(e,null));Q(n)}function X(){!function(){const t=document.createElement("style");t.textContent=`@property ${r} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${o} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${s} {syntax: "*"; inherits: false;}\n @property ${i} {syntax: "*"; inherits: false;}`,document.head.appendChild(t);const n=`span.${l}[aria-hidden]`,u=document.createElement("style");u.textContent=`:where(button[${e}]) {\n -webkit-user-select: none;\n user-select: none;\n }\n :where(${n}) {\n display: inline-block;\n vertical-align: middle;\n color: inherit;\n background-color: transparent;\n box-sizing: border-box;\n min-inline-size: 24px;\n min-block-size: 24px;\n min-block-size: max(24px, 1lh);\n align-content: center;\n text-align: center;\n -webkit-user-select: none;\n user-select: none;\n cursor: help;\n }\n :where(${n}:hover) {\n background-color: color-mix(in oklab, currentColor 10%, transparent);\n }\n :where(${n}:active) {\n background-color: color-mix(in oklab, currentColor 20%, transparent);\n }\n :where(${n})::before {\n content: var(${a});\n }`,document.head.prepend(u),document[c]={globalPropsStyle:t,uaStyle:u}}(),function(){const e=(e,t)=>{const n=e.composedPath(),r=n.length?n[0]:e.target;if(r instanceof Element)for(let e=r;e;e=D(e))j(e,t)};document.body.addEventListener("mouseover",t=>e(t,L)),document.body.addEventListener("mouseout",t=>e(t,A)),document.body.addEventListener("focusin",t=>e(t,x)),document.body.addEventListener("focusout",t=>e(t,k)),document.body.addEventListener("keydown",e=>{"Escape"!==e.key||e.defaultPrevented||[...h].reverse().forEach(e=>{N(e,e[c].target,E,{force:!0})})}),document.body.addEventListener("touchstart",e=>{g=!0;const t=e.target.closest("button[interestfor]");t&&(q(t),t[c].longPressTimer=setTimeout(()=>{t[c].state===E&&N(t,I(t),T),t[c].longPressTimer=null},500))});const t=e=>{const t=e.target.closest("button[interestfor]");t&&t[c]?.longPressTimer&&(clearTimeout(t[c].longPressTimer),t[c].longPressTimer=null)};document.body.addEventListener("touchend",e=>{t(e),g=!1}),document.body.addEventListener("touchmove",t)}(),new MutationObserver(W).observe(document,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,attributeFilter:[e,"id","href","disabled"]}),Q(document.querySelectorAll(`[${e}]`)),console.log(`interestfor polyfill installed (native: ${w}).`)}!function(){const t={configurable:!0,enumerable:!0,get(){return C(this)},set(t){if(null==t)this[m]=null,this.removeAttribute(e);else{if(!(t instanceof Element))throw new TypeError("Failed to set the 'interestForElement' property: The provided value is not of type 'Element'.");this[m]=t,this.setAttribute(e,"")}}};[window.HTMLButtonElement,window.HTMLAnchorElement,window.HTMLAreaElement,window.SVGAElement].forEach(e=>{e&&!Object.prototype.hasOwnProperty.call(e.prototype,"interestForElement")&&Object.defineProperty(e.prototype,"interestForElement",t)})}(),"complete"===document.readyState?X():window.addEventListener("load",X)}();
8
+ !function(){const e="interestfor",t="interest",n="loseinterest",r="--interest-delay-start",o="--interest-delay-end",s="--interest-delay",i="--interest-button-content",a="--interest-button-polyfill-content",l="interest-button",c="__interestForData",u="__interestForTargetData",d="__interestForButton",f="__interestForButtonInvoker",m="__interestForExplicitTarget",p="http://www.w3.org/1999/xlink",h=new Set,b=new Set,g=new Set;let y=!1;if(window.interestForPolyfillInstalled)return;window.interestForPolyfillInstalled=!0;const E=HTMLButtonElement.prototype.hasOwnProperty("interestForElement");if(E&&!window.interestForUsePolyfillAlways)return;if(E){const e=e=>{e.isTrusted&&(e.preventDefault(),e.stopImmediatePropagation())};document.body.addEventListener(t,e,{capture:!0}),document.body.addEventListener(n,e,{capture:!0})}window.InterestEvent&&!window.interestForUsePolyfillAlways||(window.InterestEvent=class extends Event{#e;constructor(e,t={}){super(e,t);const{source:n}=t;if(null!=n&&!(n instanceof Element))throw new TypeError("source must be an element");this.#e=n}get[Symbol.toStringTag](){return"InterestEvent"}get source(){const e=this.#e;if(!e)return null;const t=P(e);return t!==P(this.target||document)?t.host:e}});const w="none",v="full",T="explicit",A="hover",L="dehover",N="focus",x="blur";function P(e){return e&&"function"==typeof e.getRootNode?e.getRootNode():e&&e.parentNode?P(e.parentNode):e}function k(e,r,o,s={}){if(!e||!r)return!1;if(!e.isConnected||I(e)!==r||o===w&&F(r)!==e)return!1;if(o!==w){z(e),e[c].state!==w&&e[c].target!==r&&D(e);const n=F(r);if(n){if(n===e)return o===T&&(n[c].state=T),n[c].clearLostTask(),!1;if(!k(n,r,w))return!1;if(!e.isConnected||I(e)!==r)return!1}return function(e,n,r){const o=e[c];switch(r){case v:case T:if(o.state!==w)throw new Error("Invalid state");if(!n.dispatchEvent(new InterestEvent(t,{source:e,cancelable:!0})))return!1;if(o.state=r,o.target=n,o.clearGainedTask(),n[u]||(n[u]={}),n[u].invoker=e,n.hasAttribute("popover")&&(n.addEventListener("beforetoggle",_),n[u].shownByInterest=!1,!$(n))){try{n.showPopover({source:e})}catch{}n[u].shownByInterest=$(n)}if(h.add(e),e.classList.add("interest-source"),n.classList.add("interest-target"),H(n)||e.setAttribute("aria-expanded","true"),"none"===getComputedStyle(e).anchorName&&"auto"===getComputedStyle(n).positionAnchor){const t=`--interest-anchor-${Math.random().toString(36).substring(2)}`;e.style.anchorName=t,n.style.positionAnchor=t,o.anchorName=t}break;default:throw new Error("Invalid state")}return!0}(e,r,o)}return function(e,{force:t=!1,closePopovers:r=!0}={}){const o=e[c];if(clearTimeout(o.gainedTimer),clearTimeout(o.lostTimer),o.state===w)return!0;const s=o.target,i=s.dispatchEvent(new InterestEvent(n,{source:e,cancelable:!t}));if(!i)return!1;const a=s[u],l=r&&a?.invoker===e&&a.shownByInterest;D(e),l&&$(s)&&function(e,t){const n=e[c],r=t.contains(document.activeElement);r&&(n.suppressNextFocusInterest=!0);try{t.hidePopover()}catch{}r&&(n.suppressNextFocusInterest=!1)}(e,s);return!0}(e,s)}function S(e){const t=1e3*O(e,o);if(!M(t))return;const n=e[c];n.clearLostTask();const r=n.target;n.lostTimer=setTimeout(()=>{k(e,r,w)},t)}function F(e){const t=e[u]?.invoker||null;if(!t)return null;const n=t[c];return n&&n.state!==w&&n.target===e&&I(t)===e?t:null}function I(t){if(!t.hasAttribute(e)||!t.isConnected||!function(e){if(e instanceof HTMLButtonElement)return!e.matches(":disabled");if(e instanceof HTMLAnchorElement||e instanceof HTMLAreaElement)return e.hasAttribute("href");if(window.SVGAElement&&e instanceof SVGAElement)return e.hasAttribute("href")||e.hasAttributeNS(p,"href");return!1}(t))return null;const n=C(t);return n&&n.isConnected?n:null}function C(t){const n=t.getAttribute(e);if(null===n)return null;const r=t[m];if(r&&""===n)return function(e,t){const n=P(e);let r=P(t);for(;r;){if(r===n)return!0;r=r instanceof ShadowRoot?P(r.host):null}return!1}(r,t)?r:null;if(""===n)return null;const o=P(t);return"function"==typeof o.getElementById?o.getElementById(n):null}function $(e){try{return e.matches(":popover-open")}catch{return!1}}function _(e){if("closed"!==e.newState)return;const t=e.currentTarget,n=F(t);if(n){if(t.contains(document.activeElement)){const e=n[c];e.suppressNextFocusInterest=!0,setTimeout(()=>{e.suppressNextFocusInterest=!1},0)}k(n,t,w,{closePopovers:!1})}}function M(e){return e>=0&&e<=2147483647}function B(e){const t=String(e).trim(),n=t.match(/^([\d.]+(?:e[+-]?\d+)?)s$/i);if(n)return parseFloat(n[1]);const r=t.match(/^([\d.]+(?:e[+-]?\d+)?)ms$/i);return r?parseFloat(r[1])/1e3:/\b(infinity|nan)\b/i.test(t)?1/0:parseFloat(t)||0}function O(e,t){const n=getComputedStyle(e),o=n.getPropertyValue(t).trim();if("normal"!==o.toLowerCase())return B(o);const i=n.getPropertyValue(s).trim();if(i&&"normal"!==i.toLowerCase()){const e=i.split(/\s+/).filter(e=>e.length>0);if(e.length>0){const n=e[0],o=e.length>1?e[1]:n;let s;if(s=t===r?n:o,"normal"!==s.toLowerCase())return B(s)}}return t===r?.25:.15}function D(e){const t=e[c];if(!t)return;if(clearTimeout(t.gainedTimer),clearTimeout(t.lostTimer),t.state===w)return;const n=t.target;h.delete(e),e.classList.remove("interest-source"),n&&(n[u]?.invoker===e&&(n.removeEventListener("beforetoggle",_),n[u]=null,n.classList.remove("interest-target")),H(n)||e.setAttribute("aria-expanded","false")),t.anchorName&&(e.style.anchorName="",n&&(n.style.positionAnchor=""),t.anchorName=null),t.state=w,t.target=null}const V=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex="-1"])'].join(",");function H(e){if("hint"!==e.getAttribute("popover")?.toLowerCase())return!1;if(e.querySelector(V))return!1;if(e.querySelector("h1,h2,h3,h4,h5,h6,ul,ol,li,table,nav,header,footer,main,aside,article,section,form,blockquote,details,summary,dialog"))return!1;const t=e.querySelectorAll("[role]");for(const e of t){const t=e.getAttribute("role").toLowerCase();if(!["presentation","none","generic","image"].includes(t))return!1}return!0}function G(e){return e?e.replace(/\s+/g," ").trim():""}function j(e,t=!1){if(e.nodeType===Node.TEXT_NODE)return e.data;if(e.nodeType!==Node.ELEMENT_NODE)return"";if(!t&&(e.hasAttribute("hidden")||"true"===e.getAttribute("aria-hidden")||"script"===e.localName||"style"===e.localName))return"";const n=e.getAttribute("aria-label");if(n)return n;if("img"===e.localName||"area"===e.localName)return e.getAttribute("alt")||"";let r="";for(const t of e.childNodes)r+=j(t);return r}function q(e,t){if(t.id)if(H(t)){if((e.getAttribute("aria-labelledby")||"").trim().split(/\s+/).includes(t.id))return;const n=function(e){const t=e.getAttribute("aria-labelledby");if(t){const n=P(e);if("function"==typeof n.getElementById){const e=[];for(const r of t.trim().split(/\s+/)){const t=r?n.getElementById(r):null;t&&e.push(G(j(t,!0)))}const r=G(e.join(" "));if(r)return r}}const n=G(e.getAttribute("aria-label"));if(n)return n;if(e.labels?.length){const t=G([...e.labels].map(e=>j(e,!0)).join(" "));if(t)return t}return G(j(e,!0))||G(e.getAttribute("title"))}(e),r=G(j(t,!0));n?r&&r!==n&&!e.hasAttribute("aria-describedby")&&e.setAttribute("aria-describedby",t.id):r&&e.setAttribute("aria-labelledby",t.id)}else e.hasAttribute("aria-details")||e.setAttribute("aria-details",t.id),e.setAttribute("aria-expanded","false"),t.hasAttribute("role")||t.setAttribute("role","tooltip")}function z(e){e[c]||(e[c]={state:w,target:null,a11yTarget:null,gainedTimer:null,lostTimer:null,longPressTimer:null,suppressNextFocusInterest:!1,clearGainedTask(){clearTimeout(this.gainedTimer)},clearLostTask(){clearTimeout(this.lostTimer)}});const t=I(e);t&&e[c].a11yTarget!==t&&(e[c].a11yTarget=t,q(e,t))}function R(e){if(e[f])return e[f];if(e.assignedSlot)return e.assignedSlot;if(e.parentElement)return e.parentElement;const t=e.parentNode;return t instanceof ShadowRoot?t.host:null}function U(e){const t=new Set;if(0===h.size)return t;const n=e=>{const r=F(e);r&&!t.has(r)&&(t.add(r),n(r));const o=R(e);o&&!t.has(o)&&n(o)};return n(e),t}function X(e,t){if(y)return;if(!e.isConnected)return;const n=I(e);n&&z(e);const o=e[c];if(t===A||t===N){let s=!1;o&&(o.clearLostTask(),s=t===N&&o.suppressNextFocusInterest,o.suppressNextFocusInterest=!1);for(const t of U(e))t[c].clearLostTask();n&&!s&&o.state===w&&function(e,t){const n=I(e);if(!n)return;const o=1e3*O(e,r);M(o)&&(e[c].clearGainedTask(),e[c].gainedTimer=setTimeout(()=>{k(e,n,t)},o))}(e,v)}else if(o&&(o.clearGainedTask(),o.state===v&&S(e)),t===x||!e.matches(":hover"))for(const t of U(e))S(t)}function J(e){return e instanceof HTMLElement&&"area"!==e.localName&&!!e.parentNode&&!!I(e)}function K(e){const t=getComputedStyle(e).getPropertyValue(i).trim();return t&&"none"!==t&&"normal"!==t?t:null}function Q(){const e=this[f];e&&k(e,I(e),T)}function W(e,t){let n=e[d];t?(n||(n=e.ownerDocument.createElement("span"),n.className=l,n.setAttribute("aria-hidden","true"),n[f]=e,n.addEventListener("click",Q),e[d]=n,b.add(e)),n.style.getPropertyValue(a)!==t&&n.style.setProperty(a,t),e.nextSibling!==n&&e.after(n)):n&&(n.remove(),e[d]=null,b.delete(e))}function Y(t){const n=[];for(const r of t){let t=null;const o=I(r);o?(g.delete(r),J(r)&&(t=K(r))):r.isConnected&&r.hasAttribute(e)?g.add(r):g.delete(r),n.push([r,o,t])}for(const[e,t,r]of n)t&&z(e),W(e,r)}function Z(t){const n=new Set;let r=!1;const o=t=>{if(t.nodeType===Node.ELEMENT_NODE&&!t[f]){t.hasAttribute(e)&&n.add(t);for(const r of t.querySelectorAll(`[${e}]`))n.add(r)}};for(const s of t){if("childList"===s.type){for(const e of s.addedNodes)e.nodeType!==Node.ELEMENT_NODE||e[f]||(r=!0,o(e));continue}const t=s.target;switch(s.attributeName){case e:null!==s.oldValue&&D(t),""!==t.getAttribute(e)&&(t[m]=null),n.add(t);break;case"id":r=!0;break;default:o(t)}}for(const e of[...h]){const t=e[c].target;e.isConnected&&t&&t.isConnected||D(e)}if(r)for(const e of g)n.add(e);for(const e of[...b])n.has(e)||(J(e)?e.nextSibling!==e[d]&&e.after(e[d]):W(e,null));Y(n)}function ee(){!function(){const t=document.createElement("style");t.textContent=`@property ${r} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${o} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${s} {syntax: "*"; inherits: false;}\n @property ${i} {syntax: "*"; inherits: false;}`,document.head.appendChild(t);const n=`span.${l}[aria-hidden]`,u=document.createElement("style");u.textContent=`:where(button[${e}]) {\n -webkit-user-select: none;\n user-select: none;\n }\n :where(${n}) {\n display: inline-block;\n vertical-align: middle;\n color: inherit;\n background-color: transparent;\n box-sizing: border-box;\n min-inline-size: 24px;\n min-block-size: 24px;\n min-block-size: max(24px, 1lh);\n align-content: center;\n text-align: center;\n -webkit-user-select: none;\n user-select: none;\n cursor: help;\n }\n :where(${n}:hover) {\n background-color: color-mix(in oklab, currentColor 10%, transparent);\n }\n :where(${n}:active) {\n background-color: color-mix(in oklab, currentColor 20%, transparent);\n }\n :where(${n})::before {\n content: var(${a});\n }`,document.head.prepend(u),document[c]={globalPropsStyle:t,uaStyle:u}}(),function(){const e=(e,t)=>{const n=e.composedPath(),r=n.length?n[0]:e.target;if(r instanceof Element)for(let e=r;e;e=R(e))X(e,t)},t=e=>{"touch"!==e.pointerType&&(y=!1)};document.body.addEventListener("pointerover",t),document.body.addEventListener("pointerdown",t),document.body.addEventListener("mouseover",t=>e(t,A)),document.body.addEventListener("mouseout",t=>e(t,L)),document.body.addEventListener("focusin",t=>e(t,N)),document.body.addEventListener("focusout",t=>e(t,x)),document.body.addEventListener("keydown",e=>{y=!1,"Escape"!==e.key||e.defaultPrevented||[...h].reverse().forEach(e=>{k(e,e[c].target,w,{force:!0})})});const n=/iPad|iPhone|iPod/.test(navigator.userAgent);document.body.addEventListener("touchstart",e=>{y=!0;const t=e.target.closest("button[interestfor]");t&&(z(t),t[c].longPressActive=!1,t[c].longPressTimer=setTimeout(()=>{t[c].state===w&&(n?t[c].longPressActive=!0:k(t,I(t),T)),t[c].longPressTimer=null},500))});const r=e=>{const t=e.target.closest("button[interestfor]");t&&t[c]&&(clearTimeout(t[c].longPressTimer),t[c].longPressTimer=null,t[c].longPressActive=!1)};document.body.addEventListener("touchmove",r),document.body.addEventListener("touchend",e=>{const t=e.target.closest("button[interestfor]"),n=t?.[c]?.longPressActive;r(e),n&&(e.cancelable&&e.preventDefault(),t[c].state===w&&k(t,I(t),T))},{passive:!1})}(),new MutationObserver(Z).observe(document,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0,attributeFilter:[e,"id","href","disabled"]}),Y(document.querySelectorAll(`[${e}]`)),console.log(`interestfor polyfill installed (native: ${E}).`)}!function(){const t={configurable:!0,enumerable:!0,get(){return C(this)},set(t){if(null==t)this[m]=null,this.removeAttribute(e);else{if(!(t instanceof Element))throw new TypeError("Failed to set the 'interestForElement' property: The provided value is not of type 'Element'.");this[m]=t,this.setAttribute(e,"")}}};[window.HTMLButtonElement,window.HTMLAnchorElement,window.HTMLAreaElement,window.SVGAElement].forEach(e=>{!e||Object.prototype.hasOwnProperty.call(e.prototype,"interestForElement")&&!window.interestForUsePolyfillAlways||Object.defineProperty(e.prototype,"interestForElement",t)})}(),"complete"===document.readyState?ee():window.addEventListener("load",ee)}();
@@ -1,20 +1,23 @@
1
1
  <!DOCTYPE html>
2
+ <meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.4">
2
4
 
3
- <script src="../src/interestfor.js" async></script>
4
-
5
- <a interestfor=foo href=bar>Link - hover me, don't click me</a>
6
- <button interestfor=foo>
7
- <span style="margin:10px;">Button - hover me, don't click me</span></button>
8
- <div popover id=foo>Popover</div>
9
- <p>
10
- <a interestfor=bar href=bar class=with-button>Link with an interest button - click the "?"</a>
11
- </p>
12
- <div popover id=bar>Popover shown by the interest button</div>
5
+ <div>
6
+ <a interestfor=foo href=bar>Link - hover me</a>
7
+ </div>
8
+ <div>
9
+ <button interestfor=foo commandfor=bar command=toggle-popover>Button - hover me</button>
10
+ </div>
11
+ <div popover id=foo>Hovercard</div>
12
+ <div popover id=bar>Button action</div>
13
13
 
14
14
  <style>
15
+ body {
16
+ padding: 20px 0 0 20px;
17
+ }
15
18
  [interestfor] {
16
- interest-delay: 600ms 300ms;
17
- --interest-delay: 600ms 300ms;
19
+ --interest-delay: 600ms 500ms;
20
+ interest-delay: var(--interest-delay);
18
21
  }
19
22
  :is([interestfor]:interest-source),
20
23
  [interestfor].interest-source {
@@ -26,13 +29,53 @@
26
29
  }
27
30
  [popover] {
28
31
  position-area: right center;
32
+ margin-left:30px;
29
33
  }
30
- /* Native ::interest-button */
31
- .with-button::interest-button {
32
- content: "?";
34
+ #bar {
35
+ position-area: bottom center;
36
+ margin: 10px 0 0 0;
33
37
  }
38
+ div { margin: 10px; padding: 7.5px 0; }
34
39
  /* Polyfilled ::interest-button */
35
- .with-button {
36
- --interest-button-content: "?";
40
+ [interestfor] {
41
+ --interest-button-content: "\24D8";
42
+ }
43
+ /* Native ::interest-button */
44
+ [interestfor]::interest-button {
45
+ content: var(--interest-button-content);
37
46
  }
38
47
  </style>
48
+
49
+ <div>
50
+ <label>Mode:
51
+ <select id="mode">
52
+ <option value="polyfill">Use polyfill</option>
53
+ <option value="native">Native</option>
54
+ </select>
55
+ </label>
56
+ </div>
57
+ <div id="nav_msg"></div>
58
+ <script>
59
+ let navTimer;
60
+ document.querySelector("a[interestfor]").addEventListener("click", (e) => {
61
+ e.preventDefault();
62
+ const msg = document.getElementById("nav_msg");
63
+ msg.textContent = "would have navigated";
64
+ clearTimeout(navTimer);
65
+ navTimer = setTimeout(() => {
66
+ msg.textContent = "";
67
+ }, 1000);
68
+ });
69
+ const mode = new URLSearchParams(location.search).get("mode") || "polyfill";
70
+ const select = document.getElementById("mode");
71
+ select.value = mode;
72
+ select.addEventListener("change", () => {
73
+ location.search = `?mode=${select.value}`;
74
+ });
75
+ if (mode === "polyfill") {
76
+ window.interestForUsePolyfillAlways = true;
77
+ const s = document.createElement("script");
78
+ s.src = `../src/interestfor.js?v=${Date.now()}`;
79
+ document.head.appendChild(s);
80
+ }
81
+ </script>