PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.39
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.39
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 / assets / libraries / grid / woocommerce.js

woocommerce.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.39, at includes/assets/libraries/grid/woocommerce.js

1,166 lines 72.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // noinspection JSUnresolvedReference,DuplicatedCode
2
3 "use strict";
4 (function ($) {
5 $(window).on("elementor/frontend/init", () => {
6
7 const gridHooks = [
8 "frontend/element_ready/king-addons-woocommerce-grid.default"
9 ];
10
11 const gridHandler = ($scope) => {
12 elementorFrontend.elementsHandler.addHandler(
13 elementorModules.frontend.handlers.Base.extend({
14 onInit() {
15 const $body = $("body");
16 const isEditor = $body.hasClass("elementor-editor-active");
17 const $grid = this.$element.find(".king-addons-grid");
18 if (!$grid.length) return;
19
20 /* ──────────────────────────────────────
21 * UTILITY FUNCTIONS
22 * ────────────────────────────────────── */
23
24 // Simple debounce helper.
25 const debounce = (func, threshold = 100, execAsap) => {
26 let timeout;
27 return function (...args) {
28 const context = this;
29 const delayed = () => {
30 if (!execAsap) func.apply(context, args);
31 timeout = null;
32 };
33 if (timeout) clearTimeout(timeout);
34 else if (execAsap) func.apply(context, args);
35 timeout = setTimeout(delayed, threshold);
36 };
37 };
38
39 // Attach "smartresize" to jQuery.
40 $.fn.smartresize = function (fn) {
41 return fn ? this.on("resize", debounce(fn)) : this.trigger("smartresize");
42 };
43
44 // Parse grid settings.
45 const settingsData = $grid.attr("data-settings");
46 const settings = settingsData ? JSON.parse(settingsData) : false;
47
48 /* ──────────────────────────────────────
49 * INITIAL EVENT HANDLERS
50 * ────────────────────────────────────── */
51
52 // Handle WooCommerce orderby form.
53 if ($scope.find(".king-addons-grid-orderby form").length) {
54 const $orderbyForm = $scope.find(".king-addons-grid-orderby form");
55 $scope.find(".orderby").on("change", () => $orderbyForm.trigger("submit"));
56 }
57
58 // Adjust result count text if WooCommerce count element exists.
59 if (settings && $scope.find(".woocommerce-result-count").length) {
60 adjustResultCount($scope, $grid, settings, isEditor);
61 }
62
63 // Helper to schedule a delayed isotope layout.
64 const scheduleIsotopeLayout = (delay) =>
65 setTimeout(() => isotopeLayout(settings), delay);
66
67 if (settings) {
68 // Perform initial layout.
69 isotopeLayout(settings);
70 scheduleIsotopeLayout(100);
71 if (isEditor) {
72 scheduleIsotopeLayout(500);
73 scheduleIsotopeLayout(1000);
74 }
75 $(window).on("load", () => scheduleIsotopeLayout(100));
76 $(document).ready(() => scheduleIsotopeLayout(100));
77 $(window).smartresize(() => scheduleIsotopeLayout(200));
78
79 // If dynamic grid settings exist, set up filtering and “load more” experiments.
80 if (settings.grid_settings) {
81 loadMoreExperiment();
82 filtersExperiment();
83 }
84 isotopeFilters(settings);
85
86 let initialItems = 0;
87 $grid.on("arrangeComplete", (e, filteredItems) => {
88 animateGridItems(filteredItems, settings, $grid);
89 initialItems = filteredItems.length;
90 });
91
92 // Once images load, set grid opacity and equalize item heights.
93 $grid.imagesLoaded(() => {
94 if ($grid.css("opacity") !== "1") $grid.css("opacity", "1");
95 setTimeout(() => $grid.addClass("grid-images-loaded"), 500);
96 setEqualHeight(settings);
97 });
98
99 // Pagination: load-more or infinite-scroll.
100 if (["load-more", "infinite-scroll"].includes(settings.pagination_type)) {
101 if ($scope.find(".king-addons-grid-pagination").length && !isEditor) {
102 setupInfiniteScroll(settings);
103 } else {
104 $scope.find(".king-addons-load-more-btn").on("click", () =>
105 alert(
106 "Load More is Disabled in the Editor! Please Preview this Page to see it in action"
107 )
108 );
109 }
110 }
111 } else {
112 // Fallback: initialize Slick slider.
113 $grid.animate({opacity: "1"}, 1000);
114 initSlickSlider();
115 }
116
117 // Additional features.
118 if ($grid.find(".king-addons-grid-item-add-to-cart").length) {
119 setupAddToCart();
120 }
121 postSharing();
122 mediaHoverLink();
123 if (
124 !$scope.hasClass("elementor-widget-king-addons-woocommerce-category-grid-pro") &&
125 !$scope.hasClass("elementor-widget-king-addons-category-grid-pro")
126 ) {
127 // lightboxPopup(settings);
128 }
129 postLikes(settings);
130 lazyLoadObserver();
131
132 /* ──────────────────────────────────────
133 * FUNCTION DEFINITIONS
134 * ────────────────────────────────────── */
135
136 // Adjust WooCommerce result count.
137 function adjustResultCount($scope, $grid, s, isEditor) {
138 const $resultCount = $scope.find(".woocommerce-result-count");
139 let text = $resultCount.text();
140 const scopeId = $scope.data("id");
141 const storageKey = `king-addons-cached-items-length-${scopeId}`;
142 let cachedLength = localStorage.getItem(storageKey);
143 if (!cachedLength || isEditor) {
144 cachedLength = $scope.find(".king-addons-grid-item").length;
145 localStorage.setItem(storageKey, cachedLength);
146 }
147 const itemsPerPage = s.query_posts_per_page || parseInt(cachedLength, 10);
148 if (isNaN(itemsPerPage) || itemsPerPage <= 0) return;
149 let currentPage = 1;
150 const $currentPage = $scope.find(".king-addons-grid-current-page");
151 if ($currentPage.length) {
152 currentPage = parseInt($currentPage.text().trim(), 10) || 1;
153 }
154 const totalMatch = text.match(/of (\d+) results/);
155 const totalItems = totalMatch ? parseInt(totalMatch[1].trim(), 10) : itemsPerPage;
156 if (isNaN(totalItems) || totalItems <= 0) return;
157 const startItem = (currentPage - 1) * itemsPerPage + 1;
158 const endItem = Math.min(startItem + itemsPerPage - 1, totalItems);
159 text = text.replace(/\d+\u2013\d+/, `${startItem}\u2013${endItem}`);
160 $resultCount.text(text);
161 }
162
163 // Layout the grid using Isotope (or its custom wrapper).
164 function isotopeLayout(s, $response = "") {
165 const layout = s.layout;
166 const columnsDesktop = parseInt(s.columns_desktop, 10);
167 let items = $response ? $response : $grid.find(".king-addons-grid-item"),
168 contWidth = $grid.width() + s.gutter_hr - 0.3,
169 viewportWidth = $(window).outerWidth(),
170 columns,
171 gutterHr,
172 gutterVr;
173
174 // Breakpoints.
175 const {mobile, mobile_extra, tablet, tablet_extra, laptop, widescreen} =
176 elementorFrontend.config.responsive.breakpoints;
177 const activeBps = elementorFrontend.config.responsive.activeBreakpoints;
178 let cMobile = 1,
179 cMobileExtra,
180 cTablet = 2,
181 cTabletExtra,
182 cLaptop,
183 cWideScreen;
184 $scope
185 .attr("class")
186 .split(" ")
187 .forEach((cl) => {
188 if (/mobile\d/.test(cl)) cMobile = parseInt(cl.slice(-1), 10);
189 if (/mobile_extra\d/.test(cl)) cMobileExtra = parseInt(cl.slice(-1), 10);
190 if (/tablet\d/.test(cl)) cTablet = parseInt(cl.slice(-1), 10);
191 if (/tablet_extra\d/.test(cl)) cTabletExtra = parseInt(cl.slice(-1), 10);
192 if (/widescreen\d/.test(cl)) cWideScreen = parseInt(cl.slice(-1), 10);
193 if (/laptop\d/.test(cl)) cLaptop = parseInt(cl.slice(-1), 10);
194 });
195
196 if (viewportWidth <= mobile.value && activeBps.mobile) {
197 columns = cMobile;
198 gutterHr = s.gutter_hr_mobile;
199 gutterVr = s.gutter_vr_mobile;
200 } else if (viewportWidth <= mobile_extra.value && activeBps.mobile_extra) {
201 columns = cMobileExtra || cTablet;
202 gutterHr = s.gutter_hr_mobile_extra;
203 gutterVr = s.gutter_vr_mobile_extra;
204 } else if (viewportWidth <= tablet.value && activeBps.tablet) {
205 columns = cTablet;
206 gutterHr = s.gutter_hr_tablet;
207 gutterVr = s.gutter_vr_tablet;
208 } else if (viewportWidth <= tablet_extra.value && activeBps.tablet_extra) {
209 columns = cTabletExtra || cTablet;
210 gutterHr = s.gutter_hr_tablet_extra;
211 gutterVr = s.gutter_vr_tablet_extra;
212 } else if (viewportWidth <= laptop.value && activeBps.laptop) {
213 columns = cLaptop || columnsDesktop;
214 gutterHr = s.gutter_hr_laptop;
215 gutterVr = s.gutter_vr_laptop;
216 } else if (viewportWidth <= widescreen.value) {
217 columns = columnsDesktop;
218 gutterHr = s.gutter_hr;
219 gutterVr = s.gutter_vr;
220 } else {
221 columns = cWideScreen || columnsDesktop;
222 gutterHr = s.gutter_hr_widescreen;
223 gutterVr = s.gutter_vr_widescreen;
224 }
225 if (columns > 8) columns = 8;
226 items.outerWidth(Math.floor(contWidth / columns - gutterHr));
227 items.css("margin-bottom", `${gutterVr}px`);
228
229 if (layout === "list") {
230 handleListLayout(s, items);
231 }
232
233 const isoLayoutMode = layout === "list" ? "fitRows" : layout;
234 const transDuration = s.filters_animation === "default" ? 400 : 0;
235
236 $grid.isotopekng({
237 layoutMode: isoLayoutMode,
238 masonry: {gutter: gutterHr},
239 fitRows: {gutter: gutterHr},
240 transitionDuration: transDuration,
241 percentPosition: true,
242 });
243 }
244
245 // Handle "list" layout adjustments.
246 function handleListLayout(s, items) {
247 const imageHeight = items.find(".king-addons-grid-image-wrap").outerHeight();
248 items.find(".king-addons-grid-item-below-content").css("min-height", `${imageHeight}px`);
249
250 if ($body.width() < 480) {
251 items.find(".king-addons-grid-media-wrap").css({float: "none", width: "100%"});
252 items.find(".king-addons-grid-item-below-content").css({
253 float: "none",
254 width: "100%",
255 "min-height": "0",
256 });
257 } else {
258 const {media_align: align, media_width: mWidth, media_distance: mDistance} = s;
259 if (align === "zigzag") {
260 handleZigzag(items, mWidth, mDistance);
261 } else {
262 items.find(".king-addons-grid-media-wrap").css({
263 float: align,
264 width: `${mWidth}%`,
265 [`margin-${align === "left" ? "right" : "left"}`]: `${mDistance}px`,
266 });
267 items.find(".king-addons-grid-item-below-content").css({
268 float: align,
269 width: `calc((100% - ${mWidth}%) - ${mDistance}px)`,
270 });
271 }
272 }
273 }
274
275 // Alternate ("zigzag") layout for list items.
276 function handleZigzag(items, mWidth, mDistance) {
277 items.filter(":even").each(function () {
278 $(this).find(".king-addons-grid-media-wrap").css({
279 float: "left",
280 width: `${mWidth}%`,
281 "margin-right": `${mDistance}px`,
282 });
283 $(this).find(".king-addons-grid-item-below-content").css({
284 float: "left",
285 width: `calc((100% - ${mWidth}%) - ${mDistance}px)`,
286 });
287 });
288 items.filter(":odd").each(function () {
289 $(this).find(".king-addons-grid-media-wrap").css({
290 float: "right",
291 width: `${mWidth}%`,
292 "margin-left": `${mDistance}px`,
293 });
294 $(this).find(".king-addons-grid-item-below-content").css({
295 float: "right",
296 width: `calc((100% - ${mWidth}%) - ${mDistance}px)`,
297 });
298 });
299 }
300
301 // Equalize grid item heights for "fitRows" layout.
302 function setEqualHeight(s) {
303 if (s.layout === "fitRows") {
304 const $items = $grid.children("article");
305 const columns = Math.floor($grid.outerWidth() / $items.outerWidth());
306 if (columns > 1) {
307 const maxH = Math.max(...$items.map((_, el) => $(el).outerHeight()).get());
308 $items.css("height", `${maxH}px`);
309 if (s.stick_last_element_to_bottom === "yes") {
310 $scope.addClass("king-addons-grid-last-element-yes");
311 }
312 }
313 }
314 }
315
316 // Lazy-load images via an IntersectionObserver.
317 function lazyLoadObserver() {
318 setTimeout(() => {
319 const observer = new IntersectionObserver((entries) => {
320 entries.forEach((entry) => {
321 if (
322 entry.isIntersecting &&
323 entry.target.src.includes("icon-256x256")
324 ) {
325 setTimeout(() => {
326 entry.target.src =
327 entry.target.parentElement.dataset.src;
328 $(entry.target).toggleClass("king-addons-hidden-image");
329 $(window).trigger("resize");
330 }, 100);
331 }
332 });
333 });
334 $scope
335 .find(".king-addons-grid-image-wrap img:first-of-type")
336 .each(function () {
337 observer.observe(this);
338 });
339 }, 100);
340 }
341
342 // Update filter counts and set up filtering behavior.
343 function isotopeFilters(s, ev = "load") {
344 if (s.filters_count === "yes") {
345 $scope.find(".king-addons-grid-filters a, .king-addons-grid-filters span").each(function () {
346 const $el = $(this);
347 if (s.grid_settings && ev === "load") {
348 const thisTaxonomy =
349 $el.attr("data-filter") !== "*" ? $el.data("ajax-filter")[0] : "*";
350 const thisFilter =
351 $el.attr("data-filter") !== "*" ? $el.data("ajax-filter")[1] : "*";
352 $.ajax({
353 type: "POST",
354 url: KingAddonsGridData.ajaxUrl,
355 data: {
356 action: s.grid_settings
357 ? "king_addons_get_filtered_count"
358 : "king_addons_get_filtered_count",
359 king_addons_offset: 0,
360 king_addons_filter: thisFilter,
361 king_addons_taxonomy: thisTaxonomy,
362 grid_settings: s.grid_settings,
363 },
364 success: (response) => {
365 $el.find("sup").text(response.data.query_found);
366 },
367 });
368 } else {
369 if ($el.attr("data-filter") === "*") {
370 $el.find("sup").text(
371 $scope.find(".king-addons-grid-filters").next().find("article").length
372 );
373 } else {
374 $el.find("sup").text($scope.find($el.attr("data-filter")).length);
375 }
376 }
377 });
378 }
379
380 if (s.filters_linkable === "yes") return;
381
382 // Deeplinking support.
383 if (s.deeplinking === "yes") {
384 let deepLink = window.location.hash.replace("#filter:", ".");
385 if (window.location.hash.match("#filter:all")) deepLink = "*";
386 const activeFilter = $scope
387 .find(`.king-addons-grid-filters span[data-filter="${deepLink}"]`)
388 .not(".king-addons-back-filter");
389 $scope.find(".king-addons-grid-filters span").removeClass("king-addons-active-filter");
390 activeFilter.addClass("king-addons-active-filter");
391 $grid.isotopekng({filter: deepLink});
392 s.lightbox.selector =
393 deepLink === "*" ? ".king-addons-grid-image-wrap" : `${deepLink} .king-addons-grid-image-wrap`;
394 // lightboxPopup(s);
395 }
396
397 // Hide empty filters.
398 if (s.filters_hide_empty === "yes" && !s.grid_settings) {
399 $scope.find(".king-addons-grid-filters span").each(function () {
400 const filterClass = $(this).attr("data-filter");
401 if (filterClass !== "*" && $grid.find(filterClass).length === 0) {
402 $(this).parent("li").addClass("king-addons-hidden-element");
403 } else {
404 $(this).parent("li").removeClass("king-addons-hidden-element");
405 }
406 });
407 }
408
409 // Default filter.
410 if (
411 !$scope.hasClass("elementor-widget-king-addons-woocommerce-category-grid-pro") &&
412 !$scope.hasClass("elementor-widget-king-addons-category-grid-pro") &&
413 s.filters_default_filter
414 ) {
415 setTimeout(() => {
416 const filterEl = $scope
417 .find(".king-addons-grid-filters")
418 .find(`span[data-filter*="-${s.filters_default_filter}"]`);
419 if (filterEl.length) filterEl[0].click();
420 }, 100);
421 }
422
423 // Filter click behavior.
424 if (!s.grid_settings) {
425 $scope.find(".king-addons-grid-filters span").on("click", function () {
426 const filterClass = $(this).data("filter");
427 $scope.find(".king-addons-grid-filters span").removeClass("king-addons-active-filter");
428 $(this).addClass("king-addons-active-filter");
429 if (s.deeplinking === "yes") {
430 const filterHash =
431 filterClass === "*" ? "#filter:all" : `#filter:${filterClass.replace(".", "")}`;
432 window.location.href =
433 window.location.pathname + window.location.search + filterHash;
434 }
435 if (["infinite-scroll", "load-more"].includes(s.pagination_type)) {
436 if ($grid.find($(this).attr("data-filter")).length === 0) {
437 $grid.infiniteScroll("loadNextPage");
438 }
439 }
440 if (s.filters_animation !== "default") {
441 $scope.find(".king-addons-grid-item-inner").css({
442 opacity: "0",
443 transition: "none",
444 });
445 if (s.filters_animation === "fade-slide") {
446 $scope.find(".king-addons-grid-item-inner").css("top", "20px");
447 } else if (s.filters_animation === "zoom") {
448 $scope.find(".king-addons-grid-item-inner").css("transform", "scale(0.01)");
449 }
450 }
451 $grid.isotopekng({filter: filterClass});
452 s.lightbox.selector =
453 filterClass === "*" ? ".king-addons-grid-image-wrap" : `${filterClass} .king-addons-grid-image-wrap`;
454 $grid.data("lightGallery").destroy(true);
455 $grid.lightGallery(s.lightbox);
456 });
457 }
458 }
459
460 // Experiment: dynamic filtering via AJAX.
461 function filtersExperiment() {
462 const countAction = $scope.hasClass("elementor-widget-king-addons-woocommerce-grid")
463 ? "king_addons_get_woocommerce_filtered_count"
464 : "king_addons_get_filtered_count";
465 const contentAction = $scope.hasClass("elementor-widget-king-addons-woocommerce-grid")
466 ? "king_addons_filter_woocommerce_products"
467 : "king_addons_filter_grid_posts";
468
469 $scope.find(".king-addons-grid-filters").on("click", "span", function (e) {
470 e.preventDefault();
471 e.stopPropagation();
472 e.stopImmediatePropagation();
473
474 const filterClass = $(this).data("filter");
475 const thisTaxonomy = filterClass !== "*" ? $(this).data("ajax-filter")[0] : "*";
476 const thisFilter = filterClass !== "*" ? $(this).data("ajax-filter")[1] : "*";
477 const loader = `<div class="king-addons-grid-loader-wrap"><div class="king-addons-ring"><div></div><div></div><div></div><div></div></div></div>`;
478
479 $scope.find(".king-addons-grid-filters span").removeClass("king-addons-active-filter");
480 $(this).addClass("king-addons-active-filter");
481
482 $grid.isotopekng("destroy");
483 $grid.html(loader);
484
485 $.ajax({
486 type: "POST",
487 url: KingAddonsGridData.ajaxUrl,
488 data: {
489 action: countAction,
490 king_addons_offset:
491 +settings.grid_settings.query_offset +
492 $scope.find(".king-addons-grid-item").length,
493 king_addons_filter: thisFilter,
494 king_addons_taxonomy: thisTaxonomy,
495 grid_settings: settings.grid_settings,
496 },
497 success: function () {
498 $.ajax({
499 type: "POST",
500 url: KingAddonsGridData.ajaxUrl,
501 data: {
502 action: contentAction,
503 king_addons_item_length:
504 +settings.grid_settings.query_offset +
505 $scope.find(".king-addons-grid-item").length,
506 king_addons_filter: thisFilter,
507 king_addons_taxonomy: thisTaxonomy,
508 grid_settings: settings.grid_settings,
509 },
510 success: function (resp) {
511 setTimeout(() => {
512 $grid.addClass("king-addons-zero-opacity");
513 $grid.html($(resp));
514 isotopeLayout(settings, $(resp));
515 setTimeout(() => {
516 isotopeLayout(settings);
517 window.dispatchEvent(new Event("resize"));
518 window.dispatchEvent(new Event("scroll"));
519 }, 500);
520 mediaHoverLink();
521 $grid.removeClass("king-addons-zero-opacity");
522 }, 800);
523 },
524 });
525 },
526 });
527 });
528 }
529
530 // Experiment: load-more via AJAX.
531 function loadMoreExperiment() {
532 const countAction = $scope.hasClass("elementor-widget-king-addons-woocommerce-grid")
533 ? "king_addons_get_woocommerce_filtered_count"
534 : "king_addons_get_filtered_count";
535 const contentAction = $scope.hasClass("elementor-widget-king-addons-woocommerce-grid")
536 ? "king_addons_filter_woocommerce_products"
537 : "king_addons_filter_grid_posts";
538 const $pagination = $scope.find(".king-addons-grid-pagination");
539
540 $scope.find(".king-addons-load-more-btn").on("click", function (e) {
541 e.preventDefault();
542 e.stopPropagation();
543 e.stopImmediatePropagation();
544
545 let thisTaxonomy = "*";
546 let thisFilter = "*";
547 const $activeFilter = $scope.find(".king-addons-active-filter");
548 if ($activeFilter.length && $activeFilter.data("filter") !== "*") {
549 thisTaxonomy = $activeFilter.data("ajax-filter")[0];
550 thisFilter = $activeFilter.data("ajax-filter")[1];
551 }
552
553 $pagination.find(".king-addons-load-more-btn").hide();
554 $pagination.find(".king-addons-pagination-loading").css("display", "inline-block");
555
556 $.ajax({
557 type: "POST",
558 url: KingAddonsGridData.ajaxUrl,
559 data: {
560 action: countAction,
561 king_addons_offset:
562 +settings.grid_settings.query_offset +
563 $grid.find(".king-addons-grid-item").length,
564 king_addons_filter: thisFilter,
565 king_addons_taxonomy: thisTaxonomy,
566 grid_settings: settings.grid_settings,
567 },
568 success: function (res) {
569 $.ajax({
570 type: "POST",
571 url: KingAddonsGridData.ajaxUrl,
572 data: {
573 action: contentAction,
574 king_addons_offset:
575 +settings.grid_settings.query_offset +
576 $grid.find(".king-addons-grid-item").length,
577 king_addons_filter: thisFilter,
578 king_addons_taxonomy: thisTaxonomy,
579 grid_settings: settings.grid_settings,
580 },
581 success: function (resp) {
582 const $items = $(resp);
583 $grid.infiniteScroll("appendItems", $items);
584 $grid.isotopekng("appended", $items);
585 $items.imagesLoaded().progress(() => {
586 isotopeLayout(settings);
587 setTimeout(() => isotopeLayout(settings), 100);
588 setTimeout(() => $grid.addClass("grid-images-loaded"), 500);
589 });
590 $pagination.find(".king-addons-pagination-loading").hide();
591 if (res.data.page_count > 1) {
592 $pagination.find(".king-addons-load-more-btn").fadeIn();
593 } else {
594 $pagination.find(".king-addons-pagination-finish").fadeIn(1000);
595 $pagination.delay(2000).fadeOut(1000);
596 setTimeout(() => $pagination.find(".king-addons-pagination-loading").hide(), 500);
597 }
598 // lightboxPopup(settings);
599 $grid.data("lightGallery").destroy(true);
600 $grid.lightGallery(settings.lightbox);
601 mediaHoverLink();
602 postSharing();
603 lazyLoadObserver();
604 window.dispatchEvent(new Event("resize"));
605 window.dispatchEvent(new Event("scroll"));
606 },
607 });
608 },
609 });
610 });
611 }
612
613 // Setup Infinite Scroll.
614 function setupInfiniteScroll(s) {
615 const $pagination = $scope.find(".king-addons-grid-pagination");
616 const scopeClass = `.elementor-element-${$scope.attr("data-id")}`;
617 let navClass = false,
618 threshold = false;
619 if (s.pagination_type === "infinite-scroll") {
620 threshold = 300;
621 navClass = `${scopeClass} .king-addons-load-more-btn`;
622 }
623 $grid.infiniteScroll({
624 path: `${scopeClass} .king-addons-grid-pagination a`,
625 hideNav: navClass,
626 append: false,
627 history: false,
628 scrollThreshold: threshold,
629 status: `${scopeClass} .page-load-status`,
630 onInit() {
631 this.on("load", () => $grid.removeClass("grid-images-loaded"));
632 },
633 });
634 $grid.on("request.infiniteScroll", () => {
635 $pagination.find(".king-addons-load-more-btn").hide();
636 $pagination.find(".king-addons-pagination-loading").css("display", "inline-block");
637 });
638 let pagesLoaded = 0;
639 $grid.on("load.infiniteScroll", (event, response) => {
640 pagesLoaded++;
641 const $items = $(response).find(scopeClass).find(".king-addons-grid-item");
642 if ($scope.find(".woocommerce-result-count").length) {
643 let updatedCount = $scope.find(".woocommerce-result-count").text();
644 updatedCount = updatedCount.replace(
645 /\d\u2013\d+/,
646 `1–${$scope.find(".king-addons-grid-item").length + $items.length}`
647 );
648 $scope.find(".woocommerce-result-count").text(updatedCount);
649 }
650 $grid.infiniteScroll("appendItems", $items);
651 $grid.isotopekng("appended", $items);
652 $items.imagesLoaded().progress(() => {
653 isotopeLayout(s);
654 setTimeout(() => {
655 isotopeLayout(s);
656 isotopeFilters(s);
657 }, 10);
658 setTimeout(() => $grid.addClass("grid-images-loaded"), 500);
659 });
660 $pagination.find(".king-addons-pagination-loading").hide();
661 if (pagesLoaded < s.pagination_max_pages) {
662 if (s.pagination_type === "load-more") {
663 $pagination.find(".king-addons-load-more-btn").fadeIn();
664 if ($scope.find(".king-addons-grid-filters").length) {
665 const activeF = $scope.find(".king-addons-active-filter");
666 if (activeF.length && activeF.attr("data-filter") !== "*") {
667 const filterClass = activeF.attr("data-filter").slice(1);
668 let foundOne = false;
669 $items.each(function () {
670 if ($(this).hasClass(filterClass)) {
671 foundOne = true;
672 return false;
673 }
674 });
675 if (!foundOne) $grid.infiniteScroll("loadNextPage");
676 }
677 }
678 }
679 } else {
680 $pagination.find(".king-addons-pagination-finish").fadeIn(1000);
681 $pagination.delay(2000).fadeOut(1000);
682 setTimeout(() => $pagination.find(".king-addons-pagination-loading").hide(), 500);
683 }
684 // lightboxPopup(s);
685 $grid.data("lightGallery").destroy(true);
686 $grid.lightGallery(s.lightbox);
687 mediaHoverLink();
688 postSharing();
689 lazyLoadObserver();
690 setTimeout(() => {
691 setEqualHeight(s);
692 window.dispatchEvent(new Event("resize"));
693 }, 500);
694 });
695 $pagination.find(".king-addons-load-more-btn").on("click", () => {
696 $grid.infiniteScroll("loadNextPage");
697 return false;
698 });
699 }
700
701 // Initialize the Slick slider.
702 function initSlickSlider() {
703 const settingsSlick = JSON.parse($grid.attr("data-slick") || "{}");
704 $grid.slick({
705 appendDots: $scope.find(".king-addons-grid-slider-dots"),
706 rows: settingsSlick.sliderRows,
707 customPaging: () => `<span class="king-addons-grid-slider-dot"></span>`,
708 slidesToShow: getSliderColumns("desktop"),
709 responsive: getSliderResponsiveOptions(settingsSlick),
710 });
711 handleSlickArrows();
712 handleSlickDots();
713 }
714
715 // Get the number of slider columns.
716 function getSliderColumns(type) {
717 const match = $scope.attr("class").match(/king-addons-grid-slider-columns-(\d)/);
718 return type === "desktop" ? (match ? parseInt(match[1], 10) : 2) : 2;
719 }
720
721 // Get responsive slider options.
722 function getSliderResponsiveOptions(sl) {
723 const className = $scope.attr("class");
724 const colDesktop = getSliderColumns("desktop");
725 const colWide = extractColumns(className, "widescreen") || colDesktop;
726 const colLaptop = extractColumns(className, "laptop") || colDesktop;
727 const colTablet = extractColumns(className, "tablet") || 2;
728 const colTabletExtra = extractColumns(className, "tablet_extra") || colTablet;
729 const colMobileExtra = extractColumns(className, "mobile_extra") || colTablet;
730 const colMobile = extractColumns(className, "mobile") || 1;
731 const slidesToScroll = sl.sliderSlidesToScroll;
732 const adjust = (cols) => (slidesToScroll > cols ? 1 : slidesToScroll);
733 return [
734 {breakpoint: 10000, settings: {slidesToShow: colWide, slidesToScroll: adjust(colWide)}},
735 {
736 breakpoint: 2399,
737 settings: {slidesToShow: colDesktop, slidesToScroll: adjust(colDesktop)}
738 },
739 {
740 breakpoint: 1221,
741 settings: {slidesToShow: colLaptop, slidesToScroll: adjust(colLaptop)}
742 },
743 {
744 breakpoint: 1200,
745 settings: {slidesToShow: colTabletExtra, slidesToScroll: adjust(colTabletExtra)}
746 },
747 {
748 breakpoint: 1024,
749 settings: {slidesToShow: colTablet, slidesToScroll: adjust(colTablet)}
750 },
751 {
752 breakpoint: 880,
753 settings: {slidesToShow: colMobileExtra, slidesToScroll: adjust(colMobileExtra)}
754 },
755 {
756 breakpoint: 768,
757 settings: {slidesToShow: colMobile, slidesToScroll: adjust(colMobile)}
758 },
759 ];
760 }
761
762 // Extract a number of columns from a class name.
763 function extractColumns(className, type) {
764 const match = className.match(new RegExp(`columns--${type}(\\d)`));
765 return match ? parseInt(match[1], 10) : false;
766 }
767
768 // Adjust the slider arrows based on available space.
769 function handleSlickArrows() {
770 const $prevArrow = $scope.find(".king-addons-grid-slider-prev-arrow");
771 const $nextArrow = $scope.find(".king-addons-grid-slider-next-arrow");
772 if (!$prevArrow.length || !$nextArrow.length) return;
773 const positionSum = $prevArrow.position().left * -2;
774 $(window).on("load", checkArrows);
775 $(window).smartresize(checkArrows);
776
777 function checkArrows() {
778 if (
779 $(window).width() <=
780 $scope.outerWidth() +
781 $prevArrow.outerWidth() +
782 $nextArrow.outerWidth() +
783 positionSum
784 ) {
785 $prevArrow.addClass("king-addons-adjust-slider-prev-arrow");
786 $nextArrow.addClass("king-addons-adjust-slider-next-arrow");
787 } else {
788 $prevArrow.removeClass("king-addons-adjust-slider-prev-arrow");
789 $nextArrow.removeClass("king-addons-adjust-slider-next-arrow");
790 }
791 }
792 }
793
794 // Adjust slider dots if needed.
795 function handleSlickDots() {
796 if (
797 $scope.find(".slick-dots").length &&
798 $scope.hasClass("king-addons-grid-slider-dots-horizontal")
799 ) {
800 resizeDots();
801 $(window).smartresize(() => setTimeout(resizeDots, 300));
802 }
803
804 function resizeDots() {
805 const $dots = $scope.find(".slick-dots li");
806 const marginRight = parseInt($dots.find("span").css("margin-right"), 10);
807 const width = $dots.outerWidth() * $dots.length - marginRight;
808 $scope.find(".slick-dots").css("width", width);
809 }
810 }
811
812 // Setup "Add to Cart" behavior.
813 function setupAddToCart() {
814 const $addCartIcon = $grid.find(".king-addons-grid-item-add-to-cart i");
815 let addCartIconClass = $addCartIcon.attr("class") || "";
816 if (addCartIconClass) {
817 addCartIconClass = addCartIconClass.substring(
818 addCartIconClass.indexOf("fa-")
819 );
820 }
821 $body.on("adding_to_cart", (ev, btn) => btn.fadeTo("slow", 0));
822 $body.on("added_to_cart", (ev, fragments, hash, btn) => {
823 const productId = btn.data("product_id");
824 btn.next().fadeTo(700, 1).css("display", "inline-block");
825 btn.css("display", "none");
826 if (btn.data("atc-popup") === "sidebar") {
827 $(".king-addons-mini-cart-toggle-wrap a").each(function () {
828 const $miniCart = $(this)
829 .closest(".king-addons-mini-cart-inner")
830 .find(".king-addons-mini-cart");
831 if ($miniCart.css("display") === "none") $(this).trigger("click");
832 });
833 } else if (btn.data("atc-popup") === "popup") {
834 addToCartPopup(btn, productId);
835 }
836 if (addCartIconClass) {
837 btn.find("i").removeClass(addCartIconClass).addClass("fa-check");
838 setTimeout(() => {
839 btn.find("i").removeClass("fa-check").addClass(addCartIconClass);
840 }, 3500);
841 }
842 });
843
844 function addToCartPopup(btn, productId) {
845 const $popupItem = btn.closest(".king-addons-grid-item");
846 const popupText = $popupItem.find(".king-addons-grid-item-title").text();
847 const popupLink = btn.next().attr("href");
848 const popupImageSrc = $popupItem.find(".king-addons-grid-image-wrap").data("src");
849 const popupAnimation = btn.data("atc-animation");
850 const fadeOutIn = btn.data("atc-fade-out-in");
851 const animTime = btn.data("atc-animation-time");
852 let animationClass = "king-addons-added-to-cart-default",
853 removeAnimationClass = "king-addons-added-to-cart-popup-hide";
854 const popupImage = popupImageSrc
855 ? `<div class="king-addons-added-tc-popup-img"><img src="${popupImageSrc}" alt="" /></div>`
856 : "";
857 switch (popupAnimation) {
858 case "slide-left":
859 animationClass = "king-addons-added-to-cart-slide-in-left";
860 removeAnimationClass = "king-addons-added-to-cart-slide-out-left";
861 break;
862 case "scale-up":
863 animationClass = "king-addons-added-to-cart-scale-up";
864 removeAnimationClass = "king-addons-added-to-cart-scale-down";
865 break;
866 case "skew":
867 animationClass = "king-addons-added-to-cart-skew";
868 removeAnimationClass = "king-addons-added-to-cart-skew-off";
869 break;
870 case "fade":
871 animationClass = "king-addons-added-to-cart-fade";
872 removeAnimationClass = "king-addons-added-to-cart-fade-out";
873 break;
874 }
875 if (!$scope.find(`#king-addons-added-to-cart-${productId}`).length) {
876 $scope
877 .find(".king-addons-grid")
878 .append(
879 `<div id="king-addons-added-to-cart-${productId}" class="king-addons-added-to-cart-popup ${animationClass}">
880 ${popupImage}
881 <div class="king-addons-added-tc-title">
882 <p>${popupText} ${KingAddonsGridData.addedToCartText}</p>
883 <p><a href="${popupLink}">${KingAddonsGridData.viewCart}</a></p>
884 </div>
885 </div>`
886 );
887 setTimeout(() => {
888 $scope
889 .find(`#king-addons-added-to-cart-${productId}`)
890 .addClass(removeAnimationClass);
891 setTimeout(() => {
892 $scope.find(`#king-addons-added-to-cart-${productId}`).remove();
893 }, animTime * 1000);
894 }, fadeOutIn * 1000);
895 }
896 }
897 }
898
899 // Setup post sharing behavior.
900 function postSharing() {
901 if (!$scope.find(".king-addons-sharing-trigger").length) return;
902 const $sharingTrigger = $scope.find(".king-addons-sharing-trigger");
903 const $sharingInner = $scope.find(".king-addons-post-sharing-inner");
904 let sharingWidth = 5;
905 $sharingInner.first().find("a").each(function () {
906 sharingWidth += $(this).outerWidth() + parseInt($(this).css("margin-right"), 10);
907 });
908 const direction = $sharingTrigger.attr("data-direction");
909 if (direction === "left" || direction === "right") {
910 $sharingInner.css("width", `${sharingWidth}px`);
911 if (direction === "left") {
912 $sharingInner.css(
913 "left",
914 -(parseInt($sharingInner.find("a").css("margin-right"), 10) + sharingWidth) + "px"
915 );
916 } else {
917 $sharingInner.css({left: $sharingTrigger.css("margin-right")});
918 }
919 } else if (direction === "top") {
920 const margin = parseInt($sharingInner.find("a").css("margin-right"), 10);
921 $sharingInner.find("a").css({"margin-right": "0", "margin-top": `${margin}px`});
922 $sharingInner.css({
923 top: `-${margin}px`,
924 left: "50%",
925 transform: "translate(-50%, -100%)",
926 });
927 } else if (direction === "bottom") {
928 const margin = parseInt($sharingInner.find("a").css("margin-right"), 10);
929 $sharingInner.find("a").css({"margin-right": "0", "margin-bottom": `${margin}px`});
930 $sharingInner.css({
931 bottom: `-${margin}px`,
932 left: "50%",
933 transform: "translate(-50%, 100%)",
934 });
935 }
936 if ($sharingTrigger.attr("data-action") === "click") {
937 $sharingTrigger.on("click", function () {
938 const $inner = $(this).next();
939 if ($inner.css("visibility") === "hidden") {
940 $inner.css("visibility", "visible").find("a").css({opacity: "1", top: "0"});
941 setTimeout(() => $inner.find("a").addClass("king-addons-no-transition-delay"), $inner.find("a").length * 100);
942 } else {
943 $inner.find("a").removeClass("king-addons-no-transition-delay").css({
944 opacity: "0",
945 top: "-5px",
946 });
947 setTimeout(() => $inner.css("visibility", "hidden"), $inner.find("a").length * 100);
948 }
949 });
950 } else {
951 $sharingTrigger.on("mouseenter", function () {
952 const $inner = $(this).next();
953 $inner.css("visibility", "visible").find("a").css({opacity: "1", top: "0"});
954 setTimeout(() => $inner.find("a").addClass("king-addons-no-transition-delay"), $inner.find("a").length * 100);
955 });
956 $scope.find(".king-addons-grid-item-sharing").on("mouseleave", function () {
957 const $inner = $(this).find(".king-addons-post-sharing-inner");
958 $inner.find("a").removeClass("king-addons-no-transition-delay").css({
959 opacity: "0",
960 top: "-5px",
961 });
962 setTimeout(() => $inner.css("visibility", "hidden"), $inner.find("a").length * 100);
963 });
964 }
965 }
966
967 // Setup media hover behavior and overlay clickable links.
968 function mediaHoverLink() {
969 const $wrap = $grid.find(".king-addons-grid-image-wrap");
970 if ($wrap.data("img-on-hover") === "yes") {
971 $grid.find(".king-addons-grid-media-wrap").hover(
972 function () {
973 const $secondImg = $(this).find("img:nth-of-type(2)");
974 if ($secondImg.attr("src")) {
975 $(this).find("img:first-of-type").addClass("king-addons-hidden-img");
976 $secondImg.removeClass("king-addons-hidden-img");
977 }
978 },
979 function () {
980 const $secondImg = $(this).find("img:nth-of-type(2)");
981 if ($secondImg.attr("src")) {
982 $secondImg.addClass("king-addons-hidden-img");
983 $(this).find("img:first-of-type").removeClass("king-addons-hidden-img");
984 }
985 }
986 );
987 }
988 if ($wrap.attr("data-overlay-link") === "yes" && !isEditor) {
989 $wrap.css("cursor", "pointer").on("click", function (e) {
990 const cn = e.target.className;
991 if (
992 cn.indexOf("inner-block") !== -1 ||
993 cn.indexOf("king-addons-cv-inner") !== -1 ||
994 cn.indexOf("king-addons-grid-media-hover") !== -1
995 ) {
996 e.preventDefault();
997 let itemUrl = $(this)
998 .find(".king-addons-grid-media-hover-bg")
999 .attr("data-url")
1000 .replace("#new_tab", "");
1001 if ($grid.find(".king-addons-grid-item-title a").attr("target") === "_blank") {
1002 window.open(itemUrl, "_blank").focus();
1003 } else {
1004 window.location.href = itemUrl;
1005 }
1006 }
1007 });
1008 }
1009 }
1010
1011 // Initialize lightbox popups.
1012 function lightboxPopup(s) {
1013 if ($scope.find(".king-addons-grid-item-lightbox").length < 0) return;
1014 $grid.find(".king-addons-grid-item-lightbox").each(function () {
1015 const source = $(this).find(".inner-block > span").attr("data-src");
1016 const $article = $(this).closest("article").not(".slick-cloned");
1017 if (!$grid.hasClass("king-addons-media-grid")) {
1018 $article.find(".king-addons-grid-image-wrap").attr("data-src", source);
1019 }
1020 });
1021 $grid.lightGallery(s.lightbox);
1022 $grid.on("onAfterOpen.lg", () => {
1023 $(".lg-outer")
1024 .find(".lg-thumb-item")
1025 .each(function () {
1026 const $img = $(this).find("img");
1027 let src = $img.attr("src");
1028 const extIndex = src.lastIndexOf(".");
1029 const ext = src.slice(extIndex);
1030 const cropIndex = src.lastIndexOf("-");
1031 const cropCandidate = src.substring(cropIndex, extIndex);
1032 const isStandardCrop = /\d{3,}x\d{3,}/.test(cropCandidate);
1033 if (!isStandardCrop && cropCandidate.length > 0) {
1034 src = src.slice(0, extIndex) + "-150x150" + ext;
1035 } else {
1036 src = src.replace(cropCandidate, "-150x150");
1037 }
1038 $img.attr("src", src);
1039 });
1040 });
1041 $grid.on("onAferAppendSlide.lg onAfterSlide.lg", () => {
1042 const download = $("#lg-download").attr("href");
1043 const $controls = $("#lg-actual-size, #lg-zoom-in, #lg-zoom-out, #lg-download");
1044 if (download && download.indexOf("wp-content") === -1) {
1045 $controls.addClass("king-addons-hidden-element");
1046 } else {
1047 $controls.removeClass("king-addons-hidden-element");
1048 }
1049 if (!s.lightbox.autoplay) {
1050 $(".lg-autoplay-button").css({width: "0", height: "0", overflow: "hidden"});
1051 }
1052 });
1053 const $overlay = $scope.find(".king-addons-grid-lightbox-overlay");
1054 if ($overlay.length) {
1055 $scope.find(".king-addons-grid-media-hover-bg").after($overlay.remove());
1056 $scope.find(".king-addons-grid-lightbox-overlay").on("click", function () {
1057 if (!isEditor) {
1058 $(this).closest("article").find(".king-addons-grid-image-wrap").trigger("click");
1059 } else {
1060 alert(
1061 "Lightbox is Disabled in the Editor! Please Preview this Page to see it in action."
1062 );
1063 }
1064 });
1065 } else {
1066 $scope.find(".king-addons-grid-item-lightbox .inner-block > span").on("click", function () {
1067 if (!isEditor) {
1068 $(this).closest("article").find(".king-addons-grid-image-wrap").trigger("click");
1069 } else {
1070 alert(
1071 "Lightbox is Disabled in the Editor! Please Preview this Page to see it in action."
1072 );
1073 }
1074 });
1075 }
1076 }
1077
1078 // Handle post likes.
1079 function postLikes() {
1080 if (!$scope.find(".king-addons-post-like-button").length) return;
1081 $scope.on("click", ".king-addons-post-like-button", function (e) {
1082 e.preventDefault();
1083 const $btn = $(this);
1084 if (!$btn.attr("data-post-id")) return false;
1085 $.ajax({
1086 type: "POST",
1087 url: $btn.attr("data-ajax"),
1088 data: {
1089 action: "king_addons_likes_init",
1090 post_id: $btn.attr("data-post-id"),
1091 nonce: $btn.attr("data-nonce"),
1092 },
1093 beforeSend: () => $btn.fadeTo(500, 0.5),
1094 success: function (response) {
1095 let iconClass = $btn.attr("data-icon");
1096 let countHTML = response.count;
1097 if (!countHTML.replace(/<\/?[^>]+(>|$)/g, "")) {
1098 countHTML = `<span class="king-addons-post-like-count">${$btn.attr("data-text")}</span>`;
1099 $btn.addClass("king-addons-likes-zero");
1100 } else {
1101 $btn.removeClass("king-addons-likes-zero");
1102 }
1103 if ($btn.hasClass("king-addons-already-liked")) {
1104 $btn.prop("title", "Like").removeClass("king-addons-already-liked");
1105 $btn.html(`<i class="${iconClass.replace("fas", "far")}"></i>${countHTML}`);
1106 } else {
1107 $btn.prop("title", "Unlike").addClass("king-addons-already-liked");
1108 $btn.html(`<i class="${iconClass.replace("far", "fas")}"></i>${countHTML}`);
1109 }
1110 $btn.fadeTo(500, 1);
1111 },
1112 });
1113 return false;
1114 });
1115 }
1116
1117 // Animate grid items after layout.
1118 function animateGridItems(filteredItems, s, $grid) {
1119 let initStager = 0,
1120 filterStager = 0,
1121 deepLinkStager = 0;
1122 if (!$grid.hasClass("grid-images-loaded")) $grid.css("opacity", "1");
1123 filteredItems.forEach((item) => {
1124 initStager += s.animation_delay;
1125 $(item.element)
1126 .find(".king-addons-grid-item-inner")
1127 .css({
1128 opacity: "1",
1129 top: "0",
1130 transform: "scale(1)",
1131 transition: `all ${s.animation_duration}s ease-in ${initStager}s`,
1132 });
1133 filterStager += s.filters_animation_delay;
1134 if ($grid.hasClass("grid-images-loaded")) {
1135 $(item.element)
1136 .find(".king-addons-grid-item-inner")
1137 .css({
1138 transition: `all ${s.filters_animation_duration}s ease-in ${filterStager}s`,
1139 });
1140 }
1141 let deepLink = window.location.hash;
1142 if (deepLink.includes("#filter:") && !deepLink.includes("#filter:*")) {
1143 deepLink = deepLink.replace("#filter:", "");
1144 if ($(item.element).hasClass(deepLink)) {
1145 deepLinkStager += s.filters_animation_delay;
1146 $(item.element)
1147 .find(".king-addons-grid-item-inner")
1148 .css({"transition-delay": `${deepLinkStager}s`});
1149 }
1150 }
1151 });
1152 }
1153 },
1154 }),
1155 {$element: $scope}
1156 );
1157 }
1158
1159
1160 gridHooks.forEach((hook) => {
1161 elementorFrontend.hooks.addAction(hook, gridHandler);
1162 });
1163
1164 });
1165 })(jQuery);
1166