1var I=window.I18N||{eralbl_pop:'Pop-up stores',eralbl_her:'Heritage â the history of the address',heritage_sub:'Heritage · subpage',cremerie:'Cremerie No',cremerie_def:'Cremerie de Paris',no_match:'No match',detail:'Details \u2192',popups:'pop-ups',all:'All',none:'None',hc:'Off-site'}; 2var ALL=POPS.concat(HERITAGE); 3var locURL=window.locURL||function(u){return u;}; // absolu popupstoreparis.com -> chemin local (défini dans lang.js) 4function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"')} 5function thumbBg(p){return 'linear-gradient(150deg,'+p.col+'cc,'+p.col+'33)'} 6function firstWord(n){return n.split(' ')[0]} 7// ---------- 8 FILTRES GLOBAUX (regroupent les 19 catégories) ---------- 8var LANGX=window.LANG||'en'; 9var GDEF=[ 10 {k:'mode', en:'Fashion & Beauty', fr:'Mode & Beauté', col:'#ff2e93', cats:['Mode','Beauté']}, 11 {k:'heritage',en:'Heritage', fr:'Patrimoine', col:'#b08733', cats:['Heritage']}, 12 {k:'food', en:'Food & Drink', fr:'Food & Boissons', col:'#ffb020', cats:['Food','B2B']}, 13 {k:'tech', en:'Tech & Apps', fr:'Tech & Apps', col:'#00c2d1', cats:['Tech','Apps','Fintech']}, 14 {k:'gaming', en:'Gaming & Music', fr:'Gaming & Musique', col:'#8b5cff', cats:['Gaming','Musique']}, 15 {k:'retail', en:'Retail & Lifestyle',fr:'Retail & Lifestyle', col:'#35c07a', cats:['Retail','Maison','Divers','â']}, 16 {k:'art', en:'Art & Culture', fr:'Art & Culture', col:'#d98cc0', cats:['Art','Tourisme','Santé']}, 17 {k:'sport', en:'Sport & Auto', fr:'Sport & Auto', col:'#2ee6a0', cats:['Sport','Auto']} 18]; 19var CAT2G={},GLABEL={},GCOL={}; 20GDEF.forEach(function(g){GLABEL[g.k]=g[LANGX]||g.en;GCOL[g.k]=g.col;g.cats.forEach(function(c){CAT2G[c]=g.k;});}); 21function groupOf(c){return CAT2G[c]||'retail';} 22// ---------- card ---------- 23function cardHTML(p){ 24 var venue=p.h?I.heritage_sub:(p.hc?(p.venue||'Paris'):(p.v?I.cremerie+p.v:I.cremerie_def)); 25 var thumb; 26 if(p.hc){ 27 thumb='<div class="thumb logo'+(p.logo?'':' failed')+'"><span class="lfb">'+esc(firstWord(p.n))+'</span>'+ 28 (p.logo?'<img class="fav" src="'+p.logo+'" alt="'+esc(p.n)+' logo" loading="lazy" onerror="this.parentNode.classList.add(\'failed\');this.remove()">':'')+'</div>'; 29 }else if(p.img){ 30 thumb='<div class="thumb"><img src="'+locURL(p.img)+'" alt="'+esc(p.n)+'" loading="lazy" onerror="this.parentNode.style.background=\''+thumbBg(p)+'\';this.remove()"></div>'; 31 }else{ 32 thumb='<div class="thumb" style="background:'+thumbBg(p)+'"><span>'+esc(firstWord(p.n))+'</span></div>'; 33 } 34 var L=p.n.replace(/^[^A-Za-z]*/,'')[0]; L=L?L.toUpperCase():'#'; 35 var gk=groupOf(p.c); 36 return '<a class="card'+(p.h?' heritage':'')+(p.hc?' offsite':'')+'" href="'+p.u+'" data-cat="'+gk+'" data-letter="'+L+'" data-hc="'+(p.hc?1:0)+'">'+ 37 thumb+'<div class="cbody">'+ 38 '<div class="cat"><span class="chip2" style="background:'+GCOL[gk]+'">'+esc(GLABEL[gk])+'</span></div>'+ 39 '<div class="nm">'+esc(p.n)+'</div>'+ 40 '<div class="meta"><span>'+venue+'</span>'+(p.hc?'<span class="hc-badge">'+(I.hc||'Off-site')+'</span>':'')+'</div>'+ 41 '</div></a>'; 42} 43// ---------- voyage ---------- 44function hueFor(y){ 45 if(y>=2023)return'radial-gradient(66% 70% at 82% 14%,rgba(217,181,103,.28),transparent 55%),linear-gradient(165deg,#241d15,#171310 64%)'; 46 if(y>=2012)return'radial-gradient(66% 70% at 82% 14%,rgba(176,135,51,.24),transparent 55%),linear-gradient(165deg,#211a15,#161009 64%)'; 47 if(y>=1900)return'radial-gradient(64% 68% at 78% 18%,rgba(150,104,52,.22),transparent 55%),linear-gradient(165deg,#1e160e,#130d08 64%)'; 48 return'radial-gradient(60% 66% at 76% 20%,rgba(120,95,60,.2),transparent 58%),linear-gradient(165deg,#191309,#0f0a06 64%)'; 49} 50var voyage=document.getElementById('voyage'); 51var byYear={};ALL.forEach(function(p){(byYear[p.y]=byYear[p.y]||[]).push(p)}); 52var years=Object.keys(byYear).map(Number).sort(function(a,b){return b-a}); 53document.getElementById('yearHud').textContent=years[0]; 54// LAZY-LOAD : on crée les coquilles de section (année + filigrane) mais les CARTES ne sont 55// construites que pour la date la plus récente au chargement ; les autres se remplissent à 56// l'approche du scroll. Gain de rendu initial ; les données restent dans eraLists. 57var eraLists={};
58years.forEach(function(y){ 59 var s=document.createElement('section');s.className='era';s.dataset.y=y;s.id='era-'+y; 60 var list=byYear[y].slice().sort(function(a,b){return a.n<b.n?-1:1}); 61 var heritage=list.some(function(x){return x.h}); 62 eraLists[y]=list; 63 s.innerHTML='<div class="inner"><div class="era-ghost" aria-hidden="true">'+y+'</div><div class="eralbl">'+(heritage?I.eralbl_her:I.eralbl_pop)+'</div><div class="yr">'+y+'</div><div class="cards"></div></div>'; 64 voyage.appendChild(s); 65}); 66var eras=[].slice.call(document.querySelectorAll('.era')); 67function buildEra(s){ 68 if(!s||s.dataset.built)return; 69 s.dataset.built='1'; 70 var list=eraLists[s.dataset.y]||[]; 71 s.querySelector('.cards').innerHTML=list.map(cardHTML).join(''); 72 // applique le filtre courant aux nouvelles cartes + recalcule l'état de la section 73 s.querySelectorAll('.card').forEach(function(c){c.classList.toggle('hide',!active[c.dataset.cat]);}); 74 s.classList.toggle('hide', s.querySelectorAll('.card:not(.hide)').length===0); 75 if(typeof buildRail==='function')buildRail(); 76 if(typeof positionRail==='function')positionRail(); 77} 78// animation de révélation 79var io=new IntersectionObserver(function(es){es.forEach(function(en){ 80 if(en.isIntersecting)en.target.classList.add('show'); 81})},{threshold:.4}); 82eras.forEach(function(e){io.observe(e)}); 83// remplissage paresseux : configuré à l'init (après définition de active/cremerieOnly) 84var lazyIO=new IntersectionObserver(function(es){es.forEach(function(en){ 85 if(en.isIntersecting){buildEra(en.target);lazyIO.unobserve(en.target);} 86})},{rootMargin:'1200px 0px'}); 87// ---------- featured ---------- 88// « The collection » : les 8 pop-ups les plus récents. 89var grid=document.getElementById('grid'); 90(function(){ 91 POPS.slice().sort(function(a,b){return b.y-a.y}).slice(0,8).forEach(function(p){grid.insertAdjacentHTML('beforeend',cardHTML(p))}); 92})(); 93// ---------- AâZ ---------- (construit paresseusement au 1er passage en mode AâZ) 94var az=document.getElementById('azgrid'),azBuilt=false; 95function buildAZ(){ 96 if(azBuilt)return; azBuilt=true; 97 ALL.slice().sort(function(a,b){return a.n.toLowerCase()<b.n.toLowerCase()?-1:1}).forEach(function(p){ 98 var L=p.n.replace(/^[^A-Za-z]*/,'')[0];L=L?L.toUpperCase():'#'; 99 if(az.dataset.l!==L){az.dataset.l=L;az.insertAdjacentHTML('beforeend','<div class="azletter" id="az-'+L+'" data-l="'+L+'">'+L+'</div>')} 100 az.insertAdjacentHTML('beforeend',cardHTML(p)); 101 }); 102 if(typeof applyFilter==='function')applyFilter(); // applique le filtre courant aux cartes AâZ 103} 104// ---------- FILTRES (8 groupes) ---------- 105var gCount={};GDEF.forEach(function(g){gCount[g.k]=0;});ALL.forEach(function(p){var k=groupOf(p.c);gCount[k]=(gCount[k]||0)+1;}); 106var active={};GDEF.forEach(function(g){active[g.k]=true;}); 107var chips=document.getElementById('chips'); 108chips.innerHTML='<button class="chip reset" data-all="1">'+I.all+'</button><button class="chip reset" data-none="1">'+I.none+'</button>'+ 109 '<button class="navbtn" data-scroll="-1" aria-label="Scroll left">â¹</button>'+ 110 '<button class="navbtn" data-scroll="1" aria-label="Scroll right">âº</button>'+ 111 '<div class="chipscroll" id="chipscroll">'+GDEF.map(function(g){ 112 return '<button class="chip" data-c="'+g.k+'"><span class="dot" style="background:'+g.col+'"></span>'+esc(GLABEL[g.k])+' <span style="opacity:.6">'+gCount[g.k]+'</span></button>'; 113}).join('')+'</div>'; 114var chipscroll=document.getElementById('chipscroll'); 115var arrPrev=chips.querySelector('button[data-scroll="-1"]'),arrNext=chips.querySelector('button[data-scroll="1"]'); 116function updateArrows(){ 117 var max=chipscroll.scrollWidth-chipscroll.clientWidth-1; 118 arrPrev.classList.toggle('dis',chipscroll.scrollLeft<=0); 119 arrNext.classList.toggle('dis',chipscroll.scrollLeft>=max); 120} 121chipscroll.addEventListener('scroll',updateArrows); 122window.addEventListener('resize',updateArrows); 123updateArrows(); 124chips.addEventListener('click',function(e){ 125 var b=e.target.closest('button');if(!b)return; 126 if(b.dataset.scroll){chipscroll.scrollBy({left:Number(b.dataset.scroll)*260,behavior:'smooth'});return;}
127 if(b.dataset.all){GDEF.forEach(function(g){active[g.k]=true});} 128 else if(b.dataset.none){GDEF.forEach(function(g){active[g.k]=false});} 129 else if(b.dataset.c){active[b.dataset.c]=!active[b.dataset.c];} 130 syncChips();applyFilter(); 131}); 132function syncChips(){[].forEach.call(chips.querySelectorAll('.chip[data-c]'),function(b){b.classList.toggle('off',!active[b.dataset.c])})} 133// ---------- FILTER + RAIL ---------- 134function applyFilter(){ 135 document.querySelectorAll('.card').forEach(function(c){c.classList.toggle('hide',!active[c.dataset.cat])}); 136 eras.forEach(function(e){e.classList.toggle('hide', !!e.dataset.built && e.querySelectorAll('.card:not(.hide)').length===0)}); 137 [].forEach.call(document.querySelectorAll('.azletter'),function(h){ 138 var any=[].some.call(document.querySelectorAll('#azgrid .card[data-letter="'+h.dataset.l+'"]'),function(c){return !c.classList.contains('hide')}); 139 h.classList.toggle('hide',!any); 140 }); 141 buildRail(); 142 if(typeof positionRail==='function')positionRail(); 143} 144var rail=document.getElementById('rail'); 145function buildRail(){ 146 var azMode=document.body.classList.contains('mode-az'); 147 rail.innerHTML=''; 148 if(azMode){ 149 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').forEach(function(L){ 150 var has=[].some.call(document.querySelectorAll('#azgrid .card[data-letter="'+L+'"]'),function(c){return !c.classList.contains('hide')}); 151 var a=document.createElement('a');a.textContent=L;a.dataset.l=L; 152 if(has)a.onclick=function(){var el=document.getElementById('az-'+L);if(el)el.scrollIntoView({behavior:'smooth'})}; 153 else a.classList.add('off'); 154 rail.appendChild(a); 155 }); 156 }else{ 157 years.forEach(function(y){ 158 var era=document.getElementById('era-'+y);var has=era&&!era.classList.contains('hide'); 159 var a=document.createElement('a');a.textContent=y;a.dataset.y=y; 160 if(has)a.onclick=function(){var idx=eras.indexOf(era);for(var i=0;i<=idx;i++)buildEra(eras[i]);era.scrollIntoView({behavior:'smooth'});}; 161 else a.classList.add('off'); 162 rail.appendChild(a); 163 }); 164 } 165} 166// ---------- MODE ---------- 167function ensureLatest(){ // la SECTION LA PLUS RÃCENTE (eras[0], quelle que soit l'année) : jamais en lazy â construite + révélée d'emblée 168 if(eras[0]){buildEra(eras[0]);eras[0].classList.add('show');} 169} 170function setMode(m){ 171 if(m==='az')buildAZ(); // construit la grille AâZ au 1er passage 172 else ensureLatest(); // Timeline : la date la plus récente s'affiche immédiatement (hors lazy) 173 document.body.classList.toggle('mode-az',m==='az'); 174 [].forEach.call(document.getElementById('sort').children,function(b){b.classList.toggle('on',b.dataset.m===m)}); 175 buildRail();window.scrollTo({top:0});positionRail(); 176} 177document.getElementById('sort').addEventListener('click',function(e){if(e.target.dataset.m)setMode(e.target.dataset.m)}); 178// --- mobile bottom-bar navigation --- 179function setActiveNav(i){var a=document.querySelectorAll('#appbar a');a.forEach(function(x,ix){x.classList.toggle('active',ix===i)});} 180function navHome(){if(!(window.matchMedia&&window.matchMedia('(max-width:768px)').matches))setMode('timeline');setActiveNav(0);window.scrollTo({top:0,behavior:'smooth'});} 181function navTimeline(){ // active le filtre Timeline + remonte sur la 1re date 182 setMode('timeline');setActiveNav(1); 183 var first=document.querySelector('#voyage .era:not(.hide)'); 184 if(first){setTimeout(function(){first.scrollIntoView({behavior:'smooth',block:'start'});},50);} 185} 186function navAZ(){ // active le filtre AâZ + remonte sur la lettre A (haut de liste) 187 setMode('az');setActiveNav(2); 188 var first=document.querySelector('#azgrid .azletter:not(.hide)'); 189 setTimeout(function(){ if(first)first.scrollIntoView({behavior:'smooth',block:'start'}); else window.scrollTo({top:0,behavior:'smooth'}); },50); 190} 191function navMap(){setActiveNav(3);if(typeof buildAllForBottom==='function')buildAllForBottom();var m=document.getElementById('map');if(m)m.scrollIntoView({behavior:'smooth'});} 192 193// --- RAIL FIXE (ne glisse pas) + GROSSE DATE propre à chaque section --- 194// Le rail est ancré sous les filtres (2026 en haut) et ne bouge PAS au scroll : seule la 195// surbrillance descend. La grosse date est un filigrane interne à chaque .era (.era-ghost) 196// qui défile avec sa section. L'alignement Gauche(date)/Centre(en-tête)/Droite(rail) se 197// produit à l'instant où l'en-tête d'une section croise sa propre entrée de rail. 198var headerEl=document.querySelector('header'); 199var RAIL_GAP=14; 200var railLastKey=null; 201function stickyBottom(){ 202 var hb=headerEl?headerEl.getBoundingClientRect().bottom:0; 203 var sub=document.querySelector('.subbar'); 204 if(!sub)return hb; 205 return Math.max(hb, sub.getBoundingClientRect().bottom); // suit la barre puis se colle 206} 207function centerY(el){var r=el.getBoundingClientRect();return r.top+r.height/2;} 208function positionRail(){ 209 if(getComputedStyle(rail).display==='none')return; 210 rail.style.maxHeight='none';rail.style.overflow='visible'; 211 var azMode=document.body.classList.contains('mode-az'); 212 rail.style.top=(stickyBottom()+RAIL_GAP)+'px'; // ancré sous les filtres â FIXE, ne glisse pas 213 var list=[]; 214 if(azMode){ 215 [].forEach.call(document.querySelectorAll('#azgrid .azletter'),function(el){ 216 if(el.classList.contains('hide'))return; 217 var e=rail.querySelector('a[data-l="'+el.dataset.l+'"]'); 218 if(e)list.push({key:el.dataset.l,hy:centerY(el),entry:e}); 219 }); 220 }else{
221 years.forEach(function(y){ 222 var era=document.getElementById('era-'+y);if(!era||era.classList.contains('hide'))return; 223 var yr=era.querySelector('.yr'),e=rail.querySelector('a[data-y="'+y+'"]'); 224 if(yr&&e)list.push({key:String(y),hy:centerY(yr),entry:e}); 225 }); 226 } 227 if(!list.length)return; 228 // surbrillance = section dont l'en-tête est la plus proche de SA propre entrée de rail 229 var ref=list[0],best=Infinity; 230 list.forEach(function(o){var d=Math.abs(o.hy-centerY(o.entry));if(d<best){best=d;ref=o;}}); 231 [].forEach.call(rail.children,function(a){a.classList.toggle('on',(azMode?a.dataset.l:a.dataset.y)===ref.key);}); 232 if(!azMode && ref.key!==railLastKey){ 233 document.getElementById('bg').style.background=hueFor(Number(ref.key)); 234 railLastKey=ref.key; 235 } 236} 237var railTick=false; 238window.addEventListener('scroll',function(){if(!railTick){railTick=true;window.requestAnimationFrame(function(){positionRail();railTick=false;});}},{passive:true}); 239window.addEventListener('resize',positionRail); 240positionRail(); 241// init 242applyFilter(); 243// LAZY : construire la date la plus récente maintenant, puis observer les autres sections 244ensureLatest(); // date la plus récente : construite + révélée immédiatement (hors lazy) 245eras.forEach(function(e){if(!e.dataset.built)lazyIO.observe(e);}); 246// AâZ ACTIF PAR DÃFAUT (PC + mobile). Sur PC le bascule Timeline/AâZ reste dispo ; sur mobile il est masqué. 247setMode('az'); 248// ---------- SEARCH ---------- 249var q=document.getElementById('q'),ac=document.getElementById('ac'),hl=-1,cur=[]; 250function matchPops(v){return ALL.filter(function(p){return p.n.toLowerCase().indexOf(v)>-1||String(p.y).indexOf(v)>-1||p.c.toLowerCase().indexOf(v)>-1}).slice(0,8);} 251function resultsHTML(list){ 252 return list.length?list.map(function(p,i){ 253 var t=p.img?'style="background-image:url('+locURL(p.img)+')"':'style="background:'+thumbBg(p)+'"'; 254 return '<a href="'+p.u+'" data-i="'+i+'"><span class="t" '+t+'>'+(p.img?'':esc(firstWord(p.n))[0])+'</span><span><span class="nm">'+esc(p.n)+'</span><br><span class="mt">'+p.c+(p.h?' · heritage':'')+'</span></span><span class="yr">'+p.y+'</span></a>'; 255 }).join(''):'<a style="cursor:default;color:var(--muted-d)">'+I.no_match+'</a>'; 256} 257function search(v){ 258 v=v.trim().toLowerCase(); 259 if(!v){ac.classList.remove('show');return} 260 cur=matchPops(v); 261 ac.innerHTML=resultsHTML(cur); 262 ac.classList.add('show');hl=-1; 263} 264q.addEventListener('input',function(){search(this.value)}); 265q.addEventListener('keydown',function(e){ 266 var items=ac.querySelectorAll('a[href]'); 267 if(e.key==='ArrowDown'){e.preventDefault();hl=Math.min(hl+1,items.length-1)} 268 else if(e.key==='ArrowUp'){e.preventDefault();hl=Math.max(hl-1,0)} 269 else if(e.key==='Enter'){if(cur[hl]||cur[0])location.href=(cur[hl]||cur[0]).u;return} 270 else return; 271 items.forEach(function(a,i){a.classList.toggle('hl',i===hl)}); 272}); 273document.addEventListener('click',function(e){if(!e.target.closest('.search'))ac.classList.remove('show')}); 274// --- recherche MOBILE (barre flottante au-dessus de l'appbar, indépendante du scroll) --- 275var mq=document.getElementById('mq'),mac=document.getElementById('mac'); 276if(mq&&mac){ 277 mq.addEventListener('input',function(){ 278 var v=this.value.trim().toLowerCase(); 279 if(!v){mac.classList.remove('show');mac.innerHTML='';return;} 280 mac.innerHTML=resultsHTML(matchPops(v));mac.classList.add('show'); 281 }); 282} 283window.toggleSearch=function(){ 284 var m=document.getElementById('msearch');if(!m)return; 285 var open=m.classList.toggle('open'); 286 if(open&&mq){mq.focus();} 287 if(!open&&mac){mac.classList.remove('show');} 288}; 289// ---------- MAP (Leaflet) ---------- 290(function(){ 291 if(typeof L==='undefined')return; 292 var map=L.map('lmap',{scrollWheelZoom:false}).setView([48.86003477363194,2.3462419779980492],17); 293 L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{maxZoom:20,subdomains:'abcd',attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> © <a href="https://carto.com/attributions">CARTO</a>'}).addTo(map); 294 var V=[ 295 {no:'1',ll:[48.86003477363194,2.3462419779980492],a:'9-11 rue des Déchargeurs â VB',n:56,big:true}, 296 {no:'2·3',ll:[48.85976458091869,2.346570595253106],a:'11 rue des Halles',n:2}, 297 {no:'6·7·8',ll:[48.86040,2.34795],a:'12 rue de la Ferronnerie',n:11}, 298 {no:'9',ll:[48.86095,2.34520],a:'45 rue Saint-Honoré',n:11} 299 ];
300 V.forEach(function(v){ 301 var sz=v.big?46:34; 302 var ic=L.divIcon({className:'',html:'<div class="venpin'+(v.big?' big':'')+'" style="width:'+sz+'px;height:'+sz+'px">No'+v.no+'</div>',iconSize:[sz,sz],iconAnchor:[sz/2,sz/2]}); 303 L.marker(v.ll,{icon:ic}).addTo(map) 304 .bindPopup('<b>Cremerie No'+v.no+'</b><br>'+v.a+'<br>'+v.n+' '+I.popups+'<br><a href="#brands">'+I.detail+'</a>'); 305 }); 306})(); 307// ---------- SNAP DU HERO (directionnel) ---------- 308// La 1re partie (hero) se scrolle « d'un coup ». On complète dans le SENS du scroll : 309// vers le bas â timeline (menu + filtres + date la plus récente) ; vers le haut â hero. 310// Jamais de retour en arrière : si tu descends, tu vas en bas ; si tu montes, tu vas en haut. 311(function(){ 312 var intro=document.querySelector('.intro'); if(!intro) return; 313 var sub=document.querySelector('.subbar'); 314 var hdr=document.querySelector('header'); 315 var snapping=false,tmr,lastY=window.scrollY,dir=1; 316 function landing(){ // scrollY qui amène l'en-tête de la 1re section juste sous les barres collantes 317 var era=document.querySelector('#voyage .era:not(.hide)'); if(!era) return 0; 318 var head=era.querySelector('.eralbl')||era.querySelector('.yr')||era; 319 var desired=74+(sub?sub.offsetHeight:120)+14; // juste sous header + subbar collés 320 var docY=head.getBoundingClientRect().top+window.scrollY; 321 return Math.max(0,Math.round(docY-desired)); 322 } 323 window.addEventListener('scroll',function(){ 324 var sy=window.scrollY; 325 if(sy!==lastY){dir=sy>lastY?1:-1;lastY=sy;} // sens du dernier mouvement 326 if(snapping) return; 327 if(document.body.classList.contains('mode-az')) return; // snap uniquement en Timeline 328 clearTimeout(tmr); 329 tmr=setTimeout(function(){ 330 var s=window.scrollY,L=landing(); 331 if(s>4 && s<L-4){ // arrêté dans le hero â on complète dans le sens du scroll 332 snapping=true; 333 var target=dir>0?L:0; // bas = timeline, haut = hero 334 window.scrollTo({top:target,behavior:'smooth'}); 335 setTimeout(function(){snapping=false;lastY=window.scrollY;},640); 336 } 337 },90); 338 },{passive:true}); 339})(); 340// ---------- ONGLETS DU MENU (Brands / Timeline / Map / Your Pop-Up) ---------- 341function listTop(){ // haut de la liste : 1re section visible (Timeline) ou 1re lettre (AâZ) 342 if(document.body.classList.contains('mode-az')){ 343 var l=document.querySelector('#azgrid .azletter:not(.hide)'); if(l){l.scrollIntoView();return;} 344 }else{ 345 ensureLatest(); 346 var e=document.querySelector('#voyage .era:not(.hide)'); if(e){e.scrollIntoView();return;} 347 } 348 window.scrollTo({top:0}); 349} 350function buildAllForBottom(){ // sections du BAS (Map / Your Pop-Up) : on construit toute la liste d'abord pour un scroll fiable (sinon le lazy-load fait dériver la cible) 351 if(document.body.classList.contains('mode-az')){ if(typeof buildAZ==='function')buildAZ(); } 352 else { eras.forEach(buildEra); } 353} 354function scrollToEl(el){ // la liste est déjà entièrement construite (buildAllForBottom) â le scroll fluide atterrit juste ; 2e passe de sécurité après l'animation 355 if(!el)return; el.scrollIntoView(); setTimeout(function(){el.scrollIntoView();},900); 356} 357function gotoHash(h){ 358 if(h==='#brands'){listTop();} 359 else if(h==='#timeline'){setMode('timeline');listTop();} 360 else if(h==='#map'){buildAllForBottom();scrollToEl(document.getElementById('map'));} 361 else if(h==='#yourpopup'){buildAllForBottom();scrollToEl(document.getElementById('yourpopup'));} 362} 363[].forEach.call(document.querySelectorAll('.menu a'),function(a){ 364 var m=(a.getAttribute('href')||'').match(/#(brands|timeline|map|yourpopup)$/); 365 if(m){a.addEventListener('click',function(e){ 366 e.preventDefault(); 367 var hdr=document.querySelector('header'); if(hdr)hdr.classList.remove('menu-open'); // referme le menu burger mobile 368 history.replaceState(null,'','#'+m[1]); 369 gotoHash('#'+m[1]); 370 });} 371}); 372window.addEventListener('hashchange',function(){gotoHash(location.hash);}); 373if(/^#(brands|timeline|map|yourpopup)$/.test(location.hash)){setTimeout(function(){gotoHash(location.hash);},160);} 374 375// ---------- FORMULAIRE « Your Pop-Up » (mailto [email protected] + honeypot + captcha simple) ---------- 376(function(){ 377 var f=document.getElementById('ypForm'); if(!f)return; 378 var TT=window.T||{}; 379 var A=Math.floor(Math.random()*8)+1, B2=Math.floor(Math.random()*8)+1; // captcha additif régénéré à chaque chargement 380 var q=document.getElementById('ypCapQ'); if(q)q.textContent=(TT.yp_cap||'Anti-spam â how much is')+' '+A+' + '+B2+' ?';
381 var note=document.getElementById('ypNote'); 382 function say(msg,ok){ if(note){note.textContent=msg;note.className='yp-note '+(ok?'ok':'err');} } 383 f.addEventListener('submit',function(e){ 384 e.preventDefault(); 385 var hp=document.getElementById('ypHp'); if(hp&&hp.value){return;} // honeypot rempli = bot â ignoré silencieusement 386 var cap=document.getElementById('ypCap'); 387 if(parseInt((cap&&cap.value)||'',10)!==A+B2){ say(TT.yp_capbad||'Incorrect answer.',false); if(cap)cap.focus(); return; } 388 var em=(document.getElementById('ypEmail').value||'').trim(); 389 var nm=(document.getElementById('ypName').value||'').trim(); 390 var ms=(document.getElementById('ypMsg').value||'').trim(); 391 if(!em||!nm||!ms){ say(TT.yp_incomplete||'Please fill in all fields.',false); return; } 392 var subj=encodeURIComponent('Pop-Up project â '+nm); 393 var body=encodeURIComponent('Email: '+em+'\nPop-up store: '+nm+'\n\n'+ms); 394 say(TT.yp_ok||'Opening your email appâ¦',true); 395 window.location.href='mailto:[email protected]?subject='+subj+'&body='+body; 396 }); 397})();
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.