1/** 2 * Cayden Form Manager â journey tracking beacon. 3 * 4 * CRITICAL (spec §4.2 / §13): this is the ONLY place a page visit is recorded. 5 * The site runs behind WP Engine Global Edge Security, which serves cached 6 * HTML; a PHP write during render never fires for a cached page. So tracking 7 * must be a client-side beacon to an uncached REST endpoint. If you ever move 8 * visit-recording server-side, the journey feature silently dies in production. 9 */ 10( function () { 11 'use strict'; 12 13 var cfg = window.ACPS_ST || {}; 14 if ( ! cfg.restUrl ) { 15 return; 16 } 17 18 // Master off switch: when analytics/visitor/page tracking is disabled, do 19 // nothing at all â no beacon, heartbeat, presence, cookies or network calls. 20 // Forms still work: they read the config below and degrade without a session. 21 if ( ! cfg.analytics ) { 22 window.ACPS_ST_RT = window.ACPS_ST_RT || { token: '', active: false }; 23 return; 24 } 25 26 var SID_COOKIE = 'acps_st_sid'; 27 var CONSENT_COOKIE = 'acps_st_consent'; 28 29 /* ---- tiny cookie helpers (first-party only) ---------------------- */ 30 function readCookie( name ) { 31 var m = document.cookie.match( '(^|;)\\s*' + name + '\\s*=\\s*([^;]+)' ); 32 return m ? decodeURIComponent( m.pop() ) : ''; 33 } 34 function writeCookie( name, value, minutes ) { 35 var expires = ''; 36 if ( minutes ) { 37 var d = new Date(); 38 d.setTime( d.getTime() + minutes * 60 * 1000 ); 39 expires = '; expires=' + d.toUTCString(); 40 } 41 document.cookie = name + '=' + encodeURIComponent( value ) + expires + 42 '; path=/; SameSite=Lax' + ( location.protocol === 'https:' ? '; Secure' : '' ); 43 } 44 45 /* ---- session token (session-scoped, first-party) ----------------- */ 46 function makeToken() { 47 var bytes = new Uint8Array( 20 ); 48 ( window.crypto || window.msCrypto ).getRandomValues( bytes ); 49 var hex = ''; 50 for ( var i = 0; i < bytes.length; i++ ) { 51 hex += ( '0' + bytes[ i ].toString( 16 ) ).slice( -2 ); 52 } 53 return hex; 54 } 55 function getToken() { 56 var t = readCookie( SID_COOKIE ); 57 if ( ! /^[a-f0-9]{40}$/.test( t ) ) { 58 t = makeToken(); 59 } 60 // Refresh the idle window on every activity. 61 writeCookie( SID_COOKIE, t, cfg.idleMinutes || 30 ); 62 return t; 63 } 64 65 /* Unique-user identity is now derived SERVER-SIDE from the anonymized IP + 66 browser (the same signal the spam guard uses), so there is deliberately no 67 client-side visitor id here â clearing cookies/cache can't mint a new one. */ 68 69 /* ---- consent ----------------------------------------------------- */ 70 function hasConsent() { 71 return readCookie( CONSENT_COOKIE ) === '1'; 72 } 73 function trackingActive() { 74 if ( ! cfg.tracking ) { 75 return false; 76 } 77 // Logged-in admins are excluded from analytics entirely. 78 if ( cfg.suppress ) { 79 return false; 80 } 81 if ( cfg.consentMode && ! hasConsent() ) { 82 return false; 83 } 84 return true; 85 } 86 87 /* Public: let a cookie banner grant/revoke consent. Forms keep working 88 regardless (spec §4.4). */ 89 window.acpsStGrantConsent = function () { 90 writeCookie( CONSENT_COOKIE, '1', 60 * 24 * 365 ); 91 beacon(); 92 }; 93 window.acpsStRevokeConsent = function () { 94 writeCookie( CONSENT_COOKIE, '0', 60 * 24 * 365 ); 95 }; 96 97 /* Shared runtime other scripts read (forms/feedback). */ 98 var runtime = window.ACPS_ST_RT = { 99 token: '', 100 active: false, 101 restUrl: cfg.restUrl 102 }; 103 104 function send( path, payload, useBeacon ) { 105 var url = cfg.restUrl.replace( /\/$/, '' ) + path; 106 var body = JSON.stringify( payload ); 107 if ( useBeacon && navigator.sendBeacon ) { 108 var blob = new Blob( [ body ], { type: 'application/json' } ); 109 navigator.sendBeacon( url, blob ); 110 return; 111 } 112 fetch( url, { 113 method: 'POST', 114 headers: { 'Content-Type': 'application/json' }, 115 body: body, 116 keepalive: true, 117 credentials: 'same-origin' 118 } ).catch( function () {} ); 119 } 120 121 /* MINIMAL-REQUEST DESIGN: exactly ONE non-blocking request per pageview. 122 navigator.sendBeacon hands the request to the browser to send in the 123 background â it never delays the page or competes with rendering. There is 124 deliberately NO heartbeat, NO unload beacon and NO polling: those were the
125 source of the request flood. Time-on-page is still filled in server-side 126 from the next pageview, and "active now" means a pageview in the last few 127 minutes. */ 128 function beacon() { 129 if ( ! trackingActive() ) { 130 return; 131 } 132 // Keep the session token available to forms even on pageviews we don't 133 // record, so form submissions still link to a session. 134 runtime.token = getToken(); 135 runtime.active = true; 136 137 // Sampling: only a share of pageviews actually send a beacon. On a 138 // high-traffic cached site this is the biggest lever on origin load â 139 // e.g. 25 means one origin request for every four pageviews. 140 var rate = cfg.sampleRate || 100; 141 if ( rate < 100 && Math.random() * 100 >= rate ) { 142 return; 143 } 144 145 send( '/beacon', { 146 session: runtime.token, 147 consent: cfg.consentMode ? ( hasConsent() ? 1 : 0 ) : 1, 148 post_id: cfg.postId || 0, 149 url: location.href, 150 title: document.title, 151 referrer: document.referrer || '', 152 viewport: window.innerWidth + 'x' + window.innerHeight 153 }, true ); 154 } 155 156 // Expose a session token to forms even before the beacon fires. 157 if ( trackingActive() ) { 158 runtime.token = getToken(); 159 } 160 161 if ( document.readyState === 'loading' ) { 162 document.addEventListener( 'DOMContentLoaded', beacon ); 163 } else { 164 beacon(); 165 } 166} )();
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.