PageSourceSearch

https://www.gominerva.com/assets/screening/r25-carousel.js

js gominerva.com collected 2026-10-03 20:58:21 UTC 4,944 bytes, 181 lines download raw bytes

1(() => {
2  const root = document.querySelector(".r22-proof");
3  if (!root) return;
4  const slides = [...root.querySelectorAll(".r22-proof-slide")],
5    choices = [...root.querySelectorAll("[data-slide]")];
6  const region = root.querySelector(".r22-proof-slides"),
7    announcement = root.querySelector("[data-carousel-announcement]");
8  const reduced = matchMedia("(prefers-reduced-motion: reduce)");
9  let current = 0,
10    paused = reduced.matches,
11    visible = false,
12    hovered = false,
13    focused = false,
14    timer = 0,
15    fade = null,
16    revision = 0;
17  root.querySelectorAll("[hidden]").forEach((el) => {
18    if (!el.classList.contains("r22-proof-slide")) el.hidden = false;
19  });
20  const help = document.createElement("p");
21  help.id = "story-controls-help";
22  help.className = "r22-sr-only";
23  help.textContent =
24    "Stories rotate automatically. Choose a story or use the arrows to stop rotation. Rotation also stops while you read with a pointer or keyboard.";
25  root.append(help);
26  root.setAttribute("aria-describedby", help.id);
27  function schedule() {
28    clearTimeout(timer);
29    root.dataset.rotating = String(
30      !paused && visible && !hovered && !focused && !document.hidden,
31    );
32    if (root.dataset.rotating === "true")
33      timer = setTimeout(() => show(current + 1), 8500);
34  }
35  function render() {
36    slides.forEach((slide, i) => (slide.hidden = i !== current));
37    choices.forEach((button, i) => {
38      if (i === current) button.setAttribute("aria-current", "true");
39      else button.removeAttribute("aria-current");
40    });
41  }
42  function show(n, manual = false, immediate = false) {
43    if (manual) paused = true;
44    current = (n + slides.length) % slides.length;
45    const ticket = ++revision;
46    if (fade) {
47      fade.onfinish = null;
48      fade.cancel();
49    }
50    const apply = () => {
51      render();
52      if (manual)
53        announcement.textContent = slides[current].getAttribute("aria-label");
54    };
55    if (immediate || reduced.matches) {
56      apply();
57      fade = null;
58      schedule();
59      return;
60    }
61    fade = region.animate(
62      [
63        {
64          opacity: 1,
65        },
66        {
67          opacity: 0,
68        },
69      ],
70      {
71        duration: 180,
72        fill: "forwards",
73      },
74    );
75    fade.onfinish = () => {
76      if (ticket !== revision) return;
77      fade.cancel();
78      apply();
79      fade = region.animate(
80        [
81          {
82            opacity: 0,
83          },
84          {
85            opacity: 1,
86          },
87        ],
88        {
89          duration: 320,
90          easing: "ease-out",
91        },
92      );
93      fade.onfinish = () => {
94        fade = null;
95      };
96    };
97    schedule();
98  }
99  root
100    .querySelector('[data-carousel="prev"]')
101    .addEventListener("click", () => show(current - 1, true));
102  root
103    .querySelector('[data-carousel="next"]')
104    .addEventListener("click", () => show(current + 1, true));
105  choices.forEach((button, i) =>
106    button.addEventListener("click", () => show(i, true)),
107  );
108  root.addEventListener("mouseenter", () => {
109    hovered = true;
110    schedule();
111  });
112  root.addEventListener("mouseleave", () => {
113    hovered = false;
114    schedule();
115  });
116  root.addEventListener("focusin", () => {
117    focused = true;
118    paused = true;
119    schedule();
120  });
121  root.addEventListener("focusout", (e) => {
122    if (!root.contains(e.relatedTarget)) {
123      focused = false;
124      schedule();
125    }
126  });
127  document.addEventListener("visibilitychange", schedule);
128  reduced.addEventListener("change", () => {
129    paused = true;
130    show(current, false, true);
131  });
132  new IntersectionObserver(
133    (entries) => {
134      visible = entries[0].isIntersecting;
135      schedule();
136    },
137    {
138      threshold: 0.25,
139    },
140  ).observe(root);
141  function size() {
142    const hidden = slides.map((el) => el.hidden);
143    region.style.minHeight = "";
144    let height = 0;
145    slides.forEach((slide) => {
146      slide.hidden = false;
147      slide.style.position = "absolute";
148      slide.style.visibility = "hidden";
149      slide.style.width = "100%";
150      height = Math.max(height, slide.offsetHeight);
151      slide.style.position = "";
152      slide.style.visibility = "";
153      slide.style.width = "";
154    });
155    region.style.minHeight = `${height}px`;
156    slides.forEach((slide, i) => (slide.hidden = hidden[i]));
157  }
158  let lastWidth = 0;
159  new ResizeObserver((entries) => {
160    const width = entries[0].contentRect.width;
161    if (width === lastWidth) return;
162    lastWidth = width;
163    size();
164  }).observe(root);
165  document.fonts.ready.then(size);
166  root
167    .querySelectorAll("img")
168    .forEach((img) => img.addEventListener("load", size));
169  show(0, false, true);
170  window.customerStories = {
171    show: (n) => show(n, true),
172    get state() {
173      return {
174        current: current,
175        paused: paused,
176        visible: visible,
177        rotating: root.dataset.rotating === "true",
178      };
179    },
180  };
181})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.