PageSourceSearch

https://www.peluchesetjouetsenbois.fr/wp-content/mu-plugins/factory-chat/chat.js?ver=1.0.1790864144

js peluchesetjouetsenbois.fr collected 2026-10-04 22:20:59 UTC 12,370 bytes, 77 lines download raw bytes

1(function(){var W=document.getElementById('fchat');if(!W)return;W.hidden=false;
2/* polices : si le site n'en déclare pas dans le Customizer, on prend celles calculées du body et des titres */
3try{var cs=getComputedStyle(W);var fb=cs.getPropertyValue('--fc-fb').trim(),ft=cs.getPropertyValue('--fc-ft').trim();if(!fb||fb==='inherit'){W.style.setProperty('--fc-fb',getComputedStyle(document.body).fontFamily);}if(!ft||ft==='inherit'){var h=document.querySelector('h1,h2');W.style.setProperty('--fc-ft',h?getComputedStyle(h).fontFamily:getComputedStyle(document.body).fontFamily);}}catch(e){}
4var relais=W.getAttribute('data-relais')+'/chat',sitekey=W.getAttribute('data-turnstile'),agent=W.getAttribute('data-agent');function banque(sel){try{return JSON.parse(W.querySelector(sel).textContent);}catch(e){return [];}}var B1=banque('[data-fchat-m1]'),B2=banque('[data-fchat-m2]');function tirer(b){return b[Math.floor(Math.random()*b.length)]||'';}
5var fen=W.querySelector('[data-fchat-fen]'),fil=W.querySelector('[data-fchat-fil]'),form=W.querySelector('[data-fchat-form]'),ta=form.querySelector('textarea'),btn=form.querySelector('button'),tsBox=W.querySelector('[data-fchat-ts]'),notifs=W.querySelector('[data-fchat-notifs]'),badge=W.querySelector('[data-fchat-badge]');
6var hist=[],sid='',widget=null,tsPret=null,nonLus=0,ouvert=false,etat={};
7var persona='';/* interlocuteur choisi (sites à plusieurs intervenants) — vide = l'agent du site */
8function lire(k,d){try{var v=sessionStorage.getItem(k);return v===null?d:JSON.parse(v);}catch(e){return d;}}function ecrire(k,v){try{sessionStorage.setItem(k,JSON.stringify(v));}catch(e){}}
9sid=lire('fchat_sid','');hist=lire('fchat_h',[]);etat=lire('fchat_e',{});
10if(!sid){sid=Array.from(crypto.getRandomValues(new Uint8Array(12)),function(b){return b.toString(16).padStart(2,'0');}).join('');ecrire('fchat_sid',sid);}
11function esc(s){return s.replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
12function lier(s){var h=location.origin;return esc(s).replace(new RegExp('('+h.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'[^\\s<]*)','g'),function(u){var f=u.replace(/[.,;:!?)]+$/,'');return '<a href="'+f+'">'+f+'</a>'+u.slice(f.length);});}
13function bulle(role,txt){var d=document.createElement('div');d.className='fchat-m '+(role==='user'?'fchat-m--moi':'fchat-m--bot');if(role==='user')d.textContent=txt;else d.innerHTML=lier(txt);fil.appendChild(d);fil.scrollTop=fil.scrollHeight;return d;}
14function ecrit(mode){var e=fil.querySelector('.fchat-ecrit');if(!mode){if(e)e.remove();return;}if(!e){e=document.createElement('div');e.className='fchat-ecrit';fil.appendChild(e);}e.innerHTML=mode==='attente'?'<i class="fchat-att"><span></span></i>En attente de réponse…':'<i><span></span><span></span><span></span></i>'+esc(agent)+' écrit…';fil.scrollTop=fil.scrollHeight;}
15function sauver(){if(hist.length>16)hist=hist.slice(-16);ecrire('fchat_h',hist);}
16function aEcrit(){return hist.some(function(m){return m.role==='user';});}
17function accueil(txt){hist.push({role:'assistant',content:txt,accueil:true});sauver();if(ouvert){bulle('assistant',txt);}else{nonLus++;badge.textContent=nonLus;badge.hidden=false;notif(txt);}}
18var photo=W.getAttribute('data-photo')||'';function av(){return photo?'<img class="fchat-av" src="'+esc(photo)+'" alt="">':'<span class="fchat-av">'+esc(agent.charAt(0))+'</span>';}
19function notif(txt){var n=document.createElement('div');n.className='fchat-notif';n.innerHTML=av()+'<div><b>'+esc(agent)+'</b><p>'+esc(txt)+'</p></div>';n.addEventListener('click',ouvrir);notifs.appendChild(n);requestAnimationFrame(function(){n.classList.add('est-visible');});setTimeout(function(){n.classList.add('disparait');setTimeout(function(){n.remove();},950);},mobile()?4200:3200);}
20function mobile(){return window.matchMedia('(max-width:600px)').matches;}
21var scrollSauv=0,verrouille=false;function verrou(on){if(on&&mobile()){if(verrouille)return;verrouille=true;scrollSauv=window.scrollY||0;document.documentElement.classList.add('fchat-lock');document.body.classList.add('fchat-lock');document.body.style.top=(-scrollSauv)+'px';}else if(verrouille){verrouille=false;document.documentElement.classList.remove('fchat-lock');document.body.classList.remove('fchat-lock');document.body.style.top='';window.scrollTo(0,scrollSauv);}}
22/* mobile : la fenêtre épouse EXACTEMENT la zone visible (clavier compris) via visualVie
22wport : en-tête en haut, saisie au-dessus du clavier, seul le fil défile */
23function ajusterVV(){if(!ouvert||!mobile()||!window.visualViewport){return;}var vv=window.visualViewport;fen.style.top=Math.max(0,vv.offsetTop)+'px';fen.style.height=Math.round(vv.height)+'px';fen.style.bottom='auto';window.scrollTo(0,0);fil.scrollTop=fil.scrollHeight;}
24if(window.visualViewport){window.visualViewport.addEventListener('resize',ajusterVV);window.visualViewport.addEventListener('scroll',ajusterVV);}
25ta.addEventListener('focus',function(){setTimeout(ajusterVV,50);setTimeout(ajusterVV,350);});ta.addEventListener('blur',function(){setTimeout(ajusterVV,50);});
26function ouvrir(){ouvert=true;etat.vu=1;etat.ouvert=1;ecrire('fchat_e',etat);if(!etat.m1&&!etat.hl){clearTimeout(tm1);direM1();}W.classList.add('est-ouvert');verrou(true);fen.hidden=false;ajusterVV();nonLus=0;badge.hidden=true;notifs.innerHTML='';fil.innerHTML='';hist.forEach(function(m){bulle(m.role,m.content);});ta.focus();chargerTurnstile();verifierEtat();}
27function horsLigne(portee){etat.hl=portee;ecrire('fchat_e',etat);var d=W.querySelector('.fchat-dot'),t=W.querySelector('.fchat-tete-t span');if(d)d.style.background='#9a9a9a';if(t)t.innerHTML=t.innerHTML.replace('en ligne','hors ligne');ta.disabled=true;ta.placeholder=portee==='session'?'Conversation terminée — écrivez-nous via la page contact.':'Hors ligne pour aujourd’hui — écrivez-nous via la page contact.';btn.disabled=true;}
28function fermer(){ouvert=false;etat.ouvert=0;ecrire('fchat_e',etat);W.classList.remove('est-ouvert');verrou(false);fen.hidden=true;fen.style.top='';fen.style.height='';fen.style.bottom='';}
29/* Clic sur un lien proposé dans la conversation : on REPLIE le chat avant de naviguer.
30   Sans ça, l'état ouvert est mémorisé en session et la page d'arrivée se charge avec la
31   fenêtre de chat par-dessus le contenu qu'on venait justement consulter (07/09/2026). */
32fil.addEventListener('click',function(e){var a=e.target.closest&&e.target.closest('a[href]');if(!a)return;etat.ouvert=0;ecrire('fchat_e',etat);});
33window.addEventListener('resize',function(){verrou(ouvert);});
34if(etat.ouvert){fen.style.animation='none';ouvrir();fen.style.animation='';}
35// /etat seulement à l'ouverture, une fois par page, en text/plain (pas de pré-requête OPTIONS) : quota Workers 100k/jour (30/09/2026)
36var etatVerifie;function verifierEtat(){if(etatVerifie||etat.hl)return;etatVerifie=true;fetch(W.getAttribute('data-relais')+'/etat',{method:'POST',headers:{'Content-Type':'text/plain'},body:JSON.stringify({sid:sid})}).then(function(r){return r.json();}).then(function(d){if(d&&d.enligne===false){horsLigne(d.portee);}}).catch(function(){});}
37if(etat.hl){horsLigne(etat.hl);}
38/* les deux messages d'accueil : une fois par session — 2 s après l'arrivée, puis 10 s plus tard si le visiteur n'a rien écrit */
39var tm1=null,tm2=null;function direM1(){if(etat.m1||etat.hl)return;etat.m1=1;ecrire('fchat_e',etat);accueil(tirer(B1));tm2=setTimeout(function(){if(etat.m2||aEcrit()||etat.hl||ouvert)return;etat.m2=1;ecrire('fchat_e',etat);accueil(tirer(B2));},25000);}
40if(!etat.m1&&!etat.hl){tm1=setTimeout(direM1,6000);}
41else if(!etat.vu&&hist.length&&!aEcrit()){nonLus=hist.length;badge.textContent=nonLus;badge.hidden=false;}
42function chargerTurnstile(){if(tsPret)return tsPret;tsPret=new Promise(function(res){var s=document.createElement('script');s.src='https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';s.async=true;s.onload=function(){widget=turnstile.render(tsBox,{sitekey:sitekey,execution:'execute',appearance:'execute',callback:function(){}});res();};document.head.appendChild(s);});return tsPret;}
43var dejaExecute=false;function jeton(){return new Promise(function(res,rej){var fini=false,fin=function(v,ok){if(fini)return;fini=true;clearTimeout(tm);ok?res(v):rej();},tm=setTimeout(function(){fin(null,false);},12000);try{if(dejaExecute)turnstile.reset(widget);dejaExecute=true;turnstile.execute(widget,{callback:function(v){fin(v,true);},'error-callback':function(){fin(null,false);},'expired-callback':function(){fin(null,false);}});}catch(e){fin(null,false);}});}
44var bouton=W.querySelector('[data-fchat-ouvrir]');if(photo){var mini=document.createElement('img');mini.className='fchat-mini';mini.src=photo;mini.alt='';mini.width=26;mini.height=26;bouton.appendChild(mini);}
45bouton.addEventListener('click',ouvrir);
46/* API du site : window.factoryChat.ouvrir({persona,nom,avatar}) — ouvre le chat AVEC cette personne.
47   Changer d'interlocuteur repart sur un fil neuf (nouveau sid) : une conversation qui change de
48   personne au milieu serait illisible dans l'archive. Sans argument, comportement d'origine. */
49function neufFil(){hist=[];clearTimeout(tm1);clearTimeout(tm2);
50 /* m1/m2 marqués comme déjà dits : les accueils génériques du site ne doivent pas s'intercaler
51    au milieu d'une conversation ouverte avec quelqu'un en particulier. */
52 etat={m1:1,m2:1,vu:1,ouvert:1};ecrire('fchat_h',hist);ecrire('fchat_e',etat);
53 sid=Array.from(crypto.getRandomValues(new Uint8Array(12)),function(b){return b.toString(16).padStart(2,'0');}).join('');ecrire('fchat_sid',sid);
54 fil.innerHTML='';nonLus=0;badge.hidden=true;notifs.innerHTML='';clearTimeout(tm1);clearTimeout(tm2);}
55window.factoryChat={ouvrir:function(o){o=o||{};
56 var cle=(o.persona||'').toString();
57 if(cle!==persona){neufFil();persona=cle;
58  if(o.nom){agent=o.nom.split(' ')[0];W.setAttribute('data-agent',agent);
59   var tb=W.querySelector('.fchat-tete-t b');if(tb)tb.textContent=o.nom;
60   var ts2=W.querySelector('.fchat-tete-t span');if(ts2&&o.role)ts2.innerHTML=esc(o.role)+' · <i class="fchat-dot"></i> en ligne';}
61  if(o.avatar){photo=o.avatar;W.setAttribute('data-photo',photo);
62   W.querySelectorAll('img.fchat-av,img.fchat-mini,.fchat-tete img').forEach(function(i){i.src=photo;});}
63  if(cle){accueil('Bonjour, '+agent+'. Dites-moi ce qui vous amène — je vous dirai tout de suite si c’est de mon ressort et ce qu’il faut regarder en premier.');}}
64 ouvrir();},persona:function(){return persona;}};
65W.querySelectorAll('[data-fchat-fermer]').forEach(function(b){b.addEventListener('click',fermer);});
66ta.addEventListener('keydown',function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();form.requestSubmit();}});
67form.addEventListener('submit',async function(e){e.preventDefault();var q=ta.value.trim().slice(0,500);if(!q||btn.disabled)return;ta.value='';btn.disabled=true;bulle('user',q);hist.push({role:'user',content:q});sauver();etat.m2=1;clearTimeout(tm2);ecrire('fchat_e',etat);var t0=Date.now(),attente=2000+Math.random()*4000;ecrit('attente');var minuteur=setTimeout(function(){ecrit('ecrit');},attente);
68 try{await chargerTurnstile();var t;try{t=await jeton();}catch(x){t='';}
69  var r=await fetch(relais,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sid:sid,turnstile:t,persona:persona,messages:hist.filter(function(m){return !m.accueil;}).slice(-13)})});var d={};try{d=await r.json();}catch(x){}
70  if(r.ok&&d.silence){clearTimeout(minuteur);await new Promise(function(res){setTimeout(res,Math.max(0,1200-(Date.now()-t0)));});ecrit(false);btn.disabled=false;ta.focus();return;}
71  var rep=d.reponse||'Je n’arrive pas à répondre pour le moment. Réessayez dans quelques minutes, ou écrivez-nous par la page contact.';
72  var reste=Math.max(0,attente+900+Math.min(2500,rep.length*6)-(Date.now()-t0));await new Promise(function(res){setTimeout(res,reste);});clearTimeout(minuteur);ecrit(false);bulle('assistant',rep);
73  if(r.ok){hist.push({role:'assistant',content:rep});sauver();}else{hist.pop();sauver();}
74  if(d.erreur==='quota'){horsLigne(d.portee||'session');btn.disabled=true;ta.focus=function(){};}
75 }catch(x){clearTimeout(minuteur);ecrit(false);bulle('assistant','Impossible de joindre le site. Vérifiez votre connexion et réessayez.');hist.pop();sauver();}
76 if(!etat.hl){btn.disabled=false;ta.focus();}});
77})();

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.