diff --git a/static/js/app.js b/static/js/app.js index d84ad57ab..adbf75ea5 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,5 +1,5 @@ // Start the application code itself. -quayApp = angular.module('quay', ['restangular', 'angularMoment'], function($provide) { +quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel'], function($provide) { $provide.factory('UserService', ['Restangular', function(Restangular) { var userResponse = { verified: false, @@ -14,6 +14,15 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment'], function($pro var userFetch = Restangular.one('user/'); userFetch.get().then(function(loadedUser) { userResponse = loadedUser; + + if (!userResponse.anonymous) { + mixpanel.identify(userResponse.username); + mixpanel.people.set({ + '$email': userResponse.email, + '$username': userResponse.username, + 'verified': userResponse.verified + }); + } }); }; @@ -39,18 +48,21 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment'], function($pro } }; }). - config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { - $routeProvider. - when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}). - when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}). - when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl}). - when('/repository/', {title: 'Repositories', templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}). - when('/user', {title: 'User Admin', templateUrl: '/static/partials/user-admin.html', controller: UserAdminCtrl}). - when('/guide/', {title: 'Getting Started Guide', templateUrl: '/static/partials/guide.html', controller: GuideCtrl}). - when('/plans/', {title: 'Quay Plans', templateUrl: '/static/partials/plans.html', controller: PlansCtrl}). - when('/', {title: 'Quay', templateUrl: '/static/partials/landing.html', controller: LandingCtrl}). - otherwise({redirectTo: '/'}); - }]). + config(['$routeProvider', '$locationProvider', '$analyticsProvider', + function($routeProvider, $locationProvider, $analyticsProvider) { + $analyticsProvider.virtualPageviews(true); + + $routeProvider. + when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}). + when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}). + when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl}). + when('/repository/', {title: 'Repositories', templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}). + when('/user', {title: 'User Admin', templateUrl: '/static/partials/user-admin.html', controller: UserAdminCtrl}). + when('/guide/', {title: 'Getting Started Guide', templateUrl: '/static/partials/guide.html', controller: GuideCtrl}). + when('/plans/', {title: 'Quay Plans', templateUrl: '/static/partials/plans.html', controller: PlansCtrl}). + when('/', {title: 'Quay', templateUrl: '/static/partials/landing.html', controller: LandingCtrl}). + otherwise({redirectTo: '/'}); + }]). config(function(RestangularProvider) { RestangularProvider.setBaseUrl('/api/'); }); diff --git a/static/lib/angulartics-mixpanel.js b/static/lib/angulartics-mixpanel.js new file mode 100755 index 000000000..349ceb32d --- /dev/null +++ b/static/lib/angulartics-mixpanel.js @@ -0,0 +1,30 @@ +/** + * @license Angulartics v0.8.5 + * (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics + * Contributed by http://github.com/L42y + * License: MIT + */ +(function(angular) { +'use strict'; + +/** + * @ngdoc overview + * @name angulartics.mixpanel + * Enables analytics support for Mixpanel (http://mixpanel.com) + */ +angular.module('angulartics.mixpanel', ['angulartics']) +.config(['$analyticsProvider', function ($analyticsProvider) { + angulartics.waitForVendorApi('mixpanel', 500, function (mixpanel) { + $analyticsProvider.registerPageTrack(function (path) { + if (path.indexOf('http') == 0) { return; } + window.mixpanel.track('page_view', { 'url' : path }); + }); + }); + + angulartics.waitForVendorApi('mixpanel', 500, function (mixpanel) { + $analyticsProvider.registerEventTrack(function (action, properties) { + window.mixpanel.track(action, properties); + }); + }); +}]); +})(angular); \ No newline at end of file diff --git a/static/lib/angulartics.js b/static/lib/angulartics.js new file mode 100755 index 000000000..42278c852 --- /dev/null +++ b/static/lib/angulartics.js @@ -0,0 +1,132 @@ +/** + * @license Angulartics v0.8.5 + * (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics + * License: MIT + */ +(function(angular, analytics) { +'use strict'; + +var angulartics = window.angulartics || (window.angulartics = {}); +angulartics.waitForVendorApi = function (objectName, delay, registerFn) { + if (!window.hasOwnProperty(objectName)) { + setTimeout(function () { angulartics.waitForVendorApi(objectName, delay, registerFn); }, delay); + } + else { + registerFn(window[objectName]); + } +}; + +/** + * @ngdoc overview + * @name angulartics + */ +angular.module('angulartics', []) +.provider('$analytics', function () { + var settings = { + pageTracking: { + autoTrackFirstPage: true, + autoTrackVirtualPages: true, + basePath: '', + bufferFlushDelay: 1000 + }, + eventTracking: { + bufferFlushDelay: 1000 + } + }; + + var cache = { + pageviews: [], + events: [] + }; + + var bufferedPageTrack = function (path) { + cache.pageviews.push(path); + }; + var bufferedEventTrack = function (event, properties) { + cache.events.push({name: event, properties: properties}); + }; + + var api = { + settings: settings, + pageTrack: bufferedPageTrack, + eventTrack: bufferedEventTrack + }; + + var registerPageTrack = function (fn) { + api.pageTrack = fn; + angular.forEach(cache.pageviews, function (path, index) { + setTimeout(function () { api.pageTrack(path); }, index * settings.pageTracking.bufferFlushDelay); + }); + }; + var registerEventTrack = function (fn) { + api.eventTrack = fn; + angular.forEach(cache.events, function (event, index) { + setTimeout(function () { api.eventTrack(event.name, event.properties); }, index * settings.eventTracking.bufferFlushDelay); + }); + }; + + return { + $get: function() { return api; }, + settings: settings, + virtualPageviews: function (value) { this.settings.pageTracking.autoTrackVirtualPages = value; }, + firstPageview: function (value) { this.settings.pageTracking.autoTrackFirstPage = value; }, + withBase: function (value) { this.settings.pageTracking.basePath = (value) ? angular.element('base').attr('href').slice(0, -1) : ''; }, + registerPageTrack: registerPageTrack, + registerEventTrack: registerEventTrack + }; +}) + +.run(['$rootScope', '$location', '$analytics', function ($rootScope, $location, $analytics) { + if ($analytics.settings.pageTracking.autoTrackFirstPage) { + $analytics.pageTrack($location.absUrl()); + } + if ($analytics.settings.pageTracking.autoTrackVirtualPages) { + $rootScope.$on('$routeChangeSuccess', function (event, current) { + if (current && (current.$$route||current).redirectTo) return; + var url = $analytics.settings.pageTracking.basePath + $location.url(); + $analytics.pageTrack(url); + }); + } +}]) + +.directive('analyticsOn', ['$analytics', function ($analytics) { + function isCommand(element) { + return ['a:','button:','button:button','button:submit','input:button','input:submit'].indexOf( + element.tagName.toLowerCase()+':'+(element.type||'')) >= 0; + } + + function inferEventType(element) { + if (isCommand(element)) return 'click'; + return 'click'; + } + + function inferEventName(element) { + if (isCommand(element)) return element.innerText || element.value; + return element.id || element.name || element.tagName; + } + + function isProperty(name) { + return name.substr(0, 9) === 'analytics' && ['on', 'event'].indexOf(name.substr(10)) === -1; + } + + return { + restrict: 'A', + scope: false, + link: function ($scope, $element, $attrs) { + var eventType = $attrs.analyticsOn || inferEventType($element[0]), + eventName = $attrs.analyticsEvent || inferEventName($element[0]); + + var properties = {}; + angular.forEach($attrs.$attr, function(attr, name) { + if (isProperty(attr)) { + properties[name.slice(9).toLowerCase()] = $attrs[name]; + } + }); + + angular.element($element[0]).bind(eventType, function () { + $analytics.eventTrack(eventName, properties); + }); + } + }; +}]); +})(angular); diff --git a/templates/index.html b/templates/index.html index 71d778f44..5553dade7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -19,6 +19,10 @@ + + + + @@ -30,6 +34,11 @@ + +