Simon Hunt

GUI -- TopoView - added node selection logic.

- added inArray() and removeFromArray() functions to FnService.

Change-Id: I0e9631fa9e5865cb171e8d505f45c1963a1903dc
...@@ -117,6 +117,32 @@ ...@@ -117,6 +117,32 @@
117 return -1; 117 return -1;
118 } 118 }
119 119
120 + // search through array to find (the first occurrence of) item,
121 + // returning its index if found; otherwise returning -1.
122 + function inArray(item, array) {
123 + var i;
124 + if (isA(array)) {
125 + for (i=0; i<array.length; i++) {
126 + if (array[i] === item) {
127 + return i;
128 + }
129 + }
130 + }
131 + return -1;
132 + }
133 +
134 + // remove (the first occurrence of) the specified item from the given
135 + // array, if any. Return true if the removal was made; false otherwise.
136 + function removeFromArray(item, array) {
137 + var found = false,
138 + i = inArray(item, array);
139 + if (i >= 0) {
140 + array.splice(i, 1);
141 + found = true;
142 + }
143 + return found;
144 + }
145 +
120 angular.module('onosUtil') 146 angular.module('onosUtil')
121 .factory('FnService', ['$window', function (_$window_) { 147 .factory('FnService', ['$window', function (_$window_) {
122 $window = _$window_; 148 $window = _$window_;
...@@ -130,7 +156,9 @@ ...@@ -130,7 +156,9 @@
130 areFunctions: areFunctions, 156 areFunctions: areFunctions,
131 areFunctionsNonStrict: areFunctionsNonStrict, 157 areFunctionsNonStrict: areFunctionsNonStrict,
132 windowSize: windowSize, 158 windowSize: windowSize,
133 - find: find 159 + find: find,
160 + inArray: inArray,
161 + removeFromArray: removeFromArray
134 }; 162 };
135 }]); 163 }]);
136 164
......
...@@ -111,8 +111,7 @@ ...@@ -111,8 +111,7 @@
111 <div id="quickhelp"></div> 111 <div id="quickhelp"></div>
112 <div id="veil" 112 <div id="veil"
113 resize 113 resize
114 - ng-style="resizeWithOffset(0, 0)" 114 + ng-style="resizeWithOffset(0, 0)"></div>
115 - ></div>
116 </div> 115 </div>
117 </body> 116 </body>
118 </html> 117 </html>
......
...@@ -77,7 +77,9 @@ ...@@ -77,7 +77,9 @@
77 oblique = false, // whether we are in the oblique view 77 oblique = false, // whether we are in the oblique view
78 nodeLock = false, // whether nodes can be dragged or not (locked) 78 nodeLock = false, // whether nodes can be dragged or not (locked)
79 width, height, // the width and height of the force layout 79 width, height, // the width and height of the force layout
80 - hovered; // the node over which the mouse is hovering 80 + hovered, // the node over which the mouse is hovering
81 + selections = {}, // what is currently selected
82 + selectOrder = []; // the order in which we made selections
81 83
82 // SVG elements; 84 // SVG elements;
83 var linkG, linkLabelG, nodeG; 85 var linkG, linkLabelG, nodeG;
...@@ -1323,15 +1325,77 @@ ...@@ -1323,15 +1325,77 @@
1323 } 1325 }
1324 1326
1325 1327
1328 + function updateDetailPanel() {
1329 + // TODO update detail panel
1330 + $log.debug("TODO: updateDetailPanel() ...");
1331 + }
1332 +
1333 +
1334 + // ==========================
1335 + // === SELECTION / DESELECTION
1336 +
1337 + function selectObject(obj) {
1338 + var el = this,
1339 + ev = d3.event.sourceEvent,
1340 + n;
1341 +
1342 + if (zoomingOrPanning(ev)) {
1343 + return;
1344 + }
1345 +
1346 + if (el) {
1347 + n = d3.select(el);
1348 + } else {
1349 + node.each(function (d) {
1350 + if (d == obj) {
1351 + n = d3.select(el = this);
1352 + }
1353 + });
1354 + }
1355 + if (!n) return;
1356 +
1357 + if (ev.shiftKey && n.classed('selected')) {
1358 + deselectObject(obj.id);
1359 + updateDetailPanel();
1360 + return;
1361 + }
1362 +
1363 + if (!ev.shiftKey) {
1364 + deselectAll();
1365 + }
1366 +
1367 + selections[obj.id] = { obj: obj, el: el };
1368 + selectOrder.push(obj.id);
1369 +
1370 + n.classed('selected', true);
1371 + updateDeviceColors(obj);
1372 + updateDetailPanel();
1373 + }
1374 +
1375 + function deselectObject(id) {
1376 + var obj = selections[id];
1377 + if (obj) {
1378 + d3.select(obj.el).classed('selected', false);
1379 + delete selections[id];
1380 + fs.removeFromArray(id, selectOrder);
1381 + updateDeviceColors(obj.obj);
1382 + }
1383 + }
1384 +
1385 + function deselectAll() {
1386 + // deselect all nodes in the network...
1387 + node.classed('selected', false);
1388 + selections = {};
1389 + selectOrder = [];
1390 + updateDeviceColors();
1391 + updateDetailPanel();
1392 + }
1393 +
1326 // ========================== 1394 // ==========================
1327 // === MOUSE GESTURE HANDLERS 1395 // === MOUSE GESTURE HANDLERS
1328 1396
1329 - function selectCb(d) { 1397 + function zoomingOrPanning(ev) {
1330 - // this is the selected node 1398 + return ev.metaKey || ev.altKey;
1331 - $log.debug("\n\n\nSelect Object: ");
1332 - $log.debug("d is ", d);
1333 - $log.debug("this is ", this);
1334 - $log.debug('\n\n');
1335 } 1399 }
1336 1400
1337 function atDragEnd(d) { 1401 function atDragEnd(d) {
...@@ -1345,8 +1409,7 @@ ...@@ -1345,8 +1409,7 @@
1345 function dragEnabled() { 1409 function dragEnabled() {
1346 var ev = d3.event.sourceEvent; 1410 var ev = d3.event.sourceEvent;
1347 // nodeLock means we aren't allowing nodes to be dragged... 1411 // nodeLock means we aren't allowing nodes to be dragged...
1348 - // meta or alt key pressed means we are zooming/panning... 1412 + return !nodeLock && !zoomingOrPanning(ev);
1349 - return !nodeLock && !(ev.metaKey || ev.altKey);
1350 } 1413 }
1351 1414
1352 // predicate that indicates when clicking is active 1415 // predicate that indicates when clicking is active
...@@ -1406,7 +1469,7 @@ ...@@ -1406,7 +1469,7 @@
1406 .on('tick', tick); 1469 .on('tick', tick);
1407 1470
1408 drag = sus.createDragBehavior(force, 1471 drag = sus.createDragBehavior(force,
1409 - selectCb, atDragEnd, dragEnabled, clickEnabled); 1472 + selectObject, atDragEnd, dragEnabled, clickEnabled);
1410 } 1473 }
1411 1474
1412 function resize(dim) { 1475 function resize(dim) {
......
...@@ -201,7 +201,8 @@ describe('factory: fw/util/fn.js', function() { ...@@ -201,7 +201,8 @@ describe('factory: fw/util/fn.js', function() {
201 it('should define api functions', function () { 201 it('should define api functions', function () {
202 expect(fs.areFunctions(fs, [ 202 expect(fs.areFunctions(fs, [
203 'isF', 'isA', 'isS', 'isO', 'contains', 203 'isF', 'isA', 'isS', 'isO', 'contains',
204 - 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find' 204 + 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find',
205 + 'inArray', 'removeFromArray'
205 ])).toBeTruthy(); 206 ])).toBeTruthy();
206 }); 207 });
207 208
...@@ -260,4 +261,68 @@ describe('factory: fw/util/fn.js', function() { ...@@ -260,4 +261,68 @@ describe('factory: fw/util/fn.js', function() {
260 it('should find Zevvv', function () { 261 it('should find Zevvv', function () {
261 expect(fs.find('Zevvv', dataset, 'name')).toEqual(4); 262 expect(fs.find('Zevvv', dataset, 'name')).toEqual(4);
262 }); 263 });
264 +
265 +
266 + // === Tests for inArray()
267 + var objRef = { x:1, y:2 },
268 + array = [1, 3.14, 'hey', objRef, 'there', true],
269 + array2 = ['b', 'a', 'd', 'a', 's', 's'];
270 +
271 + it('should return -1 on non-arrays', function () {
272 + expect(fs.inArray(1, {x:1})).toEqual(-1);
273 + });
274 + it('should not find HOO', function () {
275 + expect(fs.inArray('HOO', array)).toEqual(-1);
276 + });
277 + it('should find 1', function () {
278 + expect(fs.inArray(1, array)).toEqual(0);
279 + });
280 + it('should find pi', function () {
281 + expect(fs.inArray(3.14, array)).toEqual(1);
282 + });
283 + it('should find hey', function () {
284 + expect(fs.inArray('hey', array)).toEqual(2);
285 + });
286 + it('should find the object', function () {
287 + expect(fs.inArray(objRef, array)).toEqual(3);
288 + });
289 + it('should find there', function () {
290 + expect(fs.inArray('there', array)).toEqual(4);
291 + });
292 + it('should find true', function () {
293 + expect(fs.inArray(true, array)).toEqual(5);
294 + });
295 +
296 + it('should find the first occurrence A', function () {
297 + expect(fs.inArray('a', array2)).toEqual(1);
298 + });
299 + it('should find the first occurrence S', function () {
300 + expect(fs.inArray('s', array2)).toEqual(4);
301 + });
302 + it('should not find X', function () {
303 + expect(fs.inArray('x', array2)).toEqual(-1);
304 + });
305 +
306 + // === Tests for removeFromArray()
307 + it('should ignore non-arrays', function () {
308 + expect(fs.removeFromArray(1, {x:1})).toBe(false);
309 + });
310 + it('should keep the array the same, for non-match', function () {
311 + var array = [1, 2, 3];
312 + expect(fs.removeFromArray(4, array)).toBe(false);
313 + expect(array).toEqual([1, 2, 3]);
314 + });
315 + it('should remove a value', function () {
316 + var array = [1, 2, 3];
317 + expect(fs.removeFromArray(2, array)).toBe(true);
318 + expect(array).toEqual([1, 3]);
319 + });
320 + it('should remove the first occurrence', function () {
321 + var array = ['x', 'y', 'z', 'z', 'y'];
322 + expect(fs.removeFromArray('y', array)).toBe(true);
323 + expect(array).toEqual(['x', 'z', 'z', 'y']);
324 + expect(fs.removeFromArray('x', array)).toBe(true);
325 + expect(array).toEqual(['z', 'z', 'y']);
326 + });
327 +
263 }); 328 });
......