Skip to content

Commit

Permalink
Port analytics libs from static repo
Browse files Browse the repository at this point in the history
Include sample init file.
Remove jQuery dependency from Tracker
  • Loading branch information
jonnywyatt authored and fofr committed Mar 10, 2015
1 parent 5fa6d6d commit ee2fd0d
Show file tree
Hide file tree
Showing 10 changed files with 699 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ module.exports = function(grunt) {
javascripts: {
src: [
'node_modules/jquery-browser/lib/jquery.js',
'javascripts/govuk/analytics/google-analytics-classic-tracker.js',
'javascripts/govuk/analytics/google-analytics-universal-tracker.js',
'javascripts/govuk/analytics/tracker.js',
'javascripts/**/*.js'
],
options: {
specs: 'spec/unit/*Spec.js',
specs: 'spec/unit/**/*Spec.js',
helpers: 'spec/unit/*Helper.js'
}
}
Expand Down
22 changes: 22 additions & 0 deletions javascripts/govuk/analytics/error-tracking.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Extension to track errors using google analytics as a data store.
(function() {

"use strict";
var trackJavaScriptError = function (e) {
var errorSource = e.filename + ': ' + e.lineno;
GOVUK.analytics.trackEvent('JavaScript Error', e.message, {
label: errorSource,
value: 1,
nonInteraction: true
});
};

if (window.addEventListener) {
window.addEventListener('error', trackJavaScriptError, false);
} else if (window.attachEvent) {
window.attachEvent('onerror', trackJavaScriptError);
} else {
window.onerror = trackJavaScriptError;
}

}());
111 changes: 111 additions & 0 deletions javascripts/govuk/analytics/google-analytics-classic-tracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
(function() {
"use strict";
window.GOVUK = window.GOVUK || {};

var GoogleAnalyticsClassicTracker = function(id, cookieDomain) {
window._gaq = window._gaq || [];
configureProfile(id, cookieDomain);
allowCrossDomainTracking();
anonymizeIp();

function configureProfile(id, cookieDomain) {
_gaq.push(['_setAccount', id]);
_gaq.push(['_setDomainName', cookieDomain]);
}

function allowCrossDomainTracking() {
_gaq.push(['_setAllowLinker', true]);
}

// https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gat#_gat._anonymizeIp
function anonymizeIp() {
_gaq.push(['_gat._anonymizeIp']);
}
};

GoogleAnalyticsClassicTracker.load = function() {
var ga = document.createElement('script'),
s = document.getElementsByTagName('script')[0];

ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
s.parentNode.insertBefore(ga, s);
};

// https://developers.google.com/analytics/devguides/collection/gajs/asyncMigrationExamples#VirtualPageviews
GoogleAnalyticsClassicTracker.prototype.trackPageview = function(path) {
var pageview = ['_trackPageview'];

if (typeof path === "string") {
pageview.push(path);
}

_gaq.push(pageview);
};

// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
GoogleAnalyticsClassicTracker.prototype.trackEvent = function(category, action, options) {
var value,
options = options || {},
hasLabel = false,
hasValue = false,
evt = ["_trackEvent", category, action];

// Label is optional
if (typeof options.label === "string") {
hasLabel = true;
evt.push(options.label);
}

// Value is optional, but when used must be an
// integer, otherwise the event will be invalid
// and not logged
if (options.value || options.value === 0) {
value = parseInt(options.value, 10);
if (typeof value === "number" && !isNaN(value)) {
hasValue = true;

// Push an empty label if not set for correct final argument order
if (!hasLabel) {
evt.push('');
}

evt.push(value);
}
}

// Prevents an event from affecting bounce rate
// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#non-interaction
if (options.nonInteraction) {

// Push empty label/value if not already set, for correct final argument order
if (!hasValue) {
if (!hasLabel) {
evt.push('');
}
evt.push(0);
}

evt.push(true);
}

_gaq.push(evt);
};

/*
https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiSocialTracking
network – The network on which the action occurs (e.g. Facebook, Twitter)
action – The type of action that happens (e.g. Like, Send, Tweet)
target – The text value that indicates the subject of the action
*/
GoogleAnalyticsClassicTracker.prototype.trackSocial = function(network, action, target) {
_gaq.push(['_trackSocial', network, action, target]);
};

// https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables
GoogleAnalyticsClassicTracker.prototype.setCustomVariable = function(index, value, name, scope) {
_gaq.push(['_setCustomVar', index, name, String(value), scope]);
};

GOVUK.GoogleAnalyticsClassicTracker = GoogleAnalyticsClassicTracker;
})();
104 changes: 104 additions & 0 deletions javascripts/govuk/analytics/google-analytics-universal-tracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
(function() {
"use strict";
window.GOVUK = window.GOVUK || {};

var GoogleAnalyticsUniversalTracker = function(id, cookieDomain) {
configureProfile(id, cookieDomain);
anonymizeIp();

function configureProfile(id, cookieDomain) {
sendToGa('create', id, {'cookieDomain': cookieDomain});
}

function anonymizeIp() {
// https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#anonymizeip
sendToGa('set', 'anonymizeIp', true);
}
};

GoogleAnalyticsUniversalTracker.load = function() {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
};

// https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
GoogleAnalyticsUniversalTracker.prototype.trackPageview = function(path, title) {
if (typeof path === "string") {
var pageviewObject = {
page: path
};

if (typeof title === "string") {
pageviewObject.title = title;
}
sendToGa('send', 'pageview', pageviewObject);
} else {
sendToGa('send', 'pageview');
}
};

// https://developers.google.com/analytics/devguides/collection/analyticsjs/events
GoogleAnalyticsUniversalTracker.prototype.trackEvent = function(category, action, options) {
var value,
options = options || {},
evt = {
hitType: 'event',
eventCategory: category,
eventAction: action
};

// Label is optional
if (typeof options.label === "string") {
evt.eventLabel = options.label;
}

// Value is optional, but when used must be an
// integer, otherwise the event will be invalid
// and not logged
if (options.value || options.value === 0) {
value = parseInt(options.value, 10);
if (typeof value === "number" && !isNaN(value)) {
evt.eventValue = value;
}
}

// Prevents an event from affecting bounce rate
// https://developers.google.com/analytics/devguides/collection/analyticsjs/events#implementation
if (options.nonInteraction) {
evt.nonInteraction = 1;
}

sendToGa('send', evt);
};

/*
https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions
network – The network on which the action occurs (e.g. Facebook, Twitter)
action – The type of action that happens (e.g. Like, Send, Tweet)
target – Specifies the target of a social interaction.
This value is typically a URL but can be any text.
*/
GoogleAnalyticsUniversalTracker.prototype.trackSocial = function(network, action, target) {
sendToGa('send', {
'hitType': 'social',
'socialNetwork': network,
'socialAction': action,
'socialTarget': target
});
};

// https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
GoogleAnalyticsUniversalTracker.prototype.setDimension = function(index, value) {
sendToGa('set', 'dimension' + index, String(value));
};

function sendToGa() {
if (typeof window.ga === "function") {
ga.apply(window, arguments);
}
}

GOVUK.GoogleAnalyticsUniversalTracker = GoogleAnalyticsUniversalTracker;
})();
31 changes: 31 additions & 0 deletions javascripts/govuk/analytics/print-intent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Extension to monitor attempts to print pages.
(function() {

"use strict";
var printAttempt = (function() {
GOVUK.analytics.trackEvent('Print Intent', document.location.pathname);
});

// Most browsers
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print'),
mqlListenerCount = 0;
mediaQueryList.addListener(function(mql) {
if (!mql.matches && mqlListenerCount === 0) {
printAttempt();
mqlListenerCount++;
// If we try and print again in 3 seconds, don't log it
window.setTimeout(function(){
mqlListenerCount = 0;
// printing will be tracked again now
},1e3);
}
});
}

// IE < 10
if(window.onafterprint){
window.onafterprint = printAttempt;
}

}());
20 changes: 20 additions & 0 deletions javascripts/govuk/analytics/sample-init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(function() {
"use strict";

// Load Google Analytics libraries
GOVUK.Tracker.load();

// Use document.domain in dev, preview and staging so that tracking works
// Otherwise explicitly set the domain as www.gov.uk (and not gov.uk).
var cookieDomain = (document.domain === 'www.gov.uk') ? '.www.gov.uk' : document.domain;

// Configure profiles, setup custom vars, track initial pageview
var analytics = new GOVUK.Tracker({
universalId: 'UA-XXXXXXXX-X',
classicId: 'UA-XXXXXXXX-X',
cookieDomain: cookieDomain
});

// Make interface public for virtual pageviews and events
GOVUK.analytics = analytics;
})();
67 changes: 67 additions & 0 deletions javascripts/govuk/analytics/tracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
(function() {
"use strict";
window.GOVUK = window.GOVUK || {};

var Tracker = function(config) {

this.universal = new GOVUK.GoogleAnalyticsUniversalTracker(config.universalId, config.cookieDomain);
this.classic = new GOVUK.GoogleAnalyticsClassicTracker(config.classicId, config.cookieDomain);

// Dimensions should be set before page view is tracked

if (config.prePageviewConfiguration && typeof config.prePageviewConfiguration === 'function') {
config.prePageviewConfiguration();
}

this.trackPageview();

};

Tracker.load = function() {
GOVUK.GoogleAnalyticsClassicTracker.load();
GOVUK.GoogleAnalyticsUniversalTracker.load();
};

Tracker.prototype.trackPageview = function(path, title) {
this.classic.trackPageview(path);
this.universal.trackPageview(path, title);
};

/*
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
options.label – Useful for categorizing events (eg nav buttons)
options.value – Values must be non-negative. Useful to pass counts
options.nonInteraction – Prevent event from impacting bounce rate
*/
Tracker.prototype.trackEvent = function(category, action, options) {
this.classic.trackEvent(category, action, options);
this.universal.trackEvent(category, action, options);
};

Tracker.prototype.trackShare = function(network) {
var target = location.pathname;
this.classic.trackSocial(network, 'share', target);
this.universal.trackSocial(network, 'share', target);
};

/*
Assumes that the index of the dimension is the same for both classic and universal. Check this for your app before using this
*/
Tracker.prototype.setDimension = function(index, value, name, scope) {
var PAGE_LEVEL_SCOPE = 3;
scope = scope || PAGE_LEVEL_SCOPE;

if (typeof index !== "number") {
index = parseInt(index, 10);
}

if (typeof scope !== "number") {
scope = parseInt(scope, 10);
}

this.universal.setDimension(index, value);
this.classic.setCustomVariable(index, value, name, scope);
};

GOVUK.Tracker = Tracker;
})();
Loading

0 comments on commit ee2fd0d

Please sign in to comment.