PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.0.24
Post Grid Gutenberg Blocks – PostX v5.0.24
5.0.41 5.0.39 5.0.38 5.0.37 5.0.36 5.0.35 5.0.34 5.0.33 5.0.32 5.0.31 5.0.30 5.0.29 5.0.28 5.0.27 5.0.26 5.0.25 5.0.23 5.0.24 5.0.22 5.0.21 5.0.20 5.0.19 5.0.18 5.0.17 2.1.1 All 238 releases
ultimate-post / assets / js / ultp-gallery-block.js

ultp-gallery-block.js in Post Grid Gutenberg Blocks – PostX 5.0.24, at assets/js/ultp-gallery-block.js

674 lines 20.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function ($) {
2 "use strict";
3 let galleryAvailable = $(".wp-block-ultimate-post-gallery").length != 0;
4 $(".wp-block-ultimate-post-gallery").each(function () {
5 const gl = $(this);
6 handleUltpGalleryBlock(gl);
7 handleGalleryLayout(gl);
8 });
9
10 // Run on load and resize
11 const isFront = $("body.postx-admin-page").length == 0;
12 if (isFront && galleryAvailable) {
13 $(window).on("load resize", function () {
14 $(".wp-block-ultimate-post-gallery").each(function () {
15 const gl = $(this);
16 handleGalleryLayout(gl);
17 });
18 });
19 }
20
21 function handleUltpGalleryBlock(element) {
22 const enableLightbox = element.data("lightbox");
23 const lightboxCaption = element.data("caption");
24 const gallery = element.find(".ultp-gallery-item");
25 const lightboxIndicator = element.data("indicators");
26 const lightBox = element.find(".ultp-gallery-lightbox");
27 const loadMoreButton = element.find(".ultp-gallery-loadMore");
28 const zoomIn = element.find(".ultp-gallery-lightbox__zoom-in");
29 const zoomOut = element.find(".ultp-gallery-lightbox__zoom-out");
30 const galleryClose = element.find(".ultp-gallery-lightbox__close");
31 const galleryControl = element.find(".ultp-gallery-lightbox__control");
32 const fullScreen = element.find(".ultp-gallery-lightbox__full-screen");
33 const galleryIndicator = element.find(
34 ".ultp-gallery-lightbox__indicator-control",
35 );
36
37 let galleryIndex = null;
38 let lightboxVisible = true;
39 /*
40 Lightbox Query Start
41 */
42 /* Lightbox Enable Disable */
43 $(document).on("click", function (event) {
44 const $target = $(event.target);
45 const $lightbox = $(".ultp-lightbox");
46
47 const isInsideLightboxControl =
48 $target.closest(
49 ".ultp-gallery-lightbox__control, .ultp-lightbox__left-icon, .ultp-lightbox__right-icon, .ultp-lightbox__img-container, .ultp-lightbox-indicator__item-img",
50 ).length > 0;
51
52 if (!isInsideLightboxControl && !lightboxVisible) {
53 $lightbox.hide();
54 galleryControl.hide();
55 lightboxVisible = true;
56 }
57
58 if ($lightbox.is(":visible")) {
59 lightboxVisible = false;
60 }
61 });
62
63 /* lightbox close */
64 galleryClose.on("click", function () {
65 const lightbox = $(this)
66 .parent()
67 .parent(".ultp-gallery-wrapper")
68 .find(".ultp-lightbox");
69 lightbox.hide();
70 galleryControl.hide();
71 // If Enable Full screen
72 if (document.exitFullscreen) {
73 document.exitFullscreen().catch((err) => {
74 console.error("Failed to exit fullscreen:", err);
75 });
76 } else if (document.webkitExitFullscreen) {
77 document.webkitExitFullscreen();
78 } else if (document.msExitFullscreen) {
79 document.msExitFullscreen();
80 }
81 galleryIndex = null;
82 });
83
84 /* lightbox zoom in */
85 let scale = 1;
86 zoomIn.on("click", function () {
87 scale += 0.5;
88 const mainImg = $(this)
89 .closest(".ultp-gallery-wrapper")
90 .find(".ultp-lightbox__inside img");
91
92 mainImg.css({
93 transform: `scale(${scale})`,
94 });
95 if (scale > 1) {
96 $(".ultp-lightbox__caption").slideUp(300);
97 $(".ultp-lightbox-indicator").slideUp(300);
98 } else if (!$(".ultp-lightbox__caption").is(":visible")) {
99 $(".ultp-lightbox__caption").fadeIn(300);
100 }
101 });
102
103 /* lightbox zoom Out */
104 zoomOut.on("click", function () {
105 scale -= 0.5;
106 const mainImg = $(this)
107 .closest(".ultp-gallery-wrapper")
108 .find(".ultp-lightbox__inside img");
109
110 mainImg.css({
111 transform: `scale(${scale})`,
112 });
113
114 if (!$(".ultp-lightbox__caption").is(":visible") && scale == 1) {
115 $(".ultp-lightbox__caption").fadeIn(300);
116 }
117 if (!$(".ultp-lightbox-indicator").is(":visible") && scale == 1) {
118 $(".ultp-lightbox-indicator").fadeIn(300);
119 }
120 });
121 /* lightbox Indicator */
122 galleryIndicator.off("click").on("click", function (e) {
123 e.stopPropagation();
124 const indicator = $(this)
125 .closest(".ultp-gallery-wrapper")
126 .find(".ultp-lightbox-indicator");
127
128 indicator.slideToggle(300);
129 });
130 /* Full screen enable */
131 fullScreen.on("click", function () {
132 const galleryTab = document.documentElement; // fullscreen whole page
133 const isFullscreen =
134 document.fullscreenElement ||
135 document.webkitFullscreenElement ||
136 document.msFullscreenElement;
137 if (!isFullscreen) {
138 // Enter fullscreen
139 if (galleryTab.webkitRequestFullscreen) {
140 // Safari - no Promise, so just call it
141 galleryTab.webkitRequestFullscreen();
142 } else if (galleryTab.msRequestFullscreen) {
143 // IE11 - no Promise
144 galleryTab.msRequestFullscreen();
145 }
146 } else {
147 // Exit fullscreen
148 if (document.exitFullscreen) {
149 document.exitFullscreen().catch((err) => {
150 console.error("Failed to exit fullscreen:", err);
151 });
152 } else if (document.webkitExitFullscreen) {
153 document.webkitExitFullscreen();
154 } else if (document.msExitFullscreen) {
155 document.msExitFullscreen();
156 }
157 }
158 });
159
160 enableLightbox &&
161 lightBox.each(function () {
162 $(this)
163 .off()
164 .on("click", function (e) {
165 e.stopPropagation();
166 const lightboxSelector = $(this);
167 lightboxVisible = false;
168 const imgId = lightboxSelector.data("id");
169 const imgIndex = lightboxSelector.data("index");
170 const imgSrc = lightboxSelector.data("img");
171 const imgCaption = lightboxSelector
172 .closest(".ultp-gallery-item")
173 .data("caption");
174 const glSelector = lightboxSelector.parent().parent();
175 handleLightBox(
176 lightboxSelector,
177 imgId,
178 imgIndex,
179 imgSrc,
180 imgCaption,
181 glSelector,
182 lightboxCaption,
183 lightboxIndicator,
184 );
185 });
186 });
187
188 $(document).on(
189 "click",
190 ".ultp-lightbox__right-icon, .ultp-lightbox__left-icon",
191 function () {
192 const $this = $(this);
193 const isRight = $this.hasClass("ultp-lightbox__right-icon");
194 const $lightbox = $this.closest(".ultp-lightbox");
195 const $galleryContainer = $this.closest(".ultp-gallery-container");
196 const $indicator = $lightbox.find(".ultp-lightbox-indicator");
197 const $mainImg = $lightbox.find(".ultp-lightbox__img");
198 const imgCaption = $lightbox.find(".ultp-lightbox__caption");
199 const $galleryItems = $galleryContainer.children();
200 const activeIndex = $lightbox.data("index");
201
202 if (galleryIndex == null) {
203 galleryIndex = activeIndex;
204 }
205
206 // Calculate new index
207 galleryIndex = isRight
208 ? (galleryIndex + 1) % $galleryItems.length
209 : (galleryIndex - 1 + $galleryItems.length) % $galleryItems.length;
210
211 const $targetItem = $galleryItems.eq(galleryIndex);
212 const newImgSrc = $targetItem
213 .find(".ultp-gallery-media img")
214 .attr("src");
215 const itemId = $targetItem.data("id");
216 const newCaption = $targetItem.data("caption");
217
218 // Animate image fade out -> change src -> fade in
219 $mainImg.fadeOut(200, function () {
220 $mainImg.attr("src", newImgSrc).fadeIn(200);
221 });
222
223 // Optional: animate caption change as well
224 imgCaption.fadeOut(200, function () {
225 imgCaption.text(newCaption).fadeIn(200);
226 });
227
228 // Update indicator
229 const $activeIndicator = $indicator.children().eq(galleryIndex);
230 $activeIndicator
231 .addClass("lightbox-active")
232 .siblings()
233 .removeClass("lightbox-active");
234
235 if ($activeIndicator.data("id") !== itemId) {
236 $activeIndicator.removeClass("lightbox-active");
237 }
238 },
239 );
240
241 $(document).on("click", ".ultp-lightbox-indicator__item", function () {
242 const indicator = $(this);
243 const getImgSrc = indicator.find("img").attr("src");
244 const lightbox = indicator.closest(".ultp-lightbox");
245 const mainImg = lightbox.find(".ultp-lightbox__img");
246 galleryIndex = indicator.data("index");
247
248 mainImg.fadeOut(200, function () {
249 mainImg.attr("src", getImgSrc).fadeIn(200);
250 });
251 indicator
252 .addClass("lightbox-active")
253 .siblings()
254 .removeClass("lightbox-active");
255 });
256
257 enableLightbox &&
258 !(lightBox.length > 0) &&
259 gallery.off().on("click", function (event) {
260 const downloadButton = $(event.target)
261 .closest("svg")
262 .parent()
263 .is(".ultp-gallery-action a");
264 if (!gallery.find(".ultp-lightbox").is(":visible") && !downloadButton) {
265 const glSelector = $(this);
266 galleryIndex = glSelector.data("index");
267 const imgId = glSelector.data("id");
268 const imgCaption = glSelector.data("caption");
269 const imgIndex = glSelector.data("index");
270 const imgSrc = glSelector.find(".ultp-gallery-media img").attr("src");
271 const appendSelector = glSelector.find(
272 ".ultp-gallery-action-container",
273 );
274 handleLightBox(
275 glSelector,
276 imgId,
277 imgIndex,
278 imgSrc,
279 imgCaption,
280 appendSelector,
281 lightboxCaption,
282 lightboxIndicator,
283 );
284 }
285 });
286 /*
287 Loadmore
288 */
289 const loadMoreCount = loadMoreButton[0]
290 ? Number(
291 getComputedStyle(loadMoreButton[0])
292 .getPropertyValue("--ultp-gallery-count")
293 .trim(),
294 )
295 : "";
296 let rootImgCount = loadMoreCount;
297
298 if (loadMoreButton?.length > 0) {
299 gallery.each(function (index) {
300 if (loadMoreCount < index + 1) {
301 $(this)
302 .find("img")
303 .attr({
304 width: $(this).find("img").width(),
305 height: $(this).find("img").height(),
306 });
307 $(this).css({ display: "none" });
308 }
309 });
310 }
311
312 if (gallery.length <= rootImgCount) {
313 loadMoreButton.css({ display: "none" });
314 }
315 // lightboxSelector,
316 // imgId,
317 // imgIndex,
318 // imgSrc,
319 // imgCaption,
320 // glSelector,
321 // lightboxCaption,
322 // lightboxIndicator;
323 function handleLightBox(
324 selector,
325 imgId,
326 imgIndex,
327 imgSrc,
328 imgCaption,
329 appendSelector,
330 lightboxCaption,
331 lightboxIndicator,
332 ) {
333 galleryControl.css({ display: "flex" });
334 $(".ultp-lightbox").remove();
335 const indicatorHtml = gallery
336 .map(function (index) {
337 const imgSrc = $(this).find("img").attr("src");
338 const caption = $(this).data("caption");
339 if (imgId == $(this).data("id")) {
340 galleryIndex = index;
341 }
342 console.log(imgSrc, "imgSrc");
343 return `<div data-index=${index} class="${imgId == $(this).data("id") ? "ultp-lightbox-indicator__item lightbox-active" : "ultp-lightbox-indicator__item"}" data-id=${$(this).data("id")} data-caption=${caption}><img class="ultp-lightbox-indicator__item-img" src="${imgSrc}" /></div>`;
344 })
345 .get()
346 .join("");
347
348 const galleryData = `<div class="ultp-lightbox" data-id=${imgId} data-index=${imgIndex}>
349 <div class="ultp-lightbox__container">
350 <div class="ultp-lightbox__inside">
351 <div class="ultp-lightbox__left-icon"><svg fill="currentColor"xmlns="http://www.w3.org/2000/svg" viewBox="0 0 49.16 37.25"><path stroke-miterlimit="10" d="M18.157 36.154 2.183 20.179l-.053-.053-1.423-1.423 17.45-17.449a2.05 2.05 0 0 1 2.9 2.9l-12.503 12.5h38.053a2.05 2.05 0 1 1 0 4.1H8.555l12.5 12.5a2.05 2.05 0 1 1-2.9 2.9Z"></path></svg></div>
352 <div class="ultp-lightbox__img-container">
353 <img class="ultp-lightbox__img" src="${imgSrc}" />
354 ${
355 lightboxCaption
356 ? `<span class="ultp-lightbox__caption">${imgCaption}</span>`
357 : ""
358 }
359 </div>
360 <div class="ultp-lightbox__right-icon"><svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 49.16 37.25"><path stroke-miterlimit="10" d="M28.1 36.154a2.048 2.048 0 0 1 0-2.9l12.5-12.5H2.55a2.05 2.05 0 1 1 0-4.1H40.6l-12.5-12.5a2.05 2.05 0 1 1 2.9-2.9l17.45 17.448L31 36.154a2.047 2.047 0 0 1-2.9 0Z"></path></svg></div>
361 </div>
362 ${
363 lightboxIndicator
364 ? `<div class="ultp-lightbox-indicator">${indicatorHtml}</div>`
365 : ""
366 }
367 </div>
368 </div>`;
369 appendSelector.append(galleryData);
370 }
371 }
372
373 function handleGalleryLayout(element) {
374 const $gallery = element;
375 let loader = $gallery.find(".ultp-gallery-loader");
376 let $noItems = $gallery.find(".ultp-no-gallery-message");
377 const container = $gallery.find(".ultp-gallery-container");
378 const filterItem = $gallery.find(".ultp-gallery-filter__item");
379 const loadMoreButton = $gallery.find(".ultp-gallery-loadMore");
380 const tiled = $gallery.find(".ultp-gallery-container.ultp-gallery-tiled");
381
382 const masonry = $gallery.find(
383 ".ultp-gallery-container.ultp-gallery-masonry",
384 );
385
386 const columns = container[0]
387 ? Number(
388 getComputedStyle(container[0])
389 .getPropertyValue("--ultp-gallery-columns")
390 .trim(),
391 )
392 : 3;
393 const gap = container[0]
394 ? Number(
395 getComputedStyle(container[0])
396 .getPropertyValue("--ultp-gallery-gap")
397 .trim(),
398 )
399 : 10;
400 const galleryItems = container.find(".ultp-gallery-item");
401 const loadMoreCount = loadMoreButton[0]
402 ? Number(
403 getComputedStyle(loadMoreButton[0])
404 .getPropertyValue("--ultp-gallery-count")
405 .trim(),
406 )
407 : 0;
408
409 let rootImgCount = loadMoreCount;
410
411 // Add Loader if not present
412 if (loader.length === 0 && (masonry?.length || tiled?.length)) {
413 container.css({ height: "250px", overflow: "hidden" });
414 loader =
415 $(`<div class="ultp-gallery-loader" style="display:none;position:absolute;top:0;left:0;right:0;bottom:0;background:rgb(255 255 255 / 92%);z-index:9; display: flex; align-items:center;justify-content:center;">
416 <div class="spinner" style="border: 4px solid #f3f3f3;border-top: 4px solid #037FFF;border-radius: 50%;width: 30px;height: 30px;animation: spin 0.8s linear infinite;"></div>
417 </div>`);
418 $gallery.css("position", "relative");
419 container.append(loader);
420 }
421
422 // Inject keyframes for spinner if not already added
423 if (!document.getElementById("ultp-spinner-style")) {
424 const spinnerStyle = `<style id="ultp-spinner-style">
425 @keyframes spin {
426 0% { transform: rotate(0deg); }
427 100% { transform: rotate(360deg); }
428 }
429 </style>`;
430 $("head").append(spinnerStyle);
431 }
432
433 // Show loader initially
434 loader.fadeIn(150);
435
436 // Add No Gallery Message
437 if ($noItems.length === 0) {
438 $noItems = $(
439 '<div class="ultp-no-gallery-message">No gallery item found</div>',
440 );
441 container.after($noItems);
442 }
443
444 let loadGallery = function () {};
445
446 function reLayoutTiledGallery() {
447 if (tiled.length === 0) return;
448 const visibleItems = tiled.find(".ultp-gallery-item:visible");
449 tiled.css("visibility", "hidden");
450 visibleItems.each(function () {
451 $(this).css({
452 top: "",
453 left: "",
454 width: "",
455 height: "",
456 position: "",
457 });
458 });
459 requestAnimationFrame(() => {
460 loadGallery(visibleItems);
461 tiled.css("visibility", "visible");
462 });
463 }
464
465 function reLayoutMasonryGallery() {
466 if (masonry.length === 0) return;
467 const containerWidth = masonry.width();
468 const itemWidth = (containerWidth - (columns - 1) * gap) / columns;
469 const visibleItems = masonry.find(".ultp-gallery-item:visible");
470 let columnHeights = new Array(columns).fill(0);
471 visibleItems.each(function () {
472 const $el = $(this);
473 $el.css({ width: itemWidth + "px", position: "absolute" });
474 const minCol = columnHeights.indexOf(Math.min(...columnHeights));
475 const top = columnHeights[minCol];
476 const left = minCol * (itemWidth + gap);
477 $el.stop().animate({ top: `${top}px`, left: `${left}px` }, 300);
478 columnHeights[minCol] += $el.outerHeight(true) + gap;
479 });
480 masonry.css("height", Math.max(...columnHeights) + "px");
481 }
482
483 function updateVisibleItemsByFilter(selectedTag, limit) {
484 let imgCount = 0;
485 let totalMatch = 0;
486 galleryItems.each(function () {
487 const $this = $(this);
488 const tag = $this.data("tag") || "";
489 if (selectedTag === "All" || tag.includes(selectedTag)) {
490 totalMatch++;
491 if (imgCount < limit) {
492 $this.css({ display: "block" });
493 imgCount++;
494 } else {
495 $this.css({ display: "none" });
496 }
497 } else {
498 $this.css({ display: "none" });
499 }
500 });
501 $noItems.toggle(totalMatch === 0);
502 return { visibleCount: imgCount, totalMatch };
503 }
504
505 function showLoader(callback) {
506 loader.fadeIn(150);
507 setTimeout(() => {
508 callback();
509 loader.fadeOut(150);
510 }, 400);
511 }
512
513 filterItem.on("click", function () {
514 const item = $(this);
515 const selectedTag = item.text();
516 item.siblings().removeClass("active-gallery-filter");
517 item.addClass("active-gallery-filter");
518
519 rootImgCount = loadMoreCount;
520
521 showLoader(() => {
522 const { visibleCount, totalMatch } = updateVisibleItemsByFilter(
523 selectedTag,
524 rootImgCount,
525 );
526 loadMoreButton.css({
527 display: visibleCount < totalMatch ? "block" : "none",
528 });
529 reLayoutTiledGallery();
530 reLayoutMasonryGallery();
531 });
532 });
533
534 loadMoreButton.on("click", function (e) {
535 e.preventDefault();
536 const activeTag = $gallery.find(".active-gallery-filter").text() || "All";
537
538 let visibleCount = 0;
539 let totalMatch = 0;
540
541 galleryItems.each(function () {
542 const $this = $(this);
543 const tag = $this.data("tag") || "";
544 if (activeTag === "All" || tag.includes(activeTag)) {
545 totalMatch++;
546 if ($this.is(":visible")) visibleCount++;
547 }
548 });
549
550 const remainder = visibleCount % columns;
551 let itemsToAdd = remainder !== 0 ? columns - remainder : columns;
552 rootImgCount = Math.min(visibleCount + itemsToAdd, totalMatch);
553
554 showLoader(() => {
555 let currentVisible = 0;
556 galleryItems.each(function () {
557 const $this = $(this);
558 const tag = $this.data("tag") || "";
559 if (activeTag === "All" || tag.includes(activeTag)) {
560 $this.css({
561 display: currentVisible < rootImgCount ? "block" : "none",
562 });
563 currentVisible++;
564 } else {
565 $this.css({ display: "none" });
566 }
567 });
568
569 if (rootImgCount >= totalMatch) {
570 loadMoreButton.css({ display: "none" });
571 }
572
573 $noItems.toggle(totalMatch === 0);
574 reLayoutTiledGallery();
575 reLayoutMasonryGallery();
576 });
577 });
578
579 if (tiled.length) {
580 const customHeight = Number(
581 getComputedStyle(container[0])
582 .getPropertyValue("--ultp-gallery-height")
583 .trim(),
584 );
585
586 const rowHeight = customHeight || 300; // FIX ME → the target rendered height
587
588 /* Utility: robust aspect ratio (works before <img> finishes loading) */
589 function getAspect($img) {
590 const el = $img[0];
591 const w = el.naturalWidth || el.width || 1;
592 const h = el.naturalHeight || el.height || 1;
593 return w / h;
594 }
595
596 /* Core layout function */
597 loadGallery = function (visibleItems) {
598 const $container = tiled;
599 const containerW =
600 $container.width() || $container.closest(".ultp-tab-content").width();
601 let cursorTop = 0;
602
603 /* Prepare rows---- */
604 const rows = [];
605 let currentRow = [];
606 let currentRatioSum = 0;
607
608 visibleItems.each(function () {
609 const $item = $(this);
610 const $img = $item.find("img");
611 const ratio = getAspect($img);
612
613 currentRow.push({ $item, ratio });
614 currentRatioSum += ratio;
615
616 if (currentRow.length === columns) {
617 rows.push({ cells: currentRow, ratioSum: currentRatioSum });
618 currentRow = [];
619 currentRatioSum = 0;
620 }
621 });
622
623 /* last partial row (if any) */
624 if (currentRow.length) {
625 rows.push({ cells: currentRow, ratioSum: currentRatioSum });
626 }
627
628 /* Layout every row */
629 rows.forEach((row) => {
630 const gapsTotal = gap * (row.cells.length - 1);
631 const availW = containerW - gapsTotal;
632
633 let cursorLeft = 0;
634 row.cells.forEach(({ $item, ratio }, idx) => {
635 const cellW = availW * (ratio / row.ratioSum);
636
637 $item.css({
638 position: "absolute",
639 top: cursorTop,
640 left: cursorLeft,
641 width: cellW,
642 height: rowHeight, // keeps every image exactly this tall
643 });
644
645 cursorLeft += cellW + gap;
646 });
647
648 cursorTop += rowHeight + gap;
649 });
650 /* set container height once, after all items are positioned */
651 $container.height(cursorTop - gap);
652 };
653 }
654
655 setTimeout(() => {
656 const initialFilter =
657 $gallery
658 .find(".ultp-gallery-filter__item.active-gallery-filter")
659 .text() || "All";
660 const { visibleCount, totalMatch } = updateVisibleItemsByFilter(
661 initialFilter,
662 rootImgCount,
663 );
664 loadMoreButton.css({
665 display: visibleCount < totalMatch ? "block" : "none",
666 });
667 $noItems.toggle(totalMatch === 0);
668 reLayoutTiledGallery();
669 reLayoutMasonryGallery();
670 loader.fadeOut(50);
671 }, 500);
672 }
673 })(jQuery);
674