PageSourceSearch

https://fastingtrack-app.com/start/analytics.js

js fastingtrack-app.com collected 2026-10-02 00:49:11 UTC 5,730 bytes, 100 lines download raw bytes

1/* GA4 / funnel analytics. Same consent model as the main site
2   (localStorage 'ft_cookie_consent'); nothing loads until consent = accepted.
3   window.ftFunnelEvent mirrors the app's onboarding_step events so we can see
4   per-screen drop-off. Loaded before engine.js so ftFunnelEvent exists at step 1. */
5(function () {
6  var ID = 'G-BW2N7EB3GM'; // FastTrack GA4 / Firebase measurement ID
7  var TT_ID = 'DA7GND3C77U5KJC7O8VG'; // TikTok Pixel ID (Ads Manager > Tools > Events). Empty = TikTok off.
8
9  window.ftLoadAnalytics = function () {
10    if (window.__ftGA) return;
11    window.__ftGA = true;
12    var s = document.createElement('script');
13    s.async = true;
14    s.src = 'https://www.googletagmanager.com/gtag/js?id=' + ID;
15    document.head.appendChild(s);
16    window.dataLayer = window.dataLayer || [];
17    window.gtag = function () { dataLayer.push(arguments); };
18    gtag('js', new Date());
19    gtag('config', ID, { anonymize_ip: true });
20    ftLoadTikTok();
21  };
22
23  // TikTok Pixel, behind the same consent as GA. Official loader, trimmed.
24  function ftLoadTikTok() {
25    if (!TT_ID || window.__ftTT) return;
26    window.__ftTT = true;
27    !function (w, d, t) {
28      w.TiktokAnalyticsObject = t; var ttq = w[t] = w[t] || [];
29      ttq.methods = ['page', 'track', 'identify', 'instances', 'debug', 'on', 'off', 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie', 'holdConsent', 'revokeConsent', 'grantConsent'];
30      ttq.setAndDefer = function (t, e) { t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))); }; };
31      for (var i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
32      ttq.instance = function (t) { var e = ttq._i[t] || []; for (var n = 0; n < ttq.methods.length; n++) ttq.setAndDefer(e, ttq.methods[n]); return e; };
33      ttq.load = function (e, n) {
34        var r = 'https://analytics.tiktok.com/i18n/pixel/events.js';
35        ttq._i = ttq._i || {}; ttq._i[e] = []; ttq._i[e]._u = r; ttq._t = ttq._t || {}; ttq._t[e] = +new Date(); ttq._o = ttq._o || {}; ttq._o[e] = n || {};
36        var s = d.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = r + '?sdkid=' + e + '&lib=' + t;
37        var a = d.getElementsByTagName('script')[0]; a.parentNode.insertBefore(s, a);
38      };
39      ttq.load(TT_ID);
40      ttq.page();
41    }(window, document, 'ttq');
42  }
43
44  // Map our funnel events onto TikTok's standard names, so engine.js does not
45  // need to know TikTok exists. First funnel step counts as ViewContent.
46  function ttBridge(name) {
47    try {
48      if (!TT_ID || typeof window.ttq === 'undefined') return;
49      if (name === 'funnel_step' && !window.__ftTTView) { window.__ftTTView = true; ttq.track('ViewContent'); }
50      else if (name === 'account_created') ttq.track('CompleteRegistration');
51      else if (name === 'begin_checkout') ttq.track('InitiateCheckout');
52      else if (name === 'purchase') ttq.track('CompletePayment');
53    } catch (e) {}
54  }
55
56  // Generic GA4 event. No-op until GA is loaded (consent given).
57  window.ftEvent = function (name, params) {
58    try {
59      if (typeof window.gtag === 'function') window.gtag('event', name, params || {});
60    } catch (e) {}
61    ttBridge(name);
62  };
63
64  // Funnel step event. No-op until GA is loaded (consent given).
65  window.ftFunnelEvent = function (stepId, index) {
66    window.ftEvent('funnel_step', { step_id: stepId, step_index: index });
67  };
68
69  function consent() { try { return localStorage.getItem('ft_cookie_consent'); } catch (e) { return null; } }
70  function maybeLoad() { if (consent() === 'accepted') window.ftLoadAnalytics(); }
71
72  // A choice already made on the site applies here too: honor it, skip the banner.
73  if (consent()) { maybeLoad(); return; }
74
75  // No prior choice (e.g. ad traffic landing directly on /start/): show the banner.
76  document.addEventListener('DOMContentLoaded', function () {
77    var C = (window.FUNNEL_STRINGS && window.FUNNEL_STRINGS.consent) || {};
78    var MSG = C.msg || 'We use cookies to measure traffic, with your consent.', LINK = C.link || 'Privacy Policy';
79    var bar = document.createElement('div');
80    bar.setAttribute('role', 'dialog'); bar.setAttribute('aria-label', 'Cookies');
81    bar.style.cssText = 'position:fixed;left:0;right:0;bottom:0;z-index:3000;background:#2C2C2C;color:#fff;padding:14px 18px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;justify-content:center;font-size:.88rem;box-shadow:0 -4px 20px rgba(0,0,0,.15);';
82    var msg = document.createElement('span');
83    msg.style.cssText = 'flex:1;min-width:220px;line-height:1.5;';
84    msg.innerHTML = MSG + ' <a href="/privacy.html" style="color:#E8B4B8;text-decoration:underline;">' + LINK + '</a>';
85    var wrap = document.createElement('div'); wrap.style.cssText = 'display:flex;gap:10px;flex-shrink:0;';
86    function mk(label, primary) {
87      var b = document.createElement('button'); b.textContent = label;
88      b.style.cssText = 'border:none;cursor:pointer;padding:9px 20px;border-radius:20px;font-weight:600;font-size:.88rem;white-space:nowrap;' +
89        (primary ? 'background:#E8B4B8;color:#2C2C2C;' : 'background:transparent;color:#fff;border:1px solid rgba(255,255,255,.45);');
90      return b;
91    }
92    var acc = mk(C.accept || 'Accept', true), dec = mk(C.decline || 'Decline', false);
93    function choose(v) { try { localStorage.setItem('ft_cookie_consent', v); } catch (e) {} bar.remove(); maybeLoad(); }
94    acc.addEventListener('click', function () { choose('accepted'); });
95    dec.addEventListener('click', function () { choose('declined'); });
96    wrap.appendChild(dec); wrap.appendChild(acc);
97    bar.appendChild(msg); bar.appendChild(wrap);
98    document.body.appendChild(bar);
99  });
100})();

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.