1const PERSIST_CONVERSATION=Boolean(DNChatBot.persist_conversation);const EMPTY_MESSAGE=(DNChatBot.empty_message||'').trim();const conversations=new WeakMap();const getStorageKeys=(root)=>{const keys=[];const baseKey=(root&&root.dataset&&root.dataset.conversationKey)||'default';keys.push(`dnchatbot-conversation-${baseKey}`);if(root&&root.id){const legacyKey=`dnchatbot-conversation-${root.id}`;if(!keys.includes(legacyKey)){keys.push(legacyKey)}} 2return keys};const parseConversation=(stored)=>{if(!stored)return null;try{const parsed=JSON.parse(stored);if(!Array.isArray(parsed))return null;return parsed.filter((msg)=>msg&&['user','assistant'].includes(msg.role)&&typeof msg.content==='string').map((msg)=>({role:msg.role,content:msg.content}))}catch(err){return null}};const loadConversation=(root)=>{if(!PERSIST_CONVERSATION||!window.sessionStorage){return[]} 3const keys=getStorageKeys(root);for(const key of keys){const parsed=parseConversation(sessionStorage.getItem(key));if(parsed){return parsed}} 4return[]};const saveConversation=(root,conversation)=>
4{if(!PERSIST_CONVERSATION||!window.sessionStorage){return} 5try{const keys=getStorageKeys(root);sessionStorage.setItem(keys[0],JSON.stringify(conversation));keys.slice(1).forEach((legacyKey)=>{sessionStorage.removeItem(legacyKey)})}catch(err){}};const getConversation=(root)=>{if(!conversations.has(root)){conversations.set(root,loadConversation(root))} 6return conversations.get(root)};const setChatVisibility=(root,isOpen)=>{const windowEl=root.querySelector('.dnchatbot-window');const toggle=root.querySelector('.dnchatbot-toggle');root.classList.toggle('is-open',isOpen);if(windowEl){windowEl.setAttribute('aria-hidden',isOpen?'false':'true')} 7if(toggle){toggle.setAttribute('aria-expanded',isOpen?'true':'false')} 8if(isOpen){const input=root.querySelector('.dnchatbot-form input[name="q"]');if(input)input.focus()}};const setLoading=(root,form,isLoading)=>{root.classList.toggle('is-loading',isLoading);const loader=root.querySelector('.dnchatbot-loader');if(loader)loader.setAttribute('aria-hidden',isLoading?'false':'true');const input=form.querySelector('input[name="q"]');const button=form.querySelector('button');[input,button].forEach((el)=>{if(el)el.disabled=isLoading})};const clearEmptyState=(root)=>{const placeholder=root.querySelector('.dnchatbot-messages .msg.placeholder');if(placeholder){placeholder.remove()}};const renderEmptyState=(root)=>{if(!EMPTY_MESSAGE)return;const box=root.querySelector('.dnchatbot-messages');if(!box||box.children.length>0||box.querySelector('.msg.placeholder'))return;const div=document.createElement('div');div.className='msg assistant placeholder';div.innerHTML=EMPTY_MESSAGE;box.appendChild(div)};const renderMessage=(box,role,content)=>{const div=document.createElement('div');div.className='msg '+role;div.innerHTML=content;box.appendChild(div);box.scrollTop=box.scrollHeight};const appendMessage=(root,role,content,{save=!0}={})=>{const box=root.querySelector('.dnchatbot-messages');if(!box)return;clearEmptyState(root);renderMessage(box,role,content);const conversation=getConversation(root);conversation.push({role,content});if(save){saveConversation(root,conversation)}};const restoreConversation=()=>{document.querySelectorAll('.dnchatbot').forEach((root)=>{const box=root.querySelector('.dnchatbot-messages');const conversation=getConversation(root);if(!box)return;if(conversation.length){conversation.forEach((msg)=>{renderMessage(box,msg.role,msg.content)});return} 9renderEmptyState(root)})};if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',restoreConversation)}else{restoreConversation()} 10document.addEventListener('click',(e)=>{const toggle=e.target.closest('.dnchatbot-toggle');
10if(toggle){const root=toggle.closest('.dnchatbot');if(root){const isOpen=root.classList.contains('is-open');setChatVisibility(root,!isOpen)} 11return} 12const closeBtn=e.target.closest('.dnchatbot-close');if(closeBtn){const root=closeBtn.closest('.dnchatbot');if(root){setChatVisibility(root,!1)}}});document.addEventListener('submit',async(e)=>{const form=e.target.closest('.dnchatbot form, .dnchatbot-form');if(!form)return;e.preventDefault();const root=form.closest('.dnchatbot');if(!root)return;setChatVisibility(root,!0);const box=root.querySelector('.dnchatbot-messages');const input=form.querySelector('input[name="q"]');if(!box||!input)return;const q=input.value.trim();if(!q)return;input.value='';const conversation=getConversation(root);const conversationForApi=conversation.slice();appendMessage(root,'user',q);setLoading(root,form,!0);try{const res=await fetch(DNChatBot.rest.ask,{method:'POST',headers:{'Content-Type':'application/json','X-WP-Nonce':DNChatBot.nonce||'',},body:JSON.stringify({question:q,page_url:window.location.href,conversation:conversationForApi,}),});const data=await res.json();let content=data.answer||'Aucune réponse.';if(!1&&data.sources&&data.sources.length){content+="\n\nSources:\n"+data.sources.map((u)=>`⢠${u}`).join('\n')} 13appendMessage(root,'assistant',content)}catch(err){appendMessage(root,'assistant','Erreur: '+err.message,{save:!1})}finally{setLoading(root,form,!1)}})
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.