export function safeReturn(value, origin) { try { const u = new URL(value, origin); return u.origin === origin && ['/', '/people'].includes(u.pathname) && !u.hash ? u.pathname + u.search : '/people'; } catch { return '/people'; } } export function mountDirectory(document, window) { const directory=document.querySelector('[data-people-directory]');if(!directory)return; const errorSummary=document.querySelector('#filter-errors');if(errorSummary){directory.querySelector('#filter-panel').open=true;errorSummary.focus();} const key='km-people-return:'+window.location.pathname+window.location.search; directory.querySelectorAll('.profile-link').forEach(a=>a.addEventListener('click',()=>{try{window.sessionStorage.setItem(key,JSON.stringify({y:window.scrollY,id:a.closest('article').id,at:Date.now()}));}catch{}})); let restoreVersion=0,restorePending=false,pendingRestoreRaw=null,cancelledRestoreRaw=null; function cancelPendingRestore(){if(restorePending){ restorePending=false;restoreVersion++;cancelledRestoreRaw=pendingRestoreRaw; try{if(window.sessionStorage.getItem(key)===pendingRestoreRaw)window.sessionStorage.removeItem(key);}catch{} pendingRestoreRaw=null; }} for(const event of ['wheel','pointerdown','touchstart'])window.addEventListener(event,cancelPendingRestore,{passive:true}); window.addEventListener('keydown',event=>{if(!['Shift','Control','Alt','Meta','AltGraph'].includes(event.key))cancelPendingRestore();}); async function restore(){ const token=++restoreVersion; let raw,saved; try{raw=window.sessionStorage.getItem(key);saved=JSON.parse(raw||'null');}catch{try{window.sessionStorage.removeItem(key);}catch{}return;} if(!saved||raw===cancelledRestoreRaw)return; const now=Date.now(); if(!Number.isFinite(saved.y)||saved.y<0||saved.y>10000000||!Number.isFinite(saved.at)||saved.at>now||now-saved.at>=1800000||typeof saved.id!=='string'||!/^(?:person|featured)-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(saved.id)){ try{window.sessionStorage.removeItem(key);}catch{}return; } restorePending=true;pendingRestoreRaw=raw; if(document.fonts?.ready){try{await document.fonts.ready;}catch{}} await new Promise(resolve=>window.requestAnimationFrame(()=>window.requestAnimationFrame(resolve))); if(token===restoreVersion){restorePending=false;pendingRestoreRaw=null;} if(token!==restoreVersion||document.hidden||key!=='km-people-return:'+window.location.pathname+window.location.search)return; try{if(window.sessionStorage.getItem(key)!==raw)return;}catch{return;} document.getElementById(saved.id)?.querySelector('.profile-link')?.focus({preventScroll:true}); window.scrollTo(0,saved.y); try{window.sessionStorage.removeItem(key);}catch{} } window.addEventListener('pagehide',()=>{restorePending=false;pendingRestoreRaw=null;restoreVersion++;}); window.addEventListener('pageshow',()=>{void restore();}); void restore(); const panel=directory.querySelector('#filter-panel'),mq=window.matchMedia('(max-width:700px)');if(panel&&mq.matches&&!errorSummary&&!directory.querySelector('.active-filters'))panel.open=false; const track=directory.querySelector('.featured-track'),controls=directory.querySelector('.carousel-controls');if(!track||!controls)return; const slides=[...track.children],motion=window.matchMedia('(prefers-reduced-motion:reduce)'),toggle=controls.querySelector('[data-autoplay]'),status=controls.querySelector('[data-slide-status]');let index=0,paused=motion.matches,timer=null,hover=false; controls.hidden=false; function updateStatus(user=false){status.setAttribute('aria-live',user?'polite':'off');status.textContent=(index+1)+' / '+slides.length;} updateStatus(); let scrollTimer=null,dragging=false,programmatic=false,scrollDirty=false,navigationTarget=null; function nearest(){ const box=track.getBoundingClientRect(),rtl=window.getComputedStyle(track).direction==='rtl'; const start=box.left+track.clientLeft+(rtl?track.clientWidth:0); let best=0,distance=Infinity; slides.forEach((slide,i)=>{const rect=slide.getBoundingClientRect(),delta=Math.abs((rtl?rect.right:rect.left)-start);if(delta{scrollDirty=true;if(!programmatic)status.setAttribute('aria-live','off');settle();}); track.addEventListener('scrollend',reconcile); track.addEventListener('wheel',()=>{programmatic=false;navigationTarget=null;},{passive:true}); track.addEventListener('pointerup',()=>{dragging=false;settle();}); track.addEventListener('pointercancel',()=>{dragging=false;settle();}); window.addEventListener('pointerup',()=>{if(dragging){dragging=false;settle();}}); window.addEventListener('pagehide',()=>{window.clearTimeout(scrollTimer);}); function step(amount,user=false){if(scrollDirty&&!programmatic){index=nearest();scrollDirty=false;}programmatic=true;index=((navigationTarget??index)+amount+slides.length)%slides.length;navigationTarget=index;track.scrollTo({left:slides[index].offsetLeft-slides[0].offsetLeft,behavior:motion.matches?'instant':'smooth'});updateStatus(user);scrollDirty=true;settle();} function schedule(){window.clearInterval(timer);timer=null;if(!paused&&!hover&&!document.hidden)timer=window.setInterval(()=>step(1),5000);toggle.textContent=paused?'استئناف الحركة':'إيقاف الحركة';toggle.setAttribute('aria-pressed',String(paused));} controls.querySelectorAll('[data-slide]').forEach(b=>b.addEventListener('click',()=>{step(Number(b.dataset.slide),true);schedule();})); toggle.addEventListener('click',()=>{paused=!paused;schedule();}); track.addEventListener('pointerenter',()=>{hover=true;schedule();});track.addEventListener('pointerleave',()=>{hover=false;schedule();}); track.addEventListener('focusin',()=>{paused=true;schedule();});track.addEventListener('pointerdown',()=>{dragging=true;programmatic=false;navigationTarget=null;paused=true;schedule();}); track.addEventListener('keydown',e=>{if(!['ArrowLeft','ArrowRight'].includes(e.key))return;e.preventDefault();paused=true;step(e.key==='ArrowLeft'?1:-1,true);schedule();}); motion.addEventListener('change',()=>{if(motion.matches)paused=true;schedule();});document.addEventListener('visibilitychange',schedule);window.addEventListener('pagehide',()=>window.clearInterval(timer));schedule(); } if(typeof document!=='undefined')mountDirectory(document,window); export function mountHero(document,window){ const hero=document.querySelector('[data-hero-slider]');if(!hero)return; const frames=[...hero.querySelectorAll('[data-hero-frame]')],button=hero.querySelector('[data-hero-pause]'),count=hero.querySelector('[data-hero-count]'),motion=window.matchMedia('(prefers-reduced-motion:reduce)'); let current=0,paused=motion.matches,timer; const show=step=>{current=(current+step+frames.length)%frames.length;frames.forEach((frame,index)=>frame.hidden=index!==current);count.textContent=(current+1)+' / '+frames.length;}; const schedule=()=>{window.clearInterval(timer);button.textContent=paused?'استئناف الحركة':'إيقاف الحركة';button.setAttribute('aria-pressed',String(paused));if(!paused&&!document.hidden)timer=window.setInterval(()=>show(1),5000);}; hero.querySelector('[data-hero-prev]').addEventListener('click',()=>show(-1)); hero.querySelector('[data-hero-next]').addEventListener('click',()=>show(1)); button.addEventListener('click',()=>{paused=!paused;schedule();}); hero.addEventListener('focusin',event=>{if(event.target===button)return;paused=true;schedule();}); hero.addEventListener('pointerenter',()=>window.clearInterval(timer)); hero.addEventListener('pointerleave',schedule); document.addEventListener('visibilitychange',schedule); motion.addEventListener('change',()=>{if(motion.matches)paused=true;schedule();}); window.addEventListener('pagehide',()=>window.clearInterval(timer));schedule(); } if(typeof document!=='undefined')mountHero(document,window);