From d8ebe9465747d046c4effab34874a1baaf901f7b Mon Sep 17 00:00:00 2001 From: partisan Date: Mon, 2 Dec 2024 10:45:50 +0100 Subject: [PATCH] added fallback mechanic to imageviewer.js --- static/js/dynamicscrollingimages.js | 174 +++++++++++++++++++++++++++ static/js/imageviewer.js | 44 +++++-- templates/images.html | 178 +--------------------------- 3 files changed, 207 insertions(+), 189 deletions(-) create mode 100644 static/js/dynamicscrollingimages.js diff --git a/static/js/dynamicscrollingimages.js b/static/js/dynamicscrollingimages.js new file mode 100644 index 0000000..5213f7f --- /dev/null +++ b/static/js/dynamicscrollingimages.js @@ -0,0 +1,174 @@ +(function() { + // Configuration + const imageStatusInterval = 500; // Interval in milliseconds to check image status + const scrollThreshold = 500; // Distance from bottom of the page to trigger loading + let isFetching = false; + let page = parseInt(document.getElementById('template-data').getAttribute('data-page')) || 1; + let query = document.getElementById('template-data').getAttribute('data-query'); + let hardCacheEnabled = document.getElementById('template-data').getAttribute('data-hard-cache-enabled') === 'true'; + let noMoreImages = false; // Flag to indicate if there are no more images to load + + let imageElements = []; + let imageIds = []; + + /** + * Function to handle image load errors with retry logic + * @param {HTMLElement} imgElement - The image element that failed to load + * @param {number} retryCount - Number of retries left + * @param {number} retryDelay - Delay between retries in milliseconds + */ + function handleImageError(imgElement, retryCount = 3, retryDelay = 1000) { + if (retryCount > 0) { + setTimeout(() => { + imgElement.src = imgElement.getAttribute('data-full'); + imgElement.onerror = function() { + handleImageError(imgElement, retryCount - 1, retryDelay); + }; + }, retryDelay); + } else { + // After retries, hide the image container or set a fallback image + console.warn('Image failed to load after retries:', imgElement.getAttribute('data-full')); + imgElement.parentElement.style.display = 'none'; // Hide the image container + // Alternatively, set a fallback image: + // imgElement.src = '/static/images/fallback.svg'; + } + } + + /** + * Function to ensure the page is scrollable by loading more images if necessary + */ + function ensureScrollable() { + if (noMoreImages) return; // Do not attempt if no more images are available + // Check if the page is not scrollable + if (document.body.scrollHeight <= window.innerHeight) { + // If not scrollable, fetch the next page + fetchNextPage(); + } + } + + /** + * Function to fetch the next page of images + */ + function fetchNextPage() { + if (isFetching || noMoreImages) return; + isFetching = true; + page += 1; + + fetch(`/search?q=${encodeURIComponent(query)}&t=image&p=${page}&ajax=true`) + .then(response => response.text()) + .then(html => { + // Parse the returned HTML and extract image elements + let parser = new DOMParser(); + let doc = parser.parseFromString(html, 'text/html'); + let newImages = doc.querySelectorAll('.image'); + + if (newImages.length > 0) { + let resultsContainer = document.querySelector('.images'); + newImages.forEach(imageDiv => { + // Append new images to the container + resultsContainer.appendChild(imageDiv); + + // Get the img element + let img = imageDiv.querySelector('img'); + if (img) { + if (hardCacheEnabled) { + // Replace image with placeholder + img.src = '/static/images/placeholder.svg'; + img.onerror = function() { + handleImageError(img); + }; + + let id = img.getAttribute('data-id'); + if (id) { // Only include if ID is not empty + imageElements.push(img); + imageIds.push(id); + } + } + } + }); + if (hardCacheEnabled) { + checkImageStatus(); + } + // After appending new images, ensure the page is scrollable + ensureScrollable(); + } else { + // No more images to load + noMoreImages = true; + } + isFetching = false; + }) + .catch(error => { + console.error('Error fetching next page:', error); + isFetching = false; + }); + } + + /** + * Function to check image status via AJAX + */ + function checkImageStatus() { + if (!hardCacheEnabled) return; + if (imageIds.length === 0) { + // No images to check, do nothing + return; + } + + // Send AJAX request to check image status + fetch(`/image_status?image_ids=${imageIds.join(',')}`) + .then(response => response.json()) + .then(statusMap => { + imageElements = imageElements.filter(img => { + let id = img.getAttribute('data-id'); + if (statusMap[id]) { + // Image is ready, update src + img.src = statusMap[id]; + img.onerror = function() { + handleImageError(img); + }; + // Remove the image id from the list + imageIds = imageIds.filter(imageId => imageId !== id); + return false; // Remove img from imageElements + } + return true; // Keep img in imageElements + }); + // After updating images, ensure the page is scrollable + ensureScrollable(); + }) + .catch(error => { + console.error('Error checking image status:', error); + }); + } + + // Initialize imageElements and imageIds + if (hardCacheEnabled) { + imageElements = Array.from(document.querySelectorAll('img[data-id]')); + imageIds = imageElements + .map(img => img.getAttribute('data-id')) + .filter(id => id); // Exclude empty IDs + + // Replace images with placeholders + imageElements.forEach(img => { + img.src = '/static/images/placeholder.svg'; + }); + + // Start checking image status + let imageStatusTimer = setInterval(checkImageStatus, imageStatusInterval); + checkImageStatus(); // Initial check + + // After initial images are loaded, ensure the page is scrollable + window.addEventListener('load', ensureScrollable); + } + + // Infinite scrolling + window.addEventListener('scroll', function() { + if (isFetching || noMoreImages) return; + + if (window.innerHeight + window.scrollY >= document.body.offsetHeight - scrollThreshold) { + // User scrolled near the bottom + fetchNextPage(); + } + }); + + // Remove 'js-enabled' class from content + document.getElementById('content').classList.remove('js-enabled'); +})(); \ No newline at end of file diff --git a/static/js/imageviewer.js b/static/js/imageviewer.js index d703741..740c4d1 100644 --- a/static/js/imageviewer.js +++ b/static/js/imageviewer.js @@ -57,27 +57,47 @@ document.addEventListener('DOMContentLoaded', function() { function displayImage(index) { if (index < 0 || index >= imageList.length) return; - + const imgElement = imageList[index]; const parentImageDiv = imgElement.closest('.image'); - const fullImageUrl = imgElement.getAttribute('data-full'); // Use data-full for the full image URL - const title = imgElement.alt || ''; - const sourceUrl = parentImageDiv.querySelector('.img_source').href || '#'; // Source webpage URL - + + if (!parentImageDiv) { + console.warn('Parent image div not found'); + return; + } + + // Use the `data-full` attribute for the full image URL + let fullImageUrl = imgElement.getAttribute('data-full') || imgElement.src; + const title = imgElement.alt || 'Untitled'; + + // Gracefully handle the source URL or other attributes + const sourceElement = parentImageDiv.querySelector('.img_source'); + const sourceUrl = sourceElement ? sourceElement.href : null; + + // Fallback logic: if sourceUrl is null, use `data-proxy-full` or a meaningful default + const proxyFullUrl = imgElement.getAttribute('data-proxy-full') || fullImageUrl; + + // Check if full image is missing, fallback to proxy size + if (fullImageUrl === '/static/images/missing.svg') { + fullImageUrl = proxyFullUrl; + } + + // Elements in the viewer const viewerImage = document.getElementById('viewer-image'); const viewerTitle = document.getElementById('viewer-title'); const viewerSourceButton = document.getElementById('viewer-source-button'); const fullSizeLink = document.getElementById('viewer-full-size-link'); const proxySizeLink = document.getElementById('viewer-proxy-size-link'); const viewerImageLink = document.getElementById('viewer-image-link'); - - // Set the viewer image to the full image URL - viewerImage.src = fullImageUrl; + + // Assign values to the viewer elements + viewerImage.src = fullImageUrl; // Full-size image in the viewer viewerTitle.textContent = title; - viewerSourceButton.href = sourceUrl; - fullSizeLink.href = sourceUrl; // Link to the source website - proxySizeLink.href = fullImageUrl; // Link to the proxied full-size image - viewerImageLink.href = fullImageUrl; // Make image clickable to open in new tab + + viewerSourceButton.href = sourceUrl || proxyFullUrl; // Use proxy URL if source is missing + fullSizeLink.href = sourceUrl || proxyFullUrl; // Link to source website or proxy + proxySizeLink.href = fullImageUrl; // Link to the proxied full-size image + viewerImageLink.href = fullImageUrl; // Make image clickable to open in a new tab } // Attach event listener to the document body diff --git a/templates/images.html b/templates/images.html index 62819ee..837d5ed 100755 --- a/templates/images.html +++ b/templates/images.html @@ -152,182 +152,6 @@ - - + \ No newline at end of file