// reformat geo: links based on platform const ua = String(platform.os).toLowerCase(); const android = ua.indexOf("android") > -1; const ios = ua.indexOf("ios") > -1; const windows = ua.indexOf("windows") > -1; const linux = ua.indexOf("linux") > -1; const mac = ua.indexOf("macos") > -1; var hashlinks = document.querySelectorAll('[href*="geo:"]'); var hasharray = [...hashlinks]; if ( hasharray.length > 0 ) { hasharray.forEach(hashlink => { const geolink = hashlink.getAttribute("href").match(/(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)/g); var latlon; if ( geolink.length > 1 && geolink[0] == '0,0') latlon = geolink[1]; else latlon = geolink[0]; if ( windows || mac || linux ) hashlink.href = "https://maps.google.com/?q=" + latlon; else if ( android ) hashlink.href = "geo:0,0?q=" + latlon; else if ( ios ) hashlink.href = "http://maps.apple.com/?ll=" + latlon + "&q=" + hashlink.innerHTML; }); } // fingerprint user function cyrb53(str, seed = 0){ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; for (let i = 0, ch; i < str.length; i++) { ch = str.charCodeAt(i); h1 = Math.imul(h1 ^ ch, 2654435761); h2 = Math.imul(h2 ^ ch, 1597334677); } h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909); h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); return 4294967296 * (2097151 & h2) + (h1 >>> 0); }; function audioFingerprint() { const AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContex; const context = new AudioContext(1, 5000, 44100); const oscillator = context.createOscillator(); oscillator.type = "triangle"; oscillator.frequency.value = 1000; const compressor = context.createDynamicsCompressor() compressor.threshold.value = -50; compressor.knee.value = 40; compressor.ratio.value = 12; compressor.reduction.value = 20; compressor.attack.value = 0; compressor.release.value = 0.2; oscillator.connect(compressor); compressor.connect(context.destination); oscillator.start() context.oncomplete = event => { const samples = event.renderedBuffer.getChannelData(0); let hash = 0; for (let i = 0; i < samples.length; ++i) { hash += Math.abs(samples[i]); } return hash; }; context.startRendering(); } function canvasFingerprint() { var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var txt = 'Numinous Field Communication'; ctx.textBaseline = "top"; ctx.font = "14px 'Arial'"; ctx.textBaseline = "alphabetic"; ctx.fillStyle = "#f60"; ctx.fillRect(125, 1, 62, 20); ctx.fillStyle = "#069"; ctx.fillText(txt, 2, 15); ctx.fillStyle = "rgba(102, 204, 0, 0.7)"; ctx.fillText(txt, 4, 17); src = canvas.toDataURL(); var hash; for (i = 0; i < src.length; i++) { char = src.charCodeAt(i); hash = ((hash<<5)-hash)+char; hash = hash & hash; } return hash; }; function getFingerprint() { const audioFp = audioFingerprint(); const canvasFp = canvasFingerprint(); const hash = cyrb53(audioFp + canvasFp); return hash; }; const generateUUID = () => { let d = new Date().getTime(), d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0; return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { let r = Math.random() * 16; if (d > 0) { r = (d + r) % 16 | 0; d = Math.floor(d / 16); } else { r = (d2 + r) % 16 | 0; d2 = Math.floor(d2 / 16); } return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16); }); }; if ( !Cookies.get('nfcexplorer') ) { Cookies.set('nfcexplorer', generateUUID()) } fetch('https://nmns.place/track?' + new URLSearchParams({ hash: Cookies.get('nfcexplorer'), ua: ua, }));