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 @@
+
+