1/*! 2 * Pulsetic RUM tracker 3 * Collects navigation timing + Core Web Vitals from real visitors. Sends 4 * lightweight beacons per pageview to the Pulsetic API: navigation timings 5 * once the page has loaded (powers realtime), final Core Web Vitals when the 6 * visitor leaves. Only the pageview beacon counts against the quota. 7 * 8 * Usage: 9 * <script async src="https://cdn.pulsetic.com/rum.js" data-pulsetic-rum="SITE_HASH"></script> 10 */ 11(function(){'use strict' 12if(window!==window.top||document.prerendering){return} 13var script=document.currentScript||document.querySelector('script[data-pulsetic-rum]') 14if(!script){return} 15var hash=script.getAttribute('data-pulsetic-rum') 16if(!hash){return} 17var base=script.getAttribute('data-endpoint')||'https://api.pulsetic.com' 18var endpoint=base+'/rum/beacon/'+hash 19var errorEndpoint=base+'/rum/error/'+hash 20var nativeFetch=window.fetch&&window.fetch.bind(window) 21var pageviewSent=!1 22var vitalsSent=!1 23var vitals={lcp:-1,cls:-1,inp:-1,fcp:-1} 24function post(url,body){try{if(navigator.sendBeacon){navigator.sendBeacon(url,new Blob([body],{type:'text/plain'}))}else if(nativeFetch){nativeFetch(url,{method:'POST',body:body,headers:{'Content-Type':'text/plain'},keepalive:!0}).catch(function(){})}}catch(e){}} 25var clsSessionValue=0 26var clsSessionFirstTs=0 27var clsSessionLastTs=0 28function observe(type,callback,options){try{var observer=new PerformanceObserver(function(list){list.getEntries().forEach(callback)}) 29observer.observe(Object.assign({type:type,buffered:!0},options||{})) 30return observer}catch(e){return null}} 31observe('largest-contentful-paint',function(entry){vitals.lcp=Math.round(entry.renderTime||entry.loadTime||entry.startTime)}) 32observe('paint',function(entry){if(entry.name==='first-contentful-paint'){vitals.fcp=Math.round(entry.startTime)}}) 33observe('layout-shift',function(entry){if(entry.hadRecentInput){return} 34if(clsSessionValue&&entry.startTime-clsSessionLastTs<1000&&entry.startTime-clsSessionFirstTs<5000){clsSessionValue+=entry.value}else{clsSessionValue=entry.value 35clsSessionFirstTs=entry.startTime} 36clsSessionLastTs=entry.startTime 37if(vitals.cls<0||clsSessionValue>vitals.cls){vitals.cls=clsSessionValue}}) 38observe('event',function(entry){if(entry.interactionId&&Math.round(entry.duration)>vitals.inp){vitals.inp=Math.round(entry.duration)}},{durationThreshold:40}) 39function ms(value){return value>0?Math.round(value):0} 40function timings(){var nav=(performance.getEntriesByType&&performance.getEntriesByType('navigation')) 41nav=nav&&nav[0] 42if(!nav){return null} 43var tls=nav.secureConnectionStart>0?nav.connectEnd-nav.secureConnectionStart:0 44var tcpEnd=nav.secureConnectionStart>0?nav.secureConnectionStart:nav.connectEnd 45return{dns:ms(nav.domainLookupEnd-nav.domainLookupStart),tcp:ms(tcpEnd-nav.connectStart),tls:ms(tls),ttfb:ms(nav.responseStart-nav.startTime),di:ms(nav.domInteractive-nav.startTime),dcl:ms(nav.domContentLoadedEventEnd-nav.startTime),load:ms(nav.loadEventEnd-nav.startTime)}} 46function navType(){var nav=(performance.getEntriesByType&&performance.getEntriesByType('navigation')) 47return(nav&&nav[0]&&nav[0].type)||'navigate'} 48function vitalsPayload(){return{lcp:vitals.lcp,cls:vitals.cls>=0?Math.round(vitals.cls*1000)/1000:-1,inp:vitals.inp,fcp:vitals.fcp}} 49function sendPageview(includeVitals){if(pageviewSent){return} 50pageviewSent=!0 51if(includeVitals){vitalsSent=!0} 52var connection=navigator.connection||{} 53post(endpoint,JSON.stringify({e:'pageview',u:window.location.href,r:document.referrer||'',w:(window.screen&&window.screen.width)||0,h:(window.screen&&window.screen.height)||0,ct:connection.effectiveType||'',nt:navType(),t:timings()||{},v:includeVitals?vitalsPayload():{lcp:-1,cls:-1,inp:-1,fcp:-1}}))} 54function sendVitals(){if(!pageviewSent||vitalsSent){return} 55vitalsSent=!0 56post(endpoint,JSON.stringify({e:'vitals',u:window.location.href,v:vitalsPayload()}))} 57function onLeave(){if(!pageviewSent){sendPageview(!0)}else{sendVitals()}} 58function onLoaded(){setTimeout(function(){sendPageview(!1)},60)}
59if(document.readyState==='complete'){onLoaded()}else{window.addEventListener('load',onLoaded)} 60document.addEventListener('visibilitychange',function(){if(document.visibilityState==='hidden'){onLeave()}}) 61window.addEventListener('pagehide',onLeave) 62var MAX_ERRORS=10 63var errCount=0 64var errSeen={} 65function trunc(value,max){value=value==null?'':String(value) 66return value.length>max?value.slice(0,max):value} 67function sendError(payload,dedupKey){try{if(errCount>=MAX_ERRORS||errSeen[dedupKey]){return} 68errSeen[dedupKey]=!0 69errCount++ 70payload.u=window.location.href 71post(errorEndpoint,JSON.stringify(payload))}catch(e){}} 72function jsError(message,filename,lineno,colno,stack){sendError({k:'js',m:trunc(message,1000),f:trunc(filename,1024),l:lineno||0,c:colno||0,s:trunc(stack,4000)},'js|'+message+'|'+filename+'|'+lineno)} 73window.addEventListener('error',function(event){if(!event.message&&!event.error){return} 74var err=event.error 75jsError(event.message,event.filename,event.lineno,event.colno,err&&err.stack)}) 76window.addEventListener('unhandledrejection',function(event){var reason=event.reason 77var message=reason&&reason.message?reason.message:String(reason) 78jsError('Unhandled rejection: '+message,'',0,0,reason&&reason.stack)}) 79function httpError(method,url,status){try{if(!url||String(url).indexOf(base)===0){return} 80var a=document.createElement('a') 81a.href=url 82var clean=a.protocol+'//'+a.host+a.pathname 83sendError({k:'http',st:status||0,hm:trunc(method||'GET',10).toUpperCase(),ru:trunc(clean,1024)},'http|'+method+'|'+status+'|'+a.host+a.pathname)}catch(e){}} 84if(nativeFetch){window.fetch=function(){var args=arguments 85var input=args[0] 86var url=(input&&input.url)?input.url:String(input)
87var method=(args[1]&&args[1].method)||(input&&input.method)||'GET' 88var promise=nativeFetch.apply(this,args) 89try{return promise.then(function(res){if(res&&res.type!=='opaque'&&res.status>=400){httpError(method,url,res.status)} 90return res},function(err){httpError(method,url,0) 91throw err})}catch(e){return promise}}} 92if(window.XMLHttpRequest&&XMLHttpRequest.prototype){var xhrOpen=XMLHttpRequest.prototype.open 93XMLHttpRequest.prototype.open=function(method,url){try{this.__rumMethod=method 94this.__rumUrl=url 95this.addEventListener('loadend',function(){if(this.status===0||this.status>=400){httpError(this.__rumMethod,this.__rumUrl,this.status)}})}catch(e){} 96return xhrOpen.apply(this,arguments)}}})()
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.