PageSourceSearch

https://reputationsystems.ai/chat/multisystems-chat.js

js reputationsystems.ai collected 2026-10-05 12:31:21 UTC 4,667 bytes, 89 lines download raw bytes

1import { getChatProfile, isReviewSystems } from './chatProfiles.js';
2import { installChatHome } from './chatHome.js';
3
4export const WIDGET_URL = 'https://salesiq.zohopublic.com/widget?wc=siq2cb9730a126d35f0054ef754ae294816d344a10e62833785e43c0a7ae0a7241d';
5
6/** An explicit release gate: installing assets does not activate an unverified bot. */
7export function canStartChat(config, hostname) {
8  return typeof config?.product === 'string' && config.enabled === true && !isReviewSystems(hostname) &&
9    Boolean(getChatProfile(config.product, config.surface));
10}
11
12export async function startChat(doc, config) {
13  const win = doc.defaultView;
14  const excluded = () => (config.excludedPaths || []).some(path => win.location.pathname === path || win.location.pathname.startsWith(path + '/'));
15  if (excluded()) return;
16  if (config.consentKey) {
17    try { if (win.localStorage.getItem(config.consentKey) !== 'accepted') return; } catch { return; }
18  }
19  if (!canStartChat(config, win.location.hostname) || doc.querySelector('[data-ms-chat-runtime]')) return;
20  // Never replace another brand or take over an existing visitor session.
21  if (doc.querySelector('#zsiqscript') || win.$zoho?.salesiq) return;
22  const marker = doc.createElement('meta');
23  marker.dataset.msChatRuntime = '1';
24  doc.head.append(marker);
25  let stop;
26  let previousProduct;
27  let sessionEnded = false;
28  const endSession = () => {
29    sessionEnded = true;
30    win.$zoho?.salesiq?.reset?.();
31  };
32  win.addEventListener('multisystems:session-ended', endSession);
33  const profileForPage = () => {
34    const slug = config.product === 'multisystems' && win.location.pathname.startsWith('/products/')
35      ? win.location.pathname.split('/')[2] : config.product;
36    const hostProfile = getChatProfile(config.product, config.surface, '/chat/logos/');
37    const topicProfile = getChatProfile(slug, config.surface, '/chat/logos/');
38    return topicProfile ? { ...topicProfile, logo: hostProfile.logo } : hostProfile;
39  };
40  const response = await win.fetch('/chat/welcome.css');
41  const css = response.ok ? await response.text() : '';
42  if (sessionEnded) return;
43  const launcherStyle = doc.createElement('style');
44  launcherStyle.textContent = `#zs_fl_chat.siqico-chat{background:white var(--ms-chat-logo) center/32px 32px no-repeat;border-radius:50%}#zs_fl_chat.siqico-chat::before{visibility:hidden}@media(max-width:600px){#zsiq_chat_wrap{left:0!important;right:0!important;bottom:0!important;width:100vw!important;max-width:100vw!important;height:var(--ms-chat-height,100dvh)!important;max-height:var(--ms-chat-height,100dvh)!important}#siq_chatwindow{width:100%!important;height:100%!important}}`;
45  doc.head.append(launcherStyle);
46  const resize = () => doc.documentElement.style.setProperty('--ms-chat-height', `${win.visualViewport?.height ?? win.innerHeight}px`);
47  const update = () => {
48    if (sessionEnded) return;
49    if (excluded()) {
50      win.$zoho?.salesiq?.floatbutton?.visible?.('hide');
51      win.$zoho?.salesiq?.floatwindow?.visible?.('hide');
52      return;
53    }
54    win.$zoho?.salesiq?.floatbutton?.visible?.('show');
55    const profile = profileForPage();
56    win.$zoho?.salesiq?.visitor?.info?.({ ms_product: profile.product, ms_surface: profile.surface });
57    if (previousProduct === profile.product) return;
58    previousProduct = profile.product;
59    doc.documentElement.style.setProperty('--ms-chat-logo', `url("${profile.logo}")`);
60    stop?.();
61    if (css) stop = installChatHome(doc, css, profile);
62  };
63  win.$zoho = win.$zoho || {};
64  win.$zoho.salesiq = { ready: () => {
65    if (sessionEnded) { win.$zoho?.salesiq?.reset?.(); return; }
66    update();
67  } };
68  const script = doc.createElement('script');
69  script.id = 'zsiqscript';
70  script.src = WIDGET_URL;
71  script.defer = true;
72  doc.head.append(script);
73  resize(); update();
74  win.visualViewport?.addEventListener('resize', resize);
75  win.addEventListener('resize', resize);
76  win.addEventListener('popstate', update);
77  // One adapter for Next, React Router, Astro and plain browser navigation.
78  for (const method of ['pushState', 'replaceState']) {
79    const original = win.history[method];
80    win.history[method] = function (...args) { const result = original.apply(this, args); update(); return result; };
81  }
82}
83
84const browserDocument = globalThis.document;
85if (browserDocument && !isReviewSystems(browserDocument.defaultView.location.hostname)) {
86  browserDocument.defaultView.fetch('/chat/site.json').then(response => response.ok ? response.json() : null)
87    .then(config => config && startChat(browserDocument, config))
88    .catch(() => { /* The host page and its contact links remain usable. */ });
89}

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.