/** * Product Discount Countdown Module * * This script initializes countdown timers on the product page. * It handles the initial page load and the dynamic updates that occur * when a customer changes a product combination (attribute). */ // A global array to keep track of our active timer intervals. // This is crucial for cleanup when the product block is updated via AJAX. let productCountdownIntervals = []; /** * Scans the DOM for countdown containers and initializes them. * This function is designed to be called on page load and after AJAX updates. */ function initializeProductCountdowns() { // 1. Clear any previously running timers. // This prevents multiple timers from running after a product combination change. productCountdownIntervals.forEach(intervalId => clearInterval(intervalId)); productCountdownIntervals = []; // 2. Find all countdown containers on the page that need to be processed. const countdownContainers = document.querySelectorAll('.product-countdown-container'); countdownContainers.forEach(container => { // Check if this specific container has already been initialized to avoid race conditions. if (container.dataset.initialized === 'true') { return; } const timerElement = container.querySelector('.countdown-timer'); if (!timerElement) return; // Mark as initialized container.dataset.initialized = 'true'; const targetTimestamp = parseInt(container.dataset.timestamp, 10); const onExpireAction = container.dataset.onExpire; const expiredText = container.dataset.expiredText; // Get translated units from hidden spans const units = { day: container.querySelector('[data-unit="day"]').textContent, days: container.querySelector('[data-unit="days"]').textContent, hr: container.querySelector('[data-unit="hr"]').textContent, min: container.querySelector('[data-unit="min"]').textContent, sec: container.querySelector('[data-unit="sec"]').textContent }; function updateTimer() { const now = Math.floor(new Date().getTime() / 1000); const diff = targetTimestamp - now; if (diff <= 0) { clearInterval(interval); handleExpiry(); return; } const d = Math.floor(diff / (60 * 60 * 24)); const h = Math.floor((diff % (60 * 60 * 24)) / (60 * 60)); const m = Math.floor((diff % (60 * 60)) / 60); const s = Math.floor(diff % 60); const parts = []; if (d > 0) { parts.push(`${d} ${d > 1 ? units.days : units.day}`); } // Always show hours, minutes, and seconds for a better countdown experience parts.push(`${String(h).padStart(2, '0')}${units.hr}`); parts.push(`${String(m).padStart(2, '0')}${units.min}`); parts.push(`${String(s).padStart(2, '0')}${units.sec}`); timerElement.textContent = parts.join(' '); } function handleExpiry() { switch (onExpireAction) { case 'reload': location.reload(); break; case 'message': container.querySelector('.countdown-wrapper').innerHTML = `${expiredText}`; break; case 'hide': default: container.style.display = 'none'; break; } } const interval = setInterval(updateTimer, 1000); // Add the new interval ID to our global array for tracking. productCountdownIntervals.push(interval); updateTimer(); // Initial call to display the timer immediately }); } // --- Event Listeners --- // 1. Run the initializer on the initial page load. document.addEventListener('DOMContentLoaded', () => { initializeProductCountdowns(); }); // 2. Run the initializer whenever PrestaShop updates the product block via AJAX. // The `prestashop` object is globally available in modern themes. if (typeof prestashop !== 'undefined') { prestashop.on('updateProduct', (data) => { // We use a small timeout to ensure the DOM has been fully updated by PrestaShop's scripts // before our script runs and looks for the new container. 100ms is usually safe. setTimeout(() => { initializeProductCountdowns(); }, 100); }); }