PageSourceSearch

https://astroreport.ai/attr.js

js astroreport.ai collected 2026-10-05 13:50:12 UTC 2,563 bytes, 36 lines download raw bytes

1/* AstroReport first-touch attribution (2026-09-07). Same contract as the
2   webapp's src/lib/attribution.ts: records utm_* / src / ref / click ids /
3   referrer / landing path ONCE (first touch wins, 90 days) in localStorage and
4   in the same-domain cookie `ar_attr`, so a click from an email or an ad that
5   lands here still reaches the webapp at astroreport.ai/users/... intact. */
6(function () {
7  try {
8    var KEY = 'ar_attr_v1', COOKIE = 'ar_attr', TTL = 90 * 24 * 3600 * 1000;
9    var KEYS = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content','src','ref','source','gclid','fbclid','ttclid','msclkid'];
10    function parse(raw) {
11      if (!raw) return null;
12      try { var v = JSON.parse(raw); if (!v || typeof v !== 'object') return null;
13        var at = Date.parse(v.first_touch_at || ''); if (isFinite(at) && Date.now() - at > TTL) return null; return v; } catch (e) { return null; }
14    }
15    function cookie(name) { var m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; }
16    var existing = null;
17    try { existing = parse(localStorage.getItem(KEY)); } catch (e) {}
18    if (!existing) existing = parse(cookie(COOKIE));
19    var params = new URLSearchParams(location.search), fresh = {}, has = false;
20    for (var i = 0; i < KEYS.length; i++) { var v = params.get(KEYS[i]); if (v && v.trim()) { fresh[KEYS[i]] = v.trim().slice(0, 200); has = true; } }
21    var out = null;
22    if (existing) {
23      if (has && fresh.utm_source && !existing.utm_source) { out = Object.assign({}, fresh, existing, { last_touch_at: new Date().toISOString(), last_utm_source: fresh.utm_source, last_utm_campaign: fresh.utm_campaign }); }
24      else return;
25    } else {
26      var referrer = '';
27      try { if (document.referrer) { var r = new URL(document.referrer); if (!/astroreport\.ai$|localhost/i.test(r.hostname)) referrer = document.referrer.slice(0, 200); } } catch (e) {}
28      if (!has && !referrer) return;
29      out = Object.assign({}, fresh, { landing_path: (location.pathname + (location.search ? location.search.slice(0, 120) : '')).slice(0, 200), first_touch_at: new Date().toISOString() });
30      if (referrer) out.landing_referrer = referrer;
31    }
32    var json = JSON.stringify(out);
33    try { localStorage.setItem(KEY, json); } catch (e) {}
34    try { document.cookie = COOKIE + '=' + encodeURIComponent(json) + '; Path=/; Max-Age=' + Math.floor(TTL / 1000) + '; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : ''); } catch (e) {}
35  } catch (e) {}
36})();

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.