1// Optimized JavaScript for Fortune Tiger Site 2(function() { 3 'use strict'; 4 5 // DOM ready function 6 function domReady(fn) { 7 if (document.readyState === 'loading') { 8 document.addEventListener('DOMContentLoaded', fn); 9 } else { 10 fn(); 11 } 12 } 13 14 // Mobile menu toggle 15 function initMobileMenu() { 16 const menuToggle = document.querySelector('.menu-toggle'); 17 const navMenu = document.querySelector('.nav-menu'); 18 19 if (menuToggle && navMenu) { 20 menuToggle.addEventListener('click', function() { 21 navMenu.classList.toggle('toggled'); 22 const isExpanded = navMenu.classList.contains('toggled'); 23 menuToggle.setAttribute('aria-expanded', isExpanded); 24 }); 25 26 // Close menu when clicking outside 27 document.addEventListener('click', function(e) { 28 if (!navMenu.contains(e.target) && !menuToggle.contains(e.target)) { 29 navMenu.classList.remove('toggled'); 30 menuToggle.setAttribute('aria-expanded', 'false'); 31 } 32 }); 33 } 34 } 35 36 // Search toggle 37 function initSearch() { 38 const searchToggle = document.querySelector('.toggle-search'); 39 const searchExpand = document.querySelector('.search-expand'); 40 const searchInput = document.querySelector('.search-expand input'); 41 42 if (searchToggle && searchExpand) { 43 searchToggle.addEventListener('click', function() { 44 searchToggle.classList.toggle('active'); 45 searchExpand.style.display = searchExpand.style.display === 'block' ? 'none' : 'block'; 46 47 if (searchInput && searchExpand.style.display === 'block') { 48 setTimeout(() => searchInput.focus(), 100); 49 } 50 }); 51 52 // Close search when clicking outside 53 document.addEventListener('click', function(e) { 54 if (!searchToggle.contains(e.target) && !searchExpand.contains(e.target)) { 55 searchToggle.classList.remove('active'); 56 searchExpand.style.display = 'none'; 57 } 58 }); 59 } 60 } 61 62 // Smooth scroll to top 63 function initBackToTop() { 64 const backToTop = document.getElementById('back-to-top'); 65 66 if (backToTop) { 67 backToTop.addEventListener('click', function(e) { 68 e.preventDefault(); 69 window.scrollTo({ 70 top: 0, 71 behavior: 'smooth' 72 }); 73 }); 74 75 // Show/hide back to top button based on scroll position 76 let ticking = false; 77 function updateBackToTop() { 78 if (window.pageYOffset > 300) { 79 backToTop.style.display = 'block'; 80 } else { 81 backToTop.style.display = 'none'; 82 } 83 ticking = false; 84 } 85 86 function requestTick() { 87 if (!ticking) { 88 requestAnimationFrame(updateBackToTop); 89 ticking = true; 90 } 91 } 92 93 window.addEventListener('scroll', requestTick); 94 } 95 } 96 97 // Lazy loading for images 98 function initLazyLoading() { 99 if ('IntersectionObserver' in window) { 100 const imageObserver = new IntersectionObserver((entries, observer) => { 101 entries.forEach(entry => { 102 if (entry.isIntersecting) { 103 const img = entry.target; 104 if (img.dataset.src) { 105 img.src = img.dataset.src; 106 img.removeAttribute('data-src'); 107 img.classList.remove('lazy'); 108 observer.unobserve(img); 109 } 110 } 111 }); 112 }); 113 114 document.querySelectorAll('img[data-src]').forEach(img => { 115 imageObserver.observe(img); 116 }); 117 } 118 } 119 120 // Touch optimizations for mobile 121 function initTouchOptimizations() { 122 // Add touch class to body for touch devices 123 if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { 124 document.body.classList.add('touch-device'); 125 } 126 127 // Prevent zoom on double tap for iOS 128 let lastTouchEnd = 0; 129 document.addEventListener('touchend', function(event) { 130 const now = (new Date()).getTime(); 131 if (now - lastTouchEnd <= 300) { 132 event.preventDefault(); 133 } 134 lastTouchEnd = now; 135 }, false); 136 } 137 138 // Performance optimizations 139 function initPerformanceOptimizations() { 140 // Debounce scroll events 141 let scrollTimeout; 142 const originalScroll = window.onscroll; 143 window.onscroll = function() { 144 if (scrollTimeout) { 145 clearTimeout(scrollTimeout); 146 } 147 scrollTimeout = setTimeout(() => { 148 if (originalScroll) originalScroll(); 149 }, 16); // ~60fps 150 }; 151 152 // Preload critical resources 153 const criticalImages = [ 154 'images/fortune-tiger-logo.png', 155 'images/tigre777.webp' 156 ]; 157
158 criticalImages.forEach(src => { 159 const link = document.createElement('link'); 160 link.rel = 'preload'; 161 link.as = 'image'; 162 link.href = src; 163 document.head.appendChild(link); 164 }); 165 } 166 167 // Accessibility improvements 168 function initAccessibility() { 169 // Skip link functionality 170 const skipLink = document.querySelector('.skip-link'); 171 if (skipLink) { 172 skipLink.addEventListener('click', function(e) { 173 e.preventDefault(); 174 const target = document.querySelector(this.getAttribute('href')); 175 if (target) { 176 target.focus(); 177 target.scrollIntoView(); 178 } 179 }); 180 } 181 182 // Keyboard navigation for mobile menu 183 const navMenu = document.querySelector('.nav-menu'); 184 if (navMenu) { 185 const menuItems = navMenu.querySelectorAll('a, button'); 186 menuItems.forEach((item, index) => { 187 item.addEventListener('keydown', function(e) { 188 if (e.key === 'ArrowDown' || e.key === 'ArrowRight') { 189 e.preventDefault(); 190 const nextItem = menuItems[index + 1] || menuItems[0]; 191 nextItem.focus(); 192 } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { 193 e.preventDefault(); 194 const prevItem = menuItems[index - 1] || menuItems[menuItems.length - 1]; 195 prevItem.focus(); 196 } 197 }); 198 }); 199 } 200 } 201 202 // Initialize all functions when DOM is ready 203 domReady(function() { 204 initMobileMenu(); 205 initSearch(); 206 initBackToTop(); 207 initLazyLoading(); 208 initTouchOptimizations(); 209 initPerformanceOptimizations(); 210 initAccessibility(); 211 }); 212 213 // Service Worker registration for PWA capabilities 214 if ('serviceWorker' in navigator) { 215 window.addEventListener('load', function() { 216 navigator.serviceWorker.register('/sw.js') 217 .then(function(registration) { 218 console.log('ServiceWorker registration successful'); 219 }) 220 .catch(function(err) { 221 console.log('ServiceWorker registration failed'); 222 }); 223 }); 224 } 225 226})(); 227 228
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.