GUI -- TopoView - Implemented much of the node selection logic. (WIP)
- introduced topoSelect.js.
Change-Id: Ic843c7d8dc2249fe0cb8c33de60dce12c07aea44
diff --git a/web/gui/src/main/webapp/app/fw/layer/panel.js b/web/gui/src/main/webapp/app/fw/layer/panel.js
index a29c175..46934f1 100644
--- a/web/gui/src/main/webapp/app/fw/layer/panel.js
+++ b/web/gui/src/main/webapp/app/fw/layer/panel.js
@@ -77,6 +77,7 @@
width: panelWidth,
height: panelHeight,
isVisible: panelIsVisible,
+ classed: classed,
el: panelEl
};
@@ -146,6 +147,10 @@
return p.on;
}
+ function classed(cls, bool) {
+ return p.el.classed(cls, bool);
+ }
+
function panelEl() {
return p.el;
}
diff --git a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
index ab56ddd..2c47d44 100644
--- a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
+++ b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
@@ -34,6 +34,7 @@
$log = _$log_;
fs = _fs_;
+ // TODO: change 'force' ref to be 'force.alpha' ref.
function createDragBehavior(force, selectCb, atDragEnd,
dragEnabled, clickEnabled) {
var draggedThreshold = d3.scale.linear()
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index b7b8af0..ccc737b 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -82,9 +82,10 @@
<script src="view/topo/topo.js"></script>
<script src="view/topo/topoEvent.js"></script>
<script src="view/topo/topoForce.js"></script>
+ <script src="view/topo/topoInst.js"></script>
<script src="view/topo/topoModel.js"></script>
<script src="view/topo/topoPanel.js"></script>
- <script src="view/topo/topoInst.js"></script>
+ <script src="view/topo/topoSelect.js"></script>
<script src="view/device/device.js"></script>
<!-- TODO: inject javascript refs server-side -->
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index 751d280..845bf75 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -72,71 +72,121 @@
#topo-p-summary {
/* Base css from panel.css */
-
}
-#topo-p-summary svg {
+/* --- Topo Detail Panel --- */
+
+#topo-p-detail {
+ /* Base css from panel.css */
+ top: 320px;
+}
+
+/* --- general topo-panel styling --- */
+
+.topo-p svg {
display: inline-block;
width: 42px;
height: 42px;
}
-#topo-p-summary h2 {
+.light .topo-p svg .glyph {
+ fill: #222;
+}
+
+.dark .topo-p svg .glyph.overlay {
+ fill: #222;
+}
+
+.dark .topo-p svg .glyph {
+ fill: #ddd;
+}
+.light .topo-p svg .glyph.overlay {
+ fill: #fff;
+}
+
+
+.topo-p h2 {
position: absolute;
margin: 0 4px;
top: 20px;
left: 50px;
}
-.light #topo-p-summary h2 {
+.light .topo-p h2 {
color: black;
}
-.dark #topo-p-summary h2 {
+.dark .topo-p h2 {
color: #ddd;
}
-#topo-p-summary h3 {
+.topo-p h3 {
margin: 0 4px;
top: 20px;
left: 50px;
}
-.light #topo-p-summary h3 {
+.light .topo-p h3 {
color: black;
}
-.dark #topo-p-summary h3 {
+.dark .topo-p h3 {
color: #ddd;
}
-#topo-p-summary p, table {
+.topo-p p, table {
margin: 4px 4px;
}
-#topo-p-summary td.label {
+.topo-p td.label {
font-style: italic;
padding-right: 12px;
/* works for both light and dark themes ... */
color: #777;
}
-#topo-p-summary td.value {
+.topo-p td.value {
}
-#topo-p-summary hr {
+.topo-p hr {
height: 1px;
border: 0;
}
-.light #topo-p-summary hr {
+.light .topo-p hr {
background-color: #ccc;
color: #ccc;
}
-.dark #topo-p-summary hr {
+.dark .topo-p hr {
background-color: #888;
color: #888;
}
-/* --- Topo Detail Panel --- */
+.topo-p .actionBtn {
+ margin: 6px 12px;
+ padding: 2px 6px;
+ font-size: 9pt;
+ cursor: pointer;
+ width: 200px;
+ text-align: center;
+ border-radius: 4px;
+}
+.light .topo-p .actionBtn {
+ border: 2px solid #ddd;
+ color: #eee;
+ background: #888;
+}
+.dark .topo-p .actionBtn {
+ border: 2px solid #222;
+ color: #888;
+ background: #444;
+}
-/* TODO: add CSS rules */
+.light .topo-p .actionBtn:hover {
+ color: #eee;
+ background: #444;
+}
+.dark .topo-p .actionBtn:hover {
+ color: #eee;
+ background: #666;
+}
+
/* --- Topo Instance Panel --- */
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index a512704..a835d6b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -66,7 +66,7 @@
//E: [equalizeMasters, 'Equalize mastership roles'],
- //esc: handleEscape,
+ esc: handleEscape,
_helpFormat: [
['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ],
@@ -85,12 +85,29 @@
];
}
+ // --- Keystroke functions -------------------------------------------
function toggleInstances() {
tis.toggle();
tfs.updateDeviceColors();
}
+ function resetZoom() {
+ zoomer.reset();
+ }
+
+ function handleEscape() {
+ $log.debug("TODO: handle-ESCAPE...");
+ // if showingAffinity: cancelAffinity
+
+ // else if showingDetails: deselectAll
+
+ // else if oiBox visible: hide oiBox
+
+ // else if summary panel visible: cancel Summary
+
+ // else: hoverMode = hoverModeNone
+ }
// --- Glyphs, Icons, and the like -----------------------------------
@@ -124,10 +141,6 @@
});
}
- function resetZoom() {
- zoomer.reset();
- }
-
// callback invoked when the SVG view has been resized..
function svgResized(s) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index 887fe4a..edebb52 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -27,7 +27,7 @@
'use strict';
// injected refs
- var $log, wss, wes, tps, tis, tfs;
+ var $log, wss, wes, tps, tis, tfs, tss;
// internal state
var wsock, evApis;
@@ -37,9 +37,13 @@
function bindApis() {
evApis = {
showSummary: tps,
+
+ showDetails: tss,
+
addInstance: tis,
updateInstance: tis,
removeInstance: tis,
+
addDevice: tfs,
updateDevice: tfs,
removeDevice: tfs,
@@ -100,14 +104,16 @@
.factory('TopoEventService',
['$log', '$location', 'WebSocketService', 'WsEventService',
'TopoPanelService', 'TopoInstService', 'TopoForceService',
+ 'TopoSelectService',
- function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_) {
+ function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_, _tss_) {
$log = _$log_;
wss = _wss_;
wes = _wes_;
tps = _tps_;
tis = _tis_;
tfs = _tfs_;
+ tss = _tss_;
bindApis();
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 703212f..87b554e 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -23,7 +23,7 @@
'use strict';
// injected refs
- var $log, fs, sus, is, ts, flash, tis, tms, icfg, uplink;
+ var $log, fs, sus, is, ts, flash, tis, tms, tss, icfg, uplink;
// configuration
var labelConfig = {
@@ -77,10 +77,7 @@
showOffline = true, // whether offline devices are displayed
oblique = false, // whether we are in the oblique view
nodeLock = false, // whether nodes can be dragged or not (locked)
- dim, // the dimensions of the force layout [w,h]
- hovered, // the node over which the mouse is hovering
- selections = {}, // what is currently selected
- selectOrder = []; // the order in which we made selections
+ dim; // the dimensions of the force layout [w,h]
// SVG elements;
var linkG, linkLabelG, nodeG;
@@ -311,8 +308,6 @@
.attr('stroke', linkConfig[th].baseColor);
}
-
-
function removeLinkElement(d) {
var idx = fs.find(d.key, network.links, 'key'),
removed;
@@ -418,34 +413,10 @@
});
}
- function requestTrafficForMode() {
- $log.debug('TODO: requestTrafficForMode()...');
- }
-
// ==========================
// === Devices and hosts - D3 rendering
- function nodeMouseOver(m) {
- if (!m.dragStarted) {
- $log.debug("MouseOver()...", m);
- if (hovered != m) {
- hovered = m;
- requestTrafficForMode();
- }
- }
- }
-
- function nodeMouseOut(m) {
- if (!m.dragStarted) {
- if (hovered) {
- hovered = null;
- requestTrafficForMode();
- }
- $log.debug("MouseOut()...", m);
- }
- }
-
// Returns the newly computed bounding box of the rectangle
function adjustRectToFitText(n) {
@@ -568,10 +539,11 @@
}
function unpin() {
- if (hovered) {
- sendUpdateMeta(hovered, true);
- hovered.fixed = false;
- hovered.el.classed('fixed', false);
+ var hov = tss.hovered();
+ if (hov) {
+ sendUpdateMeta(hov, true);
+ hov.fixed = false;
+ hov.el.classed('fixed', false);
fResume();
}
}
@@ -668,8 +640,8 @@
opacity: 0
})
.call(drag)
- .on('mouseover', nodeMouseOver)
- .on('mouseout', nodeMouseOut)
+ .on('mouseover', tss.nodeMouseOver)
+ .on('mouseout', tss.nodeMouseOut)
.transition()
.attr('opacity', 1);
@@ -998,72 +970,6 @@
}
- function updateDetailPanel() {
- // TODO update detail panel
- $log.debug("TODO: updateDetailPanel() ...");
- }
-
-
- // ==========================
- // === SELECTION / DESELECTION
-
- function selectObject(obj) {
- var el = this,
- ev = d3.event.sourceEvent,
- n;
-
- if (zoomingOrPanning(ev)) {
- return;
- }
-
- if (el) {
- n = d3.select(el);
- } else {
- node.each(function (d) {
- if (d == obj) {
- n = d3.select(el = this);
- }
- });
- }
- if (!n) return;
-
- if (ev.shiftKey && n.classed('selected')) {
- deselectObject(obj.id);
- updateDetailPanel();
- return;
- }
-
- if (!ev.shiftKey) {
- deselectAll();
- }
-
- selections[obj.id] = { obj: obj, el: el };
- selectOrder.push(obj.id);
-
- n.classed('selected', true);
- updateDeviceColors(obj);
- updateDetailPanel();
- }
-
- function deselectObject(id) {
- var obj = selections[id];
- if (obj) {
- d3.select(obj.el).classed('selected', false);
- delete selections[id];
- fs.removeFromArray(id, selectOrder);
- updateDeviceColors(obj.obj);
- }
- }
-
- function deselectAll() {
- // deselect all nodes in the network...
- node.classed('selected', false);
- selections = {};
- selectOrder = [];
- updateDeviceColors();
- updateDetailPanel();
- }
-
// ==========================
// === MOUSE GESTURE HANDLERS
@@ -1103,12 +1009,22 @@
};
}
+ function mkSelectApi(uplink) {
+ return {
+ node: function () { return node; },
+ zoomingOrPanning: zoomingOrPanning,
+ updateDeviceColors: updateDeviceColors,
+ sendEvent: uplink.sendEvent
+ };
+ }
+
angular.module('ovTopo')
.factory('TopoForceService',
['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
'FlashService', 'TopoInstService', 'TopoModelService',
+ 'TopoSelectService',
- function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_) {
+ function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_, _tss_) {
$log = _$log_;
fs = _fs_;
sus = _sus_;
@@ -1117,6 +1033,7 @@
flash = _flash_;
tis = _tis_;
tms = _tms_;
+ tss = _tss_;
icfg = is.iconConfig();
@@ -1131,6 +1048,7 @@
$log.debug('initForce().. dim = ' + dim);
tms.initModel(mkModelApi(uplink), dim);
+ tss.initSelect(mkSelectApi(uplink));
settings = angular.extend({}, defaultSettings, opts);
@@ -1154,7 +1072,7 @@
.on('tick', tick);
drag = sus.createDragBehavior(force,
- selectObject, atDragEnd, dragEnabled, clickEnabled);
+ tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
}
function newDim(_dim_) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index 4754f60..de31eef 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -24,18 +24,21 @@
'use strict';
// injected refs
- var $log, fs, rnd, api;
+ var $log, fs, rnd;
+
+ // api to topoForce
+ var api;
+ /*
+ projection()
+ network {...}
+ restyleLinkElement( ldata )
+ removeLinkElement( ldata )
+ */
// shorthand
var lu, rlk, nodes, links;
- // api:
- // projection: func()
- // network {...}
- // restyleLinkElement: func(ldata)
- // removeLinkElement: func(ldata)
-
- var dim; // dimensions of layout, as [w,h]
+ var dim; // dimensions of layout [w,h]
// configuration 'constants'
var defaultLinkType = 'direct',
diff --git a/web/gui/src/main/webapp/app/view/topo/topoPanel.js b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
index 627a94e..3053643 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
@@ -26,7 +26,8 @@
var $log, ps, gs;
// constants
- var idSum = 'topo-p-summary',
+ var pCls = 'topo-p',
+ idSum = 'topo-p-summary',
idDet = 'topo-p-detail',
panelOpts = {
width: 260
@@ -36,35 +37,9 @@
var summaryPanel,
detailPanel;
- // ==========================
- // *** SHOW SUMMARY ***
- function showSummary(data) {
- populateSummary(data);
- showSummaryPanel();
- }
-
- function populateSummary(data) {
- summaryPanel.empty();
-
- var svg = summaryPanel.append('svg'),
- title = summaryPanel.append('h2'),
- table = summaryPanel.append('table'),
- tbody = table.append('tbody');
-
- gs.addGlyph(svg, 'node', 40);
- gs.addGlyph(svg, 'bird', 24, true, [8,12]);
-
- title.text(data.id);
-
- data.propOrder.forEach(function(p) {
- if (p === '-') {
- addSep(tbody);
- } else {
- addProp(tbody, p, data.props[p]);
- }
- });
- }
+ // === -----------------------------------------------------
+ // Utility functions
function addSep(tbody) {
tbody.append('tr').append('td').attr('colspan', 2).append('hr');
@@ -80,16 +55,116 @@
addCell('value', value);
}
+ function listProps(tbody, data) {
+ data.propOrder.forEach(function(p) {
+ if (p === '-') {
+ addSep(tbody);
+ } else {
+ addProp(tbody, p, data.props[p]);
+ }
+ });
+ }
+
+ function dpa(x) {
+ return detailPanel.append(x);
+ }
+
+ function spa(x) {
+ return summaryPanel.append(x);
+ }
+
+ // === -----------------------------------------------------
+ // Functions for populating the summary panel
+
+ function populateSummary(data) {
+ summaryPanel.empty();
+
+ var svg = spa('svg'),
+ title = spa('h2'),
+ table = spa('table'),
+ tbody = table.append('tbody');
+
+ gs.addGlyph(svg, 'node', 40);
+ gs.addGlyph(svg, 'bird', 24, true, [8,12]);
+
+ title.text(data.id);
+ listProps(tbody, data);
+ }
+
+ // === -----------------------------------------------------
+ // Functions for populating the detail panel
+
+ function displaySingle(data) {
+ detailPanel.empty();
+
+ var svg = dpa('svg'),
+ title = dpa('h2'),
+ table = dpa('table'),
+ tbody = table.append('tbody');
+
+ gs.addGlyph(svg, (data.type || 'unknown'), 40);
+ title.text(data.id);
+ listProps(tbody, data);
+ dpa('hr');
+ }
+
+ function displayMulti(ids) {
+ detailPanel.empty();
+
+ var title = dpa('h3'),
+ table = dpa('table'),
+ tbody = table.append('tbody');
+
+ title.text('Selected Nodes');
+ ids.forEach(function (d, i) {
+ addProp(tbody, i+1, d);
+ });
+ dpa('hr');
+ }
+
+ function addAction(text, cb) {
+ dpa('div')
+ .classed('actionBtn', true)
+ .text(text)
+ .on('click', cb);
+ }
+
+ // === -----------------------------------------------------
+ // Event Handlers
+
+ function showSummary(data) {
+ populateSummary(data);
+ showSummaryPanel();
+ }
+
+
+ // === -----------------------------------------------------
+ // === LOGIC For showing/hiding summary and detail panels...
+
function showSummaryPanel() {
summaryPanel.show();
// TODO: augment, once we have the details pane also
}
+ function showDetailPanel() {
+ // TODO: augment with summary-accomodation-logic
+ detailPanel.show();
+ }
+
+ function hideDetailPanel() {
+ detailPanel.hide();
+ }
+
+
+
// ==========================
function initPanels() {
summaryPanel = ps.createPanel(idSum, panelOpts);
detailPanel = ps.createPanel(idDet, panelOpts);
+
+ summaryPanel.classed(pCls, true);
+ detailPanel.classed(pCls, true);
}
function destroyPanels() {
@@ -112,7 +187,18 @@
return {
initPanels: initPanels,
destroyPanels: destroyPanels,
- showSummary: showSummary
+
+ showSummary: showSummary,
+
+ displaySingle: displaySingle,
+ displayMulti: displayMulti,
+ addAction: addAction,
+
+ showDetailPanel: showDetailPanel,
+ hideDetailPanel: hideDetailPanel,
+
+ detailVisible: function () { return detailPanel.isVisible(); },
+ summaryVisible: function () { return summaryPanel.isVisible(); }
};
}]);
}());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
new file mode 100644
index 0000000..b7790f2
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
@@ -0,0 +1,293 @@
+/*
+ * 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 Selection Module.
+ Defines behavior when selecting nodes.
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, fs, tps;
+
+ // api to topoForce
+ var api;
+ /*
+ node() // get ref to D3 selection of nodes
+ zoomingOrPanning( ev )
+ updateDeviceColors( [dev] )
+ sendEvent( type, {payload} )
+ */
+
+ // internal state
+ var hovered, // the node over which the mouse is hovering
+ selections = {}, // currently selected nodes (by id)
+ selectOrder = [], // the order in which we made selections
+ haveDetails = false, // do we have details of one or more nodes?
+ useDetails = true; // should we show details if we have 'em?
+
+ // ==========================
+
+ function nSel() {
+ return selectOrder.length;
+ }
+ function getSel(idx) {
+ return selections[selectOrder[idx]];
+ }
+ function allSelectionsClass(cls) {
+ for (var i=0, n=nSel(); i<n; i++) {
+ if (getSel(i).obj.class !== cls) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // ==========================
+
+ function nodeMouseOver(m) {
+ if (!m.dragStarted) {
+ $log.debug("MouseOver()...", m);
+ if (hovered != m) {
+ hovered = m;
+ requestTrafficForMode();
+ }
+ }
+ }
+
+ function nodeMouseOut(m) {
+ if (!m.dragStarted) {
+ if (hovered) {
+ hovered = null;
+ requestTrafficForMode();
+ }
+ $log.debug("MouseOut()...", m);
+ }
+ }
+
+ // ==========================
+
+ function selectObject(obj) {
+ var el = this,
+ ev = d3.event.sourceEvent,
+ n;
+
+ if (api.zoomingOrPanning(ev)) {
+ return;
+ }
+
+ if (el) {
+ n = d3.select(el);
+ } else {
+ api.node().each(function (d) {
+ if (d == obj) {
+ n = d3.select(el = this);
+ }
+ });
+ }
+ if (!n) return;
+
+ if (ev.shiftKey && n.classed('selected')) {
+ deselectObject(obj.id);
+ updateDetail();
+ return;
+ }
+
+ if (!ev.shiftKey) {
+ deselectAll();
+ }
+
+ selections[obj.id] = { obj: obj, el: el };
+ selectOrder.push(obj.id);
+
+ n.classed('selected', true);
+ api.updateDeviceColors(obj);
+ updateDetail();
+
+ debugSel();
+ }
+
+ function deselectObject(id) {
+ var obj = selections[id];
+ if (obj) {
+ d3.select(obj.el).classed('selected', false);
+ delete selections[id];
+ fs.removeFromArray(id, selectOrder);
+ api.updateDeviceColors(obj.obj);
+ }
+
+ debugSel();
+ }
+
+ function deselectAll() {
+ // deselect all nodes in the network...
+ api.node().classed('selected', false);
+ selections = {};
+ selectOrder = [];
+ api.updateDeviceColors();
+ updateDetail();
+
+ debugSel();
+ }
+
+ function debugSel() {
+ $log.debug(' ..... Selected now >> ', selectOrder);
+ }
+
+ // === -----------------------------------------------------
+
+ function requestDetails() {
+ var data = getSel(0).obj;
+ api.sendEvent('requestDetails', {
+ id: data.id,
+ class: data.class
+ });
+ }
+
+ // === -----------------------------------------------------
+
+ function updateDetail() {
+ var nSel = selectOrder.length;
+ if (!nSel) {
+ emptySelect();
+ } else if (nSel === 1) {
+ singleSelect();
+ } else {
+ multiSelect();
+ }
+ }
+
+ function emptySelect() {
+ haveDetails = false;
+ tps.hideDetailPanel();
+ cancelTraffic();
+ }
+
+ function singleSelect() {
+ // NOTE: detail is shown from 'showDetails' event callback
+ requestDetails();
+ cancelTraffic();
+ requestTrafficForMode();
+ }
+
+ function multiSelect() {
+ haveDetails = true;
+
+ // display the selected nodes in the detail panel
+ tps.displayMulti(selectOrder);
+
+ // always add the 'show traffic' action
+ tps.addAction('Show Related Traffic', showRelatedIntentsAction);
+
+ // add other actions, based on what is selected...
+ if (nSel() === 2 && allSelectionsClass('host')) {
+ tps.addAction('Create Host-to-Host Flow', addHostIntentAction);
+ } else if (nSel() >= 2 && allSelectionsClass('host')) {
+ tps.addAction('Create Multi-Source Flow', addMultiSourceIntentAction);
+ }
+
+ cancelTraffic();
+ requestTrafficForMode();
+ }
+
+
+ // === -----------------------------------------------------
+ // Event Handlers
+
+ function showDetails(data) {
+ haveDetails = true;
+
+ // display the data for the single selected node
+ tps.displaySingle(data);
+
+ // always add the 'show traffic' action
+ tps.addAction('Show Related Traffic', showRelatedIntentsAction);
+
+ // add other actions, based on what is selected...
+ if (data.type === 'switch') {
+ tps.addAction('Show Device Flows', showDeviceLinkFlowsAction);
+ }
+
+ // only show the details panel if the user hasn't "hidden" it
+ if (useDetails) {
+ tps.showDetailPanel();
+ }
+ }
+
+ // === -----------------------------------------------------
+ // TODO: migrate these to topoTraffic.js
+
+ function cancelTraffic() {
+ $log.debug('TODO: cancelTraffic');
+
+ }
+ function requestTrafficForMode() {
+ $log.debug('TODO: requestTrafficForMode');
+
+ }
+ function showRelatedIntentsAction () {
+ $log.debug('TODO: showRelatedIntentsAction');
+
+ }
+ function addHostIntentAction () {
+ $log.debug('TODO: addHostIntentAction');
+
+ }
+ function addMultiSourceIntentAction () {
+ $log.debug('TODO: addMultiSourceIntentAction');
+
+ }
+ function showDeviceLinkFlowsAction () {
+ $log.debug('TODO: showDeviceLinkFlowsAction');
+
+ }
+
+
+ // === -----------------------------------------------------
+ // === MODULE DEFINITION ===
+
+ angular.module('ovTopo')
+ .factory('TopoSelectService',
+ ['$log', 'FnService', 'TopoPanelService',
+
+ function (_$log_, _fs_, _tps_) {
+ $log = _$log_;
+ fs = _fs_;
+ tps = _tps_;
+
+ function initSelect(_api_) {
+ api = _api_;
+ }
+
+ function destroySelect() { }
+
+ return {
+ initSelect: initSelect,
+ destroySelect: destroySelect,
+
+ showDetails: showDetails,
+
+ nodeMouseOver: nodeMouseOver,
+ nodeMouseOut: nodeMouseOut,
+ selectObject: selectObject,
+ deselectObject: deselectObject,
+ deselectAll: deselectAll,
+ hovered: function () { return hovered; }
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
index c6c63c3..8637258 100644
--- a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
@@ -88,7 +88,7 @@
var p = ps.createPanel('foo');
expect(fs.areFunctions(p, [
'show', 'hide', 'toggle', 'empty', 'append',
- 'width', 'height', 'isVisible', 'el'
+ 'width', 'height', 'isVisible', 'classed', 'el'
])).toBeTruthy();
});
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js
index 6018370..fe8fd68 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js
@@ -34,7 +34,16 @@
it('should define api functions', function () {
expect(fs.areFunctions(tps, [
- 'initPanels', 'destroyPanels', 'showSummary'
+ 'initPanels',
+ 'destroyPanels',
+ 'showSummary',
+ 'displaySingle',
+ 'displayMulti',
+ 'addAction',
+ 'showDetailPanel',
+ 'hideDetailPanel',
+ 'detailVisible',
+ 'summaryVisible'
])).toBeTruthy();
});
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js
new file mode 100644
index 0000000..f8bc0e7
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js
@@ -0,0 +1,44 @@
+/*
+ * 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 -- Topo View -- Topo Selection Service - Unit Tests
+ */
+describe('factory: view/topo/topoSelect.js', function() {
+ var $log, fs, tss;
+
+ beforeEach(module('ovTopo', 'onosUtil', 'onosLayer'));
+
+ beforeEach(inject(function (_$log_, FnService, TopoSelectService) {
+ $log = _$log_;
+ fs = FnService;
+ tss = TopoSelectService;
+ }));
+
+ it('should define TopoSelectService', function () {
+ expect(tss).toBeDefined();
+ });
+
+ it('should define api functions', function () {
+ expect(fs.areFunctions(tss, [
+ 'initSelect', 'destroySelect', 'showDetails',
+ 'nodeMouseOver', 'nodeMouseOut', 'selectObject', 'deselectObject',
+ 'deselectAll', 'hovered'
+ ])).toBeTruthy();
+ });
+
+ // TODO: more tests...
+});