1; (function () { 2 const btn = document.createElement("button"); 3 btn.innerText = "Chat with TaxTron Bot!"; 4 btn.id = "taxtron-chat-btn"; 5 const BOT_IMG_URL = "https://img.freepik.com/free-vector/ai-technology-robot-cyborg-illustrations_24640-134419.jpg?semt=ais_hybrid&w=740"; 6 7 // Style the button with bigger size, gradient, shadow, hover effect 8 Object.assign(btn.style, { 9 position: "fixed", 10 bottom: "20px", 11 right: "20px", 12 width: "60px", 13 height: "60px", 14 borderRadius: "50%", 15 background: "linear-gradient(135deg, #0078D4 0%, #005a9e 100%)", 16 border: "none", 17 cursor: "pointer", 18 zIndex: 9999, 19 boxShadow: "0 8px 20px rgba(0,0,0,0.25)", 20 padding: 0, 21 display: "flex", 22 alignItems: "center", 23 justifyContent: "center", 24 overflow: "visible", 25 transition: "transform 0.3s ease", 26 }); 27 28 btn.addEventListener("mouseenter", () => { 29 btn.style.transform = "scale(1.15)"; 30 }); 31 btn.addEventListener("mouseleave", () => { 32 btn.style.transform = "scale(1)"; 33 }); 34btn.innerHTML = ` 35 <img src="${BOT_IMG_URL}" alt="Open chat" style=" 36 width:32px;height:32px;border-radius:50%;object-fit:cover; 37 box-shadow:0 0 0 2px rgba(255,255,255,0.85); 38 "> 39 <span id="taxtron-launcher-label" style=" 40 position:absolute; right:68px; bottom:50%; 41 transform: translateY(50%) translateX(6px); 42 background:#111; color:#fff; padding:6px 8px; border-radius:6px; 43 font:12px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif; 44 white-space:nowrap; box-shadow:0 6px 16px rgba(0,0,0,.2); 45 opacity:0; pointer-events:none; transition:.2s; 46 "> 47 Got questions? I might be able to help! 48 </span>`; 49 50 btn.addEventListener("mouseenter", () => { 51 const lab = document.getElementById("taxtron-launcher-label"); 52 if (!lab) return; 53 lab.style.opacity = "1"; 54 lab.style.transform = "translateY(50%) translateX(0)"; 55}); 56btn.addEventListener("mouseleave", () => { 57 const lab = document.getElementById("taxtron-launcher-label"); 58 if (!lab) return; 59 lab.style.opacity = "0"; 60 lab.style.transform = "translateY(50%) translateX(6px)"; 61}); 62 document.body.appendChild(btn); 63 64 const chat = document.createElement("div"); 65 chat.id = "taxtron-chat-box"; 66 67 chat.style.cssText = ` 68 display: none; 69 position: fixed; bottom: 90px; right: 20px; 70 width: 320px; height: 420px; 71 background: #fff; 72 box-shadow: 0 12px 28px rgba(0,0,0,0.2); 73 border-radius: 16px; 74 overflow: hidden; 75 z-index: 9999; 76 font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 77 display: flex; 78 flex-direction: column; 79 `; 80 81 chat.innerHTML = ` 82 <div id="taxtron-chat-header" style=" 83 padding: 12px 16px; 84 background: linear-gradient(90deg, #005a9e, #0078D4); 85 color: #fff; 86 font-weight: 700; 87 font-size: 16px; 88 user-select: none; 89 display: flex; 90 justify-content: space-between; 91 align-items: center; 92 box-shadow: 0 2px 8px rgba(0,0,0,0.12); 93 "> 94 SofTron Bot 95 <span id="taxtron-close" style=" 96 cursor: pointer; 97 font-size: 22px; 98 line-height: 18px; 99 font-weight: 900; 100 user-select: none; 101 transition: color 0.3s; 102 " title="Close chat">Ã</span> 103 </div> 104 <div id="taxtron-messages" style=" 105 padding: 12px 16px; 106 flex-grow: 1; 107 overflow-y: auto; 108 background: #f9fafb; 109 scroll-behavior: smooth; 110 "></div> 111 <div style=" 112 border-top: 1px solid #e1e4e8; 113 padding: 8px 16px; 114 background: #fff; 115 display: flex; 116 align-items: center; 117 "> 118 <input id="taxtron-input" placeholder="Type your questionâ¦" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" 119 style=" 120 flex-grow: 1; 121 padding: 10px 14px; 122 font-size: 14px; 123 border-radius: 24px; 124 border: 1.5px solid #ddd; 125 outline: none; 126 transition: border-color 0.3s; 127 " 128 /> 129 </div> 130 `; 131 132 document.body.appendChild(chat); 133chat.style.display = "none"; 134 135 // Hover effects for input focus
136 const input = chat.querySelector("#taxtron-input"); 137 input.addEventListener("focus", () => { 138 input.style.borderColor = "#0078D4"; 139 input.style.boxShadow = "0 0 6px rgba(0,120,212,0.4)"; 140 }); 141 input.addEventListener("blur", () => { 142 input.style.borderColor = "#ddd"; 143 input.style.boxShadow = "none"; 144 }); 145 146 document.getElementById("taxtron-close").onclick = () => { 147 chat.style.display = "none"; 148 }; 149 150 btn.onclick = () => { 151 chat.style.display = "flex"; 152 input.focus(); 153 if (!chat.dataset.initialized) { 154 showInitialGreeting(); 155 chat.dataset.initialized = true; 156 } 157 }; 158 159 160 const USER_IMG_URL = "https://png.pngtree.com/png-clipart/20191121/original/pngtree-user-icon-png-image_5097430.jpg"; 161 162 const msgs = document.getElementById("taxtron-messages"); 163 164 // Chat bubble styles, appended via CSS style tag to avoid inline clutter 165 const style = document.createElement("style"); 166 style.textContent = ` 167 #taxtron-chat-box .bot-message, #taxtron-chat-box .user-message { 168 margin-bottom: 12px; 169 display: flex; 170 flex-direction: column; 171 animation: fadeIn 0.3s ease forwards; 172 } 173 #taxtron-chat-box .message-header { 174 display: flex; 175 align-items: center; 176 gap: 8px; 177 margin-bottom: 4px; 178 font-size: 13px; 179 font-weight: 600; 180 color: #333; 181 } 182 #taxtron-chat-box .header-avatar { 183 width: 30px; 184 height: 30px; 185 border-radius: 50%; 186 box-shadow: 0 0 6px rgba(0,0,0,0.15); 187 object-fit: cover; 188 } 189 #taxtron-chat-box .message-label { 190 user-select: none; 191 } 192 #taxtron-chat-box .bubble { 193 max-width: 80%; 194 padding: 10px 14px; 195 border-radius: 20px; 196 font-size: 14px; 197 line-height: 1.4; 198 white-space: pre-wrap; 199 word-wrap: break-word; 200 box-shadow: 0 2px 8px rgba(0,0,0,0.1); 201 user-select: text; 202 background-color: #e1f0ff; /* Bot bubble background */ 203 color: #003b73; 204 border-bottom-left-radius: 4px; 205 } 206 #taxtron-chat-box .user-message .bubble { 207 background-color: #f0f0f0; 208 color: #222; 209 align-self: flex-end; 210 border-bottom-right-radius: 4px; 211 box-shadow: 0 2px 6px rgba(0,0,0,0.07); 212 } 213 #taxtron-chat-box ul { 214 padding-left: 20px; 215 margin: 8px 0 0 0; 216 color: #555; 217 } 218 #taxtron-chat-box a { 219 color: #0078D4; 220 text-decoration: none; 221 } 222 #taxtron-chat-box a:hover { 223 text-decoration: underline; 224 } 225 @keyframes fadeIn { 226 from {opacity: 0; transform: translateY(6px);} 227 to {opacity: 1; transform: translateY(0);} 228 } 229 /* Scrollbar styling */ 230 #taxtron-messages::-webkit-scrollbar {
231 width: 6px; 232 } 233 #taxtron-messages::-webkit-scrollbar-thumb { 234 background-color: rgba(0,120,212,0.4); 235 border-radius: 3px; 236 } 237 // chatbot button 238 239 240 `; 241 document.head.appendChild(style); 242 243 function formatBotResponse(text) { 244 text = text.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>"); 245 text = text.replace(/((?:^|\n)(?:\s*[\*\-]\s.*(?:\n|$))+)/gm, match => { 246 const items = match 247 .trim() 248 .split('\n') 249 .map(line => line.replace(/^\s*[\*\-]\s?/, '').trim()) 250 .filter(Boolean); 251 if (items.length < 1) return match; 252 return "<ul><li>" + items.join("</li><li>") + "</li></ul>"; 253 }); 254 text = text.replace(/((?:^|\n)(?:\s*\d+\.\s.*(?:\n|$))+)/gm, match => { 255 const items = match 256 .trim() 257 .split('\n') 258 .map(line => line.replace(/^\s*\d+\.\s?/, '').trim()) 259 .filter(Boolean); 260 if (items.length < 1) return match; 261 return "<ol><li>" + items.join("</li><li>") + "</li></ol>"; 262 }); 263 return text; 264 } 265 266 function appendMessage({ text, isBot, isHtml }) { 267 const wrapper = document.createElement("div"); 268 wrapper.className = isBot ? "bot-message" : "user-message"; 269 270 const header = document.createElement("div"); 271 header.className = "message-header"; 272 273 const img = document.createElement("img"); 274 img.src = isBot ? BOT_IMG_URL : USER_IMG_URL; 275 img.className = "header-avatar"; 276 header.appendChild(img); 277 278 const label = document.createElement("span"); 279 label.className = "message-label"; 280 label.innerText = isBot ? "SofTron Bot:" : "You:"; 281 header.appendChild(label); 282 283 wrapper.appendChild(header); 284 285 const bubble = document.createElement("div"); 286 bubble.className = "bubble"; 287 288 if (isBot) { 289 bubble.innerHTML = formatBotResponse(text); 290 } else { 291 bubble.innerText = text; 292 } 293 wrapper.appendChild(bubble); 294 295 msgs.appendChild(wrapper); 296 wrapper.scrollIntoView({ block: "start", behavior: "smooth" }); 297 } 298 299 let lastLang = "en"; 300 let isBusy = false; 301 302 async function send(q) { 303 if (isBusy) return; 304 isBusy = true; 305 appendMessage({ text: q, isBot: false, isHtml: false }); 306 307 const thinkingText = "SofTron Bot is thinking..."; 308 const thinkingId = "taxtron-thinking-msg"; 309 let thinkingNode = document.createElement("div"); 310 thinkingNode.className = "bot-message"; 311 thinkingNode.id = thinkingId; 312 313 let header = document.createElement("div"); 314 header.className = "message-header"; 315 let img = document.createElement("img"); 316 img.src = BOT_IMG_URL; 317 img.className = "header-avatar"; 318 header.appendChild(img); 319 let label = document.createElement("span"); 320 label.className = "message-label"; 321 label.innerText = "SofTron Bot:"; 322 header.appendChild(label); 323 thinkingNode.appendChild(header); 324 325 let bubble = document.createElement("div"); 326 bubble.className = "bubble"; 327 thinkingNode.appendChild(bubble); 328 329 msgs.appendChild(thinkingNode); 330 thinkingNode.scrollIntoView({ block: "start", behavior: "smooth" }); 331 332 let idx = 1; 333 let typingInterval = null; 334 335 function startTyping() { 336 typingInterval = setInterval(() => { 337 bubble.innerHTML = `<b>${thinkingText.slice(0, idx)}</b>`; 338 idx++; 339 if (idx > thinkingText.length) idx = 1; 340 }, 75); 341 } 342 startTyping(); 343 344 try { 345 const res = await fetch("/api/chatbot", { 346 method: "POST", 347 headers: { "Content-Type": "application/json" }, 348 body: JSON.stringify({ query: q }) 349 }); 350 351 const data = await res.json(); 352 const { answer, source_urls, lang } = data; 353 lastLang = lang || "en"; 354 let html = formatBotResponse(answer); 355 if (source_urls && source_urls.length) { 356 const label = "This information was provided from the following sources:"; 357 html += `<br><br>${label}<ul style="padding-left: 20px;">`; 358 for (const link of source_urls) { 359 html += `<li><a href="${link}" target="_blank" rel="noopener noreferrer">${link}</a></li>`; 360 } 361 html += `</ul>`; 362 } 363 clearInterval(typingInterval); 364 bubble.innerHTML = html; 365 thinkingNode.scrollIntoView({ block: "start", behavior: "smooth" }); 366 } catch (err) { 367 clearInterval(typingInterval); 368 bubble.innerHTML = `<b>Error: ${err.message}</b>`; 369 thinkingNode.scrollIntoView({ block: "start", behavior: "smooth" }); 370 } finally { 371 isBusy = false;
372 input.focus(); 373 } 374 } 375 376 input.addEventListener("keydown", e => { 377 if (e.key === "Enter" && input.value.trim() && !isBusy) { 378 send(input.value.trim()); 379 input.value = ""; 380 } 381 }); 382 383 function showInitialGreeting() { 384 const greeting = 385 `Hi! Iâm SofTron Bot, your tax assistant. How can I help? 386If I am unable to answer your question, you are always welcome to email SofTron at [email protected] or call us at (905) 273-4444. 387 388Bonjour! Je suis SofTron Bot, votre assistant fiscal. Comment puis-je vous aider? 389Si je ne peux pas répondre à votre question, vous pouvez toujours envoyer un courriel à SofTron à lâadresse [email protected] ou nous appeler au (905) 273-4444.`; 390 appendMessage({ text: greeting.replace(/\n/g, "<br>"), isBot: true, isHtml: true }); 391 } 392})();
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.