blob: a0e8c319df7e5b80cb486fa827a677e22fa3882a [file] [log] [blame]
Simon Hunt08f841d02015-02-10 14:39:20 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Simon Hunt08f841d02015-02-10 14:39:20 -08003 *
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
Simon Hunta58d8942017-08-11 12:51:14 -070026 var $log, fs, wss, tov, tps, tts, sus;
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] )
Prince Pereira46c82d42016-09-19 13:30:50 +053034 deselectAllLinks()
Simon Hunt08f841d02015-02-10 14:39:20 -080035 */
36
37 // internal state
Steven Burrows041c1aa2016-04-12 15:45:05 +010038 var hovered, selections, selectOrder, consumeClick;
39
Simon Huntcaed0412017-08-12 13:49:17 -070040 // function to be replaced by the localization bundle function
41 var topoLion = function (x) {
42 return '#tsel#' + x + '#';
43 };
44
Steven Burrows1c2a9682017-07-14 16:52:46 +010045 function setInitialState() {
46 hovered = null; // the node over which the mouse is hovering
47 selections = {}; // currently selected nodes (by id)
48 selectOrder = []; // the order in which we made selections
49 consumeClick = false; // used to coordinate with SVG click handler
Steven Burrows041c1aa2016-04-12 15:45:05 +010050 }
Simon Hunt08f841d02015-02-10 14:39:20 -080051
52 // ==========================
53
54 function nSel() {
55 return selectOrder.length;
56 }
57 function getSel(idx) {
58 return selections[selectOrder[idx]];
59 }
60 function allSelectionsClass(cls) {
61 for (var i=0, n=nSel(); i<n; i++) {
62 if (getSel(i).obj.class !== cls) {
63 return false;
64 }
65 }
66 return true;
67 }
68
69 // ==========================
70
71 function nodeMouseOver(m) {
72 if (!m.dragStarted) {
Simon Huntcaed0412017-08-12 13:49:17 -070073 if (hovered !== m) {
Simon Hunt08f841d02015-02-10 14:39:20 -080074 hovered = m;
Simon Hunt584e92d2015-08-24 11:27:22 -070075 tov.hooks.mouseOver({
76 id: m.id,
77 class: m.class,
Steven Burrows1c2a9682017-07-14 16:52:46 +010078 type: m.type,
Simon Hunt584e92d2015-08-24 11:27:22 -070079 });
Simon Hunt08f841d02015-02-10 14:39:20 -080080 }
81 }
82 }
83
84 function nodeMouseOut(m) {
85 if (!m.dragStarted) {
86 if (hovered) {
87 hovered = null;
Simon Hunt584e92d2015-08-24 11:27:22 -070088 tov.hooks.mouseOut();
Simon Hunt08f841d02015-02-10 14:39:20 -080089 }
Simon Hunt08f841d02015-02-10 14:39:20 -080090 }
91 }
92
93 // ==========================
94
95 function selectObject(obj) {
96 var el = this,
Simon Hunt5aac2fc2015-06-09 12:34:07 -070097 nodeEv = el && el.tagName === 'g',
98 ev = d3.event.sourceEvent || {},
Simon Hunt08f841d02015-02-10 14:39:20 -080099 n;
100
101 if (api.zoomingOrPanning(ev)) {
102 return;
103 }
104
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700105 if (nodeEv) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800106 n = d3.select(el);
107 } else {
108 api.node().each(function (d) {
Simon Huntcaed0412017-08-12 13:49:17 -0700109 if (d === obj) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800110 n = d3.select(el = this);
111 }
112 });
113 }
Prince Pereira46c82d42016-09-19 13:30:50 +0530114
Simon Huntf9761452016-11-19 09:06:17 -0800115 if (obj && obj.class === 'link') {
116 if (selections[obj.key]) {
117 deselectObject(obj.key);
118 } else {
119 selections[obj.key] = { obj: obj, el: el };
120 selectOrder.push(obj.key);
Prince Pereira46c82d42016-09-19 13:30:50 +0530121 }
Simon Huntf9761452016-11-19 09:06:17 -0800122 updateDetail();
123 return;
Prince Pereira46c82d42016-09-19 13:30:50 +0530124 }
125
126 if (!n) {
127 return;
128 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800129
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700130 if (nodeEv) {
131 consumeClick = true;
132 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700133
Simon Hunt08f841d02015-02-10 14:39:20 -0800134 if (ev.shiftKey && n.classed('selected')) {
135 deselectObject(obj.id);
136 updateDetail();
137 return;
138 }
139
140 if (!ev.shiftKey) {
Simon Hunta17fa672015-08-19 18:42:22 -0700141 deselectAll(true);
Simon Hunt08f841d02015-02-10 14:39:20 -0800142 }
143
144 selections[obj.id] = { obj: obj, el: el };
145 selectOrder.push(obj.id);
146
147 n.classed('selected', true);
Simon Hunt4766dfb2016-06-14 17:16:22 -0700148 if (n.classed('device')) {
149 api.updateDeviceColors(obj);
150 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800151 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800152 }
153
Simon Hunt7faabd52016-08-18 16:16:19 -0700154 function reselect() {
155 selectOrder.forEach(function (id) {
156 var sel = d3.select('g#' + sus.safeId(id));
157 sel.classed('selected', true);
158 });
159 updateDetail();
160 }
161
Simon Hunt08f841d02015-02-10 14:39:20 -0800162 function deselectObject(id) {
163 var obj = selections[id];
164 if (obj) {
165 d3.select(obj.el).classed('selected', false);
166 delete selections[id];
167 fs.removeFromArray(id, selectOrder);
168 api.updateDeviceColors(obj.obj);
169 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800170 }
171
Simon Hunta17fa672015-08-19 18:42:22 -0700172 function deselectAll(skipUpdate) {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700173 var something = (selectOrder.length > 0);
174
Simon Hunt08f841d02015-02-10 14:39:20 -0800175 // deselect all nodes in the network...
176 api.node().classed('selected', false);
177 selections = {};
178 selectOrder = [];
179 api.updateDeviceColors();
Simon Hunta17fa672015-08-19 18:42:22 -0700180 if (!skipUpdate) {
181 updateDetail();
182 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700183
184 // return true if something was selected
185 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800186 }
187
188 // === -----------------------------------------------------
189
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700190 function requestDetails(data) {
Simon Hunta58d8942017-08-11 12:51:14 -0700191 var itemClass = data.class,
192 payload = {
193 class: itemClass,
194 id: data.id,
195 };
196
197 // special handling for links...
198 if (itemClass === 'link') {
199 payload.key = data.key;
200 if (data.source.class === 'host') {
201 payload.isEdgeLink = true;
202 payload.sourceId = data.source.id;
203 payload.targetId = data.source.cp.device;
204 payload.targetPort = data.source.cp.port;
205 } else {
206 payload.isEdgeLink = false;
207 payload.sourceId = data.source.id;
208 payload.sourcePort = data.srcPort;
209 payload.targetId = data.target.id;
210 payload.targetPort = data.tgtPort;
211 }
212 }
213
Simon Hunta58d8942017-08-11 12:51:14 -0700214 wss.sendEvent('requestDetails', payload);
Simon Hunt08f841d02015-02-10 14:39:20 -0800215 }
216
217 // === -----------------------------------------------------
218
219 function updateDetail() {
220 var nSel = selectOrder.length;
221 if (!nSel) {
222 emptySelect();
223 } else if (nSel === 1) {
224 singleSelect();
225 } else {
226 multiSelect();
227 }
228 }
229
230 function emptySelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700231 tov.hooks.emptySelect();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700232 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800233 }
234
235 function singleSelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700236 var data = getSel(0).obj;
Prince Pereira46c82d42016-09-19 13:30:50 +0530237
Simon Hunta58d8942017-08-11 12:51:14 -0700238 $log.debug('Requesting details from server for', data);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700239 requestDetails(data);
240 // NOTE: detail panel is shown as a response to receiving
241 // a 'showDetails' event from the server. See 'showDetails'
242 // callback function below...
Simon Hunt08f841d02015-02-10 14:39:20 -0800243 }
244
245 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800246 // display the selected nodes in the detail panel
247 tps.displayMulti(selectOrder);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700248 addHostSelectionActions();
249 tov.hooks.multiSelect(selectOrder);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700250 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800251 }
252
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700253 function addHostSelectionActions() {
254 if (allSelectionsClass('host')) {
255 if (nSel() === 2) {
256 tps.addAction({
257 id: 'host-flow-btn',
Simon Huntcaed0412017-08-12 13:49:17 -0700258 gid: 'm_endstation',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700259 cb: tts.addHostIntent,
Simon Huntcaed0412017-08-12 13:49:17 -0700260 tt: function () { return topoLion('tr_btn_create_h2h_flow'); },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700261 });
262 } else if (nSel() >= 2) {
263 tps.addAction({
264 id: 'mult-src-flow-btn',
265 gid: 'flows',
266 cb: tts.addMultiSourceIntent,
Simon Huntcaed0412017-08-12 13:49:17 -0700267 tt: function () { return topoLion('tr_btn_create_msrc_flow'); },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700268 });
269 }
270 }
271 }
272
Simon Hunt08f841d02015-02-10 14:39:20 -0800273
274 // === -----------------------------------------------------
275 // Event Handlers
276
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700277 // display the data for the single selected node
Simon Hunt08f841d02015-02-10 14:39:20 -0800278 function showDetails(data) {
Simon Hunt3a0598f2015-08-04 19:59:04 -0700279 var buttons = fs.isA(data.buttons) || [];
Simon Hunt08f841d02015-02-10 14:39:20 -0800280 tps.displaySingle(data);
Simon Hunt879ce452017-08-10 23:32:00 -0700281 tov.installButtons(buttons, data, data.propValues['uri']);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700282 tov.hooks.singleSelect(data);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700283 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800284 }
285
Simon Huntd2862c32015-08-24 17:41:51 -0700286 // returns true if one or more nodes are selected.
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700287 function somethingSelected() {
Simon Huntd2862c32015-08-24 17:41:51 -0700288 return nSel();
Simon Hunt08f841d02015-02-10 14:39:20 -0800289 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800290
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700291 function clickConsumed(x) {
292 var cc = consumeClick;
293 consumeClick = !!x;
294 return cc;
295 }
296
Simon Hunt8d558082015-10-29 21:32:50 -0700297 // returns a selection context, providing info about what is selected
298 function selectionContext() {
299 var devices = [],
300 hosts = [],
301 types = {};
302
303 angular.forEach(selections, function (d) {
304 var o = d.obj,
305 c = o.class;
306
307 if (c === 'device') {
308 devices.push(o.id);
309 types[o.id] = o.type;
310 }
311 if (c === 'host') {
312 hosts.push(o.id);
313 types[o.id] = o.type;
314 }
315 });
316
317 return {
318 devices: devices,
319 hosts: hosts,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100320 types: types,
Simon Hunt8d558082015-10-29 21:32:50 -0700321 };
322 }
323
Simon Hunt08f841d02015-02-10 14:39:20 -0800324 // === -----------------------------------------------------
325 // === MODULE DEFINITION ===
326
327 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800328 .factory('TopoSelectService',
Simon Hunta58d8942017-08-11 12:51:14 -0700329 ['$log', 'FnService', 'WebSocketService', 'TopoOverlayService',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100330 'TopoPanelService', 'TopoTrafficService', 'SvgUtilService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800331
Simon Hunta58d8942017-08-11 12:51:14 -0700332 function (_$log_, _fs_, _wss_, _tov_, _tps_, _tts_, _sus_) {
333 $log = _$log_;
Simon Hunt6036b192015-02-11 11:20:26 -0800334 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700335 wss = _wss_;
Simon Huntfb940112015-07-29 18:36:35 -0700336 tov = _tov_;
Simon Hunt6036b192015-02-11 11:20:26 -0800337 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800338 tts = _tts_;
Simon Hunt7faabd52016-08-18 16:16:19 -0700339 sus = _sus_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800340
Simon Hunt6036b192015-02-11 11:20:26 -0800341 function initSelect(_api_) {
342 api = _api_;
Simon Hunt7faabd52016-08-18 16:16:19 -0700343 if (!selections) {
344 setInitialState();
345 }
Simon Hunt6036b192015-02-11 11:20:26 -0800346 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800347
Simon Hunt6036b192015-02-11 11:20:26 -0800348 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800349
Simon Hunt6036b192015-02-11 11:20:26 -0800350 return {
351 initSelect: initSelect,
352 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800353
Simon Hunt6036b192015-02-11 11:20:26 -0800354 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800355
Simon Hunt6036b192015-02-11 11:20:26 -0800356 nodeMouseOver: nodeMouseOver,
357 nodeMouseOut: nodeMouseOut,
358 selectObject: selectObject,
359 deselectObject: deselectObject,
360 deselectAll: deselectAll,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700361 updateDetail: updateDetail,
Simon Huntf542d842015-02-11 16:20:33 -0800362
Simon Hunta0eb0a82015-02-11 12:30:06 -0800363 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800364 selectOrder: function () { return selectOrder; },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700365 somethingSelected: somethingSelected,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700366
Simon Hunt8d558082015-10-29 21:32:50 -0700367 clickConsumed: clickConsumed,
Simon Hunt7faabd52016-08-18 16:16:19 -0700368 selectionContext: selectionContext,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100369 reselect: reselect,
Simon Huntcaed0412017-08-12 13:49:17 -0700370 setLionBundle: function (bundle) { topoLion = bundle; },
Simon Hunt6036b192015-02-11 11:20:26 -0800371 };
372 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800373}());