PageSourceSearch

https://latepoint.com/wp-content/plugins/spectra-blocks/assets/js/responsive-videos.js?ver=1790924713

js latepoint.com collected 2026-10-02 14:34:44 UTC 6,241 bytes, 206 lines download raw bytes

1/**
2 * Responsive Videos Handler.
3 *
4 * Handles switching video sources based on viewport size for responsive video backgrounds.
5 * Works with both container and slider blocks that have responsive video backgrounds.
6 *
7 * @since x.x.x
8 */
9
10( function () {
11	'use strict';
12
13	// Fallback only: Spectra's historical breakpoints, used when the bands the
14	// stylesheet was generated with are not on the page.
15	const FALLBACK_BREAKPOINTS = {
16		'base': 1024,      // Desktop: 1024px and above.
17		'@tablet': 768,  // Tablet: 768px to 1023.98px.
18		'@mobile': 0,    // Mobile: 0px to 767.98px.
19	};
20
21	/**
22	 * Get the device whose band the viewport is in right now.
23	 *
24	 * The bands are the media queries the per-device CSS was generated with,
25	 * published by the plugin as `window.spectraBlocksViewportBands`
26	 * ({ desktop, tablet, mobile }). Matching against them keeps this switch in
27	 * step with the stylesheet whatever the breakpoints are — core's defaults,
28	 * the theme's, or Spectra's. The width comparison is only reached when that
29	 * data is absent.
30	 *
31	 * @return {string} Device key: 'base', '@tablet', or '@mobile'.
32	 */
33	function getCurrentDevice() {
34		const bands = window.spectraBlocksViewportBands;
35
36		if ( bands && typeof window.matchMedia === 'function' ) {
37			const mobile = bands.mobile ? window.matchMedia( bands.mobile ) : null;
38			const tablet = bands.tablet ? window.matchMedia( bands.tablet ) : null;
39			// A query this browser cannot parse reports `not all`; fall back to widths.
40			const parsable = ( mq ) => mq && mq.media !== 'not all';
41			if ( parsable( mobile ) && parsable( tablet ) ) {
42				if ( mobile.matches ) {
43					return '@mobile';
44				}
45				if ( tablet.matches ) {
46					return '@tablet';
47				}
48				return 'base';
49			}
50		}
51
52		const width = window.innerWidth;
53
54		if ( width >= FALLBACK_BREAKPOINTS.base ) {
55			return 'base';
56		} else if ( width >= FALLBACK_BREAKPOINTS[ '@tablet' ] ) {
57			return '@tablet';
58		}
59		return '@mobile';
60	}
61
62	/**
63	 * Update video source based on current device and available responsive videos.
64	 *
65	 * @param {HTMLElement} container The container element with data-responsive-videos attribute.
66	 */
67	function updateVideoSource( container ) {
68		const video = container.querySelector( '.spectra-background-video__wrapper video' );
69
70		if ( ! video ) {
71			return;
72		}
73
74		const responsiveVideosData = container.getAttribute( 'data-responsive-videos' );
75
76		if ( ! responsiveVideosData ) {
77			return;
78		}
79
80		let responsiveVideos;
81		try {
82			responsiveVideos = JSON.parse( responsiveVideosData );
83		} catch ( error ) {
84			return;
85		}
86
87		const currentDevice = getCurrentDevice();
88		
89		// Check if device actually changed.
90		const lastDevice = container.getAttribute( 'data-last-device' );
91		if ( lastDevice === currentDevice ) {
92			return;
93		}
94		
95		// Each viewport resolves over base only — core's inheritance model,
96		// the same one the CSS generator follows. Legacy tablet-only videos
97		// reach '@mobile' through the baked cascade before PHP emits the JSON.
98		const fallbackOrder = {
99			'@mobile': [ '@mobile', 'base' ],
100			'@tablet': [ '@tablet', 'base' ],
101			'base': [ 'base' ],
102		};
103
104		// Find the appropriate video URL using fallback hierarchy.
105		let videoUrl = null;
106		const deviceOrder = fallbackOrder[ currentDevice ] || [ 'base' ];
107
108		for ( const device of deviceOrder ) {
109			// An explicit empty entry means "this band has no video" (its
110			// background is an image or none) and must not fall back to base.
111			if ( Object.prototype.hasOwnProperty.call( responsiveVideos, device ) ) {
112				videoUrl = responsiveVideos[ device ] || null;
113				break;
114			}
115		}
116
117		const source = video.querySelector( 'source' );
118		const currentSrc = source ? source.getAttribute( 'src' ) : video.getAttribute( 'src' );
119
120		if ( ! videoUrl ) {
121			// This band has no video. The element is still in the DOM (hidden by
122			// the band's CSS), so stop it and drop its source: with a source
123			// attached the browser keeps the file buffered for a width that
124			// never shows it.
125			video.pause();
126			if ( currentSrc ) {
127				if ( source ) {
128					source.removeAttribute( 'src' );
129				} else {
130					video.removeAttribute( 'src' );
131				}
132				video.load();
133			}
134		} else {
135			if ( currentSrc !== videoUrl ) {
136				if ( source ) {
137					source.src = videoUrl;
138				} else {
139					video.src = videoUrl;
140				}
141				video.load();
142			}
143			// PHP renders without `autoplay` (and with `preload="none"`) when some
144			// band has no video, so the browser does not fetch the file for a
145			// width that never shows it; this band does, so start it here.
146			if ( video.paused ) {
147				const playing = video.play();
148				if ( playing && 'function' === typeof playing.catch ) {
149					playing.catch( function () {} );
150				}
151			}
152		}
153
154		// Update the last device.
155		container.setAttribute( 'data-last-device', currentDevice );
156	}
157
158	/**
159	 * Initialize responsive videos for all elements on the page.
160	 */
161	function initResponsiveVideos() {
162		const containers = document.querySelectorAll( '[data-responsive-videos]' );
163
164		containers.forEach( function ( container ) {
165			updateVideoSource( container );
166		} );
167	}
168
169	/**
170	 * Handle window resize events with debouncing.
171	 */
172	let resizeTimeout;
173	function handleResize() {
174		clearTimeout( resizeTimeout );
175		resizeTimeout = setTimeout( function () {
176			initResponsiveVideos();
177		}, 250 ); // 250ms debounce.
178	}
179
180	/**
181	 * Initialize when DOM is ready.
182	 */
183	function init() {
184		// Apply the viewport's own video straight away. PHP cannot know the
185		// viewport and always emits the base source; skipping this step (as an
186		// earlier version did, to avoid a flicker) left tablet and phone visitors
187		// on the desktop video until a resize crossed a band. The swap is a no-op
188		// at desktop widths, so nothing flickers there.
189		initResponsiveVideos();
190
191		// Listen for window resize events.
192		window.addEventListener( 'resize', handleResize );
193
194		// Also listen for orientation change on mobile devices.
195		window.addEventListener( 'orientationchange', function () {
196			setTimeout( initResponsiveVideos, 500 );
197		} );
198	}
199
200	// Initialize when DOM is ready.
201	if ( document.readyState === 'loading' ) {
202		document.addEventListener( 'DOMContentLoaded', init );
203	} else {
204		init();
205	}
206} )();

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.