// Initialize dataLayer for Google Tag Manager window.dataLayer = window.dataLayer || []; // Push virtual page view event on page load window.dataLayer.push({ 'event': 'virtualPageview', 'pagePath': window.location.pathname + window.location.search, 'pageTitle': document.title }); // --- Product Carousel Logic --- document.addEventListener('DOMContentLoaded', function initProductCarousel() { const carouselMain = document.querySelector('.carousel-main'); if (!carouselMain) return; const mainImg = carouselMain.querySelector('img'); const thumbs = Array.from(document.querySelectorAll('.carousel-thumbnails .product-img-small')); const leftArrow = document.querySelector('.carousel-arrow.left'); const rightArrow = document.querySelector('.carousel-arrow.right'); if (!mainImg || thumbs.length === 0) return; let current = thumbs.findIndex(t => t.classList.contains('selected')); if (current === -1) current = 0; let isAnimating = false; function updateCarousel(idx, smooth = true) { if (isAnimating || thumbs.length === 0) return; // Infinite loop logic if (idx < 0) idx = thumbs.length - 1; if (idx >= thumbs.length) idx = 0; // Animate transition if (smooth) { isAnimating = true; mainImg.style.opacity = '0.5'; } // Update selected thumbnail thumbs.forEach(t => t.classList.remove('selected')); thumbs[idx].classList.add('selected'); // Update main image const newSrc = thumbs[idx].querySelector('img').src; if (smooth) { setTimeout(() => { mainImg.src = newSrc; mainImg.style.opacity = '1'; isAnimating = false; }, 150); } else { mainImg.src = newSrc; } current = idx; // Scroll thumbnail into view (centered) const thumbContainer = document.querySelector('.carousel-thumbnails'); if (thumbContainer) { const thumbElement = thumbs[idx]; const containerRect = thumbContainer.getBoundingClientRect(); const thumbRect = thumbElement.getBoundingClientRect(); const scrollLeft = thumbElement.offsetLeft - (containerRect.width / 2) + (thumbRect.width / 2); thumbContainer.scrollTo({ left: scrollLeft, behavior: 'smooth' }); } } // Arrow click handlers if (leftArrow) { leftArrow.addEventListener('click', (e) => { e.preventDefault(); updateCarousel(current - 1); }); } if (rightArrow) { rightArrow.addEventListener('click', (e) => { e.preventDefault(); updateCarousel(current + 1); }); } // Thumbnail click handlers thumbs.forEach((thumb, idx) => { thumb.addEventListener('click', () => updateCarousel(idx)); }); // Swipe support for touch devices let touchStartX = null; carouselMain.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; }, { passive: true }); carouselMain.addEventListener('touchend', (e) => { if (touchStartX === null) return; const endX = e.changedTouches[0].clientX; const diffX = endX - touchStartX; // Require minimum swipe distance if (Math.abs(diffX) > 50) { if (diffX > 0) { updateCarousel(current - 1); // Swipe right = previous } else { updateCarousel(current + 1); // Swipe left = next } } touchStartX = null; }, { passive: true }); // Keyboard navigation document.addEventListener('keydown', (e) => { // Only if carousel is visible in viewport const rect = carouselMain.getBoundingClientRect(); const inViewport = rect.top < window.innerHeight && rect.bottom > 0; if (!inViewport) return; if (e.key === 'ArrowLeft') { e.preventDefault(); updateCarousel(current - 1); } else if (e.key === 'ArrowRight') { e.preventDefault(); updateCarousel(current + 1); } }); // Initialize first thumbnail as selected updateCarousel(current, false); }); // --- Sticky Add to Basket Button --- (function handleStickyButton() { const btn = document.querySelector('.add-to-basket-sticky'); if (!btn) return; // Store original position after page load let originalBottom = null; let isInitialized = false; function initPosition() { const rect = btn.getBoundingClientRect(); originalBottom = window.scrollY + rect.bottom; isInitialized = true; checkSticky(); } function checkSticky() { if (!isInitialized) return; // On tablet/mobile (992px and below), always keep sticky if (window.innerWidth <= 992) { btn.classList.add('is-sticky'); return; } // On desktop: sticky while user hasn't scrolled past the button's original position const viewportBottom = window.scrollY + window.innerHeight; if (viewportBottom < originalBottom) { // User hasn't scrolled enough to see the button naturally btn.classList.add('is-sticky'); } else { // User has scrolled past where the button would naturally be btn.classList.remove('is-sticky'); } } function handleResize() { // Recalculate position on resize btn.classList.remove('is-sticky'); setTimeout(() => { initPosition(); }, 100); } // Initialize after DOM is ready and images are loaded if (document.readyState === 'complete') { initPosition(); } else { window.addEventListener('load', initPosition); } window.addEventListener('scroll', checkSticky, { passive: true }); window.addEventListener('resize', handleResize, { passive: true }); })(); if (document.querySelector('.product #product-form')) { let updatePrice = () => { let price = parseFloat(document.querySelector('.product .price').dataset.price); let rrp = document.querySelector('.product .rrp') ?? 0; if (rrp !=0) { rrp = parseFloat(document.querySelector('.product .rrp').dataset.rrp) ?? 0; } document.querySelectorAll('.product #product-form .option').forEach(e => { if (e.value) { let optionPrice = e.classList.contains('text') || e.classList.contains('datetime') ? e.dataset.price : 0.00; optionPrice = e.classList.contains('select') ? e.options[e.selectedIndex].dataset.price : optionPrice; optionPrice = (e.classList.contains('radio') || e.classList.contains('checkbox')) && e.checked ? e.dataset.price : optionPrice; price = (e.classList.contains('select') ? e.options[e.selectedIndex].dataset.modifier : e.dataset.modifier) == 1 ? price+parseFloat(optionPrice) : price-parseFloat(optionPrice); let optionRRP = e.classList.contains('text') || e.classList.contains('datetime') ? e.dataset.rrp : 0.00; optionRRP = e.classList.contains('select') ? e.options[e.selectedIndex].dataset.rrp : optionRRP; optionRRP = (e.classList.contains('radio') || e.classList.contains('checkbox')) && e.checked ? e.dataset.rrp : optionRRP; rrp = (e.classList.contains('select') ? e.options[e.selectedIndex].dataset.modifier : e.dataset.modifier) == 1 ? rrp+parseFloat(optionRRP) : rrp-parseFloat(optionRRP); } }); document.querySelector('.product .price').innerHTML = currency_code + (price > 0.00 ? price.toFixed(2) : 0.00); if (rrp !=0) { document.querySelector('.product .rrp').innerHTML = currency_code + (rrp > 0.00 ? rrp.toFixed(2) : 0.00); } }; document.querySelectorAll('.product #product-form .option').forEach(ele => ele.onchange = () => updatePrice()); updatePrice(); // Add to cart event document.querySelector('.product #product-form').addEventListener('submit', function(e) { e.preventDefault(); // Prevent immediate submission let productId = document.querySelector('input[name="product[product]"]').value; let quantity = parseInt(document.querySelector('input[name="product[quantity]"]').value); let productName = document.querySelector('.product .name').textContent; let price = parseFloat(document.querySelector('.product .price').dataset.price); window.dataLayer.push({ 'event': 'addToCart', 'ecommerce': { 'add': { 'products': [{ 'id': productId, 'name': productName, 'price': price, 'quantity': quantity }] } }, 'content_type': 'product', 'content_ids': [productId], 'content_name': productName, 'value': price * quantity, 'currency': 'EUR' }); // Submit form after a short delay setTimeout(() => { e.target.submit(); }, 100); }); } if (document.querySelector('.products-form')) { let products_form_submit = () => { document.querySelector('.products-form') if (rewrite_url) { window.location.href = encodeURI(base_url + 'products/' + document.querySelector('.category select').value + '/' + document.querySelector('.sortby select').value); } else { window.location.href = encodeURI(base_url + 'index.php?page=products&category=' + document.querySelector('.category select').value + '&sort=' + document.querySelector('.sortby select').value); } }; document.querySelector('.sortby select').onchange = () => products_form_submit(); document.querySelector('.category select').onchange = () => products_form_submit(); } if (document.querySelector('.cart .ajax-update')) { document.querySelectorAll('.cart .ajax-update').forEach(ele => { ele.onchange = () => { let formEle = document.querySelector('.cart form'); let formData = new FormData(formEle); formData.append('update', 'Update'); console.log(formData); fetch(formEle.action, { method: 'POST', body: formData }).then(response => response.text()).then(html => { let doc = (new DOMParser()).parseFromString(html, 'text/html'); document.querySelector('.total').innerHTML = doc.querySelector('.total').innerHTML; document.querySelectorAll('.product-total').forEach((e,i) => { e.innerHTML = doc.querySelectorAll('.product-total')[i].innerHTML; }); // Push cart update event window.dataLayer.push({ 'event': 'updateCart' }); }); }; }); } const checkoutHandler = () => { if (document.querySelector('.checkout .ajax-update')) { document.querySelectorAll('.checkout .ajax-update').forEach(ele => { ele.onchange = () => { let formEle = document.querySelector('.checkout form'); let formData = new FormData(formEle); formData.append('update', 'Update'); fetch(formEle.action, { method: 'POST', body: formData }).then(response => response.text()).then(html => { let doc = (new DOMParser()).parseFromString(html, 'text/html'); document.querySelector('.summary').innerHTML = doc.querySelector('.summary').innerHTML; document.querySelector('.total').innerHTML = doc.querySelector('.total').innerHTML; document.querySelector('.discount-code .result').innerHTML = doc.querySelector('.discount-code .result').innerHTML; document.querySelector('.shipping-methods-container').innerHTML = doc.querySelector('.shipping-methods-container').innerHTML; // Push checkout update event window.dataLayer.push({ 'event': 'checkoutUpdate' }); checkoutHandler(); }); }; if (ele.name == 'discount_code') { ele.onkeydown = event => { if (event.key == 'Enter') { event.preventDefault(); ele.onchange(); } }; } }); } }; checkoutHandler(); function openMenu(div){ let nav_display = document.querySelector(div).style.display; document.querySelector(div).style.display = nav_display == 'block' ? 'none' : 'block'; } function update(id_large, IMG_large, option_id, price){ let url_id_a = id_large + 'A'; let url_id_b = id_large + 'B'; let url_id_c = id_large + 'C'; //change picture document.getElementById(id_large).src = IMG_large; document.getElementById(url_id_a).href = option_id; document.getElementById(url_id_b).href = option_id; document.getElementById(url_id_c).innerHTML = price; } function updateOption(id_large, IMG_large){ //change picture document.getElementById(id_large).src = IMG_large; // Update the first thumbnail to match the new main image let firstThumbnail = document.querySelector('.product-img-small img'); if (firstThumbnail) { firstThumbnail.src = IMG_large; firstThumbnail.parentElement.classList.add('selected'); } } // Function to change the title and flag when a language is selected function changeLanguage(language, langCode, flagUrl) { const title = document.getElementById("title"); title.src = flagUrl; title.alt = language+" Flag"; } // Enhanced slider functionality for multiple carousels function initializeCarousels() { // Handle product sliders (highlightedProducts2) const productSliders = document.querySelectorAll('.product-slider'); productSliders.forEach((slider) => { const carouselId = slider.getAttribute('data-carousel'); const prevBtn = slider.querySelector('.prev-btn'); const nextBtn = slider.querySelector('.next-btn'); const productContainer = slider.querySelector('.product-container-slider'); const products = slider.querySelectorAll('.product-card-slider'); if (!products.length) return; let currentIndex = 0; const productsPerView = window.innerWidth < 480 ? 1 : window.innerWidth < 768 ? 2 : window.innerWidth < 992 ? 3 : 4; function updateSliderPosition() { const productWidth = products[0].offsetWidth; productContainer.style.transform = `translateX(-${currentIndex * productWidth}px)`; } prevBtn.addEventListener('click', () => { if (currentIndex > 0) { currentIndex--; updateSliderPosition(); } }); nextBtn.addEventListener('click', () => { if (currentIndex < products.length - productsPerView) { currentIndex++; updateSliderPosition(); } }); // Update slider on window resize window.addEventListener('resize', () => { // Reset position when screen size changes currentIndex = 0; updateSliderPosition(); }); }); // Handle sample sliders (getSamples) const sampleButtons = document.querySelectorAll('.scrollButton'); sampleButtons.forEach((button) => { const samplesId = button.getAttribute('data-samples'); if (!samplesId) return; const samplesContainer = document.getElementById('add_samples_container_' + samplesId); if (!samplesContainer) return; const isLeftButton = button.id.includes('slideLeft'); button.addEventListener('click', () => { const scrollAmount = 200; // Adjust as needed const currentScroll = samplesContainer.scrollLeft; if (isLeftButton) { samplesContainer.scrollTo({ left: currentScroll - scrollAmount, behavior: 'smooth' }); } else { samplesContainer.scrollTo({ left: currentScroll + scrollAmount, behavior: 'smooth' }); } }); }); } // Only initialize carousels once DOM is loaded // (No duplicate or stray HTML allowed)