PageSourceSearch

https://veloo.com.br/js/main.js

js veloo.com.br collected 2026-10-01 14:15:41 UTC 10,971 bytes, 357 lines download raw bytes

1/* ==========================================
2   VELOO TELECOM - MAIN JAVASCRIPT
3   Carousel, Animations & Interactions
4   ========================================== */
5
6document.addEventListener('DOMContentLoaded', function () {
7    // Initialize all components
8    initCarousel();
9    initNavigation();
10    initScrollEffects();
11    initAnimations();
12    // Plan toggle is now handled by plan-toggle.js
13});
14
15/* ==========================================
16   CAROUSEL
17   ========================================== */
18function initCarousel() {
19    const carousel = document.getElementById('carousel');
20    const slides = carousel.querySelectorAll('.carousel-slide');
21    const dots = carousel.querySelectorAll('.carousel-dot');
22    const prevBtn = document.getElementById('carousel-prev');
23    const nextBtn = document.getElementById('carousel-next');
24
25    let currentSlide = 0;
26    let autoplayInterval;
27    const AUTOPLAY_DELAY = 6000; // 6 seconds
28
29    // Set initial state
30    function showSlide(index) {
31        // Handle bounds
32        if (index >= slides.length) index = 0;
33        if (index < 0) index = slides.length - 1;
34
35        // Update slides
36        slides.forEach((slide, i) => {
37            slide.classList.toggle('active', i === index);
38        });
39
40        // Update dots
41        dots.forEach((dot, i) => {
42            dot.classList.toggle('active', i === index);
43        });
44
45        currentSlide = index;
46    }
47
48    // Navigation functions
49    function nextSlide() {
50        showSlide(currentSlide + 1);
51    }
52
53    function prevSlide() {
54        showSlide(currentSlide - 1);
55    }
56
57    // Autoplay
58    function startAutoplay() {
59        autoplayInterval = setInterval(nextSlide, AUTOPLAY_DELAY);
60    }
61
62    function stopAutoplay() {
63        clearInterval(autoplayInterval);
64    }
65
66    function resetAutoplay() {
67        stopAutoplay();
68        startAutoplay();
69    }
70
71    // Event listeners
72    if (prevBtn && nextBtn) {
73        prevBtn.addEventListener('click', () => {
74            prevSlide();
75            resetAutoplay();
76        });
77
78        nextBtn.addEventListener('click', () => {
79            nextSlide();
80            resetAutoplay();
81        });
82    }
83
84    // Dot navigation
85    dots.forEach((dot, index) => {
86        dot.addEventListener('click', () => {
87            showSlide(index);
88            resetAutoplay();
89        });
90    });
91
92    // Pause on hover
93    carousel.addEventListener('mouseenter', stopAutoplay);
94    carousel.addEventListener('mouseleave', startAutoplay);
95
96    // Touch/Swipe support
97    let touchStartX = 0;
98    let touchEndX = 0;
99
100    carousel.addEventListener('touchstart', (e) => {
101        touchStartX = e.changedTouches[0].screenX;
102    }, { passive: true });
103
104    carousel.addEventListener('touchend', (e) => {
105        touchEndX = e.changedTouches[0].screenX;
106        handleSwipe();
107    }, { passive: true });
108
109    function handleSwipe() {
110        const swipeThreshold = 50;
111        const diff = touchStartX - touchEndX;
112
113        if (Math.abs(diff) > swipeThreshold) {
114            if (diff > 0) {
115                nextSlide();
116            } else {
117                prevSlide();
118            }
119            resetAutoplay();
120        }
121    }
122
123    // Keyboard navigation
124    document.addEventListener('keydown', (e) => {
125        if (e.key === 'ArrowLeft') {
126            prevSlide();
127            resetAutoplay();
128        } else if (e.key === 'ArrowRight') {
129            nextSlide();
130            resetAutoplay();
131        }
132    });
133
134    // Start autoplay
135    startAutoplay();
136}
137
138/* ==========================================
139   NAVIGATION
140   ========================================== */
141function initNavigation() {
142    const header = document.getElementById('header');
143    const navToggle = document.getElementById('nav-toggle');
144    const navMenu = document.getElementById('nav-menu');
145    const navLinks = document.querySelectorAll('.nav-link');
146
147    // Mobile menu toggle
148    if (navToggle && navMenu) {
149        navToggle.addEventListener('click', () => {
150            navMenu.classList.toggle('active');
151            const icon = navToggle.querySelector('i');
152            if (navMenu.classList.contains('active')) {
153                icon.classList.remove('fa-bars');
154                icon.classList.add('fa-times');
155            } else {
156                icon.classList.remove('fa-times');
157                icon.classList.add('fa-bars');
158            }
159        });
160    }
161
162    // Close menu on link click
163    navLinks.forEach(link => {
164        link.addEventListener('click', () => {
165            if (navMenu) {
166                navMenu.classList.remove('active');
167                const icon = navToggle.querySelector('i');
168                icon.classList.remove('fa-times');
169                icon.classList.add('fa-bars');
170            }
171        });
172    });
173
174    // Active link on scroll
175    function updateActiveLink() {
176        const sections = document.querySelectorAll('section[id]');
177        const scrollY = window.scrollY + 100;
178        const windowHeight = window.innerHeight;
179        const documentHeight = document.documentElement.scrollHeight;
180
181        // Se está no final da página (footer), ativar Contato
182        const footer = document.getElementById('footer');
183        const footerTop = footer ? footer.offsetTop : documentHeight;
184        if (scrollY >= footerTop - 100 || scrollY + windowHeight >= documentHeight - 50) {
185            navLinks.forEach(link => {
186                link.classList.remove('active');
187                if (link.getAttribute('href') === '#footer') {
188                    link.classList.add('active');
189                }
190            });
191            return;
192        }
193
194        let currentSection = null;
195
196        sections.forEach(section => {
197            const sectionTop = section.offsetTop;
198            const sectionHeight = section.offsetHeight;
199
200            if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
201                currentSection = section.getAttribute('id');
202            }
203        });
204
205        navLinks.forEach(link => {
206            link.classList.remove('active');
207            const href = link.getAttribute('href');
208            if (currentSection && (href === `#${currentSection}` || href === `/#${currentSection}`)) {
209                link.classList.add('active');
210            }
211        });
212    }
213
214    window.addEventListener('scroll', updateActiveLink);
215    updateActiveLink(); // Initial call to set active link on load
216
217    // Smooth scroll for anchor links
218    document.querySelectorAll('a[href^="#"], a[href^="/#"]').forEach(anchor => {
219        anchor.addEventListener('click', function (e) {
220            let targetId = this.getAttribute('href');
221            if (targetId.startsWith('/#')) targetId = targetId.substring(1);
222            if (targetId === '#') return;
223
224            const target = document.querySelector(targetId);
225            if (target) {
226                e.preventDefault();
227                const headerHeight = header ? header.offsetHeight : 0;
228                const targetPosition = target.offsetTop - headerHeight;
229
230                window.scrollTo({
231                    top: targetPosition,
232                    behavior: 'smooth'
233                });
234            }
235        });
236    });
237}
238
239/* ==========================================
240   SCROLL EFFECTS
241   ========================================== */
242function initScrollEffects() {
243    const header = document.getElementById('header');
244    const backToTop = document.getElementById('back-to-top');
245
246    // Header scroll effect
247    function handleHeaderScroll() {
248        if (window.scrollY > 50) {
249            header.classList.add('scrolled');
250        } else {
251            header.classList.remove('scrolled');
252        }
253    }
254
255    // Back to top visibility
256    function handleBackToTop() {
257        if (window.scrollY > 500) {
258            backToTop.classList.add('visible');
259        } else {
260            backToTop.classList.remove('visible');
261        }
262    }
263
264    // Scroll event
265    window.addEventListener('scroll', () => {
266        handleHeaderScroll();
267        handleBackToTop();
268    });
269
270    // Back to top click
271    if (backToTop) {
272        backToTop.addEventListener('click', () => {
273            window.scrollTo({
274                top: 0,
275                behavior: 'smooth'
276            });
277        });
278    }
279}
280
281/* ==========================================
282   SCROLL ANIMATIONS (AOS-like)
283   ========================================== */
284function initAnimations() {
285    const animatedElements = document.querySelectorAll('[data-aos]');
286
287    // Intersection Observer for animations
288    const observerOptions = {
289        root: null,
290        rootMargin: '0px',
291        threshold: 0.1
292    };
293
294    const observer = new IntersectionObserver((entries) => {
295        entries.forEach(entry => {
296            if (entry.isIntersecting) {
297                // Add delay if specified
298                const delay = entry.target.getAttribute('data-aos-delay');
299                if (delay) {
300                    setTimeout(() => {
301                        entry.target.classList.add('aos-animate');
302                    }, parseInt(delay));
303                } else {
304                    entry.target.classList.add('aos-animate');
305                }
306            }
307        });
308    }, observerOptions);
309
310    // Observe all animated elements
311    animatedElements.forEach(el => {
312        observer.observe(el);
313    });
314
315    // Counter animation for stats
316    const statValues = document.querySelectorAll('.stat-value');
317    const counterObserver = new IntersectionObserver((entries) => {
318        entries.forEach(entry => {
319            if (entry.isIntersecting) {
320                animateCounter(entry.target);
321                counterObserver.unobserve(entry.target);
322            }
323        });
324    }, { threshold: 0.5 });
325
326    statValues.forEach(stat => {
327        counterObserver.observe(stat);
328    });
329}
330
331function animateCounter(element) {
332    const text = element.textContent;
333    const match = text.match(/[\d,]+/);
334
335    if (!match) return;
336
337    const target = parseFloat(match[0].replace(',', '.'));
338    const duration = 2000;
339    const steps = 60;
340    const increment = target / steps;
341    let current = 0;
342
343    const timer = setInterval(() => {
344        current += increment;
345        if (current >= target) {
346            element.textContent = text;
347            clearInterval(timer);
348        } else {
349            element.textContent = text.replace(match[0],
350                match[0].includes(',')
351                    ? current.toFixed(1).replace('.', ',')
352                    : Math.floor(current).toString()
353            );
354        }
355    }, duration / steps);
356}
357

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.