📜
gallery.js
Back
📝 Javascript ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
/* ---------- Gallery Management ---------- */ /* DOM Elements */ let crumbsEl = document.getElementById('crumbs'); let foldersTrack = document.getElementById('foldersTrack'); let emptyFolders = document.getElementById('emptyFolders'); let galleryTrack = document.getElementById('galleryTrack'); let emptyImages = document.getElementById('emptyImages'); let goUpBtn = document.getElementById('goUpBtn'); let refreshBtn = document.getElementById('refreshBtn'); let imagesPanel = document.getElementById('imagesPanel'); /* State */ let currentSub = ''; let hideTimer = null; let isGalleryVisible = false; /* ---------- Auto-Hide Functionality ---------- */ function resetHideTimer() { if (hideTimer) { clearTimeout(hideTimer); } if (isGalleryVisible) { hideTimer = setTimeout(() => { hideGallery(); }, 5000); // 5 seconds } } function showGallery() { if (imagesPanel) { imagesPanel.classList.add('active'); isGalleryVisible = true; resetHideTimer(); } } function hideGallery() { if (imagesPanel) { imagesPanel.classList.remove('active'); isGalleryVisible = false; if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; } } } function addActivityListeners(element) { if (!element) return; const events = ['click', 'mousemove', 'mouseenter', 'scroll', 'touchstart', 'touchmove']; events.forEach(eventType => { element.addEventListener(eventType, resetHideTimer, { passive: true }); }); } /* ---------- API Communication ---------- */ async function fetchDir(sub = '') { const res = await fetch(`media.php?sub=${encodeURIComponent(sub)}`, {cache:'no-store'}); if(!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } /* ---------- Rendering Functions ---------- */ function renderBreadcrumb(bc) { crumbsEl.innerHTML = ''; bc.forEach((c, i) => { const b = document.createElement('button'); b.textContent = c.label; b.onclick = () => { openSub(c.sub); resetHideTimer(); }; crumbsEl.appendChild(b); if(i < bc.length - 1) { const s = document.createElement('span'); s.className = 'sep'; s.textContent = '›'; crumbsEl.appendChild(s); } }); } function renderFolders(items) { foldersTrack.innerHTML = ''; emptyFolders.hidden = !!items.length; for(const f of items) { const card = document.createElement('div'); card.className = 'folder-card'; card.innerHTML = `<span>📂</span><span>${f.name}</span>`; card.onclick = () => { openSub(f.sub); resetHideTimer(); }; foldersTrack.appendChild(card); } } function renderImages(items) { galleryTrack.innerHTML = ''; emptyImages.hidden = !!items.length; for(const im of items) { const img = document.createElement('img'); img.alt = im.name; img.loading = 'lazy'; img.decoding = 'async'; img.src = im.url; img.onclick = () => { window.CoreAPI.setImage(im.url); resetHideTimer(); }; galleryTrack.appendChild(img); } } /* ---------- Auto Tile Size Detection ---------- */ function inferTileFromPath(sub) { if (!sub) return null; const parts = sub.split('/').filter(Boolean).reverse(); for (const p of parts) { const n = parseInt(p, 10); if (String(n) === p && n > 0) return n; } return null; } /* ---------- Navigation ---------- */ async function openSub(sub) { try { const data = await fetchDir(sub); currentSub = data.cwd || ''; renderBreadcrumb(data.breadcrumb || []); renderFolders(data.folders || []); renderImages(data.images || []); // Auto-detect tile size from folder name const inferred = inferTileFromPath(currentSub); if (inferred) { window.CoreAPI.setTileSize(inferred); } // Reset timer when navigating resetHideTimer(); } catch(err) { console.error(err); alert('Failed to load folder.'); } } function parentOf(sub) { if(!sub) return ''; const p = sub.split('/').filter(Boolean); p.pop(); return p.join('/'); } /* ---------- Event Handlers ---------- */ function initGalleryEvents() { goUpBtn.addEventListener('click', () => { openSub(parentOf(currentSub)); resetHideTimer(); }); refreshBtn.addEventListener('click', () => { openSub(currentSub); resetHideTimer(); }); // Add activity listeners to all gallery elements addActivityListeners(imagesPanel); addActivityListeners(crumbsEl); addActivityListeners(foldersTrack); addActivityListeners(galleryTrack); // Listen for images button click to show gallery const imagesBtn = document.getElementById('imagesBtn'); if (imagesBtn) { imagesBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); if (isGalleryVisible) { hideGallery(); } else { showGallery(); } }); } // Hide gallery when clicking outside document.addEventListener('click', (e) => { if (isGalleryVisible && imagesPanel && !imagesPanel.contains(e.target) && e.target !== document.getElementById('imagesBtn')) { hideGallery(); } }); } /* ---------- Initialization ---------- */ function initializeGallery() { try { // Get DOM elements (reassign in case they weren't available initially) crumbsEl = document.getElementById('crumbs'); foldersTrack = document.getElementById('foldersTrack'); emptyFolders = document.getElementById('emptyFolders'); galleryTrack = document.getElementById('galleryTrack'); emptyImages = document.getElementById('emptyImages'); goUpBtn = document.getElementById('goUpBtn'); refreshBtn = document.getElementById('refreshBtn'); imagesPanel = document.getElementById('imagesPanel'); // Check required DOM elements and dependencies if (!crumbsEl || !foldersTrack || !galleryTrack || !goUpBtn) { throw new Error('Required DOM elements not found'); } if (!window.CoreAPI || typeof window.CoreAPI.setImage !== 'function') { throw new Error('CoreAPI not available'); } initGalleryEvents(); openSub(''); // Load root folder } catch (error) { alert(`Gallery module failed to initialize: ${error.message}\n\nNote: Gallery requires media.php to be present and working.`); // Don't throw here since gallery failure shouldn't break the whole app } } // Export API for other modules window.GalleryAPI = { openSub, currentSub: () => currentSub, showGallery, hideGallery, resetHideTimer };