From 63e0024daa972d670d051b36ceff1f2f8743cdb0 Mon Sep 17 00:00:00 2001 From: Nino Dafonte Date: Sat, 6 Aug 2011 15:09:00 +0200 Subject: [PATCH] Refactoring sendBeacon function for having the option for customizing formatResponse inside plugins. This open the posibilities for modifying the output format and adapt it to the required response. Signed-off-by: Nino Dafonte --- boomerang.js | 216 +++++++++++++++++++++++++++------------------------ statsplus.js | 124 +++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 101 deletions(-) create mode 100644 statsplus.js diff --git a/boomerang.js b/boomerang.js index e4e7e261e..40cb41e5c 100644 --- a/boomerang.js +++ b/boomerang.js @@ -95,7 +95,7 @@ boomr = { } name = ' ' + name + '='; - + var i, cookies; cookies = ' ' + d.cookie + ';'; if ( (i=cookies.indexOf(name)) >= 0 ) { @@ -103,19 +103,19 @@ boomr = { cookies = cookies.substring(i, cookies.indexOf(';', i)); return cookies; } - + return null; }, - + setCookie: function(name, subcookies, max_age, path, domain, sec) { var value = "", - k, nameval, c, - exp = ""; + k, nameval, c, + exp = ""; if(!name) { return false; } - + for(k in subcookies) { if(subcookies.hasOwnProperty(k)) { value += '&' + encodeURIComponent(k) @@ -123,13 +123,13 @@ boomr = { } } value = value.replace(/^&/, ''); - + if(max_age) { exp = new Date(); exp.setTime(exp.getTime() + max_age*1000); exp = exp.toGMTString(); } - + nameval = name + '=' + value; c = nameval + ((max_age) ? "; expires=" + exp : "" ) + @@ -137,44 +137,44 @@ boomr = { ((typeof domain !== "undefined") ? "; domain=" + (domain !== null ? domain : impl.site_domain ) : "") + ((sec) ? "; secure" : ""); - + if ( nameval.length < 4000 ) { d.cookie = c; // confirm cookie was set (could be blocked by user's settings, etc.) return ( value === this.getCookie(name) ); } - + return false; }, - + getSubCookies: function(cookie) { var cookies_a, - i, l, kv, - cookies={}; + i, l, kv, + cookies={}; if(!cookie) { return null; } - + cookies_a = cookie.split('&'); - + if(cookies_a.length === 0) { return null; } - + for(i=0, l=cookies_a.length; i 3) { @@ -926,10 +940,10 @@ var impl = { bandwidths = bandwidths.sort(this.ncmp); bandwidths_corrected = bandwidths_corrected.sort(this.ncmp); } - + BOOMR.debug('after iqr: ' + bandwidths, "bw"); BOOMR.debug('corrected: ' + bandwidths_corrected, "bw"); - + // Now get the mean & median. // Also get corrected values that eliminate latency n = Math.max(bandwidths.length, bandwidths_corrected.length); @@ -943,24 +957,24 @@ var impl = { sumsq_corrected += Math.pow(bandwidths_corrected[i], 2); } } - + n = bandwidths.length; amean = Math.round(sum/n); std_dev = Math.sqrt(sumsq/n - Math.pow(sum/n, 2)); std_err = Math.round(1.96 * std_dev/Math.sqrt(n)); std_dev = Math.round(std_dev); - + n = bandwidths.length-1; median = Math.round( (bandwidths[Math.floor(n/2)] + bandwidths[Math.ceil(n/2)]) / 2 ); - + n = bandwidths_corrected.length; amean_corrected = Math.round(sum_corrected/n); std_dev_corrected = Math.sqrt(sumsq_corrected/n - Math.pow(sum_corrected/n, 2)); std_err_corrected = (1.96 * std_dev_corrected/Math.sqrt(n)).toFixed(2); std_dev_corrected = std_dev_corrected.toFixed(2); - + n = bandwidths_corrected.length-1; median_corrected = Math.round( ( @@ -968,11 +982,11 @@ var impl = { + bandwidths_corrected[Math.ceil(n/2)] ) / 2 ); - + BOOMR.debug('amean: ' + amean + ', median: ' + median, "bw"); BOOMR.debug('corrected amean: ' + amean_corrected + ', ' + 'median: ' + median_corrected, "bw"); - + return { mean: amean, stddev: std_dev, @@ -984,21 +998,21 @@ var impl = { median_corrected: median_corrected }; }, - + defer: function(method) { var that=this; return setTimeout(function() { method.call(that); that=null;}, 10); }, - + load_img: function(i, run, callback) { var url = this.base_url + images[i].name + '?t=' + (new Date().getTime()) + Math.random(), - timer=0, tstart=0, - img = new Image(), - that=this; - + timer=0, tstart=0, + img = new Image(), + that=this; + img.onload=function() { img.onload=img.onerror=null; img=null; @@ -1017,7 +1031,7 @@ var impl = { } that=callback=null; }; - + // the timeout does not abort download of the current image, it just sets an // end of loop flag so we don't attempt download of the next image we still // need to wait until onload or onerror fire to be sure that the image @@ -1031,17 +1045,17 @@ var impl = { images[i].timeout + Math.min(400, this.latency ? this.latency.mean : 400) ); - + tstart = new Date().getTime(); img.src=url; }, - + lat_loaded: function(i, tstart, run, success) { if(run !== this.latency_runs+1) { return; } - + if(success !== null) { var lat = new Date().getTime() - tstart; this.latencies.push(lat); @@ -1051,26 +1065,26 @@ var impl = { if(this.latency_runs === 0) { this.latency = this.calc_latency(); } - + this.defer(this.iterate); }, - + img_loaded: function(i, tstart, run, success) { if(run !== this.runs_left+1) { return; } - + if(this.results[this.nruns-run].r[i]) { // already called on this image return; } - + // if timeout, then we set the next image to the end of loop marker if(success === null) { this.results[this.nruns-run].r[i+1] = {t:null, state: null, run: run}; return; } - + var result = { start: tstart, end: new Date().getTime(), @@ -1082,7 +1096,7 @@ var impl = { result.t = result.end-result.start; } this.results[this.nruns-run].r[i] = result; - + // we terminate if an image timed out because that means the connection is // too slow to go to the next image if(i >= images.end-1 @@ -1100,7 +1114,7 @@ var impl = { this.load_img(i+1, run, this.img_loaded); } }, - + finish: function() { if(!this.latency) { @@ -1114,9 +1128,9 @@ var impl = { lat_err: parseFloat(this.latency.stderr, 10), bw_time: Math.round(new Date().getTime()/1000) }; - + BOOMR.addVar(o); - + // If we have an IP address we can make the BA cookie persistent for a while // because we'll recalculate it if necessary (when the user's IP changes). if(!isNaN(o.bw)) { @@ -1134,18 +1148,18 @@ var impl = { null ); } - + this.complete = true; BOOMR.sendBeacon(); this.running = false; }, - + iterate: function() { if(this.aborted) { return false; } - + if(!this.runs_left) { this.finish(); } @@ -1160,18 +1174,18 @@ var impl = { setVarsFromCookie: function(cookies) { var ba = parseInt(cookies.ba, 10), - bw_e = parseFloat(cookies.be, 10), - lat = parseInt(cookies.l, 10) || 0, - lat_e = parseFloat(cookies.le, 10) || 0, - c_sn = cookies.ip.replace(/\.\d+$/, '0'), // Note this is IPv4 only - t = parseInt(cookies.t, 10), - p_sn = this.user_ip.replace(/\.\d+$/, '0'), + bw_e = parseFloat(cookies.be, 10), + lat = parseInt(cookies.l, 10) || 0, + lat_e = parseFloat(cookies.le, 10) || 0, + c_sn = cookies.ip.replace(/\.\d+$/, '0'), // Note this is IPv4 only + t = parseInt(cookies.t, 10), + p_sn = this.user_ip.replace(/\.\d+$/, '0'), // We use the subnet instead of the IP address because some people // on DHCP with the same ISP may get different IPs on the same subnet // every time they log in - t_now = Math.round((new Date().getTime())/1000); // seconds + t_now = Math.round((new Date().getTime())/1000); // seconds // If the subnet changes or the cookie is more than 7 days old, // then we recheck the bandwidth, else we just use what's in the cookie @@ -1191,7 +1205,7 @@ var impl = { } }; - + BOOMR.plugins.BW = { init: function(config) { var cookies; diff --git a/statsplus.js b/statsplus.js new file mode 100644 index 000000000..546676e53 --- /dev/null +++ b/statsplus.js @@ -0,0 +1,124 @@ +/** +\file stats_plus.js +Plugin for extending boomerang.js statistics and methods: StatsPlus + +Adds: +- Navigator and version info. +- Output format: key - value. + - For example: http://someurl?key=TEST-KEY-ABCD&value={ "someproperty": "somevalue", "otherproperty": "othervalue" } + - The goal is to insert this date in a NoSql engine for post-processing the entries. + +Instructions: + + - Include boomerang.js in your page (and his plugins, up to you :)) + - Include stats_plus.js and configure some of his values in the boomerang.js initialization: + BOOMR.init({ + beacon_url: "http://destination_url/_sp.gif", + StatsPlus: { + user_code: 'TESTKEY', + output_format: 'key-value' + } + }); + +*/ + +// w is the window object +(function(w) { + +var d=w.document; + +// First make sure BOOMR is actually defined. It's possible that your plugin is loaded before boomerang, in which case +// you'll need this. +BOOMR = BOOMR || {}; +BOOMR.plugins = BOOMR.plugins || {}; + +// A private object to encapsulate all your implementation details +// This is optional, but the way we recommend you do it. +var impl = { + complete: false, + + start: function(){ + impl.getBrowserInfo(); + impl.done(); + }, + + getBrowserInfo: function(){ + BOOMR.addVar( "appCodeName", window.navigator.appCodeName ); + BOOMR.addVar( "appName", window.navigator.appName ); + BOOMR.addVar( "appVersion", window.navigator.appVersion ); + BOOMR.addVar( "appBuildID", window.navigator.buildID ); + BOOMR.addVar( "appLanguage", window.navigator.language ); + BOOMR.addVar( "appOsCpu", window.navigator.oscpu ); + BOOMR.addVar( "appPlatform", window.navigator.platform ); + }, + + done: function() { + this.complete = true; + BOOMR.sendBeacon(); + } +}; + +BOOMR.plugins.StatsPlus = { + init: function(config) { + var i, properties = ["user_code", "output_format"]; // list of user configurable properties in O + + // This block is only needed if you actually have user configurable properties + BOOMR.utils.pluginConfig(impl, config, "StatsPlus", properties); + + // Other initialisation code here + impl.user_code = impl.user_code || 'TEST-KEY'; + BOOMR.addVar( "client_code", impl.user_code ); + + // Check if we are changing the output format of boomerang: + if ( impl.output_format != undefined ) + { + switch( impl.output_format ) + { + case "key-value": + BOOMR.formatResponse = function( url, params ) + { + var i = 0, str_params = []; + var key_num = new Date().getTime(); + + // Add random value to key (avoiding more than one request by milisecond: + key_num += (function(){ + var text = "-"; + var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for( var k = 0; k < 4; k++ ) + { + text += letters.charAt( Math.floor( Math.random() * letters.length ) ); + } + return text; + }()); + + // Generating the value part: + var value = ''; + for( i in params ) + { + str_params.push( '"' + i + '" : "' + params[i] + '"' ); + } + url += '?key=' + key_num + '&value=' + encodeURIComponent( '{' + str_params.join( ',' ) + '}' ); + return url; + } + break; + } + } + + // Subscribe to any BOOMR events here. + // Unless your code will explicitly be called by the developer + // or by another plugin, you must to do this. + BOOMR.subscribe("page_ready", impl.start, null, this); + + return this; + }, + + // Any other public methods would be defined here + + is_complete: function() { + // This method should determine if the plugin has completed doing what it + /// needs to do and return true if so or false otherwise + return impl.complete; + } +}; + +}(window)); \ No newline at end of file