Fix CPU issues by removing constant digesting. We do so by:
- Fixing the title and description (meta) to only respond to a rootScope watch, rather than using a timer - Change the tabs listening code to be completely self contained
This commit is contained in:
parent
d4b593cada
commit
31389b9974
5 changed files with 151 additions and 169 deletions
107
static/js/app.js
107
static/js/app.js
|
@ -232,10 +232,10 @@ if (window.__config && window.__config.SENTRY_PUBLIC_DSN) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application.
|
// Run the application.
|
||||||
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService', 'MetaService',
|
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService', 'MetaService', 'UIService',
|
||||||
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService, MetaService) {
|
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService, MetaService, UIService) {
|
||||||
|
|
||||||
var title = window.__config['REGISTRY_TITLE'] || 'Quay.io';
|
var defaultTitle = window.__config['REGISTRY_TITLE'] || 'Quay.io';
|
||||||
|
|
||||||
// Handle session security.
|
// Handle session security.
|
||||||
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
|
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
|
||||||
|
@ -273,38 +273,6 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var changeTab = function(activeTab, opt_timeout) {
|
|
||||||
var checkCount = 0;
|
|
||||||
$timeout(function() {
|
|
||||||
if (checkCount > 5) { return; }
|
|
||||||
checkCount++;
|
|
||||||
|
|
||||||
$('a[data-toggle="tab"]').each(function(index) {
|
|
||||||
var tabName = this.getAttribute('data-target').substr(1);
|
|
||||||
if (tabName != activeTab) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.clientWidth == 0) {
|
|
||||||
changeTab(activeTab, 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UtilService.clickElement(this);
|
|
||||||
});
|
|
||||||
}, opt_timeout);
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetDefaultTab = function() {
|
|
||||||
$timeout(function() {
|
|
||||||
$('a[data-toggle="tab"]').each(function(index) {
|
|
||||||
if (index == 0) {
|
|
||||||
UtilService.clickElement(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$rootScope.$watch('description', function(description) {
|
$rootScope.$watch('description', function(description) {
|
||||||
if (!description) {
|
if (!description) {
|
||||||
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
|
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
|
||||||
|
@ -316,11 +284,16 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
|
||||||
$('#descriptionTag').attr('content', description);
|
$('#descriptionTag').attr('content', description);
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on('$routeUpdate', function(){
|
// Listen for scope changes and update the title and description accordingly.
|
||||||
if ($location.search()['tab']) {
|
$rootScope.$watch(function() {
|
||||||
changeTab($location.search()['tab']);
|
var title = MetaService.getTitle($rootScope.currentPage) || defaultTitle;
|
||||||
} else {
|
if ($rootScope.title != title) {
|
||||||
resetDefaultTab();
|
$rootScope.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
var description = MetaService.getDescription($rootScope.currentPage) || '';
|
||||||
|
if ($rootScope.description != description) {
|
||||||
|
$rootScope.description = description;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -336,63 +309,9 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
|
||||||
$rootScope.newLayout = !!current.$$route.newLayout;
|
$rootScope.newLayout = !!current.$$route.newLayout;
|
||||||
$rootScope.fixFooter = !!current.$$route.fixFooter;
|
$rootScope.fixFooter = !!current.$$route.fixFooter;
|
||||||
|
|
||||||
MetaService.getInitialTitle(current, function(title) {
|
|
||||||
$rootScope.title = title;
|
|
||||||
});
|
|
||||||
|
|
||||||
MetaService.getInitialDescription(current, function(description) {
|
|
||||||
$rootScope.description = description
|
|
||||||
});
|
|
||||||
|
|
||||||
$anchorScroll();
|
$anchorScroll();
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on('$viewContentLoaded', function(event) {
|
|
||||||
var current = $rootScope.currentPage;
|
|
||||||
|
|
||||||
MetaService.getTitle(current, function(title) {
|
|
||||||
$rootScope.title = title;
|
|
||||||
});
|
|
||||||
|
|
||||||
MetaService.getDescription(current, function(description) {
|
|
||||||
$rootScope.description = description;
|
|
||||||
});
|
|
||||||
|
|
||||||
var activeTab = $location.search()['tab'];
|
|
||||||
var checkTabs = function() {
|
|
||||||
var tabs = $('a[data-toggle="tab"]');
|
|
||||||
if (tabs.length == 0) {
|
|
||||||
$timeout(checkTabs, 50);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabs.on('shown.bs.tab', function (e) {
|
|
||||||
var tabName = e.target.getAttribute('data-target').substr(1);
|
|
||||||
$rootScope.$apply(function() {
|
|
||||||
var isDefaultTab = $('a[data-toggle="tab"]')[0] == e.target;
|
|
||||||
var newSearch = $.extend($location.search(), {});
|
|
||||||
if (isDefaultTab) {
|
|
||||||
delete newSearch['tab'];
|
|
||||||
} else {
|
|
||||||
newSearch['tab'] = tabName;
|
|
||||||
}
|
|
||||||
|
|
||||||
$location.search(newSearch);
|
|
||||||
});
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (activeTab) {
|
|
||||||
changeTab(activeTab);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
|
|
||||||
// is changed in the UI.
|
|
||||||
$timeout(checkTabs, 50);
|
|
||||||
});
|
|
||||||
|
|
||||||
var initallyChecked = false;
|
var initallyChecked = false;
|
||||||
window.__isLoading = function() {
|
window.__isLoading = function() {
|
||||||
if (!initallyChecked) {
|
if (!initallyChecked) {
|
||||||
|
|
|
@ -243,7 +243,7 @@ angular.module("core-ui", [])
|
||||||
transclude: true,
|
transclude: true,
|
||||||
restrict: 'C',
|
restrict: 'C',
|
||||||
scope: {},
|
scope: {},
|
||||||
controller: function($rootScope, $scope, $element, $timeout) {
|
controller: function($rootScope, $scope, $element, $timeout, $location, UIService) {
|
||||||
$scope.isClosed = true;
|
$scope.isClosed = true;
|
||||||
|
|
||||||
$scope.toggleClosed = function(e) {
|
$scope.toggleClosed = function(e) {
|
||||||
|
@ -252,13 +252,9 @@ angular.module("core-ui", [])
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add a listener to auto-close the tabs when a new tab is clicked.
|
UIService.initializeTabs($scope, $element, function() {
|
||||||
$timeout(function() {
|
|
||||||
var tabs = $element.find('a[data-toggle="tab"]');
|
|
||||||
tabs.on('shown.bs.tab', function (e) {
|
|
||||||
$scope.isClosed = true;
|
$scope.isClosed = true;
|
||||||
});
|
});
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return directiveDefinitionObject;
|
return directiveDefinitionObject;
|
||||||
|
|
|
@ -4,72 +4,36 @@
|
||||||
angular.module('quay').factory('MetaService', ['$interpolate', 'Config', '$rootScope', '$interval',
|
angular.module('quay').factory('MetaService', ['$interpolate', 'Config', '$rootScope', '$interval',
|
||||||
function($interpolate, Config, $rootScope, $interval) {
|
function($interpolate, Config, $rootScope, $interval) {
|
||||||
var metaService = {};
|
var metaService = {};
|
||||||
var intervals = [];
|
|
||||||
|
|
||||||
var interpolate = function(page, expr, callback) {
|
var interpolate = function(page, expr) {
|
||||||
var previous = '';
|
if (!expr) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var currentInterval = $interval(function() {
|
|
||||||
var inter = $interpolate(expr, true, null, true);
|
var inter = $interpolate(expr, true, null, true);
|
||||||
var result = inter(page.scope)
|
if (!inter) {
|
||||||
|
return null;
|
||||||
if (previous && result != previous) {
|
|
||||||
$interval.cancel(currentInterval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
previous = result;
|
return inter(page.scope);
|
||||||
callback(result);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
intervals.push(currentInterval);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var initial = function(value, default_value, callback) {
|
metaService.getTitle = function(page) {
|
||||||
for (var i = 0; i < intervals.length; ++i) {
|
if (!page || !page.$$route) {
|
||||||
$interval.cancel(intervals[i]);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
intervals = [];
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
callback(default_value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.indexOf('{{') < 0) {
|
|
||||||
callback(default_value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback('Loading...');
|
|
||||||
};
|
|
||||||
|
|
||||||
metaService.getInitialTitle = function(page, callback) {
|
|
||||||
var route = page.$$route;
|
var route = page.$$route;
|
||||||
initial(route && route.title, Config.REGISTRY_TITLE_SHORT, callback);
|
return interpolate(page, route && route.title);
|
||||||
};
|
};
|
||||||
|
|
||||||
metaService.getInitialDescription = function(page, callback) {
|
metaService.getDescription = function(page) {
|
||||||
var route = page.$$route;
|
if (!page || !page.$$route) {
|
||||||
initial(route && route.description, Config.REGISTRY_TITLE_SHORT, callback);
|
return null;
|
||||||
};
|
|
||||||
|
|
||||||
metaService.getTitle = function(page, callback) {
|
|
||||||
var route = page.$$route;
|
|
||||||
if (!route || !route.title || route.title.indexOf('{{') < 0) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolate(page, route.title, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
metaService.getDescription = function(page, callback) {
|
|
||||||
var route = page.$$route;
|
var route = page.$$route;
|
||||||
if (!route || !route.description || route.description.indexOf('{{') < 0) {
|
return interpolate(route && route.description);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
interpolate(page, route.description, callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return metaService;
|
return metaService;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Service which provides helper methods for performing some simple UI operations.
|
* Service which provides helper methods for performing some simple UI operations.
|
||||||
*/
|
*/
|
||||||
angular.module('quay').factory('UIService', [function() {
|
angular.module('quay').factory('UIService', ['$timeout', '$rootScope', '$location', function($timeout, $rootScope, $location) {
|
||||||
var CheckStateController = function(items, opt_checked) {
|
var CheckStateController = function(items, opt_checked) {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.checked = opt_checked || [];
|
this.checked = opt_checked || [];
|
||||||
|
@ -134,5 +134,121 @@ angular.module('quay').factory('UIService', [function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uiService.clickElement = function(el) {
|
||||||
|
// From: http://stackoverflow.com/questions/16802795/click-not-working-in-mocha-phantomjs-on-certain-elements
|
||||||
|
var ev = document.createEvent("MouseEvent");
|
||||||
|
ev.initMouseEvent(
|
||||||
|
"click",
|
||||||
|
true /* bubble */, true /* cancelable */,
|
||||||
|
window, null,
|
||||||
|
0, 0, 0, 0, /* coordinates */
|
||||||
|
false, false, false, false, /* modifier keys */
|
||||||
|
0 /*left*/, null);
|
||||||
|
el.dispatchEvent(ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
uiService.initializeTabs = function(scope, element, opt_clickCallback) {
|
||||||
|
var locationListener = null;
|
||||||
|
var disposed = false;
|
||||||
|
|
||||||
|
var changeTab = function(activeTab) {
|
||||||
|
if (disposed) { return; }
|
||||||
|
|
||||||
|
$('a[data-toggle="tab"]').each(function(index) {
|
||||||
|
var tabName = this.getAttribute('data-target').substr(1);
|
||||||
|
if (tabName != activeTab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($(this).parent().hasClass('active')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.clientWidth == 0) {
|
||||||
|
setTimeout(function() {
|
||||||
|
changeTab(activeTab);
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiService.clickElement(this);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var resetDefaultTab = function() {
|
||||||
|
if (disposed) { return; }
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
element.find('a[data-toggle="tab"]').each(function(index) {
|
||||||
|
if (index == 0) {
|
||||||
|
uiService.clickElement(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkTabs = function() {
|
||||||
|
if (disposed) { return; }
|
||||||
|
|
||||||
|
// Poll until we find the tabs.
|
||||||
|
var tabs = element.find('a[data-toggle="tab"]');
|
||||||
|
if (tabs.length == 0) {
|
||||||
|
$timeout(checkTabs, 50);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register listeners.
|
||||||
|
registerListeners(tabs);
|
||||||
|
|
||||||
|
// Set the active tab (if any).
|
||||||
|
var activeTab = $location.search()['tab'];
|
||||||
|
if (activeTab) {
|
||||||
|
changeTab(activeTab);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var registerListeners = function(tabs) {
|
||||||
|
// Listen for scope destruction.
|
||||||
|
scope.$on('$destroy', function() {
|
||||||
|
dispoed = true;
|
||||||
|
locationListener && locationListener();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for route changes and update the tabs accordingly.
|
||||||
|
locationListener = $rootScope.$on('$routeUpdate', function(){
|
||||||
|
if ($location.search()['tab']) {
|
||||||
|
changeTab($location.search()['tab']);
|
||||||
|
} else {
|
||||||
|
resetDefaultTab();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for tab changes.
|
||||||
|
tabs.on('shown.bs.tab', function (e) {
|
||||||
|
// Invoke the callback, if any.
|
||||||
|
opt_clickCallback && opt_clickCallback();
|
||||||
|
|
||||||
|
// Update the search location.
|
||||||
|
var tabName = e.target.getAttribute('data-target').substr(1);
|
||||||
|
$rootScope.$apply(function() {
|
||||||
|
var isDefaultTab = tabs[0] == e.target;
|
||||||
|
var newSearch = $.extend($location.search(), {});
|
||||||
|
if (isDefaultTab) {
|
||||||
|
delete newSearch['tab'];
|
||||||
|
} else {
|
||||||
|
newSearch['tab'] = tabName;
|
||||||
|
}
|
||||||
|
|
||||||
|
$location.search(newSearch);
|
||||||
|
});
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start the checkTabs timer.
|
||||||
|
checkTabs();
|
||||||
|
};
|
||||||
|
|
||||||
return uiService;
|
return uiService;
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -76,18 +76,5 @@ angular.module('quay').factory('UtilService', ['$sanitize', function($sanitize)
|
||||||
return $sanitize(utilService.escapeHtmlString(text));
|
return $sanitize(utilService.escapeHtmlString(text));
|
||||||
};
|
};
|
||||||
|
|
||||||
utilService.clickElement = function(el) {
|
|
||||||
// From: http://stackoverflow.com/questions/16802795/click-not-working-in-mocha-phantomjs-on-certain-elements
|
|
||||||
var ev = document.createEvent("MouseEvent");
|
|
||||||
ev.initMouseEvent(
|
|
||||||
"click",
|
|
||||||
true /* bubble */, true /* cancelable */,
|
|
||||||
window, null,
|
|
||||||
0, 0, 0, 0, /* coordinates */
|
|
||||||
false, false, false, false, /* modifier keys */
|
|
||||||
0 /*left*/, null);
|
|
||||||
el.dispatchEvent(ev);
|
|
||||||
};
|
|
||||||
|
|
||||||
return utilService;
|
return utilService;
|
||||||
}]);
|
}]);
|
||||||
|
|
Reference in a new issue