PageSourceSearch

https://unyform.ai/assets/animations.js

js unyform.ai collected 2026-10-05 13:11:12 UTC 2,834 bytes, 80 lines download raw bytes

1// Scroll-triggered animations using Intersection Observer
2(function() {
3    'use strict';
4
5    const observer = new IntersectionObserver((entries) => {
6        entries.forEach(entry => {
7            if (entry.isIntersecting) {
8                entry.target.classList.add('animated');
9            }
10        });
11    }, {
12        threshold: 0.1,
13        rootMargin: '0px 0px -50px 0px'
14    });
15
16    function initAnimations() {
17        document.querySelectorAll('[data-animate]:not(.observed)').forEach(el => {
18            el.classList.add('observed');
19            observer.observe(el);
20        });
21    }
22
23    // Counter animation for metrics section.
24    // Guard with [data-counted] to prevent re-initialization from MutationObserver.
25    function animateCounters() {
26        document.querySelectorAll('[data-counter]:not([data-counted])').forEach(el => {
27            el.setAttribute('data-counted', '');
28
29            const target = el.getAttribute('data-counter');
30            const suffix = el.getAttribute('data-suffix') || '';
31            const prefix = el.getAttribute('data-prefix') || '';
32            const num = parseInt(target, 10);
33
34            if (isNaN(num)) {
35                el.textContent = prefix + target + suffix;
36                return;
37            }
38
39            const counterObserver = new IntersectionObserver((entries) => {
40                entries.forEach(entry => {
41                    if (entry.isIntersecting) {
42                        let current = 0;
43                        const step = Math.max(1, Math.floor(num / 40));
44                        const timer = setInterval(() => {
45                            current += step;
46                            if (current >= num) {
47                                current = num;
48                                clearInterval(timer);
49                            }
50                            el.textContent = prefix + current + suffix;
51                        }, 30);
52                        counterObserver.unobserve(entry.target);
53                    }
54                });
55            }, { threshold: 0.5 });
56
57            counterObserver.observe(el);
58        });
59    }
60
61    // Init on DOM ready and after Leptos hydration
62    if (document.readyState === 'loading') {
63        document.addEventListener('DOMContentLoaded', () => { initAnimations(); animateCounters(); });
64    } else {
65        initAnimations();
66        animateCounters();
67    }
68
69    // Re-init after Leptos client-side navigation (debounced)
70    let reinitTimer = null;
71    const bodyObserver = new MutationObserver(() => {
72        if (reinitTimer) return;
73        reinitTimer = setTimeout(() => {
74            reinitTimer = null;
75            initAnimations();
76            animateCounters();
77        }, 100);
78    });
79    bodyObserver.observe(document.body, { childList: true, subtree: true });
80})();

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.