├── .gitignore ├── .travis.yml ├── README.md ├── bower.json ├── ga.js ├── package.json └── test ├── karma.conf.js └── spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by bundle gem [NAME] 2 | 3 | ### Bundler ### 4 | *.gem 5 | *.rbc 6 | .bundle 7 | .config 8 | .yardoc 9 | Gemfile.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | ### Bundler End ### 22 | 23 | # Created by http://gitignore.io 24 | 25 | ### Node ### 26 | lib-cov 27 | *.seed 28 | *.log 29 | *.csv 30 | *.dat 31 | *.out 32 | *.pid 33 | *.gz 34 | 35 | pids 36 | logs 37 | results 38 | 39 | npm-debug.log 40 | node_modules 41 | ### Node End ### 42 | 43 | # Manually Created 44 | 45 | ### Bower ### 46 | bower_components 47 | ### Bower End ### 48 | 49 | ### Project ### 50 | tmp 51 | ### Project End ### 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Google Universal Analytics for AngularJS 2 | ======================================== 3 | 4 | `angular-ga` is a very straightforward AngularJS adapter of the 5 | **new** [google analytics script](https://developers.google.com/analytics/devguides/collection/analyticsjs/). 6 | 7 | It gives you full control of your analytics, exposing the google's `ga()` function for you. 8 | What it means is, that it will set the page field on every route change for You, but you will 9 | have to send the pageviews, events etc. manually. But on the other hand, you have the full control of that process. 10 | 11 | [![Build Status](https://travis-ci.org/panrafal/angular-ga.png?branch=master)](https://travis-ci.org/panrafal/angular-ga) 12 | 13 | Usage 14 | ===== 15 | 16 | ## Embed tracking code 17 | 18 | Include the **new** universal analytics script in your html as usual, but remove `ga('send', 'pageview');` 19 | 20 | ## Enable the ga module 21 | 22 | ```js 23 | angular.module('yourModule', ['ga']) 24 | ``` 25 | 26 | ## Use ga service in your controllers, directives etc... 27 | 28 | `angular-ga` service is accessible as `ga`. Use it exactly the same, as `ga()` [asynchronous function](https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference): 29 | 30 | ```js 31 | angular.module('myModule') 32 | .controller('myCtrl', function (ga) { 33 | ga('set', 'dimension1', 'Hello!'); 34 | ga('send', 'pageview', {title: 'Hello world!'}); 35 | }); 36 | ``` 37 | 38 | ## Use ga directive in html 39 | 40 | Contents of the directive should be the array of parameters for `ga()` function. 41 | You can skip the enclosing array '[]' if you start with the single-quote character. 42 | 43 | Of course, you can use angular expressions, as this is evaluated. 44 | 45 | Both samples are equivalent to calling `ga('send', 'event', 'player', 'play', video.id)` on the `click` event: 46 | 47 | ```html 48 | 49 | 50 | ``` 51 | 52 | You can call `ga` several times by passing an array of arrays: 53 | 54 | ```html 55 | 56 | ``` 57 | 58 | You can change the event by providing `ga-on` attribute: 59 | 60 | ```html 61 | 62 | ``` 63 | 64 | By using `ga-on="init"` you can call `ga` as soon as the html is parsed: 65 | 66 | ```html 67 |
68 | ``` 69 | 70 | ## Use ga directive's auto events 71 | 72 | If `ga` attribute is empty, the event is guesses from the context as follows: 73 | 74 | **category** 75 | 76 | - `link-out` if href begins with `http://` 77 | - `link-in` if href is anything else, except `#` 78 | - `button` for anything else 79 | 80 | **action** 81 | 82 | - value of `href` attribute if present 83 | - `click` for anything else 84 | 85 | **label** 86 | 87 | - value of `title` attribute if present 88 | - for `input` elements value of `value` attribute if present 89 | - text contents for anything else 90 | 91 | You can use attributes `ga-category`, `ga-action`, `ga-label` and `ga-value` to override 92 | default behaviour. 93 | 94 | 95 | 96 | [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/1b31cde4eb48524cf5194d3c2bf1ef68 "githalytics.com")](http://githalytics.com/panrafal/angular-ga) 97 | 98 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Rafal Lindemann", 3 | "name": "angular-ga", 4 | "description": "Google Universal Analytics adapter for AngularJS", 5 | "version": "0.1.4", 6 | "homepage": "https://github.com/panrafal/angular-ga", 7 | "main": "./ga.js", 8 | "dependencies": { 9 | "angular": "^1.0" 10 | }, 11 | "devDependencies": { 12 | "angular-mocks": "^1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ga.js: -------------------------------------------------------------------------------- 1 | (function (angular) { 2 | 'use strict'; 3 | 4 | angular.module('ga', []) 5 | .factory('ga', ['$window', function ($window) { 6 | 7 | var ga = function() { 8 | if (angular.isArray(arguments[0])) { 9 | for(var i = 0; i < arguments.length; ++i) { 10 | ga.apply(this, arguments[i]); 11 | } 12 | return; 13 | } 14 | // console.log('ga', arguments); 15 | if ($window.ga) { 16 | $window.ga.apply(this, arguments); 17 | } 18 | }; 19 | 20 | return ga; 21 | }]) 22 | .run(['$rootScope', '$location', 'ga', function ($rootScope, $location, ga) { 23 | 24 | $rootScope.$on('$routeChangeStart', function() { 25 | ga('set', 'page', $location.url()); 26 | }); 27 | 28 | }]) 29 | /** 30 | ga="'send', 'event', 'test'" ga-on="click|hover|init" 31 | */ 32 | .directive('ga', ['ga', function(ga) { 33 | return { 34 | restrict: 'A', 35 | scope: false, 36 | link: function($scope, $element, $attrs) { 37 | var bindToEvent = $attrs.gaOn || 'click'; 38 | 39 | var onEvent = function() { 40 | var command = $attrs.ga; 41 | if (command) { 42 | if (command[0] === '\'') command = '[' + command + ']'; 43 | 44 | command = $scope.$eval(command); 45 | } else { 46 | // auto command 47 | var href = $element.attr('href'); 48 | if (href && href === '#') href = ''; 49 | var category = $attrs.gaCategory ? $scope.$eval($attrs.gaCategory) : 50 | (href && href[0] !== '#' ? (href.match(/\/\//) ? 'link-out' : 'link-in') : 'button'), 51 | action = $attrs.gaAction ? $scope.$eval($attrs.gaAction) : 52 | (href ? href : 'click'), 53 | label = $attrs.gaLabel ? $scope.$eval($attrs.gaLabel) : 54 | ($element[0].title || ($element[0].tagName.match(/input/i) ? $element.attr('value') : $element.text())).substr(0, 64), 55 | value = $attrs.gaValue ? $scope.$eval($attrs.gaValue) : null; 56 | command = ['send', 'event', category, action, label]; 57 | if (value !== null) command.push(value); 58 | } 59 | ga.apply(null, command); 60 | }; 61 | 62 | if (bindToEvent === 'init') { 63 | onEvent(); 64 | } else { 65 | $element.bind(bindToEvent, onEvent); 66 | } 67 | } 68 | }; 69 | }]); 70 | })(angular); 71 | 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ga", 3 | "description": "Google Universal Analytics adapter for AngularJS", 4 | "version": "0.1.5", 5 | "author": "Rafal Lindemann ", 6 | "main": "ga.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/panrafal/angular-ga" 10 | }, 11 | "scripts": { 12 | "postinstall": "bower install", 13 | "test": "./node_modules/karma/bin/karma start test/karma.conf.js" 14 | }, 15 | "dependencies": { 16 | "bower": "^1.3.2" 17 | }, 18 | "devDependencies": { 19 | "karma": "~0.12.9", 20 | "karma-jasmine": "~0.2.0", 21 | "karma-phantomjs-launcher": "~0.1.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/karma-runner/karma/blob/master/test/client/karma.conf.js 3 | */ 4 | module.exports = function(config) { 5 | config.set({ 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '../', 8 | 9 | frameworks: ['jasmine'], 10 | 11 | // list of files / patterns to load in the browser 12 | files: [ 13 | 'bower_components/angular/angular.min.js', 14 | 'bower_components/angular-mocks/angular-mocks.js', 15 | 'ga.js', 16 | 'test/spec.js' 17 | ], 18 | 19 | // use dots reporter, as travis terminal does not support escaping sequences 20 | // possible values: 'dots', 'progress' 21 | // CLI --reporters progress 22 | reporters: ['progress'], 23 | 24 | preprocessors: { 25 | // source files, that you wanna generate coverage for 26 | // do not include tests or libraries 27 | // (these files will be instrumented by Istanbul) 28 | }, 29 | 30 | // web server port 31 | // CLI --port 9876 32 | port: 9876, 33 | 34 | // enable / disable colors in the output (reporters and logs) 35 | // CLI --colors --no-colors 36 | colors: true, 37 | 38 | // level of logging 39 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 40 | // CLI --log-level debug 41 | logLevel: config.LOG_INFO, 42 | 43 | // enable / disable watching file and executing tests whenever any file changes 44 | // CLI --auto-watch --no-auto-watch 45 | autoWatch: false, 46 | 47 | // Start these browsers, currently available: 48 | // - Chrome 49 | // - ChromeCanary 50 | // - Firefox 51 | // - Opera 52 | // - Safari (only Mac) 53 | // - PhantomJS 54 | // - IE (only Windows) 55 | // CLI --browsers Chrome,Firefox,Safari 56 | browsers: ['PhantomJS'], 57 | 58 | // If browser does not capture in given timeout [ms], kill it 59 | // CLI --capture-timeout 5000 60 | captureTimeout: 20000, 61 | 62 | // Auto run tests on start (when browsers are captured) and exit 63 | // CLI --single-run --no-single-run 64 | singleRun: true, 65 | 66 | // report which specs are slower than 500ms 67 | // CLI --report-slower-than 500 68 | reportSlowerThan: 500, 69 | 70 | // optionally, configure the reporter 71 | coverageReporter: { 72 | type : 'lcovonly', 73 | dir : 'tmp/coverage' 74 | }, 75 | 76 | plugins: [ 77 | 'karma-jasmine', 78 | 'karma-phantomjs-launcher' 79 | ] 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /test/spec.js: -------------------------------------------------------------------------------- 1 | describe('ga', function () { 2 | 'use strict'; 3 | 4 | // load the service's module 5 | beforeEach(module('ga')); 6 | 7 | // instantiate service 8 | var ga, window, compileElement; 9 | beforeEach(inject(function (_ga_, $window, $rootScope, $compile) { 10 | ga = _ga_; 11 | window = $window; 12 | window.gaCalled = false; 13 | window.ga = function() {window.gaCalled = arguments;} 14 | spyOn(window, 'ga').and.callThrough(); 15 | 16 | compileElement = function(html) { 17 | return $compile(angular.element(html))($rootScope.$new()); 18 | } 19 | 20 | })); 21 | 22 | 23 | describe('service', function () { 24 | 25 | it('should pass params', function () { 26 | ga('send', 'pageview'); 27 | expect(window.ga).toHaveBeenCalledWith('send', 'pageview') 28 | }); 29 | 30 | it('should pass multiple params', function () { 31 | ga([['set', 'page', '/'], ['send', 'pageview']]); 32 | expect(window.ga.calls.first().args).toEqual(['set', 'page', '/']) 33 | expect(window.ga).toHaveBeenCalledWith('send', 'pageview') 34 | }); 35 | 36 | it('should not throw anything', function () { 37 | delete window.ga; 38 | expect(window.ga).toBeUndefined(); 39 | ga('send', 'pageview'); 40 | expect(window.gaCalled).toBeFalsy(); 41 | }); 42 | 43 | }); 44 | 45 | describe('directive', function () { 46 | var el; 47 | it('should call on init', function() { 48 | el = compileElement('
') 49 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'init'); 50 | }) 51 | 52 | it('should handle shorthand array', function() { 53 | el = compileElement('
') 54 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'short'); 55 | }) 56 | 57 | it('should call on click', function() { 58 | el = compileElement('
') 59 | expect(window.ga).not.toHaveBeenCalled(); 60 | el.triggerHandler('click'); 61 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'click'); 62 | }) 63 | 64 | it('should call on blur', function() { 65 | el = compileElement('
') 66 | expect(window.ga).not.toHaveBeenCalled(); 67 | el.triggerHandler('blur'); 68 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'blur'); 69 | }) 70 | 71 | it('should handle repeated events', function() { 72 | el = compileElement('
') 73 | el.scope().testValue = 'click1'; 74 | el.triggerHandler('click'); 75 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'click1'); 76 | el.scope().testValue = 'click2'; 77 | el.triggerHandler('click'); 78 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'click2'); 79 | }) 80 | 81 | it('should click button', function() { 82 | el = compileElement('
Label
').triggerHandler('click') 83 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'button', 'click', 'Label'); 84 | }) 85 | 86 | it('should click # link', function() { 87 | el = compileElement('Label').triggerHandler('click') 88 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'button', 'click', 'Label'); 89 | }) 90 | 91 | it('should click anchor link', function() { 92 | el = compileElement('Label').triggerHandler('click') 93 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'button', '#anchor', 'Label'); 94 | }) 95 | 96 | it('should have title', function() { 97 | el = compileElement('Label').triggerHandler('click') 98 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'button', '#anchor', 'Title'); 99 | }) 100 | 101 | it('should click link', function() { 102 | el = compileElement('Label').triggerHandler('click') 103 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'link-in', '/', 'Label'); 104 | }) 105 | 106 | it('should click out link', function() { 107 | el = compileElement('Label').triggerHandler('click') 108 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'link-out', 'http://www.stamina.pl/', 'Label'); 109 | }) 110 | 111 | it('should click button', function() { 112 | el = compileElement('').triggerHandler('click') 113 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'button', 'click', 'Submit'); 114 | }) 115 | 116 | it('should have special attributes', function() { 117 | el = compileElement('Label').triggerHandler('click') 118 | expect(window.ga).toHaveBeenCalledWith('send', 'event', 'cat', 'act', 'lab', 1); 119 | }) 120 | 121 | 122 | 123 | 124 | }); 125 | 126 | 127 | }); 128 | --------------------------------------------------------------------------------