PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.79
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.79
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
king-addons / includes / widgets / Woo_Product_Images_Gallery / script.js
includes/widgets/Woo_Product_Images_Gallery
Woo_Product_Images_Gallery.php 19.8 KB 2 weeks ago script.js 12.9 KB 2 weeks ago style.css 7.4 KB 2 weeks ago

script.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.79, at includes/widgets/Woo_Product_Images_Gallery/script.js

329 lines 12.9 KB Raw Download Zip
1 /**
2 * Woo Product Images Gallery widget behavior.
3 *
4 * Provides gallery navigation, optional lightbox, and optional zoom.
5 */
6 (function ($) {
7 "use strict";
8
9 const INIT_FLAG = "kaWooGalleryInit";
10 const KEYUP_FLAG = "kaWooGalleryKeyupBound";
11
12 // Global active lightbox controller (single active at a time).
13 window.kaWooGalleryActive = window.kaWooGalleryActive || null;
14
15 const lockScroll = () => {
16 document.body.classList.add('ka-woo-gallery--lock');
17 };
18
19 const unlockScroll = () => {
20 document.body.classList.remove('ka-woo-gallery--lock');
21 };
22
23 const enableZoom = (slide) => {
24 if (!slide || slide.dataset.zoomBound === 'yes') return;
25 if (window.matchMedia('(hover: none)').matches) return; // avoid on touch-only
26 const full = slide.dataset.full;
27 if (!full) return;
28 const lens = document.createElement('div');
29 lens.className = 'ka-woo-gallery__zoom-lens';
30 lens.style.backgroundImage = `url(${full})`;
31 slide.appendChild(lens);
32 slide.dataset.zoomBound = 'yes';
33
34 const move = (event) => {
35 const rect = slide.getBoundingClientRect();
36 const x = event.clientX - rect.left;
37 const y = event.clientY - rect.top;
38 const xPercent = Math.max(0, Math.min(100, (x / rect.width) * 100));
39 const yPercent = Math.max(0, Math.min(100, (y / rect.height) * 100));
40 lens.style.left = `${x - lens.offsetWidth / 2}px`;
41 lens.style.top = `${y - lens.offsetHeight / 2}px`;
42 lens.style.backgroundPosition = `${xPercent}% ${yPercent}%`;
43 lens.classList.add('is-visible');
44 };
45
46 slide.addEventListener('mousemove', move);
47 slide.addEventListener('mouseenter', () => lens.classList.add('is-visible'));
48 slide.addEventListener('mouseleave', () => lens.classList.remove('is-visible'));
49 };
50
51 const initGallery = (wrapper) => {
52 if (!wrapper || !wrapper.dataset) return;
53 if (wrapper.dataset[INIT_FLAG] === "1") return;
54 wrapper.dataset[INIT_FLAG] = "1";
55
56 const slides = wrapper.querySelectorAll('.ka-woo-gallery__slide');
57 const thumbs = wrapper.querySelectorAll('.ka-woo-gallery__thumb');
58 if (!slides.length) return;
59
60 const layout = wrapper.dataset.layout || 'slider';
61 const mobileLayout = wrapper.dataset.mobileLayout || '';
62 const loop = wrapper.dataset.loop === 'yes';
63 const autoplay = wrapper.dataset.autoplay === 'yes';
64 const speed = parseInt(wrapper.dataset.speed || '4000', 10);
65 const captions = wrapper.dataset.captions === 'yes';
66 const lightboxEnabled = wrapper.dataset.lightbox === 'yes';
67 const zoomEnabled = wrapper.classList.contains('ka-woo-gallery--zoom');
68 const lightboxSkin = wrapper.dataset.lightboxSkin || 'dark';
69 const lightboxCounterEnabled = wrapper.dataset.lightboxCounter === 'yes';
70 const lightboxThumbsEnabled = wrapper.dataset.lightboxThumbs === 'yes';
71 let activeIndex = 0;
72 let timer = null;
73
74 const overlayId = `ka-woo-gallery-lightbox-${Math.random().toString(36).slice(2)}`;
75
76 const lightbox = document.createElement('div');
77 lightbox.className = 'ka-woo-gallery__lightbox';
78 lightbox.id = overlayId;
79 lightbox.innerHTML = `
80 <button type="button" class="ka-woo-gallery__lightbox-close" aria-label="Close">&times;</button>
81 <div class="ka-woo-gallery__lightbox-body">
82 <div class="ka-woo-gallery__lightbox-top">
83 <div class="ka-woo-gallery__lightbox-counter"></div>
84 <div class="ka-woo-gallery__lightbox-nav">
85 <button class="ka-woo-gallery__lb-prev" aria-label="Previous">&#10094;</button>
86 <button class="ka-woo-gallery__lb-next" aria-label="Next">&#10095;</button>
87 </div>
88 </div>
89 <div class="ka-woo-gallery__lightbox-media">
90 <img alt="" />
91 <div class="ka-woo-gallery__lightbox-caption"></div>
92 </div>
93 <div class="ka-woo-gallery__lightbox-thumbs"></div>
94 </div>
95 `;
96 if (lightboxEnabled) {
97 document.body.appendChild(lightbox);
98 lightbox.classList.add('ka-woo-gallery__lightbox--' + lightboxSkin);
99 }
100
101 const lightboxImg = lightbox.querySelector('img');
102 const lightboxCaption = lightbox.querySelector('.ka-woo-gallery__lightbox-caption');
103 const lightboxThumbs = lightbox.querySelector('.ka-woo-gallery__lightbox-thumbs');
104 const lightboxCounter = lightbox.querySelector('.ka-woo-gallery__lightbox-counter');
105 const lbPrev = lightbox.querySelector('.ka-woo-gallery__lb-prev');
106 const lbNext = lightbox.querySelector('.ka-woo-gallery__lb-next');
107 const lbClose = lightbox.querySelector('.ka-woo-gallery__lightbox-close');
108 const lightboxThumbButtons = [];
109
110 const updateCounter = () => {
111 if (!lightboxCounterEnabled || !lightboxCounter) return;
112 lightboxCounter.textContent = `${activeIndex + 1} / ${slides.length}`;
113 };
114
115 const syncLightboxThumbs = () => {
116 if (!lightboxThumbsEnabled || !lightboxThumbButtons.length) return;
117 lightboxThumbButtons.forEach((btn, i) => btn.classList.toggle('is-active', i === activeIndex));
118 };
119
120 const setActive = (index) => {
121 activeIndex = index;
122 slides.forEach((slide, i) => {
123 const isGrid = layout === 'grid' || layout === 'masonry' || (mobileLayout === 'grid' && window.matchMedia('(max-width: 767px)').matches);
124 slide.style.display = i === activeIndex || isGrid ? 'block' : 'none';
125 });
126 thumbs.forEach((thumb, i) => {
127 thumb.classList.toggle('is-active', i === activeIndex);
128 });
129 dots.forEach((dot, i) => {
130 dot.classList.toggle('is-active', i === activeIndex);
131 });
132 if (lightboxEnabled && captions && lightboxCaption) {
133 const cap = slides[activeIndex]?.dataset.caption || '';
134 lightboxCaption.textContent = cap;
135 lightboxCaption.style.display = cap ? 'block' : 'none';
136 }
137 updateCounter();
138 syncLightboxThumbs();
139 if (zoomEnabled) {
140 enableZoom(slides[activeIndex]);
141 }
142 };
143
144 thumbs.forEach((thumb, index) => {
145 thumb.addEventListener('click', () => setActive(index));
146 });
147
148 const prev = wrapper.querySelector('.ka-woo-gallery__arrow--prev');
149 const next = wrapper.querySelector('.ka-woo-gallery__arrow--next');
150 const dots = wrapper.querySelectorAll('.ka-woo-gallery__dot');
151
152 const goTo = (delta) => {
153 const total = slides.length;
154 let target = activeIndex + delta;
155 if (loop) {
156 target = (target + total) % total;
157 } else {
158 target = Math.min(Math.max(target, 0), total - 1);
159 }
160 setActive(target);
161 };
162
163 if (prev) prev.addEventListener('click', () => goTo(-1));
164 if (next) next.addEventListener('click', () => goTo(1));
165 dots.forEach((dot, idx) => dot.addEventListener('click', () => setActive(idx)));
166
167 // Lightbox
168 const openLightbox = (index) => {
169 if (!lightboxEnabled || !lightboxImg) return;
170 const slide = slides[index];
171 if (!slide) return;
172 const src = slide.dataset.full || '';
173 if (!src) return;
174 activeIndex = index;
175 lightboxImg.src = src;
176 if (captions && lightboxCaption) {
177 const cap = slide.dataset.caption || '';
178 lightboxCaption.textContent = cap;
179 lightboxCaption.style.display = cap ? 'block' : 'none';
180 }
181 updateCounter();
182 syncLightboxThumbs();
183 lightbox.classList.add('is-open');
184 lockScroll();
185
186 // Set global active controller
187 window.kaWooGalleryActive = {
188 isOpen: () => lightbox.classList.contains("is-open"),
189 close: closeLightbox,
190 prev: () => {
191 goTo(-1);
192 openLightbox(activeIndex);
193 },
194 next: () => {
195 goTo(1);
196 openLightbox(activeIndex);
197 },
198 };
199 };
200
201 const closeLightbox = () => {
202 lightbox.classList.remove('is-open');
203 unlockScroll();
204 if (window.kaWooGalleryActive && window.kaWooGalleryActive.close === closeLightbox) {
205 window.kaWooGalleryActive = null;
206 }
207 };
208
209 if (lightboxEnabled) {
210 lightbox.addEventListener('click', (event) => {
211 const isBody = event.target.classList.contains('ka-woo-gallery__lightbox');
212 const isClose = event.target === lbClose;
213 if (isBody || isClose) {
214 closeLightbox();
215 }
216 });
217 lightbox.querySelector('.ka-woo-gallery__lightbox-body')?.addEventListener('click', (e) => e.stopPropagation());
218
219 if (lightboxThumbsEnabled && lightboxThumbs) {
220 slides.forEach((slide, idx) => {
221 const imgEl = slide.querySelector('img');
222 const src = slide.dataset.full || imgEl?.src || '';
223 const btn = document.createElement('button');
224 btn.type = 'button';
225 btn.className = 'ka-woo-gallery__lightbox-thumb';
226 btn.innerHTML = `<span style="background-image:url('${src}')"></span>`;
227 btn.addEventListener('click', (e) => {
228 e.stopPropagation();
229 setActive(idx);
230 openLightbox(idx);
231 });
232 lightboxThumbButtons.push(btn);
233 lightboxThumbs.appendChild(btn);
234 });
235 }
236
237 if (!lightboxCounterEnabled && lightboxCounter) {
238 lightboxCounter.style.display = 'none';
239 }
240
241 if (!lightboxThumbsEnabled && lightboxThumbs) {
242 lightboxThumbs.style.display = 'none';
243 }
244 }
245
246 slides.forEach((slide, idx) => {
247 slide.addEventListener('click', () => {
248 if (!lightboxEnabled) return;
249 openLightbox(idx);
250 });
251 if (zoomEnabled) {
252 enableZoom(slide);
253 }
254 });
255
256 if (lbPrev) lbPrev.addEventListener('click', (e) => { e.stopPropagation(); goTo(-1); openLightbox(activeIndex); });
257 if (lbNext) lbNext.addEventListener('click', (e) => { e.stopPropagation(); goTo(1); openLightbox(activeIndex); });
258
259 // Global key handler is bound once for all instances below.
260
261 // Autoplay
262 const startAutoplay = () => {
263 if (!autoplay || slides.length <= 1) return;
264 stopAutoplay();
265 timer = setInterval(() => {
266 const nextIndex = loop ? (activeIndex + 1) % slides.length : Math.min(activeIndex + 1, slides.length - 1);
267 setActive(nextIndex);
268 }, speed > 500 ? speed : 4000);
269 };
270 const stopAutoplay = () => {
271 if (timer) {
272 clearInterval(timer);
273 timer = null;
274 }
275 };
276 wrapper.addEventListener('mouseenter', stopAutoplay);
277 wrapper.addEventListener('mouseleave', startAutoplay);
278
279 setActive(0);
280 startAutoplay();
281 };
282
283 const bindGlobalKeyupOnce = () => {
284 if (window[KEYUP_FLAG]) {
285 return;
286 }
287 window[KEYUP_FLAG] = true;
288
289 document.addEventListener("keyup", (e) => {
290 const active = window.kaWooGalleryActive;
291 if (!active || typeof active.isOpen !== "function" || !active.isOpen()) {
292 return;
293 }
294 if (e.key === "Escape") {
295 active.close();
296 }
297 if (e.key === "ArrowLeft") {
298 active.prev();
299 }
300 if (e.key === "ArrowRight") {
301 active.next();
302 }
303 });
304 };
305
306 const initAll = (root) => {
307 const ctx = root && root.querySelectorAll ? root : document;
308 bindGlobalKeyupOnce();
309 ctx.querySelectorAll(".ka-woo-gallery").forEach(initGallery);
310 };
311
312 document.addEventListener("DOMContentLoaded", () => initAll(document));
313
314 $(window).on("elementor/frontend/init", function () {
315 elementorFrontend.hooks.addAction(
316 "frontend/element_ready/woo_product_images_gallery.default",
317 function ($scope) {
318 initAll($scope && $scope[0] ? $scope[0] : document);
319 }
320 );
321 });
322 })(jQuery);
323
324
325
326
327
328
329