1/** 2 * Glide.js Slider Initialization and Control Logic 3 * Assumes that Glide CSS and JS files are already enqueued (or loaded). 4 */ 5document.addEventListener('DOMContentLoaded', function () { 6 7 // --- Configuration Variables --- 8 const defaultInterval = 3; // seconds 9 // CRITICAL: Must match the Glide animationDuration option (0.5s = 500ms) 10 const ANIMATION_DURATION_MS = 500; 11 12 // --- DOM Element Selectors --- 13 const sliderContainerId = 'my-glide'; 14 const slideListSelector = `#${sliderContainerId} .glide__slides`; 15 const intervalInputId = 'interval-input'; 16 const numImagesInputId = 'num-images-input'; 17 18 // Image Grid Container 19 const imageGridContainerId = 'image-grid-container'; 20 21 // Selector for hidden initial mode input 22 const initialModeInputId = 'initial-view-mode'; 23 24 // NEW SELECTOR for single-option tag filter 25 const singleTagSelectId = 'tag-select-single'; 26 27 // Selector for view selection radio buttons (for runtime switching) 28 const viewSelectionName = 'view-option'; 29 30 // Control Buttons 31 const playButtonId = 'play-slider-button'; 32 const pauseButtonId = 'pause-slider-button'; 33 const loadBtnId = 'load-posts'; 34 const timelapseContainerId = 'timelapse'; 35 const controlsOnlySelector = '.slider-controls-only'; // Selector for Slider-only controls 36 37 // Navigation Controls 38 const nextButtonId = 'next-slide-button'; 39 const prevButtonId = 'prev-slide-button'; 40 41 // Fullscreen Selector 42 const fullscreenButtonId = 'fullscreen-toggle-button'; 43 44 45 // Check if the container exists (prevent errors if shortcode isn't used) 46 const glideElement = document.getElementById(sliderContainerId); 47 if (!glideElement) { 48 console.warn('Glide container (#my-glide) not found. Slider script terminated.'); 49 return; 50 } 51 52 // --- Core Variables --- 53 let glide = null; // Variable to hold the Glide instance 54 let countdownInterval = null; // Variable to hold the timer interval 55 let isPlaying = false; // Custom state variable for play/pause status 56 let currentIntervalMs = defaultInterval * 1000; // Stores the current interval in ms 57 58 // Get the initial view mode from the hidden input, default to 'slider' 59 const initialModeInput = document.getElementById(initialModeInputId); 60 let currentViewMode = initialModeInput ? initialModeInput.value : 'slider'; 61 62 63 // --- Utility Functions --- 64 65 /** 66 * Converts milliseconds into a formatted MM:SS string. 67 */ 68 function formatTime(ms) { 69 const totalSeconds = Math.max(0, Math.floor(ms / 1000)); 70 const minutes = Math.floor(totalSeconds / 60); 71 const seconds = totalSeconds % 60; 72 const formattedMinutes = String(minutes).padStart(2, '0'); 73 const formattedSeconds = String(seconds).padStart(2, '0'); 74 return `${formattedMinutes}:${formattedSeconds}`; 75 } 76 77 // ---------------------------------------------------------------- 78 // --- UTILITY FUNCTION: Random Selection --- 79 // ---------------------------------------------------------------- 80 81 /** 82 * Selects 'count' elements from an array randomly without repetition. 83 * Assumes the input array contains post URLs (or similar data). 84 */ 85 function selectRandomItems(array, count) { 86 if (count >= array.length) { 87 return array; 88 } 89 90 const shuffled = [...array]; 91 92 for (let i = array.length - 1; i > 0; i--) { 93 const j = Math.floor(Math.random() * (i + 1)); 94 [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; // Swap 95 } 96 97 return shuffled.slice(0, count); 98 } 99 100 101 // ---------------------------------------------------------------- 102 // --- Rendering Slides (Slider) --- 103 // ---------------------------------------------------------------- 104 105 /** 106 * Generates the slide HTML from an array of URLs and injects it. 107 */
108 function renderSlidesFromUrls(imageUrls) { 109 const slideList = document.querySelector(slideListSelector); 110 if (!slideList) return; 111 112 let slidesHtml = ''; 113 114 imageUrls.forEach(url => { 115 slidesHtml += ` 116 <li class="glide__slide"> 117 <img src="${url}"> 118 </li> 119 `; 120 }); 121 122 slideList.innerHTML = slidesHtml; 123 } 124 125 // ---------------------------------------------------------------- 126 // --- Rendering Image Grid --- 127 // ---------------------------------------------------------------- 128 129 /** 130 * Generates the grid HTML from an array of URLs and injects it. 131 */ 132 function renderImageGrid(imageUrls) { 133 const gridContainer = document.getElementById(imageGridContainerId); 134 if (!gridContainer) return; 135 136 let gridHtml = ''; 137 138 imageUrls.forEach((url, index) => { 139 gridHtml += ` 140 <div class="grid-item"> 141 <img src="${url}" alt="Gallery Image ${index + 1}" data-full-url="${url}"> 142 </div> 143 `; 144 }); 145 146 gridContainer.innerHTML = gridHtml; 147 148 const images = gridContainer.querySelectorAll('.grid-item img'); 149 images.forEach(img => { 150 img.addEventListener('click', function () { 151 setupLightbox(this.getAttribute('data-full-url')); 152 }); 153 }); 154 } 155 156 157 // ---------------------------------------------------------------- 158 // --- LIGHTBOX LOGIC --- 159 // ---------------------------------------------------------------- 160 161 /** 162 * Shows the lightbox with the selected image. 163 */ 164 function setupLightbox(imageUrl) { 165 let lightbox = document.getElementById('image-lightbox'); 166 167 if (!lightbox) { 168 // If the lightbox doesn't exist, create it dynamically 169 lightbox = document.createElement('div'); 170 lightbox.id = 'image-lightbox'; 171 lightbox.className = 'image-lightbox'; 172 lightbox.innerHTML = ` 173 <div class="lightbox-content"> 174 <button class="lightbox-close-btn" id="lightbox-close-btn"> 175 <i class="fas fa-times"></i> 176 </button> 177 <img src="" alt="Zoomed Image" class="lightbox-img"> 178 </div> 179 `; 180 document.body.appendChild(lightbox); 181 182 // Assign listener to the close button 183 document.getElementById('lightbox-close-btn').addEventListener('click', closeLightbox); 184 // Close on click on the background 185 lightbox.addEventListener('click', function (e) { 186 if (e.target.id === 'image-lightbox') { 187 closeLightbox(); 188 } 189 }); 190 // Close with the ESC key 191 document.addEventListener('keydown', (e) => { 192 if (e.key === 'Escape' && lightbox.classList.contains('is-visible')) { 193 closeLightbox(); 194 } 195 }); 196 } 197 198 const lightboxImg = lightbox.querySelector('.lightbox-img'); 199 lightboxImg.src = imageUrl; 200 lightbox.classList.add('is-visible'); 201 document.body.classList.add('no-scroll'); // Prevent scroll in the background 202 } 203 204 /** 205 * Closes the lightbox. 206 */ 207 function closeLightbox() { 208 const lightbox = document.getElementById('image-lightbox'); 209 if (lightbox) { 210 lightbox.classList.remove('is-visible'); 211 document.body.classList.remove('no-scroll'); 212 } 213 } 214 215 216 // ---------------------------------------------------------------- 217 // --- SYNCHRONIZED TIMER LOGIC --- 218 // ---------------------------------------------------------------- 219 220 /** 221 * Stops the existing countdown timer. 222 */ 223 function stopCountdown() { 224 if (countdownInterval) { 225 clearInterval(countdownInterval); 226 countdownInterval = null; 227 } 228 const timelapseElement = document.getElementById(timelapseContainerId); 229 if (timelapseElement) { 230 timelapseElement.textContent = 'Paused.'; 231 } 232 } 233 234 /** 235 * Starts a new synchronized countdown timer. 236 */ 237 function startCountdown(totalMs) { 238 stopCountdown(); 239 240 const timelapseElement = document.getElementById(timelapseContainerId); 241 242 if (!timelapseElement) { 243 return; 244 } 245 246 if (totalMs <= ANIMATION_DURATION_MS) { 247 timelapseElement.textContent = `Interval too short for visible timer.`; 248 return; 249 } 250 251 let timeRemainingMs = totalMs; 252 // Initial display 253 timelapseElement.textContent = `${formatTime(timeRemainingMs)}`; 254 255 countdownInterval = setInterval(() => { 256 timeRemainingMs -= 1000; 257 258 if (timeRemainingMs <= 0) { 259 // The counter ends and waits for Glide to trigger run.before 260 timeRemainingMs = 0; 261 clearInterval(countdownInterval); 262 countdownInterval = null; 263 return; 264 } 265 266 const currentTimelapseElement = document.getElementById(timelapseContainerId); 267 268 if (currentTimelapseElement) { 269 currentTimelapseElement.textContent = `${formatTime(timeRemainingMs)}`; 270 } else { 271 clearInterval(countdownInterval); 272 countdownInterval = null; 273 } 274 275 }, 1000); 276 } 277 278 // ---------------------------------------------------------------- 279 // --- Main Glide Initialization --- 280 // ---------------------------------------------------------------- 281 282 /** 283 * Initializes or re-initializes the Glide slider. 284 * @param {number} intervalMs - The new interval in milliseconds (used for options and currentIntervalMs). 285 * @param {boolean} startPlaying - Whether to start playing immediately. 286 */ 287 function initializeGlide(intervalMs, startPlaying = false) { 288 const container = document.getElementById(sliderContainerId); 289 if (!container) { 290 console.warn(`Cannot initialize Glide: container #${sliderContainerId} not found.`); 291 return; 292 } 293 294 // Don't initialize Glide if the view is Gallery (early check) 295 if (currentViewMode === 'gallery') { 296 if (glide) { 297 glide.destroy(); 298 glide = null; 299 } 300 stopCountdown(); 301 container.classList.remove('is-visible'); 302 return; 303 } 304 305 // If it already exists, we exit (it should not be re-initialized unless we change the interval in handlePlayClick) 306 if (glide) { 307 isPlaying = startPlaying; 308 updateButtonVisibility(); 309 return; 310 } 311 312 // --- INSTANCE INITIALIZATION --- 313 currentIntervalMs = intervalMs; 314 const options = { 315 type: 'carousel', 316 perView: 1, 317 // We use the 'startPlaying' state only if it is the first initialization 318 autoplay: startPlaying && intervalMs > 0 ? intervalMs : false, 319 focusAt: 'center', 320 gap: 0,
321 animationDuration: ANIMATION_DURATION_MS, 322 hoverpause: false 323 }; 324 325 const totalSlides = document.querySelectorAll(slideListSelector + ' .glide__slide').length; 326 if (totalSlides > 0) { 327 glide = new Glide(`#${sliderContainerId}`, options); 328 glide.mount(); 329 updateBullets(); 330 } else { 331 // If there are no slides, there is nothing to initialize or play. 332 return; 333 } 334 335 // --- AUTOPLAY LISTENERS CONFIGURATION (SINGLE PLACE) --- 336 if (intervalMs > 0) { 337 // Restarts the counter every time the slider moves 338 glide.on('run.before', function () { 339 // Here we use the current interval, synchronized with the Glide transition 340 startCountdown(currentIntervalMs); 341 }); 342 343 // If it starts in autoplay, starts the counter immediately 344 if (options.autoplay) { 345 startCountdown(intervalMs); 346 console.log(`Glide slider started with an initial interval of ${intervalMs}ms.`); 347 } 348 } 349 350 // Synchronize states and buttons 351 isPlaying = startPlaying; 352 updateButtonVisibility(); 353 } 354 355 /** 356 * Generates and inserts the navigation bullets based on the number of slides. 357 */ 358 function updateBullets() { 359 // Note: Bullets container is not included in the PHP shortcode anymore, 360 // so this function might be legacy unless bullets are added back. 361 const bulletsContainer = document.querySelector(`#${sliderContainerId} .glide__bullets`); 362 const totalSlides = document.querySelectorAll(slideListSelector + ' .glide__slide').length; 363 364 if (!bulletsContainer) return; 365 366 let bulletsHtml = ''; 367 for (let i = 0; i < totalSlides; i++) { 368 bulletsHtml += `<button class="glide__bullet" data-glide-dir="=${i}"></button>`; 369 } 370 bulletsContainer.innerHTML = bulletsHtml; 371 } 372 373 /** 374 * Toggles the visibility of the Play and Pause buttons based on the isPlaying state. 375 */ 376 function updateButtonVisibility() { 377 const playButton = document.getElementById(playButtonId); 378 const pauseButton = document.getElementById(pauseButtonId); 379 if (playButton && pauseButton) { 380 playButton.style.display = isPlaying ? 'none' : 'flex'; 381 pauseButton.style.display = isPlaying ? 'flex' : 'none'; 382 } 383 384 // Controls that are ONLY visible in Slider mode (Timelapse/Play/Pause/Nav) 385 const topControls = document.querySelector(`#${sliderContainerId} .custom-controls-top`); 386 const bottomControls = document.querySelector(`#${sliderContainerId} .custom-controls-bottom`); 387 const timelapse = document.getElementById(timelapseContainerId); 388 389 if (currentViewMode === 'gallery') { 390 if (topControls) topControls.style.display = 'none'; 391 if (bottomControls) bottomControls.style.display = 'none'; 392 if (timelapse) timelapse.style.display = 'none'; 393 } else { 394 if (topControls) topControls.style.display = 'flex'; 395 if (bottomControls) bottomControls.style.display = 'flex'; 396 if (timelapse) timelapse.style.display = 'block'; 397 } 398 399 } 400 401 402 // ---------------------------------------------------------------- 403 // --- Control Handlers --- 404 // ---------------------------------------------------------------- 405 406 /** 407 * Handles the click on the 'Play' button. (Resume/Restart) 408 */ 409 function handlePlayClick() { 410 if (currentViewMode === 'gallery') { 411 console.warn('Play button is only active in Slider mode.'); 412 return; 413 } 414 415 const intervalInput = document.getElementById(intervalInputId); 416 const intervalSeconds = parseFloat(intervalInput.value); 417 const newIntervalMs = intervalSeconds * 1000; 418 419 const totalSlides = document.querySelectorAll(slideListSelector + ' .glide__slide').length; 420 if (totalSlides === 0 || document.querySelector('.loading-slide')) { 421 console.error('Cannot start play: Slider is empty or still loading. Please load images first.'); 422 return; 423 } 424 425 if (isNaN(intervalSeconds) || intervalSeconds <= 0) { 426 console.error('Please enter a valid time (seconds) greater than 0.'); 427 return; 428 } 429 430 // 1. If the slider does not exist, we initialize it and start playing. 431 if (!glide) { 432 console.log('Glide not initialized. Re-initializing now.'); 433 initializeGlide(newIntervalMs, true); 434 return; 435 } 436 437 // 2. If the interval has changed, we must destroy and re-initialize to apply the new configuration. 438 if (newIntervalMs !== currentIntervalMs) { 439 console.log('Interval changed. Destroying and re-initializing to apply new autoplay duration.'); 440 if (glide) { 441 glide.destroy(); 442 glide = null; 443 } 444 // This initializes with the new interval and starts playing. 445 initializeGlide(newIntervalMs, true); 446 return; 447 } 448 449 450 // 3. IF THE SLIDER EXISTS AND THE INTERVAL HAS NOT CHANGED, WE SIMPLY RESUME/RESTART THE CYCLE. 451 if (glide && !isPlaying) { 452 // Resumes playback. Glide will trigger 'run.before' before the next transition. 453 glide.play(currentIntervalMs); 454 isPlaying = true; 455 updateButtonVisibility(); 456 // We start the countdown to immediately synchronize with the current slide 457 startCountdown(currentIntervalMs); 458 console.log('Glide slider resumed via control button.'); 459 } else if (glide && isPlaying) { 460 // If it is already playing, restart the cycle (moves to the next slide). 461 // This forces Glide to trigger 'run.before' immediately after the pause. 462 glide.pause(); 463 glide.play(currentIntervalMs); 464 // We start the countdown to immediately synchronize with the current slide 465 startCountdown(currentIntervalMs); 466 console.log('Glide slider cycle restarted (synced).'); 467 } 468 } 469 470 471 /** 472 * Pauses the slider and stops the countdown timer. 473 */ 474 function handlePauseClick() { 475 if (glide) { 476 glide.pause(); 477 isPlaying = false; 478 updateButtonVisibility(); 479 stopCountdown(); 480 console.log('Glide slider paused via control button.'); 481 } 482 } 483 484 485 /** 486 * Handles the click on the 'Load posts' button, including view selection. 487 */ 488 function handleLoadClick() { 489 490 // The view is determined by the current state, which might have been set by the radio buttons change listener 491 const selectedView = currentViewMode; 492 493 const sliderContainer = document.getElementById(sliderContainerId); 494 const gridContainer = document.getElementById(imageGridContainerId); 495
496 // --- 2. Prepare containers before AJAX --- 497 // When loading new images, ALWAYS DESTROY the previous instance 498 if (glide) { 499 console.log('Destroying old Glide instance before AJAX...'); 500 glide.destroy(); 501 glide = null; 502 } 503 stopCountdown(); 504 isPlaying = false; 505 updateButtonVisibility(); 506 507 // Hide both and then show the spinner in the ACTIVE container 508 sliderContainer.classList.remove('is-visible'); 509 gridContainer.classList.remove('is-visible'); 510 511 512 if (selectedView === 'slider') { 513 sliderContainer.classList.add('is-visible'); 514 const slideList = document.querySelector(slideListSelector); 515 if (slideList) { 516 slideList.innerHTML = ` 517 <li class="glide__slide loading-slide"> 518 <i class="fas fa-spinner fa-spin"></i> 519 <p>Loading images for slider...</p> 520 </li> 521 `; 522 } 523 } else { // 'gallery' 524 gridContainer.classList.add('is-visible'); 525 gridContainer.innerHTML = '<p class="loading-message"><i class="fas fa-spinner fa-spin"></i> Loading images for grid...</p>'; 526 } 527 528 529 // --- 3. Get values and make the AJAX call --- 530 531 // --- MODIFICATION: Read value from the SINGLE OPTION SELECT --- 532 const tagSelect = document.getElementById(singleTagSelectId); 533 let selectedTagValue = ''; 534 535 if (tagSelect && tagSelect.value !== "") { 536 selectedTagValue = tagSelect.value; 537 } 538 539 // Create the tags array. If a value was selected, it is added to the array; otherwise, it is an empty array. 540 const selected_tags = selectedTagValue ? [selectedTagValue] : []; 541 const current_tag = JSON.stringify(selected_tags); 542 // --- END MODIFICATION --- 543 544 545 const current_page = 1; 546 547 const perPageInput = document.getElementById(numImagesInputId); 548 let requestedCount = parseInt(perPageInput.value); 549 550 if (isNaN(requestedCount) || requestedCount < 1) { 551 requestedCount = 5; 552 } 553 const perPage = requestedCount; 554 555 // Obtener el intervalo actual para el futuro initializeGlide 556 const intervalInput = document.getElementById(intervalInputId); 557 currentIntervalMs = parseFloat(intervalInput.value) * 1000; 558 559 560 jQuery.ajax({ 561 type: "POST", 562 url: ajax_object.ajax_url, 563 dataType: "json", 564 data: { 565 action: "f1_advanced_search", 566 security: ajax_object.nonce, 567 tag: current_tag, // Ahora solo contiene un ID o un array vacÃo 568 paged: current_page, 569 ppp: perPage, 570 }, 571 success: function success(res) { 572 573 console.log('Response: ', res); 574 575 // Ensure the final container visibility is correct 576 if (selectedView === 'slider') { 577 gridContainer.classList.remove('is-visible'); 578 sliderContainer.classList.add('is-visible'); 579 } else { 580 sliderContainer.classList.remove('is-visible'); 581 gridContainer.classList.add('is-visible'); 582 } 583 584 585 if (res.posts && res.posts.length > 0) { 586 587 // Note: Selecting random items ensures we only use 'requestedCount' items 588 const imagesToRender = selectRandomItems(res.posts, requestedCount); 589 590 if (imagesToRender.length > 0) { 591 592 // Render according to the selected view 593 if (selectedView === 'slider') { 594 renderSlidesFromUrls(imagesToRender); 595 // Initializes paused (autoplay: false). The user must press Play 596 initializeGlide(currentIntervalMs, false); 597 } else { 598 renderImageGrid(imagesToRender); 599 } 600 601 } else { 602 // Message for No posts (Empty Array) 603 const messageHtml = 'No posts found matching your criteria.'; 604 if (selectedView === 'slider') { 605 const slideList = document.querySelector(slideListSelector); 606 if (slideList) { 607 slideList.innerHTML = `<li class="glide__slide message-slide">${messageHtml}</li>`; 608 } 609 // Re-initializes to clear listeners and show the message in the slider 610 initializeGlide(0, false); 611 } else { 612 gridContainer.innerHTML = `<p class="message-slide">${messageHtml}</p>`; 613 } 614 } 615 616 } else { 617 // No posts. (Empty server response) 618 const messageHtml = 'No posts found.'; 619 if (selectedView === 'slider') { 620 const slideList = document.querySelector(slideListSelector); 621 if (slideList) { 622 slideList.innerHTML = `<li class="glide__slide message-slide">${messageHtml}</li>`; 623 } 624 // Re-initializes to clear listeners and show the message in the slider 625 initializeGlide(0, false); 626 } else { 627 gridContainer.innerHTML = `<p class="message-slide">${messageHtml}</p>`; 628 } 629 } 630 }, 631 error: function (xhr, status, error) { 632 console.error("AJAX Error:", status, error); 633 634 // Ensure error container visibility 635 if (selectedView === 'slider') { 636 gridContainer.classList.remove('is-visible'); 637 sliderContainer.classList.add('is-visible'); 638 } else { 639 sliderContainer.classList.remove('is-visible'); 640 gridContainer.classList.add('is-visible'); 641 } 642 643 // Display error in the visible container 644 const errorHtml = `Error loading content: ${error}`; 645 if (selectedView === 'slider') { 646 const slideList = document.querySelector(slideListSelector); 647 if (slideList) { 648 slideList.innerHTML = `<li class="glide__slide error-slide">${errorHtml}</li>`; 649 } 650 initializeGlide(0, false); 651 } else { 652 gridContainer.innerHTML = `<p class="error-slide">${errorHtml}</p>`; 653 } 654 } 655 });
656 } 657 658 659 /** 660 * Moves to the next slide. 661 */ 662 function handleNextClick() { 663 if (glide) { 664 glide.go('>'); 665 // Manual navigation stops the timer if it's currently paused 666 if (!isPlaying) { 667 stopCountdown(); 668 } 669 } 670 } 671 672 /** 673 * Moves to the previous slide. 674 */ 675 function handlePrevClick() { 676 if (glide) { 677 glide.go('<'); 678 // Manual navigation stops the timer if it's currently paused 679 if (!isPlaying) { 680 stopCountdown(); 681 } 682 } 683 } 684 685 686 // --- FULLSCREEN LOGIC --- 687 688 689 /** 690 * Toggles in or out of fullscreen mode. 691 */ 692 function toggleFullscreen() { 693 // Only allow fullscreen in Slider mode 694 if (currentViewMode !== 'slider') { 695 console.warn('Fullscreen is only available in Slider mode.'); 696 return; 697 } 698 699 const slider = document.getElementById(sliderContainerId); 700 const isFullscreen = document.fullscreenElement; 701 702 if (!isFullscreen) { 703 if (slider.requestFullscreen) { 704 slider.requestFullscreen(); 705 } else if (slider.webkitRequestFullscreen) { /* Safari */ 706 slider.webkitRequestFullscreen(); 707 } else if (slider.msRequestFullscreen) { /* IE11 */ 708 slider.msRequestFullscreen(); 709 } 710 } else { 711 if (document.exitFullscreen) { 712 document.exitFullscreen(); 713 } else if (document.webkitExitFullscreen) { /* Safari */ 714 document.webkitExitFullscreen(); 715 } else if (document.msExitFullscreen) { /* IE11 */ 716 document.msExitFullscreen(); 717 } 718 } 719 720 if (glide) { 721 // Update Glide dimensions after fullscreen change 722 setTimeout(() => { 723 glide.update(); 724 }, 50); 725 } 726 } 727 728 /** 729 * Global listener to handle exiting fullscreen and updating the icon. 730 */ 731 document.addEventListener('fullscreenchange', function () { 732 const isFullscreen = document.fullscreenElement; 733 const fullscreenButton = document.getElementById(fullscreenButtonId); 734 735 if (fullscreenButton) { 736 const icon = fullscreenButton.querySelector('i'); 737 if (icon) { 738 icon.className = isFullscreen ? 'fas fa-compress-alt' : 'fas fa-expand-alt'; 739 } 740 } 741 if (glide) { 742 setTimeout(() => { 743 glide.update(); 744 }, 50); 745 } 746 }); 747 748 749 // ---------------------------------------------------------------- 750 // --- Setup and Event Listeners --- 751 // ---------------------------------------------------------------- 752 753 754 /** 755 * RE-ASSIGNMENT OF LISTENERS 756 */ 757 function assignControlListeners() { 758 const playButton = document.getElementById(playButtonId); 759 const pauseButton = document.getElementById(pauseButtonId); 760 const nextButton = document.getElementById(nextButtonId); 761 const prevButton = document.getElementById(prevButtonId); 762 const fullscreenButton = document.getElementById(fullscreenButtonId); 763 764 // Play/Pause/Nav Controls 765 if (playButton) { 766 playButton.addEventListener('click', handlePlayClick); 767 } 768 if (pauseButton) { 769 pauseButton.addEventListener('click', handlePauseClick); 770 } 771 if (prevButton) { 772 prevButton.addEventListener('click', handlePrevClick); 773 } 774 if (nextButton) { 775 nextButton.addEventListener('click', handleNextClick); 776 } 777 778 // Fullscreen 779 if (fullscreenButton) { 780 fullscreenButton.addEventListener('click', toggleFullscreen); 781 } 782 783 // Listen for changes in view radios to ensure control visibility and container display 784 const viewRadios = document.querySelectorAll(`input[name="${viewSelectionName}"]`); 785 viewRadios.forEach(radio => { 786 radio.addEventListener('change', function () { 787 const newViewMode = this.value; 788 currentViewMode = newViewMode; // Update global state 789 790 const isSlider = newViewMode === 'slider'; 791 792 // Toggle visibility of slider-specific controls (Interval Time)
793 document.querySelectorAll(controlsOnlySelector).forEach(control => { 794 control.style.display = isSlider ? 'block' : 'none'; 795 }); 796 797 // Toggle main container visibility 798 const sliderContainer = document.getElementById(sliderContainerId); 799 const gridContainer = document.getElementById(imageGridContainerId); 800 801 if (isSlider) { 802 sliderContainer.classList.add('is-visible'); 803 gridContainer.classList.remove('is-visible'); 804 } else { 805 sliderContainer.classList.remove('is-visible'); 806 gridContainer.classList.add('is-visible'); 807 } 808 809 810 // If switching to Gallery, ensure the slider is destroyed and countdown stopped 811 if (!isSlider && glide) { 812 glide.destroy(); 813 glide = null; 814 stopCountdown(); 815 isPlaying = false; 816 updateButtonVisibility(); 817 } 818 819 // If switching back to Slider, ensure the controls are correct and placeholder is set 820 if (isSlider) { 821 // Placeholder message (if not already initialized by Load Images) 822 if (!glide) { 823 const slideList = document.querySelector(slideListSelector); 824 if (slideList && slideList.children.length === 0) { 825 slideList.innerHTML = `<li class="glide__slide message-slide">Use the controls above and click "Load Images" to begin.</li>`; 826 } 827 } 828 // Re-run initialization to set up buttons and potential autoplay if image data exists 829 initializeGlide(currentIntervalMs, false); 830 } 831 832 updateButtonVisibility(); // Update Play/Pause/Nav controls visibility based on new mode 833 834 }); 835 }); 836 } 837 838 839 // 1. Assign listeners to configuration controls 840 const intervalInput = document.getElementById(intervalInputId); 841 const loadBtn = document.getElementById(loadBtnId); 842 843 if (intervalInput) { 844 intervalInput.value = defaultInterval; 845 } 846 847 if (loadBtn) { 848 loadBtn.addEventListener('click', handleLoadClick); 849 } 850 851 // 2. Assign listeners to navigation and Fullscreen controls (initial load) 852 assignControlListeners(); 853 854 855 // 3. Initial Load: Based on initialMode (from PHP shortcode attribute) 856 // Ensure the interval is set based on the default or user input 857 currentIntervalMs = defaultInterval * 1000; 858 859 if (currentViewMode === 'slider') { 860 // Initialize slider (empty and paused) 861 const slideList = document.querySelector(slideListSelector); 862 if (slideList) { 863 // Only inject placeholder if it wasn't already done by PHP (which is fine) 864 } 865 initializeGlide(currentIntervalMs, false); 866 } else { 867 // If 'gallery' (grid), ensure controls are hidden and the grid is visible 868 const gridContainer = document.getElementById(imageGridContainerId); 869 if (gridContainer) { 870 // Grid is already visible due to PHP class insertion, add placeholder message 871 gridContainer.innerHTML = `<p class="message-slide">Use the controls above and click "Load Images" to begin.</p>`; 872 } 873 updateButtonVisibility(); // Hide slider controls 874 } 875});
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.