Calendrier de l'Avent 🎄

Ouvrez une case chaque jour pour découvrir cadeaux, offres et codes de réduction ✨

`; container.innerHTML = ''; container.appendChild(wrapper); } function uidReady(){ return state.uid && (localStorage.getItem(LS_ADFREE_PREFIX + state.uid) || '').length>0; } async function initUID(){ let ip=''; try{ const r=await fetch('https://api.ipify.org?format=json'); const j=await r.json(); ip=j&&j.ip||''; }catch{} const ua=navigator.userAgent||''; const tz=(Intl.DateTimeFormat().resolvedOptions().timeZone)||''; const base=ip+'|'+ua+'|'+tz; state.uid = await sha256(base); try{ localStorage.setItem(LS_UID_KEY,state.uid);}catch{} const url=new URL(window.location.href); const paid=url.searchParams.get('paid'); const uid=url.searchParams.get('uid'); if(paid==='1'){ try{ localStorage.setItem(LS_ADFREE_PREFIX+(uid||state.uid),'true'); }catch{} } state.adsRemoved = !!localStorage.getItem(LS_ADFREE_PREFIX+state.uid); state.showAds = !state.adsRemoved && !state.isPreview; } async function sha256(str){ const enc=new TextEncoder(); const buf=await crypto.subtle.digest('SHA-256', enc.encode(str)); return Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''); } function openPaywall(){ const returnUrl = location.origin + location.pathname; const html = `

Retirer les publicités (0,50€)

Votre identifiant: ${state.uid||'...'}

Après paiement, vous serez redirigé ici. Les pubs seront désactivées automatiquement pour cet identifiant.

cards
Optimisé par paypal
`; openModal(html); const f = document.getElementById('ppForm'); f.addEventListener('submit', function(){ const base='https://www.paypal.com/ncp/payment/PX66WSWHFH9NW'; const u=new URL(base); u.searchParams.set('uid', state.uid||''); u.searchParams.set('return', returnUrl + '?paid=1&uid=' + (state.uid||'')); f.action = u.toString(); }); document.getElementById('closePay').onclick = closeModal; } function renderCountdown(){ const target = new Date('2025-12-01T00:00:00+01:00').getTime(); const show = !state.isPreview && Date.now() < target; const overlay = document.getElementById('overlayCountdown'); if(show){ overlay.classList.remove('hidden'); } else { overlay.classList.add('hidden'); } if(show){ const tick = () => { const diff = Math.max(0, target - Date.now()); const d = Math.floor(diff / (1000*60*60*24)); const h = Math.floor((diff / (1000*60*60)) % 24); const m = Math.floor((diff / (1000*60)) % 60); const s = Math.floor((diff / 1000) % 60); document.getElementById('countdown').textContent = String(d).padStart(2,'0')+'j : '+String(h).padStart(2,'0')+'h : '+String(m).padStart(2,'0')+'m : '+String(s).padStart(2,'0')+'s'; }; tick(); if(window.__countId) clearInterval(window.__countId); window.__countId = setInterval(tick, 1000); } else { if(window.__countId) clearInterval(window.__countId); } } function render(){ renderDate(); makeFlakes(); renderGrid(); if(!state.adsRemoved){ renderAds(document.getElementById('adsTop')); renderAds(document.getElementById('adsBottom')); renderAds(document.getElementById('countAdsTop')); renderAds(document.getElementById('countAdsBottom')); } renderCountdown(); } function bindTopButtons(){ const openAdmin = ()=> openAdminLogin(); document.getElementById('btnAdminTop').onclick = openAdmin; document.getElementById('btnAdminBottom').onclick = openAdmin; const openPubs = ()=> openPaywall(); document.getElementById('btnPubsTop').onclick = openPubs; document.getElementById('btnPubsBottom').onclick = openPubs; } function openAdminLogin(){ const html = `

Accès administrateur

`; openModal(html); document.getElementById('adminCancel').onclick = closeModal; document.getElementById('adminOk').onclick = ()=>{ const v = document.getElementById('adminCode').value.trim(); if(v==='MDPADMIN123'){ state.isAdmin=true; closeModal(); openAdminPanel(); } else { alert('Code incorrect'); } }; } function openAdminPanel(){ const emailsUnique = Array.from(new Set(state.emails.map(e=>e.trim().toLowerCase()).filter(Boolean))); const tabBtn = (id, label, active) => ``; const dayOptions = Array.from({length:24},(_,i)=>``).join(''); const r = state.rewards[1]; const html = `
${tabBtn('content','Contenu',true)}${tabBtn('stats','Statistiques',false)}${tabBtn('emails','Emails',false)}
Mode d'affichage
Preview = toutes les cases ouvrables | Réel = cases ≤ jour courant (Déc. 2025)

Astuce : Exportez le JSON une fois votre calendrier prêt pour le réutiliser plus tard.

