Skip to content

Instantly share code, notes, and snippets.

@alasdairham
Created September 6, 2017 09:10
Show Gist options
  • Select an option

  • Save alasdairham/90debd8b325ba310dc67695815ac650b to your computer and use it in GitHub Desktop.

Select an option

Save alasdairham/90debd8b325ba310dc67695815ac650b to your computer and use it in GitHub Desktop.
/*
*
* Eg. SEND_INTERVAL = 3000; CAPTURE_INTERVAL = 50
*
* - Capture mousemove & mousedown event after every 50 ms
* - For each captured mouse event, save it into a local cache object
* - After every 3 seconds, sends the cached mouse event history to server,
* then clear cache. Exceptions are:
* 1. A click event triggers data submission to server (as described above) immediately
* 2. Pause sending data to server when there is no new movement,
* e.g, when the last mouse event happens earlier than 3s ago (if data submission interval is 3s)
*/
(function() {
var SEND_INTERVAL = 3000; // interval of data upload to server
var CAPTURE_INTERVAL = 50; // interval of capturing a mouse event
var MAX_SAVED = (1000/CAPTURE_INTERVAL) * (SEND_INTERVAL/1000); // max size of data cache
var MouseCache = {
saved: [],
add: function(mouse) {
if (this.saved.length >= MAX_SAVED)
this.saved.shift();
this.saved.push(mouse);
},
clear: function() {
this.saved = [];
}
};
var MouseTracker = {
dom: null,
user_agent: navigator.userAgent,
ip: undefined,
last_mouse: {x: 0, y:0, ts: 0},
track_document: function () {
this.dom = document.scrollingElement;
this.dom.onmousedown = this.handle_mouse_event.bind(this);
this.dom.onmousemove = this.handle_mouse_event.bind(this);
},
handle_mouse_event: function(evt) {
getType = function() {
switch(tagName) {
case 'INPUT': return evt.target.type;
case 'BUTTON': return 'button';
case 'A': return 'hyperlink';
}
};
var tagName = evt.target.tagName;
if (Date.now() - this.last_mouse.ts >= CAPTURE_INTERVAL) {
if (evt.type === 'mousedown') {
this.last_mouse = this.parse_evt(evt, {
isClick: true,
type: getType(),
target: tagName === 'A' ? evt.target.href : '',
text: evt.target.innerText.length > 100 ? "": evt.target.innerText || evt.target.value// possibly clicked the background if target text is too long
});
send_to_server(); // don't miss out any click event
} else if (evt.type == 'mousemove'){
this.last_mouse = this.parse_evt(evt, {isClick: false});
}
MouseCache.add(this.last_mouse);
}
},
parse_evt: function(evt, additional) {
var parsed = {
x: evt.clientX,
y: evt.clientY + this.dom.scrollTop,
ts: Date.now(),
w: this.dom.scrollWidth,
h: this.dom.scrollHeight,
url: window.location.href
};
for (var add in additional) if (additional.hasOwnProperty(add)) parsed[add] = additional[add];
return parsed;
},
to_json: function() {
return {
ip: this.ip,
user_agent: this.user_agent,
data: MouseCache.saved
}
},
has_moved: function () {
return (Date.now() - this.last_mouse.ts)/1000 <= SEND_INTERVAL/1000;
}
};
function find_user_ip(callback) {
var url = location.protocol === 'https:' ? 'https://api.ipify.org?format=json' : 'http://myexternalip.com/json';
var http = new XMLHttpRequest();
http.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
MouseTracker.ip = JSON.parse(http.responseText)['ip'];
callback(true);
} else {
callback(false);
}
};
http.open('GET', url);
http.send();
}
function send_to_server(){
if (!MouseTracker.has_moved()) return;
var url = 'https://example_php_url.php';
var payload = MouseTracker.to_json();
var http = new XMLHttpRequest();
http.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// pass
}
};
http.open('POST', url, true);
http.send(JSON.stringify(payload));
MouseCache.clear();
}
function check_cache() {
if (MouseCache.saved.length !== 0) {
send_to_server()
}
}
/***************** BEGIN *****************/
document.addEventListener("DOMContentLoaded", function(event) {
find_user_ip(function(succ) {
if (succ) {
MouseTracker.track_document();
setInterval(send_to_server, SEND_INTERVAL);
}
});
});
window.addEventListener('beforeunload', check_cache)
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment