1/* Consent is separate from login, checkout, contractual acceptance and the NDA. */ 2(function () { 3 'use strict'; 4 if (window.GEXPrivacy) return; 5 var KEY = 'gex_privacy_choices_v1', TTL = 180 * 86400000; 6 var defaults = { analytics: false, marketing: false, referral: false }; 7 var current = null, panel, trigger, returnFocus; 8 var DISMISSED = 'gex_privacy_dismissed_v1'; 9 function read() { 10 try { 11 var x = JSON.parse(localStorage.getItem(KEY)); 12 if (x && x.version === 1 && Number.isFinite(x.savedAt) && x.savedAt <= Date.now() && Date.now() - x.savedAt < TTL) return x; 13 } catch (_) { /* Fail closed when storage is blocked or malformed. */ } 14 return null; 15 } 16 function allowed(category) { return !!(current && current[category] === true); } 17 function cleanLocal() { 18 if (!allowed('analytics')) { 19 try { localStorage.removeItem('gex_analytics_visitor_v1'); } catch (_) {} 20 ['gex_analytics_session_v1', 'gex_analytics_seen_v1', 'gex_analytics_touched_v1'].forEach(function (k) { try { sessionStorage.removeItem(k); } catch (_) {} }); 21 } 22 if (!allowed('referral')) ['gex_ref_code', 'gex_ref_code_at'].forEach(function (k) { try { localStorage.removeItem(k); } catch (_) {} }); 23 } 24 function save(values) { 25 var revoked = current && Object.keys(defaults).some(function (k) { return current[k] === true && values[k] !== true; }); 26 current = { version: 1, savedAt: Date.now(), analytics: values.analytics === true, marketing: values.marketing === true, referral: values.referral === true }; 27 try { localStorage.setItem(KEY, JSON.stringify(current)); } catch (_) { /* The choice still applies to this page. */ } 28 cleanLocal(); 29 close(); 30 window.dispatchEvent(new CustomEvent('gex:privacy-changed', { detail: Object.assign({}, current) })); 31 // A loaded third-party pixel cannot be reliably unloaded. Reload with the new denied state. 32 if (revoked) location.reload(); 33 } 34 function whenAllowed(category, fn) { 35 var started = false; 36 function run() { if (!started && allowed(category)) { started = true; fn(); } } 37 window.addEventListener('gex:privacy-changed', run); 38 run(); 39 } 40 function close() { 41 if (!panel) return; 42 if (panel.open) panel.close(); 43 panel.hidden = true; 44 try { sessionStorage.setItem(DISMISSED, '1'); } catch (_) {} 45 if (returnFocus && returnFocus.isConnected) returnFocus.focus(); 46 } 47 function open(event) { 48 if (!panel) return; 49 returnFocus = event && event.currentTarget || document.activeElement; 50 Object.keys(defaults).forEach(function (k) { panel.querySelector('[name="' + k + '"]').checked = allowed(k); }); 51 panel.hidden = false; 52 if (!panel.open) panel.showModal(); 53 panel.querySelector('[data-privacy-reject]').focus(); 54 } 55 current = read(); 56 cleanLocal(); 57 window.GEXPrivacy = Object.freeze({ allowed: allowed, whenAllowed: whenAllowed, open: open }); 58 window.dispatchEvent(new CustomEvent('gex:privacy-ready')); 59 window.addEventListener('storage', function (event) { 60 if (event.key !== KEY && event.key !== null) return; 61 var before = current; current = read(); cleanLocal(); 62 if (before && Object.keys(defaults).some(function (k) { return before[k] && !allowed(k); })) location.reload(); 63 else window.dispatchEvent(new CustomEvent('gex:privacy-changed')); 64 }); 65 function mount() { 66 var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/assets/privacy-choices.css?v=dialog-20260930'; document.head.appendChild(link); 67 panel = document.createElement('dialog'); panel.className = 'gex-privacy'; panel.hidden = true; 68 panel.setAttribute('aria-labelledby', 'gex-privacy-title'); panel.setAttribute('aria-describedby', 'gex-privacy-summary'); 69 panel.innerHTML = '<div class="gex-privacy__head"><span class="gex-privacy__eyebrow">GEX Levels · Privacy</span><button type="button" data-privacy-close aria-label="Close privacy choices">Close</button></div><h2 id="gex-privacy-title">Your privacy, your choice.</h2><p id="gex-privacy-summary">Optional tracking stays off unless you choose it. Refusing or closing this window does not block sign-in or purchases.</p><div class="gex-privacy__essential"><strong>Essential storage · always on</strong><span>Sign-in, security and checkout. Separate from optional tracking and contractual acceptance.</span></div><fieldset><legend>Optional purposes</legend><label><span><strong>Audience measurement</strong><small>First-party page visits to help improve the site.</small></span><input type="checkbox" name="analytics"></label><label><span><strong>Marketing</strong><small>Whop attribution pixel;
69 Meta only if configured.</small></span><input type="checkbox" name="marketing"></label><label><span><strong>Affiliate attribution</strong><small>Remember a referral for up to 30 days.</small></span><input type="checkbox" name="referral"></label></fieldset><div class="gex-privacy__buttons"><button type="button" data-privacy-reject>Reject optional</button><button type="button" data-privacy-accept>Accept all</button><button type="button" data-privacy-save>Save my choices</button></div><p class="gex-privacy__foot">Change your choices anytime using Privacy choices. <a href="/privacy#privacy-storage">Privacy details</a>.</p>'; 70 panel.querySelector('[data-privacy-reject]').addEventListener('click', function () { save(defaults); }); 71 panel.querySelector('[data-privacy-accept]').addEventListener('click', function () { save({ analytics: true, marketing: true, referral: true }); }); 72 panel.querySelector('[data-privacy-close]').addEventListener('click', close); 73 panel.addEventListener('cancel', function (event) { event.preventDefault(); close(); }); 74 panel.addEventListener('click', function (event) { 75 if (event.target !== panel) return; 76 var rect = panel.getBoundingClientRect(); 77 if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) close(); 78 }); 79 panel.querySelector('[data-privacy-save]').addEventListener('click', function () { var v = {}; Object.keys(defaults).forEach(function (k) { v[k] = panel.querySelector('[name="' + k + '"]').checked; }); save(v); }); 80 document.body.appendChild(panel); 81 trigger = document.createElement('button'); trigger.type = 'button'; trigger.className = 'gex-privacy-trigger'; trigger.textContent = 'Privacy choices'; trigger.setAttribute('aria-haspopup', 'dialog'); trigger.addEventListener('click', open); 82 var footer = document.querySelector('footer'); (footer || document.body).appendChild(trigger); 83 document.querySelectorAll('[data-privacy-open]').forEach(function (button) { button.addEventListener('click', open); }); 84 var dismissed = false; 85 try { dismissed = sessionStorage.getItem(DISMISSED) === '1'; } catch (_) {} 86 // Private workspaces stay usable without an intrusive first-visit prompt; 87 // optional tracking still defaults to denied, with choices in settings/footer. 88 if (!current && !dismissed && !document.querySelector('[data-account-page], [data-admin-page]')) open(); 89 } 90 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount, { once: true }); else mount(); 91})();
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.