1// wpc_fp.js â lightweight, privacy-scoped device fingerprint for ANONYMOUS 2// tool-credit accounting. All signals stay on our own origin; nothing is sent 3// to any third party (no FingerprintJS / external service), so this is safe for 4// our EU traffic as a fraud-prevention legitimate interest. 5// 6// Why it exists: tool credits are the only gate, and an anon identity used to be 7// hash(IP + session-cookie) â clearing cookies / incognito minted a brand-new 8// identity and refilled the free credit. This fingerprint is DETERMINISTIC: if 9// the user wipes cookies AND localStorage it recomputes the SAME id from device 10// signals, so storage clearing can't refill credits. The server keys the anon 11// grant on sha256(IP + wpc_fp); only a genuinely different device/browser 12// changes it. Stable URL â a JS edit is a single-URL CDN purge. 13(function () { 14 var COOKIE = 'wpc_fp', LS = 'wpc_fp', MAXAGE = 60 * 60 * 24 * 365; 15 16 // cyrb53 â fast non-crypto hash; we only need a stable, well-distributed id 17 // from the concatenated signal string (collision risk between distinct device 18 // strings is negligible for this purpose). 19 function cyrb53(str, seed) { 20 seed = seed || 0; 21 var h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; 22 for (var i = 0, ch; i < str.length; i++) { 23 ch = str.charCodeAt(i); 24 h1 = Math.imul(h1 ^ ch, 2654435761); 25 h2 = Math.imul(h2 ^ ch, 1597334677); 26 } 27 h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); 28 h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); 29 h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); 30 h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); 31 return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36); 32 } 33 34 function signals() { 35 var s = []; 36 try { s.push(navigator.userAgent || ''); } catch (e) {} 37 try { s.push(navigator.language || ''); } catch (e) {} 38 try { s.push((navigator.languages || []).join(',')); } catch (e) {} 39 try { s.push(navigator.platform || ''); } catch (e) {} 40 try { s.push((navigator.hardwareConcurrency || '') + ''); } catch (e) {} 41 try { s.push((navigator.deviceMemory || '') + ''); } catch (e) {} 42 try { s.push(screen.width + 'x' + screen.height + 'x' + screen.colorDepth); } catch (e) {} 43 try { s.push('' + new Date().getTimezoneOffset()); } catch (e) {} 44 try { s.push(Intl.DateTimeFormat().resolvedOptions().timeZone || ''); } catch (e) {} 45 // canvas render â GPU/driver/font rasterisation differences 46 try { 47 var c = document.createElement('canvas'); c.width = 200; c.height = 40; 48 var x = c.getContext('2d'); 49 x.textBaseline = 'top'; x.font = "14px 'Arial'"; 50 x.fillStyle = '#f60'; x.fillRect(120, 1, 60, 20); 51 x.fillStyle = '#069'; x.fillText('wpc-fp-\u{1F600}', 2, 15); 52 s.push(c.toDataURL()); 53 } catch (e) {} 54 // WebGL vendor/renderer â distinguishes GPUs 55 try { 56 var gl = document.createElement('canvas').getContext('webgl') || 57 document.createElement('canvas').getContext('experimental-webgl'); 58 if (gl) { 59 var d = gl.getExtension('WEBGL_debug_renderer_info'); 60 if (d) { 61 s.push(gl.getParameter(d.UNMASKED_VENDOR_WEBGL)); 62 s.push(gl.getParameter(d.UNMASKED_RENDERER_WEBGL)); 63 } 64 } 65 } catch (e) {} 66 return s.join('||'); 67 } 68 69 function compute() { 70 var cached; 71 try { cached = localStorage.getItem(LS); } catch (e) {} 72 if (cached) return cached; 73 var fp = cyrb53(signals()); 74 try { localStorage.setItem(LS, fp); } catch (e) {} 75 return fp; 76 } 77 78 function setCookie(v) { 79 try { 80 var sec = location.protocol === 'https:' ? '; Secure' : ''; 81 document.cookie = COOKIE + '=' + v + '; Max-Age=' + MAXAGE + 82 '; Path=/; SameSite=Lax' + sec; 83 } catch (e) {} 84 } 85 86 try { 87 var fp = compute(); 88 window.wpcFP = fp; 89 // Always (re)assert the cookie so a cookie-only clear is repaired from the 90 // localStorage cache / recompute on the very next page view. 91 setCookie(fp); 92 } catch (e) { /* never block the page */ } 93})();
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.