Add a loading bar and convert to using the new ApiService and resource-view (part #2)
This commit is contained in:
parent
414bd34d52
commit
b2e4b8152e
24 changed files with 1243 additions and 481 deletions
|
@ -272,9 +272,10 @@ def get_matching_users(prefix):
|
||||||
def get_matching_entities(prefix):
|
def get_matching_entities(prefix):
|
||||||
teams = []
|
teams = []
|
||||||
|
|
||||||
namespace_name = request.args.get('namespace', None)
|
namespace_name = request.args.get('namespace', '')
|
||||||
robot_namespace = None
|
robot_namespace = None
|
||||||
organization = None
|
organization = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
organization = model.get_organization(namespace_name)
|
organization = model.get_organization(namespace_name)
|
||||||
|
|
||||||
|
@ -308,7 +309,7 @@ def get_matching_entities(prefix):
|
||||||
'is_robot': user.is_robot,
|
'is_robot': user.is_robot,
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.is_org_member is not None:
|
if organization is not None:
|
||||||
user_json['is_org_member'] = user.is_robot or user.is_org_member
|
user_json['is_org_member'] = user.is_robot or user.is_org_member
|
||||||
|
|
||||||
return user_json
|
return user_json
|
||||||
|
|
|
@ -963,12 +963,20 @@ form input.ng-valid.ng-dirty,
|
||||||
.entity-mini-listing {
|
.entity-mini-listing {
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-mini-listing i {
|
.entity-mini-listing i {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entity-mini-listing i.fa-exclamation-triangle {
|
||||||
|
position: absolute;
|
||||||
|
right: -16px;
|
||||||
|
top: 4px;
|
||||||
|
color: #c09853;
|
||||||
|
}
|
||||||
|
|
||||||
.entity-mini-listing .warning {
|
.entity-mini-listing .warning {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
Credit Card
|
Credit Card
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<i class="fa fa-spinner fa-spin fa-2x" ng-show="!currentCard || changingCard"></i>
|
<div class="quay-spinner" ng-show="!currentCard || changingCard"></div>
|
||||||
<div class="current-card" ng-show="currentCard && !changingCard">
|
<div class="current-card" ng-show="currentCard && !changingCard">
|
||||||
<img src="{{ '/static/img/creditcards/' + getCreditImage(currentCard) }}" ng-show="currentCard.last4">
|
<img src="{{ '/static/img/creditcards/' + getCreditImage(currentCard) }}" ng-show="currentCard.last4">
|
||||||
<span class="no-card-outline" ng-show="!currentCard.last4"></span>
|
<span class="no-card-outline" ng-show="!currentCard.last4"></span>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
Billing Options
|
Billing Options
|
||||||
<i class="fa fa-spinner fa-spin" ng-show="working"></i>
|
<div class="quay-spinner" ng-show="working"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="settings-option">
|
<div class="settings-option">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="entityDropdownMenu">
|
<ul class="dropdown-menu" role="menu" aria-labelledby="entityDropdownMenu">
|
||||||
<li ng-show="lazyLoading"><i class="fa fa-spinner"></i></li>
|
<li ng-show="lazyLoading" style="padding: 10px"><div class="quay-spinner"></div></li>
|
||||||
|
|
||||||
<li role="presentation" ng-repeat="team in teams" ng-show="!lazyLoading"
|
<li role="presentation" ng-repeat="team in teams" ng-show="!lazyLoading"
|
||||||
ng-click="setEntity(team.name, 'team', false)">
|
ng-click="setEntity(team.name, 'team', false)">
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="loading">
|
<div ng-show="loading">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner 3x"></div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!loading">
|
<div ng-show="!loading">
|
||||||
<div id="bar-chart" style="width: 800px; height: 500px;" ng-show="chartVisible">
|
<div id="bar-chart" style="width: 800px; height: 500px;" ng-show="chartVisible">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="plan-manager-element">
|
<div class="plan-manager-element">
|
||||||
<!-- Loading/Changing -->
|
<!-- Loading/Changing -->
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="planLoading"></i>
|
<div class="quay-spinner 3x" ng-show="planLoading"></div>
|
||||||
|
|
||||||
<!-- Alerts -->
|
<!-- Alerts -->
|
||||||
<div class="alert alert-danger" ng-show="limit == 'over' && !planLoading">
|
<div class="alert alert-danger" ng-show="limit == 'over' && !planLoading">
|
||||||
|
@ -45,13 +45,13 @@
|
||||||
<button class="btn" ng-show="subscribedPlan.stripeId !== plan.stripeId"
|
<button class="btn" ng-show="subscribedPlan.stripeId !== plan.stripeId"
|
||||||
ng-class="subscribedPlan.price == 0 ? 'btn-primary' : 'btn-default'"
|
ng-class="subscribedPlan.price == 0 ? 'btn-primary' : 'btn-default'"
|
||||||
ng-click="changeSubscription(plan.stripeId)">
|
ng-click="changeSubscription(plan.stripeId)">
|
||||||
<i class="fa fa-spinner fa-spin" ng-show="planChanging"></i>
|
<span class="quay-spinner" ng-show="planChanging"></span>
|
||||||
<span ng-show="!planChanging && subscribedPlan.price != 0">Change</span>
|
<span ng-show="!planChanging && subscribedPlan.price != 0">Change</span>
|
||||||
<span ng-show="!planChanging && subscribedPlan.price == 0">Subscribe</span>
|
<span ng-show="!planChanging && subscribedPlan.price == 0">Subscribe</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" ng-show="subscription.plan === plan.stripeId && plan.price > 0"
|
<button class="btn btn-danger" ng-show="subscription.plan === plan.stripeId && plan.price > 0"
|
||||||
ng-click="cancelSubscription()">
|
ng-click="cancelSubscription()">
|
||||||
<i class="fa fa-spinner fa-spin" ng-show="planChanging"></i>
|
<span class="quay-spinner" ng-show="planChanging"></span>
|
||||||
<span ng-show="!planChanging">Cancel</span>
|
<span ng-show="!planChanging">Cancel</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="robots-manager-element">
|
<div class="robots-manager-element">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="loading"></i>
|
<div class="quay-spinner" ng-show="loading"></div>
|
||||||
<div class="alert alert-info">Robot accounts allow for delegating access in multiple repositories to role-based accounts that you manage</div>
|
<div class="alert alert-info">Robot accounts allow for delegating access in multiple repositories to role-based accounts that you manage</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!loading">
|
<div class="container" ng-show="!loading">
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div ng-show="registering" style="text-align: center">
|
<div ng-show="registering" style="text-align: center">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner 2x"></div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="awaitingConfirmation">
|
<div ng-show="awaitingConfirmation">
|
||||||
<div class="sub-message">
|
<div class="sub-message">
|
||||||
|
|
1
static/directives/spinner.html
Normal file
1
static/directives/spinner.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<div class="small-spinner"></div>
|
|
@ -586,9 +586,9 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'rest
|
||||||
// WARNING WARNING WARNING
|
// WARNING WARNING WARNING
|
||||||
$routeProvider.
|
$routeProvider.
|
||||||
when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
||||||
fixFooter: true, reloadOnSearch: false}).
|
fixFooter: false, reloadOnSearch: false}).
|
||||||
when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
||||||
fixFooter: true}).
|
fixFooter: false}).
|
||||||
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}).
|
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}).
|
||||||
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}).
|
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}).
|
||||||
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
|
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
|
||||||
|
@ -1316,6 +1316,21 @@ quayApp.directive('resourceView', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('quaySpinner', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/spinner.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
quayApp.directive('organizationHeader', function () {
|
quayApp.directive('organizationHeader', function () {
|
||||||
var directiveDefinitionObject = {
|
var directiveDefinitionObject = {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
|
@ -1579,12 +1594,12 @@ quayApp.directive('entitySearch', function () {
|
||||||
number++;
|
number++;
|
||||||
|
|
||||||
var input = $element[0].firstChild.firstChild;
|
var input = $element[0].firstChild.firstChild;
|
||||||
var namespace = $scope.namespace || '';
|
|
||||||
$(input).typeahead({
|
$(input).typeahead({
|
||||||
name: 'entities' + number,
|
name: 'entities' + number,
|
||||||
remote: {
|
remote: {
|
||||||
url: '/api/entities/%QUERY',
|
url: '/api/entities/%QUERY',
|
||||||
replace: function (url, uriEncodedQuery) {
|
replace: function (url, uriEncodedQuery) {
|
||||||
|
var namespace = $scope.namespace || '';
|
||||||
url = url.replace('%QUERY', uriEncodedQuery);
|
url = url.replace('%QUERY', uriEncodedQuery);
|
||||||
url += '?namespace=' + encodeURIComponent(namespace);
|
url += '?namespace=' + encodeURIComponent(namespace);
|
||||||
if ($scope.includeTeams) {
|
if ($scope.includeTeams) {
|
||||||
|
@ -1616,8 +1631,8 @@ quayApp.directive('entitySearch', function () {
|
||||||
}
|
}
|
||||||
template += '<span class="name">' + datum.value + '</span>';
|
template += '<span class="name">' + datum.value + '</span>';
|
||||||
|
|
||||||
if (datum.entity.is_org_member !== undefined && !datum.entity.is_org_member && datum.kind == 'user') {
|
if (datum.entity.is_org_member === false && datum.entity.kind == 'user') {
|
||||||
template += '<div class="alert-warning warning">This user is outside your organization</div>';
|
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>';
|
||||||
}
|
}
|
||||||
|
|
||||||
template += '</div>';
|
template += '</div>';
|
||||||
|
|
|
@ -539,7 +539,7 @@ function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope
|
||||||
newWebhook.customPOST($scope.newWebhook).then(function(resp) {
|
newWebhook.customPOST($scope.newWebhook).then(function(resp) {
|
||||||
$scope.webhooks.push(resp);
|
$scope.webhooks.push(resp);
|
||||||
$scope.newWebhook.url = '';
|
$scope.newWebhook.url = '';
|
||||||
$scope.newWebhookForm.$setPristine();
|
$scope.createWebhookForm.$setPristine();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -590,23 +590,19 @@ function RepoAdminCtrl($scope, Restangular, ApiService, $routeParams, $rootScope
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, UserService, KeyService, $routeParams) {
|
function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, UserService, KeyService, $routeParams) {
|
||||||
$scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) {
|
if ($routeParams['migrate']) {
|
||||||
$scope.askForPassword = currentUser.askForPassword;
|
$('#migrateTab').tab('show')
|
||||||
if (!currentUser.anonymous) {
|
|
||||||
$scope.user = currentUser;
|
|
||||||
}
|
}
|
||||||
$scope.loading = false;
|
|
||||||
}, true);
|
UserService.updateUserIn($scope, function(user) {
|
||||||
|
$scope.askForPassword = currentUser.askForPassword;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.readyForPlan = function() {
|
$scope.readyForPlan = function() {
|
||||||
// Show the subscribe dialog if a plan was requested.
|
// Show the subscribe dialog if a plan was requested.
|
||||||
return $routeParams['plan'];
|
return $routeParams['plan'];
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($routeParams['migrate']) {
|
|
||||||
$('#migrateTab').tab('show')
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
$scope.updatingUser = false;
|
$scope.updatingUser = false;
|
||||||
$scope.changePasswordSuccess = false;
|
$scope.changePasswordSuccess = false;
|
||||||
|
@ -685,13 +681,11 @@ function UserAdminCtrl($scope, $timeout, $location, Restangular, PlanService, Us
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImageViewCtrl($scope, $routeParams, $rootScope, Restangular) {
|
function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, Restangular) {
|
||||||
var namespace = $routeParams.namespace;
|
var namespace = $routeParams.namespace;
|
||||||
var name = $routeParams.name;
|
var name = $routeParams.name;
|
||||||
var imageid = $routeParams.image;
|
var imageid = $routeParams.image;
|
||||||
|
|
||||||
$('#copyClipboard').clipboardCopy();
|
|
||||||
|
|
||||||
$scope.parseDate = function(dateString) {
|
$scope.parseDate = function(dateString) {
|
||||||
return Date.parse(dateString);
|
return Date.parse(dateString);
|
||||||
};
|
};
|
||||||
|
@ -737,30 +731,32 @@ function ImageViewCtrl($scope, $routeParams, $rootScope, Restangular) {
|
||||||
if ($scope.tree) { return; }
|
if ($scope.tree) { return; }
|
||||||
|
|
||||||
$scope.tree = new ImageFileChangeTree($scope.image, $scope.combinedChanges);
|
$scope.tree = new ImageFileChangeTree($scope.image, $scope.combinedChanges);
|
||||||
setTimeout(function() {
|
$timeout(function() {
|
||||||
$scope.tree.draw('changes-tree-container');
|
$scope.tree.draw('changes-tree-container');
|
||||||
}, 10);
|
}, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch the image.
|
var fetchImage = function() {
|
||||||
$scope.loading = true;
|
$scope.image = ApiService.at('repository', namespace, name, 'image', imageid).get(function(image) {
|
||||||
var imageFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid);
|
|
||||||
imageFetch.get().then(function(image) {
|
|
||||||
$scope.loading = false;
|
|
||||||
$scope.repo = {
|
$scope.repo = {
|
||||||
'name': name,
|
'name': name,
|
||||||
'namespace': namespace
|
'namespace': namespace
|
||||||
};
|
};
|
||||||
$scope.image = image;
|
|
||||||
$rootScope.title = 'View Image - ' + image.id;
|
$rootScope.title = 'View Image - ' + image.id;
|
||||||
$rootScope.description = 'Viewing docker image ' + image.id + ' under repository ' + namespace + '/' + name +
|
$rootScope.description = 'Viewing docker image ' + image.id + ' under repository ' + namespace + '/' + name +
|
||||||
': Image changes tree and list view';
|
': Image changes tree and list view';
|
||||||
}, function() {
|
|
||||||
$rootScope.title = 'Unknown Image';
|
|
||||||
$scope.loading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch the image changes.
|
// Fetch the image's changes.
|
||||||
|
fetchChanges();
|
||||||
|
|
||||||
|
$('#copyClipboard').clipboardCopy();
|
||||||
|
|
||||||
|
return image;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchChanges = function() {
|
||||||
var changesFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid + '/changes');
|
var changesFetch = Restangular.one('repository/' + namespace + '/' + name + '/image/' + imageid + '/changes');
|
||||||
changesFetch.get().then(function(changes) {
|
changesFetch.get().then(function(changes) {
|
||||||
var combinedChanges = [];
|
var combinedChanges = [];
|
||||||
|
@ -780,18 +776,18 @@ function ImageViewCtrl($scope, $routeParams, $rootScope, Restangular) {
|
||||||
$scope.combinedChanges = combinedChanges;
|
$scope.combinedChanges = combinedChanges;
|
||||||
$scope.imageChanges = changes;
|
$scope.imageChanges = changes;
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch the image.
|
||||||
|
fetchImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
function V1Ctrl($scope, $location, UserService) {
|
function V1Ctrl($scope, $location, UserService) {
|
||||||
$scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) {
|
UserService.updateUserIn($scope);
|
||||||
$scope.user = currentUser;
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangular, PlanService) {
|
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangular, PlanService) {
|
||||||
$scope.$watch( function () { return UserService.currentUser(); }, function (currentUser) {
|
UserService.updateUserIn($scope);
|
||||||
$scope.user = currentUser;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
$scope.repo = {
|
$scope.repo = {
|
||||||
'is_public': 1,
|
'is_public': 1,
|
||||||
|
@ -805,6 +801,94 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangula
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Watch the namespace on the repo. If it changes, we update the plan and the public/private
|
||||||
|
// accordingly.
|
||||||
|
$scope.isUserNamespace = true;
|
||||||
|
$scope.$watch('repo.namespace', function(namespace) {
|
||||||
|
// Note: Can initially be undefined.
|
||||||
|
if (!namespace) { return; }
|
||||||
|
|
||||||
|
var isUserNamespace = (namespace == $scope.user.username);
|
||||||
|
|
||||||
|
$scope.planRequired = null;
|
||||||
|
$scope.isUserNamespace = isUserNamespace;
|
||||||
|
|
||||||
|
if (isUserNamespace) {
|
||||||
|
// Load the user's subscription information in case they want to create a private
|
||||||
|
// repository.
|
||||||
|
PlanService.getSubscription(null, subscribedToPlan, function() {
|
||||||
|
PlanService.getMinimumPlan(1, false, function(minimum) { $scope.planRequired = minimum; });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$scope.planRequired = null;
|
||||||
|
|
||||||
|
var checkPrivateAllowed = Restangular.one('organization/' + namespace + '/private');
|
||||||
|
checkPrivateAllowed.get().then(function(resp) {
|
||||||
|
$scope.planRequired = resp.privateAllowed ? null : {};
|
||||||
|
}, function() {
|
||||||
|
$scope.planRequired = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-set to private repo.
|
||||||
|
$scope.repo.is_public = '0';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.createNewRepo = function() {
|
||||||
|
$('#repoName').popover('hide');
|
||||||
|
|
||||||
|
var uploader = $('#file-drop')[0];
|
||||||
|
if ($scope.repo.initialize && uploader.files.length < 1) {
|
||||||
|
$('#missingfileModal').modal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.creating = true;
|
||||||
|
var repo = $scope.repo;
|
||||||
|
var data = {
|
||||||
|
'namespace': repo.namespace,
|
||||||
|
'repository': repo.name,
|
||||||
|
'visibility': repo.is_public == '1' ? 'public' : 'private',
|
||||||
|
'description': repo.description
|
||||||
|
};
|
||||||
|
|
||||||
|
var createPost = Restangular.one('repository');
|
||||||
|
createPost.customPOST(data).then(function(created) {
|
||||||
|
$scope.creating = false;
|
||||||
|
$scope.created = created;
|
||||||
|
|
||||||
|
// Repository created. Start the upload process if applicable.
|
||||||
|
if ($scope.repo.initialize) {
|
||||||
|
startFileUpload(created);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, redirect to the repo page.
|
||||||
|
$location.path('/repository/' + created.namespace + '/' + created.name);
|
||||||
|
}, function(result) {
|
||||||
|
$scope.creating = false;
|
||||||
|
$scope.createError = result.data;
|
||||||
|
$timeout(function() {
|
||||||
|
$('#repoName').popover('show');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.upgradePlan = function() {
|
||||||
|
var callbacks = {
|
||||||
|
'started': function() { $scope.planChanging = true; },
|
||||||
|
'opened': function() { $scope.planChanging = true; },
|
||||||
|
'closed': function() { $scope.planChanging = false; },
|
||||||
|
'success': subscribedToPlan,
|
||||||
|
'failure': function(resp) {
|
||||||
|
$('#couldnotsubscribeModal').modal();
|
||||||
|
$scope.planChanging = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks);
|
||||||
|
};
|
||||||
|
|
||||||
var startBuild = function(repo, fileId) {
|
var startBuild = function(repo, fileId) {
|
||||||
$scope.building = true;
|
$scope.building = true;
|
||||||
|
|
||||||
|
@ -890,97 +974,11 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangula
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.createNewRepo = function() {
|
|
||||||
$('#repoName').popover('hide');
|
|
||||||
|
|
||||||
var uploader = $('#file-drop')[0];
|
|
||||||
if ($scope.repo.initialize && uploader.files.length < 1) {
|
|
||||||
$('#missingfileModal').modal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.creating = true;
|
|
||||||
var repo = $scope.repo;
|
|
||||||
var data = {
|
|
||||||
'namespace': repo.namespace,
|
|
||||||
'repository': repo.name,
|
|
||||||
'visibility': repo.is_public == '1' ? 'public' : 'private',
|
|
||||||
'description': repo.description
|
|
||||||
};
|
|
||||||
|
|
||||||
var createPost = Restangular.one('repository');
|
|
||||||
createPost.customPOST(data).then(function(created) {
|
|
||||||
$scope.creating = false;
|
|
||||||
$scope.created = created;
|
|
||||||
|
|
||||||
// Repository created. Start the upload process if applicable.
|
|
||||||
if ($scope.repo.initialize) {
|
|
||||||
startFileUpload(created);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, redirect to the repo page.
|
|
||||||
$location.path('/repository/' + created.namespace + '/' + created.name);
|
|
||||||
}, function(result) {
|
|
||||||
$scope.creating = false;
|
|
||||||
$scope.createError = result.data;
|
|
||||||
$timeout(function() {
|
|
||||||
$('#repoName').popover('show');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.upgradePlan = function() {
|
|
||||||
var callbacks = {
|
|
||||||
'started': function() { $scope.planChanging = true; },
|
|
||||||
'opened': function() { $scope.planChanging = true; },
|
|
||||||
'closed': function() { $scope.planChanging = false; },
|
|
||||||
'success': subscribedToPlan,
|
|
||||||
'failure': function(resp) {
|
|
||||||
$('#couldnotsubscribeModal').modal();
|
|
||||||
$scope.planChanging = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
PlanService.changePlan($scope, null, $scope.planRequired.stripeId, callbacks);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Watch the namespace on the repo. If it changes, we update the plan and the public/private
|
|
||||||
// accordingly.
|
|
||||||
$scope.isUserNamespace = true;
|
|
||||||
$scope.$watch('repo.namespace', function(namespace) {
|
|
||||||
// Note: Can initially be undefined.
|
|
||||||
if (!namespace) { return; }
|
|
||||||
|
|
||||||
var isUserNamespace = (namespace == $scope.user.username);
|
|
||||||
|
|
||||||
$scope.planRequired = null;
|
|
||||||
$scope.isUserNamespace = isUserNamespace;
|
|
||||||
|
|
||||||
if (isUserNamespace) {
|
|
||||||
// Load the user's subscription information in case they want to create a private
|
|
||||||
// repository.
|
|
||||||
PlanService.getSubscription(null, subscribedToPlan, function() {
|
|
||||||
PlanService.getMinimumPlan(1, false, function(minimum) { $scope.planRequired = minimum; });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$scope.planRequired = null;
|
|
||||||
|
|
||||||
var checkPrivateAllowed = Restangular.one('organization/' + namespace + '/private');
|
|
||||||
checkPrivateAllowed.get().then(function(resp) {
|
|
||||||
$scope.planRequired = resp.privateAllowed ? null : {};
|
|
||||||
}, function() {
|
|
||||||
$scope.planRequired = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Auto-set to private repo.
|
|
||||||
$scope.repo.is_public = '0';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
function OrgViewCtrl($rootScope, $scope, Restangular, ApiService, $routeParams) {
|
||||||
|
var orgname = $routeParams.orgname;
|
||||||
|
|
||||||
$('.info-icon').popover({
|
$('.info-icon').popover({
|
||||||
'trigger': 'hover',
|
'trigger': 'hover',
|
||||||
'html': true
|
'html': true
|
||||||
|
@ -989,21 +987,6 @@ function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
||||||
$scope.TEAM_PATTERN = TEAM_PATTERN;
|
$scope.TEAM_PATTERN = TEAM_PATTERN;
|
||||||
$rootScope.title = 'Loading...';
|
$rootScope.title = 'Loading...';
|
||||||
|
|
||||||
var orgname = $routeParams.orgname;
|
|
||||||
|
|
||||||
var loadOrganization = function() {
|
|
||||||
var getOrganization = Restangular.one(getRestUrl('organization', orgname));
|
|
||||||
getOrganization.get().then(function(resp) {
|
|
||||||
$scope.organization = resp;
|
|
||||||
$scope.loading = false;
|
|
||||||
|
|
||||||
$rootScope.title = orgname;
|
|
||||||
$rootScope.description = 'Viewing organization ' + orgname;
|
|
||||||
}, function() {
|
|
||||||
$scope.loading = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.teamRoles = [
|
$scope.teamRoles = [
|
||||||
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
||||||
{ 'id': 'creator', 'title': 'Creator', 'kind': 'success' },
|
{ 'id': 'creator', 'title': 'Creator', 'kind': 'success' },
|
||||||
|
@ -1063,10 +1046,21 @@ function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var loadOrganization = function() {
|
||||||
|
$scope.orgResource = ApiService.at('organization', orgname).get(function(org) {
|
||||||
|
$scope.organization = org;
|
||||||
|
$rootScope.title = orgname;
|
||||||
|
$rootScope.description = 'Viewing organization ' + orgname;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the organization.
|
||||||
loadOrganization();
|
loadOrganization();
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService, PlanService) {
|
function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService, PlanService, ApiService) {
|
||||||
|
var orgname = $routeParams.orgname;
|
||||||
|
|
||||||
// Load the list of plans.
|
// Load the list of plans.
|
||||||
PlanService.getPlans(function(plans) {
|
PlanService.getPlans(function(plans) {
|
||||||
$scope.plans = plans.business;
|
$scope.plans = plans.business;
|
||||||
|
@ -1082,8 +1076,6 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
|
||||||
addPlans(plans.business);
|
addPlans(plans.business);
|
||||||
});
|
});
|
||||||
|
|
||||||
var orgname = $routeParams.orgname;
|
|
||||||
|
|
||||||
$scope.orgname = orgname;
|
$scope.orgname = orgname;
|
||||||
$scope.membersLoading = true;
|
$scope.membersLoading = true;
|
||||||
$scope.membersFound = null;
|
$scope.membersFound = null;
|
||||||
|
@ -1133,35 +1125,32 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadOrganization = function() {
|
var loadOrganization = function() {
|
||||||
var getOrganization = Restangular.one(getRestUrl('organization', orgname));
|
$scope.orgResource = ApiService.at('organization', orgname).get(function(org) {
|
||||||
getOrganization.get().then(function(resp) {
|
if (org && org.is_admin) {
|
||||||
if (resp && resp.is_admin) {
|
$scope.organization = org;
|
||||||
$scope.organization = resp;
|
|
||||||
$rootScope.title = orgname + ' (Admin)';
|
$rootScope.title = orgname + ' (Admin)';
|
||||||
$rootScope.description = 'Administration page for organization ' + orgname;
|
$rootScope.description = 'Administration page for organization ' + orgname;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.loading = false;
|
|
||||||
}, function() {
|
|
||||||
$scope.loading = false;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load the organization.
|
||||||
loadOrganization();
|
loadOrganization();
|
||||||
}
|
}
|
||||||
|
|
||||||
function TeamViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
function TeamViewCtrl($rootScope, $scope, Restangular, ApiService, $routeParams) {
|
||||||
$('.info-icon').popover({
|
$('.info-icon').popover({
|
||||||
'trigger': 'hover',
|
'trigger': 'hover',
|
||||||
'html': true
|
'html': true
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.orgname = $routeParams.orgname;
|
|
||||||
var teamname = $routeParams.teamname;
|
var teamname = $routeParams.teamname;
|
||||||
|
var orgname = $routeParams.orgname;
|
||||||
|
|
||||||
|
$scope.orgname = orgname;
|
||||||
|
$scope.teamname = teamname;
|
||||||
|
|
||||||
$rootScope.title = 'Loading...';
|
$rootScope.title = 'Loading...';
|
||||||
$scope.loading = true;
|
|
||||||
$scope.teamname = teamname;
|
|
||||||
|
|
||||||
$scope.addNewMember = function(member) {
|
$scope.addNewMember = function(member) {
|
||||||
if ($scope.members[member.name]) { return; }
|
if ($scope.members[member.name]) { return; }
|
||||||
|
@ -1195,57 +1184,38 @@ function TeamViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadOrganization = function() {
|
var loadOrganization = function() {
|
||||||
var getOrganization = Restangular.one(getRestUrl('organization', $scope.orgname))
|
$scope.orgResource = ApiService.at('organization', orgname).get(function(org) {
|
||||||
getOrganization.get().then(function(resp) {
|
$scope.organization = org;
|
||||||
$scope.organization = resp;
|
|
||||||
$scope.team = $scope.organization.teams[teamname];
|
$scope.team = $scope.organization.teams[teamname];
|
||||||
$scope.loading = !$scope.organization || !$scope.members;
|
$rootScope.title = teamname + ' (' + $scope.orgname + ')';
|
||||||
}, function() {
|
$rootScope.description = 'Team management page for team ' + teamname + ' under organization ' + $scope.orgname;
|
||||||
$scope.organization = null;
|
loadMembers();
|
||||||
$scope.members = null;
|
return org;
|
||||||
$scope.loading = false;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadMembers = function() {
|
var loadMembers = function() {
|
||||||
var getMembers = Restangular.one(getRestUrl('organization', $scope.orgname, 'team', teamname, 'members'));
|
$scope.membersResource = ApiService.at('organization', $scope.orgname, 'team', teamname, 'members').get(function(resp) {
|
||||||
getMembers.get().then(function(resp) {
|
|
||||||
$scope.members = resp.members;
|
$scope.members = resp.members;
|
||||||
$scope.canEditMembers = resp.can_edit;
|
$scope.canEditMembers = resp.can_edit;
|
||||||
$scope.loading = !$scope.organization || !$scope.members;
|
return resp.members;
|
||||||
$rootScope.title = teamname + ' (' + $scope.orgname + ')';
|
|
||||||
$rootScope.description = 'Team management page for team ' + teamname + ' under organization ' + $scope.orgname;
|
|
||||||
}, function() {
|
|
||||||
$scope.organization = null;
|
|
||||||
$scope.members = null;
|
|
||||||
$scope.loading = false;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load the organization.
|
||||||
loadOrganization();
|
loadOrganization();
|
||||||
loadMembers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrgsCtrl($scope, UserService) {
|
function OrgsCtrl($scope, UserService) {
|
||||||
$scope.loading = true;
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
$scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) {
|
|
||||||
$scope.user = currentUser;
|
|
||||||
$scope.loading = false;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
browserchrome.update();
|
browserchrome.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, Restangular) {
|
function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, Restangular) {
|
||||||
$scope.loading = true;
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
$scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) {
|
var requested = $routeParams['plan'];
|
||||||
$scope.user = currentUser;
|
|
||||||
$scope.loading = false;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
requested = $routeParams['plan'];
|
|
||||||
|
|
||||||
// Load the list of plans.
|
// Load the list of plans.
|
||||||
PlanService.getPlans(function(plans) {
|
PlanService.getPlans(function(plans) {
|
||||||
|
@ -1284,13 +1254,13 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
|
||||||
|
|
||||||
var createPost = Restangular.one('organization/');
|
var createPost = Restangular.one('organization/');
|
||||||
createPost.customPOST(data).then(function(created) {
|
createPost.customPOST(data).then(function(created) {
|
||||||
$scope.creating = false;
|
|
||||||
$scope.created = created;
|
$scope.created = created;
|
||||||
|
|
||||||
// Reset the organizations list.
|
// Reset the organizations list.
|
||||||
UserService.load();
|
UserService.load();
|
||||||
|
|
||||||
var showOrg = function() {
|
var showOrg = function() {
|
||||||
|
$scope.creating = false;
|
||||||
$location.path('/organization/' + org.name + '/');
|
$location.path('/organization/' + org.name + '/');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1301,6 +1271,7 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, show the subscribe for the plan.
|
// Otherwise, show the subscribe for the plan.
|
||||||
|
$scope.creating = true;
|
||||||
var callbacks = {
|
var callbacks = {
|
||||||
'opened': function() { $scope.creating = true; },
|
'opened': function() { $scope.creating = true; },
|
||||||
'closed': showOrg,
|
'closed': showOrg,
|
||||||
|
@ -1308,7 +1279,7 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
|
||||||
'failure': showOrg
|
'failure': showOrg
|
||||||
};
|
};
|
||||||
|
|
||||||
PlanService.changePlan($scope, org.name, $scope.currentPlan.stripeId, false, callbacks);
|
PlanService.changePlan($scope, org.name, $scope.currentPlan.stripeId, callbacks);
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
$scope.creating = false;
|
$scope.creating = false;
|
||||||
$scope.createError = result.data.message || result.data;
|
$scope.createError = result.data.message || result.data;
|
||||||
|
@ -1320,49 +1291,38 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, Plan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular) {
|
function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular, ApiService) {
|
||||||
var orgname = $routeParams.orgname;
|
var orgname = $routeParams.orgname;
|
||||||
var membername = $routeParams.membername;
|
var membername = $routeParams.membername;
|
||||||
|
|
||||||
$scope.orgname = orgname;
|
$scope.orgname = orgname;
|
||||||
$scope.loading = true;
|
|
||||||
$scope.memberInfo = null;
|
$scope.memberInfo = null;
|
||||||
$scope.ready = false;
|
$scope.ready = false;
|
||||||
|
|
||||||
var checkReady = function() {
|
|
||||||
$scope.loading = !$scope.organization || !$scope.memberInfo;
|
|
||||||
if (!$scope.loading) {
|
|
||||||
$rootScope.title = 'Logs for ' + $scope.memberInfo.username + ' (' + $scope.orgname + ')';
|
|
||||||
$rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username +
|
|
||||||
' under organization ' + $scope.orgname;
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.ready = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadOrganization = function() {
|
var loadOrganization = function() {
|
||||||
var getOrganization = Restangular.one(getRestUrl('organization', orgname))
|
$scope.orgResource = ApiService.at('organization', orgname).get(function(org) {
|
||||||
getOrganization.get().then(function(resp) {
|
$scope.organization = org;
|
||||||
$scope.organization = resp;
|
return org;
|
||||||
checkReady();
|
|
||||||
}, function() {
|
|
||||||
$scope.organization = null;
|
|
||||||
$scope.loading = false;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadMemberInfo = function() {
|
var loadMemberInfo = function() {
|
||||||
var getMemberInfo = Restangular.one(getRestUrl('organization', orgname, 'members', membername))
|
$scope.memberResource = ApiService.at('organization', $scope.orgname, 'members', membername).get(function(resp) {
|
||||||
getMemberInfo.get().then(function(resp) {
|
|
||||||
$scope.memberInfo = resp.member;
|
$scope.memberInfo = resp.member;
|
||||||
checkReady();
|
|
||||||
}, function() {
|
$rootScope.title = 'Logs for ' + $scope.memberInfo.username + ' (' + $scope.orgname + ')';
|
||||||
$scope.memberInfo = null;
|
$rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username +
|
||||||
$scope.loading = false;
|
' under organization ' + $scope.orgname;
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.ready = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return resp.member;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load the org info and the member info.
|
||||||
loadOrganization();
|
loadOrganization();
|
||||||
loadMemberInfo();
|
loadMemberInfo();
|
||||||
}
|
}
|
|
@ -54,7 +54,7 @@ ImageHistoryTree.prototype.calculateDimensions_ = function(container) {
|
||||||
var cw = Math.max(document.getElementById(container).clientWidth, this.maxWidth_ * DEPTH_WIDTH);
|
var cw = Math.max(document.getElementById(container).clientWidth, this.maxWidth_ * DEPTH_WIDTH);
|
||||||
var ch = this.maxHeight_ * (DEPTH_HEIGHT + 10);
|
var ch = this.maxHeight_ * (DEPTH_HEIGHT + 10);
|
||||||
|
|
||||||
var margin = { top: 40, right: 20, bottom: 20, left: 40 };
|
var margin = { top: 40, right: 20, bottom: 20, left: 80 };
|
||||||
var m = [margin.top, margin.right, margin.bottom, margin.left];
|
var m = [margin.top, margin.right, margin.bottom, margin.left];
|
||||||
var w = cw - m[1] - m[3];
|
var w = cw - m[1] - m[3];
|
||||||
var h = ch - m[0] - m[2];
|
var h = ch - m[0] - m[2];
|
||||||
|
@ -87,6 +87,7 @@ ImageHistoryTree.prototype.updateDimensions_ = function() {
|
||||||
var viewportHeight = $(window).height();
|
var viewportHeight = $(window).height();
|
||||||
var boundingBox = document.getElementById(container).getBoundingClientRect();
|
var boundingBox = document.getElementById(container).getBoundingClientRect();
|
||||||
document.getElementById(container).style.maxHeight = (viewportHeight - boundingBox.top - 110) + 'px';
|
document.getElementById(container).style.maxHeight = (viewportHeight - boundingBox.top - 110) + 'px';
|
||||||
|
|
||||||
$('#' + container).overscroll();
|
$('#' + container).overscroll();
|
||||||
|
|
||||||
// Update the tree.
|
// Update the tree.
|
||||||
|
@ -94,9 +95,13 @@ ImageHistoryTree.prototype.updateDimensions_ = function() {
|
||||||
var tree = this.tree_;
|
var tree = this.tree_;
|
||||||
var vis = this.vis_;
|
var vis = this.vis_;
|
||||||
|
|
||||||
|
|
||||||
|
var ow = w + m[1] + m[3];
|
||||||
|
var oh = h + m[0] + m[2];
|
||||||
rootSvg
|
rootSvg
|
||||||
.attr("width", w + m[1] + m[3])
|
.attr("width", ow)
|
||||||
.attr("height", h + m[0] + m[2]);
|
.attr("height", oh)
|
||||||
|
.attr("style", "width: " + ow + "px; height: " + oh + "px");
|
||||||
|
|
||||||
tree.size([w, h]);
|
tree.size([w, h]);
|
||||||
vis.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
|
vis.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
|
||||||
|
|
793
static/lib/jquery.overscroll.js
Normal file
793
static/lib/jquery.overscroll.js
Normal file
|
@ -0,0 +1,793 @@
|
||||||
|
/**
|
||||||
|
* Overscroll v1.7.3
|
||||||
|
* A jQuery Plugin that emulates the iPhone scrolling experience in a browser.
|
||||||
|
* http://azoffdesign.com/overscroll
|
||||||
|
*
|
||||||
|
* Intended for use with the latest jQuery
|
||||||
|
* http://code.jquery.com/jquery-latest.js
|
||||||
|
*
|
||||||
|
* Copyright 2013, Jonathan Azoff
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
* https://github.com/azoff/overscroll/blob/master/mit.license
|
||||||
|
*
|
||||||
|
* For API documentation, see the README file
|
||||||
|
* http://azof.fr/pYCzuM
|
||||||
|
*
|
||||||
|
* Date: Tuesday, March 18th 2013
|
||||||
|
*/
|
||||||
|
(function(global, dom, browser, math, wait, cancel, namespace, $, none){
|
||||||
|
|
||||||
|
// We want to run this plug-in in strict-mode
|
||||||
|
// so that we may benefit from its optimizations
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// The key used to bind-instance specific data to an object
|
||||||
|
var datakey = 'overscroll';
|
||||||
|
|
||||||
|
// create <body> node if there's not one present (e.g., for test runners)
|
||||||
|
if (dom.body === null) {
|
||||||
|
dom.documentElement.appendChild(
|
||||||
|
dom.createElement('body')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// quick fix for IE 8 and below since getComputedStyle() is not supported
|
||||||
|
// TODO: find a better solution
|
||||||
|
if (!global.getComputedStyle) {
|
||||||
|
global.getComputedStyle = function (el, pseudo) {
|
||||||
|
this.el = el;
|
||||||
|
this.getPropertyValue = function (prop) {
|
||||||
|
var re = /(\-([a-z]){1})/g;
|
||||||
|
if (prop == 'float') prop = 'styleFloat';
|
||||||
|
if (re.test(prop)) {
|
||||||
|
prop = prop.replace(re, function () {
|
||||||
|
return arguments[2].toUpperCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// runs feature detection for overscroll
|
||||||
|
var compat = {
|
||||||
|
animate: (function(){
|
||||||
|
var fn = global.requestAnimationFrame ||
|
||||||
|
global.webkitRequestAnimationFrame ||
|
||||||
|
global.mozRequestAnimationFrame ||
|
||||||
|
global.oRequestAnimationFrame ||
|
||||||
|
global.msRequestAnimationFrame ||
|
||||||
|
function(callback) { wait(callback, 1000/60); };
|
||||||
|
return function(callback) {
|
||||||
|
fn.call(global, callback);
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
overflowScrolling: (function(){
|
||||||
|
var style = '';
|
||||||
|
var div = dom.createElement('div');
|
||||||
|
var prefixes = ['webkit', 'moz', 'o', 'ms'];
|
||||||
|
dom.body.appendChild(div);
|
||||||
|
$.each(prefixes, function(i, prefix){
|
||||||
|
div.style[prefix + 'OverflowScrolling'] = 'touch';
|
||||||
|
});
|
||||||
|
div.style.overflowScrolling = 'touch';
|
||||||
|
var computedStyle = global.getComputedStyle(div);
|
||||||
|
if (!!computedStyle.overflowScrolling) {
|
||||||
|
style = 'overflow-scrolling';
|
||||||
|
} else {
|
||||||
|
$.each(prefixes, function(i, prefix){
|
||||||
|
if (!!computedStyle[prefix + 'OverflowScrolling']) {
|
||||||
|
style = '-' + prefix + '-overflow-scrolling';
|
||||||
|
}
|
||||||
|
return !style;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
div.parentNode.removeChild(div);
|
||||||
|
return style;
|
||||||
|
})(),
|
||||||
|
cursor: (function() {
|
||||||
|
var div = dom.createElement('div');
|
||||||
|
var prefixes = ['webkit', 'moz'];
|
||||||
|
var gmail = 'https://mail.google.com/mail/images/2/';
|
||||||
|
var style = {
|
||||||
|
grab: 'url('+gmail+'openhand.cur), move',
|
||||||
|
grabbing: 'url('+gmail+'closedhand.cur), move'
|
||||||
|
};
|
||||||
|
dom.body.appendChild(div);
|
||||||
|
$.each(prefixes, function(i, prefix){
|
||||||
|
var found, cursor = '-' + prefix + '-grab';
|
||||||
|
div.style.cursor = cursor;
|
||||||
|
var computedStyle = global.getComputedStyle(div);
|
||||||
|
found = computedStyle.cursor === cursor;
|
||||||
|
if (found) {
|
||||||
|
style = {
|
||||||
|
grab: '-' + prefix + '-grab',
|
||||||
|
grabbing: '-' + prefix + '-grabbing'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return !found;
|
||||||
|
});
|
||||||
|
div.parentNode.removeChild(div);
|
||||||
|
return style;
|
||||||
|
})()
|
||||||
|
};
|
||||||
|
|
||||||
|
// These are all the events that could possibly
|
||||||
|
// be used by the plug-in
|
||||||
|
var events = {
|
||||||
|
drag: 'mousemove touchmove',
|
||||||
|
end: 'mouseup mouseleave click touchend touchcancel',
|
||||||
|
hover: 'mouseenter mouseleave',
|
||||||
|
ignored: 'select dragstart drag',
|
||||||
|
scroll: 'scroll',
|
||||||
|
start: 'mousedown touchstart',
|
||||||
|
wheel: 'mousewheel DOMMouseScroll'
|
||||||
|
};
|
||||||
|
|
||||||
|
// These settings are used to tweak drift settings
|
||||||
|
// for the plug-in
|
||||||
|
var settings = {
|
||||||
|
captureThreshold: 3,
|
||||||
|
driftDecay: 1.1,
|
||||||
|
driftSequences: 22,
|
||||||
|
driftTimeout: 100,
|
||||||
|
scrollDelta: 15,
|
||||||
|
thumbOpacity: 0.7,
|
||||||
|
thumbThickness: 6,
|
||||||
|
thumbTimeout: 400,
|
||||||
|
wheelDelta: 20,
|
||||||
|
wheelTicks: 120
|
||||||
|
};
|
||||||
|
|
||||||
|
// These defaults are used to complement any options
|
||||||
|
// passed into the plug-in entry point
|
||||||
|
var defaults = {
|
||||||
|
cancelOn: 'select,input,textarea',
|
||||||
|
direction: 'multi',
|
||||||
|
dragHold: false,
|
||||||
|
hoverThumbs: false,
|
||||||
|
scrollDelta: settings.scrollDelta,
|
||||||
|
showThumbs: true,
|
||||||
|
persistThumbs: false,
|
||||||
|
captureWheel: true,
|
||||||
|
wheelDelta: settings.wheelDelta,
|
||||||
|
wheelDirection: 'multi',
|
||||||
|
zIndex: 999,
|
||||||
|
ignoreSizing: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Triggers a DOM event on the overscrolled element.
|
||||||
|
// All events are namespaced under the overscroll name
|
||||||
|
function triggerEvent(event, target) {
|
||||||
|
target.trigger('overscroll:' + event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to return a timestamp
|
||||||
|
function time() {
|
||||||
|
return (new Date()).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captures the position from an event, modifies the properties
|
||||||
|
// of the second argument to persist the position, and then
|
||||||
|
// returns the modified object
|
||||||
|
function capturePosition(event, position, index) {
|
||||||
|
position.x = event.pageX;
|
||||||
|
position.y = event.pageY;
|
||||||
|
position.time = time();
|
||||||
|
position.index = index;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to move the thumbs around an overscrolled element
|
||||||
|
function moveThumbs(thumbs, sizing, left, top) {
|
||||||
|
|
||||||
|
var ml, mt;
|
||||||
|
|
||||||
|
if (thumbs && thumbs.added) {
|
||||||
|
if (thumbs.horizontal) {
|
||||||
|
ml = left * (1 + sizing.container.width / sizing.container.scrollWidth);
|
||||||
|
mt = top + sizing.thumbs.horizontal.top;
|
||||||
|
thumbs.horizontal.css('margin', mt + 'px 0 0 ' + ml + 'px');
|
||||||
|
}
|
||||||
|
if (thumbs.vertical) {
|
||||||
|
ml = left + sizing.thumbs.vertical.left;
|
||||||
|
mt = top * (1 + sizing.container.height / sizing.container.scrollHeight);
|
||||||
|
thumbs.vertical.css('margin', mt + 'px 0 0 ' + ml + 'px');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to toggle the thumbs on and off
|
||||||
|
// of an overscrolled element
|
||||||
|
function toggleThumbs(thumbs, options, dragging) {
|
||||||
|
if (thumbs && thumbs.added && !options.persistThumbs) {
|
||||||
|
if (dragging) {
|
||||||
|
if (thumbs.vertical) {
|
||||||
|
thumbs.vertical.stop(true, true).fadeTo('fast', settings.thumbOpacity);
|
||||||
|
}
|
||||||
|
if (thumbs.horizontal) {
|
||||||
|
thumbs.horizontal.stop(true, true).fadeTo('fast', settings.thumbOpacity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (thumbs.vertical) {
|
||||||
|
thumbs.vertical.fadeTo('fast', 0);
|
||||||
|
}
|
||||||
|
if (thumbs.horizontal) {
|
||||||
|
thumbs.horizontal.fadeTo('fast', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defers click event listeners to after a mouseup event.
|
||||||
|
// Used to avoid unintentional clicks
|
||||||
|
function deferClick(target) {
|
||||||
|
var clicks, key = 'events';
|
||||||
|
var events = $._data ? $._data(target[0], key) : target.data(key);
|
||||||
|
if (events && events.click) {
|
||||||
|
clicks = events.click.slice();
|
||||||
|
target.off('click').one('click', function(){
|
||||||
|
$.each(clicks, function(i, click){
|
||||||
|
target.click(click);
|
||||||
|
}); return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggles thumbs on hover. This event is only triggered
|
||||||
|
// if the hoverThumbs option is set
|
||||||
|
function hover(event) {
|
||||||
|
var data = event.data,
|
||||||
|
thumbs = data.thumbs,
|
||||||
|
options = data.options,
|
||||||
|
dragging = event.type === 'mouseenter';
|
||||||
|
toggleThumbs(thumbs, options, dragging);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is only ever used when the overscrolled element
|
||||||
|
// scrolled outside of the scope of this plugin.
|
||||||
|
function scroll(event) {
|
||||||
|
var data = event.data;
|
||||||
|
if (!data.flags.dragged) {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
moveThumbs(data.thumbs, data.sizing, this.scrollLeft, this.scrollTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles mouse wheel scroll events
|
||||||
|
function wheel(event) {
|
||||||
|
|
||||||
|
// prevent any default wheel behavior
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var data = event.data,
|
||||||
|
options = data.options,
|
||||||
|
sizing = data.sizing,
|
||||||
|
thumbs = data.thumbs,
|
||||||
|
dwheel = data.wheel,
|
||||||
|
flags = data.flags,
|
||||||
|
original = event.originalEvent,
|
||||||
|
delta = 0, deltaX = 0, deltaY = 0;
|
||||||
|
|
||||||
|
// stop any drifts
|
||||||
|
flags.drifting = false;
|
||||||
|
|
||||||
|
// normalize the wheel ticks
|
||||||
|
if (original.detail) {
|
||||||
|
delta = -original.detail;
|
||||||
|
if (original.detailX) {
|
||||||
|
deltaX = -original.detailX;
|
||||||
|
}
|
||||||
|
if (original.detailY) {
|
||||||
|
deltaY = -original.detailY;
|
||||||
|
}
|
||||||
|
} else if (original.wheelDelta) {
|
||||||
|
delta = original.wheelDelta / settings.wheelTicks;
|
||||||
|
if (original.wheelDeltaX) {
|
||||||
|
deltaX = original.wheelDeltaX / settings.wheelTicks;
|
||||||
|
}
|
||||||
|
if (original.wheelDeltaY) {
|
||||||
|
deltaY = original.wheelDeltaY / settings.wheelTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply a pixel delta to each tick
|
||||||
|
delta *= options.wheelDelta;
|
||||||
|
deltaX *= options.wheelDelta;
|
||||||
|
deltaY *= options.wheelDelta;
|
||||||
|
|
||||||
|
// initialize flags if this is the first tick
|
||||||
|
if (!dwheel) {
|
||||||
|
data.target.data(datakey).dragging = flags.dragging = true;
|
||||||
|
data.wheel = dwheel = { timeout: null };
|
||||||
|
toggleThumbs(thumbs, options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually modify scroll offsets
|
||||||
|
if (options.wheelDirection === 'vertical'){
|
||||||
|
/*jshint validthis:true */
|
||||||
|
this.scrollTop -= delta;
|
||||||
|
} else if ( options.wheelDirection === 'horizontal') {
|
||||||
|
this.scrollLeft -= delta;
|
||||||
|
} else {
|
||||||
|
this.scrollLeft -= deltaX;
|
||||||
|
this.scrollTop -= deltaY || delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwheel.timeout) { cancel(dwheel.timeout); }
|
||||||
|
|
||||||
|
moveThumbs(thumbs, sizing, this.scrollLeft, this.scrollTop);
|
||||||
|
|
||||||
|
dwheel.timeout = wait(function() {
|
||||||
|
data.target.data(datakey).dragging = flags.dragging = false;
|
||||||
|
toggleThumbs(thumbs, options, data.wheel = null);
|
||||||
|
}, settings.thumbTimeout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the current scroll offset during a mouse move
|
||||||
|
function drag(event) {
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var data = event.data,
|
||||||
|
touches = event.originalEvent.touches,
|
||||||
|
options = data.options,
|
||||||
|
sizing = data.sizing,
|
||||||
|
thumbs = data.thumbs,
|
||||||
|
position = data.position,
|
||||||
|
flags = data.flags,
|
||||||
|
target = data.target.get(0);
|
||||||
|
|
||||||
|
|
||||||
|
// correct page coordinates for touch devices
|
||||||
|
if (touches && touches.length) {
|
||||||
|
event = touches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flags.dragged) {
|
||||||
|
toggleThumbs(thumbs, options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.dragged = true;
|
||||||
|
|
||||||
|
if (options.direction !== 'vertical') {
|
||||||
|
target.scrollLeft -= (event.pageX - position.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.options.direction !== 'horizontal') {
|
||||||
|
target.scrollTop -= (event.pageY - position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
capturePosition(event, data.position);
|
||||||
|
|
||||||
|
if (--data.capture.index <= 0) {
|
||||||
|
data.target.data(datakey).dragging = flags.dragging = true;
|
||||||
|
capturePosition(event, data.capture, settings.captureThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveThumbs(thumbs, sizing, target.scrollLeft, target.scrollTop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// sends the overscrolled element into a drift
|
||||||
|
function drift(target, event, callback) {
|
||||||
|
|
||||||
|
var data = event.data, dx, dy, xMod, yMod,
|
||||||
|
capture = data.capture,
|
||||||
|
options = data.options,
|
||||||
|
sizing = data.sizing,
|
||||||
|
thumbs = data.thumbs,
|
||||||
|
elapsed = time() - capture.time,
|
||||||
|
scrollLeft = target.scrollLeft,
|
||||||
|
scrollTop = target.scrollTop,
|
||||||
|
decay = settings.driftDecay;
|
||||||
|
|
||||||
|
// only drift if enough time has passed since
|
||||||
|
// the last capture event
|
||||||
|
if (elapsed > settings.driftTimeout) {
|
||||||
|
callback(data); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine offset between last capture and current time
|
||||||
|
dx = options.scrollDelta * (event.pageX - capture.x);
|
||||||
|
dy = options.scrollDelta * (event.pageY - capture.y);
|
||||||
|
|
||||||
|
// update target scroll offsets
|
||||||
|
if (options.direction !== 'vertical') {
|
||||||
|
scrollLeft -= dx;
|
||||||
|
} if (options.direction !== 'horizontal') {
|
||||||
|
scrollTop -= dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split the distance to travel into a set of sequences
|
||||||
|
xMod = dx / settings.driftSequences;
|
||||||
|
yMod = dy / settings.driftSequences;
|
||||||
|
|
||||||
|
triggerEvent('driftstart', data.target);
|
||||||
|
|
||||||
|
data.drifting = true;
|
||||||
|
|
||||||
|
// animate the drift sequence
|
||||||
|
compat.animate(function render() {
|
||||||
|
if (data.drifting) {
|
||||||
|
var min = 1, max = -1;
|
||||||
|
data.drifting = false;
|
||||||
|
if (yMod > min && target.scrollTop > scrollTop || yMod < max && target.scrollTop < scrollTop) {
|
||||||
|
data.drifting = true;
|
||||||
|
target.scrollTop -= yMod;
|
||||||
|
yMod /= decay;
|
||||||
|
}
|
||||||
|
if (xMod > min && target.scrollLeft > scrollLeft || xMod < max && target.scrollLeft < scrollLeft) {
|
||||||
|
data.drifting = true;
|
||||||
|
target.scrollLeft -= xMod;
|
||||||
|
xMod /= decay;
|
||||||
|
}
|
||||||
|
moveThumbs(thumbs, sizing, target.scrollLeft, target.scrollTop);
|
||||||
|
compat.animate(render);
|
||||||
|
} else {
|
||||||
|
triggerEvent('driftend', data.target);
|
||||||
|
callback(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// starts the drag operation and binds the mouse move handler
|
||||||
|
function start(event) {
|
||||||
|
|
||||||
|
var data = event.data,
|
||||||
|
touches = event.originalEvent.touches,
|
||||||
|
target = data.target,
|
||||||
|
dstart = data.start = $(event.target),
|
||||||
|
flags = data.flags;
|
||||||
|
|
||||||
|
// stop any drifts
|
||||||
|
flags.drifting = false;
|
||||||
|
|
||||||
|
// only start drag if the user has not explictly banned it.
|
||||||
|
if (dstart.size() && !dstart.is(data.options.cancelOn)) {
|
||||||
|
|
||||||
|
// without this the simple "click" event won't be recognized on touch clients
|
||||||
|
if (!touches) { event.preventDefault(); }
|
||||||
|
|
||||||
|
if (!compat.overflowScrolling) {
|
||||||
|
target.css('cursor', compat.cursor.grabbing);
|
||||||
|
target.data(datakey).dragging = flags.dragging = flags.dragged = false;
|
||||||
|
|
||||||
|
// apply the drag listeners to the doc or target
|
||||||
|
if(data.options.dragHold) {
|
||||||
|
$(document).on(events.drag, data, drag);
|
||||||
|
} else {
|
||||||
|
target.on(events.drag, data, drag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.position = capturePosition(event, {});
|
||||||
|
data.capture = capturePosition(event, {}, settings.captureThreshold);
|
||||||
|
triggerEvent('dragstart', target);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ends the drag operation and unbinds the mouse move handler
|
||||||
|
function stop(event) {
|
||||||
|
|
||||||
|
var data = event.data,
|
||||||
|
target = data.target,
|
||||||
|
options = data.options,
|
||||||
|
flags = data.flags,
|
||||||
|
thumbs = data.thumbs,
|
||||||
|
|
||||||
|
// hides the thumbs after the animation is done
|
||||||
|
done = function () {
|
||||||
|
if (thumbs && !options.hoverThumbs) {
|
||||||
|
toggleThumbs(thumbs, options, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove drag listeners from doc or target
|
||||||
|
if(options.dragHold) {
|
||||||
|
$(document).unbind(events.drag, drag);
|
||||||
|
} else {
|
||||||
|
target.unbind(events.drag, drag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only fire events and drift if we started with a
|
||||||
|
// valid position
|
||||||
|
if (data.position) {
|
||||||
|
|
||||||
|
triggerEvent('dragend', target);
|
||||||
|
|
||||||
|
// only drift if a drag passed our threshold
|
||||||
|
if (flags.dragging && !compat.overflowScrolling) {
|
||||||
|
drift(target.get(0), event, done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// only if we moved, and the mouse down is the same as
|
||||||
|
// the mouse up target do we defer the event
|
||||||
|
if (flags.dragging && !compat.overflowScrolling && data.start && data.start.is(event.target)) {
|
||||||
|
deferClick(data.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear all internal flags and settings
|
||||||
|
target.data(datakey).dragging =
|
||||||
|
data.start =
|
||||||
|
data.capture =
|
||||||
|
data.position =
|
||||||
|
flags.dragged =
|
||||||
|
flags.dragging = false;
|
||||||
|
|
||||||
|
// set the cursor back to normal
|
||||||
|
target.css('cursor', compat.cursor.grab);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures that a full set of options are provided
|
||||||
|
// for the plug-in. Also does some validation
|
||||||
|
function getOptions(options) {
|
||||||
|
|
||||||
|
// fill in missing values with defaults
|
||||||
|
options = $.extend({}, defaults, options);
|
||||||
|
|
||||||
|
// check for inconsistent directional restrictions
|
||||||
|
if (options.direction !== 'multi' && options.direction !== options.wheelDirection) {
|
||||||
|
options.wheelDirection = options.direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure positive values for deltas
|
||||||
|
options.scrollDelta = math.abs(parseFloat(options.scrollDelta));
|
||||||
|
options.wheelDelta = math.abs(parseFloat(options.wheelDelta));
|
||||||
|
|
||||||
|
// fix values for scroll offset
|
||||||
|
options.scrollLeft = options.scrollLeft === none ? null : math.abs(parseFloat(options.scrollLeft));
|
||||||
|
options.scrollTop = options.scrollTop === none ? null : math.abs(parseFloat(options.scrollTop));
|
||||||
|
|
||||||
|
return options;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the sizing information (bounding box) for the
|
||||||
|
// target DOM element
|
||||||
|
function getSizing(target) {
|
||||||
|
|
||||||
|
var $target = $(target),
|
||||||
|
width = $target.width(),
|
||||||
|
height = $target.height(),
|
||||||
|
scrollWidth = width >= target.scrollWidth ? width : target.scrollWidth,
|
||||||
|
scrollHeight = height >= target.scrollHeight ? height : target.scrollHeight,
|
||||||
|
hasScroll = scrollWidth > width || scrollHeight > height;
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: hasScroll,
|
||||||
|
container: {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
scrollWidth: scrollWidth,
|
||||||
|
scrollHeight: scrollHeight
|
||||||
|
},
|
||||||
|
thumbs: {
|
||||||
|
horizontal: {
|
||||||
|
width: width * width / scrollWidth,
|
||||||
|
height: settings.thumbThickness,
|
||||||
|
corner: settings.thumbThickness / 2,
|
||||||
|
left: 0,
|
||||||
|
top: height - settings.thumbThickness
|
||||||
|
},
|
||||||
|
vertical: {
|
||||||
|
width: settings.thumbThickness,
|
||||||
|
height: height * height / scrollHeight,
|
||||||
|
corner: settings.thumbThickness / 2,
|
||||||
|
left: width - settings.thumbThickness,
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to get (or implicitly creates) the
|
||||||
|
// remover function for the target passed
|
||||||
|
// in as an argument
|
||||||
|
function getRemover(target, orCreate) {
|
||||||
|
|
||||||
|
var $target = $(target), thumbs,
|
||||||
|
data = $target.data(datakey) || {},
|
||||||
|
style = $target.attr('style'),
|
||||||
|
fallback = orCreate ? function () {
|
||||||
|
|
||||||
|
data = $target.data(datakey);
|
||||||
|
thumbs = data.thumbs;
|
||||||
|
|
||||||
|
// restore original styles (if any)
|
||||||
|
if (style) {
|
||||||
|
$target.attr('style', style);
|
||||||
|
} else {
|
||||||
|
$target.removeAttr('style');
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any created thumbs
|
||||||
|
if (thumbs) {
|
||||||
|
if (thumbs.horizontal) { thumbs.horizontal.remove(); }
|
||||||
|
if (thumbs.vertical) { thumbs.vertical.remove(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any bound overscroll events and data
|
||||||
|
$target
|
||||||
|
.removeData(datakey)
|
||||||
|
.off(events.wheel, wheel)
|
||||||
|
.off(events.start, start)
|
||||||
|
.off(events.end, stop)
|
||||||
|
.off(events.ignored, ignore);
|
||||||
|
|
||||||
|
} : $.noop;
|
||||||
|
|
||||||
|
return $.isFunction(data.remover) ? data.remover : fallback;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Genterates CSS specific to a particular thumb.
|
||||||
|
// It requires sizing data and options
|
||||||
|
function getThumbCss(size, options) {
|
||||||
|
return {
|
||||||
|
position: 'absolute',
|
||||||
|
opacity: options.persistThumbs ? settings.thumbOpacity : 0,
|
||||||
|
'background-color': 'black',
|
||||||
|
width: size.width + 'px',
|
||||||
|
height: size.height + 'px',
|
||||||
|
'border-radius': size.corner + 'px',
|
||||||
|
'margin': size.top + 'px 0 0 ' + size.left + 'px',
|
||||||
|
'z-index': options.zIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the DOM elements used as "thumbs" within
|
||||||
|
// the target container.
|
||||||
|
function createThumbs(target, sizing, options) {
|
||||||
|
|
||||||
|
var div = '<div/>',
|
||||||
|
thumbs = {},
|
||||||
|
css = false;
|
||||||
|
|
||||||
|
if (sizing.container.scrollWidth > 0 && options.direction !== 'vertical') {
|
||||||
|
css = getThumbCss(sizing.thumbs.horizontal, options);
|
||||||
|
thumbs.horizontal = $(div).css(css).prependTo(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizing.container.scrollHeight > 0 && options.direction !== 'horizontal') {
|
||||||
|
css = getThumbCss(sizing.thumbs.vertical, options);
|
||||||
|
thumbs.vertical = $(div).css(css).prependTo(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbs.added = !!css;
|
||||||
|
|
||||||
|
return thumbs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignores events on the overscroll element
|
||||||
|
function ignore(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function takes a jQuery element, some
|
||||||
|
// (optional) options, and sets up event metadata
|
||||||
|
// for each instance the plug-in affects
|
||||||
|
function setup(target, options) {
|
||||||
|
|
||||||
|
// create initial data properties for this instance
|
||||||
|
options = getOptions(options);
|
||||||
|
var sizing = getSizing(target),
|
||||||
|
thumbs, data = {
|
||||||
|
options: options, sizing: sizing,
|
||||||
|
flags: { dragging: false },
|
||||||
|
remover: getRemover(target, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
// only apply handlers if the overscrolled element
|
||||||
|
// actually has an area to scroll
|
||||||
|
if (sizing.valid || options.ignoreSizing) {
|
||||||
|
// provide a circular-reference, enable events, and
|
||||||
|
// apply any required CSS
|
||||||
|
data.target = target = $(target).css({
|
||||||
|
position: 'relative',
|
||||||
|
cursor: compat.cursor.grab
|
||||||
|
}).on(events.start, data, start)
|
||||||
|
.on(events.end, data, stop)
|
||||||
|
.on(events.ignored, data, ignore);
|
||||||
|
|
||||||
|
// apply the stop listeners for drag end
|
||||||
|
if(options.dragHold) {
|
||||||
|
$(document).on(events.end, data, stop);
|
||||||
|
} else {
|
||||||
|
data.target.on(events.end, data, stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply any user-provided scroll offsets
|
||||||
|
if (options.scrollLeft !== null) {
|
||||||
|
target.scrollLeft(options.scrollLeft);
|
||||||
|
} if (options.scrollTop !== null) {
|
||||||
|
target.scrollTop(options.scrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use native oversroll, if it exists
|
||||||
|
if (compat.overflowScrolling) {
|
||||||
|
target.css(compat.overflowScrolling, 'touch');
|
||||||
|
} else {
|
||||||
|
target.on(events.scroll, data, scroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if the user would like mousewheel support
|
||||||
|
if (options.captureWheel) {
|
||||||
|
target.on(events.wheel, data, wheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add thumbs and listeners (if we're showing them)
|
||||||
|
if (options.showThumbs) {
|
||||||
|
if (compat.overflowScrolling) {
|
||||||
|
target.css('overflow', 'scroll');
|
||||||
|
} else {
|
||||||
|
target.css('overflow', 'hidden');
|
||||||
|
data.thumbs = thumbs = createThumbs(target, sizing, options);
|
||||||
|
if (thumbs.added) {
|
||||||
|
moveThumbs(thumbs, sizing, target.scrollLeft(), target.scrollTop());
|
||||||
|
if (options.hoverThumbs) {
|
||||||
|
target.on(events.hover, data, hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target.css('overflow', 'hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
target.data(datakey, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes any event listeners and other instance-specific
|
||||||
|
// data from the target. It attempts to leave the target
|
||||||
|
// at the state it found it.
|
||||||
|
function teardown(target) {
|
||||||
|
getRemover(target)();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the entry-point for enabling the plug-in;
|
||||||
|
// You can find it's exposure point at the end
|
||||||
|
// of this closure
|
||||||
|
function overscroll(options) {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
return this.removeOverscroll().each(function() {
|
||||||
|
setup(this, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the entry-point for disabling the plug-in;
|
||||||
|
// You can find it's exposure point at the end
|
||||||
|
// of this closure
|
||||||
|
function removeOverscroll() {
|
||||||
|
/*jshint validthis:true */
|
||||||
|
return this.each(function () {
|
||||||
|
teardown(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend overscroll to expose settings to the user
|
||||||
|
overscroll.settings = settings;
|
||||||
|
|
||||||
|
// Extend jQuery's prototype to expose the plug-in.
|
||||||
|
// If the supports native overflowScrolling, overscroll will not
|
||||||
|
// attempt to override the browser's built in support
|
||||||
|
$.extend(namespace, {
|
||||||
|
overscroll: overscroll,
|
||||||
|
removeOverscroll: removeOverscroll
|
||||||
|
});
|
||||||
|
|
||||||
|
})(window, document, navigator, Math, setTimeout, clearTimeout, jQuery.fn, jQuery);
|
|
@ -1,8 +1,5 @@
|
||||||
<div class="loading-status" loading="loading" has-error="!image">
|
<div class="resource-view" resource="image" error-message="'No image found'">
|
||||||
No image found
|
<div class="container repo repo-image-view">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container repo repo-image-view" ng-show="!loading && image">
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -11,13 +8,13 @@
|
||||||
<span style="color: #ccc">/</span>
|
<span style="color: #ccc">/</span>
|
||||||
<span style="color: #666;">{{repo.name}}</span>
|
<span style="color: #666;">{{repo.name}}</span>
|
||||||
<span style="color: #ccc">/</span>
|
<span style="color: #ccc">/</span>
|
||||||
<span>{{image.id.substr(0, 12)}}</span>
|
<span>{{image.value.id.substr(0, 12)}}</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Comment -->
|
<!-- Comment -->
|
||||||
<blockquote ng-show="image.comment">
|
<blockquote ng-show="image.value.comment">
|
||||||
<span class="markdown-view" content="image.comment"></span>
|
<span class="markdown-view" content="image.value.comment"></span>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
<!-- Information -->
|
<!-- Information -->
|
||||||
|
@ -27,7 +24,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="id-container">
|
<div class="id-container">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="full-id" type="text" class="form-control" value="{{ image.id }}" readonly>
|
<input id="full-id" type="text" class="form-control" value="{{ image.value.id }}" readonly>
|
||||||
<span id="copyClipboard" class="input-group-addon" title="Copy to Clipboard" data-clipboard-target="full-id">
|
<span id="copyClipboard" class="input-group-addon" title="Copy to Clipboard" data-clipboard-target="full-id">
|
||||||
<i class="fa fa-copy"></i>
|
<i class="fa fa-copy"></i>
|
||||||
</span>
|
</span>
|
||||||
|
@ -88,6 +85,5 @@
|
||||||
<div id="changes-tree-container" class="changes-container" onresize="tree && tree.notifyResized()"></div>
|
<div id="changes-tree-container" class="changes-container" onresize="tree && tree.notifyResized()"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<div class="loading" ng-show="loading || creating">
|
<div class="loading" ng-show="creating">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container create-org" ng-show="!loading && !creating">
|
<div class="container create-org" ng-show="!creating">
|
||||||
|
|
||||||
<div class="row header-row">
|
<div class="row header-row">
|
||||||
<div class="col-md-8 col-md-offset-1">
|
<div class="col-md-8 col-md-offset-1">
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
<div class="container" ng-show="user.anonymous">
|
<div class="container" ng-show="user.anonymous">
|
||||||
<h3>Please <a href="/signin/">sign in</a></h3>
|
<div class="col-sm-6 col-sm-offset-3">
|
||||||
|
<div class="user-setup" redirect-url="'/new/'"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!user.anonymous && building">
|
<div class="container" ng-show="!user.anonymous && building">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!user.anonymous && creating">
|
<div class="container" ng-show="!user.anonymous && creating">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" ng-show="!user.anonymous && uploading">
|
<div class="container" ng-show="!user.anonymous && uploading">
|
||||||
|
@ -75,7 +77,7 @@
|
||||||
In order to make this repository private, you’ll need to upgrade your plan from <b>{{ subscribedPlan.title }}</b> to <b>{{ planRequired.title }}</b>. This will cost $<span>{{ planRequired.price / 100 }}</span>/month.
|
In order to make this repository private, you’ll need to upgrade your plan from <b>{{ subscribedPlan.title }}</b> to <b>{{ planRequired.title }}</b>. This will cost $<span>{{ planRequired.price / 100 }}</span>/month.
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-primary" ng-click="upgradePlan()" ng-show="!planChanging">Upgrade now</a>
|
<a class="btn btn-primary" ng-click="upgradePlan()" ng-show="!planChanging">Upgrade now</a>
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="planChanging"></i>
|
<div class="quay-spinner" ng-show="planChanging"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && !isUserNamespace">
|
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && !isUserNamespace">
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
<div class="loading" ng-show="loading">
|
<div class="resource-view" resource="orgResource" error-message="'No organization found'"></div>
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="org-admin container" ng-show="organization">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" ng-show="!loading && !organization">
|
|
||||||
No matching organization found
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="org-admin container" ng-show="!loading && organization">
|
|
||||||
<div class="organization-header" organization="organization" clickable="true"></div>
|
<div class="organization-header" organization="organization" clickable="true"></div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -48,7 +41,7 @@
|
||||||
<!-- Billing History tab -->
|
<!-- Billing History tab -->
|
||||||
<div id="billing" class="tab-pane">
|
<div id="billing" class="tab-pane">
|
||||||
<div ng-show="invoiceLoading">
|
<div ng-show="invoiceLoading">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="!invoiceLoading && !invoices">
|
<div ng-show="!invoiceLoading && !invoices">
|
||||||
|
@ -105,8 +98,7 @@
|
||||||
|
|
||||||
<!-- Members tab -->
|
<!-- Members tab -->
|
||||||
<div id="members" class="tab-pane">
|
<div id="members" class="tab-pane">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x" ng-show="membersLoading"></i>
|
<div class="quay-spinner" ng-show="membersLoading"></div>
|
||||||
|
|
||||||
<div ng-show="!membersLoading">
|
<div ng-show="!membersLoading">
|
||||||
<div class="side-controls">
|
<div class="side-controls">
|
||||||
<div class="result-count">
|
<div class="result-count">
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
<div class="org-member-logs container" ng-show="loading">
|
<div class="resource-view" resource="memberResource" error-message="'Member not found'">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="org-member-logs container">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container" ng-show="!loading && !organization">
|
|
||||||
Organization not found
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container" ng-show="!loading && !memberInfo">
|
|
||||||
Member not found
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="org-member-logs container" ng-show="!loading && organization && memberInfo">
|
|
||||||
<div class="organization-header" organization="organization" clickable="true"></div>
|
<div class="organization-header" organization="organization" clickable="true"></div>
|
||||||
<div class="logs-view" organization="organization" performer="memberInfo" visible="organization && memberInfo && ready"></div>
|
<div class="logs-view" organization="organization" performer="memberInfo" visible="organization && memberInfo && ready"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
<div class="loading" ng-show="loading">
|
<div class="resource-view" resource="orgResource" error-message="'No matching organization found'">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="org-view container">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" ng-show="!loading && !organization">
|
|
||||||
No matching organization found
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="org-view container" ng-show="!loading && organization">
|
|
||||||
<div class="organization-header" organization="organization">
|
<div class="organization-header" organization="organization">
|
||||||
<div class="header-buttons" ng-show="organization.is_admin">
|
<div class="header-buttons" ng-show="organization.is_admin">
|
||||||
|
|
||||||
|
@ -49,6 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="container org-list conntent-container">
|
<div class="container org-list conntent-container">
|
||||||
<div class="loading" ng-show="loading">
|
<div class="loading" ng-show="!user">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="button-bar-right">
|
<div class="button-bar-right">
|
||||||
|
|
|
@ -129,7 +129,7 @@
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="admin-search">
|
<td class="admin-search">
|
||||||
<input type="text" class="form-control" placeholder="New token description" ng-model="newToken.friendlyName"required>
|
<input type="text" class="form-control" placeholder="New token description" ng-model="newToken.friendlyName" required>
|
||||||
</td>
|
</td>
|
||||||
<td class="admin-search">
|
<td class="admin-search">
|
||||||
<button type="submit" ng-disabled="createTokenForm.$invalid" class="btn btn-sm btn-default">Create</button>
|
<button type="submit" ng-disabled="createTokenForm.$invalid" class="btn btn-sm btn-default">Create</button>
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="URLs which will be invoked with an HTTP POST and JSON payload when a successful push to the repository occurs."></i>
|
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="URLs which will be invoked with an HTTP POST and JSON payload when a successful push to the repository occurs."></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-body" ng-form="newWebhookForm">
|
<div class="panel-body">
|
||||||
<div class="resource-view" resource="webhooksResource" error-message="'Could not load webhooks'">
|
<div class="resource-view" resource="webhooksResource" error-message="'Could not load webhooks'">
|
||||||
<table class="permissions">
|
<table class="permissions">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -168,17 +168,24 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form name="createWebhookForm" ng-submit="createWebhook()">
|
||||||
|
<table class="permissions">
|
||||||
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td style="width: 500px;">
|
||||||
<input type="url" class="form-control" placeholder="New webhook url..." ng-model="newWebhook.url" required>
|
<input type="url" class="form-control" placeholder="New webhook url..." ng-model="newWebhook.url" required>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-primary" type="submit" ng-click="createWebhook()">Create</button>
|
<button class="btn btn-primary" type="submit" ng-disabled="createWebhookForm.$invalid">Create</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
<div class="right-info">
|
<div class="right-info">
|
||||||
Quay will <b>POST</b> to these webhooks whenever a push occurs. See the <a href="/guide">User Guide</a> for more information.
|
Quay will <b>POST</b> to these webhooks whenever a push occurs. See the <a href="/guide">User Guide</a> for more information.
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
<div class="loading" ng-show="loading">
|
<div class="resource-view" resource="orgResource" error-message="'No matching organization'">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="team-view container">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" ng-show="!loading && !organization">
|
|
||||||
No matching team found
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="team-view container" ng-show="!loading && organization">
|
|
||||||
<div class="organization-header" organization="organization" team-name="teamname"></div>
|
<div class="organization-header" organization="organization" team-name="teamname"></div>
|
||||||
|
|
||||||
|
<div class="resource-view" resource="membersResource" error-message="'No matching team found'">
|
||||||
<div class="description markdown-input" content="team.description" can-write="organization.is_admin"
|
<div class="description markdown-input" content="team.description" can-write="organization.is_admin"
|
||||||
content-changed="updateForDescription" field-title="'team description'"></div>
|
content-changed="updateForDescription" field-title="'team description'"></div>
|
||||||
|
|
||||||
|
@ -39,9 +33,10 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Modal message dialog -->
|
<!-- Modal message dialog -->
|
||||||
<div class="modal fade" id="cannotChangeTeamModal">
|
<div class="modal fade" id="cannotChangeTeamModal">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<div class="loading" ng-show="loading">
|
<div class="loading" ng-show="!user">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="loading" ng-show="!loading && !user">
|
<div class="loading" ng-show="user.anonymous">
|
||||||
No matching user found
|
No matching user found
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-admin container" ng-show="!loading && user">
|
<div class="user-admin container" ng-show="!user.anonymous">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="organization-header-element">
|
<div class="organization-header-element">
|
||||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon">
|
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=24&d=identicon">
|
||||||
|
@ -45,20 +45,23 @@
|
||||||
<!-- Change password tab -->
|
<!-- Change password tab -->
|
||||||
<div id="password" class="tab-pane">
|
<div id="password" class="tab-pane">
|
||||||
<div class="loading" ng-show="updatingUser">
|
<div class="loading" ng-show="updatingUser">
|
||||||
<i class="fa fa-spinner fa-spin fa-3x"></i>
|
<div class="quay-spinner 3x"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
||||||
|
|
||||||
|
<div ng-show="!updatingUser">
|
||||||
<form class="form-change-pw col-md-6" name="changePasswordForm" ng-submit="changePassword()" data-trigger="manual"
|
<form class="form-change-pw col-md-6" name="changePasswordForm" ng-submit="changePassword()" data-trigger="manual"
|
||||||
data-content="{{ changePasswordError }}" data-placement="right" ng-show="!awaitingConfirmation && !registering">
|
data-content="{{ changePasswordError }}" data-placement="right" ng-show="!awaitingConfirmation && !registering">
|
||||||
<input type="password" class="form-control" placeholder="Your new password" ng-model="user.password" required>
|
<input type="password" class="form-control" placeholder="Your new password" ng-model="user.password" required>
|
||||||
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="user.repeatPassword"
|
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="user.repeatPassword"
|
||||||
match="user.password" required>
|
match="user.password" required>
|
||||||
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit"
|
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit"
|
||||||
analytics-on analytics-event="register">Change Password</button>
|
analytics-on analytics-event="change-pass">Change Password</button>
|
||||||
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Robot accounts tab -->
|
<!-- Robot accounts tab -->
|
||||||
<div id="robots" class="tab-pane">
|
<div id="robots" class="tab-pane">
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
<div id="buildInfoBox" class="status-box" ng-show="repo.is_building"
|
<div id="buildInfoBox" class="status-box" ng-show="repo.is_building"
|
||||||
bs-popover="'static/partials/build-status-item.html'" data-placement="bottom">
|
bs-popover="'static/partials/build-status-item.html'" data-placement="bottom">
|
||||||
<span class="title">
|
<span class="title">
|
||||||
<i class="fa fa-spinner fa-spin"></i>
|
<span class="quay-spinner"></span>
|
||||||
<b>Building Images</b>
|
<b>Building Images</b>
|
||||||
</span>
|
</span>
|
||||||
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '-' }}</span></span>
|
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '-' }}</span></span>
|
||||||
|
|
Reference in a new issue