added fallback mechanic to imageviewer.js

This commit is contained in:
partisan 2024-12-02 10:45:50 +01:00
parent 5881fa76e0
commit d8ebe94657
3 changed files with 207 additions and 189 deletions

View file

@ -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');
})();

View file

@ -57,27 +57,47 @@ document.addEventListener('DOMContentLoaded', function() {
function displayImage(index) { function displayImage(index) {
if (index < 0 || index >= imageList.length) return; if (index < 0 || index >= imageList.length) return;
const imgElement = imageList[index]; const imgElement = imageList[index];
const parentImageDiv = imgElement.closest('.image'); const parentImageDiv = imgElement.closest('.image');
const fullImageUrl = imgElement.getAttribute('data-full'); // Use data-full for the full image URL
const title = imgElement.alt || ''; if (!parentImageDiv) {
const sourceUrl = parentImageDiv.querySelector('.img_source').href || '#'; // Source webpage URL 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 viewerImage = document.getElementById('viewer-image');
const viewerTitle = document.getElementById('viewer-title'); const viewerTitle = document.getElementById('viewer-title');
const viewerSourceButton = document.getElementById('viewer-source-button'); const viewerSourceButton = document.getElementById('viewer-source-button');
const fullSizeLink = document.getElementById('viewer-full-size-link'); const fullSizeLink = document.getElementById('viewer-full-size-link');
const proxySizeLink = document.getElementById('viewer-proxy-size-link'); const proxySizeLink = document.getElementById('viewer-proxy-size-link');
const viewerImageLink = document.getElementById('viewer-image-link'); const viewerImageLink = document.getElementById('viewer-image-link');
// Set the viewer image to the full image URL // Assign values to the viewer elements
viewerImage.src = fullImageUrl; viewerImage.src = fullImageUrl; // Full-size image in the viewer
viewerTitle.textContent = title; viewerTitle.textContent = title;
viewerSourceButton.href = sourceUrl;
fullSizeLink.href = sourceUrl; // Link to the source website viewerSourceButton.href = sourceUrl || proxyFullUrl; // Use proxy URL if source is missing
proxySizeLink.href = fullImageUrl; // Link to the proxied full-size image fullSizeLink.href = sourceUrl || proxyFullUrl; // Link to source website or proxy
viewerImageLink.href = fullImageUrl; // Make image clickable to open in new tab 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 // Attach event listener to the document body

View file

@ -152,182 +152,6 @@
<script defer src="/static/js/autocomplete.js"></script> <script defer src="/static/js/autocomplete.js"></script>
<script defer src="/static/js/imagetitletrim.js"></script> <script defer src="/static/js/imagetitletrim.js"></script>
<script defer src="/static/js/imageviewer.js"></script> <script defer src="/static/js/imageviewer.js"></script>
<!-- JavaScript to Load Images and Dynamic loading of the page --> <script defer src="/static/js/dynamicscrollingimages.js"></script>
<script>
(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');
})();
</script>
</body> </body>
</html> </html>