Skip to content

Instantly share code, notes, and snippets.

@RHeijnen
Last active February 13, 2018 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RHeijnen/59c1751abf4ba2c34aeb4bc7533f4dbe to your computer and use it in GitHub Desktop.
Save RHeijnen/59c1751abf4ba2c34aeb4bc7533f4dbe to your computer and use it in GitHub Desktop.
(function() {
'use strict';
/*
Config
*/
const timeFormat = 'YYYYMMDD/HH:mm:ss'
const pollingTime = 1000; //ms
const fingerprinting = "uid";
const scanWebElements = false;
const logCoords = true;
const destination = "none";
/* ----------------------------------*/
/**
* @function loadScripts is workaround I created to be in total control of imports from one JS file.
* @param {array} scriptURL - take 1 url from array of urls, shift it (select it)
* --------------------------------------- then create a script tag, add the url as src attribute - and append it to the head
*
* @param {callback} @function callback - calls the callback function when it is done importing scripts.
* -> see it as a window onload
*/
function loadScripts(array,callback){
var loader = function(src,handler){
var script = document.createElement("script");
script.src = src;
script.onload = script.onreadystatechange = function(){
script.onreadystatechange = script.onload = null;
handler();
};
var head = document.getElementsByTagName("head")[0];
(head || document.body).appendChild( script );
};
(function run(){
if(array.length!=0){
loader(array.shift(), run);
}else{
callback && callback();
};
})();
}
loadScripts([
// load scripts..
// todo make conditional imports based domain
// appbuilder already has these files minimized
"https://www.gstatic.com/firebasejs/4.1.3/firebase.js",
"https://momentjs.com/downloads/moment.js"
],function(){
var coords = [];
var firstRun = false;
var CookieDate = new Date;
CookieDate.setFullYear(CookieDate.getFullYear( ) +10);
/**
* Generate a unique user ID
* @return {string}
* example: "f498a727-9987-bc60-7f66-b6fa28165af8"
*/
function createID(){
function createSubset(){
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return createSubset() + createSubset() + '-' + createSubset() + '-' + createSubset()
+ '-' + createSubset() + '-' + createSubset() + createSubset() + createSubset();
};
/**
* Find cookie by
* @param {string} [name]
* the name of the cookie, returns cookie if found
*/
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) {
return parts.pop().split(";").shift();
};
};
/**
* creates getBrowserType() @function
* @returns {string} type of browser
* used to identify the browser types [];
*/
navigator.getBrowserType = (function(){
var ua= navigator.userAgent, tem,
M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if(/trident/i.test(M[1])){
tem= /\brv[ :]+(\d+)/g.exec(ua) || [];
return 'IE '+(tem[1] || '');
};
if(M[1]=== 'Chrome'){
tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
};
M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
return M.join(' ');
})();
/**
* @function @returns {boolean} based on matching in the userAgent
*
*/
var isMobile = {
Android: function() {
return navigator.userAgent.match(/Android/i);
},
BlackBerry: function() {
return navigator.userAgent.match(/BlackBerry/i);
},
iPad: function() {
return navigator.userAgent.match(/iPad/i);
},
iPhone: function() {
return navigator.userAgent.match(/iPhone/i);
},
Opera: function() {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function() {
return navigator.userAgent.match(/IEMobile/i);
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
}
};
/**
* call @function isMobile and @returns {string}
* save @return value as mobileType
*/
var mobileType = "none"
if(isMobile.Android()){
mobileType = 'Android';
}else if(isMobile.Opera()){
mobileType = 'Opera';
}else if (isMobile.BlackBerry()){
mobileType = 'BlackBerry';
}else if(isMobile.iPad()){
mobileType = 'iPad';
}else if(isMobile.iPhone()){
mobileType = 'iPhone';
}else if(isMobile.Windows()){
mobileType = 'Windows mobile';
}
/**
* @function (identifyOS)
* @returns {string} based on userAgent identification
*/
var identifyOS = function(){
var OSName = "Unknown OS"; // fallback
if (navigator.userAgent.indexOf("Win") != -1) OSName="Windows";
if (navigator.userAgent.indexOf("Mac") != -1) OSName="Macintosh";
if (navigator.userAgent.indexOf("Linux") != -1) OSName="Linux";
if (navigator.userAgent.indexOf("Android") != -1) OSName="Android";
if (navigator.userAgent.indexOf("like Mac") != -1) OSName = "iOS";
return OSName
}
/**
* @function (identifyBrowser_version)
* @returns {string} returns browser version (based on user agent)
*/
var identifyBrowser_version = function(){
return navigator.appVersion;
}
/**
* @function (identifyOS_platform)
* @returns {string} returns platform the OS is based on
*/
var identifyOS_platform = function(){
return navigator.platform;
}
/**
*
* set up time options
* @var {string} start_date -> moment formated based on @const timeFormat
* TODO: found out if this is all the date magic we need - do we need unix etc ?
*/
var start_date = moment().format(timeFormat)
var start_date_iso = moment().toISOString();
/**
* Clean up host name
* Apps within appbuilder 'can' have prefix url's like -> appbuilder.tst...
* This @function removes_prefix
* @param hostname -> full url
* @returns @var {string} cleanURL (removed prefix and postfix)
*/
var cleanUpHostName = function(hostname){
var cleanHostName = hostname.replace(/([.])\w+/g,"")
if(cleanHostName == "appbuilder"){
var workAroundString = window.location.href;
var regexResult1 = workAroundString.match(/(apps\/com.)(.*)(\/.)/);
var regexResult2 = regexResult1[2].match(/([.])\w+/g);
var regexResult3 = regexResult2[0].replace(".","");
return regexResult3
}else{
return cleanHostName
}
}
/** @var fallback to "UnknownApp" if for some magical reason we can not determine the hostname */
var application = cleanUpHostName(document.location.hostname) || "UnknownApp" ;
/**
* @function setUID - returns a UID if it already exists, if it does not exist it returns a new one based on
* config: @const fingerprinting values. [uid - adv - mix]
* @param {string} param values [existing - createNew]
* @returns either existing UID or creates a new UID and returns that one.
*/
var setUID = function(param){
if(param == "existing"){
return getCookie("RA_UID");
}else if(param == "createNew"){
firstRun = true;
if(fingerprinting == "uid"){
var uid = createID();
document.cookie = "RA_UID="+uid+"; path=/; expires=" + CookieDate.toGMTString( ) + ';';
return uid;
}else if(fingerprinting == "adv"){
}else if(fingerprinting == "mix"){
}
}
}
/** @var {simple} listings
* ----------------------
* @var {string} currentPage - Get current location
* @var {string} referPage - Get referPage, even tho it has awefull support.
* @var {boolean} isCookieSet - Verify if there is already a cookie set
* @var {string} currentUser - Gets or Creates a userUID so we can identify this user
*/
var currentPage = window.location.href;
var referPage = document.referrer;
var isCookieSet = getCookie('RA_UID');
var currentUser = isCookieSet ? setUID("existing") : setUID('createNew');
/**
* @function getScrollPercent returns % scrolled on current page
* @thanks https://stackoverflow.com/questions/2387136/cross-browser-method-to-determine-vertical-scroll-percentage-in-javascript
*/
function getScrollPercent() {
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}
/** @function getXpathTo - used as click event on dom
* @param element - event passess clicked element
* @return {string} - returns xpath to this element as string
* Used to determine what elements the user clicks on
*/
function getXPathTo(element) {
// if the element has an ID
if (element.id!==''){
return "//*[@id='"+element.id+"']";
}
// if the element is the body
if (element===document.body){
return element.tagName.toLowerCase();
}
// if part of a nodelist
var ix= 0;
var siblings= element.parentNode.childNodes;
for (var i= 0; i<siblings.length; i++) {
var sibling= siblings[i];
if (sibling===element){
return getXPathTo(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix + 1) + ']';
};
if (sibling.nodeType===1 && sibling.tagName === element.tagName) {
ix++;
};
};
};
/** @function getCoordOnClick - used as click event on dom
* @param event - event passed
* @return {nothing} - saved data in
* Used to determine where the user clicks
*/
var getCoordOnClick = function(event){
coords.push({
"x": event.clientX,
"y": event.clientY
});
};
if(logCoords) document.addEventListener("click", getCoordOnClick);
/**
* @plain javascript way of doing $(window).width();
* [tested in: Chrome - Safari]
*/
var getWindowWidth = function(){
return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
};
/**
* @plain javascript way of doing $(window).height();
* [tested in: Chrome - Safari]
*/
var getWindowHeight = function(){
return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
};
/**
* @function getScreenHeight @returns screen.height from screen object
*/
var getScreenHeight = function(){
return screen.height;
};
/**
* @function getScreenWidth @returns screen.width from screen object
*/
var getScreenWidth = function(){
return screen.width;
};
/**
* Set up @var trackingObject tracking object
*/
var trackingObject = {
_uid : currentUser,
_start_time : start_date,
_start_time_iso : start_date_iso,
_end_time : start_date,
_end_time_iso : start_date_iso,
};
if(firstRun){
trackingObject.device_Storage = {
screen_width : getScreenWidth(),
screen_height : getScreenHeight(),
window_width : getWindowWidth(),
window_height : getWindowHeight(),
os_system : identifyOS(),
os_platform : identifyOS_platform(),
browser_type : navigator.getBrowserType,
browser_v : identifyBrowser_version(),
mobile : mobileType
};
firstRun = false;
}
(function update(){
/**
* @function update call is made every @const pollingTime
* When we update we log datestamps:
* @var end_date - by polling time, and substracting that from each other we can determine how long a user's stay is.
* @var end_date_iso - converting the values to ISO for standards
* ----------------------------
* Based on config we also update
* ----------------------------
* @var scanWebElements -
* @var destination - the tracking object
*
*/
var end_date = moment().format(timeFormat);
// userInformation.timeInfo.end_time = end_date;
var end_date_iso = moment().toISOString();
if(destination == 'none'){
console.log(trackingObject);
delete trackingObject.device_Storage;
}else{
// upload to destination
};
// database.ref('tracking/'+application+"/"+current_user+"/"+start_date).update(userInformation);
setTimeout(update, pollingTime);
})();
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment