(() => { 'use strict'; let apiPromise, playerIndex=0; const players=new Map(); function youtubeApi() { if(window.YT?.Player) return Promise.resolve(window.YT); if(apiPromise) return apiPromise; apiPromise=new Promise((resolve,reject)=>{ const script=document.createElement('script'); script.src='https://www.youtube.com/iframe_api'; script.async=true; const timer=setTimeout(failed,15000); function failed(){clearTimeout(timer);script.remove();apiPromise=null;reject(new Error('YouTube could not load. Try again or watch on YouTube.'));} window.onYouTubeIframeAPIReady=()=>{clearTimeout(timer);resolve(window.YT);}; script.onerror=failed;document.head.append(script); }); return apiPromise; } function setVideoState(shell,state,message='') { shell.dataset.videoState=state; shell.querySelector('.video-status').textContent=message; shell.querySelector('.video-retry').hidden=state!=='error'; } async function launch(shell) { if(shell.dataset.videoState==='loading') return; const stage=shell.querySelector('.video-stage'); try {players.get(shell)?.destroy();}catch{} players.delete(shell); for(const player of players.values())try{player.pauseVideo();}catch{} stage.replaceChildren(); setVideoState(shell,'loading','Loading video… Your summary is ready below.'); let timer=setTimeout(()=>setVideoState(shell,'error','This video is taking too long to load. Retry or open it on YouTube.'),25000); try { const YT=await youtubeApi(); const iframe=document.createElement('iframe');iframe.id='dispatch-player-'+(++playerIndex); iframe.title=shell.dataset.title;iframe.allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';iframe.allowFullscreen=true; iframe.referrerPolicy='strict-origin-when-cross-origin'; iframe.src='https://www.youtube-nocookie.com/embed/'+encodeURIComponent(shell.dataset.videoId)+'?enablejsapi=1&playsinline=1&autoplay=1&origin='+encodeURIComponent(location.origin); stage.append(iframe); const player=new YT.Player(iframe.id,{host:'https://www.youtube-nocookie.com',events:{ onReady(event){clearTimeout(timer);setVideoState(shell,'ready','Use the player controls to watch.');event.target.playVideo();}, onStateChange(event){ clearTimeout(timer); if(event.data===YT.PlayerState.PLAYING){setVideoState(shell,'playing');for(const [other,p]of players)if(other!==shell)try{p.pauseVideo();}catch{}} else if(event.data===YT.PlayerState.BUFFERING){setVideoState(shell,'loading','Buffering video…');timer=setTimeout(()=>setVideoState(shell,'error','Playback is taking too long. Try again or watch on YouTube.'),25000);} else if(event.data===YT.PlayerState.PAUSED || event.data===YT.PlayerState.ENDED)setVideoState(shell,'paused'); }, onError(event){clearTimeout(timer);const messages={100:'This video was removed or made private.',101:'The uploader does not allow playback on other websites.',150:'The uploader does not allow playback on other websites.',153:'YouTube could not verify this player.'};setVideoState(shell,'error',(messages[event.data]||'This video cannot play here right now.')+' Your summary is still available below.');} }}); players.set(shell,player); }catch(error){clearTimeout(timer);setVideoState(shell,'error',error.message);} } document.querySelectorAll('.video-shell').forEach(shell=>{ shell.querySelector('.video-launch')?.addEventListener('click',()=>launch(shell)); shell.querySelector('.video-retry')?.addEventListener('click',()=>launch(shell)); }); const search=document.querySelector('#story-search'); if(search){ const cards=[...document.querySelectorAll('[data-story]')], sections=[...document.querySelectorAll('[data-feed-section]')]; const competition=document.querySelector('#competition-filter'),team=document.querySelector('#team-filter'); let type='all'; const expanded=new Set(); function filter(){ const query=search.value.toLocaleLowerCase().trim();let count=0; for(const card of cards){ const matches=query.split(/\s+/).every(q=>card.dataset.search.includes(q)) && (!competition.value||card.dataset.category===competition.value) && (!team.value||JSON.parse(card.dataset.teams).includes(team.value)) && (type==='all'||card.dataset.kind===type); card.dataset.matches=String(matches);card.hidden=!matches;if(matches)count++; if(!matches){const shell=card.querySelector('.video-shell');try{players.get(shell)?.pauseVideo();}catch{}} } for(const section of sections){ const matches=[...section.querySelectorAll('[data-story]')].filter(c=>c.dataset.matches==='true'); const more=section.querySelector('.load-more');const limited=more&&!expanded.has(section); matches.forEach((card,i)=>card.hidden=Boolean(limited&&i>=6)); if(more)more.hidden=matches.length<=6||expanded.has(section); section.hidden=matches.length===0; } document.querySelector('#no-results').hidden=count!==0; document.querySelector('#result-count').textContent=count+' '+(count===1?'story':'stories')+' found'; } [search,competition,team].forEach(control=>control.addEventListener('input',()=>{expanded.clear();filter();})); document.querySelectorAll('[data-type]').forEach(button=>button.addEventListener('click',()=>{type=button.dataset.type;expanded.clear();document.querySelectorAll('[data-type]').forEach(b=>b.setAttribute('aria-pressed',String(b===button)));filter();})); document.querySelector('#reset-filters').addEventListener('click',()=>{search.value='';competition.value='';team.value='';type='all';expanded.clear();document.querySelectorAll('[data-type]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.type==='all')));filter();}); document.querySelectorAll('.load-more').forEach(b=>b.addEventListener('click',()=>{expanded.add(b.closest('[data-feed-section]'));filter();})); document.querySelectorAll('nav a[href="#website-news"],nav a[href="#video-news"],nav a[href="#rugby-shorts"]').forEach(link=>link.addEventListener('click',()=>{ document.querySelector('#reset-filters').click(); const target=document.querySelector(link.getAttribute('href')); if(target?.hidden) {const kind=link.hash==='#website-news'?'news':link.hash==='#rugby-shorts'?'short':'video';cards.find(c=>c.dataset.kind===kind)?.scrollIntoView({block:'start'});} })); filter(); } const stale=document.querySelector('[data-updated]'); if(stale){const check=()=>{stale.hidden=Date.now()-new Date(stale.dataset.updated)<24*3600000;};check();setInterval(check,60000);} document.querySelector('[data-copy-email]')?.addEventListener('click',async event=>{ const status=document.querySelector('.copy-status');try{await navigator.clipboard.writeText(event.target.dataset.copyEmail);status.textContent='Email address copied.';}catch{status.textContent='Copy this address: '+event.target.dataset.copyEmail;} }); const form=document.querySelector('#contact-form'); form?.addEventListener('submit',async event=>{ event.preventDefault();if(!form.reportValidity())return; const status=form.querySelector('.form-status'),button=form.querySelector('button[type="submit"]'); button.disabled=true;status.textContent='Sending your message…'; try{ const response=await fetch(form.dataset.endpoint,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.fromEntries(new FormData(form))),signal:AbortSignal.timeout(20000)}); const result=await response.json();if(!response.ok||result.ok!==true)throw new Error(result.message||'Your message was not sent. Please try again or email us directly.'); status.textContent='Your message has been received. Thank you for getting in touch.';form.reset(); }catch(error){status.textContent=error.name==='TimeoutError'?'Delivery could not be confirmed. Please email us directly.':error.message;} finally{button.disabled=false;} }); })();