From eb1e3e826f7854abd20e49c32c0d710358f5e064 Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Mon, 16 Jun 2014 12:11:53 -0400 Subject: [PATCH] initial commit --- README.md | 2 + sleuth.js | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ sleuth.min.js | 1 + 3 files changed, 129 insertions(+) create mode 100644 README.md create mode 100644 sleuth.js create mode 100644 sleuth.min.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..678a006 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +Sleuth.js +======== diff --git a/sleuth.js b/sleuth.js new file mode 100644 index 0000000..ee4d5ef --- /dev/null +++ b/sleuth.js @@ -0,0 +1,126 @@ +(function(window, document){ + if(window.Sleuth !== undefined){ + return; + } + + if(!window.btoa){ + var base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); + window.btoa = function(data){ + var result = ''; + var padding = ''; + var padCount = data.length % 3; + + if(padCount > 0){ + for(; padCount < 3; ++padCount){ + padding += '='; + data += '\0'; + } + } + + for(var i = 0; i < data.length; i += 3){ + var next = (data.charCodeAt(i) << 16) + (data.charCodeAt(i + 1) << 8) + data.charCodeAt(i + 2); + next = [(next >>> 18) & 63, (next >>> 12) & 63, (next >>> 6) & 63, next & 63]; + result += base64chars[next[0]] + base64chars[next[1]] + base64chars[next[2]] + base64chars[next[3]]; + } + + return result.substring(0, result.length - padding.length) + padding; + }; + } + + var encodeObject = function(data){ + var query = []; + for(var key in data){ + query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); + }; + + return query.join('&'); + }; + + var post = function(url, data, tags){ + var req = new XMLHttpRequest(); + url += '?' + encodeObject(tags); + req.open('POST', url, true); + req.send(JSON.stringify(data)); + }; + + var drop = function(url, data, tags){ + tags.d = window.btoa(JSON.stringify(data)); + tags.n = new Date().getTime(); + tags.r = Math.random() * 99999999; + url += '?' + encodeObject(tags); + document.write(''); + }; + + var Sleuth = function(){ + this.config = { + url: '/track', + unload: true, + ajax: true, + performance: true, + useLocalStorage: true, + }; + + this.tags = {}; + this.data = {}; + }; + + Sleuth.prototype.init = function(options){ + for(var key in options){ + this.config[key] = options[key]; + } + }; + + Sleuth.prototype.onunload = function(){ + if(this.config.performance && window.performance !== undefined){ + if(window.performance.timing !== undefined){ + this.data.timing = window.performance.timing; + } + if(window.performance.navigation !== undefined){ + this.data.navigation = { + redirectCount: window.performance.navigation.redirectCount, + type: window.performance.navigation.type, + }; + } + } + this.sendAllData(); + }; + + Sleuth.prototype.sendData = function(data){ + if(this.config.ajax){ + post(this.config.url, data, this.tags); + } else { + drop(this.config.url, data, this.tags); + } + }; + + Sleuth.prototype.sendAllData = function(){ + var toSend = this.data; + this.data = {}; + if(this.config.useLocalStorage){ + for(var key in window.localStorage){ + if(key.indexOf('Sleuth:') === 0){ + var value = window.localStorage.getItem(key); + window.localStorage.removeItem(key); + key = key.substring(6); + toSend[key] = value; + } + } + } + this.sendData(this.config.url, toSend, this.tags); + }; + + Sleuth.prototype.track = function(key, value){ + if(this.config.useLocalStorage && window.localStorage !== undefined){ + window.localStorage.setItem('Sleuth:' + key, value); + } else { + this.data[key] = value; + } + }; + + Sleuth.prototype.tag = function(key, value){ + this.tags[key] = value; + }; + + window.Sleuth = new Sleuth(); + window.onbeforeunload = window.Sleuth.onunload.bind(window.Sleuth); +})(window, document); diff --git a/sleuth.min.js b/sleuth.min.js new file mode 100644 index 0000000..9e6fdd7 --- /dev/null +++ b/sleuth.min.js @@ -0,0 +1 @@ +(function(window,document){if(window.Sleuth!==undefined){return}if(!window.btoa){var base64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");window.btoa=function(data){var result="";var padding="";var padCount=data.length%3;if(padCount>0){for(;padCount<3;++padCount){padding+="=";data+="\x00"}}for(var i=0;i>>18&63,next>>>12&63,next>>>6&63,next&63];result+=base64chars[next[0]]+base64chars[next[1]]+base64chars[next[2]]+base64chars[next[3]]}return result.substring(0,result.length-padding.length)+padding}}var encodeObject=function(data){var query=[];for(var key in data){query.push(encodeURIComponent(key)+"="+encodeURIComponent(data[key]))}return query.join("&")};var post=function(url,data,tags){var req=new XMLHttpRequest;url+="?"+encodeObject(tags);req.open("POST",url,true);req.send(JSON.stringify(data))};var drop=function(url,data,tags){tags.d=window.btoa(JSON.stringify(data));tags.n=(new Date).getTime();tags.r=Math.random()*99999999;url+="?"+encodeObject(tags);document.write("")};var Sleuth=function(){this.config={url:"/track",unload:true,ajax:true,performance:true,useLocalStorage:true};this.tags={};this.data={}};Sleuth.prototype.init=function(options){for(var key in options){this.config[key]=options[key]}};Sleuth.prototype.onunload=function(){if(this.config.performance&&window.performance!==undefined){if(window.performance.timing!==undefined){this.data.timing=window.performance.timing}if(window.performance.navigation!==undefined){this.data.navigation={redirectCount:window.performance.navigation.redirectCount,type:window.performance.navigation.type}}}this.sendAllData()};Sleuth.prototype.sendData=function(data){if(this.config.ajax){post(this.config.url,data,this.tags)}else{drop(this.config.url,data,this.tags)}};Sleuth.prototype.sendAllData=function(){var toSend=this.data;this.data={};if(this.config.useLocalStorage){for(var key in window.localStorage){if(key.indexOf("Sleuth:")===0){var value=window.localStorage.getItem(key);window.localStorage.removeItem(key);key=key.substring(6);toSend[key]=value}}}this.sendData(this.config.url,toSend,this.tags)};Sleuth.prototype.track=function(key,value){if(this.config.useLocalStorage&&window.localStorage!==undefined){window.localStorage.setItem("Sleuth:"+key,value)}else{this.data[key]=value}};Sleuth.prototype.tag=function(key,value){this.tags[key]=value};window.Sleuth=new Sleuth;window.onbeforeunload=window.Sleuth.onunload.bind(window.Sleuth)})(window,document); \ No newline at end of file