blob: cf08d2facc494981d7e645e35c466c8e876e13b9 [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
44 var flowPath = 'flow';
45
Simon Hunt08f841d02015-02-10 14:39:20 -080046 // ==========================
47
48 function nSel() {
49 return selectOrder.length;
50 }
51 function getSel(idx) {
52 return selections[selectOrder[idx]];
53 }
54 function allSelectionsClass(cls) {
55 for (var i=0, n=nSel(); i<n; i++) {
56 if (getSel(i).obj.class !== cls) {
57 return false;
58 }
59 }
60 return true;
61 }
62
63 // ==========================
64
65 function nodeMouseOver(m) {
66 if (!m.dragStarted) {
Simon Hunt36a58c62015-04-08 11:00:07 -070067 //$log.debug("MouseOver()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080068 if (hovered != m) {
69 hovered = m;
Simon Huntf542d842015-02-11 16:20:33 -080070 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080071 }
72 }
73 }
74
75 function nodeMouseOut(m) {
76 if (!m.dragStarted) {
77 if (hovered) {
78 hovered = null;
Simon Huntf542d842015-02-11 16:20:33 -080079 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080080 }
Simon Hunt36a58c62015-04-08 11:00:07 -070081 //$log.debug("MouseOut()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080082 }
83 }
84
85 // ==========================
86
87 function selectObject(obj) {
88 var el = this,
89 ev = d3.event.sourceEvent,
90 n;
91
92 if (api.zoomingOrPanning(ev)) {
93 return;
94 }
95
96 if (el) {
97 n = d3.select(el);
98 } else {
99 api.node().each(function (d) {
100 if (d == obj) {
101 n = d3.select(el = this);
102 }
103 });
104 }
105 if (!n) return;
106
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700107 consumeClick = true;
108 api.deselectLink();
109
Simon Hunt08f841d02015-02-10 14:39:20 -0800110 if (ev.shiftKey && n.classed('selected')) {
111 deselectObject(obj.id);
112 updateDetail();
113 return;
114 }
115
116 if (!ev.shiftKey) {
117 deselectAll();
118 }
119
120 selections[obj.id] = { obj: obj, el: el };
121 selectOrder.push(obj.id);
122
123 n.classed('selected', true);
124 api.updateDeviceColors(obj);
125 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800126 }
127
128 function deselectObject(id) {
129 var obj = selections[id];
130 if (obj) {
131 d3.select(obj.el).classed('selected', false);
132 delete selections[id];
133 fs.removeFromArray(id, selectOrder);
134 api.updateDeviceColors(obj.obj);
135 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800136 }
137
138 function deselectAll() {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700139 var something = (selectOrder.length > 0);
140
Simon Hunt08f841d02015-02-10 14:39:20 -0800141 // deselect all nodes in the network...
142 api.node().classed('selected', false);
143 selections = {};
144 selectOrder = [];
145 api.updateDeviceColors();
146 updateDetail();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700147
148 // return true if something was selected
149 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800150 }
151
152 // === -----------------------------------------------------
153
154 function requestDetails() {
155 var data = getSel(0).obj;
Simon Hunt237676b52015-03-10 19:04:26 -0700156 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800157 id: data.id,
158 class: data.class
159 });
160 }
161
162 // === -----------------------------------------------------
163
164 function updateDetail() {
165 var nSel = selectOrder.length;
166 if (!nSel) {
167 emptySelect();
168 } else if (nSel === 1) {
169 singleSelect();
170 } else {
171 multiSelect();
172 }
173 }
174
175 function emptySelect() {
Simon Huntf542d842015-02-11 16:20:33 -0800176 tts.cancelTraffic();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700177 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800178 }
179
180 function singleSelect() {
181 // NOTE: detail is shown from 'showDetails' event callback
182 requestDetails();
Simon Huntf542d842015-02-11 16:20:33 -0800183 tts.cancelTraffic();
184 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800185 }
186
187 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800188 // display the selected nodes in the detail panel
189 tps.displayMulti(selectOrder);
190
191 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700192 tps.addAction({
193 id: '-mult-rel-traf-btn',
194 gid: 'allTraffic',
195 cb: tts.showRelatedIntentsAction,
196 tt: 'Show Related Traffic'
197 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800198
199 // add other actions, based on what is selected...
200 if (nSel() === 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700201 tps.addAction({
202 id: 'host-flow-btn',
203 gid: 'endstation',
204 cb: tts.addHostIntentAction,
205 tt: 'Create Host-to-Host Flow'
206 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800207 } else if (nSel() >= 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700208 tps.addAction({
209 id: 'mult-src-flow-btn',
210 gid: 'flows',
211 cb: tts.addMultiSourceIntentAction,
212 tt: 'Create Multi-Source Flow'
213 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800214 }
215
Simon Huntf542d842015-02-11 16:20:33 -0800216 tts.cancelTraffic();
217 tts.requestTrafficForMode();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700218 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800219 }
220
221
222 // === -----------------------------------------------------
223 // Event Handlers
224
225 function showDetails(data) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800226 // display the data for the single selected node
227 tps.displaySingle(data);
228
229 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700230 tps.addAction({
231 id: '-sin-rel-traf-btn',
232 gid: 'intentTraffic',
233 cb: tts.showRelatedIntentsAction,
234 tt: 'Show Related Traffic'
235 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800236
237 // add other actions, based on what is selected...
238 if (data.type === 'switch') {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700239 tps.addAction({
240 id: 'sin-dev-flows-btn',
241 gid: 'flows',
242 cb: tts.showDeviceLinkFlowsAction,
243 tt: 'Show Device Flows'
244 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800245 }
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700246 // TODO: have the server return explicit class and ID of each node
247 // for now, we assume the node is a device if it has a URI
248 if ((data.props).hasOwnProperty('URI')) {
249 tps.addAction({
250 id: 'flows-table-btn',
251 gid: 'flowsTable',
252 cb: function () {
253 ns.navTo(flowPath, { devId: data.id });
254 },
255 tt: 'Show flows for this device'
256 });
257 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800258
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700259 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800260 }
261
Simon Huntf542d842015-02-11 16:20:33 -0800262 function validateSelectionContext() {
263 if (!hovered && !nSel()) {
264 tts.cancelTraffic();
265 return false;
266 }
267 return true;
Simon Hunt08f841d02015-02-10 14:39:20 -0800268 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800269
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700270 function clickConsumed(x) {
271 var cc = consumeClick;
272 consumeClick = !!x;
273 return cc;
274 }
275
Simon Hunt08f841d02015-02-10 14:39:20 -0800276 // === -----------------------------------------------------
277 // === MODULE DEFINITION ===
278
279 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800280 .factory('TopoSelectService',
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700281 ['$log', 'FnService', 'WebSocketService',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700282 'TopoPanelService', 'TopoTrafficService', 'NavService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800283
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700284 function (_$log_, _fs_, _wss_, _tps_, _tts_, _ns_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800285 $log = _$log_;
286 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700287 wss = _wss_;
Simon Hunt6036b192015-02-11 11:20:26 -0800288 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800289 tts = _tts_;
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700290 ns = _ns_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800291
Simon Hunt6036b192015-02-11 11:20:26 -0800292 function initSelect(_api_) {
293 api = _api_;
294 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800295
Simon Hunt6036b192015-02-11 11:20:26 -0800296 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800297
Simon Hunt6036b192015-02-11 11:20:26 -0800298 return {
299 initSelect: initSelect,
300 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800301
Simon Hunt6036b192015-02-11 11:20:26 -0800302 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800303
Simon Hunt6036b192015-02-11 11:20:26 -0800304 nodeMouseOver: nodeMouseOver,
305 nodeMouseOut: nodeMouseOut,
306 selectObject: selectObject,
307 deselectObject: deselectObject,
308 deselectAll: deselectAll,
Simon Huntf542d842015-02-11 16:20:33 -0800309
Simon Hunta0eb0a82015-02-11 12:30:06 -0800310 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800311 selectOrder: function () { return selectOrder; },
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700312 validateSelectionContext: validateSelectionContext,
313
314 clickConsumed: clickConsumed
Simon Hunt6036b192015-02-11 11:20:26 -0800315 };
316 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800317}());