1/** 2 * MetaSlider Lightbox - LightGallery.js Integration 3 * 4 * Initializes LightGallery.js for MetaSlider when built-in lightbox is enabled 5 * Optimized for smooth fade transitions with consistent settings 6 */ 7(function ($) { 8 'use strict'; 9 10 var isInitializing = false; 11 12 // Initialize when document is ready 13 $(document).ready(function () { 14 // Clean up duplicate auto-lightbox links in MetaSlider slides 15 cleanupDuplicateLightboxLinks(); 16 initLightbox(); 17 }); 18 19 // Also initialize on AJAX complete for dynamic content 20 $(document).ajaxComplete(function () { 21 // Add a small delay to prevent multiple rapid calls 22 setTimeout(function () { 23 cleanupDuplicateLightboxLinks(); 24 initLightbox(); 25 }, 50); 26 }); 27 28 /** 29 * Clean up duplicate auto-lightbox links in MetaSlider slides 30 * This removes ml-auto-lightbox links that are inside MetaSlider slides 31 * since MetaSlider provides its own lightbox buttons 32 */ 33 function cleanupDuplicateLightboxLinks() { 34 // Find all MetaSlider slides that contain auto-lightbox links 35 $('.ms-custom-html, .ms-external, .ms-folder, .ms-vimeo, .ms-youtube, .ms-local-video, .ms-external-video, .ms-postfeed, .ms-layer').each(function() { 36 var $slide = $(this); 37 38 // Remove any ml-auto-lightbox links inside this slide 39 $slide.find('a.ml-auto-lightbox').each(function() { 40 var $autoLink = $(this); 41 var $img = $autoLink.find('img'); 42 43 // If there's an image inside the auto-lightbox link, unwrap it 44 if ($img.length > 0) { 45 $autoLink.replaceWith($img); 46 } else { 47 // If no image, just remove the link wrapper 48 $autoLink.replaceWith($autoLink.contents()); 49 } 50 }); 51 }); 52 53 // Also check for any slides with both ml-auto-lightbox and ml-lightbox-button 54 $('.ml-lightbox-slide').each(function() { 55 var $slide = $(this); 56 57 // If this slide has a lightbox button, remove any auto-lightbox links 58 if ($slide.find('.ml-lightbox-button').length > 0) { 59 $slide.find('a.ml-auto-lightbox').each(function() { 60 var $autoLink = $(this); 61 var $img = $autoLink.find('img'); 62 63 if ($img.length > 0) { 64 $autoLink.replaceWith($img); 65 } else { 66 $autoLink.replaceWith($autoLink.contents()); 67 } 68 }); 69 } 70 }); 71 } 72 73 /** 74 * Get placeholder thumbnail image (SVG data URL) 75 */ 76 function getPlaceholderThumb() 77 { 78 // Create a simple SVG placeholder with a camera icon 79 var svgContent = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="80" viewBox="0 0 100 80" fill="none">' + 80 '<rect width="100" height="80" fill="#f0f0f0" stroke="#ddd" stroke-width="1"/>' + 81 '<path d="M30 25h40c2 0 4 2 4 4v22c0 2-2 4-4 4H30c-2 0-4-2-4-4V29c0-2 2-4 4-4z" fill="#ccc"/>' + 82 '<circle cx="50" cy="40" r="8" fill="#999"/>' + 83 '<circle cx="50" cy="40" r="5" fill="#666"/>' + 84 '<rect x="65" y="30" width="4" height="3" rx="1" fill="#999"/>' + 85 '<text x="50" y="65" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#999">No Image</text>' + 86 '</svg>'; 87 88 return 'data:image/svg+xml;base64,' + btoa(svgContent); 89 } 90 91 /** 92 * Get common LightGallery 2.x settings 93 */ 94 function getCommonLightboxSettings() 95 { 96 return { 97 speed: 350, 98 mode: 'lg-fade', 99 easing: 'ease-in-out', 100 hideBarsDelay: 2000, 101 showBarsAfter: 100, 102 backdropDuration: 300, 103 loadYouTubeThumbnail: true, 104 loadVimeoThumbnail: true, 105 zoom: false, 106 scale: 1, 107 zoomFromOrigin: false,
108 dynamic: false, 109 swipeThreshold: 50, 110 enableSwipe: true, 111 enableDrag: true, 112 preload: 2, 113 allowMediaOverlap: true, 114 animateThumb: true, 115 toggleThumb: true, 116 hideScrollbar: true, 117 loop: true, 118 escKey: true, 119 controls: true, 120 download: false, 121 counter: true, 122 mobileSettings: { 123 controls: true, 124 showCloseIcon: true, 125 download: false 126 } 127 }; 128 } 129 130 /** 131 * Initialize lightbox functionality 132 */ 133 function initLightbox() 134 { 135 // Prevent multiple simultaneous initializations 136 if (isInitializing) { 137 return; 138 } 139 isInitializing = true; 140 141 // Check settings 142 var detectGalleries = false; 143 var detectStandaloneImages = false; 144 var detectVideos = false; 145 146 if (typeof mlLightboxSettings !== 'undefined') { 147 detectGalleries = mlLightboxSettings.detect_all_galleries === true || mlLightboxSettings.detect_all_galleries === '1'; 148 detectStandaloneImages = mlLightboxSettings.detect_all_images === true || mlLightboxSettings.detect_all_images === '1'; 149 detectVideos = mlLightboxSettings.detect_all_videos === true || mlLightboxSettings.detect_all_videos === '1'; 150 } 151 152 // Initialize MetaSlider lightbox 153 initMetaSliderLightbox(); 154 155 // Initialize galleries 156 if (detectGalleries) { 157 initWordPressGalleries(); 158 } 159 160 // Initialize standalone images 161 if (detectStandaloneImages) { 162 initStandaloneImageLightbox(); 163 } 164 165 // Initialize videos 166 if (detectVideos) { 167 initAllVideos(); 168 } 169 170 // Reset initialization flag 171 isInitializing = false; 172 } 173 174 /** 175 * Initialize WordPress galleries 176 */ 177 function initWordPressGalleries() 178 { 179 $('.gallery, .wp-block-gallery, .tiled-gallery').each(function () { 180 var $gallery = $(this); 181 182 // Skip if already initialized 183 if ($gallery.hasClass('lg-initialized')) { 184 return; 185 } 186 187 // Check if this gallery is inside a MetaSlider container 188 var $metaSliderContainer = $gallery.closest('.metaslider, [class*="ml-slider-lightbox-"]'); 189 if ($metaSliderContainer.length > 0) { 190 // Get MetaSlider ID and check if lightbox is disabled 191 var sliderId = $metaSliderContainer.attr('id'); 192 if (sliderId) { 193 var matches = sliderId.match(/(\d+)/); 194 if (matches) { 195 sliderId = matches[1]; 196 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.slider_settings && sliderId) { 197 var sliderSettings = mlLightboxSettings.slider_settings[sliderId]; 198 if (!sliderSettings || !sliderSettings.lightbox_enabled) { 199 return; // Skip if MetaSlider lightbox is disabled 200 } 201 } 202 } 203 } 204 } 205 206 // Only initialize galleries that have been processed by PHP (have lightbox attributes) 207 var $lightboxLinks = $gallery.find('a.ml-auto-lightbox, a[data-src]'); 208 209 if ($lightboxLinks.length > 0 && typeof lightGallery !== 'undefined') { 210 try { 211 // Initialize LightGallery on the gallery container 212 var gallerySettings = getCommonLightboxSettings(); 213 gallerySettings.selector = 'a.ml-auto-lightbox, a[data-src]'; 214 gallerySettings.controls = true; 215 gallerySettings.counter = true; 216 gallerySettings.closable = true; 217 gallerySettings.closeOnTap = true; 218 gallerySettings.hideBarsDelay = 2000; 219 220 lightGallery($gallery[0], gallerySettings); 221 222 223 $gallery.addClass('lg-initialized'); 224 $lightboxLinks.addClass('lg-initialized'); 225 } catch (error) { 226 console.error('MetaSlider Lightbox: Error initializing gallery', error); 227 } 228 } 229 }); 230 } 231 232 /** 233 * Initialize standalone images (not in galleries or sliders) 234 */ 235 function initStandaloneImageLightbox() 236 { 237 // Find standalone images that aren't inside galleries or sliders and have been processed by PHP 238 var $standaloneLinks = $('a.ml-auto-lightbox, a[data-src]').filter(function () { 239 var $link = $(this); 240 var href = $link.attr('href'); 241 242 // Must be an image 243 if (!href || !isImageUrl(href)) { 244 return false;
245 } 246 247 // Check if inside a MetaSlider container (exclude ALL MetaSlider content) 248 var $metaSliderContainer = $link.closest('.metaslider, [class*="ml-slider-lightbox-"], [id*="metaslider_"], .ms-external, .ms-folder, .ms-vimeo, .ms-youtube, .ms-local-video, .ms-external-video, .ms-custom-html, .ms-postfeed, .ms-layer, .youtube, .vimeo, .local-video, .external-video'); 249 if ($metaSliderContainer.length > 0) { 250 return false; // Always skip MetaSlider content - it has its own button handling 251 } 252 253 // Must not be inside a gallery/slider (excluding MetaSlider which we handled above) 254 return $link.closest('.gallery, .wp-block-gallery, .tiled-gallery').length === 0; 255 }); 256 257 if ($standaloneLinks.length > 0 && typeof lightGallery !== 'undefined') { 258 $standaloneLinks.each(function () { 259 var $link = $(this); 260 261 // Skip if already initialized 262 if ($link.hasClass('lg-initialized')) { 263 return; 264 } 265 266 try { 267 // Wrap in container and initialize 268 var $wrapper = $('<div class="ml-standalone-wrapper"></div>'); 269 $link.wrap($wrapper); 270 271 var standaloneSettings = getCommonLightboxSettings(); 272 standaloneSettings.selector = 'a'; 273 standaloneSettings.loop = false; 274 standaloneSettings.controls = false; 275 standaloneSettings.counter = false; 276 277 lightGallery($link.parent()[0], standaloneSettings); 278 279 // Apply theme class if settings are available 280 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.theme) { 281 $link.parent().on('lgAfterOpen', function () { 282 var $lgOuter = $('.lg-outer'); 283 if ($lgOuter.length && mlLightboxSettings.theme !== 'default') { 284 $lgOuter.addClass('lg-' + mlLightboxSettings.theme + '-theme'); 285 } 286 }); 287 } 288 289 $link.addClass('lg-initialized'); 290 } catch (error) { 291 console.error('MetaSlider Lightbox: Error initializing standalone image', error); 292 } 293 }); 294 } 295 } 296 297 /** 298 * Detect Radix theme flex-pauseplay content type and add CSS class 299 */ 300 function detectRadixPausePlayContent() { 301 $('.metaslider.ms-theme-radix').each(function() { 302 var $slider = $(this); 303 var $flexPausePlay = $slider.find('.flex-pauseplay'); 304 305 if ($flexPausePlay.length > 0) { 306 // Check the width of the flex-pauseplay a elements 307 var $pausePlayLinks = $flexPausePlay.find('a'); 308 var isIconOnly = false; 309 310 if ($pausePlayLinks.length > 0) { 311 // Check if any of the links has a width of 30px (icon-only mode) 312 $pausePlayLinks.each(function() { 313 var width = $(this).outerWidth(); 314 if (width === 30) { 315 isIconOnly = true; 316 return false; // break out of each loop 317 } 318 }); 319 } 320 321 if (isIconOnly) { 322 $slider.addClass('ml-radix-icon-only'); 323 } else { 324 $slider.addClass('ml-radix-with-text'); 325 } 326 } 327 }); 328 } 329 330 /** 331 * Initialize LightGallery for MetaSlider instances 332 */ 333 function initMetaSliderLightbox() 334 { 335 // Detect Radix theme content type first 336 detectRadixPausePlayContent(); 337 338 // Find all MetaSlider containers 339 $('.metaslider, [class*="ml-slider-lightbox-"]').each(function () { 340 var $slider = $(this); 341 342 // Skip if already initialized 343 if ($slider.hasClass('lg-initialized')) { 344 return; 345 } 346 347 // Get slider ID from various possible attributes 348 var sliderId = $slider.attr('id'); 349 if (sliderId) { 350 // Extract numeric ID from string like "metaslider_123" 351 var matches = sliderId.match(/(\d+)/); 352 if (matches) { 353 sliderId = matches[1]; 354 } 355 } 356 357 // Check if lightbox is enabled for this specific slider
358 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.slider_settings && sliderId) { 359 var sliderSettings = mlLightboxSettings.slider_settings[sliderId]; 360 if (!sliderSettings || !sliderSettings.lightbox_enabled) { 361 return; // Skip if lightbox is disabled for this slider 362 } 363 } 364 365 // Initialize empty collection - we'll only use our button approach 366 var $lightboxLinks = $(); 367 368 // Handle external images using ms-external class (exclude clones) 369 var $externalImageSlides = $slider.find('.ms-external').not('.clone'); 370 if ($externalImageSlides.length > 0) { 371 $externalImageSlides.each(function () { 372 var $slide = $(this); 373 var $img = $slide.find('img').first(); 374 375 if ($img.length > 0) { 376 var imgSrc = $img.attr('src'); 377 378 // Get MetaSlider lightbox settings 379 var showArrows = true; 380 var showThumbnails = false; 381 382 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 383 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 384 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 385 } 386 387 // Get actual image dimensions for smooth loading 388 var actualWidth = $img.get(0).naturalWidth || $img.width() || $img.attr('width') || $img.data('width') || 1280; 389 var actualHeight = $img.get(0).naturalHeight || $img.height() || $img.attr('height') || $img.data('height') || 720; 390 391 // Add caption from img alt or title 392 var caption = $img.attr('alt') || $img.attr('title') || ''; 393 394 // Create lightbox button using helper function 395 var $lightboxBtn = createLightboxButton(imgSrc, imgSrc, 'Open in Lightbox', { 396 showArrows: showArrows, 397 showThumbnails: showThumbnails, 398 slideType: 'external', 399 caption: caption ? '<p>' + escapeHtml(caption) + '</p>' : null, 400 lgSize: actualWidth + '-' + actualHeight, 401 dataThumb: imgSrc // Add data-thumb for LightGallery 2.x 402 }); 403 404 // Insert button into slide 405 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 406 407 // Add to lightbox links 408 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 409 } 410 }); 411 } 412 413 // Handle Vimeo video slides using ms-vimeo class (exclude clones) 414 var $vimeoSlides = $slider.find('.ms-vimeo').not('.clone'); 415 416 if ($vimeoSlides.length > 0) { 417 $vimeoSlides.each(function () { 418 var $slide = $(this); 419 var $vimeoDiv = $slide.find('div.vimeo[data-url*="vimeo.com"]'); 420 421 if ($vimeoDiv.length > 0) { 422 var dataUrl = $vimeoDiv.attr('data-url'); 423 var vimeoId = $vimeoDiv.attr('data-id'); 424 425 if (!vimeoId) { 426 return; 427 } 428 429 // Get MetaSlider lightbox settings 430 var showArrows = true; 431 var showThumbnails = false; 432 433 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 434 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 435 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 436 } 437 438 // For Vimeo, use the slide's existing image as fallback poster 439 var $slideImg = $slide.find('img').first(); 440 var vimeoPoster = null; 441 if ($slideImg.length > 0) { 442 vimeoPoster = $slideImg.attr('src'); 443 } 444 445 // Create lightbox button using helper function 446 // Note: Vimeo thumbnails plugin will automatically load thumbnails for Vimeo URLs 447 var $lightboxBtn = createLightboxButton('//vimeo.com/' + vimeoId, '//vimeo.com/' + vimeoId, 'Open in Lightbox', { 448 showArrows: showArrows, 449 showThumbnails: showThumbnails, 450 slideType: 'vimeo', 451 lgSize: '1280-720', 452 dataVideo: 'true', 453 poster: vimeoPoster, 454 // Don't set dataThumb - let Vimeo thumbnails plugin handle it automatically 455 }); 456 457 // Insert button into slide 458 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 459 460 // Add to lightbox links 461 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 462 } 463 }); 464 } 465 466 // Handle YouTube video slides using ms-youtube class (exclude clones) 467 var $youtubeSlides = $slider.find('.ms-youtube').not('.clone'); 468 469 if ($youtubeSlides.length > 0) { 470 $youtubeSlides.each(function () { 471 var $slide = $(this); 472 var $youtubeDiv = $slide.find('div.youtube'); 473 474 if ($youtubeDiv.length > 0) { 475 // Try to get video ID from data-id attribute first 476 var youtubeId = $youtubeDiv.attr('data-id'); 477 478 // If no data-id, try to extract from data-url 479 if (!youtubeId) { 480 var dataUrl = $youtubeDiv.attr('data-url'); 481 if (dataUrl) { 482 youtubeId = extractYouTubeId(dataUrl); 483 } 484 } 485 486 if (!youtubeId) { 487 return; 488 } 489 490 // Get MetaSlider lightbox settings 491 var showArrows = true; 492 var showThumbnails = false;
493 494 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 495 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 496 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 497 } 498 499 // Add YouTube thumbnail poster image 500 var youtubePoster = 'https://img.youtube.com/vi/' + youtubeId + '/maxresdefault.jpg'; 501 502 // Create lightbox button using helper function 503 var $lightboxBtn = createLightboxButton('//www.youtube.com/watch?v=' + youtubeId, '//www.youtube.com/watch?v=' + youtubeId, 'Open in Lightbox', { 504 showArrows: showArrows, 505 showThumbnails: showThumbnails, 506 slideType: 'youtube', 507 lgSize: '1280-720', 508 dataVideo: 'true', 509 poster: youtubePoster, 510 dataThumb: youtubePoster // Add data-thumb for LightGallery 2.x 511 }); 512 513 // Insert button into slide 514 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 515 516 // Add to lightbox links 517 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 518 } 519 }); 520 } 521 522 // Handle Local Video slides using ms-local-video class (exclude clones) 523 var $localVideoSlides = $slider.find('.ms-local-video').not('.clone'); 524 525 if ($localVideoSlides.length > 0) { 526 $localVideoSlides.each(function () { 527 var $slide = $(this); 528 var $videoDiv = $slide.find('div.local-video'); 529 530 if ($videoDiv.length > 0) { 531 // Get video sources from data-sources attribute 532 var dataSources = $videoDiv.attr('data-sources'); 533 var videoSources = []; 534 var videoUrl = null; 535 536 if (dataSources) { 537 try { 538 var sources = JSON.parse(dataSources); 539 if (sources && sources.length > 0) { 540 // Use the first source as the primary video URL 541 videoUrl = sources[0].src; 542 543 // Prepare all sources for LightGallery 544 videoSources = sources.map(function (source) { 545 return { 546 'src': source.src, 547 'type': source.type || getVideoType(source.src) 548 }; 549 }); 550 } 551 } catch (e) { 552 console.error('MetaSlider Lightbox: Error parsing local video sources', e); 553 } 554 } 555 556 if (!videoUrl || videoSources.length === 0) { 557 return; 558 } 559 560 // Get MetaSlider lightbox settings 561 var showArrows = true; 562 var showThumbnails = false; 563 564 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 565 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 566 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 567 } 568 569 // Get poster image if available 570 var posterUrl = $videoDiv.attr('data-poster'); 571 572 // Create data-video JSON attribute with Video.js configuration 573 var videoData = { 574 'source': videoSources, 575 'attributes': { 576 'preload': false, 577 'controls': true 578 }, 579 'videojs': true, 580 'videojsOptions': { 581 'fluid': true, 582 'responsive': true, 583 'playsinline': true, 584 'preload': 'none' 585 } 586 }; 587 588 // Don't add poster to Video.js to avoid overlay issues 589 590 // Create lightbox button (special handling for local video) 591 var $lightboxBtn = $('<a class="ml-lightbox-button" data-lg-size="1280-720">Open in Lightbox</a>'); 592 $lightboxBtn.attr('data-lightbox-arrows', showArrows ? 'true' : 'false');
593 $lightboxBtn.attr('data-lightbox-thumbnails', showThumbnails ? 'true' : 'false'); 594 $lightboxBtn.attr('data-slide-type', 'local-video'); 595 $lightboxBtn.attr('data-video', JSON.stringify(videoData)); 596 if (posterUrl) { 597 $lightboxBtn.attr('data-poster', posterUrl); 598 $lightboxBtn.attr('data-thumb', posterUrl); // Add data-thumb for thumbnails 599 } else { 600 $lightboxBtn.attr('data-thumb', getPlaceholderThumb()); // Use placeholder if no poster 601 } 602 603 // Insert button into slide 604 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 605 606 // Add to lightbox links 607 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 608 } 609 }); 610 } 611 612 // Handle External Video slides using ms-external-video class (exclude clones) 613 var $externalVideoSlides = $slider.find('.ms-external-video').not('.clone'); 614 615 if ($externalVideoSlides.length > 0) { 616 $externalVideoSlides.each(function () { 617 var $slide = $(this); 618 var $videoDiv = $slide.find('div.external-video'); 619 620 if ($videoDiv.length > 0) { 621 // Get video sources from data-sources attribute 622 var dataSources = $videoDiv.attr('data-sources'); 623 var videoSources = []; 624 var videoUrl = null; 625 626 if (dataSources) { 627 try { 628 var sources = JSON.parse(dataSources); 629 if (sources && sources.length > 0) { 630 // Use the first source as the primary video URL 631 videoUrl = sources[0].src; 632 633 // Prepare all sources for LightGallery 634 videoSources = sources.map(function (source) { 635 return { 636 'src': source.src, 637 'type': source.type || getVideoType(source.src) 638 }; 639 }); 640 } 641 } catch (e) { 642 console.error('MetaSlider Lightbox: Error parsing video sources', e); 643 } 644 } 645 646 if (!videoUrl || videoSources.length === 0) { 647 return; 648 } 649 650 // Get MetaSlider lightbox settings 651 var showArrows = true; 652 var showThumbnails = false; 653 654 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 655 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 656 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 657 } 658 659 // Get poster image if available 660 var posterUrl = $videoDiv.attr('data-poster'); 661 662 // Create data-video JSON attribute with Video.js configuration 663 var videoData = { 664 'source': videoSources, 665 'attributes': { 666 'preload': false, 667 'controls': true 668 }, 669 'videojs': true, 670 'videojsOptions': { 671 'fluid': true, 672 'responsive': true, 673 'playsinline': true, 674 'preload': 'none' 675 } 676 }; 677 678 // Don't add poster to Video.js to avoid overlay issues 679 680 // Create lightbox button using helper function
681 // For videos with data-video attribute, don't set href/data-src to avoid conflicts 682 var $lightboxBtn = createLightboxButton('', '', 'Open in Lightbox', { 683 showArrows: showArrows, 684 showThumbnails: showThumbnails, 685 slideType: 'external-video', 686 lgSize: '1280-720', 687 dataVideo: JSON.stringify(videoData), 688 dataPoster: posterUrl, 689 dataThumb: posterUrl || getPlaceholderThumb() 690 }); 691 692 // Insert button into slide 693 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 694 695 // Add to lightbox links 696 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 697 } 698 }); 699 } 700 701 // Handle Custom HTML slides using ms-custom-html class (exclude clones) 702 var $customHtmlSlides = $slider.find('.ms-custom-html').not('.clone'); 703 704 if ($customHtmlSlides.length > 0) { 705 $customHtmlSlides.each(function () { 706 var $slide = $(this); 707 708 // Get MetaSlider lightbox settings 709 var showArrows = true; 710 var showThumbnails = false; 711 712 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 713 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 714 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 715 } 716 717 // Get the current slide content for the lightbox 718 var slideContent = $slide.html(); 719 720 // Create lightbox button with placeholder image using helper function 721 var placeholderSrc = getPlaceholderThumb(); 722 var $lightboxBtn = createLightboxButton(placeholderSrc, placeholderSrc, 'Open in Lightbox', { 723 showArrows: showArrows, 724 showThumbnails: showThumbnails, 725 slideType: 'custom-html', 726 caption: slideContent, 727 lgSize: '1280-720', 728 dataThumb: placeholderSrc 729 }); 730 731 // Insert button into slide 732 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 733 734 // Add to lightbox links 735 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 736 }); 737 } 738 739 // Handle Image Folder slides using ms-folder class (exclude clones) 740 var $folderSlides = $slider.find('.ms-folder').not('.clone'); 741 742 if ($folderSlides.length > 0) { 743 $folderSlides.each(function () { 744 var $slide = $(this); 745 746 // Get MetaSlider lightbox settings 747 var showArrows = true; 748 var showThumbnails = false; 749 750 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 751 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 752 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 753 } 754 755 // Find all images in the folder slide 756 var $images = $slide.find('img'); 757 758 if ($images.length > 0) { 759 // Use the first image as representative for the folder 760 var $firstImg = $images.first(); 761 var imgSrc = $firstImg.attr('src'); 762 var imgAlt = $firstImg.attr('alt') || ''; 763 764 // Get actual image dimensions for smooth loading 765 var actualWidth = $firstImg.get(0).naturalWidth || $firstImg.width() || $firstImg.attr('width') || $firstImg.data('width') || 1280; 766 var actualHeight = $firstImg.get(0).naturalHeight || $firstImg.height() || $firstImg.attr('height') || $firstImg.data('height') || 720; 767 768 // Create lightbox button using helper function 769 var $lightboxBtn = createLightboxButton(imgSrc, imgSrc, 'Open in Lightbox', { 770 showArrows: showArrows, 771 showThumbnails: showThumbnails, 772 slideType: 'image-folder', 773 caption: imgAlt ? '<p>' + escapeHtml(imgAlt) + '</p>' : null, 774 lgSize: actualWidth + '-' + actualHeight, 775 dataThumb: imgSrc // Add data-thumb for LightGallery 2.x 776 }); 777 778 // Insert button into slide 779 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 780 781 // Add to lightbox links 782 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 783 } 784 }); 785 } 786 787 // Handle Post Feed slides using ms-postfeed class (exclude clones) 788 var $postFeedSlides = $slider.find('.ms-postfeed').not('.clone'); 789 790 if ($postFeedSlides.length > 0) { 791 $postFeedSlides.each(function () { 792 var $slide = $(this); 793 794 // Get MetaSlider lightbox settings 795 var showArrows = true; 796 var showThumbnails = false;
797 798 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 799 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 800 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 801 } 802 803 // Find the main image in the post feed slide 804 var $img = $slide.find('img').first(); 805 806 if ($img.length > 0) { 807 var imgSrc = $img.attr('src'); 808 809 // Extract caption information from the post feed slide 810 var caption = ''; 811 var $captionWrap = $slide.find('.caption-wrap .caption'); 812 813 if ($captionWrap.length > 0) { 814 // Use the existing caption content 815 caption = $captionWrap.html(); 816 } else { 817 // Fallback to image alt text 818 var imgAlt = $img.attr('alt') || ''; 819 if (imgAlt) { 820 caption = '<p>' + escapeHtml(imgAlt) + '</p>'; 821 } 822 } 823 824 // Get actual image dimensions for smooth loading 825 var actualWidth = $img.get(0).naturalWidth || $img.width() || $img.attr('width') || $img.data('width') || 1280; 826 var actualHeight = $img.get(0).naturalHeight || $img.height() || $img.attr('height') || $img.data('height') || 720; 827 828 // Create lightbox button using helper function 829 var $lightboxBtn = createLightboxButton(imgSrc, imgSrc, 'Open in Lightbox', { 830 showArrows: showArrows, 831 showThumbnails: showThumbnails, 832 slideType: 'post-feed', 833 caption: caption, 834 lgSize: actualWidth + '-' + actualHeight, 835 dataThumb: imgSrc // Add data-thumb for LightGallery 2.x 836 }); 837 838 // Insert button into slide 839 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 840 841 // Add to lightbox links 842 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 843 } 844 }); 845 } 846 847 // Handle Layer slides using ms-layer class (exclude clones) 848 var $layerSlides = $slider.find('.ms-layer').not('.clone'); 849 850 if ($layerSlides.length > 0) { 851 $layerSlides.each(function () { 852 var $slide = $(this); 853 854 // Get MetaSlider lightbox settings 855 var showArrows = true; 856 var showThumbnails = false; 857 858 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 859 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 860 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 861 } 862 863 // Find the background image in the layer slide 864 var $backgroundImg = $slide.find('img.msDefaultImage'); 865 866 if ($backgroundImg.length > 0) { 867 var imgSrc = $backgroundImg.attr('src'); 868 var imgAlt = $backgroundImg.attr('alt') || ''; 869 870 // Get layer content from the overlay 871 var layerContent = ''; 872 var $layerOverlay = $slide.find('.msHtmlOverlay'); 873 874 if ($layerOverlay.length > 0) { 875 // Extract the text content from layers, preserving basic formatting 876 var $layers = $layerOverlay.find('.layer .content'); 877 if ($layers.length > 0) { 878 var layerTexts = []; 879 $layers.each(function () { 880 var $layer = $(this); 881 var layerHtml = $layer.html(); 882 if (layerHtml && layerHtml.trim() !== '') { 883 layerTexts.push('<div class="lightbox-layer-content">' + layerHtml + '</div>'); 884 } 885 }); 886 if (layerTexts.length > 0) { 887 layerContent = '<div class="lightbox-layer-wrapper">' + layerTexts.join('') + '</div>'; 888 } 889 } 890 } 891 892 // Get actual image dimensions 893 var actualWidth = $backgroundImg.get(0).naturalWi
893dth || $backgroundImg.width() || $backgroundImg.attr('width') || $backgroundImg.data('width') || 1280; 894 var actualHeight = $backgroundImg.get(0).naturalHeight || $backgroundImg.height() || $backgroundImg.attr('height') || 720; 895 896 // Combine image alt and layer content for caption 897 var captionHtml = ''; 898 if (imgAlt) { 899 captionHtml += '<p>' + escapeHtml(imgAlt) + '</p>'; 900 } 901 if (layerContent) { 902 captionHtml += layerContent; 903 } 904 905 // Create lightbox button using helper function 906 var $lightboxBtn = createLightboxButton(imgSrc, imgSrc, 'Open in Lightbox', { 907 showArrows: showArrows, 908 showThumbnails: showThumbnails, 909 slideType: 'layer', 910 caption: captionHtml || null, 911 lgSize: actualWidth + '-' + actualHeight, 912 dataThumb: imgSrc // Add data-thumb for LightGallery 2.x 913 }); 914 915 // Insert button into slide 916 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 917 918 // Add to lightbox links 919 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 920 } 921 }); 922 } 923 924 // Handle regular MetaSlider image slides (ms-image or regular slides) 925 var $regularImageSlides = $slider.find('li').not('.clone').filter(function () { 926 var $slide = $(this); 927 // Skip if this slide is already handled by other slide types 928 return !$slide.hasClass('ms-external') && 929 !$slide.hasClass('ms-vimeo') && 930 !$slide.hasClass('ms-youtube') && 931 !$slide.hasClass('ms-external-video') && 932 !$slide.hasClass('ms-local-video') && 933 !$slide.hasClass('ms-custom-html') && 934 !$slide.hasClass('ms-folder') && 935 !$slide.hasClass('ms-postfeed') && 936 !$slide.hasClass('ms-layer'); 937 }); 938 939 if ($regularImageSlides.length > 0) { 940 $regularImageSlides.each(function () { 941 var $slide = $(this); 942 var $img = $slide.find('img').first(); 943 944 if ($img.length > 0) { 945 var imgSrc = $img.attr('src'); 946 947 // Get MetaSlider lightbox settings 948 var showArrows = true; 949 var showThumbnails = false; 950 951 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 952 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 953 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 954 } 955 956 // Add caption from img alt or title 957 var caption = $img.attr('alt') || $img.attr('title') || ''; 958 var captionHtml = caption ? '<p>' + escapeHtml(caption) + '</p>' : null; 959 960 // Get actual image dimensions for smooth loading 961 var actualWidth = $img.get(0).naturalWidth || $img.width() || $img.attr('width') || $img.data('width') || 1280; 962 var actualHeight = $img.get(0).naturalHeight || $img.height() || $img.attr('height') || $img.data('height') || 720; 963 964 // Create lightbox button using helper function 965 var $lightboxBtn = createLightboxButton(imgSrc, imgSrc, 'Open in Lightbox', { 966 showArrows: showArrows, 967 showThumbnails: showThumbnails, 968 slideType: 'image', 969 caption: captionHtml, 970 lgSize: actualWidth + '-' + actualHeight, 971 dataThumb: imgSrc 972 }); 973 974 // Insert button into slide 975 $slide.addClass('ml-lightbox-slide').append($lightboxBtn); 976 977 // Add to lightbox links 978 $lightboxLinks = $lightboxLinks.add($lightboxBtn); 979 } 980 }); 981 } 982 983 984 985 // Only initialize if we have lightbox links 986 if ($lightboxLinks.length > 0) { 987 // Check if lightGallery is already initialized on this slider 988 if ($slider.data('lightgallery') || $slider.hasClass('lg-initialized')) { 989 return; 990 } 991 992 // Check if lightGallery is available 993 if (typeof lightGallery === 'undefined') { 994 console.error('MetaSlider Lightbox: lightGallery is not defined. Make sure the LightGallery library is loaded.'); 995 return; 996 } 997 998 try { 999 var $firstSlide = $slider.find('a[data-src], a[data-video]').first(); 1000 var showArrows = true; 1001 var showThumbnails = false;
1002 1003 if ($firstSlide.length > 0) { 1004 showArrows = $firstSlide.attr('data-lightbox-arrows') !== 'false'; 1005 showThumbnails = $firstSlide.attr('data-lightbox-thumbnails') === 'true'; 1006 } else { 1007 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.metaslider_options) { 1008 showArrows = mlLightboxSettings.metaslider_options.show_arrows !== false; 1009 showThumbnails = mlLightboxSettings.metaslider_options.show_thumbnails === true; 1010 } 1011 } 1012 1013 var metaSliderSettings = getCommonLightboxSettings(); 1014 metaSliderSettings.selector = 'a[data-src]:not(.clone):not(.clone *), a[data-video]:not(.clone):not(.clone *)'; 1015 metaSliderSettings.hideBarsDelay = 6000; 1016 metaSliderSettings.controls = showArrows; 1017 metaSliderSettings.counter = true; 1018 1019 // Set enterprise license key from PHP settings 1020 if (typeof mlLightboxSettings !== 'undefined' && mlLightboxSettings.license_key) { 1021 metaSliderSettings.licenseKey = mlLightboxSettings.license_key; 1022 } 1023 1024 metaSliderSettings.plugins = []; 1025 if (typeof lgVideo !== 'undefined') { 1026 metaSliderSettings.plugins.push(lgVideo); 1027 } 1028 // Check if Video.js is available and configure LightGallery to use it 1029 if (typeof videojs !== 'undefined') { 1030 metaSliderSettings.videojs = true; // Enable Video.js in LightGallery 1031 // Video.js library loaded - LightGallery configured to use Video.js 1032 } 1033 if (showThumbnails && typeof lgThumbnail !== 'undefined') { 1034 metaSliderSettings.plugins.push(lgThumbnail); 1035 metaSliderSettings.thumbnail = true; 1036 metaSliderSettings.thumbWidth = 100; 1037 metaSliderSettings.thumbHeight = "80px"; 1038 metaSliderSettings.thumbMargin = 5; 1039 metaSliderSettings.exThumbImage = 'data-thumb'; 1040 metaSliderSettings.enableThumbDrag = true; 1041 metaSliderSettings.enableThumbSwipe = true; 1042 metaSliderSettings.toggleThumb = true; 1043 metaSliderSettings.alignThumbnails = "middle"; 1044 metaSliderSettings.animateThumb = true; 1045 1046 // Keep thumbnails on one line 1047 metaSliderSettings.thumbContHeight = 80; // Fixed height to prevent wrapping 1048 metaSliderSettings.thumbHeight = "80px"; // Keep consistent height 1049 1050 if (typeof lgVimeoThumbnail !== 'undefined') { 1051 metaSliderSettings.plugins.push(lgVimeoThumbnail); 1052 metaSliderSettings.loadVimeoThumbnail = true; 1053 } else { 1054 metaSliderSettings.loadVimeoThumbnail = false; 1055 } 1056 metaSliderSettings.loadYouTubeThumbnail = true; 1057 } else { 1058 metaSliderSettings.thumbnail = false; 1059 } 1060 metaSliderSettings.thumbImg = function (currentSlide, index) { 1061 var slideElement; 1062 var imgSrc; 1063 if (typeof currentSlide === 'string') { 1064 slideElement = document.querySelector(currentSlide); 1065 } else if (currentSlide && currentSlide.slideElement) { 1066 slideElement = currentSlide.slideElement; 1067 } else if (currentSlide && currentSlide.el) { 1068 slideElement = currentSlide.el; 1069 } else if (currentSlide && currentSlide.tagName) { 1070 slideElement = currentSlide; 1071 } else { 1072 return ''; 1073 } 1074 1075 var $slide = $(slideElement); 1076 1077 var poster = $slide.attr('data-poster'); 1078 var isVideo = $slide.attr('data-video') === 'true'; 1079 imgSrc = $slide.attr('data-src') || $slide.attr('href'); 1080 1081 1082 if (isVideo && poster) { 1083 return poster; 1084 } else if (imgSrc) { 1085 return imgSrc; 1086 } else { 1087 return getPlaceholderThumb(); 1088 } 1089 }; 1090 metaSliderSettings.prevHtml = '<span class="lg-icon lg-icon-prev" aria-label="Previous image"></span>'; 1091 metaSliderSettings.nextHtml = '<span class="lg-icon lg-icon-next" aria-label="Next image"></span>'; 1092 metaSliderSettings.aria = { 1093 slideShow: 'MetaSlider image slideshow', 1094 slide: 'Image {index} of {totalSlides}', 1095 closeGallery: 'Close gallery' 1096 }; 1097 1098 var lgInstance = lightGallery($slider[0], metaSliderSettings); 1099 1100 $slider.addClass('lg-initialized'); 1101 1102 if (showThumbnails) { 1103 $slider.on('lgAfterOpen', function () { 1104 setTimeout(function () { 1105 $lightboxLinks.each(function (index) { 1106 var $link = $(this); 1107 var $thumbnail = $('.lg-thumb-item').eq(index); 1108 1109 if ($link.attr('data-video') === 'true') { 1110 $thumbnail.attr('data-video', 'true'); 1111 if ($link.attr('data-poster')) { 1112 $thumbnail.attr('data-poster', $link.attr('data-poster')); 1113 } 1114 } 1115 }); 1116 }, 100); 1117 }); 1118 } 1119 1120 $slider.addClass('lg-initialized'); 1121 $slider.addClass('ml-lightgallery-active'); 1122 } catch (error) { 1123 console.error('MetaSlider Lightbox: Error initializing LightGallery', error); 1124 } 1125 } 1126 }); 1127 } 1128 1129 /** 1130 * Check if URL is an image 1131 * @param {string} url 1132 * @returns {boolean} 1133 */ 1134 function isImageUrl(url) 1135 { 1136 if (!url) { 1137 return false;
1138 } 1139 var imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'tiff']; 1140 var extension = url.split('.').pop().toLowerCase().split('?')[0].split('#')[0]; 1141 return imageExtensions.indexOf(extension) !== -1; 1142 } 1143 1144 /** 1145 * Extract YouTube video ID from URL 1146 * @param {string} url 1147 * @returns {string|null} 1148 */ 1149 function extractYouTubeId(url) 1150 { 1151 if (!url) { 1152 return null; 1153 } 1154 1155 // Match various YouTube URL formats 1156 var youtubeRegex = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/; 1157 var match = url.match(youtubeRegex); 1158 1159 if (match && match[2] && match[2].length === 11) { 1160 return match[2]; 1161 } 1162 1163 return null; 1164 } 1165 1166 /** 1167 * Get video MIME type from file extension 1168 * @param {string} url 1169 * @returns {string} 1170 */ 1171 function getVideoType(url) 1172 { 1173 if (!url) { 1174 return 'video/mp4'; // Default fallback 1175 } 1176 1177 var extension = url.split('.').pop().toLowerCase().split('?')[0].split('#')[0]; 1178 1179 switch (extension) { 1180 case 'mp4': 1181 return 'video/mp4'; 1182 case 'webm': 1183 return 'video/webm'; 1184 case 'ogg': 1185 case 'ogv': 1186 return 'video/ogg'; 1187 case 'mov': 1188 return 'video/quicktime'; 1189 case 'avi': 1190 return 'video/x-msvideo'; 1191 case 'm4v': 1192 return 'video/mp4'; 1193 default: 1194 return 'video/mp4'; // Default fallback 1195 } 1196 } 1197 1198 /** 1199 * Escape HTML to prevent XSS 1200 * @param {string} text 1201 * @returns {string} 1202 */ 1203 function escapeHtml(text) 1204 { 1205 var map = { 1206 '&': '&', 1207 '<': '<', 1208 '>': '>', 1209 '"': '"', 1210 "'": ''' 1211 }; 1212 return text.replace(/[&<>"']/g, function (m) { 1213 return map[m]; }); 1214 } 1215 1216 /** 1217 * Create a lightbox button with CSS classes 1218 * @param {string} href The image/video URL 1219 * @param {string} dataSrc The data-src attribute value 1220 * @param {string} buttonText The button text 1221 * @param {object} options Additional options like slideType, caption, etc. 1222 * @returns {jQuery} The button element 1223 */ 1224 function createLightboxButton(href, dataSrc, buttonText, options) 1225 { 1226 options = options || {}; 1227 1228 var $button = $('<a class="ml-lightbox-button" href="' + href + '" data-src="' + dataSrc + '">' + buttonText + '</a>'); 1229 $button.attr({ 1230 'role': 'button', 1231 'tabindex': '0', 1232 'aria-label': 'Open ' + (options.slideType || 'image') + ' in lightbox' 1233 }); 1234 1235 // Add lightbox settings 1236 if (options.showArrows !== undefined) { 1237 $button.attr('data-lightbox-arrows', options.showArrows ? 'true' : 'false'); 1238 } 1239 if (options.showThumbnails !== undefined) { 1240 $button.attr('data-lightbox-thumbnails', options.showThumbnails ? 'true' : 'false'); 1241 } 1242 if (options.slideType) { 1243 $button.attr('data-slide-type', options.slideType); 1244 } 1245 if (options.caption) { 1246 $button.attr('data-sub-html', options.caption); 1247 } 1248 if (options.lgSize) { 1249 $button.attr('data-lg-size', options.lgSize); 1250 } 1251 if (options.dataVideo) { 1252 $button.attr('data-video', options.dataVideo); 1253 } 1254 if (options.poster) { 1255 $button.attr('data-poster', options.poster); 1256 } 1257 if (options.dataThumb) { 1258 $button.attr('data-thumb', options.dataThumb); 1259 } else if (options.showThumbnails !== false) { 1260 // Use placeholder thumbnail if thumbnails are enabled but no specific thumb provided 1261 $button.attr('data-thumb', getPlaceholderThumb()); 1262 } 1263 1264 return $button; 1265 } 1266 1267 /** 1268 * Reinitialize lightbox after MetaSlider updates 1269 */ 1270 $(document).on('metaslider:after:resize metaslider:after:change', function (event, slider) { 1271 if (slider && slider.container) { 1272 var $container = $(slider.container); 1273 if ($container.hasClass('ml-builtin-lightbox') || ($container.attr('class') && $container.attr('class').indexOf('metaslider-built-in-lightbox') !== -1)) { 1274 // Properly destroy existing LightGallery instance 1275 if ($container.data('lightgallery')) { 1276 $container.data('lightgallery').destroy(); 1277 } 1278 1279 // Remove existing initialization markers 1280 $container.removeClass('lg-initialized ml-lightgallery-active'); 1281 1282 // Reinitialize after a short delay 1283 setTimeout(function () { 1284 initLightbox(); 1285 }, 100); 1286 } 1287 } 1288 }); 1289 1290 /** 1291 * Initialize all videos detection 1292 */ 1293 function initAllVideos() 1294 { 1295 // Find all video elements on the page (not inside MetaSlider) 1296 var $videos = $('iframe[src*="youtube.com"], iframe[src*="youtu.be"], iframe[src*="vimeo.com"], video') 1297 .not('.metaslider iframe, .metaslider video') 1298 .not('.ml-lightbox-processed'); 1299 1300 $videos.each(function () { 1301 var $video = $(this); 1302 var $container = $video.closest('div, p, figure, .wp-block-embed, .wp-block-video'); 1303 1304 // Skip if already processed 1305 if ($video.hasClass('ml-lightbox-processed') || $container.find('.ml-lightbox-button').length > 0) { 1306 return; 1307 } 1308 1309 // Mark as processed 1310 $video.addClass('ml-lightbox-processed'); 1311 1312 // Get video URL 1313 var videoUrl = ''; 1314 var videoType = ''; 1315 var posterUrl = ''; 1316 1317 if ($video.is('iframe')) { 1318 var src = $video.attr('src'); 1319 if (src.includes('youtube.com') || src.includes('youtu.be')) { 1320 videoType = 'youtube'; 1321 // Extract YouTube ID 1322 var youtubeId = src.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/); 1323 if (youtubeId) { 1324 videoUrl = 'https://www.youtube.com/watch?v=' + youtubeId[1]; 1325 posterUrl = 'https://img.youtube.com/vi/' + youtubeId[1] + '/maxresdefault.jpg'; 1326 } 1327 } else if (src.includes('vimeo.com')) { 1328 videoType = 'vimeo'; 1329 // Extract Vimeo ID 1330 var vimeoId = src.match(/vimeo\.com\/(?:video\/)?(\d+)/); 1331 if (vimeoId) { 1332 videoUrl = 'https://vimeo.com/' + vimeoId[1]; 1333 } 1334 } 1335 } else if ($video.is('video')) { 1336 videoType = 'external-video'; 1337 videoUrl = $video.attr('src') || $video.find('source').first().attr('src'); 1338 posterUrl = $video.attr('poster'); 1339 } 1340 1341 if (videoUrl) { 1342 // Create lightbox button 1343 var $button = $('<a class="ml-lightbox-button ml-auto-lightbox" title="Open in Lightbox">Open in Lightbox</a>'); 1344 1345 // Add common lightbox attributes 1346 $button.attr('data-lg-size', '1280-720'); 1347 $button.attr('data-lightbox-arrows', 'true');
1348 $button.attr('data-lightbox-thumbnails', 'true'); 1349 $button.attr('data-slide-type', videoType); 1350 1351 if (videoType === 'external-video') { 1352 // For external videos (MP4, etc.), use data-video JSON format 1353 var videoMimeType = 'video/mp4'; 1354 if (videoUrl.includes('.webm')) { 1355 videoMimeType = 'video/webm'; 1356 } else if (videoUrl.includes('.ogg')) { 1357 videoMimeType = 'video/ogg'; 1358 } else if (videoUrl.includes('.mov')) { 1359 videoMimeType = 'video/quicktime'; 1360 } 1361 1362 var videoData = { 1363 source: [{ 1364 src: videoUrl, 1365 type: videoMimeType 1366 }], 1367 attributes: { 1368 preload: false, 1369 controls: true 1370 } 1371 }; 1372 1373 $button.attr('data-video', JSON.stringify(videoData)); 1374 1375 if (posterUrl) { 1376 $button.attr('data-poster', posterUrl); 1377 } 1378 } else { 1379 // For YouTube/Vimeo, use standard format 1380 $button.attr('href', videoUrl); 1381 $button.attr('data-src', videoUrl); 1382 $button.attr('data-video', 'true'); 1383 1384 if (posterUrl) { 1385 $button.attr('data-poster', posterUrl); 1386 } 1387 } 1388 1389 // Position container relatively (CSS class will handle button styling) 1390 $container.css('position', 'relative'); 1391 1392 // Add button to container 1393 $container.append($button); 1394 1395 // Initialize lightbox on the button 1396 if (typeof lightGallery !== 'undefined') { 1397 var settings = getCommonLightboxSettings(); 1398 settings.selector = 'a.ml-auto-lightbox'; 1399 settings.plugins = [ 1400 ...(typeof lgVideo !== 'undefined' ? [lgVideo] : []) 1401 ]; 1402 1403 lightGallery($container[0], settings); 1404 } 1405 } 1406 }); 1407 } 1408 1409})(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.