JavaScript Utilities
Useful JavaScript Utilities
Exponential idle-time tracker
Tracking exponential time intervals when the mouse is idle. These events will not be triggered if the user is not on the application browser tab.
function trackIdleTime(idleTimeSec) {
if(idleTimeSec < 1)
{
return;
}
var t;
window.onload = resetTimer;
window.onmousemove = resetTimer;
window.onmousedown = resetTimer; // catches touchscreen presses as well
window.ontouchstart = resetTimer; // catches touchscreen swipes as well
window.onclick = resetTimer; // catches touchpad clicks as well
window.onkeypress = resetTimer;
window.addEventListener('scroll', resetTimer, true); // improved; see comments
function fireCustomEvent() {
console.log('custom event ex');
aptrinsic('track', 'idleTime', {"totalNumOfSeconds":idleTimeSec} );
idleTimeSec = 2 * idleTimeSec;
resetTimer();
}
function resetTimer() {
clearTimeout(t);
t = setTimeout(fireCustomEvent, (idleTimeSec*1000)); // time is in milliseconds
}
}
trackIdleTime(15);
Triggering an event on a given schedule
This code will trigger a custom event on a fixed schedule which may be relevant for virtual events/webinars or seasonal events.
// **********************************************
// Sends PX custom event on given schedule
// Usage:
//
// // Construct eventTimer, pass in name of event, and optional timer resolution, defaults to 1 second
// let eventTimer = new PXCustomEventTimer("timerTest");
//
// // Start timer with list of times in the future to fire evens
// eventTimer.startTimer([time1, time2, time3, ...]); // Where times are millisecond epoch values
//
// **********************************************
function PXCustomEventTimer(customEventName, timerResolution, maxEventAge) {
this.customEventName = customEventName;
this.timerResolution = timerResolution || 5000; // How often timer checks events, default to every 5 seconds
this.maxEventAge = maxEventAge || (1000 * 60 * 60 * 12); // Ignore events more than 12 hours in the past
this.interval = null;
this.eventTimes = null;
this.startTimer = function(eventTimes) {
this.endTimer();
this.eventTimes = eventTimes.sort((a,b) => { return a - b; });
this.interval = setInterval(() => this.__checkForEventTimes(), this.timerResolution);
};
this.endTimer = function() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
};
this.__checkForEventTimes = function() {
try {
if (this.eventTimes && this.eventTimes.length > 0 && this.eventTimes[0] <= Date.now() ) {
// Check to make sure date isn't really old
if ((Date.now() - this.eventTimes[0]) < this.maxEventAge) {
// Fire event, pop eventTime
if (window.aptrinsic) {
window.aptrinsic('track', this.customEventName);
// console.log("sending event at ", Date.now());
} else {
// console.log("Gainsight PX not installed, skipping custom event");
}
}
// Pop time from queue
this.eventTimes.shift();
if (this.eventTimes.length === 0) {
// Done, clear the timer
this.endTimer();
}
}
} catch (e) {
// ignore exception
}
}
// Build timer object, pass in PX event name
let eventTimer = new PXCustomEventTimer('VideoTimerEvent');
// Start the timer with list of event times
// Set UTC times to trigger events
// YYYY,MM,DD,HH,MM,SS
// NOTE: The month parameter is zero-relative, January is 0, December is 11
const eventTimes = [
new Date(Date.UTC(2020,10,13,21,30,0)),
new Date(Date.UTC(2020,10,13,21,35,0)),
new Date(Date.UTC(2020,10,13,21,40,0)),
new Date(Date.UTC(2020,10,13,21,45,0)),
];
eventTimer.startTimer(eventTimes);
Tracking time spent on page
When a user leaves a page or the max time defined have reached then a custom event will be triggered, tracking the total time spent on the page. You can decide to track or disregard pages that reached the timeout.
function PXPageTimer(maxSecondsTracked, trackPagesOverMax) {
this.pageName = null;
this.startTime = null;
this.maxSecondsTracked = maxSecondsTracked || 3600 * 24;
this.trackPagesOverMax = trackPagesOverMax !== false;
this.startTimer = function (pageName) {
try {
if (this.pageName && this.pageName !== pageName) {
this.endTimer();
}
this.pageName = pageName;
this.startTime = Date.now();
} catch (e) {
console.log("Unable to start timer on ", this.pageName);
}
};
this.endTimer = function (pageUnloaded) {
try {
if (this.pageName) {
let endTime = Date.now();
let secondsOnPage = (endTime - this.startTime) / 1000;
if (secondsOnPage <= this.maxSecondsTracked || this.trackPagesOverMax) {
window.aptrinsic('track', 'timeOnPage', {
'pageName': this.pageName,
'seconds': Math.min(secondsOnPage,this.maxSecondsTracked),
'pageUnloaded': pageUnloaded
});
}
this.pageName = undefined;
}
} catch (e) {
console.log("Unable to log time on page", this.pageName);
}
};
}
(function InitializeTimer() {
let maxSecondsTracked = 60 * 60; // Track time on page for up to one hour
let trackPagesOverMax = false; // Ignore any pages over one hour
let featureTimer = new PXPageTimer(maxSecondsTracked, trackPagesOverMax);
// Initial page
featureTimer.startTimer(window.location.href);
window.addEventListener('hashchange', () => {
featureTimer.startTimer(window.location.href);
});
window.addEventListener('popstate', () => {
featureTimer.startTimer(window.location.href);
});
window.addEventListener('beforeunload', () => {
featureTimer.endTimer(true);
});
let realPushState = window.history.pushState;
window.history.pushState = function (state, title, newLocation) {
featureTimer.startTimer(newLocation.href);
return realPushState.apply(window.history, arguments); // Call the original
};
})();
Tracking and generating durable random user ID
Using cookies for tracking anonymous users is done automatically by Gainsight PX, however, if you need to generate client-side IDs to de-anonymize users for engagement and tracking you can use the following functions below.
Note: relying on cookies without server-side authentication is not advised. users that use different devices or clean up their browser cookies will be treated as new users.
/**
* generate random ID
**/
function _px_generateRandomId(){
return `${new Date().getTime()}-${this._px_generateRandom(8)}`;
}
/**
* Set cookie
**/
function setCookie(name, value, daysToLive) {
// Encode value in order to escape semicolons, commas, and whitespace
var cookie = name + "=" + encodeURIComponent(value);
if(typeof daysToLive === "number") {
/* Sets the max-age attribute so that the cookie expires
after the specified number of days */
cookie += "; max-age=" + (daysToLive*24*60*60);
document.cookie = cookie;
}
}
/**
* check if the user is anonymous
**/
function _px_isAnonymous(){
const PX_COOKIE = "apt.uid";
var cookieParts = _px_getCookie(PX_COOKIE).split('.')
var anonymous = cookieParts.length != 4 || cookieParts[2] === "0"
return(anonymous);
}
/**
* generate anonymous visitor ID
**/
function _px_getAnonymousVisitorID(){
return `${new Date().getTime()}-${this._px_generateRandom(8)}`;
}
function _px_getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function _px_generateRandom(howMany){
let random = '';
//run and generate as many as needed random numbers and concat
for(let i = 0; i < howMany; i++){
random += this._px_getRandomInt(1, 10);
}
return random;
}
function _px_getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}