PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / 1.0.2
FrontBlocks for Gutenberg/GeneratePress v1.0.2
trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / includes / dist / frontblocks-gallery.js
frontblocks / includes / dist Last commit date
frontblocks-advanced-option.js 10 months ago frontblocks-animation-option.js 11 months ago frontblocks-animations-min.js 11 months ago frontblocks-animations.css 11 months ago frontblocks-animations.js 11 months ago frontblocks-carousel-min.js 11 months ago frontblocks-carousel.css 11 months ago frontblocks-carousel.js 11 months ago frontblocks-gallery-min.js 11 months ago frontblocks-gallery-option.js 11 months ago frontblocks-gallery.css 11 months ago frontblocks-gallery.js 11 months ago frontblocks-insert-post-option.js 11 months ago frontblocks-insert-post.css 11 months ago frontblocks-sticky-column-min.js 11 months ago frontblocks-sticky-column-option.js 11 months ago frontblocks-sticky-column.css 11 months ago frontblocks-sticky-column.js 11 months ago glide.min.js 1 year ago masonry.min.js 11 months ago
frontblocks-gallery.js
324 lines
1 window.addEventListener('load', function() {
2 initFrontBlocksGallery();
3 });
4
5 function initFrontBlocksGallery() {
6 initMasonryGalleries();
7 initGridGalleries();
8 initLightbox();
9 }
10
11 function initMasonryGalleries() {
12 const masonryGalleries = document.querySelectorAll('.frontblocks-gallery-masonry');
13
14 masonryGalleries.forEach(function(gallery) {
15 const columns = parseInt(gallery.getAttribute('data-columns')) || 3;
16 const gutter = parseInt(gallery.getAttribute('data-gutter')) || 20;
17 setupMasonryLayout(gallery, columns, gutter);
18 });
19 }
20
21 function initGridGalleries() {
22 const gridGalleries = document.querySelectorAll('.frontblocks-gallery-grid');
23
24 gridGalleries.forEach(function(gallery) {
25 const columns = parseInt(gallery.getAttribute('data-columns')) || 3;
26 const gutter = parseInt(gallery.getAttribute('data-gutter')) || 20;
27 // Set up grid layout
28 setupGridLayout(gallery, columns, gutter);
29 });
30 }
31
32 function setupGridLayout(gallery, columns, gutter) {
33 gallery.style.setProperty('--frontblocks-gutter', gutter + 'px');
34 }
35
36 function setupMasonryLayout(gallery, columns, gutter) {
37 // Set container styles for masonry
38 gallery.style.position = 'relative';
39 gallery.style.display = 'block';
40 gallery.style.width = '100%';
41 gallery.style.setProperty('--frontblocks-gutter', gutter + 'px');
42
43 // Get all figure elements
44 const figures = gallery.querySelectorAll('figure.wp-block-image');
45 if (figures.length === 0) return;
46
47 // Calculate optimal column count based on available width and breakpoints
48 const containerWidth = gallery.offsetWidth;
49 const minColumnWidth = 200;
50 const totalGutterWidth = gutter * (columns - 1);
51 const availableWidthForColumns = containerWidth - totalGutterWidth;
52 const actualColumnWidth = availableWidthForColumns / columns;
53
54 let optimalColumns = getResponsiveColumns(columns, containerWidth);
55
56 // If columns are too narrow, reduce the number of columns
57 if (actualColumnWidth < minColumnWidth) {
58 optimalColumns = Math.max(1, Math.floor((containerWidth + gutter) / (minColumnWidth + gutter)));
59 }
60
61 // Only set margin-bottom for gutter spacing
62 figures.forEach(function(figure) {
63 figure.style.margin = '0';
64 figure.style.marginBottom = gutter + 'px';
65 // Do NOT set width, float, or clear - let Masonry handle this
66 });
67
68 // Initialize Masonry with calculated column width
69 const masonry = new Masonry(gallery, {
70 itemSelector: 'figure.wp-block-image',
71 columnWidth: '.wp-block-image', // Let Masonry calculate column width
72 gutter: gutter,
73 percentPosition: true,
74 transitionDuration: '0.3s'
75 });
76
77 // Layout Masonry after images load
78 const images = gallery.querySelectorAll('img');
79 let loadedImages = 0;
80
81 if (images.length === 0) {
82 masonry.layout();
83 return;
84 }
85
86 images.forEach(function(img) {
87 if (img.complete) {
88 loadedImages++;
89 if (loadedImages === images.length) {
90 masonry.layout();
91 }
92 } else {
93 img.addEventListener('load', function() {
94 loadedImages++;
95 if (loadedImages === images.length) {
96 masonry.layout();
97 }
98 });
99 }
100 });
101
102 // Layout immediately if all images are already loaded
103 if (loadedImages === images.length) {
104 masonry.layout();
105 }
106
107 // Store masonry instance for resize handling
108 gallery.masonryInstance = masonry;
109
110 // Handle window resize
111 window.addEventListener('resize', function() {
112 if (gallery.masonryInstance) {
113 gallery.masonryInstance.layout();
114 }
115 });
116 }
117
118 function getResponsiveColumns(originalColumns, containerWidth) {
119 // Responsive breakpoints
120 if (containerWidth <= 480) {
121 return Math.min(originalColumns, 2);
122 } else if (containerWidth <= 768) {
123 return Math.min(originalColumns, 3);
124 } else if (containerWidth <= 1024) {
125 return Math.min(originalColumns, 4);
126 } else {
127 return originalColumns;
128 }
129 }
130
131 function initLightbox() {
132 // Create lightbox container if it doesn't exist
133 let lightbox = document.getElementById('frontblocks-lightbox');
134 if (!lightbox) {
135 lightbox = createLightbox();
136 document.body.appendChild(lightbox);
137 }
138
139 // Add click event listeners to gallery images
140 const galleries = document.querySelectorAll('.frontblocks-gallery-masonry, .frontblocks-gallery-grid');
141
142 galleries.forEach(function(gallery) {
143 const enableLightbox = gallery.getAttribute('data-lightbox') === 'true';
144
145 if (enableLightbox) {
146 const images = gallery.querySelectorAll('img');
147 images.forEach(function(img, index) {
148 img.style.cursor = 'pointer';
149 img.addEventListener('click', function(e) {
150 e.preventDefault();
151 openLightbox(images, index);
152 });
153 });
154 }
155 });
156 }
157
158 function createLightbox() {
159 const lightbox = document.createElement('div');
160 lightbox.id = 'frontblocks-lightbox';
161 lightbox.className = 'frontblocks-lightbox';
162
163 lightbox.innerHTML = `
164 <div class="frontblocks-lightbox-content">
165 <button class="frontblocks-lightbox-close">&times;</button>
166 <img src="" alt="">
167 <div class="frontblocks-lightbox-caption"></div>
168 <div class="frontblocks-lightbox-counter"></div>
169 <button class="frontblocks-lightbox-nav frontblocks-lightbox-prev">&lt;</button>
170 <button class="frontblocks-lightbox-nav frontblocks-lightbox-next">&gt;</button>
171 </div>
172 `;
173
174 // Add event listeners
175 const closeBtn = lightbox.querySelector('.frontblocks-lightbox-close');
176 const prevBtn = lightbox.querySelector('.frontblocks-lightbox-prev');
177 const nextBtn = lightbox.querySelector('.frontblocks-lightbox-next');
178
179 closeBtn.addEventListener('click', closeLightbox);
180 prevBtn.addEventListener('click', showPreviousImage);
181 nextBtn.addEventListener('click', showNextImage);
182
183 // Close on background click
184 lightbox.addEventListener('click', function(e) {
185 if (e.target === lightbox) {
186 closeLightbox();
187 }
188 });
189
190 // Close on escape key
191 document.addEventListener('keydown', function(e) {
192 if (e.key === 'Escape' && lightbox.classList.contains('active')) {
193 closeLightbox();
194 } else if (e.key === 'ArrowLeft' && lightbox.classList.contains('active')) {
195 showPreviousImage();
196 } else if (e.key === 'ArrowRight' && lightbox.classList.contains('active')) {
197 showNextImage();
198 }
199 });
200
201 return lightbox;
202 }
203
204 let currentImages = [];
205 let currentImageIndex = 0;
206
207 function openLightbox(images, index) {
208 currentImages = Array.from(images);
209 currentImageIndex = index;
210
211 const lightbox = document.getElementById('frontblocks-lightbox');
212 const lightboxImg = lightbox.querySelector('img');
213
214 // Set image source
215 lightboxImg.src = currentImages[currentImageIndex].src;
216 lightboxImg.alt = currentImages[currentImageIndex].alt || '';
217
218 // Show lightbox
219 lightbox.classList.add('active');
220 document.body.style.overflow = 'hidden';
221
222 // Update caption and counter
223 updateLightboxCaption();
224 updateLightboxCounter();
225 updateLightboxNavigation();
226 }
227
228 function closeLightbox() {
229 const lightbox = document.getElementById('frontblocks-lightbox');
230 lightbox.classList.remove('active');
231 document.body.style.overflow = '';
232
233 currentImages = [];
234 currentImageIndex = 0;
235 }
236
237 function showPreviousImage() {
238 if (currentImages.length === 0) return;
239
240 currentImageIndex = (currentImageIndex - 1 + currentImages.length) % currentImages.length;
241 updateLightboxImage();
242 }
243
244 function showNextImage() {
245 if (currentImages.length === 0) return;
246
247 currentImageIndex = (currentImageIndex + 1) % currentImages.length;
248 updateLightboxImage();
249 }
250
251 function updateLightboxImage() {
252 const lightbox = document.getElementById('frontblocks-lightbox');
253 const lightboxImg = lightbox.querySelector('img');
254
255 lightboxImg.src = currentImages[currentImageIndex].src;
256 lightboxImg.alt = currentImages[currentImageIndex].alt || '';
257
258 updateLightboxCaption();
259 updateLightboxCounter();
260 updateLightboxNavigation();
261 }
262
263 function updateLightboxCaption() {
264 const lightbox = document.getElementById('frontblocks-lightbox');
265 const captionElement = lightbox.querySelector('.frontblocks-lightbox-caption');
266
267 if (currentImages.length === 0) {
268 captionElement.textContent = '';
269 return;
270 }
271
272 const currentImage = currentImages[currentImageIndex];
273 let caption = '';
274
275 // Try to get caption from different sources
276 if (currentImage.alt && currentImage.alt.trim() !== '') {
277 caption = currentImage.alt;
278 } else if (currentImage.title && currentImage.title.trim() !== '') {
279 caption = currentImage.title;
280 } else {
281 // Try to get caption from parent figure element
282 const figure = currentImage.closest('figure');
283 if (figure) {
284 const figcaption = figure.querySelector('figcaption');
285 if (figcaption) {
286 caption = figcaption.textContent.trim();
287 }
288 }
289 }
290
291 captionElement.textContent = caption;
292 captionElement.style.display = caption ? 'block' : 'none';
293 }
294
295 function updateLightboxCounter() {
296 const lightbox = document.getElementById('frontblocks-lightbox');
297 const counterElement = lightbox.querySelector('.frontblocks-lightbox-counter');
298
299 if (currentImages.length <= 1) {
300 counterElement.style.display = 'none';
301 return;
302 }
303
304 counterElement.style.display = 'block';
305 counterElement.textContent = `${currentImageIndex + 1} / ${currentImages.length}`;
306 }
307
308 function updateLightboxNavigation() {
309 const lightbox = document.getElementById('frontblocks-lightbox');
310 const prevBtn = lightbox.querySelector('.frontblocks-lightbox-prev');
311 const nextBtn = lightbox.querySelector('.frontblocks-lightbox-next');
312
313 const showNav = currentImages.length > 1;
314 prevBtn.style.display = showNav ? 'block' : 'none';
315 nextBtn.style.display = showNav ? 'block' : 'none';
316 }
317
318 // Handle dynamic content loading (for AJAX-loaded galleries)
319 function reinitFrontBlocksGallery() {
320 initFrontBlocksGallery();
321 }
322
323 // Expose function globally for external use
324 window.reinitFrontBlocksGallery = reinitFrontBlocksGallery;