PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.63
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.63
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_Products_Grid / script.js

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

525 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 const setGridVars = (grid) => {
5 grid.style.setProperty('--ka-grid-desktop', grid.dataset.colsDesktop || 4);
6 grid.style.setProperty('--ka-grid-tablet', grid.dataset.colsTablet || 3);
7 grid.style.setProperty('--ka-grid-mobile', grid.dataset.colsMobile || 2);
8 grid.style.setProperty('--ka-masonry-row', '8px');
9 };
10
11 const appendItems = (grid, html, replace = false) => {
12 const track = grid.querySelector('.ka-woo-products-grid__track') || grid;
13 const temp = document.createElement('div');
14 temp.innerHTML = html;
15 const items = temp.querySelectorAll('.ka-woo-products-grid__item');
16 if (replace) {
17 track.innerHTML = '';
18 }
19 items.forEach((item) => track.appendChild(item));
20 return items.length;
21 };
22
23 const reflowMasonry = (grid) => {
24 if (grid.dataset.layoutType !== 'masonry') return;
25 const rowHeight = parseFloat(getComputedStyle(grid).getPropertyValue('--ka-masonry-row')) || 8;
26 const items = grid.querySelectorAll('.ka-woo-products-grid__item');
27 items.forEach((item) => {
28 const span = Math.ceil(item.getBoundingClientRect().height / rowHeight);
29 item.style.gridRowEnd = `span ${span}`;
30 });
31 };
32
33 const setupMasonry = (grid) => {
34 if (grid.dataset.layoutType !== 'masonry') return;
35 if (typeof ResizeObserver === 'undefined') return;
36 const resizeObserver = new ResizeObserver(() => reflowMasonry(grid));
37 resizeObserver.observe(grid);
38 (grid.querySelector('.ka-woo-products-grid__track') || grid).querySelectorAll('img').forEach((img) => {
39 if (img.complete) {
40 reflowMasonry(grid);
41 } else {
42 img.addEventListener('load', () => reflowMasonry(grid));
43 }
44 });
45 reflowMasonry(grid);
46 };
47
48 const setupSlider = (grid) => {
49 if (grid.dataset.layoutType !== 'slider') return;
50 const parent = grid.parentElement;
51 if (!parent) return;
52 const track = grid.querySelector('.ka-woo-products-grid__track') || grid;
53 const existingNav = parent.querySelector('.ka-woo-products-grid__slider-nav');
54 if (existingNav) {
55 existingNav.remove();
56 }
57 const slides = Array.from(track.children);
58 if (!slides.length) return;
59
60 grid.classList.add('ka-woo-products-grid--slider');
61 slides.forEach((slide) => {
62 slide.classList.add('ka-woo-products-grid__slide');
63 });
64
65 let index = 0;
66 const loop = grid.dataset.sliderLoop === 'true';
67 const autoplay = grid.dataset.sliderAutoplay === 'true';
68 const speed = parseInt(grid.dataset.sliderAutoplaySpeed || '5000', 10);
69 const skin = grid.dataset.sliderSkin || 'arrows';
70 let timer;
71 let isDragging = false;
72 let startX = 0;
73 let currentX = 0;
74
75 const dots = [];
76
77 const update = () => {
78 const offset = index * 100;
79 track.style.transform = `translateX(-${offset}%)`;
80 dots.forEach((dot, idx) => {
81 dot.classList.toggle('is-active', idx === index);
82 });
83 };
84
85 const go = (dir) => {
86 const last = slides.length - 1;
87 let next = index + dir;
88 if (next > last) {
89 next = loop ? 0 : last;
90 } else if (next < 0) {
91 next = loop ? last : 0;
92 }
93 if (next === index && !loop) return;
94 index = next;
95 update();
96 };
97
98 const nav = document.createElement('div');
99 nav.className = 'ka-woo-products-grid__slider-nav';
100 const dotsWrap = document.createElement('div');
101 dotsWrap.className = 'ka-woo-products-grid__slider-dots';
102 slides.forEach((_, idx) => {
103 const dot = document.createElement('button');
104 dot.type = 'button';
105 dot.className = 'ka-woo-products-grid__slider-dot';
106 dot.addEventListener('click', () => {
107 index = idx;
108 update();
109 resetAutoplay();
110 });
111 dotsWrap.appendChild(dot);
112 dots.push(dot);
113 });
114
115 const controls = document.createElement('div');
116 controls.className = 'ka-woo-products-grid__slider-controls';
117 const prev = document.createElement('button');
118 prev.type = 'button';
119 prev.className = 'ka-woo-products-grid__slider-btn ka-woo-products-grid__slider-btn--prev';
120 prev.textContent = '‹';
121 prev.addEventListener('click', () => {
122 go(-1);
123 resetAutoplay();
124 });
125 const next = document.createElement('button');
126 next.type = 'button';
127 next.className = 'ka-woo-products-grid__slider-btn ka-woo-products-grid__slider-btn--next';
128 next.textContent = '›';
129 next.addEventListener('click', () => {
130 go(1);
131 resetAutoplay();
132 });
133
134 if (skin === 'arrows' || skin === 'both') {
135 controls.appendChild(prev);
136 controls.appendChild(next);
137 }
138 if (skin === 'dots' || skin === 'both') {
139 nav.appendChild(dotsWrap);
140 }
141 if (controls.children.length) {
142 nav.appendChild(controls);
143 }
144 parent.appendChild(nav);
145
146 const stopAutoplay = () => {
147 if (timer) {
148 window.clearInterval(timer);
149 timer = undefined;
150 }
151 };
152 const startAutoplay = () => {
153 if (!autoplay) return;
154 stopAutoplay();
155 timer = window.setInterval(() => go(1), speed);
156 };
157 const resetAutoplay = () => {
158 stopAutoplay();
159 startAutoplay();
160 };
161
162 grid.addEventListener('mouseenter', stopAutoplay);
163 grid.addEventListener('mouseleave', startAutoplay);
164
165 const getPointerX = (event) => (event.touches && event.touches.length ? event.touches[0].clientX : event.clientX);
166
167 const onPointerDown = (event) => {
168 isDragging = true;
169 startX = getPointerX(event);
170 currentX = startX;
171 track.style.transition = 'none';
172 event.preventDefault();
173 };
174
175 const onPointerMove = (event) => {
176 if (!isDragging) return;
177 currentX = getPointerX(event);
178 };
179
180 const onPointerUp = () => {
181 if (!isDragging) return;
182 const delta = currentX - startX;
183 track.style.transition = '';
184 if (Math.abs(delta) > 40) {
185 if (delta < 0) {
186 go(1);
187 } else {
188 go(-1);
189 }
190 resetAutoplay();
191 } else {
192 update();
193 }
194 isDragging = false;
195 };
196
197 track.addEventListener('mousedown', onPointerDown);
198 track.addEventListener('mousemove', onPointerMove);
199 track.addEventListener('mouseup', onPointerUp);
200 track.addEventListener('mouseleave', () => {
201 if (isDragging) {
202 onPointerUp();
203 }
204 });
205 track.addEventListener('touchstart', onPointerDown, { passive: true });
206 track.addEventListener('touchmove', onPointerMove, { passive: true });
207 track.addEventListener('touchend', onPointerUp);
208 track.addEventListener('touchcancel', onPointerUp);
209
210 update();
211 startAutoplay();
212 };
213
214 const buildFormData = (grid, page) => {
215 const boolVal = (value) => (value === 'true' || value === '1' ? '1' : '');
216 const form = new FormData();
217 form.append('action', 'ka_products_grid');
218 form.append('nonce', grid.dataset.nonce || '');
219 form.append('page', page);
220 form.append('per_page', grid.dataset.perPage || '8');
221 form.append('order', grid.dataset.order || 'DESC');
222 form.append('orderby', grid.dataset.orderby || 'date');
223 form.append('show_rating', boolVal(grid.dataset.showRating));
224 form.append('show_excerpt', boolVal(grid.dataset.showExcerpt));
225 form.append('excerpt_length', grid.dataset.excerptLength || '15');
226 form.append('show_badge', boolVal(grid.dataset.showBadge));
227 form.append('show_best_badge', boolVal(grid.dataset.showBestBadge));
228 form.append('show_custom_badge', boolVal(grid.dataset.showCustomBadge));
229 form.append('custom_badge_text', grid.dataset.customBadgeText || '');
230 form.append('show_brand', boolVal(grid.dataset.showBrand));
231 form.append('show_sku', boolVal(grid.dataset.showSku));
232 form.append('card_layout', grid.dataset.cardLayout || 'classic');
233
234 const filters = grid.dataset.filters ? grid.dataset.filters : '';
235 if (filters) {
236 form.append('filters', filters);
237 }
238 return form;
239 };
240
241 const fetchPage = (grid, page, replace = false) =>
242 fetch(grid.dataset.ajaxUrl, {
243 method: 'POST',
244 body: buildFormData(grid, page),
245 }).then((res) => res.json());
246
247 const bindLightbox = (grid, rebind = false) => {
248 if (!rebind && grid.dataset.lightboxBound === 'true') return;
249 grid.dataset.lightboxBound = 'true';
250 const overlayId = 'ka-woo-products-grid-lightbox';
251 let overlay = document.getElementById(overlayId);
252 if (!overlay) {
253 overlay = document.createElement('div');
254 overlay.id = overlayId;
255 overlay.className = 'ka-woo-products-grid__lightbox';
256 overlay.innerHTML = `
257 <button class="ka-woo-products-grid__lightbox-close" type="button" aria-label="Close">×</button>
258 <figure class="ka-woo-products-grid__lightbox-figure">
259 <img src="" alt="" />
260 <figcaption></figcaption>
261 </figure>
262 `;
263 document.body.appendChild(overlay);
264 }
265 const imgEl = overlay.querySelector('img');
266 const captionEl = overlay.querySelector('figcaption');
267 const closeBtn = overlay.querySelector('.ka-woo-products-grid__lightbox-close');
268
269 const close = () => {
270 overlay.classList.remove('is-open');
271 document.body.classList.remove('ka-woo-products-grid--lock');
272 };
273
274 closeBtn.addEventListener('click', close);
275 overlay.addEventListener('click', (e) => {
276 if (e.target === overlay) {
277 close();
278 }
279 });
280 document.addEventListener('keyup', (e) => {
281 if (e.key === 'Escape') {
282 close();
283 }
284 });
285
286 grid.addEventListener('click', (e) => {
287 const link = e.target.closest('.ka-woo-products-grid__thumb');
288 if (!link || !grid.contains(link)) return;
289 const full = link.dataset.full || link.href;
290 if (!full) return;
291 e.preventDefault();
292 if (imgEl) {
293 imgEl.src = full;
294 }
295 if (captionEl) {
296 captionEl.textContent = link.dataset.caption || '';
297 }
298 overlay.classList.add('is-open');
299 document.body.classList.add('ka-woo-products-grid--lock');
300 });
301 };
302
303 const handleAjax = (grid) => {
304 const paginationType = grid.dataset.paginationType;
305 if (!paginationType || paginationType === 'none' || paginationType === 'numbers') return;
306
307 const parent = grid.parentElement;
308 if (!parent) return;
309 const btn = parent.querySelector('.ka-woo-products-grid__load-more');
310 let page = parseInt(grid.dataset.page || '1', 10);
311 const max = () => parseInt(grid.dataset.maxPages || '1', 10);
312 let sentinelObserver;
313 let sentinelEl;
314 let isProcessing = false;
315
316 const setButtonState = (state) => {
317 if (!btn) return;
318 const labelEl = btn.querySelector('.ka-woo-products-grid__load-more-label');
319 const spinner = btn.querySelector('.ka-woo-products-grid__spinner');
320 const defaultText = btn.dataset.defaultText || btn.textContent || '';
321 const loadingText = btn.dataset.loadingText || 'Loading…';
322 if (state === 'loading') {
323 btn.disabled = true;
324 if (labelEl) labelEl.textContent = loadingText;
325 spinner?.classList.add('is-visible');
326 btn.classList.add('is-loading');
327 } else if (state === 'ready') {
328 btn.disabled = false;
329 if (labelEl) labelEl.textContent = defaultText;
330 spinner?.classList.remove('is-visible');
331 btn.classList.remove('is-loading');
332 } else if (state === 'done') {
333 spinner?.classList.remove('is-visible');
334 btn.classList.remove('is-loading');
335 btn.remove();
336 }
337 };
338
339 const cleanupSentinel = () => {
340 if (sentinelObserver && sentinelEl) {
341 sentinelObserver.unobserve(sentinelEl);
342 }
343 if (sentinelEl) {
344 sentinelEl.remove();
345 }
346 };
347
348 const process = (replace = false) => {
349 if (isProcessing) return;
350 if (page >= max() && !replace) {
351 setButtonState('done');
352 cleanupSentinel();
353 return;
354 }
355 isProcessing = true;
356 setButtonState('loading');
357 fetchPage(grid, replace ? 1 : page + 1, replace)
358 .then((res) => {
359 if (!res || !res.success || !res.data) {
360 setButtonState('done');
361 cleanupSentinel();
362 return;
363 }
364 const added = appendItems(grid, res.data.html || '', replace);
365 const maxPages = res.data.max_pages ? parseInt(res.data.max_pages, 10) : max();
366 grid.dataset.maxPages = maxPages;
367 if (replace) {
368 page = 1;
369 grid.dataset.page = '1';
370 } else {
371 page += 1;
372 grid.dataset.page = page;
373 }
374 if (page >= maxPages || added === 0) {
375 setButtonState('done');
376 cleanupSentinel();
377 } else {
378 setButtonState('ready');
379 }
380 reflowMasonry(grid);
381 setupSlider(grid);
382 bindLightbox(grid, true);
383 })
384 .catch(() => {
385 setButtonState('ready');
386 })
387 .finally(() => {
388 isProcessing = false;
389 });
390 };
391
392 if (btn) {
393 btn.addEventListener('click', () => process(false));
394 }
395
396 if (paginationType === 'infinite') {
397 if (typeof IntersectionObserver === 'undefined') {
398 return;
399 }
400 sentinelEl = document.createElement('div');
401 sentinelEl.className = 'ka-woo-products-grid__sentinel';
402 parent.appendChild(sentinelEl);
403 sentinelObserver = new IntersectionObserver(
404 (entries) => {
405 entries.forEach((entry) => {
406 if (entry.isIntersecting) {
407 process(false);
408 }
409 });
410 },
411 { rootMargin: '200px' }
412 );
413 sentinelObserver.observe(sentinelEl);
414 }
415
416 const applyFilters = (filters) => {
417 if (filters && typeof filters === 'object') {
418 grid.dataset.filters = JSON.stringify(filters);
419 } else {
420 grid.dataset.filters = '';
421 }
422 grid.dataset.page = '1';
423 process(true);
424 };
425
426 const applySorting = (orderby, order) => {
427 if (orderby) {
428 grid.dataset.orderby = orderby;
429 }
430 if (order) {
431 grid.dataset.order = order;
432 }
433 grid.dataset.page = '1';
434 process(true);
435 };
436
437 window.addEventListener('kaWooGridApplyFilters', (event) => {
438 const detail = event.detail || {};
439 if (detail.queryId && detail.queryId !== grid.dataset.queryId) {
440 return;
441 }
442 applyFilters(detail.filters || {});
443 });
444
445 document.addEventListener('kingaddons:filters:apply', (event) => {
446 const detail = event.detail || {};
447 if (!detail.queryId || detail.queryId !== grid.dataset.queryId) {
448 return;
449 }
450 applyFilters(detail.filters || {});
451 });
452
453 document.addEventListener('kingaddons:filters:reset', (event) => {
454 const detail = event.detail || {};
455 if (!detail.queryId || detail.queryId !== grid.dataset.queryId) {
456 return;
457 }
458 applyFilters({});
459 });
460
461 document.addEventListener('kingaddons:sorting:apply', (event) => {
462 const detail = event.detail || {};
463 if (detail.queryId && detail.queryId !== grid.dataset.queryId) {
464 return;
465 }
466 applySorting(detail.orderby, detail.order);
467 });
468 };
469
470 const initGrid = (grid) => {
471 if (grid.dataset.kngBound === 'true') return;
472 grid.dataset.kngBound = 'true';
473 setGridVars(grid);
474 setupMasonry(grid);
475 setupSlider(grid);
476 bindLightbox(grid);
477 handleAjax(grid);
478 };
479
480 const initAll = (context) => {
481 const root = context && context.querySelectorAll ? context : document;
482 root.querySelectorAll('.ka-woo-products-grid').forEach((grid) => initGrid(grid));
483 };
484
485 const observeNewGrids = () => {
486 if (!('MutationObserver' in window)) return;
487 const observer = new MutationObserver((mutations) => {
488 mutations.forEach((mutation) => {
489 mutation.addedNodes.forEach((node) => {
490 if (!(node instanceof HTMLElement)) return;
491 if (node.classList.contains('ka-woo-products-grid')) {
492 initGrid(node);
493 }
494 node.querySelectorAll?.('.ka-woo-products-grid').forEach((grid) => initGrid(grid));
495 });
496 });
497 });
498 observer.observe(document.body, { childList: true, subtree: true });
499 };
500
501 document.addEventListener('DOMContentLoaded', () => {
502 initAll(document);
503 observeNewGrids();
504 });
505
506 const onElementorReady = () => {
507 if (!window.elementorFrontend || !window.elementorFrontend.hooks) {
508 return;
509 }
510 window.elementorFrontend.hooks.addAction('frontend/element_ready/woo_products_grid.default', (scope) => {
511 const root = scope && scope[0] ? scope[0] : scope;
512 initAll(root || document);
513 });
514 };
515
516 if (document.readyState === 'complete') {
517 onElementorReady();
518 } else {
519 window.addEventListener('DOMContentLoaded', onElementorReady);
520 }
521 })();
522
523
524
525