Skip to content

Instantly share code, notes, and snippets.

@jstott
Created November 22, 2013 18:10
Show Gist options
  • Save jstott/7604340 to your computer and use it in GitHub Desktop.
Save jstott/7604340 to your computer and use it in GitHub Desktop.
Durandal plugin to support google analytics. Usage sample: .trackPage ( url, page, title ) .trackEvent ( category, action, label, Value ) .userTimings ( timingCategory, timingVar, timingValue, timingLabel )
/**
* Integrates Google Analytics within Durandal, optionally (re)mapping system.error.
* @module analytics
* @requires system
* @example
// sample configuration from main.js
app.configurePlugins({
... other plugins ...
analytics: {
enable: true, // !system.isDebugging,
localhost: false,
errorOption: { systemError: true, suppress: true },
apiKey: 'your-apikey-here',
domain: 'your-domain-here
}
*/
define(['durandal/system'], function (system) {
var _error = system.error, // if duck-punching system.error
ces = ces || {},
noop = system.noop,
/**
* @class analytics
* @static
*/
analytics = {
/**
* Returns boolean for active state of analytics
* @property {Boolean} isEnabled
*/
isEnabled: false,
/**
* Page tracking allows you to measure the number of views you had of a particular page on your web site. For a web app, you might decide to track a page when a large portion of the screen changes, for example when the user goes from the home screen to the settings screen.
* @method trackPage
* @param {String} url URL URL of the page being tracked. By default, analytics.js sets this to the full document URL, excluding the fragment identifier.
* @param {String} page Page Extra object information to attach to the error log
* @param {String} title Title The title of the page (e.g. homepage)
*/
trackPage: noop,
/**
* Event tracking allows you to measure how users interact with the content of your website
* An event consists of four values that you can use to describe a user's interaction
* @method trackEvent
* @param {String} category Category Typically the object that was interacted with (e.g. Roster, Playlist, Session)
* @required
* @param {String} action Action The type of interaction (e.g. search, sort, callOn, StartSession, StopSession, StartPolling)
* @required
* @param {String} label Label Useful for categorizing events (e.g. nav buttons)
* @param {String} Value Values must be non-negative. Useful to pass counts (e.g. 4 times)
*/
trackEvent: noop,
/**
* User Timings allow developers to measure periods of time using the analytics.js library. This is particularly useful for developers to measure the latency, or time spent, making AJAX requests and loading web resources.
* @method userTimings
* @param {String} timingCategory timingCategory A string for categorizing all user timing variables into logical groups (e.g jQuery).
* @param {String} timingVar timingVar A string to identify the variable being recorded. (e.g. JavaScript Load).
* @param {Number} timingValue timingValue The number of milliseconds in elapsed time to report to Google Analytics. (e.g. 20)
* @param {String} timingLabel timingLabel A string that can be used to add flexibility in visualizing user timings in the reports. (e.g. Google CDN)
*/
userTimings: noop,
/**
In some cases, you might need to anonymize the IP address of the hit (http request) sent to Google Analytics.
* @method anonymizeIp
* @param {Boolean} state state You can anonymize the IP address for all the hits sent from a page (for the lifetime of the tracker object)
*/
anonymizeIp: noop,
/**
* Disables analytics.js from sending data to Google Analytics
* @method optOut
* @param {Boolean} state state To disable tracking, set true
*/
optOut: noop,
/**
* Installs Googles Analytics and system.error mapping if enabled.
* @method install
* @param {object} config Config options for analytics operation
* @example
// durandal fragment from main.js
app.configurePlugins({
... other plugins ...
analytics: {
enable: true, // !system.isDebugging,
localhost: false,
errorOption: { systemError: true, suppress: true },
apiKey: 'your-apikey-here',
domain: 'your-domain-here
}
*/
install: function (config) {
var self = this,
ga = window.ga,
errorOption = defValue(config.errorOption, { error: true, suppress: true }),
enabled = defValue(config.enable, true),
allowLocalHost = defValue(config.localhost, false);
if (!enabled) {
system.log('Plugin:Disabled plugins/analytics');
return;
}
if (!ga) {
abortPlugin('Google "analytics.js" library is missing, not defined as "ga", or is not loaded');
return;
}
if (!config.apiKey) {
abortPlugin('Google analytics not apiKey not defined');
return;
}
if (!config.domain) {
abortPlugin('Google analytics domain not defined');
return;
}
configure();
mapSystemError();
this.isEnabled = true;
function configure() {
if (allowLocalHost) {
ga('create', config.apiKey, config.domain, {
'cookieDomain': 'none'
});
} else {
ga('create', config.apiKey, config.domain);
}
// ga('send', 'pageview', location);
self.trackPage = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift('pageview');
args.unshift('send');
ga.apply(null, args); // use apply vs call as we need to send an array of args, not the argument 'object'
};
//ga('send', 'event', 'category', 'action', 'label', cnt, { 'nonInteraction': 1 });
self.trackEvent = function () { // send an event, but not impact bounce rate.
var args = Array.prototype.slice.call(arguments);
args.unshift('event');
args.unshift('send');
args.push({ 'nonInteraction': 1 });
ga.apply(null, args);
};
self.anonymizeIp = function (state) {
ga('set', 'anonymizeIp', state);
},
self.optOut = function (state) {
if (errorOption.systemError && errorOption.suppress) {
system.error = _error;
}
window['ga-disable-' + ces.analyticsCode] = state;
self.isEnabled = state;
};
}
function mapSystemError() {
if (errorOption.systemError) {
system.error = function (error, extra) {
var errors = Array.prototype.slice.apply(arguments),
errorMessage = errors[0],
errorUrl = errors[1],
errorLine = errors[2],
errorColumn = errors[3] + 1,
lineString = isNaN(errorLine) ? "" : " (line: " + errorLine + "",
columnString = isNaN(errorColumn) ? "" : ", column: " + errorColumn + ")",
errorEvent = "[" + errorMessage + "][" + errorUrl + lineString + columnString + "]";
system.log("Plugin:analytics", { source: "system.error", 'error': errorEvent });
ga('send', 'event', 'Exceptions', 'JS errors', errorEvent);
if (!errorOption.suppress) {
throw error;
}
};
}
}
}
};
return analytics;
/* internals */
function abortPlugin(errorMsg) {
_error(new Error(errorMsg));
}
// helper allows 'falsey' values for default values - so check for undefined vs more typical x || 'dftValue' style
function defValue(source, dftValue) {
return (typeof source !== 'undefined' ? source : dftValue);
}
});
// fragment from main.js
// assumes google analyitc script has been added to your page as google recomends
//specify which plugins to install and their configuration v2.0
app.configurePlugins({
router: true,
dialog: true,
serializer: true,
analytics: {
enable: !system.isDebugging,
localhost: false,
errorOption: { systemError: true, suppress: true },
apiKey: yourKeyHere,
domain: yourDomainHere
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment