PageSourceSearch

https://www.rexity.ai/assets/js/devices.js?v=d353c04358

js rexity.ai collected 2026-10-05 15:16:38 UTC 25,385 bytes, 517 lines download raw bytes

1/*!
2 * Rexity device mockups (Sprint 7A) — assets/js/devices.js
3 * Vanilla JS. Uses the vendored GSAP Draggable + InertiaPlugin (already on
4 * every page) for the marquee; everything else needs no library.
5 *
6 * Include on a page with the build-refreshed block (see src/layouts/README.md → "Devices"):
7 *   <!-- rx-devices --><!-- /rx-devices -->       (in <head>; npm run build fills it)
8 *
9 * ------------------------------------------------------------------ markup
10 *   [data-rx-cycle="slide-up|fade|zoom"]   children .rx-slide; one is shown at a time
11 *        data-rx-interval="3200"          ms per slide (default 3200)
12 *        data-rx-autoplay="false"         only move when driven (RxDevices.goTo)
13 *        data-rx-pause-hover="false"      keep running on hover/focus (default: pause)
14 *        id + <div class="rx-dots" data-rx-dots-for="id">  optional dot buttons
15 *        Non-first slides: put the image URL in data-src (and data-srcset) — it is
16 *        loaded just before the slide is shown (and one ahead).
17 *   [data-rx-autoscroll]                    a long capture (.rx-long / img) scrolls inside the screen
18 *        data-rx-speed="60"               px per second at the rendered size (default 60)
19 *        data-rx-hold="1400"              ms pause at top and bottom (default 1400)
20 *   [data-rx-chat]                          chat thread: .rx-chat__msg bubbles reveal one by one
21 *        data-rx-interval="1500"          ms between bubbles (default 1500)
22 *        data-rx-typing="900"             ms typing indicator before each bubble (default 900)
23 *        data-rx-start="3"                bubbles already visible when the thread starts (default 0)
24 *        data-rx-loop-pause="3200"        ms the full thread stays before it restarts
25 *   [data-rx-marquee]                       infinite, draggable (inertia) row of cards
26 *        data-rx-speed="36"               px per second drift (default 36; negative = rightwards)
27 *        data-rx-marquee-touch="snap"     below 768 px: a native scroll-snap row (84 % cards, the next one
28 *                                         peeks) with passive position dots and no drift (Sprint 12)
29 *        Touch pauses the drift while a finger is down and for 3 s after (all marquees).
30 *
31 * ------------------------------------------------------------------ API (window.RxDevices)
32 *   RxDevices.init(root = document)       bind every [data-rx-*] under root (idempotent; runs on DOMContentLoaded)
33 *   RxDevices.goTo(el, index, {instant})  show slide `index` of a cycle (wraps); returns the index
34 *   RxDevices.next(el) / .prev(el)        step a cycle
35 *   RxDevices.pause(el) / .play(el)       hold / release any component (user-level hold, stacks with off-screen pause)
36 *   RxDevices.get(el)                     { type, index, count, running } or null
37 *   RxDevices.destroy(el)                 unbind and restore the first frame
38 *   RxDevices.reducedMotion               true when the visitor prefers reduced motion
39 *   Events: a cycle dispatches "rx:cycle" (detail { index, count }) on its element.
40 *   Declarative control: <button data-rx-goto="#stage" data-rx-index="2"> shows slide 2 of #stage
41 *   (data-rx-index="next" / "prev" step it); the button gets aria-pressed while its slide is shown.
42 *
43 * Behaviour: everything pauses off-screen (IntersectionObserver), when the tab
44 * is hidden, and (cycle/marquee) on hover or keyboard focus. With
45 * prefers-reduced-motion: reduce nothing moves: cycles and autoscroll stay on
46 * their first frame, the chat shows the whole thread, the marquee is a
47 * normal scrollable row. Components inside an inactive cycle slide pause too.
48 */
49(function () {
50  "use strict";
51  if (window.RxDevices) return;
52
53  var mq = window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : { matches: false };
54  var instances = new Map(); // element -> instance
55  var io = "IntersectionObserver" in window ? new IntersectionObserver(onIntersect, { rootMargin: "120px 0px" }) : null;
56  var raf = window.requestAnimationFrame.bind(window);
57
58  function num(el, name, dflt) {
59    var v = parseFloat(el.getAttribute(name));
60    return isFinite(v) ? v : dflt;
61  }
62  function onIntersect(entries) {
63    entries.forEach(function (e) {
64      var inst = instances.get(e.target);
65      if (inst) { inst.inView = e.isIntersecting; update(inst); }
66    });
67  }
68  function canRun(inst) {
69    return inst.inView && !document.hidden && !mq.matches && !inst.holds.user && !inst.holds.hover && !inst.holds.parent;
70  }
71  function update(inst) {
72    var run = canRun(inst);
73    if (run !== inst.running) {
74      inst.running = run;
75      run ? inst.start() : inst.stop();
76    }
77  }
78  function updateAll() { instances.forEach(update); }
79  function hoverPause(inst, el) {
80    if (el.getAttribute("data-rx-pause-hover") === "false") return;
81    var set = function (v) { return function () { inst.holds.hover = v; update(inst); }; };
82    el.addEventListener("mouseenter", set(true));
83    el.addEventListener("mouseleave", set(false));
84    el.addEventListener("focusin", set(true));
85    el.addEventListener("focusout", function (e) { if (!el.contains(e.relatedTarget)) { inst.holds.hover = false; update(inst); } });
86  }
87  function base(el, type) {
88    var inst = { el: el, type: type, inView: !io, running: false, holds: { user: false, hover: false, parent: false }, start: function () {}, stop: function () {}, reset: function () {} };
89    instances.set(el, inst);
90    if (io) io.observe(el);
91    return inst;
92  }
93  function promote(img) {
94    if (!img) return;
95    var list = img.tagName === "IMG" ? [img] : img.querySelectorAll("img[data-src], source[data-srcset]");
96    Array.prototype.forEach.call(list, function (i) {
97      if (i.getAttribute("data-srcset")) { i.setAttribute("srcset", i.getAttribute("data-srcset")); i.removeAttribute("data-srcset"); }
98      if (i.getAttribute("data-src")) { i.setAttribute("src", i.getAttribute("data-src")); i.removeAttribute("data-src"); }
99    });
100  }
101  function nested(slide, hold) {
102    instances.forEach(function (inst) {
103      if (inst.el !== slide && slide.contains(inst.el)) { inst.holds.parent = hold; update(inst); }
104    });
105  }
106
107  // ---------------------------------------------------------------- cycle
108  function initCycle(el) {
109    var slides = Array.prototype.filter.call(el.children, function (c) { return c.classList.contains("rx-slide"); });
110    var inst = base(el, "cycle");
111    inst.count = slides.length;
112    inst.index = 0;
113    var interval = num(el, "data-rx-interval", 3200);
114    var autoplay = el.getAttribute("data-rx-autoplay") !== "false";
115    var timer = null, leaveTimer = null;
116    var dots = el.id ? document.querySelector('[data-rx-dots-for="' + el.id + '"]') : null;
117
118    function paint(i, instant) {
119      if (instant) el.classList.add("is-instant");
120      slides.forEach(function (s, k) {
121        var on = k === i;
122        s.classList.toggle("is-active", on);
123        s.classList.toggle("is-leaving", !on && k === inst.index && !instant);
124        s.setAttribute("aria-hidden", on ? "false" : "true");
125        if ("inert" in s) s.inert = !on;
126      });
127      clearTimeout(leaveTimer);
128      leaveTimer = setTimeout(function () { slides.forEach(function (s) { s.classList.remove("is-leaving"); }); }, 900);
129      if (instant) { void el.offsetWidth; el.classList.remove("is-instant"); }
130      if (dots) Array.prototype.forEach.call(dots.children, function (d, k) { d.setAttribute("aria-current", k === i ? "true" : "false"); });
131    }
132    inst.goTo = function (i, opts) {
133      if (!slides.length) return 0;
134      i = ((Math.round(i) % slides.length) + slides.length) % slides.length;
135      promote(slides[i]);
136      promote(slides[(i + 1) % slides.length]);
137      if (i === inst.index && el.classList.contains("is-live")) return i;
138      var instant = !!(opts && opts.instant) || mq.matches;
139      if (el.classList.contains("is-live")) paint(i, instant);
140      var prev = inst.index;
141      inst.index = i;
142      slides.forEach(function (s, k) { nested(s, k !== i); });
143      if (prev !== i) el.dispatchEvent(new CustomEvent("rx:cycle", { bubbles: true, detail: { index: i, count: slides.length } }));
144      if (inst.running) schedule();
145      return i;
146    };
147    function schedule() {
148      clearTimeout(timer);
149      if (autoplay && slides.length > 1) timer = setTimeout(function () { inst.goTo(inst.index + 1); }, interval);
150    }
151    inst.start = schedule;
152    inst.stop = function () { clearTimeout(timer); };
153    inst.reset = function () {
154      inst.stop();
155      el.classList.remove("is-live");
156      slides.forEach(function (s) { s.classList.remove("is-active", "is-leaving"); s.removeAttribute("aria-hidden"); if ("inert" in s) s.inert = false; });
157    };
158    inst.live = function () {
159      if (mq.matches) {
160        // reduced motion: first frame only (or the externally chosen one), no transitions
161        el.classList.add("is-live");
162        paint(inst.index, true);
163        return;
164      }
165      el.classList.add("is-live");
166      paint(inst.index, true);
167    };
168    if (dots && !dots.children.length) {
169      slides.forEach(function (s, k) {
170        var b = document.createElement("button");
171        b.type = "button";
172        b.setAttribute("aria-label", (document.documentElement.lang === "en" ? "Screen " : "Bild ") + (k + 1));
173        b.addEventListener("click", function () { inst.goTo(k); });
174        dots.appendChild(b);
175      });
176    }
177    hoverPause(inst, el);
178    inst.live();
179    slides.forEach(function (s, k) { if (k) nested(s, true); });
180    promote(slides[1]);
181    return inst;
182  }
183
184  // ------------------------------------------------------------ autoscroll
185  function initAutoscroll(el) {
186    var img = el.querySelector(".rx-long") || el.querySelector("img");
187    var inst = base(el, "autoscroll");
188    if (!img) return inst;
189    var speed = num(el, "data-rx-speed", 60), hold = num(el, "data-rx-hold", 1400);
190    var y = 0, dir = 1, waitUntil = 0, last = 0, frame = 0;
191    function dist() { return Math.max(0, img.getBoundingClientRect().height - el.getBoundingClientRect().height); }
192    function tick(t) {
193      if (!inst.running) return;
194      frame = raf(tick);
195      if (!last) last = t;
196      var dt = Math.min(64, t - last);
197      last = t;
198      if (t < waitUntil) return;
199      var d = dist();
200      if (d < 2) return;
201      y += dir * speed * (dir > 0 ? 1 : 2.6) * dt / 1000; // scroll back up faster
202      if (y >= d) { y = d; dir = -1; waitUntil = t + hold; }
203      else if (y <= 0) { y = 0; dir = 1; waitUntil = t + hold; }
204      img.style.transform = "translate3d(0," + (-y).toFixed(2) + "px,0)";
205    }
206    inst.start = function () { last = 0; if (!y) waitUntil = performance.now() + hold * 0.6; frame = raf(tick); };
207    inst.stop = function () { cancelAnimationFrame(frame); };
208    inst.reset = function () { inst.stop(); y = 0; dir = 1; img.style.transform = ""; };
209    window.addEventListener("resize", function () { var d = dist(); if (y > d) y = d; }, { passive: true });
210    return inst;
211  }
212
213  // ------------------------------------------------------------------ chat
214  function initChat(el) {
215    var inst = base(el, "chat");
216    var thread = el.querySelector(".rx-chat__thread") || el;
217    var msgs = Array.prototype.slice.call(thread.querySelectorAll(".rx-chat__msg"));
218    var gap = num(el, "data-rx-interval", 1500), typing = num(el, "data-rx-typing", 900), loopPause = num(el, "data-rx-loop-pause", 3200);
219    var start = Math.min(num(el, "data-rx-start", 0), msgs.length); // first `start` bubbles stay visible; the rest type in
220    var dots = document.createElement("li");
221    dots.className = "rx-chat__typing";
222    dots.setAttribute("aria-hidden", "true");
223    dots.innerHTML = "<i></i><i></i><i></i>";
224    thread.appendChild(dots);
225    var step = 0, timer = null, phase = "idle";
226    function showAll() {
227      msgs.forEach(function (m) { m.classList.remove("is-pending", "is-entering"); });
228      dots.classList.remove("is-on");
229    }
230    function hideAll() {
231      msgs.forEach(function (m, i) { m.classList.toggle("is-pending", i >= start); m.classList.remove("is-entering"); });
232      step = start;
233    }
234    function next() {
235      if (!inst.running) return;
236      if (step >= msgs.length) {
237        phase = "done";
238        timer = setTimeout(function () {
239          el.classList.add("is-fading");
240          timer = setTimeout(function () { hideAll(); el.classList.remove("is-fading"); next(); }, 550);
241        }, loopPause);
242        return;
243      }
244      var m = msgs[step];
245      var me = m.classList.contains("rx-chat__msg--me");
246      dots.classList.toggle("is-me", me);
247      thread.appendChild(dots); // keep the indicator after the last visible bubble
248      dots.classList.add("is-on");
249      phase = "typing";
250      timer = setTimeout(function () {
251        dots.classList.remove("is-on");
252        m.classList.remove("is-pending");
253        m.classList.add("is-entering");
254        void m.offsetWidth;
255        m.classList.remove("is-entering");
256        step++;
257        phase = "gap";
258        timer = setTimeout(next, gap);
259      }, step === start ? typing * 0.6 : typing);
260    }
261    inst.start = function () {
262      if (!el.classList.contains("is-live")) { el.classList.add("is-live"); hideAll(); }
263      next();
264    };
265    inst.stop = function () { clearTimeout(timer); dots.classList.remove("is-on"); };
266    inst.reset = function () { inst.stop(); el.classList.remove("is-live", "is-fading"); showAll(); step = 0; };
267    return inst;
268  }
269
270  // --------------------------------------------------------------- marquee
271  function initMarquee(el) {
272    var inst = base(el, "marquee");
273    var track = el.querySelector(".rx-marquee__track");
274    if (!track) return inst;
275    var originals = Array.prototype.slice.call(track.children);
276    var speed = num(el, "data-rx-speed", 36);
277    var x = 0, setW = 0, frame = 0, last = 0, dragging = false, vx = 0, clones = [], drag = null, tween = null, wheelTimer = null;
278    var G = window.gsap, D = window.Draggable;
279    // Sprint 12 (opt-in): data-rx-marquee-touch="snap" — below 768 px the row is a native scroll-snap row
280    // with passive position dots and no drift at all; at 768 px and up it stays the draggable marquee.
281    var snapMq = el.getAttribute("data-rx-marquee-touch") === "snap" && window.matchMedia ? window.matchMedia("(max-width: 767px)") : null;
282    var dots = null, dotFrame = 0;
283    function snapOn() { return !!(snapMq && snapMq.matches); }
284    function paintDots() {
285      dotFrame = 0;
286      if (!dots) return;
287      var step = cardStep() || 1, i = Math.round(el.scrollLeft / step);
288      if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 4) i = originals.length - 1;
289      Array.prototype.forEach.call(dots.children, function (d, k) { d.classList.toggle("is-on", k === i); });
290    }
291    function setSnap(on) {
292      el.classList.toggle("is-snap", on);
293      if (on && !dots) {
294        dots = document.createElement("div");
295        dots.className = "rx-marquee__dots";
296        dots.setAttribute("aria-hidden", "true");
297        originals.forEach(function () { dots.appendChild(document.createElement("span")); });
298        el.parentNode.insertBefore(dots, el.nextSibling);
299      }
300      if (dots) dots.hidden = !on;
301      if (on) paintDots();
302    }
303    el.addEventListener("scroll", function () { if (dots && !dotFrame) dotFrame = raf(paintDots); }, { passive: true });
304    // touch pauses the drift (≥ 768 px): hold while the finger is down and for 3 s after
305    var touchTimer = null;
306    el.addEventListener("touchstart", function () { clearTimeout(touchTimer); inst.holds.hover = true; update(inst); }, { passive: true });
307    el.addEventListener("touchend", function () { clearTimeout(touchTimer); touchTimer = setTimeout(function () { inst.holds.hover = false; update(inst); }, 3000); }, { passive: true });
308
309    // Sprint 16 A2.2: the loop clones keep loading="lazy" until the row is within one viewport height, then switch
310    // to eager, so a clone that drifts in from the clipped overflow is already there (no blank tile). They reuse the
311    // originals' URLs, so by then it is a cache hit; off-screen rows fetch nothing early.
312    var near = !io;
313    function eager(c) { c.querySelectorAll("img[loading=lazy]").forEach(function (i) { i.loading = "eager"; }); }
314    if (!near) {
315      var nearIo = new IntersectionObserver(function (es) {
316        if (!es[es.length - 1].isIntersecting) return;
317        near = true;
318        nearIo.disconnect();
319        clones.forEach(eager);
320      }, { rootMargin: "100% 0px" });
321      nearIo.observe(el);
322    }
323
324    function measure() {
325      var gapPx = parseFloat(getComputedStyle(track).columnGap || getComputedStyle(track).gap) || 0;
326      var first = originals[0], lastEl = originals[originals.length - 1];
327      setW = lastEl.offsetLeft + lastEl.offsetWidth - first.offsetLeft + gapPx;
328    }
329    function fill() {
330      clones.forEach(function (c) { c.remove(); });
331      clones = [];
332      measure();
333      var need = Math.ceil((el.clientWidth + setW) / Math.max(1, setW)) + 1;
334      for (var r = 0; r < need; r++) originals.forEach(function (o) {
335        var c = o.cloneNode(true);
336        c.setAttribute("aria-hidden", "true");
337        c.setAttribute("data-rx-clone", "");
338        if ("inert" in c) c.inert = true;
339        c.querySelectorAll("a,button,input,[tabindex]").forEach(function (f) { f.setAttribute("tabindex", "-1"); });
340        if (c.matches("a,button,[tabindex]")) c.setAttribute("tabindex", "-1");
341        if (near) eager(c);
342        track.appendChild(c);
343        clones.push(c);
344      });
345    }
346    function wrap(v) { return setW ? ((v % setW) - setW) % setW : v; } // always in (-setW, 0]
347    function render() { x = wrap(x); track.style.transform = "translate3d(" + x.toFixed(2) + "px,0,0)"; }
348    function tick(t) {
349      frame = raf(tick);
350      var dt = last ? Math.min(64, t - last) : 16;
351      last = t;
352      if (dragging || tween) return;
353      x -= speed * dt / 1000;
354      render();
355    }
356    function nudge(dx) {
357      if (!G) { x += dx; render(); return; }
358      if (tween) tween.kill();
359      var o = { v: x };
360      tween = G.to(o, { v: x + dx, duration: 0.55, ease: "power3.out", onUpdate: function () { x = o.v; render(); }, onComplete: function () { tween = null; } });
361    }
362    function cardStep() {
363      var a = originals[0], b = originals[1];
364      return b ? b.offsetLeft - a.offsetLeft : a.offsetWidth;
365    }
366    function live() {
367      if (el.classList.contains("is-live") || mq.matches || snapOn()) return;
368      el.classList.add("is-live");
369      el.scrollLeft = 0;
370      fill();
371      render();
372      if (D && G) {
373        if (window.InertiaPlugin) G.registerPlugin(D, window.InertiaPlugin); else G.registerPlugin(D);
374        var proxy = document.createElement("div");
375        var startX = 0;
376        drag = D.create(proxy, {
377          type: "x",
378          trigger: el,
379          inertia: !!window.InertiaPlugin,
380          allowNativeTouchScrolling: true,
381          allowContextMenu: true,
382          minimumMovement: 6,
383          dragClickables: true,
384          onPress: function () { if (tween) { tween.kill(); tween = null; } startX = x; G.set(proxy, { x: 0 }); this.update(); },
385          onDragStart: function () { dragging = true; el.classList.add("is-dragging"); },
386          onDrag: function () { x = startX + this.x; render(); },
387          onThrowUpdate: function () { x = startX + this.x; render(); },
388          onRelease: function () { if (!this.isThrowing) end(); },
389          onThrowComplete: end,
390        })[0];
391      }
392      // horizontal trackpad / shift+wheel
393      el.addEventListener("wheel", onWheel, { passive: false });
394    }
395    function end() {
396      dragging = false;
397      setTimeout(function () { el.classList.remove("is-dragging"); }, 30);
398    }
399    function onWheel(e) {
400      var dx = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.shiftKey ? e.deltaY : 0;
401      if (!dx) return;
402      e.preventDefault();
403      x -= dx;
404      render();
405      dragging = true;
406      clearTimeout(wheelTimer);
407      wheelTimer = setTimeout(function () { dragging = false; }, 160);
408    }
409    // a click that ends a drag must not follow the link
410    el.addEventListener("click", function (e) { if (el.classList.contains("is-dragging")) { e.preventDefault(); e.stopPropagation(); } }, true);
411    // keyboard: arrows move by one card; focusing a card brings it into view
412    el.addEventListener("keydown", function (e) {
413      if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
414      e.preventDefault();
415      var d = e.key === "ArrowRight" ? -cardStep() : cardStep();
416      if (el.classList.contains("is-live")) nudge(d);
417      else el.scrollBy({ left: -d, behavior: mq.matches ? "auto" : "smooth" });
418    });
419    el.addEventListener("focusin", function (e) {
420      if (!el.classList.contains("is-live")) return;
421      var card = originals.filter(function (o) { return o.contains(e.target); })[0];
422      if (!card) return;
423      el.scrollLeft = 0; // browsers may scroll the hidden overflow on focus
424      var left = card.offsetLeft + x, right = left + card.offsetWidth, pad = 24;
425      if (left < pad) nudge(pad - left);
426      else if (right > el.clientWidth - pad) nudge(el.clientWidth - pad - right);
427    });
428    window.addEventListener("resize", function () { if (el.classList.contains("is-live")) { fill(); render(); } }, { passive: true });
429    hoverPause(inst, el);
430    inst.start = function () { if (snapOn()) return; live(); last = 0; cancelAnimationFrame(frame); frame = raf(tick); };
431    inst.stop = function () { cancelAnimationFrame(frame); };
432    inst.reset = function () {
433      inst.stop();
434      if (drag) { drag.kill(); drag = null; }
435      clones.forEach(function (c) { c.remove(); });
436      clones = [];
437      el.removeEventListener("wheel", onWheel);
438      el.classList.remove("is-live", "is-dragging");
439      track.style.transform = "";
440      x = 0;
441    };
442    if (snapMq) {
443      var onSnap = function () {
444        inst.running = false;
445        inst.reset();
446        setSnap(snapOn());
447        if (!snapOn() && !mq.matches) live();
448        update(inst);
449      };
450      if (snapMq.addEventListener) snapMq.addEventListener("change", onSnap); else if (snapMq.addListener) snapMq.addListener(onSnap);
451      setSnap(snapOn());
452    }
453    if (!mq.matches && !snapOn()) live();
454    return inst;
455  }
456
457  // ------------------------------------------------------------------ core
458  var KINDS = [
459    ["[data-rx-marquee]", initMarquee],
460    ["[data-rx-chat]", initChat],
461    ["[data-rx-autoscroll]", initAutoscroll],
462    ["[data-rx-cycle]", initCycle], // last: a cycle holds the components inside its inactive slides
463  ];
464  function init(root) {
465    root = root || document;
466    KINDS.forEach(function (k) {
467      // innermost first, so an outer cycle can hold the components inside its hidden slides
468      Array.prototype.slice.call(root.querySelectorAll(k[0])).reverse().forEach(function (el) { if (!instances.has(el)) k[1](el); });
469    });
470    updateAll();
471  }
472  function inst(el) { return typeof el === "string" ? instances.get(document.querySelector(el)) : instances.get(el); }
473
474  document.addEventListener("visibilitychange", updateAll);
475  document.addEventListener("click", function (e) {
476    var b = e.target.closest && e.target.closest("[data-rx-goto]");
477    if (!b) return;
478    var target = document.querySelector(b.getAttribute("data-rx-goto"));
479    var x = target && instances.get(target);
480    if (!x || !x.goTo) return;
481    e.preventDefault();
482    var i = b.getAttribute("data-rx-index");
483    x.goTo(i === "next" ? x.index + 1 : i === "prev" ? x.index - 1 : +i || 0);
484  });
485  document.addEventListener("rx:cycle", function (e) {
486    if (!e.target.id) return;
487    Array.prototype.forEach.call(document.querySelectorAll('[data-rx-goto="#' + e.target.id + '"][data-rx-index]'), function (b) {
488      var i = b.getAttribute("data-rx-index");
489      if (i !== "next" && i !== "prev") b.setAttribute("aria-pressed", +i === e.detail.index ? "true" : "false");
490    });
491  });
492  var onMotion = function () {
493    api.reducedMotion = mq.matches;
494    instances.forEach(function (i) {
495      i.running = false;
496      i.reset();
497      if (i.type === "cycle") i.live();
498    });
499    updateAll();
500  };
501  if (mq.addEventListener) mq.addEventListener("change", onMotion); else if (mq.addListener) mq.addListener(onMotion);
502
503  var api = {
504    init: init,
505    goTo: function (el, i, opts) { var x = inst(el); return x && x.goTo ? x.goTo(i, opts) : -1; },
506    next: function (el) { var x = inst(el); return x && x.goTo ? x.goTo(x.index + 1) : -1; },
507    prev: function (el) { var x = inst(el); return x && x.goTo ? x.goTo(x.index - 1) : -1; },
508    pause: function (el) { var x = inst(el); if (x) { x.holds.user = true; update(x); } },
509    play: function (el) { var x = inst(el); if (x) { x.holds.user = false; update(x); } },
510    get: function (el) { var x = inst(el); return x ? { type: x.type, index: x.index || 0, count: x.count || 0, running: x.running } : null; },
511    destroy: function (el) { var x = inst(el); if (!x) return; x.running = false; x.reset(); if (io) io.unobserve(x.el); instances.delete(x.el); },
512    reducedMotion: mq.matches,
513  };
514  window.RxDevices = api;
515  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", function () { init(document); });
516  else init(document);
517})();

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.