GUI -- Added onos.ui.addFloatingPanel() function.
- re-instated detail pane in topo2.js; triggered of non-zero selection state. - single-select now requests details and displays them in detail pane. - multi-select WIP. Change-Id: I300a3dfd4d35abc82f832a172854c6aff50d8cd6
Showing
9 changed files
with
346 additions
and
33 deletions
web/gui/src/main/webapp/floatPanel.css
0 → 100644
1 | +/* | ||
2 | + * Copyright 2014 Open Networking Laboratory | ||
3 | + * | ||
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 -- Floating Panels -- CSS file | ||
19 | + | ||
20 | + @author Simon Hunt | ||
21 | + */ | ||
22 | + | ||
23 | +.fpanel { | ||
24 | + position: absolute; | ||
25 | + z-index: 100; | ||
26 | + display: block; | ||
27 | + top: 10%; | ||
28 | + width: 280px; | ||
29 | + right: -300px; | ||
30 | + opacity: 0; | ||
31 | + background-color: rgba(255,255,255,0.8); | ||
32 | + | ||
33 | + padding: 10px; | ||
34 | + color: black; | ||
35 | + font-size: 10pt; | ||
36 | + box-shadow: 2px 2px 16px #777; | ||
37 | +} | ||
38 | + | ||
39 | +/* TODO: light/dark themes */ | ||
40 | +.light .fpanel { | ||
41 | + | ||
42 | +} | ||
43 | +.dark .fpanel { | ||
44 | + | ||
45 | +} |
... | @@ -40,6 +40,7 @@ | ... | @@ -40,6 +40,7 @@ |
40 | <link rel="stylesheet" href="base.css"> | 40 | <link rel="stylesheet" href="base.css"> |
41 | <link rel="stylesheet" href="onos2.css"> | 41 | <link rel="stylesheet" href="onos2.css"> |
42 | <link rel="stylesheet" href="mast2.css"> | 42 | <link rel="stylesheet" href="mast2.css"> |
43 | + <link rel="stylesheet" href="floatPanel.css"> | ||
43 | 44 | ||
44 | <!-- This is where contributed stylesheets get INJECTED --> | 45 | <!-- This is where contributed stylesheets get INJECTED --> |
45 | <!-- TODO: replace with template marker and inject refs server-side --> | 46 | <!-- TODO: replace with template marker and inject refs server-side --> |
... | @@ -62,8 +63,9 @@ | ... | @@ -62,8 +63,9 @@ |
62 | <div id="view"> | 63 | <div id="view"> |
63 | <!-- NOTE: views injected here by onos.js --> | 64 | <!-- NOTE: views injected here by onos.js --> |
64 | </div> | 65 | </div> |
65 | - <div id="overlays"> | 66 | + <div id="floatPanels"> |
66 | - <!-- NOTE: overlays injected here, as needed --> | 67 | + <!-- NOTE: floating panels injected here, as needed --> |
68 | + <!-- see onos.ui.addFloatingPanel --> | ||
67 | </div> | 69 | </div> |
68 | <div id="alerts"> | 70 | <div id="alerts"> |
69 | <!-- NOTE: alert content injected here, as needed --> | 71 | <!-- NOTE: alert content injected here, as needed --> | ... | ... |
1 | +{ | ||
2 | + "event": "showDetails", | ||
3 | + "sid": 9, | ||
4 | + "payload": { | ||
5 | + "id": "CA:4B:EE:A4:B0:33/-1", | ||
6 | + "type": "host", | ||
7 | + "propOrder": [ | ||
8 | + "MAC", | ||
9 | + "IP", | ||
10 | + "-", | ||
11 | + "Latitude", | ||
12 | + "Longitude" | ||
13 | + ], | ||
14 | + "props": { | ||
15 | + "MAC": "CA:4B:EE:A4:B0:33", | ||
16 | + "IP": "[10.0.0.1]", | ||
17 | + "-": "", | ||
18 | + "Latitude": null, | ||
19 | + "Longitude": null | ||
20 | + } | ||
21 | + } | ||
22 | +} |
1 | +{ | ||
2 | + "event": "showDetails", | ||
3 | + "sid": 37, | ||
4 | + "payload": { | ||
5 | + "id": "of:000000000000000a", | ||
6 | + "type": "switch", | ||
7 | + "propOrder": [ | ||
8 | + "Name", | ||
9 | + "Vendor", | ||
10 | + "H/W Version", | ||
11 | + "S/W Version", | ||
12 | + "Serial Number", | ||
13 | + "-", | ||
14 | + "Latitude", | ||
15 | + "Longitude", | ||
16 | + "Ports", | ||
17 | + "-", | ||
18 | + "Master" | ||
19 | + ], | ||
20 | + "props": { | ||
21 | + "Name": null, | ||
22 | + "Vendor": "Nicira, Inc.", | ||
23 | + "H/W Version": "Open vSwitch", | ||
24 | + "S/W Version": "2.0.1", | ||
25 | + "Serial Number": "None", | ||
26 | + "-": "", | ||
27 | + "Latitude": null, | ||
28 | + "Longitude": null, | ||
29 | + "Ports": "5", | ||
30 | + "Master":"local" | ||
31 | + } | ||
32 | + } | ||
33 | +} |
... | @@ -50,6 +50,7 @@ | ... | @@ -50,6 +50,7 @@ |
50 | 50 | ||
51 | // internal state | 51 | // internal state |
52 | var views = {}, | 52 | var views = {}, |
53 | + fpanels = {}, | ||
53 | current = { | 54 | current = { |
54 | view: null, | 55 | view: null, |
55 | ctx: '', | 56 | ctx: '', |
... | @@ -57,7 +58,7 @@ | ... | @@ -57,7 +58,7 @@ |
57 | theme: settings.theme | 58 | theme: settings.theme |
58 | }, | 59 | }, |
59 | built = false, | 60 | built = false, |
60 | - errorCount = 0, | 61 | + buildErrors = [], |
61 | keyHandler = { | 62 | keyHandler = { |
62 | globalKeys: {}, | 63 | globalKeys: {}, |
63 | maskedKeys: {}, | 64 | maskedKeys: {}, |
... | @@ -70,7 +71,11 @@ | ... | @@ -70,7 +71,11 @@ |
70 | }; | 71 | }; |
71 | 72 | ||
72 | // DOM elements etc. | 73 | // DOM elements etc. |
73 | - var $view, | 74 | + // TODO: verify existence of following elements... |
75 | + var $view = d3.select('#view'), | ||
76 | + $floatPanels = d3.select('#floatPanels'), | ||
77 | + $alerts = d3.select('#alerts'), | ||
78 | + // note, following elements added programmatically... | ||
74 | $mastRadio; | 79 | $mastRadio; |
75 | 80 | ||
76 | 81 | ||
... | @@ -241,10 +246,22 @@ | ... | @@ -241,10 +246,22 @@ |
241 | setView(view, hash, t); | 246 | setView(view, hash, t); |
242 | } | 247 | } |
243 | 248 | ||
249 | + function buildError(msg) { | ||
250 | + buildErrors.push(msg); | ||
251 | + } | ||
252 | + | ||
244 | function reportBuildErrors() { | 253 | function reportBuildErrors() { |
245 | traceFn('reportBuildErrors'); | 254 | traceFn('reportBuildErrors'); |
246 | - // TODO: validate registered views / nav-item linkage etc. | 255 | + var nerr = buildErrors.length, |
247 | - console.log('(no build errors)'); | 256 | + errmsg; |
257 | + if (!nerr) { | ||
258 | + console.log('(no build errors)'); | ||
259 | + } else { | ||
260 | + errmsg = 'Build errors: ' + nerr + ' found...\n\n' + | ||
261 | + buildErrors.join('\n'); | ||
262 | + doAlert(errmsg); | ||
263 | + console.error(errmsg); | ||
264 | + } | ||
248 | } | 265 | } |
249 | 266 | ||
250 | // returns the reference if it is a function, null otherwise | 267 | // returns the reference if it is a function, null otherwise |
... | @@ -449,22 +466,20 @@ | ... | @@ -449,22 +466,20 @@ |
449 | } | 466 | } |
450 | 467 | ||
451 | function createAlerts() { | 468 | function createAlerts() { |
452 | - var al = d3.select('#alerts') | 469 | + $alerts.style('display', 'block'); |
453 | - .style('display', 'block'); | 470 | + $alerts.append('span') |
454 | - al.append('span') | ||
455 | .attr('class', 'close') | 471 | .attr('class', 'close') |
456 | .text('X') | 472 | .text('X') |
457 | .on('click', closeAlerts); | 473 | .on('click', closeAlerts); |
458 | - al.append('pre'); | 474 | + $alerts.append('pre'); |
459 | - al.append('p').attr('class', 'footnote') | 475 | + $alerts.append('p').attr('class', 'footnote') |
460 | .text('Press ESCAPE to close'); | 476 | .text('Press ESCAPE to close'); |
461 | alerts.open = true; | 477 | alerts.open = true; |
462 | alerts.count = 0; | 478 | alerts.count = 0; |
463 | } | 479 | } |
464 | 480 | ||
465 | function closeAlerts() { | 481 | function closeAlerts() { |
466 | - d3.select('#alerts') | 482 | + $alerts.style('display', 'none') |
467 | - .style('display', 'none') | ||
468 | .html(''); | 483 | .html(''); |
469 | alerts.open = false; | 484 | alerts.open = false; |
470 | } | 485 | } |
... | @@ -474,7 +489,7 @@ | ... | @@ -474,7 +489,7 @@ |
474 | oldContent; | 489 | oldContent; |
475 | 490 | ||
476 | if (alerts.count) { | 491 | if (alerts.count) { |
477 | - oldContent = d3.select('#alerts pre').html(); | 492 | + oldContent = $alerts.select('pre').html(); |
478 | } | 493 | } |
479 | 494 | ||
480 | lines = msg.split('\n'); | 495 | lines = msg.split('\n'); |
... | @@ -485,7 +500,7 @@ | ... | @@ -485,7 +500,7 @@ |
485 | lines += '\n----\n' + oldContent; | 500 | lines += '\n----\n' + oldContent; |
486 | } | 501 | } |
487 | 502 | ||
488 | - d3.select('#alerts pre').html(lines); | 503 | + $alerts.select('pre').html(lines); |
489 | alerts.count++; | 504 | alerts.count++; |
490 | } | 505 | } |
491 | 506 | ||
... | @@ -691,6 +706,53 @@ | ... | @@ -691,6 +706,53 @@ |
691 | libApi[libName] = api; | 706 | libApi[libName] = api; |
692 | }, | 707 | }, |
693 | 708 | ||
709 | + // TODO: implement floating panel as a class | ||
710 | + // TODO: parameterize position (currently hard-coded to TopRight) | ||
711 | + /* | ||
712 | + * Creates div in floating panels block, with the given id. | ||
713 | + * Returns panel token used to interact with the panel | ||
714 | + */ | ||
715 | + addFloatingPanel: function (id, position) { | ||
716 | + var pos = position || 'TR', | ||
717 | + el, | ||
718 | + fp; | ||
719 | + | ||
720 | + if (fpanels[id]) { | ||
721 | + buildError('Float panel with id "' + id + '" already exists.'); | ||
722 | + return null; | ||
723 | + } | ||
724 | + | ||
725 | + el = $floatPanels.append('div') | ||
726 | + .attr('id', id) | ||
727 | + .attr('class', 'fpanel'); | ||
728 | + | ||
729 | + fp = { | ||
730 | + id: id, | ||
731 | + el: el, | ||
732 | + pos: pos, | ||
733 | + show: function () { | ||
734 | + console.log('show pane: ' + id); | ||
735 | + el.transition().duration(750) | ||
736 | + .style('right', '20px') | ||
737 | + .style('opacity', 1); | ||
738 | + }, | ||
739 | + hide: function () { | ||
740 | + console.log('hide pane: ' + id); | ||
741 | + el.transition().duration(750) | ||
742 | + .style('right', '-320px') | ||
743 | + .style('opacity', 0); | ||
744 | + }, | ||
745 | + empty: function () { | ||
746 | + return el.html(''); | ||
747 | + }, | ||
748 | + append: function (what) { | ||
749 | + return el.append(what); | ||
750 | + } | ||
751 | + }; | ||
752 | + fpanels[id] = fp; | ||
753 | + return fp; | ||
754 | + }, | ||
755 | + | ||
694 | // TODO: it remains to be seen whether we keep this style of docs | 756 | // TODO: it remains to be seen whether we keep this style of docs |
695 | /** @api ui addView( vid, nid, cb ) | 757 | /** @api ui addView( vid, nid, cb ) |
696 | * Adds a view to the UI. | 758 | * Adds a view to the UI. |
... | @@ -782,7 +844,6 @@ | ... | @@ -782,7 +844,6 @@ |
782 | } | 844 | } |
783 | built = true; | 845 | built = true; |
784 | 846 | ||
785 | - $view = d3.select('#view'); | ||
786 | $mastRadio = d3.select('#mastRadio'); | 847 | $mastRadio = d3.select('#mastRadio'); |
787 | 848 | ||
788 | $(window).on('hashchange', hash); | 849 | $(window).on('hashchange', hash); | ... | ... |
... | @@ -96,3 +96,45 @@ | ... | @@ -96,3 +96,45 @@ |
96 | fill: white; | 96 | fill: white; |
97 | stroke: red; | 97 | stroke: red; |
98 | } | 98 | } |
99 | + | ||
100 | + | ||
101 | +/* detail topo-detail pane */ | ||
102 | + | ||
103 | +#topo-detail { | ||
104 | +/* gets base CSS from .fpanel in floatPanel.css */ | ||
105 | +} | ||
106 | + | ||
107 | + | ||
108 | +#topo-detail h2 { | ||
109 | + margin: 8px 4px; | ||
110 | + color: black; | ||
111 | + vertical-align: middle; | ||
112 | +} | ||
113 | + | ||
114 | +#topo-detail h2 img { | ||
115 | + height: 32px; | ||
116 | + padding-right: 8px; | ||
117 | + vertical-align: middle; | ||
118 | +} | ||
119 | + | ||
120 | +#topo-detail p, table { | ||
121 | + margin: 4px 4px; | ||
122 | +} | ||
123 | + | ||
124 | +#topo-detail td.label { | ||
125 | + font-style: italic; | ||
126 | + color: #777; | ||
127 | + padding-right: 12px; | ||
128 | +} | ||
129 | + | ||
130 | +#topo-detail td.value { | ||
131 | + | ||
132 | +} | ||
133 | + | ||
134 | +#topo-detail hr { | ||
135 | + height: 1px; | ||
136 | + color: #ccc; | ||
137 | + background-color: #ccc; | ||
138 | + border: 0; | ||
139 | +} | ||
140 | + | ... | ... |
... | @@ -152,7 +152,7 @@ | ... | @@ -152,7 +152,7 @@ |
152 | webSock, | 152 | webSock, |
153 | deviceLabelIndex = 0, | 153 | deviceLabelIndex = 0, |
154 | hostLabelIndex = 0, | 154 | hostLabelIndex = 0, |
155 | - | 155 | + detailPane, |
156 | selectOrder = [], | 156 | selectOrder = [], |
157 | selections = {}, | 157 | selections = {}, |
158 | 158 | ||
... | @@ -192,6 +192,10 @@ | ... | @@ -192,6 +192,10 @@ |
192 | 192 | ||
193 | function testMe(view) { | 193 | function testMe(view) { |
194 | view.alert('test'); | 194 | view.alert('test'); |
195 | + detailPane.show(); | ||
196 | + setTimeout(function () { | ||
197 | + detailPane.hide(); | ||
198 | + }, 3000); | ||
195 | } | 199 | } |
196 | 200 | ||
197 | function abortIfLive() { | 201 | function abortIfLive() { |
... | @@ -285,14 +289,6 @@ | ... | @@ -285,14 +289,6 @@ |
285 | view.alert('unpin() callback') | 289 | view.alert('unpin() callback') |
286 | } | 290 | } |
287 | 291 | ||
288 | - function requestPath(view) { | ||
289 | - var payload = { | ||
290 | - one: selections[selectOrder[0]].obj.id, | ||
291 | - two: selections[selectOrder[1]].obj.id | ||
292 | - } | ||
293 | - sendMessage('requestPath', payload); | ||
294 | - } | ||
295 | - | ||
296 | // ============================== | 292 | // ============================== |
297 | // Radio Button Callbacks | 293 | // Radio Button Callbacks |
298 | 294 | ||
... | @@ -353,6 +349,7 @@ | ... | @@ -353,6 +349,7 @@ |
353 | removeDevice: stillToImplement, | 349 | removeDevice: stillToImplement, |
354 | removeLink: removeLink, | 350 | removeLink: removeLink, |
355 | removeHost: removeHost, | 351 | removeHost: removeHost, |
352 | + showDetails: showDetails, | ||
356 | showPath: showPath | 353 | showPath: showPath |
357 | }; | 354 | }; |
358 | 355 | ||
... | @@ -463,6 +460,12 @@ | ... | @@ -463,6 +460,12 @@ |
463 | } | 460 | } |
464 | } | 461 | } |
465 | 462 | ||
463 | + function showDetails(data) { | ||
464 | + fnTrace('showDetails', data.payload.id); | ||
465 | + populateDetails(data.payload); | ||
466 | + detailPane.show(); | ||
467 | + } | ||
468 | + | ||
466 | function showPath(data) { | 469 | function showPath(data) { |
467 | fnTrace('showPath', data.payload.id); | 470 | fnTrace('showPath', data.payload.id); |
468 | var links = data.payload.links, | 471 | var links = data.payload.links, |
... | @@ -500,6 +503,32 @@ | ... | @@ -500,6 +503,32 @@ |
500 | } | 503 | } |
501 | 504 | ||
502 | // ============================== | 505 | // ============================== |
506 | + // Out-going messages... | ||
507 | + | ||
508 | + function getSel(idx) { | ||
509 | + return selections[selectOrder[idx]]; | ||
510 | + } | ||
511 | + | ||
512 | + // for now, just a host-to-host intent, (and implicit start-monitoring) | ||
513 | + function requestPath() { | ||
514 | + var payload = { | ||
515 | + one: getSel(0).obj.id, | ||
516 | + two: getSel(1).obj.id | ||
517 | + }; | ||
518 | + sendMessage('requestPath', payload); | ||
519 | + } | ||
520 | + | ||
521 | + // request details for the selected element | ||
522 | + function requestDetails() { | ||
523 | + var data = getSel(0).obj, | ||
524 | + payload = { | ||
525 | + id: data.id, | ||
526 | + class: data.class | ||
527 | + }; | ||
528 | + sendMessage('requestDetails', payload); | ||
529 | + } | ||
530 | + | ||
531 | + // ============================== | ||
503 | // force layout modification functions | 532 | // force layout modification functions |
504 | 533 | ||
505 | function translate(x, y) { | 534 | function translate(x, y) { |
... | @@ -1015,6 +1044,8 @@ | ... | @@ -1015,6 +1044,8 @@ |
1015 | 1044 | ||
1016 | var sid = 0; | 1045 | var sid = 0; |
1017 | 1046 | ||
1047 | + // TODO: use cache of pending messages (key = sid) to reconcile responses | ||
1048 | + | ||
1018 | function sendMessage(evType, payload) { | 1049 | function sendMessage(evType, payload) { |
1019 | var toSend = { | 1050 | var toSend = { |
1020 | event: evType, | 1051 | event: evType, |
... | @@ -1033,7 +1064,6 @@ | ... | @@ -1033,7 +1064,6 @@ |
1033 | wsTrace('rx', msg); | 1064 | wsTrace('rx', msg); |
1034 | } | 1065 | } |
1035 | function wsTrace(rxtx, msg) { | 1066 | function wsTrace(rxtx, msg) { |
1036 | - | ||
1037 | console.log('[' + rxtx + '] ' + msg); | 1067 | console.log('[' + rxtx + '] ' + msg); |
1038 | // TODO: integrate with trace view | 1068 | // TODO: integrate with trace view |
1039 | //if (trace) { | 1069 | //if (trace) { |
... | @@ -1062,7 +1092,7 @@ | ... | @@ -1062,7 +1092,7 @@ |
1062 | 1092 | ||
1063 | if (meta && n.classed('selected')) { | 1093 | if (meta && n.classed('selected')) { |
1064 | deselectObject(obj.id); | 1094 | deselectObject(obj.id); |
1065 | - //flyinPane(null); | 1095 | + updateDetailPane(); |
1066 | return; | 1096 | return; |
1067 | } | 1097 | } |
1068 | 1098 | ||
... | @@ -1074,17 +1104,16 @@ | ... | @@ -1074,17 +1104,16 @@ |
1074 | selectOrder.push(obj.id); | 1104 | selectOrder.push(obj.id); |
1075 | 1105 | ||
1076 | n.classed('selected', true); | 1106 | n.classed('selected', true); |
1077 | - //flyinPane(obj); | 1107 | + updateDetailPane(); |
1078 | } | 1108 | } |
1079 | 1109 | ||
1080 | function deselectObject(id) { | 1110 | function deselectObject(id) { |
1081 | var obj = selections[id]; | 1111 | var obj = selections[id]; |
1082 | if (obj) { | 1112 | if (obj) { |
1083 | d3.select(obj.el).classed('selected', false); | 1113 | d3.select(obj.el).classed('selected', false); |
1084 | - selections[id] = null; | 1114 | + delete selections[id]; |
1085 | - // TODO: use splice to remove element | ||
1086 | } | 1115 | } |
1087 | - //flyinPane(null); | 1116 | + updateDetailPane(); |
1088 | } | 1117 | } |
1089 | 1118 | ||
1090 | function deselectAll() { | 1119 | function deselectAll() { |
... | @@ -1092,10 +1121,10 @@ | ... | @@ -1092,10 +1121,10 @@ |
1092 | node.classed('selected', false); | 1121 | node.classed('selected', false); |
1093 | selections = {}; | 1122 | selections = {}; |
1094 | selectOrder = []; | 1123 | selectOrder = []; |
1095 | - //flyinPane(null); | 1124 | + updateDetailPane(); |
1096 | } | 1125 | } |
1097 | 1126 | ||
1098 | - // TODO: this click handler does not get unloaded when the view does | 1127 | + // FIXME: this click handler does not get unloaded when the view does |
1099 | $('#view').on('click', function(e) { | 1128 | $('#view').on('click', function(e) { |
1100 | if (!$(e.target).closest('.node').length) { | 1129 | if (!$(e.target).closest('.node').length) { |
1101 | if (!e.metaKey) { | 1130 | if (!e.metaKey) { |
... | @@ -1104,6 +1133,66 @@ | ... | @@ -1104,6 +1133,66 @@ |
1104 | } | 1133 | } |
1105 | }); | 1134 | }); |
1106 | 1135 | ||
1136 | + // update the state of the detail pane, based on current selections | ||
1137 | + function updateDetailPane() { | ||
1138 | + var nSel = selectOrder.length; | ||
1139 | + if (!nSel) { | ||
1140 | + detailPane.hide(); | ||
1141 | + } else if (nSel === 1) { | ||
1142 | + singleSelect(); | ||
1143 | + } else { | ||
1144 | + multiSelect(); | ||
1145 | + } | ||
1146 | + } | ||
1147 | + | ||
1148 | + function singleSelect() { | ||
1149 | + requestDetails(); | ||
1150 | + // NOTE: detail pane will be shown from showDetails event. | ||
1151 | + } | ||
1152 | + | ||
1153 | + function multiSelect() { | ||
1154 | + // TODO: use detail pane for multi-select view. | ||
1155 | + //detailPane.show(); | ||
1156 | + } | ||
1157 | + | ||
1158 | + function populateDetails(data) { | ||
1159 | + detailPane.empty(); | ||
1160 | + | ||
1161 | + var title = detailPane.append("h2"), | ||
1162 | + table = detailPane.append("table"), | ||
1163 | + tbody = table.append("tbody"); | ||
1164 | + | ||
1165 | + $('<img src="img/' + data.type + '.png">').appendTo(title); | ||
1166 | + $('<span>').attr('class', 'icon').text(data.id).appendTo(title); | ||
1167 | + | ||
1168 | + data.propOrder.forEach(function(p) { | ||
1169 | + if (p === '-') { | ||
1170 | + addSep(tbody); | ||
1171 | + } else { | ||
1172 | + addProp(tbody, p, data.props[p]); | ||
1173 | + } | ||
1174 | + }); | ||
1175 | + | ||
1176 | + function addSep(tbody) { | ||
1177 | + var tr = tbody.append('tr'); | ||
1178 | + $('<hr>').appendTo(tr.append('td').attr('colspan', 2)); | ||
1179 | + } | ||
1180 | + | ||
1181 | + function addProp(tbody, label, value) { | ||
1182 | + var tr = tbody.append('tr'); | ||
1183 | + | ||
1184 | + tr.append('td') | ||
1185 | + .attr('class', 'label') | ||
1186 | + .text(label + ' :'); | ||
1187 | + | ||
1188 | + tr.append('td') | ||
1189 | + .attr('class', 'value') | ||
1190 | + .text(value); | ||
1191 | + } | ||
1192 | + } | ||
1193 | + | ||
1194 | + // ============================== | ||
1195 | + // Test harness code | ||
1107 | 1196 | ||
1108 | function prepareScenario(view, ctx, dbg) { | 1197 | function prepareScenario(view, ctx, dbg) { |
1109 | var sc = scenario, | 1198 | var sc = scenario, |
... | @@ -1272,4 +1361,6 @@ | ... | @@ -1272,4 +1361,6 @@ |
1272 | resize: resize | 1361 | resize: resize |
1273 | }); | 1362 | }); |
1274 | 1363 | ||
1364 | + detailPane = onos.ui.addFloatingPanel('topo-detail'); | ||
1365 | + | ||
1275 | }(ONOS)); | 1366 | }(ONOS)); | ... | ... |
-
Please register or login to post a comment