1/** 2 * Custom Vimeo Player Implementation with lazy loading and anchor navigation 3 */ 4(function() { 5 // Wait for DOM to be fully loaded 6 document.addEventListener('DOMContentLoaded', function() { 7 // Load Vimeo Player API 8 const tag = document.createElement('script'); 9 tag.src = "https://player.vimeo.com/api/player.js"; 10 const firstScriptTag = document.getElementsByTagName('script')[0]; 11 firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 12 13 // Wait for Vimeo API to load 14 tag.onload = function() { 15 setTimeout(function() { 16 initCustomPlayers(); 17 // Handle initial hash if present 18 handleHashNavigation(); 19 }, 100); 20 }; 21 }); 22 23 // Global variables 24 let currentlyPlaying = null; 25 let players = {}; 26 let initialized = false; 27 let observerInitialized = false; 28 29 // Create intersection observer for lazy loading 30 const observerOptions = { 31 root: null, 32 rootMargin: '50px', 33 threshold: 0.1 34 }; 35 36 let observer = new IntersectionObserver((entries) => { 37 entries.forEach(entry => { 38 if (entry.isIntersecting) { 39 const container = entry.target; 40 initializePlayer(container); 41 // Stop observing after initialization 42 observer.unobserve(container); 43 } 44 }); 45 }, observerOptions); 46 47 function handleHashNavigation() { 48 if (window.location.hash) { 49 console.log('hash', window.location.hash); 50 const targetId = window.location.hash.substring(1); 51 const targetElement = document.getElementById(targetId); 52 53 if (targetElement) { 54 // Find the closest video container 55 const videoContainer = targetElement.closest('.custom-vimeo-player'); 56 57 if (videoContainer) { 58 // Ensure smooth scroll 59 targetElement.scrollIntoView({ behavior: 'smooth' }); 60 // Initialize the target video if not already initialized 61 initializePlayer(videoContainer); 62 } else { 63 // Element exists but is not inside a video container 64 console.warn('Target element is not inside a video container:', targetId); 65 // Still scroll to the element 66 targetElement.scrollIntoView({ behavior: 'smooth' }); 67 } 68 } else { 69 console.warn('Target element not found:', targetId); 70 } 71 } 72 } 73 74 // Listen for hash changes 75 window.addEventListener('hashchange', handleHashNavigation); 76 77 function initCustomPlayers() { 78 if (initialized) return; 79 initialized = true; 80 81 const playerContainers = document.querySelectorAll('.custom-vimeo-player'); 82 83 playerContainers.forEach((container) => { 84 // Start observing each container 85 observer.observe(container); 86 }); 87 } 88 89 function initializePlayer(container) { 90 if (container.dataset.initialized === 'true') return; 91 container.dataset.initialized = 'true'; 92 93 const iframe = container.querySelector('iframe'); 94 const overlay = container.querySelector('.video-overlay'); 95 const videoWrapper = container.querySelector('.video-embed-wrapper'); 96 97 if (!iframe) return; 98 99 // Validate Vimeo URL 100 let src = iframe.getAttribute('src'); 101 if (!src || !src.includes('vimeo.com')) { 102 console.warn('Invalid or missing Vimeo URL:', src); 103 return; 104 } 105 106 // Force additional parameters in src URL 107 if (src.indexOf('?') === -1) { 108 src += '?'; 109 } else { 110 src += '&'; 111 } 112 113 src = src.replace('background=1', ''); 114 src += 'autoplay=0&controls=0&title=0&byline=0&portrait=0&transparent=1&dnt=1&autopause=0&pip=0&sidedock=0'; 115 iframe.setAttribute('src', src); 116 117 iframe.classList.add('vimeo-embed'); 118 119 const playerId = 'vimeo-player-' + Math.random().toString(36).substr(2, 9); 120 container.setAttribute('data-player-id', playerId); 121 122 try { 123 const player = new Vimeo.Player(iframe, { 124 autoplay: false, 125 controls: false, 126 title: false, 127 byline: false, 128 portrait: false, 129 transparent: true, 130 dnt: true, 131 autopause: false, 132 pip: false 133 }); 134 135 players[playerId] = player; 136 137 // Ensure video is paused on initialization 138 player.ready().then(function() { 139 player.pause(); 140 }).catch(function(error) { 141 console.error('Player ready error:', error); 142 }); 143 144 // Create a click catcher for playing state 145 const clickCatcher = document.createElement('div'); 146 clickCatcher.className = 'video-click-catcher'; 147 videoWrapper.appendChild(clickCatcher); 148 149 // Handle click on click catcher (when video is playing) 150 clickCatcher.addEventListener('click', function() { 151 if (currentlyPlaying === playerId) { 152 player.pause(); 153 overlay.classList.remove('hidden'); 154 clickCatcher.classList.remove('active'); 155 currentlyPlaying = null; 156 } 157 }); 158 159 // Handle click on overlay/play button 160 overlay.addEventListener('click', function() { 161 if (currentlyPlaying && currentlyPlaying !== playerId) { 162 // Pause any other playing video 163 pauseAllOtherVideos(playerId); 164 } 165 166 player.getPaused().then(function(paused) { 167 if (paused) { 168 player.play(); 169 overlay.classList.add('hidden'); 170 clickCatcher.classList.add('active'); 171 currentlyPlaying = playerId; 172 } else { 173 player.pause(); 174 overlay.classList.remove('hidden'); 175 clickCatcher.classList.remove('active'); 176 currentlyPlaying = null; 177 } 178 }).catch(function(error) { 179 console.error('Get paused error:', error); 180 }); 181 }); 182 183 // Show overlay when video ends 184 player.on('ended', function() { 185 overlay.classList.remove('hidden'); 186 clickCatcher.classList.remove('active'); 187 currentlyPlaying = null; 188 }); 189 190 // Show overlay when video is paused 191 player.on('pause', function() { 192 overlay.classList.remove('hidden'); 193 clickCatcher.classList.remove('active'); 194 }); 195 196 // Force hiding any UI elements after player initialization 197 player.ready().then(function() { 198 // Direct control over the iframe content 199 setTimeout(function() { 200 try { 201 // Try to post a message to the iframe to hide controls 202 iframe.contentWindow.postMessage(JSON.stringify({ 203 method: 'setVolume', 204 value: 1 205 }), '*'); 206 207 // Apply CSS overrides to hide controls 208 const styleTag = document.createElement('style'); 209 styleTag.textContent = ` 210 .vp-controls, .vp-sidedock, .vp-title, .vp-progress-bar-container { 211 display: none !important; 212 opacity: 0 !important; 213 visibility: hidden !important; 214 } 215 `; 216 document.head.appendChild(styleTag); 217 } catch (e) { 218 console.error('Error customizing Vimeo player:', e); 219 } 220 }, 500); 221 }).catch(function(error) { 222 console.error('Player customization error:', error); 223 }); 224 } catch (error) { 225 console.error('Error initializing Vimeo player:', error, 'for iframe:', iframe); 226 } 227 } 228 229 function pauseAllOtherVideos(exceptPlayerId) { 230 for (const playerId in players) { 231 if (playerId !== exceptPlayerId) { 232 const player = players[playerId]; 233 player.pause(); 234 235 const container = document.querySelector(`[data-
235player-id="${playerId}"]`); 236 if (container) { 237 const overlay = container.querySelector('.video-overlay'); 238 const clickCatcher = container.querySelector('.video-click-catcher'); 239 if (overlay) { 240 overlay.classList.remove('hidden'); 241 } 242 if (clickCatcher) { 243 clickCatcher.classList.remove('active'); 244 } 245 } 246 } 247 } 248 } 249})();
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.