GUI -- TopoView - Refactored a bunch of functions out of topoForce into topoD3.
- finally, topoForce is a respectable sub-1000 LOC :)
Change-Id: I2a9ac2881c9d54663faecf338c512a368f17bc34
diff --git a/web/gui/src/main/webapp/app/view/topo/topoD3.js b/web/gui/src/main/webapp/app/view/topo/topoD3.js
new file mode 100644
index 0000000..ced1656
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Topology D3 Module.
+ Functions for manipulating the D3 visualizations of the Topology
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, fs, sus, is, ts;
+
+ // api to topoForce
+ var api;
+ /*
+ node() // get ref to D3 selection of nodes
+ link() // get ref to D3 selection of links
+ linkLabel() // get ref to D3 selection of link labels
+ instVisible() // true if instances panel is visible
+ posNode() // position node
+ showHosts() // true if hosts are to be shown
+ restyleLinkElement() // update link styles based on backing data
+ updateLinkLabelModel() // update backing data for link labels
+ */
+
+ // configuration
+ var devCfg = {
+ xoff: -20,
+ yoff: -18
+ },
+ labelConfig = {
+ imgPad: 16,
+ padLR: 4,
+ padTB: 3,
+ marginLR: 3,
+ marginTB: 2,
+ port: {
+ gap: 3,
+ width: 18,
+ height: 14
+ }
+ },
+ icfg;
+
+ // internal state
+ var deviceLabelIndex = 0,
+ hostLabelIndex = 0;
+
+
+ var dCol = {
+ black: '#000',
+ paleblue: '#acf',
+ offwhite: '#ddd',
+ darkgrey: '#444',
+ midgrey: '#888',
+ lightgrey: '#bbb',
+ orange: '#f90'
+ };
+
+ // note: these are the device icon colors without affinity
+ var dColTheme = {
+ light: {
+ rfill: dCol.offwhite,
+ online: {
+ glyph: dCol.darkgrey,
+ rect: dCol.paleblue
+ },
+ offline: {
+ glyph: dCol.midgrey,
+ rect: dCol.lightgrey
+ }
+ },
+ dark: {
+ rfill: dCol.midgrey,
+ online: {
+ glyph: dCol.darkgrey,
+ rect: dCol.paleblue
+ },
+ offline: {
+ glyph: dCol.midgrey,
+ rect: dCol.darkgrey
+ }
+ }
+ };
+
+ function devBaseColor(d) {
+ var o = d.online ? 'online' : 'offline';
+ return dColTheme[ts.theme()][o];
+ }
+
+ function setDeviceColor(d) {
+ var o = d.online,
+ s = d.el.classed('selected'),
+ c = devBaseColor(d),
+ a = instColor(d.master, o),
+ icon = d.el.select('g.deviceIcon'),
+ g, r;
+
+ if (s) {
+ g = c.glyph;
+ r = dCol.orange;
+ } else if (api.instVisible()) {
+ g = o ? a : c.glyph;
+ r = o ? c.rfill : a;
+ } else {
+ g = c.glyph;
+ r = c.rect;
+ }
+
+ icon.select('use').style('fill', g);
+ icon.select('rect').style('fill', r);
+ }
+
+ function instColor(id, online) {
+ return sus.cat7().getColor(id, !online, ts.theme());
+ }
+
+ // ====
+
+ function incDevLabIndex() {
+ deviceLabelIndex = (deviceLabelIndex+1) % 3;
+ }
+
+ // Returns the newly computed bounding box of the rectangle
+ function adjustRectToFitText(n) {
+ var text = n.select('text'),
+ box = text.node().getBBox(),
+ lab = labelConfig;
+
+ text.attr('text-anchor', 'middle')
+ .attr('y', '-0.8em')
+ .attr('x', lab.imgPad/2);
+
+ // translate the bbox so that it is centered on [x,y]
+ box.x = -box.width / 2;
+ box.y = -box.height / 2;
+
+ // add padding
+ box.x -= (lab.padLR + lab.imgPad/2);
+ box.width += lab.padLR * 2 + lab.imgPad;
+ box.y -= lab.padTB;
+ box.height += lab.padTB * 2;
+
+ return box;
+ }
+
+ function hostLabel(d) {
+ var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
+ return d.labels[idx];
+ }
+ function deviceLabel(d) {
+ var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
+ return d.labels[idx];
+ }
+ function trimLabel(label) {
+ return (label && label.trim()) || '';
+ }
+
+ function emptyBox() {
+ return {
+ x: -2,
+ y: -2,
+ width: 4,
+ height: 4
+ };
+ }
+
+
+ function updateDeviceLabel(d) {
+ var label = trimLabel(deviceLabel(d)),
+ noLabel = !label,
+ node = d.el,
+ dim = icfg.device.dim,
+ box, dx, dy;
+
+ node.select('text')
+ .text(label)
+ .style('opacity', 0)
+ .transition()
+ .style('opacity', 1);
+
+ if (noLabel) {
+ box = emptyBox();
+ dx = -dim/2;
+ dy = -dim/2;
+ } else {
+ box = adjustRectToFitText(node);
+ dx = box.x + devCfg.xoff;
+ dy = box.y + devCfg.yoff;
+ }
+
+ node.select('rect')
+ .transition()
+ .attr(box);
+
+ node.select('g.deviceIcon')
+ .transition()
+ .attr('transform', sus.translate(dx, dy));
+ }
+
+ function updateHostLabel(d) {
+ var label = trimLabel(hostLabel(d));
+ d.el.select('text').text(label);
+ }
+
+ function updateDeviceColors(d) {
+ if (d) {
+ setDeviceColor(d);
+ } else {
+ api.node().filter('.device').each(function (d) {
+ setDeviceColor(d);
+ });
+ }
+ }
+
+
+ // ==========================
+ // updateNodes - subfunctions
+
+ function deviceExisting(d) {
+ var node = d.el;
+ node.classed('online', d.online);
+ updateDeviceLabel(d);
+ api.posNode(d, true);
+ }
+
+ function hostExisting(d) {
+ updateHostLabel(d);
+ api.posNode(d, true);
+ }
+
+ function deviceEnter(d) {
+ var node = d3.select(this),
+ glyphId = d.type || 'unknown',
+ label = trimLabel(deviceLabel(d)),
+ //devCfg = deviceIconConfig,
+ noLabel = !label,
+ box, dx, dy, icon;
+
+ d.el = node;
+
+ node.append('rect').attr({ rx: 5, ry: 5 });
+ node.append('text').text(label).attr('dy', '1.1em');
+ box = adjustRectToFitText(node);
+ node.select('rect').attr(box);
+
+ icon = is.addDeviceIcon(node, glyphId);
+
+ if (noLabel) {
+ dx = -icon.dim/2;
+ dy = -icon.dim/2;
+ } else {
+ box = adjustRectToFitText(node);
+ dx = box.x + devCfg.xoff;
+ dy = box.y + devCfg.yoff;
+ }
+
+ icon.attr('transform', sus.translate(dx, dy));
+ }
+
+ function hostEnter(d) {
+ var node = d3.select(this),
+ gid = d.type || 'unknown',
+ rad = icfg.host.radius,
+ r = d.type ? rad.withGlyph : rad.noGlyph,
+ textDy = r + 10;
+
+ d.el = node;
+ sus.visible(node, api.showHosts());
+
+ is.addHostIcon(node, r, gid);
+
+ node.append('text')
+ .text(hostLabel)
+ .attr('dy', textDy)
+ .attr('text-anchor', 'middle');
+ }
+
+ function hostExit(d) {
+ var node = d.el;
+ node.select('use')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(800)
+ .style('opacity', 0);
+
+ node.select('text')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(800)
+ .style('opacity', 0);
+
+ node.select('circle')
+ .style('stroke-fill', '#555')
+ .style('fill', '#888')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(1500)
+ .attr('r', 0);
+ }
+
+ function deviceExit(d) {
+ var node = d.el;
+ node.select('use')
+ .style('opacity', 0.5)
+ .transition()
+ .duration(800)
+ .style('opacity', 0);
+
+ node.selectAll('rect')
+ .style('stroke-fill', '#555')
+ .style('fill', '#888')
+ .style('opacity', 0.5);
+ }
+
+
+ // ==========================
+ // updateLinks - subfunctions
+
+ function linkExisting(d) {
+ // this is supposed to be an existing link, but we have observed
+ // occasions (where links are deleted and added rapidly?) where
+ // the DOM element has not been defined. So protection against that...
+ if (d.el) {
+ api.restyleLinkElement(d, true);
+ }
+ }
+
+ function linkEntering(d) {
+ var link = d3.select(this);
+ d.el = link;
+ api.restyleLinkElement(d);
+ if (d.type() === 'hostLink') {
+ sus.visible(link, api.showHosts());
+ }
+ }
+
+ var linkLabelOffset = '0.3em';
+
+ function applyLinkLabels() {
+ var entering;
+
+ api.updateLinkLabelModel();
+
+ // for elements already existing, we need to update the text
+ // and adjust the rectangle size to fit
+ api.linkLabel().each(function (d) {
+ var el = d3.select(this),
+ rect = el.select('rect'),
+ text = el.select('text');
+ text.text(d.label);
+ rect.attr(rectAroundText(el));
+ });
+
+ entering = api.linkLabel().enter().append('g')
+ .classed('linkLabel', true)
+ .attr('id', function (d) { return d.id; });
+
+ entering.each(function (d) {
+ var el = d3.select(this),
+ rect,
+ text,
+ parms = {
+ x1: d.ldata.source.x,
+ y1: d.ldata.source.y,
+ x2: d.ldata.target.x,
+ y2: d.ldata.target.y
+ };
+
+ if (d.ldata.type() === 'hostLink') {
+ el.classed('hostLinkLabel', true);
+ sus.visible(el, api.showHosts());
+ }
+
+ d.el = el;
+ rect = el.append('rect');
+ text = el.append('text').text(d.label);
+ rect.attr(rectAroundText(el));
+ text.attr('dy', linkLabelOffset);
+
+ el.attr('transform', transformLabel(parms));
+ });
+
+ // Remove any labels that are no longer required.
+ api.linkLabel().exit().remove();
+ }
+
+ function rectAroundText(el) {
+ var text = el.select('text'),
+ box = text.node().getBBox();
+
+ // translate the bbox so that it is centered on [x,y]
+ box.x = -box.width / 2;
+ box.y = -box.height / 2;
+
+ // add padding
+ box.x -= 1;
+ box.width += 2;
+ return box;
+ }
+
+ function transformLabel(p) {
+ var dx = p.x2 - p.x1,
+ dy = p.y2 - p.y1,
+ xMid = dx/2 + p.x1,
+ yMid = dy/2 + p.y1;
+ return sus.translate(xMid, yMid);
+ }
+
+
+ // ==========================
+ // Module definition
+
+ angular.module('ovTopo')
+ .factory('TopoD3Service',
+ ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
+
+ function (_$log_, _fs_, _sus_, _is_, _ts_) {
+ $log = _$log_;
+ fs = _fs_;
+ sus = _sus_;
+ is = _is_;
+ ts = _ts_;
+
+ icfg = is.iconConfig();
+
+ function initD3(_api_) {
+ api = _api_;
+ }
+
+ function destroyD3() { }
+
+ return {
+ initD3: initD3,
+ destroyD3: destroyD3,
+
+ incDevLabIndex: incDevLabIndex,
+ adjustRectToFitText: adjustRectToFitText,
+ hostLabel: hostLabel,
+ deviceLabel: deviceLabel,
+ trimLabel: trimLabel,
+
+ updateDeviceLabel: updateDeviceLabel,
+ updateHostLabel: updateHostLabel,
+ updateDeviceColors: updateDeviceColors,
+
+ deviceExisting: deviceExisting,
+ hostExisting: hostExisting,
+ deviceEnter: deviceEnter,
+ hostEnter: hostEnter,
+ hostExit: hostExit,
+ deviceExit: deviceExit,
+
+ linkExisting: linkExisting,
+ linkEntering: linkEntering,
+ applyLinkLabels: applyLinkLabels,
+
+ transformLabel: transformLabel
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index da42832..d97111f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -23,28 +23,10 @@
'use strict';
// injected refs
- var $log, fs, sus, is, ts, flash, tis, tms, tss, tts, tos, fltr,
+ var $log, fs, sus, is, ts, flash, tis, tms, td3, tss, tts, tos, fltr,
icfg, uplink;
// configuration
- var labelConfig = {
- imgPad: 16,
- padLR: 4,
- padTB: 3,
- marginLR: 3,
- marginTB: 2,
- port: {
- gap: 3,
- width: 18,
- height: 14
- }
- };
-
- var deviceIconConfig = {
- xoff: -20,
- yoff: -18
- };
-
var linkConfig = {
light: {
baseColor: '#666',
@@ -72,8 +54,6 @@
},
lu = network.lookup, // shorthand
rlk = network.revLinkToKey,
- deviceLabelIndex = 0, // for device label cycling
- hostLabelIndex = 0, // for host label cycling
showHosts = false, // whether hosts are displayed
showOffline = true, // whether offline devices are displayed
nodeLock = false, // whether nodes can be dragged or not (locked)
@@ -152,9 +132,6 @@
tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
updateOfflineVisibility(d);
}
- } else {
- // TODO: decide whether we want to capture logic errors
- //logicError('updateDevice lookup fail. ID = "' + id + '"');
}
}
@@ -163,9 +140,6 @@
d = lu[id];
if (d) {
removeDeviceElement(d);
- } else {
- // TODO: decide whether we want to capture logic errors
- //logicError('removeDevice lookup fail. ID = "' + id + '"');
}
}
@@ -206,9 +180,6 @@
sendUpdateMeta(d);
}
updateNodes();
- } else {
- // TODO: decide whether we want to capture logic errors
- //logicError('updateHost lookup fail. ID = "' + id + '"');
}
}
@@ -217,9 +188,6 @@
d = lu[id];
if (d) {
removeHostElement(d, true);
- } else {
- // may have already removed host, if attached to removed device
- //console.warn('removeHost lookup fail. ID = "' + id + '"');
}
}
@@ -260,14 +228,11 @@
}
function removeLink(data) {
- var result = tms.findLink(data, 'remove'),
- bad = result.badLogic;
- if (bad) {
- // may have already removed link, if attached to removed device
- //console.warn(bad + ': ' + link.id);
- return;
+ var result = tms.findLink(data, 'remove');
+
+ if (!result.badLogic) {
+ result.removeRawLink();
}
- result.removeRawLink();
}
// ========================
@@ -417,107 +382,10 @@
}
- // ==========================
- // === Devices and hosts - D3 rendering
-
-
- // Returns the newly computed bounding box of the rectangle
- function adjustRectToFitText(n) {
- var text = n.select('text'),
- box = text.node().getBBox(),
- lab = labelConfig;
-
- text.attr('text-anchor', 'middle')
- .attr('y', '-0.8em')
- .attr('x', lab.imgPad/2);
-
- // translate the bbox so that it is centered on [x,y]
- box.x = -box.width / 2;
- box.y = -box.height / 2;
-
- // add padding
- box.x -= (lab.padLR + lab.imgPad/2);
- box.width += lab.padLR * 2 + lab.imgPad;
- box.y -= lab.padTB;
- box.height += lab.padTB * 2;
-
- return box;
- }
-
function mkSvgClass(d) {
return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
}
- function hostLabel(d) {
- var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
- return d.labels[idx];
- }
- function deviceLabel(d) {
- var idx = (deviceLabelIndex < d.labels.length) ? deviceLabelIndex : 0;
- return d.labels[idx];
- }
- function trimLabel(label) {
- return (label && label.trim()) || '';
- }
-
- function emptyBox() {
- return {
- x: -2,
- y: -2,
- width: 4,
- height: 4
- };
- }
-
-
- function updateDeviceLabel(d) {
- var label = trimLabel(deviceLabel(d)),
- noLabel = !label,
- node = d.el,
- dim = icfg.device.dim,
- devCfg = deviceIconConfig,
- box, dx, dy;
-
- node.select('text')
- .text(label)
- .style('opacity', 0)
- .transition()
- .style('opacity', 1);
-
- if (noLabel) {
- box = emptyBox();
- dx = -dim/2;
- dy = -dim/2;
- } else {
- box = adjustRectToFitText(node);
- dx = box.x + devCfg.xoff;
- dy = box.y + devCfg.yoff;
- }
-
- node.select('rect')
- .transition()
- .attr(box);
-
- node.select('g.deviceIcon')
- .transition()
- .attr('transform', sus.translate(dx, dy));
- }
-
- function updateHostLabel(d) {
- var label = trimLabel(hostLabel(d));
- d.el.select('text').text(label);
- }
-
- function updateDeviceColors(d) {
- if (d) {
- setDeviceColor(d);
- } else {
- node.filter('.device').each(function (d) {
- setDeviceColor(d);
- });
- }
- }
-
function vis(b) {
return b ? 'visible' : 'hidden';
}
@@ -535,9 +403,9 @@
}
function cycleDeviceLabels() {
- deviceLabelIndex = (deviceLabelIndex+1) % 3;
+ td3.incDevLabIndex();
tms.findDevices().forEach(function (d) {
- updateDeviceLabel(d);
+ td3.updateDeviceLabel(d);
});
}
@@ -583,84 +451,14 @@
// ==========================================
- var dCol = {
- black: '#000',
- paleblue: '#acf',
- offwhite: '#ddd',
- darkgrey: '#444',
- midgrey: '#888',
- lightgrey: '#bbb',
- orange: '#f90'
- };
-
- // note: these are the device icon colors without affinity
- var dColTheme = {
- light: {
- rfill: dCol.offwhite,
- online: {
- glyph: dCol.darkgrey,
- rect: dCol.paleblue
- },
- offline: {
- glyph: dCol.midgrey,
- rect: dCol.lightgrey
- }
- },
- dark: {
- rfill: dCol.midgrey,
- online: {
- glyph: dCol.darkgrey,
- rect: dCol.paleblue
- },
- offline: {
- glyph: dCol.midgrey,
- rect: dCol.darkgrey
- }
- }
- };
-
- function devBaseColor(d) {
- var o = d.online ? 'online' : 'offline';
- return dColTheme[ts.theme()][o];
- }
-
- function setDeviceColor(d) {
- var o = d.online,
- s = d.el.classed('selected'),
- c = devBaseColor(d),
- a = instColor(d.master, o),
- icon = d.el.select('g.deviceIcon'),
- g, r;
-
- if (s) {
- g = c.glyph;
- r = dCol.orange;
- } else if (tis.isVisible()) {
- g = o ? a : c.glyph;
- r = o ? c.rfill : a;
- } else {
- g = c.glyph;
- r = c.rect;
- }
-
- icon.select('use').style('fill', g);
- icon.select('rect').style('fill', r);
- }
-
- function instColor(id, online) {
- return sus.cat7().getColor(id, !online, ts.theme());
- }
-
- // ==========================
-
function updateNodes() {
// select all the nodes in the layout:
node = nodeG.selectAll('.node')
.data(network.nodes, function (d) { return d.id; });
// operate on existing nodes:
- node.filter('.device').each(deviceExisting);
- node.filter('.host').each(hostExisting);
+ node.filter('.device').each(td3.deviceExisting);
+ node.filter('.host').each(td3.hostExisting);
// operate on entering nodes:
var entering = node.enter()
@@ -678,11 +476,11 @@
.attr('opacity', 1);
// augment entering nodes:
- entering.filter('.device').each(deviceEnter);
- entering.filter('.host').each(hostEnter);
+ entering.filter('.device').each(td3.deviceEnter);
+ entering.filter('.host').each(td3.hostEnter);
// operate on both existing and new nodes:
- updateDeviceColors();
+ td3.updateDeviceColors();
// operate on exiting nodes:
// Note that the node is removed after 2 seconds.
@@ -694,113 +492,14 @@
.remove();
// exiting node specifics:
- exiting.filter('.host').each(hostExit);
- exiting.filter('.device').each(deviceExit);
+ exiting.filter('.host').each(td3.hostExit);
+ exiting.filter('.device').each(td3.deviceExit);
// finally, resume the force layout
fResume();
}
// ==========================
- // updateNodes - subfunctions
-
- function deviceExisting(d) {
- var node = d.el;
- node.classed('online', d.online);
- updateDeviceLabel(d);
- tms.positionNode(d, true);
- }
-
- function hostExisting(d) {
- updateHostLabel(d);
- tms.positionNode(d, true);
- }
-
- function deviceEnter(d) {
- var node = d3.select(this),
- glyphId = d.type || 'unknown',
- label = trimLabel(deviceLabel(d)),
- devCfg = deviceIconConfig,
- noLabel = !label,
- box, dx, dy, icon;
-
- d.el = node;
-
- node.append('rect').attr({ rx: 5, ry: 5 });
- node.append('text').text(label).attr('dy', '1.1em');
- box = adjustRectToFitText(node);
- node.select('rect').attr(box);
-
- icon = is.addDeviceIcon(node, glyphId);
-
- if (noLabel) {
- dx = -icon.dim/2;
- dy = -icon.dim/2;
- } else {
- box = adjustRectToFitText(node);
- dx = box.x + devCfg.xoff;
- dy = box.y + devCfg.yoff;
- }
-
- icon.attr('transform', sus.translate(dx, dy));
- }
-
- function hostEnter(d) {
- var node = d3.select(this),
- gid = d.type || 'unknown',
- rad = icfg.host.radius,
- r = d.type ? rad.withGlyph : rad.noGlyph,
- textDy = r + 10;
-
- d.el = node;
- sus.visible(node, showHosts);
-
- is.addHostIcon(node, r, gid);
-
- node.append('text')
- .text(hostLabel)
- .attr('dy', textDy)
- .attr('text-anchor', 'middle');
- }
-
- function hostExit(d) {
- var node = d.el;
- node.select('use')
- .style('opacity', 0.5)
- .transition()
- .duration(800)
- .style('opacity', 0);
-
- node.select('text')
- .style('opacity', 0.5)
- .transition()
- .duration(800)
- .style('opacity', 0);
-
- node.select('circle')
- .style('stroke-fill', '#555')
- .style('fill', '#888')
- .style('opacity', 0.5)
- .transition()
- .duration(1500)
- .attr('r', 0);
- }
-
- function deviceExit(d) {
- var node = d.el;
- node.select('use')
- .style('opacity', 0.5)
- .transition()
- .duration(800)
- .style('opacity', 0);
-
- node.selectAll('rect')
- .style('stroke-fill', '#555')
- .style('fill', '#888')
- .style('opacity', 0.5);
- }
-
- // ==========================
function updateLinks() {
var th = ts.theme();
@@ -809,7 +508,7 @@
.data(network.links, function (d) { return d.key; });
// operate on existing links:
- link.each(linkExisting);
+ link.each(td3.linkExisting);
// operate on entering links:
var entering = link.enter()
@@ -824,14 +523,13 @@
});
// augment links
- entering.each(linkEntering);
+ entering.each(td3.linkEntering);
// operate on both existing and new links:
//link.each(...)
// apply or remove labels
- var labelData = getLabelData();
- applyLinkLabels(labelData);
+ td3.applyLinkLabels();
// operate on exiting links:
link.exit()
@@ -848,117 +546,6 @@
.remove();
}
- // ==========================
- // updateLinks - subfunctions
-
- function getLabelData() {
- // create the backing data for showing labels..
- var data = [];
- link.each(function (d) {
- if (d.label) {
- data.push({
- id: 'lab-' + d.key,
- key: d.key,
- label: d.label,
- ldata: d
- });
- }
- });
- return data;
- }
-
- function linkExisting(d) {
- // this is supposed to be an existing link, but we have observed
- // occasions (where links are deleted and added rapidly?) where
- // the DOM element has not been defined. So protection against that...
- if (d.el) {
- restyleLinkElement(d, true);
- }
- }
-
- function linkEntering(d) {
- var link = d3.select(this);
- d.el = link;
- restyleLinkElement(d);
- if (d.type() === 'hostLink') {
- sus.visible(link, showHosts);
- }
- }
-
- //function linkExiting(d) { }
-
- var linkLabelOffset = '0.3em';
-
- function applyLinkLabels(data) {
- var entering;
-
- linkLabel = linkLabelG.selectAll('.linkLabel')
- .data(data, function (d) { return d.id; });
-
- // for elements already existing, we need to update the text
- // and adjust the rectangle size to fit
- linkLabel.each(function (d) {
- var el = d3.select(this),
- rect = el.select('rect'),
- text = el.select('text');
- text.text(d.label);
- rect.attr(rectAroundText(el));
- });
-
- entering = linkLabel.enter().append('g')
- .classed('linkLabel', true)
- .attr('id', function (d) { return d.id; });
-
- entering.each(function (d) {
- var el = d3.select(this),
- rect,
- text,
- parms = {
- x1: d.ldata.source.x,
- y1: d.ldata.source.y,
- x2: d.ldata.target.x,
- y2: d.ldata.target.y
- };
-
- if (d.ldata.type() === 'hostLink') {
- el.classed('hostLinkLabel', true);
- sus.visible(el, showHosts);
- }
-
- d.el = el;
- rect = el.append('rect');
- text = el.append('text').text(d.label);
- rect.attr(rectAroundText(el));
- text.attr('dy', linkLabelOffset);
-
- el.attr('transform', transformLabel(parms));
- });
-
- // Remove any labels that are no longer required.
- linkLabel.exit().remove();
- }
-
- function rectAroundText(el) {
- var text = el.select('text'),
- box = text.node().getBBox();
-
- // translate the bbox so that it is centered on [x,y]
- box.x = -box.width / 2;
- box.y = -box.height / 2;
-
- // add padding
- box.x -= 1;
- box.width += 2;
- return box;
- }
-
- function transformLabel(p) {
- var dx = p.x2 - p.x1,
- dy = p.y2 - p.y1,
- xMid = dx/2 + p.x1,
- yMid = dy/2 + p.y1;
- return sus.translate(xMid, yMid);
- }
// ==========================
// force layout tick function
@@ -989,7 +576,7 @@
transform: function (d) {
var lnk = tms.findLinkById(d.key);
if (lnk) {
- return transformLabel({
+ return td3.transformLabel({
x1: lnk.source.x,
y1: lnk.source.y,
x2: lnk.target.x,
@@ -1049,6 +636,24 @@
});
}
+ function updateLinkLabelModel() {
+ // create the backing data for showing labels..
+ var data = [];
+ link.each(function (d) {
+ if (d.label) {
+ data.push({
+ id: 'lab-' + d.key,
+ key: d.key,
+ label: d.label,
+ ldata: d
+ });
+ }
+ });
+
+ linkLabel = linkLabelG.selectAll('.linkLabel')
+ .data(data, function (d) { return d.id; });
+ }
+
// ==========================
// Module definition
@@ -1061,11 +666,24 @@
};
}
+ function mkD3Api(uplink) {
+ return {
+ node: function () { return node; },
+ link: function () { return link; },
+ linkLabel: function () { return linkLabel; },
+ instVisible: function () { return tis.isVisible(); },
+ posNode: tms.positionNode,
+ showHosts: function () { return showHosts; },
+ restyleLinkElement: restyleLinkElement,
+ updateLinkLabelModel: updateLinkLabelModel
+ }
+ }
+
function mkSelectApi(uplink) {
return {
node: function () { return node; },
zoomingOrPanning: zoomingOrPanning,
- updateDeviceColors: updateDeviceColors,
+ updateDeviceColors: td3.updateDeviceColors,
sendEvent: uplink.sendEvent
};
}
@@ -1114,11 +732,11 @@
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
'FlashService', 'TopoInstService', 'TopoModelService',
- 'TopoSelectService', 'TopoTrafficService',
+ 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
'TopoObliqueService', 'TopoFilterService',
function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_,
- _tis_, _tms_, _tss_, _tts_, _tos_, _fltr_) {
+ _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
@@ -1127,6 +745,7 @@
flash = _flash_;
tis = _tis_;
tms = _tms_;
+ td3 = _td3_;
tss = _tss_;
tts = _tts_;
tos = _tos_;
@@ -1150,6 +769,7 @@
$log.debug('initForce().. dim = ' + dim);
tms.initModel(mkModelApi(uplink), dim);
+ td3.initD3(mkD3Api(uplink));
tss.initSelect(mkSelectApi(uplink));
tts.initTraffic(mkTrafficApi(uplink));
tos.initOblique(mkObliqueApi(uplink, fltr));
@@ -1192,6 +812,7 @@
tos.destroyOblique();
tts.destroyTraffic();
tss.destroySelect();
+ td3.destroyD3();
tms.destroyModel();
ts.removeListener(themeListener);
themeListener = null;
@@ -1202,7 +823,7 @@
newDim: newDim,
destroyForce: destroyForce,
- updateDeviceColors: updateDeviceColors,
+ updateDeviceColors: td3.updateDeviceColors,
toggleHosts: toggleHosts,
toggleOffline: toggleOffline,
cycleDeviceLabels: cycleDeviceLabels,
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 083162a..530bfa9 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -86,6 +86,7 @@
<!-- {INJECTED-JAVASCRIPT-START} -->
<script src="app/view/sample/sample.js"></script>
<script src="app/view/topo/topo.js"></script>
+ <script src="app/view/topo/topoD3.js"></script>
<script src="app/view/topo/topoEvent.js"></script>
<script src="app/view/topo/topoFilter.js"></script>
<script src="app/view/topo/topoForce.js"></script>
@@ -104,7 +105,6 @@
<link rel="stylesheet" href="app/view/sample/sample.css">
<link rel="stylesheet" href="app/view/topo/topo.css">
<link rel="stylesheet" href="app/view/device/device.css">
- <!-- TODO: inject style-sheet refs server-side -->
<!-- {INJECTED-STYLESHEETS-END} -->
</head>