/* Warmbly website tracking. Records page views for the workspace whose site
 * key the script tag carries, sends nothing before consent when the
 * workspace requires it, and honours Global Privacy Control / Do Not Track.
 * API: warmbly('consent','granted'|'denied'), warmbly('page'), warmbly('reset'). */
(function () {
  if (window.__wbly) return;
  window.__wbly = 1;
  var w = window, d = document, n = navigator;
  var sc = d.currentScript;
  if (!sc) {
    var all = d.getElementsByTagName('script');
    for (var i = all.length - 1; i >= 0; i--) {
      if (all[i].getAttribute('data-site')) { sc = all[i]; break; }
    }
  }
  if (!sc) return;
  var key = sc.getAttribute('data-site');
  if (!key) return;
  var mode = sc.getAttribute('data-consent') === 'implicit' ? 'implicit' : 'explicit';
  var base = sc.getAttribute('data-endpoint') || String(sc.src).replace(/\/tracking\.js.*$/, '');
  var endpoint = base + '/p';
  var VID = 'wbly_vid', SID = 'wbly_sid', SAT = 'wbly_sat', CON = 'wbly_consent', TOK = 'wbly_t';

  function rnd() {
    var a = new Uint8Array(16);
    if (w.crypto && w.crypto.getRandomValues) { w.crypto.getRandomValues(a); }
    else { for (var i = 0; i < 16; i++) a[i] = (Math.random() * 256) | 0; }
    var s = '';
    for (var j = 0; j < 16; j++) s += ('0' + a[j].toString(16)).slice(-2);
    return s;
  }
  function get(k) { try { return w.localStorage.getItem(k); } catch (e) { return null; } }
  function set(k, v) { try { w.localStorage.setItem(k, v); } catch (e) {} }
  function del(k) { try { w.localStorage.removeItem(k); } catch (e) {} }
  function sget(k) { try { return w.sessionStorage.getItem(k); } catch (e) { return null; } }
  function sset(k, v) { try { w.sessionStorage.setItem(k, v); } catch (e) {} }
  function sdel(k) { try { w.sessionStorage.removeItem(k); } catch (e) {} }

  function optedOut() {
    return n.globalPrivacyControl === true || n.doNotTrack === '1' || w.doNotTrack === '1';
  }
  function consent() {
    if (optedOut()) return null;
    var c = get(CON);
    if (c === 'granted') return 'granted';
    if (c === 'denied') return null;
    return mode === 'implicit' ? 'implicit' : null;
  }

  /* The click redirect appends the identification ticket; take it off the
   * address bar right away so it is never bookmarked or shared. */
  var token = '';
  try {
    var u = new URL(w.location.href);
    var t = u.searchParams.get(TOK);
    if (t) {
      token = t;
      u.searchParams.delete(TOK);
      w.history.replaceState(w.history.state, '', u.toString());
    }
  } catch (e) {}

  var landing = false, lastUrl = '', lastAt = 0;
  function session() {
    var now = Date.now();
    var sid = sget(SID), at = +sget(SAT) || 0;
    if (!sid || now - at > 30 * 60 * 1000) { sid = rnd(); landing = true; }
    sset(SID, sid);
    sset(SAT, String(now));
    return sid;
  }
  function tz() {
    try { return Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch (e) { return ''; }
  }

  function send() {
    var c = consent();
    if (!c) return;
    var url = w.location.href, now = Date.now();
    if (url === lastUrl && now - lastAt < 1000) return;
    lastUrl = url; lastAt = now;
    var vid = get(VID);
    if (!vid) { vid = rnd(); set(VID, vid); }
    var sid = session();
    var body = {
      k: key, v: vid, s: sid, c: c, t: token, u: url,
      ti: d.title || '', r: d.referrer || '', l: n.language || '', tz: tz(),
      sw: (w.screen && w.screen.width) || 0, sh: (w.screen && w.screen.height) || 0, ld: landing
    };
    landing = false; token = '';
    try {
      /* text/plain keeps this a simple request: no preflight on every view. */
      fetch(endpoint, {
        method: 'POST', keepalive: true, credentials: 'omit',
        headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify(body)
      }).then(function (r) { return r.status === 200 ? r.json() : null; })
        .then(function (j) { if (j && j.vid) set(VID, j.vid); })
        .catch(function () {});
    } catch (e) {}
  }

  /* Single-page apps: a pushed history entry is a page view. */
  var push = w.history.pushState;
  if (push) {
    w.history.pushState = function () {
      push.apply(this, arguments);
      setTimeout(send, 0);
    };
  }
  w.addEventListener('popstate', function () { setTimeout(send, 0); });

  var queued = (w.warmbly && w.warmbly.q) || [];
  function api(cmd, arg) {
    if (cmd === 'consent') {
      if (arg === 'granted') { set(CON, 'granted'); send(); }
      else if (arg === 'denied') { set(CON, 'denied'); del(VID); sdel(SID); sdel(SAT); }
    } else if (cmd === 'reset') {
      del(VID); sdel(SID); sdel(SAT);
    } else if (cmd === 'page') {
      send();
    }
  }
  w.warmbly = function () { api.apply(null, arguments); };
  w.warmbly.q = [];
  for (var q = 0; q < queued.length; q++) api.apply(null, queued[q]);
  send();
})();
