PageSourceSearch

https://www.xyner.ai/assets/js/whatsapp-widget.js?v=4

js xyner.ai collected 2026-10-05 14:03:17 UTC 6,693 bytes, 122 lines download raw bytes

1/* =========================================================
2   xyner.ai — Floating WhatsApp button + regional contact popup
3   Self-contained widget. Include with a <script> tag on every
4   page. Path resolves automatically (root, /folder/, etc.).
5   ========================================================= */
6(function () {
7  if (window.__xynerWhatsAppLoaded) return;
8  window.__xynerWhatsAppLoaded = true;
9
10  // Resolve image path relative to whichever copy of this script loaded
11  function resolveAssetPath() {
12    var scripts = document.getElementsByTagName('script');
13    for (var i = scripts.length - 1; i >= 0; i--) {
14      var src = scripts[i].getAttribute('src') || '';
15      if (src.indexOf('whatsapp-widget.js') !== -1) {
16        return src.replace(/[?#].*$/, '').replace(/assets\/js\/whatsapp-widget\.js$/, 'assets/img/whatsapp.png');
17      }
18    }
19    return '/assets/img/whatsapp.png';
20  }
21  var WA_ICON = resolveAssetPath();
22
23  // wa.me requires digits only
24  var OFFICES = [
25    { iso: 'US', name: 'USA · Global HQ', phone: '+1 (774) 542 1363', wa: '17745421363'  },
26    { iso: 'IN', name: 'India',           phone: '+91 96008 52044',   wa: '919600852044' },
27    { iso: 'AE', name: 'UAE',             phone: '+971 58 557 6809',  wa: '971585576809' },
28    { iso: 'SA', name: 'Saudi Arabia',    phone: '+966 55 108 1650',  wa: '966551081650' },
29    { iso: 'SG', name: 'Singapore',       phone: '+65 6866 3337',     wa: '6568663337'   },
30    { iso: 'BH', name: 'Bahrain',         phone: '+973 3373 3381',    wa: '97333733381'  }
31  ];
32
33  // Inject required styles once
34  var css =
35    '.xy-wa-fab{position:fixed;left:20px;bottom:20px;background:transparent;border:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:none;z-index:9998;transition:transform .18s ease;padding:0;outline-offset:4px;overflow:visible}' +
36    '.xy-wa-fab:hover{transform:translateY(-2px) scale(1.06)}' +
37    '.xy-wa-fab img{display:block;width:64px;height:auto}' +
38    '.xy-wa-pop-bg{position:fixed;inset:0;background:rgba(6,7,13,0.55);backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);z-index:9999;display:none;align-items:flex-end;justify-content:flex-start;padding:0 20px 96px}' +
39    '.xy-wa-pop-bg.is-open{display:flex}' +
40    '.xy-wa-pop{width:100%;max-width:380px;background:#fff;color:#1a1f2e;border-radius:18px;box-shadow:0 24px 60px -12px rgba(0,0,0,0.45),0 0 0 1px rgba(0,0,0,0.05);overflow:hidden;display:flex;flex-direction:column;max-height:75vh}' +
41    '.xy-wa-pop header{padding:22px 24px 16px;border-bottom:1px solid rgba(0,0,0,0.07);position:relative}' +
42    '.xy-wa-pop h3{margin:0 0 4px;font-size:18px;font-weight:700;color:#1a1f2e}' +
43    '.xy-wa-pop p{margin:0;font-size:13.5px;color:#5b6273}' +
44    '.xy-wa-close{position:absolute;top:14px;right:14px;width:28px;height:28px;border:0;background:rgba(0,0,0,0.05);color:#5b6273;border-radius:7px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;line-height:1;padding:0}' +
45    '.xy-wa-close:hover{background:rgba(0,0,0,0.1);color:#1a1f2e}' +
46    '.xy-wa-list{list-style:none;margin:0;padding:6px 10px 14px;overflow-y:auto}' +
47    '.xy-wa-row{display:flex;align-items:center;gap:14px;padding:12px 14px;border-radius:12px;text-decoration:none;color:inherit;transition:background .15s;border:1px solid transparent}' +
48    '.xy-wa-row:hover{background:rgba(37,211,102,0.08);border-color:rgba(37,211,102,0.20)}' +
49    '.xy-wa-iso{flex-shrink:0;width:38px;font-size:13px;font-weight:700;color:#5b6273;letter-spacing:0.02em}' +
50    '.xy-wa-info{display:flex;flex-direction:column;min-width:0}' +
51    '.xy-wa-name{font-size:15px;font-weight:700;color:#1a1f2e;line-height:1.2}' +
52    '.xy-wa-num{font-size:13px;color:#5b6273;font-family:"JetBrains Mono","SF Mono",Menlo,Consolas,monospace;margin-top:2px;letter-spacing:0.02em}' +
53    '@media (max-width:640px){.xy-wa-pop-bg{padding:0 12px 92px}.xy-wa-fab{left:14px;bottom:14px}.xy-wa-fab img{width:56px}}';
54  var styleTag = document.createElement('style');
55  styleTag.setAttribute('data-xy', 'whatsapp');
56  styleTag.textContent = css;
57  document.head.appendChild(styleTag);
58
59  // Build popup HTML
60  var rows = '';
61  for (var i = 0; i < OFFICES.length; i++) {
62    var o = OFFICES[i];
63    rows += '<li><a class="xy-wa-row" href="https://wa.me/' + o.wa +
64      '?text=' + encodeURIComponent('Hi xyner.ai team, I would like to chat.') +
65      '" target="_blank" rel="noopener noreferrer">' +
66      '<span class="xy-wa-iso">' + o.iso + '</span>' +
67      '<span class="xy-wa-info"><span class="xy-wa-name">' + o.name + '</span>' +
68      '<span class="xy-wa-num">' + o.phone + '</span></span></a></li>';
69  }
70
71  var html =
72    '<button type="button" class="xy-wa-fab" aria-label="Chat on WhatsApp">' +
73      '<img src="' + WA_ICON + '" alt="WhatsApp" loading="eager"/>' +
74    '</button>' +
75    '<div class="xy-wa-pop-bg" id="xyWaPopBg">' +
76      '<div class="xy-wa-pop" id="xyWaPop" role="dialog" aria-modal="true">' +
77        '<header>' +
78          '<button type="button" class="xy-wa-close" aria-label="Close">&times;</button>' +
79          '<h3>Chat with our regional team</h3>' +
80          '<p>Pick the office closest to you.</p>' +
81        '</header>' +
82        '<ul class="xy-wa-list">' + rows + '</ul>' +
83      '</div>' +
84    '</div>';
85
86  function mount() {
87    try {
88      if (document.querySelector('.xy-wa-fab')) return;
89      if (!document.body) { setTimeout(mount, 50); return; }
90      var holder = document.createElement('div');
91      holder.innerHTML = html;
92      while (holder.firstChild) document.body.appendChild(holder.firstChild);
93
94      var fab = document.querySelector('.xy-wa-fab');
95      var bg = document.getElementById('xyWaPopBg');
96      var pop = document.getElementById('xyWaPop');
97      if (!fab || !bg || !pop) return;
98      var closeBtn = pop.querySelector('.xy-wa-close');
99
100      function openPopup() { bg.classList.add('is-open'); }
101      function closePopup() { bg.classList.remove('is-open'); }
102
103      fab.addEventListener('click', openPopup);
104      closeBtn.addEventListener('click', closePopup);
105      bg.addEventListener('click', function (e) { if (e.target === bg) closePopup(); });
106      document.addEventListener('keydown', function (e) {
107        if (e.key === 'Escape' && bg.classList.contains('is-open')) closePopup();
108      });
109
110      window.openXynerWhatsApp = openPopup;
111      window.closeXynerWhatsApp = closePopup;
112    } catch (err) {
113      if (window.console && console.error) console.error('[xyner WhatsApp]', err);
114    }
115  }
116
117  if (document.readyState === 'loading') {
118    document.addEventListener('DOMContentLoaded', mount);
119  } else {
120    mount();
121  }
122})();

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.