(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 let allMediaElements = []; let allMediaIds = []; const mediaMap = new Map(); // 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/globe.svg')) { container.remove(); } } // Handle image/favicon loading errors function handleImageError(imgElement, retryCount = 8, retryDelay = 500) { const isFavicon = !!imgElement.closest('.favicon-wrapper'); const container = imgElement.closest(type === 'image' ? '.image' : '.result_item'); const titleSelector = type === 'image' ? '.img_title' : '.result-url'; const title = container?.querySelector(titleSelector); const fullURL = imgElement.getAttribute('data-full'); if (retryCount > 0 && !imgElement.dataset.checked404) { imgElement.dataset.checked404 = '1'; // avoid infinite loop fetch(fullURL, { method: 'HEAD' }) .then(res => { if (res.status === 404) { fallbackToGlobe(imgElement); } else { setTimeout(() => { imgElement.src = fullURL; imgElement.onerror = () => handleImageError(imgElement, retryCount - 1, retryDelay); }, retryDelay); } }) .catch(() => { fallbackToGlobe(imgElement); }); } else { fallbackToGlobe(imgElement); } function fallbackToGlobe(imgElement) { imgElement.closest('.favicon-wrapper')?.classList.remove('loading'); if (title) title.classList.remove('title-loading'); if (isFavicon) { const wrapper = imgElement.closest('.favicon-wrapper') || imgElement.parentElement; const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttribute("viewBox", "0 -960 960 960"); svg.setAttribute("height", imgElement.height || "16"); svg.setAttribute("width", imgElement.width || "16"); svg.setAttribute("fill", "currentColor"); svg.classList.add("favicon", "globe-fallback"); svg.innerHTML = `<path d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q83 0 155.5 31.5t127 86q54.5 54.5 86 127T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Zm0-82q26-36 45-75t31-83H404q12 44 31 83t45 75Zm-104-16q-18-33-31.5-68.5T322-320H204q29 50 72.5 87t99.5 55Zm208 0q56-18 99.5-55t72.5-87H638q-9 38-22.5 73.5T584-178ZM170-400h136q-3-20-4.5-39.5T300-480q0-21 1.5-40.5T306-560H170q-5 20-7.5 39.5T160-480q0 21 2.5 40.5T170-400Zm216 0h188q3-20 4.5-39.5T580-480q0-21-1.5-40.5T574-560H386q-3 20-4.5 39.5T380-480q0 21 1.5 40.5T386-400Zm268 0h136q5-20 7.5-39.5T800-480q0-21-2.5-40.5T790-560H654q3 20 4.5 39.5T660-480q0 21-1.5 40.5T654-400Zm-16-240h118q-29-50-72.5-87T584-782q18 33 31.5 68.5T638-640Zm-234 0h152q-12-44-31-83t-45-75q-26 36-45 75t-31 83Zm-200 0h118q9-38 22.5-73.5T376-782q-56 18-99.5 55T204-640Z"/>`; imgElement.remove(); wrapper.appendChild(svg); } else if (type === 'image') { container?.remove(); } } } // 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) return; let wrapper = imgElement.closest('.favicon-wrapper'); if (!wrapper) { wrapper = document.createElement('span'); wrapper.classList.add('favicon-wrapper'); imgElement.replaceWith(wrapper); wrapper.appendChild(imgElement); } addLoadingEffects(imgElement); if (hardCacheEnabled) { imgElement.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; imgElement.onerror = () => handleImageError(imgElement, 3, 1000); } else { imgElement.src = imgElement.getAttribute('data-full'); imgElement.onload = () => removeLoadingEffects(imgElement); imgElement.onerror = () => handleImageError(imgElement, 3, 1000); } // Track it if (!mediaMap.has(id)) { mediaMap.set(id, []); } mediaMap.get(id).push(imgElement); } // Check status of all tracked media elements function checkMediaStatus() { const allIds = Array.from(mediaMap.keys()); if (allIds.length === 0) return; const idGroups = []; for (let i = 0; i < allIds.length; i += 50) { idGroups.push(allIds.slice(i, i + 50)); } const processGroups = async () => { const stillPending = new Map(); for (const group of idGroups) { try { const response = await fetch(`/image_status?image_ids=${group.join(',')}`); const statusMap = await response.json(); group.forEach(id => { const elements = mediaMap.get(id); const resolved = statusMap[id]; if (!elements) return; if (resolved && resolved !== 'pending') { elements.forEach(img => { img.src = resolved; img.onload = () => removeLoadingEffects(img); img.onerror = () => handleImageError(img); }); } else { stillPending.set(id, elements); } }); } catch (err) { console.error('Status check failed:', err); group.forEach(id => { if (mediaMap.has(id)) { stillPending.set(id, mediaMap.get(id)); } }); } } mediaMap.clear(); for (const [id, imgs] of stillPending) { mediaMap.set(id, imgs); } }; 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); }); } function startStatusPolling() { checkMediaStatus(); setInterval(checkMediaStatus, statusCheckInterval); } if (document.readyState === 'complete') { initializeMediaElements(); if (hardCacheEnabled) startStatusPolling(); } else { window.addEventListener('load', () => { initializeMediaElements(); if (hardCacheEnabled) startStatusPolling(); }); } // 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); // } // }); })();