/** * An element which displays the changes visualization panel for a repository view. */ angular.module('quay').directive('repoPanelChanges', function () { var RepositoryImageTracker = function(repository, images) { this.repository = repository; this.images = images; // Build a map of image ID -> image. var imageIDMap = {}; this.images.map(function(image) { imageIDMap[image.id] = image; }); this.imageMap_ = imageIDMap; }; RepositoryImageTracker.prototype.imageLink = function(image) { return '/repository/' + this.repository.namespace + '/' + this.repository.name + '/image/' + image; }; RepositoryImageTracker.prototype.getImageForTag = function(tag) { var tagData = this.lookupTag(tag); if (!tagData) { return null; } return this.imageMap_[tagData.image_id]; }; RepositoryImageTracker.prototype.lookupTag = function(tag) { return this.repository.tags[tag]; }; RepositoryImageTracker.prototype.lookupImage = function(image) { return this.imageMap_[image]; }; RepositoryImageTracker.prototype.forAllTagImages = function(tag, callback) { var tagData = this.lookupTag(tag); if (!tagData) { return; } var tagImage = this.imageMap_[tagData.image_id]; if (!tagImage) { return; } // Callback the tag's image itself. callback(tagImage); // Callback any parent images. if (!tagImage.ancestors) { return; } var ancestors = tagImage.ancestors.split('/'); for (var i = 0; i < ancestors.length; ++i) { var image = this.imageMap_[ancestors[i]]; if (image) { callback(image); } } }; RepositoryImageTracker.prototype.getTotalSize = function(tag) { var size = 0; this.forAllTagImages(tag, function(image) { size += image.size; }); return size; }; RepositoryImageTracker.prototype.getImagesForTagBySize = function(tag) { var images = []; this.forAllTagImages(tag, function(image) { images.push(image); }); images.sort(function(a, b) { return b.size - a.size; }); return images; }; /////////////////////////////////////////////////////////////////////////////////////// var directiveDefinitionObject = { priority: 0, templateUrl: '/static/directives/repo-view/repo-panel-changes.html', replace: false, transclude: false, restrict: 'C', scope: { 'repository': '=repository' }, controller: function($scope, $element, $location, $timeout, ApiService, UtilService, ImageMetadataService) { var update = function() { if (!$scope.repository) { return; } var tagString = $location.search()['tags'] || ''; if (!tagString) { $scope.selectedTags = []; return; } $scope.currentImage = null; $scope.currentImage = null; $scope.selectedTags = tagString.split(','); if (!$scope.imageResource) { loadImages(); } else { refreshTree(); } }; $scope.$on('$routeUpdate', update); $scope.$watch('repository', update); var refreshTree = function() { if (!$scope.repository || !$scope.images) { return; } $('#image-history-container').empty(); var tree = new ImageHistoryTree( $scope.repository.namespace, $scope.repository.name, $scope.images, UtilService.getFirstMarkdownLineAsText, $scope.getTimeSince, ImageMetadataService.getEscapedFormattedCommand, function(tag) { return $.inArray(tag, $scope.selectedTags) >= 0; }); $scope.tree = tree.draw('image-history-container'); if ($scope.tree) { // Give enough time for the UI to be drawn before we resize the tree. $timeout(function() { $scope.tree.notifyResized(); }, 100); // Listen for changes to the selected tag and image in the tree. $($scope.tree).bind('tagChanged', function(e) { $scope.$apply(function() { $scope.setTag(e.tag); }); }); $($scope.tree).bind('imageChanged', function(e) { $scope.$apply(function() { $scope.setImage(e.image.id); }); }); } }; var loadImages = function(opt_callback) { var params = { 'repository': $scope.repository.namespace + '/' + $scope.repository.name }; $scope.imagesResource = ApiService.listRepositoryImagesAsResource(params).get(function(resp) { $scope.images = resp.images; $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images); $scope.selectedTags = $.grep($scope.selectedTags, function(tag) { return !!$scope.tracker.lookupTag(tag); }); if ($scope.selectedTags && $scope.selectedTags.length) { refreshTree(); } opt_callback && opt_callback(); }); }; $scope.setImage = function(image_id) { $scope.currentTag = null; $scope.currentImage = image_id; $scope.tree.setImage(image_id); }; $scope.setTag = function(tag) { $scope.currentTag = tag; $scope.currentImage = null; $scope.tree.setTag(tag); }; $scope.parseDate = function(dateString) { return Date.parse(dateString); }; $scope.getTimeSince = function(createdTime) { return moment($scope.parseDate(createdTime)).fromNow(); }; $scope.handleTagChanged = function(data) { $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.images); data.removed.map(function(tag) { $scope.selectedTags = $.grep($scope.selectedTags, function(cTag) { return cTag != tag; }); if ($scope.selectedTags.length) { $location.search('tags', $scope.selectedTags.join(',')); } else { $location.search('tags', null); } $scope.currentImage = null; $scope.currentTag = null; }); data.added.map(function(tag) { $scope.selectedTags.push(tag); $location.search('tags', $scope.selectedTags.join(',')); $scope.currentTag = tag; }); }; } }; return directiveDefinitionObject; });