1/** 2 * IMKBY Stats Tracker 3 * Î£Ï Î»Î»Îγει anonymous ÏÏαÏιÏÏικά εÏιÏκÎÏεÏν. 4 * Îεν ÏÏηÏιμοÏοιεί cookies. ΧÏηÏιμοÏοιεί localStorage για visitor_hash. 5 */ 6(function() { 7 'use strict'; 8 9 // Find own script tag to get collect endpoint 10 var script = document.currentScript; 11 if (!script) { 12 var scripts = document.getElementsByTagName('script'); 13 for (var i = scripts.length - 1; i >= 0; i--) { 14 if (scripts[i].src && scripts[i].src.indexOf('tracker.js') !== -1) { 15 script = scripts[i]; 16 break; 17 } 18 } 19 } 20 if (!script) return; 21 22 var collectUrl = script.getAttribute('data-collect'); 23 if (!collectUrl) { 24 // Default: same dir 25 collectUrl = script.src.replace(/tracker\.js.*/, 'collect.php'); 26 } 27 28 // Don't track admin / preview pages 29 if (location.pathname.indexOf('/administrator') !== -1) return; 30 if (location.search.indexOf('tmpl=component') !== -1) return; 31 32 // ===== Bot detection (basic) ===== 33 var ua = navigator.userAgent || ''; 34 var botPattern = /bot|crawl|spider|slurp|search|fetch|monitor|preview|headless|phantom|selenium|playwright|puppeteer|lighthouse|pagespeed|gtmetrix|pingdom/i; 35 var isBot = botPattern.test(ua); 36 37 // ===== Visitor identification ===== 38 // localStorage-based persistent visitor hash (per-browser) 39 var visitorHash; 40 try { 41 visitorHash = localStorage.getItem('_uss_v'); 42 if (!visitorHash) { 43 visitorHash = generateHash(); 44 localStorage.setItem('_uss_v', visitorHash); 45 } 46 } catch (e) { 47 visitorHash = generateHash(); 48 } 49 50 // sessionStorage-based session hash (per-tab session) 51 var sessionHash; 52 try { 53 sessionHash = sessionStorage.getItem('_uss_s'); 54 if (!sessionHash) { 55 sessionHash = generateHash(); 56 sessionStorage.setItem('_uss_s', sessionHash); 57 } 58 } catch (e) { 59 sessionHash = generateHash(); 60 } 61 62 function generateHash() { 63 var s = ''; 64 var chars = 'abcdef0123456789'; 65 for (var i = 0; i < 32; i++) { 66 s += chars[Math.floor(Math.random() * chars.length)]; 67 } 68 return s + Date.now().toString(16); 69 } 70 71 // ===== Browser & device detection ===== 72 function detectBrowser() { 73 var b = { name: 'Unknown', version: '' }; 74 if (/Edg\//.test(ua)) { b.name = 'Edge'; b.version = (ua.match(/Edg\/([\d.]+)/) || [])[1] || ''; } 75 else if (/OPR\//.test(ua)) { b.name = 'Opera'; b.version = (ua.match(/OPR\/([\d.]+)/) || [])[1] || ''; } 76 else if (/Chrome\//.test(ua)) { b.name = 'Chrome'; b.version = (ua.match(/Chrome\/([\d.]+)/) || [])[1] || ''; } 77 else if (/Safari\//.test(ua)) { b.name = 'Safari'; b.version = (ua.match(/Version\/([\d.]+)/) || [])[1] || ''; } 78 else if (/Firefox\//.test(ua)) { b.name = 'Firefox'; b.version = (ua.match(/Firefox\/([\d.]+)/) || [])[1] || ''; } 79 return b; 80 } 81 82 function detectOS() { 83 if (/Windows NT 10/.test(ua)) return 'Windows 10/11'; 84 if (/Windows NT 6\.3/.test(ua)) return 'Windows 8.1'; 85 if (/Windows NT 6\.2/.test(ua)) return 'Windows 8'; 86 if (/Windows NT 6\.1/.test(ua)) return 'Windows 7'; 87 if (/Windows/.test(ua)) return 'Windows'; 88 if (/iPhone|iPad/.test(ua)) return 'iOS'; 89 if (/Macintosh|Mac OS/.test(ua)) return 'macOS'; 90 if (/Android/.test(ua)) return 'Android'; 91 if (/Linux/.test(ua)) return 'Linux'; 92 return 'Unknown'; 93 } 94 95 function detectDevice() { 96 if (/Mobile|iPhone|iPod/.test(ua)) return 'mobile'; 97 if (/Tablet|iPad/.test(ua)) return 'tablet'; 98 return 'desktop'; 99 } 100 101 var browser = detectBrowser(); 102 103 // ===== Build payload ===== 104 var payload = { 105 visitor_hash: visitorHash, 106 session_hash: sessionHash, 107 url: location.href, 108 path: location.pathname, 109 query: location.search.substring(0, 500), 110 referrer: document.referrer.substring(0, 500), 111 user_agent: ua.substring(0, 500), 112 browser: browser.name, 113 browser_version: browser.version.substring(0, 30), 114 os: detectOS(), 115 device_type: detectDevice(), 116 screen_w: screen.width, 117 screen_h: screen.height, 118 viewport_w: window.innerWidth, 119 viewport_h: window.innerHeight, 120 language: navigator.language || '', 121 is_bot: isBot ? 1 : 0, 122 is_new_visitor: !localStorage.getItem('_uss_returning') ? 1 : 0, 123 timestamp: Date.now() 124 }; 125 126 // Mark as returning visitor for next time 127 try { localStorage.setItem('_uss_returning', '1'); } catch (e) {} 128 129 // ===== Send beacon ===== 130 function send(data) { 131 var json = JSON.stringify(data); 132 // Prefer sendBeacon (works on page unload too) 133 if (navigator.sendBeacon) { 134 try { 135 var blob = new Blob([json], { type: 'application/json' }); 136 if (navigator.sendBeacon(collectUrl, blob)) return; 137 } catch (e) {} 138 } 139 // Fallback to fetch (keepalive) 140 if (window.fetch) { 141 try { 142 fetch(collectUrl, { 143 method: 'POST', 144 headers: { 'Content-Type': 'application/json' }, 145 body: json, 146 keepalive: true, 147 credentials: 'omit' 148 }).catch(function() {}); 149 return; 150 } catch (e) {} 151 } 152 // Last resort: image pixel 153 try { 154 var img = new Image(); 155 img.src = collectUrl + '?p=' + encodeURIComponent(json); 156 } catch (e) {} 157 } 158 159 // ===== Track time on page ===== 160 var pageStart = Date.now(); 161 function sendFinal() { 162 payload.duration_ms = Date.now() - pageStart; 163 send(payload); 164 } 165 166 // Send initial pageview (without duration) 167 send(payload); 168 169 // Send final on unload (with duration) 170 window.addEventListener('beforeunload', sendFinal); 171 window.addEventListener('pagehide', sendFinal); 172 173 // Also send search query if this looks like a search page 174 var searchQuery = ''; 175 var params = new URLSearchParams(location.search);
176 ['searchword', 'q', 'search', 's', 'query'].forEach(function(key) { 177 if (params.get(key)) searchQuery = params.get(key); 178 }); 179 if (searchQuery) { 180 send({ 181 type: 'search', 182 query: searchQuery.substring(0, 200), 183 path: location.pathname, 184 visitor_hash: visitorHash, 185 session_hash: sessionHash 186 }); 187 } 188})();
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.