Merge pull request #1473 from coreos-inc/angularup

Move to Angular 1.5
This commit is contained in:
josephschorr 2016-05-17 16:34:01 -04:00
commit d596a6d5b2
19 changed files with 91 additions and 133 deletions

View file

@ -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):

View file

@ -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',

View file

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

View file

@ -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'">&Omega;</span>
</span>

View file

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

View file

@ -1 +1 @@
<ng-transclude>
<span></span>

View file

@ -6,7 +6,7 @@
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
&equiv;
</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>

View file

@ -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)">

View file

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

View file

@ -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);

View file

@ -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) {

View file

@ -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) {

View file

@ -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);
};

View file

@ -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() {

View file

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

View file

@ -21,7 +21,7 @@
UserService.updateUserIn($scope, function(user) {
if (!user.anonymous) {
$location.path('/repository');
$location.path('/repository/');
}
});

View file

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

View file

@ -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);

View file

@ -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;
}
});
}
};
}]);