Paul Greyson
Committed by Gerrit Code Review

First pass at svg icons and dark theme for topo

Have not addressed top bar etc

Change-Id: I0cc47a1f500bd9d8589eeaf8042f21ec4c8e6cbe
This diff is collapsed. Click to expand it.
......@@ -40,12 +40,14 @@
<!-- Base library and framework stylesheets included here -->
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="onos2.css">
<link rel="stylesheet" href="onos_theme.css">
<link rel="stylesheet" href="mast2.css">
<link rel="stylesheet" href="floatPanel.css">
<!-- This is where contributed stylesheets get INJECTED -->
<!-- TODO: replace with template marker and inject refs server-side -->
<link rel="stylesheet" href="topo2.css">
<link rel="stylesheet" href="topo_theme.css">
<link rel="stylesheet" href="webSockTrace.css">
......@@ -56,7 +58,7 @@
<script src="onos2.js"></script>
</head>
<body>
<body class='theme_dark'>
<div id="frame">
<div id="mast">
<!-- NOTE: masthead injected here by mast.js -->
......
......@@ -25,14 +25,11 @@
}
#topo #map {
stroke-width: 2px;
stroke: #aaaaaa;
fill: transparent;
}
#topo svg .glyph {
fill: white;
stroke: none;
}
......@@ -68,39 +65,20 @@
#topo svg .node.device.fixed rect {
stroke-width: 1.5;
stroke: #ccc;
}
/* note: device is offline without the 'online' class */
#topo svg .node.device {
fill: #777;
}
#topo svg .node.device.switch.online {
fill: #17f;
}
#topo svg .node.device.roadm.online {
fill: #03c;
#topo .node text {
font-weight: 100;
}
/* note: device is offline without the 'online' class */
/* note: device is offline without the 'online' class */
#topo svg .node.device text {
fill: #aaa;
font: 10pt sans-serif;
}
#topo svg .node.device.online text {
fill: white;
}
/* Host Nodes */
#topo svg .node.host {
stroke: #000;
}
#topo svg .node.host text {
fill: #846;
stroke: none;
......@@ -127,29 +105,23 @@ svg .node.host circle {
}
#topo svg .link.primary {
stroke: #f11;
stroke-width: 6px;
}
#topo svg .link.secondary {
stroke: rgba(255,100,100,0.5);
stroke-width: 4px;
}
#topo svg .link.animated {
stroke: #f11;
stroke-width: 10px;
stroke-dasharray: 8 8
}
#topo svg .link.primary.optical {
stroke: #74f;
stroke-width: 6px;
}
#topo svg .link.secondary.optical {
stroke: rgba(128,64,255,0.5);
stroke-width: 4px;
}
#topo svg .link.animated.optical {
stroke: #74f;
stroke-width: 10px;
stroke-dasharray: 8 8
}
......@@ -205,17 +177,13 @@ svg .node.host circle {
cursor: pointer;
width: 50%;
text-align: center;
/* theme specific... */
border: 1px solid #ddf;
color: #99f;
border-width: 1px;
borer-style: solid;
}
#topo-detail .actionBtn:hover {
/* theme specific... */
border: 1px solid #ddf;
background: #eef;
color: #77d;
border-width: 1px;
border-style: solid;
}
......@@ -239,29 +207,14 @@ svg .node.host circle {
height: 80px;
margin: 4px 0;
cursor: pointer;
/* theme-related */
color: #444;
background-color: #ccc;
border: 2px solid #aaa;
}
#topo-oibox .onosInst.online {
/* theme-related */
color: #113;
background-color: #bbf;
border: 2px solid #555;
border-width: 2px;
border-style: solid;
}
#topo-oibox .onosInst .onosTitle {
text-align: center;
font-size: 11pt;
margin-top: 6px;
color: #888;
}
#topo-oibox .onosInst.online .onosTitle {
color: black;
}
#topo-oibox .onosInst img {
......
......@@ -70,23 +70,17 @@
}
},
topo: {
linkBaseColor: '#666',
linkInColor: '#66f',
linkInWidth: 14,
linkOutColor: '#f00',
linkInWidth: 14,
linkOutWidth: 30
},
icons: {
w: 28,
h: 28,
xoff: -12,
yoff: -8
map: {
strokeWidth: 1
},
iconUrl: {
device: 'img/device.png',
host: 'img/host.png',
pkt: 'img/pkt.png',
opt: 'img/opt.png'
icons: {
w: 100,
h: 100
},
force: {
note_for_links: 'link.type is used to differentiate',
......@@ -532,8 +526,7 @@
}
el.transition()
.duration(1000)
.attr('stroke-width', linkScale(lw))
.attr('stroke', config.topo.linkBaseColor);
.attr('stroke-width', linkScale(lw));
}
// ==============================
......@@ -1234,8 +1227,8 @@
$.extend(node, xy);
}
function iconUrl(d) {
return 'img/' + d.type + '.png';
function getIconUrl(d) {
return 'icons.svg#' + d.type;
}
// returns the newly computed bounding box of the rectangle
......@@ -1277,6 +1270,29 @@
return (label && label.trim()) ? label : '.';
}
function updateDeviceIconAppearance(node, box, animate) {
var u = node.select('use');
var ubbox = u.node().getBBox();
var xoff = -ubbox.width/2 - box.width/2 - 4;
var yoff = -ubbox.height;
var iconTransform = 'translate(' + xoff + ',' + yoff + ')';
if (animate) {
node.select('use')
.transition()
.attr('transform', iconTransform);
} else {
node.select('use')
.attr('transform', iconTransform);
}
var computedStyle = window.getComputedStyle(node.node());
u.attr({
fill: computedStyle.fill,
color: computedStyle.color
});
}
function updateDeviceLabel(d) {
var label = niceLabel(deviceLabel(d)),
node = d.el,
......@@ -1294,10 +1310,7 @@
.transition()
.attr(box);
node.select('image')
.transition()
.attr('x', box.x + config.icons.xoff)
.attr('y', box.y + config.icons.yoff);
updateDeviceIconAppearance(node, box, true);
}
function updateHostLabel(d) {
......@@ -1339,18 +1352,6 @@
}
}
function addHostIcon(node, radius, iconId) {
var dim = radius * 1.5,
xlate = -dim / 2;
node.append('use')
.classed('glyph', true)
.attr('transform', translate(xlate,xlate))
.attr('xlink:href', '#' + iconId)
.attr('width', dim)
.attr('height', dim);
}
function updateNodes() {
node = nodeG.selectAll('.node')
.data(network.nodes, function (d) { return d.id; });
......@@ -1377,7 +1378,7 @@
// augment device nodes...
entering.filter('.device').each(function (d) {
var node = d3.select(this),
icon = iconUrl(d),
iconUrl = getIconUrl(d),
label = niceLabel(deviceLabel(d)),
box;
......@@ -1399,18 +1400,17 @@
node.select('rect')
.attr(box);
if (icon) {
var cfg = config.icons;
node.append('svg:image')
if (iconUrl) {
node.append('svg:use')
.attr({
x: box.x + config.icons.xoff,
y: box.y + config.icons.yoff,
width: cfg.w,
height: cfg.h,
'xlink:href': icon
'xlink:href': iconUrl,
width: config.icons.w,
height: config.icons.h
});
}
updateDeviceIconAppearance(node, box, false);
// debug function to show the modelled x,y coordinates of nodes...
if (debug('showNodeXY')) {
node.select('rect').attr('fill-opacity', 0.5);
......@@ -1424,36 +1424,43 @@
}
});
// TODO: better place for this configuration state
var defaultHostRadius = 9,
hostRadius = {
bgpSpeaker: 20
},
hostIcon = {
bgpSpeaker: 'bullhorn'
};
// augment host nodes...
entering.filter('.host').each(function (d) {
var node = d3.select(this),
r = hostRadius[d.type] || defaultHostRadius,
textDy = r + 10,
icon = hostIcon[d.type];
iconUrl = getIconUrl(d);
// provide ref to element from backing data....
d.el = node;
node.append('circle')
.attr('r', r);
// var box = node.append('circle')
// .attr('r', r).node().getBBox();
if (icon) {
addHostIcon(node, r, icon);
var textYOff = 0;
var textXOff = 0;
if (iconUrl) {
var computedStyle = window.getComputedStyle(node.node());
var u = node.append('svg:use')
.attr({
'xlink:href': iconUrl,
width: config.icons.w,
height: config.icons.h,
fill: computedStyle.fill,
color: computedStyle.color
});
var ubbox = node.select('use').node().getBBox();
u.attr('y', -ubbox.height/2);
textYOff = ubbox.height/2 + 4; // pad by 4 pixels
textXOff = ubbox.width/2;
}
node.append('text')
.text(hostLabel)
.attr('dy', textDy)
.attr('alignment-baseline', 'text-before-edge')
.attr('x', textXOff)
.attr('y', textYOff)
.attr('text-anchor', 'middle');
// debug function to show the modelled x,y coordinates of nodes...
......@@ -1492,9 +1499,9 @@
.style('opacity', 0);
// note, leave <g>.remove to remove this element
node.select('circle')
.style('stroke-fill', '#555')
.style('fill', '#888')
node.select('use')
.style('color', '#aaa')
.style('fill', '#000')
.style('opacity', 0.5)
.transition()
.duration(1500)
......@@ -1817,7 +1824,7 @@
function zoomPan(scale, translate) {
zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
// keep the map lines constant width while zooming
bgImg.style("stroke-width", 2.0 / scale + "px");
bgImg.attr("stroke-width", config.map.strokeWidth / scale + "px");
}
function resetZoomPan() {
......@@ -1934,6 +1941,27 @@
gly.defBullhorn(defs);
}
// create references to bring these into cache so that getBBox() works when they
// are inserted later
function preloadIcons(svg) {
var icons = [
"router",
"switch",
"roadm",
"endstation",
"bgpSpeaker"
];
var g = svg.append('g');
for (var icon in icons) {
g.append('use')
.attr({
'xlink:href': 'icons.svg#' + icon
});
}
g.style('visibility', 'hidden');
}
// ==============================
// View life-cycle callbacks
......@@ -1950,6 +1978,7 @@
setSize(svg, view);
loadGlyphs(svg);
preloadIcons(svg);
zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
setupZoomPan();
......@@ -2046,7 +2075,7 @@
width: config.birdDim,
height: config.birdDim,
fill: '#111'
})
});
}
function para(sel, text) {
......@@ -2161,11 +2190,13 @@
.translate(t);
bgImg = zoomPanContainer.insert("g", '#topo-G');
bgImg.attr('id', 'map').selectAll('path')
.data(topoData.features)
.enter()
.append('path')
.attr('d', path);
// pointer-events: none so that browser select tools don't pick up the map svg
bgImg.attr('id', 'map').attr('stroke-width', config.map.strokeWidth + 'px').style('pointer-events', 'none')
.selectAll('path')
.data(topoData.features)
.enter()
.append('path')
.attr('d', path);
}
function resize(view, ctx, flags) {
......
.theme_dark #topo {
background-color: #20201D;
}
.theme_dark #topo #map {
stroke: #444;
}
/* note: device is offline without the 'online' class */
.theme_dark #topo svg .node.device {
fill: #777;
}
.theme_dark #topo .host {
fill: #000;
color: white;
}
.theme_dark #topo svg .node.device.switch.online {
fill: #000;
}
.theme_dark #topo svg .node.device.roadm.online {
fill: #03c;
}
/* note: device is offline without the 'online' class */
.theme_dark #topo svg .node.device text {
fill: #aaa;
}
.theme_dark #topo svg .node.device.online {
color: white;
}
.theme_dark #topo svg .node.device.online text {
fill: currentColor;
}
.theme_dark #topo svg .glyph {
fill: white;
}
.theme_dark #topo-oibox .onosInst {
color: #444;
background-color: #ccc;
border-color: #aaa;
}
.theme_dark #topo-oibox .onosInst.online {
color: #113;
background-color: #bbf;
border-color: #555;
}
.theme_dark #topo-oibox .onosInst .onosTitle {
color: #888;
}
.theme_dark #topo svg .node.device.fixed.online rect {
stroke: #666;
}
.theme_dark #topo svg .node.host {
stroke: #000;
}
.theme_dark #topo-detail .actionBtn {
border-color: #ddf;
color: #99f;
}
.theme_dark #topo-detail .actionBtn:hover {
border-color: #ddf;
background: #eef;
color: #77d;
}
.theme_dark #topo svg .link.primary {
stroke: #f11;
}
.theme_dark #topo svg .link.secondary {
stroke: rgba(255,100,100,0.5);
}
.theme_dark #topo svg .link.animated {
stroke: #f11;
}
.theme_dark #topo svg .link.primary.optical {
stroke: #74f;
}
.theme_dark #topo svg .link.secondary.optical {
stroke: rgba(128,64,255,0.5);
}
.theme_dark #topo svg .link.animated.optical {
stroke: #74f;
}
/*.theme_dark #topo .link {
stroke: #666;
}
*/