1// ===== Production-Optimized Navigation Smooth Scroll & Active Link ===== 2window.addEventListener('load', () => { 3 const allNavLinks = Array.from(document.querySelectorAll('#wiguard-main-menu a.nav-link')); 4 const anchorLinks = allNavLinks.filter(link => { 5 const href = link.getAttribute('href'); 6 return href && href.startsWith('#') && href.length > 1; 7 }); 8 9 const homeLink = document.querySelector('#wiguard-main-menu a.nav-link[href="#section-top"]'); 10 11 // Map links to sections 12 let linkMap = anchorLinks.map(link => { 13 const id = link.getAttribute('href').slice(1); 14 const section = document.getElementById(id); 15 return section ? { link, section, top: 0, bottom: 0 } : null; 16 }).filter(Boolean); 17 18 // Update section positions 19 const updateSectionPositions = () => { 20 const scrollTop = window.scrollY || window.pageYOffset; 21 linkMap.forEach(item => { 22 const rect = item.section.getBoundingClientRect(); 23 item.top = rect.top + scrollTop; 24 item.bottom = rect.bottom + scrollTop; 25 }); 26 }; 27 updateSectionPositions(); 28 window.addEventListener('resize', updateSectionPositions); 29 30 // Active link update using rAF throttling 31 let ticking = false; 32 const updateActiveLink = () => { 33 const scrollMiddle = window.scrollY + window.innerHeight / 2; 34 let activeLink = null; 35 36 linkMap.forEach(({ link, top, bottom }) => { 37 if (scrollMiddle >= top && scrollMiddle <= bottom) activeLink = link; 38 }); 39 40 anchorLinks.forEach(l => l.classList.remove('active')); 41 if (activeLink) activeLink.classList.add('active'); 42 else if (homeLink) homeLink.classList.add('active'); 43 44 ticking = false; 45 }; 46 47 const onScroll = () => { 48 if (!ticking) { 49 window.requestAnimationFrame(updateActiveLink); 50 ticking = true; 51 } 52 }; 53 window.addEventListener('scroll', onScroll); 54 55 // Smooth scroll function with ease-in-out 56 const smoothScrollTo = (targetY, duration = 600) => { 57 const startY = window.scrollY; 58 const diff = targetY - startY; 59 let startTime; 60 61 const step = timestamp => { 62 if (!startTime) startTime = timestamp; 63 const time = timestamp - startTime; 64 const percent = Math.min(time / duration, 1); 65 const ease = percent < 0.5 ? 2 * percent * percent : -1 + (4 - 2 * percent) * percent; 66 window.scrollTo(0, startY + diff * ease); 67 if (time < duration) window.requestAnimationFrame(step); 68 }; 69 window.requestAnimationFrame(step); 70 }; 71 72 // Click event for smooth scrolling with header offset 73 anchorLinks.forEach(link => { 74 link.addEventListener('click', e => { 75 e.preventDefault(); 76 const targetId = link.getAttribute('href').slice(1); 77 const targetSection = document.getElementById(targetId); 78 if (targetSection) { 79 const header = document.querySelector('.sticky-header'); 80 const headerOffset = header ? header.offsetHeight : 80; 81 const targetRect = targetSection.getBoundingClientRect(); 82 const scrollTop = window.scrollY || window.pageYOffset; 83 const targetY = scrollTop + targetRect.top - headerOffset; 84 smoothScrollTo(targetY, 600); 85 } 86 }); 87 }); 88 89 // Initial highlight on load 90 updateActiveLink(); 91});
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.