1/* ADVON CHAT WIDGET 2 * Drop into any client site with: 3 * <script src="https://YOUR-DEPLOYMENT.vercel.app/widget.js" data-site-id="advon" defer></script> 4 * Optional: data-lang="el" or data-lang="en" (otherwise auto-detected). 5 */ 6(function () { 7 "use strict"; 8 9 var script = document.currentScript; 10 if (!script) return; 11 var SITE_ID = script.getAttribute("data-site-id"); 12 if (!SITE_ID) { console.error("[advon-chat] missing data-site-id"); return; } 13 var API_BASE = new URL(script.src).origin; 14 15 // ---- Language (EL default for Greek pages/browsers) ---- 16 var forced = script.getAttribute("data-lang"); 17 var pageLang = (document.documentElement.lang || navigator.language || "el").toLowerCase(); 18 var LANG = forced === "en" || forced === "el" ? forced : (pageLang.indexOf("el") === 0 || pageLang.indexOf("gr") === 0 ? "el" : "en"); 19 20 var T = { 21 el: { 22 title: "ΡÏÏήÏÏε μαÏ", 23 greeting: "Îεια ÏαÏ! ð Î ÏÏ Î¼ÏοÏÏ Î½Î± βοηθήÏÏ;", 24 placeholder: "ÎÏάÏÏε Ïο Î¼Î®Î½Ï Î¼Î¬ ÏαÏâ¦", 25 send: "ÎÏοÏÏολή", 26 error: "ÎάÏι Ïήγε ÏÏÏαβά â δοκιμάÏÏε ξανά.", 27 poweredBy: "Powered by Advon Media", 28 }, 29 en: { 30 title: "Ask us anything", 31 greeting: "Hi there! ð How can I help?", 32 placeholder: "Type your messageâ¦", 33 send: "Send", 34 error: "Something went wrong â please try again.", 35 poweredBy: "Powered by Advon Media", 36 }, 37 }[LANG]; 38 39 var messages = []; // {role, content} 40 var busy = false; 41 42 fetch(API_BASE + "/api/chat?siteId=" + encodeURIComponent(SITE_ID)) 43 .then(function (r) { return r.ok ? r.json() : Promise.reject(); }) 44 .then(function (cfg) { if (cfg.hasChat) build(cfg); }) 45 .catch(function () { console.error("[advon-chat] could not load config"); }); 46 47 function build(cfg) { 48 var color = cfg.brandColor || "#4F46E5"; 49 // Readable foreground on the brand color: dark ink for light brands 50 // (e.g. Advon cyan), white for dark ones. 51 var fg = "#fff"; 52 var m = /^#?([0-9a-f]{6})$/i.exec(color); 53 if (m) { 54 var n = parseInt(m[1], 16); 55 var lum = 0.2126 * (n >> 16 & 255) + 0.7152 * (n >> 8 & 255) + 0.0722 * (n & 255); 56 if (lum > 150) fg = "#050a0e"; 57 } 58 59 var style = document.createElement("style"); 60 style.textContent = 61 ".advon-chat-btn{position:fixed;bottom:20px;right:20px;width:56px;height:56px;border-radius:50%;border:none;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.25);z-index:99999;display:flex;align-items:center;justify-content:center;transition:transform .15s}" + 62 ".advon-chat-btn:hover{transform:scale(1.08)}" + 63 ".advon-chat-panel{position:fixed;bottom:88px;right:20px;width:min(360px,calc(100vw - 40px));height:min(480px,calc(100vh - 120px));background:#fff;border-radius:16px;box-shadow:0 8px 40px rgba(0,0,0,.3);z-index:99999;display:none;flex-direction:column;overflow:hidden;font-family:system-ui,-apple-system,Arial,sans-serif}" + 64 ".advon-chat-panel.open{display:flex}" + 65 ".advon-chat-head{padding:14px 16px;color:#fff;font-weight:600;font-size:15px;display:flex;align-items:center;gap:10px}" + 66 ".advon-chat-logo{width:26px;height:26px;border-radius:50%;object-fit:cover;background:#fff;flex:none}" + 67 ".advon-chat-log{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:8px;background:#f7f7f9}" + 68 ".advon-msg{max-width:82%;padding:9px 12px;border-radius:14px;font-size:14px;line-height:1.45;white-space:pre-wrap;word-wrap:break-word}" + 69 ".advon-msg.bot{background:#fff;color:#222;border:1px solid #e6e6ea;align-self:flex-start;border-bottom-left-radius:4px}" + 70 ".advon-msg.user{color:#fff;align-self:flex-end;border-bottom-right-radius:4px}" + 71 ".advon-chat-form{display:flex;gap:8px;padding:10px;border-top:1px solid #eee;background:#fff}" + 72 // color/background/caret pinned explicitly: host pages with CSS resets 73 // (e.g. Tailwind preflight) make inputs inherit the page color on a 74 // transparent background â white-on-white text on dark-themed sites. 75 ".advon-chat-input{flex:1;border:1px solid #ddd;border-radius:10px;padding:9px 12px;font-size:14px;outline:none;font-family:inherit;color:#111 !important;background:#fff !important;caret-color:#111}" + 76 ".advon-chat-send{border:none;border-radius:10px;color:#fff;padding:0 14px;font-size:14px;cursor:pointer;font-family:inherit}" + 77 ".advon-chat-send:disabled{opacity:.5;cursor:default}" + 78 ".advon-chat-foot{text-align:center;font-size:10px;color:#aaa;padding:4px;background:#fff}" + 79 ".advon-typing{opacity:.6;font-style:italic}"; 80 document.head.appendChild(style); 81 82 var btn = document.createElement("button"); 83 btn.className = "advon-chat-btn"; 84 btn.style.background = color;
85 btn.setAttribute("aria-label", T.title); 86 btn.innerHTML = 87 '<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="' + fg + '" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>'; 88 89 var panel = document.createElement("div"); 90 panel.className = "advon-chat-panel"; 91 panel.innerHTML = 92 '<div class="advon-chat-head" style="background:' + color + ';color:' + fg + '"></div>' + 93 '<div class="advon-chat-log"></div>' + 94 '<form class="advon-chat-form">' + 95 '<input class="advon-chat-input" type="text" maxlength="1000" autocomplete="off">' + 96 '<button class="advon-chat-send" type="submit" style="background:' + color + ';color:' + fg + '"></button>' + 97 "</form>" + 98 '<div class="advon-chat-foot"></div>'; 99 100 var head = panel.querySelector(".advon-chat-head"); 101 if (cfg.logo) { 102 var logoImg = document.createElement("img"); 103 logoImg.className = "advon-chat-logo"; 104 logoImg.src = cfg.logo; 105 logoImg.alt = ""; 106 head.appendChild(logoImg); 107 } 108 head.appendChild(document.createTextNode((cfg.name || "") + " â " + T.title)); 109 panel.querySelector(".advon-chat-input").placeholder = T.placeholder; 110 panel.querySelector(".advon-chat-send").textContent = T.send; 111 panel.querySelector(".advon-chat-foot").textContent = T.poweredBy; 112 113 document.body.appendChild(btn); 114 document.body.appendChild(panel); 115 116 var log = panel.querySelector(".advon-chat-log"); 117 var input = panel.querySelector(".advon-chat-input"); 118 var sendBtn = panel.querySelector(".advon-chat-send"); 119 var form = panel.querySelector(".advon-chat-form"); 120 121 addBubble("bot", T.greeting); 122 123 btn.addEventListener("click", function () { 124 panel.classList.toggle("open"); 125 if (panel.classList.contains("open")) input.focus(); 126 }); 127 128 form.addEventListener("submit", function (e) { 129 e.preventDefault(); 130 var text = input.value.trim(); 131 if (!text || busy) return; 132 input.value = ""; 133 addBubble("user", text); 134 messages.push({ role: "user", content: text }); 135 ask(); 136 }); 137 138 function addBubble(who, text, extraClass) { 139 var el = document.createElement("div"); 140 el.className = "advon-msg " + who + (extraClass ? " " + extraClass : ""); 141 if (who === "user") { el.style.background = color; el.style.color = fg; } 142 el.textContent = text; 143 log.appendChild(el); 144 log.scrollTop = log.scrollHeight; 145 return el; 146 } 147 148 function ask() { 149 busy = true; 150 sendBtn.disabled = true; 151 var typing = addBubble("bot", "â¦", "advon-typing"); 152 153 fetch(API_BASE + "/api/chat", { 154 method: "POST", 155 headers: { "Content-Type": "application/json" }, 156 body: JSON.stringify({ siteId: SITE_ID, messages: messages }), 157 }) 158 .then(function (r) { return r.json(); }) 159 .then(function (data) { 160 typing.remove(); 161 var reply = data.reply || T.error; 162 addBubble("bot", reply); 163 if (data.reply) messages.push({ role: "assistant", content: data.reply }); 164 }) 165 .catch(function () { 166 typing.remove(); 167 addBubble("bot", T.error); 168 }) 169 .finally(function () { 170 busy = false; 171 sendBtn.disabled = false; 172 input.focus(); 173 }); 174 } 175 } 176})();
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.