PageSourceSearch

https://dashboard.datalookout.com/static/js/consent.js?v=1370f4f1d4

js datalookout.com collected 2026-10-07 20:57:22 UTC 7,459 bytes, 161 lines download raw bytes

1/* Consent gate for the two optional trackers (legal audit 2026-10-06, commit 4).
2 *
3 * ONE file for both sites: datalookout.com serves this copy at /assets/consent.js and
4 * dashboard.datalookout.com serves a byte-identical copy at /static/js/consent.js
5 * (tests/test_consent_dashboard.py pins the parity). Configuration rides on the <script>
6 * tag's data attributes, so the file itself never changes per host:
7 *
8 *   data-ga       Google Analytics measurement id (loads the gtag library + config)
9 *   data-attr     URL of the first-touch attribution script to load (marketing only;
10 *                 the dashboard sets its attribution cookie server-side and gates it on
11 *                 the same dl_consent cookie this file writes)
12 *   data-privacy  href for the "Privacy policy" link (default /privacy)
13 *
14 * Region comes from the server: the page's <html data-dl-region="eu|other"> attribute,
15 * set by site/functions/_middleware.js (Pages) or dashboard/main.py (FastAPI) from
16 * Cloudflare's country code. A MISSING attribute counts as "eu", so an unknown country
17 * is asked, never tracked.
18 *
19 * Rules (the owner's, 2026-10-06):
20 *   - eu, no choice yet: show the bar, load NOTHING until Accept.
21 *   - other, no choice yet: load, no prompt (today's behaviour).
22 *   - dl_consent=no, any region: load nothing, ever; expire the trackers' cookies.
23 *   - dl_consent=yes, any region: load.
24 *   - Ignoring, scrolling or closing never counts as consent (there is no close button).
25 *   - The choice lives 180 days in one cookie on .datalookout.com, so it carries across
26 *     both sites. A footer link with data-dl-consent-open reopens the bar.
27 *   - Two buttons, same size, same style, neither pre-selected. Keyboard reachable with a
28 *     visible focus ring; the bar is an aria-live region so screen readers announce it.
29 */
30(function () {
31  "use strict";
32  var script = document.currentScript;
33  var ds = (script && script.dataset) || {};
34  var GA_ID = ds.ga || "";
35  var ATTR_SRC = ds.attr || "";
36  var PRIVACY_HREF = ds.privacy || "/privacy";
37
38  var COOKIE = "dl_consent";
39  var MAX_AGE = 180 * 24 * 60 * 60; // six months
40  var DOMAIN = ".datalookout.com";
41  var TEXT = "We use Google Analytics and a cookie that remembers how you found us, to see how people find and use DataLookout.";
42
43  function readChoice() {
44    var m = document.cookie.match(/(?:^|;\s*)dl_consent=(yes|no)(?:;|$)/);
45    return m ? m[1] : null;
46  }
47  function writeChoice(v) {
48    document.cookie = COOKIE + "=" + v + "; Domain=" + DOMAIN + "; Path=/; Max-Age=" + MAX_AGE + "; Secure; SameSite=Lax";
49  }
50  function expire(name) {
51    // Both scopes: GA writes on the registrable domain, dl_src too; be thorough.
52    document.cookie = name + "=; Domain=" + DOMAIN + "; Path=/; Max-Age=0; Secure; SameSite=Lax";
53    document.cookie = name + "=; Path=/; Max-Age=0; Secure; SameSite=Lax";
54  }
55  function clearTrackingCookies() {
56    var names = document.cookie.split(";").map(function (c) { return c.split("=")[0].trim(); });
57    names.forEach(function (n) { if (n === "_ga" || n.indexOf("_ga_") === 0 || n === "dl_src") expire(n); });
58  }
59
60  var region = document.documentElement.getAttribute("data-dl-region") === "other" ? "other" : "eu";
61  var loaded = false;
62  function loadTrackers() {
63    if (loaded) return;
64    loaded = true;
65    if (GA_ID) {
66      window.dataLayer = window.dataLayer || [];
67      window.gtag = window.gtag || function () { window.dataLayer.push(arguments); };
68      window.gtag("js", new Date());
69      window.gtag("config", GA_ID);
70      var g = document.createElement("script");
71      g.async = true;
72      g.src = "https://www.googletagmanager.com/gtag/js?id=" + encodeURIComponent(GA_ID);
73      document.head.appendChild(g);
74    }
75    if (ATTR_SRC) {
76      var a = document.createElement("script");
77      a.defer = true;
78      a.src = ATTR_SRC;
79      document.head.appendChild(a);
80    }
81  }
82
83  var CSS =
84    "#dl-consent{position:fixed;left:0;right:0;bottom:0;z-index:1000;box-sizing:border-box;" +
85    "background:var(--dl-panel,#fff);color:var(--dl-ink-1,#1e1d19);border-top:1px solid var(--dl-rule,#e4e1d7);" +
86    "box-shadow:0 -2px 12px rgba(0,0,0,.08);padding:14px 16px;font:16px/1.5 var(--dl-font-sans,system-ui,sans-serif);" +
87    "display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 24px}" +
88    "#dl-consent p{margin:0;flex:1 1 320px;max-width:760px}" +
89    "#dl-consent a{color:inherit;text-decoration:underline}" +
90    "#dl-consent .dl-consent-actions{display:flex;gap:10px;flex:0 0 auto}" +
91    "#dl-consent button{font:inherit;font-weight:500;padding:10px 18px;min-width:112px;cursor:pointer;" +
92    "background:transparent;color:var(--dl-ink-1,#1e1d19);border:1px solid var(--dl-ink-1,#1e1d19);border-radius:var(--dl-radius-sm,4px)}" +
93    "#dl-consent button:hover{background:var(--dl-canvas,#f6f5f0)}" +
94    "#dl-consent button:focus-visible,#dl-consent a:focus-visible{outline:3px solid var(--dl-action,#0f6e4d);outline-offset:2px}" +
95    "@media (max-width:480px){#dl-consent .dl-consent-actions{flex:1 1 100%}#dl-consent button{flex:1 1 0;min-width:0}}";
96
97  var bar = null;
98  function hide() {
99    if (!bar) return;
100    bar.parentNode.removeChild(bar);
101    bar = null;
102    document.body.style.paddingBottom = "";
103  }
104  function choose(v) {
105    writeChoice(v);
106    hide();
107    if (v === "yes") loadTrackers();
108    else clearTrackingCookies();
109  }
110  function show() {
111    if (bar) return;
112    if (!document.getElementById("dl-consent-style")) {
113      var st = document.createElement("style");
114      st.id = "dl-consent-style";
115      st.textContent = CSS;
116      document.head.appendChild(st);
117    }
118    bar = document.createElement("div");
119    bar.id = "dl-consent";
120    bar.setAttribute("role", "region");
121    bar.setAttribute("aria-label", "Cookie notice");
122    bar.setAttribute("aria-live", "polite");
123    var p = document.createElement("p");
124    p.id = "dl-consent-text";
125    p.appendChild(document.createTextNode(TEXT + " "));
126    var link = document.createElement("a");
127    link.href = PRIVACY_HREF;
128    link.textContent = "Privacy policy";
129    p.appendChild(link);
130    var actions = document.createElement("div");
131    actions.className = "dl-consent-actions";
132    [["yes", "Accept"], ["no", "Decline"]].forEach(function (pair) {
133      var b = document.createElement("button");
134      b.type = "button";
135      b.textContent = pair[1];
136      b.setAttribute("data-dl-consent", pair[0]);
137      b.addEventListener("click", function () { choose(pair[0]); });
138      actions.appendChild(b);
139    });
140    bar.appendChild(p);
141    bar.appendChild(actions);
142    document.body.appendChild(bar);
143    // Keep the page's own bottom content reachable while the bar is up.
144    document.body.style.paddingBottom = bar.offsetHeight + "px";
145  }
146
147  // Footer "Cookie settings": reopen the bar so the choice can be changed as easily as made.
148  document.addEventListener("click", function (e) {
149    var t = e.target && e.target.closest ? e.target.closest("[data-dl-consent-open]") : null;
150    if (!t) return;
151    e.preventDefault();
152    show();
153    var first = bar && bar.querySelector("button");
154    if (first) first.focus();
155  });
156
157  var choice = readChoice();
158  if (choice === "no") return;               // declined: nothing loads, no prompt
159  if (choice === "yes" || region === "other") { loadTrackers(); return; }
160  show();                                     // eu, undecided: ask, load nothing
161})();

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.