|
@ -41,6 +41,7 @@ DockerUI listens on port 9000 by default. If you run DockerUI inside a container
|
||||||
* [Gritter](https://github.com/jboesch/Gritter)
|
* [Gritter](https://github.com/jboesch/Gritter)
|
||||||
* [Spin.js](https://github.com/fgnass/spin.js/)
|
* [Spin.js](https://github.com/fgnass/spin.js/)
|
||||||
* [Golang](https://golang.org/)
|
* [Golang](https://golang.org/)
|
||||||
|
* [Vis.js](http://visjs.org/)
|
||||||
|
|
||||||
|
|
||||||
### Todo:
|
### Todo:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events'])
|
angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services', 'dockerui.filters', 'masthead', 'footer', 'dashboard', 'container', 'containers', 'containersNetwork', 'images', 'image', 'startContainer', 'sidebar', 'info', 'builder', 'containerLogs', 'containerTop', 'events'])
|
||||||
.config(['$routeProvider', function ($routeProvider) {
|
.config(['$routeProvider', function ($routeProvider) {
|
||||||
'use strict';
|
'use strict';
|
||||||
$routeProvider.when('/', {
|
$routeProvider.when('/', {
|
||||||
|
@ -21,6 +21,10 @@ angular.module('dockerui', ['dockerui.templates', 'ngRoute', 'dockerui.services'
|
||||||
templateUrl: 'app/components/containerTop/containerTop.html',
|
templateUrl: 'app/components/containerTop/containerTop.html',
|
||||||
controller: 'ContainerTopController'
|
controller: 'ContainerTopController'
|
||||||
});
|
});
|
||||||
|
$routeProvider.when('/containers_network', {
|
||||||
|
templateUrl: 'app/components/containersNetwork/containersNetwork.html',
|
||||||
|
controller: 'ContainersNetworkController'
|
||||||
|
});
|
||||||
$routeProvider.when('/images/', {
|
$routeProvider.when('/images/', {
|
||||||
templateUrl: 'app/components/images/images.html',
|
templateUrl: 'app/components/images/images.html',
|
||||||
controller: 'ImagesController'
|
controller: 'ImagesController'
|
||||||
|
|
|
@ -40,6 +40,14 @@
|
||||||
ng-click="unpause()"
|
ng-click="unpause()"
|
||||||
ng-show="container.State.Running && container.State.Paused">Unpause
|
ng-show="container.State.Running && container.State.Paused">Unpause
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-success"
|
||||||
|
ng-click="restart()"
|
||||||
|
ng-show="container.State.Running && !container.State.Stopped">Restart
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
ng-click="commit()"
|
||||||
|
ng-show="container.State.Running && !container.State.Paused">Commit
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('container', [])
|
angular.module('container', [])
|
||||||
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'Messages', 'ViewSpinner',
|
.controller('ContainerController', ['$scope', '$routeParams', '$location', 'Container', 'ContainerCommit', 'Messages', 'ViewSpinner',
|
||||||
function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
function($scope, $routeParams, $location, Container, ContainerCommit, Messages, ViewSpinner) {
|
||||||
$scope.changes = [];
|
$scope.changes = [];
|
||||||
$scope.edit = false;
|
$scope.edit = false;
|
||||||
|
|
||||||
|
@ -58,6 +58,16 @@ function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.commit = function() {
|
||||||
|
ViewSpinner.spin();
|
||||||
|
ContainerCommit.commit({id: $routeParams.id, repo: $scope.container.Config.Image}, function(d) {
|
||||||
|
update();
|
||||||
|
Messages.send("Container commited", $routeParams.id);
|
||||||
|
}, function(e) {
|
||||||
|
update();
|
||||||
|
Messages.error("Failure", "Container failed to commit." + e.data);
|
||||||
|
});
|
||||||
|
};
|
||||||
$scope.pause = function() {
|
$scope.pause = function() {
|
||||||
ViewSpinner.spin();
|
ViewSpinner.spin();
|
||||||
Container.pause({id: $routeParams.id}, function(d) {
|
Container.pause({id: $routeParams.id}, function(d) {
|
||||||
|
@ -91,6 +101,17 @@ function($scope, $routeParams, $location, Container, Messages, ViewSpinner) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.restart = function() {
|
||||||
|
ViewSpinner.spin();
|
||||||
|
Container.restart({id: $routeParams.id}, function(d) {
|
||||||
|
update();
|
||||||
|
Messages.send("Container restarted", $routeParams.id);
|
||||||
|
}, function(e){
|
||||||
|
update();
|
||||||
|
Messages.error("Failure", "Container failed to restart." + e.data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.hasContent = function(data) {
|
$scope.hasContent = function(data) {
|
||||||
return data !== null && data !== undefined;
|
return data !== null && data !== undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<div class="detail">
|
||||||
|
<h2>Containers Network</h2>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" ng-model="query" autofocus="true" class="form-control"
|
||||||
|
placeholder="Search" ng-change="network.selectContainers(query)"/>
|
||||||
|
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-warning" ng-click="network.hideSelected()">Hide Selected</button>
|
||||||
|
<button class="btn btn-info" ng-click="network.showSelectedDownstream()">Show Selected Downstream</button>
|
||||||
|
<button class="btn btn-info" ng-click="network.showSelectedUpstream()">Show Selected Upstream</button>
|
||||||
|
<button class="btn btn-success" ng-click="network.showAll()">Show All</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<vis-network data="network.data" options="network.options" events="network.events"
|
||||||
|
component="network.component"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,249 @@
|
||||||
|
angular.module('containersNetwork', ['ngVis'])
|
||||||
|
.controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function($scope, $location, Container, Messages, VisDataSet) {
|
||||||
|
|
||||||
|
function ContainerNode(data) {
|
||||||
|
this.Id = data.Id;
|
||||||
|
// names have the following format: /Name
|
||||||
|
this.Name = data.Name.substring(1);
|
||||||
|
this.Image = data.Config.Image;
|
||||||
|
var dataLinks = data.HostConfig.Links;
|
||||||
|
if (dataLinks != null) {
|
||||||
|
this.Links = {};
|
||||||
|
for (var i = 0; i < dataLinks.length; i++) {
|
||||||
|
// links have the following format: /TargetContainerName:/SourceContainerName/LinkAlias
|
||||||
|
var link = dataLinks[i].split(":");
|
||||||
|
var target = link[0].substring(1);
|
||||||
|
var alias = link[1].substring(link[1].lastIndexOf("/") + 1);
|
||||||
|
// only keep shortest alias
|
||||||
|
if (this.Links[target] == null || alias.length < this.Links[target].length) {
|
||||||
|
this.Links[target] = alias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dataVolumes = data.HostConfig.VolumesFrom;
|
||||||
|
//converting array into properties for simpler and faster access
|
||||||
|
if (dataVolumes != null) {
|
||||||
|
this.VolumesFrom = {};
|
||||||
|
for (var j = 0; j < dataVolumes.length; j++) {
|
||||||
|
this.VolumesFrom[dataVolumes[j]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContainersNetworkData() {
|
||||||
|
this.nodes = new VisDataSet();
|
||||||
|
this.edges = new VisDataSet();
|
||||||
|
|
||||||
|
this.addContainerNode = function(container) {
|
||||||
|
this.nodes.add({
|
||||||
|
id: container.Id,
|
||||||
|
label: container.Name,
|
||||||
|
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
|
||||||
|
"<li><strong>ID:</strong> " + container.Id + "</li>" +
|
||||||
|
"<li><strong>Image:</strong> " + container.Image + "</li>" +
|
||||||
|
"</ul>"});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addLinkEdgeIfExists = function(from, to) {
|
||||||
|
if (from.Links != null && from.Links[to.Name] != null) {
|
||||||
|
this.edges.add({
|
||||||
|
from: from.Id,
|
||||||
|
to: to.Id,
|
||||||
|
label: from.Links[to.Name] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addVolumeEdgeIfExists = function(from, to) {
|
||||||
|
if (from.VolumesFrom != null && from.VolumesFrom[to.Id] != null) {
|
||||||
|
this.edges.add({
|
||||||
|
from: from.Id,
|
||||||
|
to: to.Id,
|
||||||
|
color: { color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeContainersNodes = function(containersIds) {
|
||||||
|
this.nodes.remove(containersIds);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContainersNetwork() {
|
||||||
|
this.data = new ContainersNetworkData();
|
||||||
|
this.containers = {};
|
||||||
|
this.selectedContainersIds = [];
|
||||||
|
this.shownContainersIds = [];
|
||||||
|
this.events = {
|
||||||
|
select : function(event) {
|
||||||
|
$scope.network.selectedContainersIds = event.nodes;
|
||||||
|
$scope.$apply( function() {
|
||||||
|
$scope.query = '';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
doubleClick : function(event) {
|
||||||
|
$scope.$apply( function() {
|
||||||
|
$location.path('/containers/' + event.nodes[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.options = {
|
||||||
|
navigation: true,
|
||||||
|
keyboard: true,
|
||||||
|
height: '500px', width: '700px',
|
||||||
|
nodes: {
|
||||||
|
shape: 'box'
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
style: 'arrow'
|
||||||
|
},
|
||||||
|
physics: {
|
||||||
|
barnesHut : {
|
||||||
|
springLength: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addContainer = function(data) {
|
||||||
|
var container = new ContainerNode(data);
|
||||||
|
this.containers[container.Id] = container;
|
||||||
|
this.shownContainersIds.push(container.Id);
|
||||||
|
this.data.addContainerNode(container);
|
||||||
|
for (var otherContainerId in this.containers) {
|
||||||
|
var otherContainer = this.containers[otherContainerId];
|
||||||
|
this.data.addLinkEdgeIfExists(container, otherContainer);
|
||||||
|
this.data.addLinkEdgeIfExists(otherContainer, container);
|
||||||
|
this.data.addVolumeEdgeIfExists(container, otherContainer);
|
||||||
|
this.data.addVolumeEdgeIfExists(otherContainer, container);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.selectContainers = function(query) {
|
||||||
|
if (this.component != null) {
|
||||||
|
this.selectedContainersIds = this.searchContainers(query);
|
||||||
|
this.component.selectNodes(this.selectedContainersIds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.searchContainers = function(query) {
|
||||||
|
if (query.trim() === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var selectedContainersIds = [];
|
||||||
|
for (var i=0; i < this.shownContainersIds.length; i++) {
|
||||||
|
var container = this.containers[this.shownContainersIds[i]];
|
||||||
|
if (container.Name.indexOf(query) > -1 ||
|
||||||
|
container.Image.indexOf(query) > -1 ||
|
||||||
|
container.Id.indexOf(query) > -1) {
|
||||||
|
selectedContainersIds.push(container.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedContainersIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.hideSelected = function() {
|
||||||
|
var i=0;
|
||||||
|
while ( i < this.shownContainersIds.length ) {
|
||||||
|
if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) {
|
||||||
|
this.shownContainersIds.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.data.removeContainersNodes(this.selectedContainersIds);
|
||||||
|
$scope.query = '';
|
||||||
|
this.selectedContainersIds = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.searchDownstream = function(containerId, downstreamContainersIds) {
|
||||||
|
if (downstreamContainersIds.indexOf(containerId) > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downstreamContainersIds.push(containerId);
|
||||||
|
var container = this.containers[containerId];
|
||||||
|
if (container.Links == null && container.VolumesFrom == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var otherContainerId in this.containers) {
|
||||||
|
var otherContainer = this.containers[otherContainerId];
|
||||||
|
if (container.Links != null && container.Links[otherContainer.Name] != null) {
|
||||||
|
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||||
|
} else if (container.VolumesFrom != null &&
|
||||||
|
container.VolumesFrom[otherContainer.Id] != null) {
|
||||||
|
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateShownContainers = function(newShownContainersIds) {
|
||||||
|
for (var containerId in this.containers) {
|
||||||
|
if (newShownContainersIds.indexOf(containerId) > -1 &&
|
||||||
|
this.shownContainersIds.indexOf(containerId) === -1) {
|
||||||
|
this.data.addContainerNode(this.containers[containerId]);
|
||||||
|
} else if (newShownContainersIds.indexOf(containerId) === -1 &&
|
||||||
|
this.shownContainersIds.indexOf(containerId) > -1) {
|
||||||
|
this.data.removeContainersNodes(containerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.shownContainersIds = newShownContainersIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.showSelectedDownstream = function() {
|
||||||
|
var downstreamContainersIds = [];
|
||||||
|
for (var i=0; i < this.selectedContainersIds.length; i++) {
|
||||||
|
this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds);
|
||||||
|
}
|
||||||
|
this.updateShownContainers(downstreamContainersIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.searchUpstream = function(containerId, upstreamContainersIds) {
|
||||||
|
if (upstreamContainersIds.indexOf(containerId) > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upstreamContainersIds.push(containerId);
|
||||||
|
var container = this.containers[containerId];
|
||||||
|
for (var otherContainerId in this.containers) {
|
||||||
|
var otherContainer = this.containers[otherContainerId];
|
||||||
|
if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) {
|
||||||
|
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||||
|
} else if (otherContainer.VolumesFrom != null &&
|
||||||
|
otherContainer.VolumesFrom[container.Id] != null) {
|
||||||
|
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.showSelectedUpstream = function() {
|
||||||
|
var upstreamContainersIds = [];
|
||||||
|
for (var i=0; i < this.selectedContainersIds.length; i++) {
|
||||||
|
this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds);
|
||||||
|
}
|
||||||
|
this.updateShownContainers(upstreamContainersIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.showAll = function() {
|
||||||
|
for (var containerId in this.containers) {
|
||||||
|
if (this.shownContainersIds.indexOf(containerId) === -1) {
|
||||||
|
this.data.addContainerNode(this.containers[containerId]);
|
||||||
|
this.shownContainersIds.push(containerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.network = new ContainersNetwork();
|
||||||
|
|
||||||
|
var showFailure = function (event) {
|
||||||
|
Messages.error('Failure', e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
var addContainer = function (container) {
|
||||||
|
$scope.network.addContainer(container);
|
||||||
|
};
|
||||||
|
|
||||||
|
Container.query({all: 0}, function(d) {
|
||||||
|
for (var i = 0; i < d.length; i++) {
|
||||||
|
Container.get({id: d[i].Id}, addContainer, showFailure);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
|
@ -3,6 +3,7 @@
|
||||||
<ul class="nav well">
|
<ul class="nav well">
|
||||||
<li><a href="#">Dashboard</a></li>
|
<li><a href="#">Dashboard</a></li>
|
||||||
<li><a href="#/containers/">Containers</a></li>
|
<li><a href="#/containers/">Containers</a></li>
|
||||||
|
<li><a href="#/containers_network/">Containers Network</a></li>
|
||||||
<li><a href="#/images/">Images</a></li>
|
<li><a href="#/images/">Images</a></li>
|
||||||
<li><a href="#/info/">Info</a></li>
|
<li><a href="#/info/">Info</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -20,6 +20,23 @@ angular.module('dockerui.services', ['ngResource'])
|
||||||
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false}
|
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
.factory('ContainerCommit', function ($resource, $http, Settings) {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
commit: function (params, callback) {
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: Settings.url + '/commit',
|
||||||
|
params: {
|
||||||
|
'container': params.id,
|
||||||
|
'repo': params.repo
|
||||||
|
}
|
||||||
|
}).success(callback).error(function (data, status, headers, config) {
|
||||||
|
console.log(error, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
.factory('ContainerLogs', function ($resource, $http, Settings) {
|
.factory('ContainerLogs', function ($resource, $http, Settings) {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
|
|
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
@ -0,0 +1,238 @@
|
||||||
|
angular.module('ngVis', [])
|
||||||
|
|
||||||
|
.factory('VisDataSet', function () {
|
||||||
|
'use strict';
|
||||||
|
return function (data, options) {
|
||||||
|
// Create the new dataSets
|
||||||
|
return new vis.DataSet(data, options);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TimeLine directive
|
||||||
|
*/
|
||||||
|
.directive('visTimeline', function () {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
transclude: false,
|
||||||
|
scope: {
|
||||||
|
data: '=',
|
||||||
|
options: '=',
|
||||||
|
events: '=',
|
||||||
|
component: '='
|
||||||
|
},
|
||||||
|
link: function (scope, element, attr) {
|
||||||
|
var timelineEvents = [
|
||||||
|
'rangechange',
|
||||||
|
'rangechanged',
|
||||||
|
'timechange',
|
||||||
|
'timechanged'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Declare the timeline
|
||||||
|
var timeline = null;
|
||||||
|
|
||||||
|
scope.$watch('data', function () {
|
||||||
|
// Sanity check
|
||||||
|
if (scope.data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've actually changed the data set, then recreate the graph
|
||||||
|
// We can always update the data by adding more data to the existing data set
|
||||||
|
if (timeline != null) {
|
||||||
|
timeline.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the timeline object
|
||||||
|
timeline = new vis.Timeline(element[0]);
|
||||||
|
scope.component = timeline;
|
||||||
|
|
||||||
|
// Attach an event handler if defined
|
||||||
|
angular.forEach(scope.events, function (callback, event) {
|
||||||
|
if (timelineEvents.indexOf(String(event)) >= 0) {
|
||||||
|
timeline.on(event, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the options first
|
||||||
|
timeline.setOptions(scope.options);
|
||||||
|
|
||||||
|
// Add groups and items
|
||||||
|
if (scope.data.groups != null) {
|
||||||
|
timeline.setGroups(scope.data.groups);
|
||||||
|
}
|
||||||
|
if (scope.data.items != null) {
|
||||||
|
timeline.setItems(scope.data.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onLoad callback
|
||||||
|
if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||||
|
scope.events.onload(timeline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$watchCollection('options', function (options) {
|
||||||
|
if(timeline == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timeline.setOptions(options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive for network chart.
|
||||||
|
*/
|
||||||
|
.directive('visNetwork', function () {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
transclude: false,
|
||||||
|
scope: {
|
||||||
|
data: '=',
|
||||||
|
options: '=',
|
||||||
|
events: '=',
|
||||||
|
component: '='
|
||||||
|
},
|
||||||
|
link: function (scope, element, attr) {
|
||||||
|
var networkEvents = [
|
||||||
|
'rangechange',
|
||||||
|
'rangechanged',
|
||||||
|
'timechange',
|
||||||
|
'timechanged'
|
||||||
|
];
|
||||||
|
|
||||||
|
var network = new vis.Network(element[0], scope.data, scope.options);
|
||||||
|
scope.component = network;
|
||||||
|
|
||||||
|
scope.$watch('data', function () {
|
||||||
|
// Sanity check
|
||||||
|
if (scope.data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've actually changed the data set, then recreate the graph
|
||||||
|
// We can always update the data by adding more data to the existing data set
|
||||||
|
// if (network !== undefined) {
|
||||||
|
// network.destroy();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Create the graph2d object
|
||||||
|
network = new vis.Network(element[0]);
|
||||||
|
scope.component = network;
|
||||||
|
|
||||||
|
// Attach an event handler if defined
|
||||||
|
angular.forEach(scope.events, function (callback, event) {
|
||||||
|
if (networkEvents.indexOf(String(event)) >= 0) {
|
||||||
|
network.on(event, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the options first
|
||||||
|
network.setOptions(scope.options);
|
||||||
|
network.setData(scope.data);
|
||||||
|
|
||||||
|
|
||||||
|
// onLoad callback
|
||||||
|
// if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||||
|
// scope.events.onload(graph);
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$watchCollection('options', function (options) {
|
||||||
|
if(network == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
network.setOptions(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$watch('events', function (events) {
|
||||||
|
angular.forEach(events, function (callback, event) {
|
||||||
|
if (['select', 'click', 'hoverNode', 'doubleClick'].indexOf(String(event)) >= 0) {
|
||||||
|
network.on(event, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive for graph2d.
|
||||||
|
*/
|
||||||
|
.directive('visGraph2d', function () {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
transclude: false,
|
||||||
|
scope: {
|
||||||
|
data: '=',
|
||||||
|
options: '=',
|
||||||
|
events: '=',
|
||||||
|
component: '='
|
||||||
|
},
|
||||||
|
link: function (scope, element, attr) {
|
||||||
|
var graphEvents = [
|
||||||
|
'rangechange',
|
||||||
|
'rangechanged',
|
||||||
|
'timechange',
|
||||||
|
'timechanged'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create the chart
|
||||||
|
var graph = new vis.Graph2d(element[0]);
|
||||||
|
scope.component = graph;
|
||||||
|
|
||||||
|
scope.$watch('data', function () {
|
||||||
|
// Sanity check
|
||||||
|
if (scope.data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've actually changed the data set, then recreate the graph
|
||||||
|
// We can always update the data by adding more data to the existing data set
|
||||||
|
if (graph != null) {
|
||||||
|
graph.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the graph2d object
|
||||||
|
graph = new vis.Graph2d(element[0]);
|
||||||
|
scope.component = graph;
|
||||||
|
|
||||||
|
// Attach an event handler if defined
|
||||||
|
angular.forEach(scope.events, function (callback, event) {
|
||||||
|
if (graphEvents.indexOf(String(event)) >= 0) {
|
||||||
|
graph.on(event, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the options first
|
||||||
|
graph.setOptions(scope.options);
|
||||||
|
|
||||||
|
// Add groups and items
|
||||||
|
if (scope.data.groups != null) {
|
||||||
|
graph.setGroups(scope.data.groups);
|
||||||
|
}
|
||||||
|
if (scope.data.items != null) {
|
||||||
|
graph.setItems(scope.data.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onLoad callback
|
||||||
|
if (scope.events != null && scope.events.onload != null && angular.isFunction(scope.events.onload)) {
|
||||||
|
scope.events.onload(graph);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$watchCollection('options', function (options) {
|
||||||
|
if(graph == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
graph.setOptions(options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
;
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
<link href="assets/css/bootstrap.min.css" rel="stylesheet">
|
<link href="assets/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="assets/css/jquery.gritter.css" rel="stylesheet">
|
<link href="assets/css/jquery.gritter.css" rel="stylesheet">
|
||||||
|
<link href="assets/css/vis.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
<link href="<%= pkg.name %>.css" rel="stylesheet">
|
||||||
|
|
||||||
|
@ -27,6 +28,8 @@
|
||||||
<script src="assets/js/jquery.gritter.min.js"></script>
|
<script src="assets/js/jquery.gritter.min.js"></script>
|
||||||
<script src="assets/js/Chart.min.js"></script>
|
<script src="assets/js/Chart.min.js"></script>
|
||||||
<script src="assets/js/legend.js"></script>
|
<script src="assets/js/legend.js"></script>
|
||||||
|
<script src="assets/js/vis.min.js"></script>
|
||||||
|
<script src="assets/js/angular-vis.js"></script>
|
||||||
<script src="assets/js/oboe-browser.min.js"></script>
|
<script src="assets/js/oboe-browser.min.js"></script>
|
||||||
|
|
||||||
<script src="<%= pkg.name %>.js"></script>
|
<script src="<%= pkg.name %>.js"></script>
|
||||||
|
|
|
@ -11,6 +11,7 @@ files = [
|
||||||
'assets/js/spin.js',
|
'assets/js/spin.js',
|
||||||
'dist/angular.js',
|
'dist/angular.js',
|
||||||
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
|
'assets/js/ui-bootstrap/ui-bootstrap-custom-tpls-0.12.0.min.js',
|
||||||
|
'assets/js/angular-vis.js',
|
||||||
'test/assets/angular/angular-mocks.js',
|
'test/assets/angular/angular-mocks.js',
|
||||||
'app/**/*.js',
|
'app/**/*.js',
|
||||||
'test/unit/**/*.spec.js',
|
'test/unit/**/*.spec.js',
|
||||||
|
|