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