PageSourceSearch

https://softrontax.com/taxtron-bot.js

js softrontax.com collected 2026-09-30 22:29:44 UTC 12,914 bytes, 392 lines download raw bytes

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.