PageSourceSearch

https://www.annemax.nl/app/mu-plugins/vibrant-modules/vibrant-loca…/location-detail-actions/view.js?ver=1.0.0

js annemax.nl collected 2026-10-04 11:40:56 UTC 4,689 bytes, 169 lines download raw bytes

1/**
2 * Frontend behavior for mobile sticky location detail actions.
3 */
4( function() {
5	'use strict';
6
7	const MOBILE_BREAKPOINT = 1024;
8	const body = document.body;
9	const openFrames = new WeakMap();
10	const closeTimers = new WeakMap();
11	const closeDelay = 240;
12
13	function setModalState( modalNode, isOpen ) {
14		if ( ! modalNode ) {
15			return;
16		}
17
18		if ( closeTimers.has( modalNode ) ) {
19			window.clearTimeout( closeTimers.get( modalNode ) );
20			closeTimers.delete( modalNode );
21		}
22
23		if ( openFrames.has( modalNode ) ) {
24			window.cancelAnimationFrame( openFrames.get( modalNode ) );
25			openFrames.delete( modalNode );
26		}
27
28		if ( isOpen ) {
29			modalNode.hidden = false;
30
31			openFrames.set( modalNode, window.requestAnimationFrame( function() {
32				modalNode.classList.add( 'is-open' );
33				openFrames.delete( modalNode );
34			} ) );
35		} else {
36			modalNode.classList.remove( 'is-open' );
37
38			closeTimers.set( modalNode, window.setTimeout( function() {
39				modalNode.hidden = true;
40				closeTimers.delete( modalNode );
41			}, closeDelay ) );
42		}
43
44		if ( body ) {
45			body.classList.toggle( 'vb-delivery-modal-open', isOpen );
46		}
47	}
48
49	function initDeliveryModal( scopeNode ) {
50		const modalNode = scopeNode.querySelector( '[data-vb-delivery-modal]' );
51		const triggerNodes = scopeNode.querySelectorAll( '[data-vb-delivery-open]' );
52
53		if ( ! modalNode || ! triggerNodes.length ) {
54			return;
55		}
56
57		if ( modalNode.parentElement !== document.body ) {
58			document.body.appendChild( modalNode );
59		}
60
61		const closeNodes = modalNode.querySelectorAll( '[data-vb-delivery-close]' );
62		const optionNodes = modalNode.querySelectorAll( 'a' );
63
64		triggerNodes.forEach( function( triggerNode ) {
65			triggerNode.addEventListener( 'click', function() {
66				setModalState( modalNode, true );
67			} );
68		} );
69
70		closeNodes.forEach( function( closeNode ) {
71			closeNode.addEventListener( 'click', function() {
72				setModalState( modalNode, false );
73			} );
74		} );
75
76		optionNodes.forEach( function( optionNode ) {
77			optionNode.addEventListener( 'click', function() {
78				setModalState( modalNode, false );
79			} );
80		} );
81
82		modalNode.addEventListener( 'click', function( event ) {
83			if ( event.target === modalNode ) {
84				setModalState( modalNode, false );
85			}
86		} );
87
88		document.addEventListener( 'keydown', function( event ) {
89			if ( 'Escape' === event.key && ! modalNode.hidden ) {
90				setModalState( modalNode, false );
91			}
92		} );
93	}
94
95	function setStickyState( stickyNode, isVisible ) {
96		stickyNode.classList.toggle( 'is-visible', isVisible );
97		stickyNode.setAttribute( 'aria-hidden', isVisible ? 'false' : 'true' );
98
99		// Body-class zodat we los-in-de-body geplaatste widgets (zoals #gifty-container) kunnen
100		// verbergen zolang de sticky-actiebalk in beeld is.
101		if ( body ) {
102			body.classList.toggle( 'vb-location-sticky-visible', isVisible );
103		}
104	}
105
106	function initBlock( block ) {
107		const sourceNode = block.querySelector( '[data-vb-location-detail-actions-source]' );
108		const stickyNode = block.querySelector( '[data-vb-location-detail-actions-sticky]' );
109		const footerNode = document.querySelector( 'footer' );
110
111		if ( ! sourceNode || ! stickyNode ) {
112			return;
113		}
114
115		const entryContent =
116			block.closest( '.entry-content' ) ||
117			block.closest( '.wp-block-post-content' ) ||
118			block.parentElement;
119
120		if ( entryContent && stickyNode.parentElement !== entryContent ) {
121			entryContent.appendChild( stickyNode );
122		}
123
124		initDeliveryModal( entryContent || block );
125
126		let ticking = false;
127
128		function updateSticky() {
129			ticking = false;
130
131			if ( window.innerWidth > MOBILE_BREAKPOINT ) {
132				setStickyState( stickyNode, false );
133				return;
134			}
135
136			const rect = sourceNode.getBoundingClientRect();
137			const isPastSource = rect.bottom <= 0;
138			let isAboveFooter = false;
139
140			if ( footerNode ) {
141				const footerRect = footerNode.getBoundingClientRect();
142				const stickyBottom = parseFloat( window.getComputedStyle( stickyNode ).bottom || '18' ) || 18;
143				const stickyHeight = stickyNode.offsetHeight || 0;
144				const stickyTopThreshold = window.innerHeight - stickyBottom - stickyHeight - 8;
145
146				isAboveFooter = footerRect.top <= stickyTopThreshold;
147			}
148
149			setStickyState( stickyNode, isPastSource && ! isAboveFooter );
150		}
151
152		function requestUpdate() {
153			if ( ticking ) {
154				return;
155			}
156
157			ticking = true;
158			window.requestAnimationFrame( updateSticky );
159		}
160
161		requestUpdate();
162		window.addEventListener( 'scroll', requestUpdate, { passive: true } );
163		window.addEventListener( 'resize', requestUpdate );
164	}
165
166	document.addEventListener( 'DOMContentLoaded', function() {
167		document.querySelectorAll( '[data-vb-location-detail-actions]' ).forEach( initBlock );
168	} );
169} )();

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.