Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 230 additions & 75 deletions riveted.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,108 @@
/*!
* riveted.js | v0.1
* Copyright (c) 2014 Rob Flaherty (@robflaherty)
* Licensed under the MIT and GPL licenses.
* @preserve
* riveted.js | v0.6.2
* Copyright (c) 2016 Rob Flaherty (@robflaherty)
* Licensed under the MIT license
*/

;(function ($,window,document,undefined) {

var defaults = {
elements: [],
minHeight: 0,
percentage: true,
testing: false
},
/* Universal module definition */

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// Browser global
root.riveted = factory();
}
}(this, function () {

/* Riveted */

var riveted = (function() {

var started = false,
stopped = false,
turnedOff = false,
clockTime = 0,
startTime = new Date(),
clockTimer = null,
idleTimer = null,
sendEvent,
sendUserTiming,
reportInterval,
idleTimeout,
nonInteraction,
universalGA,
classicGA,
universalSendCommand,
googleTagManager,
gtagFunc = false,
gaGlobal;

function init(options) {

// Set up options and defaults
options = options || {};
reportInterval = parseInt(options.reportInterval, 10) || 5;
idleTimeout = parseInt(options.idleTimeout, 10) || 30;
gaGlobal = options.gaGlobal || 'ga';

/*
* Determine which version of GA is being used
* "ga", "_gaq", and "dataLayer" are the possible globals
*/

if (typeof window[gaGlobal] === "function") {
universalGA = true;
}

$window = $(window),

cache = [];
if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
classicGA = true;
}

/*
* Plugin
*/
if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
googleTagManager = true;
}

if(typeof gtag === 'function'){
gtagFunc = true;
}

$.riveted = function(options) {

var active = false;
if ('gaTracker' in options && typeof options.gaTracker === 'string') {
universalSendCommand = options.gaTracker + '.send';
} else {
universalSendCommand = 'send';
}

if (typeof options.eventHandler == 'function') {
sendEvent = options.eventHandler;
}

options = $.extend({}, defaults, options);
if (typeof options.userTimingHandler == 'function') {
sendUserTiming = options.userTimingHandler;
}

var setIdle;
if ('nonInteraction' in options && (options.nonInteraction === false || options.nonInteraction === 'false')) {
nonInteraction = false;
} else {
nonInteraction = true;
}

var startCount = 0;
// Basic activity event listeners
addListener(document, 'keydown', trigger);
addListener(document, 'click', trigger);
addListener(window, 'mousemove', throttle(trigger, 500));
addListener(window, 'scroll', throttle(trigger, 500));

var started = false;
// Page visibility listeners
addListener(document, 'visibilitychange', visibilityChange);
addListener(document, 'webkitvisibilitychange', visibilityChange);
}

/*
* Functions
*/

/*
* Throttle function borrowed from:
Expand Down Expand Up @@ -70,92 +137,180 @@
}
return result;
};
}
}

/*
* Cross-browser event listening
*/

function sendEvent(action, label) {
function addListener(element, eventName, handler) {
if (element.addEventListener) {
element.addEventListener(eventName, handler, false);
}
else if (element.attachEvent) {
element.attachEvent('on' + eventName, handler);
}
else {
element['on' + eventName] = handler;
}
}

console.log('Ping');
/*
* Function for logging User Timing event on initial interaction
*/

if (!options.testing) {
sendUserTiming = function (timingValue) {

if (typeof(ga) !== "undefined") {
//ga('send', 'event', 'Riveted', action, label, 1);
}
if (googleTagManager) {
if (gtagFunc){

if (typeof(_gaq) !== "undefined") {
//_gaq.push(['_trackEvent', 'Riveted', action, label, 1]);
}
gtag('event', 'RivetedTiming', {'event_category':'Riveted', 'event_label': 'First Interaction', 'value': timingValue});

if (typeof(dataLayer) !== "undefined") {
//dataLayer.push({'event':'Riveted', 'eventCategory':'Riveted', 'eventAction': action, 'eventLabel': label, 'eventValue': 1});
} else {

dataLayer.push({'event':'RivetedTiming', 'eventCategory':'Riveted', 'timingVar': 'First Interaction', 'timingValue': timingValue});

}

} else {

console.log('action: ' + action + '; label: ' + label);
if (universalGA) {
window[gaGlobal](universalSendCommand, 'timing', 'Riveted', 'First Interaction', timingValue);
}

if (classicGA) {
_gaq.push(['_trackTiming', 'Riveted', 'First Interaction', timingValue, null, 100]);
}

}
}

function checkIdle() {
active = false;
console.log('Setting to false');
}
};

/*
* Function for logging ping events
*/

sendEvent = function (time) {

function startRiveted(diff) {

started = true;
if (googleTagManager) {
if (gtagFunc){

setIdle = setTimeout(checkIdle, 3000);
gtag('event','Riveted', {'event_category':'Riveted', 'event_action': 'Time Spent', 'event_label': time, 'value': reportInterval, 'non_interaction': nonInteraction});

var pingCheck = setInterval(function() {
if (active) {
sendEvent('Ping', currentCount);
} else {
console.log('Idle');

dataLayer.push({'event':'Riveted', 'eventCategory':'Riveted', 'eventAction': 'Time Spent', 'eventLabel': time, 'eventValue': reportInterval, 'eventNonInteraction': nonInteraction});

}
}, 1000);

} else {

if (universalGA) {
window[gaGlobal](universalSendCommand, 'event', 'Riveted', 'Time Spent', time.toString(), reportInterval, {'nonInteraction': nonInteraction});
}

if (classicGA) {
_gaq.push(['_trackEvent', 'Riveted', 'Time Spent', time.toString(), reportInterval, nonInteraction]);
}

}

};

function setIdle() {
clearTimeout(idleTimer);
stopClock();
}

/*
* Time lapsed until engaged could be an interesting metric.
* Send a user timing event on the first ping? I think so.
*/
function visibilityChange() {
if (document.hidden || document.webkitHidden) {
setIdle();
}
}

function clock() {
clockTime += 1;
if (clockTime > 0 && (clockTime % reportInterval === 0)) {
sendEvent(clockTime);
}

}

function stopClock() {
stopped = true;
clearInterval(clockTimer);
}

function turnOff() {
setIdle();
turnedOff = true;
}

function resetActive() {
function turnOn() {
turnedOff = false;
}

console.log('reset');
function restartClock() {
stopped = false;
clearInterval(clockTimer);
clockTimer = setInterval(clock, 1000);
}

function startRiveted() {

// Calculate seconds from start to first interaction
var currentTime = new Date();
var diff = Math.floor((currentTime - startTime)/1000);
var diff = currentTime - startTime;

// Set global
started = true;

// Send User Timing Event
sendUserTiming(diff);

// Start clock
clockTimer = setInterval(clock, 1000);

if (!started) {
startRiveted(diff);
} else {
active = true;
clearTimeout(setIdle);
setIdle = setTimeout(checkIdle, 3000);
}
}

function init() {
function resetRiveted() {
startTime = new Date();
clockTime = 0;
started = false;
stopped = false;
clearInterval(clockTimer);
clearTimeout(idleTimer);
}

var startTime = Date.now();
function trigger() {

if (turnedOff) {
return;
}

$(document).on('keypress click', resetActive);
$(window).on('scroll', throttle(resetActive, 500));
if (!started) {
startRiveted();
}

if (stopped) {
restartClock();
}

clearTimeout(idleTimer);
idleTimer = setTimeout(setIdle, idleTimeout * 1000 + 100);
}

init();
return {
init: init,
trigger: trigger,
setIdle: setIdle,
on: turnOn,
off: turnOff,
reset: resetRiveted
};

})();

};
return riveted;

})(jQuery,window,document);
}));
7 changes: 7 additions & 0 deletions riveted.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.