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.