1/** 2 * WP Power Analytics â Frontend Tracker v4 3 * 4 * Strategy for reliable duration on shared hosting: 5 * 1. Page load â POST to record hit, get back row_id 6 * 2. Every 5 seconds: update time_on_page IN MEMORY (no network) 7 * 3. Every 30 seconds OR on page hide: send heartbeat with current time 8 * 4. On unload/pagehide: send final time synchronously 9 * 10 * Uses active-time-only (pauses when tab is hidden). 11 */ 12(function () { 13 'use strict'; 14 15 if (!window.wpta_obj) return; 16 17 /* ââ Session ID ââ */ 18 function getSession() { 19 var KEY = 'wpta_sid'; 20 var sid = sessionStorage.getItem(KEY); 21 if (!sid) { 22 sid = 'ss_' + Math.random().toString(36).substr(2, 14) + '_' + Date.now(); 23 sessionStorage.setItem(KEY, sid); 24 } 25 return sid; 26 } 27 28 var SESSION = getSession(); 29 var ROW_ID = null; // set after initial hit confirms insert 30 var EXIT_SENT = false; 31 var HB_TIMER = null; 32 33 /* ââ Active-time accumulator (pauses when tab hidden) ââ */ 34 var activeMs = 0; 35 var activeStart = null; // null = not counting 36 37 function startTimer() { if (!activeStart) activeStart = Date.now(); } 38 function pauseTimer() { 39 if (activeStart) { 40 activeMs += Date.now() - activeStart; 41 activeStart = null; 42 } 43 } 44 function getSeconds() { 45 var ms = activeMs + (activeStart ? Date.now() - activeStart : 0); 46 return Math.max(1, Math.round(ms / 1000)); 47 } 48 49 /* ââ POST with JSON callback ââ */ 50 function post(params, cb) { 51 var fd = new FormData(); 52 Object.keys(params).forEach(function(k){ fd.append(k, params[k]); }); 53 var xhr = new XMLHttpRequest(); 54 xhr.open('POST', wpta_obj.ajax_url, true); 55 xhr.onload = function() { 56 if (cb) try { cb(JSON.parse(xhr.responseText)); } catch(e) {} 57 }; 58 xhr.send(fd); 59 } 60 61 /* ââ Reliable beacon (tries sendBeacon first, falls back to sync XHR) ââ */ 62 function sendBeaconData(params) { 63 var fd = new FormData(); 64 Object.keys(params).forEach(function(k){ fd.append(k, params[k]); }); 65 66 // Try sendBeacon (async, survives page close) 67 if (navigator.sendBeacon && navigator.sendBeacon(wpta_obj.ajax_url, fd)) { 68 return; 69 } 70 // Fallback: synchronous XHR (blocks until complete) 71 try { 72 var xhr = new XMLHttpRequest(); 73 xhr.open('POST', wpta_obj.ajax_url, false); 74 xhr.send(fd); 75 } catch(e) {} 76 } 77 78 /* ââ Heartbeat: update time_on_page for this row ââ */ 79 function sendHeartbeat() { 80 if (!ROW_ID || EXIT_SENT) return; 81 pauseTimer(); 82 var secs = getSeconds(); 83 startTimer(); 84 post({ 85 action: 'wpta_heartbeat', 86 nonce: wpta_obj.nonce, 87 row_id: ROW_ID, 88 time_on_page: secs, 89 }); 90 } 91 92 /* ââ Exit: final time update, one shot only ââ */ 93 function sendExit() { 94 if (!ROW_ID || EXIT_SENT) return; 95 EXIT_SENT = true; 96 pauseTimer(); 97 clearInterval(HB_TIMER); 98 sendBeaconData({ 99 action: 'wpta_exit', 100 nonce: wpta_obj.nonce, 101 row_id: ROW_ID, 102 time_on_page: getSeconds(), 103 }); 104 } 105 106 /* ââ Visibility change ââ */ 107 document.addEventListener('visibilitychange', function() { 108 if (document.hidden) { 109 sendHeartbeat(); // capture time before backgrounding 110 pauseTimer(); 111 } else { 112 startTimer(); // resume when user comes back 113 } 114 }); 115 116 /* ââ Unload / navigate away ââ */ 117 window.addEventListener('pagehide', sendExit); 118 window.addEventListener('beforeunload', sendExit); 119 120 /* ââ Click on any link: send heartbeat immediately before navigation ââ */ 121 document.addEventListener('click', function(e) { 122 var a = e.target.closest ? e.target.closest('a') : null; 123 if (!a) { 124 // IE/old: walk up 125 var t = e.target; 126 while (t && t.tagName !== 'A') t = t.parentElement; 127 a = t; 128 } 129 if (a && a.href && a.href.indexOf(window.location.hostname) > -1) { 130 sendHeartbeat(); 131 } 132 }, true); 133 134 /* ââ Initial page-view ââ */ 135 function init() { 136 startTimer(); 137 post({ 138 action: 'wpta_track', 139 nonce: wpta_obj.nonce, 140 session: SESSION, 141 url: window.location.href, 142 title: document.title, 143 referrer: document.referrer, 144 }, function(r) { 145 if (r && r.success && r.data && r.data.row_id) { 146 ROW_ID = r.data.row_id; 147 // Heartbeat every 30 seconds 148 HB_TIMER = setInterval(function() { 149 if (!document.hidden) sendHeartbeat(); 150 }, 30000); 151 } 152 }); 153 } 154 155 if (document.readyState === 'complete') { 156 init(); 157 } else { 158 window.addEventListener('load', init); 159 } 160 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.