blob: 1b73fbf2ed21e5e94469ae9fb925332f650615b3 [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',
Andrea Campanella1c24fb92018-12-20 16:43:59 +010077 ols: 'm_roadm',
78 terminal_device: 'm_otn',
Simon Hunte9062fc2016-12-22 11:40:06 -080079 roadm_otn: 'm_roadm_otn',
80 fiber_switch: 'm_fiberSwitch',
81 microwave: 'm_microwave',
82 };
83
84 var remappedHostTypes = {
85 router: 'm_router',
86 endstation: 'm_endstation',
Steven Burrows1c2a9682017-07-14 16:52:46 +010087 bgpSpeaker: 'm_bgpSpeaker',
Simon Hunt1eee51d2016-02-26 19:12:13 -080088 };
89
90 function mapDeviceTypeToGlyph(type) {
91 return remappedDeviceTypes[type] || type || 'unknown';
92 }
93
Simon Hunte9062fc2016-12-22 11:40:06 -080094 function mapHostTypeToGlyph(type) {
95 return remappedHostTypes[type] || type || 'unknown';
96 }
97
Simon Hunt5674db92015-10-22 16:12:48 -070098 function badgeStatus(badge) {
99 return status[badge.status] || status.i;
100 }
101
Simon Hunta4242de2015-02-24 17:11:55 -0800102 // internal state
103 var deviceLabelIndex = 0,
104 hostLabelIndex = 0;
105
Simon Hunta5487ad2016-06-16 13:10:41 -0700106 // note: these are the device icon colors without affinity (no master)
Simon Hunta4242de2015-02-24 17:11:55 -0800107 var dColTheme = {
108 light: {
Simon Huntf44d7262016-06-14 14:46:56 -0700109 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100110 offline: '#cccccc',
Simon Hunta4242de2015-02-24 17:11:55 -0800111 },
112 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -0700113 // TODO: theme
114 online: '#444444',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100115 offline: '#cccccc',
116 },
Simon Hunta4242de2015-02-24 17:11:55 -0800117 };
118
Simon Huntf44d7262016-06-14 14:46:56 -0700119 function devGlyphColor(d) {
120 var o = d.online,
121 id = d.master,
122 otag = o ? 'online' : 'offline';
123 return o ? sus.cat7().getColor(id, 0, ts.theme())
124 : dColTheme[ts.theme()][otag];
Simon Hunta4242de2015-02-24 17:11:55 -0800125 }
126
127 function setDeviceColor(d) {
Simon Hunte9062fc2016-12-22 11:40:06 -0800128 // want to color the square rectangle (no longer the 'use' glyph)
Steven Burrows1c2a9682017-07-14 16:52:46 +0100129 d.el.selectAll('rect').filter(function (d, i) { return i === 1; })
Simon Huntf44d7262016-06-14 14:46:56 -0700130 .style('fill', devGlyphColor(d));
Simon Hunta4242de2015-02-24 17:11:55 -0800131 }
132
Simon Hunta4242de2015-02-24 17:11:55 -0800133 function incDevLabIndex() {
Steven Burrowsbbe3dda2016-09-26 14:41:59 -0700134 setDevLabIndex(deviceLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100135 switch (deviceLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700136 case 0: return topoLion('fl_device_labels_hide');
137 case 1: return topoLion('fl_device_labels_show_friendly');
138 case 2: return topoLion('fl_device_labels_show_id');
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700139 }
Simon Hunta4242de2015-02-24 17:11:55 -0800140 }
141
Thomas Vachuska0af26912016-03-21 21:37:30 -0700142 function setDevLabIndex(mode) {
143 deviceLabelIndex = mode % 3;
144 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
145 p.dlbls = deviceLabelIndex;
146 ps.setPrefs('topo_prefs', p);
147 }
148
Simon Hunt10618f62017-06-15 19:30:52 -0700149 function incHostLabIndex() {
150 setHostLabIndex(hostLabelIndex+1);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100151 switch (hostLabelIndex) {
Simon Hunte2d9dc72017-08-10 15:21:04 -0700152 case 0: return topoLion('fl_host_labels_show_friendly');
153 case 1: return topoLion('fl_host_labels_show_ip');
154 case 2: return topoLion('fl_host_labels_show_mac');
Thomas Vachuska528ea952017-10-09 18:25:35 +0200155 case 3: return topoLion('fl_host_labels_hide');
Simon Hunt10618f62017-06-15 19:30:52 -0700156 }
157 }
158
159 function setHostLabIndex(mode) {
Thomas Vachuska528ea952017-10-09 18:25:35 +0200160 hostLabelIndex = mode % 4;
Simon Hunt10618f62017-06-15 19:30:52 -0700161 var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
162 p.hlbls = hostLabelIndex;
163 ps.setPrefs('topo_prefs', p);
164 }
165
Simon Hunta4242de2015-02-24 17:11:55 -0800166 function hostLabel(d) {
167 var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
168 return d.labels[idx];
169 }
Simon Huntf44d7262016-06-14 14:46:56 -0700170
Simon Hunta4242de2015-02-24 17:11:55 -0800171 function deviceLabel(d) {
172 var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
173 return d.labels[idx];
174 }
Simon Huntf44d7262016-06-14 14:46:56 -0700175
Simon Hunta4242de2015-02-24 17:11:55 -0800176 function trimLabel(label) {
177 return (label && label.trim()) || '';
178 }
179
Simon Huntf44d7262016-06-14 14:46:56 -0700180 function computeLabelWidth(n) {
181 var text = n.select('text'),
182 box = text.node().getBBox();
183 return box.width + labelPad * 2;
184 }
185
186 function iconBox(dim, labelWidth) {
Simon Hunta4242de2015-02-24 17:11:55 -0800187 return {
Simon Huntf44d7262016-06-14 14:46:56 -0700188 x: -dim/2,
189 y: -dim/2,
190 width: dim + labelWidth,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100191 height: dim,
192 };
Simon Hunta4242de2015-02-24 17:11:55 -0800193 }
194
Simon Hunt5674db92015-10-22 16:12:48 -0700195 function updateDeviceRendering(d) {
Simon Huntf44d7262016-06-14 14:46:56 -0700196 var node = d.el,
197 bdg = d.badge,
198 label = trimLabel(deviceLabel(d)),
199 labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800200
Simon Huntf44d7262016-06-14 14:46:56 -0700201 node.select('text').text(label);
202 labelWidth = label ? computeLabelWidth(node) : 0;
Simon Hunta4242de2015-02-24 17:11:55 -0800203
204 node.select('rect')
205 .transition()
Simon Huntf44d7262016-06-14 14:46:56 -0700206 .attr(iconBox(devIconDim, labelWidth));
Simon Hunta4242de2015-02-24 17:11:55 -0800207
Simon Hunt5674db92015-10-22 16:12:48 -0700208 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700209 renderBadge(node, bdg, devBadgeOff);
Simon Hunte9343f32015-10-21 18:07:46 -0700210 }
211 }
212
Andrea Campanella52125412015-12-03 14:50:40 -0800213 function updateHostRendering(d) {
214 var node = d.el,
Simon Huntc2bfe332015-12-04 11:01:24 -0800215 bdg = d.badge;
Andrea Campanella52125412015-12-03 14:50:40 -0800216
217 updateHostLabel(d);
Andrea Campanella52125412015-12-03 14:50:40 -0800218
Andrea Campanella52125412015-12-03 14:50:40 -0800219 if (bdg) {
Simon Hunta5487ad2016-06-16 13:10:41 -0700220 renderBadge(node, bdg, hostBadgeOff);
Simon Huntc2bfe332015-12-04 11:01:24 -0800221 }
222 }
Andrea Campanella52125412015-12-03 14:50:40 -0800223
Simon Huntc2bfe332015-12-04 11:01:24 -0800224 function renderBadge(node, bdg, boff) {
225 var bsel,
226 bcr = badgeConfig.radius,
227 bcgd = badgeConfig.gdelta;
Andrea Campanella52125412015-12-03 14:50:40 -0800228
Simon Huntc2bfe332015-12-04 11:01:24 -0800229 node.select('g.badge').remove();
Andrea Campanella52125412015-12-03 14:50:40 -0800230
Simon Huntc2bfe332015-12-04 11:01:24 -0800231 bsel = node.append('g')
232 .classed('badge', true)
233 .classed(badgeStatus(bdg), true)
234 .attr('transform', sus.translate(boff.dx, boff.dy));
Andrea Campanella52125412015-12-03 14:50:40 -0800235
Simon Huntc2bfe332015-12-04 11:01:24 -0800236 bsel.append('circle')
237 .attr('r', bcr);
238
239 if (bdg.txt) {
240 bsel.append('text')
241 .attr('dy', badgeConfig.yoff)
242 .attr('text-anchor', 'middle')
243 .text(bdg.txt);
244 } else if (bdg.gid) {
245 bsel.append('use')
246 .attr({
247 width: bcgd * 2,
248 height: bcgd * 2,
249 transform: sus.translate(-bcgd, -bcgd),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100250 'xlink:href': '#' + bdg.gid,
Simon Huntc2bfe332015-12-04 11:01:24 -0800251 });
Andrea Campanella52125412015-12-03 14:50:40 -0800252 }
253 }
254
Simon Hunta4242de2015-02-24 17:11:55 -0800255 function updateHostLabel(d) {
256 var label = trimLabel(hostLabel(d));
257 d.el.select('text').text(label);
258 }
259
260 function updateDeviceColors(d) {
261 if (d) {
262 setDeviceColor(d);
263 } else {
264 api.node().filter('.device').each(function (d) {
265 setDeviceColor(d);
266 });
267 }
268 }
269
270
271 // ==========================
272 // updateNodes - subfunctions
273
274 function deviceExisting(d) {
275 var node = d.el;
276 node.classed('online', d.online);
Simon Hunt5674db92015-10-22 16:12:48 -0700277 updateDeviceRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800278 api.posNode(d, true);
279 }
280
281 function hostExisting(d) {
Andrea Campanella52125412015-12-03 14:50:40 -0800282 updateHostRendering(d);
Simon Hunta4242de2015-02-24 17:11:55 -0800283 api.posNode(d, true);
284 }
285
286 function deviceEnter(d) {
287 var node = d3.select(this),
Simon Hunt1eee51d2016-02-26 19:12:13 -0800288 glyphId = mapDeviceTypeToGlyph(d.type),
Simon Hunta4242de2015-02-24 17:11:55 -0800289 label = trimLabel(deviceLabel(d)),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100290 rect, crect, glyph, labelWidth;
Simon Hunta4242de2015-02-24 17:11:55 -0800291
292 d.el = node;
293
Simon Huntf44d7262016-06-14 14:46:56 -0700294 rect = node.append('rect');
Simon Hunte9062fc2016-12-22 11:40:06 -0800295 crect = node.append('rect');
Simon Hunta4242de2015-02-24 17:11:55 -0800296
Steven Burrows1c2a9682017-07-14 16:52:46 +0100297 node.append('text').text(label)
Simon Huntf44d7262016-06-14 14:46:56 -0700298 .attr('text-anchor', 'left')
299 .attr('y', '0.3em')
Simon Hunta5487ad2016-06-16 13:10:41 -0700300 .attr('x', halfDevIcon + labelPad);
Simon Hunta4242de2015-02-24 17:11:55 -0800301
Simon Huntf44d7262016-06-14 14:46:56 -0700302 glyph = is.addDeviceIcon(node, glyphId, devIconDim);
Simon Hunta4242de2015-02-24 17:11:55 -0800303
Simon Huntf44d7262016-06-14 14:46:56 -0700304 labelWidth = label ? computeLabelWidth(node) : 0;
305
306 rect.attr(iconBox(devIconDim, labelWidth));
Simon Hunte9062fc2016-12-22 11:40:06 -0800307 crect.attr(iconBox(devColorDim, 0));
Simon Huntf44d7262016-06-14 14:46:56 -0700308 glyph.attr(iconBox(devIconDim, 0));
309
Simon Hunta5487ad2016-06-16 13:10:41 -0700310 node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700311
312 d.el.selectAll('*')
313 .style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800314 }
315
316 function hostEnter(d) {
317 var node = d3.select(this),
Simon Hunte9062fc2016-12-22 11:40:06 -0800318 glyphId = mapHostTypeToGlyph(d.type),
Simon Hunta5487ad2016-06-16 13:10:41 -0700319 textDy = hostRadius + 10;
Simon Hunta4242de2015-02-24 17:11:55 -0800320
321 d.el = node;
322 sus.visible(node, api.showHosts());
323
Simon Hunte9062fc2016-12-22 11:40:06 -0800324 is.addHostIcon(node, hostRadius, glyphId);
Simon Hunta4242de2015-02-24 17:11:55 -0800325
326 node.append('text')
327 .text(hostLabel)
328 .attr('dy', textDy)
329 .attr('text-anchor', 'middle');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700330
331 d.el.selectAll('g').style('transform', 'scale(' + api.deviceScale() + ')');
332 d.el.selectAll('text').style('transform', 'scale(' + api.deviceScale() + ')');
Simon Hunta4242de2015-02-24 17:11:55 -0800333 }
334
335 function hostExit(d) {
336 var node = d.el;
337 node.select('use')
338 .style('opacity', 0.5)
339 .transition()
340 .duration(800)
341 .style('opacity', 0);
342
343 node.select('text')
344 .style('opacity', 0.5)
345 .transition()
346 .duration(800)
347 .style('opacity', 0);
348
349 node.select('circle')
350 .style('stroke-fill', '#555')
351 .style('fill', '#888')
352 .style('opacity', 0.5)
353 .transition()
354 .duration(1500)
355 .attr('r', 0);
356 }
357
358 function deviceExit(d) {
359 var node = d.el;
360 node.select('use')
361 .style('opacity', 0.5)
362 .transition()
363 .duration(800)
364 .style('opacity', 0);
365
366 node.selectAll('rect')
367 .style('stroke-fill', '#555')
368 .style('fill', '#888')
369 .style('opacity', 0.5);
370 }
371
372
373 // ==========================
374 // updateLinks - subfunctions
375
Simon Hunta4242de2015-02-24 17:11:55 -0800376 function linkEntering(d) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100377
Simon Hunta4242de2015-02-24 17:11:55 -0800378 var link = d3.select(this);
379 d.el = link;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700380 d.el.style('stroke-width', api.linkWidthScale() + 'px');
Simon Hunta4242de2015-02-24 17:11:55 -0800381 api.restyleLinkElement(d);
382 if (d.type() === 'hostLink') {
383 sus.visible(link, api.showHosts());
384 }
385 }
386
387 var linkLabelOffset = '0.3em';
388
389 function applyLinkLabels() {
390 var entering;
391
392 api.updateLinkLabelModel();
393
394 // for elements already existing, we need to update the text
395 // and adjust the rectangle size to fit
396 api.linkLabel().each(function (d) {
397 var el = d3.select(this),
398 rect = el.select('rect'),
399 text = el.select('text');
400 text.text(d.label);
401 rect.attr(rectAroundText(el));
402 });
403
404 entering = api.linkLabel().enter().append('g')
405 .classed('linkLabel', true)
406 .attr('id', function (d) { return d.id; });
407
408 entering.each(function (d) {
409 var el = d3.select(this),
410 rect,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700411 text;
Simon Hunta4242de2015-02-24 17:11:55 -0800412
413 if (d.ldata.type() === 'hostLink') {
414 el.classed('hostLinkLabel', true);
415 sus.visible(el, api.showHosts());
416 }
417
418 d.el = el;
419 rect = el.append('rect');
420 text = el.append('text').text(d.label);
421 rect.attr(rectAroundText(el));
422 text.attr('dy', linkLabelOffset);
423
Carmelo Casconed01eda62016-08-02 10:19:15 -0700424 el.attr('transform', transformLabel(d.ldata.position, d.key));
Simon Hunta4242de2015-02-24 17:11:55 -0800425 });
426
427 // Remove any labels that are no longer required.
428 api.linkLabel().exit().remove();
429 }
430
431 function rectAroundText(el) {
432 var text = el.select('text'),
433 box = text.node().getBBox();
434
435 // translate the bbox so that it is centered on [x,y]
436 box.x = -box.width / 2;
437 box.y = -box.height / 2;
438
439 // add padding
440 box.x -= 1;
441 box.width += 2;
442 return box;
443 }
444
Carmelo Casconed01eda62016-08-02 10:19:15 -0700445 function generateLabelFunction() {
Simon Hunte093e782017-03-31 10:19:08 -0700446 var labels = [],
447 xGap = 15,
448 yGap = 17;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700449
Simon Hunte093e782017-03-31 10:19:08 -0700450 return function (newId, newX, newY) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700451 var idx = -1;
452
Simon Hunte093e782017-03-31 10:19:08 -0700453 labels.forEach(function (lab, i) {
454 var minX, maxX, minY, maxY;
455
456 if (lab.id === newId) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700457 idx = i;
458 return;
459 }
Simon Hunte093e782017-03-31 10:19:08 -0700460 minX = lab.x - xGap;
461 maxX = lab.x + xGap;
462 minY = lab.y - yGap;
463 maxY = lab.y + yGap;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700464
465 if (newX > minX && newX < maxX && newY > minY && newY < maxY) {
466 // labels are overlapped
467 newX = newX - xGap;
468 newY = newY - yGap;
469 }
470 });
471
472 if (idx === -1) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100473 labels.push({ id: newId, x: newX, y: newY });
Simon Hunte093e782017-03-31 10:19:08 -0700474 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100475 labels[idx] = { id: newId, x: newX, y: newY };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700476 }
477
Steven Burrows1c2a9682017-07-14 16:52:46 +0100478 return { x: newX, y: newY };
479 };
Carmelo Casconed01eda62016-08-02 10:19:15 -0700480 }
481
Simon Hunte093e782017-03-31 10:19:08 -0700482 var getLabelPos = generateLabelFunction();
Carmelo Casconed01eda62016-08-02 10:19:15 -0700483
484 function transformLabel(p, id) {
Simon Hunta4242de2015-02-24 17:11:55 -0800485 var dx = p.x2 - p.x1,
486 dy = p.y2 - p.y1,
487 xMid = dx/2 + p.x1,
488 yMid = dy/2 + p.y1;
Carmelo Casconed01eda62016-08-02 10:19:15 -0700489
490 if (id) {
Simon Hunte093e782017-03-31 10:19:08 -0700491 var pos = getLabelPos(id, xMid, yMid);
Carmelo Casconed01eda62016-08-02 10:19:15 -0700492 return sus.translate(pos.x, pos.y);
493 }
494
Simon Hunta4242de2015-02-24 17:11:55 -0800495 return sus.translate(xMid, yMid);
496 }
497
Simon Hunt1a5301e2015-02-25 15:31:25 -0800498 function applyPortLabels(data, portLabelG) {
499 var entering = portLabelG.selectAll('.portLabel')
500 .data(data).enter().append('g')
501 .classed('portLabel', true)
502 .attr('id', function (d) { return d.id; });
503
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700504 var labelScale = portLabelDim / (portLabelDim * zoomer.scale());
505
Simon Hunt1a5301e2015-02-25 15:31:25 -0800506 entering.each(function (d) {
507 var el = d3.select(this),
508 rect = el.append('rect'),
509 text = el.append('text').text(d.num);
510
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700511 rect.attr(rectAroundText(el))
512 .style('transform', 'scale(' + labelScale + ')');
513 text.attr('dy', linkLabelOffset)
514 .style('transform', 'scale(' + labelScale + ')');
515
Simon Hunt969b3c92015-02-25 18:11:31 -0800516 el.attr('transform', sus.translate(d.x, d.y));
Simon Hunt1a5301e2015-02-25 15:31:25 -0800517 });
518 }
519
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700520 function labelPoint(linkPos) {
521 var lengthUpLine = 1 / 3,
522 dx = linkPos.x2 - linkPos.x1,
523 dy = linkPos.y2 - linkPos.y1,
524 movedX = dx * lengthUpLine,
525 movedY = dy * lengthUpLine;
526
527 return {
528 x: movedX,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100529 y: movedY,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700530 };
531 }
532
533 function calcGroupPos(linkPos) {
534 var moved = labelPoint(linkPos);
535 return sus.translate(linkPos.x1 + moved.x, linkPos.y1 + moved.y);
536 }
537
538 // calculates where on the link that the hash line for 5+ label appears
539 function hashAttrs(linkPos) {
540 var hashLength = 25,
541 halfLength = hashLength / 2,
542 dx = linkPos.x2 - linkPos.x1,
543 dy = linkPos.y2 - linkPos.y1,
544 length = Math.sqrt((dx * dx) + (dy * dy)),
545 moveAmtX = (dx / length) * halfLength,
546 moveAmtY = (dy / length) * halfLength,
547 mid = labelPoint(linkPos),
548 angle = Math.atan(dy / dx) + 45;
549
550 return {
551 x1: mid.x - moveAmtX,
552 y1: mid.y - moveAmtY,
553 x2: mid.x + moveAmtX,
554 y2: mid.y + moveAmtY,
555 stroke: api.linkConfig()[ts.theme()].baseColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100556 transform: 'rotate(' + angle + ',' + mid.x + ',' + mid.y + ')',
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700557 };
558 }
559
560 function textLabelPos(linkPos) {
561 var point = labelPoint(linkPos),
562 dist = 20;
563 return {
564 x: point.x + dist,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100565 y: point.y + dist,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700566 };
567 }
568
569 function applyNumLinkLabels(data, lblsG) {
570 var labels = lblsG.selectAll('g.numLinkLabel')
571 .data(data, function (d) { return 'pair-' + d.id; }),
572 entering;
573
574 // update existing labels
575 labels.each(function (d) {
576 var el = d3.select(this);
577
578 el.attr({
Steven Burrows1c2a9682017-07-14 16:52:46 +0100579 transform: function (d) { return calcGroupPos(d.linkCoords); },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700580 });
581 el.select('line')
582 .attr(hashAttrs(d.linkCoords));
583 el.select('text')
584 .attr(textLabelPos(d.linkCoords))
585 .text(d.num);
586 });
587
588 // add new labels
589 entering = labels
590 .enter()
591 .append('g')
592 .attr({
593 transform: function (d) { return calcGroupPos(d.linkCoords); },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100594 id: function (d) { return 'pair-' + d.id; },
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700595 })
596 .classed('numLinkLabel', true);
597
598 entering.each(function (d) {
599 var el = d3.select(this);
600
601 el.append('line')
602 .classed('numLinkHash', true)
603 .attr(hashAttrs(d.linkCoords));
604 el.append('text')
605 .classed('numLinkText', true)
606 .attr(textLabelPos(d.linkCoords))
607 .text(d.num);
608 });
609
610 // remove old labels
611 labels.exit().remove();
612 }
613
Simon Hunte2d9dc72017-08-10 15:21:04 -0700614 // invoked after the localization bundle has been received from the server
615 function setLionBundle(bundle) {
616 topoLion = bundle;
617 }
618
Simon Hunta4242de2015-02-24 17:11:55 -0800619 // ==========================
620 // Module definition
621
622 angular.module('ovTopo')
623 .factory('TopoD3Service',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100624 ['SvgUtilService', 'IconService', 'ThemeService',
Thomas Vachuska0af26912016-03-21 21:37:30 -0700625 'PrefsService', 'TopoToolbarService',
Simon Hunta4242de2015-02-24 17:11:55 -0800626
Steven Burrows1c2a9682017-07-14 16:52:46 +0100627 function (_sus_, _is_, _ts_, _ps_, _ttbs_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800628 sus = _sus_;
629 is = _is_;
630 ts = _ts_;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700631 ps = _ps_;
632 ttbs = _ttbs_;
Simon Hunta4242de2015-02-24 17:11:55 -0800633
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700634 function initD3(_api_, _zoomer_) {
Simon Hunta4242de2015-02-24 17:11:55 -0800635 api = _api_;
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700636 zoomer = _zoomer_;
Simon Hunta4242de2015-02-24 17:11:55 -0800637 }
638
639 function destroyD3() { }
640
641 return {
642 initD3: initD3,
643 destroyD3: destroyD3,
644
645 incDevLabIndex: incDevLabIndex,
Thomas Vachuska0af26912016-03-21 21:37:30 -0700646 setDevLabIndex: setDevLabIndex,
Simon Hunt10618f62017-06-15 19:30:52 -0700647 incHostLabIndex: incHostLabIndex,
648 setHostLabIndex: setHostLabIndex,
Simon Hunta4242de2015-02-24 17:11:55 -0800649 hostLabel: hostLabel,
650 deviceLabel: deviceLabel,
651 trimLabel: trimLabel,
652
Simon Hunt5674db92015-10-22 16:12:48 -0700653 updateDeviceLabel: updateDeviceRendering,
Simon Hunta4242de2015-02-24 17:11:55 -0800654 updateHostLabel: updateHostLabel,
655 updateDeviceColors: updateDeviceColors,
656
657 deviceExisting: deviceExisting,
658 hostExisting: hostExisting,
659 deviceEnter: deviceEnter,
660 hostEnter: hostEnter,
661 hostExit: hostExit,
662 deviceExit: deviceExit,
663
Simon Hunta4242de2015-02-24 17:11:55 -0800664 linkEntering: linkEntering,
665 applyLinkLabels: applyLinkLabels,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800666 transformLabel: transformLabel,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700667 applyPortLabels: applyPortLabels,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100668 applyNumLinkLabels: applyNumLinkLabels,
Simon Hunte2d9dc72017-08-10 15:21:04 -0700669
670 setLionBundle: setLionBundle,
Simon Hunta4242de2015-02-24 17:11:55 -0800671 };
672 }]);
673}());