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 @@ ...@@ -40,12 +40,14 @@
40 <!-- Base library and framework stylesheets included here --> 40 <!-- Base library and framework stylesheets included here -->
41 <link rel="stylesheet" href="base.css"> 41 <link rel="stylesheet" href="base.css">
42 <link rel="stylesheet" href="onos2.css"> 42 <link rel="stylesheet" href="onos2.css">
43 + <link rel="stylesheet" href="onos_theme.css">
43 <link rel="stylesheet" href="mast2.css"> 44 <link rel="stylesheet" href="mast2.css">
44 <link rel="stylesheet" href="floatPanel.css"> 45 <link rel="stylesheet" href="floatPanel.css">
45 46
46 <!-- This is where contributed stylesheets get INJECTED --> 47 <!-- This is where contributed stylesheets get INJECTED -->
47 <!-- TODO: replace with template marker and inject refs server-side --> 48 <!-- TODO: replace with template marker and inject refs server-side -->
48 <link rel="stylesheet" href="topo2.css"> 49 <link rel="stylesheet" href="topo2.css">
50 + <link rel="stylesheet" href="topo_theme.css">
49 <link rel="stylesheet" href="webSockTrace.css"> 51 <link rel="stylesheet" href="webSockTrace.css">
50 52
51 53
...@@ -56,7 +58,7 @@ ...@@ -56,7 +58,7 @@
56 <script src="onos2.js"></script> 58 <script src="onos2.js"></script>
57 59
58 </head> 60 </head>
59 -<body> 61 +<body class='theme_dark'>
60 <div id="frame"> 62 <div id="frame">
61 <div id="mast"> 63 <div id="mast">
62 <!-- NOTE: masthead injected here by mast.js --> 64 <!-- NOTE: masthead injected here by mast.js -->
......
...@@ -25,14 +25,11 @@ ...@@ -25,14 +25,11 @@
25 } 25 }
26 26
27 #topo #map { 27 #topo #map {
28 - stroke-width: 2px;
29 - stroke: #aaaaaa;
30 fill: transparent; 28 fill: transparent;
31 } 29 }
32 30
33 31
34 #topo svg .glyph { 32 #topo svg .glyph {
35 - fill: white;
36 stroke: none; 33 stroke: none;
37 } 34 }
38 35
...@@ -68,39 +65,20 @@ ...@@ -68,39 +65,20 @@
68 65
69 #topo svg .node.device.fixed rect { 66 #topo svg .node.device.fixed rect {
70 stroke-width: 1.5; 67 stroke-width: 1.5;
71 - stroke: #ccc;
72 -}
73 -
74 -/* note: device is offline without the 'online' class */
75 -#topo svg .node.device {
76 - fill: #777;
77 } 68 }
78 69
79 -#topo svg .node.device.switch.online { 70 +#topo .node text {
80 - fill: #17f; 71 + font-weight: 100;
81 -}
82 -
83 -#topo svg .node.device.roadm.online {
84 - fill: #03c;
85 } 72 }
86 73
87 /* note: device is offline without the 'online' class */ 74 /* note: device is offline without the 'online' class */
75 +/* note: device is offline without the 'online' class */
88 #topo svg .node.device text { 76 #topo svg .node.device text {
89 - fill: #aaa;
90 font: 10pt sans-serif; 77 font: 10pt sans-serif;
91 } 78 }
92 79
93 -#topo svg .node.device.online text {
94 - fill: white;
95 -}
96 -
97 -
98 /* Host Nodes */ 80 /* Host Nodes */
99 81
100 -#topo svg .node.host {
101 - stroke: #000;
102 -}
103 -
104 #topo svg .node.host text { 82 #topo svg .node.host text {
105 fill: #846; 83 fill: #846;
106 stroke: none; 84 stroke: none;
...@@ -127,29 +105,23 @@ svg .node.host circle { ...@@ -127,29 +105,23 @@ svg .node.host circle {
127 } 105 }
128 106
129 #topo svg .link.primary { 107 #topo svg .link.primary {
130 - stroke: #f11;
131 stroke-width: 6px; 108 stroke-width: 6px;
132 } 109 }
133 #topo svg .link.secondary { 110 #topo svg .link.secondary {
134 - stroke: rgba(255,100,100,0.5);
135 stroke-width: 4px; 111 stroke-width: 4px;
136 } 112 }
137 #topo svg .link.animated { 113 #topo svg .link.animated {
138 - stroke: #f11;
139 stroke-width: 10px; 114 stroke-width: 10px;
140 stroke-dasharray: 8 8 115 stroke-dasharray: 8 8
141 } 116 }
142 117
143 #topo svg .link.primary.optical { 118 #topo svg .link.primary.optical {
144 - stroke: #74f;
145 stroke-width: 6px; 119 stroke-width: 6px;
146 } 120 }
147 #topo svg .link.secondary.optical { 121 #topo svg .link.secondary.optical {
148 - stroke: rgba(128,64,255,0.5);
149 stroke-width: 4px; 122 stroke-width: 4px;
150 } 123 }
151 #topo svg .link.animated.optical { 124 #topo svg .link.animated.optical {
152 - stroke: #74f;
153 stroke-width: 10px; 125 stroke-width: 10px;
154 stroke-dasharray: 8 8 126 stroke-dasharray: 8 8
155 } 127 }
...@@ -205,17 +177,13 @@ svg .node.host circle { ...@@ -205,17 +177,13 @@ svg .node.host circle {
205 cursor: pointer; 177 cursor: pointer;
206 width: 50%; 178 width: 50%;
207 text-align: center; 179 text-align: center;
208 - 180 + border-width: 1px;
209 - /* theme specific... */ 181 + borer-style: solid;
210 - border: 1px solid #ddf;
211 - color: #99f;
212 } 182 }
213 183
214 #topo-detail .actionBtn:hover { 184 #topo-detail .actionBtn:hover {
215 - /* theme specific... */ 185 + border-width: 1px;
216 - border: 1px solid #ddf; 186 + border-style: solid;
217 - background: #eef;
218 - color: #77d;
219 } 187 }
220 188
221 189
...@@ -239,29 +207,14 @@ svg .node.host circle { ...@@ -239,29 +207,14 @@ svg .node.host circle {
239 height: 80px; 207 height: 80px;
240 margin: 4px 0; 208 margin: 4px 0;
241 cursor: pointer; 209 cursor: pointer;
242 - 210 + border-width: 2px;
243 - /* theme-related */ 211 + border-style: solid;
244 - color: #444;
245 - background-color: #ccc;
246 - border: 2px solid #aaa;
247 -}
248 -
249 -#topo-oibox .onosInst.online {
250 - /* theme-related */
251 - color: #113;
252 - background-color: #bbf;
253 - border: 2px solid #555;
254 } 212 }
255 213
256 #topo-oibox .onosInst .onosTitle { 214 #topo-oibox .onosInst .onosTitle {
257 text-align: center; 215 text-align: center;
258 font-size: 11pt; 216 font-size: 11pt;
259 margin-top: 6px; 217 margin-top: 6px;
260 - color: #888;
261 -}
262 -
263 -#topo-oibox .onosInst.online .onosTitle {
264 - color: black;
265 } 218 }
266 219
267 #topo-oibox .onosInst img { 220 #topo-oibox .onosInst img {
......
...@@ -70,23 +70,17 @@ ...@@ -70,23 +70,17 @@
70 } 70 }
71 }, 71 },
72 topo: { 72 topo: {
73 - linkBaseColor: '#666',
74 linkInColor: '#66f', 73 linkInColor: '#66f',
75 - linkInWidth: 14,
76 linkOutColor: '#f00', 74 linkOutColor: '#f00',
75 + linkInWidth: 14,
77 linkOutWidth: 30 76 linkOutWidth: 30
78 }, 77 },
79 - icons: { 78 + map: {
80 - w: 28, 79 + strokeWidth: 1
81 - h: 28,
82 - xoff: -12,
83 - yoff: -8
84 }, 80 },
85 - iconUrl: { 81 + icons: {
86 - device: 'img/device.png', 82 + w: 100,
87 - host: 'img/host.png', 83 + h: 100
88 - pkt: 'img/pkt.png',
89 - opt: 'img/opt.png'
90 }, 84 },
91 force: { 85 force: {
92 note_for_links: 'link.type is used to differentiate', 86 note_for_links: 'link.type is used to differentiate',
...@@ -532,8 +526,7 @@ ...@@ -532,8 +526,7 @@
532 } 526 }
533 el.transition() 527 el.transition()
534 .duration(1000) 528 .duration(1000)
535 - .attr('stroke-width', linkScale(lw)) 529 + .attr('stroke-width', linkScale(lw));
536 - .attr('stroke', config.topo.linkBaseColor);
537 } 530 }
538 531
539 // ============================== 532 // ==============================
...@@ -1234,8 +1227,8 @@ ...@@ -1234,8 +1227,8 @@
1234 $.extend(node, xy); 1227 $.extend(node, xy);
1235 } 1228 }
1236 1229
1237 - function iconUrl(d) { 1230 + function getIconUrl(d) {
1238 - return 'img/' + d.type + '.png'; 1231 + return 'icons.svg#' + d.type;
1239 } 1232 }
1240 1233
1241 // returns the newly computed bounding box of the rectangle 1234 // returns the newly computed bounding box of the rectangle
...@@ -1277,6 +1270,29 @@ ...@@ -1277,6 +1270,29 @@
1277 return (label && label.trim()) ? label : '.'; 1270 return (label && label.trim()) ? label : '.';
1278 } 1271 }
1279 1272
1273 + function updateDeviceIconAppearance(node, box, animate) {
1274 + var u = node.select('use');
1275 + var ubbox = u.node().getBBox();
1276 +
1277 + var xoff = -ubbox.width/2 - box.width/2 - 4;
1278 + var yoff = -ubbox.height;
1279 + var iconTransform = 'translate(' + xoff + ',' + yoff + ')';
1280 + if (animate) {
1281 + node.select('use')
1282 + .transition()
1283 + .attr('transform', iconTransform);
1284 + } else {
1285 + node.select('use')
1286 + .attr('transform', iconTransform);
1287 + }
1288 +
1289 + var computedStyle = window.getComputedStyle(node.node());
1290 + u.attr({
1291 + fill: computedStyle.fill,
1292 + color: computedStyle.color
1293 + });
1294 + }
1295 +
1280 function updateDeviceLabel(d) { 1296 function updateDeviceLabel(d) {
1281 var label = niceLabel(deviceLabel(d)), 1297 var label = niceLabel(deviceLabel(d)),
1282 node = d.el, 1298 node = d.el,
...@@ -1294,10 +1310,7 @@ ...@@ -1294,10 +1310,7 @@
1294 .transition() 1310 .transition()
1295 .attr(box); 1311 .attr(box);
1296 1312
1297 - node.select('image') 1313 + updateDeviceIconAppearance(node, box, true);
1298 - .transition()
1299 - .attr('x', box.x + config.icons.xoff)
1300 - .attr('y', box.y + config.icons.yoff);
1301 } 1314 }
1302 1315
1303 function updateHostLabel(d) { 1316 function updateHostLabel(d) {
...@@ -1339,18 +1352,6 @@ ...@@ -1339,18 +1352,6 @@
1339 } 1352 }
1340 } 1353 }
1341 1354
1342 - function addHostIcon(node, radius, iconId) {
1343 - var dim = radius * 1.5,
1344 - xlate = -dim / 2;
1345 -
1346 - node.append('use')
1347 - .classed('glyph', true)
1348 - .attr('transform', translate(xlate,xlate))
1349 - .attr('xlink:href', '#' + iconId)
1350 - .attr('width', dim)
1351 - .attr('height', dim);
1352 - }
1353 -
1354 function updateNodes() { 1355 function updateNodes() {
1355 node = nodeG.selectAll('.node') 1356 node = nodeG.selectAll('.node')
1356 .data(network.nodes, function (d) { return d.id; }); 1357 .data(network.nodes, function (d) { return d.id; });
...@@ -1377,7 +1378,7 @@ ...@@ -1377,7 +1378,7 @@
1377 // augment device nodes... 1378 // augment device nodes...
1378 entering.filter('.device').each(function (d) { 1379 entering.filter('.device').each(function (d) {
1379 var node = d3.select(this), 1380 var node = d3.select(this),
1380 - icon = iconUrl(d), 1381 + iconUrl = getIconUrl(d),
1381 label = niceLabel(deviceLabel(d)), 1382 label = niceLabel(deviceLabel(d)),
1382 box; 1383 box;
1383 1384
...@@ -1399,18 +1400,17 @@ ...@@ -1399,18 +1400,17 @@
1399 node.select('rect') 1400 node.select('rect')
1400 .attr(box); 1401 .attr(box);
1401 1402
1402 - if (icon) { 1403 + if (iconUrl) {
1403 - var cfg = config.icons; 1404 + node.append('svg:use')
1404 - node.append('svg:image')
1405 .attr({ 1405 .attr({
1406 - x: box.x + config.icons.xoff, 1406 + 'xlink:href': iconUrl,
1407 - y: box.y + config.icons.yoff, 1407 + width: config.icons.w,
1408 - width: cfg.w, 1408 + height: config.icons.h
1409 - height: cfg.h,
1410 - 'xlink:href': icon
1411 }); 1409 });
1412 } 1410 }
1413 1411
1412 + updateDeviceIconAppearance(node, box, false);
1413 +
1414 // debug function to show the modelled x,y coordinates of nodes... 1414 // debug function to show the modelled x,y coordinates of nodes...
1415 if (debug('showNodeXY')) { 1415 if (debug('showNodeXY')) {
1416 node.select('rect').attr('fill-opacity', 0.5); 1416 node.select('rect').attr('fill-opacity', 0.5);
...@@ -1424,36 +1424,43 @@ ...@@ -1424,36 +1424,43 @@
1424 } 1424 }
1425 }); 1425 });
1426 1426
1427 - // TODO: better place for this configuration state
1428 - var defaultHostRadius = 9,
1429 - hostRadius = {
1430 - bgpSpeaker: 20
1431 - },
1432 - hostIcon = {
1433 - bgpSpeaker: 'bullhorn'
1434 - };
1435 -
1436 -
1437 // augment host nodes... 1427 // augment host nodes...
1438 entering.filter('.host').each(function (d) { 1428 entering.filter('.host').each(function (d) {
1439 var node = d3.select(this), 1429 var node = d3.select(this),
1440 - r = hostRadius[d.type] || defaultHostRadius, 1430 + iconUrl = getIconUrl(d);
1441 - textDy = r + 10,
1442 - icon = hostIcon[d.type];
1443 1431
1444 // provide ref to element from backing data.... 1432 // provide ref to element from backing data....
1445 d.el = node; 1433 d.el = node;
1446 1434
1447 - node.append('circle') 1435 + // var box = node.append('circle')
1448 - .attr('r', r); 1436 + // .attr('r', r).node().getBBox();
1449 1437
1450 - if (icon) { 1438 + var textYOff = 0;
1451 - addHostIcon(node, r, icon); 1439 + var textXOff = 0;
1440 + if (iconUrl) {
1441 + var computedStyle = window.getComputedStyle(node.node());
1442 + var u = node.append('svg:use')
1443 + .attr({
1444 + 'xlink:href': iconUrl,
1445 + width: config.icons.w,
1446 + height: config.icons.h,
1447 + fill: computedStyle.fill,
1448 + color: computedStyle.color
1449 + });
1450 +
1451 + var ubbox = node.select('use').node().getBBox();
1452 + u.attr('y', -ubbox.height/2);
1453 + textYOff = ubbox.height/2 + 4; // pad by 4 pixels
1454 + textXOff = ubbox.width/2;
1452 } 1455 }
1453 1456
1457 +
1458 +
1454 node.append('text') 1459 node.append('text')
1455 .text(hostLabel) 1460 .text(hostLabel)
1456 - .attr('dy', textDy) 1461 + .attr('alignment-baseline', 'text-before-edge')
1462 + .attr('x', textXOff)
1463 + .attr('y', textYOff)
1457 .attr('text-anchor', 'middle'); 1464 .attr('text-anchor', 'middle');
1458 1465
1459 // debug function to show the modelled x,y coordinates of nodes... 1466 // debug function to show the modelled x,y coordinates of nodes...
...@@ -1492,9 +1499,9 @@ ...@@ -1492,9 +1499,9 @@
1492 .style('opacity', 0); 1499 .style('opacity', 0);
1493 // note, leave <g>.remove to remove this element 1500 // note, leave <g>.remove to remove this element
1494 1501
1495 - node.select('circle') 1502 + node.select('use')
1496 - .style('stroke-fill', '#555') 1503 + .style('color', '#aaa')
1497 - .style('fill', '#888') 1504 + .style('fill', '#000')
1498 .style('opacity', 0.5) 1505 .style('opacity', 0.5)
1499 .transition() 1506 .transition()
1500 .duration(1500) 1507 .duration(1500)
...@@ -1817,7 +1824,7 @@ ...@@ -1817,7 +1824,7 @@
1817 function zoomPan(scale, translate) { 1824 function zoomPan(scale, translate) {
1818 zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); 1825 zoomPanContainer.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
1819 // keep the map lines constant width while zooming 1826 // keep the map lines constant width while zooming
1820 - bgImg.style("stroke-width", 2.0 / scale + "px"); 1827 + bgImg.attr("stroke-width", config.map.strokeWidth / scale + "px");
1821 } 1828 }
1822 1829
1823 function resetZoomPan() { 1830 function resetZoomPan() {
...@@ -1934,6 +1941,27 @@ ...@@ -1934,6 +1941,27 @@
1934 gly.defBullhorn(defs); 1941 gly.defBullhorn(defs);
1935 } 1942 }
1936 1943
1944 + // create references to bring these into cache so that getBBox() works when they
1945 + // are inserted later
1946 + function preloadIcons(svg) {
1947 + var icons = [
1948 + "router",
1949 + "switch",
1950 + "roadm",
1951 + "endstation",
1952 + "bgpSpeaker"
1953 + ];
1954 +
1955 + var g = svg.append('g');
1956 + for (var icon in icons) {
1957 + g.append('use')
1958 + .attr({
1959 + 'xlink:href': 'icons.svg#' + icon
1960 + });
1961 + }
1962 + g.style('visibility', 'hidden');
1963 + }
1964 +
1937 // ============================== 1965 // ==============================
1938 // View life-cycle callbacks 1966 // View life-cycle callbacks
1939 1967
...@@ -1950,6 +1978,7 @@ ...@@ -1950,6 +1978,7 @@
1950 setSize(svg, view); 1978 setSize(svg, view);
1951 1979
1952 loadGlyphs(svg); 1980 loadGlyphs(svg);
1981 + preloadIcons(svg);
1953 1982
1954 zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer'); 1983 zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
1955 setupZoomPan(); 1984 setupZoomPan();
...@@ -2046,7 +2075,7 @@ ...@@ -2046,7 +2075,7 @@
2046 width: config.birdDim, 2075 width: config.birdDim,
2047 height: config.birdDim, 2076 height: config.birdDim,
2048 fill: '#111' 2077 fill: '#111'
2049 - }) 2078 + });
2050 } 2079 }
2051 2080
2052 function para(sel, text) { 2081 function para(sel, text) {
...@@ -2161,11 +2190,13 @@ ...@@ -2161,11 +2190,13 @@
2161 .translate(t); 2190 .translate(t);
2162 2191
2163 bgImg = zoomPanContainer.insert("g", '#topo-G'); 2192 bgImg = zoomPanContainer.insert("g", '#topo-G');
2164 - bgImg.attr('id', 'map').selectAll('path') 2193 + // pointer-events: none so that browser select tools don't pick up the map svg
2165 - .data(topoData.features) 2194 + bgImg.attr('id', 'map').attr('stroke-width', config.map.strokeWidth + 'px').style('pointer-events', 'none')
2166 - .enter() 2195 + .selectAll('path')
2167 - .append('path') 2196 + .data(topoData.features)
2168 - .attr('d', path); 2197 + .enter()
2198 + .append('path')
2199 + .attr('d', path);
2169 } 2200 }
2170 2201
2171 function resize(view, ctx, flags) { 2202 function resize(view, ctx, flags) {
......
1 +.theme_dark #topo {
2 + background-color: #20201D;
3 +}
4 +
5 +.theme_dark #topo #map {
6 + stroke: #444;
7 +}
8 +
9 +/* note: device is offline without the 'online' class */
10 +.theme_dark #topo svg .node.device {
11 + fill: #777;
12 +}
13 +
14 +.theme_dark #topo .host {
15 + fill: #000;
16 + color: white;
17 +}
18 +
19 +
20 +.theme_dark #topo svg .node.device.switch.online {
21 + fill: #000;
22 +}
23 +
24 +.theme_dark #topo svg .node.device.roadm.online {
25 + fill: #03c;
26 +}
27 +
28 +/* note: device is offline without the 'online' class */
29 +.theme_dark #topo svg .node.device text {
30 + fill: #aaa;
31 +}
32 +
33 +.theme_dark #topo svg .node.device.online {
34 + color: white;
35 +}
36 +.theme_dark #topo svg .node.device.online text {
37 + fill: currentColor;
38 +}
39 +
40 +.theme_dark #topo svg .glyph {
41 + fill: white;
42 +}
43 +
44 +.theme_dark #topo-oibox .onosInst {
45 + color: #444;
46 + background-color: #ccc;
47 + border-color: #aaa;
48 +}
49 +
50 +.theme_dark #topo-oibox .onosInst.online {
51 + color: #113;
52 + background-color: #bbf;
53 + border-color: #555;
54 +}
55 +
56 +.theme_dark #topo-oibox .onosInst .onosTitle {
57 + color: #888;
58 +}
59 +
60 +.theme_dark #topo svg .node.device.fixed.online rect {
61 + stroke: #666;
62 +}
63 +
64 +.theme_dark #topo svg .node.host {
65 + stroke: #000;
66 +}
67 +
68 +.theme_dark #topo-detail .actionBtn {
69 + border-color: #ddf;
70 + color: #99f;
71 +}
72 +
73 +.theme_dark #topo-detail .actionBtn:hover {
74 + border-color: #ddf;
75 + background: #eef;
76 + color: #77d;
77 +}
78 +
79 +.theme_dark #topo svg .link.primary {
80 + stroke: #f11;
81 +}
82 +
83 +.theme_dark #topo svg .link.secondary {
84 + stroke: rgba(255,100,100,0.5);
85 +}
86 +
87 +.theme_dark #topo svg .link.animated {
88 + stroke: #f11;
89 +}
90 +
91 +.theme_dark #topo svg .link.primary.optical {
92 + stroke: #74f;
93 +}
94 +
95 +.theme_dark #topo svg .link.secondary.optical {
96 + stroke: rgba(128,64,255,0.5);
97 +}
98 +
99 +.theme_dark #topo svg .link.animated.optical {
100 + stroke: #74f;
101 +}
102 +
103 +/*.theme_dark #topo .link {
104 + stroke: #666;
105 +}
106 +*/
107 +