PageSourceSearch

https://unissula.ac.id/js/all-article.js

js unissula.ac.id collected 2026-10-02 05:06:39 UTC 5,314 bytes, 145 lines download raw bytes

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.