1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

[bug] Google Analytics not loaded

- angular-google-analytics plugin update from 0.0.15 to 1.1.7
- google analytics config moved into secrets.yml/production
This commit is contained in:
Sylvain 2016-05-24 14:51:23 +02:00
parent dc5c125cdb
commit 139d7d9f0f
29 changed files with 5124 additions and 3029 deletions

View File

@ -26,16 +26,16 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
// Google analytics
<% if Rails.env.production? %>
<% #if Rails.env.production? %>
AnalyticsProvider.setAccount(Fablab.gaId);
// track all routes (or not)
AnalyticsProvider.trackPages(true);
AnalyticsProvider.setDomainName(Fablab.defaultHost);
AnalyticsProvider.useAnalytics(true);
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
<% else %>
AnalyticsProvider.setAccount('DISABLED');
<% end %>
<% #else %>
//AnalyticsProvider.setAccount('DISABLED');
<% #end %>
// Custom messages for the date-picker widget
uibDatepickerPopupConfig.closeText = Fablab.translations.app.shared.buttons.close;
@ -62,8 +62,8 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
// Set the langage of the instance (from ruby configuration)
$translateProvider.preferredLanguage(Fablab.locale);
}]).run(["$rootScope", "$log", "AuthService", "Auth", "amMoment", "$state", "editableOptions",
function($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions) {
}]).run(["$rootScope", "$log", "AuthService", "Auth", "amMoment", "$state", "editableOptions", 'Analytics',
function($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions, Analytics) {
// Angular-moment (date-time manipulations library)
amMoment.changeLocale(Fablab.moment_locale);
@ -121,6 +121,10 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
});
});
// This code does nothing but it is here to remember to not remove the Analytics dependency,
// see https://github.com/revolunet/angular-google-analytics#automatic-page-view-tracking
Analytics.pageView();
}]).constant('angularMomentConfig', {
timezone: Fablab.timezone
});

View File

@ -45,7 +45,7 @@
//= require angular-unsavedChanges/src/unsavedChanges
//= require angular-loading-bar/src/loading-bar
//= require angular-scroll/angular-scroll
//= require angular-google-analytics/src/angular-google-analytics
//= require angular-google-analytics/dist/angular-google-analytics
//= require dirDisqus
//= require humanize
//= require underscore/underscore

View File

@ -25,8 +25,8 @@
Fablab.withoutPlans = ('<%= Rails.application.secrets.fablab_without_plans %>' == 'true');
Fablab.disqusShortname = "<%= Rails.application.secrets.disqus_shortname %>";
Fablab.defaultHost = "<%= ENV['DEFAULT_HOST'] %>";
Fablab.gaId = "<%= ENV['GA_ID'] %>";
Fablab.defaultHost = "<%= Rails.application.secrets.default_host %>";
Fablab.gaId = "<%= Rails.application.secrets.google_analytics_id %>";
// i18n stuff
Fablab.locale = "<%= I18n.locale %>";

View File

@ -29,7 +29,7 @@
"angular-unsavedChanges": "~0.2.0",
"angular-loading-bar": ">=0.4.3",
"angular-scroll": "~0.6.5",
"angular-google-analytics": "~0.0.3",
"angular-google-analytics": "~1.1.7",
"underscore": "~1.7.0",
"angular-devise": "~1.0.2",
"elasticsearch": "~3.1.1",

View File

@ -113,3 +113,4 @@ production:
openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %>
openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %>
openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %>
google_analytics_id: <%= ENV["GA_ID"] %>

View File

@ -1,9 +1,24 @@
{
"author": "revolunet",
"authors": [
"Julien Bouquillon <julien@revolunet.com> (https://github.com/revolunet)",
"Justin Saunders (https://github.com/justinsa)",
"Chris Esplin (https://github.com/deltaepsilon)",
"Adam Misiorny (https://github.com/adam187)"
],
"name": "angular-google-analytics",
"description": "Easy Analytics for your AngularJS application",
"version": "0.0.15",
"keywords": [
"analytics",
"angular",
"angularjs",
"angular-js",
"google-analytics",
"tracking",
"visitor-tracking",
"universal-analytics"
],
"homepage": "https://github.com/revolunet/angular-google-analytics",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/revolunet/angular-google-analytics.git"
@ -16,13 +31,14 @@
"angular-mocks": ">=1",
"angular-scenario": ">=1"
},
"_release": "0.0.15",
"version": "1.1.7",
"_release": "1.1.7",
"_resolution": {
"type": "version",
"tag": "0.0.15",
"commit": "966ec3a7752cfe6eb11c5649134bb07e21d1207c"
"tag": "1.1.7",
"commit": "bb470439432bb3b039a0f02995b37a8843bd6df7"
},
"_source": "git://github.com/revolunet/angular-google-analytics.git",
"_target": "~0.0.3",
"_source": "https://github.com/revolunet/angular-google-analytics.git",
"_target": "~1.1.7",
"_originalSource": "angular-google-analytics"
}

View File

@ -1,4 +1,5 @@
*.DS_Store
*.log
node_modules
components
bower_components

View File

@ -1,34 +0,0 @@
# 0.0.1 (2014-02-18)
## Features
### tracker
* add ecommerce tracking (5dac41c)
* added setDomainName (ce2889f)
## Bug fixes
### main
* use $window._gaq (a630757)
# 0.0.1 (2014-02-18)
## Features
### tracker
* add ecommerce tracking (5dac41c)
* added setDomainName (ce2889f)
## Bug fixes
### main
* use $window._gaq (a630757)

View File

