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 ""; }