PageSourceSearch

https://vk-panel.ru/js/main.js

js vk-panel.ru collected 2026-10-02 02:06:55 UTC 6,764 bytes, 175 lines download raw bytes

1/**
2 * VK Panel Landing Main JS
3 */
4
5document.addEventListener('DOMContentLoaded', () => {
6    // 1. Navbar Scroll & Mobile Behavior
7    const mainNav = document.getElementById('mainNav');
8    const menuToggle = document.getElementById('navbarResponsive');
9    const navbarToggler = document.querySelector('.navbar-toggler');
10    
11    const handleScroll = () => {
12        if (window.scrollY > 50 || window.innerWidth < 992) {
13            mainNav.classList.add('navbar-scrolled', 'bg-dark', 'shadow');
14        } else {
15            mainNav.classList.remove('navbar-scrolled', 'bg-dark', 'shadow');
16        }
17    };
18
19    window.addEventListener('scroll', handleScroll);
20    window.addEventListener('resize', handleScroll);
21    handleScroll();
22
23    if (navbarToggler) {
24        navbarToggler.addEventListener('click', () => {
25            mainNav.classList.add('bg-dark', 'shadow', 'navbar-scrolled');
26        });
27    }
28
29    // 2. Smooth Scrolling & Mobile Menu Auto-close
30    if (typeof bootstrap !== 'undefined') {
31        const bsCollapse = menuToggle ? new bootstrap.Collapse(menuToggle, { toggle: false }) : null;
32        
33        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
34            anchor.addEventListener('click', function(e) {
35                e.preventDefault();
36                const targetId = this.getAttribute('href');
37                if (targetId === '#') return;
38                
39                const targetElement = document.querySelector(targetId);
40                if (!targetElement) return;
41
42                // Close mobile menu
43                if (window.innerWidth < 992 && menuToggle && menuToggle.classList.contains('show')) {
44                    bsCollapse.hide();
45                }
46
47                const navHeight = mainNav ? mainNav.offsetHeight : 0;
48                const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY - navHeight;
49                
50                window.scrollTo({
51                    top: targetPosition,
52                    behavior: 'smooth'
53                });
54            });
55        });
56    }
57
58    // 3. Dynamic Copyright Year
59    const copyrightElement = document.getElementById('copyright-years');
60    if (copyrightElement) {
61        const startYear = 2016;
62        const currentYear = new Date().getFullYear();
63        copyrightElement.textContent = (startYear === currentYear) ? startYear : `${startYear}-${currentYear}`;
64    }
65
66    // 4. Bootstrap Components Initialization
67    if (typeof bootstrap !== 'undefined') {
68        // Testimonial Carousel
69        const testimonialCarousel = document.getElementById('testimonialCarousel');
70        if (testimonialCarousel) {
71            new bootstrap.Carousel(testimonialCarousel, { interval: 5000, pause: 'hover' });
72        }
73        
74        // Tooltips
75        const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
76        tooltipTriggerList.map(el => new bootstrap.Tooltip(el));
77    }
78
79    // 5. VK Community Messages Widget
80    if (typeof VK !== 'undefined' && VK.Widgets) {
81        VK.Widgets.CommunityMessages("vk_community_messages", 164537430, {
82            tooltipButtonText: "Есть вопросы? Напишите нам",
83            disableExpandChatSound: "1",
84            disableButtonTooltip: "1"
85        });
86    }
87
88    // 6. Dashboard Preview Animation
89    initDashboardAnimation();
90    
91    // 7. Form Handling (Mock)
92    const contactForm = document.querySelector('#contact form');
93    if (contactForm) {
94        contactForm.addEventListener('submit', (e) => {
95            e.preventDefault();
96            const btn = contactForm.querySelector('button[type="submit"]');
97            if (btn.disabled) return;
98
99            const originalText = btn.textContent;
100            btn.disabled = true;
101            btn.textContent = 'Отправка...';
102            
103            // Simulate API call
104            setTimeout(() => {
105                alert('Спасибо! Ваше сообщение отправлено (имитация).');
106                btn.disabled = false;
107                btn.textContent = originalText;
108                contactForm.reset();
109            }, 1500);
110        });
111    }
112});
113
114/**
115 * Animated fake dashboard for hero area
116 */
117function initDashboardAnimation() {
118    const operations = [
119        { icon: 'fa-film', text: 'Удаляю клипы', total: 100, startAt: 17 },
120        { icon: 'fa-video', text: 'Удаляю видео', total: 78, startAt: 52 },
121        { icon: 'fa-user-times', text: 'Удаляю собак', total: 45, startAt: 12 },
122        { icon: 'fa-comments', text: 'Парсинг чатов', total: 1000, startAt: 840 },
123        { icon: 'fa-broom', text: 'Очистка стены', total: 250, startAt: 180 }
124    ];
125
126    let currentOperationIndex = 0;
127    let currentCount = 0;
128    let countInterval;
129
130    const elements = {
131        icon: document.getElementById('operationIcon'),
132        text: document.getElementById('operationText'),
133        counter: document.getElementById('counter'),
134        progress: document.getElementById('progressBar')
135    };
136
137    if (!elements.icon || !elements.text || !elements.progress) return;
138
139    function updateOperation(index) {
140        const op = operations[index];
141        elements.icon.className = `fas ${op.icon}`;
142        elements.text.textContent = op.text;
143        
144        // Trigger CSS fade animation
145        elements.text.classList.remove('fade-in-out');
146        void elements.text.offsetWidth; // Force reflow
147        elements.text.classList.add('fade-in-out');
148
149        currentCount = op.startAt || 0;
150        elements.counter.textContent = `${currentCount}/${op.total}`;
151
152        const initialProgress = (currentCount / op.total) * 100;
153        elements.progress.style.width = `${initialProgress}%`;
154        elements.progress.style.animation = 'none';
155        void elements.progress.offsetWidth; // Force reflow
156        elements.progress.style.setProperty('--initial-width', `${initialProgress}%`);
157
158        const duration = (op.total - currentCount) * 0.08;
159        elements.progress.style.animation = `progressAnimation ${duration}s linear forwards`;
160
161        if (countInterval) clearInterval(countInterval);
162        countInterval = setInterval(() => {
163            currentCount++;
164            elements.counter.textContent = `${currentCount}/${op.total}`;
165            if (currentCount >= op.total) {
166                clearInterval(countInterval);
167                setTimeout(() => {
168                    currentOperationIndex = (currentOperationIndex + 1) % operations.length;
169                    updateOperation(currentOperationIndex);
170                }, 1500);
171            }
172        }, 80);
173    }
174    updateOperation(currentOperationIndex);
175}

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.