Skip to content

Instantly share code, notes, and snippets.

@saadtazi
Last active December 12, 2015 08:09
Show Gist options
  • Save saadtazi/4742356 to your computer and use it in GitHub Desktop.
Save saadtazi/4742356 to your computer and use it in GitHub Desktop.
canjs google analtyics plugin

Usage

A canjs Control that setup Google Analytics (injects JS) and can automatically:

  • track hashChange (on or off) as pageView
  • track any can.Model / Observe / Model.List / Observe.List events as Google Analytics events

Observe Event

Per observe Class, you can pass the following configuration attribute:

  • construct: the Constructor function (type "Function", mandatory)
  • listenTo: an array of string event (type Array, mandatory)
  • trackingCategory: a string to (type String, optional - default: model.shortName) (@todo: rename to trackingCategory)
  • labelAttribute: an attribute name (type string, optional - default: 'id')

The following value will be sent to Google:

  • category: trackingCategory or class.shortName or '<unknown_classname>'
  • action: the event name
  • label: instance.attr(labelAttribute) or instance.attr('id')

Setup

An sample is better than 1 thousand words:

require(
    ...
  , 'can-ga'
  , 'path/to/model1',
  , 'path/to/model2'
  ...
  , function(..., Analytics, ModelClass1, ModelClass2...) {
    new Analytics( 
        {
            // options
            trackHashChange: true,  // boolean

            classes: [{  // @todo: rename 'model(s)' because it works with any Observe
                class: ModelClass1,
                // see http://donejs.com/docs.html#!can.Model and http://donejs.com/docs.html#!can.Observe for default events
                // it can also track specific attribute changes
                // for ex., assuming that ModelClass1 instance myModelInstance1 have a 'name' attribute
                // the following options will track every successful call to:
                // - myModelInstance1.save() on new instance ('created' event)
                // - myModelInstance1.save() on existing instance ('updated' event)
                // - myModelInstance1.destroy() ('destroyed' event)
                // - myModelInstance1.attr('name', newValue) ('name' event)
                // - can.trigger(myModelInstance1, 'customEvent') ('customEvent' event)
                listenTo: ['created', 'updated', 'deleted', 'name', 'customEvent']  
            }]
        }
    )
  });
)

TODO

(maybe) No need to have a Control, it can be a Construct... Pros and cons:

  • Pro control: we can add an 'prefix' in option and have multiple analytics trackingID associated to different controls
  • is previous argument really an argument??
define([
'can'
], function (can) {
var globalVar = this; _gaq = globalVar._gaq = globalVar._gaq || []
;
var Analytics = can.Constuct('can.Analytics', {
defaults: {
trackHashChange: true // track hashChange by default
}
}, {
init: function(options) {
var self = this;
if (!options.trackingID) {
console.warn('no Google Analytics Tracking ID provided!! Nothing will be tracked!');
} else {
this.injectScriptTag(options);
// if tracking models or observe or anything that you can bind on
if (options.classes) {
// automatically track model events
can.each(options.classes, function (info) {
can.each(info.listenTo, function(eventName) {
info.construct.bind(eventName, function(ev, instance) {
console.log("Analytics: received ev:", ev, " on instance: ", instance);
self.trackObserveChange(ev, instance, info);
});
});
});
}
if (options.trackHashChange) {
// this probably does not work in ie < 8
// but I was not able to make that line (found in can.route) works (if location.hash = '#!ss': // or 'rr=23'
// (triggers a jQuery exception)
// can.bind.call(window,'hashchange', setState);
window.onhashchange = function() {
console.log("Analytics: received change evt on route");
self.trackHashChange();
};
}
}
},
trackHashChange: function() {
globalVar._gaq.push(['_trackPageview', location.hash.replace("#!", "/-hash-/")]);
},
trackObserveChange: function(ev, instance, info) {
//category, action, opt_label, opt_value, opt_noninteraction
var gaCall = [
'_trackEvent',
info.trackingCategory || info.construct.shortName || "<unknown_observe>",
ev.type,
instance.attr(info.labelAttribute || 'id') || null
];
globalVar._gaq.push(gaCall);
},
injectScriptTag: function(options) {
_gaq = globalVar._gaq = globalVar._gaq || [];
globalVar._gaq.push(['_setAccount', options.trackingID]);
if (options.initActions) { // example: [['_setDomainName', 'mydomain.com'], ]
can.each(initActions, function(initAction) {
globalVar._gaq.push(initAction);
});
}
globalVar._gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
}
});
return Analytics;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment