commit
d596a6d5b2
19 changed files with 91 additions and 133 deletions
|
@ -18,7 +18,7 @@ CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'MIXPANEL_KEY',
|
|||
'STRIPE_PUBLISHABLE_KEY', 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN',
|
||||
'AUTHENTICATION_TYPE', 'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT',
|
||||
'CONTACT_INFO', 'AVATAR_KIND', 'LOCAL_OAUTH_HANDLER', 'DOCUMENTATION_LOCATION',
|
||||
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE']
|
||||
'DOCUMENTATION_METADATA', 'SETUP_COMPLETE', 'DEBUG']
|
||||
|
||||
|
||||
def frontend_visible_config(config_dict):
|
||||
|
|
|
@ -7,10 +7,11 @@ LOCAL_DIRECTORY = '/static/ldn/'
|
|||
EXTERNAL_JS = [
|
||||
'code.jquery.com/jquery.js',
|
||||
'netdna.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-route.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-sanitize.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-animate.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-route.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-sanitize.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-animate.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-cookies.min.js',
|
||||
'cdn.jsdelivr.net/g/momentjs',
|
||||
'cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.2.0/js/bootstrap-datepicker.min.js',
|
||||
'cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/js/bootstrap-datetimepicker.min.js',
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
color: white !important;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -16,7 +15,6 @@
|
|||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.avatar-element .letter {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<span class="avatar-element"
|
||||
ng-style="{'width': size, 'height': size, 'backgroundColor': data.color, 'fontSize': fontSize, 'lineHeight': lineHeight}"
|
||||
ng-style="{'width': size, 'height': size, 'backgroundColor': (showGravatar || isLoading) ? 'transparent' : data.color, 'fontSize': fontSize, 'lineHeight': lineHeight}"
|
||||
ng-class="data.kind">
|
||||
<img ng-src="//www.gravatar.com/avatar/{{ data.hash }}?d=404&size={{ size }}"
|
||||
<img ng-src="//www.gravatar.com/avatar/{{ data.hash }}?d=blank&size=512"
|
||||
ng-if="loadGravatar"
|
||||
ng-show="hasGravatar"
|
||||
ng-image-watch="imageCallback(result)">
|
||||
<span class="default-avatar" ng-if="!isLoading && !hasGravatar">
|
||||
ng-visible="showGravatar"
|
||||
ng-image-watch="imageCallback(result)"
|
||||
ng-style="{'width': imageSize + 'px', 'height': imageSize + 'px'}"
|
||||
crossorigin="anonymous">
|
||||
<span class="default-avatar" ng-if="!isLoading && !showGravatar">
|
||||
<span class="letter" ng-if="data.kind != 'team' || data.name != 'owners'">{{ data.name.charAt(0).toUpperCase() }}</span>
|
||||
<span class="letter" ng-if="data.kind == 'team' && data.name == 'owners'">Ω</span>
|
||||
</span>
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
<td>
|
||||
<div class="co-checkbox">
|
||||
<input id="enable-ssl" type="checkbox" ng-model="config.PREFERRED_URL_SCHEME"
|
||||
ng-true-value="https" ng-false-value="http">
|
||||
ng-true-value="'https'" ng-false-value="'http'">
|
||||
<label for="enable-ssl">Enable SSL</label>
|
||||
</div>
|
||||
<div class="help-text" style="margin-bottom: 10px">
|
||||
|
@ -322,7 +322,7 @@
|
|||
<tr>
|
||||
<td>Authentication Key:</td>
|
||||
<td>
|
||||
<span class="config-service-key-field" service-name="{{ config.SECURITY_SCANNER_ISSUER_NAME }}"></span>
|
||||
<span class="config-service-key-field" service-name="{{ config.SECURITY_SCANNER_ISSUER_NAME || 'secscan' }}"></span>
|
||||
<div class="help-text">
|
||||
The security scanning service requires an authorized service key to speak to Quay. Once setup, the key
|
||||
can be managed in the Service Keys panel under the Super User Admin Panel.
|
||||
|
|
|
@ -1 +1 @@
|
|||
<ng-transclude>
|
||||
<span></span>
|
|
@ -6,7 +6,7 @@
|
|||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
≡
|
||||
</button>
|
||||
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}" target="{{ appLinkTarget() }}">
|
||||
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}">
|
||||
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"
|
||||
ng-class="Config.ENTERPRISE_LOGO_URL ? 'enterprise-logo' : 'hosted-logo'"></span>
|
||||
</a>
|
||||
|
@ -21,17 +21,17 @@
|
|||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<!-- Not signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="user.anonymous">
|
||||
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
|
||||
<li><a ng-href="/tour/" quay-section="tour">Tour</a></li>
|
||||
<li><a ng-href="/tutorial/" quay-section="tutorial">Tutorial</a></li>
|
||||
<li quay-require="['BILLING']"><a ng-href="/plans/" quay-section="plans">Pricing</a></li>
|
||||
<li><a href="https://docs.quay.io/" ng-safenewtab>Docs</a></li>
|
||||
<li><a href="https://blog.quay.io/" ng-safenewtab>Blog</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="!user.anonymous">
|
||||
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li><a ng-href="/repository/" quay-section="repository">Repositories</a></li>
|
||||
<li><a ng-href="/tutorial/" quay-section="tutorial">Tutorial</a></li>
|
||||
<li><a href="https://docs.quay.io/" ng-safenewtab>Docs</a></li>
|
||||
<li><a href="https://blog.quay.io/" ng-safenewtab>Blog</a></li>
|
||||
</ul>
|
||||
|
@ -39,13 +39,13 @@
|
|||
<!-- Phone -->
|
||||
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
|
||||
<li ng-switch-when="false">
|
||||
<a href="/user/{{ user.username }}?tab=settings" class="user-view" target="{{ appLinkTarget() }}">
|
||||
<a href="/user/{{ user.username }}?tab=settings" class="user-view">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}" ng-if="!externalSigninUrl">Sign in</a>
|
||||
<a class="user-view" href="/signin/" ng-if="!externalSigninUrl">Sign in</a>
|
||||
<a class="user-view" ng-href="{{ externalSigninUrl }}" ng-if="externalSigninUrl">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -128,7 +128,7 @@
|
|||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="/user/{{ user.username }}?tab=settings" target="{{ appLinkTarget() }}">
|
||||
<a href="/user/{{ user.username }}?tab=settings">
|
||||
Account Settings
|
||||
</a>
|
||||
</li>
|
||||
|
@ -137,7 +137,7 @@
|
|||
</ul>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}" ng-if="!externalSigninUrl">Sign in</a>
|
||||
<a class="user-view" href="/signin/" ng-if="!externalSigninUrl">Sign in</a>
|
||||
<a class="user-view" ng-href="{{ externalSigninUrl }}" ng-if="externalSigninUrl">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -149,7 +149,6 @@
|
|||
<div class="search-box-wrapper">
|
||||
<input id="search-box-input" type="search" placeholder="(Enter Search Terms)"
|
||||
ng-model-options="{'debounce': 250}" ng-model="currentSearchQuery"
|
||||
debounce="250"
|
||||
ng-keydown="handleSearchKeyDown($event)">
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<div class="repo-list-table-element">
|
||||
<div class="cor-loader" ng-if="isLoading"></div>
|
||||
<div ng-if="orderedRepositories.length == 0 && !isLoading">
|
||||
<div ng-if="orderedRepositories.entries.length == 0 && !isLoading">
|
||||
<div class="empty-primary-msg" ng-if="namespaces.length != 1">You do not have any viewable repositories.</div>
|
||||
<div class="empty-primary-msg" ng-if="namespaces.length == 1">This namespace doesn't have any viewable repositories.</div>
|
||||
<div class="empty-secondary-msg">Either no repositories exist yet or you may not have permission to view any. If you have permission, try <a href="/new">creating a new repository</a>.</div>
|
||||
</div>
|
||||
|
||||
<table class="co-table" ng-if="orderedRepositories.length && !isLoading">
|
||||
<table class="co-table" ng-if="orderedRepositories.entries.length && !isLoading">
|
||||
<thead>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('full_name', options.predicate, options.reverse)">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="user-setup-element">
|
||||
<div class="user-setup-element-view">
|
||||
<div class="setup-logo-container" ng-show="hideLogo != 'true'">
|
||||
<img src="{{ Config.getEnterpriseLogo(true) }}">
|
||||
<img ng-src="{{ Config.getEnterpriseLogo(true) }}">
|
||||
</div>
|
||||
|
||||
<div class="user-setup-content">
|
||||
|
|
|
@ -36,7 +36,7 @@ quayPages.constant('pages', {
|
|||
});
|
||||
|
||||
quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'cfp.hotkeys', 'angular-tour', 'restangular', 'angularMoment',
|
||||
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml', 'debounce',
|
||||
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml',
|
||||
'core-ui', 'core-config-setup', 'quayPages', 'infinite-scroll'];
|
||||
|
||||
if (window.__config && window.__config.MIXPANEL_KEY) {
|
||||
|
@ -72,6 +72,12 @@ quayApp.config(['$tooltipProvider', function ($tooltipProvider) {
|
|||
};
|
||||
}]);
|
||||
|
||||
quayApp.config(['$compileProvider', function ($compileProvider) {
|
||||
if (!window.__config['DEBUG']) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
}]);
|
||||
|
||||
// Configure the routes.
|
||||
quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeProvider, $locationProvider, pages) {
|
||||
$locationProvider.html5Mode(true);
|
||||
|
|
|
@ -1133,12 +1133,19 @@ angular.module("core-config-setup", ['angularFileUpload'])
|
|||
controller: function($scope, $element) {
|
||||
var firstSet = true;
|
||||
|
||||
$scope.patternMap = {};
|
||||
|
||||
$scope.getRegexp = function(pattern) {
|
||||
if (!pattern) {
|
||||
pattern = '.*';
|
||||
}
|
||||
return new RegExp(pattern);
|
||||
};
|
||||
if (!pattern) {
|
||||
pattern = '.*';
|
||||
}
|
||||
|
||||
if ($scope.patternMap[pattern]) {
|
||||
return $scope.patternMap[pattern];
|
||||
}
|
||||
|
||||
return $scope.patternMap[pattern] = new RegExp(pattern);
|
||||
};
|
||||
|
||||
$scope.$watch('binding', function(binding) {
|
||||
if (firstSet && !binding && $scope.defaultValue) {
|
||||
|
|
|
@ -16,20 +16,38 @@ angular.module('quay').directive('avatar', function () {
|
|||
$scope.AvatarService = AvatarService;
|
||||
$scope.Config = Config;
|
||||
$scope.isLoading = true;
|
||||
$scope.hasGravatar = false;
|
||||
$scope.showGravatar = false;
|
||||
$scope.loadGravatar = false;
|
||||
|
||||
$scope.imageCallback = function(r) {
|
||||
$timeout(function() {
|
||||
$scope.isLoading = false;
|
||||
$scope.hasGravatar = r;
|
||||
}, 1);
|
||||
$scope.imageCallback = function(result) {
|
||||
$scope.isLoading = false;
|
||||
|
||||
if (!result) {
|
||||
$scope.showGravatar = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine whether the gravatar is blank.
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 512;
|
||||
canvas.height = 512;
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage($element.find('img')[0], 0, 0);
|
||||
|
||||
var blank = document.createElement("canvas");
|
||||
blank.width = 512;
|
||||
blank.height = 512;
|
||||
|
||||
var isBlank = canvas.toDataURL('text/png') == blank.toDataURL('text/png');
|
||||
$scope.showGravatar = !isBlank;
|
||||
};
|
||||
|
||||
$scope.$watch('size', function(size) {
|
||||
size = size * 1 || 16;
|
||||
$scope.fontSize = (size - 4) + 'px';
|
||||
$scope.lineHeight = size + 'px';
|
||||
$scope.imageSize = size;
|
||||
});
|
||||
|
||||
$scope.$watch('data', function(data) {
|
||||
|
|
|
@ -147,18 +147,6 @@ angular.module('quay').directive('headerBar', function () {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.appLinkTarget = function() {
|
||||
if ($scope._appLinkTarget) {
|
||||
return $scope._appLinkTarget;
|
||||
}
|
||||
|
||||
if ($("div[ng-view]").length === 0) {
|
||||
return $scope._appLinkTarget = "_self";
|
||||
}
|
||||
|
||||
return $scope._appLinkTarget = "";
|
||||
};
|
||||
|
||||
$scope.getEnterpriseLogo = function() {
|
||||
return Config.getEnterpriseLogo(false);
|
||||
};
|
||||
|
|
|
@ -14,6 +14,8 @@ angular.module('quay').directive('popupInputButton', function () {
|
|||
'submitted': '&submitted'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
$scope.patternMap = {};
|
||||
|
||||
$scope.popupShown = function() {
|
||||
setTimeout(function() {
|
||||
var box = $('#input-box');
|
||||
|
@ -26,7 +28,12 @@ angular.module('quay').directive('popupInputButton', function () {
|
|||
if (!pattern) {
|
||||
pattern = '.*';
|
||||
}
|
||||
return new RegExp(pattern);
|
||||
|
||||
if ($scope.patternMap[pattern]) {
|
||||
return $scope.patternMap[pattern];
|
||||
}
|
||||
|
||||
return $scope.patternMap[pattern] = new RegExp(pattern);
|
||||
};
|
||||
|
||||
$scope.inputSubmit = function() {
|
||||
|
|
|
@ -77,7 +77,12 @@ angular.module('quay').directive('signinForm', function () {
|
|||
if (redirectUrl == $location.path() || redirectUrl == null) {
|
||||
return;
|
||||
}
|
||||
window.location = (redirectUrl ? redirectUrl : '/');
|
||||
|
||||
if (redirectUrl) {
|
||||
window.location = redirectUrl
|
||||
} else {
|
||||
$location.path('/');
|
||||
}
|
||||
}, 500);
|
||||
}, function(result) {
|
||||
$scope.signingIn = false;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
UserService.updateUserIn($scope, function(user) {
|
||||
if (!user.anonymous) {
|
||||
$location.path('/repository');
|
||||
$location.path('/repository/');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
/**
|
||||
* Helper service for working with cookies.
|
||||
*/
|
||||
angular.module('quay').factory('CookieService', ['$cookies', '$cookieStore', function($cookies, $cookieStore) {
|
||||
angular.module('quay').factory('CookieService', ['$cookies', function($cookies) {
|
||||
var cookieService = {};
|
||||
cookieService.putPermanent = function(name, value) {
|
||||
document.cookie = escape(name) + "=" + escape(value) + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
|
||||
};
|
||||
|
||||
cookieService.putSession = function(name, value) {
|
||||
$cookies[name] = value;
|
||||
$cookies.put(name, value);
|
||||
};
|
||||
|
||||
cookieService.clear = function(name) {
|
||||
$cookies[name] = '';
|
||||
$cookies.remove(name);
|
||||
};
|
||||
|
||||
cookieService.get = function(name) {
|
||||
return $cookies[name];
|
||||
return $cookies.get(name);
|
||||
};
|
||||
|
||||
return cookieService;
|
||||
|
|
7
static/lib/angular-cookies.min.js
vendored
7
static/lib/angular-cookies.min.js
vendored
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.2.0-ed8640b
|
||||
(c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(p,f,n){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,b){var c={},g={},h,k=!1,l=f.copy,m=f.isUndefined;b.addPollFn(function(){var a=b.cookies();h!=a&&(h=a,l(a,g),l(a,c),k&&d.$apply())})();k=!0;d.$watch(function(){var a,e,d;for(a in g)m(c[a])&&b.cookies(a,n);for(a in c)(e=c[a],f.isString(e))?e!==g[a]&&(b.cookies(a,e),d=!0):f.isDefined(g[a])?c[a]=g[a]:delete c[a];if(d)for(a in e=b.cookies(),c)c[a]!==e[a]&&(m(e[a])?delete c[a]:c[a]=e[a])});
|
||||
return c}]).factory("$cookieStore",["$cookies",function(d){return{get:function(b){return(b=d[b])?f.fromJson(b):b},put:function(b,c){d[b]=f.toJson(c)},remove:function(b){delete d[b]}}}])})(window,window.angular);
|
66
static/lib/angular-debounce.js
vendored
66
static/lib/angular-debounce.js
vendored
|
@ -1,66 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('debounce', [])
|
||||
.service('debounce', ['$timeout', function ($timeout) {
|
||||
return function (func, wait, immediate) {
|
||||
var timeout, args, context, result;
|
||||
function debounce() {
|
||||
/* jshint validthis:true */
|
||||
context = this;
|
||||
args = arguments;
|
||||
var later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
if (timeout) {
|
||||
$timeout.cancel(timeout);
|
||||
}
|
||||
timeout = $timeout(later, wait);
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
debounce.cancel = function () {
|
||||
$timeout.cancel(timeout);
|
||||
timeout = null;
|
||||
};
|
||||
return debounce;
|
||||
};
|
||||
}])
|
||||
.directive('debounce', ['debounce', '$parse', function (debounce, $parse) {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
priority: 999,
|
||||
link: function ($scope, $element, $attrs, ngModelController) {
|
||||
var debounceDuration = $parse($attrs.debounce)($scope);
|
||||
var immediate = !!$parse($attrs.immediate)($scope);
|
||||
var debouncedValue, pass;
|
||||
var prevRender = ngModelController.$render.bind(ngModelController);
|
||||
var commitSoon = debounce(function (viewValue) {
|
||||
pass = true;
|
||||
ngModelController.$setViewValue(viewValue);
|
||||
pass = false;
|
||||
}, parseInt(debounceDuration, 10), immediate);
|
||||
ngModelController.$render = function () {
|
||||
prevRender();
|
||||
commitSoon.cancel();
|
||||
//we must be first parser for this to work properly,
|
||||
//so we have priority 999 so that we unshift into parsers last
|
||||
debouncedValue = this.$viewValue;
|
||||
};
|
||||
ngModelController.$parsers.unshift(function (value) {
|
||||
if (pass) {
|
||||
debouncedValue = value;
|
||||
return value;
|
||||
} else {
|
||||
commitSoon(ngModelController.$viewValue);
|
||||
return debouncedValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
Reference in a new issue