PageSourceSearch

https://www.justborn.com/wp-content/themes/studioswift/blocks/text-image-slide/script.js

js justborn.com collected 2026-10-02 08:01:13 UTC 6,842 bytes, 232 lines download raw bytes

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.