1// ============================================ 2// ALL ARTICLE - BLOG PAGE SCRIPT 3// ============================================ 4 5document.addEventListener('DOMContentLoaded', function() { 6 'use strict'; 7 8 // ===== DARK MODE TOGGLE ===== 9 const themeToggle = document.getElementById('themeToggle'); 10 const iconSun = document.getElementById('iconSun'); 11 const iconMoon = document.getElementById('iconMoon'); 12 const themeText = document.querySelector('.theme-toggle-text'); 13 14 // Check saved theme 15 const savedTheme = localStorage.getItem('blogTheme') || 'light'; 16 if (savedTheme === 'dark') { 17 document.body.classList.add('dark-mode'); 18 iconSun.style.display = 'none'; 19 iconMoon.style.display = 'block'; 20 if (themeText) themeText.textContent = window.publicTranslations.lightMode; 21 } 22 23 // Toggle theme 24 if (themeToggle) { 25 themeToggle.addEventListener('click', function() { 26 const isDark = document.body.classList.toggle('dark-mode'); 27 28 if (isDark) { 29 iconSun.style.display = 'none'; 30 iconMoon.style.display = 'block'; 31 if (themeText) themeText.textContent = window.publicTranslations.lightMode; 32 localStorage.setItem('blogTheme', 'dark'); 33 } else { 34 iconSun.style.display = 'block'; 35 iconMoon.style.display = 'none'; 36 if (themeText) themeText.textContent = window.publicTranslations.darkMode; 37 localStorage.setItem('blogTheme', 'light'); 38 } 39 }); 40 } 41 42 // ===== SEARCH AUTO-SUBMIT ===== 43 const searchInput = document.querySelector('.search-input'); 44 let searchTimeout; 45 46 if (searchInput) { 47 searchInput.addEventListener('input', function() { 48 clearTimeout(searchTimeout); 49 searchTimeout = setTimeout(() => { 50 const form = this.closest('form'); 51 if (form && this.value.length >= 3) { 52 form.submit(); 53 } 54 }, 500); 55 }); 56 } 57 58 // ===== TAGS TRACKING ===== 59 const tagLinks = document.querySelectorAll('.tag-link'); 60 tagLinks.forEach(link => { 61 link.addEventListener('click', function(e) { 62 // Track tag click for analytics if needed 63 const tagName = this.textContent.trim(); 64 console.log('Tag clicked:', tagName); 65 }); 66 }); 67 68 // ===== LAZY LOAD IMAGES ===== 69 if ('IntersectionObserver' in window) { 70 const images = document.querySelectorAll('.blog-card-thumbnail img'); 71 const imageObserver = new IntersectionObserver((entries, observer) => { 72 entries.forEach(entry => { 73 if (entry.isIntersecting) { 74 const img = entry.target; 75 img.loading = 'lazy'; 76 img.decoding = 'async'; 77 observer.unobserve(img); 78 } 79 }); 80 }); 81 82 images.forEach(img => { 83 imageObserver.observe(img); 84 }); 85 } 86 87 // ===== KEYBOARD NAVIGATION ===== 88 document.addEventListener('keydown', function(e) { 89 // Ctrl + / untuk focus search 90 if ((e.ctrlKey || e.metaKey) && e.key === '/') { 91 e.preventDefault(); 92 const searchInput = document.querySelector('.search-input'); 93 if (searchInput) { 94 searchInput.focus(); 95 } 96 } 97 }); 98 99 // ===== READ TIME CALCULATION ===== 100 // Optional: Add read time to blog cards if needed 101 const excerpts = document.querySelectorAll('.blog-card-excerpt'); 102 excerpts.forEach(excerpt => { 103 const text = excerpt.textContent; 104 const wordCount = text.split(/\s+/).length; 105 const readTime = Math.ceil(wordCount / 200); // 200 words per minute 106 if (readTime > 0) { 107 const timeElement = document.createElement('span'); 108 timeElement.className = 'read-time'; 109 timeElement.textContent = `â± ${window.publicTranslations.readTime.replace(':count', readTime)}`; 110 timeElement.style.cssText = `
111 font-size: 11px; 112 color: var(--text-soft); 113 margin-left: 8px; 114 `; 115 const meta = excerpt.closest('.blog-card-content')?.querySelector('.blog-card-meta'); 116 if (meta) { 117 meta.appendChild(timeElement); 118 } 119 } 120 }); 121 122 // ===== SMOOTH SCROLL TO TOP ===== 123 const paginationLinks = document.querySelectorAll('.pagination-wrapper a'); 124 paginationLinks.forEach(link => { 125 link.addEventListener('click', function(e) { 126 // Scroll to top of blog page 127 const blogPage = document.getElementById('blogPage'); 128 if (blogPage) { 129 setTimeout(() => { 130 blogPage.scrollIntoView({ behavior: 'smooth', block: 'start' }); 131 }, 100); 132 } 133 }); 134 }); 135 136 console.log('Blog page initialized successfully'); 137}); 138 139// ===== PERFORMANCE: Defer non-critical scripts ===== 140if ('requestIdleCallback' in window) { 141 requestIdleCallback(() => { 142 // Load analytics or other non-critical scripts 143 console.log('Blog page idle tasks completed'); 144 }); 145}
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.