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/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; }
+ };
+ }]);
+}());