mirror of https://github.com/rancher/ui.git
197 lines
6.1 KiB
JavaScript
197 lines
6.1 KiB
JavaScript
import Ember from 'ember';
|
|
import Util from 'ui/utils/util';
|
|
import ThrottledResize from 'ui/mixins/throttled-resize';
|
|
import { activeIcon } from 'ui/models/service';
|
|
|
|
export default Ember.View.extend(ThrottledResize,{
|
|
classNames: ['environment-graph'],
|
|
graphZoom: null,
|
|
graphInner: null,
|
|
graphOuter: null,
|
|
graphRender: null,
|
|
graph: null,
|
|
|
|
didInsertElement: function() {
|
|
this._super();
|
|
var elem = $('<div id="environment-svg"><svg style="width: 100%; height: 100%;"><g/></svg></div>').appendTo('BODY');
|
|
this.set('graphElem', elem[0]);
|
|
|
|
Ember.run.later(this,'initGraph',100);
|
|
},
|
|
|
|
onResize: function() {
|
|
$('#environment-svg').css('top', $('MAIN').position().top + 55 + 'px');
|
|
if ( this.get('graph') )
|
|
{
|
|
this.renderGraph();
|
|
}
|
|
},
|
|
|
|
initGraph: function() {
|
|
var outer = d3.select("#environment-svg svg");
|
|
var inner = outer.select("g");
|
|
var zoom = d3.behavior.zoom().on("zoom", function() {
|
|
inner.attr("transform", "translate(" + d3.event.translate + ")" +
|
|
"scale(" + d3.event.scale + ")");
|
|
});
|
|
|
|
outer.call(zoom);
|
|
|
|
var g = new dagreD3.graphlib.Graph().setGraph({
|
|
rankdir: "TB",
|
|
nodesep: 50,
|
|
ranksep: 50,
|
|
marginx: 30,
|
|
marginy: 30
|
|
});
|
|
|
|
var render = new dagreD3.render();
|
|
|
|
this.setProperties({
|
|
graphZoom: zoom,
|
|
graphOuter: outer,
|
|
graphInner: inner,
|
|
graphRender: render,
|
|
graph: g
|
|
});
|
|
|
|
this.updateGraph();
|
|
},
|
|
|
|
crosslinkServices: function() {
|
|
// Add services that are cross-linked from another environment
|
|
var out = [];
|
|
|
|
var unremovedServices = this.get('context.model.services').filter(function(service) {
|
|
return ['removed','purging','purged'].indexOf(service.get('state')) === -1;
|
|
});
|
|
|
|
unremovedServices.forEach((service) => {
|
|
var externals = (service.get('consumedServicesWithNames')||[]).filter((linked) => {
|
|
return linked.get('service.environmentId') !== this.get('context.model.id');
|
|
}).map((linked) => { return linked.get('service'); });
|
|
out.pushObjects(externals);
|
|
});
|
|
|
|
return out;
|
|
}.property('context.model.services.@each.consumedServicesUpdated'),
|
|
|
|
updateGraph: function() {
|
|
var g = this.get('graph');
|
|
var services = this.get('context.model.services');
|
|
var unremovedServices = services.filter(function(service) {
|
|
return ['removed','purging','purged'].indexOf(service.get('state')) === -1;
|
|
});
|
|
|
|
var unexpectedNodes = g.nodes();
|
|
var unexpectedEdges = g.edges();
|
|
|
|
var expectedServices = unremovedServices.slice();
|
|
expectedServices.pushObjects(this.get('crosslinkServices'));
|
|
|
|
expectedServices.forEach((service) => {
|
|
var serviceId = service.get('id');
|
|
var color = (service.get('state') === 'active' ? 'green' : (service.get('state') === 'inactive' ? 'red' : 'yellow'));
|
|
var instances = service.get('instances.length')||'No';
|
|
var isCrossLink = service.get('environmentId') !== this.get('context.model.id');
|
|
|
|
var envName = '';
|
|
if ( isCrossLink )
|
|
{
|
|
envName = service.get('displayEnvironment') + '/';
|
|
}
|
|
|
|
var html = '<i class="icon '+ activeIcon(service) +'"></i>' +
|
|
'<h4 class="clip">'+ envName + Util.escapeHtml(service.get('displayName')) + '</h4>' +
|
|
'<h6 class="count"><b>' + instances + '</b> container' + (instances === 1 ? '' : 's') + '</h6>' +
|
|
'<h6><span class="state '+ color +'">' + Util.escapeHtml(Util.ucFirst(service.get('state'))) + '</span></h6>';
|
|
|
|
g.setNode(serviceId, {
|
|
labelType: "html",
|
|
label: html,
|
|
padding: 0,
|
|
class: color + (isCrossLink ? ' crosslink' : ''),
|
|
});
|
|
|
|
unexpectedNodes.removeObject(serviceId);
|
|
});
|
|
|
|
unremovedServices.forEach(function(service) {
|
|
var serviceId = service.get('id');
|
|
(service.get('consumedServicesWithNames')||[]).map(function(map) {
|
|
var target = map.get('service');
|
|
var targetId = target.get('id');
|
|
var color = (target.get('state') === 'active' ? 'green' : (target.get('state') === 'inactive' ? 'red' : 'yellow'));
|
|
|
|
var edgeOpts = {
|
|
arrowhead: 'vee',
|
|
lineInterpolate: 'bundle',
|
|
class: color,
|
|
};
|
|
|
|
var mapName = map.get('name');
|
|
if ( mapName && mapName !== target.get('name') )
|
|
{
|
|
edgeOpts.label = mapName;
|
|
}
|
|
|
|
g.setEdge(serviceId, targetId, edgeOpts);
|
|
|
|
var existing = unexpectedEdges.filter(function(edge) {
|
|
return edge.v === serviceId && edge.w === targetId;
|
|
});
|
|
unexpectedEdges.removeObjects(existing);
|
|
});
|
|
});
|
|
|
|
// Remove nodes & edges that shouldn't be there anymore
|
|
unexpectedNodes.forEach(function(node) {
|
|
g.removeNode(node);
|
|
});
|
|
|
|
unexpectedEdges.forEach(function(edge) {
|
|
g.removeEdge(edge.v, edge.w);
|
|
});
|
|
|
|
this.renderGraph();
|
|
},
|
|
|
|
renderGraph: function() {
|
|
var zoom = this.get('graphZoom');
|
|
var render = this.get('graphRender');
|
|
var inner = this.get('graphInner');
|
|
var outer = this.get('graphOuter');
|
|
var g = this.get('graph');
|
|
|
|
inner.call(render, g);
|
|
|
|
// Zoom and scale to fit
|
|
var zoomScale = zoom.scale();
|
|
var graphWidth = g.graph().width;
|
|
var graphHeight = g.graph().height;
|
|
var width = $('#environment-svg').width();
|
|
var height = $('#environment-svg').height();
|
|
zoomScale = Math.min(2.0, Math.min(width / graphWidth, height / graphHeight));
|
|
var translate = [(width/2) - ((graphWidth*zoomScale)/2), (height/2) - ((graphHeight*zoomScale)/2)];
|
|
zoom.translate(translate);
|
|
zoom.scale(zoomScale);
|
|
zoom.event(outer);
|
|
|
|
// Overflow the foreignObjects
|
|
$(this.get('graphElem').getElementsByTagName('foreignObject')).css('overflow','visible');
|
|
},
|
|
|
|
throttledUpdateGraph: function() {
|
|
Ember.run.throttle(this,'updateGraph',250);
|
|
}.observes('context.model.services.@each.{id,name,state,consumedServicesUpdated}','crosslinkServices.@each.{id,name,state,displayEnvironment}'),
|
|
|
|
willDestroyElement: function() {
|
|
this._super();
|
|
var elem = this.get('graphElem');
|
|
if ( elem )
|
|
{
|
|
$(elem).remove();
|
|
}
|
|
},
|
|
});
|