1/** 2 * Text Image Slide Block - Slick slider and modal 3 * 4 * @package StudioSwift 5 */ 6 7(function($) { 8 'use strict'; 9 10 11 function initFeaturedSlider() { 12 // Check if sliders exist 13 if (!$('.js-featured-slider').length) { 14 return; 15 } 16 17 // Check if Slick is available 18 if (typeof $.fn.slick === 'undefined') { 19 console.error('Slick slider library not loaded'); 20 return; 21 } 22 23 // Initialize each slider 24 $('.js-featured-slider').each(function() { 25 const $slider = $(this); 26 27 // Editor preview: show the first slide as a static block, skip Slick entirely 28 // (avoids all N slides stacking full-height before/if Slick fails to init). 29 if ($slider.hasClass('featured-slider__wrapper--static-preview')) { 30 return; 31 } 32 33 // Skip if already initialized 34 if ($slider.hasClass('slick-initialized')) { 35 return; 36 } 37 38 39 // Get slider settings from data attributes 40 const autoplay = $slider.data('autoplay') === true || $slider.data('autoplay') === 'true'; 41 const autoplaySpeed = parseInt($slider.data('autoplay-speed'), 10) || 3000; 42 const dots = $slider.data('dots') !== false && $slider.data('dots') !== 'false'; 43 const slideToShowDesktop = parseInt($slider.data('slide-to-show-desktop'), 10) || 2; 44 const slideToShowLarger = parseInt($slider.data('slide-to-show-larger-screen'), 10) || 3; 45 const slideTabletRaw = $slider.data('slide-to-show-tablet'); 46 const slideMobileRaw = $slider.data('slide-to-show-mobile'); 47 const slideTabletParsed = parseInt(slideTabletRaw, 10); 48 const slideMobileParsed = parseInt(slideMobileRaw, 10); 49 const slideToShowTablet = (!isNaN(slideTabletParsed) && slideTabletParsed > 0) ? slideTabletParsed : 1; 50 const slideToShowMobile = (!isNaN(slideMobileParsed) && slideMobileParsed > 0) ? slideMobileParsed : slideToShowTablet; 51 // Show arrows on mobile when autoplay is false (manual mode) 52 const showArrowsOnMobile = true; 53 54 // Initialize Slick 55 $slider.slick({ 56 dots: dots, 57 arrows: true, 58 prevArrow: '<button class="slick-prev" aria-label="Previous slide" title="Previous"><svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="0.5" y="0.5" width="47" height="47" rx="23.5" transform="matrix(-4.37114e-08 1 1 4.37114e-08 -2.18557e-08 2.18557e-08)" stroke="#025FD8"/><path d="M27.1151 19.41L22.5351 24L27.1151 28.59L25.7051 30L19.7051 24L25.7051 18L27.1151 19.41Z" fill="#024482"/></svg></button>', 59 nextArrow: '<button class="slick-next" aria-label="Next slide" title="Next"><svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none"><rect x="-0.5" y="-0.5" width="47" height="47" rx="23.5" transform="matrix(-4.37114e-08 -1 -1 4.37114e-08 47 47)" stroke="#025FD8"/><path d="M20.8849 28.59L25.4649 24L20.8849 19.41L22.2949 18L28.2949 24L22.2949 30L20.8849 28.59Z" fill="#024482"/></svg></button>', 60 infinite: true, 61 speed: 1000, 62 autoplaySpeed: autoplaySpeed, 63 autoplay: autoplay, 64 pauseOnHover: true, 65 pauseOnFocus: true, 66 slidesToShow: slideToShowLarger, 67 slidesToScroll: 1, 68 accessibility: true, 69 adaptiveHeight: false, 70 draggable: true, 71 swipe: true, 72 touchMove: true, 73 responsive: [ 74 { 75 breakpoint: 1599, 76 settings: { 77 arrows: showArrowsOnMobile, 78 slidesToShow: slideToShowDesktop, 79 slidesToScroll: 1 80 } 81 }, 82 { 83 breakpoint: 991, 84 settings: { 85 arrows: showArrowsOnMobile, 86 slidesToShow: slideToShowTablet, 87 slidesToScroll: 1 88 } 89 }, 90 { 91 breakpoint: 600, 92 settings: { 93 arrows: showArrowsOnMobile, 94 slidesToShow: slideToShowMobile, 95 slidesToScroll: 1 96 } 97 } 98 ] 99 }); 100 }); 101 } 102 103 /** 104 * Initialize on document ready 105 */ 106 $(document).ready(function() { 107 initFeaturedSlider(); 108 }); 109 110 /** 111 * Re-initialize when new blocks are added (Block Editor) 112 */ 113 if (typeof acf !== 'undefined') { 114 acf.addAction('render_block_preview', function() { 115 // Destroy existing slick instances in preview 116 $('.js-featured-slider.slick-initialized').slick('unslick'); 117 // Re-initialize 118 setTimeout(initFeaturedSlider, 300); 119 }); 120 } 121 122})(jQuery); 123 124/** 125 * Generic modal: .js-modal-btn (data-target = modal id), .js-modal-close, focus trap, Escape. 126 * 127 * @package StudioSwift 128 */ 129(function () { 130 'use strict'; 131 132 const FOCUSABLE_SELECTOR = 133 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not
133([tabindex="-1"])'; 134 135 let activeModal = null; 136 let lastFocus = null; 137 138 function closeModal(modal, options) { 139 const restoreFocus = !options || options.restoreFocus !== false; 140 if (!modal) { 141 return; 142 } 143 modal.classList.remove('is-open'); 144 modal.setAttribute('aria-hidden', 'true'); 145 if (activeModal === modal) { 146 activeModal = null; 147 document.body.style.overflow = ''; 148 } 149 if (restoreFocus) { 150 if (lastFocus && typeof lastFocus.focus === 'function') { 151 lastFocus.focus(); 152 } 153 lastFocus = null; 154 } 155 } 156 157 function openModal(modal, trigger) { 158 if (!modal) { 159 return; 160 } 161 if (activeModal && activeModal !== modal) { 162 closeModal(activeModal, { restoreFocus: false }); 163 } 164 lastFocus = trigger || document.activeElement; 165 activeModal = modal; 166 modal.classList.add('is-open'); 167 modal.setAttribute('aria-hidden', 'false'); 168 document.body.style.overflow = 'hidden'; 169 170 const focusables = modal.querySelectorAll(FOCUSABLE_SELECTOR); 171 const first = focusables[0]; 172 if (first) { 173 requestAnimationFrame(function () { 174 first.focus(); 175 }); 176 } 177 } 178 179 function trapTabInModal(e) { 180 if (e.key !== 'Tab' || !activeModal) { 181 return; 182 } 183 const focusables = activeModal.querySelectorAll(FOCUSABLE_SELECTOR); 184 if (!focusables.length) { 185 return; 186 } 187 const first = focusables[0]; 188 const last = focusables[focusables.length - 1]; 189 if (e.shiftKey) { 190 if (document.activeElement === first) { 191 e.preventDefault(); 192 last.focus(); 193 } 194 } else if (document.activeElement === last) { 195 e.preventDefault(); 196 first.focus(); 197 } 198 } 199 200 document.addEventListener('click', function (e) { 201 const openBtn = e.target.closest('.js-modal-btn'); 202 if (openBtn) { 203 const targetId = openBtn.getAttribute('data-target'); 204 if (!targetId) { 205 return; 206 } 207 const modal = document.getElementById(targetId); 208 if (modal) { 209 e.preventDefault(); 210 openModal(modal, openBtn); 211 } 212 return; 213 } 214 215 const closeEl = e.target.closest('.js-modal-close'); 216 if (closeEl) { 217 const modalEl = closeEl.closest('.modal'); 218 if (modalEl && modalEl.classList.contains('is-open')) { 219 closeModal(modalEl); 220 } 221 } 222 }); 223 224 document.addEventListener('keydown', function (e) { 225 if (e.key === 'Escape' && activeModal) { 226 e.preventDefault(); 227 closeModal(activeModal); 228 return; 229 } 230 trapTabInModal(e); 231 }); 232})();
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.