1window.chat_session_id = null; 2window.chat_session_token = null; 3window.last_message_id = null; 4window.alertTimer = null; 5window.backgroundTimeout = null; 6const $backgroundImage2 = document.querySelector(".default-background-img-2"); 7const $chatList = document.getElementById("chatList"); 8const $chatHeader = document.getElementById("chatHeader"); 9const $chatWrapper = document.querySelector(".chat"); 10const $chatInput = document.querySelector(".chat-input"); 11const $form = document.querySelector(".chat-form"); 12const $alertDanger = document.querySelector("#alert-danger"); 13const $chatBtnSubmit = document.querySelector("#chat-btn-submit"); 14const promptElement = document.getElementById("prompt-id"); 15const action = $form.action || 'https://chat-widget-api.avesco.ch/'; 16// const prompt_id = parseInt(promptElement?.dataset.promptId); 17// const persona_id = parseInt(promptElement?.dataset.customerId); 18const securityToken = promptElement?.dataset.token; 19 20const decodeConfig = (token) => { 21 const decoded = atob(token); 22 return JSON.parse(decoded); 23}; 24 25const config = decodeConfig(securityToken); 26const prompt_id = parseInt(config.p); 27const persona_id = parseInt(config.c); 28 29$chatInput.addEventListener("focus", () => { 30 clearTimeout(window.backgroundTimeout); 31 //$chatHeader.style.display = 'none'; 32 //$chatBtnSubmit.innerHTML = "Senden"; 33 $chatWrapper.classList.add("__focused"); 34 //$backgroundImage2.classList.add("__focused"); 35}); 36 37document.querySelector(".chat-close-btn").addEventListener("click", (e) => { 38 // window.backgroundTimeout = setTimeout(() => { 39 // $chatHeader.style.display = 'flex'; 40 // }, 800); 41 //$chatBtnSubmit.innerHTML = "Beratung starten"; 42 $chatWrapper.classList.remove("__focused"); 43 //$backgroundImage2.classList.remove("__focused"); 44}); 45 46const selectFromPredefinedOptions = (event) => { 47 $chatInput.value = event.target.innerHTML.trim(); 48 $chatInput.focus(); 49}; 50 51const showAlertDangerMessage = (message) => { 52 if (window.alertTimer) { 53 clearTimeout(window.alertTimer); 54 } 55 $alertDanger.innerHTML = message || "Problem with server. Try again later."; 56 $alertDanger.style.display = "block"; 57 58 window.alertTimer = setTimeout(() => { 59 $alertDanger.style.display = "none"; 60 }, 5000); 61}; 62 63const errorInSendingMessage = () => { 64 $chatList.querySelector(".chat-message.__typing.__bot").remove(); 65 const userMessages = $chatList.querySelectorAll(".chat-message.__user"); 66 if (userMessages.length > 0) { 67 const lastUserMessage = userMessages[userMessages.length - 1]; 68 lastUserMessage.remove(); 69 } 70}; 71 72const createEl = (textContent = "", elClass = "chat-message", tag = "div") => { 73 const el = document.createElement(tag); 74 el.textContent = textContent; 75 el.className = elClass; 76 return el; 77}; 78 79const toggleForm = ($form, disabled = true) => { 80 const $fields = $form.querySelectorAll("button"); 81 82 if (disabled) { 83 $form.setAttribute("disabled", ""); 84 [...$fields].forEach(($field) => { 85 $field.setAttribute("disabled", ""); 86 }); 87 return; 88 } 89 $form.removeAttribute("disabled"); 90 [...$fields].forEach(($field) => { 91 $field.removeAttribute("disabled"); 92 }); 93}; 94 95 96const openBanner = (event) => { 97 open("https://de.linkedin.com/in/oliver-storm-1828b544", "_blank"); 98}; 99 100const transformLinks = (message) => { 101 const regex = /\[(.*?)\]\((.*?)\)/g; 102 const matches = [...message.matchAll(regex)].reverse(); 103 let innerHTML = message; 104 105 matches.forEach((match) => { 106 innerHTML = 107 innerHTML.slice(0, match.index) + 108 `<a target="_blank" class="chat_link" href="${match[2]}">${match[1]}</a>` + 109 innerHTML.slice(match.index + match[0].length); 110 }); 111 112 return innerHTML; 113}; 114 115const transformBold = (message) => { 116 const regex = /\*\*(.*?)\*\*/g; // Matches **text** 117 const matches = [...message.matchAll(regex)].reverse(); 118 let innerHTML = message; 119 120 matches.forEach((match) => { 121 innerHTML = 122 innerHTML.slice(0, match.index) + 123 `<span style="font-weight: bold;">${match[1]}</span>` + 124 innerHTML.slice(match.index + match[0].length); 125 }); 126 127 return innerHTML; 128}; 129 130 131const initChat = async () => { 132 toggleForm($form); 133 const response = await fetch(`${action}chat/create-chat-session`, { 134 method: "POST", 135 headers: { 136 "Content-Type": "application/json", 137 }, 138 body: JSON.stringify({ 139 persona_id, 140 }), 141 }); 142 143 if (!response.ok) { 144 const errorJson = await response.json(); 145 const errorMsg = errorJson.message || errorJson.detail || ""; 146 showAlertDangerMessage(errorMsg); 147 throw new Error(response); 148 } 149 150 const result = await response.json(); 151 152 window.chat_session_id = result.chat_session_id; 153 window.chat_session_token = result.chat_session_token; 154 toggleForm($form, false); 155 return; 156}; 157 158async function sendChat(event) { 159 event.preventDefault(); // Prevent the form from submitting the traditional way 160 161 const {target} = event; 162 if (target.hasAttribute("disabled")) return; 163 const $chatInput = target.querySelector("#question"); 164 const question = $chatInput.value;
165 $chatList.appendChild(createEl(question, "chat-message __user")); 166 const typerBlock = createEl("", "chat-message __typing __bot"); 167 $chatList.appendChild(typerBlock); 168 typerBlock.appendChild(createEl("", "typing-loader")); 169 $chatList.scrollTo(0, $chatList.scrollHeight); 170 if (!window.chat_session_token) { 171 await initChat(); 172 } 173 toggleForm(target); 174 const data = { 175 chat_session_id: window.chat_session_id, 176 chat_session_token: window.chat_session_token, 177 message: question, 178 prompt_id: prompt_id, 179 parent_message_id: window.last_message_id 180 ? `${window.last_message_id}` 181 : null, 182 }; 183 184 sendMessage(data, target).next(); 185} 186 187async function* sendMessage(data, target) { 188 const sendMessageResponse = await fetch(`${action}chat/send-message`, { 189 method: "POST", 190 headers: { 191 "Content-Type": "application/json", 192 }, 193 body: JSON.stringify(data), 194 }); 195 if (!sendMessageResponse.ok) { 196 const errorJson = await sendMessageResponse.json(); 197 const errorMsg = errorJson.message || errorJson.detail || ""; 198 errorInSendingMessage(); 199 showAlertDangerMessage(errorMsg); 200 toggleForm(target, false); 201 $chatInput.focus(); 202 throw Error(`Failed to send message - ${errorMsg}`); 203 } 204 205 yield* handleStream(sendMessageResponse, target); 206} 207 208const processSingleChunk = (chunk, currPartialChunk) => { 209 const completeChunk = (currPartialChunk || "") + chunk; 210 try { 211 const chunkJson = JSON.parse(completeChunk); 212 return [chunkJson, null]; 213 } catch (err) { 214 return [null, completeChunk]; 215 } 216}; 217 218const processRawChunkString = (rawChunkString, previousPartialChunk) => { 219 if (!rawChunkString) { 220 return [[], null]; 221 } 222 const chunkSections = rawChunkString 223 .split("\n") 224 .filter((chunk) => chunk.length > 0); 225 let parsedChunkSections = []; 226 let currPartialChunk = previousPartialChunk; 227 chunkSections.forEach((chunk) => { 228 const [processedChunk, partialChunk] = processSingleChunk( 229 chunk, 230 currPartialChunk 231 ); 232 if (processedChunk) { 233 parsedChunkSections.push(processedChunk); 234 currPartialChunk = null; 235 } else { 236 currPartialChunk = partialChunk; 237 } 238 }); 239 240 return [parsedChunkSections, currPartialChunk]; 241}; 242 243async function* handleStream(streamingResponse, target) { 244 const reader = streamingResponse.body?.getReader(); 245 const decoder = new TextDecoder("utf-8"); 246 247 let message = ""; 248 let messageRender = null; 249 let isClearedDot = false; 250 let previousPartialChunk = null; 251 while (true) { 252 const rawChunk = await reader?.read(); 253 if (!rawChunk) { 254 showAlertDangerMessage(); 255 throw new Error("Unable to process chunk"); 256 } 257 const {done, value} = rawChunk; 258 if (done) { 259 break; 260 } 261 262 const [completedChunks, partialChunk] = processRawChunkString( 263 decoder.decode(value, {stream: true}), 264 previousPartialChunk 265 ); 266 267 if (!completedChunks.length && !partialChunk) { 268 break; 269 } 270 previousPartialChunk = partialChunk; 271 272 if (completedChunks.length) { 273 if ( 274 completedChunks[completedChunks.length - 1].hasOwnProperty("message_id") 275 ) { 276 message = completedChunks[completedChunks.length - 1].message; 277 window.last_message_id = 278 completedChunks[completedChunks.length - 1].message_id; 279 } else if (completedChunks[0].hasOwnProperty("answer_piece")) { 280 message += completedChunks.map((chunk) => chunk.answer_piece).join(""); 281 } 282 } 283 284 const innerHTML = transformBold(transformLinks(message)); 285 286 if (!isClearedDot && message) { 287 isClearedDot = true; 288 $chatList.querySelector(".chat-message.__typing.__bot").remove(); 289 messageRender = createEl(innerHTML, "chat-message __bot"); 290 $chatList.appendChild(messageRender); 291 $chatList.scrollTo(0, $chatList.scrollHeight); 292 } else if (messageRender) { 293 messageRender.innerHTML = innerHTML; 294 $chatList.scrollTo(0, $chatList.scrollHeight); 295 } 296 } 297 toggleForm(target, false); 298 $chatInput.value = ""; 299 $chatInput.focus(); 300}
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.