https://goldenoakranch.com/app/themes/velocity-golden-oak-v2/assets/js/location-gallery.js?ver=2.0.0
1/** 2 * Location Gallery - Swiper Implementation 3 * 4 * Replaces legacy mediaGallery.js (CodeCanyon) with modern Swiper 11. 5 * Features: Main gallery, thumbnails, video support, info panel, controls. 6 * 7 * @package Velocity_Golden_Oak_V2 8 * @since 2.0.0 9 */ 10 11(function($) { 12 'use strict'; 13 14 // Store Swiper instances 15 var galleryInstances = {}; 16 17 /** 18 * Initialize all location galleries on the page 19 */ 20 function initLocationGalleries() { 21 // Check if Swiper is loaded 22 if (typeof Swiper === 'undefined') { 23 console.error('Swiper.js is not loaded'); 24 return; 25 } 26 27 // Check if we have gallery data 28 if (typeof velocityLocationGalleryData === 'undefined') { 29 return; 30 } 31 32 // Initialize each gallery 33 Object.keys(velocityLocationGalleryData).forEach(function(key) { 34 var data = velocityLocationGalleryData[key]; 35 initGallery(data); 36 }); 37 } 38 39 /** 40 * Initialize a single gallery 41 * 42 * @param {Object} data Gallery configuration data 43 */ 44 function initGallery(data) { 45 var mainContainer = document.getElementById(data.mainId); 46 var thumbsContainer = document.getElementById(data.thumbsId); 47 48 if (!mainContainer) { 49 console.error('Gallery container not found:', data.mainId); 50 return; 51 } 52 53 // Initialize thumbnails Swiper first (if exists) 54 // Vertical layout on right side (custom_right style from legacy mediaGallery) 55 var thumbsSwiper = null; 56 if (thumbsContainer) { 57 thumbsSwiper = new Swiper('#' + data.thumbsId, { 58 direction: 'vertical', 59 spaceBetween: 10, 60 slidesPerView: 'auto', 61 freeMode: true, 62 watchSlidesProgress: true, 63 initialSlide: data.startIndex, 64 mousewheel: true, 65 }); 66 } 67 68 // Initialize main Swiper 69 var mainSwiper = new Swiper('#' + data.mainId, { 70 spaceBetween: 0, 71 slidesPerView: 1, 72 initialSlide: data.startIndex, 73 loop: true, // Circular navigation 74 keyboard: { 75 enabled: true, 76 }, 77 // Navigation arrows 78 navigation: { 79 nextEl: '#' + data.mainId + ' .location-nav-next', 80 prevEl: '#' + data.mainId + ' .location-nav-prev', 81 }, 82 // Autoplay (disabled by default, controlled via play button) 83 autoplay: data.autoplay ? { 84 delay: data.autoplayDelay, 85 disableOnInteraction: false, 86 pauseOnMouseEnter: true, 87 } : false, 88 // Thumbs 89 thumbs: thumbsSwiper ? { 90 swiper: thumbsSwiper, 91 } : undefined, 92 // Events 93 on: { 94 slideChange: function() { 95 onSlideChange(this, data); 96 }, 97 init: function() { 98 onSlideChange(this, data); 99 } 100 } 101 }); 102 103 // Store instances 104 galleryInstances[data.mainId] = { 105 main: mainSwiper, 106 thumbs: thumbsSwiper, 107 data: data, 108 isPlaying: data.autoplay 109 }; 110 111 // Set up controls 112 setupControls(data.mainId); 113 114 // Set up info panel 115 setupInfoPanel(data.mainId); 116 117 // Set up video handling 118 setupVideoHandling(data.mainId); 119 120 // Set up thumbnail toggle 121 setupThumbnailToggle(data.mainId); 122 123 console.log('Location gallery initialized:', data.mainId); 124 } 125 126 /** 127 * Handle slide change event 128 * 129 * @param {Swiper} swiper Swiper instance 130 * @param {Object} data Gallery data 131 */ 132 function onSlideChange(swiper, data) { 133 var currentIndex = swiper.realIndex; // Use realIndex for loop mode 134 var $gallery = $(swiper.el).closest('.velocity-location-gallery'); 135 136 // Update counter in gallery 137 $gallery.find('.counter-current').text(currentIndex + 1); 138 139 // Update counter in header (.location-image-counter) 140 $('.location-image-counter .counter-current').text(currentIndex + 1); 141 $('.location-image-counter .counter-total').text(data.imageCount); 142
143 // Update info panel content 144 if (data.images && data.images[currentIndex]) { 145 var image = data.images[currentIndex]; 146 $gallery.find('.info-title').text(image.altText || ''); 147 $gallery.find('.info-description').text(image.description || ''); 148 149 // Update download link 150 if (image.type === 'image' && image.imageUrl) { 151 $gallery.find('.location-gallery-download') 152 .attr('href', image.imageUrl) 153 .show(); 154 } else { 155 $gallery.find('.location-gallery-download').hide(); 156 } 157 } 158 159 // Close info_holder when slide changes (navigation button pressed) 160 var $infoHolder = $('.info_holder'); 161 if ($infoHolder.is(':visible')) { 162 $infoHolder.fadeOut(200); 163 } 164 165 // Pause all videos when changing slides 166 pauseAllVideos($gallery); 167 168 // Auto-play video if current slide is video 169 var $currentSlide = $(swiper.slides[currentIndex]); 170 if ($currentSlide.data('type') === 'video') { 171 var $video = $currentSlide.find('video'); 172 if ($video.length) { 173 $video[0].play().catch(function() { 174 // Auto-play failed, show play button 175 $currentSlide.find('.video-play-overlay').show(); 176 }); 177 } 178 } 179 } 180 181 /** 182 * Set up gallery controls (prev, next, play/pause) 183 * 184 * @param {string} galleryId Main gallery ID 185 */ 186 function setupControls(galleryId) { 187 var instance = galleryInstances[galleryId]; 188 if (!instance) return; 189 190 var $gallery = $('#' + galleryId).closest('.velocity-location-gallery'); 191 192 // Previous button 193 $gallery.find('.control-prev').on('click', function() { 194 instance.main.slidePrev(); 195 }); 196 197 // Next button 198 $gallery.find('.control-next').on('click', function() { 199 instance.main.slideNext(); 200 }); 201 202 // Play/Pause button 203 $gallery.find('.control-play-pause').on('click', function() { 204 var $btn = $(this); 205 206 if (instance.isPlaying) { 207 // Stop autoplay 208 instance.main.autoplay.stop(); 209 instance.isPlaying = false; 210 $btn.find('.play-icon').show(); 211 $btn.find('.pause-icon').hide(); 212 } else { 213 // Start autoplay 214 if (!instance.main.autoplay.running) { 215 instance.main.params.autoplay = { 216 delay: instance.data.autoplayDelay || 3000, 217 disableOnInteraction: false, 218 }; 219 } 220 instance.main.autoplay.start(); 221 instance.isPlaying = true; 222 $btn.find('.play-icon').hide(); 223 $btn.find('.pause-icon').show(); 224 } 225 }); 226 } 227 228 /** 229 * Set up info panel toggle (info_holder) 230 * 231 * @param {string} galleryId Main gallery ID 232 */ 233 function setupInfoPanel(galleryId) { 234 var $infoHolder = $('.info_holder'); 235 var $infoToggleBtn = $('.info-toggle-btn'); 236 237 // Toggle info panel on "i" button click 238 $infoToggleBtn.on('click', function() { 239 if ($infoHolder.is(':visible')) { 240 $infoHolder.fadeOut(200); 241 } else { 242 // Update info content with current slide data 243 updateInfoHolder(galleryId); 244 $infoHolder.fadeIn(200); 245 } 246 }); 247 248 // Close info panel on "x" click 249 $infoHolder.find('.info-holder-close').on('click', function(e) { 250 e.preventDefault(); 251 $infoHolder.fadeOut(200); 252 }); 253 } 254 255 /** 256 * Update info holder with current slide data 257 * 258 * @param {string} galleryId Main gallery ID 259 */ 260 function updateInfoHolder(galleryId) { 261 var instance = galleryInstances[galleryId]; 262 if (!instance) return; 263 264 var $infoHolder = $('.info_holder'); 265 var currentIndex = instance.main.realIndex; 266 var $currentSlide = $(instance.main.slides[instance.main.activeIndex]); 267 268 // Get data from slide attributes 269 var title = $currentSlide.attr('data-title') || ''; 270 var description = $currentSlide.attr('data-description') || ''; 271 272 // Update info holder content 273 $infoHolder.find('.location_img_name').text(title); 274 $infoHolder.find('.location_img_description').html(description); 275 276 // Hide if no description 277 if (!description && !title) { 278 $infoHolder.hide(); 279 } 280 } 281 282 /** 283 * Set up video handling 284 * 285 * @param {string} galleryId Main gallery ID 286 */ 287 function setupVideoHandling(galleryId) { 288 var $gallery = $('#' + galleryId).closest('.velocity-location-gallery'); 289 290 // Video play button click 291 $gallery.find('.video-play-btn').on('click', function() { 292 var $wrapper = $(this).closest('.slide-video-wrapper'); 293 var $video = $wrapper.find('video');
294 var $overlay = $wrapper.find('.video-play-overlay'); 295 296 if ($video.length) { 297 $video[0].play(); 298 $overlay.fadeOut(200); 299 } 300 }); 301 302 // Video ended - show play button again 303 $gallery.find('video').on('ended', function() { 304 $(this).closest('.slide-video-wrapper').find('.video-play-overlay').fadeIn(200); 305 }); 306 307 // Video click to pause/play 308 $gallery.find('video').on('click', function() { 309 if (this.paused) { 310 this.play(); 311 $(this).closest('.slide-video-wrapper').find('.video-play-overlay').fadeOut(200); 312 } else { 313 this.pause(); 314 $(this).closest('.slide-video-wrapper').find('.video-play-overlay').fadeIn(200); 315 } 316 }); 317 } 318 319 /** 320 * Set up thumbnail panel toggle (collapse/expand) 321 * 322 * @param {string} galleryId Main gallery ID 323 */ 324 function setupThumbnailToggle(galleryId) { 325 var $gallery = $('#' + galleryId).closest('.velocity-location-gallery'); 326 var $thumbsWrapper = $gallery.find('.location-gallery-thumbs-wrapper'); 327 var $toggleBtn = $gallery.find('.thumbs-toggle-btn'); 328 var isOpen = true; 329 var originalTop = parseInt($thumbsWrapper.css('top'), 10) || 130; 330 var footerHeight = 60; 331 332 // Main slideshow next arrow position when collapsed must be driven by CSS class: 333 // .location-nav-next uses right: ⦠!important, so jQuery .animate() cannot override it. 334 335 // Minus/plus visibility when collapsed is handled in location-gallery.css (.collapsed). 336 $toggleBtn.on('click', function() { 337 if (isOpen) { 338 // Collapse - slide down above footer 339 $thumbsWrapper.addClass('collapsed'); 340 $thumbsWrapper.css('top', 'auto'); 341 $thumbsWrapper.css('bottom', (footerHeight + 15) + 'px'); 342 $thumbsWrapper.css('height', '48px'); 343 $thumbsWrapper.css('padding', '5px'); 344 345 $gallery.addClass('thumbs-panel-collapsed'); 346 347 isOpen = false; 348 } else { 349 // Expand - slide back up 350 $thumbsWrapper.removeClass('collapsed'); 351 $thumbsWrapper.css('top', originalTop + 'px'); 352 $thumbsWrapper.css('bottom', footerHeight + 'px'); 353 $thumbsWrapper.css('height', 'auto'); 354 $thumbsWrapper.css('padding', '15px'); 355 356 $gallery.removeClass('thumbs-panel-collapsed'); 357 358 isOpen = true; 359 } 360 }); 361 362 // Thumbnail navigation buttons (vertical prev/next for scrolling thumbs) 363 var $prevThumbBtn = $gallery.find('.thumb-nav-prev'); 364 var $nextThumbBtn = $gallery.find('.thumb-nav-next'); 365 // Each thumbnail: 100px height + 8px border (4px*2) + 10px margin = 118px 366 var thumbHeight = 118; 367 var thumbScrollAmount = thumbHeight; // Scroll 1 thumbnail at a time 368 369 // Check if we need navigation buttons (content taller than container) 370 function checkThumbNavigation() { 371 var instance = galleryInstances[galleryId]; 372 if (!instance || !instance.thumbs) return; 373 374 var thumbsSwiper = instance.thumbs; 375 var wrapperEl = thumbsSwiper.wrapperEl; 376 var containerEl = thumbsSwiper.el; 377 378 // Calculate if content overflows 379 var wrapperHeight = wrapperEl.scrollHeight; 380 var containerHeight = containerEl.clientHeight; 381 382 if (wrapperHeight > containerHeight) { 383 // Show navigation buttons 384 $prevThumbBtn.show(); 385 $nextThumbBtn.show(); 386 387 // Update button states 388 updateThumbNavState(thumbsSwiper); 389 } else { 390 // Hide navigation buttons - not needed 391 $prevThumbBtn.hide(); 392 $nextThumbBtn.hide(); 393 } 394 } 395 396 // Update button enabled/disabled state based on scroll position 397 function updateThumbNavState(thumbsSwiper) { 398 var translate = thumbsSwiper.translate; 399 var maxTranslate = thumbsSwiper.maxTranslate(); 400 var minTranslate = thumbsSwiper.minTranslate(); 401 402 // At the top (translate = 0 or close to it) 403 if (translate >= minTranslate - 5) { 404 $prevThumbBtn.addClass('disabled'); 405 } else { 406 $prevThumbBtn.removeClass('disabled'); 407 } 408 409 // At the bottom 410 if (translate <= maxTranslate + 5) { 411 $nextThumbBtn.addClass('disabled'); 412 } else { 413 $nextThumbBtn.removeClass('disabled'); 414 } 415 } 416 417 // Initialize after a short delay to ensure Swiper is ready 418 setTimeout(function() { 419 checkThumbNavigation(); 420 421 var instance = galleryInstances[galleryId]; 422 if (instance && instance.thumbs) { 423 // Listen for scroll/translate changes 424 instance.thumbs.on('setTranslate', function() { 425 updateThumbNavState(this); 426 }); 427 instance.thumbs.on('touchEnd', function() { 428 updateThumbNavState(this); 429 }); 430 } 431 }, 500); 432 433 // Click handlers 434 $prevThumbBtn.on('click', function() { 435 if ($(this).hasClass('disabled')) return; 436 var instance = galleryInstances[galleryId]; 437 if (instance && instance.thumbs) { 438 // Scroll up by ~3 thumbnails 439 var currentTranslate = instance.thumbs.translate; 440 var minTranslate = instance.thumbs.minTranslate(); 441 var newTranslate = Math.min(currentTranslate + thumbScrollAmount, minTranslate); 442 instance.thumbs.translateTo(newTranslate, 300); 443 setTimeout(function() { 444 updateThumbNavState(instance.thumbs); 445 }, 350); 446 } 447 }); 448 449 $nextThumbBtn.on('click', function() { 450 if ($(this).hasClass('disabled')) return; 451 var instance = galleryInstances[galleryId]; 452 if (instance && instance.thumbs) { 453 // Scroll down by ~3 thumbnails 454 var currentTranslate = instance.thumbs.translate; 455 var maxTranslate = instance.thumbs.maxTranslate(); 456 var newTranslate = Math.max(currentTranslate - thumbScrollAmount, maxTranslate); 457 instance.thumbs.translateTo(newTranslate, 300); 458 setTimeout(function() { 459 updateThumbNavState(instance.thumbs); 460 }, 350); 461 } 462 }); 463 } 464 465 /** 466 * Pause all videos in gallery 467 * 468 * @param {jQuery} $gallery Gallery container 469 */ 470 function pauseAllVideos($gallery) { 471 $gallery.find('video').each(function() { 472 this.pause(); 473 }); 474 $gallery.find('.video-play-overlay').show(); 475 } 476 477 // Initialize on DOM ready 478 $(document).ready(function() { 479 initLocationGalleries(); 480 }); 481 482 // Expose for external access 483 window.velocityLocationGallery = { 484 instances: galleryInstances, 485 init: initLocationGalleries 486 }; 487 488})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.