1// 2// Making a change to this file? Be sure to apply the changes to the WPCOM 3// stats JS file (w.js) as well! 4// 5 6window.wpcom = window.wpcom || {}; 7window._stq = window._stq || []; 8 9function st_go( a ) { 10 window._stq.push([ 'view', a ]); 11}; 12 13function linktracker_init( b, p ) { 14 window._stq.push([ 'clickTrackerInit', b, p ]); 15}; 16 17window.wpcom.stats = (function() { 18 var _clickTracker = (function() { 19 var _blog, _post; 20 21 var _addEvent = function( el, t, cb ) { 22 if ( 'function' === typeof el.addEventListener ) { 23 el.addEventListener( t, cb ); 24 } else if ( 'object' === typeof el.attachEvent ) { 25 el.attachEvent( 'on' + t, cb ); 26 } 27 }; 28 29 var _getClickTarget = function( e ) { 30 if ( 'object' === typeof e && e.target ) { 31 return e.target; 32 } else { 33 return window.event.srcElement; 34 } 35 }; 36 37 var _clickTrack = function( e ) { 38 var d = 0; 39 if ( 'object' === typeof InstallTrigger ) d = 100; // Firefox 40 if ( 7 === _getIEVer() ) d = 100; // IE 7 with empty cache needs to wait 41 _processLink( _getClickTarget( e ), d ); 42 }; 43 44 var _contextTrack = function( e ) { 45 _processLink( _getClickTarget( e ), 0 ); 46 }; 47 48 var _isSameHost = function( a ) { 49 var l = document.location; 50 if ( l.host === a.host ) return true; 51 // Oy, IE9! Sometimes host is blank 52 if ( '' === a.host ) return true; 53 // Oy, IE6/7/8! We need to handle default ports manually 54 if ( l.protocol === a.protocol && l.host === a.hostname ) { 55 if ( 'http:' === l.protocol && l.host + ':80' === a.host ) return true; 56 if ( 'https:' === l.protocol && l.host + ':443' === a.host ) return true; 57 }; 58 return false; 59 }; 60 61 var _processLink = function( a, d ) { 62 try { 63 if ( 'object' !== typeof a ) return; 64 while ( 'A' !== a.nodeName ) { 65 if ( 'undefined' === typeof a.nodeName ) return; 66 if ( 'object' !== typeof a.parentNode ) return; 67 a = a.parentNode; 68 }; 69 if ( _isSameHost( a ) ) return; 70 if ( 'javascript:' === a.protocol ) return; 71 window._stq.push([ 'click', { 72 s: '2', 73 u: a.href, 74 r: ( 'undefined' !== typeof a.rel ) ? a.rel : '0', 75 b: ( 'undefined' !== typeof _blog ) ? _blog : '0', 76 p: ( 'undefined' !== typeof _post ) ? _post : '0' 77 } ]); 78 if ( d ) { 79 var now = new Date(); 80 var end = now.getTime() + d; 81 while ( true ) { 82 now = new Date(); 83 if ( now.getTime() > end ) { 84 break 85 } 86 } 87 } 88 } catch ( e ) {} 89 }; 90 91 var API = { 92 init: function( b, p ) { 93 _blog = b; 94 _post = p; 95 if ( document.body ) { 96 _addEvent( document.body, 'click', _clickTrack ); 97 _addEvent( document.body, 'contextmenu', _contextTrack ); 98 } else if ( document ) { 99 _addEvent( document, 'click', _clickTrack ); 100 _addEvent( document, 'contextmenu', _contextTrack ); 101 } 102 } 103 }; 104 105 return API; 106 })(); 107 108 var _getIEVer = function() { 109 var v = 0; 110 if ( 'object' === typeof navigator && navigator.appName == 'Microsoft Internet Explorer' ) { 111 var m = navigator.userAgent.match( /MSIE ([0-9]{1,})[\.0-9]{0,}/ ); 112 if ( null !== m ) { 113 v = parseInt( m[1] ); 114 } 115 }; 116 return v; 117 }; 118 119 var _serialize = function( o ) { 120 var p, q = []; 121 for ( p in o ) { 122 if ( o.hasOwnProperty( p ) ) { 123 q.push( encodeURIComponent( p ) + '=' + encodeURIComponent( o[ p ] ) ); 124 } 125 }; 126 return q.join( '&' ); 127 }; 128 129 var _loadGif = function( t, q, id ) { 130 var i = new Image(); 131 i.src = document.location.protocol + 'http://pixel.wp.com/' + t + '?' + q + '&rand=' + Math.random(); 132 i.alt = ""; 133 i.width = '6'; 134 i.height = '5'; 135 if ( 'string' === typeof id && document.body ) { 136 i.id = id; 137 document.body.appendChild( i ); 138 } 139 }; 140 141 // try to fetch paint and network timings using the 142 // various performance APIs 143 var _computePerformance = function( o ) { 144 var conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection; 145 146 if ( conn ) { 147 // 'slow-sg', '2g', '3g', or '4g' 148 if ( conn.effectiveType ) { o.conn_type = conn.effectiveType; } 149 if ( conn.rtt ) { o.conn_rtt = conn.rtt; } 150 if ( conn.downlink ) { o.conn_downlink = conn.downlink; } 151 } 152 153 if ( window.performance ) { 154 var performance = window.performance; 155 156 if ( window.PerformanceNavigationTiming ) { 157 var navigationTiming = performance.getEntriesByType('navigation')[0];
158 if ( navigationTiming.nextHopProtocol ) { 159 o.protocol = navigationTiming.nextHopProtocol; 160 } 161 } 162 163 // response start time, only if we're loading or reloading (not hitting back button, for example) 164 // see: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming 165 if ( performance.timing && performance.navigation && ( performance.navigation.type === 0 || performance.navigation.type === 1 ) ) { 166 // these are about how long the page took to download 167 var t = performance.timing; 168 169 // these are about network conditions 170 o.dns_latency = Math.round( t.domainLookupEnd - t.domainLookupStart ); 171 o.conn_latency = Math.round( t.connectEnd - t.connectStart ); 172 o.resp_latency = Math.round( t.responseStart - t.requestStart ); 173 o.resp_duration = Math.round( t.responseEnd - t.responseStart ); 174 175 // these are about how long the page took to process 176 o.dom_interact = Math.round( t.domInteractive - t.navigationStart ); 177 o.dom_load = Math.round( t.domContentLoadedEventStart - t.navigationStart ); 178 179 if ( t.loadEventStart > 0 ) { 180 o.page_load = Math.round( t.loadEventStart - t.navigationStart ); 181 } 182 } 183 184 // resource types, protocols and duration 185 var resources = performance.getEntriesByType('resource'); 186 187 if ( resources.length > 0 ) { 188 var cssFiles = 0, jsFiles = 0, imgFiles = 0, fontFiles = 0, otherFiles = 0, 189 cssDuration = 0, jsDuration = 0, imgDuration = 0, fontDuration = 0, otherDuration = 0, 190 http1Files = 0, http2Files = 0, sslFiles = 0, 191 originFiles = 0, externalFiles = 0; 192 193 for( var i = 0; i < resources.length; i++ ) { 194 var resource = resources[i]; 195 196 // figure out how many bytes were transferred over http 1/2 and with/without SSL 197 if ( resource.nextHopProtocol ) { 198 if ( resource.nextHopProtocol.startsWith( 'http/1' ) ) { 199 http1Files += 1; 200 } else if ( 'h2' === resource.nextHopProtocol ) { 201 http2Files += 1; 202 } 203 204 if ( resource.name.startsWith( 'https' ) ) { 205 sslFiles += 1; 206 } 207 } else { 208 http1Files += 1; 209 if ( resource.name.startsWith( 'https' ) ) { 210 sslFiles += 1; 211 } 212 } 213 214 // are files loaded from origin, or remote? 215 if ( resource.name.indexOf( location.hostname ) >= 0 ) { 216 originFiles += 1; 217 } else { 218 externalFiles += 1; 219 } 220 221 var extension; 222 223 // special, common case 224 if( resource.name.indexOf( 'fonts.googleapis.com/css' ) >= 0 ) { 225 extension = 'css'; 226 } else { 227 extension = resource.name.split(/\#|\?/)[0].split('.').pop(); 228 } 229 230 if ( extension ) { 231 extension = extension.toLowerCase(); 232 // figure out file types number and duration 233 if ( 'js' === extension ) { 234 jsDuration += resource.duration; 235 jsFiles += 1; 236 } else if ( 'css' === extension ) { 237 cssDuration += resource.duration; 238 cssFiles += 1; 239 } else if ( 'gif' === extension || 'jpg' === extension || 'jpeg' === extension || 'png' === extension ) { 240 imgDuration += resource.duration; 241 imgFiles += 1; 242 } else if ( 'woff' === extension || 'woff2' === extension || 'ttf' === extension || 'otf' === extension ) { 243 fontDuration += resource.duration; 244 fontFiles += 1; 245 } else { 246 otherDuration += resource.duration; 247 otherFiles += 1; 248 } 249 } else { 250 otherDuration += resource.duration; 251 otherFiles += 1; 252 } 253 } 254 255 o.files_origin = originFiles; 256 o.files_ext = externalFiles; 257 o.files_ssl = sslFiles; 258 o.files_http1 = http1Files; 259 o.files_http2 = http2Files; 260 o.files_js = jsFiles; 261 o.files_css = cssFiles; 262 o.files_img = imgFiles; 263 o.files_font = fontFiles; 264 o.files_other = otherFiles; 265 o.duration_js = Math.round( jsDuration ); 266 o.duration_css = Math.round( cssDuration ); 267 o.duration_img = Math.round( imgDuration ); 268 o.duration_font = Math.round( fontDuration ); 269 o.duration_other = Math.round( otherDuration ); 270 } 271 272 // how long did it take for content to appear on the screen? 273 // see: https://developer.mozilla.org/en-US/docs/Web/API/PerformancePaintTiming 274 var paintEntries = performance.getEntriesByType('paint'); 275 276 if ( paintEntries === undefined ) { 277 return; 278 } 279 280 for( var i = 0; i < paintEntries.length; i++ ) { 281 var performanceEntry = paintEntries[i]; 282 if ( 'first-paint' === performanceEntry.name ) { 283 o.first_paint = Math.round( performanceEntry.startTime ); 284 } else if ( 'first-contentful-paint' === performanceEntry.name ) { 285 o.first_cf_paint = Math.round( performanceEntry.startTime ); 286 } 287 } 288 } 289 }; 290 291 // <3 KM 292 var STQ = function( q ) { 293 this.a = 1; 294 if ( q && q.length ) { 295 for ( var i = 0; i < q.length; i++ ) { 296 this.push( q[ i ] ); 297 } 298 } 299 }; 300 301 STQ.prototype.push = function( args ) { 302 if ( args ) { 303 if ( "object" === typeof args && args.length ) { 304 var cmd = args.splice( 0, 1 ); 305 if ( API[ cmd ] ) API[ cmd ].apply( null, args ); 306 } else if ( "function" === typeof args ) { 307 args(); 308 } 309 } 310 }; 311 312 var initQueue = function() { 313 if ( !window._stq.a ) { 314 window._stq = new STQ( window._stq ); 315 } 316 }; 317 318 var newAnonId = function() { 319 var randomBytesLength = 18, // 18 * 4/3 = 24 (base64 encoded chars) 320 randomBytes = []; 321 322 if ( window.crypto && window.crypto.getRandomValues ) { 323 randomBytes = new Uint8Array( randomBytesLength ); 324 window.crypto.getRandomValues( randomBytes ); 325 } else { 326 for ( var i = 0; i < randomBytesLength; ++i ) { 327 randomBytes[ i ] = Math.floor( Math.random() * 256 ); 328 } 329 } 330 331 return btoa( String.fromCharCode.apply( String, randomBytes ) ); 332 }; 333 334 var _initTracks = function( o ) { 335 o._ui = newAnonId(); 336 o._ut = 'anon'; 337 o._en = 'jetpack_pageview_timing'; 338 var date = new Date(); 339 o._ts = date.getTime(); 340 o._tz = date.getTimezoneOffset() / 60; 341 342 var nav = window.navigator; 343 var screen = window.screen; 344 o._lg = nav.language; 345 o._pf = nav.platform; 346 o._ht = screen.height; 347 o._wd = screen.width; 348 var sx = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; 349 var sy = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; 350 351 o._sx = (sx !== undefined) ? sx : 0; 352 o._sy = (sy !== undefined) ? sy : 0; 353 354 if( document.location !== undefined ) { 355 o._dl = document.location.toString(); 356 } 357 358 if( document.referrer !== undefined ) { 359 o._dr = document.referrer; 360 } 361 }; 362 363 var API = { 364 view: function( o ) { 365 o.host = document.location.host; 366 o.ref = document.referrer; 367 o.fcp = getFirstContentfulPaint(); 368 _loadGif( 'g.gif', _serialize( o ), 'wpstats' ); 369 // run performance analytics after load event has finished processing 370 if ( window.performance && Math.random() < 0.005 ) { 371 window.addEventListener( 'load', function(event) { 372 window.setTimeout( API.samplePerformance.bind( this, o.blog, o.post, o.j.split(':').reverse()[0] ), 100 ); 373 } ); 374 } 375 }, 376 click: function( o ) { 377 _loadGif( 'c.gif', _serialize( o ), false ); 378 }, 379 clickTrackerInit: function( b, p ) { 380 _clickTracker.init( b, p ); 381 }, 382 samplePerformance: function( blogId, postId, jetpackVersion ) { 383 if ( ! window.performance ) { 384 return; 385 } 386 387 var o = { blog: blogId, post: postId, blog_id: blogId, jetpack_version: jetpackVersion }; 388 _initTracks( o ); 389 _computePerformance( o ); 390 _loadGif( 't.gif', _serialize( o ) ); 391 } 392 }; 393 394 var isDocumentHidden = function() { 395 return typeof document.hidden !== "undefined" && document.hidden; 396 }; 397 398 var onDocumentVisibilityChange = function() { 399 // no need to use `isDocumentHidden()` since this is only called
399if the Page Visibility API is supported 400 if ( !document.hidden ) { 401 // we only want to do this the first time the document becomes visible 402 document.removeEventListener( 'visibilitychange', onDocumentVisibilityChange ); 403 initQueue(); 404 } 405 }; 406 407 var initQueueAfterDocumentIsVisible = function() { 408 document.addEventListener( 'visibilitychange', onDocumentVisibilityChange ); 409 }; 410 411 function getFirstContentfulPaint() { 412 if ( window.performance ) { 413 var paints = window.performance.getEntriesByType( 'paint' ); 414 for ( var i = 0; i < paints.length; i++ ) { 415 if ( paints[i]['name'] === 'first-contentful-paint' ) { 416 return Math.round( paints[i]['startTime'] ); 417 } 418 } 419 } 420 421 // If we are still here then return a default value of zero. The 422 // stats parser will take that as an invalid value and skip it. 423 return 0; 424 } 425 426 // Wait for document ready +250ms before init for IE6 only otherwise stats pixels may get sent twice 427 if ( 6 === _getIEVer() && 'complete' !== document.readyState && 'object' === typeof document.attachEvent ) { 428 document.attachEvent( 'onreadystatechange', function( e ) { 429 if ( 'complete' === document.readyState ) window.setTimeout( initQueue, 250 ); 430 } ); 431 } else { 432 // only init stats queue once document is visible; 433 // this prevents miscounting stats when a page is prerendered 434 // or if opened in a new hidden tab and never actually viewed 435 if ( isDocumentHidden() ) { 436 initQueueAfterDocumentIsVisible(); 437 } else { 438 initQueue(); 439 } 440 }; 441 442 return API; 443})();
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.