1/** 2 * GWS Monitor - Analytics Tracker v2.5.3 3 * Loaded as external JS so it works even with LiteSpeed/page cache. 4 * Expects gwsTrackerData = { trackUrl, siteUrl, contactPages } 5 */ 6(function(){ 7 if(typeof localStorage==='undefined'||typeof gwsTrackerData==='undefined')return; 8 var T=gwsTrackerData.trackUrl; 9 var S=gwsTrackerData.siteUrl; 10 var contactPages=gwsTrackerData.contactPages||[]; 11 var contactTrackUrl=gwsTrackerData.contactTrackUrl||''; 12 13 // --- Fingerprint --- 14 var ua=navigator.userAgent||''; 15 var fp=ua+navigator.language+(screen.width||0)+'x'+(screen.height||0) 16 +(navigator.platform||'')+(new Date().getTimezoneOffset()) 17 +(screen.colorDepth||0)+(navigator.hardwareConcurrency||0) 18 +(navigator.maxTouchPoints||0); 19 20 function djb2(s){var h=5381;for(var i=0;i<s.length;i++){h=((h<<5)+h)+s.charCodeAt(i);h=h&h;}return(h>>>0).toString(16).padStart(8,'0');} 21 var visitorHash=djb2(fp)+djb2(fp.split('').reverse().join('')); 22 23 // --- Session (localStorage, 30 min timeout) --- 24 var SESSION_TIMEOUT=30*60*1000; 25 var now=Date.now(); 26 var sidData=null; 27 try{sidData=JSON.parse(localStorage.getItem('gws_session'));}catch(e){} 28 var sid,isNew=false,pc=1; 29 if(sidData&&sidData.id&&(now-sidData.t)<SESSION_TIMEOUT){ 30 sid=sidData.id; 31 pc=(sidData.pc||0)+1; 32 sidData.t=now;sidData.pc=pc; 33 localStorage.setItem('gws_session',JSON.stringify(sidData)); 34 }else{ 35 sid='s'+Math.random().toString(36).substr(2,12)+now.toString(36); 36 isNew=true;pc=1; 37 localStorage.setItem('gws_session',JSON.stringify({id:sid,t:now,pc:1})); 38 } 39 40 // --- Device Detection --- 41 var dt='desktop'; 42 if(/Mobi|Android.*Mobile|iPhone|iPod/i.test(ua))dt='mobile'; 43 else if(/iPad|Android(?!.*Mobile)|Tablet/i.test(ua))dt='tablet'; 44 45 var br='',bv=''; 46 if(/Edg\//i.test(ua)){br='Edge';bv=(ua.match(/Edg\/(\d+)/)||[])[1]||'';} 47 else if(/OPR\//i.test(ua)){br='Opera';bv=(ua.match(/OPR\/(\d+)/)||[])[1]||'';} 48 else if(/Chrome\//i.test(ua)&&!/Edg|OPR/i.test(ua)){br='Chrome';bv=(ua.match(/Chrome\/(\d+)/)||[])[1]||'';} 49 else if(/Safari\//i.test(ua)&&!/Chrome/i.test(ua)){br='Safari';bv=(ua.match(/Version\/(\d+)/)||[])[1]||'';} 50 else if(/Firefox\//i.test(ua)){br='Firefox';bv=(ua.match(/Firefox\/(\d+)/)||[])[1]||'';} 51 else{br='Autre';} 52 53 var os='',ov=''; 54 if(/Windows/i.test(ua)){os='Windows';ov=(ua.match(/Windows NT (\d+\.\d+)/)||[])[1]||'';} 55 else if(/Mac OS X/i.test(ua)){os='macOS';ov=(ua.match(/Mac OS X (\d+[._]\d+)/)||[])[1]||'';ov=ov.replace(/_/g,'.');} 56 else if(/iPhone|iPad/i.test(ua)){os='iOS';ov=(ua.match(/OS (\d+_\d+)/)||[])[1]||'';ov=ov.replace(/_/g,'.');} 57 else if(/Android/i.test(ua)){os='Android';ov=(ua.match(/Android (\d+\.?\d*)/)||[])[1]||'';} 58 else if(/Linux/i.test(ua)){os='Linux';} 59 else if(/CrOS/i.test(ua)){os='Chrome OS';} 60 61 // --- Referrer --- 62 var ref=document.referrer||''; 63 var rt='direct'; 64 if(ref){ 65 try{ 66 var rh=new URL(ref).hostname; 67 if(rh&&S.indexOf(rh)===-1){ 68 if(/google|bing|yahoo|duckduckgo|ecosia|qwant|baidu|yandex/i.test(rh))rt='search'; 69 else if(/facebook|twitter|x\.com|instagram|linkedin|youtube|tiktok|pinterest|reddit|threads/i.test(rh))rt='social'; 70 else if(/mail|outlook|gmail/i.test(rh))rt='email'; 71 else rt='referral'; 72 } 73 }catch(e){} 74 } 75 76 // --- UTM --- 77 var utmS='',utmM='',utmC=''; 78 if(isNew){ 79 try{ 80 var p=new URLSearchParams(location.search); 81 utmS=p.get('utm_source')||'';utmM=p.get('utm_medium')||'';utmC=p.get('utm_campaign')||''; 82 if(utmS)localStorage.setItem('gws_utm_s',utmS); 83 if(utmM)localStorage.setItem('gws_utm_m',utmM); 84 if(utmC)localStorage.setItem('gws_utm_c',utmC); 85 if(utmM==='email')rt='email'; 86 }catch(e){} 87 }else{ 88 utmS=localStorage.getItem('gws_utm_s')||''; 89 utmM=localStorage.getItem('gws_utm_m')||''; 90 utmC=localStorage.getItem('gws_utm_c')||''; 91 } 92 93 // --- Scroll depth --- 94 var maxScroll=0; 95 var scrollTimer=null; 96 function updateScroll(){ 97 var s=window.scrollY||window.pageYOffset||0; 98 var wh=window.innerHeight||document.documentElement.clientHeight||0; 99 var dh=Math.max((document.body&&document.body.scrollHeight)||0,document.documentElement.scrollHeight||0); 100 if(dh>0){var pct=Math.round((s+wh)/dh*100);if(pct>maxScroll)maxScroll=Math.min(pct,100);} 101 } 102 window.addEventListener('scroll',function(){ 103 if(scrollTimer)return; 104 scrollTimer=setTimeout(function(){scrollTimer=null;updateScroll();},500); 105 }); 106 updateScroll(); 107 108 // --- Page info --- 109 var pageUrl=location.pathname+location.search; 110 var pageTitle=document.title||''; 111 if(pageTitle.length>200)pageTitle=pageTitle.substring(0,200); 112
113 // --- Send initial track (page load) --- 114 var initData={ 115 session_id:sid,visitor_hash:visitorHash, 116 page_url:pageUrl,page_title:pageTitle, 117 referrer:ref.substring(0,500),referrer_type:rt, 118 utm_source:utmS,utm_medium:utmM,utm_campaign:utmC, 119 device_type:dt,browser:br,browser_version:bv, 120 os:os,os_version:ov, 121 screen_width:screen.width||0,screen_height:screen.height||0, 122 language:navigator.language||'', 123 is_new_session:isNew,page_count:pc 124 }; 125 function sendData(url,data){ 126 try{ 127 var blob=new Blob([JSON.stringify(data)],{type:'application/json'}); 128 if(navigator.sendBeacon)navigator.sendBeacon(url,blob); 129 else{var x=new XMLHttpRequest();x.open('POST',url,true);x.setRequestHeader('Content-Type','application/json');x.send(JSON.stringify(data));} 130 }catch(e){} 131 } 132 sendData(T,initData); 133 134 // --- Send exit data (time on page + scroll depth) --- 135 var startTime=Date.now(); 136 var exitSent=false; 137 function sendExit(){ 138 if(exitSent)return;exitSent=true; 139 var t=Math.round((Date.now()-startTime)/1000); 140 if(t<1)t=1;if(t>1800)t=1800; 141 updateScroll(); 142 try{var sd=JSON.parse(localStorage.getItem('gws_session'));if(sd){sd.t=Date.now();localStorage.setItem('gws_session',JSON.stringify(sd));}}catch(e){} 143 sendData(T,{session_id:sid,page_url:pageUrl,time_on_page:t,scroll_depth:maxScroll,is_exit:true}); 144 } 145 window.addEventListener('beforeunload',sendExit); 146 document.addEventListener('visibilitychange',function(){if(document.visibilityState==='hidden')sendExit();}); 147 setTimeout(sendExit,300000); 148 149 // --- Contact page tracking (integrated, no separate script needed) --- 150 if(contactPages.length>0&&contactTrackUrl){ 151 var fullUrl=location.origin+location.pathname.replace(/\/$/,''); 152 var isContact=false; 153 for(var i=0;i<contactPages.length;i++){ 154 if(contactPages[i].replace(/\/$/,'')===fullUrl){isContact=true;break;} 155 } 156 if(isContact&&typeof sessionStorage!=='undefined'){ 157 var sessionKey='gws_visited_'+btoa(fullUrl).slice(0,20); 158 if(!sessionStorage.getItem(sessionKey)){ 159 sessionStorage.setItem(sessionKey,'1'); 160 var contactStart=Date.now(); 161 var contactSent=false; 162 function sendContact(){ 163 if(contactSent)return;contactSent=true; 164 var ts=Math.round((Date.now()-contactStart)/1000); 165 if(ts<1)ts=1;if(ts>1800)ts=1800; 166 sendData(contactTrackUrl,{page_url:fullUrl,time_seconds:ts}); 167 } 168 window.addEventListener('beforeunload',sendContact); 169 document.addEventListener('visibilitychange',function(){if(document.visibilityState==='hidden')sendContact();}); 170 setTimeout(sendContact,300000); 171 } 172 } 173 } 174})();
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.