blob: 61ec1e291315ba15d5ee9f59cc5d2c54d663c10e [file] [log] [blame]
Matteo Scandolo219b1a72016-02-09 11:19:22 -08001(function () {
2 'use strict';
3
Matteo Scandolo04564952016-02-24 11:22:48 -08004 angular.module('xos.diagnostic')
Matteo Scandolo35d53c82016-02-16 14:44:51 -08005 .service('ServiceTopologyHelper', function($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3){
Matteo Scandolo219b1a72016-02-09 11:19:22 -08006
Matteo Scandolo574c73f2016-03-01 17:08:45 -08007 var _svg, _layout, _source, _el;
Matteo Scandolo219b1a72016-02-09 11:19:22 -08008
9 var i = 0;
10
11 // given a canvas, a layout and a data source, draw a tree layout
Matteo Scandolo574c73f2016-03-01 17:08:45 -080012 const updateTree = (svg, layout, source, el = _el) => {
Matteo Scandolo574c73f2016-03-01 17:08:45 -080013 if(el){
14 _el = el;
15 }
Matteo Scandolo79108192016-03-08 09:33:26 -080016
17 let targetWidth = _el.clientWidth - serviceTopologyConfig.widthMargin * 2;
Matteo Scandolo219b1a72016-02-09 11:19:22 -080018
19 //cache data
20 _svg = svg;
21 _layout = layout;
22 _source = source;
23
24 const maxDepth = ServiceRelation.depthOf(source);
25
26 const diagonal = d3.svg.diagonal()
27 .projection(d => [d.y, d.x]);
28
29 // Compute the new tree layout.
30 var nodes = layout.nodes(source).reverse(),
31 links = layout.links(nodes);
32
33 // Normalize for fixed-depth.
34 nodes.forEach(function(d) {
35 // position the child node horizontally
Matteo Scandolo79108192016-03-08 09:33:26 -080036 const step = ((targetWidth - (serviceTopologyConfig.widthMargin * 2)) / (maxDepth - 1));
Matteo Scandolo219b1a72016-02-09 11:19:22 -080037 d.y = d.depth * step;
38 });
39
40 // Update the nodes…
41 var node = svg.selectAll('g.node')
42 .data(nodes, function(d) { return d.id || (d.id = ++i); });
43
44 // Enter any new nodes at the parent's previous position.
45 var nodeEnter = node.enter().append('g')
46 .attr({
Matteo Scandolo77d8fa02016-02-16 17:43:00 -080047 class: d => {
Matteo Scandolo77d8fa02016-02-16 17:43:00 -080048 return `node ${d.type}`
49 },
Matteo Scandolo26d17e12016-02-23 13:47:14 -080050 transform: d => (d.x && d.y) ? `translate(${d.y}, ${d.x})` : `translate(${source.y0}, ${source.x0})`
Matteo Scandolo219b1a72016-02-09 11:19:22 -080051 });
52
53 const subscriberNodes = nodeEnter.filter('.subscriber');
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080054 const internetNodes = nodeEnter.filter('.router');
Matteo Scandolo219b1a72016-02-09 11:19:22 -080055 const serviceNodes = nodeEnter.filter('.service');
Matteo Scandolo219b1a72016-02-09 11:19:22 -080056
57 subscriberNodes.append('rect')
Matteo Scandolo574c73f2016-03-01 17:08:45 -080058 .attr(serviceTopologyConfig.square)
59 // add event listener to subscriber
60 .on('click', () => {
61 $rootScope.$emit('subscriber.modal.open');
62 });
Matteo Scandolo219b1a72016-02-09 11:19:22 -080063
64 internetNodes.append('rect')
65 .attr(serviceTopologyConfig.square);
66
67 serviceNodes.append('circle')
68 .attr('r', 1e-6)
69 .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
70 .on('click', serviceClick);
71
Matteo Scandolo219b1a72016-02-09 11:19:22 -080072 nodeEnter.append('text')
73 .attr({
74 x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
75 dy: '.35em',
76 transform: d => {
77 if (d.children && d.parent){
78 if(d.parent.x < d.x){
79 return 'rotate(-30)';
80 }
81 return 'rotate(30)';
82 }
83 },
84 'text-anchor': d => d.children ? 'end' : 'start'
85 })
86 .text(d => d.name)
87 .style('fill-opacity', 1e-6);
88
89 // Transition nodes to their new position.
90 var nodeUpdate = node.transition()
91 .duration(serviceTopologyConfig.duration)
92 .attr({
93 'transform': d => `translate(${d.y},${d.x})`
94 });
95
96 nodeUpdate.select('circle')
Matteo Scandolo35d53c82016-02-16 14:44:51 -080097 .attr('r', d => {
98 return d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius
99 })
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800100 .style('fill', d => d.selected ? 'lightsteelblue' : '#fff');
101
102 nodeUpdate.select('text')
103 .style('fill-opacity', 1);
104
105 // Transition exiting nodes to the parent's new position.
106 var nodeExit = node.exit().transition()
107 .duration(serviceTopologyConfig.duration)
108 .remove();
109
110 nodeExit.select('circle')
111 .attr('r', 1e-6);
112
113 nodeExit.select('text')
114 .style('fill-opacity', 1e-6);
115
116 // Update the links…
117 var link = svg.selectAll('path.link')
118 .data(links, function(d) { return d.target.id; });
119
120 // Enter any new links at the parent's previous position.
121 link.enter().insert('path', 'g')
122 .attr('class', d => `link ${d.target.type} ${d.target.active ? '' : 'active'}`)
123 .attr('d', function(d) {
124 var o = {x: source.x0, y: source.y0};
125 return diagonal({source: o, target: o});
126 });
127
128 // Transition links to their new position.
129 link.transition()
130 .duration(serviceTopologyConfig.duration)
131 .attr('d', diagonal);
132
133 // Transition exiting nodes to the parent's new position.
134 link.exit().transition()
135 .duration(serviceTopologyConfig.duration)
136 .attr('d', function(d) {
137 var o = {x: source.x, y: source.y};
138 return diagonal({source: o, target: o});
139 })
140 .remove();
141
142 // Stash the old positions for transition.
143 nodes.forEach(function(d) {
144 d.x0 = d.x;
145 d.y0 = d.y;
146 });
147 };
148
149 const serviceClick = function(d) {
150
Matteo Scandoloc49ff702016-02-17 15:11:33 -0800151 // if was selected
152 if(d.selected){
153 d.selected = !d.selected;
154 $rootScope.$emit('instance.detail.hide', {});
155 return updateTree(_svg, _layout, _source);
156 }
Matteo Scandolo45fba732016-02-22 14:53:44 -0800157
Matteo Scandolo012dddb2016-02-22 16:53:22 -0800158 $rootScope.$emit('instance.detail', {name: d.name, service: d.service, tenant: d.tenant});
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800159
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800160 // unselect all
161 _svg.selectAll('circle')
162 .each(d => d.selected = false);
163
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800164 // toggling selected status
165 d.selected = !d.selected;
166
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800167 updateTree(_svg, _layout, _source);
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800168 };
169
170 this.updateTree = updateTree;
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800171 });
172
173}());