PageSourceSearch

https://everything.thetool.company/track.js

js thetool.company collected 2026-10-02 01:56:12 UTC 11,854 bytes, 313 lines download raw bytes

1(function () {
2  if (window.__everythingTracker) return;
3
4  var KEY = "";
5  var HOST = "https://everything.thetool.company";
6  var COOKIE = '_ev_vid';
7  var COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 2; // 2 years
8  var SESSION_KEY = '_ev_sid';
9  // Google Ads click id, kept in its own cookie so it survives the whole
10  // consideration window: the click lands on a pricing page, the signup
11  // happens days later, and the conversion still has to name the click.
12  // 90 days matches Google's conversion import cutoff — an id older than
13  // that is rejected on upload, so there's no point keeping it longer.
14  var CLICK_COOKIE = '_ev_gclid';
15  var CLICK_COOKIE_MAX_AGE = 60 * 60 * 24 * 90; // 90 days
16
17  // Honor Do Not Track / Global Privacy Control. We never send beacons in
18  // these cases — visitors get a no-op tracker. Opt-in override per
19  // workspace can come later via a settings toggle if needed.
20  var dnt = navigator.doNotTrack === '1' || window.doNotTrack === '1' ||
21            navigator.msDoNotTrack === '1' || navigator.globalPrivacyControl === true;
22  if (dnt) {
23    window.everything = { identify: function(){}, track: function(){}, pageview: function(){}, visitorId: function(){ return null; } };
24    window.__everythingTracker = true;
25    return;
26  }
27
28  // ── Cookie helpers (first-party on customer domain) ───────────────────
29
30  function getCookie(name) {
31    var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
32    return match ? decodeURIComponent(match[1]) : null;
33  }
34
35  function setCookie(name, value, maxAge) {
36    var parts = [name + '=' + encodeURIComponent(value), 'path=/', 'max-age=' + maxAge, 'SameSite=Lax'];
37    if (location.protocol === 'https:') parts.push('Secure');
38    document.cookie = parts.join('; ');
39  }
40
41  function uuid() {
42    if (window.crypto && crypto.randomUUID) return crypto.randomUUID();
43    var hex = 'abcdef0123456789';
44    var s = '';
45    for (var i = 0; i < 36; i++) {
46      if (i === 8 || i === 13 || i === 18 || i === 23) s += '-';
47      else if (i === 14) s += '4';
48      else if (i === 19) s += hex[(Math.random() * 4) | 8];
49      else s += hex[(Math.random() * 16) | 0];
50    }
51    return s;
52  }
53
54  // ── Visitor identity ──────────────────────────────────────────────────
55
56  var vid = getCookie(COOKIE);
57  if (!vid) {
58    try { vid = localStorage.getItem(COOKIE); } catch (e) {}
59  }
60  if (!vid) vid = uuid();
61  setCookie(COOKIE, vid, COOKIE_MAX_AGE);
62  try { localStorage.setItem(COOKIE, vid); } catch (e) {}
63
64  // Per-tab session id, reset when the tab is closed. The server still
65  // owns session boundaries (30min inactivity), this is just for grouping
66  // beacons sent in quick succession.
67  var sid = null;
68  try { sid = sessionStorage.getItem(SESSION_KEY); } catch (e) {}
69  if (!sid) {
70    sid = uuid();
71    try { sessionStorage.setItem(SESSION_KEY, sid); } catch (e) {}
72  }
73
74  // ── UTM + referrer (captured once per session) ────────────────────────
75
76  function parseQuery(qs) {
77    var out = {};
78    if (!qs) return out;
79    qs.replace(/^\?/, '').split('&').forEach(function (pair) {
80      if (!pair) return;
81      var eq = pair.indexOf('=');
82      var k = eq >= 0 ? pair.slice(0, eq) : pair;
83      var v = eq >= 0 ? pair.slice(eq + 1) : '';
84      try { out[decodeURIComponent(k)] = decodeURIComponent(v); } catch (e) { out[k] = v; }
85    });
86    return out;
87  }
88
89  function utmParams() {
90    var q = parseQuery(location.search);
91    return {
92      utm_source: q.utm_source || null,
93      utm_medium: q.utm_medium || null,
94      utm_campaign: q.utm_campaign || null,
95      utm_term: q.utm_term || null,
96      utm_content: q.utm_content || null,
97    };
98  }
99
100  // ── Google Ads click ids ──────────────────────────────────────────────
101  // Exactly one of gclid / gbraid / wbraid rides on a paid click, depending
102  // on the privacy context (wbraid/gbraid appear on iOS where gclid is
103  // withheld). Whichever we see is written to a cookie on first touch and
104  // replayed on every later beacon, so a signup that happens three pages or
105  // three days after the click still carries the id that paid for it.
106  //
107  // First-touch wins: overwriting on a later organic visit would be a no-op
108  // (no id in the URL), but a second *paid* click legitimately replaces it —
109  // Google attributes to the most recent click.
110
111  function readStoredClick() {
112    var raw = getCookie(CLICK_COOKIE);
113    if (!raw) {
114      try { raw = localStorage.getItem(CLICK_COOKIE); } catch (e) {}
115    }
116    if (!raw) return null;
117    try {
118      var parsed = JSON.parse(raw);
119      if (parsed && (parsed.gclid || parsed.gbraid || parsed.wbraid)) return parsed;
120    } catch (e) {}
121    return null;
122  }
123
124  function clickIds() {
125    var q = parseQuery(location.search);
126    var fresh = q.gclid || q.gbraid || q.wbraid;
127
128    if (fresh) {
129      var record = {
130        gclid: q.gclid || null,
131        gbraid: q.gbraid || null,
132        wbraid: q.wbraid || null,
133        at: new Date().toISOString(),
134      };
135      var json = JSON.stringify(record);
136      setCookie(CLICK_COOKIE, json, CLICK_COOKIE_MAX_AGE);
137      try { localStorage.setItem(CLICK_COOKIE, json); } catch (e) {}
138      return record;
139    }
140
141    // No id in this URL — replay whatever the original click stored.
142    var stored = readStoredClick();
143    if (!stored) return { gclid: null, gbraid: null, wbraid: null, at: null };
144    return {
145      gclid: stored.gclid || null,
146      gbraid: stored.gbraid || null,
147      wbraid: stored.wbraid || null,
148      at: stored.at || null,
149    };
150  }
151
152  // ── Beacon ────────────────────────────────────────────────────────────
153
154  function send(type, body) {
155    var payload = Object.assign({
156      type: type,
157      key: KEY,
158      vid: vid,
159      sid: sid,
160    }, body || {});
161    var json = JSON.stringify(payload);
162    var url = HOST + '/api/track/web';
163    try {
164      if (navigator.sendBeacon) {
165        var blob = new Blob([json], { type: 'application/json' });
166        if (navigator.sendBeacon(url, blob)) return;
167      }
168    } catch (e) {}
169    // Fallback fetch (keepalive so it survives unload).
170    try {
171      fetch(url, {
172        method: 'POST',
173        headers: { 'Content-Type': 'application/json' },
174        body: json,
175        // 'include' so the Everything-domain _evd cookie (identity network)
176        // rides along; sendBeacon above already sends cookies by default.
177        credentials: 'include',
178        keepalive: true,
179      }).catch(function () {});
180    } catch (e) {}
181  }
182
183  // ── Pageview tracking ─────────────────────────────────────────────────
184
185  var lastPath = null;
186  var pageStartedAt = Date.now();
187
188  function pageviewBody() {
189    var q = parseQuery(location.search);
190    return {
191      url: location.href,
192      path: location.pathname,
193      title: document.title || null,
194      referrer: document.referrer || null,
195      lang: navigator.language || null,
196      screen_w: window.screen ? window.screen.width : null,
197      screen_h: window.screen ? window.screen.height : null,
198      utm: utmParams(),
199      click: clickIds(),
200      // _evid lets us auto-stitch when a visitor clicks an outbound email
201      // link tracked by /api/track/click — the redirect appends _evid.
202      identify_contact_id: q._evid || null,
203    };
204  }
205
206  function pageview() {
207    if (lastPath !== null) {
208      // Send duration of the previous page before recording the new one.
209      var dur = Math.round((Date.now() - pageStartedAt) / 1000);
210      if (dur > 0) send('page_unload', { path: lastPath, duration_seconds: dur });
211    }
212    lastPath = location.pathname + location.search;
213    pageStartedAt = Date.now();
214    send('pageview', pageviewBody());
215  }
216
217  // SPA navigation: hook history methods + popstate.
218  function patchHistory(name) {
219    var orig = history[name];
220    if (typeof orig !== 'function') return;
221    history[name] = function () {
222      var ret = orig.apply(this, arguments);
223      setTimeout(pageview, 0);
224      return ret;
225    };
226  }
227  patchHistory('pushState');
228  patchHistory('replaceState');
229  window.addEventListener('popstate', function () { setTimeout(pageview, 0); });
230
231  // Send unload duration on tab close / blur (so server-side time-on-page is accurate).
232  function flushUnload() {
233    if (lastPath === null) return;
234    var dur = Math.round((Date.now() - pageStartedAt) / 1000);
235    if (dur > 0) send('page_unload', { path: lastPath, duration_seconds: dur });
236  }
237  window.addEventListener('pagehide', flushUnload);
238  window.addEventListener('beforeunload', flushUnload);
239
240  // ── Public API: window.everything ─────────────────────────────────────
241
242  var api = {
243    visitorId: function () { return vid; },
244    // Lets our own signup page read the click id that paid for this visitor
245    // and attach it to the account, so the eventual paid conversion can be
246    // imported back into Google Ads against the right click.
247    clickIds: clickIds,
248    pageview: pageview,
249    track: function (name, properties) {
250      if (!name) return;
251      send('event', {
252        name: String(name),
253        properties: properties || {},
254        url: location.href,
255        path: location.pathname,
256        click: clickIds(),
257      });
258    },
259    identify: function (email, name, traits) {
260      if (!email) return;
261      send('identify', {
262        email: String(email).trim().toLowerCase(),
263        name: name ? String(name) : null,
264        traits: traits || {},
265        url: location.href,
266        path: location.pathname,
267        click: clickIds(),
268      });
269    },
270  };
271
272  // Replay queued calls made before the script loaded:
273  //   window.everything = window.everything || function(){(everything.q=everything.q||[]).push(arguments)};
274  var existing = window.everything;
275  window.everything = api;
276  if (existing && existing.q && existing.q.length) {
277    for (var i = 0; i < existing.q.length; i++) {
278      try {
279        var call = existing.q[i];
280        var method = call[0];
281        if (api[method]) api[method].apply(null, Array.prototype.slice.call(call, 1));
282      } catch (e) {}
283    }
284  }
285
286  // ── Auto-identify on form submit ──────────────────────────────────────
287  // Scan submitted forms for any email-shaped input and call identify() so
288  // customers get visitor stitching without any manual code.
289  document.addEventListener('submit', function (ev) {
290    try {
291      var form = ev.target;
292      if (!form || form.tagName !== 'FORM') return;
293      var inputs = form.querySelectorAll('input, textarea');
294      var email = null;
295      var name = null;
296      for (var i = 0; i < inputs.length; i++) {
297        var el = inputs[i];
298        var type = (el.getAttribute('type') || '').toLowerCase();
299        var nm = (el.getAttribute('name') || el.getAttribute('id') || el.getAttribute('autocomplete') || '').toLowerCase();
300        var val = (el.value || '').trim();
301        if (!val) continue;
302        if (!email && (type === 'email' || /email|e-mail/.test(nm)) && /.+@.+\..+/.test(val)) email = val;
303        if (!name && /^(name|full[-_]?name|fname|firstname)/.test(nm)) name = val;
304      }
305      if (email) api.identify(email, name);
306    } catch (e) {}
307  }, true);
308
309  window.__everythingTracker = true;
310
311  // First pageview.
312  pageview();
313})();

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.