blob: 3759017c01ffccdc4d3116bd9dadc3768beea860 [file] [log] [blame]
Matteo Scandoloeeb9c082016-02-09 11:19:22 -08001(function () {
2 'use strict';
3
Matteo Scandolo4b3d8722016-02-24 11:22:48 -08004 angular.module('xos.diagnostic')
Matteo Scandolo7fd4d042016-02-16 14:44:51 -08005 .service('ServiceTopologyHelper', function($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3){
Matteo Scandoloeeb9c082016-02-09 11:19:22 -08006
Matteo Scandolo5bb16682016-03-01 17:08:45 -08007 var _svg, _layout, _source, _el;
Matteo Scandoloeeb9c082016-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 Scandolo5bb16682016-03-01 17:08:45 -080012 const updateTree = (svg, layout, source, el = _el) => {
13
14 if(el){
15 _el = el;
16 }
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080017
18 //cache data
19 _svg = svg;
20 _layout = layout;
21 _source = source;
22
23 const maxDepth = ServiceRelation.depthOf(source);
24
25 const diagonal = d3.svg.diagonal()
26 .projection(d => [d.y, d.x]);
27
28 // Compute the new tree layout.
29 var nodes = layout.nodes(source).reverse(),
30 links = layout.links(nodes);
31
32 // Normalize for fixed-depth.
33 nodes.forEach(function(d) {
34 // position the child node horizontally
Matteo Scandolo5bb16682016-03-01 17:08:45 -080035 const step = ((_el.clientWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080036 d.y = d.depth * step;
37 });
38
39 // Update the nodes…
40 var node = svg.selectAll('g.node')
41 .data(nodes, function(d) { return d.id || (d.id = ++i); });
42
43 // Enter any new nodes at the parent's previous position.
44 var nodeEnter = node.enter().append('g')
45 .attr({
Matteo Scandolo657d1322016-02-16 17:43:00 -080046 class: d => {
Matteo Scandolo657d1322016-02-16 17:43:00 -080047 return `node ${d.type}`
48 },
Matteo Scandolo06afdfe2016-02-23 13:47:14 -080049 transform: d => (d.x && d.y) ? `translate(${d.y}, ${d.x})` : `translate(${source.y0}, ${source.x0})`
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080050 });
51
52 const subscriberNodes = nodeEnter.filter('.subscriber');
Matteo Scandolo38ba3312016-02-09 16:01:49 -080053 const internetNodes = nodeEnter.filter('.router');
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080054 const serviceNodes = nodeEnter.filter('.service');
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080055
56 subscriberNodes.append('rect')
Matteo Scandolo5bb16682016-03-01 17:08:45 -080057 .attr(serviceTopologyConfig.square)
58 // add event listener to subscriber
59 .on('click', () => {
60 $rootScope.$emit('subscriber.modal.open');
61 });
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080062
63 internetNodes.append('rect')
64 .attr(serviceTopologyConfig.square);
65
66 serviceNodes.append('circle')
67 .attr('r', 1e-6)
68 .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
69 .on('click', serviceClick);
70
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080071 nodeEnter.append('text')
72 .attr({
73 x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
74 dy: '.35em',
75 transform: d => {
76 if (d.children && d.parent){
77 if(d.parent.x < d.x){
78 return 'rotate(-30)';
79 }
80 return 'rotate(30)';
81 }
82 },
83 'text-anchor': d => d.children ? 'end' : 'start'
84 })
85 .text(d => d.name)
86 .style('fill-opacity', 1e-6);
87
88 // Transition nodes to their new position.
89 var nodeUpdate = node.transition()
90 .duration(serviceTopologyConfig.duration)
91 .attr({
92 'transform': d => `translate(${d.y},${d.x})`
93 });
94
95 nodeUpdate.select('circle')
Matteo Scandolo7fd4d042016-02-16 14:44:51 -080096 .attr('r', d => {
97 return d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius
98 })
Matteo Scandoloeeb9c082016-02-09 11:19:22 -080099 .style('fill', d => d.selected ? 'lightsteelblue' : '#fff');
100
101 nodeUpdate.select('text')
102 .style('fill-opacity', 1);
103
104 // Transition exiting nodes to the parent's new position.
105 var nodeExit = node.exit().transition()
106 .duration(serviceTopologyConfig.duration)
107 .remove();
108
109 nodeExit.select('circle')
110 .attr('r', 1e-6);
111
112 nodeExit.select('text')
113 .style('fill-opacity', 1e-6);
114
115 // Update the links…
116 var link = svg.selectAll('path.link')
117 .data(links, function(d) { return d.target.id; });
118
119 // Enter any new links at the parent's previous position.
120 link.enter().insert('path', 'g')
121 .attr('class', d => `link ${d.target.type} ${d.target.active ? '' : 'active'}`)
122 .attr('d', function(d) {
123 var o = {x: source.x0, y: source.y0};
124 return diagonal({source: o, target: o});
125 });
126
127 // Transition links to their new position.
128 link.transition()
129 .duration(serviceTopologyConfig.duration)
130 .attr('d', diagonal);
131
132 // Transition exiting nodes to the parent's new position.
133 link.exit().transition()
134 .duration(serviceTopologyConfig.duration)
135 .attr('d', function(d) {
136 var o = {x: source.x, y: source.y};
137 return diagonal({source: o, target: o});
138 })
139 .remove();
140
141 // Stash the old positions for transition.
142 nodes.forEach(function(d) {
143 d.x0 = d.x;
144 d.y0 = d.y;
145 });
146 };
147
148 const serviceClick = function(d) {
149
Matteo Scandoloc303fd02016-02-17 15:11:33 -0800150 // if was selected
151 if(d.selected){
152 d.selected = !d.selected;
153 $rootScope.$emit('instance.detail.hide', {});
154 return updateTree(_svg, _layout, _source);
155 }
Matteo Scandolodffc1382016-02-22 14:53:44 -0800156
Matteo Scandolo0344ef32016-02-22 16:53:22 -0800157 $rootScope.$emit('instance.detail', {name: d.name, service: d.service, tenant: d.tenant});
Matteo Scandoloeeb9c082016-02-09 11:19:22 -0800158
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800159 // unselect all
160 _svg.selectAll('circle')
161 .each(d => d.selected = false);
162
Matteo Scandoloeeb9c082016-02-09 11:19:22 -0800163 // toggling selected status
164 d.selected = !d.selected;
165
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800166 updateTree(_svg, _layout, _source);
Matteo Scandoloeeb9c082016-02-09 11:19:22 -0800167 };
168
169 this.updateTree = updateTree;
Matteo Scandoloeeb9c082016-02-09 11:19:22 -0800170 });
171
172}());