`; openModal(html); function switchTab(id){ ['content','stats','emails'].forEach(t=>{ document.getElementById('tab-'+t)?.classList.add('hidden'); }); if(id==='content') document.getElementById('tab-content').classList.remove('hidden'); if(id==='stats') document.getElementById('tab-stats').classList.remove('hidden'); if(id==='emails') document.getElementById('tab-emails').classList.remove('hidden'); } document.querySelectorAll('[data-tab]').forEach(btn=> btn.addEventListener('click', ()=>{ switchTab(btn.getAttribute('data-tab')); document.querySelectorAll('[data-tab]').forEach(b=>b.className='rounded-lg px-3 py-1.5 text-sm font-semibold bg-gray-100 text-gray-800 hover:bg-emerald-500 hover:text-white'); btn.className='rounded-lg px-3 py-1.5 text-sm font-semibold bg-emerald-600 text-white'; })); const fldDay = document.getElementById('fldDay'); const fldType = document.getElementById('fldType'); const fldTitle = document.getElementById('fldTitle'); const fldDesc = document.getElementById('fldDesc'); const fldCode = document.getElementById('fldCode'); const fldCtaText = document.getElementById('fldCtaText'); const fldCtaHref = document.getElementById('fldCtaHref'); const rowCode = document.getElementById('rowCode'); function loadForm(){ const d=Number(fldDay.value); const r=state.rewards[d]||{}; fldType.value=r.type||'gift'; fldTitle.value=r.title||''; fldDesc.value=r.description||''; fldCtaText.value=r.ctaText||''; fldCtaHref.value=r.ctaHref||''; fldCode.value=r.code||''; rowCode.classList.toggle('hidden', !(fldType.value==='discount' || fldType.value==='lead')); } fldDay.onchange = loadForm; loadForm(); fldType.onchange = ()=>{ rowCode.classList.toggle('hidden', !(fldType.value==='discount' || fldType.value==='lead')); updateReward(); }; [fldTitle,fldDesc,fldCode,fldCtaText,fldCtaHref].forEach(el=> el.addEventListener('input', updateReward)); function updateReward(){ const d=Number(fldDay.value); state.rewards[d] = { ...(state.rewards[d]||{}), type: fldType.value, title: fldTitle.value, description: fldDesc.value, code: fldCode.value, ctaText: fldCtaText.value, ctaHref: fldCtaHref.value }; save(LS_REWARDS_KEY,state.rewards); } document.getElementById('previewSwitch').onchange = (e)=>{ state.isPreview = !e.target.checked; try{ localStorage.setItem(LS_PREVIEW_KEY, state.isPreview?'true':'false'); }catch{}; render(); }; document.getElementById('resetLocal').onclick = ()=>{ state.unwrapped={}; save(LS_UNWRAPPED_KEY,state.unwrapped); render(); }; document.getElementById('genReset').onclick = async ()=>{ const url=new URL(location.href); url.searchParams.set('reset', Date.now().toString()); try{ await navigator.clipboard.writeText(url.toString()); alert('Lien copié ✅'); }catch{ const ta=document.createElement('textarea'); ta.value=url.toString(); document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); alert('Lien copié ✅'); } }; document.getElementById('exportJson').onclick = ()=>{ const blob=new Blob([JSON.stringify(state.rewards,null,2)],{type:'application/json'}); const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download='avent_rewards.json'; document.body.appendChild(a); a.click(); a.remove(); }; document.getElementById('importJson').onchange = (e)=>{ const f=e.target.files&&e.target.files[0]; if(!f) return; const r=new FileReader(); r.onload = ()=>{ try{ const data=JSON.parse(String(r.result)); if(data && typeof data==='object'){ state.rewards={...state.rewards,...data}; save(LS_REWARDS_KEY,state.rewards); loadForm(); render(); document.getElementById('importDone').classList.remove('hidden'); setTimeout(()=>document.getElementById('importDone').classList.add('hidden'),1500); } else throw new Error(); }catch{ alert('Fichier invalide'); } }; r.readAsText(f); e.target.value=''; }; document.getElementById('adminQuit').onclick = closeModal; const statsBody = document.getElementById('statsBody'); statsBody.innerHTML=''; for(let d=1; d<=24; d++){ const s=state.stats[d]||{opens:0,copies:0,ctaClicks:0,emails:0}; const tr=document.createElement('tr'); tr.className='odd:bg-white even:bg-gray-50'; tr.innerHTML=`${d}${s.opens}${s.ctaClicks}${s.copies}${s.emails}`; statsBody.appendChild(tr); } const emailsList = document.getElementById('emailsList'); emailsList.innerHTML=''; emailsUnique.forEach((e,i)=>{ const li=document.createElement('li'); li.className='px-3 py-2 text-sm'; li.textContent=e; emailsList.appendChild(li); }); document.getElementById('exportCSV').onclick = ()=>{ const csv = 'email\n' + emailsUnique.join('\n'); const blob = new Blob([csv], {type:'text/csv;charset=utf-8;'}); const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download='emails_calendrier.csv'; document.body.appendChild(a); a.click(); a.remove(); }; } function initTimers(){ setInterval(()=>{ state.now=new Date(); renderDate(); }, 1000); } function installResetHandler(){ const url=new URL(location.href); const reset=url.searchParams.get('reset'); if(reset){ try{ localStorage.removeItem(LS_UNWRAPPED_KEY); state.unwrapped={}; }catch{} url.searchParams.delete('reset'); history.replaceState({},'',url.toString()); render(); } } async function start(){ loadState(); await initUID(); bindTopButtons(); installResetHandler(); render(); initTimers(); if(!state.adsRemoved && !state.isPreview){ openPaywall(); } } document.addEventListener('DOMContentLoaded', start); })();