diff --git a/dist/angular.js b/dist/angular.js
index cedbd4c38..09000f025 100644
--- a/dist/angular.js
+++ b/dist/angular.js
@@ -228,3 +228,12 @@ var B={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},r
g[c]:r.defaults[c];a.isDefined(f)&&null!==f?(p=encodeURIComponent(f).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+c+"(\\W|$)","g"),p+"$1")):e=e.replace(RegExp("(/?):"+c+"(\\W|$)","g"),function(a,c,d){return"/"==d.charAt(0)?d:c+d})});e=e.replace(/\/+$/,"");e=e.replace(/\/\.(?=\w+($|\?))/,".");c.url=e.replace(/\/\\\./,"/.");s(g,function(a,e){r.urlParams[e]||
(c.params=c.params||{},c.params[e]=a)})}};return t}])})(window,window.angular);
//# sourceMappingURL=angular-resource.min.js.map
+
+/*
+ * angular-ui-bootstrap
+ * http://angular-ui.github.io/bootstrap/
+
+ * Version: 0.12.0 - 2014-11-16
+ * License: MIT
+ */
+angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.accordion","ui.bootstrap.collapse","ui.bootstrap.transition"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html"]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(n,o,i){this.groups=[],this.closeOthers=function(t){var e=angular.isDefined(o.closeOthers)?n.$eval(o.closeOthers):i.closeOthers;e&&angular.forEach(this.groups,function(n){n!==t&&(n.isOpen=!1)})},this.addGroup=function(n){var o=this;this.groups.push(n),n.$on("$destroy",function(){o.removeGroup(n)})},this.removeGroup=function(n){var o=this.groups.indexOf(n);-1!==o&&this.groups.splice(o,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(n){this.heading=n}},link:function(n,o,i,t){t.addGroup(n),n.$watch("isOpen",function(o){o&&t.closeOthers(n)}),n.toggleOpen=function(){n.isDisabled||(n.isOpen=!n.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(n,o,i,t,e){t.setHeading(e(n,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(n,o,i,t){n.$watch(function(){return t[i.accordionTransclude]},function(n){n&&(o.html(""),o.append(n))})}}}),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(n){return{link:function(o,i,t){function e(o){function t(){l===e&&(l=void 0)}var e=n(i,o);return l&&l.cancel(),l=e,e.then(t,t),e}function a(){u?(u=!1,r()):(i.removeClass("collapse").addClass("collapsing"),e({height:i[0].scrollHeight+"px"}).then(r))}function r(){i.removeClass("collapsing"),i.addClass("collapse in"),i.css({height:"auto"})}function c(){if(u)u=!1,s(),i.css({height:0});else{i.css({height:i[0].scrollHeight+"px"});{i[0].offsetWidth}i.removeClass("collapse in").addClass("collapsing"),e({height:0}).then(s)}}function s(){i.removeClass("collapsing"),i.addClass("collapse")}var l,u=!0;o.$watch(t.collapse,function(n){n?c():a()})}}}]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(n,o,i){function t(n){for(var o in n)if(void 0!==a.style[o])return n[o]}var e=function(t,a,r){r=r||{};var c=n.defer(),s=e[r.animation?"animationEndEventName":"transitionEndEventName"],l=function(){i.$apply(function(){t.unbind(s,l),c.resolve(t)})};return s&&t.bind(s,l),o(function(){angular.isString(a)?t.addClass(a):angular.isFunction(a)?a(t):angular.isObject(a)&&t.css(a),s||c.resolve(t)}),c.promise.cancel=function(){s&&t.unbind(s,l),c.reject("Transition cancelled")},c.promise},a=document.createElement("trans"),r={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},c={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=t(r),e.animationEndEventName=t(c),e}]),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion-group.html",'
\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion.html",'')}]);
\ No newline at end of file
diff --git a/dist/assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js b/dist/assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js
new file mode 100644
index 000000000..c9e65424f
--- /dev/null
+++ b/dist/assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js
@@ -0,0 +1,8 @@
+/*
+ * angular-ui-bootstrap
+ * http://angular-ui.github.io/bootstrap/
+
+ * Version: 0.12.0 - 2014-11-16
+ * License: MIT
+ */
+angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.accordion","ui.bootstrap.collapse","ui.bootstrap.transition"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html"]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(n,o,i){this.groups=[],this.closeOthers=function(t){var e=angular.isDefined(o.closeOthers)?n.$eval(o.closeOthers):i.closeOthers;e&&angular.forEach(this.groups,function(n){n!==t&&(n.isOpen=!1)})},this.addGroup=function(n){var o=this;this.groups.push(n),n.$on("$destroy",function(){o.removeGroup(n)})},this.removeGroup=function(n){var o=this.groups.indexOf(n);-1!==o&&this.groups.splice(o,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(n){this.heading=n}},link:function(n,o,i,t){t.addGroup(n),n.$watch("isOpen",function(o){o&&t.closeOthers(n)}),n.toggleOpen=function(){n.isDisabled||(n.isOpen=!n.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(n,o,i,t,e){t.setHeading(e(n,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(n,o,i,t){n.$watch(function(){return t[i.accordionTransclude]},function(n){n&&(o.html(""),o.append(n))})}}}),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(n){return{link:function(o,i,t){function e(o){function t(){l===e&&(l=void 0)}var e=n(i,o);return l&&l.cancel(),l=e,e.then(t,t),e}function a(){u?(u=!1,r()):(i.removeClass("collapse").addClass("collapsing"),e({height:i[0].scrollHeight+"px"}).then(r))}function r(){i.removeClass("collapsing"),i.addClass("collapse in"),i.css({height:"auto"})}function c(){if(u)u=!1,s(),i.css({height:0});else{i.css({height:i[0].scrollHeight+"px"});{i[0].offsetWidth}i.removeClass("collapse in").addClass("collapsing"),e({height:0}).then(s)}}function s(){i.removeClass("collapsing"),i.addClass("collapse")}var l,u=!0;o.$watch(t.collapse,function(n){n?c():a()})}}}]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(n,o,i){function t(n){for(var o in n)if(void 0!==a.style[o])return n[o]}var e=function(t,a,r){r=r||{};var c=n.defer(),s=e[r.animation?"animationEndEventName":"transitionEndEventName"],l=function(){i.$apply(function(){t.unbind(s,l),c.resolve(t)})};return s&&t.bind(s,l),o(function(){angular.isString(a)?t.addClass(a):angular.isFunction(a)?a(t):angular.isObject(a)&&t.css(a),s||c.resolve(t)}),c.promise.cancel=function(){s&&t.unbind(s,l),c.reject("Transition cancelled")},c.promise},a=document.createElement("trans"),r={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},c={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=t(r),e.animationEndEventName=t(c),e}]),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion-group.html",'\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(n){n.put("template/accordion/accordion.html",'')}]);
\ No newline at end of file
diff --git a/dist/dockerui.js b/dist/dockerui.js
index 1cec52022..67b0d0359 100644
--- a/dist/dockerui.js
+++ b/dist/dockerui.js
@@ -1,4 +1,4 @@
-/*! dockerui - v0.6.0 - 2015-01-18
+/*! dockerui - v0.6.0 - 2015-01-25
* https://github.com/crosbymichael/dockerui
* Copyright (c) 2015 Michael Crosby;
* Licensed MIT
@@ -17,10 +17,10 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
}])
// This is your docker url that the api will use to make requests
// 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_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.6.0')
- .constant('DOCKER_API_VERSION', 'v1.15');
+ .constant('DOCKER_API_VERSION', 'v1.16');
angular.module('builder', [])
.controller('BuilderController', ['$scope', 'Dockerfile', 'Messages',
@@ -501,44 +501,108 @@ function($scope, Container, Settings) {
});
}]);
-angular.module('startContainer', [])
-.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages',
-function($scope, $routeParams, $location, Container, Messages) {
+angular.module('startContainer', ['ui.bootstrap'])
+.controller('StartContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'containernameFilter', 'errorMsgFilter',
+function($scope, $routeParams, $location, Container, Messages, containernameFilter, errorMsgFilter) {
$scope.template = 'app/components/startContainer/startcontainer.html';
+
+ Container.query({all: 1}, function(d) {
+ $scope.containerNames = d.map(function(container){
+ return containernameFilter(container);
+ });
+ });
+
$scope.config = {
- name: '',
- memory: 0,
- memorySwap: 0,
- cpuShares: 1024,
- env: '',
- commands: '',
- volumesFrom: ''
+ Env: [],
+ Volumes: [],
+ SecurityOpts: [],
+ HostConfig: {
+ PortBindings: [],
+ Binds: [],
+ Links: [],
+ Dns: [],
+ DnsSearch: [],
+ VolumesFrom: [],
+ CapAdd: [],
+ CapDrop: []
+ }
+ };
+
+ $scope.menuStatus = {
+ containerOpen: true,
+ hostConfigOpen: false
};
- $scope.commandPlaceholder = '["/bin/echo", "Hello world"]';
function failedRequestHandler(e, Messages) {
- Messages.send({class: 'text-error', data: e.data});
+ Messages.error('Error', errorMsgFilter(e));
+ }
+
+ function rmEmptyKeys(col) {
+ for (var key in col) {
+ if (col[key] === null || col[key] === undefined || col[key] === '' || $.isEmptyObject(col[key]) || col[key].length === 0) {
+ delete col[key];
+ }
+ }
+ }
+
+ function getNames(arr) {
+ return arr.map(function(item) {return item.name;});
}
$scope.create = function() {
- var cmds = null;
- if ($scope.config.commands !== '') {
- cmds = angular.fromJson($scope.config.commands);
+ // Copy the config before transforming fields to the remote API format
+ var config = angular.copy($scope.config);
+
+ config.Image = $routeParams.id;
+
+ if (config.Cmd && config.Cmd[0] === "[") {
+ config.Cmd = angular.fromJson(config.Cmd);
}
- var id = $routeParams.id;
+
+ config.Env = config.Env.map(function(envar) {return envar.name + '=' + envar.value;});
+
+ config.Volumes = getNames(config.Volumes);
+ config.SecurityOpts = getNames(config.SecurityOpts);
+
+ config.HostConfig.VolumesFrom = getNames(config.HostConfig.VolumesFrom);
+ config.HostConfig.Binds = getNames(config.HostConfig.Binds);
+ config.HostConfig.Links = getNames(config.HostConfig.Links);
+ config.HostConfig.Dns = getNames(config.HostConfig.Dns);
+ config.HostConfig.DnsSearch = getNames(config.HostConfig.DnsSearch);
+ config.HostConfig.CapAdd = getNames(config.HostConfig.CapAdd);
+ config.HostConfig.CapDrop = getNames(config.HostConfig.CapDrop);
+
+ var ExposedPorts = {};
+ var PortBindings = {};
+ // TODO: consider using compatibility library
+ config.HostConfig.PortBindings.forEach(function(portBinding) {
+ var intPort = portBinding.intPort + "/tcp";
+ var binding = {
+ HostIp: portBinding.ip,
+ HostPort: portBinding.extPort
+ };
+ if (portBinding.intPort) {
+ ExposedPorts[intPort] = {};
+ if (intPort in PortBindings) {
+ PortBindings[intPort].push(binding);
+ } else {
+ PortBindings[intPort] = [binding];
+ }
+ } else {
+ // TODO: Send warning message? Internal port need to be specified.
+ }
+ });
+ config.ExposedPorts = ExposedPorts;
+ config.HostConfig.PortBindings = PortBindings;
+
+ // Remove empty fields from the request to avoid overriding defaults
+ rmEmptyKeys(config.HostConfig);
+ rmEmptyKeys(config);
+
var ctor = Container;
var loc = $location;
var s = $scope;
-
- Container.create({
- Image: id,
- name: $scope.config.name,
- Memory: $scope.config.memory,
- MemorySwap: $scope.config.memorySwap,
- CpuShares: $scope.config.cpuShares,
- Cmd: cmds,
- VolumesFrom: $scope.config.volumesFrom
- }, function(d) {
+ Container.create(config, function(d) {
if (d.Id) {
ctor.start({id: d.Id}, function(cd) {
$('#create-modal').modal('hide');
@@ -553,6 +617,14 @@ function($scope, $routeParams, $location, Container, Messages) {
failedRequestHandler(e, Messages);
});
};
+
+ $scope.addEntry = function(array, entry) {
+ array.push(entry);
+ };
+ $scope.rmEntry = function(array, entry) {
+ var idx = array.indexOf(entry);
+ array.splice(idx, 1);
+ };
}]);
angular.module('dockerui.filters', [])
@@ -656,6 +728,17 @@ angular.module('dockerui.filters', [])
var date = new Date(data * 1000);
return date.toDateString();
};
+ })
+ .filter('errorMsg', function() {
+ return function(object) {
+ var idx = 0;
+ var msg = '';
+ while (object[idx] && typeof(object[idx]) === 'string') {
+ msg += object[idx];
+ idx++;
+ }
+ return msg;
+ };
});
angular.module('dockerui.services', ['ngResource'])
@@ -779,7 +862,7 @@ angular.module('dockerui.services', ['ngResource'])
$.gritter.add({
title: title,
text: text,
- time: 6000,
+ time: 10000,
before_open: function() {
if($('.gritter-item-wrapper').length === 4) {
return false;
@@ -1432,38 +1515,248 @@ angular.module("app/components/startContainer/startcontainer.html", []).run(["$t
" Create And Start Container From Image
\n" +
" \n" +
" \n" +
"