PageSourceSearch

https://www.avesco-cat.fi/_assets/accb2b4b68cfe1dd53fd06e27cc1734b/JavaScript/chat.js?1790591613

js avesco-cat.fi collected 2026-10-08 18:54:27 UTC 9,075 bytes, 300 lines download raw bytes

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.