PageSourceSearch

https://www.justborn.com/wp-content/themes/studioswift/blocks/gallery/script.js

js justborn.com collected 2026-10-02 08:01:11 UTC 6,191 bytes, 216 lines download raw bytes

1(function () {
2	'use strict';
3
4	var SPEED        = 5;   // seconds it takes for ONE slide to scroll past
5	var REVERSE      = false; // false = standard (track moves left), true = reversed
6	function getMaxElevation() {
7		return window.innerWidth >= 768 ? 100 : 26; // desktop: 100px, below 768: 26px
8	}
9
10	function slideIntersectsContainer(containerRect, slideRect) {
11		return slideRect.right > containerRect.left &&
12			slideRect.left < containerRect.right &&
13			slideRect.bottom > containerRect.top &&
14			slideRect.top < containerRect.bottom;
15	}
16
17	function updateSlidesAriaHidden(containerRect, slides) {
18		for (var i = 0; i < slides.length; i++) {
19			var slide = slides[i];
20			var sr = slide.getBoundingClientRect();
21			var visible = slideIntersectsContainer(containerRect, sr);
22			var next = visible ? 'false' : 'true';
23			if (slide.getAttribute('aria-hidden') !== next) {
24				slide.setAttribute('aria-hidden', next);
25			}
26		}
27	}
28
29	function syncMotionToggleUi(toggle, paused) {
30		if (!toggle) return;
31
32		var pauseIcon = toggle.querySelector('.js-gallery-motion-icon-pause');
33		var playIcon = toggle.querySelector('.js-gallery-motion-icon-play');
34		var ariaPlaying = toggle.getAttribute('data-aria-when-playing');
35		var ariaPaused = toggle.getAttribute('data-aria-when-paused');
36
37		toggle.setAttribute('aria-pressed', paused ? 'true' : 'false');
38
39		if (pauseIcon && playIcon) {
40			pauseIcon.hidden = paused;
41			playIcon.hidden = !paused;
42		}
43
44		if (ariaPlaying && ariaPaused) {
45			var label = paused ? ariaPaused : ariaPlaying;
46			toggle.setAttribute('aria-label', label);
47			toggle.setAttribute('title', label);
48		}
49	}
50
51	function initSlider(slider) {
52		var container = slider.closest('.js-gallery-container');
53		if (!container) return;
54
55		// Editor preview: leave slides as a plain static row, skip the marquee/animation entirely.
56		if (container.classList.contains('gallery__container--static')) {
57			return;
58		}
59
60		var galleryRoot = container.closest('.ss.gallery');
61		var motionToggle = galleryRoot ? galleryRoot.querySelector('.js-gallery-motion-toggle') : null;
62
63		var originals  = Array.from(slider.querySelectorAll('.js-gallery-slide'));
64		var slideCount = originals.length;
65		if (!slideCount) return;
66
67		var track = document.createElement('div');
68		track.className = 'gallery__slider-track';
69
70		for (var s = 0; s < 3; s++) {
71			originals.forEach(function (el) {
72				track.appendChild(el.cloneNode(true));
73			});
74		}
75
76		slider.innerHTML = '';
77		slider.appendChild(track);
78
79		var kfId = 'slider-kf-' + (Math.random().toString(36).slice(2));
80		var kfStyle = document.createElement('style');
81		kfStyle.id = kfId;
82		document.head.appendChild(kfStyle);
83
84		function getVisibleCount() {
85			if (window.innerWidth <= 767)   return 1.79;
86			if (window.innerWidth <= 1199)  return 3;
87			return 4.23;
88		}
89
90		var lastInnerWidth;
91		var lastContainerWidth;
92		var motionPaused = false;
93
94		function applyMotionState() {
95			track.style.animationPlayState = motionPaused ? 'paused' : 'running';
96			syncMotionToggleUi(motionToggle, motionPaused);
97		}
98
99		function setMotionPaused(paused) {
100			motionPaused = paused;
101			applyMotionState();
102		}
103
104		function setup() {
105			// Reserve top space so cards stay visible when translated up
106			container.style.paddingTop = getMaxElevation() + 'px';
107
108			var count = getVisibleCount();
109			var cw    = container.offsetWidth;
110			var sw    = cw / count;
111
112			track.querySelectorAll('.js-gallery-slide').forEach(function (s) {
113				s.style.width = sw + 'px';
114			});
115
116			var setW     = sw * slideCount;
117			var duration = SPEED * slideCount;
118
119			var fromX = REVERSE ? -2 * setW : -setW;
120			var toX   = REVERSE ? -setW : -2 * setW;
121
122			kfStyle.textContent =
123				'@keyframes slide-' + kfId + '{' +
124				'from{transform:translateX(' + fromX + 'px)}' +
125				'to{transform:translateX(' + toX + 'px)}' +
126				'}';
127
128			track.style.animation = 'none';
129			void track.offsetHeight;
130			track.style.animation = 'slide-' + kfId + ' ' + duration + 's linear infinite';
131			applyMotionState();
132
133			lastInnerWidth     = window.innerWidth;
134			lastContainerWidth = container.offsetWidth;
135		}
136
137		setup();
138
139		if (motionToggle) {
140			motionToggle.addEventListener('click', function () {
141				setMotionPaused(!motionPaused);
142			});
143		}
144
145		if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
146			setMotionPaused(true);
147		}
148
149		function wave() {
150			var cr   = container.getBoundingClientRect();
151			var cx   = cr.left + cr.width / 2;
152			var half = cr.width * 0.5;
153			var allSlides = track.querySelectorAll('.js-gallery-slide');
154
155			updateSlidesAriaHidden(cr, allSlides);
156
157			for (var i = 0; i < allSlides.length; i++) {
158				var sr = allSlides[i].getBoundingClientRect();
159				var card = allSlides[i].querySelector('.js-gallery-card');
160				if (!card) continue;
161
162				if (sr.right < cr.left - sr.width || sr.left > cr.right + sr.width) {
163					card.style.transform = 'translateY(0)';
164					continue;
165				}
166
167				var midX = sr.left + sr.width / 2;
168				var norm = Math.min(Math.abs(midX - cx) / half, 1);
169				var t    = Math.cos(norm * Math.PI / 2);
170
171				var elevation = getMaxElevation() * t;
172				card.style.transform = 'translateY(-' + elevation + 'px)';
173			}
174
175			requestAnimationFrame(wave);
176		}
177
178		requestAnimationFrame(wave);
179
180		var resizeTimer;
181		window.addEventListener('resize', function () {
182			clearTimeout(resizeTimer);
183			resizeTimer = setTimeout(function () {
184				var iw = window.innerWidth;
185				var cw = container.offsetWidth;
186				if (iw === lastInnerWidth && cw === lastContainerWidth) {
187					return;
188				}
189				setup();
190			}, 150);
191		});
192	}
193
194	function run() {
195		// Scope to gallery block so we don't conflict with other sliders
196		var sliders = document.querySelectorAll('.js-gallery-slider');
197		if (!sliders.length) return;
198		sliders.forEach(function (slider) {
199			if (slider.dataset.galleryInitialized) return;
200			slider.dataset.galleryInitialized = '1';
201			initSlider(slider);
202		});
203	}
204
205	if (document.readyState === 'loading') {
206		document.addEventListener('DOMContentLoaded', run);
207	} else {
208		run();
209	}
210
211	if (typeof window.acf !== 'undefined') {
212		window.acf.addAction('render_block_preview/type=gallery', function () {
213			setTimeout(run, 100);
214		});
215	}
216})();

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.