(function() { // Get template data and configuration const templateData = document.getElementById('template-data'); const type = templateData.getAttribute('data-type'); const hardCacheEnabled = templateData.getAttribute('data-hard-cache-enabled') === 'true'; // Track all favicon/image elements and their IDs const allMediaElements = []; const allMediaIds = []; let statusCheckTimeout = null; // Add loading effects to image/favicon and associated text function addLoadingEffects(imgElement) { const container = imgElement.closest(type === 'image' ? '.image' : '.result_item'); if (!container) return; const titleSelector = type === 'image' ? '.img_title' : '.result-url'; const title = container.querySelector(titleSelector); imgElement.closest('.favicon-wrapper')?.classList.add('loading'); // if (title) title.classList.add('title-loading'); } // Remove loading effects when image/favicon loads function removeLoadingEffects(imgElement) { const container = imgElement.closest(type === 'image' ? '.image' : '.result_item'); const titleSelector = type === 'image' ? '.img_title' : '.result-url'; const title = container?.querySelector(titleSelector); imgElement.closest('.favicon-wrapper')?.classList.remove('loading'); if (title) title.classList.remove('title-loading'); if (type === 'image' && imgElement.src.endsWith('/images/missing.svg')) { container.remove(); } } // Handle image/favicon loading errors function handleImageError(imgElement) { const container = imgElement.closest(type === 'image' ? '.image' : '.result_item'); const titleSelector = type === 'image' ? '.img_title' : '.result-url'; const title = container?.querySelector(titleSelector); imgElement.closest('.favicon-wrapper')?.classList.remove('loading'); if (title) title.classList.remove('title-loading'); if (type === 'image') { container.style.display = 'none'; } else { imgElement.src = '/static/images/missing.svg'; } } // Shared configuration const statusCheckInterval = 500; const scrollThreshold = 500; const loadingIndicator = document.getElementById('message-bottom-right'); let loadingTimer; let isFetching = false; let page = parseInt(templateData.getAttribute('data-page')) || 1; let query = templateData.getAttribute('data-query'); let noMoreImages = false; function showLoadingMessage() { loadingIndicator.classList.add('visible'); } function hideLoadingMessage() { loadingIndicator.classList.remove('visible'); } function ensureScrollable() { if (noMoreImages) return; if (document.body.scrollHeight <= window.innerHeight) { fetchNextPage(); } } // Register a new media element for tracking function registerMediaElement(imgElement) { const id = imgElement.getAttribute('data-id'); if (!id || allMediaIds.includes(id)) return; // Wrap the image in a .favicon-wrapper if not already if (!imgElement.parentElement.classList.contains('favicon-wrapper')) { const wrapper = document.createElement('span'); wrapper.classList.add('favicon-wrapper'); imgElement.parentElement.replaceChild(wrapper, imgElement); wrapper.appendChild(imgElement); } // Track and style allMediaElements.push(imgElement); allMediaIds.push(id); addLoadingEffects(imgElement); if (hardCacheEnabled) { imgElement.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; } else { imgElement.src = '/static/images/placeholder.svg'; } // Schedule a status check if not already pending if (!statusCheckTimeout) { statusCheckTimeout = setTimeout(checkMediaStatus, statusCheckInterval); } } // Check status of all tracked media elements function checkMediaStatus() { statusCheckTimeout = null; if (allMediaIds.length === 0) return; // Group IDs to avoid very long URLs const idGroups = []; for (let i = 0; i < allMediaIds.length; i += 50) { idGroups.push(allMediaIds.slice(i, i + 50)); } const checkGroup = (group) => { return fetch(`/image_status?image_ids=${group.join(',')}`) .then(response => response.json()) .then(statusMap => { const pendingElements = []; const pendingIds = []; allMediaElements.forEach((imgElement, index) => { const id = allMediaIds[index]; if (group.includes(id)) { if (statusMap[id]) { if (imgElement.src !== statusMap[id]) { imgElement.src = statusMap[id]; imgElement.onload = () => removeLoadingEffects(imgElement); imgElement.onerror = () => handleImageError(imgElement); } } else { pendingElements.push(imgElement); pendingIds.push(id); } } }); // Update global arrays with remaining pending items allMediaElements = pendingElements; allMediaIds = pendingIds; }); }; // Process all groups sequentially const processGroups = async () => { for (const group of idGroups) { try { await checkGroup(group); } catch (error) { console.error('Status check error:', error); } } // If we still have pending items, schedule another check if (allMediaIds.length > 0) { statusCheckTimeout = setTimeout(checkMediaStatus, statusCheckInterval); } }; processGroups(); } function fetchNextPage() { if (isFetching || noMoreImages) return; loadingTimer = setTimeout(() => { showLoadingMessage(); }, 150); isFetching = true; page += 1; fetch(`/search?q=${encodeURIComponent(query)}&t=${type}&p=${page}&ajax=true`) .then(response => response.text()) .then(html => { clearTimeout(loadingTimer); hideLoadingMessage(); let tempDiv = document.createElement('div'); tempDiv.innerHTML = html; let newItems = tempDiv.querySelectorAll(type === 'image' ? '.image' : '.result_item'); if (newItems.length > 0) { let resultsContainer = document.querySelector(type === 'image' ? '.images' : '.results'); newItems.forEach(item => { let clonedItem = item.cloneNode(true); resultsContainer.appendChild(clonedItem); // Register any new media elements const img = clonedItem.querySelector('img[data-id]'); if (img) { registerMediaElement(img); } }); ensureScrollable(); } else { noMoreImages = true; } isFetching = false; }) .catch(error => { clearTimeout(loadingTimer); hideLoadingMessage(); console.error('Fetch error:', error); isFetching = false; }); } // Initialize all existing media elements function initializeMediaElements() { document.querySelectorAll('img[data-id]').forEach(img => { registerMediaElement(img); }); // Start periodic checks if hard cache is enabled if (hardCacheEnabled && allMediaIds.length > 0) { statusCheckTimeout = setTimeout(checkMediaStatus, statusCheckInterval); } } // Initialize when DOM is ready if (document.readyState === 'complete') { initializeMediaElements(); } else { window.addEventListener('load', initializeMediaElements); } // Infinite scroll handler window.addEventListener('scroll', () => { if (isFetching || noMoreImages) return; if (window.innerHeight + window.scrollY >= document.body.offsetHeight - scrollThreshold) { fetchNextPage(); } }); // Clean up on page unload window.addEventListener('beforeunload', () => { if (statusCheckTimeout) { clearTimeout(statusCheckTimeout); } }); })();