This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/static/js/app.js

405 lines
12 KiB
JavaScript
Raw Normal View History

var TEAM_PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$';
var ROBOT_PATTERN = '^[a-zA-Z][a-zA-Z0-9]{3,29}$';
var USER_PATTERN = '^[a-z0-9_]{4,30}$';
// Define the pages module.
quayPages = angular.module('quayPages', [], function(){});
// Define a constant for creating pages.
quayPages.constant('pages', {
'_pages': {},
'create': function(pageName, templateName, opt_controller, opt_flags, opt_profiles) {
var profiles = opt_profiles || ['old-layout', 'layout'];
for (var i = 0; i < profiles.length; ++i) {
this._pages[profiles[i] + ':' + pageName] = {
'name': pageName,
'controller': opt_controller,
'templateName': templateName,
'flags': opt_flags || {}
};
}
},
'get': function(pageName, profiles) {
for (var i = 0; i < profiles.length; ++i) {
var current = profiles[i];
var key = current.id + ':' + pageName;
var page = this._pages[key];
if (page) {
return [current, page];
}
}
return null;
}
});
quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'cfp.hotkeys', 'angular-tour', 'restangular', 'angularMoment',
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml', 'debounce',
2015-04-06 23:17:18 +00:00
'core-ui', 'core-config-setup', 'quayPages'];
if (window.__config && window.__config.MIXPANEL_KEY) {
quayDependencies.push('angulartics');
quayDependencies.push('angulartics.mixpanel');
}
// Define the application.
quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoadingBarProvider) {
cfpLoadingBarProvider.includeSpinner = false;
});
// Disable tooltips on touch devices.
quayApp.config(['$tooltipProvider', function ($tooltipProvider) {
var tooltipFactory = $tooltipProvider.$get[$tooltipProvider.$get.length - 1];
// decorate the tooltip getter
$tooltipProvider.$get[$tooltipProvider.$get.length - 1] = function($window) {
if ('ontouchstart' in $window) {
var existing = tooltipFactory.apply(this, arguments);
return function(element) {
// Note: We only disable bs-tooltip's themselves. $tooltip is used for other things
// (such as the datepicker), so we need to be specific when canceling it.
if (element.attr('bs-tooltip') == null) {
return existing.apply(this, arguments);
} else {
return null;
}
};
}
return tooltipFactory.apply(this, arguments);
};
}]);
// Configure the routes.
quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeProvider, $locationProvider, pages) {
var title = window.__config['REGISTRY_TITLE'] || 'Quay.io';
$locationProvider.html5Mode(true);
// WARNING WARNING WARNING
// If you add a route here, you must add a corresponding route in thr endpoints/web.py
// index rule to make sure that deep links directly deep into the app continue to work.
// WARNING WARNING WARNING
var layoutProfile = 'old-layout';
// Check for the cookie for turning on the new layout.
if (document.cookie.toString().indexOf('quay.exp-new-layout=true') >= 0) {
layoutProfile = 'layout';
}
// Check for the override flag.
if (window.location.search.indexOf('old-ui=1') >= 0) {
layoutProfile = 'old-layout';
}
window.console.log('Using layout profile: ' + layoutProfile);
var routeBuilder = new AngularRouteBuilder($routeProvider, pages, [
// Start with the old pages (if we asked for it).
{id: 'old-layout', templatePath: '/static/partials/'},
// Fallback back combined new/existing pages.
{id: 'layout', templatePath: '/static/partials/'}
], layoutProfile);
routeBuilder
// Repository View
.route('/repository/:namespace/:name', 'repo-view')
2015-02-23 19:55:55 +00:00
.route('/repository/:namespace/:name/tag/:tag', 'repo-view')
// Image View
.route('/repository/:namespace/:name/image/:image', 'image-view')
// Repo Admin
.route('/repository/:namespace/:name/admin', 'repo-admin')
// Repo Builds
.route('/repository/:namespace/:name/build', 'repo-build')
2015-02-26 22:45:28 +00:00
// Repo Build View
.route('/repository/:namespace/:name/build/:buildid', 'build-view')
// Repo Build Package
.route('/repository/:namespace/:name/build/:buildid/buildpack', 'build-package')
// Repo List
.route('/repository/', 'repo-list')
// Organizations
.route('/organizations/', 'organizations')
// New Organization
.route('/organizations/new/', 'new-organization')
// View Organization
.route('/organization/:orgname', 'org-view')
// Organization Admin
.route('/organization/:orgname/admin', 'org-admin')
// View Organization Team
.route('/organization/:orgname/teams/:teamname', 'team-view')
// Organization Member Logs
.route('/organization/:orgname/logs/:membername', 'org-member-logs')
// Organization View Application
.route('/organization/:orgname/application/:clientid', 'manage-application')
// View User
.route('/user/:username', 'user-view')
// DEPRECATED: User Admin
.route('/user/', 'user-admin')
// Sign In
.route('/signin/', 'signin')
// New Repository
.route('/new/', 'new-repo')
// ER Management
.route('/superuser/', 'superuser')
// ER Setup
.route('/setup/', 'setup')
// Plans
.route('/plans/', 'plans')
// Tutorial
.route('/tutorial/', 'tutorial')
// Contact
.route('/contact/', 'contact')
// About
.route('/about/', 'about')
// Security
.route('/security/', 'security')
// Landing Page
.route('/', 'landing')
// Tour
.route('/tour/', 'tour')
.route('/tour/features', 'tour')
.route('/tour/organizations', 'tour')
.route('/tour/enterprise', 'tour')
// Confirm Invite
.route('/confirminvite', 'confirm-invite')
// Enable/disable experimental layout
.route('/__exp/newlayout', 'exp-new-layout')
// Default: Redirect to the landing page
.otherwise({redirectTo: '/'});
}]);
// Configure compile provider to add additional URL prefixes to the sanitization list. We use
// these on the Contact page.
quayApp.config(function($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|irc):/);
});
// Configure the API provider.
quayApp.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('/api/v1/');
});
// Configure analytics.
if (window.__config && window.__config.MIXPANEL_KEY) {
quayApp.config(['$analyticsProvider', function($analyticsProvider) {
$analyticsProvider.virtualPageviews(true);
}]);
}
// Configure sentry.
if (window.__config && window.__config.SENTRY_PUBLIC_DSN) {
quayApp.config(function($provide) {
$provide.decorator("$exceptionHandler", function($delegate) {
return function(ex, cause) {
$delegate(ex, cause);
Raven.captureException(ex, {extra: {cause: cause}});
};
2013-11-05 22:20:43 +00:00
});
});
}
// Run the application.
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService', 'MetaService',
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService, MetaService) {
2013-12-28 19:07:44 +00:00
var title = window.__config['REGISTRY_TITLE'] || 'Quay.io';
2013-12-28 19:07:44 +00:00
// Handle session security.
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
2013-12-28 19:07:44 +00:00
// Handle session expiration.
Restangular.setErrorInterceptor(function(response) {
if (response.status == 401 && response.data['error_type'] == 'invalid_token' &&
response.data['session_required'] !== false) {
$('#sessionexpiredModal').modal({});
return false;
}
if (response.status == 503) {
$('#cannotContactService').modal({});
return false;
}
if (response.status == 500) {
document.location = '/500';
return false;
}
return true;
});
// Check if we need to redirect based on a previously chosen plan.
var result = PlanService.handleNotedPlan();
// Check to see if we need to show a redirection page.
var redirectUrl = CookieService.get('quay.redirectAfterLoad');
CookieService.clear('quay.redirectAfterLoad');
if (!result && redirectUrl && redirectUrl.indexOf(window.location.href) == 0) {
window.location = redirectUrl;
return;
}
var changeTab = function(activeTab, opt_timeout) {
var checkCount = 0;
2013-12-10 04:33:28 +00:00
$timeout(function() {
if (checkCount > 5) { return; }
checkCount++;
2013-12-10 04:33:28 +00:00
$('a[data-toggle="tab"]').each(function(index) {
var tabName = this.getAttribute('data-target').substr(1);
if (tabName != activeTab) {
return;
}
if (this.clientWidth == 0) {
changeTab(activeTab, 500);
return;
2013-12-10 04:33:28 +00:00
}
UtilService.clickElement(this);
2013-12-10 04:33:28 +00:00
});
}, opt_timeout);
2013-12-10 04:33:28 +00:00
};
var resetDefaultTab = function() {
$timeout(function() {
$('a[data-toggle="tab"]').each(function(index) {
if (index == 0) {
UtilService.clickElement(this);
2013-12-10 04:33:28 +00:00
}
});
});
};
$rootScope.$watch('description', function(description) {
if (!description) {
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
}
2014-11-24 21:07:38 +00:00
// Note: We set the content of the description tag manually here rather than using Angular binding
// because we need the <meta> tag to have a default description that is not of the form "{{ description }}",
// we read by tools that do not properly invoke the Angular code.
2014-11-24 21:07:38 +00:00
$('#descriptionTag').attr('content', description);
});
2013-12-10 04:33:28 +00:00
$rootScope.$on('$routeUpdate', function(){
if ($location.search()['tab']) {
changeTab($location.search()['tab']);
} else {
resetDefaultTab();
}
});
2013-09-26 23:07:25 +00:00
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
2014-04-30 03:48:06 +00:00
$rootScope.current = current.$$route;
$rootScope.currentPage = current;
2014-04-30 03:48:06 +00:00
$rootScope.pageClass = '';
if (!current.$$route) { return; }
2014-04-24 04:40:01 +00:00
$rootScope.pageClass = current.$$route.pageClass || '';
$rootScope.newLayout = !!current.$$route.newLayout;
$rootScope.fixFooter = !!current.$$route.fixFooter;
MetaService.getInitialTitle(current, function(title) {
$rootScope.title = title;
});
MetaService.getInitialDescription(current, function(description) {
$rootScope.description = description
});
$anchorScroll();
2013-12-10 04:33:28 +00:00
});
$rootScope.$on('$viewContentLoaded', function(event) {
var current = $rootScope.currentPage;
MetaService.getTitle(current, function(title) {
$rootScope.title = title;
});
MetaService.getDescription(current, function(description) {
$rootScope.description = description;
});
2013-12-10 04:33:28 +00:00
var activeTab = $location.search()['tab'];
2015-03-31 18:40:34 +00:00
var checkTabs = function() {
var tabs = $('a[data-toggle="tab"]');
if (tabs.length == 0) {
$timeout(checkTabs, 50);
return;
}
2013-12-10 04:33:28 +00:00
2015-03-31 18:40:34 +00:00
tabs.on('shown.bs.tab', function (e) {
var tabName = e.target.getAttribute('data-target').substr(1);
$rootScope.$apply(function() {
var isDefaultTab = $('a[data-toggle="tab"]')[0] == e.target;
var newSearch = $.extend($location.search(), {});
if (isDefaultTab) {
delete newSearch['tab'];
} else {
newSearch['tab'] = tabName;
}
$location.search(newSearch);
});
2014-11-24 21:07:38 +00:00
e.preventDefault();
});
2013-12-10 04:33:28 +00:00
if (activeTab) {
changeTab(activeTab);
}
2015-03-31 18:40:34 +00:00
};
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
// is changed in the UI.
$timeout(checkTabs, 50);
2013-09-26 23:07:25 +00:00
});
var initallyChecked = false;
window.__isLoading = function() {
if (!initallyChecked) {
initallyChecked = true;
return true;
}
return $http.pendingRequests.length > 0;
};
}]);