@ -1,8 +1,6 @@
/* global require, module, */
'use strict';
/* global require, module */
module.exports = function(grunt) {
'use strict';
// Project configuration.
grunt.initConfig({
@ -13,6 +11,7 @@ module.exports = function(grunt) {
' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
' * @link <%= pkg.homepage %>\n' +
' * @author <%= pkg.author %>\n' +
' * @contributors <%= pkg.contributors %>\n' +
' * @license MIT License, http://www.opensource.org/licenses/MIT\n' +
' */\n'
},
@ -24,7 +23,7 @@ module.exports = function(grunt) {
banner: '<%= meta.banner %>'
},
dist: {
src: ['src/*.js'],
src: ['index.js'],
dest: '<%= dirs.dest %>/<%= pkg.name %>.js'
}
},
@ -37,21 +36,11 @@ module.exports = function(grunt) {
dest: '<%= dirs.dest %>/<%= pkg.name %>.min.js'
}
},
changelog: {
options: {
dest: 'CHANGELOG.md',
versionFile: 'package.json'
}
},
stage: {
options: {
files: ['CHANGELOG.md']
}
},
stage: {},
release: {
options: {
commitMessage: '<%= version %>',
tagName: 'v<%= version %>',
tagName: '<%= version %>',
file: 'package.json',
push: false,
tag: false,
@ -60,7 +49,7 @@ module.exports = function(grunt) {
}
},
jshint: {
files: ['Gruntfile.js', 'src/*.js', 'test/unit/*.js'],
files: ['Gruntfile.js', 'index.js', 'test/*.js', 'test/unit/*.js'],
options: {
curly: true,
browser: true,
@ -83,10 +72,6 @@ module.exports = function(grunt) {
}
}
},
// watch: {
// files: '<config:jshint.files>',
// tasks: 'default'
// },
karma: {
test: {
options: {
@ -100,7 +85,7 @@ module.exports = function(grunt) {
}
},
options: {
configFile: 'test/karma.conf.js'
configFile: __dirname + '/test/karma.conf.js'
}
}
});
@ -114,12 +99,6 @@ module.exports = function(grunt) {
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Load the plugin that provides the "watch" task.
//grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-release');
grunt.loadNpmTasks('grunt-conventional-changelog');
grunt.registerTask('stage', 'git add files before running the release task', function () {
var files = this.options().files;
grunt.util.spawn({
@ -128,13 +107,12 @@ module.exports = function(grunt) {
}, grunt.task.current.async());
});
grunt.renameTask('release', 'originalRelease');
// Default task.
grunt.registerTask('default', ['test']);
// Static analysis
grunt.registerTask('lint', ['jshint']);
// Test tasks.
grunt.registerTask('test', ['jshint', 'karma:test']);
grunt.registerTask('test-server', ['karma:server']);
@ -142,16 +120,16 @@ module.exports = function(grunt) {
// Build task.
grunt.registerTask('build', ['test', 'concat', 'uglify']);
// release task
// Release task.
grunt.registerTask('release', ['build']);
// Provides the "karma" task.
grunt.registerMultiTask('karma', 'Starts up a karma server.', function() {
var done = this.async();
require('karma').server.start(this.options(), function(code) {
var karma = require('karma'),
done = this.async();
var server = new karma.Server(this.options(), function(code) {
done(code === 0);
});
server.start();
});
};

View File

@ -0,0 +1,19 @@
Copyright (c) 2015 "Julien Bouquillon <julien@bouquillon.com>"
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,285 +1,669 @@
# angular-google-analytics
[![Bower Version](https://img.shields.io/bower/v/angular-google-analytics.svg)](https://github.com/revolunet/angular-google-analytics)
[![NPM Version](https://img.shields.io/npm/v/angular-google-analytics.svg)](https://www.npmjs.com/package/angular-google-analytics)
[![Master Build Status](https://codeship.com/projects/ba7a0af0-33fe-0133-927c-127922174191/status?branch=master)](https://codeship.com/projects)
[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/revolunet/angular-google-analytics/blob/master/LICENSE)
This service lets you integrate google analytics tracker in your AngularJS applications easily.
You can use basic functions, `Analytics.trackEvent('video', 'play', 'django.mp4');` or more advanced e-commerce features like product tracking, promo codes, transactions...
Proudly brought to you by [@revolunet](http://twitter.com/revolunet) and [@deltaepsilon](https://github.com/deltaepsilon).
Proudly brought to you by [@revolunet](http://twitter.com/revolunet), [@deltaepsilon](https://github.com/deltaepsilon), [@justinsa](https://github.com/justinsa) and [contributors](https://github.com/revolunet/angular-google-analytics/graphs/contributors)
## features
## Features
- configurable
- highly configurable
- automatic page tracking
- events tracking
- e-commerce tracking
- enhanced e-commerce tracking
- event tracking
- e-commerce (ecommerce.js) support
- enhanced e-commerce (ec.js) support
- multiple-domains
- ga.js and and analytics.js support
- ga.js (classic) and analytics.js (universal) support
- cross-domain support
- multiple tracking objects
- hybrid mobile application support
- offline mode
- analytics.js advanced debugging support
## install
## Installation
`bower install angular-google-analytics`
Or alternatively, grab the dist/angular-google-analytics.min.js and include it in your project
## example
In your application, declare the angular-google-analytics module dependency.
```html
<script src="bower_components/angular-google-analytics/dist/angular-google-analytics.js"></script>
```
In your application, declare dependency injection like so.
```javascript
angular.module('myModule', ['angular-google-analytics']);
```
## Configure Service
```js
app.config(function (AnalyticsProvider) {
// Add configuration code as desired - see below
});
```
### Configuration Method Chaining
```js
// All configuration methods return the provider object and can be chained to reduce typing.
// For example:
AnalyticsProvider
.logAllCalls(true)
.startOffline(true)
.useEcommerce(true, true);
```
### Use Classic Analytics
```js
// Use ga.js (classic) instead of analytics.js (universal)
// By default, universal analytics is used, unless this is called with a falsey value.
AnalyticsProvider.useAnalytics(false);
```
### Set Google Analytics Accounts (Required)
```js
// Set a single account
AnalyticsProvider.setAccount('UA-XXXXX-xx');
```
**Note:** the single account syntax is internally represented as an unnamed account object that will have all properties defined to defaults, except for name.
```js
var app = angular.module('app', ['angular-google-analytics'])
.config(function(AnalyticsProvider) {
// initial configuration
AnalyticsProvider.setAccount('UA-XXXXX-xx');
// using multiple tracking objects (analytics.js only)
// AnalyticsProvider.setAccount([
// { tracker: 'UA-12345-12', name: "tracker1" },
// { tracker: 'UA-12345-34', name: "tracker2" }
// ]);
// Set multiple accounts
// Universal Analytics only
AnalyticsProvider.setAccount([
{ tracker: 'UA-12345-12', name: "tracker1" },
{ tracker: 'UA-12345-34', name: "tracker2" }
]);
```
**Note:** the above account objects will have all properties defined to defaults that are not defined.
// track all routes (or not)
AnalyticsProvider.trackPages(true);
// track all url query params (default is false)
AnalyticsProvider.trackUrlParams(true);
```js
// Set a single account with all properties defined
// Universal Analytics only
AnalyticsProvider.setAccount({
tracker: 'UA-12345-12',
name: "tracker1",
fields: {
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
// See: [Analytics Field Reference](https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference) for a list of all fields.
},
crossDomainLinker: true,
crossLinkDomains: ['domain-1.com', 'domain-2.com'],
displayFeatures: true,
enhancedLinkAttribution: true,
select: function (args) {
// This function is used to qualify or disqualify an account object to be run with commands.
// If the function does not exist, is not a function, or returns true then the account object will qualify.
// If the function exists and returns false then the account object will be disqualified.
// The 'args' parameter is the set of arguments (which contains the command name) that will be sent to Universal Analytics.
return true;
},
set: {
forceSSL: true
// This is any set of `set` commands to make for the account immediately after the `create` command for the account.
// The property key is the command and the property value is passed in as the argument, _e.g._, `ga('set', 'forceSSL', true)`.
// Order of commands is not guaranteed as it is dependent on the implementation of the `for (property in object)` iterator.
},
trackEvent: true,
trackEcommerce: true
});
```
**Note:** the above properties are referenced and discussed in proceeding sections.
**Note:** the `cookieConfig` property is being **deprecated** for the `fields` property. At present `cookieConfig` is an alias for `fields` in an account object.
// Optional set domain (Use 'none' for testing on localhost)
// AnalyticsProvider.setDomainName('XXX');
### Use Display Features
```js
// Use display features module
AnalyticsProvider.useDisplayFeatures(true);
```
// Use display features plugin
AnalyticsProvider.useDisplayFeatures(true);
If set to a truthy value then the display features module is loaded with Google Analytics.
// url prefix (default is empty)
// - for example: when an app doesn't run in the root directory
AnalyticsProvider.trackPrefix('my-application');
In the case of universal analytics, this value will be used as the default for any tracker that does not have the `displayFeatures` property defined. All trackers with `displayFeatures: true` will be registered for display features.
// Use analytics.js instead of ga.js
AnalyticsProvider.useAnalytics(true);
### Use Enhanced Link Attribution
```js
// Enable enhanced link attribution module
AnalyticsProvider.useEnhancedLinkAttribution(true);
```
// Use cross domain linking
AnalyticsProvider.useCrossDomainLinker(true);
AnalyticsProvider.setCrossLinkDomains(['domain-1.com', 'domain-2.com']);
If set to a truthy value then the enhanced link attribution module is loaded with Google Analytics.
// Ignore first page view... helpful when using hashes and whenever your bounce rate looks obscenely low.
AnalyticsProvider.ignoreFirstPageLoad(true);
In the case of universal analytics, this value will be used as the default for any tracker that does not have the `enhancedLinkAttribution` property defined. All trackers with `enhancedLinkAttribution: true` will be registered for enhanced link attribution.
// Enabled eCommerce module for analytics.js(uses legacy ecommerce plugin)
AnalyticsProvider.useECommerce(true, false);
### Use Cross Domain Linking
```js
// Use cross domain linking and set cross-linked domains
AnalyticsProvider.useCrossDomainLinker(true);
AnalyticsProvider.setCrossLinkDomains(['domain-1.com', 'domain-2.com']);
```
// Enabled eCommerce module for analytics.js(uses ec plugin instead of ecommerce plugin)
AnalyticsProvider.useECommerce(true, true);
If set to a truthy value then the cross-linked domains are registered with Google Analytics.
// Enable enhanced link attribution
AnalyticsProvider.useEnhancedLinkAttribution(true);
In the case of universal analytics, these values will be used as the default for any tracker that does not have the `crossDomainLinker` and `crossLinkDomains` properties defined. All trackers with `crossDomainLinker: true` will register the cross-linked domains.
// Enable analytics.js experiments
AnalyticsProvider.setExperimentId('12345');
### Set Cookie Configuration
**NOTE:** This method is being **deprecated**. Use the `fields` property on the account object instead.
This property is defined for universal analytics account objects only and is set to `auto` by default.
// Set custom cookie parameters for analytics.js
AnalyticsProvider.setCookieConfig({
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
});
```js
// Set custom cookie parameters
AnalyticsProvider.setCookieConfig({
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
});
```
This cookie configuration will be used as the default for any tracker that does not have the `cookieConfig` property defined.
// change page event name
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
### Track Events
This property is defined for universal analytics account objects only and is false by default.
If `trackEvent: true` for an account object then all `trackEvent` calls will be supported for that account object.
// Delay script tage creation
// must manually call Analytics.createScriptTag(cookieConfig) or Analytics.createAnalyticsScriptTag(cookieConfig)
AnalyticsProvider.delayScriptTag(true);
}))
.run(function(Analytics) {
// In case you are relying on automatic page tracking, you need to inject Analytics
// at least once in your application (for example in the main run() block)
Set `trackEvent: false` for an account object that is not tracking events.
### Track E-Commerce
This property is defined for universal analytics account objects only. This property defaults to true if e-commerce is enabled (either classic or enhanced) and false otherwise.
If `trackEcommerce: true` for an account object then all e-commerce calls will be supported for that account object.
Set `trackEcommerce: false` for an account object that is not tracking e-commerce.
### Enable E-Commerce
```js
// Enable e-commerce module (ecommerce.js)
AnalyticsProvider.useECommerce(true, false);
// Enable enhanced e-commerce module (ec.js)
// Universal Analytics only
AnalyticsProvider.useECommerce(true, true);
// Set Currency
// Default is 'USD'. Use ISO currency codes.
AnalyticsProvider.setCurrency('CDN');
```
**Note:** When enhanced e-commerce is enabled, the legacy e-commerce module is disabled and unsupported. This is a requirement of Google Analytics.
### Set Route Tracking Behaviors
```js
// Track all routes (default is true).
AnalyticsProvider.trackPages(true);
// Track all URL query params (default is false).
AnalyticsProvider.trackUrlParams(true);
// Ignore first page view (default is false).
// Helpful when using hashes and whenever your bounce rate looks obscenely low.
AnalyticsProvider.ignoreFirstPageLoad(true);
// URL prefix (default is empty).
// Helpful when the app doesn't run in the root directory.
AnalyticsProvider.trackPrefix('my-application');
// Change the default page event name.
// Helpful when using ui-router, which fires $stateChangeSuccess instead of $routeChangeSuccess.
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
// RegEx to scrub location before sending to analytics.
// Internally replaces all matching segments with an empty string.
AnalyticsProvider.setRemoveRegExp(/\/\d+?$/);
// Activate reading custom tracking urls from $routeProvider config (default is false)
// This is more flexible than using RegExp and easier to maintain for multiple parameters.
// It also reduces tracked pages to routes defined in the $routeProvider and therefor reduces
// bounce rate created by redirects. You can also exclude certain routes from tracking by
// adding 'doNotTrack' property
AnalyticsProvider.readFromRoute(true);
// Add custom routes to the $routeProvider like this
$routeProvider
.when('/sessions', {
templateUrl: 'list.html',
controller: 'ListController'
})
.controller('SampleController', function(Analytics) {
// create a new pageview event
Analytics.trackPage('/video/detail/XXX');
// or create a new pageview event with optional page title
Analytics.trackPage('/video/detail/XXX', 'Video XXX');
// or create a new pageview event with optional page title, custom dimension and metric
// (analytics.js only)
Analytics.trackPage('/video/detail/XXX', 'Video XXX', {dimension15: 'My Custom Dimension', metric18: 8000});
// create a new tracking event
Analytics.trackEvent('video', 'play', 'django.mp4');
// create a new tracking event with optional value
Analytics.trackEvent('video', 'play', 'django.mp4', 4);
// create a new tracking event with optional value and noninteraction flag
Analytics.trackEvent('video', 'play', 'django.mp4', 4, true);
// create a new tracking event with optional value, noninteraction flag, and custom dimension and metric
// (analytics.js only)
Analytics.trackEvent('video', 'play', 'django.mp4', 4, true, {dimension15: 'My Custom Dimension', metric18: 8000});
// tracking e-commerce
// - create transaction
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands', 'EUR');
// - add items to transaction
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
Analytics.addItem('1', 'sku-2', 'Test product 2', 'Testing', '1', '1');
// - complete transaction
Analytics.trackTrans();
// Enhanced Ecommerce Tracking
// Product Impression Tracking
Analytics.addImpression(productId, name, list, brand, category, variant, position, price);
Analytics.pageView();
// example:
Analytics.addImpression('sku-1', 'Test Product 1', 'Category List', 'Brand 1', 'Category-1', 'variant-1', '1', '24990');
Analytics.addImpression('sku-2', 'Test Product 2', 'Category List', 'Brand 2', 'Category-1', 'variant-3', '2', '2499');
Analytics.pageView();
// Product Click Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.productClick(listName);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.productClick('Search Result');
// Product Detail Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackDetail();
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackDetail();
// Add to cart Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCart('add');
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('add');
// Remove from cart Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCart('remove');
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('remove');
// Checkout Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCheckout(checkoutStep, optionValue);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCheckout(1, 'Visa');
// Transaction Tracking
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackTransaction(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2222', '1', 'MEN10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '1111', '1', 'WOMEN10', '1');
Analytics.trackTransaction('T1234', 'Online Store - Web', '3333', '10', '200', 'FLAT10', '', '', '');
// Promotion Impressions
Analytics.addPromo(productId, name, creative, position);
Analytics.addPromo(productId, name, creative, position);
Analytics.addPromo(productId, name, creative, position);
Analytics.pageView();
// Note: Before tracking promotion Click, call pageView otherwise promotion impressions will be treated as promotion clicks
// example:
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.pageView();
// Promotion Clicks
Analytics.addPromo(promotionId, promotionName, creative, position);
Analytics.addPromo(promotionId, promotionName, creative, position);
Analytics.addPromo(promotionId, promotionName, creative, position);
Analytics.promoClick(promotionName);
// example:
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.promoClick('Summer Sale');
// populate a custom dimension
Analytics.set('dimension1', 'Paid');
// Manually create script tag after using delayScriptTag
Analytics.createScriptTag({userId: 1234});
// Manually create Analytics script tag after using delayScriptTag
Analytics.createAnalyticsScriptTag({userId: 1234})
//Track User Timings
Analytics.trackTimings(timingCategory, timingVar, timingValue, timingLabel)
//example:
var endTime = new Date().getTime();
var timeSpent = endTime - startTime;
Analytics.trackTimings('Time to Checkout', 'User Timings', timeSpent);
.when('/session/:id',{
templateUrl : 'master.html',
controller: 'MasterController',
pageTrack: '/session'
})
.when('/member/:sessionId/:memberId', {
templateUrl : 'member.html',
controller: 'CardController',
pageTrack: '/member',
})
.otherwise({
templateUrl: '404.html',
doNotTrack: true
});
```
### Set Domain Name
```js
// Set the domain name
AnalyticsProvider.setDomainName('XXX');
```
**Note:** Use the string `'none'` for testing on localhost.
### Enable Experiment (universal analytics only)
```js
// Enable analytics.js experiments
AnalyticsProvider.setExperimentId('12345');
```
**Note:** only a single experiment can be defined.
### directive
Alternatively you can use a directive to avoid filling controllers with `Analytics.trackEvent()` statements. Note: the directive does not create an isolate scope.
<button type="button" ga-track-event="['video', 'play', 'django.mp4']"></button>
<!-- OR -->
<button type="button" ga-track-event="['video', 'play', 'django.mp4', 4, true, {dimension15: 'My Custom Dimension', metric18: 8000}]"></button>
You can define the properties on your controller too `$scope.event = ['video', 'play', 'django.mp4']` and reference them
<button type="button" ga-track-event="event"></button>
## configuration
### Support Hybrid Mobile Applications
This property is defined for universal analytics only and is false by default.
```js
// setup your account
AnalyticsProvider.setAccount('UA-XXXXX-xx');
// automatic route tracking (default=true)
AnalyticsProvider.trackPages(false);
// Optional set domain (Use 'none' for testing on localhost)
AnalyticsProvider.setDomainName('XXX');
// Use display features plugin
AnalyticsProvider.useDisplayFeatures(true);
// Use analytics.js instead of ga.js
AnalyticsProvider.useAnalytics(true);
// Ignore first page view.
AnalyticsProvider.ignoreFirstPageLoad(true);
// Enable eCommerce module for analytics.js
AnalyticsProvider.useECommerce(true, false);
// Enable enhanced eCommerce module for analytics.js
AnalyticsProvider.useECommerce(true, true);
// Enable enhanced link attribution module for analytics.js or ga.js
AnalyticsProvider.useEnhancedLinkAttribution(true);
// Enable analytics.js experiments
AnalyticsProvider.setExperimentId('12345');
// Set custom cookie parameters for analytics.js
AnalyticsProvider.setCookieConfig({
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
});
// Change the default page event name. This is useful for ui-router, which fires $stateChangeSuccess instead of $routeChangeSuccess
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
// Delay script tage creation...must manually call Analytics.createScriptTag() or Analytics.createAnalyticsScriptTag() to enable analytics
AnalyticsProvider.delayScriptTag(true);
// Set hybrid mobile application support
AnalyticsProvider.setHybridMobileSupport(true);
```
## AdBlock EasyPrivacy
If set to a truthy value then each account object will disable protocol checking and all injected scripts will use the HTTPS protocol.
### Delay Script Tag Insertion
```js
// Must manually call create script tag method in order to insert and configure Google Analytics:
// Classic analytics: Analytics.createScriptTag();
// Universal analytics: Analytics.createAnalyticsScriptTag();
// Helpful when needing to do advanced configuration or user opt-out and wanting explicit control
// over when the Google Analytics script gets injected.
AnalyticsProvider.delayScriptTag(true);
```
### Offline Mode
```js
// Start in offline mode if set to true. This also calls delayScriptTag(true) since the script cannot be
// fetched if offline and must be manually called when the application goes online.
AnalyticsProvider.startOffline(true);
```
### Disable Analytics / User Opt-out
```js
// Disable analytics data gathering via the user opt-out feature in Google Analytics. More information on this
// is available here: https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#optout.
AnalyticsProvider.disableAnalytics(true);
```
**Note:** Using this configuration option requires that you already know the user wants to opt-out before the analytics script is injected on the page. This is somewhat unlikely for most use cases given the nature of a single page application. This module provides a better alternative with `Offline` mode since you can effectively opt the user out of tracking by enabling offline mode at any time during execution.
### Service Logging
```js
// Log all outbound calls to an in-memory array accessible via ```Analytics.log``` (default is false).
// This is useful for troubleshooting and seeing the order of calls with parameters.
AnalyticsProvider.logAllCalls(true);
```
### Test Mode
```js
// This method is designed specifically for unit testing and entering test mode cannot be changed after
// being called. Test mode skips the insertion of the Google Analytics script tags (both classic and universal)
// and ensures there is a $window.ga() method available for calling by unit tests. This corrects transient
// errors that were seen during unit tests due to the operation of the Google Analytics scripts.
AnalyticsProvider.enterTestMode();
```
### Debug Mode
```js
// Calling this method will enable debugging mode for Universal Analytics. Supplying a truthy value for the
// optional parameter will further enable trace debugging for Universal Analytics. More information on this
// is available here: https://developers.google.com/analytics/devguides/collection/analyticsjs/debugging.
AnalyticsProvider.enterDebugMode(Boolean);
```
## Using the Analytics Service
**IMPORTANT!** Due to how Google Analytics works, it is important to remember that you must always call `Analytics.pageView();` when you want to push setting changes and function calls to Google Analytics.
### Automatic Page View Tracking
If you are relying on automatic page tracking, you need to inject Analytics at least once in your application.
```js
// As an example, add the service to the run call:
app.run(function(Analytics) {});
```
### Declaring a Controller
```js
// As an example, a simple controller to make calls from:
app.controller('SampleController', function (Analytics) {
// Add calls as desired - see below
});
```
### Accessing Configuration Settings
The following configuration settings are intended to be immutable. While the values can be changed in this list by the user, this will not impact the behavior of the service as these values are not referenced internally; exceptions are noted below but are not intended to be utilized in such a way by the user. No guarantee will be made for future versions of this service supporting any functionality beyond reading values from this list.
```js
// This is a mutable array. Changes to this list will impact service behaviors.
Analytics.configuration.accounts;
// If `true` then universal analytics is being used.
// If `false` then classic analytics is being used.
Analytics.configuration.universalAnalytics;
Analytics.configuration.crossDomainLinker;
Analytics.configuration.crossLinkDomains;
Analytics.configuration.currency;
Analytics.configuration.debugMode;
Analytics.configuration.delayScriptTag;
Analytics.configuration.disableAnalytics;
Analytics.configuration.displayFeatures;
Analytics.configuration.domainName;
// ecommerce and enhancedEcommerce are mutually exclusive; either both will be false or one will be true.
Analytics.configuration.ecommerce;
Analytics.configuration.enhancedEcommerce;
Analytics.configuration.enhancedLinkAttribution;
Analytics.configuration.experimentId;
Analytics.configuration.ignoreFirstPageLoad;
Analytics.configuration.logAllCalls;
Analytics.configuration.pageEvent;
Analytics.configuration.removeRegExp;
Analytics.configuration.traceDebuggingMode;
Analytics.configuration.trackPrefix;
Analytics.configuration.trackRoutes;
Analytics.configuration.trackUrlParams;
```
### Get or Set Cookie Configuration
**NOTE:** These methods are being **deprecated**. Use the `fields` property on the account object instead.
```js
// Get the global cookie config.
Analytics.getCookieConfig();
// Set the global cookie config.
// Impacts all future calls for classic analytics (ga.js).
Analytics.setCookieConfig();
```
**Note:** Changing the cookie configuration after the AnalyticsProvider configuration does not update the individual account objects used by universal analytics (analytics.js). If you want to change the account objects used by universal analytics those can be accessed through `Analytics.configuration.accounts`, but such modification to the accounts object is unsupported.
### Get URL
```js
// Returns the current URL that would be sent if a `trackPage` call was made.
// The returned value takes into account all configuration settings that modify the URL.
Analytics.getUrl();
```
### Manual Script Tag Injection
If `delayScriptTag(true)` was set during configuration then manual script tag injection is required. Otherwise, the script tag will be automatically injected and configured when the service is instantiated.
```js
// Manually create classic analytics (ga.js) script tag
Analytics.createScriptTag();
// Manually create universal analytics (analytics.js) script tag
Analytics.createAnalyticsScriptTag();
```
### Advanced Settings / Custom Dimensions
The `set` call allows for advanced configuration and definitions in univeral analytics only. This is a no-op when using classic analytics.
```js
// Set the User Id
Analytics.set('&uid', 1234);
// Register a custom dimension for the default, unnamed account object
// e.g., ga('set', 'dimension1', 'Paid');
Analytics.set('dimension1', 'Paid');
// Register a custom dimenstion for a named account object
// e.g., ga('accountName.set', 'dimension2', 'Paid');
Analytics.set('dimension2', 'Paid', 'accountName');
```
### Page Tracking
```js
// Create a new pageview event
Analytics.trackPage('/video/detail/XXX');
// Create a new pageview event with page title
Analytics.trackPage('/video/detail/XXX', 'Video XXX');
// Create a new pageview event with page title, custom dimension, and custom metric
// Universal Analytics only
Analytics.trackPage('/video/detail/XXX', 'Video XXX', { dimension15: 'My Custom Dimension', metric18: 8000 });
```
### Event Tracking
```js
// Create a new tracking event
Analytics.trackEvent('video', 'play', 'django.mp4');
// Create a new tracking event with a value
Analytics.trackEvent('video', 'play', 'django.mp4', 4);
// Create a new tracking event with a value and non-interaction flag
Analytics.trackEvent('video', 'play', 'django.mp4', 4, true);
// Create a new tracking event with a value, non-interaction flag, custom dimension, and custom metric
// Universal Analytics only
Analytics.trackEvent('video', 'play', 'django.mp4', 4, true, { dimension15: 'My Custom Dimension', metric18: 8000 });
```
### Track User Timings
The `trackTimings` call is available for univeral analytics only. This is a no-op when using classic analytics.
```js
Analytics.trackTimings(timingCategory, timingVar, timingValue, timingLabel);
// example:
var endTime = new Date().getTime(),
timeSpent = endTime - startTime;
Analytics.trackTimings('Time to Checkout', 'User Timings', timeSpent);
```
### Classic E-Commerce (ecommerce.js)
Classic e-commerce and enhanced e-commerce are mutually exclusive.
```js
// Create transaction
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands', 'EUR');
// Add items to transaction
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
Analytics.addItem('1', 'sku-2', 'Test product 2', 'Testing', '1', '1');
// Complete transaction
Analytics.trackTrans();
// Clear transaction
Analytics.clearTrans();
```
### Enhanced E-Commerce (ec.js)
Enhanced e-commerce is only available for universal analytics. Enhanced e-commerce and classic e-commerce are mutually exclusive.
#### Product Impression Tracking
```js
Analytics.addImpression(productId, name, list, brand, category, variant, position, price);
Analytics.pageView();
// example:
Analytics.addImpression('sku-1', 'Test Product 1', 'Category List', 'Brand 1', 'Category-1', 'variant-1', '1', '24990');
Analytics.addImpression('sku-2', 'Test Product 2', 'Category List', 'Brand 2', 'Category-1', 'variant-3', '2', '2499');
Analytics.pageView();
```
#### Product Click Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position, custom);
Analytics.productClick(listName);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.productClick('Search Result');
// example with custom dimension and metric:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1', { dimension4: 'strong', metric2: 5 });
Analytics.productClick('Search Result');
```
#### Product Detail Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackDetail();
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackDetail();
```
#### Add to Cart Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCart('add', listName); // listname is optional
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('add', 'Search Result');
```
#### Remove from Cart Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCart('remove', listName); // listname is optional
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('remove', 'Search Result');
```
#### Checkout Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackCheckout(checkoutStep, optionValue);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCheckout(1, 'Visa');
```
#### Transaction Tracking
```js
Analytics.addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
Analytics.trackTransaction(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option);
// example:
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2222', '1', 'MEN10', '1');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '1111', '1', 'WOMEN10', '1');
Analytics.trackTransaction('T1234', 'Online Store - Web', '3333', '10', '200', 'FLAT10', '', '', '');
```
#### Promotion Impressions
```js
Analytics.addPromo(productId, name, creative, position);
Analytics.pageView();
// example:
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.pageView();
```
**Note:** Before tracking promotion clicks, call pageView, otherwise promotion impressions will be treated as promotion clicks.
#### Promotion Clicks
```js
Analytics.addPromo(promotionId, promotionName, creative, position);
Analytics.promoClick(promotionName);
// example:
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.promoClick('Summer Sale');
```
### Exception Tracking
```js
Analytics.trackException(description, isFatal);
// example:
Analytics.trackException('Function "foo" is undefined on object "bar"', true);
```
### Online / Offline Mode
```js
// While in offline mode, no calls to the ga function or pushes to the gaq array are made.
// This will queue all calls for later sending once offline mode is reset to false.
Analytics.offline(true);
// Reset offline mode to false
Analytics.offline(false);
```
### In-Memory Queues
```js
// If logging is enabled then all outbound calls are accessible via an in-memory array.
// This is useful for troubleshooting and seeing the order of outbound calls with parameters.
Analytics.log;
// If in offline mode then all calls are queued to an in-memory array for future processing.
// All calls queued to the offlineQueue are not outbound calls yet and hence do not show up in the log.
Analytics.offlineQueue;
```
## Directive
Alternatively, you can use a directive to avoid filling controllers with `Analytics.trackEvent();` statements.
**Note:** the directive does not create an isolate scope.
```html
<button type="button" ga-track-event="['video', 'play', 'django.mp4']"></button>
<!-- OR -->
<button type="button" ga-track-event="['video', 'play', 'django.mp4', 4, true, {dimension15: 'My Custom Dimension', metric18: 8000}]"></button>
```
You can define the properties on your controller too, `$scope.event = ['video', 'play', 'django.mp4']` and reference them.
```html
<button type="button" ga-track-event="event"></button>
```
`ga-track-event-if` is a conditional check. If the attribute value evaluates falsey, the event will **NOT** be fired. This is useful for user tracking opt-out, _etc._
```html
<button type="button" ga-track-event="['video', 'play', 'django.mp4']" ga-track-event-if="shouldTrack"></button>
```
## Troubleshooting
### AdBlock EasyPrivacy
AdBlock has a module named [EasyPrivacy](https://easylist-downloads.adblockplus.org/easyprivacy.txt) that is meant to block web tracking scripts. angular-google-analytics.js gets filtered out by the EasyPrivacy blacklist.
Users who are already concatenating and minifying their scripts should not notice a problem as long as the new script name is not also on the EasyPrivacy blacklist. Alternatively, consider changing the filename manually.
Users who are already concatenating and minifying their scripts should not notice a problem as long as the new script name is not also on the EasyPrivacy blacklist. Alternatively, consider changing the file name manually.
### Debugging Resources
Chrome Extension: [Google Analytics Debugger](https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna)
Firefox Add-on: [Google Analytics Debugger](https://addons.mozilla.org/en-US/firefox/addon/gadebugger/)
## Licence
As AngularJS itself, this module is released under the permissive [MIT license](http://revolunet.mit-license.org). Your contributions are always welcome.
As AngularJS itself, this module is released under the permissive [MIT License](http://revolunet.mit-license.org). Your contributions are always welcome.
## Development
After forking you will need to run the following from a command line to get your environment setup:
1. ```npm install```
2. ```bower install```
After install you have the following commands available to you from a command line:
1. ```grunt lint```
2. ```npm test``` or ```grunt``` or ```grunt test```
3. ```npm test-server``` or ```grunt test-server```
4. ```grunt build``` or ```grunt release```
5. ```grunt stage```

View File

@ -1,9 +1,24 @@
{
"author": "revolunet",
"authors": [
"Julien Bouquillon <julien@revolunet.com> (https://github.com/revolunet)",
"Justin Saunders (https://github.com/justinsa)",
"Chris Esplin (https://github.com/deltaepsilon)",
"Adam Misiorny (https://github.com/adam187)"
],
"name": "angular-google-analytics",
"description": "Easy Analytics for your AngularJS application",
"version": "0.0.15",
"keywords": [
"analytics",
"angular",
"angularjs",
"angular-js",
"google-analytics",
"tracking",
"visitor-tracking",
"universal-analytics"
],
"homepage": "https://github.com/revolunet/angular-google-analytics",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/revolunet/angular-google-analytics.git"

View File

@ -1,909 +1,1255 @@
/**
* Angular Google Analytics - Easy tracking for your AngularJS application
* @version v0.0.15 - 2015-04-27
* @version v1.1.7 - 2016-03-25
* @link http://github.com/revolunet/angular-google-analytics
* @author Julien Bouquillon <julien@revolunet.com>
* @author Julien Bouquillon <julien@revolunet.com> (https://github.com/revolunet)
* @contributors Julien Bouquillon (https://github.com/revolunet),Justin Saunders (https://github.com/justinsa),Chris Esplin (https://github.com/deltaepsilon),Adam Misiorny (https://github.com/adam187)
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
'use strict';
/* globals define */
(function (root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['angular'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('angular'));
} else {
factory(root.angular);
}
}(this, function (angular, undefined) {
'use strict';
angular.module('angular-google-analytics', [])
.provider('Analytics', function () {
var accounts,
analyticsJS = true,
cookieConfig = 'auto', // DEPRECATED
created = false,
crossDomainLinker = false,
crossLinkDomains,
currency = 'USD',
debugMode = false,
delayScriptTag = false,
displayFeatures = false,
disableAnalytics = false,
domainName,
ecommerce = false,
enhancedEcommerce = false,
enhancedLinkAttribution = false,
experimentId,
ignoreFirstPageLoad = false,
logAllCalls = false,
hybridMobileSupport = false,
offlineMode = false,
pageEvent = '$routeChangeSuccess',
readFromRoute = false,
removeRegExp,
testMode = false,
traceDebuggingMode = false,
trackPrefix = '',
trackRoutes = true,
trackUrlParams = false;
angular.module('angular-google-analytics', [])
.provider('Analytics', function () {
var created = false,
trackRoutes = true,
accountId,
displayFeatures,
trackPrefix = '',
domainName,
analyticsJS = false,
pageEvent = '$routeChangeSuccess',
cookieConfig = 'auto',
ecommerce = false,
enhancedEcommerce = false,
enhancedLinkAttribution = false,
removeRegExp,
experimentId,
ignoreFirstPageLoad = false,
crossDomainLinker = false,
crossLinkDomains,
linkerConfig = {'allowLinker': true},
trackUrlParams = false,
delayScriptTag = false;
this.log = [];
this.offlineQueue = [];
this._logs = [];
/**
* Configuration Methods
**/
// config methods
this.setAccount = function (id) {
accountId = id;
return true;
};
this.setAccount = function (tracker) {
if (angular.isUndefined(tracker) || tracker === false) {
accounts = undefined;
} else if (angular.isArray(tracker)) {
accounts = tracker;
} else if (angular.isObject(tracker)) {
accounts = [tracker];
} else {
// In order to preserve an existing behavior with how the _trackEvent function works,
// the trackEvent property must be set to true when there is only a single tracker.
accounts = [{ tracker: tracker, trackEvent: true }];
}
return this;
};
this.trackPages = function (doTrack) {
trackRoutes = doTrack;
return true;
};
this.trackPages = function (val) {
trackRoutes = !!val;
return this;
};
this.trackPrefix = function (prefix) {
trackPrefix = prefix;
return true;
};
this.trackPrefix = function (prefix) {
trackPrefix = prefix;
return this;
};
this.setDomainName = function (domain) {
domainName = domain;
return true;
};
this.setDomainName = function (domain) {
domainName = domain;
return this;
};
this.useDisplayFeatures = function (val) {
displayFeatures = !!val;
return true;
};
this.useDisplayFeatures = function (val) {
displayFeatures = !!val;
return this;
};
this.useAnalytics = function (val) {
analyticsJS = !!val;
return true;
};
this.useAnalytics = function (val) {
analyticsJS = !!val;
return this;
};
this.useEnhancedLinkAttribution = function (val) {
enhancedLinkAttribution = !!val;
return true;
};
this.useEnhancedLinkAttribution = function (val) {
enhancedLinkAttribution = !!val;
return this;
};
this.useCrossDomainLinker = function (val) {
crossDomainLinker = !!val;
return true;
};
this.useCrossDomainLinker = function (val) {
crossDomainLinker = !!val;
return this;
};
this.setCrossLinkDomains = function (domains) {
crossLinkDomains = domains;
return true;
};
this.setCrossLinkDomains = function (domains) {
crossLinkDomains = domains;
return this;
};
this.setPageEvent = function (name) {
pageEvent = name;
return true;
};
this.setPageEvent = function (name) {
pageEvent = name;
return this;
};
this.setCookieConfig = function (config) {
cookieConfig = config;
return true;
};
/* DEPRECATED */
this.setCookieConfig = function (config) {
cookieConfig = config;
return this;
};
this.useECommerce = function (val, enhanced) {
ecommerce = !!val;
enhancedEcommerce = !!enhanced;
return true;
};
this.useECommerce = function (val, enhanced) {
ecommerce = !!val;
enhancedEcommerce = !!enhanced;
return this;
};
this.setRemoveRegExp = function (regex) {
if (regex instanceof RegExp) {
removeRegExp = regex;
return true;
}
return false;
};
this.setCurrency = function (currencyCode) {
currency = currencyCode;
return this;
};
this.setExperimentId = function (id) {
experimentId = id;
return true;
};
this.setRemoveRegExp = function (regex) {
if (regex instanceof RegExp) {
removeRegExp = regex;
}
return this;
};
this.ignoreFirstPageLoad = function (val) {
ignoreFirstPageLoad = !!val;
return true;
};
this.setExperimentId = function (id) {
experimentId = id;
return this;
};
this.trackUrlParams = function (val) {
trackUrlParams = !!val;
return true;
};
this.ignoreFirstPageLoad = function (val) {
ignoreFirstPageLoad = !!val;
return this;
};
this.delayScriptTag = function (val) {
delayScriptTag = !!val;
return true;
};
this.trackUrlParams = function (val) {
trackUrlParams = !!val;
return this;
};
/**
* Public Service
*/
this.$get = ['$document', '$location', '$log', '$rootScope', '$window', function ($document, $location, $log, $rootScope, $window) {
var me = this;
this.disableAnalytics = function (val) {
disableAnalytics = !!val;
return this;
};
var getUrl = function () {
var url = trackUrlParams ? $location.url() : $location.path();
return removeRegExp ? url.replace(removeRegExp, '') : url;
this.setHybridMobileSupport = function (val) {
hybridMobileSupport = !!val;
return this;
};
this.startOffline = function (val) {
offlineMode = !!val;
if (offlineMode === true) {
this.delayScriptTag(true);
}
return this;
};
this.delayScriptTag = function (val) {
delayScriptTag = !!val;
return this;
};
this.logAllCalls = function (val) {
logAllCalls = !!val;
return this;
};
this.enterTestMode = function () {
testMode = true;
return this;
};
this.enterDebugMode = function (enableTraceDebugging) {
debugMode = true;
traceDebuggingMode = !!enableTraceDebugging;
return this;
};
// Enable reading page url from route object
this.readFromRoute = function(val) {
readFromRoute = !!val;
return this;
};
/**
* Private Methods
* Public Service
*/
this.$get = ['$document', // To read title
'$location', //
'$log', //
'$rootScope',//
'$window', //
'$injector', // To access ngRoute module without declaring a fixed dependency
function ($document, $location, $log, $rootScope, $window, $injector) {
var that = this;
function _gaJs(fn) {
if (!analyticsJS && $window._gaq && typeof fn === 'function') {
fn();
}
}
/**
* Side-effect Free Helper Methods
**/
function _analyticsJs(fn) {
if (analyticsJS && $window.ga && typeof fn === 'function') {
fn();
}
}
var isPropertyDefined = function (key, config) {
return angular.isObject(config) && angular.isDefined(config[key]);
};
function _generateCommandName(commandName, config) {
if (!angular.isUndefined(config) && 'name' in config && config.name) {
return config.name + '.' + commandName;
} else {
return commandName;
}
}
var isPropertySetTo = function (key, config, value) {
return isPropertyDefined(key, config) && config[key] === value;
};
function _checkOption(key, config) {
return key in config && config[key];
}
this._log = function () {
if (arguments.length > 0) {
if (arguments.length > 1 && arguments[0] === 'warn') {
$log.warn(Array.prototype.slice.call(arguments, 1));
var generateCommandName = function (commandName, config) {
if (angular.isString(config)) {
return config + '.' + commandName;
}
// console.log('analytics', arguments);
this._logs.push(arguments);
}
};
this._createScriptTag = function () {
if (!accountId) {
me._log('warn', 'No account id set to create script tag');
return;
}
if (created) {
me._log('warn', 'Script tag already created');
return;
}
// inject the google analytics tag
$window._gaq = [];
$window._gaq.push(['_setAccount', accountId]);
if(domainName) {
$window._gaq.push(['_setDomainName', domainName]);
}
if (enhancedLinkAttribution) {
$window._gaq.push(['_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js']);
}
if (trackRoutes && !ignoreFirstPageLoad) {
if (removeRegExp) {
$window._gaq.push(['_trackPageview', getUrl()]);
} else {
$window._gaq.push(['_trackPageview']);
}
}
var gaSrc;
if (displayFeatures) {
gaSrc = ('https:' === document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
} else {
gaSrc = ('https:' === document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
}
(function () {
var document = $document[0];
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = gaSrc;
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})(gaSrc);
return isPropertyDefined('name', config) ? (config.name + '.' + commandName) : commandName;
};
return created = true;
};
this._createAnalyticsScriptTag = function () {
if (!accountId) {
me._log('warn', 'No account id set to create analytics script tag');
return;
// Try to read route configuration and log warning if not possible
var $route = {};
if (readFromRoute) {
if (!$injector.has('$route')) {
$log.warn('$route service is not available. Make sure you have included ng-route in your application dependencies.');
} else {
$route = $injector.get('$route');
}
}
if (created) {
me._log('warn', 'Analytics script tag already created');
return;
}
// Get url for current page
var getUrl = function () {
// Using ngRoute provided tracking urls
if (readFromRoute && $route.current && ('pageTrack' in $route.current)) {
return $route.current.pageTrack;
}
// Otherwise go the old way
var url = trackUrlParams ? $location.url() : $location.path();
return removeRegExp ? url.replace(removeRegExp, '') : url;
};
// inject the google analytics tag
(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');
var getUtmParams = function () {
var utmToCampaignVar = {
utm_source: 'campaignSource',
utm_medium: 'campaignMedium',
utm_term: 'campaignTerm',
utm_content: 'campaignContent',
utm_campaign: 'campaignName'
};
var object = {};
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
var _cookieConfig = 'cookieConfig' in trackerObj ? trackerObj.cookieConfig : cookieConfig;
var options;
if (_checkOption('crossDomainLinker', trackerObj)) {
trackerObj.allowLinker = trackerObj.crossDomainLinker;
}
angular.forEach(['name', 'allowLinker'], function(key) {
if (key in trackerObj) {
if (angular.isUndefined(options)) {
options = {};
}
options[key] = trackerObj[key];
}
});
if (angular.isUndefined(options)) {
$window.ga('create', trackerObj.tracker, _cookieConfig);
} else {
$window.ga('create', trackerObj.tracker, _cookieConfig, options);
}
if (options && 'allowLinker' in options && options.allowLinker) {
$window.ga(_generateCommandName('require', trackerObj), 'linker');
if (_checkOption('crossLinkDomains', trackerObj)) {
$window.ga(_generateCommandName('linker:autoLink', trackerObj), trackerObj.crossLinkDomains);
}
angular.forEach($location.search(), function (value, key) {
var campaignVar = utmToCampaignVar[key];
if (angular.isDefined(campaignVar)) {
object[campaignVar] = value;
}
});
} else if (crossDomainLinker) {
$window.ga('create', accountId, cookieConfig, linkerConfig);
$window.ga('require', 'linker');
if(crossLinkDomains) {
$window.ga('linker:autoLink', crossLinkDomains );
}
} else {
$window.ga('create', accountId, cookieConfig);
}
if (displayFeatures) {
$window.ga('require', 'displayfeatures');
}
return object;
};
if (trackRoutes && !ignoreFirstPageLoad) {
$window.ga('send', 'pageview', getUrl());
}
/**
* get ActionFieldObject
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-data
* @param id
* @param affliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
var getActionFieldObject = function (id, affiliation, revenue, tax, shipping, coupon, list, step, option) {
var obj = {};
if (id) { obj.id = id; }
if (affiliation) { obj.affiliation = affiliation; }
if (revenue) { obj.revenue = revenue; }
if (tax) { obj.tax = tax; }
if (shipping) { obj.shipping = shipping; }
if (coupon) { obj.coupon = coupon; }
if (list) { obj.list = list; }
if (step) { obj.step = step; }
if (option) { obj.option = option; }
return obj;
};
if ($window.ga) {
if (ecommerce) {
if (!enhancedEcommerce) {
$window.ga('require', 'ecommerce', 'ecommerce.js');
} else {
$window.ga('require', 'ec', 'ec.js');
}
}
if (enhancedLinkAttribution) {
$window.ga('require', 'linkid', 'linkid.js');
}
if (experimentId) {
var expScript = document.createElement('script'),
s = document.getElementsByTagName('script')[0];
expScript.src = "//www.google-analytics.com/cx/api.js?experiment=" + experimentId;
s.parentNode.insertBefore(expScript, s);
}
}
/**
* Private Methods
*/
return created = true;
};
this._ecommerceEnabled = function () {
if (!ecommerce) {
this._log('warn', 'ecommerce not set. Use AnalyticsProvider.setECommerce(true, false);');
return false;
} else if (enhancedEcommerce) {
this._log('warn', 'Enhanced ecommerce plugin is enabled. Only one plugin(ecommerce/ec) can be used at a time. ' +
'Use AnalyticsProvider.setECommerce(true, false);');
return false;
}
return true;
};
this._enhancedEcommerceEnabled = function () {
if (!ecommerce) {
this._log('warn', 'ecommerce not set. Use AnalyticsProvider.setECommerce(true, true);');
return false;
} else if (!enhancedEcommerce) {
this._log('warn', 'Enhanced ecommerce plugin is disabled. Use AnalyticsProvider.setECommerce(true, true);');
return false;
}
return true;
};
/**
* Track page
https://developers.google.com/analytics/devguides/collection/gajs/
https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
* @param url
* @param title
* @param custom
* @private
*/
this._trackPage = function (url, title, custom) {
var that = this, args = arguments;
url = url ? url : getUrl();
title = title ? title : $document[0].title;
_gaJs(function () {
// http://stackoverflow.com/questions/7322288/how-can-i-set-a-page-title-with-google-analytics
$window._gaq.push(["_set", "title", title]);
$window._gaq.push(['_trackPageview', trackPrefix + url]);
that._log('_trackPageview', url, title, args);
});
_analyticsJs(function () {
var opt_fieldObject = {
'page': trackPrefix + url,
'title': title
};
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
var _gaJs = function (fn) {
if (!analyticsJS && $window._gaq && typeof fn === 'function') {
fn();
}
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
$window.ga(_generateCommandName('send', trackerObj), 'pageview', opt_fieldObject);
});
} else {
$window.ga('send', 'pageview', opt_fieldObject);
}
that._log('pageview', url, title, args);
});
};
};
/**
* Track event
https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
* @param category
* @param action
* @param label
* @param value
* @param noninteraction
* @param custom
* @private
*/
this._trackEvent = function (category, action, label, value, noninteraction, custom) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_trackEvent', category, action, label, value, !!noninteraction]);
that._log('trackEvent', args);
});
_analyticsJs(function () {
var opt_fieldObject = {};
if (angular.isDefined(noninteraction)) {
opt_fieldObject['nonInteraction'] = !!noninteraction;
var _gaq = function () {
var args = Array.prototype.slice.call(arguments);
if (offlineMode === true) {
that.offlineQueue.push([_gaq, args]);
return;
}
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
if (!$window._gaq) {
$window._gaq = [];
}
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
if (_checkOption('trackEvent', trackerObj)) {
$window.ga(_generateCommandName('send', trackerObj), 'event', category, action, label, value, opt_fieldObject);
if (logAllCalls === true) {
that._log.apply(that, args);
}
$window._gaq.push(args);
};
var _analyticsJs = function (fn) {
if (analyticsJS && $window.ga && typeof fn === 'function') {
fn();
}
};
var _ga = function () {
var args = Array.prototype.slice.call(arguments);
if (offlineMode === true) {
that.offlineQueue.push([_ga, args]);
return;
}
if (typeof $window.ga !== 'function') {
that._log('warn', 'ga function not set on window');
return;
}
if (logAllCalls === true) {
that._log.apply(that, args);
}
$window.ga.apply(null, args);
};
var _gaMultipleTrackers = function (includeFn) {
// Drop the includeFn from the arguments and preserve the original command name
var args = Array.prototype.slice.call(arguments, 1),
commandName = args[0],
trackers = [];
if (typeof includeFn === 'function') {
accounts.forEach(function (account) {
if (includeFn(account)) {
trackers.push(account);
}
});
} else {
$window.ga('send', 'event', category, action, label, value, opt_fieldObject);
// No include function indicates that all accounts are to be used
trackers = accounts;
}
that._log('event', args);
});
};
/**
* Add transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addTrans
* @param transactionId
* @param affiliation
* @param total
* @param tax
* @param shipping
* @param city
* @param state
* @param country
* @private
*/
this._addTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addTrans', transactionId, affiliation, total, tax, shipping, city, state, country]);
that._log('_addTrans', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:addTransaction', {
id: transactionId,
affiliation: affiliation,
revenue: total,
tax: tax,
shipping: shipping,
currency: currency || 'USD'
});
that._log('ecommerce:addTransaction', args);
// To preserve backwards compatibility fallback to _ga method if no account
// matches the specified includeFn. This preserves existing behaviors by
// performing the single tracker operation.
if (trackers.length === 0) {
_ga.apply(that, args);
return;
}
});
};
/**
* Add item to transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addItem
* @param transactionId
* @param sku
* @param name
* @param category
* @param price
* @param quantity
* @private
*/
this._addItem = function (transactionId, sku, name, category, price, quantity) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addItem', transactionId, sku, name, category, price, quantity]);
that._log('_addItem', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:addItem', {
id: transactionId,
name: name,
sku: sku,
category: category,
price: price,
quantity: quantity
});
that._log('ecommerce:addItem', args);
trackers.forEach(function (tracker) {
// Check tracker 'select' function, if it exists, for whether the tracker should be used with the current command.
// If the 'select' function returns false then the tracker will not be used with the current command.
if (isPropertyDefined('select', tracker) && typeof tracker.select === 'function' && !tracker.select(args)) {
return;
}
args[0] = generateCommandName(commandName, tracker);
_ga.apply(that, args);
});
};
this._log = function () {
var args = Array.prototype.slice.call(arguments);
if (args.length > 0) {
if (args.length > 1) {
switch (args[0]) {
case 'debug':
case 'error':
case 'info':
case 'log':
case 'warn':
$log[args[0]](args.slice(1));
break;
}
}
that.log.push(args);
}
});
};
};
/**
* Track transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#sendingData
* @private
*/
this._trackTrans = function () {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_trackTrans']);
that._log('_trackTrans', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:send');
that._log('ecommerce:send', args);
this._createScriptTag = function () {
if (!accounts || accounts.length < 1) {
that._log('warn', 'No account id set to create script tag');
return;
}
});
};
/**
* Clear transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#clearingData
* @private
*/
this._clearTrans = function () {
var that = this, args = arguments;
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:clear');
that._log('ecommerce:clear', args);
if (accounts.length > 1) {
that._log('warn', 'Multiple trackers are not supported with ga.js. Using first tracker only');
accounts = accounts.slice(0, 1);
}
});
};
/**
* Enhanced Ecommerce
*/
/**
* Add product data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-data
* @param productId
* @param name
* @param category
* @param brand
* @param variant
* @param price
* @param quantity
* @param coupon
* @param position
*/
this._addProduct = function (productId, name, category, brand, variant, price, quantity, coupon, position) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addProduct', productId, name, category, brand, variant, price, quantity, coupon, position]);
that._log('_addProduct', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addProduct', {
id: productId,
name: name,
category: category,
brand: brand,
variant: variant,
price: price,
quantity: quantity,
coupon: coupon,
position: position
});
that._log('ec:addProduct', args);
if (created === true) {
that._log('warn', 'ga.js or analytics.js script tag already created');
return;
}
});
};
/**
* Add Impression data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#impression-data
* @param id
* @param name
* @param list
* @param brand
* @param category
* @param variant
* @param position
* @param price
*/
this._addImpression = function (id, name, list, brand, category, variant, position, price){
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addImpression', id, name, list, brand, category, variant, position, price]);
that._log('_addImpression', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addImpression', {
id: id,
name: name,
category: category,
brand: brand,
variant: variant,
list: list,
position: position,
price: price
if (disableAnalytics === true) {
that._log('info', 'Analytics disabled: ' + accounts[0].tracker);
$window['ga-disable-' + accounts[0].tracker] = true;
}
_gaq('_setAccount', accounts[0].tracker);
if(domainName) {
_gaq('_setDomainName', domainName);
}
if (enhancedLinkAttribution) {
_gaq('_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js');
}
if (trackRoutes && !ignoreFirstPageLoad) {
if (removeRegExp) {
_gaq('_trackPageview', getUrl());
} else {
_gaq('_trackPageview');
}
}
var document = $document[0];
var scriptSource;
if (displayFeatures === true) {
scriptSource = ('https:' === document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
} else {
scriptSource = ('https:' === document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
}
if (testMode !== true) {
// If not in test mode inject the Google Analytics tag
(function () {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = scriptSource;
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
} else {
// Log the source location for validation
that._log('inject', scriptSource);
}
created = true;
return true;
};
this._createAnalyticsScriptTag = function () {
if (!accounts) {
that._log('warn', 'No account id set to create analytics script tag');
return;
}
if (created === true) {
that._log('warn', 'ga.js or analytics.js script tag already created');
return;
}
if (disableAnalytics === true) {
accounts.forEach(function (trackerObj) {
that._log('info', 'Analytics disabled: ' + trackerObj.tracker);
$window['ga-disable-' + trackerObj.tracker] = true;
});
}
that._log('ec:addImpression', args);
});
};
/**
* Add promo data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce
* @param productId
* @param name
* @param creative
* @param position
*/
this._addPromo = function (productId, name, creative, position) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addPromo', productId, name, creative, position]);
that._log('_addPromo', arguments);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addPromo', {
id: productId,
name: name,
creative: creative,
position: position
});
that._log('ec:addPromo', args);
var document = $document[0];
var protocol = hybridMobileSupport === true ? 'https:' : '';
var scriptSource = protocol + '//www.google-analytics.com/' + (debugMode ? 'analytics_debug.js' : 'analytics.js');
if (testMode !== true) {
// If not in test mode inject the Google Analytics tag
(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',scriptSource,'ga');
} else {
if (typeof $window.ga !== 'function') {
// In test mode create a ga function if none exists that is a noop sink.
$window.ga = function () {};
}
// Log script injection.
that._log('inject', scriptSource);
}
});
};
/**
* get ActionFieldObject
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-data
* @param id
* @param affliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
this._getActionFieldObject = function (id, affiliation, revenue, tax, shipping, coupon, list, step, option) {
var obj = {};
if (id) { obj.id = id; }
if (affiliation) { obj.affiliation = affiliation; }
if (revenue) { obj.revenue = revenue; }
if (tax) { obj.tax = tax; }
if (shipping) { obj.shipping = shipping; }
if (coupon) { obj.coupon = coupon; }
if (list) { obj.list = list; }
if (step) { obj.step = step; }
if (option) { obj.option = option; }
return obj;
};
/**
* Set Action being performed
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-actions
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-types
* @param action
* @param name
* @param obj
*/
this._setAction = function (action, obj){
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_setAction', action, obj]);
that._log('__setAction', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:setAction', action, obj);
that._log('ec:setAction', args);
if (traceDebuggingMode) {
$window.ga_debug = { trace: true };
}
});
};
/**
* Track Transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-transactions
* @param transactionId
* @param affiliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
this._trackTransaction = function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
this._setAction('purchase', this._getActionFieldObject(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option));
this._pageView();
};
accounts.forEach(function (trackerObj) {
trackerObj.crossDomainLinker = isPropertyDefined('crossDomainLinker', trackerObj) ? trackerObj.crossDomainLinker : crossDomainLinker;
trackerObj.crossLinkDomains = isPropertyDefined('crossLinkDomains', trackerObj) ? trackerObj.crossLinkDomains : crossLinkDomains;
trackerObj.displayFeatures = isPropertyDefined('displayFeatures', trackerObj) ? trackerObj.displayFeatures : displayFeatures;
trackerObj.enhancedLinkAttribution = isPropertyDefined('enhancedLinkAttribution', trackerObj) ? trackerObj.enhancedLinkAttribution : enhancedLinkAttribution;
trackerObj.set = isPropertyDefined('set', trackerObj) ? trackerObj.set : {};
trackerObj.trackEcommerce = isPropertyDefined('trackEcommerce', trackerObj) ? trackerObj.trackEcommerce : ecommerce;
trackerObj.trackEvent = isPropertyDefined('trackEvent', trackerObj) ? trackerObj.trackEvent : false;
/**
* Track Refund
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-refunds
* @param transactionId
*
*/
this._trackRefund = function (transactionId) {
this._setAction('refund', this._getActionFieldObject(transactionId));
this._pageView();
};
// Logic to choose the account fields to be used.
// cookieConfig is being deprecated for a tracker specific property: fields.
var fields = {};
if (isPropertyDefined('fields', trackerObj)) {
fields = trackerObj.fields;
} else if (isPropertyDefined('cookieConfig', trackerObj)) {
if (angular.isString(trackerObj.cookieConfig)) {
fields.cookieDomain = trackerObj.cookieConfig;
} else {
fields = trackerObj.cookieConfig;
}
} else if (angular.isString(cookieConfig)) {
fields.cookieDomain = cookieConfig;
} else if (cookieConfig) {
fields = cookieConfig;
}
if (trackerObj.crossDomainLinker === true) {
fields.allowLinker = true;
}
if (isPropertyDefined('name', trackerObj)) {
fields.name = trackerObj.name;
}
trackerObj.fields = fields;
/**
* Track Checkout
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-checkout
* @param step
* @param option
*
*/
this._trackCheckOut = function (step, option) {
this._setAction('checkout', this._getActionFieldObject(null, null, null, null, null, null, null, step, option));
this._pageView();
};
_ga('create', trackerObj.tracker, trackerObj.fields);
/**
* Track add/remove to cart
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
* @param action
*
*/
this._trackCart = function (action) {
if (['add', 'remove'].indexOf(action) !== -1) {
this._setAction(action);
this._send('event', 'UX', 'click', action + ' to cart');
// Hybrid mobile application support
// https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks
if (hybridMobileSupport === true) {
_ga(generateCommandName('set', trackerObj), 'checkProtocolTask', null);
}
// Send all custom set commands from the trackerObj.set property
for (var key in trackerObj.set) {
if (trackerObj.set.hasOwnProperty(key)) {
_ga(generateCommandName('set', trackerObj), key, trackerObj.set[key]);
}
}
if (trackerObj.crossDomainLinker === true) {
_ga(generateCommandName('require', trackerObj), 'linker');
if (angular.isDefined(trackerObj.crossLinkDomains)) {
_ga(generateCommandName('linker:autoLink', trackerObj), trackerObj.crossLinkDomains);
}
}
if (trackerObj.displayFeatures) {
_ga(generateCommandName('require', trackerObj), 'displayfeatures');
}
if (trackerObj.trackEcommerce) {
if (!enhancedEcommerce) {
_ga(generateCommandName('require', trackerObj), 'ecommerce');
} else {
_ga(generateCommandName('require', trackerObj), 'ec');
_ga(generateCommandName('set', trackerObj), '&cu', currency);
}
}
if (trackerObj.enhancedLinkAttribution) {
_ga(generateCommandName('require', trackerObj), 'linkid');
}
if (trackRoutes && !ignoreFirstPageLoad) {
_ga(generateCommandName('send', trackerObj), 'pageview', trackPrefix + getUrl());
}
});
if (experimentId) {
var expScript = document.createElement('script'),
s = document.getElementsByTagName('script')[0];
expScript.src = protocol + '//www.google-analytics.com/cx/api.js?experiment=' + experimentId;
s.parentNode.insertBefore(expScript, s);
}
created = true;
return true;
};
this._ecommerceEnabled = function (warn, command) {
var result = ecommerce && !enhancedEcommerce;
if (warn === true && result === false) {
if (ecommerce && enhancedEcommerce) {
that._log('warn', command + ' is not available when Enhanced Ecommerce is enabled with analytics.js');
} else {
that._log('warn', 'Ecommerce must be enabled to use ' + command + ' with analytics.js');
}
}
return result;
};
this._enhancedEcommerceEnabled = function (warn, command) {
var result = ecommerce && enhancedEcommerce;
if (warn === true && result === false) {
that._log('warn', 'Enhanced Ecommerce must be enabled to use ' + command + ' with analytics.js');
}
return result;
};
/**
* Track page
https://developers.google.com/analytics/devguides/collection/gajs/
https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
* @param url
* @param title
* @param custom
* @private
*/
this._trackPage = function (url, title, custom) {
url = url ? url : getUrl();
title = title ? title : $document[0].title;
_gaJs(function () {
// http://stackoverflow.com/questions/7322288/how-can-i-set-a-page-title-with-google-analytics
_gaq('_set', 'title', title);
_gaq('_trackPageview', (trackPrefix + url));
});
_analyticsJs(function () {
var opt_fieldObject = {
'page': trackPrefix + url,
'title': title
};
angular.extend(opt_fieldObject, getUtmParams());
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
_gaMultipleTrackers(undefined, 'send', 'pageview', opt_fieldObject);
});
};
/**
* Track event
https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
* @param category
* @param action
* @param label
* @param value
* @param noninteraction
* @param custom
* @private
*/
this._trackEvent = function (category, action, label, value, noninteraction, custom) {
_gaJs(function () {
_gaq('_trackEvent', category, action, label, value, !!noninteraction);
});
_analyticsJs(function () {
var opt_fieldObject = {};
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEvent', trackerObj, true);
};
if (angular.isDefined(noninteraction)) {
opt_fieldObject.nonInteraction = !!noninteraction;
}
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
if (!angular.isDefined(opt_fieldObject.page)) {
opt_fieldObject.page = getUrl();
}
_gaMultipleTrackers(includeFn, 'send', 'event', category, action, label, value, opt_fieldObject);
});
};
/**
* Add transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addTrans
* @param transactionId
* @param affiliation
* @param total
* @param tax
* @param shipping
* @param city
* @param state
* @param country
* @private
*/
this._addTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
_gaJs(function () {
_gaq('_addTrans', transactionId, affiliation, total, tax, shipping, city, state, country);
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'addTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ecommerce:addTransaction',
{
id: transactionId,
affiliation: affiliation,
revenue: total,
tax: tax,
shipping: shipping,
currency: currency || 'USD'
});
}
});
};
/**
* Add item to transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addItem
* @param transactionId
* @param sku
* @param name
* @param category
* @param price
* @param quantity
* @private
*/
this._addItem = function (transactionId, sku, name, category, price, quantity) {
_gaJs(function () {
_gaq('_addItem', transactionId, sku, name, category, price, quantity);
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'addItem')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ecommerce:addItem',
{
id: transactionId,
name: name,
sku: sku,
category: category,
price: price,
quantity: quantity
});
}
});
};
/**
* Track transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#sendingData
* @private
*/
this._trackTrans = function () {
_gaJs(function () {
_gaq('_trackTrans');
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'trackTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ecommerce:send');
}
});
};
/**
* Clear transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#clearingData
* @private
*/
this._clearTrans = function () {
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'clearTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ecommerce:clear');
}
});
};
/**
* Enhanced Ecommerce
*/
/**
* Add Product
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-data
* @param productId
* @param name
* @param category
* @param brand
* @param variant
* @param price
* @param quantity
* @param coupon
* @param position
* @param custom
* @private
*/
this._addProduct = function (productId, name, category, brand, variant, price, quantity, coupon, position, custom) {
_gaJs(function () {
_gaq('_addProduct', productId, name, category, brand, variant, price, quantity, coupon, position);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addProduct')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
var details = {
id: productId,
name: name,
category: category,
brand: brand,
variant: variant,
price: price,
quantity: quantity,
coupon: coupon,
position: position
};
if (angular.isObject(custom)) {
angular.extend(details, custom);
}
_gaMultipleTrackers(includeFn, 'ec:addProduct', details);
}
});
};
/**
* Add Impression
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#impression-data
* @param id
* @param name
* @param list
* @param brand
* @param category
* @param variant
* @param position
* @param price
* @private
*/
this._addImpression = function (id, name, list, brand, category, variant, position, price){
_gaJs(function () {
_gaq('_addImpression', id, name, list, brand, category, variant, position, price);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addImpression')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ec:addImpression',
{
id: id,
name: name,
category: category,
brand: brand,
variant: variant,
list: list,
position: position,
price: price
});
}
});
};
/**
* Add Promo
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce
* @param productId
* @param name
* @param creative
* @param position
* @private
*/
this._addPromo = function (productId, name, creative, position) {
_gaJs(function () {
_gaq('_addPromo', productId, name, creative, position);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addPromo')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ec:addPromo',
{
id: productId,
name: name,
creative: creative,
position: position
});
}
});
};
/**
* Set Action
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-actions
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-types
* @param action
* @param obj
* @private
*/
this._setAction = function (action, obj){
_gaJs(function () {
_gaq('_setAction', action, obj);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'setAction')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ec:setAction', action, obj);
}
});
};
/**
* Track Transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-transactions
* @param transactionId
* @param affiliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
* @private
*/
this._trackTransaction = function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
this._setAction('purchase', getActionFieldObject(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option));
};
/**
* Track Refund
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-refunds
* @param transactionId
* @private
*/
this._trackRefund = function (transactionId) {
this._setAction('refund', getActionFieldObject(transactionId));
};
/**
* Track Checkout
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-checkout
* @param step
* @param option
* @private
*/
this._trackCheckOut = function (step, option) {
this._setAction('checkout', getActionFieldObject(null, null, null, null, null, null, null, step, option));
};
/**
* Track detail
* @private
*/
this._trackDetail = function () {
this._setAction('detail');
this._pageView();
};
/**
* Track add/remove to cart
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
* @param action
* @param list
* @private
*/
this._trackCart = function (action, listName) {
if (['add', 'remove'].indexOf(action) !== -1) {
this._setAction(action, { list: listName });
this._trackEvent('UX', 'click', action + (action === 'add' ? ' to cart' : ' from cart'));
}
};
/**
* Track promo click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
* @private
*/
this._promoClick = function (promotionName) {
this._setAction('promo_click');
this._trackEvent('Internal Promotions', 'click', promotionName);
};
/**
* Track product click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
* @private
*/
this._productClick = function (listName) {
this._setAction('click', getActionFieldObject(null, null, null, null, null, null, listName, null, null));
this._trackEvent('UX', 'click', listName);
};
/**
* Send page view
* @param trackerName
* @private
*/
this._pageView = function (trackerName) {
_analyticsJs(function () {
_ga(generateCommandName('send', trackerName), 'pageview');
});
};
/**
* Send custom events
* https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#implementation
* https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions#implementation
* @private
*/
this._send = function () {
var args = Array.prototype.slice.call(arguments);
args.unshift('send');
_analyticsJs(function () {
_ga.apply(that, args);
});
};
/**
* Set custom dimensions, metrics or experiment
* https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
* https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
* @param name (Required)
* @param value (Required)
* @param trackerName (Optional)
* @private
*/
this._set = function (name, value, trackerName) {
_analyticsJs(function () {
_ga(generateCommandName('set', trackerName), name, value);
});
};
/**
* Track user timings
* @param timingCategory (Required): A string for categorizing all user timing variables into logical groups(e.g jQuery).
* @param timingVar (Required): A string to identify the variable being recorded(e.g. JavaScript Load).
* @param timingValue (Required): The number of milliseconds in elapsed time to report to Google Analytics(e.g. 20).
* @param timingLabel (Optional): A string that can be used to add flexibility in visualizing user timings in the reports(e.g. Google CDN).
* @private
*/
this._trackTimings = function (timingCategory, timingVar, timingValue, timingLabel) {
_analyticsJs(function () {
_gaMultipleTrackers(undefined, 'send', 'timing', timingCategory, timingVar, timingValue, timingLabel);
});
};
/**
* Exception tracking
* https://developers.google.com/analytics/devguides/collection/analyticsjs/exceptions
* @param description (Optional): A description of the exception.
* @param isFatal (Optional): true if the exception was fatal, false otherwise.
* @private
*/
this._trackException = function (description, isFatal) {
_analyticsJs(function () {
_gaMultipleTrackers(undefined, 'send', 'exception', { exDescription: description, exFatal: !!isFatal});
});
};
// creates the Google Analytics tracker
if (!delayScriptTag) {
if (analyticsJS) {
this._createAnalyticsScriptTag();
} else {
this._createScriptTag();
}
}
};
/**
* Track promo click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
*
*/
this._promoClick = function (promotionName) {
this._setAction('promo_click');
this._send('event', 'Internal Promotions', 'click', promotionName);
};
/**
* Track product click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
*
*/
this._productClick = function (listName) {
this._setAction('click', this._getActionFieldObject(null, null, null, null, null, null, listName, null, null));
this._send('event', 'UX', 'click', listName);
};
/**
* Send custom events
* https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#implementation
* https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions#implementation
*
* @param obj
* @private
*/
this._send = function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
args.unshift('send');
_analyticsJs(function () {
$window.ga.apply(this, args);
that._log(args);
});
};
this._pageView = function() {
this._send('pageview');
};
/**
* Set custom dimensions, metrics or experiment
* https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
* https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
* @param name
* @param value
* @private
*/
this._set = function (name, value) {
var that = this;
_analyticsJs(function () {
$window.ga('set', name, value);
that._log('set', name, value);
});
};
// creates the ganalytics tracker
if (!delayScriptTag) {
if (analyticsJS) {
this._createAnalyticsScriptTag();
} else {
this._createScriptTag();
// activates page tracking
if (trackRoutes) {
$rootScope.$on(pageEvent, function () {
// Apply $route based filtering if configured
if (readFromRoute) {
// Avoid tracking undefined routes, routes without template (e.g. redirect routes)
// and those explicitly marked as 'do not track'
if (!$route.current || !$route.current.templateUrl || $route.current.doNotTrack) {
return;
}
}
that._trackPage();
});
}
}
// activates page tracking
if (trackRoutes) {
$rootScope.$on(pageEvent, function () {
me._trackPage();
});
}
/**
* Track User Timings
* @timingCategory (Required): A string for categorizing all user timing variables into logical groups(e.g jQuery).
* @timingVar (Required): A string to identify the variable being recorded(e.g. JavaScript Load).
* @timingValue (Required): The number of milliseconds in elapsed time to report to Google Analytics(e.g. 20).
* @timingLabel (Not Required): A string that can be used to add flexibility in visualizing user timings in the reports(e.g. Google CDN).
*/
this._trackTimings = function (timingCategory, timingVar, timingValue, timingLabel) {
this._send('timing', timingCategory, timingVar, timingValue, timingLabel);
};
return {
log: that.log,
offlineQueue: that.offlineQueue,
configuration: {
accounts: accounts,
universalAnalytics: analyticsJS,
crossDomainLinker: crossDomainLinker,
crossLinkDomains: crossLinkDomains,
currency: currency,
debugMode: debugMode,
delayScriptTag: delayScriptTag,
disableAnalytics: disableAnalytics,
displayFeatures: displayFeatures,
domainName: domainName,
ecommerce: that._ecommerceEnabled(),
enhancedEcommerce: that._enhancedEcommerceEnabled(),
enhancedLinkAttribution: enhancedLinkAttribution,
experimentId: experimentId,
hybridMobileSupport: hybridMobileSupport,
ignoreFirstPageLoad: ignoreFirstPageLoad,
logAllCalls: logAllCalls,
pageEvent: pageEvent,
readFromRoute: readFromRoute,
removeRegExp: removeRegExp,
testMode: testMode,
traceDebuggingMode: traceDebuggingMode,
trackPrefix: trackPrefix,
trackRoutes: trackRoutes,
trackUrlParams: trackUrlParams
},
getUrl: getUrl,
/* DEPRECATED */
setCookieConfig: that._setCookieConfig,
/* DEPRECATED */
getCookieConfig: function () {
return cookieConfig;
},
createAnalyticsScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return that._createAnalyticsScriptTag();
},
createScriptTag: function () {
return that._createScriptTag();
},
offline: function (mode) {
if (mode === true && offlineMode === false) {
// Go to offline mode
offlineMode = true;
}
if (mode === false && offlineMode === true) {
// Go to online mode and process the offline queue
offlineMode = false;
while (that.offlineQueue.length > 0) {
var obj = that.offlineQueue.shift();
obj[0].apply(that, obj[1]);
}
}
return offlineMode;
},
trackPage: function (url, title, custom) {
that._trackPage.apply(that, arguments);
},
trackEvent: function (category, action, label, value, noninteraction, custom) {
that._trackEvent.apply(that, arguments);
},
addTrans: function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
that._addTrans.apply(that, arguments);
},
addItem: function (transactionId, sku, name, category, price, quantity) {
that._addItem.apply(that, arguments);
},
trackTrans: function () {
that._trackTrans.apply(that, arguments);
},
clearTrans: function () {
that._clearTrans.apply(that, arguments);
},
addProduct: function (productId, name, category, brand, variant, price, quantity, coupon, position, custom) {
that._addProduct.apply(that, arguments);
},
addPromo: function (productId, name, creative, position) {
that._addPromo.apply(that, arguments);
},
addImpression: function (productId, name, list, brand, category, variant, position, price) {
that._addImpression.apply(that, arguments);
},
productClick: function (listName) {
that._productClick.apply(that, arguments);
},
promoClick : function (promotionName) {
that._promoClick.apply(that, arguments);
},
trackDetail: function () {
that._trackDetail.apply(that, arguments);
},
trackCart: function (action, list) {
that._trackCart.apply(that, arguments);
},
trackCheckout: function (step, option) {
that._trackCheckOut.apply(that, arguments);
},
trackTimings: function (timingCategory, timingVar, timingValue, timingLabel) {
that._trackTimings.apply(that, arguments);
},
trackTransaction: function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
that._trackTransaction.apply(that, arguments);
},
trackException: function (description, isFatal) {
that._trackException.apply(that, arguments);
},
setAction: function (action, obj) {
that._setAction.apply(that, arguments);
},
pageView: function () {
that._pageView.apply(that, arguments);
},
send: function (obj) {
that._send.apply(that, arguments);
},
set: function (name, value, trackerName) {
that._set.apply(that, arguments);
}
};
}];
})
.directive('gaTrackEvent', ['Analytics', '$parse', function (Analytics, $parse) {
return {
_logs: me._logs,
displayFeatures: displayFeatures,
ecommerce: ecommerce,
enhancedEcommerce: enhancedEcommerce,
enhancedLinkAttribution: enhancedLinkAttribution,
getUrl: getUrl,
experimentId: experimentId,
ignoreFirstPageLoad: ignoreFirstPageLoad,
delayScriptTag: delayScriptTag,
setCookieConfig: me._setCookieConfig,
getCookieConfig: function () {
return cookieConfig;
},
createAnalyticsScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return me._createAnalyticsScriptTag();
},
createScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return me._createScriptTag();
},
ecommerceEnabled: function () {
return me._ecommerceEnabled();
},
enhancedEcommerceEnabled: function () {
return me._enhancedEcommerceEnabled();
},
trackPage: function (url, title, custom) {
me._trackPage(url, title, custom);
},
trackEvent: function (category, action, label, value, noninteraction, custom) {
me._trackEvent(category, action, label, value, noninteraction, custom);
},
addTrans: function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
me._addTrans(transactionId, affiliation, total, tax, shipping, city, state, country, currency);
},
addItem: function (transactionId, sku, name, category, price, quantity) {
me._addItem(transactionId, sku, name, category, price, quantity);
},
trackTrans: function () {
me._trackTrans();
},
clearTrans: function () {
me._clearTrans();
},
addProduct: function (productId, name, category, brand, variant, price, quantity, coupon, position) {
me._addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
},
addPromo: function (productId, name, creative, position) {
me._addPromo(productId, name, creative, position);
},
addImpression: function (productId, name, list, brand, category, variant, position, price) {
me._addImpression(productId, name, list, brand, category, variant, position, price);
},
productClick: function (listName) {
me._productClick(listName);
},
promoClick : function (promotionName) {
me._promoClick(promotionName);
},
trackDetail: function () {
me._setAction('detail');
me._pageView();
},
trackCart: function (action) {
me._trackCart(action);
},
trackCheckout: function (step, option) {
me._trackCheckOut(step, option);
},
trackTimings: function (timingCategory, timingVar, timingValue, timingLabel) {
me._trackTimings(timingCategory, timingVar, timingValue, timingLabel);
},
trackTransaction: function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option){
me._trackTransaction(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option);
},
setAction: function (action, obj) {
me._setAction(action, obj);
},
send: function (obj) {
me._send(obj);
},
pageView: function () {
me._pageView();
},
set: function (name, value) {
me._set(name, value);
restrict: 'A',
link: function (scope, element, attrs) {
var options = $parse(attrs.gaTrackEvent);
element.bind('click', function () {
if(attrs.gaTrackEventIf){
if(!scope.$eval(attrs.gaTrackEventIf)){
return; // Cancel this event if we don't pass the ga-track-event-if condition
}
}
if (options.length > 1) {
Analytics.trackEvent.apply(Analytics, options(scope));
}
});
}
};
}];
})
.directive('gaTrackEvent', ['Analytics', '$parse', function (Analytics, $parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var options = $parse(attrs.gaTrackEvent)(scope);
element.bind('click', function () {
if (options.length > 1) {
Analytics.trackEvent.apply(Analytics, options);
}
});
}
};
}]);
}]);
return angular.module('angular-google-analytics');
}));

File diff suppressed because one or more lines are too long

View File

@ -1,46 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=620, user-scalable=no">
<link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
<style>
html, body {
height:100%;
padding:0;
margin:0;
}
body {
margin: 0;
background: #333;
color:white;
padding: 10px;
}
* {
font-family: 'Droid Sans', sans-serif;
}
h1 {
margin-top: 5px;
}
h3 {
margin: 20px 0 10px 0;
}
div.gist {
width:700px;
}
</style>
</head>
<body ng-app="myApp" ng-controller='demoController'>
<a href="https://github.com/revolunet/angular-google-analytics"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
<h1>AngularJS google analytics</h1>
<div class='intro'>A simple service to use google analytics tracker in your AngularJS apps.</div>
<br><br>
Here's an example usage :
<br><br>
<script src="https://gist.github.com/revolunet/5514543.js"></script>
</body>
</html>

View File

@ -0,0 +1,1247 @@
/* globals define */
(function (root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['angular'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('angular'));
} else {
factory(root.angular);
}
}(this, function (angular, undefined) {
'use strict';
angular.module('angular-google-analytics', [])
.provider('Analytics', function () {
var accounts,
analyticsJS = true,
cookieConfig = 'auto', // DEPRECATED
created = false,
crossDomainLinker = false,
crossLinkDomains,
currency = 'USD',
debugMode = false,
delayScriptTag = false,
displayFeatures = false,
disableAnalytics = false,
domainName,
ecommerce = false,
enhancedEcommerce = false,
enhancedLinkAttribution = false,
experimentId,
ignoreFirstPageLoad = false,
logAllCalls = false,
hybridMobileSupport = false,
offlineMode = false,
pageEvent = '$routeChangeSuccess',
readFromRoute = false,
removeRegExp,
testMode = false,
traceDebuggingMode = false,
trackPrefix = '',
trackRoutes = true,
trackUrlParams = false;
this.log = [];
this.offlineQueue = [];
/**
* Configuration Methods
**/
this.setAccount = function (tracker) {
if (angular.isUndefined(tracker) || tracker === false) {
accounts = undefined;
} else if (angular.isArray(tracker)) {
accounts = tracker;
} else if (angular.isObject(tracker)) {
accounts = [tracker];
} else {
// In order to preserve an existing behavior with how the _trackEvent function works,
// the trackEvent property must be set to true when there is only a single tracker.
accounts = [{ tracker: tracker, trackEvent: true }];
}
return this;
};
this.trackPages = function (val) {
trackRoutes = !!val;
return this;
};
this.trackPrefix = function (prefix) {
trackPrefix = prefix;
return this;
};
this.setDomainName = function (domain) {
domainName = domain;
return this;
};
this.useDisplayFeatures = function (val) {
displayFeatures = !!val;
return this;
};
this.useAnalytics = function (val) {
analyticsJS = !!val;
return this;
};
this.useEnhancedLinkAttribution = function (val) {
enhancedLinkAttribution = !!val;
return this;
};
this.useCrossDomainLinker = function (val) {
crossDomainLinker = !!val;
return this;
};
this.setCrossLinkDomains = function (domains) {
crossLinkDomains = domains;
return this;
};
this.setPageEvent = function (name) {
pageEvent = name;
return this;
};
/* DEPRECATED */
this.setCookieConfig = function (config) {
cookieConfig = config;
return this;
};
this.useECommerce = function (val, enhanced) {
ecommerce = !!val;
enhancedEcommerce = !!enhanced;
return this;
};
this.setCurrency = function (currencyCode) {
currency = currencyCode;
return this;
};
this.setRemoveRegExp = function (regex) {
if (regex instanceof RegExp) {
removeRegExp = regex;
}
return this;
};
this.setExperimentId = function (id) {
experimentId = id;
return this;
};
this.ignoreFirstPageLoad = function (val) {
ignoreFirstPageLoad = !!val;
return this;
};
this.trackUrlParams = function (val) {
trackUrlParams = !!val;
return this;
};
this.disableAnalytics = function (val) {
disableAnalytics = !!val;
return this;
};
this.setHybridMobileSupport = function (val) {
hybridMobileSupport = !!val;
return this;
};
this.startOffline = function (val) {
offlineMode = !!val;
if (offlineMode === true) {
this.delayScriptTag(true);
}
return this;
};
this.delayScriptTag = function (val) {
delayScriptTag = !!val;
return this;
};
this.logAllCalls = function (val) {
logAllCalls = !!val;
return this;
};
this.enterTestMode = function () {
testMode = true;
return this;
};
this.enterDebugMode = function (enableTraceDebugging) {
debugMode = true;
traceDebuggingMode = !!enableTraceDebugging;
return this;
};
// Enable reading page url from route object
this.readFromRoute = function(val) {
readFromRoute = !!val;
return this;
};
/**
* Public Service
*/
this.$get = ['$document', // To read title
'$location', //
'$log', //
'$rootScope',//
'$window', //
'$injector', // To access ngRoute module without declaring a fixed dependency
function ($document, $location, $log, $rootScope, $window, $injector) {
var that = this;
/**
* Side-effect Free Helper Methods
**/
var isPropertyDefined = function (key, config) {
return angular.isObject(config) && angular.isDefined(config[key]);
};
var isPropertySetTo = function (key, config, value) {
return isPropertyDefined(key, config) && config[key] === value;
};
var generateCommandName = function (commandName, config) {
if (angular.isString(config)) {
return config + '.' + commandName;
}
return isPropertyDefined('name', config) ? (config.name + '.' + commandName) : commandName;
};
// Try to read route configuration and log warning if not possible
var $route = {};
if (readFromRoute) {
if (!$injector.has('$route')) {
$log.warn('$route service is not available. Make sure you have included ng-route in your application dependencies.');
} else {
$route = $injector.get('$route');
}
}
// Get url for current page
var getUrl = function () {
// Using ngRoute provided tracking urls
if (readFromRoute && $route.current && ('pageTrack' in $route.current)) {
return $route.current.pageTrack;
}
// Otherwise go the old way
var url = trackUrlParams ? $location.url() : $location.path();
return removeRegExp ? url.replace(removeRegExp, '') : url;
};
var getUtmParams = function () {
var utmToCampaignVar = {
utm_source: 'campaignSource',
utm_medium: 'campaignMedium',
utm_term: 'campaignTerm',
utm_content: 'campaignContent',
utm_campaign: 'campaignName'
};
var object = {};
angular.forEach($location.search(), function (value, key) {
var campaignVar = utmToCampaignVar[key];
if (angular.isDefined(campaignVar)) {
object[campaignVar] = value;
}
});
return object;
};
/**
* get ActionFieldObject
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-data
* @param id
* @param affliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
var getActionFieldObject = function (id, affiliation, revenue, tax, shipping, coupon, list, step, option) {
var obj = {};
if (id) { obj.id = id; }
if (affiliation) { obj.affiliation = affiliation; }
if (revenue) { obj.revenue = revenue; }
if (tax) { obj.tax = tax; }
if (shipping) { obj.shipping = shipping; }
if (coupon) { obj.coupon = coupon; }
if (list) { obj.list = list; }
if (step) { obj.step = step; }
if (option) { obj.option = option; }
return obj;
};
/**
* Private Methods
*/
var _gaJs = function (fn) {
if (!analyticsJS && $window._gaq && typeof fn === 'function') {
fn();
}
};
var _gaq = function () {
var args = Array.prototype.slice.call(arguments);
if (offlineMode === true) {
that.offlineQueue.push([_gaq, args]);
return;
}
if (!$window._gaq) {
$window._gaq = [];
}
if (logAllCalls === true) {
that._log.apply(that, args);
}
$window._gaq.push(args);
};
var _analyticsJs = function (fn) {
if (analyticsJS && $window.ga && typeof fn === 'function') {
fn();
}
};
var _ga = function () {
var args = Array.prototype.slice.call(arguments);
if (offlineMode === true) {
that.offlineQueue.push([_ga, args]);
return;
}
if (typeof $window.ga !== 'function') {
that._log('warn', 'ga function not set on window');
return;
}
if (logAllCalls === true) {
that._log.apply(that, args);
}
$window.ga.apply(null, args);
};
var _gaMultipleTrackers = function (includeFn) {
// Drop the includeFn from the arguments and preserve the original command name
var args = Array.prototype.slice.call(arguments, 1),
commandName = args[0],
trackers = [];
if (typeof includeFn === 'function') {
accounts.forEach(function (account) {
if (includeFn(account)) {
trackers.push(account);
}
});
} else {
// No include function indicates that all accounts are to be used
trackers = accounts;
}
// To preserve backwards compatibility fallback to _ga method if no account
// matches the specified includeFn. This preserves existing behaviors by
// performing the single tracker operation.
if (trackers.length === 0) {
_ga.apply(that, args);
return;
}
trackers.forEach(function (tracker) {
// Check tracker 'select' function, if it exists, for whether the tracker should be used with the current command.
// If the 'select' function returns false then the tracker will not be used with the current command.
if (isPropertyDefined('select', tracker) && typeof tracker.select === 'function' && !tracker.select(args)) {
return;
}
args[0] = generateCommandName(commandName, tracker);
_ga.apply(that, args);
});
};
this._log = function () {
var args = Array.prototype.slice.call(arguments);
if (args.length > 0) {
if (args.length > 1) {
switch (args[0]) {
case 'debug':
case 'error':
case 'info':
case 'log':
case 'warn':
$log[args[0]](args.slice(1));
break;
}
}
that.log.push(args);
}
};
this._createScriptTag = function () {
if (!accounts || accounts.length < 1) {
that._log('warn', 'No account id set to create script tag');
return;
}
if (accounts.length > 1) {
that._log('warn', 'Multiple trackers are not supported with ga.js. Using first tracker only');
accounts = accounts.slice(0, 1);
}
if (created === true) {
that._log('warn', 'ga.js or analytics.js script tag already created');
return;
}
if (disableAnalytics === true) {
that._log('info', 'Analytics disabled: ' + accounts[0].tracker);
$window['ga-disable-' + accounts[0].tracker] = true;
}
_gaq('_setAccount', accounts[0].tracker);
if(domainName) {
_gaq('_setDomainName', domainName);
}
if (enhancedLinkAttribution) {
_gaq('_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js');
}
if (trackRoutes && !ignoreFirstPageLoad) {
if (removeRegExp) {
_gaq('_trackPageview', getUrl());
} else {
_gaq('_trackPageview');
}
}
var document = $document[0];
var scriptSource;
if (displayFeatures === true) {
scriptSource = ('https:' === document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
} else {
scriptSource = ('https:' === document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
}
if (testMode !== true) {
// If not in test mode inject the Google Analytics tag
(function () {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = scriptSource;
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
} else {
// Log the source location for validation
that._log('inject', scriptSource);
}
created = true;
return true;
};
this._createAnalyticsScriptTag = function () {
if (!accounts) {
that._log('warn', 'No account id set to create analytics script tag');
return;
}
if (created === true) {
that._log('warn', 'ga.js or analytics.js script tag already created');
return;
}
if (disableAnalytics === true) {
accounts.forEach(function (trackerObj) {
that._log('info', 'Analytics disabled: ' + trackerObj.tracker);
$window['ga-disable-' + trackerObj.tracker] = true;
});
}
var document = $document[0];
var protocol = hybridMobileSupport === true ? 'https:' : '';
var scriptSource = protocol + '//www.google-analytics.com/' + (debugMode ? 'analytics_debug.js' : 'analytics.js');
if (testMode !== true) {
// If not in test mode inject the Google Analytics tag
(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',scriptSource,'ga');
} else {
if (typeof $window.ga !== 'function') {
// In test mode create a ga function if none exists that is a noop sink.
$window.ga = function () {};
}
// Log script injection.
that._log('inject', scriptSource);
}
if (traceDebuggingMode) {
$window.ga_debug = { trace: true };
}
accounts.forEach(function (trackerObj) {
trackerObj.crossDomainLinker = isPropertyDefined('crossDomainLinker', trackerObj) ? trackerObj.crossDomainLinker : crossDomainLinker;
trackerObj.crossLinkDomains = isPropertyDefined('crossLinkDomains', trackerObj) ? trackerObj.crossLinkDomains : crossLinkDomains;
trackerObj.displayFeatures = isPropertyDefined('displayFeatures', trackerObj) ? trackerObj.displayFeatures : displayFeatures;
trackerObj.enhancedLinkAttribution = isPropertyDefined('enhancedLinkAttribution', trackerObj) ? trackerObj.enhancedLinkAttribution : enhancedLinkAttribution;
trackerObj.set = isPropertyDefined('set', trackerObj) ? trackerObj.set : {};
trackerObj.trackEcommerce = isPropertyDefined('trackEcommerce', trackerObj) ? trackerObj.trackEcommerce : ecommerce;
trackerObj.trackEvent = isPropertyDefined('trackEvent', trackerObj) ? trackerObj.trackEvent : false;
// Logic to choose the account fields to be used.
// cookieConfig is being deprecated for a tracker specific property: fields.
var fields = {};
if (isPropertyDefined('fields', trackerObj)) {
fields = trackerObj.fields;
} else if (isPropertyDefined('cookieConfig', trackerObj)) {
if (angular.isString(trackerObj.cookieConfig)) {
fields.cookieDomain = trackerObj.cookieConfig;
} else {
fields = trackerObj.cookieConfig;
}
} else if (angular.isString(cookieConfig)) {
fields.cookieDomain = cookieConfig;
} else if (cookieConfig) {
fields = cookieConfig;
}
if (trackerObj.crossDomainLinker === true) {
fields.allowLinker = true;
}
if (isPropertyDefined('name', trackerObj)) {
fields.name = trackerObj.name;
}
trackerObj.fields = fields;
_ga('create', trackerObj.tracker, trackerObj.fields);
// Hybrid mobile application support
// https://developers.google.com/analytics/devguides/collection/analyticsjs/tasks
if (hybridMobileSupport === true) {
_ga(generateCommandName('set', trackerObj), 'checkProtocolTask', null);
}
// Send all custom set commands from the trackerObj.set property
for (var key in trackerObj.set) {
if (trackerObj.set.hasOwnProperty(key)) {
_ga(generateCommandName('set', trackerObj), key, trackerObj.set[key]);
}
}
if (trackerObj.crossDomainLinker === true) {
_ga(generateCommandName('require', trackerObj), 'linker');
if (angular.isDefined(trackerObj.crossLinkDomains)) {
_ga(generateCommandName('linker:autoLink', trackerObj), trackerObj.crossLinkDomains);
}
}
if (trackerObj.displayFeatures) {
_ga(generateCommandName('require', trackerObj), 'displayfeatures');
}
if (trackerObj.trackEcommerce) {
if (!enhancedEcommerce) {
_ga(generateCommandName('require', trackerObj), 'ecommerce');
} else {
_ga(generateCommandName('require', trackerObj), 'ec');
_ga(generateCommandName('set', trackerObj), '&cu', currency);
}
}
if (trackerObj.enhancedLinkAttribution) {
_ga(generateCommandName('require', trackerObj), 'linkid');
}
if (trackRoutes && !ignoreFirstPageLoad) {
_ga(generateCommandName('send', trackerObj), 'pageview', trackPrefix + getUrl());
}
});
if (experimentId) {
var expScript = document.createElement('script'),
s = document.getElementsByTagName('script')[0];
expScript.src = protocol + '//www.google-analytics.com/cx/api.js?experiment=' + experimentId;
s.parentNode.insertBefore(expScript, s);
}
created = true;
return true;
};
this._ecommerceEnabled = function (warn, command) {
var result = ecommerce && !enhancedEcommerce;
if (warn === true && result === false) {
if (ecommerce && enhancedEcommerce) {
that._log('warn', command + ' is not available when Enhanced Ecommerce is enabled with analytics.js');
} else {
that._log('warn', 'Ecommerce must be enabled to use ' + command + ' with analytics.js');
}
}
return result;
};
this._enhancedEcommerceEnabled = function (warn, command) {
var result = ecommerce && enhancedEcommerce;
if (warn === true && result === false) {
that._log('warn', 'Enhanced Ecommerce must be enabled to use ' + command + ' with analytics.js');
}
return result;
};
/**
* Track page
https://developers.google.com/analytics/devguides/collection/gajs/
https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
* @param url
* @param title
* @param custom
* @private
*/
this._trackPage = function (url, title, custom) {
url = url ? url : getUrl();
title = title ? title : $document[0].title;
_gaJs(function () {
// http://stackoverflow.com/questions/7322288/how-can-i-set-a-page-title-with-google-analytics
_gaq('_set', 'title', title);
_gaq('_trackPageview', (trackPrefix + url));
});
_analyticsJs(function () {
var opt_fieldObject = {
'page': trackPrefix + url,
'title': title
};
angular.extend(opt_fieldObject, getUtmParams());
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
_gaMultipleTrackers(undefined, 'send', 'pageview', opt_fieldObject);
});
};
/**
* Track event
https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
* @param category
* @param action
* @param label
* @param value
* @param noninteraction
* @param custom
* @private
*/
this._trackEvent = function (category, action, label, value, noninteraction, custom) {
_gaJs(function () {
_gaq('_trackEvent', category, action, label, value, !!noninteraction);
});
_analyticsJs(function () {
var opt_fieldObject = {};
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEvent', trackerObj, true);
};
if (angular.isDefined(noninteraction)) {
opt_fieldObject.nonInteraction = !!noninteraction;
}
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
if (!angular.isDefined(opt_fieldObject.page)) {
opt_fieldObject.page = getUrl();
}
_gaMultipleTrackers(includeFn, 'send', 'event', category, action, label, value, opt_fieldObject);
});
};
/**
* Add transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addTrans
* @param transactionId
* @param affiliation
* @param total
* @param tax
* @param shipping
* @param city
* @param state
* @param country
* @private
*/
this._addTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
_gaJs(function () {
_gaq('_addTrans', transactionId, affiliation, total, tax, shipping, city, state, country);
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'addTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ecommerce:addTransaction',
{
id: transactionId,
affiliation: affiliation,
revenue: total,
tax: tax,
shipping: shipping,
currency: currency || 'USD'
});
}
});
};
/**
* Add item to transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addItem
* @param transactionId
* @param sku
* @param name
* @param category
* @param price
* @param quantity
* @private
*/
this._addItem = function (transactionId, sku, name, category, price, quantity) {
_gaJs(function () {
_gaq('_addItem', transactionId, sku, name, category, price, quantity);
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'addItem')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ecommerce:addItem',
{
id: transactionId,
name: name,
sku: sku,
category: category,
price: price,
quantity: quantity
});
}
});
};
/**
* Track transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#sendingData
* @private
*/
this._trackTrans = function () {
_gaJs(function () {
_gaq('_trackTrans');
});
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'trackTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ecommerce:send');
}
});
};
/**
* Clear transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#clearingData
* @private
*/
this._clearTrans = function () {
_analyticsJs(function () {
if (that._ecommerceEnabled(true, 'clearTrans')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ecommerce:clear');
}
});
};
/**
* Enhanced Ecommerce
*/
/**
* Add Product
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-data
* @param productId
* @param name
* @param category
* @param brand
* @param variant
* @param price
* @param quantity
* @param coupon
* @param position
* @param custom
* @private
*/
this._addProduct = function (productId, name, category, brand, variant, price, quantity, coupon, position, custom) {
_gaJs(function () {
_gaq('_addProduct', productId, name, category, brand, variant, price, quantity, coupon, position);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addProduct')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
var details = {
id: productId,
name: name,
category: category,
brand: brand,
variant: variant,
price: price,
quantity: quantity,
coupon: coupon,
position: position
};
if (angular.isObject(custom)) {
angular.extend(details, custom);
}
_gaMultipleTrackers(includeFn, 'ec:addProduct', details);
}
});
};
/**
* Add Impression
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#impression-data
* @param id
* @param name
* @param list
* @param brand
* @param category
* @param variant
* @param position
* @param price
* @private
*/
this._addImpression = function (id, name, list, brand, category, variant, position, price){
_gaJs(function () {
_gaq('_addImpression', id, name, list, brand, category, variant, position, price);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addImpression')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ec:addImpression',
{
id: id,
name: name,
category: category,
brand: brand,
variant: variant,
list: list,
position: position,
price: price
});
}
});
};
/**
* Add Promo
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce
* @param productId
* @param name
* @param creative
* @param position
* @private
*/
this._addPromo = function (productId, name, creative, position) {
_gaJs(function () {
_gaq('_addPromo', productId, name, creative, position);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'addPromo')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(
includeFn,
'ec:addPromo',
{
id: productId,
name: name,
creative: creative,
position: position
});
}
});
};
/**
* Set Action
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-actions
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-types
* @param action
* @param obj
* @private
*/
this._setAction = function (action, obj){
_gaJs(function () {
_gaq('_setAction', action, obj);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled(true, 'setAction')) {
var includeFn = function (trackerObj) {
return isPropertySetTo('trackEcommerce', trackerObj, true);
};
_gaMultipleTrackers(includeFn, 'ec:setAction', action, obj);
}
});
};
/**
* Track Transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-transactions
* @param transactionId
* @param affiliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
* @private
*/
this._trackTransaction = function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
this._setAction('purchase', getActionFieldObject(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option));
};
/**
* Track Refund
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-refunds
* @param transactionId
* @private
*/
this._trackRefund = function (transactionId) {
this._setAction('refund', getActionFieldObject(transactionId));
};
/**
* Track Checkout
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-checkout
* @param step
* @param option
* @private
*/
this._trackCheckOut = function (step, option) {
this._setAction('checkout', getActionFieldObject(null, null, null, null, null, null, null, step, option));
};
/**
* Track detail
* @private
*/
this._trackDetail = function () {
this._setAction('detail');
this._pageView();
};
/**
* Track add/remove to cart
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
* @param action
* @param list
* @private
*/
this._trackCart = function (action, listName) {
if (['add', 'remove'].indexOf(action) !== -1) {
this._setAction(action, { list: listName });
this._trackEvent('UX', 'click', action + (action === 'add' ? ' to cart' : ' from cart'));
}
};
/**
* Track promo click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
* @private
*/
this._promoClick = function (promotionName) {
this._setAction('promo_click');
this._trackEvent('Internal Promotions', 'click', promotionName);
};
/**
* Track product click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
* @private
*/
this._productClick = function (listName) {
this._setAction('click', getActionFieldObject(null, null, null, null, null, null, listName, null, null));
this._trackEvent('UX', 'click', listName);
};
/**
* Send page view
* @param trackerName
* @private
*/
this._pageView = function (trackerName) {
_analyticsJs(function () {
_ga(generateCommandName('send', trackerName), 'pageview');
});
};
/**
* Send custom events
* https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#implementation
* https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions#implementation
* @private
*/
this._send = function () {
var args = Array.prototype.slice.call(arguments);
args.unshift('send');
_analyticsJs(function () {
_ga.apply(that, args);
});
};
/**
* Set custom dimensions, metrics or experiment
* https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
* https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
* @param name (Required)
* @param value (Required)
* @param trackerName (Optional)
* @private
*/
this._set = function (name, value, trackerName) {
_analyticsJs(function () {
_ga(generateCommandName('set', trackerName), name, value);
});
};
/**
* Track user timings
* @param timingCategory (Required): A string for categorizing all user timing variables into logical groups(e.g jQuery).
* @param timingVar (Required): A string to identify the variable being recorded(e.g. JavaScript Load).
* @param timingValue (Required): The number of milliseconds in elapsed time to report to Google Analytics(e.g. 20).
* @param timingLabel (Optional): A string that can be used to add flexibility in visualizing user timings in the reports(e.g. Google CDN).
* @private
*/
this._trackTimings = function (timingCategory, timingVar, timingValue, timingLabel) {
_analyticsJs(function () {
_gaMultipleTrackers(undefined, 'send', 'timing', timingCategory, timingVar, timingValue, timingLabel);
});
};
/**
* Exception tracking
* https://developers.google.com/analytics/devguides/collection/analyticsjs/exceptions
* @param description (Optional): A description of the exception.
* @param isFatal (Optional): true if the exception was fatal, false otherwise.
* @private
*/
this._trackException = function (description, isFatal) {
_analyticsJs(function () {
_gaMultipleTrackers(undefined, 'send', 'exception', { exDescription: description, exFatal: !!isFatal});
});
};
// creates the Google Analytics tracker
if (!delayScriptTag) {
if (analyticsJS) {
this._createAnalyticsScriptTag();
} else {
this._createScriptTag();
}
}
// activates page tracking
if (trackRoutes) {
$rootScope.$on(pageEvent, function () {
// Apply $route based filtering if configured
if (readFromRoute) {
// Avoid tracking undefined routes, routes without template (e.g. redirect routes)
// and those explicitly marked as 'do not track'
if (!$route.current || !$route.current.templateUrl || $route.current.doNotTrack) {
return;
}
}
that._trackPage();
});
}
return {
log: that.log,
offlineQueue: that.offlineQueue,
configuration: {
accounts: accounts,
universalAnalytics: analyticsJS,
crossDomainLinker: crossDomainLinker,
crossLinkDomains: crossLinkDomains,
currency: currency,
debugMode: debugMode,
delayScriptTag: delayScriptTag,
disableAnalytics: disableAnalytics,
displayFeatures: displayFeatures,
domainName: domainName,
ecommerce: that._ecommerceEnabled(),
enhancedEcommerce: that._enhancedEcommerceEnabled(),
enhancedLinkAttribution: enhancedLinkAttribution,
experimentId: experimentId,
hybridMobileSupport: hybridMobileSupport,
ignoreFirstPageLoad: ignoreFirstPageLoad,
logAllCalls: logAllCalls,
pageEvent: pageEvent,
readFromRoute: readFromRoute,
removeRegExp: removeRegExp,
testMode: testMode,
traceDebuggingMode: traceDebuggingMode,
trackPrefix: trackPrefix,
trackRoutes: trackRoutes,
trackUrlParams: trackUrlParams
},
getUrl: getUrl,
/* DEPRECATED */
setCookieConfig: that._setCookieConfig,
/* DEPRECATED */
getCookieConfig: function () {
return cookieConfig;
},
createAnalyticsScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return that._createAnalyticsScriptTag();
},
createScriptTag: function () {
return that._createScriptTag();
},
offline: function (mode) {
if (mode === true && offlineMode === false) {
// Go to offline mode
offlineMode = true;
}
if (mode === false && offlineMode === true) {
// Go to online mode and process the offline queue
offlineMode = false;
while (that.offlineQueue.length > 0) {
var obj = that.offlineQueue.shift();
obj[0].apply(that, obj[1]);
}
}
return offlineMode;
},
trackPage: function (url, title, custom) {
that._trackPage.apply(that, arguments);
},
trackEvent: function (category, action, label, value, noninteraction, custom) {
that._trackEvent.apply(that, arguments);
},
addTrans: function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
that._addTrans.apply(that, arguments);
},
addItem: function (transactionId, sku, name, category, price, quantity) {
that._addItem.apply(that, arguments);
},
trackTrans: function () {
that._trackTrans.apply(that, arguments);
},
clearTrans: function () {
that._clearTrans.apply(that, arguments);
},
addProduct: function (productId, name, category, brand, variant, price, quantity, coupon, position, custom) {
that._addProduct.apply(that, arguments);
},
addPromo: function (productId, name, creative, position) {
that._addPromo.apply(that, arguments);
},
addImpression: function (productId, name, list, brand, category, variant, position, price) {
that._addImpression.apply(that, arguments);
},
productClick: function (listName) {
that._productClick.apply(that, arguments);
},
promoClick : function (promotionName) {
that._promoClick.apply(that, arguments);
},
trackDetail: function () {
that._trackDetail.apply(that, arguments);
},
trackCart: function (action, list) {
that._trackCart.apply(that, arguments);
},
trackCheckout: function (step, option) {
that._trackCheckOut.apply(that, arguments);
},
trackTimings: function (timingCategory, timingVar, timingValue, timingLabel) {
that._trackTimings.apply(that, arguments);
},
trackTransaction: function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
that._trackTransaction.apply(that, arguments);
},
trackException: function (description, isFatal) {
that._trackException.apply(that, arguments);
},
setAction: function (action, obj) {
that._setAction.apply(that, arguments);
},
pageView: function () {
that._pageView.apply(that, arguments);
},
send: function (obj) {
that._send.apply(that, arguments);
},
set: function (name, value, trackerName) {
that._set.apply(that, arguments);
}
};
}];
})
.directive('gaTrackEvent', ['Analytics', '$parse', function (Analytics, $parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var options = $parse(attrs.gaTrackEvent);
element.bind('click', function () {
if(attrs.gaTrackEventIf){
if(!scope.$eval(attrs.gaTrackEventIf)){
return; // Cancel this event if we don't pass the ga-track-event-if condition
}
}
if (options.length > 1) {
Analytics.trackEvent.apply(Analytics, options(scope));
}
});
}
};
}]);
return angular.module('angular-google-analytics');
}));

View File

@ -1,33 +1,36 @@
{
"name": "angular-google-analytics",
"description": "Angular Google Analytics - Easy tracking for your AngularJS application",
"version": "0.0.15",
"version": "1.1.7",
"homepage": "http://github.com/revolunet/angular-google-analytics",
"author": "Julien Bouquillon <julien@revolunet.com>",
"main": "./src/angular-google-analytics.js",
"author": "Julien Bouquillon <julien@revolunet.com> (https://github.com/revolunet)",
"contributors": [
"Julien Bouquillon (https://github.com/revolunet)",
"Justin Saunders (https://github.com/justinsa)",
"Chris Esplin (https://github.com/deltaepsilon)",
"Adam Misiorny (https://github.com/adam187)"
],
"main": "index.js",
"repository": {
"type": "git",
"url": "git://github.com/revolunet/angular-google-analytics.git"
},
"engines": {
"node": ">= 0.8"
"node": "*",
"npm": "*"
},
"licenses": [
{
"type": "MIT",
"url": "http://revolunet.mit-license.org"
}
],
"license": "MIT",
"dependencies": {},
"devDependencies": {
"bower": "*",
"karma": "~0.8.0",
"grunt": ">=0.4.0",
"karma": "*",
"karma-chrome-launcher": "*",
"karma-jasmine": "*",
"grunt": "*",
"grunt-cli": "*",
"grunt-contrib-concat": "*",
"grunt-contrib-jshint": "*",
"grunt-contrib-uglify": "*",
"grunt-release": "~0.3.3",
"grunt-conventional-changelog": "0.0.12"
"grunt-contrib-uglify": "*"
},
"scripts": {
"test": "karma start test/karma.conf.js --single-run --browsers Chrome",

View File

@ -1,902 +0,0 @@
'use strict';
angular.module('angular-google-analytics', [])
.provider('Analytics', function () {
var created = false,
trackRoutes = true,
accountId,
displayFeatures,
trackPrefix = '',
domainName,
analyticsJS = false,
pageEvent = '$routeChangeSuccess',
cookieConfig = 'auto',
ecommerce = false,
enhancedEcommerce = false,
enhancedLinkAttribution = false,
removeRegExp,
experimentId,
ignoreFirstPageLoad = false,
crossDomainLinker = false,
crossLinkDomains,
linkerConfig = {'allowLinker': true},
trackUrlParams = false,
delayScriptTag = false;
this._logs = [];
// config methods
this.setAccount = function (id) {
accountId = id;
return true;
};
this.trackPages = function (doTrack) {
trackRoutes = doTrack;
return true;
};
this.trackPrefix = function (prefix) {
trackPrefix = prefix;
return true;
};
this.setDomainName = function (domain) {
domainName = domain;
return true;
};
this.useDisplayFeatures = function (val) {
displayFeatures = !!val;
return true;
};
this.useAnalytics = function (val) {
analyticsJS = !!val;
return true;
};
this.useEnhancedLinkAttribution = function (val) {
enhancedLinkAttribution = !!val;
return true;
};
this.useCrossDomainLinker = function (val) {
crossDomainLinker = !!val;
return true;
};
this.setCrossLinkDomains = function (domains) {
crossLinkDomains = domains;
return true;
};
this.setPageEvent = function (name) {
pageEvent = name;
return true;
};
this.setCookieConfig = function (config) {
cookieConfig = config;
return true;
};
this.useECommerce = function (val, enhanced) {
ecommerce = !!val;
enhancedEcommerce = !!enhanced;
return true;
};
this.setRemoveRegExp = function (regex) {
if (regex instanceof RegExp) {
removeRegExp = regex;
return true;
}
return false;
};
this.setExperimentId = function (id) {
experimentId = id;
return true;
};
this.ignoreFirstPageLoad = function (val) {
ignoreFirstPageLoad = !!val;
return true;
};
this.trackUrlParams = function (val) {
trackUrlParams = !!val;
return true;
};
this.delayScriptTag = function (val) {
delayScriptTag = !!val;
return true;
};
/**
* Public Service
*/
this.$get = ['$document', '$location', '$log', '$rootScope', '$window', function ($document, $location, $log, $rootScope, $window) {
var me = this;
var getUrl = function () {
var url = trackUrlParams ? $location.url() : $location.path();
return removeRegExp ? url.replace(removeRegExp, '') : url;
};
/**
* Private Methods
*/
function _gaJs(fn) {
if (!analyticsJS && $window._gaq && typeof fn === 'function') {
fn();
}
}
function _analyticsJs(fn) {
if (analyticsJS && $window.ga && typeof fn === 'function') {
fn();
}
}
function _generateCommandName(commandName, config) {
if (!angular.isUndefined(config) && 'name' in config && config.name) {
return config.name + '.' + commandName;
} else {
return commandName;
}
}
function _checkOption(key, config) {
return key in config && config[key];
}
this._log = function () {
if (arguments.length > 0) {
if (arguments.length > 1 && arguments[0] === 'warn') {
$log.warn(Array.prototype.slice.call(arguments, 1));
}
// console.log('analytics', arguments);
this._logs.push(arguments);
}
};
this._createScriptTag = function () {
if (!accountId) {
me._log('warn', 'No account id set to create script tag');
return;
}
if (created) {
me._log('warn', 'Script tag already created');
return;
}
// inject the google analytics tag
$window._gaq = [];
$window._gaq.push(['_setAccount', accountId]);
if(domainName) {
$window._gaq.push(['_setDomainName', domainName]);
}
if (enhancedLinkAttribution) {
$window._gaq.push(['_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js']);
}
if (trackRoutes && !ignoreFirstPageLoad) {
if (removeRegExp) {
$window._gaq.push(['_trackPageview', getUrl()]);
} else {
$window._gaq.push(['_trackPageview']);
}
}
var gaSrc;
if (displayFeatures) {
gaSrc = ('https:' === document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';
} else {
gaSrc = ('https:' === document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
}
(function () {
var document = $document[0];
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = gaSrc;
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})(gaSrc);
return created = true;
};
this._createAnalyticsScriptTag = function () {
if (!accountId) {
me._log('warn', 'No account id set to create analytics script tag');
return;
}
if (created) {
me._log('warn', 'Analytics script tag already created');
return;
}
// inject the google analytics tag
(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');
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
var _cookieConfig = 'cookieConfig' in trackerObj ? trackerObj.cookieConfig : cookieConfig;
var options;
if (_checkOption('crossDomainLinker', trackerObj)) {
trackerObj.allowLinker = trackerObj.crossDomainLinker;
}
angular.forEach(['name', 'allowLinker'], function(key) {
if (key in trackerObj) {
if (angular.isUndefined(options)) {
options = {};
}
options[key] = trackerObj[key];
}
});
if (angular.isUndefined(options)) {
$window.ga('create', trackerObj.tracker, _cookieConfig);
} else {
$window.ga('create', trackerObj.tracker, _cookieConfig, options);
}
if (options && 'allowLinker' in options && options.allowLinker) {
$window.ga(_generateCommandName('require', trackerObj), 'linker');
if (_checkOption('crossLinkDomains', trackerObj)) {
$window.ga(_generateCommandName('linker:autoLink', trackerObj), trackerObj.crossLinkDomains);
}
}
});
} else if (crossDomainLinker) {
$window.ga('create', accountId, cookieConfig, linkerConfig);
$window.ga('require', 'linker');
if(crossLinkDomains) {
$window.ga('linker:autoLink', crossLinkDomains );
}
} else {
$window.ga('create', accountId, cookieConfig);
}
if (displayFeatures) {
$window.ga('require', 'displayfeatures');
}
if (trackRoutes && !ignoreFirstPageLoad) {
$window.ga('send', 'pageview', getUrl());
}
if ($window.ga) {
if (ecommerce) {
if (!enhancedEcommerce) {
$window.ga('require', 'ecommerce', 'ecommerce.js');
} else {
$window.ga('require', 'ec', 'ec.js');
}
}
if (enhancedLinkAttribution) {
$window.ga('require', 'linkid', 'linkid.js');
}
if (experimentId) {
var expScript = document.createElement('script'),
s = document.getElementsByTagName('script')[0];
expScript.src = "//www.google-analytics.com/cx/api.js?experiment=" + experimentId;
s.parentNode.insertBefore(expScript, s);
}
}
return created = true;
};
this._ecommerceEnabled = function () {
if (!ecommerce) {
this._log('warn', 'ecommerce not set. Use AnalyticsProvider.setECommerce(true, false);');
return false;
} else if (enhancedEcommerce) {
this._log('warn', 'Enhanced ecommerce plugin is enabled. Only one plugin(ecommerce/ec) can be used at a time. ' +
'Use AnalyticsProvider.setECommerce(true, false);');
return false;
}
return true;
};
this._enhancedEcommerceEnabled = function () {
if (!ecommerce) {
this._log('warn', 'ecommerce not set. Use AnalyticsProvider.setECommerce(true, true);');
return false;
} else if (!enhancedEcommerce) {
this._log('warn', 'Enhanced ecommerce plugin is disabled. Use AnalyticsProvider.setECommerce(true, true);');
return false;
}
return true;
};
/**
* Track page
https://developers.google.com/analytics/devguides/collection/gajs/
https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
* @param url
* @param title
* @param custom
* @private
*/
this._trackPage = function (url, title, custom) {
var that = this, args = arguments;
url = url ? url : getUrl();
title = title ? title : $document[0].title;
_gaJs(function () {
// http://stackoverflow.com/questions/7322288/how-can-i-set-a-page-title-with-google-analytics
$window._gaq.push(["_set", "title", title]);
$window._gaq.push(['_trackPageview', trackPrefix + url]);
that._log('_trackPageview', url, title, args);
});
_analyticsJs(function () {
var opt_fieldObject = {
'page': trackPrefix + url,
'title': title
};
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
$window.ga(_generateCommandName('send', trackerObj), 'pageview', opt_fieldObject);
});
} else {
$window.ga('send', 'pageview', opt_fieldObject);
}
that._log('pageview', url, title, args);
});
};
/**
* Track event
https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
* @param category
* @param action
* @param label
* @param value
* @param noninteraction
* @param custom
* @private
*/
this._trackEvent = function (category, action, label, value, noninteraction, custom) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_trackEvent', category, action, label, value, !!noninteraction]);
that._log('trackEvent', args);
});
_analyticsJs(function () {
var opt_fieldObject = {};
if (angular.isDefined(noninteraction)) {
opt_fieldObject['nonInteraction'] = !!noninteraction;
}
if (angular.isObject(custom)) {
angular.extend(opt_fieldObject, custom);
}
if (angular.isArray(accountId)) {
accountId.forEach(function (trackerObj) {
if (_checkOption('trackEvent', trackerObj)) {
$window.ga(_generateCommandName('send', trackerObj), 'event', category, action, label, value, opt_fieldObject);
}
});
} else {
$window.ga('send', 'event', category, action, label, value, opt_fieldObject);
}
that._log('event', args);
});
};
/**
* Add transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addTrans
* @param transactionId
* @param affiliation
* @param total
* @param tax
* @param shipping
* @param city
* @param state
* @param country
* @private
*/
this._addTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addTrans', transactionId, affiliation, total, tax, shipping, city, state, country]);
that._log('_addTrans', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:addTransaction', {
id: transactionId,
affiliation: affiliation,
revenue: total,
tax: tax,
shipping: shipping,
currency: currency || 'USD'
});
that._log('ecommerce:addTransaction', args);
}
});
};
/**
* Add item to transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addItem
* @param transactionId
* @param sku
* @param name
* @param category
* @param price
* @param quantity
* @private
*/
this._addItem = function (transactionId, sku, name, category, price, quantity) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addItem', transactionId, sku, name, category, price, quantity]);
that._log('_addItem', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:addItem', {
id: transactionId,
name: name,
sku: sku,
category: category,
price: price,
quantity: quantity
});
that._log('ecommerce:addItem', args);
}
});
};
/**
* Track transaction
* https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#sendingData
* @private
*/
this._trackTrans = function () {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_trackTrans']);
that._log('_trackTrans', args);
});
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:send');
that._log('ecommerce:send', args);
}
});
};
/**
* Clear transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#clearingData
* @private
*/
this._clearTrans = function () {
var that = this, args = arguments;
_analyticsJs(function () {
if (that._ecommerceEnabled()) {
$window.ga('ecommerce:clear');
that._log('ecommerce:clear', args);
}
});
};
/**
* Enhanced Ecommerce
*/
/**
* Add product data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#product-data
* @param productId
* @param name
* @param category
* @param brand
* @param variant
* @param price
* @param quantity
* @param coupon
* @param position
*/
this._addProduct = function (productId, name, category, brand, variant, price, quantity, coupon, position) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addProduct', productId, name, category, brand, variant, price, quantity, coupon, position]);
that._log('_addProduct', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addProduct', {
id: productId,
name: name,
category: category,
brand: brand,
variant: variant,
price: price,
quantity: quantity,
coupon: coupon,
position: position
});
that._log('ec:addProduct', args);
}
});
};
/**
* Add Impression data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#impression-data
* @param id
* @param name
* @param list
* @param brand
* @param category
* @param variant
* @param position
* @param price
*/
this._addImpression = function (id, name, list, brand, category, variant, position, price){
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addImpression', id, name, list, brand, category, variant, position, price]);
that._log('_addImpression', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addImpression', {
id: id,
name: name,
category: category,
brand: brand,
variant: variant,
list: list,
position: position,
price: price
});
}
that._log('ec:addImpression', args);
});
};
/**
* Add promo data
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce
* @param productId
* @param name
* @param creative
* @param position
*/
this._addPromo = function (productId, name, creative, position) {
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_addPromo', productId, name, creative, position]);
that._log('_addPromo', arguments);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:addPromo', {
id: productId,
name: name,
creative: creative,
position: position
});
that._log('ec:addPromo', args);
}
});
};
/**
* get ActionFieldObject
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-data
* @param id
* @param affliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
this._getActionFieldObject = function (id, affiliation, revenue, tax, shipping, coupon, list, step, option) {
var obj = {};
if (id) { obj.id = id; }
if (affiliation) { obj.affiliation = affiliation; }
if (revenue) { obj.revenue = revenue; }
if (tax) { obj.tax = tax; }
if (shipping) { obj.shipping = shipping; }
if (coupon) { obj.coupon = coupon; }
if (list) { obj.list = list; }
if (step) { obj.step = step; }
if (option) { obj.option = option; }
return obj;
};
/**
* Set Action being performed
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-actions
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#action-types
* @param action
* @param name
* @param obj
*/
this._setAction = function (action, obj){
var that = this, args = arguments;
_gaJs(function () {
$window._gaq.push(['_setAction', action, obj]);
that._log('__setAction', args);
});
_analyticsJs(function () {
if (that._enhancedEcommerceEnabled()) {
$window.ga('ec:setAction', action, obj);
that._log('ec:setAction', args);
}
});
};
/**
* Track Transaction
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-transactions
* @param transactionId
* @param affiliation
* @param revenue
* @param tax
* @param shipping
* @param coupon
* @param list
* @param step
* @param option
*/
this._trackTransaction = function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option) {
this._setAction('purchase', this._getActionFieldObject(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option));
this._pageView();
};
/**
* Track Refund
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-refunds
* @param transactionId
*
*/
this._trackRefund = function (transactionId) {
this._setAction('refund', this._getActionFieldObject(transactionId));
this._pageView();
};
/**
* Track Checkout
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-checkout
* @param step
* @param option
*
*/
this._trackCheckOut = function (step, option) {
this._setAction('checkout', this._getActionFieldObject(null, null, null, null, null, null, null, step, option));
this._pageView();
};
/**
* Track add/remove to cart
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#add-remove-cart
* @param action
*
*/
this._trackCart = function (action) {
if (['add', 'remove'].indexOf(action) !== -1) {
this._setAction(action);
this._send('event', 'UX', 'click', action + ' to cart');
}
};
/**
* Track promo click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
*
*/
this._promoClick = function (promotionName) {
this._setAction('promo_click');
this._send('event', 'Internal Promotions', 'click', promotionName);
};
/**
* Track product click
* https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-promo-clicks
* @param promotionName
*
*/
this._productClick = function (listName) {
this._setAction('click', this._getActionFieldObject(null, null, null, null, null, null, listName, null, null));
this._send('event', 'UX', 'click', listName);
};
/**
* Send custom events
* https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#implementation
* https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions#implementation
*
* @param obj
* @private
*/
this._send = function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
args.unshift('send');
_analyticsJs(function () {
$window.ga.apply(this, args);
that._log(args);
});
};
this._pageView = function() {
this._send('pageview');
};
/**
* Set custom dimensions, metrics or experiment
* https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
* https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs
* @param name
* @param value
* @private
*/
this._set = function (name, value) {
var that = this;
_analyticsJs(function () {
$window.ga('set', name, value);
that._log('set', name, value);
});
};
// creates the ganalytics tracker
if (!delayScriptTag) {
if (analyticsJS) {
this._createAnalyticsScriptTag();
} else {
this._createScriptTag();
}
}
// activates page tracking
if (trackRoutes) {
$rootScope.$on(pageEvent, function () {
me._trackPage();
});
}
/**
* Track User Timings
* @timingCategory (Required): A string for categorizing all user timing variables into logical groups(e.g jQuery).
* @timingVar (Required): A string to identify the variable being recorded(e.g. JavaScript Load).
* @timingValue (Required): The number of milliseconds in elapsed time to report to Google Analytics(e.g. 20).
* @timingLabel (Not Required): A string that can be used to add flexibility in visualizing user timings in the reports(e.g. Google CDN).
*/
this._trackTimings = function (timingCategory, timingVar, timingValue, timingLabel) {
this._send('timing', timingCategory, timingVar, timingValue, timingLabel);
};
return {
_logs: me._logs,
displayFeatures: displayFeatures,
ecommerce: ecommerce,
enhancedEcommerce: enhancedEcommerce,
enhancedLinkAttribution: enhancedLinkAttribution,
getUrl: getUrl,
experimentId: experimentId,
ignoreFirstPageLoad: ignoreFirstPageLoad,
delayScriptTag: delayScriptTag,
setCookieConfig: me._setCookieConfig,
getCookieConfig: function () {
return cookieConfig;
},
createAnalyticsScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return me._createAnalyticsScriptTag();
},
createScriptTag: function (config) {
if (config) {
cookieConfig = config;
}
return me._createScriptTag();
},
ecommerceEnabled: function () {
return me._ecommerceEnabled();
},
enhancedEcommerceEnabled: function () {
return me._enhancedEcommerceEnabled();
},
trackPage: function (url, title, custom) {
me._trackPage(url, title, custom);
},
trackEvent: function (category, action, label, value, noninteraction, custom) {
me._trackEvent(category, action, label, value, noninteraction, custom);
},
addTrans: function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) {
me._addTrans(transactionId, affiliation, total, tax, shipping, city, state, country, currency);
},
addItem: function (transactionId, sku, name, category, price, quantity) {
me._addItem(transactionId, sku, name, category, price, quantity);
},
trackTrans: function () {
me._trackTrans();
},
clearTrans: function () {
me._clearTrans();
},
addProduct: function (productId, name, category, brand, variant, price, quantity, coupon, position) {
me._addProduct(productId, name, category, brand, variant, price, quantity, coupon, position);
},
addPromo: function (productId, name, creative, position) {
me._addPromo(productId, name, creative, position);
},
addImpression: function (productId, name, list, brand, category, variant, position, price) {
me._addImpression(productId, name, list, brand, category, variant, position, price);
},
productClick: function (listName) {
me._productClick(listName);
},
promoClick : function (promotionName) {
me._promoClick(promotionName);
},
trackDetail: function () {
me._setAction('detail');
me._pageView();
},
trackCart: function (action) {
me._trackCart(action);
},
trackCheckout: function (step, option) {
me._trackCheckOut(step, option);
},
trackTimings: function (timingCategory, timingVar, timingValue, timingLabel) {
me._trackTimings(timingCategory, timingVar, timingValue, timingLabel);
},
trackTransaction: function (transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option){
me._trackTransaction(transactionId, affiliation, revenue, tax, shipping, coupon, list, step, option);
},
setAction: function (action, obj) {
me._setAction(action, obj);
},
send: function (obj) {
me._send(obj);
},
pageView: function () {
me._pageView();
},
set: function (name, value) {
me._set(name, value);
}
};
}];
})
.directive('gaTrackEvent', ['Analytics', '$parse', function (Analytics, $parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var options = $parse(attrs.gaTrackEvent)(scope);
element.bind('click', function () {
if (options.length > 1) {
Analytics.trackEvent.apply(Analytics, options);
}
});
}
};
}]);

View File

@ -1,73 +1,24 @@
// Testacular configuration file
//
// For all available config options and default values, see:
// https://github.com/vojtajina/testacular/blob/stable/lib/config.js#L54
// base path, that will be used to resolve files and exclude
basePath = './..';
// frameworks to use
// frameworks = ['jasmine'];
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'src/*.js',
'test/unit/*.js'
];
// list of files to exclude
exclude = [];
// use dots reporter, as travis terminal does not support escaping sequences
// possible values: 'dots', 'progress', 'junit'
// CLI --reporters progress
// 'coverage',
reporters = ['progress'];
// web server port
// CLI --port 9876
port = 9876;
// cli runner port
// CLI --runner-port 9100
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
// CLI --colors --no-colors
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
// CLI --log-level debug
logLevel = LOG_INFO;
// enable / disable watching file and executing tests whenever any file changes
// CLI --auto-watch --no-auto-watch
autoWatch = true;
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
// CLI --browsers Chrome,Firefox,Safari
browsers = ['Chrome'];
// If browser does not capture in given timeout [ms], kill it
// CLI --capture-timeout 5000
captureTimeout = 5000;
// Auto run tests on start (when browsers are captured) and exit
// CLI --single-run --no-single-run
singleRun = false;
// report which specs are slower than 500ms
// CLI --report-slower-than 500
reportSlowerThan = 500;
// Karma v0.13 http://karma-runner.github.io/0.13/config/configuration-file.html
// See configuration documentation above for CLI arguments
module.exports = function(config) {
config.set({
basePath: './..',
frameworks: ['jasmine'],
autoWatch: true,
colors: true,
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'index.js',
'test/unit/*.js'
],
reporters: ['progress'],
browsers: ['Chrome'],
logLevel: config.LOG_INFO,
captureTimeout: 5000,
singleRun: false,
port: 9876,
runnerPort: 9100,
reportSlowerThan: 500
});
};

View File

@ -1,837 +0,0 @@
/* global before, beforeEach, describe, expect, inject, it, module, spyOn */
'use strict';
describe('angular-google-analytics', function() {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount('UA-XXXXXX-xx');
}));
describe('required settings missing', function () {
describe('for default ga script injection', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(false);
AnalyticsProvider.useAnalytics(false);
}));
it('should not inject a script tag', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='http://www.google-analytics.com/ga.js']").length).toBe(0);
});
});
it('should issue a warning to the log', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('warn');
expect(Analytics._logs[0][1]).toBe('No account id set to create script tag');
expect($log.warn).toHaveBeenCalledWith(['No account id set to create script tag']);
});
});
});
});
describe('for analytics script injection', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(false);
AnalyticsProvider.useAnalytics(true);
}));
it('should not inject a script tag', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='//www.google-analytics.com/analytics.js']").length).toBe(0);
});
});
it('should issue a warning to the log', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('warn');
expect(Analytics._logs[0][1]).toBe('No account id set to create analytics script tag');
expect($log.warn).toHaveBeenCalledWith(['No account id set to create analytics script tag']);
});
});
});
});
});
describe('enabled delayedScriptTag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should have a truthy value for Analytics.delayScriptTag', function () {
inject(function (Analytics, $location) {
expect(Analytics.delayScriptTag).toBe(true);
});
});
it('should not inject a script tag', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='http://www.google-analytics.com/ga.js']").length).toBe(0);
});
});
});
describe('automatic trackPages with ga.js', function () {
it('should inject the GA script', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='http://www.google-analytics.com/ga.js']").length).toBe(1);
});
});
it('should generate trackPages', function () {
inject(function (Analytics, $window) {
expect(Analytics._logs.length).toBe(0);
Analytics.trackPage('test');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
expect($window._gaq.length).toBe(4);
expect($window._gaq[0][0]).toBe('_setAccount');
expect($window._gaq[2][0]).toBe('_set');
expect($window._gaq[2][1]).toBe('title');
expect($window._gaq[2][2]).toBe('');
expect($window._gaq[3][0]).toBe('_trackPageview');
expect($window._gaq[3][1]).toBe('test');
});
});
it('should generate a trackPage on routeChangeSuccess', function () {
inject(function (Analytics, $rootScope) {
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
});
});
});
describe('NOT automatic trackPages', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should NOT generate a trackpage on routeChangeSuccess', function () {
inject(function (Analytics, $rootScope) {
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics._logs.length).toBe(0);
});
});
it('should generate a trackpage when explicitly called', function () {
inject(function (Analytics, $window) {
Analytics.trackPage('/page/here');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
expect($window._gaq.length).toBe(3);
expect($window._gaq[0][0]).toBe('_setAccount');
expect($window._gaq[1][0]).toBe('_set');
expect($window._gaq[1][1]).toBe('title');
expect($window._gaq[1][2]).toBe('');
expect($window._gaq[2][0]).toBe('_trackPageview');
expect($window._gaq[2][1]).toBe('/page/here');
});
});
});
describe('eventTracks with ga.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should generate eventTracks', function () {
inject(function (Analytics, $window) {
Analytics.trackEvent('test');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('trackEvent');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0][0]).toBe('_setAccount');
expect($window._gaq[1][0]).toBe('_trackEvent');
expect($window._gaq[1][1]).toBe('test');
expect($window._gaq[1][2]).toBe(undefined);
expect($window._gaq[1][3]).toBe(undefined);
expect($window._gaq[1][4]).toBe(undefined);
expect($window._gaq[1][5]).toBe(false);
});
});
it('should generate eventTracks with non-interactions', function () {
inject(function (Analytics, $window) {
Analytics.trackEvent('test', 'action', 'label', 0, true);
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('trackEvent');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0][0]).toBe('_setAccount');
expect($window._gaq[1][0]).toBe('_trackEvent');
expect($window._gaq[1][1]).toBe('test');
expect($window._gaq[1][2]).toBe('action');
expect($window._gaq[1][3]).toBe('label');
expect($window._gaq[1][4]).toBe(0);
expect($window._gaq[1][5]).toBe(true);
});
});
});
describe('supports dc.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useDisplayFeatures(true);
}));
it('should inject the DC script', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='http://stats.g.doubleclick.net/dc.js']").length).toBe(1);
});
});
});
describe('e-commerce transactions', function () {
it('should add transcation', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands');
expect(Analytics._logs.length).toBe(1);
});
});
it('should add an item to transaction', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
expect(Analytics._logs.length).toBe(1);
Analytics.addItem('1', 'sku-2', 'Test product 2', 'Testing', '1', '1');
expect(Analytics._logs.length).toBe(2);
});
});
it('should track the transaction', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.trackTrans();
expect(Analytics._logs.length).toBe(1);
});
});
});
describe('supports ignoreFirstPageLoad', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.ignoreFirstPageLoad(true);
}));
it('supports ignoreFirstPageLoad config', function () {
inject(function (Analytics, $rootScope) {
expect(Analytics.ignoreFirstPageLoad).toBe(true);
});
});
});
describe('supports analytics.js', function () {
var cookieConfig = {
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
};
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
AnalyticsProvider.setCookieConfig(cookieConfig);
AnalyticsProvider.useDisplayFeatures(true);
AnalyticsProvider.useECommerce(true);
AnalyticsProvider.useEnhancedLinkAttribution(true);
AnalyticsProvider.setExperimentId('12345');
}));
it('should inject the Analytics script', function () {
inject(function (Analytics) {
expect(document.querySelectorAll("script[src='//www.google-analytics.com/analytics.js']").length).toBe(1);
});
});
it('should respect cookieConfig', function () {
inject(function (Analytics) {
expect(Analytics.getCookieConfig()).toEqual(cookieConfig);
});
});
it('should support displayFeatures config', function () {
inject(function (Analytics) {
expect(Analytics.displayFeatures).toBe(true);
});
});
it('should support ecommerce config', function () {
inject(function (Analytics) {
expect(Analytics.ecommerce).toBe(true);
});
});
it('should support enhancedLinkAttribution config', function () {
inject(function (Analytics) {
expect(Analytics.enhancedLinkAttribution).toBe(true);
});
});
it('should support experimentId config', function () {
inject(function (Analytics) {
expect(Analytics.experimentId).toBe('12345');
});
});
it('should allow transaction clearing', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.clearTrans();
expect(Analytics._logs.length).toBe(1);
});
});
it('should allow sending custom events', function () {
inject(function (Analytics) {
var social = {
hitType: 'social',
socialNetwork: 'facebook',
socialAction: 'like',
socialTarget: 'http://mycoolpage.com',
page: '/my-new-page'
};
expect(Analytics._logs.length).toBe(0);
Analytics.send(social);
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0]).toEqual({
'0': ['send', social]
});
});
});
it('should allow setting custom dimensions, metrics or experiment', function () {
inject(function (Analytics) {
var data = {
name: "dimension1",
value: "value1"
};
expect(Analytics._logs.length).toBe(0);
Analytics.set(data.name, data.value);
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0]).toEqual({
'0': 'set',
'1': data.name,
'2': data.value
});
});
});
describe('with eventTracks', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should generate eventTracks', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackEvent('test');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('event');
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'test', undefined, undefined, undefined, {});
});
});
});
it('should generate eventTracks and honour non-interactions', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackEvent('test', 'action', 'label', 0, true);
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('event');
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'test', 'action', 'label', 0, {nonInteraction: true});
});
});
});
});
});
describe('e-commerce transactions with analytics.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
AnalyticsProvider.useECommerce(true);
}));
it('should have ecommerce enabled', function () {
inject(function (Analytics) {
expect(Analytics.ecommerceEnabled()).toBe(true);
});
});
it('should have enhanced ecommerce disabled', function () {
inject(function (Analytics) {
expect(Analytics.enhancedEcommerceEnabled()).toBe(false);
});
});
it('should add transcation', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ecommerce:addTransaction');
});
});
it('should add an item to transaction', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ecommerce:addItem');
});
});
it('should track the transaction', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.trackTrans();
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ecommerce:send');
});
});
});
describe('enhanced e-commerce transactions with analytics.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
AnalyticsProvider.useECommerce(true, true);
}));
it('should have ecommerce disabled', function () {
inject(function (Analytics) {
expect(Analytics.ecommerceEnabled()).toBe(false);
});
});
it('should have enhanced ecommerce enabled', function () {
inject(function (Analytics) {
expect(Analytics.enhancedEcommerceEnabled()).toBe(true);
});
});
it('should add product Impression', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addImpression('sku-1', 'Test Product 1', 'Category List', 'Brand 1', 'Category-1', 'variant-1', '1', '24990');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ec:addImpression');
});
});
it('should add product data', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[0][1][0]).toBe('sku-2');
expect(Analytics._logs[0][1][1]).toBe('Test Product 2');
expect(Analytics._logs[0][1][2]).toBe('Category-1');
expect(Analytics._logs[0][1][3]).toBe('Brand 2');
expect(Analytics._logs[0][1][4]).toBe('variant-3');
expect(Analytics._logs[0][1][5]).toBe('2499');
expect(Analytics._logs[0][1][6]).toBe('1');
expect(Analytics._logs[0][1][7]).toBe('FLAT10');
expect(Analytics._logs[0][1][8]).toBe('1');
expect($window.ga).toHaveBeenCalledWith('ec:addProduct', {
id: 'sku-2',
name: 'Test Product 2',
category: 'Category-1',
brand: 'Brand 2',
variant: 'variant-3',
price: '2499',
quantity: '1',
coupon: 'FLAT10',
position: '1'
});
});
});
});
it('should add promo data', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ec:addPromo');
});
});
it('should set Action', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
var dummyAction = 'dummy';
Analytics.setAction(dummyAction);
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('ec:setAction');
expect(Analytics._logs[0][1][0]).toBe(dummyAction);
});
});
it('should track product click', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
var dummyList = 'dummy list';
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.productClick(dummyList);
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('click');
expect(Analytics._logs[1][1][1]['list']).toBe(dummyList);
expect(Analytics._logs[2][0]).toEqual([ 'send', 'event', 'UX', 'click', 'dummy list' ]);
});
});
it('should track product detail', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackDetail();
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('detail');
expect(Analytics._logs[2][0]).toEqual(['send', 'pageview']);
});
});
it('should track add to cart event', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('add');
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('add');
expect(Analytics._logs[2][0]).toEqual([ 'send', 'event', 'UX', 'click', 'add to cart' ]);
});
});
it('should track Remove from cart event', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('remove');
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('remove');
expect(Analytics._logs[2][0]).toEqual([ 'send', 'event', 'UX', 'click', 'remove to cart' ]);
});
});
it('should track checkout', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCheckout();
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('checkout');
expect(Analytics._logs[2][0]).toEqual(['send', 'pageview']);
});
});
it('should track transaction', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-3', 'Test Product 3', 'Category-1', 'Brand 2', 'variant-5', '299', '1', 'FLAT10', '1');
Analytics.trackTransaction();
expect(Analytics._logs.length).toBe(4);
expect(Analytics._logs[0][0]).toBe('ec:addProduct');
expect(Analytics._logs[1][0]).toBe('ec:addProduct');
expect(Analytics._logs[2][0]).toBe('ec:setAction');
expect(Analytics._logs[2][1][0]).toBe('purchase');
expect(Analytics._logs[3][0]).toEqual(['send', 'pageview']);
});
});
it('should track promo click', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.promoClick('Summer Sale');
expect(Analytics._logs.length).toBe(3);
expect(Analytics._logs[0][0]).toBe('ec:addPromo');
expect(Analytics._logs[1][0]).toBe('ec:setAction');
expect(Analytics._logs[1][1][0]).toBe('promo_click');
expect(Analytics._logs[2][0]).toEqual([ 'send', 'event', 'Internal Promotions', 'click', 'Summer Sale' ]);
});
});
});
describe('supports arbitrary page events', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
}));
it('should inject the Analytics script', function () {
inject(function (Analytics, $rootScope) {
$rootScope.$broadcast('$stateChangeSuccess');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
});
});
});
describe('supports RegExp path scrubbing', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setRemoveRegExp(new RegExp(/\/\d+?$/));
}));
it('should scrub urls', function () {
inject(function (Analytics, $location) {
$location.path('/some-crazy/page/with/numbers/123456');
expect(Analytics.getUrl()).toBe('/some-crazy/page/with/numbers');
});
});
});
describe('parameter defaulting on trackPage', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should set url and title when no parameters provided', function () {
inject(function (Analytics, $document, $location) {
$location.path('/page/here');
$document[0] = { title: 'title here' };
Analytics.trackPage();
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
expect(Analytics._logs[0][1]).toBe('/page/here');
expect(Analytics._logs[0][2]).toBe('title here');
});
});
it('should set title when no title provided', function () {
inject(function (Analytics, $document) {
$document[0] = { title: 'title here' };
Analytics.trackPage('/page/here');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toBe('_trackPageview');
expect(Analytics._logs[0][1]).toBe('/page/here');
expect(Analytics._logs[0][2]).toBe('title here');
});
});
});
describe('supports multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: "tracker1" },
{ tracker: 'UA-12345-34', name: "tracker2" },
{ tracker: 'UA-12345-45' }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
AnalyticsProvider.useAnalytics(true);
}));
it('should call ga create event for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('create', trackers[0].tracker, 'auto', { name: trackers[0].name });
expect($window.ga).toHaveBeenCalledWith('create', trackers[1].tracker, 'auto', { name: trackers[1].name });
expect($window.ga).toHaveBeenCalledWith('create', trackers[2].tracker, 'auto');
});
});
});
describe('#trackPage', function () {
it('should call ga send pageview event for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackPage('/mypage', 'My Page');
expect($window.ga).toHaveBeenCalledWith(trackers[0].name + '.send', 'pageview', { page: '/mypage', title: 'My Page' });
expect($window.ga).toHaveBeenCalledWith(trackers[1].name + '.send', 'pageview', { page: '/mypage', title: 'My Page' });
expect($window.ga).toHaveBeenCalledWith('send', 'pageview', { page: '/mypage', title: 'My Page' });
});
});
});
});
});
describe('supports advanced options for multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: "tracker1", crossDomainLinker: true },
{ tracker: 'UA-12345-34', name: "tracker2", crossDomainLinker: true, crossLinkDomains: ['domain-1.com'] },
{ tracker: 'UA-12345-45', crossDomainLinker: true, crossLinkDomains: ['domain-2.com'] },
{ tracker: 'UA-12345-67', cookieConfig: 'yourdomain.org' }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
AnalyticsProvider.useAnalytics(true);
}));
it('should call ga require for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('tracker1.require', 'linker');
expect($window.ga).toHaveBeenCalledWith('tracker2.require', 'linker');
expect($window.ga).toHaveBeenCalledWith('require', 'linker');
});
});
});
it('should call ga linker autoLink for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).not.toHaveBeenCalledWith('tracker1.linker:autoLink');
expect($window.ga).toHaveBeenCalledWith('tracker2.linker:autoLink', ['domain-1.com']);
expect($window.ga).toHaveBeenCalledWith('linker:autoLink', ['domain-2.com']);
});
});
});
it ('should call ga create with custom cookie config', function() {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('create', 'UA-12345-67', 'yourdomain.org');
});
});
});
});
describe('supports advanced tracking for multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: "tracker1", trackEvent: true },
{ tracker: 'UA-12345-34', name: "tracker2" },
{ tracker: 'UA-12345-45', trackEvent: true }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
AnalyticsProvider.useAnalytics(true);
}));
it('should track events for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackEvent('category', 'action', 'label', 'value');
expect($window.ga).toHaveBeenCalledWith('tracker1.send', 'event', 'category', 'action', 'label', 'value', {});
expect($window.ga).not.toHaveBeenCalledWith('tracker2.send', 'event', 'category', 'action', 'label', 'value', {});
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'category', 'action', 'label', 'value', {});
});
});
});
});
describe('enabled url params tracking', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackUrlParams(true);
}));
it('should grab query params in the url', function () {
inject(function (Analytics, $location) {
$location.url('/some/page?with_params=foo&more_param=123');
expect(Analytics.getUrl()).toContain('?with_params=foo&more_param=123');
});
});
});
describe('createAnalyticsScriptTag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should inject a script tag', function () {
inject(function (Analytics, $location) {
var scriptCount = document.querySelectorAll("script[src='//www.google-analytics.com/analytics.js']").length;
Analytics.createAnalyticsScriptTag({userId: 1234});
expect(Analytics.getCookieConfig().userId).toBe(1234);
expect(document.querySelectorAll("script[src='//www.google-analytics.com/analytics.js']").length).toBe(scriptCount + 1);
});
});
});
describe('createAnalyticsScriptTag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should inject a script tag', function () {
inject(function (Analytics, $location) {
var scriptCount = document.querySelectorAll("script[src='http://www.google-analytics.com/ga.js']").length;
Analytics.createScriptTag({userId: 1234});
expect(Analytics.getCookieConfig().userId).toBe(1234);
expect(document.querySelectorAll("script[src='http://www.google-analytics.com/ga.js']").length).toBe(scriptCount + 1);
});
});
});
describe('should add user timing', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
}));
it('should add user timing', function () {
inject(function (Analytics) {
expect(Analytics._logs.length).toBe(0);
Analytics.trackTimings('Time to Checkout', 'User Timings', '32', 'My Timings');
expect(Analytics._logs.length).toBe(1);
expect(Analytics._logs[0][0]).toEqual([ 'send', 'timing', 'Time to Checkout', 'User Timings', '32', 'My Timings']);
});
});
});
describe('directives', function () {
describe('gaTrackEvent', function () {
it('should track an event when clicked', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(),
element = '<div ga-track-event="[\'button\', \'click\', \'Some Button\']">test</div>',
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent).toHaveBeenCalledWith('button', 'click', 'Some Button');
});
});
it('should inherit parent scope', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(), element, compiled;
scope.event = ['button', 'click', 'Some Button'];
element = '<div ga-track-event="event">test</div>';
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent).toHaveBeenCalledWith('button', 'click', 'Some Button');
});
});
});
});
});

View File

@ -0,0 +1,317 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('classic analytics', function() {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.useAnalytics(false)
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
describe('required settings missing', function () {
describe('for default ga script injection', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(undefined);
}));
it('should not inject a script tag', function () {
var scriptCount = document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length;
inject(function (Analytics) {
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(scriptCount);
});
});
it('should issue a warning to the log', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['warn', 'No account id set to create script tag']);
expect($log.warn).toHaveBeenCalledWith(['No account id set to create script tag']);
});
});
});
});
});
describe('enabled delayed script tag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should have a truthy value for delayScriptTag', function () {
inject(function (Analytics, $location) {
expect(Analytics.configuration.delayScriptTag).toBe(true);
});
});
it('should not inject a script tag', function () {
var scriptCount = document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length;
inject(function (Analytics) {
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(scriptCount);
});
});
});
describe('does not support multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1' },
{ tracker: 'UA-12345-45' }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should issue a warning to the log', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics.log.length).toBe(4);
expect(Analytics.log[0]).toEqual(['warn', 'Multiple trackers are not supported with ga.js. Using first tracker only']);
expect($log.warn).toHaveBeenCalledWith(['Multiple trackers are not supported with ga.js. Using first tracker only']);
});
});
});
});
describe('create script tag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should inject a script tag', function () {
inject(function (Analytics) {
Analytics.createScriptTag();
expect(Analytics.log[Analytics.log.length - 1]).toEqual(['inject', 'http://www.google-analytics.com/ga.js']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
it('should warn and prevent a second attempt to inject a script tag', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
Analytics.createScriptTag();
expect(Analytics.log[Analytics.log.length - 1]).toEqual(['inject', 'http://www.google-analytics.com/ga.js']);
Analytics.createScriptTag();
expect($log.warn).toHaveBeenCalledWith(['ga.js or analytics.js script tag already created']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
});
});
describe('automatic page tracking', function () {
it('should inject the GA script', function () {
inject(function (Analytics) {
expect(Analytics.log[Analytics.log.length - 1]).toEqual(['inject', 'http://www.google-analytics.com/ga.js']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
it('should generate trackPages', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackPage('test');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', '']);
expect($window._gaq[1]).toEqual(['_trackPageview', 'test']);
});
});
it('should generate a trackPage on routeChangeSuccess', function () {
inject(function (Analytics, $rootScope, $window) {
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', '']);
expect($window._gaq[1]).toEqual(['_trackPageview', '']);
});
});
});
describe('NOT automatic page tracking', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should NOT generate a trackpage on routeChangeSuccess', function () {
inject(function (Analytics, $rootScope, $window) {
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(0);
});
});
it('should generate a trackpage when explicitly called', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackPage('/page/here');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', '']);
expect($window._gaq[1]).toEqual(['_trackPageview', '/page/here']);
});
});
});
describe('event tracking', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should generate eventTracks', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackEvent('test');
expect($window._gaq.length).toBe(1);
expect($window._gaq[0]).toEqual(['_trackEvent', 'test', undefined, undefined, undefined, false]);
});
});
it('should generate eventTracks with non-interactions', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackEvent('test', 'action', 'label', 0, true);
expect($window._gaq.length).toBe(1);
expect($window._gaq[0]).toEqual(['_trackEvent', 'test', 'action', 'label', 0, true]);
});
});
});
describe('supports dc.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useDisplayFeatures(true);
}));
it('should inject the DC script and not the analytics script', function () {
inject(function (Analytics) {
expect(Analytics.log[Analytics.log.length - 1]).toEqual(['inject', 'http://stats.g.doubleclick.net/dc.js']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/ga.js"]').length).toBe(0);
expect(document.querySelectorAll('script[src="http://stats.g.doubleclick.net/dc.js"]').length).toBe(0);
});
});
});
describe('e-commerce transactions', function () {
it('should add transcation', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands');
expect($window._gaq.length).toBe(1);
expect($window._gaq[0]).toEqual(['_addTrans', '1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands']);
});
});
it('should add an item to transaction', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
expect($window._gaq.length).toBe(1);
expect($window._gaq[0]).toEqual(['_addItem', '1', 'sku-1', 'Test product 1', 'Testing', '1', '1']);
Analytics.addItem('1', 'sku-2', 'Test product 2', 'Testing', '1', '1');
expect($window._gaq.length).toBe(2);
expect($window._gaq[1]).toEqual(['_addItem', '1', 'sku-2', 'Test product 2', 'Testing', '1', '1']);
});
});
it('should track the transaction', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackTrans();
expect($window._gaq.length).toBe(1);
expect($window._gaq[0]).toEqual(['_trackTrans']);
});
});
});
describe('supports ignoreFirstPageLoad', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.ignoreFirstPageLoad(true);
}));
it('supports ignoreFirstPageLoad config', function () {
inject(function (Analytics, $rootScope) {
expect(Analytics.configuration.ignoreFirstPageLoad).toBe(true);
});
});
});
describe('supports arbitrary page events', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
}));
it('should inject the Analytics script', function () {
inject(function (Analytics, $rootScope, $window) {
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$stateChangeSuccess');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', '']);
expect($window._gaq[1]).toEqual(['_trackPageview', '']);
});
});
});
describe('supports RegExp path scrubbing', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setRemoveRegExp(new RegExp(/\/\d+?$/));
}));
it('should scrub urls', function () {
inject(function (Analytics, $location) {
$location.path('/some-crazy/page/with/numbers/123456');
expect(Analytics.getUrl()).toBe('/some-crazy/page/with/numbers');
});
});
});
describe('parameter defaulting on trackPage', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should set url and title when no parameters provided', function () {
inject(function (Analytics, $document, $location, $window) {
$window._gaq.length = 0; // clear queue
$location.path('/page/here');
$document[0] = { title: 'title here' };
Analytics.trackPage();
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', 'title here']);
expect($window._gaq[1]).toEqual(['_trackPageview', '/page/here']);
});
});
it('should set title when no title provided', function () {
inject(function (Analytics, $document, $window) {
$window._gaq.length = 0; // clear queue
$document[0] = { title: 'title here' };
Analytics.trackPage('/page/here');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', 'title here']);
expect($window._gaq[1]).toEqual(['_trackPageview', '/page/here']);
});
});
});
describe('enabled url params tracking', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackUrlParams(true);
}));
it('should grab query params in the url', function () {
inject(function (Analytics, $location) {
$location.url('/some/page?with_params=foo&more_param=123');
expect(Analytics.getUrl()).toContain('?with_params=foo&more_param=123');
});
});
});
});

View File

@ -0,0 +1,89 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('universal analytics debug mode', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.useAnalytics(true)
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
it('should have debug mode disabled by default', function () {
inject(function (Analytics) {
expect(Analytics.configuration.debugMode).toBe(false);
});
});
it('should have trace debugging mode disabled by default', function () {
inject(function (Analytics) {
expect(Analytics.configuration.traceDebuggingMode).toBe(false);
});
});
describe('without trace debugging mode', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.enterDebugMode();
}));
it('should have debug mode enabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.debugMode).toBe(true);
});
});
it('should have trace debugging disabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.traceDebuggingMode).toBe(false);
});
});
it('should inject the analytics debug script', function () {
inject(function (Analytics) {
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics_debug.js']);
});
});
it('should not set the analytics trace debugging variable', function () {
inject(function (Analytics, $window) {
expect($window.ga_debug).toEqual(undefined);
});
});
});
describe('with trace debugging mode', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.enterDebugMode(true);
}));
it('should have debug mode enabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.debugMode).toBe(true);
});
});
it('should have trace debugging enabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.traceDebuggingMode).toBe(true);
});
});
it('should inject the analytics debug script', function () {
inject(function (Analytics) {
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics_debug.js']);
});
});
it('should set the analytics trace debugging variable', function () {
inject(function (Analytics, $window) {
expect($window.ga_debug).toEqual({ trace: true });
});
});
});
});

View File

@ -0,0 +1,76 @@
/* global before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('directives', function() {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.logAllCalls(true)
.enterTestMode();
}));
describe('directives', function () {
describe('gaTrackEvent', function () {
it('should evaluate scope params', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(),
element = '<div ga-track-event="[event, action, label]">test</div>',
compiled = $compile(element)(scope);
scope.event = 'button';
scope.action = 'click';
scope.label = 'Some Button';
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent).toHaveBeenCalledWith('button', 'click', 'Some Button');
});
});
it('should track an event when clicked', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(),
element = '<div ga-track-event="[\'button\', \'click\', \'Some Button\']">test</div>',
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent).toHaveBeenCalledWith('button', 'click', 'Some Button');
});
});
it('should inherit parent scope', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(), element, compiled;
scope.event = ['button', 'click', 'Some Button'];
element = '<div ga-track-event="event">test</div>';
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent).toHaveBeenCalledWith('button', 'click', 'Some Button');
});
});
it('should abort if gaTrackEventIf is false', function () {
inject(function (Analytics, $rootScope, $compile) {
spyOn(Analytics, 'trackEvent');
var scope = $rootScope.$new(),
element = '<div ga-track-event="[\'button\', \'click\', \'Some Button\']" ga-track-event-if="false">test</div>',
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent.calls.count()).toBe(0);
element = '<div ga-track-event="[\'button\', \'click\', \'Some Button\']" ga-track-event-if="true">test</div>';
compiled = $compile(element)(scope);
scope.$digest();
compiled.triggerHandler('click');
expect(Analytics.trackEvent.calls.count()).toBe(1);
});
});
});
});
});

View File

@ -0,0 +1,78 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('disable analytics / user opt-out', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
describe('with universal analytics', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
}));
it('should be enabled by default', function () {
inject(function (Analytics) {
expect(Analytics.configuration.disableAnalytics).toBe(false);
});
});
describe('when disabled', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.disableAnalytics(true);
}));
it('should be disabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.disableAnalytics).toBe(true);
});
});
it('should log an info message about the account being disabled', function () {
inject(function (Analytics, $window) {
expect(Analytics.log[0]).toEqual([ 'info', 'Analytics disabled: UA-XXXXXX-xx' ]);
expect($window['ga-disable-UA-XXXXXX-xx']).toBe(true);
});
});
});
});
describe('with classic analytics', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(false);
}));
it('should be enabled by default', function () {
inject(function (Analytics) {
expect(Analytics.configuration.disableAnalytics).toBe(false);
});
});
describe('when disabled', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.disableAnalytics(true);
}));
it('should be disabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.disableAnalytics).toBe(true);
});
});
it('should log an info message about the account being disabled', function () {
inject(function (Analytics, $window) {
expect(Analytics.log[0]).toEqual([ 'info', 'Analytics disabled: UA-XXXXXX-xx' ]);
expect($window['ga-disable-UA-XXXXXX-xx']).toBe(true);
});
});
});
});
});

View File

@ -0,0 +1,201 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('offline mode', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
describe('with universal analytics', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(true);
}));
describe('at startup', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.startOffline(true);
}));
it('should have offline set to true', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(true);
});
});
it('should have delay script tag set to true', function () {
inject(function (Analytics) {
expect(Analytics.configuration.delayScriptTag).toBe(true);
});
});
it('should not have sent any commands while offline', function () {
inject(function (Analytics) {
Analytics.trackPage('/page/here');
expect(Analytics.log.length).toBe(0);
});
});
it('should send everything when script is added and reset to online', function () {
inject(function (Analytics, $window) {
Analytics.createAnalyticsScriptTag();
Analytics.offline(false);
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics.js']);
expect(Analytics.log[1]).toEqual(['create', 'UA-XXXXXX-xx', { cookieDomain: 'auto' }]);
expect(Analytics.log[2]).toEqual(['send', 'pageview', '']);
});
});
});
it('should be online by default', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
});
});
it('should respect being set to offline', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
Analytics.offline(true);
expect(Analytics.offline()).toBe(true);
});
});
it('should respect being reset to online', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
Analytics.offline(true);
expect(Analytics.offline()).toBe(true);
Analytics.offline(false);
expect(Analytics.offline()).toBe(false);
});
});
it('should not send any commands while offline', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.offline(true);
Analytics.trackPage('/page/here');
expect(Analytics.log.length).toBe(0);
});
});
it('should send all queued commands when reset to online', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.offline(true);
Analytics.trackPage('/page/here');
expect(Analytics.log.length).toBe(0);
Analytics.offline(false);
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['send', 'pageview', { page : '/page/here', title : '' }]);
});
});
});
describe('with classic analytics', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useAnalytics(false);
}));
describe('at startup', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.startOffline(true);
}));
it('should have offline set to true', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(true);
});
});
it('should have delay script tag set to true', function () {
inject(function (Analytics) {
expect(Analytics.configuration.delayScriptTag).toBe(true);
});
});
it('should not have sent any commands while offline', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.trackPage('/page/here');
expect($window._gaq.length).toBe(0);
});
});
it('should send everything when script is added and reset to online', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.createScriptTag();
Analytics.offline(false);
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0]).toEqual(['inject', 'http://www.google-analytics.com/ga.js']);
expect(Analytics.log[1]).toEqual(['_setAccount', 'UA-XXXXXX-xx']);
expect(Analytics.log[2]).toEqual(['_trackPageview']);
expect($window._gaq.length).toBe(Analytics.log.length - 1);
expect($window._gaq[0]).toEqual(Analytics.log[1]);
expect($window._gaq[1]).toEqual(Analytics.log[2]);
});
});
});
it('should be online by default', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
});
});
it('should respect being set to offline', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
Analytics.offline(true);
expect(Analytics.offline()).toBe(true);
});
});
it('should respect being reset to online', function () {
inject(function (Analytics) {
expect(Analytics.offline()).toBe(false);
Analytics.offline(true);
expect(Analytics.offline()).toBe(true);
Analytics.offline(false);
expect(Analytics.offline()).toBe(false);
});
});
it('should not send any commands while offline', function () {
inject(function (Analytics, $window) {
$window._gaq.length = 0; // clear queue
Analytics.offline(true);
Analytics.trackPage('/page/here');
expect($window._gaq.length).toBe(0);
});
});
it('should send all queued commands when reset to online', function () {
inject(function (Analytics, $window) {
Analytics.log.length = 0; // clear log
$window._gaq.length = 0; // clear queue
Analytics.offline(true);
Analytics.trackPage('/page/here');
expect(Analytics.log.length).toBe(0);
expect($window._gaq.length).toBe(0);
Analytics.offline(false);
expect(Analytics.log.length).toBe(2);
expect(Analytics.log[0]).toEqual(['_set', 'title', '']);
expect(Analytics.log[1]).toEqual(['_trackPageview', '/page/here']);
expect($window._gaq.length).toBe(Analytics.log.length);
expect($window._gaq[0]).toEqual(Analytics.log[0]);
expect($window._gaq[1]).toEqual(Analytics.log[1]);
});
});
});
});

View File

@ -0,0 +1,166 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('Reading from $route service', function() {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
it('should not activate $route reading', function() {
inject(function(Analytics) {
expect(Analytics.configuration.readFromRoute).toBe(false);
});
});
describe('without $route service available', function() {
beforeEach(module(function(AnalyticsProvider){
AnalyticsProvider.readFromRoute(true);
}));
it('should log warning if service is missing', function() {
inject(function($log) {
spyOn($log, 'warn');
inject(function(Analytics) {
expect($log.warn).toHaveBeenCalledWith('$route service is not available. Make sure you have included ng-route in your application dependencies.');
});
});
});
});
describe('after setting readFromRoute', function() {
beforeEach(module(function(AnalyticsProvider, $provide){
AnalyticsProvider.readFromRoute(true);
$provide.service('$route', function () {
this.routes = {
someroute: { pageTrack: '/some' },
otherroute: { }
};
});
}));
it('should activate $route reading', function(){
inject(function(Analytics){
expect(Analytics.configuration.readFromRoute).toBe(true);
});
});
it('should read \'/someroute\' from routes object', function(){
inject(function(Analytics, $route){
$route.current = $route.routes.someroute;
expect(Analytics.getUrl()).toBe('/some');
});
});
it('should fallback to url for \'/otherroute\' without \'pageTrack\' property', function(){
inject(function(Analytics, $route, $location){
$route.current = $route.routes.otherroute;
$location.url('/otherroute');
expect(Analytics.getUrl()).toBe('/otherroute');
});
});
it('should fallback to url for \'/undefinedroute\' which is not present in $route config', function(){
inject(function(Analytics, $location) {
$location.url('/undefinedroute');
expect(Analytics.getUrl()).toBe('/undefinedroute');
});
});
});
describe('after setting readFromRoute for classic analytics', function() {
beforeEach(module(function(AnalyticsProvider, $provide){
AnalyticsProvider.readFromRoute(true)
.useAnalytics(false);
$provide.service('$route', function () { });
}));
it('should not track undefined routes', function() {
inject(function(Analytics, $window, $rootScope) {
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(0);
});
});
it('should not track routes without template', function() {
inject(function(Analytics, $window, $rootScope, $route) {
$route.current = { };
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(0);
});
});
it('should not track routes with \'doNotTrack\' attribute', function() {
inject(function(Analytics, $window, $rootScope, $route) {
$route.current = { templateUrl: '/myTemplate', doNotTrack: true };
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(0);
});
});
it('should track routes with a defined template (no redirect)', function() {
inject(function(Analytics, $window, $rootScope, $route) {
$route.current = { templateUrl: '/myTemplate', pageTrack: '/myTrack' };
$window._gaq.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect($window._gaq.length).toBe(2);
expect($window._gaq[0]).toEqual(['_set', 'title', '']);
expect($window._gaq[1]).toEqual(['_trackPageview', '/myTrack']);
});
});
});
describe('after setting readFromRoute for universal analytics', function() {
beforeEach(module(function(AnalyticsProvider, $provide){
AnalyticsProvider.readFromRoute(true)
.useAnalytics(true);
$provide.service('$route', function () { });
}));
it('should not track undefined routes', function() {
inject(function(Analytics, $rootScope) {
Analytics.log.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics.log.length).toBe(0);
});
});
it('should not track routes without template', function() {
inject(function(Analytics, $rootScope, $route) {
$route.current = { };
Analytics.log.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics.log.length).toBe(0);
});
});
it('should not track routes with \'doNotTrack\' attribute', function() {
inject(function(Analytics, $rootScope, $route) {
$route.current = { templateUrl: '/myTemplate', doNotTrack: true };
Analytics.log.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics.log.length).toBe(0);
});
});
it('should track routes with a defined template (no redirect)', function() {
inject(function(Analytics, $window, $rootScope, $route) {
$route.current = { templateUrl: '/myTemplate', pageTrack: '/myTrack' };
Analytics.log.length = 0; // clear queue
$rootScope.$broadcast('$routeChangeSuccess');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['send', 'pageview', { page: '/myTrack', title: '' }]);
});
});
});
});

View File

@ -0,0 +1,51 @@
/* global afterEach, before, beforeEach, console, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('universal analytics scenarios', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.useECommerce(true, true)
.setCurrency('EUR')
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
it('should handle e-commerce scenario #1', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
var i, count, expected = [
['inject', '//www.google-analytics.com/analytics.js'], // This entry is in the log only due to test mode
['create', 'UA-XXXXXX-xx', { cookieDomain: 'auto' }],
['require', 'ec'],
['set', '&cu', 'EUR'],
['send', 'pageview', ''],
['send', 'pageview', {page: '/foobar', title: ''}],
['ec:addProduct', {id: 'sku-2', name: 'Test Product 2', category: 'Category-1', brand: 'Brand 2', variant: 'variant-3', price: '2499', quantity: '1', coupon: 'FLAT10', position: '1'}],
['ec:setAction', 'checkout', {step: 1, option: 'Visa'}],
['ec:addProduct', {id: 'sku-2', name: 'Test Product 2', category: 'Category-1', brand: 'Brand 2', variant: 'variant-3', price: '1111', quantity: '1', coupon: 'WOMEN10', position: '1'}],
['ec:setAction', 'purchase', {id: 'T1234', affiliation: 'Online Store - Web', revenue: '3333', tax: '10', shipping: '200', coupon: 'FLAT10'}]
];
Analytics.trackPage('/foobar');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCheckout(1, 'Visa');
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '1111', '1', 'WOMEN10', '1');
Analytics.trackTransaction('T1234', 'Online Store - Web', '3333', '10', '200', 'FLAT10', '', '', '');
count = $window.ga.calls.count();
expect(count).toBe(expected.length - 1);
expect(Analytics.log.length).toBe(expected.length);
expect(Analytics.log[0]).toEqual(expected[0]);
for (i = 0; i < count; ++i) {
expect(Analytics.log[i]).toEqual(expected[i]);
expect(Analytics.log[i + 1]).toEqual($window.ga.calls.argsFor(i));
}
});
});
});
});

View File

@ -0,0 +1,63 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('universal analytics exception tracking', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.useAnalytics(true)
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
it('should have a trackException method', function () {
inject(function (Analytics) {
expect(typeof Analytics.trackException === 'function').toBe(true);
});
});
it('should allow for tracking an exception with no parameters provided', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackException();
expect(Analytics.log[0]).toEqual(['send', 'exception', { exDescription: undefined, exFatal: false }]);
});
});
it('should allow for tracking an exception with all parameters provided', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackException('Something fatal happened!', true);
expect(Analytics.log[0]).toEqual(['send', 'exception', { exDescription: 'Something fatal happened!', exFatal: true }]);
});
});
describe('supports tracking for multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1', trackEvent: true },
{ tracker: 'UA-12345-34', name: 'tracker2' },
{ tracker: 'UA-12345-45', trackEvent: true }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should track exceptions for all objects', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackException('Something fatal happened!', true);
expect($window.ga).toHaveBeenCalledWith('tracker1.send', 'exception', { exDescription: 'Something fatal happened!', exFatal: true });
expect($window.ga).toHaveBeenCalledWith('tracker2.send', 'exception', { exDescription: 'Something fatal happened!', exFatal: true });
expect($window.ga).toHaveBeenCalledWith('send', 'exception', { exDescription: 'Something fatal happened!', exFatal: true });
});
});
});
});
});

View File

@ -0,0 +1,907 @@
/* global afterEach, before, beforeEach, describe, document, expect, inject, it, module, spyOn */
'use strict';
describe('universal analytics', function () {
beforeEach(module('angular-google-analytics'));
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount('UA-XXXXXX-xx')
.useAnalytics(true)
.logAllCalls(true)
.enterTestMode();
}));
afterEach(inject(function (Analytics) {
Analytics.log.length = 0; // clear log
}));
describe('required settings missing', function () {
describe('for analytics script injection', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(undefined);
}));
it('should not inject a script tag', function () {
inject(function (Analytics) {
expect(Analytics.log.length).toBe(1);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
it('should issue a warning to the log', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['warn', 'No account id set to create analytics script tag']);
expect($log.warn).toHaveBeenCalledWith(['No account id set to create analytics script tag']);
});
});
});
});
});
describe('delay script tag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should have a truthy value for Analytics.delayScriptTag', function () {
inject(function (Analytics, $location) {
expect(Analytics.configuration.delayScriptTag).toBe(true);
});
});
it('should not inject a script tag', function () {
inject(function (Analytics) {
expect(Analytics.log.length).toBe(0);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
});
describe('automatically create analytics script tag', function () {
it('should inject the script tag', function () {
inject(function (Analytics) {
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics.js']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
it('should warn and prevent a second attempt to inject a script tag', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics.js']);
Analytics.createAnalyticsScriptTag();
expect($log.warn).toHaveBeenCalledWith(['ga.js or analytics.js script tag already created']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
});
});
describe('manually create analytics script tag', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.delayScriptTag(true);
}));
it('should inject the script tag', function () {
inject(function (Analytics, $location) {
Analytics.log.length = 0; // clear log
Analytics.createAnalyticsScriptTag();
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics.js']);
});
});
it('should warn and prevent a second attempt to inject a script tag', function () {
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.createAnalyticsScriptTag();
expect(Analytics.log[0]).toEqual(['inject', '//www.google-analytics.com/analytics.js']);
Analytics.createAnalyticsScriptTag();
expect($log.warn).toHaveBeenCalledWith(['ga.js or analytics.js script tag already created']);
expect(document.querySelectorAll('script[src="//www.google-analytics.com/analytics.js"]').length).toBe(0);
});
});
});
it('should support cookie config with the script call', function () {
inject(function (Analytics) {
Analytics.createAnalyticsScriptTag({ userId: 1234 });
expect(Analytics.getCookieConfig()).toEqual({ userId: 1234 });
});
});
describe('with a prefix set', function(){
beforeEach(module(function (AnalyticsProvider){
AnalyticsProvider
.trackPrefix("test-prefix");
}));
it('should send the url, including the prefix', function(){
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.createAnalyticsScriptTag();
expect(Analytics.log[2]).toEqual(['send', 'pageview', 'test-prefix']);
});
});
});
});
describe('hybrid mobile application support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setHybridMobileSupport(true)
.delayScriptTag(true);
}));
it('should support hybridMobileSupport', function () {
inject(function (Analytics) {
expect(Analytics.configuration.hybridMobileSupport).toBe(true);
});
});
it('should inject a script tag with the HTTPS protocol and set checkProtocolTask to null', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.createAnalyticsScriptTag();
expect(Analytics.log[0]).toEqual(['inject', 'https://www.google-analytics.com/analytics.js']);
expect(Analytics.log[1]).toEqual(['create', 'UA-XXXXXX-xx', { cookieDomain: 'auto' }]);
expect(Analytics.log[2]).toEqual(['set', 'checkProtocolTask', null]);
});
});
});
describe('account custom set commands support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider
.setAccount({
tracker: 'UA-XXXXXX-xx',
set: {
forceSSL: true
}
})
.setHybridMobileSupport(true)
.delayScriptTag(true);
}));
it('should set the account object to use forceSSL', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.createAnalyticsScriptTag();
expect(Analytics.log[0]).toEqual(['inject', 'https://www.google-analytics.com/analytics.js']);
expect(Analytics.log[1]).toEqual(['create', 'UA-XXXXXX-xx', { cookieDomain: 'auto' }]);
expect(Analytics.log[2]).toEqual(['set', 'checkProtocolTask', null]);
expect(Analytics.log[3]).toEqual(['set', 'forceSSL', true]);
});
});
});
describe('account select support', function () {
var account;
beforeEach(module(function (AnalyticsProvider) {
account = {
tracker: 'UA-XXXXXX-xx',
select: function () {
return false;
}
};
spyOn(account, 'select');
AnalyticsProvider.setAccount(account);
}));
it('should not run with commands after configuration when select returns false', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackPage('/path/to', 'title');
expect(Analytics.log.length).toEqual(0);
expect(account.select).toHaveBeenCalledWith(['send', 'pageview', { page: '/path/to', title: 'title' }]);
});
});
});
describe('ignoreFirstPageLoad configuration support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.ignoreFirstPageLoad(true);
}));
it('should support ignoreFirstPageLoad', function () {
inject(function (Analytics) {
expect(Analytics.configuration.ignoreFirstPageLoad).toBe(true);
});
});
});
describe('cookie configuration support', function () {
var cookieConfig = {
cookieDomain: 'foo.example.com',
cookieName: 'myNewName',
cookieExpires: 20000
};
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setCookieConfig(cookieConfig);
}));
it('should support cookie config', function () {
inject(function (Analytics) {
expect(Analytics.getCookieConfig()).toEqual(cookieConfig);
});
});
});
describe('displayFeature configuration support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useDisplayFeatures(true);
}));
it('should support displayFeatures config', function () {
inject(function (Analytics) {
expect(Analytics.configuration.displayFeatures).toBe(true);
});
});
});
describe('enhancedLinkAttribution configuration support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useEnhancedLinkAttribution(true);
}));
it('should support enhancedLinkAttribution config', function () {
inject(function (Analytics) {
expect(Analytics.configuration.enhancedLinkAttribution).toBe(true);
});
});
});
describe('experiment configuration support', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setExperimentId('12345');
}));
it('should support experimentId config', function () {
inject(function (Analytics) {
expect(Analytics.configuration.experimentId).toBe('12345');
});
});
});
describe('supports custom events, dimensions, and metrics', function () {
it('should allow sending custom events', function () {
inject(function (Analytics) {
var social = {
hitType: 'social',
socialNetwork: 'facebook',
socialAction: 'like',
socialTarget: 'http://mycoolpage.com',
page: '/my-new-page'
};
Analytics.log.length = 0; // clear log
Analytics.send(social);
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['send', social]);
});
});
it('should allow setting custom dimensions, metrics or experiment', function () {
inject(function (Analytics) {
var data = {
name: 'dimension1',
value: 'value1'
};
Analytics.log.length = 0; // clear log
Analytics.set(data.name, data.value);
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['set', data.name, data.value]);
});
});
describe('with eventTracks', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should generate eventTracks', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackEvent('test');
expect(Analytics.log.length).toBe(1);
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'test', undefined, undefined, undefined, { page: '' });
});
});
});
it('should generate eventTracks and honor non-interactions', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackEvent('test', 'action', 'label', 0, true);
expect(Analytics.log.length).toBe(1);
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'test', 'action', 'label', 0, { nonInteraction: true, page: '' });
});
});
});
});
});
describe('e-commerce transactions with analytics.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useECommerce(true);
}));
it('should have e-commerce enabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.ecommerce).toBe(true);
});
});
it('should have enhanced e-commerce disabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.enhancedEcommerce).toBe(false);
});
});
it('should add transcation', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addTrans('1', '', '2.42', '0.42', '0', 'Amsterdam', '', 'Netherlands');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0][0]).toEqual('ecommerce:addTransaction');
});
});
it('should add an item to transaction', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addItem('1', 'sku-1', 'Test product 1', 'Testing', '1', '1');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0][0]).toEqual('ecommerce:addItem');
});
});
it('should track the transaction', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackTrans();
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['ecommerce:send']);
});
});
it('should allow transaction clearing', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.clearTrans();
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['ecommerce:clear']);
});
});
it('should not support enhanced e-commerce commands', function () {
var commands = [
'addImpression',
'addProduct',
'addPromo',
'setAction'
];
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
commands.forEach(function (command) {
Analytics[command]();
expect($log.warn).toHaveBeenCalledWith(['Enhanced Ecommerce must be enabled to use ' + command + ' with analytics.js']);
});
});
});
});
describe('supports multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1', trackEcommerce: true },
{ tracker: 'UA-12345-34', name: 'tracker2', trackEcommerce: false },
{ tracker: 'UA-12345-45', trackEcommerce: true }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should track transactions for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.trackTrans();
expect(Analytics.log.length).toBe(2);
expect($window.ga).toHaveBeenCalledWith('tracker1.ecommerce:send');
expect($window.ga).toHaveBeenCalledWith('ecommerce:send');
});
});
});
});
});
describe('enhanced e-commerce transactions with analytics.js', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.useECommerce(true, true);
}));
it('should have ecommerce disabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.ecommerce).toBe(false);
});
});
it('should have enhanced ecommerce enabled', function () {
inject(function (Analytics) {
expect(Analytics.configuration.enhancedEcommerce).toBe(true);
});
});
it('should add product impression', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addImpression('sku-1', 'Test Product 1', 'Category List', 'Brand 1', 'Category-1', 'variant-1', '1', '24990');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0][0]).toBe('ec:addImpression');
});
});
it('should add product data', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
expect(Analytics.log.length).toBe(1);
expect($window.ga).toHaveBeenCalledWith(
'ec:addProduct',
{
id: 'sku-2',
name: 'Test Product 2',
category: 'Category-1',
brand: 'Brand 2',
variant: 'variant-3',
price: '2499',
quantity: '1',
coupon: 'FLAT10',
position: '1'
});
});
});
});
it('should add product data with custom properties', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', undefined, undefined, { dimension1: '1' });
expect(Analytics.log.length).toBe(1);
expect($window.ga).toHaveBeenCalledWith(
'ec:addProduct',
{
id: 'sku-2',
name: 'Test Product 2',
category: 'Category-1',
brand: 'Brand 2',
variant: 'variant-3',
price: '2499',
quantity: '1',
coupon: undefined,
position: undefined,
dimension1: '1'
});
});
});
});
it('should add promo data', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0][0]).toBe('ec:addPromo');
});
});
it('should set action', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.setAction('dummy');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual(['ec:setAction', 'dummy', undefined]);
});
});
it('should track product click', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.productClick('dummy list');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'click', { list: 'dummy list' } ]);
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'UX', 'click', 'dummy list', undefined, { page: '' } ]);
});
});
it('should track product detail', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackDetail();
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'detail', undefined ]);
expect(Analytics.log[2]).toEqual(['send', 'pageview']);
});
});
it('should track add to cart event', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('add');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'add', { list: undefined } ]);
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'UX', 'click', 'add to cart', undefined, { page: '' } ]);
});
});
it('should track add to cart event with product list', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('add', 'product-list');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'add', { list: 'product-list' } ]);
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'UX', 'click', 'add to cart', undefined, { page: '' } ]);
});
});
it('should track remove from cart event', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('remove');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'remove', { list: undefined } ]);
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'UX', 'click', 'remove from cart', undefined, { page: '' } ]);
});
});
it('should track remove from cart event with product list', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCart('remove', 'product-list');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1]).toEqual([ 'ec:setAction', 'remove', {list: 'product-list'} ]);
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'UX', 'click', 'remove from cart', undefined, { page: '' } ]);
});
});
it('should track checkout', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.trackCheckout();
expect(Analytics.log.length).toBe(2);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1][0]).toBe('ec:setAction');
expect(Analytics.log[1][1]).toBe('checkout');
});
});
it('should track transaction', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
Analytics.addProduct('sku-3', 'Test Product 3', 'Category-1', 'Brand 2', 'variant-5', '299', '1', 'FLAT10', '1');
Analytics.trackTransaction();
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addProduct');
expect(Analytics.log[1][0]).toBe('ec:addProduct');
expect(Analytics.log[2][0]).toBe('ec:setAction');
expect(Analytics.log[2][1]).toBe('purchase');
});
});
it('should track promo click', function () {
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addPromo('PROMO_1234', 'Summer Sale', 'summer_banner2', 'banner_slot1');
Analytics.promoClick('Summer Sale');
expect(Analytics.log.length).toBe(3);
expect(Analytics.log[0][0]).toBe('ec:addPromo');
expect(Analytics.log[1][0]).toBe('ec:setAction');
expect(Analytics.log[1][1]).toBe('promo_click');
expect(Analytics.log[2]).toEqual([ 'send', 'event', 'Internal Promotions', 'click', 'Summer Sale', undefined, { page: '' } ]);
});
});
it('should not support ecommerce commands', function () {
var commands = [
'addItem',
'addTrans',
'clearTrans',
'trackTrans'
];
inject(function ($log) {
spyOn($log, 'warn');
inject(function (Analytics) {
commands.forEach(function (command) {
Analytics[command]();
expect($log.warn).toHaveBeenCalledWith([command + ' is not available when Enhanced Ecommerce is enabled with analytics.js']);
});
});
});
});
describe('supports multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1', trackEcommerce: false },
{ tracker: 'UA-12345-34', name: 'tracker2', trackEcommerce: true },
{ tracker: 'UA-12345-45', trackEcommerce: true }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should add product for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.log.length = 0; // clear log
Analytics.addProduct('sku-2', 'Test Product 2', 'Category-1', 'Brand 2', 'variant-3', '2499', '1', 'FLAT10', '1');
expect(Analytics.log.length).toBe(2);
expect($window.ga).toHaveBeenCalledWith(
'ec:addProduct',
{
id: 'sku-2',
name: 'Test Product 2',
category: 'Category-1',
brand: 'Brand 2',
variant: 'variant-3',
price: '2499',
quantity: '1',
coupon: 'FLAT10',
position: '1'
});
expect($window.ga).toHaveBeenCalledWith(
'tracker2.ec:addProduct',
{
id: 'sku-2',
name: 'Test Product 2',
category: 'Category-1',
brand: 'Brand 2',
variant: 'variant-3',
price: '2499',
quantity: '1',
coupon: 'FLAT10',
position: '1'
});
});
});
});
});
});
describe('supports arbitrary page events', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
}));
it('should respond to non-default page event', function () {
inject(function (Analytics, $rootScope) {
Analytics.log.length = 0; // clear log
$rootScope.$broadcast('$stateChangeSuccess');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual([ 'send', 'pageview', { page: '', title: '' } ]);
});
});
});
describe('supports RegExp path scrubbing', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setRemoveRegExp(new RegExp(/\/\d+?$/));
}));
it('should scrub urls', function () {
inject(function (Analytics, $location) {
$location.path('/some-crazy/page/with/numbers/123456');
expect(Analytics.getUrl()).toBe('/some-crazy/page/with/numbers');
});
});
});
describe('parameter defaulting on trackPage', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackPages(false);
}));
it('should set url and title when no parameters provided', function () {
inject(function (Analytics, $document, $location) {
$location.path('/page/here');
$document[0] = { title: 'title here' };
Analytics.log.length = 0; // clear log
Analytics.trackPage();
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual([ 'send', 'pageview', { page: '/page/here', title: 'title here' } ]);
});
});
it('should set title when no title provided', function () {
inject(function (Analytics, $document) {
$document[0] = { title: 'title here' };
Analytics.log.length = 0; // clear log
Analytics.trackPage('/page/here');
expect(Analytics.log.length).toBe(1);
expect(Analytics.log[0]).toEqual([ 'send', 'pageview', { page: '/page/here', title: 'title here' } ]);
});
});
});
describe('supports multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1' },
{ tracker: 'UA-12345-34', name: 'tracker2' },
{ tracker: 'UA-12345-45' }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should call create event for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('create', trackers[0].tracker, { cookieDomain: 'auto', name: trackers[0].name });
expect($window.ga).toHaveBeenCalledWith('create', trackers[1].tracker, { cookieDomain: 'auto', name: trackers[1].name });
expect($window.ga).toHaveBeenCalledWith('create', trackers[2].tracker, { cookieDomain: 'auto' });
});
});
});
it('should call send pageview event for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackPage('/mypage', 'My Page');
expect($window.ga).toHaveBeenCalledWith(trackers[0].name + '.send', 'pageview', { page: '/mypage', title: 'My Page' });
expect($window.ga).toHaveBeenCalledWith(trackers[1].name + '.send', 'pageview', { page: '/mypage', title: 'My Page' });
expect($window.ga).toHaveBeenCalledWith('send', 'pageview', { page: '/mypage', title: 'My Page' });
});
});
});
});
describe('supports advanced options for multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1', crossDomainLinker: true },
{ tracker: 'UA-12345-34', name: 'tracker2', crossDomainLinker: true, crossLinkDomains: ['domain-1.com'] },
{ tracker: 'UA-12345-45', crossDomainLinker: true, crossLinkDomains: ['domain-2.com'] },
{ tracker: 'UA-12345-67', cookieConfig: 'yourdomain.org' }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should call require for each tracker', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('tracker1.require', 'linker');
expect($window.ga).toHaveBeenCalledWith('tracker2.require', 'linker');
expect($window.ga).toHaveBeenCalledWith('require', 'linker');
});
});
});
it('should call linker autoLink for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).not.toHaveBeenCalledWith('tracker1.linker:autoLink');
expect($window.ga).toHaveBeenCalledWith('tracker2.linker:autoLink', ['domain-1.com']);
expect($window.ga).toHaveBeenCalledWith('linker:autoLink', ['domain-2.com']);
});
});
});
it ('should call create with custom cookie config', function() {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
expect($window.ga).toHaveBeenCalledWith('create', 'UA-12345-67', { cookieDomain: 'yourdomain.org' });
});
});
});
});
describe('supports advanced tracking for multiple tracking objects', function () {
var trackers = [
{ tracker: 'UA-12345-12', name: 'tracker1', trackEvent: true },
{ tracker: 'UA-12345-34', name: 'tracker2' },
{ tracker: 'UA-12345-45', trackEvent: true }
];
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.setAccount(trackers);
}));
it('should track events for configured tracking objects only', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackEvent('category', 'action', 'label', 'value');
expect($window.ga).toHaveBeenCalledWith('tracker1.send', 'event', 'category', 'action', 'label', 'value', { page: '' });
expect($window.ga).not.toHaveBeenCalledWith('tracker2.send', 'event', 'category', 'action', 'label', 'value', { page: '' });
expect($window.ga).toHaveBeenCalledWith('send', 'event', 'category', 'action', 'label', 'value', { page: '' });
});
});
});
it('should track user timings for all objects', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.trackTimings('Time to Checkout', 'User Timings', '32', 'My Timings');
expect($window.ga).toHaveBeenCalledWith('tracker1.send', 'timing', 'Time to Checkout', 'User Timings', '32', 'My Timings');
expect($window.ga).toHaveBeenCalledWith('tracker2.send', 'timing', 'Time to Checkout', 'User Timings', '32', 'My Timings');
expect($window.ga).toHaveBeenCalledWith('send', 'timing', 'Time to Checkout', 'User Timings', '32', 'My Timings');
});
});
});
it('should set value for default tracker if no trackerName provided', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.set('dimension1', 'metric1');
expect($window.ga).toHaveBeenCalledWith('set', 'dimension1', 'metric1');
});
});
});
it('should set value for named tracker if a trackerName provided', function () {
inject(function ($window) {
spyOn($window, 'ga');
inject(function (Analytics) {
Analytics.set('dimension2', 'metric2', 'tracker1');
expect($window.ga).toHaveBeenCalledWith('tracker1.set', 'dimension2', 'metric2');
});
});
});
});
describe('enabled url params tracking', function () {
beforeEach(module(function (AnalyticsProvider) {
AnalyticsProvider.trackUrlParams(true);
}));
it('should grab query params in the url', function () {
inject(function (Analytics, $location) {
$location.url('/some/page?with_params=foo&more_param=123');
expect(Analytics.getUrl()).toContain('?with_params=foo&more_param=123');
});
});
});
});