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:
Joseph Schorr 2015-04-22 13:16:10 -04:00
parent d4b593cada
commit 31389b9974
5 changed files with 151 additions and 169 deletions

View file

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

View file

@ -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() { $scope.isClosed = true;
var tabs = $element.find('a[data-toggle="tab"]'); });
tabs.on('shown.bs.tab', function (e) {
$scope.isClosed = true;
});
}, 100);
} }
}; };
return directiveDefinitionObject; return directiveDefinitionObject;

View file

@ -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); if (!inter) {
var result = inter(page.scope) return null;
}
if (previous && result != previous) { return inter(page.scope);
$interval.cancel(currentInterval);
}
previous = result;
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;

View file

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

View file

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