PageSourceSearch

https://www.royalbrackla.com/wp-content/plugins/nwp-fse-blocks/blocks/video/block.mjs?ver=7.0.5

js royalbrackla.com collected 2026-10-09 00:23:25 UTC 9,468 bytes, 406 lines download raw bytes

1// !IMPORTANT: This JS file is loaded as module in browser
2
3// TODO: use --mobile-bp instead
4const MOBILE_BREAKPOINT = 768;
5
6document.addEventListener('DOMContentLoaded', () => {
7	document.querySelectorAll('.nblock-video').forEach((block) => {
8		window.lozad
9			? window
10				.lozad(block, { load: () => Main.initBlock(block) })
11				.observe()
12			: Main.initBlock(block);
13	});
14});
15
16class Main {
17	static initBlock(block) {
18		if (block._initialised) {
19			return;
20		}
21		block._initialised = true;
22		block.classList.remove('nblock-video--playing');
23
24		block._jsData = Utils.normalizeJsData(block.dataset['js_data']);
25
26		Main.setPlayer(block);
27		Main.onResizeInit(block);
28
29		if (!block._activeData) {
30			console.error('Video block: video URL not set'); // eslint-disable-line no-console
31			return;
32		}
33
34		Main.rotatePlayButtonOnScroll(block);
35		Main.playButtonInit(block);
36
37		// For autoplayLoop file videos, hide the cover immediately.
38		if (
39			block._activeData.autoplayLoop &&
40			block._activeData.provider === 'file'
41		) {
42			const overlay = block.querySelector('.nblock-video__cover');
43			if (overlay) overlay.classList.add('nblock-video__cover--hidden');
44			block.classList.add('nblock-video--playing');
45		}
46	}
47
48	static setPlayer(block) {
49		const data = block._jsData;
50
51		const deviceVideoData = Utils.getDeviceVideoData(data);
52		if (!deviceVideoData.embed_url) {
53			return;
54		}
55
56		const muted = !!data.muted;
57		const autoplayLoop = !!data.autoplayLoop;
58		const finalEmbedUrl = VideoBlock.addUrlParams(
59			deviceVideoData.embed_url,
60			deviceVideoData.provider,
61			{ muted },
62		);
63
64		block._activeData = {
65			provider: deviceVideoData.provider,
66			embed_url: finalEmbedUrl,
67			muted,
68			autoplayLoop,
69		};
70
71		const wrapper = block.querySelector('.nblock-video__player');
72
73		const player =
74			Utils._getValidPlayer({ wrapper, videoData: deviceVideoData }) ||
75			Utils._createPlayer({
76				wrapper,
77				videoData: deviceVideoData,
78				finalEmbedUrl,
79				muted,
80				autoplayLoop,
81			});
82
83		if (deviceVideoData.provider === 'vimeo') {
84			Iframer.enforceVimeoIframeVolume({ iframe: player, muted });
85		}
86	}
87
88	static onResizeInit(block) {
89		block._isMobileSaved = Utils.isMobile();
90		window.addEventListener('resize', () => {
91			if (Utils.isMobile() !== block._isMobileSaved) {
92				block._isMobileSaved = Utils.isMobile();
93				Main.setPlayer(block);
94			}
95		});
96	}
97
98	static playButtonInit(block) {
99		const overlay = block.querySelector('.nblock-video__cover');
100		const overlayInteractiveSelector = [
101			'.nblock-video__content a[href]',
102			'.nblock-video__content button',
103			'.nblock-video__content input',
104			'.nblock-video__content textarea',
105			'.nblock-video__content select',
106			'.nblock-video__content [tabindex]:not([tabindex="-1"])',
107			'.nblock-video__content [role="button"]',
108		].join(', ');
109
110		overlay.addEventListener('click', (event) => {
111			if (event.target.closest(overlayInteractiveSelector)) {
112				return;
113			}
114
115			event.preventDefault();
116			Main.onPlayButtonClick(block);
117		});
118	}
119
120	static rotatePlayButtonOnScroll(block) {
121		const playEl = block.querySelector('.js-vimeo-content-play-text');
122		if (!playEl) {
123			return;
124		}
125
126		const rotate = () => {
127			const rotation = window.pageYOffset || window.scrollY || 0;
128			playEl.style.transform = `rotate(${rotation}deg)`;
129		};
130
131		rotate();
132		document.addEventListener('scroll', rotate);
133	}
134
135	static onPlayButtonClick(block) {
136		if (block._isPlaying) {
137			return;
138		}
139
140		const playerWrapper = block.querySelector('.nblock-video__player');
141		const targetContainer = playerWrapper || block;
142		const activePlayer = targetContainer.querySelector('iframe, video');
143
144		if (!activePlayer) {
145			console.warn('[nblock-video] Missing pre-rendered player element'); // eslint-disable-line no-console
146			return;
147		}
148		block._isPlaying = true;
149
150		if (typeof activePlayer.play === 'function') {
151			activePlayer.play();
152		} else {
153			const fallbackVariant = Utils.getDeviceVideoData(block._jsData);
154			const active = block._activeData || {
155				provider: fallbackVariant.provider,
156				embed_url: fallbackVariant.embed_url,
157				muted: Boolean(block._jsData?.muted),
158			};
159			const provider = active.provider;
160
161			Iframer.startIframePlayback({
162				iframe: activePlayer,
163				provider,
164				muted: Boolean(active.muted),
165			});
166		}
167
168		const overlay = block.querySelector('.nblock-video__cover');
169		if (overlay) {
170			overlay.classList.add('nblock-video__cover--hidden');
171		}
172		block.classList.add('nblock-video--playing');
173		const playButton = block.querySelector('.nblock-video__play');
174		if (playButton) {
175			playButton.setAttribute('aria-hidden', 'true');
176		}
177	}
178}
179
180class Utils {
181	static _getValidPlayer({ wrapper, videoData }) {
182		let player = wrapper.querySelector('iframe, video');
183
184		const isFile = videoData.provider === 'file';
185
186		return player &&
187			((isFile && player.tagName === 'VIDEO') ||
188				(!isFile && player.tagName === 'IFRAME'))
189			? player
190			: null;
191	}
192
193	static _createPlayer({
194		wrapper,
195		videoData,
196		finalEmbedUrl,
197		muted,
198		autoplayLoop,
199	}) {
200		const playerHtml = VideoBlock.getPlayerHtml({
201			provider: videoData.provider,
202			embedUrl: finalEmbedUrl,
203			attr: { muted, autoplayLoop },
204		});
205
206		const player = Utils._createEl(playerHtml);
207
208		wrapper.innerHTML = ''; // Clear existing content
209		wrapper.appendChild(player);
210
211		return player;
212	}
213
214	static getDeviceVideoData(data) {
215		const useMobile =
216			Utils.isMobile() && data.mobile && data.mobile.embed_url;
217		const preferred = useMobile ? data.mobile : data.desktop;
218
219		if (preferred && preferred.embed_url) {
220			return preferred;
221		}
222		if (data.mobile && data.mobile.embed_url) {
223			return data.mobile;
224		}
225		if (data.desktop && data.desktop.embed_url) {
226			return data.desktop;
227		}
228
229		return { provider: '', embed_url: '' };
230	}
231
232	static isMobile() {
233		return window.innerWidth <= MOBILE_BREAKPOINT;
234	}
235
236	static normalizeJsData(raw) {
237		let parsed = {};
238		try {
239			parsed = raw ? JSON.parse(raw) : {};
240		} catch (error) {
241			parsed = {};
242		}
243
244		const desktop = parsed.desktop || {};
245		const mobile = parsed.mobile || {};
246
247		return {
248			desktop: {
249				provider: desktop.provider || '',
250				embed_url: desktop.embed_url || '',
251			},
252			mobile: {
253				provider: mobile.provider || '',
254				embed_url: mobile.embed_url || '',
255			},
256			muted: parsed.muted,
257			autoplayLoop: parsed.autoplayLoop,
258		};
259	}
260
261	static _createEl(html) {
262		const temp = document.createElement('div');
263		temp.innerHTML = html;
264		return temp.firstElementChild;
265	}
266}
267
268export default class VideoBlock {
269	static addUrlParams(embedUrl, provider, options = {}) {
270		if (!embedUrl) {
271			return '';
272		}
273
274		let params = {};
275
276		if (provider === 'youtube') {
277			params = {
278				mute: options.muted ? '1' : '0',
279				rel: '0',
280				modestbranding: '1',
281				enablejsapi: '1',
282			};
283		}
284
285		if (provider === 'vimeo') {
286			params = {
287				playsinline: '1',
288				muted: options.muted ? '1' : '0',
289				title: '0',
290				byline: '0',
291				portrait: '0',
292				api: '1',
293				background: '0',
294			};
295		}
296
297		const url = new URL(embedUrl);
298		for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
299
300		return url.href;
301	}
302
303	static getPlayerHtml({ provider, embedUrl, attr }) {
304		if (provider === 'file') {
305			const autoplayLoop = attr.autoplayLoop;
306			const muted = attr.muted;
307			return `
308				<video
309					class="nblock-video__video"
310					src="${embedUrl}"
311					${autoplayLoop ? '' : 'controls'}
312					playsinline
313					preload="${autoplayLoop ? 'auto' : 'metadata'}"
314					${muted || autoplayLoop ? 'muted' : ''}
315					${autoplayLoop ? 'autoplay loop' : ''}
316				></video>
317			`;
318		}
319
320		const permissions =
321			provider === 'youtube'
322				? 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'
323				: 'autoplay; fullscreen; picture-in-picture';
324
325		let title = 'Video player';
326		if (provider === 'youtube') title = 'YouTube video player';
327		if (provider === 'vimeo') title = 'Vimeo video player';
328
329		return `
330			<iframe
331				class="nblock-video__iframe"
332				src="${embedUrl}"
333				title="${title}"
334				loading="lazy"
335				referrerpolicy="strict-origin-when-cross-origin"
336				allow="${permissions}"
337			></iframe>
338		`;
339	}
340}
341
342class Iframer {
343	static startIframePlayback({ iframe, provider }) {
344		if (!iframe || !iframe.contentWindow) {
345			return;
346		}
347
348		try {
349			if (provider === 'youtube') {
350				iframe.contentWindow.postMessage(
351					JSON.stringify({
352						event: 'command',
353						func: 'playVideo',
354						args: [],
355					}),
356					'*',
357				);
358				return;
359			}
360
361			if (provider === 'vimeo') {
362				iframe.contentWindow.postMessage(
363					JSON.stringify({ method: 'play' }),
364					'*',
365				);
366				return;
367			}
368		} catch (error) {
369			console.warn(
370				'[nblock-video] Unable to trigger iframe playback via API',
371				error,
372			); // eslint-disable-line no-console
373		}
374	}
375
376	// Vimeo renders muted by default, so we enforce it
377	static enforceVimeoIframeVolume({ iframe, muted }) {
378		const sendVolume = () => {
379			if (!iframe.contentWindow) {
380				return;
381			}
382
383			try {
384				iframe.contentWindow.postMessage(
385					JSON.stringify({
386						method: 'setVolume',
387						value: muted ? 0 : 1,
388					}),
389					'*',
390				);
391			} catch (error) {
392				console.warn(
393					'[nblock-video] Unable to set Vimeo volume',
394					error,
395				); // eslint-disable-line no-console
396			}
397		};
398
399		if (iframe._volumeEnforcer) {
400			iframe.removeEventListener('load', iframe._volumeEnforcer);
401		}
402
403		iframe._volumeEnforcer = () => sendVolume();
404		iframe.addEventListener('load', iframe._volumeEnforcer);
405	}
406}

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.