PageSourceSearch

https://www.sedgwick.com/wp-content/themes/sedgwick/blocks/2025/re…/readiness-gap-animation.js?ver=1789486612

js sedgwick.com collected 2026-09-24 17:40:51 UTC 6,510 bytes, 194 lines download raw bytes

1(() => {
2    const scgHelper = window.sedgwickAnimationHelper || {};
3    const scgCountUp = typeof scgHelper.countUp === 'function' ? scgHelper.countUp : null;
4    const scgEaseOutCubic = typeof scgHelper.easeOutCubic === 'function' ? scgHelper.easeOutCubic : (t) => 1 - Math.pow(1 - t, 3);
5
6    document.addEventListener('DOMContentLoaded', function () {
7        const grids = document.querySelectorAll('.readiness-gap-animation-block');
8        if (!grids.length) {
9            return;
10        }
11
12        const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
13        const isMobile = window.matchMedia('(max-width: 767px)').matches;
14
15        grids.forEach((grid) => {
16            const cards = Array.from(grid.querySelectorAll('.stat-card-block'));
17            if (!cards.length) {
18                return;
19            }
20
21            cards.forEach((card) => prepareCard(card, prefersReducedMotion));
22
23            if (prefersReducedMotion) {
24                return;
25            }
26
27            if (isMobile) {
28                const cardObserver = new IntersectionObserver(
29                    (entries, obs) => {
30                        entries.forEach((entry) => {
31                            if (entry.isIntersecting) {
32                                const card = entry.target;
33                                animateCard(card);
34                                obs.unobserve(card);
35                            }
36                        });
37                    },
38                    {
39                        root: null,
40                        threshold: 0.25,
41                    },
42                );
43
44                cards.forEach((card) => cardObserver.observe(card));
45                return;
46            }
47
48            let hasAnimated = false;
49
50            const observer = new IntersectionObserver(
51                (entries, obs) => {
52                    entries.forEach((entry) => {
53                        if (entry.isIntersecting && !hasAnimated) {
54                            hasAnimated = true;
55                            obs.unobserve(entry.target);
56                            animateCardsSequentially(cards);
57                        }
58                    });
59                },
60                {
61                    root: null,
62                    threshold: 0.25,
63                },
64            );
65
66            observer.observe(grid);
67        });
68    });
69
70    function prepareCard(card, prefersReducedMotion) {
71        const heading = card.querySelector('.stat-card-heading span, .stat-card-heading');
72        const parsed = heading ? parseNumberValue(heading.textContent) : null;
73
74        if (heading && parsed) {
75            heading.dataset.statPrefix = parsed.prefix;
76            heading.dataset.statSuffix = parsed.suffix;
77            heading.dataset.statNumber = parsed.number;
78            heading.dataset.statDecimals = parsed.decimals;
79        }
80
81        if (!prefersReducedMotion) {
82            if (heading && parsed) {
83                heading.textContent = `${parsed.prefix}0${parsed.suffix}`;
84            }
85            card.classList.add('stat-card-hidden');
86        } else {
87            card.style.transition = 'none';
88            card.classList.add('stat-card-visible');
89            card.classList.remove('stat-card-hidden');
90
91            if (heading && parsed) {
92                const prefix = parsed.prefix || '';
93                const suffix = parsed.suffix || '';
94                const decimals = Number.isFinite(parsed.decimals) ? parsed.decimals : 0;
95                heading.textContent = `${prefix}${parsed.number.toFixed(decimals)}${suffix}`;
96            }
97        }
98    }
99
100    function animateCardsSequentially(cards) {
101        const delayBetweenCards = 750;
102
103        cards.forEach((card, index) => {
104            const delay = index * delayBetweenCards;
105
106            setTimeout(() => {
107                animateCard(card);
108            }, delay);
109        });
110    }
111
112    function animateCard(card) {
113        const heading = card.querySelector('.stat-card-heading span, .stat-card-heading');
114
115        card.classList.add('stat-card-visible');
116        card.classList.remove('stat-card-hidden');
117
118        if (heading && heading.dataset.statNumber) {
119            const targetNumber = parseFloat(heading.dataset.statNumber);
120
121            if (Number.isNaN(targetNumber)) {
122                return;
123            }
124
125            const prefix = heading.dataset.statPrefix || '';
126            const suffix = heading.dataset.statSuffix || '';
127            const decimals = parseInt(heading.dataset.statDecimals || '0', 10);
128
129            if (scgCountUp) {
130                scgCountUp(heading, {
131                    end: targetNumber,
132                    duration: 1500,
133                    prefix,
134                    suffix,
135                    decimals,
136                    easing: scgEaseOutCubic,
137                });
138            } else {
139                animateCountUpFallback(heading, targetNumber, prefix, suffix, decimals);
140            }
141        }
142    }
143
144    function animateCountUpFallback(heading, targetNumber, prefix, suffix, decimals) {
145        const decimalPlaces = Number.isFinite(decimals) ? decimals : 0;
146        const duration = 1500;
147
148        let startTime = null;
149
150        const step = (timestamp) => {
151            if (!startTime) {
152                startTime = timestamp;
153            }
154
155            const progress = Math.min((timestamp - startTime) / duration, 1);
156            const easedProgress = scgEaseOutCubic(progress);
157            const currentValue = targetNumber * easedProgress;
158
159            heading.textContent = `${prefix}${currentValue.toFixed(decimalPlaces)}${suffix}`;
160
161            if (progress < 1) {
162                requestAnimationFrame(step);
163            }
164        };
165
166        requestAnimationFrame(step);
167    }
168
169    function parseNumberValue(rawValue) {
170        if (!rawValue) {
171            return null;
172        }
173
174        const trimmed = rawValue.trim();
175        const match = trimmed.match(/(-?[\d.,]+(?:\.\d+)?)/);
176
177        if (!match) {
178            return null;
179        }
180
181        const numberPortion = match[1].replace(/,/g, '');
182        const number = parseFloat(numberPortion);
183
184        if (Number.isNaN(number)) {
185            return null;
186        }
187
188        const decimals = (numberPortion.split('.')[1] || '').length;
189        const prefix = trimmed.slice(0, match.index);
190        const suffix = trimmed.slice(match.index + match[1].length);
191
192        return { number, prefix, suffix, decimals };
193    }
194})();

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.