PageSourceSearch

https://attentus.app/analytics.js?v=2

js attentus.app collected 2026-10-02 01:12:36 UTC 5,953 bytes, 126 lines download raw bytes

1/* First-party aggregate website events. No person/device identifiers or app telemetry. */
2(function () {
3  'use strict';
4  var REF = /^[a-z0-9][a-z0-9_-]{0,47}$/;
5  var PAGES = ['/', '/download', '/pricing', '/about', '/roadmap', '/account', '/welcome', '/signin', '/trial', '/privacy'];
6  var DETAILS = {
7    download_click: ['nav', 'hero', 'footer', 'pricing', 'download', 'account', 'welcome'],
8    discord_click: ['nav', 'hero', 'footer', 'community', 'about', 'roadmap'],
9    faq_open: ['screen_privacy', 'end_session', 'offline', 'supported_browsers', 'restarting', 'trial_account', 'trial_end', 'cancel', 'multiple_macs'],
10    calculator_use: ['']
11  };
12  var path = location.pathname.replace(/\.html$/, '').replace(/\/$/, '') || '/';
13  if (path === '/index') path = '/';
14  var page = PAGES.indexOf(path) !== -1 ? path : '';
15  var referral = '';
16  var seen = Object.create(null);
17
18  function optedOut() {
19    return navigator.globalPrivacyControl === true || navigator.doNotTrack === '1' ||
20      navigator.doNotTrack === 'yes' || window.doNotTrack === '1';
21  }
22  function clearReferral() { referral = ''; }
23  function getReferral() {
24    if (optedOut() || !page) clearReferral();
25    return referral;
26  }
27
28  // The shared campaign ID stays in the URL so reloads, copied links and new
29  // tabs work without cookies, browser storage or a visitor/session identifier.
30  try {
31    var url = new URL(location.href);
32    var candidate = url.searchParams.get('ref') || '';
33    if (!optedOut() && page && REF.test(candidate)) referral = candidate;
34  } catch (_) { /* Bad URLs must not affect the page. */ }
35
36  function track(event, detail) {
37    detail = detail || '';
38    if (!page || optedOut() || !Object.prototype.hasOwnProperty.call(DETAILS, event) || DETAILS[event].indexOf(detail) === -1) return;
39    var once = event === 'faq_open' || event === 'calculator_use';
40    var key = event + ':' + detail;
41    if (once && seen[key]) return;
42    if (once) seen[key] = true;
43    try {
44      // Never read page contents, input values, account state, full URLs, or referrers.
45      var request = fetch('/api/analytics.php', {
46        method: 'POST', credentials: 'omit', keepalive: true, referrerPolicy: 'no-referrer',
47        headers: { 'Content-Type': 'application/json' },
48        body: JSON.stringify({ event: event, page: page, detail: detail, referral_id: getReferral() })
49      });
50      if (request && typeof request.catch === 'function') request.catch(function () {});
51    } catch (_) { /* Analytics never blocks navigation or other interactions. */ }
52  }
53
54  function installerURL(channel) {
55    channel = channel === 'beta' ? 'beta' : 'stable';
56    var query = new URLSearchParams({ channel: channel, page: page || '/download' });
57    var ref = getReferral();
58    if (ref) query.set('ref', ref);
59    if (optedOut()) query.set('tracking', 'off');
60    return '/api/download.php?' + query.toString();
61  }
62  function checkoutContext() {
63    if (optedOut()) { clearReferral(); return false; }
64    if (['/pricing', '/account', '/welcome'].indexOf(page) === -1) return undefined;
65    return { page: page, referral_id: getReferral() };
66  }
67  window.AttentusAnalytics = { track: track, getReferral: getReferral, installerURL: installerURL, checkoutContext: checkoutContext };
68
69  function updateInstaller(link) {
70    try {
71      var url = new URL(link.href, location.href);
72      if (url.origin !== location.origin) return;
73      if (url.pathname === '/api/download.php' ||
74          (url.pathname === '/update/serve.php' && url.searchParams.get('asset') === 'installer')) {
75        link.href = installerURL(url.searchParams.get('channel'));
76      }
77    } catch (_) {}
78  }
79
80  function updateReferral(link) {
81    try {
82      var url = new URL(link.href, location.href);
83      var destination = url.pathname.replace(/\.html$/, '').replace(/\/$/, '') || '/';
84      if (destination === '/index') destination = '/';
85      // Only public website pages; never append campaign state to external,
86      // authentication API, or arbitrary download URLs.
87      if (url.origin !== location.origin || PAGES.indexOf(destination) === -1) return;
88      var ref = getReferral();
89      if (optedOut()) url.searchParams.delete('ref');
90      else if (ref && !url.searchParams.has('ref')) url.searchParams.set('ref', ref);
91      link.href = url.pathname + url.search + url.hash;
92    } catch (_) {}
93  }
94
95  function ready() {
96    if (!page) return;
97    document.querySelectorAll('a[href]').forEach(updateReferral);
98    document.querySelectorAll('a[data-analytics-event="download_click"]').forEach(updateInstaller);
99    document.addEventListener('click', function (event) {
100      var link = event.target.closest && event.target.closest('a[href]');
101      if (!link) return;
102      updateReferral(link);
103      var name = link.dataset.analyticsEvent;
104      try {
105        var url = new URL(link.href, location.href);
106        if (name === 'download_click') {
107          // Signed-in UI may turn a download CTA into an account link.
108          if (url.origin !== location.origin || ['/download', '/api/download.php', '/update/serve.php'].indexOf(url.pathname) === -1) return;
109          updateInstaller(link);
110        } else if (name === 'discord_click') {
111          if (url.protocol !== 'https:' || url.hostname !== 'discord.gg') return;
112        } else { return; }
113        track(name, link.dataset.analyticsDetail);
114      } catch (_) {}
115    }, true);
116    document.querySelectorAll('details[data-analytics-faq]').forEach(function (item) {
117      item.addEventListener('toggle', function () {
118        if (item.open) track('faq_open', item.dataset.analyticsFaq);
119      });
120    });
121    var calculator = document.getElementById('cHrs');
122    if (page === '/' && calculator) calculator.addEventListener('input', function () { track('calculator_use', ''); });
123  }
124  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready, { once: true });
125  else ready();
126})();

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.