const words={ en:{title:'From the vitrines',demo:'A preview of the vitrines',all:'Explore all',pause:'Pause the ribbon',play:'Play the ribbon',fresh:'New',sample:'Demo',request:'Price on request',asking:'Seller’s asking price'}, fr:{title:'Au fil des vitrines',demo:'Un aperçu des vitrines',all:'Tout découvrir',pause:'Mettre le ruban en pause',play:'Animer le ruban',fresh:'Nouveau',sample:'Démo',request:'Prix sur demande',asking:'Prix demandé par la boutique'}, ar:{title:'من الڤيترينات',demo:'نظرة على الڤيترينات',all:'شوف الكل',pause:'وقف الحركة',play:'شغل الحركة',fresh:'جديد',sample:'تجريبي',request:'الثمن عند المحل',asking:'الثمن اللي حاط المحل'} }; const safeId=value=>typeof value==='string'&&/^[a-zA-Z0-9_-]{1,160}$/.test(value); const escapeHtml=value=>String(value??'').replace(/[&<>"']/g,ch=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[ch])); export function ribbonListings(rows){ const cities=new Set(),ids=new Set();return (Array.isArray(rows)?rows:[]).filter(item=>{ if(item?.type!=='PROFESSIONAL'||item.status!=='PUBLISHED'||!safeId(item.id)||!safeId(item.owner_id)||!safeId(item.city_id)||cities.has(item.city_id)||ids.has(item.id)||!/^\/api\/(?:v1\/)?images\/[a-zA-Z0-9_-]+$/.test(item.images?.[0]||''))return false; cities.add(item.city_id);ids.add(item.id);return true; }).slice(0,12); } export function ribbonCard(item,language,city,duplicate=false){ const lang=words[language]?language:'en',copy=words[lang],badge=ribbonPrice(item,lang); const legacyDemo=item.is_demo&&/^\/api\/images\/demo_image_\d+_\d+$/.test(item.images[0]); const fresh=!item.is_demo&&item.is_new===true; const href='/jewelry/'+encodeURIComponent(item.id)+'?lang='+lang+'&from=homepage'; return ``; } export function ribbonPrice(item,language='en'){ const lang=words[language]?language:'en'; return Number.isSafeInteger(item.total_asking_cents)&&item.total_asking_cents>0?new Intl.NumberFormat(lang==='fr'?'fr-MA':lang==='ar'?'ar-MA':'en-GB',{maximumFractionDigits:2}).format(item.total_asking_cents/100)+' MAD':words[lang].request; } async function startRibbon(){ const root=document.getElementById('homeVitrine');if(!root)return; const viewport=root.querySelector('.vitrine-window'),track=root.querySelector('.vitrine-track'),seed=crypto.randomUUID().replaceAll('-',''); let items=[],cities=[],cycle=-1,nextBatch=null,busy=false,waiting=false,interval=0,observer=null; const seen=new Set(),lastByCity=new Map(),timers=new Map(),reduced=matchMedia('(prefers-reduced-motion:reduce)'); const locale=()=>{const value=document.documentElement.lang.split('-')[0];return words[value]?value:'en';}; const report=(kind,id)=>{if(typeof dhReportSend==='function')dhReportSend(kind,id,{source:'HOMEPAGE'});}; function stopObserver(){observer?.disconnect();for(const timer of timers.values())clearTimeout(timer);timers.clear();} function watch(){ stopObserver();if(typeof IntersectionObserver!=='function'||typeof dhMetricsAllowed!=='function'||!dhMetricsAllowed())return; observer=new IntersectionObserver(entries=>{for(const entry of entries){ const card=entry.target,id=card.dataset.vitrineListing;clearTimeout(timers.get(card)); if(!entry.isIntersecting||entry.intersectionRatio<.5||document.hidden||seen.has(id))continue; timers.set(card,setTimeout(()=>{if(!card.isConnected||document.hidden||seen.has(id))return;seen.add(id);report('IMPRESSION',id);},1000)); }},{threshold:[0,.5]});track.querySelectorAll('[data-vitrine-listing]').forEach(card=>observer.observe(card)); } function render(){ root.hidden=!items.length;if(!items.length){stopObserver();return;} const language=locale(),names={en:'Jewelry from cities across Morocco',fr:'Bijoux des villes du Maroc',ar:'حلي من مدن المغرب'}; root.setAttribute('aria-label',names[language]+(items.some(item=>item.is_demo)?' · '+words[language].sample:'')); root.dataset.cycle=String(cycle); const cityName=item=>{const city=cities.find(city=>city.id===item.city_id);return (language==='ar'?city?.name_ar:city?.name)||item.city_id;}; const group=duplicate=>items.map(item=>ribbonCard(item,language,cityName(item),duplicate)).join(''); const lapWidth=items.length*(matchMedia('(max-width:600px)').matches?138:160); const copies=Math.max(2,Math.ceil(viewport.clientWidth/lapWidth)+1); track.innerHTML=`