PageSourceSearch

https://www.wmn.de/_static/??-eJyVjk2uwkAMgy9ECEjoAQvEWcIkLSnzp0n6…WSx51AuCZoQT8ukeU4d02G93a9Wu93fdjPcAI9eh0o=

js wmn.de collected 2026-10-02 10:14:16 UTC 51,482 bytes, 1,591 lines download raw bytes

1!function(){const e=document.getElementById("masthead"),t=document.getElementById("search-toggle");if(null!==t){const n=document.getElementById("header-search"),s=n.getElementsByTagName("input")[0],a=t.getElementsByTagName("span")[0],o=a.innerText;t.addEventListener("click",function(){e.classList.toggle("hide-header-search"),o===a.innerText?(a.innerText=newspackScreenReaderText.close_search,n.setAttribute("aria-expanded","true"),t.setAttribute("aria-expanded","true"),s.focus()):(a.innerText=o,n.setAttribute("aria-expanded","false"),t.setAttribute("aria-expanded","false"),t.focus())},!1)}const n=document.getElementsByClassName("mobile-menu-toggle"),s=document.body,a=document.getElementById("mobile-sidebar-fallback"),o=document.getElementsByClassName("desktop-menu-toggle"),d=document.getElementById("desktop-sidebar-fallback"),c=document.getElementsByClassName("subpage-toggle");function i(e){const t=document.getElementById(e);t.parentNode.removeChild(t)}function l(e,t,n){s.classList.add(e),t.focus(),function(e){const t=document.createElement("div");t.setAttribute("class","overlay-mask"),t.setAttribute("id",e),document.body.appendChild(t)}(n)}function r(e,t,n){s.classList.remove(e),t.focus(),i(n)}for(let t=0;t<n.length;t++){const o=e.querySelector(".mobile-menu-toggle"),d=a.querySelector(".mobile-menu-toggle");n[t].addEventListener("click",function(){s.classList.contains("mobile-menu-opened")?r("mobile-menu-opened",o,"mask-mobile"):l("mobile-menu-opened",d,"mask-mobile")},!1)}for(let t=0;t<o.length;t++){const n=e.querySelector(".desktop-menu-toggle"),a=d.querySelector(".desktop-menu-toggle");o[t].addEventListener("click",function(){s.classList.contains("desktop-menu-opened")?r("desktop-menu-opened",n,"mask-desktop"):l("desktop-menu-opened",a,"mask-desktop")},!1)}if(0<c.length&&e){const t=document.getElementById("subpage-sidebar-fallback"),n=e.querySelector(".subpage-toggle"),a=t.querySelector(".subpage-toggle");for(let e=0;e<c.length;e++)c[e].addEventListener("click",function(){s.classList.contains("subpage-menu-opened")?r("subpage-menu-opened",n,"mask-subpage"):l("subpage-menu-opened",a,"mask-subpage")},!1)}if(document.addEventListener("click",function(e){if(e.target&&"overlay-mask"===e.target.className){const t=e.target.id,n=t.split("-");s.classList.remove(n[1]+"-menu-opened"),i(t)}}),e){const t=e.getElementsByClassName("submenu-expand");if(0<t.length)for(let e=0;e<t.length;e++){const n=t[e].querySelector("span.screen-reader-text");t[e].addEventListener("click",function(){t[e].classList.contains("open-dropdown")?(t[e].classList.remove("open-dropdown"),t[e].setAttribute("aria-expanded","false"),n.innerText=newspackScreenReaderText.close_dropdown_menu):(t[e].classList.add("open-dropdown"),t[e].setAttribute("aria-expanded","true"),n.innerText=newspackScreenReaderText.open_dropdown_menu)},!1)}}if(s.classList.contains("h-stk")&&s.classList.contains("h-sub")&&(s.classList.contains("single-featured-image-behind")||s.classList.contains("single-featured-image-beside"))){let t,n=0;window.onscroll=function(){!function(){const s=window.pageYOffset,a=(new Date).getTime();t||(a-n>300&&(n=a),t=setTimeout(function(){t=null,n=(new Date).getTime()},100)),200>=s?e.classList.remove("head-scroll"):e.classList.add("head-scroll")}()}}const m=document.getElementById("comments-toggle");if(null!==m){const e=document.getElementById("comments-wrapper"),t=m.getElementsByTagName("span")[0];m.addEventListener("click",function(){e.classList.contains("comments-hide")?(e.classList.remove("comments-hide"),t.innerText=newspackScreenReaderText.collapse_comments):(e.classList.add("comments-hide"),t.innerText=newspackScreenReaderText.expand_comments)},!1)}const u=document.getElementById("toggle-order-details");if(null!==u){const e=document.getElementById("order-details-wrapper"),t=u.getElementsByTagName("span")[0],n=newspackScreenReaderText.hide_order_details,s=newspackScreenReaderText.show_order_details;u.addEventListener("click",function(){e.classList.contains("order-details-hidden")?(e.classList.remove("order-details-hidden"),u.classList.remove("order-details-hidden"),t.innerText=n):(e.classList.add("order-details-hidden"),u.classList.add("order-details-hidden"),t.innerText=s)},!1)}}();;
2!function(){function e(){const e=document.getElementById("masthead");if(e){const t=e.getElementsByClassName("submenu-expand");if(0<t.length)for(let e=0;e<t.length;e++){const n=t[e].getAttribute("data-toggle-parent-id"),s=t[e].nextElementSibling,i=n.replace("toggle-","submenu-");s.setAttribute("id",i),t[e].setAttribute("aria-controls",i)}}}if(newspackScreenReaderText.is_amp){const t=document.getElementById("site-navigation"),n=document.getElementById("secondary-nav-contain"),s={childList:!0},i=new MutationObserver(t=>{for(const n of t)"childList"===n.type&&(e(),i.disconnect())});t&&i.observe(t,s),n&&i.observe(n,s)}else e()}();;
3(()=>
3{const n=window.newspackFontLoading?.fonts||[];Promise.all(n.map(n=>{const o=String(n).replace(/"/g,'\\"');return document.fonts.load(`1rem "${o}"`)})).then(o=>{o.length===n.length&&document.body.classList.remove("newspack--font-loading")})})();;
4(function () {
5	'use strict';
6
7	/**
8	 * Check if debug logging is enabled via URL parameter ?_video_debug=1
9	 */
10	function isDebugEnabled() {
11		if (typeof URLSearchParams !== 'undefined') {
12			// phpcs:ignore WordPressVIPMinimum.JS.Window.location -- read-only query string check; no DOM output
13			return new URLSearchParams(window.location.search).get('_video_debug') === '1';
14		}
15		// Fallback for older browsers
16		// phpcs:ignore WordPressVIPMinimum.JS.Window.location -- read-only query string check; no DOM output
17		return /[\?&]_video_debug=1/.test(window.location.search);
18	}
19
20	function fdLog() {
21		if (!isDebugEnabled() || !window.console) {
22			return;
23		}
24
25		if (typeof window.console.info === 'function') {
26			window.console.info.apply(window.console, arguments);
27			return;
28		}
29
30		if (typeof window.console.log === 'function') {
31			window.console.log.apply(window.console, arguments);
32		}
33	}
34
35	function debugWarn() {
36		if (!isDebugEnabled() || !window.console || typeof window.console.warn !== 'function') {
37			return;
38		}
39		window.console.warn.apply(window.console, arguments);
40	}
41
42	function debugDebug() {
43		if (!isDebugEnabled() || !window.console || typeof window.console.debug !== 'function') {
44			return;
45		}
46		window.console.debug.apply(window.console, arguments);
47	}
48
49	function debugError() {
50		if (!isDebugEnabled() || !window.console || typeof window.console.error !== 'function') {
51			return;
52		}
53		window.console.error.apply(window.console, arguments);
54	}
55
56	/**
57	 * Check if current User-Agent matches the override pattern.
58	 * When matched, use override player later
59	 */
60	function isUserAgentOverridden() {
61		var pattern = window.FDVideoTargetVideoConfig
62			&& window.FDVideoTargetVideoConfig.useragentOverridePattern;
63		if (!pattern || typeof pattern !== 'string' || pattern.trim() === '') {
64			return false;
65		}
66		try {
67			return new RegExp(pattern, 'i').test(navigator.userAgent);
68		} catch (e) {
69			// Invalid regex – no match
70			return false;
71		}
72	}
73
74	/**
75	 * Get optional override player ID for UA-matched requests.
76	 */
77	function getUserAgentOverridePlayerId() {
78		var value = window.FDVideoTargetVideoConfig
79			&& window.FDVideoTargetVideoConfig.useragentOverridePlayerId;
80
81		if (typeof value !== 'string') {
82			return '';
83		}
84
85		return value.trim();
86	}
87
88	function getUserAgentOverrideAutoplayPolicy() {
89		var value = window.FDVideoTargetVideoConfig
90			&& window.FDVideoTargetVideoConfig.useragentOverrideAutoplayPolicy;
91
92		if (typeof value !== 'string') {
93			return 'default';
94		}
95
96		value = value.trim();
97
98		if (
99			value !== 'default'
100			&& value !== 'force_off_on_match'
101			&& value !== 'force_off_on_match_with_override_player'
102		) {
103			return 'default';
104		}
105
106		return value;
107	}
108
109	function shouldForceClickToPlayForUaOverride(uaMatched, overridePlayerId) {
110		if (!uaMatched) {
111			return false;
112		}
113
114		var policy = getUserAgentOverrideAutoplayPolicy();
115
116		if (policy === 'force_off_on_match') {
117			return true;
118		}
119
120		if (policy === 'force_off_on_match_with_override_player') {
121			return !!(typeof overridePlayerId === 'string' && overridePlayerId.trim() !== '');
122		}
123
124		return false;
125	}
126
127	function loadScriptOnce(src, callback) {
128		if (!src) {
129			callback(false);
130			return;
131		}
132
133		var existing = document.querySelector('script[src="' + src + '"]');
134		if (existing) {
135			if (typeof window.$bp === 'function') {
136				callback(true);
137				return;
138			}
139
140			var called = false;
141			function completeOnce(ready) {
142				if (called) {
143					return;
144				}
145
146				called = true;
147				callback(ready);
148			}
149
150			existing.addEventListener('load', function () {
151				completeOnce(typeof window.$bp === 'function');
152			});
153			existing.addEventListener('error', function () {
154				completeOnce(false);
155			});
156			return;
157		}
158
159		var script = document.createElement('script');
160		script.src = src;
161		script.async = true;
162		script.onload = function () {
163			callback(typeof window.$bp === 'function');
164		};
165		script.onerror = function () {
166			callback(false);
167		};
168		document.head.appendChild(script);
169	}
170
171	function toPlayerConfig(config) {
172		var playerConfig = {
173			id: String(config.playerId || ''),
174			video: String(config.videoId || ''),
175			showSnapshotOnLoadingAd: true
176		};
177
178		if (config.freqCap != null && config.freqCap !== '') {
179			playerConfig.freqCap = Number(config.freqCap);
180		}
181
182		if (config.prebidTimeout != null && config.prebidTimeout !== '') {
183			playerConfig.prebid_timeout = Number(config.prebidTimeout);
184		}
185
186		if (config.a9Timeout != null && config.a9Timeout !== '') {
187			playerConfig.a9_timeout = Number(config.a9Timeout);
188		}
189
190		if (config.useTcfApiLocator === true) {
191			playerConfig.use_tcfapi_locator = true;
192		}
193
194		if (config.playerWidth != null) {
195			playerConfig.width = String(config.playerWidth);
196		}
197
198		if (config.playerHeight != null) {
199			playerConfig.height = String(config.playerHeight);
200		}
201
202		// Explicitly set autoplay only when force_autoplay is requested.
203		// For 'provider' mode, omit the flag so the player SDK uses its own config.
204		if (config.initMode === 'force_autoplay') {
205			playerConfig.autoplay = true;
206			playerConfig.autoplay_desktop = true;
207		}		
208
209		return playerConfig;
210	}
211
212	function applyAdTagConfig(playerConfig, adTagUrl) {
213		if (!adTagUrl || typeof adTagUrl !== 'string') {
214			return playerConfig;
215		}
216
217		// Use configurable mobile breakpoint (from WordPress settings, default 767px).
218		// Falls back to 767 if not provided.
219		var mobileBreakpointPx = 767;
220		if (window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.getMobileBreakpointPx === 'function') {
221			mobileBreakpointPx = window.FDVideoPlayerDirector.getMobileBreakpointPx();
222		}
223
224		var isMobile = window.matchMedia && window.matchMedia('(max-width: ' + (mobileBreakpointPx - 1) + 'px)').matches;
225		// TargetVideo: mobile is used on both mobile+desktop when desktop is omitted/null.
226		// To target desktop only: set desktop array and leave mobile null.
227		var ad = {
228			mobile: isMobile ? [adTagUrl] : null,
229			desktop: isMobile ? null : [adTagUrl],
230			adType: '0',
231			adTimeType: 's',
232			cuepoints: null
233		};
234
235		playerConfig.Ad = [ad];
236		return playerConfig;
237	}
238
239	function hasConsentFromDmpEvent(eventPayload) {
240		if (!eventPayload || eventPayload.gdprApplies === false) {
241			return true;
242		}
243
244		var eventStatusOk = eventPayload.eventStatus === 'useractioncomplete' || eventPayload.eventStatus === 'tcloaded';
245		var consents = eventPayload.vendor && eventPayload.vendor.consents;
246		var vendorConsent = !!(consents && (consents[1016] === true || consents[755] === true));
247
248		return eventStatusOk && vendorConsent;
249	}
250
251	function isSparkReadyForVideoAds() {
252		return !!(
253			window.Spark &&
254			window.Spark.definitionService &&
255			typeof window.Spark.definitionService.VideoAdRequest === 'function'
256		);
257	}
258
259	function buildSparkVideoAdRequestConfig(config) {
260		return {
261			videoID: config.videoID || config.videoId || '',
262			entryId: config.entryId || config.videoId || '',
263			tags: config.tags || config.videoTags || '',
264			adkeys: config.adkeys || config.pgname || '',
265			inline: config.inline === true,
266			playerPos: config.playerPos,
267			playerHeight: config.playerHeight,
268			playerWidth: config.playerWidth,
269			playerSize: config.playerSize,
270			videoHbPlayerSize: config.videoHbPlayerSize,
271			autoplay: config.initMode === 'force_autoplay'
272				|| (config.initMode === 'provider' && (config.inline === true
273					? !!config.providerModeIsAutoplayInline
274					: !!config.providerModeIsAutoplayFeatured)),
275			continuousAutoplay: config.continuousAutoplay,
276			verticalVideo: config.verticalVideo,
277			player: config.player,
278			playlist: config.playlist,
279			playlistId: config.playlistId,
280			playlistcount: config.playlistcount,
281			paid: config.paid
282		};
283	}
284
285	function dispatchTrackingEvent(eventName, config) {
286		if (!window.FDVideoPlayerDirector || !document) {
287			return;
288		}
289
290		var eventMap = {
291			'mediaLoaded': 'fdvideo:media_loaded',
292			'play': 'fdvideo:play',
293			'pause': 'fdvideo:pause',
294			'seeked': 'fdvideo:seeked',
295			'ended': 'fdvideo:ended',
296			'error': 'fdvideo:error',
297			'firstPlay': 'fdvideo:first_play',
298			'enterFullscreen': 'fdvideo:enter_fullscreen',
299			'exitFullscreen': 'fdvideo:exit_fullscreen',
300			'adCanSkip': 'fdvideo:ad_can_skip',
301			'adLoaded': 'fdvideo:ad_loaded',
302			'adStarted': 'fdvideo:ad_started',
303			'adCompleted': 'fdvideo:ad_completed',
304			'adError': 'fdvideo:ad_error',
305			'playReached25': 'fdvideo:play_reached_25',
306			'playReached50': 'fdvideo:play_reached_50',
307			'playReached75': 'fdvideo:play_reached_75',
308			'playReached100': 'fdvideo:play_reached_100'
309		};
310
311		fdLog('[fd-video][targetvideo] Dispatching tracking event', {
312			eventName: eventName,
313			config: config
314		});
315		
316		var customEventName = eventMap[eventName];
317		if (!customEventName) {
318			return;
319		}
320
321		var event = new CustomEvent(customEventName, { detail: config, bubbles: true });
322		document.dispatchEvent(event);
323	}
324
325	function initTargetVideo(runtime) {
326		if (!runtime || !runtime.element || !runtime.config) {
327			fdLog('[fd-video][targetvideo] Invalid runtime payload.', runtime);
328			return;
329		}
330
331		var element = runtime.element;
332		var config = runtime.config;
333		var uaOverrideMatched = isUserAgentOverridden();
334		var overridePlayerId = '';
335
336		// Override player ID from User-Agent if override pattern is configured and matched.
337		if (uaOverrideMatched) {
338			overridePlayerId = getUserAgentOverridePlayerId();
339
340			if (overridePlayerId) {
341				config.playerId = overridePlayerId;
342			}
343
344			fdLog('[fd-video][targetvideo] User-Agent matched override pattern – applying override settings.', {
345				userAgent: navigator.userAgent.slice(0, 100),
346				overridePlayerId: overridePlayerId || null,
347				playerId: config.playerId || null
348			});
349		}
350
351		if (shouldForceClickToPlayForUaOverride(uaOverrideMatched, overridePlayerId)) {
352			config.initMode = 'overlay';
353			fdLog('[fd-video][targetvideo] User-Agent override autoplay policy forced click-to-play.', {
354				domId: element.id || null,
355				policy: getUserAgentOverrideAutoplayPolicy(),
356				uaMatched: uaOverrideMatched,
357				overridePlayerIdActive: !!overridePlayerId
358			});
359		}
360
361		var pendingUserPlay = false;
362		var playerApi = null;
363		var instancePlayerId = '';
364		var firstPlayTracked = false;
365		var playbackProgressTracked = {
366			25: false,
367			50: false,
368			75: false,
369			100: false
370		};
371		var adRequest = null;
372		var playlistLoadedAfterEnd = false;
373
374		if (!element.id) {
375			element.id = 'fd-video-targetvideo-' + Date.now();
376		}
377
378		instancePlayerId = String(element.id);
379
380		function pauseCurrentPlayer() {
381			if (!playerApi || typeof playerApi.pause !== 'function') {
382				return;
383			}
384
385			try {
386				playerApi.pause();
387			} catch (pauseError) {
388				debugError('[fd-video][targetvideo] player.pause() failed on global pause request.', pauseError);
389			}
390		}
391
392		function canLoadPlaylistAfterEnd(player) {
393			return !!(
394				config.playlistAfterEndEnabled === true &&
395				typeof config.playlistAfterEndSource === 'string' &&
396				config.playlistAfterEndSource.trim() !== '' &&
397				player &&
398				typeof player.loadPlaylist === 'function'
399			);
400		}
401
402		document.addEventListener('fdvideo:pause_others', function (e) {
403			var activeInstanceId = e && e.detail && e.detail.activeInstanceId ? String(e.detail.activeInstanceId) : '';
404
405			if (!activeInstanceId || activeInstanceId === instancePlayerId) {
406				return;
407			}
408
409			pauseCurrentPlayer();
410		});
411
412		fdLog('[fd-video][targetvideo] Runtime config received', {
413			domId: element.id,
414			providerId: runtime.instance && runtime.instance.providerId ? runtime.instance.providerId : null,
415			videoObjectId: runtime.instance && runtime.instance.videoObjectId ? runtime.instance.videoObjectId : null,
416			placement: runtime.instance && runtime.instance.placementMeta ? runtime.instance.placementMeta.placement : null,
417			videoId: config.videoId || config.videoID || null,
418			playerId: config.playerId || null,
419			adsEnabled: config.adsEnabled !== false,
420			playerWidth: config.playerWidth || null,
421			playerHeight: config.playerHeight || null,
422			sparkEnabled: !!window.Spark
423		});
424
425		if (!config.playerId || !config.videoId) {
426			fdLog('[fd-video][targetvideo] Missing playerId or videoId.', {
427				playerId: config.playerId || null,
428				videoId: config.videoId || null,
429				videoIDAlias: config.videoID || null,
430				videoObjectId: runtime.instance && runtime.instance.videoObjectId ? runtime.instance.videoObjectId : null
431			});
432			return;
433		}
434
435		function runWhenCmpConsentGranted(callback) {
436			if (typeof callback !== 'function') {
437				return;
438			}
439
440			var called = false;
441			var rawTimeout = 6000;
442
443			if (config.cmpReadyTimeoutMs !== undefined && config.cmpReadyTimeoutMs !== null) {
444				rawTimeout = config.cmpReadyTimeoutMs;
445			} else if (config.sparkDmpReadyTimeoutMs !== undefined && config.sparkDmpReadyTimeoutMs !== null) {
446				rawTimeout = config.sparkDmpReadyTimeoutMs;
447			}
448
449			var timeoutMs = Number(rawTimeout);
450			if (!isFinite(timeoutMs) || timeoutMs < 0) {
451				timeoutMs = 6000;
452			}
453
454			function invokeOnce() {
455				if (called) {
456					return;
457				}
458
459				called = true;
460				window.clearTimeout(cmpTimeoutHandle);
461				callback();
462			}
463
464			// If the CMP itself is blocked (adblockers commonly block the CMP
465			// script), dmpReady never fires and the player would stay stuck
466			// waiting forever. Treat a timeout the same as a blocked script.
467			var cmpTimeoutHandle = window.setTimeout(function () {
468				if (called) {
469					return;
470				}
471
472				called = true;
473				debugError('[fd-video][targetvideo] No CMP consent signal received within ' + timeoutMs + 'ms (CMP likely blocked).', {
474					domId: element.id
475				});
476
477				if (window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.removePlayerElement === 'function') {
478					window.FDVideoPlayerDirector.removePlayerElement(element);
479				}
480			}, timeoutMs);
481
482			function handleConsentPayload(eventPayload) {
483				if (hasConsentFromDmpEvent(eventPayload)) { 
484					window.clearTimeout(cmpTimeoutHandle); 
485				}
486				
487				debugWarn('[fd-video][targetvideo] CMP dmpReady received.', {
488					domId: element.id,
489					eventStatus: eventPayload && eventPayload.eventStatus ? eventPayload.eventStatus : null,
490					gdprApplies: eventPayload && eventPayload.gdprApplies !== undefined ? eventPayload.gdprApplies : null
491				});
492
493				if (!hasConsentFromDmpEvent(eventPayload)) {
494					debugWarn('[fd-video][targetvideo] CMP consent missing. Player SDK load blocked.', {
495						domId: element.id,
496						hasPayload: !!eventPayload
497					});
498					return;
499				}
500
501				debugWarn('[fd-video][targetvideo] CMP consent granted. Loading TargetVideo SDK.', {
502					domId: element.id,
503					playerSdkUrl: config.playerSdkUrl || null
504				});
505
506				invokeOnce();
507			}
508
509			if (window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.onDmpReady === 'function') {
510				window.FDVideoPlayerDirector.onDmpReady(handleConsentPayload);
511				return;
512			}
513
514			if (window.Spark && window.Spark.cmd && typeof window.Spark.cmd.push === 'function') {
515				window.Spark.cmd.push(function () {
516					window.Spark.addEventListener('dmpReady', handleConsentPayload);
517				});
518				return;
519			}
520
521			debugWarn('[fd-video][targetvideo] No dmpReady source available. Player SDK load blocked until CMP is available.', {
522				domId: element.id
523			});
524		}
525
526		function createClickToLoadOverlay(onStart) {
527			if (typeof onStart !== 'function') {
528				return;
529			}
530
531			var existingOverlay = element.querySelector('.fd-video-click-to-load-overlay');
532			if (existingOverlay) {
533				return;
534			}
535
536			var elementPosition = window.getComputedStyle && window.getComputedStyle(element)
537				? window.getComputedStyle(element).position
538				: '';
539			if (!elementPosition || 'static' === elementPosition) {
540				element.style.position = 'relative';
541			}
542
543			var overlay = document.createElement('button');
544			overlay.type = 'button';
545			overlay.className = 'fd-video-click-to-load-overlay';
546			overlay.setAttribute('aria-label', 'Play video');
547			overlay.style.position = 'absolute';
548			overlay.style.inset = '0';
549			overlay.style.width = '100%';
550			overlay.style.height = '100%';
551			overlay.style.display = 'flex';
552			overlay.style.flexDirection = 'column';
553			overlay.style.alignItems = 'center';
554			overlay.style.justifyContent = 'center';
555			overlay.style.gap = '0.5rem';
556			overlay.style.border = '0';
557			overlay.style.padding = '0';
558			overlay.style.cursor = 'pointer';
559			overlay.style.zIndex = '2';
560			overlay.style.backgroundColor = '#000';
561			overlay.style.color = '#fff';
562
563			if (config.snapshotImage) {
564				overlay.style.backgroundImage = 'linear-gradient(rgba(0,0,0,0.25), rgba(0,0,0,0.45)), url("' + String(config.snapshotImage) + '")';
565				overlay.style.backgroundSize = 'cover';
566				overlay.style.backgroundPosition = 'center';
567				overlay.style.backgroundRepeat = 'no-repeat';
568			}
569
570			var playIcon = document.createElement('span');
571			playIcon.textContent = '▶';
572			playIcon.style.width = '64px';
573			playIcon.style.height = '64px';
574			playIcon.style.borderRadius = '50%';
575			playIcon.style.display = 'inline-flex';
576			playIcon.style.alignItems = 'center';
577			playIcon.style.justifyContent = 'center';
578			playIcon.style.fontSize = '30px';
579			playIcon.style.background = 'rgba(0,0,0,0.72)';
580			playIcon.style.paddingLeft = '4px';
581
582			var label = document.createElement('span');
583			label.textContent = 'Video laden';
584			label.style.fontSize = '14px';
585			label.style.fontWeight = '600';
586			label.style.textShadow = '0 1px 2px rgba(0,0,0,0.6)';
587
588			overlay.appendChild(playIcon);
589			overlay.appendChild(label);
590
591			overlay.addEventListener('click', function () {
592				overlay.disabled = true;
593				overlay.style.cursor = 'default';
594				overlay.style.opacity = '0.9';
595				pendingUserPlay = true;
596				onStart(function finishOverlay() {
597					if (overlay.parentNode) {
598						overlay.parentNode.removeChild(overlay);
599					}
600				});
601			});
602
603			element.appendChild(overlay);
604			debugWarn('[fd-video][targetvideo] Click-to-load overlay ready for non-autoplay video.', {
605				domId: element.id,
606				hasSnapshot: !!config.snapshotImage
607			});
608		}
609
610		function initPlayer(adTagUrl) {
611			function resetPlaybackProgressMarkers() {
612				playbackProgressTracked[25] = false;
613				playbackProgressTracked[50] = false;
614				playbackProgressTracked[75] = false;
615				playbackProgressTracked[100] = false;
616			}
617
618			function trackPlaybackProgress(player) {
619				if (!player || typeof player.currentTime !== 'function' || typeof player.duration !== 'function') {
620					return;
621				}
622
623				var duration = Number(player.duration());
624				if (!duration || !isFinite(duration) || duration <= 0) {
625					return;
626				}
627
628				var currentTime = Number(player.currentTime());
629				if (!isFinite(currentTime) || currentTime < 0) {
630					return;
631				}
632
633				var percent = (currentTime / duration) * 100;
634
635				if (!playbackProgressTracked[25] && percent >= 25) {
636					playbackProgressTracked[25] = true;
637					dispatchTrackingEvent('playReached25', config);
638				}
639
640				if (!playbackProgressTracked[50] && percent >= 50) {
641					playbackProgressTracked[50] = true;
642					dispatchTrackingEvent('playReached50', config);
643				}
644
645				if (!playbackProgressTracked[75] && percent >= 75) {
646					playbackProgressTracked[75] = true;
647					dispatchTrackingEvent('playReached75', config);
648				}
649
650				if (!playbackProgressTracked[100] && percent >= 100) {
651					playbackProgressTracked[100] = true;
652					dispatchTrackingEvent('playReached100', config);
653				}
654			}
655
656			runWhenCmpConsentGranted(function () {
657				loadScriptOnce(config.playerSdkUrl, function (ready) {
658					if (!ready || typeof window.$bp !== 'function') {
659						debugError('[fd-video][targetvideo] TargetVideo SDK not available (likely blocked by an adblocker).');
660
661						if (window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.removePlayerElement === 'function') {
662							window.FDVideoPlayerDirector.removePlayerElement(element);
663						}
664
665						return;
666					}
667
668					var playerConfig = toPlayerConfig(config);
669					applyAdTagConfig(playerConfig, adTagUrl);
670
671					debugWarn('[fd-video][targetvideo] >>> Calling $bp():', {
672						domId: element.id,
673						playerId: playerConfig.id,
674						videoId: playerConfig.video,
675						hasAdConfig: !!playerConfig.Ad,
676						adTagUrlPreview: adTagUrl ? adTagUrl.slice(0, 120) : null,
677						fullPlayerConfig: playerConfig
678					});
679
680					var player;
681					let isAdRunning = false;
682					try {
683						player = window.$bp(element.id, playerConfig);
684						playerApi = player;
685					} catch (bpError) {
686						debugError('[fd-video][targetvideo] $bp() threw an error:', bpError);
687						return;
688					}
689
690					debugWarn('[fd-video][targetvideo] $bp() returned:', player);
691
692					if (!player || typeof player.on !== 'function') {
693						debugWarn('[fd-video][targetvideo] $bp() returned no player API (player.on not available). ' +
694							'This may be expected for async init — watching for player events via data-bp may still work.');
695						return;
696					}
697
698					player.on('start', function () {
699						if (firstPlayTracked) {
700							return;
701						}
702
703						firstPlayTracked = true;
704						dispatchTrackingEvent('firstPlay', config);
705					});
706
707					player.on('play', function () {
708						dispatchTrackingEvent('play', config);
709
710						if (window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.notifyPlay === 'function') {
711							window.FDVideoPlayerDirector.notifyPlay(instancePlayerId);
712						}
713					});
714
715					player.on('pause', function () {
716						dispatchTrackingEvent('pause', config);
717					});
718
719					player.on('seeked', function () {
720						dispatchTrackingEvent('seeked', config);
721					});
722
723					player.on('ended', function () {
724						if (!playlistLoadedAfterEnd && canLoadPlaylistAfterEnd(player)) {
725							try {
726								player.loadPlaylist(config.playlistAfterEndSource.trim());
727								player.play();
728								playlistLoadedAfterEnd = true;
729								debugWarn('[fd-video][targetvideo] Playlist loaded after ended event.', {
730									domId: element.id,
731									playlistSource: config.playlistAfterEndSource
732								});
733							} catch (playlistError) {
734								debugError('[fd-video][targetvideo] player.loadPlaylist() failed.', playlistError);
735							}
736						}
737
738						dispatchTrackingEvent('ended', config);
739						resetPlaybackProgressMarkers();
740					});
741
742					player.on('playbackerror', function (error) {
743						debugError('[fd-video][targetvideo] Playback error.', element.i
743d, error);
744						dispatchTrackingEvent('error', config);
745					});
746
747					player.on('timeupdate', function () {
748						if(isAdRunning) return;
749						trackPlaybackProgress(player);
750					});
751
752					player.on('fullscreenchange', function () {
753						var isFullscreen = false;
754
755						if (typeof player.isFullScreen === 'function') {
756							isFullscreen = !!player.isFullScreen();
757						} else {
758							isFullscreen = !!player.isFullScreen;
759						}
760
761						dispatchTrackingEvent(isFullscreen ? 'enterFullscreen' : 'exitFullscreen', config);
762					});
763
764					
765					player.on('requestAd', function () {
766						// dispatchTrackingEvent('adLoaded', config);
767						isAdRunning = true;
768					});				
769
770					player.on('adImpression', function () {
771						dispatchTrackingEvent('adLoaded', config);
772					});
773
774					player.on('adStart', function () {
775						dispatchTrackingEvent('adStarted', config);
776					});
777
778					player.on('adComplete', function () {
779						isAdRunning = false;
780						dispatchTrackingEvent('adCompleted', config);
781					});
782
783					player.on('adSkip', function () {
784						dispatchTrackingEvent('adCanSkip', config);
785					});
786
787					player.on('ready', function () {
788						debugDebug('[fd-video][targetvideo] Player ready.', element.id);
789						dispatchTrackingEvent('mediaLoaded', config);
790
791						if (pendingUserPlay && typeof player.play === 'function') {
792							pendingUserPlay = false;
793							try {
794								player.play();
795								debugWarn('[fd-video][targetvideo] Starting playback after user click-to-load.', {
796									domId: element.id,
797									initMode: config.initMode
798								});
799							} catch (playError) {
800								debugError('[fd-video][targetvideo] player.play() failed after click-to-load.', playError);
801							}
802						}
803
804						var readyEvent = new CustomEvent('fdvideo:player_ready', { bubbles: true });
805						element.dispatchEvent(readyEvent);
806					});
807
808					/*
809					player.on('adEnd', function () {
810						dispatchTrackingEvent('adCompleted', config);
811					});
812					*/
813
814					player.on('adError', function (error) {
815						debugError('[fd-video][targetvideo] Ad error.', element.id, error);
816						dispatchTrackingEvent('adError', config);
817					});
818
819					player.on('newContent', function () {
820						resetPlaybackProgressMarkers();
821
822						// Only handle newContent after the first video has started playing
823						if (!firstPlayTracked) {
824							debugWarn('[fd-video][targetvideo] newContent: first video has not started yet, skipping ad refresh.', {
825								domId: element.id
826							});
827							return;
828						}
829
830						firstPlayTracked = false;
831
832						if (!adRequest || typeof adRequest.refreshAndThen !== 'function') {
833							debugWarn('[fd-video][targetvideo] newContent: no adRequest available, skipping ad refresh.', {
834								domId: element.id
835							});
836							return;
837						}
838
839						debugWarn('[fd-video][targetvideo] newContent: requesting new ad via refreshAndThen.', {
840							domId: element.id
841						});
842
843						if (player && player.config) {
844 							player.config.Ad = null;
845 						}
846 						if (player && player.Advertising && Array.isArray(player.Advertising.adsPool)) {
847 							player.Advertising.adsPool = [];
848 						}
849
850						adRequest.refreshAndThen(null, function (adTagUrl) {
851							if (!adTagUrl || typeof player.playAd !== 'function') {
852								debugWarn('[fd-video][targetvideo] newContent: no adTagUrl or playAd unavailable.', {
853									domId: element.id,
854									hasAdTagUrl: !!adTagUrl
855								});
856								return;
857							}
858
859							debugWarn('[fd-video][targetvideo] newContent: playing new ad via playAd().', {
860								domId: element.id,
861								adTagUrlPreview: adTagUrl.slice(0, 120)
862							});
863
864							try {
865								player.playAd(adTagUrl);
866							} catch (playAdError) {
867								debugError('[fd-video][targetvideo] player.playAd() failed after newContent.', playAdError);
868							}
869						});
870					});
871				});
872			});
873		}
874
875		var playerStartTriggered = false;
876
877		function startPlayerFlow(done) {
878			if (playerStartTriggered) {
879				return;
880			}
881
882			playerStartTriggered = true;
883			var finish = typeof done === 'function' ? done : function () {};
884
885			if (config.adsEnabled === false) {
886				fdLog('[fd-video][targetvideo] Ad integration disabled for this instance. Initializing player directly.');
887				finish();
888				initPlayer(null);
889				return;
890			}
891
892			if (!window.Spark || !window.Spark.cmd || typeof window.Spark.cmd.push !== 'function') {
893				fdLog('[fd-video][targetvideo] Spark unavailable, starting player without ads.', {
894					hasSpark: !!window.Spark,
895					hasSparkCmd: !!(window.Spark && window.Spark.cmd)
896				});
897				finish();
898				initPlayer(null);
899				return;
900			}
901
902			var started = false;
903			var timeoutMs = Number(config.sparkDmpReadyTimeoutMs || 6000);
904
905			function startWithoutAds() {
906				if (started) {
907					return;
908				}
909
910				started = true;
911				finish();
912				debugWarn('[fd-video][targetvideo] >>> Starting player WITHOUT ads (timeout or no consent).', {
913					domId: element.id,
914					videoId: config.videoId || null,
915					playerId: config.playerId || null
916				});
917				initPlayer(null);
918			}
919
920			function startWithSparkAds() {
921				if (started) {
922					return;
923				}
924
925				if (!isSparkReadyForVideoAds()) {
926					debugDebug('[fd-video][targetvideo] Spark VideoAdRequest not available after dmpReady – starting without ads.');
927					startWithoutAds();
928					return;
929				}
930
931				var req;
932				try {
933					const ad_request_config = buildSparkVideoAdRequestConfig(config);
934					req = new window.Spark.definitionService.VideoAdRequest(ad_request_config);
935					fdLog('[fd-video][targetvideo] VideoAdRequest created.', {
936						domId: element.id,
937						videoId: config.videoId || null,
938						playerPos: config.playerPos || null,
939						adRequestConfig: ad_request_config
940					});
941				} catch (error) {
942					debugError('[fd-video][targetvideo] Failed to create VideoAdRequest.', error);
943					startWithoutAds();
944					return;
945				}
946
947				adRequest = req;
948
949				try {
950					req.requestHb();
951					debugWarn('[fd-video][targetvideo] requestHb() sent — waiting for onReady or timeout (' + timeoutMs + 'ms).', element.id);
952					req.onReady(function (adTagUrl) {
953						if (started) {
954							return;
955						}
956
957						started = true;
958						finish();
959						debugWarn('[fd-video][targetvideo] >>> onReady received.', {
960							domId: element.id,
961							hasAdTagUrl: !!adTagUrl,
962							adTagUrlPreview: adTagUrl ? adTagUrl.slice(0, 120) : null
963						});
964						initPlayer(adTagUrl || null);
965					});
966				} catch (error) {
967					debugError('[fd-video][targetvideo] VideoAdRequest flow failed.', error);
968					startWithoutAds();
969				}
970			}
971
972			var useBroker = window.FDVideoPlayerDirector && typeof window.FDVideoPlayerDirector.onDmpReady === 'function';
973
974			if (useBroker) {
975				var timeoutHandle = window.setTimeout(startWithoutAds, timeoutMs);
976				fdLog('[fd-video][targetvideo] Waiting for dmpReady via PlayerDirector broker.', {
977					domId: element.id,
978					timeoutMs: timeoutMs
979				});
980
981				window.FDVideoPlayerDirector.onDmpReady(function (eventPayload) {
982					window.clearTimeout(timeoutHandle);
983					fdLog('[fd-video][targetvideo] dmpReady received via broker.', {
984						domId: element.id,
985						hasPayload: !!eventPayload
986					});
987
988					if (!hasConsentFromDmpEvent(eventPayload)) {
989						startWithoutAds();
990						return;
991					}
992
993					startWithSparkAds();
994				});
995			} else {
996				fdLog('[fd-video][targetvideo] Using direct Spark listener fallback (no broker).', {
997					domId: element.id,
998					timeoutMs: timeoutMs
999				});
1000
1001				window.Spark.cmd.push(function () {
1002					var dmpReadyFired = false;
1003					var timeoutHandle = window.setTimeout(function () {
1004						if (!dmpReadyFired) {
1005							startWithoutAds();
1006						}
1007					}, timeoutMs);
1008
1009					window.Spark.addEventListener('dmpReady', function (eventPayload) {
1010						dmpReadyFired = true;
1011						window.clearTimeout(timeoutHandle);
1012
1013						if (!hasConsentFromDmpEvent(eventPayload)) {
1014							startWithoutAds();
1015							return;
1016						}
1017
1018						startWithSparkAds();
1019					});
1020				});
1021			}
1022		}
1023
1024		if (config.initMode === 'overlay') {
1025			runWhenCmpConsentGranted(function () {
1026				createClickToLoadOverlay(function (done) {
1027					debugWarn('[fd-video][targetvideo] User initiated playback for overlay video.', {
1028						domId: element.id
1029					});
1030					startPlayerFlow(done);
1031				});
1032			});
1033			return;
1034		}
1035
1036		startPlayerFlow();
1037	}
1038
1039	function register() {
1040		if (!window.FDVideoPlayerDirector || typeof window.FDVideoPlayerDirector.registerAdapter !== 'function') {
1041			return false;
1042		}
1043
1044		window.FDVideoPlayerDirector.registerAdapter('targetvideo', {
1045			init: initTargetVideo
1046		});
1047
1048		if (typeof window.FDVideoPlayerDirector.scan === 'function') {
1049			window.FDVideoPlayerDirector.scan(document);
1050		}
1051
1052		return true;
1053	}
1054
1055	if (register()) {
1056		return;
1057	}
1058
1059	document.addEventListener('DOMContentLoaded', function () {
1060		register();
1061	});
1062})();
1063;
1064(function () {
1065	'use strict';
1066
1067	var adapters = {};
1068
1069	/**
1070	 * Check if debug logging is enabled via URL parameter ?_video_debug=1
1071	 */
1072	function isDebugEnabled() {
1073		if (typeof URLSearchParams !== 'undefined') {
1074			return new URLSearchParams(window.location.search).get('_video_debug') 
1074=== '1';
1075		}
1076		// Fallback for older browsers
1077		return /[\?&]_video_debug=1/.test(window.location.search);
1078	}
1079
1080	function fdLog() {
1081		if (!isDebugEnabled() || !window.console) {
1082			return;
1083		}
1084
1085		if (typeof window.console.info === 'function') {
1086			window.console.info.apply(window.console, arguments);
1087			return;
1088		}
1089
1090		if (typeof window.console.log === 'function') {
1091			window.console.log.apply(window.console, arguments);
1092		}
1093	}
1094
1095	function debugError() {
1096		if (!isDebugEnabled() || !window.console || typeof window.console.error !== 'function') {
1097			return;
1098		}
1099		window.console.error.apply(window.console, arguments);
1100	}
1101
1102	// dmpReady broker: receives the Spark dmpReady event once and replays
1103	// the payload to all subscribers, including those that register after
1104	// the event has already fired.
1105	var dmpReadyPayload = null;
1106	var dmpReadyFired = false;
1107	var dmpReadyCallbacks = [];
1108
1109	function fireDmpReady(payload) {
1110		if (dmpReadyFired) {
1111			return;
1112		}
1113
1114		dmpReadyFired = true;
1115		dmpReadyPayload = payload !== undefined ? payload : null;
1116
1117		var callbacks = dmpReadyCallbacks.slice();
1118		dmpReadyCallbacks = [];
1119
1120		for (var i = 0; i < callbacks.length; i++) {
1121			try {
1122				callbacks[i](dmpReadyPayload);
1123			} catch (error) {
1124			debugError('[fd-video] dmpReady callback error:', error);
1125			}
1126		}
1127	}
1128
1129	function onDmpReady(callback) {
1130		if (typeof callback !== 'function') {
1131			return;
1132		}
1133
1134		if (dmpReadyFired) {
1135			// Event already received: replay immediately.
1136			try {
1137				callback(dmpReadyPayload);
1138			} catch (error) {
1139				debugError('[fd-video] dmpReady callback error:', error);
1140			}
1141			return;
1142		}
1143
1144		dmpReadyCallbacks.push(callback);
1145	}
1146
1147	// Register broker listener with Spark as soon as possible.
1148	// The cmd queue ensures we run after Spark.init.
1149	function installSparkDmpBroker() {
1150		if (!window.Spark || !window.Spark.cmd || typeof window.Spark.cmd.push !== 'function') {
1151			return;
1152		}
1153
1154		window.Spark.cmd.push(function () {
1155			window.Spark.addEventListener('dmpReady', function (payload) {
1156				fireDmpReady(payload);
1157			});
1158		});
1159	}
1160
1161	installSparkDmpBroker();
1162
1163	// DataLayer Tracking Module: centralized handling of provider-agnostic video events.
1164	var trackingModule = (function () {
1165		var trackedEvents = {
1166			'fdvideo:media_loaded': 'MEDIA_LOADED',
1167			'fdvideo:play': 'PLAY',
1168			'fdvideo:pause': 'PAUSE',
1169			'fdvideo:seeked': 'SEEKED',
1170			'fdvideo:ended': 'ENDED',
1171			'fdvideo:enter_fullscreen': 'ENTER_FULLSCREEN',
1172			'fdvideo:exit_fullscreen': 'EXIT_FULLSCREEN',
1173			'fdvideo:error': 'ERROR',
1174			'fdvideo:first_play': 'FIRST_PLAY',
1175			'fdvideo:ad_can_skip': 'AD_CAN_SKIP',
1176			'fdvideo:ad_loaded': 'AD_LOADED',
1177			'fdvideo:ad_completed': 'AD_COMPLETED',
1178			'fdvideo:ad_started': 'AD_STARTED',
1179			'fdvideo:ad_error': 'AD_ERROR',
1180			'fdvideo:play_reached_25': 'PLAY_REACHED_25',
1181			'fdvideo:play_reached_50': 'PLAY_REACHED_50',
1182			'fdvideo:play_reached_75': 'PLAY_REACHED_75',
1183			'fdvideo:play_reached_100': 'PLAY_REACHED_100'
1184		};
1185
1186		function normalizeDatalayerEvent(eventCategory, detail) {
1187			if (!detail || typeof detail !== 'object') {
1188				return null;
1189			}
1190
1191			// Map detail (resolvedConfig) to EVENTS.md schema.
1192			var obj = {
1193				event: 'targetvideo_video',
1194				video_event_category: eventCategory,
1195				playerid: detail.playerId || '',
1196				adkeys: detail.adkeys || detail.pgname || '',
1197				tags: detail.tags || detail.videoTags || '',
1198				videosource: detail.videosource || detail.player || 'unknown',
1199				accountid: detail.accountId || '',
1200				createddate: detail.createddate || '',
1201				videoid: detail.videoId || detail.videoID || '',
1202				videoname: detail.videoname || '',
1203				position: detail.playerPos || '',
1204				videoreferenceid: detail.videoreferenceid || '',
1205				videoduration: detail.videoduration || ''
1206			};
1207
1208			return obj;
1209		}
1210
1211		function pushDataLayer(obj) {
1212			if (!obj || !window.dataLayer || !Array.isArray(window.dataLayer)) {
1213				return;
1214			}
1215
1216			window.dataLayer.push(obj);
1217		}
1218
1219		// Listen for custom events on document.
1220		Object.keys(trackedEvents).forEach(function (customEventName) {
1221			document.addEventListener(customEventName, function (e) {
1222				var obj = normalizeDatalayerEvent(trackedEvents[customEventName], e.detail);
1223				pushDataLayer(obj);
1224			});
1225		});
1226
1227		return {
1228			pushEvent: function (eventName, detail) {
1229				var event = new CustomEvent(eventName, { detail: detail, bubbles: true });
1230				document.dispatchEvent(event);
1231			}
1232		};
1233	})();
1234
1235	function safeParseJson(rawValue) {
1236		if (!rawValue || typeof rawValue !== 'string') {
1237			return null;
1238		}
1239
1240		try {
1241			return JSON.parse(rawValue);
1242		} catch (error) {
1243			debugError('[fd-video] Invalid JSON payload in container data attribute.', error);
1244			return null;
1245		}
1246	}
1247
1248	function registerAdapter(providerId, adapter) {
1249		if (!providerId || typeof providerId !== 'string') {
1250			return;
1251		}
1252
1253		if (!adapter || typeof adapter.init !== 'function') {
1254			return;
1255		}
1256
1257		adapters[providerId] = adapter;
1258	}
1259
1260	function initContainer(element) {
1261		if (!element || element.nodeType !== 1) {
1262			return;
1263		}
1264
1265		if (element.dataset.fdVideoInitialized === '1') {
1266			return;
1267		}
1268
1269		var providerId = element.getAttribute('data-fd-provider') || '';
1270		var instance = safeParseJson(element.getAttribute('data-fd-video-instance'));
1271		var config = safeParseJson(element.getAttribute('data-fd-config')) || {};
1272
1273		fdLog('[fd-video] Container discovered', {
1274			domId: element.id || null,
1275			providerId: providerId,
1276			hasInstance: !!instance,
1277			hasConfig: !!config,
1278			videoObjectId: instance && instance.videoObjectId ? instance.videoObjectId : null,
1279			placement: instance && instance.placementMeta ? instance.placementMeta.placement : null,
1280			configVideoId: config.videoId || config.videoID || null,
1281			configPlayerId: config.playerId || null
1282		});
1283
1284		if (!providerId || !instance) {
1285			return;
1286		}
1287
1288		element.dataset.fdVideoInitialized = '1';
1289		if (isDeferredContainer(element)) {
1290			element.dataset.fdVideoInitPending = '1';
1291		}
1292
1293		if (!adapters[providerId]) {
1294			fdLog('[fd-video] No adapter registered for provider:', providerId);
1295			failDeferredContainer(element, 'missing_adapter');
1296			return;
1297		}
1298
1299		try {
1300			fdLog('[fd-video] Initializing adapter', {
1301				providerId: providerId,
1302				domId: element.id || null,
1303				videoObjectId: instance.videoObjectId || null,
1304				configVideoId: config.videoId || config.videoID || null,
1305				configPlayerId: config.playerId || null
1306			});
1307
1308			armPlayerLoadTimeout(element, config);
1309			adapters[providerId].init({
1310				element: element,
1311				instance: instance,
1312				config: config
1313			});
1314		} catch (error) {
1315			debugError('[fd-video] Adapter initialization failed for provider:', providerId, error);
1316			failDeferredContainer(element, 'adapter_init_failed');
1317		}
1318	}
1319
1320	function scan(root) {
1321		var scope = root && root.querySelectorAll ? root : document;
1322		var containers = scope.querySelectorAll('[data-fd-video-instance][data-fd-provider]');
1323
1324		fdLog('[fd-video] Scan completed', {
1325			containerCount: containers.length,
1326			scope: scope === document ? 'document' : 'custom-root'
1327		});
1328
1329		for (var i = 0; i < containers.length; i++) {
1330			initContainer(containers[i]);
1331		}
1332	}
1333
1334	/**
1335	 * Notify all provider instances that one player started playback.
1336	 *
1337	 * Providers should call this from their native play event handler.
1338	 * FD Video then broadcasts a single pause request to all instances,
1339	 * excluding the currently active instance.
1340	 *
1341	 * @param {string} activeInstanceId The currently playing instance ID.
1342	 */
1343	function notifyPlay(activeInstanceId) {
1344		if (!activeInstanceId) {
1345			return;
1346		}
1347
1348		fdLog('[fd-video] notifyPlay received, broadcasting pause for other instances.', {
1349			activeInstanceId: activeInstanceId
1350		});
1351
1352		var pauseEvent;
1353		try {
1354			pauseEvent = new CustomEvent('fdvideo:pause_others', {
1355				detail: {
1356					activeInstanceId: activeInstanceId
1357				},
1358				bubbles: true
1359			});
1360		} catch (error) {
1361			debugError('[fd-video] Failed to create pause broadcast event.', error);
1362			return;
1363		}
1364
1365		document.dispatchEvent(pauseEvent);
1366	}
1367
1368	var PLAYER_READY_TIMEOUT_MS = 6000;
1369
1370	function getContainerFromNode(node) {
1371		if (!node || node.nodeType !== 1) {
1372			return null;
1373		}
1374
1375		if (node.hasAttribute('data-fd-video-instance')) {
1376			return node;
1377		}
1378
1379		return node.closest ? node.closest('[data-fd-video-instance]') : null;
1380	}
1381
1382	function getContainerFromEvent(event) {
1383		var element = getContainerFromNode(event.target);
1384		if (!element && event.detail && event.detail.element) {
1385			element = getContainerFromNode(event.detail.element);
1386		}
1387
1388		return element;
1389	}
1390
1391	function isDeferredContainer(element) {
1392		return !!(element && element.closest && element.closest('.fd-video-deferred'));
1393	}
1394
1395	function getPlayerReadyTimeoutMs(config) {
1396		if (config && typeof config.playerReadyTimeoutMs === 'number' && config.playerReadyTimeoutMs > 0) {
1397			return config.playerReadyTimeoutMs;
1398		}
1399
1400		if (config && typeof config.sparkDmpReadyTimeoutMs === 'number' && config.sparkDmpReadyTimeoutMs > 0) {
1401			return config.sparkDmpReadyTimeoutMs;
1402		}
1403
1404		return PLAYER_READY_TIMEOUT_MS;
1405	}
1406
1407	function clearPlayerLoadTimeout(element) {
1408		if (!element || !element.__fdVideoLoadTimeoutId) {
1409			return;
1410		}
1411
1412		window.clearTimeout(element.__fdVideoLoadTimeoutId);
1413		element.__fdVideoLoadTimeoutId = null;
1414	}
1415
1416	function clearPendingPlayerState(element) {
1417		if (!element) {
1418			return;
1419		}
1420
1421		clearPlayerLoadTimeout(element);
1422		delete element.dataset.fdVideoInitPending;
1423	}
1424
1425	function failDeferredContainer(element, reason) {
1426		if (!isDeferredContainer(element) || element.dataset.fdVideoInitPending !== '1') {
1427			return;
1428		}
1429
1430		clearPendingPlayerState(element);
1431		fdLog('[fd-video] Removing deferred player after initialization failure.', {
1432			domId: element.id || null,
1433			reason: reason || 'unknown'
1434		});
1435		removePlayerElement(element);
1436	}
1437
1438	function armPlayerLoadTimeout(element, config) {
1439		if (!isDeferredContainer(element)) {
1440			return;
1441		}
1442
1443		element.dataset.fdVideoInitPending = '1';
1444		clearPlayerLoadTimeout(element);
1445		element.__fdVideoLoadTimeoutId = window.setTimeout(function () {
1446			failDeferredContainer(element, 'player_ready_timeout');
1447		}, getPlayerReadyTimeoutMs(config));
1448	}
1449
1450	/**
1451	 * Remove a blocked player's container from the DOM (e.g. adblocker
1452	 * prevented the SDK/adapter script from loading), so any element
1453	 * underneath (thumbnail, teaser markup) becomes visible again.
1454	 *
1455	 * @param {Element} element The provider container element (data-fd-video-instance).
1456	 */
1457	function removePlayerElement(element) {
1458		if (!element || element.nodeType !== 1) {
1459			return;
1460		}
1461
1462		clearPendingPlayerState(element);
1463
1464		var domId = element.id || '';
1465		var deferred = element.closest ? element.closest('.fd-video-deferred') : null;
1466		var toRemove = deferred || element;
1467		var slot = domId ? document.getElementById('fd-video-slot-' + domId) : null;
1468		var wrapper = element.closest ? element.closest('.header-featured-video-wrapper') : null;
1469
1470		if (toRemove.parentNode) {
1471			toRemove.parentNode.removeChild(toRemove);
1472		}
1473
1474		if (slot && slot.parentNode) {
1475			slot.parentNode.removeChild(slot);
1476		}
1477
1478		// Toggle a class instead of an inline style: survives figcaption
1479		// re-renders and always wins via !important, regardless of when
1480		// stylesheets finish loading relative to this script.
1481		if (wrapper) {
1482			wrapper.classList.add('fd-video-player-blocked');
1483		}
1484
1485		fdLog('[fd-video] Player element removed after load failure.', { domId: domId });
1486
1487		var blockedEvent;
1488		try {
1489			blockedEvent = new CustomEvent('fdvideo:player_blocked', {
1490				detail: { domId: domId },
1491				bubbles: true
1492			});
1493			document.dispatchEvent(blockedEvent);
1494		} catch (error) {
1495			debugError('[fd-video] Failed to create blocked-player event.', error);
1496		}
1497	}
1498
1499	window.FDVideoPlayerDirector = {
1500		registerAdapter: registerAdapter,
1501		scan: scan,
1502		initContainer: initContainer,
1503		onDmpReady: onDmpReady,
1504		notifyPlay: notifyPlay,
1505		removePlayerElement: removePlayerElement,
1506		getMobileBreakpointPx: function() {
1507			// Find the first container element with data-fd-config and extract mobileBreakpointPx.
1508			var container = document.querySelector('[data-fd-config]');
1509			if (container) {
1510				var config = safeParseJson(container.getAttribute('data-fd-config')) || {};
1511				if (config.mobileBreakpointPx && typeof config.mobileBreakpointPx === 'number') {
1512					return config.mobileBreakpointPx;
1513				}
1514			}
1515			// Fallback to default (Bootstrap md breakpoint).
1516			return 767;
1517		}
1518	};
1519
1520	/**
1521	 * Move each deferred container from the hidden wp_footer pool to its
1522	 * matching slot placeholder. Called synchronously on DOMContentLoaded,
1523	 * before adapter initialisation, so no layout shift occurs.
1524	 */
1525	function relocateDeferredContainers() {
1526		var pool = document.getElementById('fd-video-deferred-pool');
1527		if (!pool) {
1528			return;
1529		}
1530
1531		var deferreds = pool.querySelectorAll('[data-fd-for-slot]');
1532		for (var i = 0; i < deferreds.length; i++) {
1533			var deferred = deferreds[i];
1534			var domId = deferred.getAttribute('data-fd-for-slot');
1535			var slot = document.getElementById('fd-video-slot-' + domId);
1536			if (slot && slot.parentNode) {
1537				slot.parentNode.replaceChild(deferred, slot);
1538			}
1539		}
1540
1541		if (pool.parentNode) {
1542			pool.parentNode.removeChild(pool);
1543		}
1544	}
1545
1546	// Show the visible video meta when the player signals it is ready.
1547	document.addEventListener('fdvideo:player_ready', function (e) {
1548		var element = getContainerFromEvent(e);
1549		if (!element) {
1550			return;
1551		}
1552		clearPendingPlayerState(element);
1553		var wrapper = element.parentNode;
1554		if (!wrapper) {
1555			return;
1556		}
1557		var meta = wrapper.querySelector('.fd-video-meta');
1558		if (meta) {
1559			meta.removeAttribute('hidden');
1560		}
1561		var link = wrapper.querySelector('.fd-video-link');
1562		if (link) {
1563			link.removeAttribute('hidden');
1564		}
1565	});
1566
1567	document.addEventListener('fdvideo:error', function (e) {
1568		var element = getContainerFromEvent(e);
1569		var detail = e.detail && typeof e.detail === 'object' ? e.detail : {};
1570		var isInitFailure = detail.phase === 'init' || detail.status === 'init_failed';
1571		if (!element) {
1572			return;
1573		}
1574
1575		if (!isInitFailure) {
1576			return;
1577		}
1578
1579		failDeferredContainer(element, 'player_error');
1580	});
1581
1582	document.addEventListener('DOMContentLoaded', function () {
1583		relocateDeferredContainers();
1584		scan(document);
1585	});
1586})();
1587;
1588/*! This file is auto-generated */
1589"use strict";var wp;(wp||={}).domReady=(()=>{var o=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var f=(e,t)=>{for(var n in t)o(e,n,{get:t[n],enumerable:!0})},m=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let d of a(t))!i.call(e,d)&&d!==n&&o(e,d,{get:()=>
1589t[d],enumerable:!(r=u(t,d))||r.enumerable});return e};var c=e=>m(o({},"__esModule",{value:!0}),e);var p={};f(p,{default:()=>y});function y(e){if(!(typeof document>"u")){if(document.readyState==="complete"||document.readyState==="interactive")return void e();document.addEventListener("DOMContentLoaded",e)}}return c(p);})();
1590if (typeof wp.domReady === 'object' && wp.domReady.default) { wp.domReady = wp.domReady.default; }
1591;

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.