/**
* Woo Product Images Gallery widget behavior.
*
* Provides gallery navigation, optional lightbox, and optional zoom.
*/
(function ($) {
"use strict";
const INIT_FLAG = "kaWooGalleryInit";
const KEYUP_FLAG = "kaWooGalleryKeyupBound";
// Global active lightbox controller (single active at a time).
window.kaWooGalleryActive = window.kaWooGalleryActive || null;
const lockScroll = () => {
document.body.classList.add('ka-woo-gallery--lock');
};
const unlockScroll = () => {
document.body.classList.remove('ka-woo-gallery--lock');
};
const enableZoom = (slide) => {
if (!slide || slide.dataset.zoomBound === 'yes') return;
if (window.matchMedia('(hover: none)').matches) return; // avoid on touch-only
const full = slide.dataset.full;
if (!full) return;
const lens = document.createElement('div');
lens.className = 'ka-woo-gallery__zoom-lens';
lens.style.backgroundImage = `url(${full})`;
slide.appendChild(lens);
slide.dataset.zoomBound = 'yes';
const move = (event) => {
const rect = slide.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const xPercent = Math.max(0, Math.min(100, (x / rect.width) * 100));
const yPercent = Math.max(0, Math.min(100, (y / rect.height) * 100));
lens.style.left = `${x - lens.offsetWidth / 2}px`;
lens.style.top = `${y - lens.offsetHeight / 2}px`;
lens.style.backgroundPosition = `${xPercent}% ${yPercent}%`;
lens.classList.add('is-visible');
};
slide.addEventListener('mousemove', move);
slide.addEventListener('mouseenter', () => lens.classList.add('is-visible'));
slide.addEventListener('mouseleave', () => lens.classList.remove('is-visible'));
};
const initGallery = (wrapper) => {
if (!wrapper || !wrapper.dataset) return;
if (wrapper.dataset[INIT_FLAG] === "1") return;
wrapper.dataset[INIT_FLAG] = "1";
const slides = wrapper.querySelectorAll('.ka-woo-gallery__slide');
const thumbs = wrapper.querySelectorAll('.ka-woo-gallery__thumb');
if (!slides.length) return;
const layout = wrapper.dataset.layout || 'slider';
const mobileLayout = wrapper.dataset.mobileLayout || '';
const loop = wrapper.dataset.loop === 'yes';
const autoplay = wrapper.dataset.autoplay === 'yes';
const speed = parseInt(wrapper.dataset.speed || '4000', 10);
const captions = wrapper.dataset.captions === 'yes';
const lightboxEnabled = wrapper.dataset.lightbox === 'yes';
const zoomEnabled = wrapper.classList.contains('ka-woo-gallery--zoom');
const lightboxSkin = wrapper.dataset.lightboxSkin || 'dark';
const lightboxCounterEnabled = wrapper.dataset.lightboxCounter === 'yes';
const lightboxThumbsEnabled = wrapper.dataset.lightboxThumbs === 'yes';
let activeIndex = 0;
let timer = null;
const overlayId = `ka-woo-gallery-lightbox-${Math.random().toString(36).slice(2)}`;
const lightbox = document.createElement('div');
lightbox.className = 'ka-woo-gallery__lightbox';
lightbox.id = overlayId;
lightbox.innerHTML = `
`;
if (lightboxEnabled) {
document.body.appendChild(lightbox);
lightbox.classList.add('ka-woo-gallery__lightbox--' + lightboxSkin);
}
const lightboxImg = lightbox.querySelector('img');
const lightboxCaption = lightbox.querySelector('.ka-woo-gallery__lightbox-caption');
const lightboxThumbs = lightbox.querySelector('.ka-woo-gallery__lightbox-thumbs');
const lightboxCounter = lightbox.querySelector('.ka-woo-gallery__lightbox-counter');
const lbPrev = lightbox.querySelector('.ka-woo-gallery__lb-prev');
const lbNext = lightbox.querySelector('.ka-woo-gallery__lb-next');
const lbClose = lightbox.querySelector('.ka-woo-gallery__lightbox-close');
const lightboxThumbButtons = [];
const updateCounter = () => {
if (!lightboxCounterEnabled || !lightboxCounter) return;
lightboxCounter.textContent = `${activeIndex + 1} / ${slides.length}`;
};
const syncLightboxThumbs = () => {
if (!lightboxThumbsEnabled || !lightboxThumbButtons.length) return;
lightboxThumbButtons.forEach((btn, i) => btn.classList.toggle('is-active', i === activeIndex));
};
const setActive = (index) => {
activeIndex = index;
slides.forEach((slide, i) => {
const isGrid = layout === 'grid' || layout === 'masonry' || (mobileLayout === 'grid' && window.matchMedia('(max-width: 767px)').matches);
slide.style.display = i === activeIndex || isGrid ? 'block' : 'none';
});
thumbs.forEach((thumb, i) => {
thumb.classList.toggle('is-active', i === activeIndex);
});
dots.forEach((dot, i) => {
dot.classList.toggle('is-active', i === activeIndex);
});
if (lightboxEnabled && captions && lightboxCaption) {
const cap = slides[activeIndex]?.dataset.caption || '';
lightboxCaption.textContent = cap;
lightboxCaption.style.display = cap ? 'block' : 'none';
}
updateCounter();
syncLightboxThumbs();
if (zoomEnabled) {
enableZoom(slides[activeIndex]);
}
};
thumbs.forEach((thumb, index) => {
thumb.addEventListener('click', () => setActive(index));
});
const prev = wrapper.querySelector('.ka-woo-gallery__arrow--prev');
const next = wrapper.querySelector('.ka-woo-gallery__arrow--next');
const dots = wrapper.querySelectorAll('.ka-woo-gallery__dot');
const goTo = (delta) => {
const total = slides.length;
let target = activeIndex + delta;
if (loop) {
target = (target + total) % total;
} else {
target = Math.min(Math.max(target, 0), total - 1);
}
setActive(target);
};
if (prev) prev.addEventListener('click', () => goTo(-1));
if (next) next.addEventListener('click', () => goTo(1));
dots.forEach((dot, idx) => dot.addEventListener('click', () => setActive(idx)));
// Lightbox
const openLightbox = (index) => {
if (!lightboxEnabled || !lightboxImg) return;
const slide = slides[index];
if (!slide) return;
const src = slide.dataset.full || '';
if (!src) return;
activeIndex = index;
lightboxImg.src = src;
if (captions && lightboxCaption) {
const cap = slide.dataset.caption || '';
lightboxCaption.textContent = cap;
lightboxCaption.style.display = cap ? 'block' : 'none';
}
updateCounter();
syncLightboxThumbs();
lightbox.classList.add('is-open');
lockScroll();
// Set global active controller
window.kaWooGalleryActive = {
isOpen: () => lightbox.classList.contains("is-open"),
close: closeLightbox,
prev: () => {
goTo(-1);
openLightbox(activeIndex);
},
next: () => {
goTo(1);
openLightbox(activeIndex);
},
};
};
const closeLightbox = () => {
lightbox.classList.remove('is-open');
unlockScroll();
if (window.kaWooGalleryActive && window.kaWooGalleryActive.close === closeLightbox) {
window.kaWooGalleryActive = null;
}
};
if (lightboxEnabled) {
lightbox.addEventListener('click', (event) => {
const isBody = event.target.classList.contains('ka-woo-gallery__lightbox');
const isClose = event.target === lbClose;
if (isBody || isClose) {
closeLightbox();
}
});
lightbox.querySelector('.ka-woo-gallery__lightbox-body')?.addEventListener('click', (e) => e.stopPropagation());
if (lightboxThumbsEnabled && lightboxThumbs) {
slides.forEach((slide, idx) => {
const imgEl = slide.querySelector('img');
const src = slide.dataset.full || imgEl?.src || '';
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'ka-woo-gallery__lightbox-thumb';
btn.innerHTML = ``;
btn.addEventListener('click', (e) => {
e.stopPropagation();
setActive(idx);
openLightbox(idx);
});
lightboxThumbButtons.push(btn);
lightboxThumbs.appendChild(btn);
});
}
if (!lightboxCounterEnabled && lightboxCounter) {
lightboxCounter.style.display = 'none';
}
if (!lightboxThumbsEnabled && lightboxThumbs) {
lightboxThumbs.style.display = 'none';
}
}
slides.forEach((slide, idx) => {
slide.addEventListener('click', () => {
if (!lightboxEnabled) return;
openLightbox(idx);
});
if (zoomEnabled) {
enableZoom(slide);
}
});
if (lbPrev) lbPrev.addEventListener('click', (e) => { e.stopPropagation(); goTo(-1); openLightbox(activeIndex); });
if (lbNext) lbNext.addEventListener('click', (e) => { e.stopPropagation(); goTo(1); openLightbox(activeIndex); });
// Global key handler is bound once for all instances below.
// Autoplay
const startAutoplay = () => {
if (!autoplay || slides.length <= 1) return;
stopAutoplay();
timer = setInterval(() => {
const nextIndex = loop ? (activeIndex + 1) % slides.length : Math.min(activeIndex + 1, slides.length - 1);
setActive(nextIndex);
}, speed > 500 ? speed : 4000);
};
const stopAutoplay = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
};
wrapper.addEventListener('mouseenter', stopAutoplay);
wrapper.addEventListener('mouseleave', startAutoplay);
setActive(0);
startAutoplay();
};
const bindGlobalKeyupOnce = () => {
if (window[KEYUP_FLAG]) {
return;
}
window[KEYUP_FLAG] = true;
document.addEventListener("keyup", (e) => {
const active = window.kaWooGalleryActive;
if (!active || typeof active.isOpen !== "function" || !active.isOpen()) {
return;
}
if (e.key === "Escape") {
active.close();
}
if (e.key === "ArrowLeft") {
active.prev();
}
if (e.key === "ArrowRight") {
active.next();
}
});
};
const initAll = (root) => {
const ctx = root && root.querySelectorAll ? root : document;
bindGlobalKeyupOnce();
ctx.querySelectorAll(".ka-woo-gallery").forEach(initGallery);
};
document.addEventListener("DOMContentLoaded", () => initAll(document));
$(window).on("elementor/frontend/init", function () {
elementorFrontend.hooks.addAction(
"frontend/element_ready/woo_product_images_gallery.default",
function ($scope) {
initAll($scope && $scope[0] ? $scope[0] : document);
}
);
});
})(jQuery);