PageSourceSearch

https://advon-services.vercel.app/widget.js

js advon-services.vercel.app collected 2026-10-03 06:11:13 UTC 8,259 bytes, 176 lines download raw bytes

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.