app/assets/javascripts/support/angulartics.js
/**
* @license Angulartics v0.17.2
* (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
* License: MIT
*/
(function(angular, analytics) {
'use strict';
var angulartics = window.angulartics || (window.angulartics = {});
angulartics.waitForVendorCount = 0;
angulartics.waitForVendorApi = function (objectName, delay, containsField, registerFn, onTimeout) {
if (!onTimeout) { angulartics.waitForVendorCount++; }
if (!registerFn) { registerFn = containsField; containsField = undefined; }
if (!Object.prototype.hasOwnProperty.call(window, objectName) || (containsField !== undefined && window[objectName][containsField] === undefined)) {
setTimeout(function () { angulartics.waitForVendorApi(objectName, delay, containsField, registerFn, true); }, delay);
}
else {
angulartics.waitForVendorCount--;
registerFn(window[objectName]);
}
};
/**
* @ngdoc overview
* @name angulartics
*/
angular.module('angulartics', [])
.provider('$analytics', function () {
var settings = {
pageTracking: {
autoTrackFirstPage: true,
autoTrackVirtualPages: true,
trackRelativePath: false,
autoBasePath: false,
basePath: ''
},
eventTracking: {},
bufferFlushDelay: 1000, // Support only one configuration for buffer flush delay to simplify buffering
developerMode: false // Prevent sending data in local/development environment
};
// List of known handlers that plugins can register themselves for
var knownHandlers = [
'pageTrack',
'eventTrack',
'setAlias',
'setUsername',
'setAlias',
'setUserProperties',
'setUserPropertiesOnce',
'setSuperProperties',
'setSuperPropertiesOnce'
];
// Cache and handler properties will match values in 'knownHandlers' as the buffering functons are installed.
var cache = {};
var handlers = {};
// General buffering handler
var bufferedHandler = function(handlerName){
return function(){
if(angulartics.waitForVendorCount){
if(!cache[handlerName]){ cache[handlerName] = []; }
cache[handlerName].push(arguments);
}
};
};
// As handlers are installed by plugins, they get pushed into a list and invoked in order.
var updateHandlers = function(handlerName, fn){
if(!handlers[handlerName]){
handlers[handlerName] = [];
}
handlers[handlerName].push(fn);
return function(){
var handlerArgs = arguments;
angular.forEach(handlers[handlerName], function(handler){
handler.apply(this, handlerArgs);
}, this);
};
};
// The api (returned by this provider) gets populated with handlers below.
var api = {
settings: settings
};
// Will run setTimeout if delay is > 0
// Runs immediately if no delay to make sure cache/buffer is flushed before anything else.
// Plugins should take care to register handlers by order of precedence.
var onTimeout = function(fn, delay){
if(delay){
setTimeout(fn, delay);
} else {
fn();
}
};
var provider = {
$get: function() { return api; },
api: api,
settings: settings,
virtualPageviews: function (value) { this.settings.pageTracking.autoTrackVirtualPages = value; },
firstPageview: function (value) { this.settings.pageTracking.autoTrackFirstPage = value; },
withBase: function (value) { this.settings.pageTracking.basePath = (value) ? angular.element('base').attr('href').slice(0, -1) : ''; },
withAutoBase: function (value) { this.settings.pageTracking.autoBasePath = value; },
developerMode: function(value) { this.settings.developerMode = value; }
};
// General function to register plugin handlers. Flushes buffers immediately upon registration according to the specified delay.
var register = function(handlerName, fn){
api[handlerName] = updateHandlers(handlerName, fn);
var handlerSettings = settings[handlerName];
var handlerDelay = (handlerSettings) ? handlerSettings.bufferFlushDelay : null;
var delay = (handlerDelay !== null) ? handlerDelay : settings.bufferFlushDelay;
angular.forEach(cache[handlerName], function (args, index) {
onTimeout(function () { fn.apply(this, args); }, index * delay);
});
};
var capitalize = function (input) {
return input.replace(/^./, function (match) {
return match.toUpperCase();
});
};
// Adds to the provider a 'register#{handlerName}' function that manages multiple plugins and buffer flushing.
var installHandlerRegisterFunction = function(handlerName){
var registerName = 'register'+capitalize(handlerName);
provider[registerName] = function(fn){
register(handlerName, fn);
};
api[handlerName] = updateHandlers(handlerName, bufferedHandler(handlerName));
};
// Set up register functions for each known handler
angular.forEach(knownHandlers, installHandlerRegisterFunction);
return provider;
})
.run(['$rootScope', '$window', '$analytics', '$injector', function ($rootScope, $window, $analytics, $injector) {
if ($analytics.settings.pageTracking.autoTrackFirstPage) {
$injector.invoke(['$location', function ($location) {
/* Only track the 'first page' if there are no routes or states on the page */
var noRoutesOrStates = true;
if ($injector.has('$route')) {
var $route = $injector.get('$route');
for (var route in $route.routes) {
noRoutesOrStates = false;
break;
}
} else if ($injector.has('$state')) {
var $state = $injector.get('$state');
for (var state in $state.get()) {
noRoutesOrStates = false;
break;
}
}
if (noRoutesOrStates) {
if ($analytics.settings.pageTracking.autoBasePath) {
$analytics.settings.pageTracking.basePath = $window.location.pathname;
}
if ($analytics.settings.trackRelativePath) {
var url = $analytics.settings.pageTracking.basePath + $location.url();
$analytics.pageTrack(url, $location);
} else {
$analytics.pageTrack($location.absUrl(), $location);
}
}
}]);
}
if ($analytics.settings.pageTracking.autoTrackVirtualPages) {
$injector.invoke(['$location', function ($location) {
if ($analytics.settings.pageTracking.autoBasePath) {
/* Add the full route to the base. */
$analytics.settings.pageTracking.basePath = $window.location.pathname + "#";
}
if ($injector.has('$route')) {
$rootScope.$on('$routeChangeSuccess', function (event, current) {
if (current && (current.$$route||current).redirectTo) return;
var url = $analytics.settings.pageTracking.basePath + $location.url();
$analytics.pageTrack(url, $location);
});
}
if ($injector.has('$state')) {
$rootScope.$on('$stateChangeSuccess', function (event, current) {
var url = $analytics.settings.pageTracking.basePath + $location.url();
$analytics.pageTrack(url, $location);
});
}
}]);
}
if ($analytics.settings.developerMode) {
angular.forEach($analytics, function(attr, name) {
if (typeof attr === 'function') {
$analytics[name] = function(){};
}
});
}
}])
.directive('analyticsOn', ['$analytics', function ($analytics) {
function isCommand(element) {
return ['a:','button:','button:button','button:submit','input:button','input:submit'].indexOf(
element.tagName.toLowerCase()+':'+(element.type||'')) >= 0;
}
function inferEventType(element) {
if (isCommand(element)) return 'click';
return 'click';
}
function inferEventName(element) {
if (isCommand(element)) return element.innerText || element.value;
return element.id || element.name || element.tagName;
}
function isProperty(name) {
return name.substr(0, 9) === 'analytics' && ['On', 'Event', 'If', 'Properties', 'EventType'].indexOf(name.substr(9)) === -1;
}
function propertyName(name) {
var s = name.slice(9); // slice off the 'analytics' prefix
if (typeof s !== 'undefined' && s!==null && s.length > 0) {
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
else {
return s;
}
}
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
var eventType = $attrs.analyticsOn || inferEventType($element[0]);
var trackingData = {};
angular.forEach($attrs.$attr, function(attr, name) {
if (isProperty(name)) {
trackingData[propertyName(name)] = $attrs[name];
$attrs.$observe(name, function(value){
trackingData[propertyName(name)] = value;
});
}
});
angular.element($element[0]).bind(eventType, function ($event) {
var eventName = $attrs.analyticsEvent || inferEventName($element[0]);
trackingData.eventType = $event.type;
if($attrs.analyticsIf){
if(! $scope.$eval($attrs.analyticsIf)){
return; // Cancel this event if we don't pass the analytics-if condition
}
}
// Allow components to pass through an expression that gets merged on to the event properties
// eg. analytics-properites='myComponentScope.someConfigExpression.$analyticsProperties'
if($attrs.analyticsProperties){
angular.extend(trackingData, $scope.$eval($attrs.analyticsProperties));
}
$analytics.eventTrack(eventName, trackingData);
});
}
};
}]);
})(angular);