/* Askivio Frontend v2 (consent, multilingual, header avatar, CTA, usage banners) */ (function(){ "use strict"; function onReady(fn){ if (document.readyState==='loading') document.addEventListener('DOMContentLoaded', fn, {once:true}); else fn(); } function css(el, st){ for (const k in st){ el.style[k]=st[k]; } } function isMobile(){ return window.innerWidth<768; } function renderRich(t){ const esc = String(t||'') .replace(/&/g,'&').replace(//g,'>') .replace(/"/g,'"').replace(/'/g,'''); let html = esc.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (_m, label, href) => { return `${label}`; }); html = html.replace(/((https?:\/\/|www\.)[^\s<]+[^<.,:;"')\]\s])/gi, (m) => { const href = m.startsWith('http') ? m : `https://${m}`; return `${m}`; }); html = html.replace(/\n{2,}/g, '\n\n').replace(/\n/g, '
'); return html; } function mapPos(pos,x,y){ const o={top:'',right:'',bottom:'',left:'',transform:''}; x=x||'16px'; y=y||'16px'; switch(pos){ case'tl':o.top=y;o.left=x;break; case'tc':o.top=y;o.left='50%';o.transform='translateX(-50%)';break; case'tr':o.top=y;o.right=x;break; case'cl':o.top='50%';o.left=x;o.transform='translateY(-50%)';break; case'cc':o.top='50%';o.left='50%';o.transform='translate(-50%,-50%)';break; case'cr':o.top='50%';o.right=x;o.transform='translateY(-50%)';break; case'bl':o.bottom=y;o.left=x;break; case'bc':o.bottom=y;o.left='50%';o.transform='translateX(-50%)';break; default:o.bottom=y;o.right=x; } return o; } function el(tag, cls, st){ const d=document.createElement(tag); if(cls)d.className=cls; if(st)css(d,st); return d; } /* ===== Language helpers ===== */ function askivioPreferredLang() { const cfg = window.AskivioCFG || {}; const v = (cfg.lang || '') || (document.documentElement && document.documentElement.lang) || (navigator.language || navigator.userLanguage || ''); const code = String(v || 'en').toLowerCase().slice(0,2); return ['ro','en','it','es','fr','de'].includes(code) ? code : 'en'; } function askivioGuessLang(s){ s = (s||'').toLowerCase().trim(); if (/(bun[ăa]|salut|buna ziua|servus|hei|neața|neata)/.test(s)) return 'ro'; if (/[ăâîșşţț]/.test(s) || /(cina|pranz|mic dejun|rezerv|pret|preț|ofert|program)/.test(s)) return 'ro'; if (/(prenot|prezzo|orario|ristorante|cena|camera)/.test(s)) return 'it'; if (/(reserv|precio|horario|cena|restaurante)/.test(s)) return 'es'; if (/(réserv|prix|horaire|dîner|restaurant)/i.test(s)) return 'fr'; if (/(reserv|preis|öffnungs|abendessen|restaurant|zimmer)/i.test(s)) return 'de'; return 'en'; } /* ===== Consent + session ===== */ const ASKIVIO_LS_CONSENT_KEY = 'AskivioConsentV1'; const ASKIVIO_LS_SID_KEY = 'AskivioSID'; function askivioGetSID(){ let sid = localStorage.getItem(ASKIVIO_LS_SID_KEY); if (!sid) { sid = (crypto && crypto.randomUUID) ? crypto.randomUUID() : 'sid-' + Math.random().toString(36).slice(2) + Date.now().toString(36); localStorage.setItem(ASKIVIO_LS_SID_KEY, sid); } return sid; } function askivioGetConsent(){ const v = localStorage.getItem(ASKIVIO_LS_CONSENT_KEY); return (v === 'yes' || v === 'no') ? v : null; } function askivioSetConsent(ok){ localStorage.setItem(ASKIVIO_LS_CONSENT_KEY, ok ? 'yes' : 'no'); } function askivioConsentText(lang){ const t = { en:{title:'Consent to improve the service', body:'Do you allow us to record this conversation for a limited time, only to improve answer quality?', agree:'Yes, I agree', deny:'No, thanks'}, ro:{title:'Consimțământ pentru îmbunătățirea serviciului', body:'Ne permiți să înregistrăm conversația pentru o perioadă limitată, exclusiv pentru a îmbunătăți calitatea răspunsurilor?', agree:'Da, sunt de acord', deny:'Nu, mulțumesc'}, it:{title:'Consenso', body:'Ci permetti di registrare la conversazione per un periodo limitato, solo per migliorare la qualità?', agree:'Sì, accetto', deny:'No, grazie'}, es:{title:'Consentimiento', body:'¿Nos permites registrar la conversación por un tiempo limitado, solo para mejorar la calidad?', agree:'Sí, acepto', deny:'No, gracias'}, fr:{title:'Consentement', body:'Autorisez-vous l’enregistrement temporaire de cette conversation pour améliorer la qualité ?', agree:'Oui, j’accepte', deny:'Non, merci'}, de:{title:'Einwilligung', body:'Erlauben Sie die zeitlich begrenzte Aufzeichnung des Gesprächs, nur zur Qualitätsverbesserung?', agree:'Ja, einverstanden', deny:'Nein, danke'}, }; return t[lang] || t.en; } function askivioRenderConsentBar(lang){ const t = askivioConsentText(lang); return ` `; } function askivioMountConsent(lang){ if (askivioGetConsent() !== null) return; const msgs = document.querySelector('.askivio-messages'); if (!msgs) return; const wrap = document.createElement('div'); wrap.innerHTML = askivioRenderConsentBar(lang); msgs.appendChild(wrap); const agree = wrap.querySelector('.agree'); const deny = wrap.querySelector('.deny'); agree.addEventListener('click', ()=>{ askivioSetConsent(true); wrap.remove(); }); deny .addEventListener('click', ()=>{ askivioSetConsent(false); wrap.remove(); }); } onReady(function(){ const cfg = window.AskivioCFG || {}; const rawBase = (cfg.restUrl || '/wp-json/askivio2/v1'); let API = rawBase.replace(/\/+$/,''); if (/\/askivio\/v1$/.test(API)) { API = API.replace('/askivio/v1','/askivio2/v1'); } // ROOT + LAUNCHER let root=document.getElementById('askivio-root'); if(!root){ root=el('div'); root.id='askivio-root'; document.body.appendChild(root); } let launcher=document.getElementById('askivio-launcher'); if(!launcher){ launcher=el('button'); launcher.id='askivio-launcher'; launcher.setAttribute('aria-label','Open Askivio chat'); document.body.appendChild(launcher); } const pos=mapPos(cfg.launcherPos,cfg.launcherOffX,cfg.launcherOffY); const position=(cfg.launcherMode==='floated')?'absolute':'fixed'; const radius=(cfg.launcherShape==='square')?'8px':'9999px'; css(launcher,{position,width:'56px',height:'56px',border:'none',borderRadius:radius,display:'inline-flex',alignItems:'center',justifyContent:'center',backgroundColor:cfg.bubbleBg||'#0ea5e9',boxShadow:'0 10px 20px rgba(0,0,0,.15)',cursor:'pointer',zIndex:'2147483643',backgroundImage:cfg.bubbleIconUrl?`url("${cfg.bubbleIconUrl}")`:'',backgroundRepeat:'no-repeat',backgroundPosition:'center',backgroundSize:cfg.launcherIconSize||'80%'}); ['top','right','bottom','left','transform'].forEach(k=>{ if(pos[k]) launcher.style[k]=pos[k]; }); // CHAT WINDOW let win=document.querySelector('.askivio-chat-window'); if(!win){ win=el('div','askivio-chat-window'); document.body.appendChild(win); } const sizeW=isMobile()?(cfg.winWMobile||'95%'):(cfg.winWDesktop||'380px'); const sizeH=isMobile()?(cfg.winHMobile||'70%'):(cfg.winHDesktop||'520px'); css(win,{position:position,display:'none',right:cfg.launcherOffX||'16px',bottom:cfg.launcherOffY||'16px',width:sizeW,height:sizeH,background:'#fff',borderRadius:'12px',overflow:'hidden',boxShadow:'0 10px 30px rgba(0,0,0,.2)',zIndex:'2147483000',fontFamily:cfg.fontFamily||'Inter, Arial, sans-serif',fontSize:cfg.fontSize||'14px',flexDirection:'column'}); // HEADER (with avatar) const header=el('div','askivio-header',{ display:'flex',alignItems:'center',gap:'10px', padding:'10px 12px',background:cfg.headerBg||'#0ea5e9',color:'#fff' }); if(cfg.titleLogoUrl){ const logo=el('img','askivio-header-logo',{maxHeight:cfg.logoMaxH||'22px',maxWidth:cfg.logoMaxW||'120px',objectFit:'contain'}); logo.src=cfg.titleLogoUrl; header.appendChild(logo); } const ht=el('div','',{display:'flex',flexDirection:'column',minWidth:'0',flex:'1 1 auto'}); const title=el('div','',{fontWeight:'600',lineHeight:'1.2',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}); title.textContent=cfg.headerTitle||'Askivio Chat'; ht.appendChild(title); if(cfg.headerSubtitle){ const sub=el('div','',{opacity:'.9',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}); sub.textContent=cfg.headerSubtitle; ht.appendChild(sub); } header.appendChild(ht); if ((window.AskivioCFG||{}).avatarUrl){ const hdrAv = el('img','askivio-header-avatar',{ width:'28px',height:'28px',borderRadius:'50%', objectFit:'cover',background:'#eee', border:'1px solid rgba(255,255,255,.35)', boxShadow:'0 1px 2px rgba(0,0,0,.1)' }); hdrAv.src = window.AskivioCFG.avatarUrl; hdrAv.alt = 'Bot avatar'; header.appendChild(hdrAv); } // BODY + MESSAGES const body=el('div','askivio-body',{flex:'1',overflow:'auto',background:'#fafafa',padding:'12px'}); const msgs=el('div','askivio-messages',{display:'flex',flexDirection:'column',gap:'8px'}); body.appendChild(msgs); // FORM const form=el('form','askivio-input',{display:'flex',gap:'8px',padding:'10px',borderTop:'1px solid #eee',background:'#fff'}); const input=el('input','askivio-text',{flex:'1',border:'1px solid #ddd',borderRadius:'8px',padding:'8px 10px'}); input.type='text'; input.placeholder='Type your message...'; const sendBtn=el('button','askivio-send',{background:cfg.btnSendColor||'#0ea5e9',color:'#fff',border:'none',borderRadius:'8px',padding:'8px 12px',cursor:'pointer'}); sendBtn.type='submit'; sendBtn.textContent='Send'; form.appendChild(input); form.appendChild(sendBtn); win.replaceChildren(header,body,form); function renderSystem(text, extraCls = '') { const esc = (s) => String(s || '').replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); return `
${esc(text)}
`; } // Place window above launcher try { const rect = launcher.getBoundingClientRect(); const gap = 12; const isBottom = (cfg.launcherPos||'br').startsWith('b'); if (isBottom) { win.style.bottom = `calc(${cfg.launcherOffY||'16px'} + ${Math.round(rect.height)}px + ${gap}px)`; if ((cfg.launcherPos||'br')==='bc') { win.style.left = '50%'; win.style.transform = 'translateX(-50%)'; } else { win.style.right = cfg.launcherOffX||'16px'; } } win.style.zIndex = '2147483000'; launcher.style.zIndex = '2147483001'; } catch(e){} // Open/Close launcher.addEventListener('click',e=>{ e.preventDefault(); const opened=win.style.display==='flex'; win.style.display=opened?'none':'flex'; if(!opened) input.focus(); }); // (Close x a fost eliminat la cerere; dacă vrei, îl poți adăuga înapoi) window.addEventListener('keydown',e=>{ if(e.key==='Escape' && win.style.display==='flex') win.style.display='none'; }); window.addEventListener('resize',()=>{ const W=isMobile()?(cfg.winWMobile||'95%'):(cfg.winWDesktop||'380px'); const H=isMobile()?(cfg.winHMobile||'70%'):(cfg.winHDesktop||'520px'); win.style.width=W; win.style.height=H; }); // Append bubbles (optional avatar in bubbles) function append(role, html){ const row = el('div', 'askivio-row', {display:'flex', alignItems:'flex-start', gap:'8px'}); const cfg = window.AskivioCFG || {}; const showBubbleAvatar = (cfg.avatarUrl && cfg.avatarInBubbles !== false); if (role === 'bot' && showBubbleAvatar) { const av = el('img','askivio-avatar',{width:'32px', height:'32px', borderRadius:'50%', flex:'0 0 32px', objectFit:'cover', background:'#eee'}); av.src = cfg.avatarUrl; row.appendChild(av); } else { const spacer = el('div','',{width:'0', height:'0', flex:'0 0 0'}); row.appendChild(spacer); } const b=el('div','askivio-msg '+role,{maxWidth:'90%',padding:'8px 10px',borderRadius:'10px',lineHeight:'1.35',wordBreak:'break-word',background:role==='user'?'#e5f2ff':'#fff',color:role==='user'?'#0b4a80':'#222',border:role==='bot'?'1px solid #eee':'none'}); b.innerHTML=html; b.querySelectorAll('a').forEach(a=>{a.style.color='#0a7bdc';a.style.textDecoration='underline';}); row.appendChild(b); msgs.appendChild(row); body.scrollTop=body.scrollHeight; } // Network let inflight=null; function askServer(q){ if(inflight && typeof inflight.abort==='function'){ inflight.abort(); } const ctl=('AbortController' in window)?new AbortController():null; inflight=ctl; const payload = { q: q, consent: (askivioGetConsent()==='yes') ? 1 : 0, sid: askivioGetSID() }; const opt={method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}; if(ctl){opt.signal=ctl.signal;} return fetch(API+'/chat',opt).then(r=>r.json()).finally(()=>{inflight=null;}); } // Submit form.addEventListener('submit',ev=>{ ev.preventDefault(); const q=(input.value||'').trim(); if(!q) return; append('user', renderRich(q)); input.value=''; const typing=el('div','askivio-msg bot',{maxWidth:'90%',padding:'8px 10px',borderRadius:'10px',lineHeight:'1.35',wordBreak:'break-word',background:'#fff',border:'1px solid #eee',color:'#888',fontStyle:'italic'}); typing.textContent='…'; msgs.appendChild(typing); body.scrollTop=body.scrollHeight; askServer(q).then(res=>{ typing.remove(); const reply=(res && typeof res.reply==='string' && res.reply.trim()!=='') ? res.reply : (cfg.fallback||"Sorry, I can't answer that right now."); append('bot', renderRich(reply)); // Usage banners try { if (res && res.usage && typeof res.usage.count === 'number' && typeof res.usage.limit === 'number') { const limit = res.usage.limit; const count = res.usage.count; const near = !!res.usage.near_limit; if (limit > 0) { if (count >= limit) { append('system', renderSystem('Service limit reached for this month. Please use the official site in the meantime.', 'limit')); } else if (near) { append('system', renderSystem('Heads up: we are close to the monthly limit. If replies slow down, please use the official site.')); } } } } catch (e) {} // CTA button — only if server decided to show it if (res && res.cta && res.cta.url) { const wrap = el('div','askivio-cta-wrap',{marginTop:'8px'}); const btn = el('a','askivio-cta-btn',{display:'inline-block', padding:'8px 14px', borderRadius:'999px', background:(cfg.btnColor||'#0b79d0'), color:'#fff', textDecoration:'none', fontWeight:'600'}); btn.href = res.cta.url; btn.target = '_blank'; btn.rel = 'noopener noreferrer'; btn.textContent = res.cta.label || 'Open site'; wrap.appendChild(btn); document.querySelector('.askivio-messages').appendChild(wrap); body.scrollTop = body.scrollHeight; } // Consent după primul mesaj try { const lang = (q.length <= 4) ? askivioPreferredLang() : askivioGuessLang(q); if (askivioGetConsent() === null) { askivioMountConsent(lang); } } catch(_) {} }) .catch(()=>{ typing.remove(); append('bot', renderRich(cfg.fallback||"Sorry, I can't answer that right now.")); }); }); }); })();