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.