Search/static/js/imageviewer.js
partisan 5fdbe231d1
All checks were successful
Run Integration Tests / test (push) Successful in 29s
improved image viewer and some css
2025-04-19 21:24:27 +02:00

169 lines
No EOL
6.9 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
let viewerOpen = false;
let currentIndex = -1;
let imageList = [];
// Initialize imageList with all images on the page
function initializeImageList() {
imageList = Array.from(document.querySelectorAll('.image img.clickable'));
}
const viewerOverlay = document.getElementById('image-viewer-overlay');
// Set the innerHTML of viewerOverlay
viewerOverlay.innerHTML = `
<div id="image-viewer" class="image_view image_hide">
<div class="btn-nostyle">
<button class="btn-nostyle" id="viewer-prev-button">
<div class="material-icons-round icon_visibility clickable image-before">&#xe408;</div> <!-- navigate_before -->
</button>
<button class="btn-nostyle" id="viewer-next-button">
<div class="material-icons-round icon_visibility clickable image-next">&#xe409;</div> <!-- navigate_next -->
</button>
<button class="btn-nostyle" id="viewer-close-button">
<div class="material-icons-round icon_visibility clickable image-close">&#xe5cd;</div> <!-- close -->
</button>
</div>
<div class="view-image" id="viewer-image-container">
<img id="viewer-image" class="view-image-img" src="" alt="">
</div>
<p class="image-alt" id="viewer-title"></p>
<br>
<div class="search-type-icons" style="display:flex; justify-content:center; gap:15px; flex-wrap: wrap;">
<div class="icon-button">
<button class="material-icons-round clickable btn-nostyle" id="viewer-copy-link">
<span class="material-icons-round">&#xe37c;</span>
<p>Copy link</p>
</button>
</div>
<div class="icon-button">
<button class="material-icons-round clickable btn-nostyle" id="viewer-open-image">
<span class="material-icons-round">&#xe193;</span>
<p>Open image</p>
</button>
</div>
<div class="icon-button">
<button class="material-icons-round clickable btn-nostyle" id="viewer-open-source">
<span class="material-icons-round">&#xe366;</span>
<p>Go to source</p>
</button>
</div>
<div class="icon-button">
<button class="material-icons-round clickable btn-nostyle" id="viewer-download-image">
<span class="material-icons-round">&#xe2d1;</span>
<p>Download</p>
</button>
</div>
</div>
</div>
`;
const imageView = viewerOverlay.querySelector('#image-viewer');
const imagesContainer = document.querySelector('.images');
function openImageViewer(element) {
initializeImageList();
const parentImageDiv = element.closest('.image');
if (!parentImageDiv) return;
currentIndex = imageList.findIndex(img => img === parentImageDiv.querySelector('img.clickable'));
if (currentIndex === -1) return;
displayImage(currentIndex);
viewerOpen = true;
imagesContainer.classList.remove('images_viewer_hidden');
document.body.classList.add('viewer-open');
viewerOverlay.style.display = 'block';
imageView.classList.replace('image_hide', 'image_show');
}
let fullImageUrl, sourceUrl, proxyFullUrl;
function displayImage(index) {
if (index < 0 || index >= imageList.length) return;
imageList.forEach(img => {
const parentImageDiv = img.closest('.image');
parentImageDiv?.classList.remove('image_selected');
});
const imgElement = imageList[index];
const parentImageDiv = imgElement.closest('.image');
parentImageDiv?.classList.add('image_selected');
fullImageUrl = imgElement.getAttribute('data-full') || imgElement.src;
sourceUrl = imgElement.getAttribute('data-source');
proxyFullUrl = imgElement.getAttribute('data-proxy-full') || fullImageUrl;
const viewerImage = document.getElementById('viewer-image');
const viewerTitle = document.getElementById('viewer-title');
viewerTitle.textContent = imgElement.alt || 'Untitled';
viewerImage.onerror = () => viewerImage.src = proxyFullUrl;
viewerImage.onload = () => {};
viewerImage.src = fullImageUrl;
}
document.getElementById('viewer-copy-link').onclick = () => {
navigator.clipboard.writeText(window.location.origin + fullImageUrl).catch(console.error);
};
document.getElementById('viewer-open-image').onclick = () => {
window.open(fullImageUrl, '_blank');
};
document.getElementById('viewer-open-source').onclick = () => {
window.open(sourceUrl || proxyFullUrl, '_blank');
};
document.getElementById('viewer-download-image').onclick = (event) => {
event.stopPropagation();
const a = document.createElement('a');
a.href = fullImageUrl;
a.download = fullImageUrl.split('/').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
document.body.addEventListener('click', e => {
const clickableElement = e.target.closest('img.clickable, .img_title.clickable');
if (clickableElement) {
e.preventDefault();
openImageViewer(clickableElement);
}
});
function closeImageViewer() {
imageView.classList.replace('image_show', 'image_hide');
viewerOpen = false;
currentIndex = -1;
imagesContainer.classList.add('images_viewer_hidden');
document.body.classList.remove('viewer-open');
viewerOverlay.style.display = 'none';
imageList.forEach(img => img.closest('.image')?.classList.remove('image_selected'));
}
document.getElementById('viewer-close-button').onclick = closeImageViewer;
document.getElementById('viewer-prev-button').onclick = () => currentIndex > 0 && displayImage(--currentIndex);
document.getElementById('viewer-next-button').onclick = () => currentIndex < imageList.length - 1 && displayImage(++currentIndex);
document.addEventListener('click', e => {
if (viewerOpen && !viewerOverlay.contains(e.target) && !e.target.closest('.image')) {
closeImageViewer();
}
});
document.addEventListener('keydown', e => {
if (!viewerOpen) return;
if (e.key === 'Escape') closeImageViewer();
if (e.key === 'ArrowLeft' && currentIndex > 0) displayImage(--currentIndex);
if (e.key === 'ArrowRight' && currentIndex < imageList.length - 1) displayImage(++currentIndex);
});
});