1/** 2* A class to record page visits and duration. 3* Works with jQuery ajax methods. 4* config values: 5* - serverUrl [mandatory] : server side handler for log requests 6* - frequency [optional] : frequency of requests, given in number of seconds between requests 7* - maxRuntime [optional] : limits the number of requests by specifying a time limit in seconds 8* - pageId [optional] : a unique page id, so that the server is able to differentiate among various page requests 9* @author Slavic Dragovtev [[email protected]] 10*/ 11function PI_ActivityLogger(config){ 12 var default_frequency = 6000; 13 // make config object visible throughtout this class 14 this.config = config 15 if(!jQuery) throw 'ActivityLogger requires jQuery to run' 16 if(!config) throw 'ActivityLogger needs configuration' 17 if(!config.serverUrl) throw 'ActivityLogger needs serverUrl parameter' 18 if(!config.frequency){ 19 this.config.frequency = default_frequency 20 } else { 21 this.config.frequency = this.config.frequency * 1000 22 } 23 if (!this.config.pageId) { 24 this.config.pageId = null 25 } 26 // mark start of the app 27 var d=new Date(); 28 this.start = this.currentRun = d.getTime(); 29 // start the chain 30 if(this.shouldSend()) this.sendLogRequest(); 31 // setup corrections 32 var obj = this; 33 $(window).bind('unload',function(){ 34 obj.setCorrection(); 35 }) 36 this.secondarySent = false; // secondary requests sent, flag 37} 38 39/** 40* Will write to session storage (browser) any correction needed for 41* not-waiting for the next request, but rather navigating away to another page. 42*/ 43PI_ActivityLogger.prototype.setCorrection = function(){ 44 if(!this.shouldSend()) return ; 45 if(!this.config.pageId) return ; 46 var d=new Date(); 47 var remaining = d.getTime() - (this.currentRun + this.config.frequency) 48 // will only set a correction if the error is big enough 49 if(Math.abs(remaining) > 1000){ 50 sessionStorage.setItem('aLoggerCorrection', Math.round(remaining/1000)); 51 sessionStorage.setItem('aLoggerCorrectionPage', this.config.pageId); 52 } 53} 54 55/** 56* Will send an activity log request to the server 57* secondary means it is not a request from the main chain. It will still schedule 58* further self-requests if shouldSend() returns false 59*/ 60PI_ActivityLogger.prototype.sendLogRequest = function(secondary) { 61 // secondary requests are requests that are manually sent from special pages like search page 62 if(secondary && !this.secondarySent){ 63 // first request from secondary sources happens to be upon page load. We dont'need it. 64 this.secondarySent = true 65 return ; 66 } 67 if(secondary == undefined) secondary = false 68 // Only send the URI 69 var parsed = this.parse_url(window.location) 70 var toSend = {uri: parsed.relative}; 71 if(this.config.pageId) toSend.pageId = this.config.pageId 72 var correction = false; 73 // if a correction was sent from another page, include it 74 if(window.sessionStorage && sessionStorage.getItem('aLoggerCorrection')){ 75 correction = sessionStorage.getItem('aLoggerCorrection') 76 sessionStorage.removeItem('aLoggerCorrection') 77 } 78 if(correction){ 79 toSend.timeCorrection = correction 80 toSend.timeCorrectionPage = sessionStorage.getItem('aLoggerCorrectionPage') 81 sessionStorage.removeItem('aLoggerCorrectionPage') 82 } 83 var obj = this 84 $.ajax({ 85 url: this.config.serverUrl, 86 data: toSend, 87 success: function(response){ 88 if(response.status === 0){ 89 if(response.pageId){ 90 obj.config.pageId = response.pageId 91 var d=new Date(); 92 obj.start = obj.currentRun = d.getTime(); 93 } 94 } 95 } 96 }) 97 // if conditions meet, schedule another request 98 if(this.shouldSend() && !secondary || !this.shouldSend() && secondary) { 99 var obj = this 100 var d=new Date(); 101 this.currentRun = d.getTime(); 102 setTimeout(function(){ obj.sendLogRequest(); },this.config.frequency); 103 } 104} 105 106/** 107* returns whether the app should continue sennding requests 108*/
109PI_ActivityLogger.prototype.shouldSend = function(){ 110 if(!this.config.maxRuntime) return true; 111 if(this.config.maxRuntime<0) return false; 112 var d=new Date(); 113 var now = d.getTime(); 114 if(this.config.maxRuntime * 1000 <= now - this.start) return false; 115 return true; 116} 117 118// parseUri 1.2.2 119// (c) Steven Levithan <stevenlevithan.com> 120// MIT License 121PI_ActivityLogger.prototype.parse_url = function (str) { 122 var o = { 123 strictMode: false, 124 key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], 125 q: { 126 name: "queryKey", 127 parser: /(?:^|&)([^&=]*)=?([^&]*)/g 128 }, 129 parser: { 130 strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, 131 loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ 132 } 133 }, 134 m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), 135 uri = {}, 136 i = 14; 137 while (i--) uri[o.key[i]] = m[i] || ""; 138 uri[o.q.name] = {}; 139 uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { 140 if ($1) uri[o.q.name][$1] = $2; 141 }); 142 return uri; 143};
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.