1(function () { 2 'use strict'; 3 if (window.NOONAnalytics) return; 4 var script = document.currentScript; 5 var base = new URL('../', script ? script.src : location.origin + '/noon-analytics/assets/tracker.js'); 6 if (base.origin !== location.origin) return; 7 var endpoint = new URL('api/collect.php', base).href; 8 var preferencesOnly = !!(script && script.hasAttribute('data-preferences-only')); 9 var disabled = navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.globalPrivacyControl === true; 10 var current = null, timer = null, lastHeartbeat = 0, active = 0, lastTick = Date.now(), lastAction = Date.now(), maxScroll = 0, queue = Promise.resolve(); 11 12 function day() { return new Date(Date.now() + 9 * 3600000).toISOString().slice(0, 10); } 13 function id() { 14 if (!window.crypto || !crypto.getRandomValues) return ''; 15 var bytes = new Uint8Array(16); crypto.getRandomValues(bytes); 16 return Array.from(bytes, function (n) { return n.toString(16).padStart(2, '0'); }).join(''); 17 } 18 function path() { 19 var p = location.pathname; 20 if (p === '/bbs/board.php') { 21 var q = new URLSearchParams(location.search), board = q.get('bo_table') || ''; 22 if (!/^(project|digital_living|industrial_design|engineering|development|mold_design|graphic_design|packaing_design|notice)$/.test(board)) return ''; 23 p += '?bo_table=' + board; 24 if (/^[1-9][0-9]{0,9}$/.test(q.get('wr_id') || '')) p += '&wr_id=' + q.get('wr_id'); 25 } else if (/\/(?:adm|member|data|api|noon-analytics)\//.test(p) || /\/(?:login|register|write|password|memo|profile|search|download|link)[^/]*\.php$/.test(p)) return ''; 26 return p; 27 } 28 function campaign(name) { 29 var v = (new URLSearchParams(location.search)).get(name) || ''; 30 if (v.length > 80 || /@|https?:|\d{7,}|\d{2,4}[-. ]\d{3,4}[-. ]\d{4}/i.test(v)) return ''; 31 return v; 32 } 33 function attribution() { 34 var host = ''; try { host = document.referrer ? new URL(document.referrer).hostname : ''; } catch (_) {} 35 var ua = navigator.userAgent; 36 return {ref_domain:host,source:campaign('utm_source'),medium:campaign('utm_medium'),campaign:campaign('utm_campaign'),device:/iPad|Tablet|PlayBook|Silk/i.test(ua) || (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1) ? 'tablet' : /Mobi|Android|iPhone|iPod/i.test(ua) ? 'mobile' : 'desktop',browser:/Edg\//.test(ua) ? 'Edge' : /SamsungBrowser/.test(ua) ? 'Samsung' : /Firefox\//.test(ua) ? 'Firefox' : /Chrome\//.test(ua) ? 'Chrome' : /Safari\//.test(ua) ? 'Safari' : 'Other'}; 37 } 38 function usable() { return !disabled && !preferencesOnly && !!path(); } 39 function context() { 40 var date = day(); 41 if (!current || current.day !== date || current.path !== path()) { 42 // Ephemeral IDs live only in this page's JavaScript memory. No cookies, 43 // localStorage or sessionStorage identifiers are created. 44 var visit = id(); 45 current = {day:date,vid:visit,sid:visit,page:id(),path:path(),registered:false}; 46 active = 0; maxScroll = 0; 47 } 48 return current; 49 } 50 function tick() { var now = Date.now(); if (document.visibilityState === 'visible' && now - lastAction < 60000) active += Math.min(5000, Math.max(0,now - lastTick)); lastTick = now; } 51 function payload(ctx, type, extra) { return Object.assign({type:type,anonymous:true,day:ctx.day,vid:ctx.vid,sid:ctx.sid,page:ctx.page,path:ctx.path},extra || {}); } 52 async function post(body, beacon) { 53 if (!usable()) return false; 54 var text = JSON.stringify(body); 55 try { 56 if (beacon && navigator.sendBeacon) return navigator.sendBeacon(endpoint,new Blob([text],{type:'application/json'})); 57 var result = await fetch(endpoint,{method:'POST',credentials:'omit',headers:{'Content-Type':'application/json'},body:text,keepalive:true}); 58 if (!result.ok) return false; 59 var data = await result.json(); return !!data.ok && !data.ignored; 60 } catch (_) { return false; } 61 } 62 function send(type, extra, beacon) { 63 if (!usable()) return; 64 var ctx = context(); tick(); 65 var body = payload(ctx,type,extra); 66 if (type === 'heartbeat') { body.duration = Math.round(active / 1000); body.scroll = maxScroll; } 67 if (beacon && ctx.registered) { post(body,true); return; } 68 queue = queue.then(async function () { 69 if (!usable()) return; 70 if (!ctx.registered) ctx.registered = await post(payload(ctx,'page_view',attribution()),false); 71 if (ctx.registered && type !== 'page_view') await post(body,false); 72 }).catch(function () {}); 73 } 74 function event(name, beacon) { send('event',{event:name,event_id:id()},beacon); } 75 function start() { 76 if (!usable() || timer) return; 77 lastAction = Date.now(); lastTick = Date.now(); send('page_view'); 78 timer = setInterval(function () { tick(); if (document.visibilityState === 'visible' && Date.now() - lastAction < 60000 && Date.now() - lastHeartbeat >= 30000) { lastHeartbeat = Date.now(); send('heartbeat'); } },5000); 79 } 80 function stop() { clearInterval(timer); timer = null; current = null; } 81 function showSettings() { /* No consent banner: privacy-friendly aggregate analytics only. */ } 82 function setConsent() { /* Backward-compatible no-op. */ } 83 84 window.NOONAnalytics = {setConsent:setConsent,showSettings:showSettings,isAllowed:function(){return usable();},confirmed:function(kind){if(kind==='contact'||kind==='quote')event(kind+'_success_reported',true);}}; 85 document.addEventListener('noon:conversion-confirmed',function(e){var kind=e.detail&&e.detail.kind;if(kind==='contact'||kind==='quote')event(kind+'_success_reported',true);}); 86 ['pointerdown','keydown','scroll','touchstart'].forEach(function(name){document.addEventListener(name,function(){lastAction=Date.now();},{passive:true});}); 87 document.addEventListener('scroll',function(){var total=document.documentElement.scrollHeight-innerHeight;maxScroll=Math.max(maxScroll,total>0?Math.min(100,Math.round((scrollY||0)/total*100)):100);},{passive:true}); 88 document.addEventListener('visibilitychange',function(){tick();if(document.visibilityState==='hidden')send('heartbeat',null,true);else {lastAction=Date.now();lastTick=Date.now();}}); 89 addEventListener('pagehide',function(){send('heartbeat',null,true);}); 90 document.addEventListener('submit',function(e){var f=e.target;if(!f||f.tagName!=='FORM')return;var action=f.getAttribute('action')||'';if(location.pathname==='/sub02/sub01.php'||/write_update01\.php/.test(action))event('contact_intent',true);else if(location.pathname.indexOf('/development-consult/')===0||f.hasAttribute('data-noon-quote'))event('quote_intent',true);},true); 91 document.addEventListener('click',function(e){var a=e.target.closest?e.target.closest('a'):null;if(!a)return;var href=a.getAttribute('href')||'';if(/^tel:/i.test(href))event('phone_click',true);else if(/^mailto:/i.test(href))event('email_click',true);else if(/(?:\/sub02\/sub01\.php|\/development-consult\/)/.test(href))event('lead_cta_click',true);},true);
92 function init(){if(!preferencesOnly&&!path())return;if(disabled)stop();else if(!preferencesOnly)start();} 93 if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init,{once:true});else init(); 94}());
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.