// Main JavaScript file for Palina theme document.addEventListener('DOMContentLoaded', () => { console.log('Palina theme loaded!'); const htmlElement = document.documentElement; // Remove 'hidden' class from html to prevent FOUC htmlElement.classList.remove('hidden'); // Staggered fade-in animation for post grid (initial load) const initialGridItems = document.querySelectorAll('.post-grid-item'); initialGridItems.forEach((item, index) => { item.style.animationDelay = `${index * 100}ms`; item.classList.add('animate-fadeInUp'); }); // Lightbox for post images const lightbox = document.getElementById('lightbox'); if (lightbox) { const lightboxImage = document.getElementById('lightbox-image'); const lightboxClose = document.getElementById('lightbox-close'); const setupLightboxImage = (image) => { image.style.cursor = 'pointer'; image.addEventListener('click', () => { lightbox.classList.remove('hidden'); lightbox.classList.add('show'); lightboxImage.src = image.src; }); }; // Initial setup for existing images document.querySelectorAll('.kg-image-card img, .post-content img').forEach(setupLightboxImage); lightboxClose.addEventListener('click', () => { lightbox.classList.add('hidden'); lightbox.classList.remove('show'); lightboxImage.src = ''; }); lightbox.addEventListener('click', (e) => { if (e.target.id !== 'lightbox-image') { lightbox.classList.add('hidden'); lightbox.classList.remove('show'); lightboxImage.src = ''; } }); } // Theme Switcher const themeToggle = document.getElementById('theme-toggle'); // const htmlElement = document.documentElement; // Already defined above const currentTheme = localStorage.getItem('theme'); if (currentTheme) { htmlElement.setAttribute('data-theme', currentTheme); } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { // Default to light theme if system preference is light htmlElement.setAttribute('data-theme', 'light'); } if (themeToggle) { themeToggle.addEventListener('click', () => { let newTheme = htmlElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; htmlElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); }); } // Mobile Menu const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); const mobileMenuClose = document.getElementById('mobile-menu-close'); const mobileMenu = document.getElementById('mobile-menu'); if (mobileMenuToggle && mobileMenu && mobileMenuClose) { mobileMenuToggle.addEventListener('click', () => { mobileMenu.classList.remove('-translate-x-full'); mobileMenu.classList.add('translate-x-0'); }); mobileMenuClose.addEventListener('click', () => { mobileMenu.classList.remove('translate-x-0'); mobileMenu.classList.add('-translate-x-full'); }); // Close menu if a link is clicked const mobileNavLinks = mobileMenu.querySelectorAll('a'); mobileNavLinks.forEach(link => { link.addEventListener('click', () => { mobileMenu.classList.remove('translate-x-0'); mobileMenu.classList.add('-translate-x-full'); }); }); } // Infinite Scroll const postsContainer = document.getElementById('posts-container'); const loadingSpinner = document.getElementById('loading-spinner'); if (postsContainer && loadingSpinner) { let currentPage = 1; let isLoading = false; let hasMorePosts = true; const ghostApiKeyMeta = document.querySelector('meta[name="ghost-api-key"]'); const ghostApiUrlMeta = document.querySelector('meta[name="ghost-api-url"]'); if (!ghostApiKeyMeta || !ghostApiUrlMeta) { console.error('Ghost Content API Key or URL meta tag not found. Infinite scroll will not work.'); return; } const GHOST_API_KEY = ghostApiKeyMeta.content; const GHOST_API_URL = ghostApiUrlMeta.content; const fetchPosts = async () => { if (isLoading || !hasMorePosts) return; isLoading = true; loadingSpinner.classList.remove('hidden'); currentPage++; const postsPerPage = 12; // Matches posts_per_page in package.json config const url = `${GHOST_API_URL}/ghost/api/content/posts/?key=${GHOST_API_KEY}&limit=${postsPerPage}&page=${currentPage}&include=tags,authors`; try { const response = await fetch(url); const data = await response.json(); const newPosts = data.posts; const pagination = data.meta.pagination; if (newPosts.length > 0) { newPosts.forEach((post, index) => { const article = document.createElement('article'); article.className = 'post-grid-item opacity-0 relative bg-[var(--bg-secondary)] rounded-lg shadow-lg overflow-hidden group'; // Basic rendering of a post for infinite scroll. // This should ideally match the structure in index.hbs // For simplicity, I'm reconstructing it here. article.innerHTML = ` ${post.feature_image ? ` ${post.title} ` : `
No Image
`}

${post.title}

`; postsContainer.appendChild(article); // Apply staggered animation to newly added posts // Calculate delay based on total items const totalItems = postsContainer.children.length; article.style.animationDelay = `${(totalItems - newPosts.length + index) * 100}ms`; article.classList.add('animate-fadeInUp'); // If lightbox is present, set up new images for lightbox if (lightbox) { article.querySelectorAll('.kg-image-card img, .post-content img').forEach(setupLightboxImage); } }); } else { hasMorePosts = false; } if (pagination && pagination.next === null) { hasMorePosts = false; } } catch (error) { console.error('Error fetching posts:', error); hasMorePosts = false; // Stop trying to fetch if there's an error } finally { isLoading = false; loadingSpinner.classList.add('hidden'); } }; const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting && hasMorePosts && !isLoading) { fetchPosts(); } }); }, { threshold: 0.1 }); // Trigger when 10% of the spinner is visible observer.observe(loadingSpinner); } });