PageSourceSearch

https://organicskincare.com/wp-content/plugins/osc-concierge/assets/ds-motion.js?ver=0.6.0

js organicskincare.com collected 2026-10-08 06:39:20 UTC 4,879 bytes, 123 lines download raw bytes

1/**
2 * OSC design-system motion engine — scroll reveals, stat counters, bar draws.
3 *
4 * Deliberately follows the proven osc-cinematic.js pattern for THIS site:
5 * NO IntersectionObserver, NO requestAnimationFrame (both observed dead in the
6 * driven-browser env on live OSC pages) — a single passive native `scroll`
7 * listener + getBoundingClientRect, throttled with a dirty-flag + setTimeout.
8 *
9 * Contract with motion.css:
10 *  - Base markup is 100% visible. This script ADDS .osc-anim-prep (hide) only to
11 *    [data-osc-anim] elements fully below the fold at boot, then .osc-anim-in
12 *    (reveal transition) as they cross 88% of the viewport.
13 *  - Left under WP Rocket delay-JS ON PURPOSE: absent/late JS costs nothing;
14 *    delay-JS releases on first scroll — exactly when reveals become relevant.
15 *  - prefers-reduced-motion: engine no-ops entirely (CSS also hard-overrides).
16 *  - Counters ([data-osc-count]): the final value is real HTML text (AEO truth).
17 *    We snapshot the string, tick numerically, and restore the EXACT original.
18 * No innerHTML anywhere. No layout writes (transform/opacity only) — zero CLS.
19 */
20(function () {
21  "use strict";
22  if (window.__oscDsMotion) return; // idempotent under double-enqueue
23  window.__oscDsMotion = 1;
24  var mq = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)");
25  if (mq && mq.matches) return;
26
27  var THRESH = 0.88; // reveal when element top crosses 88% of viewport height
28  var items = [];    // { el, counted }
29  var dirty = false, timer = null, bound = false;
30
31  function collect() {
32    var nodes = document.querySelectorAll("[data-osc-anim]");
33    var vh = window.innerHeight || 0;
34    for (var i = 0; i < nodes.length; i++) {
35      var el = nodes[i];
36      if (el.__oscDs) continue;
37      el.__oscDs = 1;
38      var top = el.getBoundingClientRect().top;
39      // Only elements FULLY below the fold ever get hidden — above-fold and
40      // partially visible content is never touched (no boot flash, no CLS).
41      if (top >= vh) {
42        var d = parseInt(el.getAttribute("data-osc-anim-delay") || "0", 10);
43        if (d > 0) el.style.transitionDelay = d + "ms";
44        el.classList.add("osc-anim-prep");
45        items.push(el);
46      } else if (el.hasAttribute("data-osc-count") || el.querySelector("[data-osc-count]")) {
47        // Above-fold counters still tick once, from their visible state.
48        runCounters(el);
49      }
50    }
51  }
52
53  function parseNum(s) {
54    var m = String(s).match(/-?\d[\d,]*\.?\d*/);
55    if (!m) return null;
56    return { raw: m[0], idx: m.index, val: parseFloat(m[0].replace(/,/g, "")) };
57  }
58
59  function runCounters(scope) {
60    var list = scope.hasAttribute && scope.hasAttribute("data-osc-count") ? [scope] : [];
61    var found = scope.querySelectorAll ? scope.querySelectorAll("[data-osc-count]") : [];
62    for (var i = 0; i < found.length; i++) list.push(found[i]);
63    for (var j = 0; j < list.length; j++) (function (el) {
64      if (el.__oscDsCounted) return;
65      el.__oscDsCounted = 1;
66      var original = el.textContent;           // exact restore target (AEO truth)
67      var n = parseNum(original);
68      if (!n || !isFinite(n.val)) return;
69      var decimals = (n.raw.split(".")[1] || "").length;
70      var hasComma = n.raw.indexOf(",") !== -1;
71      var steps = 18, step = 0;
72      var iv = setInterval(function () {
73        step++;
74        if (step >= steps) { clearInterval(iv); el.textContent = original; return; }
75        var eased = 1 - Math.pow(1 - step / steps, 3);
76        var v = (n.val * eased).toFixed(decimals);
77        if (hasComma) v = v.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
78        el.textContent = original.slice(0, n.idx) + v + original.slice(n.idx + n.raw.length);
79      }, 700 / steps);
80    })(list[j]);
81  }
82
83  function check() {
84    var vh = window.innerHeight || 0;
85    var remaining = [];
86    for (var i = 0; i < items.length; i++) {
87      var el = items[i];
88      if (el.getBoundingClientRect().top < vh * THRESH) {
89        el.classList.add("osc-anim-in");
90        runCounters(el);
91      } else {
92        remaining.push(el);
93      }
94    }
95    items = remaining;
96    if (!items.length && bound) {
97      window.removeEventListener("scroll", onScroll);
98      window.removeEventListener("resize", onScroll);
99      bound = false;
100    }
101  }
102
103  function onScroll() {
104    if (dirty) return;
105    dirty = true;
106    timer = setTimeout(function () { dirty = false; check(); }, 80);
107  }
108
109  function boot() {
110    collect();
111    if (!items.length) return;
112    window.addEventListener("scroll", onScroll, { passive: true });
113    window.addEventListener("resize", onScroll, { passive: true });
114    bound = true;
115    check(); // settle anything already near the threshold at boot
116  }
117
118  if (document.readyState === "loading") {
119    document.addEventListener("DOMContentLoaded", boot);
120  } else {
121    boot();
122  }
123})();

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.