blob: 008eaa33d64eccab57a16d56905f90cb6a378fdf [file] [log] [blame]
Matteo Scandolobe9b13d2016-01-21 11:21:03 -08001(function () {
2 'use strict';
3
4 angular.module('xos.serviceTopology')
5 .directive('serviceCanvas', function(){
6 return {
7 restrict: 'E',
8 scope: {},
9 bindToController: true,
10 controllerAs: 'vm',
11 templateUrl: 'templates/topology_canvas.tpl.html',
Matteo Scandolo89276392016-01-22 16:36:34 -080012 controller: function($element, $window, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers){
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080013
Matteo Scandolo793e1dd2016-01-22 09:33:26 -080014 this.instances = [];
15 this.slices = [];
16
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080017 const width = $window.innerWidth - serviceTopologyConfig.widthMargin;
18 const height = $window.innerHeight - serviceTopologyConfig.heightMargin;
19
20 const tree = d3.layout.tree()
21 .size([height, width]);
22
23 const diagonal = d3.svg.diagonal()
24 .projection(d => [d.y, d.x]);
25
26 const svg = d3.select($element[0])
27 .append('svg')
28 .style('width', `${$window.innerWidth}px`)
29 .style('height', `${$window.innerHeight}px`)
30 .append('g')
Matteo Scandolofb46f5b2016-01-25 10:10:38 -080031 .attr('transform', 'translate(' + serviceTopologyConfig.widthMargin+ '','' + serviceTopologyConfig.heightMargin + '')'');;
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080032
Matteo Scandolo85aad312016-01-21 14:23:28 -080033 //const resizeCanvas = () => {
34 // var targetSize = svg.node().getBoundingClientRect();
35 //
36 // d3.select(self.frameElement)
37 // .attr('width', `${targetSize.width}px`)
38 // .attr('height', `${targetSize.height}px`)
39 //};
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080040 //d3.select(window)
41 // .on('load', () => {
42 // resizeCanvas();
43 // });
44 //d3.select(window)
45 // .on('resize', () => {
46 // resizeCanvas();
47 // update(root);
48 // });
Matteo Scandolo85aad312016-01-21 14:23:28 -080049 var root;
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080050 var i = 0;
51 var duration = 750;
52
Matteo Scandolo85aad312016-01-21 14:23:28 -080053 const draw = (tree) => {
54 root = tree;
55 root.x0 = $window.innerHeight / 2;
56 root.y0 = 0;
57
58 update(root);
59 };
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080060
61 function update(source) {
62
63 const maxDepth = depthOf(source);
64
65 // Compute the new tree layout.
66 var nodes = tree.nodes(root).reverse(),
67 links = tree.links(nodes);
68
69 // Normalize for fixed-depth.
70 nodes.forEach(function(d) {
Matteo Scandolo53a02262016-01-21 16:02:57 -080071 // position the child node horizontally
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080072 d.y = d.depth * (($window.innerWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080073 });
74
75 // Update the nodes…
76 var node = svg.selectAll('g.node')
77 .data(nodes, function(d) { return d.id || (d.id = ++i); });
78
79 // Enter any new nodes at the parent's previous position.
80 var nodeEnter = node.enter().append('g')
Matteo Scandolofb46f5b2016-01-25 10:10:38 -080081 .attr({
82 class: d => `node ${d.type}`
83 })
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080084 .attr('transform', function(d) {
85 // this is the starting position
86 return 'translate(' + source.y0 + ',' + source.x0 + ')';
Matteo Scandolo53a02262016-01-21 16:02:57 -080087 });
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080088
89 nodeEnter.append('circle')
90 .attr('r', 1e-6)
Matteo Scandolo53a02262016-01-21 16:02:57 -080091 .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; })
92 .on('click', click);
Matteo Scandolobe9b13d2016-01-21 11:21:03 -080093
94 nodeEnter.append('text')
95 .attr('x', function(d) { return d.children || d._children ? -13 : 13; })
Matteo Scandolo68236262016-01-21 15:38:06 -080096 .attr('transform', function(d) {
97 if((d.children || d._children) && d.parent || d._parent){
98 return 'rotate(30)';
99 }
Matteo Scandolo68236262016-01-21 15:38:06 -0800100 })
Matteo Scandolobe9b13d2016-01-21 11:21:03 -0800101 .attr('dy', '.35em')
102 .attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
103 .text(function(d) { return d.name; })
104 .style('fill-opacity', 1e-6);
105
106 // Transition nodes to their new position.
107 var nodeUpdate = node.transition()
108 .duration(duration)
109 .attr('transform', function(d) {
110 return 'translate(' + d.y + ',' + d.x + ')';
111 });
112
113 nodeUpdate.select('circle')
114 .attr('r', 10)
115 .style('fill', function(d) { return d._children ? 'lightsteelblue' : '#fff'; });
116
117 nodeUpdate.select('text')
118 .style('fill-opacity', 1);
119
120 // Transition exiting nodes to the parent's new position.
121 var nodeExit = node.exit().transition()
122 .duration(duration)
123 .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
124 .remove();
125
126 nodeExit.select('circle')
127 .attr('r', 1e-6);
128
129 nodeExit.select('text')
130 .style('fill-opacity', 1e-6);
131
132 // Update the links…
133 var link = svg.selectAll('path.link')
134 .data(links, function(d) { return d.target.id; });
135
136 // Enter any new links at the parent's previous position.
137 link.enter().insert('path', 'g')
138 .attr('class', 'link')
139 .attr('d', function(d) {
140 var o = {x: source.x0, y: source.y0};
141 return diagonal({source: o, target: o});
142 });
143
144 // Transition links to their new position.
145 link.transition()
146 .duration(duration)
147 .attr('d', diagonal);
148
149 // Transition exiting nodes to the parent's new position.
150 link.exit().transition()
151 .duration(duration)
152 .attr('d', function(d) {
153 var o = {x: source.x, y: source.y};
154 return diagonal({source: o, target: o});
155 })
156 .remove();
157
158 // Stash the old positions for transition.
159 nodes.forEach(function(d) {
160 d.x0 = d.x;
161 d.y0 = d.y;
162 });
163 }
Matteo Scandolo85aad312016-01-21 14:23:28 -0800164
Matteo Scandolo53a02262016-01-21 16:02:57 -0800165 var _this = this;
166 const click = function(d) {
167
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800168 // empty panel
169 _this.slices = [];
170 _this.instances = [];
171
172 var nodes = d3.selectAll('circle')
173 .transition()
174 .duration(duration)
175 .attr('r', 10);
176
177 d3.selectAll('rect.slice-detail')
178 .remove();
179
180 d3.selectAll('text.slice-name')
181 .remove();
182
183 var selectedNode = d3.select(this);
184
185 selectedNode
186 .transition()
187 .duration(duration)
Matteo Scandolofb46f5b2016-01-25 10:10:38 -0800188 .attr('r', 15);
Matteo Scandolo53a02262016-01-21 16:02:57 -0800189
Matteo Scandolo89276392016-01-22 16:36:34 -0800190 if(!d.service){
191 return;
192 }
193
Matteo Scandolo53a02262016-01-21 16:02:57 -0800194 _this.selectedService = {
Matteo Scandolo68236262016-01-21 15:38:06 -0800195 id: d.id,
196 name: d.name
197 };
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800198
Matteo Scandolo148f22b2016-01-22 13:17:33 -0800199 Slice.query({service: d.service.id}).$promise
Matteo Scandolo68236262016-01-21 15:38:06 -0800200 .then(slices => {
Matteo Scandolo53a02262016-01-21 16:02:57 -0800201 _this.slices = slices;
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800202
203 if(slices.length > 0){
Matteo Scandolofb46f5b2016-01-25 10:10:38 -0800204 // TODO slice can be more than 1 create a for loop
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800205 const parentNode = d3.select(this.parentNode);
206
207 parentNode
208 .append('rect')
209 .style('opacity', 0)
210 .attr({
211 width: 150,
212 height: 50,
213 y: 35,
214 x: -75,
215 class: 'slice-detail'
216 })
217 .transition()
218 .duration(duration)
219 .style('opacity', 1);
Matteo Scandolofb46f5b2016-01-25 10:10:38 -0800220 // TODO attach a click listener to draw instances and networks
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800221
222 parentNode
223 .append('text')
224 .style('opacity', 0)
225 .attr({
226 y: 65,
227 x: -60,
228 class: 'slice-name'
229 })
230 .text(() => {
231 if(slices[0]){
Matteo Scandolo793e1dd2016-01-22 09:33:26 -0800232 return slices[0].humanReadableName;
233 }
234
235 return '';
236 })
237 .transition()
238 .duration(duration)
239 .style('opacity', 1);
Matteo Scandolofb46f5b2016-01-25 10:10:38 -0800240 }
Matteo Scandolo68236262016-01-21 15:38:06 -0800241 })
242 };
243
Matteo Scandolo89276392016-01-22 16:36:34 -0800244 Subscribers.query().$promise
245 .then((subscribers) => {
246 this.subscribers = subscribers;
Matteo Scandolofb46f5b2016-01-25 10:10:38 -0800247 if(subscribers.length === 1){
248 this.selectedSubscriber = subscribers[0];
249 this.getServiceChain(this.selectedSubscriber);
250 }
Matteo Scandolo85aad312016-01-21 14:23:28 -0800251 });
Matteo Scandolo68236262016-01-21 15:38:06 -0800252
253 this.getInstances = (slice) => {
254 Instances.query({slice: slice.id}).$promise
255 .then((instances) => {
256 this.selectedSlice = slice;
257 this.instances = instances;
258 })
259 };
Matteo Scandolo5f525542016-01-22 16:48:54 -0800260
261 this.getServiceChain = (subscriber) => {
262 ServiceRelation.get(subscriber)
263 .then((tree) => {
264 draw(tree);
265 });
266 };
Matteo Scandolobe9b13d2016-01-21 11:21:03 -0800267 }
268 }
269 });
270
271}());