blob: 957c5017a79957b6e0c3b83f49fd477fa64cf7bb [file] [log] [blame]
Simon Hunt08f841d02015-02-10 14:39:20 -08001/*
2 * Copyright 2015 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 -- Topology Selection Module.
19 Defines behavior when selecting nodes.
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -070026 var $log, fs, wss, tps, tts, ns;
Simon Hunt08f841d02015-02-10 14:39:20 -080027
28 // api to topoForce
29 var api;
30 /*
31 node() // get ref to D3 selection of nodes
32 zoomingOrPanning( ev )
33 updateDeviceColors( [dev] )
Simon Hunt0c6b2d32015-03-26 17:46:29 -070034 deselectLink()
Simon Hunt08f841d02015-02-10 14:39:20 -080035 */
36
37 // internal state
38 var hovered, // the node over which the mouse is hovering
39 selections = {}, // currently selected nodes (by id)
40 selectOrder = [], // the order in which we made selections
Simon Hunt0c6b2d32015-03-26 17:46:29 -070041 consumeClick = false; // used to coordinate with SVG click handler
Simon Hunt08f841d02015-02-10 14:39:20 -080042
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -070043 // constants
Bri Prebilic Coleac829e42015-05-05 13:42:06 -070044 var flowPath = 'flow',
45 portPath ='port';
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -070046
Simon Hunt08f841d02015-02-10 14:39:20 -080047 // ==========================
48
49 function nSel() {
50 return selectOrder.length;
51 }
52 function getSel(idx) {
53 return selections[selectOrder[idx]];
54 }
55 function allSelectionsClass(cls) {
56 for (var i=0, n=nSel(); i<n; i++) {
57 if (getSel(i).obj.class !== cls) {
58 return false;
59 }
60 }
61 return true;
62 }
63
64 // ==========================
65
66 function nodeMouseOver(m) {
67 if (!m.dragStarted) {
Simon Hunt36a58c62015-04-08 11:00:07 -070068 //$log.debug("MouseOver()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080069 if (hovered != m) {
70 hovered = m;
Simon Huntf542d842015-02-11 16:20:33 -080071 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080072 }
73 }
74 }
75
76 function nodeMouseOut(m) {
77 if (!m.dragStarted) {
78 if (hovered) {
79 hovered = null;
Simon Huntf542d842015-02-11 16:20:33 -080080 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080081 }
Simon Hunt36a58c62015-04-08 11:00:07 -070082 //$log.debug("MouseOut()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080083 }
84 }
85
86 // ==========================
87
88 function selectObject(obj) {
89 var el = this,
90 ev = d3.event.sourceEvent,
91 n;
92
93 if (api.zoomingOrPanning(ev)) {
94 return;
95 }
96
97 if (el) {
98 n = d3.select(el);
99 } else {
100 api.node().each(function (d) {
101 if (d == obj) {
102 n = d3.select(el = this);
103 }
104 });
105 }
106 if (!n) return;
107
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700108 consumeClick = true;
109 api.deselectLink();
110
Simon Hunt08f841d02015-02-10 14:39:20 -0800111 if (ev.shiftKey && n.classed('selected')) {
112 deselectObject(obj.id);
113 updateDetail();
114 return;
115 }
116
117 if (!ev.shiftKey) {
118 deselectAll();
119 }
120
121 selections[obj.id] = { obj: obj, el: el };
122 selectOrder.push(obj.id);
123
124 n.classed('selected', true);
125 api.updateDeviceColors(obj);
126 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800127 }
128
129 function deselectObject(id) {
130 var obj = selections[id];
131 if (obj) {
132 d3.select(obj.el).classed('selected', false);
133 delete selections[id];
134 fs.removeFromArray(id, selectOrder);
135 api.updateDeviceColors(obj.obj);
136 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800137 }
138
139 function deselectAll() {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700140 var something = (selectOrder.length > 0);
141
Simon Hunt08f841d02015-02-10 14:39:20 -0800142 // deselect all nodes in the network...
143 api.node().classed('selected', false);
144 selections = {};
145 selectOrder = [];
146 api.updateDeviceColors();
147 updateDetail();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700148
149 // return true if something was selected
150 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800151 }
152
153 // === -----------------------------------------------------
154
155 function requestDetails() {
156 var data = getSel(0).obj;
Simon Hunt237676b52015-03-10 19:04:26 -0700157 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800158 id: data.id,
159 class: data.class
160 });
161 }
162
163 // === -----------------------------------------------------
164
165 function updateDetail() {
166 var nSel = selectOrder.length;
167 if (!nSel) {
168 emptySelect();
169 } else if (nSel === 1) {
170 singleSelect();
171 } else {
172 multiSelect();
173 }
174 }
175
176 function emptySelect() {
Simon Huntf542d842015-02-11 16:20:33 -0800177 tts.cancelTraffic();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700178 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800179 }
180
181 function singleSelect() {
182 // NOTE: detail is shown from 'showDetails' event callback
183 requestDetails();
Simon Huntf542d842015-02-11 16:20:33 -0800184 tts.cancelTraffic();
185 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800186 }
187
188 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800189 // display the selected nodes in the detail panel
190 tps.displayMulti(selectOrder);
191
192 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700193 tps.addAction({
194 id: '-mult-rel-traf-btn',
195 gid: 'allTraffic',
196 cb: tts.showRelatedIntentsAction,
197 tt: 'Show Related Traffic'
198 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800199
200 // add other actions, based on what is selected...
201 if (nSel() === 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700202 tps.addAction({
203 id: 'host-flow-btn',
204 gid: 'endstation',
205 cb: tts.addHostIntentAction,
206 tt: 'Create Host-to-Host Flow'
207 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800208 } else if (nSel() >= 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700209 tps.addAction({
210 id: 'mult-src-flow-btn',
211 gid: 'flows',
212 cb: tts.addMultiSourceIntentAction,
213 tt: 'Create Multi-Source Flow'
214 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800215 }
216
Simon Huntf542d842015-02-11 16:20:33 -0800217 tts.cancelTraffic();
218 tts.requestTrafficForMode();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700219 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800220 }
221
222
223 // === -----------------------------------------------------
224 // Event Handlers
225
226 function showDetails(data) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800227 // display the data for the single selected node
228 tps.displaySingle(data);
229
230 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700231 tps.addAction({
232 id: '-sin-rel-traf-btn',
233 gid: 'intentTraffic',
234 cb: tts.showRelatedIntentsAction,
235 tt: 'Show Related Traffic'
236 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800237
238 // add other actions, based on what is selected...
239 if (data.type === 'switch') {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700240 tps.addAction({
241 id: 'sin-dev-flows-btn',
242 gid: 'flows',
243 cb: tts.showDeviceLinkFlowsAction,
244 tt: 'Show Device Flows'
245 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800246 }
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700247 // TODO: have the server return explicit class and ID of each node
248 // for now, we assume the node is a device if it has a URI
249 if ((data.props).hasOwnProperty('URI')) {
250 tps.addAction({
251 id: 'flows-table-btn',
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700252 gid: 'flowTable',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700253 cb: function () {
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700254 ns.navTo(flowPath, { devId: data.props['URI'] });
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700255 },
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700256 tt: 'Show flow view for this device'
257 });
258 tps.addAction({
259 id: 'ports-table-btn',
Bri Prebilic Cole9467a232015-05-06 16:59:05 -0700260 gid: 'portTable',
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700261 cb: function () {
262 ns.navTo(portPath, { devId: data.props['URI'] });
263 },
264 tt: 'Show port view for this device'
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700265 });
266 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800267
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700268 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800269 }
270
Simon Huntf542d842015-02-11 16:20:33 -0800271 function validateSelectionContext() {
272 if (!hovered && !nSel()) {
273 tts.cancelTraffic();
274 return false;
275 }
276 return true;
Simon Hunt08f841d02015-02-10 14:39:20 -0800277 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800278
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700279 function clickConsumed(x) {
280 var cc = consumeClick;
281 consumeClick = !!x;
282 return cc;
283 }
284
Simon Hunt08f841d02015-02-10 14:39:20 -0800285 // === -----------------------------------------------------
286 // === MODULE DEFINITION ===
287
288 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800289 .factory('TopoSelectService',
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700290 ['$log', 'FnService', 'WebSocketService',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700291 'TopoPanelService', 'TopoTrafficService', 'NavService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800292
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700293 function (_$log_, _fs_, _wss_, _tps_, _tts_, _ns_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800294 $log = _$log_;
295 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700296 wss = _wss_;
Simon Hunt6036b192015-02-11 11:20:26 -0800297 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800298 tts = _tts_;
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700299 ns = _ns_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800300
Simon Hunt6036b192015-02-11 11:20:26 -0800301 function initSelect(_api_) {
302 api = _api_;
303 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800304
Simon Hunt6036b192015-02-11 11:20:26 -0800305 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800306
Simon Hunt6036b192015-02-11 11:20:26 -0800307 return {
308 initSelect: initSelect,
309 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800310
Simon Hunt6036b192015-02-11 11:20:26 -0800311 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800312
Simon Hunt6036b192015-02-11 11:20:26 -0800313 nodeMouseOver: nodeMouseOver,
314 nodeMouseOut: nodeMouseOut,
315 selectObject: selectObject,
316 deselectObject: deselectObject,
317 deselectAll: deselectAll,
Simon Huntf542d842015-02-11 16:20:33 -0800318
Simon Hunta0eb0a82015-02-11 12:30:06 -0800319 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800320 selectOrder: function () { return selectOrder; },
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700321 validateSelectionContext: validateSelectionContext,
322
323 clickConsumed: clickConsumed
Simon Hunt6036b192015-02-11 11:20:26 -0800324 };
325 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800326}());