blob: 00684eea950c91f635a052f968dca3ed0697c703 [file] [log] [blame]
Matteo Scandolo8a64fa42016-01-21 11:21:03 -08001(function () {
2 'use strict';
3
4 angular.module('xos.serviceTopology')
5 .factory('d3', function($window){
6 return $window.d3;
7 })
Matteo Scandolo071ef462016-01-25 12:00:42 -08008 .service('TreeLayout', function($window, lodash, ServiceRelation, serviceTopologyConfig, Slice, Instances){
Matteo Scandolof2c99012016-01-25 10:10:38 -08009
Matteo Scandolo071ef462016-01-25 12:00:42 -080010 var _svg, _layout, _source;
11
12 var i = 0;
13
14 // given a canvas, a layout and a data source, draw a tree layout
15 const updateTree = (svg, layout, source) => {
16
17 //cache data
18 _svg = svg;
19 _layout = layout;
20 _source = source;
21
22 const maxDepth = ServiceRelation.depthOf(source);
23
24 const diagonal = d3.svg.diagonal()
25 .projection(d => [d.y, d.x]);
26
27 // Compute the new tree layout.
28 var nodes = layout.nodes(source).reverse(),
29 links = layout.links(nodes);
30
31 // Normalize for fixed-depth.
32 nodes.forEach(function(d) {
33 // position the child node horizontally
Matteo Scandolo9ef3c842016-01-25 13:55:09 -080034 const step = (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
35 d.y = d.depth * step;
36 if(d.type === 'slice' || d.type === 'instance'){
37 d.y = d.depth * step - (step / 2);
38 //d.x = d.parent.x + (step / 2);
39 //console.log(d.parent);
Matteo Scandolo071ef462016-01-25 12:00:42 -080040 }
41 });
42
43 // Update the nodes…
44 var node = svg.selectAll('g.node')
45 .data(nodes, function(d) { return d.id || (d.id = ++i); });
46
47 // Enter any new nodes at the parent's previous position.
48 var nodeEnter = node.enter().append('g')
49 .attr({
50 class: d => `node ${d.type}`,
51 transform: d => `translate(${source.y0},${source.x0})` // this is the starting position
52 });
53
Matteo Scandolo9ef3c842016-01-25 13:55:09 -080054 // TODO append different shapes base on type
Matteo Scandolo071ef462016-01-25 12:00:42 -080055 nodeEnter.append('circle')
56 .attr('r', 1e-6)
57 .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
58 .on('click', serviceClick);
59
60 nodeEnter.append('text')
61 .attr('x', function(d) { return d.children || d._children ? -13 : 13; })
62 .attr('transform', function(d) {
63 if((d.children || d._children) && d.parent || d._parent){
64 return 'rotate(30)';
65 }
66 })
67 .attr('dy', '.35em')
68 .attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
69 .text(function(d) { return d.name; })
70 .style('fill-opacity', 1e-6);
71
72 // Transition nodes to their new position.
73 var nodeUpdate = node.transition()
74 .duration(serviceTopologyConfig.duration)
75 .attr('transform', function(d) {
76 return 'translate(' + d.y + ',' + d.x + ')';
77 });
78
79 nodeUpdate.select('circle')
80 .attr('r', d => d.selected ? 15 : 10)
81 .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; });
82
83 nodeUpdate.select('text')
84 .style('fill-opacity', 1);
85
86 // Transition exiting nodes to the parent's new position.
87 var nodeExit = node.exit().transition()
88 .duration(serviceTopologyConfig.duration)
89 .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
90 .remove();
91
92 nodeExit.select('circle')
93 .attr('r', 1e-6);
94
95 nodeExit.select('text')
96 .style('fill-opacity', 1e-6);
97
98 // Update the links…
99 var link = svg.selectAll('path.link')
100 .data(links, function(d) { return d.target.id; });
101
102 // Enter any new links at the parent's previous position.
103 link.enter().insert('path', 'g')
Matteo Scandolo9ef3c842016-01-25 13:55:09 -0800104 .attr('class', d => `link ${d.target.type}`)
Matteo Scandolo071ef462016-01-25 12:00:42 -0800105 .attr('d', function(d) {
106 var o = {x: source.x0, y: source.y0};
107 return diagonal({source: o, target: o});
108 });
109
110 // Transition links to their new position.
111 link.transition()
112 .duration(serviceTopologyConfig.duration)
113 .attr('d', diagonal);
114
115 // Transition exiting nodes to the parent's new position.
116 link.exit().transition()
117 .duration(serviceTopologyConfig.duration)
118 .attr('d', function(d) {
119 var o = {x: source.x, y: source.y};
120 return diagonal({source: o, target: o});
121 })
122 .remove();
123
124 // Stash the old positions for transition.
125 nodes.forEach(function(d) {
126 d.x0 = d.x;
127 d.y0 = d.y;
128 });
129 };
130
131 const serviceClick = function(d) {
132
Matteo Scandolo9ef3c842016-01-25 13:55:09 -0800133 // toggling selected status
134 d.selected = !d.selected;
Matteo Scandolo071ef462016-01-25 12:00:42 -0800135
136 // reset all the nodes to default radius
137 var nodes = d3.selectAll('circle')
138 .transition()
139 .duration(serviceTopologyConfig.duration)
140 .attr('r', 10);
141
142 // remove slices details
143 d3.selectAll('rect.slice-detail')
144 .remove();
145 d3.selectAll('text.slice-name')
146 .remove();
147
148 var selectedNode = d3.select(this);
149
150 selectedNode
151 .transition()
152 .duration(serviceTopologyConfig.duration)
153 .attr('r', 15);
154
155 if(!d.service){
156 return;
157 }
158
Matteo Scandolo071ef462016-01-25 12:00:42 -0800159 ServiceRelation.getServiceInterfaces(d.service.id)
160 .then(interfaceTree => {
161
162 const isDetailed = lodash.find(d.children, {type: 'slice'});
163 if(isDetailed){
Matteo Scandolo071ef462016-01-25 12:00:42 -0800164 lodash.remove(d.children, {type: 'slice'});
165 }
166 else {
Matteo Scandolo071ef462016-01-25 12:00:42 -0800167 d.children = d.children.concat(interfaceTree);
168 }
169
170 updateTree(_svg, _layout, _source);
Matteo Scandolo071ef462016-01-25 12:00:42 -0800171 });
172 };
173
174 this.updateTree = updateTree;
Matteo Scandolof2c99012016-01-25 10:10:38 -0800175 });
Matteo Scandolo8a64fa42016-01-21 11:21:03 -0800176
177}());