blob: 15499a5e3ee33fdccc7dee31bd60fff42b51ddaf [file] [log] [blame]
Simon Hunta4242de2015-02-24 17:11:55 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Simon Hunta4242de2015-02-24 17:11:55 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 ONOS GUI -- Topology D3 Module.
19 Functions for manipulating the D3 visualizations of the Topology
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Steven Burrows1c2a9682017-07-14 16:52:46 +010026 var sus, is, ts, ps, ttbs;
Simon Hunta4242de2015-02-24 17:11:55 -080027
Simon Hunte2d9dc72017-08-10 15:21:04 -070028 // function to be replaced by the localization bundle function
29 var topoLion = function (x) {
30 return '#tfs#' + x + '#';
31 };
32
Simon Hunta4242de2015-02-24 17:11:55 -080033 // api to topoForce
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070034 var zoomer, api;
Simon Hunta4242de2015-02-24 17:11:55 -080035 /*
36 node() // get ref to D3 selection of nodes
37 link() // get ref to D3 selection of links
38 linkLabel() // get ref to D3 selection of link labels
39 instVisible() // true if instances panel is visible
40 posNode() // position node
41 showHosts() // true if hosts are to be shown
42 restyleLinkElement() // update link styles based on backing data
43 updateLinkLabelModel() // update backing data for link labels
44 */
45
46 // configuration
Simon Hunta5487ad2016-06-16 13:10:41 -070047 var devIconDim = 36,
Simon Hunte9062fc2016-12-22 11:40:06 -080048 devColorDim = 32,
Simon Hunta5487ad2016-06-16 13:10:41 -070049 labelPad = 4,
50 hostRadius = 14,
51 badgeConfig = {
Simon Hunt5674db92015-10-22 16:12:48 -070052 radius: 12,
Simon Hunt004fc2c2015-10-23 11:55:58 -070053 yoff: 5,
Steven Burrows1c2a9682017-07-14 16:52:46 +010054 gdelta: 10,
Simon Hunta5487ad2016-06-16 13:10:41 -070055 },
56 halfDevIcon = devIconDim / 2,
57 devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
58 hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070059 portLabelDim = 30,
Simon Hunta5487ad2016-06-16 13:10:41 -070060 status = {
61 i: 'badgeInfo',
62 w: 'badgeWarn',
Steven Burrows1c2a9682017-07-14 16:52:46 +010063 e: 'badgeError',
Simon Huntf44d7262016-06-14 14:46:56 -070064 };
65
Simon Hunt1eee51d2016-02-26 19:12:13 -080066 // NOTE: this type of hack should go away once we have implemented
67 // the server-side UiModel code.
68 // {virtual -> cord} is for the E-CORD demo at ONS 2016
69 var remappedDeviceTypes = {
Simon Hunte9062fc2016-12-22 11:40:06 -080070 virtual: 'cord',
71
72 // for now, map to the new glyphs via this lookup.
73 // may have to find a better way to do this...
74 'switch': 'm_switch',
75 roadm: 'm_roadm',
76 otn: 'm_otn',
77 roadm_otn: 'm_roadm_otn',
78 fiber_switch: 'm_fiberSwitch',
79 microwave: 'm_microwave',
80 };
81
82 var remappedHostTypes = {
83 router: 'm_router',
84 endstation: 'm_endstation',
Steven Burrows1c2a9682017-07-14 16:52:46 +010085 bgpSpeaker: 'm_bgpSpeaker',
Simon Hunt1eee51d2016-02-26 19:12:13 -080086 };
87
88 function mapDeviceTypeToGlyph(type) {
89 return remappedDeviceTypes[type] || type || 'unknown';
90 }
91
Simon Hunte9062fc2016-12-22 11:40:06 -080092 function mapHostTypeToGlyph(type) {
93 return remappedHostTypes[type] || type || 'unknown';
94 }
95
Simon Hunt5674db92015-10-22 16:12:48 -070096 function badgeStatus(badge) {
97 return status[badge.status] || status.i;
98 }
99
Simon Hunta4242de2015-02-24 17:11:55 -0800100 // internal state
101 var deviceLabelIndex = 0,
102 hostLabelIndex = 0;
103
Simon Hunta5487ad2016-06-16 13:10:41 -0700104 // note: these are the device icon colors without affinity (no master)
Simon Hunta4242de2015-02-24 17:11:55 -0800105 var dColTheme = {
106 light: {
Simon Huntf44d7262016-06-14 14:46:56 -0700107 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100108 offline: '#cccccc',
Simon Hunta4242de2015-02-24 17:11:55 -0800109 },
110 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -0700111 // TODO: theme
112 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100113 offline: '#cccccc',
114 },
Simon Hunta4242de2015-02-24 17:11:55 -0800115 };
116
Simon Huntf44d7262016-06-14 14:46:56 -0700117 function devGlyphColor(d) {
118 var o = d.online,
119 id = d.master,
120 otag = o ? 'online' : 'offline';
121 return o ? sus.cat7().getColor(id, 0, ts.theme())
122 : dColTheme[ts.theme()][otag];
Simon Hunta4242de2015-02-24 17:11:55 -0800123 }
124
125 function setDeviceColor(d) {
Simon Hunte9062fc2016-12-22 11:40:06 -0800126 // want to color the square rectangle (no longer the 'use' glyph)
Steven Burrows1c2a9682017-07-14 16:52:46 +0100127 d.el.selectAll('rect').filter(function (d, i) { return i === 1; })
Simon Huntf44d7262016-06-14 14:46:56 -0700128 .style('fill', devGlyphColor(d));
Simon Hunta4242de2015-02-24 17:11:55 -0800129 }
130
Simon Hunta4242de2015-02-24 17:11:55 -0800131 function incDevLabIndex() {
Steven Burrowsbbe3dda2016-09-26 14:41:59 -0700132 setDevLabIndex(deviceLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100133 switch (deviceLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700134 case 0: return topoLion('fl_device_labels_hide');
135 case 1: return topoLion('fl_device_labels_show_friendly');
136 case 2: return topoLion('fl_device_labels_show_id');
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700137 }
Simon Hunta4242de2015-02-24 17:11:55 -0800138 }
139
Thomas Vachuska0af26912016-03-21 21:37:30 -0700140 function setDevLabIndex(mode) {
141 deviceLabelIndex = mode % 3;
142 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
143 p.dlbls = deviceLabelIndex;
144 ps.setPrefs('topo_prefs', p);
145 }
146
Simon Hunt10618f62017-06-15 19:30:52 -0700147 function incHostLabIndex() {
148 setHostLabIndex(hostLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100149 switch (hostLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700150 case 0: return topoLion('fl_host_labels_show_friendly');
151 case 1: return topoLion('fl_host_labels_show_ip');
152 case 2: return topoLion('fl_host_labels_show_mac');
Thomas Vachuska528ea952017-10-09 18:25:35 +0200153 case 3: return topoLion('fl_host_labels_hide');
Simon Hunt10618f62017-06-15 19:30:52 -0700154 }
155 }
156
157 function setHostLabIndex(mode) {
Thomas Vachuska528ea952017-10-09 18:25:35 +0200158 hostLabelIndex = mode % 4;
Simon Hunt10618f62017-06-15 19:30:52 -0700159 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
160 p.hlbls = hostLabelIndex;
161 ps.setPrefs('topo_prefs', p);
162 }
163
Simon Hunta4242de2015-02-24 17:11:55 -0800164 function hostLabel(d) {
165 var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
166 return d.labels[idx];
167 }
Simon Huntf44d7262016-06-14 14:46:56 -0700168
Simon Hunta4242de2015-02-24 17:11:55 -0800169 function deviceLabel(d) {
170 var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
171 return d.labels[idx];
172 }
Simon Huntf44d7262016-06-14 14:46:56 -0700173
Simon Hunta4242de2015-02-24 17:11:55 -0800174 function trimLabel(label) {
175 return (label && label.trim()) || '';
176 }
177
Simon Huntf44d7262016-06-14 14:46:56 -0700178 function computeLabelWidth(n) {
179 var text = n.select('text'),
180 box = text.node().getBBox();
181 return box.width + labelPad * 2;
182 }
183
184 function iconBox(dim, labelWidth) {
Simon Hunta4242de2015-02-24 17:11:55 -0800185 return {
Simon Huntf44d7262016-06-14 14:46:56 -0700186 x: -dim/2,
187 y: -dim/2,
188 width: dim + labelWidth,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100189 height: dim,
190 };
Simon Hunta4242de2015-02-24 17:11:55 -0800191 }
192
Simon Hunt5674db92015-10-22 16:12:48 -0700193 function updateDeviceRendering(d) {
Simon Huntf44d7262016-06-14 14:46:56 -0700194 var node = d.el,
195 bdg = d.badge,
196 label = trimLabel(deviceLabel(d)),
197 labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800198
Simon Huntf44d7262016-06-14 14:46:56 -0700199 node.select('text').text(label);
200 labelWidth = label ? computeLabelWidth(node) : 0;
Simon Hunta4242de2015-02-24 17:11:55 -0800201
202 node.select('rect')
203 .transition()
Simon Huntf44d7262016-06-14 14:46:56 -0700204 .attr(iconBox(devIconDim, labelWidth));
Simon Hunta4242de2015-02-24 17:11:55 -0800205
Simon Hunt5674db92015-10-22 16:12:48 -0700206 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700207 renderBadge(node, bdg, devBadgeOff);
Simon Hunte9343f32015-10-21 18:07:46 -0700208 }
209 }
210
Andrea Campanella52125412015-12-03 14:50:40 -0800211 function updateHostRendering(d) {
212 var node = d.el,
Simon Huntc2bfe332015-12-04 11:01:24 -0800213 bdg = d.badge;
Andrea Campanella52125412015-12-03 14:50:40 -0800214
215 updateHostLabel(d);
Andrea Campanella52125412015-12-03 14:50:40 -0800216
Andrea Campanella52125412015-12-03 14:50:40 -0800217 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700218 renderBadge(node, bdg, hostBadgeOff);
Simon Huntc2bfe332015-12-04 11:01:24 -0800219 }
220 }
Andrea Campanella52125412015-12-03 14:50:40 -0800221
Simon Huntc2bfe332015-12-04 11:01:24 -0800222 function renderBadge(node, bdg, boff) {
223 var bsel,
224 bcr = badgeConfig.radius,
225 bcgd = badgeConfig.gdelta;
Andrea Campanella52125412015-12-03 14:50:40 -0800226
Simon Huntc2bfe332015-12-04 11:01:24 -0800227 node.select('g.badge').remove();
Andrea Campanella52125412015-12-03 14:50:40 -0800228
Simon Huntc2bfe332015-12-04 11:01:24 -0800229 bsel = node.append('g')
230 .classed('badge', true)
231 .classed(badgeStatus(bdg), true)
232 .attr('transform', sus.translate(boff.dx, boff.dy));
Andrea Campanella52125412015-12-03 14:50:40 -0800233
Simon Huntc2bfe332015-12-04 11:01:24 -0800234 bsel.append('circle')
235 .attr('r', bcr);
236
237 if (bdg.txt) {
238 bsel.append('text')
239 .attr('dy', badgeConfig.yoff)
240 .attr('text-anchor', 'middle')
241 .text(bdg.txt);
242 } else if (bdg.gid) {
243 bsel.append('use')
244 .attr({
245 width: bcgd * 2,
246 height: bcgd * 2,
247 transform: sus.translate(-bcgd, -bcgd),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100248 'xlink:href': '#' + bdg.gid,
Simon Huntc2bfe332015-12-04 11:01:24 -0800249 });
Andrea Campanella52125412015-12-03 14:50:40 -0800250 }
251 }
252
Simon Hunta4242de2015-02-24 17:11:55 -0800253 function updateHostLabel(d) {
254 var label = trimLabel(hostLabel(d));
255 d.el.select('text').text(label);
256 }
257
258 function updateDeviceColors(d) {
259 if (d) {
260 setDeviceColor(d);
261 } else {
262 api.node().filter('.device').each(function (d) {
263 setDeviceColor(d);
264 });
265 }
266 }
267
268
269 // ==========================
270 // updateNodes - subfunctions
271
272 function deviceExisting(d) {
273 var node = d.el;
274 node.classed('online', d.online);
Simon Hunt5674db92015-10-22 16:12:48 -0700275 updateDeviceRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800276 api.posNode(d, true);
277 }
278
279 function hostExisting(d) {
Andrea Campanella52125412015-12-03 14:50:40 -0800280 updateHostRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800281 api.posNode(d, true);
282 }
283
284 function deviceEnter(d) {
285 var node = d3.select(this),
Simon Hunt1eee51d2016-02-26 19:12:13 -0800286 glyphId = mapDeviceTypeToGlyph(d.type),
Simon Hunta4242de2015-02-24 17:11:55 -0800287 label = trimLabel(deviceLabel(d)),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100288 rect, crect, glyph, labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800289
290 d.el = node;
291
Simon Huntf44d7262016-06-14 14:46:56 -0700292 rect = node.append('rect');
Simon Hunte9062fc2016-12-22 11:40:06 -0800293 crect = node.append('rect');
Simon Hunta4242de2015-02-24 17:11:55 -0800294
Steven Burrows1c2a9682017-07-14 16:52:46 +0100295 node.append('text').text(label)
Simon Huntf44d7262016-06-14 14:46:56 -0700296 .attr('text-anchor', 'left')
297 .attr('y', '0.3em')
Simon Hunta5487ad2016-06-16 13:10:41 -0700298 .attr('x', halfDevIcon + labelPad);
Simon Hunta4242de2015-02-24 17:11:55 -0800299
Simon Huntf44d7262016-06-14 14:46:56 -0700300 glyph = is.addDeviceIcon(node, glyphId, devIconDim);
Simon Hunta4242de2015-02-24 17:11:55 -0800301
Simon Huntf44d7262016-06-14 14:46:56 -0700302 labelWidth = label ? computeLabelWidth(node) : 0;
303
304 rect.attr(iconBox(devIconDim, labelWidth));
Simon Hunte9062fc2016-12-22 11:40:06 -0800305 crect.attr(iconBox(devColorDim, 0));
Simon Huntf44d7262016-06-14 14:46:56 -0700306 glyph.attr(iconBox(devIconDim, 0));
307
Simon Hunta5487ad2016-06-16 13:10:41 -0700308 node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700309
310 d.el.selectAll('*')
311 .style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800312 }
313
314 function hostEnter(d) {
315 var node = d3.select(this),
Simon Hunte9062fc2016-12-22 11:40:06 -0800316 glyphId = mapHostTypeToGlyph(d.type),
Simon Hunta5487ad2016-06-16 13:10:41 -0700317 textDy = hostRadius + 10;
Simon Hunta4242de2015-02-24 17:11:55 -0800318
319 d.el = node;
320 sus.visible(node, api.showHosts());
321
Simon Hunte9062fc2016-12-22 11:40:06 -0800322 is.addHostIcon(node, hostRadius, glyphId);
Simon Hunta4242de2015-02-24 17:11:55 -0800323
324 node.append('text')
325 .text(hostLabel)
326 .attr('dy', textDy)
327 .attr('text-anchor', 'middle');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700328
329 d.el.selectAll('g').style('transform', 'scale(' + api.deviceScale() + ')');
330 d.el.selectAll('text').style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800331 }
332
333 function hostExit(d) {
334 var node = d.el;
335 node.select('use')
336 .style('opacity', 0.5)
337 .transition()
338 .duration(800)
339 .style('opacity', 0);
340
341 node.select('text')
342 .style('opacity', 0.5)
343 .transition()
344 .duration(800)
345 .style('opacity', 0);
346
347 node.select('circle')
348 .style('stroke-fill', '#555')
349 .style('fill', '#888')
350 .style('opacity', 0.5)
351 .transition()
352 .duration(1500)
353 .attr('r', 0);
354 }
355
356 function deviceExit(d) {
357 var node = d.el;
358 node.select('use')
359 .style('opacity', 0.5)
360 .transition()
361 .duration(800)
362 .style('opacity', 0);
363
364 node.selectAll('rect')
365 .style('stroke-fill', '#555')
366 .style('fill', '#888')
367 .style('opacity', 0.5);
368 }
369
370
371 // ==========================
372 // updateLinks - subfunctions
373
Simon Hunta4242de2015-02-24 17:11:55 -0800374 function linkEntering(d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100375
Simon Hunta4242de2015-02-24 17:11:55 -0800376 var link = d3.select(this);
377 d.el = link;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700378 d.el.style('stroke-width', api.linkWidthScale() + 'px');
Simon Hunta4242de2015-02-24 17:11:55 -0800379 api.restyleLinkElement(d);
380 if (d.type() === 'hostLink') {
381 sus.visible(link, api.showHosts());
382 }
383 }
384
385 var linkLabelOffset = '0.3em';
386
387 function applyLinkLabels() {
388 var entering;
389
390 api.updateLinkLabelModel();
391
392 // for elements already existing, we need to update the text
393 // and adjust the rectangle size to fit
394 api.linkLabel().each(function (d) {
395 var el = d3.select(this),
396 rect = el.select('rect'),
397 text = el.select('text');
398 text.text(d.label);
399 rect.attr(rectAroundText(el));
400 });
401
402 entering = api.linkLabel().enter().append('g')
403 .classed('linkLabel', true)
404 .attr('id', function (d) { return d.id; });
405
406 entering.each(function (d) {
407 var el = d3.select(this),
408 rect,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700409 text;
Simon Hunta4242de2015-02-24 17:11:55 -0800410
411 if (d.ldata.type() === 'hostLink') {
412 el.classed('hostLinkLabel', true);
413 sus.visible(el, api.showHosts());
414 }
415
416 d.el = el;
417 rect = el.append('rect');
418 text = el.append('text').text(d.label);
419 rect.attr(rectAroundText(el));
420 text.attr('dy', linkLabelOffset);
421
Carmelo Casconed01eda62016-08-02 10:19:15 -0700422 el.attr('transform', transformLabel(d.ldata.position, d.key));
Simon Hunta4242de2015-02-24 17:11:55 -0800423 });
424
425 // Remove any labels that are no longer required.
426 api.linkLabel().exit().remove();
427 }
428
429 function rectAroundText(el) {
430 var text = el.select('text'),
431 box = text.node().getBBox();
432
433 // translate the bbox so that it is centered on [x,y]
434 box.x = -box.width / 2;
435 box.y = -box.height / 2;
436
437 // add padding
438 box.x -= 1;
439 box.width += 2;
440 return box;
441 }
442
Carmelo Casconed01eda62016-08-02 10:19:15 -0700443 function generateLabelFunction() {
Simon Hunte093e782017-03-31 10:19:08 -0700444 var labels = [],
445 xGap = 15,
446 yGap = 17;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700447
Simon Hunte093e782017-03-31 10:19:08 -0700448 return function (newId, newX, newY) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700449 var idx = -1;
450
Simon Hunte093e782017-03-31 10:19:08 -0700451 labels.forEach(function (lab, i) {
452 var minX, maxX, minY, maxY;
453
454 if (lab.id === newId) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700455 idx = i;
456 return;
457 }
Simon Hunte093e782017-03-31 10:19:08 -0700458 minX = lab.x - xGap;
459 maxX = lab.x + xGap;
460 minY = lab.y - yGap;
461 maxY = lab.y + yGap;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700462
463 if (newX > minX && newX < maxX && newY > minY && newY < maxY) {
464 // labels are overlapped
465 newX = newX - xGap;
466 newY = newY - yGap;
467 }
468 });
469
470 if (idx === -1) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100471 labels.push({ id: newId, x: newX, y: newY });
Simon Hunte093e782017-03-31 10:19:08 -0700472 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100473 labels[idx] = { id: newId, x: newX, y: newY };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700474 }
475
Steven Burrows1c2a9682017-07-14 16:52:46 +0100476 return { x: newX, y: newY };
477 };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700478 }
479
Simon Hunte093e782017-03-31 10:19:08 -0700480 var getLabelPos = generateLabelFunction();
Carmelo Casconed01eda62016-08-02 10:19:15 -0700481
482 function transformLabel(p, id) {
Simon Hunta4242de2015-02-24 17:11:55 -0800483 var dx = p.x2 - p.x1,
484 dy = p.y2 - p.y1,
485 xMid = dx/2 + p.x1,
486 yMid = dy/2 + p.y1;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700487
488 if (id) {
Simon Hunte093e782017-03-31 10:19:08 -0700489 var pos = getLabelPos(id, xMid, yMid);
Carmelo Casconed01eda62016-08-02 10:19:15 -0700490 return sus.translate(pos.x, pos.y);
491 }
492
Simon Hunta4242de2015-02-24 17:11:55 -0800493 return sus.translate(xMid, yMid);
494 }
495
Simon Hunt1a5301e2015-02-25 15:31:25 -0800496 function applyPortLabels(data, portLabelG) {
497 var entering = portLabelG.selectAll('.portLabel')
498 .data(data).enter().append('g')
499 .classed('portLabel', true)
500 .attr('id', function (d) { return d.id; });
501
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700502 var labelScale = portLabelDim / (portLabelDim * zoomer.scale());
503
Simon Hunt1a5301e2015-02-25 15:31:25 -0800504 entering.each(function (d) {
505 var el = d3.select(this),
506 rect = el.append('rect'),
507 text = el.append('text').text(d.num);
508
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700509 rect.attr(rectAroundText(el))
510 .style('transform', 'scale(' + labelScale + ')');
511 text.attr('dy', linkLabelOffset)
512 .style('transform', 'scale(' + labelScale + ')');
513
Simon Hunt969b3c92015-02-25 18:11:31 -0800514 el.attr('transform', sus.translate(d.x, d.y));
Simon Hunt1a5301e2015-02-25 15:31:25 -0800515 });
516 }
517
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700518 function labelPoint(linkPos) {
519 var lengthUpLine = 1 / 3,
520 dx = linkPos.x2 - linkPos.x1,
521 dy = linkPos.y2 - linkPos.y1,
522 movedX = dx * lengthUpLine,
523 movedY = dy * lengthUpLine;
524
525 return {
526 x: movedX,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100527 y: movedY,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700528 };
529 }
530
531 function calcGroupPos(linkPos) {
532 var moved = labelPoint(linkPos);
533 return sus.translate(linkPos.x1 + moved.x, linkPos.y1 + moved.y);
534 }
535
536 // calculates where on the link that the hash line for 5+ label appears
537 function hashAttrs(linkPos) {
538 var hashLength = 25,
539 halfLength = hashLength / 2,
540 dx = linkPos.x2 - linkPos.x1,
541 dy = linkPos.y2 - linkPos.y1,
542 length = Math.sqrt((dx * dx) + (dy * dy)),
543 moveAmtX = (dx / length) * halfLength,
544 moveAmtY = (dy / length) * halfLength,
545 mid = labelPoint(linkPos),
546 angle = Math.atan(dy / dx) + 45;
547
548 return {
549 x1: mid.x - moveAmtX,
550 y1: mid.y - moveAmtY,
551 x2: mid.x + moveAmtX,
552 y2: mid.y + moveAmtY,
553 stroke: api.linkConfig()[ts.theme()].baseColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100554 transform: 'rotate(' + angle + ',' + mid.x + ',' + mid.y + ')',
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700555 };
556 }
557
558 function textLabelPos(linkPos) {
559 var point = labelPoint(linkPos),
560 dist = 20;
561 return {
562 x: point.x + dist,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100563 y: point.y + dist,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700564 };
565 }
566
567 function applyNumLinkLabels(data, lblsG) {
568 var labels = lblsG.selectAll('g.numLinkLabel')
569 .data(data, function (d) { return 'pair-' + d.id; }),
570 entering;
571
572 // update existing labels
573 labels.each(function (d) {
574 var el = d3.select(this);
575
576 el.attr({
Steven Burrows1c2a9682017-07-14 16:52:46 +0100577 transform: function (d) { return calcGroupPos(d.linkCoords); },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700578 });
579 el.select('line')
580 .attr(hashAttrs(d.linkCoords));
581 el.select('text')
582 .attr(textLabelPos(d.linkCoords))
583 .text(d.num);
584 });
585
586 // add new labels
587 entering = labels
588 .enter()
589 .append('g')
590 .attr({
591 transform: function (d) { return calcGroupPos(d.linkCoords); },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100592 id: function (d) { return 'pair-' + d.id; },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700593 })
594 .classed('numLinkLabel', true);
595
596 entering.each(function (d) {
597 var el = d3.select(this);
598
599 el.append('line')
600 .classed('numLinkHash', true)
601 .attr(hashAttrs(d.linkCoords));
602 el.append('text')
603 .classed('numLinkText', true)
604 .attr(textLabelPos(d.linkCoords))
605 .text(d.num);
606 });
607
608 // remove old labels
609 labels.exit().remove();
610 }
611
Simon Hunte2d9dc72017-08-10 15:21:04 -0700612 // invoked after the localization bundle has been received from the server
613 function setLionBundle(bundle) {
614 topoLion = bundle;
615 }
616
Simon Hunta4242de2015-02-24 17:11:55 -0800617 // ==========================
618 // Module definition
619
620 angular.module('ovTopo')
621 .factory('TopoD3Service',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100622 ['SvgUtilService', 'IconService', 'ThemeService',
Thomas Vachuska0af26912016-03-21 21:37:30 -0700623 'PrefsService', 'TopoToolbarService',
Simon Hunta4242de2015-02-24 17:11:55 -0800624
Steven Burrows1c2a9682017-07-14 16:52:46 +0100625 function (_sus_, _is_, _ts_, _ps_, _ttbs_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800626 sus = _sus_;
627 is = _is_;
628 ts = _ts_;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700629 ps = _ps_;
630 ttbs = _ttbs_;
Simon Hunta4242de2015-02-24 17:11:55 -0800631
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700632 function initD3(_api_, _zoomer_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800633 api = _api_;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700634 zoomer = _zoomer_;
Simon Hunta4242de2015-02-24 17:11:55 -0800635 }
636
637 function destroyD3() { }
638
639 return {
640 initD3: initD3,
641 destroyD3: destroyD3,
642
643 incDevLabIndex: incDevLabIndex,
Thomas Vachuska0af26912016-03-21 21:37:30 -0700644 setDevLabIndex: setDevLabIndex,
Simon Hunt10618f62017-06-15 19:30:52 -0700645 incHostLabIndex: incHostLabIndex,
646 setHostLabIndex: setHostLabIndex,
Simon Hunta4242de2015-02-24 17:11:55 -0800647 hostLabel: hostLabel,
648 deviceLabel: deviceLabel,
649 trimLabel: trimLabel,
650
Simon Hunt5674db92015-10-22 16:12:48 -0700651 updateDeviceLabel: updateDeviceRendering,
Simon Hunta4242de2015-02-24 17:11:55 -0800652 updateHostLabel: updateHostLabel,
653 updateDeviceColors: updateDeviceColors,
654
655 deviceExisting: deviceExisting,
656 hostExisting: hostExisting,
657 deviceEnter: deviceEnter,
658 hostEnter: hostEnter,
659 hostExit: hostExit,
660 deviceExit: deviceExit,
661
Simon Hunta4242de2015-02-24 17:11:55 -0800662 linkEntering: linkEntering,
663 applyLinkLabels: applyLinkLabels,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800664 transformLabel: transformLabel,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700665 applyPortLabels: applyPortLabels,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100666 applyNumLinkLabels: applyNumLinkLabels,
Simon Hunte2d9dc72017-08-10 15:21:04 -0700667
668 setLionBundle: setLionBundle,
Simon Hunta4242de2015-02-24 17:11:55 -0800669 };
670 }]);
671}());