mirror of https://github.com/portainer/portainer
commit
9eaa75a572
|
@ -6,10 +6,11 @@ MAINTAINER Michael Crosby http://crosbymichael.com
|
|||
|
||||
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
||||
RUN apt-get update
|
||||
RUN apt-get upgrade
|
||||
RUN apt-get upgrade -y
|
||||
|
||||
ADD . /app/
|
||||
RUN ln -s /app/dockerui /dockerui
|
||||
|
||||
EXPOSE 9000
|
||||
WORKDIR /app/
|
||||
|
||||
ENTRYPOINT ["./dockerui"]
|
||||
|
|
|
@ -109,3 +109,10 @@
|
|||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.legend .title {
|
||||
margin: 0.5em;
|
||||
border-style: solid;
|
||||
border-width: 0 0 0 1em;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
endpoint = flag.String("e", "", "Docker d endpoint.")
|
||||
verbose = flag.Bool("v", false, "Verbose logging.")
|
||||
port = flag.String("p", "9000", "Port to serve dockerui.")
|
||||
assets = flag.String("-a", "/app", "Path to the assets.")
|
||||
endpoint = flag.String("e", "", "Dockerd endpoint")
|
||||
verbose = flag.Bool("v", false, "Verbose logging")
|
||||
port = flag.String("p", "9000", "Port to serve dockerui")
|
||||
assets = flag.String("a", ".", "Path to the assets")
|
||||
)
|
||||
|
||||
type multiHandler struct {
|
||||
|
@ -34,6 +34,7 @@ func (h *multiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func isDockerRequest(url string) bool {
|
||||
log.Printf("Got request: %s", url)
|
||||
return strings.Contains(url, "dockerapi/")
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 B |
16
index.html
16
index.html
|
@ -7,10 +7,11 @@
|
|||
<meta name="description" content="">
|
||||
<meta name="author" content="Michael Crosby crosbymichael.com">
|
||||
|
||||
<link href="../assets/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="../assets/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="assets/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="assets/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="lib/jquery.gritter.css" rel="stylesheet">
|
||||
|
||||
<link href="../css/app.css" rel="stylesheet">
|
||||
<link href="css/app.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
|
@ -29,7 +30,6 @@
|
|||
|
||||
<div class="container">
|
||||
<div ng-include="template" ng-controller="MastheadController"></div>
|
||||
<div ng-include="template" ng-controller="MessageController"></div>
|
||||
|
||||
<div id="view" ng-view></div>
|
||||
|
||||
|
@ -51,12 +51,16 @@
|
|||
<script src="../assets/js/bootstrap-carousel.js"></script>
|
||||
<script src="../assets/js/bootstrap-typeahead.js"></script>
|
||||
|
||||
<script src="/lib/ace-builds/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="/lib/spin.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="lib/ace-builds/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="lib/spin.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="lib/angular/angular-resource.js"></script>
|
||||
|
||||
<script src="lib/jquery.gritter.min.js"></script>
|
||||
<script src="lib/Chart.min.js"></script>
|
||||
<script src="lib/legend.js"></script>
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
<script src="js/filters.js"></script>
|
||||
|
|
|
@ -14,5 +14,5 @@ angular.module('dockerui', ['dockerui.services', 'dockerui.filters'])
|
|||
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
||||
.constant('DOCKER_ENDPOINT', '/dockerapi')
|
||||
.constant('DOCKER_PORT', '') // Docker port, leave as an empty string if no port is requred. If you have a port, prefix it with a ':' i.e. :4243
|
||||
.constant('UI_VERSION', 'v0.2')
|
||||
.constant('DOCKER_API_VERSION', 'v1.2');
|
||||
.constant('UI_VERSION', 'v0.3')
|
||||
.constant('DOCKER_API_VERSION', 'v1.4');
|
||||
|
|
|
@ -3,22 +3,125 @@ function MastheadController($scope) {
|
|||
$scope.template = 'partials/masthead.html';
|
||||
}
|
||||
|
||||
function DashboardController($scope, Container) {
|
||||
function newLineChart(id, data, getkey) {
|
||||
var chart = getChart(id);
|
||||
var map = {};
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = data[i];
|
||||
var key = getkey(c);
|
||||
|
||||
var count = map[key];
|
||||
if (count === undefined) {
|
||||
count = 0;
|
||||
}
|
||||
count += 1;
|
||||
map[key] = count;
|
||||
}
|
||||
|
||||
var labels = [];
|
||||
var data = [];
|
||||
var keys = Object.keys(map);
|
||||
|
||||
for (var i = keys.length - 1; i > -1; i--) {
|
||||
var k = keys[i];
|
||||
labels.push(k);
|
||||
data.push(map[k]);
|
||||
}
|
||||
var dataset = {
|
||||
fillColor : "rgba(151,187,205,0.5)",
|
||||
strokeColor : "rgba(151,187,205,1)",
|
||||
pointColor : "rgba(151,187,205,1)",
|
||||
pointStrokeColor : "#fff",
|
||||
data : data
|
||||
};
|
||||
chart.Line({
|
||||
labels: labels,
|
||||
datasets: [dataset]
|
||||
},
|
||||
{
|
||||
scaleStepWidth: 1,
|
||||
pointDotRadius:1,
|
||||
scaleOverride: true,
|
||||
scaleSteps: labels.length
|
||||
});
|
||||
}
|
||||
|
||||
function MessageController($scope, Messages) {
|
||||
$scope.template = 'partials/messages.html';
|
||||
$scope.messages = [];
|
||||
$scope.$watch('messages.length', function(o, n) {
|
||||
$('#message-display').show();
|
||||
});
|
||||
function DashboardController($scope, Container, Image, Settings) {
|
||||
$scope.predicate = '-Created';
|
||||
$scope.containers = [];
|
||||
|
||||
$scope.$on(Messages.event, function(e, msg) {
|
||||
$scope.messages.push(msg);
|
||||
setTimeout(function() {
|
||||
$('#message-display').hide('slow');
|
||||
}, 30000);
|
||||
});
|
||||
var getStarted = function(data) {
|
||||
$scope.totalContainers = data.length;
|
||||
newLineChart('#containers-started-chart', data, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
var s = $scope;
|
||||
Image.query({}, function(d) {
|
||||
s.totalImages = d.length;
|
||||
newLineChart('#images-created-chart', d, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {animation:false};
|
||||
if (Settings.firstLoad) {
|
||||
$('#stats').hide();
|
||||
opts.animation = true;
|
||||
Settings.firstLoad = false;
|
||||
$('#masthead').show();
|
||||
|
||||
setTimeout(function() {
|
||||
$('#masthead').slideUp('slow');
|
||||
$('#stats').slideDown('slow');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
Container.query({all: 1}, function(d) {
|
||||
var running = 0
|
||||
var ghost = 0;
|
||||
var stopped = 0;
|
||||
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var item = d[i];
|
||||
|
||||
if (item.Status === "Ghost") {
|
||||
ghost += 1;
|
||||
} else if (item.Status.indexOf('Exit') !== -1) {
|
||||
stopped += 1;
|
||||
} else {
|
||||
running += 1;
|
||||
$scope.containers.push(new ContainerViewModel(item));
|
||||
}
|
||||
}
|
||||
|
||||
getStarted(d);
|
||||
|
||||
var c = getChart('#containers-chart');
|
||||
var data = [
|
||||
{
|
||||
value: running,
|
||||
color: '#5bb75b',
|
||||
title: 'Running'
|
||||
}, // running
|
||||
{
|
||||
value: stopped,
|
||||
color: '#C7604C',
|
||||
title: 'Stopped'
|
||||
}, // stopped
|
||||
{
|
||||
value: ghost,
|
||||
color: '#E2EAE9',
|
||||
title: 'Ghost'
|
||||
} // ghost
|
||||
];
|
||||
|
||||
c.Doughnut(data, opts);
|
||||
var lgd = $('#chart-legend').get(0);
|
||||
legend(lgd, data);
|
||||
});
|
||||
}
|
||||
|
||||
function getChart(id) {
|
||||
var ctx = $(id).get(0).getContext("2d");
|
||||
return new Chart(ctx);
|
||||
}
|
||||
|
||||
function StatusBarController($scope, Settings) {
|
||||
|
@ -53,46 +156,34 @@ function ContainerController($scope, $routeParams, $location, Container, Message
|
|||
$scope.changes = [];
|
||||
|
||||
$scope.start = function(){
|
||||
ViewSpinner.spin();
|
||||
Container.start({id: $routeParams.id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Container started.'});
|
||||
ViewSpinner.stop();
|
||||
Messages.send("Container started", $routeParams.id);
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
ViewSpinner.stop();
|
||||
Messages.error("Failure", "Container failed to start." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.stop = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.stop({id: $routeParams.id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Container stopped.'});
|
||||
ViewSpinner.stop();
|
||||
Messages.send("Container stopped", $routeParams.id);
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
ViewSpinner.stop();
|
||||
Messages.error("Failure", "Container failed to stop." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.kill = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.kill({id: $routeParams.id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Container killed.'});
|
||||
ViewSpinner.stop();
|
||||
Messages.send("Container killed", $routeParams.id);
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
ViewSpinner.stop();
|
||||
Messages.error("Failure", "Container failed to die." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
ViewSpinner.spin();
|
||||
Container.remove({id: $routeParams.id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Container removed.'});
|
||||
ViewSpinner.stop();
|
||||
Messages.send("Container removed", $routeParams.id);
|
||||
}, function(e){
|
||||
failedRequestHandler(e, Messages);
|
||||
ViewSpinner.stop();
|
||||
Messages.error("Failure", "Container failed to remove." + e.data);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -108,12 +199,14 @@ function ContainerController($scope, $routeParams, $location, Container, Message
|
|||
|
||||
Container.get({id: $routeParams.id}, function(d) {
|
||||
$scope.container = d;
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
}, function(e) {
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
Messages.error("Not found", "Container not found.");
|
||||
} else {
|
||||
Messages.error("Failure", e.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.getChanges();
|
||||
}
|
||||
|
@ -132,7 +225,7 @@ function ContainersController($scope, Container, Settings, Messages, ViewSpinner
|
|||
});
|
||||
};
|
||||
|
||||
var batch = function(items, action) {
|
||||
var batch = function(items, action, msg) {
|
||||
ViewSpinner.spin();
|
||||
var counter = 0;
|
||||
var complete = function() {
|
||||
|
@ -145,13 +238,13 @@ function ContainersController($scope, Container, Settings, Messages, ViewSpinner
|
|||
if (c.Checked) {
|
||||
counter = counter + 1;
|
||||
action({id: c.Id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Container ' + c.Id + ' Removed.'});
|
||||
Messages.send("Container " + msg, c.Id);
|
||||
var index = $scope.containers.indexOf(c);
|
||||
$scope.containers.splice(index, 1);
|
||||
complete();
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
complete();
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -174,19 +267,19 @@ function ContainersController($scope, Container, Settings, Messages, ViewSpinner
|
|||
};
|
||||
|
||||
$scope.startAction = function() {
|
||||
batch($scope.containers, Container.start);
|
||||
batch($scope.containers, Container.start, "Started");
|
||||
};
|
||||
|
||||
$scope.stopAction = function() {
|
||||
batch($scope.containers, Container.stop);
|
||||
batch($scope.containers, Container.stop, "Stopped");
|
||||
};
|
||||
|
||||
$scope.killAction = function() {
|
||||
batch($scope.containers, Container.kill);
|
||||
batch($scope.containers, Container.kill, "Killed");
|
||||
};
|
||||
|
||||
$scope.removeAction = function() {
|
||||
batch($scope.containers, Container.remove);
|
||||
batch($scope.containers, Container.remove, "Removed");
|
||||
};
|
||||
|
||||
update({all: $scope.displayAll ? 1 : 0});
|
||||
|
@ -215,14 +308,13 @@ function ImagesController($scope, Image, ViewSpinner, Messages) {
|
|||
counter = counter + 1;
|
||||
Image.remove({id: i.Id}, function(d) {
|
||||
angular.forEach(d, function(resource) {
|
||||
Messages.send({class: 'text-success', data: 'Deleted: ' + resource.Deleted});
|
||||
Messages.send("Image deleted", resource.Deleted);
|
||||
});
|
||||
//Remove the image from the list
|
||||
var index = $scope.images.indexOf(i);
|
||||
$scope.images.splice(index, 1);
|
||||
complete();
|
||||
}, function(e) {
|
||||
Messages.send({class: 'text-error', data: e.data});
|
||||
Messages.error("Failure", e.data);
|
||||
complete();
|
||||
});
|
||||
}
|
||||
|
@ -240,21 +332,22 @@ function ImagesController($scope, Image, ViewSpinner, Messages) {
|
|||
$scope.images = d.map(function(item) { return new ImageViewModel(item); });
|
||||
ViewSpinner.stop();
|
||||
}, function (e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
Messages.error("Failure", e.data);
|
||||
ViewSpinner.stop();
|
||||
});
|
||||
}
|
||||
|
||||
// Controller for a single image and actions on that image
|
||||
function ImageController($scope, $routeParams, $location, Image, Messages) {
|
||||
function ImageController($scope, $q, $routeParams, $location, Image, Container, Messages) {
|
||||
$scope.history = [];
|
||||
$scope.tag = {repo: '', force: false};
|
||||
|
||||
$scope.remove = function() {
|
||||
Image.remove({id: $routeParams.id}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Image removed.'});
|
||||
Messages.send("Image Removed", $routeParams.id);
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -267,9 +360,10 @@ function ImageController($scope, $routeParams, $location, Image, Messages) {
|
|||
$scope.updateTag = function() {
|
||||
var tag = $scope.tag;
|
||||
Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) {
|
||||
Messages.send({class: 'text-success', data: 'Tag added.'});
|
||||
Messages.send("Tag Added", $routeParams.id);
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
$scope.error = e.data;
|
||||
$('#error-message').show();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -279,11 +373,24 @@ function ImageController($scope, $routeParams, $location, Image, Messages) {
|
|||
|
||||
Image.get({id: $routeParams.id}, function(d) {
|
||||
$scope.image = d;
|
||||
$scope.tag = d.id;
|
||||
var t = $routeParams.tag;
|
||||
if (t && t !== ":") {
|
||||
$scope.tag = t;
|
||||
var promise = getContainersFromImage($q, Container, t);
|
||||
|
||||
promise.then(function(containers) {
|
||||
newLineChart('#containers-started-chart', containers, function(c) { return new Date(c.Created * 1000).toLocaleDateString(); });
|
||||
});
|
||||
}
|
||||
}, function(e) {
|
||||
failedRequestHandler(e, Messages);
|
||||
if (e.status === 404) {
|
||||
$('.detail').hide();
|
||||
$scope.error = "Image not found.<br />" + $routeParams.id;
|
||||
} else {
|
||||
$scope.error = e.data;
|
||||
}
|
||||
$('#error-message').show();
|
||||
});
|
||||
|
||||
$scope.getHistory();
|
||||
|
@ -355,3 +462,21 @@ function BuilderController($scope, Dockerfile, Messages) {
|
|||
function failedRequestHandler(e, Messages) {
|
||||
Messages.send({class: 'text-error', data: e.data});
|
||||
}
|
||||
|
||||
// This gonna get messy but we don't have a good way to do this right now
|
||||
function getContainersFromImage($q, Container, tag) {
|
||||
var defer = $q.defer();
|
||||
|
||||
Container.query({all:1, notruc:1}, function(d) {
|
||||
var containers = [];
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var c = d[i];
|
||||
if (c.Image == tag) {
|
||||
containers.push(new ContainerViewModel(c));
|
||||
}
|
||||
}
|
||||
defer.resolve(containers);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
version: DOCKER_API_VERSION,
|
||||
rawUrl: DOCKER_ENDPOINT + DOCKER_PORT + '/' + DOCKER_API_VERSION,
|
||||
uiVersion: UI_VERSION,
|
||||
url: url
|
||||
url: url,
|
||||
firstLoad: true,
|
||||
};
|
||||
})
|
||||
.factory('ViewSpinner', function() {
|
||||
|
@ -78,9 +79,30 @@ angular.module('dockerui.services', ['ngResource'])
|
|||
})
|
||||
.factory('Messages', function($rootScope) {
|
||||
return {
|
||||
event: 'messageSend',
|
||||
send: function(msg) {
|
||||
$rootScope.$broadcast('messageSend', msg);
|
||||
send: function(title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 2000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length == 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
error: function(title, text) {
|
||||
$.gritter.add({
|
||||
title: title,
|
||||
text: text,
|
||||
time: 6000,
|
||||
before_open: function() {
|
||||
if($('.gritter-item-wrapper').length == 4) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/* the norm */
|
||||
#gritter-notice-wrapper {
|
||||
position:fixed;
|
||||
top:20px;
|
||||
right:20px;
|
||||
width:301px;
|
||||
z-index:9999;
|
||||
}
|
||||
#gritter-notice-wrapper.top-left {
|
||||
left: 20px;
|
||||
right: auto;
|
||||
}
|
||||
#gritter-notice-wrapper.bottom-right {
|
||||
top: auto;
|
||||
left: auto;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
#gritter-notice-wrapper.bottom-left {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
.gritter-item-wrapper {
|
||||
position:relative;
|
||||
margin:0 0 10px 0;
|
||||
background:url('../images/ie-spacer.gif'); /* ie7/8 fix */
|
||||
}
|
||||
.gritter-top {
|
||||
background:url(../images/gritter.png) no-repeat left -30px;
|
||||
height:10px;
|
||||
}
|
||||
.hover .gritter-top {
|
||||
background-position:right -30px;
|
||||
}
|
||||
.gritter-bottom {
|
||||
background:url(../images/gritter.png) no-repeat left bottom;
|
||||
height:8px;
|
||||
margin:0;
|
||||
}
|
||||
.hover .gritter-bottom {
|
||||
background-position: bottom right;
|
||||
}
|
||||
.gritter-item {
|
||||
display:block;
|
||||
background:url(../images/gritter.png) no-repeat left -40px;
|
||||
color:#eee;
|
||||
padding:2px 11px 8px 11px;
|
||||
font-size: 11px;
|
||||
font-family:verdana;
|
||||
}
|
||||
.hover .gritter-item {
|
||||
background-position:right -40px;
|
||||
}
|
||||
.gritter-item p {
|
||||
padding:0;
|
||||
margin:0;
|
||||
word-wrap:break-word;
|
||||
}
|
||||
.gritter-close {
|
||||
display:none;
|
||||
position:absolute;
|
||||
top:5px;
|
||||
left:3px;
|
||||
background:url(../images/gritter.png) no-repeat left top;
|
||||
cursor:pointer;
|
||||
width:30px;
|
||||
height:30px;
|
||||
}
|
||||
.gritter-title {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
padding:0 0 7px 0;
|
||||
display:block;
|
||||
text-shadow:1px 1px 0 #000; /* Not supported by IE :( */
|
||||
}
|
||||
.gritter-image {
|
||||
width:48px;
|
||||
height:48px;
|
||||
float:left;
|
||||
}
|
||||
.gritter-with-image,
|
||||
.gritter-without-image {
|
||||
padding:0;
|
||||
}
|
||||
.gritter-with-image {
|
||||
width:220px;
|
||||
float:right;
|
||||
}
|
||||
/* for the light (white) version of the gritter notice */
|
||||
.gritter-light .gritter-item,
|
||||
.gritter-light .gritter-bottom,
|
||||
.gritter-light .gritter-top,
|
||||
.gritter-light .gritter-close {
|
||||
background-image: url(../images/gritter-light.png);
|
||||
color: #222;
|
||||
}
|
||||
.gritter-light .gritter-title {
|
||||
text-shadow: none;
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* Gritter for jQuery
|
||||
* http://www.boedesign.com/
|
||||
*
|
||||
* Copyright (c) 2012 Jordan Boesch
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*
|
||||
* Date: February 24, 2012
|
||||
* Version: 1.7.4
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
/**
|
||||
* Set it up as an object under the jQuery namespace
|
||||
*/
|
||||
$.gritter = {};
|
||||
|
||||
/**
|
||||
* Set up global options that the user can over-ride
|
||||
*/
|
||||
$.gritter.options = {
|
||||
position: '',
|
||||
class_name: '', // could be set to 'gritter-light' to use white notifications
|
||||
fade_in_speed: 'medium', // how fast notifications fade in
|
||||
fade_out_speed: 1000, // how fast the notices fade out
|
||||
time: 6000 // hang on the screen for...
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a gritter notification to the screen
|
||||
* @see Gritter#add();
|
||||
*/
|
||||
$.gritter.add = function(params){
|
||||
|
||||
try {
|
||||
return Gritter.add(params || {});
|
||||
} catch(e) {
|
||||
|
||||
var err = 'Gritter Error: ' + e;
|
||||
(typeof(console) != 'undefined' && console.error) ?
|
||||
console.error(err, params) :
|
||||
alert(err);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a gritter notification from the screen
|
||||
* @see Gritter#removeSpecific();
|
||||
*/
|
||||
$.gritter.remove = function(id, params){
|
||||
Gritter.removeSpecific(id, params || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all notifications
|
||||
* @see Gritter#stop();
|
||||
*/
|
||||
$.gritter.removeAll = function(params){
|
||||
Gritter.stop(params || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Big fat Gritter object
|
||||
* @constructor (not really since its object literal)
|
||||
*/
|
||||
var Gritter = {
|
||||
|
||||
// Public - options to over-ride with $.gritter.options in "add"
|
||||
position: '',
|
||||
fade_in_speed: '',
|
||||
fade_out_speed: '',
|
||||
time: '',
|
||||
|
||||
// Private - no touchy the private parts
|
||||
_custom_timer: 0,
|
||||
_item_count: 0,
|
||||
_is_setup: 0,
|
||||
_tpl_close: '<div class="gritter-close"></div>',
|
||||
_tpl_title: '<span class="gritter-title">[[title]]</span>',
|
||||
_tpl_item: '<div id="gritter-item-[[number]]" class="gritter-item-wrapper [[item_class]]" style="display:none"><div class="gritter-top"></div><div class="gritter-item">[[close]][[image]]<div class="[[class_name]]">[[title]]<p>[[text]]</p></div><div style="clear:both"></div></div><div class="gritter-bottom"></div></div>',
|
||||
_tpl_wrap: '<div id="gritter-notice-wrapper"></div>',
|
||||
|
||||
/**
|
||||
* Add a gritter notification to the screen
|
||||
* @param {Object} params The object that contains all the options for drawing the notification
|
||||
* @return {Integer} The specific numeric id to that gritter notification
|
||||
*/
|
||||
add: function(params){
|
||||
// Handle straight text
|
||||
if(typeof(params) == 'string'){
|
||||
params = {text:params};
|
||||
}
|
||||
|
||||
// We might have some issues if we don't have a title or text!
|
||||
if(params.text === null){
|
||||
throw 'You must supply "text" parameter.';
|
||||
}
|
||||
|
||||
// Check the options and set them once
|
||||
if(!this._is_setup){
|
||||
this._runSetup();
|
||||
}
|
||||
|
||||
// Basics
|
||||
var title = params.title,
|
||||
text = params.text,
|
||||
image = params.image || '',
|
||||
sticky = params.sticky || false,
|
||||
item_class = params.class_name || $.gritter.options.class_name,
|
||||
position = $.gritter.options.position,
|
||||
time_alive = params.time || '';
|
||||
|
||||
this._verifyWrapper();
|
||||
|
||||
this._item_count++;
|
||||
var number = this._item_count,
|
||||
tmp = this._tpl_item;
|
||||
|
||||
// Assign callbacks
|
||||
$(['before_open', 'after_open', 'before_close', 'after_close']).each(function(i, val){
|
||||
Gritter['_' + val + '_' + number] = ($.isFunction(params[val])) ? params[val] : function(){}
|
||||
});
|
||||
|
||||
// Reset
|
||||
this._custom_timer = 0;
|
||||
|
||||
// A custom fade time set
|
||||
if(time_alive){
|
||||
this._custom_timer = time_alive;
|
||||
}
|
||||
|
||||
var image_str = (image != '') ? '<img src="' + image + '" class="gritter-image" />' : '',
|
||||
class_name = (image != '') ? 'gritter-with-image' : 'gritter-without-image';
|
||||
|
||||
// String replacements on the template
|
||||
if(title){
|
||||
title = this._str_replace('[[title]]',title,this._tpl_title);
|
||||
}else{
|
||||
title = '';
|
||||
}
|
||||
|
||||
tmp = this._str_replace(
|
||||
['[[title]]', '[[text]]', '[[close]]', '[[image]]', '[[number]]', '[[class_name]]', '[[item_class]]'],
|
||||
[title, text, this._tpl_close, image_str, this._item_count, class_name, item_class], tmp
|
||||
);
|
||||
|
||||
// If it's false, don't show another gritter message
|
||||
if(this['_before_open_' + number]() === false){
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#gritter-notice-wrapper').addClass(position).append(tmp);
|
||||
|
||||
var item = $('#gritter-item-' + this._item_count);
|
||||
|
||||
item.fadeIn(this.fade_in_speed, function(){
|
||||
Gritter['_after_open_' + number]($(this));
|
||||
});
|
||||
|
||||
if(!sticky){
|
||||
this._setFadeTimer(item, number);
|
||||
}
|
||||
|
||||
// Bind the hover/unhover states
|
||||
$(item).bind('mouseenter mouseleave', function(event){
|
||||
if(event.type == 'mouseenter'){
|
||||
if(!sticky){
|
||||
Gritter._restoreItemIfFading($(this), number);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!sticky){
|
||||
Gritter._setFadeTimer($(this), number);
|
||||
}
|
||||
}
|
||||
Gritter._hoverState($(this), event.type);
|
||||
});
|
||||
|
||||
// Clicking (X) makes the perdy thing close
|
||||
$(item).find('.gritter-close').click(function(){
|
||||
Gritter.removeSpecific(number, {}, null, true);
|
||||
});
|
||||
|
||||
return number;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* If we don't have any more gritter notifications, get rid of the wrapper using this check
|
||||
* @private
|
||||
* @param {Integer} unique_id The ID of the element that was just deleted, use it for a callback
|
||||
* @param {Object} e The jQuery element that we're going to perform the remove() action on
|
||||
* @param {Boolean} manual_close Did we close the gritter dialog with the (X) button
|
||||
*/
|
||||
_countRemoveWrapper: function(unique_id, e, manual_close){
|
||||
|
||||
// Remove it then run the callback function
|
||||
e.remove();
|
||||
this['_after_close_' + unique_id](e, manual_close);
|
||||
|
||||
// Check if the wrapper is empty, if it is.. remove the wrapper
|
||||
if($('.gritter-item-wrapper').length == 0){
|
||||
$('#gritter-notice-wrapper').remove();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Fade out an element after it's been on the screen for x amount of time
|
||||
* @private
|
||||
* @param {Object} e The jQuery element to get rid of
|
||||
* @param {Integer} unique_id The id of the element to remove
|
||||
* @param {Object} params An optional list of params to set fade speeds etc.
|
||||
* @param {Boolean} unbind_events Unbind the mouseenter/mouseleave events if they click (X)
|
||||
*/
|
||||
_fade: function(e, unique_id, params, unbind_events){
|
||||
|
||||
var params = params || {},
|
||||
fade = (typeof(params.fade) != 'undefined') ? params.fade : true,
|
||||
fade_out_speed = params.speed || this.fade_out_speed,
|
||||
manual_close = unbind_events;
|
||||
|
||||
this['_before_close_' + unique_id](e, manual_close);
|
||||
|
||||
// If this is true, then we are coming from clicking the (X)
|
||||
if(unbind_events){
|
||||
e.unbind('mouseenter mouseleave');
|
||||
}
|
||||
|
||||
// Fade it out or remove it
|
||||
if(fade){
|
||||
|
||||
e.animate({
|
||||
opacity: 0
|
||||
}, fade_out_speed, function(){
|
||||
e.animate({ height: 0 }, 300, function(){
|
||||
Gritter._countRemoveWrapper(unique_id, e, manual_close);
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
this._countRemoveWrapper(unique_id, e);
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform actions based on the type of bind (mouseenter, mouseleave)
|
||||
* @private
|
||||
* @param {Object} e The jQuery element
|
||||
* @param {String} type The type of action we're performing: mouseenter or mouseleave
|
||||
*/
|
||||
_hoverState: function(e, type){
|
||||
|
||||
// Change the border styles and add the (X) close button when you hover
|
||||
if(type == 'mouseenter'){
|
||||
|
||||
e.addClass('hover');
|
||||
|
||||
// Show close button
|
||||
e.find('.gritter-close').show();
|
||||
|
||||
}
|
||||
// Remove the border styles and hide (X) close button when you mouse out
|
||||
else {
|
||||
|
||||
e.removeClass('hover');
|
||||
|
||||
// Hide close button
|
||||
e.find('.gritter-close').hide();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific notification based on an ID
|
||||
* @param {Integer} unique_id The ID used to delete a specific notification
|
||||
* @param {Object} params A set of options passed in to determine how to get rid of it
|
||||
* @param {Object} e The jQuery element that we're "fading" then removing
|
||||
* @param {Boolean} unbind_events If we clicked on the (X) we set this to true to unbind mouseenter/mouseleave
|
||||
*/
|
||||
removeSpecific: function(unique_id, params, e, unbind_events){
|
||||
|
||||
if(!e){
|
||||
var e = $('#gritter-item-' + unique_id);
|
||||
}
|
||||
|
||||
// We set the fourth param to let the _fade function know to
|
||||
// unbind the "mouseleave" event. Once you click (X) there's no going back!
|
||||
this._fade(e, unique_id, params || {}, unbind_events);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* If the item is fading out and we hover over it, restore it!
|
||||
* @private
|
||||
* @param {Object} e The HTML element to remove
|
||||
* @param {Integer} unique_id The ID of the element
|
||||
*/
|
||||
_restoreItemIfFading: function(e, unique_id){
|
||||
|
||||
clearTimeout(this['_int_id_' + unique_id]);
|
||||
e.stop().css({ opacity: '', height: '' });
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the global options - only once
|
||||
* @private
|
||||
*/
|
||||
_runSetup: function(){
|
||||
|
||||
for(opt in $.gritter.options){
|
||||
this[opt] = $.gritter.options[opt];
|
||||
}
|
||||
this._is_setup = 1;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the notification to fade out after a certain amount of time
|
||||
* @private
|
||||
* @param {Object} item The HTML element we're dealing with
|
||||
* @param {Integer} unique_id The ID of the element
|
||||
*/
|
||||
_setFadeTimer: function(e, unique_id){
|
||||
|
||||
var timer_str = (this._custom_timer) ? this._custom_timer : this.time;
|
||||
this['_int_id_' + unique_id] = setTimeout(function(){
|
||||
Gritter._fade(e, unique_id);
|
||||
}, timer_str);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Bring everything to a halt
|
||||
* @param {Object} params A list of callback functions to pass when all notifications are removed
|
||||
*/
|
||||
stop: function(params){
|
||||
|
||||
// callbacks (if passed)
|
||||
var before_close = ($.isFunction(params.before_close)) ? params.before_close : function(){};
|
||||
var after_close = ($.isFunction(params.after_close)) ? params.after_close : function(){};
|
||||
|
||||
var wrap = $('#gritter-notice-wrapper');
|
||||
before_close(wrap);
|
||||
wrap.fadeOut(function(){
|
||||
$(this).remove();
|
||||
after_close();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* An extremely handy PHP function ported to JS, works well for templating
|
||||
* @private
|
||||
* @param {String/Array} search A list of things to search for
|
||||
* @param {String/Array} replace A list of things to replace the searches with
|
||||
* @return {String} sa The output
|
||||
*/
|
||||
_str_replace: function(search, replace, subject, count){
|
||||
|
||||
var i = 0, j = 0, temp = '', repl = '', sl = 0, fl = 0,
|
||||
f = [].concat(search),
|
||||
r = [].concat(replace),
|
||||
s = subject,
|
||||
ra = r instanceof Array, sa = s instanceof Array;
|
||||
s = [].concat(s);
|
||||
|
||||
if(count){
|
||||
this.window[count] = 0;
|
||||
}
|
||||
|
||||
for(i = 0, sl = s.length; i < sl; i++){
|
||||
|
||||
if(s[i] === ''){
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0, fl = f.length; j < fl; j++){
|
||||
|
||||
temp = s[i] + '';
|
||||
repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0];
|
||||
s[i] = (temp).split(f[j]).join(repl);
|
||||
|
||||
if(count && s[i] !== temp){
|
||||
this.window[count] += (temp.length-s[i].length) / f[j].length;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return sa ? s : s[0];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* A check to make sure we have something to wrap our notices with
|
||||
* @private
|
||||
*/
|
||||
_verifyWrapper: function(){
|
||||
|
||||
if($('#gritter-notice-wrapper').length == 0){
|
||||
$('body').append(this._tpl_wrap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})(jQuery);
|
|
@ -0,0 +1 @@
|
|||
(function(b){b.gritter={};b.gritter.options={position:"",class_name:"",fade_in_speed:"medium",fade_out_speed:1000,time:6000};b.gritter.add=function(f){try{return a.add(f||{})}catch(d){var c="Gritter Error: "+d;(typeof(console)!="undefined"&&console.error)?console.error(c,f):alert(c)}};b.gritter.remove=function(d,c){a.removeSpecific(d,c||{})};b.gritter.removeAll=function(c){a.stop(c||{})};var a={position:"",fade_in_speed:"",fade_out_speed:"",time:"",_custom_timer:0,_item_count:0,_is_setup:0,_tpl_close:'<div class="gritter-close"></div>',_tpl_title:'<span class="gritter-title">[[title]]</span>',_tpl_item:'<div id="gritter-item-[[number]]" class="gritter-item-wrapper [[item_class]]" style="display:none"><div class="gritter-top"></div><div class="gritter-item">[[close]][[image]]<div class="[[class_name]]">[[title]]<p>[[text]]</p></div><div style="clear:both"></div></div><div class="gritter-bottom"></div></div>',_tpl_wrap:'<div id="gritter-notice-wrapper"></div>',add:function(g){if(typeof(g)=="string"){g={text:g}}if(g.text===null){throw'You must supply "text" parameter.'}if(!this._is_setup){this._runSetup()}var k=g.title,n=g.text,e=g.image||"",l=g.sticky||false,m=g.class_name||b.gritter.options.class_name,j=b.gritter.options.position,d=g.time||"";this._verifyWrapper();this._item_count++;var f=this._item_count,i=this._tpl_item;b(["before_open","after_open","before_close","after_close"]).each(function(p,q){a["_"+q+"_"+f]=(b.isFunction(g[q]))?g[q]:function(){}});this._custom_timer=0;if(d){this._custom_timer=d}var c=(e!="")?'<img src="'+e+'" class="gritter-image" />':"",h=(e!="")?"gritter-with-image":"gritter-without-image";if(k){k=this._str_replace("[[title]]",k,this._tpl_title)}else{k=""}i=this._str_replace(["[[title]]","[[text]]","[[close]]","[[image]]","[[number]]","[[class_name]]","[[item_class]]"],[k,n,this._tpl_close,c,this._item_count,h,m],i);if(this["_before_open_"+f]()===false){return false}b("#gritter-notice-wrapper").addClass(j).append(i);var o=b("#gritter-item-"+this._item_count);o.fadeIn(this.fade_in_speed,function(){a["_after_open_"+f](b(this))});if(!l){this._setFadeTimer(o,f)}b(o).bind("mouseenter mouseleave",function(p){if(p.type=="mouseenter"){if(!l){a._restoreItemIfFading(b(this),f)}}else{if(!l){a._setFadeTimer(b(this),f)}}a._hoverState(b(this),p.type)});b(o).find(".gritter-close").click(function(){a.removeSpecific(f,{},null,true)});return f},_countRemoveWrapper:function(c,d,f){d.remove();this["_after_close_"+c](d,f);if(b(".gritter-item-wrapper").length==0){b("#gritter-notice-wrapper").remove()}},_fade:function(g,d,j,f){var j=j||{},i=(typeof(j.fade)!="undefined")?j.fade:true,c=j.speed||this.fade_out_speed,h=f;this["_before_close_"+d](g,h);if(f){g.unbind("mouseenter mouseleave")}if(i){g.animate({opacity:0},c,function(){g.animate({height:0},300,function(){a._countRemoveWrapper(d,g,h)})})}else{this._countRemoveWrapper(d,g)}},_hoverState:function(d,c){if(c=="mouseenter"){d.addClass("hover");d.find(".gritter-close").show()}else{d.removeClass("hover");d.find(".gritter-close").hide()}},removeSpecific:function(c,g,f,d){if(!f){var f=b("#gritter-item-"+c)}this._fade(f,c,g||{},d)},_restoreItemIfFading:function(d,c){clearTimeout(this["_int_id_"+c]);d.stop().css({opacity:"",height:""})},_runSetup:function(){for(opt in b.gritter.options){this[opt]=b.gritter.options[opt]}this._is_setup=1},_setFadeTimer:function(f,d){var c=(this._custom_timer)?this._custom_timer:this.time;this["_int_id_"+d]=setTimeout(function(){a._fade(f,d)},c)},stop:function(e){var c=(b.isFunction(e.before_close))?e.before_close:function(){};var f=(b.isFunction(e.after_close))?e.after_close:function(){};var d=b("#gritter-notice-wrapper");c(d);d.fadeOut(function(){b(this).remove();f()})},_str_replace:function(v,e,o,n){var k=0,h=0,t="",m="",g=0,q=0,l=[].concat(v),c=[].concat(e),u=o,d=c instanceof Array,p=u instanceof Array;u=[].concat(u);if(n){this.window[n]=0}for(k=0,g=u.length;k<g;k++){if(u[k]===""){continue}for(h=0,q=l.length;h<q;h++){t=u[k]+"";m=d?(c[h]!==undefined?c[h]:""):c[0];u[k]=(t).split(l[h]).join(m);if(n&&u[k]!==t){this.window[n]+=(t.length-u[k].length)/l[h].length}}}return p?u:u[0]},_verifyWrapper:function(){if(b("#gritter-notice-wrapper").length==0){b("body").append(this._tpl_wrap)}}}})(jQuery);
|
|
@ -22,6 +22,27 @@
|
|||
<td>Args:</td>
|
||||
<td>{{ container.Args }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ports:</td>
|
||||
<td>{{ container.Config.PortSpecs }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hostname:</td>
|
||||
<td>{{ container.Config.Hostname }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cmd:</td>
|
||||
<td>{{ container.Config.Cmd }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Entrypoint:</td>
|
||||
<td>{{ container.Config.Entrypoint }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Volumes:</td>
|
||||
<td>{{ container.Volumes }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>SysInitpath:</td>
|
||||
<td>{{ container.SysInitPath }}</td>
|
||||
|
|
|
@ -1,13 +1,48 @@
|
|||
|
||||
<div class="row-fluid">
|
||||
<div class="row-fluid center">
|
||||
<!--<div class="sidebar span4">
|
||||
<div ng-include="template" ng-controller="SideBarController"></div>
|
||||
</div>-->
|
||||
<div class="span12">
|
||||
<div class="span12" id="masthead" style="display:none">
|
||||
<div class="jumbotron">
|
||||
<h1>DockerUI</h1>
|
||||
<p class="lead">The Linux container engine</p>
|
||||
<a class="btn btn-large btn-success" href="http://docker.io">Learn more.</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span12">
|
||||
<div class="span6">
|
||||
<h3>Running Containers</h3>
|
||||
<ul>
|
||||
<li ng-repeat="container in containers|orderBy:predicate">
|
||||
<a href="/#/containers/{{ container.Id }}/">{{ container.Image }}</a>
|
||||
<span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span6 pull-right">
|
||||
<h3 style="float:right">Status</h3>
|
||||
<canvas id="containers-chart" style="float:right;">
|
||||
Get a better broswer... Your holding everyone back.
|
||||
</canvas>
|
||||
<div id="chart-legend" style="float:right;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="span12" id="stats">
|
||||
<hr />
|
||||
<h4>Containers created</h4>
|
||||
<canvas id="containers-started-chart" width="700" style="float:left">
|
||||
Get a better broswer... Your holding everyone back.
|
||||
</canvas>
|
||||
<h1 style="float:right">{{ totalContainers }}</h1>
|
||||
<hr />
|
||||
<h4>Images created</h4>
|
||||
<canvas id="images-created-chart" width="700" style="float:left">
|
||||
Get a better broswer... Your holding everyone back.
|
||||
</canvas>
|
||||
<h1 style="float:right">{{ totalImages }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
<div ng-include="template" ng-controller="StartContainerController"></div>
|
||||
|
||||
<div class="alert alert-error" id="error-message" style="display:none">
|
||||
{{ error }}
|
||||
</div>
|
||||
|
||||
<div class="detail">
|
||||
|
||||
<h4>Image: {{ image.id }}</h4>
|
||||
<h4>Image: {{ tag }}</h4>
|
||||
|
||||
<div class="btn-group detail">
|
||||
<button class="btn btn-success" ng-click="create()">Create</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Containers created:</h4>
|
||||
<canvas id="containers-started-chart" width="750">
|
||||
Get a better broswer... Your holding everyone back.
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
|
@ -19,10 +29,6 @@
|
|||
<td>Parent:</td>
|
||||
<td><a href="/#/images/{{ image.parent }}/">{{ image.parent }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Container:</td>
|
||||
<td><a href="/#/containers/{{ image.container }}/">{{ image.container }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size:</td>
|
||||
<td>{{ image.Size|humansize }}</td>
|
||||
|
@ -91,7 +97,6 @@
|
|||
|
||||
<hr />
|
||||
|
||||
|
||||
<div class="btn-remove">
|
||||
<button class="btn btn-large btn-block btn-primary btn-danger" ng-click="remove()">Remove Image</button>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<h2>Images:</h2>
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
<li class="active"><a href="" ng-click="showBuilder()">Build Image</a></li>
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" id="drop4" role="button" data-toggle="dropdown" href="#">Actions <b class="caret"></b></a>
|
||||
<ul id="menu1" class="dropdown-menu" role="menu" aria-labelledby="drop4">
|
||||
|
@ -17,7 +16,6 @@
|
|||
<tr>
|
||||
<th><input type="checkbox" ng-model="toggle" ng-click="toggleSelectAll()" /> Action</th>
|
||||
<th>Id</th>
|
||||
<th>Tag</th>
|
||||
<th>Repository</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
|
@ -25,9 +23,8 @@
|
|||
<tbody>
|
||||
<tr ng-repeat="image in images | orderBy:predicate">
|
||||
<td><input type="checkbox" ng-model="image.Checked" /></td>
|
||||
<td><a href="/#/images/{{ image.Id }}/">{{ image.Id|truncate:20}}</a></td>
|
||||
<td>{{ image.Tag }}</td>
|
||||
<td>{{ image.Repository }}</td>
|
||||
<td><a href="/#/images/{{ image.Id }}/?tag={{ image.Repository }}:{{ image.Tag }}">{{ image.Id|truncate:20}}</a></td>
|
||||
<td>{{ image.Repository }}:{{ image.Tag }}</td>
|
||||
<td>{{ image.Created|getdate }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<div id="message-display" class="center well messages" style="display:none;">
|
||||
<p ng-repeat="message in messages"><span class="{{ message.class }}">{{ message.data }}</span></p>
|
||||
</div>
|
Loading…
Reference in New Issue