added fallback mechanic to imageviewer.js
This commit is contained in:
parent
5881fa76e0
commit
d8ebe94657
3 changed files with 207 additions and 189 deletions
174
static/js/dynamicscrollingimages.js
Normal file
174
static/js/dynamicscrollingimages.js
Normal 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');
|
||||||
|
})();
|
|
@ -60,10 +60,29 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
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 || '';
|
|
||||||
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 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');
|
||||||
|
@ -71,13 +90,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
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
|
||||||
|
|
|
@ -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>
|
Loading…
Add table
Reference in a new issue