PageSourceSearch

https://www.redditcommentscraper.com/js/main.js

js redditcommentscraper.com collected 2026-10-01 23:45:22 UTC 7,322 bytes, 180 lines download raw bytes

1// Reddit Comment Scraper Landing Page
2console.log('Reddit Comment Scraper Landing Page Loaded');
3
4// Pricing billing toggle: swaps price + note copy via data-monthly/data-yearly
5document.addEventListener('DOMContentLoaded', function () {
6    const buttons = document.querySelectorAll('.pricing-toggle-btn');
7    if (!buttons.length) return;
8    buttons.forEach(function (btn) {
9        btn.addEventListener('click', function () {
10            const mode = btn.dataset.billing;
11            buttons.forEach(function (b) {
12                b.classList.toggle('is-active', b === btn);
13            });
14            document.querySelectorAll('.pricing-card [data-monthly]').forEach(function (el) {
15                el.textContent = el.dataset[mode];
16            });
17        });
18    });
19});
20
21// Mobile hamburger menu: close on outside click and nav link click
22document.addEventListener('DOMContentLoaded', function () {
23    const navToggle = document.getElementById('nav-toggle');
24    if (navToggle) {
25        // Close menu when clicking outside (on the body::after overlay)
26        document.addEventListener('click', function (e) {
27            if (!navToggle.checked) return;
28            const navLinks = document.querySelector('.nav-links');
29            const toggleLabel = document.querySelector('.nav-toggle-label');
30            if (navLinks && !navLinks.contains(e.target) && !toggleLabel.contains(e.target)) {
31                navToggle.checked = false;
32            }
33        });
34
35        // Close menu when a nav link is clicked
36        document.querySelectorAll('.nav-links .nav-link, .nav-links .btn').forEach(function (link) {
37            link.addEventListener('click', function () {
38                navToggle.checked = false;
39            });
40        });
41    }
42});
43
44// TinyAdz Conversion Tracking for CTA Clicks
45document.addEventListener('DOMContentLoaded', function () {
46    // Track all "Add to Chrome" button clicks
47    const chromeButtons = document.querySelectorAll('a[href*="chromewebstore.google.com"]');
48
49    chromeButtons.forEach(button => {
50        button.addEventListener('click', function () {
51            // TinyAdz conversion tracking
52            if (typeof window.$tac === 'function') {
53                window.$tac('conversion', { type: 'CTA_CLICK' });
54            }
55            // Meta Pixel events are handled by /meta-pixel-events.js
56        });
57    });
58
59    // ========================================
60    // SCROLL-TRIGGERED ANIMATIONS
61    // ========================================
62
63    // Create intersection observer for scroll animations
64    const observerOptions = {
65        root: null,
66        rootMargin: '0px 0px -100px 0px',
67        threshold: 0.1
68    };
69
70    const animationObserver = new IntersectionObserver((entries) => {
71        entries.forEach(entry => {
72            if (entry.isIntersecting) {
73                entry.target.classList.add('visible');
74                // Stop observing once animated
75                animationObserver.unobserve(entry.target);
76            }
77        });
78    }, observerOptions);
79
80    // Observe section titles
81    document.querySelectorAll('.section-title').forEach(el => {
82        animationObserver.observe(el);
83    });
84
85    // Observe feature grids for staggered animation
86    document.querySelectorAll('.features-grid').forEach(el => {
87        animationObserver.observe(el);
88    });
89
90    // Observe CLI section elements
91    document.querySelectorAll('.cli-terminal, .cli-features, .mcp-chat').forEach(el => {
92        animationObserver.observe(el);
93    });
94
95    // Observe elements with animate-on-scroll class
96    document.querySelectorAll('.animate-on-scroll').forEach(el => {
97        animationObserver.observe(el);
98    });
99
100    // Observe stagger-children containers
101    document.querySelectorAll('.stagger-children').forEach(el => {
102        animationObserver.observe(el);
103    });
104
105    // Observe use case list items with stagger
106    const useCaseList = document.querySelector('.use-case-list');
107    if (useCaseList) {
108        const useCaseItems = useCaseList.querySelectorAll('li');
109        useCaseItems.forEach((item, index) => {
110            item.style.opacity = '0';
111            item.style.transform = 'translateY(20px)';
112            item.style.transition = `all 0.5s ease-out ${index * 0.1}s`;
113        });
114
115        const useCaseObserver = new IntersectionObserver((entries) => {
116            entries.forEach(entry => {
117                if (entry.isIntersecting) {
118                    useCaseItems.forEach(item => {
119                        item.style.opacity = '1';
120                        item.style.transform = 'translateY(0)';
121                    });
122                    useCaseObserver.unobserve(entry.target);
123                }
124            });
125        }, observerOptions);
126
127        useCaseObserver.observe(useCaseList);
128    }
129
130    // Lazy YouTube facade: only load the player iframe on click (keeps the
131    // page fast — no YouTube JS until the user actually plays a video).
132    document.querySelectorAll('.video-card').forEach(card => {
133        const thumb = card.querySelector('.video-thumb');
134        if (!thumb) return;
135        thumb.addEventListener('click', () => {
136            const id = card.dataset.videoId;
137            if (!id) return;
138            const iframe = document.createElement('iframe');
139            iframe.className = 'video-iframe';
140            iframe.src = `https://www.youtube-nocookie.com/embed/${id}?autoplay=1&rel=0`;
141            iframe.title = card.querySelector('.video-title')?.textContent || 'Video';
142            iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
143            iframe.allowFullscreen = true;
144            thumb.replaceWith(iframe);
145        });
146    });
147});
148
149// ========================================
150// PLATFORM LOGO MARQUEE — fill any viewport for an endless loop
151// ========================================
152// The CSS loop is seamless, but the static track only holds a couple of
153// screen-widths of logos, so on wide displays it runs out and shows empty
154// space. Duplicate the strip until it comfortably exceeds the viewport, and
155// scale the duration to the new width so the scroll speed stays constant.
156document.addEventListener('DOMContentLoaded', function () {
157    const track = document.querySelector('.platform-track');
158    const marquee = track && track.closest('.platform-marquee');
159    if (!track || !marquee) return;
160    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
161
162    // Clone the full original set (an even number of logos, so the -50% loop
163    // stays seamless). Mark clones hidden from assistive tech.
164    const originals = Array.from(track.children);
165    const target = Math.max(marquee.offsetWidth, (window.screen && screen.width) || 0) * 2 + 400;
166    let guard = 0;
167    while (track.scrollWidth < target && guard < 12) {
168        originals.forEach(function (el) {
169            const clone = el.cloneNode(true);
170            clone.setAttribute('aria-hidden', 'true');
171            clone.setAttribute('alt', '');
172            track.appendChild(clone);
173        });
174        guard++;
175    }
176
177    // translateX(-50%) travels half the track width; ~60px/s reads as calm.
178    const duration = (track.scrollWidth / 2) / 60;
179    track.style.animationDuration = duration.toFixed(1) + 's';
180});

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.