document.addEventListener('DOMContentLoaded', function() { const card = document.getElementById('current-card'); const lastActionText = document.getElementById('last-action'); const leftHint = document.querySelector('.left-hint'); const rightHint = document.querySelector('.right-hint'); const upHint = document.querySelector('.up-hint'); const downHint = document.querySelector('.down-hint'); // Modal elements const modal = document.getElementById('fullscreen-modal'); const fullscreenImage = document.getElementById('fullscreen-image'); const closeModal = document.querySelector('.close-modal'); const modalResolution = document.getElementById('modal-resolution'); const modalFilename = document.getElementById('modal-filename'); const modalCreationDate = document.getElementById('modal-creation-date'); // Current image information let currentImageInfo = null; // Button event listeners document.getElementById('btn-left').addEventListener('click', () => performSwipe('left')); document.getElementById('btn-right').addEventListener('click', () => performSwipe('right')); document.getElementById('btn-up').addEventListener('click', () => performSwipe('up')); document.getElementById('btn-down').addEventListener('click', () => performSwipe('down')); // Touch start time for distinguishing between swipe and tap let touchStartTime = 0; // Touch variables let startX, startY, moveX, moveY; let isDragging = false; const swipeThreshold = 100; // Minimum distance for a swipe to be registered // Touch event handlers card.addEventListener('touchstart', handleTouchStart, false); card.addEventListener('touchmove', handleTouchMove, false); card.addEventListener('touchend', handleTouchEnd, false); // Mouse event handlers (for desktop testing) card.addEventListener('mousedown', handleMouseDown, false); document.addEventListener('mousemove', handleMouseMove, false); document.addEventListener('mouseup', handleMouseUp, false); // Click handler for viewing full-resolution image card.addEventListener('click', handleCardClick); // Close modal when clicking the close button closeModal.addEventListener('click', () => { modal.style.display = 'none'; }); // Close modal when clicking outside the image window.addEventListener('click', (e) => { if (e.target === modal) { modal.style.display = 'none'; } }); // Close modal with escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal.style.display === 'block') { modal.style.display = 'none'; } }); function handleTouchStart(e) { startX = e.touches[0].clientX; startY = e.touches[0].clientY; isDragging = true; card.classList.add('swiping'); // Store touch start time to differentiate between swipe and tap touchStartTime = new Date().getTime(); } function handleTouchMove(e) { if (!isDragging) return; moveX = e.touches[0].clientX - startX; moveY = e.touches[0].clientY - startY; // Apply transform to the card card.style.transform = `translate(${moveX}px, ${moveY}px) rotate(${moveX * 0.1}deg)`; // Show appropriate hint based on direction updateHints(moveX, moveY); } function handleTouchEnd(e) { if (!isDragging) return; // Calculate touch duration const touchEndTime = new Date().getTime(); const touchDuration = touchEndTime - touchStartTime; // Determine if this was a tap (short touch with minimal movement) const absX = Math.abs(moveX || 0); const absY = Math.abs(moveY || 0); const isTap = touchDuration < 300 && Math.max(absX, absY) < 10; isDragging = false; if (isTap) { // This was a tap, not a swipe resetCardPosition(); handleCardClick(e); } else if (Math.max(absX, absY) > swipeThreshold) { // This was a swipe if (absX > absY) { // Horizontal swipe if (moveX > 0) { performSwipe('right'); } else { performSwipe('left'); } } else { // Vertical swipe if (moveY > 0) { performSwipe('down'); } else { performSwipe('up'); } } } else { // Reset card position if swipe wasn't strong enough resetCardPosition(); } // Hide all hints hideAllHints(); } function handleMouseDown(e) { // Store the initial position and set the dragging flag startX = e.clientX; startY = e.clientY; isDragging = true; card.classList.add('swiping'); // Prevent default to avoid text selection during drag e.preventDefault(); } function handleMouseMove(e) { if (!isDragging) return; moveX = e.clientX - startX; moveY = e.clientY - startY; // Apply transform to the card card.style.transform = `translate(${moveX}px, ${moveY}px) rotate(${moveX * 0.1}deg)`; // Show appropriate hint based on direction updateHints(moveX, moveY); } function handleMouseUp(e) { if (!isDragging) return; // Determine if this was a click (minimal movement) or a swipe const absX = Math.abs(moveX || 0); const absY = Math.abs(moveY || 0); isDragging = false; if (Math.max(absX, absY) > swipeThreshold) { if (absX > absY) { // Horizontal swipe if (moveX > 0) { performSwipe('right'); } else { performSwipe('left'); } } else { // Vertical swipe if (moveY > 0) { performSwipe('down'); } else { performSwipe('up'); } } } else { // Reset card position if swipe wasn't strong enough resetCardPosition(); // We don't trigger click here because the card already has a click event listener } // Hide all hints hideAllHints(); } function updateHints(moveX, moveY) { hideAllHints(); const absX = Math.abs(moveX); const absY = Math.abs(moveY); if (absX > absY) { // Horizontal movement is dominant if (moveX > 0) { rightHint.style.opacity = '1'; } else { leftHint.style.opacity = '1'; } } else { // Vertical movement is dominant if (moveY > 0) { downHint.style.opacity = '1'; } else { upHint.style.opacity = '1'; } } } function hideAllHints() { leftHint.style.opacity = '0'; rightHint.style.opacity = '0'; upHint.style.opacity = '0'; downHint.style.opacity = '0'; } function resetCardPosition() { card.classList.remove('swiping'); card.style.transform = ''; } function performSwipe(direction) { // Add the appropriate swipe class card.classList.add(`swipe-${direction}`); // Update the last action text lastActionText.textContent = `Last action: Swiped ${direction}`; // Record the selection in the database if we have a current image if (currentImageInfo) { recordSelection(currentImageInfo, direction); } // After animation completes, reset and load a new image setTimeout(() => { card.classList.remove(`swipe-${direction}`); card.classList.remove('swiping'); card.style.transform = ''; // Load a new random image from our server loadNewImage(); }, 300); } // Function to record a selection in the database function recordSelection(imageInfo, action) { // Create the data to send const data = { path: imageInfo.path, resolution: imageInfo.resolution, action: action }; // Send the data to the server fetch('/record-selection', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => { if (!response.ok) { throw new Error('Failed to record selection'); } return response.json(); }) .then(data => { console.log('Selection recorded:', data); }) .catch(error => { console.error('Error recording selection:', error); }); } // Function to load a new image from our local server function loadNewImage() { // Show loading state const img = card.querySelector('img'); img.style.opacity = '0.5'; // Fetch a random image from our API fetch('/random-image') .then(response => { if (!response.ok) { throw new Error('Failed to fetch image'); } return response.json(); }) .then(data => { // Store current image info currentImageInfo = data; // Extract filename from path const pathParts = data.path.split('/'); const filename = pathParts[pathParts.length - 1]; currentImageInfo.filename = filename; currentImageInfo.creation_date = data.creation_date || 'Unknown'; // Update the image source img.onload = function() { img.style.opacity = '1'; }; img.src = data.path; // Update status with resolution info const statusElement = document.querySelector('.status-area p:first-child'); statusElement.textContent = `Current resolution: ${data.resolution}`; }) .catch(error => { console.error('Error loading image:', error); img.style.opacity = '1'; img.src = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22400%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22400%22%20height%3D%22400%22%20fill%3D%22%23e0e0e0%22%2F%3E%3Ctext%20x%3D%22200%22%20y%3D%22200%22%20font-size%3D%2220%22%20text-anchor%3D%22middle%22%20alignment-baseline%3D%22middle%22%20fill%3D%22%23999%22%3EImage%20not%20found%3C%2Ftext%3E%3C%2Fsvg%3E'; }); } // Function to handle card click for viewing full-resolution image function handleCardClick(e) { // Only process click if we have image info and we're not in the middle of a swipe if (!currentImageInfo || card.classList.contains('swiping')) return; // Prevent click from propagating (important for touch devices) if (e) e.stopPropagation(); // Set the full-resolution image source fullscreenImage.src = currentImageInfo.path; // Update modal info modalResolution.textContent = `Resolution: ${currentImageInfo.resolution}`; modalFilename.textContent = `Filename: ${currentImageInfo.filename || 'Unknown'}`; modalCreationDate.textContent = `Creation Date: ${currentImageInfo.creation_date || 'Unknown'}`; // Display the modal modal.style.display = 'block'; } // Load initial image loadNewImage(); });