PageSourceSearch

https://walletinvestor.com/js/lead.1789468388.js

js walletinvestor.com collected 2026-09-25 19:05:46 UTC 7,925 bytes, 174 lines download raw bytes

1/* Lead attribution (2026-08-18) - runs on EVERY page, including the
2 * full-page /account/* forms, which is why it lives outside
3 * account-modal.js (that file returns early when the modal markup is
4 * absent, so the capture never ran on the account pages).
5 *
6 * Two fields, two questions:
7 *   src/page = the ENTRY: the click that sent them toward Premium and the
8 *              page it sat on (header button on the BTC forecast page, a 7d
9 *              lock chip, the popup, or an external referrer)
10 *   cta      = which button on /premium/ finally closed it
11 *
12 * Clicks made ON /premium/ never overwrite the entry: every purchase
13 * happens there, so last-touch would report "/premium/" for everyone.
14 *
15 * Coverage rule: a signup with NO recorded click still records where the
16 * visitor was standing (src="direct"), because "unknown" taught us nothing
17 * - 22 of 26 signups came back blank on the first version. */
18(function () {
19  'use strict';
20  var KEY = 'wiLead';
21
22  function read() {
23    try { return JSON.parse(sessionStorage.getItem(KEY) || 'null') || {}; } catch (e) { return {}; }
24  }
25  function save(lead) {
26    try { sessionStorage.setItem(KEY, JSON.stringify(lead)); } catch (e) {}
27  }
28  function onPremium() { return /^\/premium/.test(window.location.pathname); }
29  function here() { return window.location.pathname.slice(0, 200); }
30
31  function sourceOf(el) {
32    var explicit = el.getAttribute('data-src');
33    if (explicit) return explicit;
34    if (el.closest('#wiPopup')) return 'popup';
35    var chip = el.classList.contains('nt-lock') ? el : el.closest('.nt-lock');
36    if (chip) {
37      var kind = chip.getAttribute('data-nt-kind') || 'lock';
38      var sym = chip.getAttribute('data-nt') || '';
39      return 'lock_' + kind + (sym ? '_' + sym : '');
40    }
41    if (el.closest('header')) return 'header';
42    if (el.closest('footer')) return 'footer';
43    var acct = el.getAttribute('data-account');
44    if (acct) return acct;              // login / register / checkout trigger
45    return 'link';
46  }
47
48  // arriving from outside counts as an entry, but only if nothing on-site
49  // has been clicked yet
50  (function () {
51    var lead = read();
52    if (lead.src || !document.referrer) return;
53    try {
54      var u = new URL(document.referrer);
55      if (u.host === window.location.host) return;
56      lead.src = 'ext';
57      lead.page = (u.host + u.pathname).slice(0, 200);
58      save(lead);
59    } catch (e) {}
60  })();
61
62  // ?src= on the landing URL (popup hand-off, campaigns) sets the entry and
63  // credits the page the visitor came from
64  (function () {
65    var m = /[?&]src=([A-Za-z0-9_.-]{1,60})/.exec(window.location.search);
66    if (!m) return;
67    var ref = '';
68    try {
69      var u = document.referrer ? new URL(document.referrer) : null;
70      if (u && u.host === window.location.host) ref = u.pathname;
71    } catch (e) {}
72    var lead = read();
73    lead.src = m[1];
74    lead.page = (ref || here()).slice(0, 200);
75    save(lead);
76  })();
77
78  document.addEventListener('click', function (e) {
79    var t = e.target.closest('a[href^="/premium"], a[href^="/account"], [data-account], .nt-lock');
80    if (!t) return;
81    var lead = read();
82    var src = sourceOf(t);
83    if (onPremium()) {
84      lead.cta = src;
85      if (!lead.src) { lead.src = src; lead.page = here(); }
86    } else {
87      lead.src = src;
88      lead.page = here();
89      lead.cta = '';
90    }
91    save(lead);
92  }, true);
93
94  // email-flow attribution (2026-09-07): links in the MailerLite automations
95  // and the daily CTA carry ?ml=<flow>&e=<email#> or &v=<variant>&d=<day>.
96  // Kept 30 days in a plain cookie (the checkout stamps it onto the
97  // purchase) and counted server-side - pages are cached, the server never
98  // sees the landing itself.
99  (function () {
100    var q = window.location.search, m = /[?&]ml=(cart|ebook|daily|gov)\b/.exec(q);
101    if (!m) return;
102    var e = (/[?&]e=(\d{1,2})\b/.exec(q) || [])[1], v = (/[?&]v=([ab])\b/.exec(q) || [])[1], d = (/[?&]d=(\d{8})\b/.exec(q) || [])[1];
103    var ref = e || (v && d ? v + '.' + d : (v || '')); // gov.capital's top bar: just the variant
104    if (!ref) return;
105    // &l=<link#>: the second (third...) link of the same email is its own
106    // ref ("3.2" = email 3, link 2) so each link converts on its own; the
107    // first link stays the bare ref
108    var l = (/[?&]l=(\d{1,2})\b/.exec(q) || [])[1];
109    if (l && l !== '1') ref += '.' + l;
110    // &until=<unix>
110: when this person's offer closes (the MailerLite field
111    // our flow job set) - the offer page counts down to it
112    var u = (/[?&]until=(\d{10})\b/.exec(q) || [])[1];
113    if (u) document.cookie = 'wi_offer_until=' + u + '; expires=' + exp + '; path=/; SameSite=Lax';
114    var cid = (/(?:^|; )wi_cid=([A-Za-z0-9]{8,32})/.exec(document.cookie) || [])[1] || Math.random().toString(36).slice(2, 14);
115    var exp = new Date(Date.now() + 30 * 86400000).toUTCString();
116    document.cookie = 'wi_cid=' + cid + '; expires=' + exp + '; path=/; SameSite=Lax';
117    document.cookie = 'wi_flow=' + m[1] + '|' + ref + '|' + cid + '; expires=' + exp + '; path=/; SameSite=Lax';
118    try {
119      fetch('/account/flow-hit', { method: 'POST', keepalive: true, headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify({ f: m[1], r: ref, cid: cid }) }).catch(function () {});
120    } catch (err) {}
121  })();
122
123  // campaign attribution (2026-09-15): any link carrying standard UTM
124  // parameters. The five fields ride in ONE cookie for 30 days, the newest
125  // landing overwriting the last (last touch, like the flows above), and
126  // the landing is counted server-side because the page itself is cached.
127  (function () {
128    var q = window.location.search;
129    if (!/[?&]utm_source=/.test(q)) return;
130    var fields = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_date'], utm = {}, any = false;
131    fields.forEach(function (f) {
132      var m = new RegExp('[?&]' + f + '=([^&#]{1,120})').exec(q);
133      var v = m ? decodeURIComponent(m[1].replace(/\+/g, ' ')).trim().slice(0, 80) : '';
134      utm[f] = v;
135      if (v) any = true;
136    });
137    if (!any || !utm.utm_source) return;
138    var exp = new Date(Date.now() + 30 * 86400000).toUTCString();
139    var cid = (/(?:^|; )wi_cid=([A-Za-z0-9]{8,32})/.exec(document.cookie) || [])[1] || Math.random().toString(36).slice(2, 14);
140    document.cookie = 'wi_cid=' + cid + '; expires=' + exp + '; path=/; SameSite=Lax';
141    document.cookie = 'wi_utm=' + fields.map(function (f) { return encodeURIComponent(utm[f]); }).join('|') + '; expires=' + exp + '; path=/; SameSite=Lax';
142    utm.f = 'utm'; utm.cid = cid;
143    try {
144      fetch('/account/flow-hit', { method: 'POST', keepalive: true, headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(utm) }).catch(function () {});
145    } catch (err) {}
146  })();
147
148  /** What to send with a signup. Never empty: falls back to this page. */
149  window.wiLead = function () {
150    var lead = read();
151    if (!lead.src) { lead.src = 'direct'; lead.page = here(); }
152
153    return lead;
154  };
155
156  // full-page /account/register and /account/checkout forms post normally,
157  // so the fields have to ride along as hidden inputs
158  document.addEventListener('submit', function (e) {
159    var form = e.target;
160    if (!form || form.tagName !== 'FORM') return;
161    var action = form.getAttribute('action') || window.location.pathname;
162    if (!/\/account\/(register|checkout)/.test(action)) return;
163    var lead = window.wiLead();
164    ['src', 'src_page', 'cta'].forEach(function (name) {
165      var value = name === 'src' ? lead.src : (name === 'src_page' ? lead.page : lead.cta);
166      if (!value || form.querySelector('input[name="' + name + '"]')) return;
167      var input = document.createElement('input');
168      input.type = 'hidden';
169      input.name = name;
170      input.value = value;
171      form.appendChild(input);
172    });
173  }, true);
174})();

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.