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

343 lines
10 KiB
JavaScript

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', 'angular-tour', 'restangular', 'angularMoment',
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml',
'ngAnimate', '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;
});
// 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 = window.location.search.indexOf('old-ui=1') >= 0 ? 'old-layout' : 'layout';
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')
.route('/repository/:namespace/:name/tag/:tag', 'repo-view', 'RepoCtrl')
// 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')
// 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')
// 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')
// 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}});
};
});
});
}
// Run the application.
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService',
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService) {
// Handle session security.
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
// 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;
$timeout(function() {
if (checkCount > 5) { return; }
checkCount++;
$('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;
}
UtilService.clickElement(this);
});
}, opt_timeout);
};
var resetDefaultTab = function() {
$timeout(function() {
$('a[data-toggle="tab"]').each(function(index) {
if (index == 0) {
UtilService.clickElement(this);
}
});
});
};
$rootScope.$watch('description', function(description) {
if (!description) {
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
}
// 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.
$('#descriptionTag').attr('content', description);
});
$rootScope.$on('$routeUpdate', function(){
if ($location.search()['tab']) {
changeTab($location.search()['tab']);
} else {
resetDefaultTab();
}
});
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
$rootScope.pageClass = '';
$rootScope.current = current.$$route;
if (!current.$$route) { return; }
if (current.$$route.title) {
$rootScope.title = current.$$route.title;
}
if (current.$$route.pageClass) {
$rootScope.pageClass = current.$$route.pageClass;
}
$rootScope.newLayout = !!current.$$route.newLayout;
if (current.$$route.description) {
$rootScope.description = current.$$route.description;
} else {
$rootScope.description = '';
}
$rootScope.fixFooter = !!current.$$route.fixFooter;
$anchorScroll();
});
$rootScope.$on('$viewContentLoaded', function(event, current) {
var activeTab = $location.search()['tab'];
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
// is changed in the UI.
$timeout(function() {
$('a[data-toggle="tab"]').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);
});
e.preventDefault();
});
if (activeTab) {
changeTab(activeTab);
}
}, 400); // 400ms to make sure angular has rendered.
});
var initallyChecked = false;
window.__isLoading = function() {
if (!initallyChecked) {
initallyChecked = true;
return true;
}
return $http.pendingRequests.length > 0;
};
}]);