Skip to main content
Gainsight Inc.

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 "";
}
  • Was this article helpful?