blob: d026e736a7cc7f7889846460db7a9fb739585e7e [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;
Simon Hunt12c79ed2017-09-12 11:58:44 -0700200 payload.sourceId = data.source.id;
201 payload.targetId = data.target.id;
202 payload.targetPort = data.tgtPort;
203
Simon Hunta58d8942017-08-11 12:51:14 -0700204 if (data.source.class === 'host') {
205 payload.isEdgeLink = true;
Simon Hunta58d8942017-08-11 12:51:14 -0700206 } else {
207 payload.isEdgeLink = false;
Simon Hunta58d8942017-08-11 12:51:14 -0700208 payload.sourcePort = data.srcPort;
Simon Hunta58d8942017-08-11 12:51:14 -0700209 }
210 }
211
Simon Hunta58d8942017-08-11 12:51:14 -0700212 wss.sendEvent('requestDetails', payload);
Simon Hunt08f841d02015-02-10 14:39:20 -0800213 }
214
215 // === -----------------------------------------------------
216
217 function updateDetail() {
218 var nSel = selectOrder.length;
219 if (!nSel) {
220 emptySelect();
221 } else if (nSel === 1) {
222 singleSelect();
223 } else {
224 multiSelect();
225 }
226 }
227
228 function emptySelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700229 tov.hooks.emptySelect();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700230 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800231 }
232
233 function singleSelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700234 var data = getSel(0).obj;
Prince Pereira46c82d42016-09-19 13:30:50 +0530235
Simon Hunta58d8942017-08-11 12:51:14 -0700236 $log.debug('Requesting details from server for', data);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700237 requestDetails(data);
238 // NOTE: detail panel is shown as a response to receiving
239 // a 'showDetails' event from the server. See 'showDetails'
240 // callback function below...
Simon Hunt08f841d02015-02-10 14:39:20 -0800241 }
242
243 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800244 // display the selected nodes in the detail panel
245 tps.displayMulti(selectOrder);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700246 addHostSelectionActions();
247 tov.hooks.multiSelect(selectOrder);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700248 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800249 }
250
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700251 function addHostSelectionActions() {
252 if (allSelectionsClass('host')) {
253 if (nSel() === 2) {
254 tps.addAction({
255 id: 'host-flow-btn',
Simon Huntcaed0412017-08-12 13:49:17 -0700256 gid: 'm_endstation',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700257 cb: tts.addHostIntent,
Simon Huntcaed0412017-08-12 13:49:17 -0700258 tt: function () { return topoLion('tr_btn_create_h2h_flow'); },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700259 });
260 } else if (nSel() >= 2) {
261 tps.addAction({
262 id: 'mult-src-flow-btn',
263 gid: 'flows',
264 cb: tts.addMultiSourceIntent,
Simon Huntcaed0412017-08-12 13:49:17 -0700265 tt: function () { return topoLion('tr_btn_create_msrc_flow'); },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700266 });
267 }
268 }
269 }
270
Simon Hunt08f841d02015-02-10 14:39:20 -0800271
272 // === -----------------------------------------------------
273 // Event Handlers
274
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700275 // display the data for the single selected node
Simon Hunt08f841d02015-02-10 14:39:20 -0800276 function showDetails(data) {
Simon Hunt3a0598f2015-08-04 19:59:04 -0700277 var buttons = fs.isA(data.buttons) || [];
Simon Hunt08f841d02015-02-10 14:39:20 -0800278 tps.displaySingle(data);
Simon Hunt879ce452017-08-10 23:32:00 -0700279 tov.installButtons(buttons, data, data.propValues['uri']);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700280 tov.hooks.singleSelect(data);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700281 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800282 }
283
Simon Huntd2862c32015-08-24 17:41:51 -0700284 // returns true if one or more nodes are selected.
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700285 function somethingSelected() {
Simon Huntd2862c32015-08-24 17:41:51 -0700286 return nSel();
Simon Hunt08f841d02015-02-10 14:39:20 -0800287 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800288
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700289 function clickConsumed(x) {
290 var cc = consumeClick;
291 consumeClick = !!x;
292 return cc;
293 }
294
Simon Hunt8d558082015-10-29 21:32:50 -0700295 // returns a selection context, providing info about what is selected
296 function selectionContext() {
297 var devices = [],
298 hosts = [],
299 types = {};
300
301 angular.forEach(selections, function (d) {
302 var o = d.obj,
303 c = o.class;
304
305 if (c === 'device') {
306 devices.push(o.id);
307 types[o.id] = o.type;
308 }
309 if (c === 'host') {
310 hosts.push(o.id);
311 types[o.id] = o.type;
312 }
313 });
314
315 return {
316 devices: devices,
317 hosts: hosts,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100318 types: types,
Simon Hunt8d558082015-10-29 21:32:50 -0700319 };
320 }
321
Simon Hunt08f841d02015-02-10 14:39:20 -0800322 // === -----------------------------------------------------
323 // === MODULE DEFINITION ===
324
325 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800326 .factory('TopoSelectService',
Simon Hunta58d8942017-08-11 12:51:14 -0700327 ['$log', 'FnService', 'WebSocketService', 'TopoOverlayService',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100328 'TopoPanelService', 'TopoTrafficService', 'SvgUtilService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800329
Simon Hunta58d8942017-08-11 12:51:14 -0700330 function (_$log_, _fs_, _wss_, _tov_, _tps_, _tts_, _sus_) {
331 $log = _$log_;
Simon Hunt6036b192015-02-11 11:20:26 -0800332 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700333 wss = _wss_;
Simon Huntfb940112015-07-29 18:36:35 -0700334 tov = _tov_;
Simon Hunt6036b192015-02-11 11:20:26 -0800335 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800336 tts = _tts_;
Simon Hunt7faabd52016-08-18 16:16:19 -0700337 sus = _sus_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800338
Simon Hunt6036b192015-02-11 11:20:26 -0800339 function initSelect(_api_) {
340 api = _api_;
Simon Hunt7faabd52016-08-18 16:16:19 -0700341 if (!selections) {
342 setInitialState();
343 }
Simon Hunt6036b192015-02-11 11:20:26 -0800344 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800345
Simon Hunt6036b192015-02-11 11:20:26 -0800346 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800347
Simon Hunt6036b192015-02-11 11:20:26 -0800348 return {
349 initSelect: initSelect,
350 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800351
Simon Hunt6036b192015-02-11 11:20:26 -0800352 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800353
Simon Hunt6036b192015-02-11 11:20:26 -0800354 nodeMouseOver: nodeMouseOver,
355 nodeMouseOut: nodeMouseOut,
356 selectObject: selectObject,
357 deselectObject: deselectObject,
358 deselectAll: deselectAll,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700359 updateDetail: updateDetail,
Simon Huntf542d842015-02-11 16:20:33 -0800360
Simon Hunta0eb0a82015-02-11 12:30:06 -0800361 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800362 selectOrder: function () { return selectOrder; },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700363 somethingSelected: somethingSelected,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700364
Simon Hunt8d558082015-10-29 21:32:50 -0700365 clickConsumed: clickConsumed,
Simon Hunt7faabd52016-08-18 16:16:19 -0700366 selectionContext: selectionContext,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100367 reselect: reselect,
Simon Huntcaed0412017-08-12 13:49:17 -0700368 setLionBundle: function (bundle) { topoLion = bundle; },
Simon Hunt6036b192015-02-11 11:20:26 -0800369 };
370 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800371}());