GUI -- TopoView - added node selection logic.
- added inArray() and removeFromArray() functions to FnService.
Change-Id: I0e9631fa9e5865cb171e8d505f45c1963a1903dc
diff --git a/web/gui/src/main/webapp/app/fw/util/fn.js b/web/gui/src/main/webapp/app/fw/util/fn.js
index 690c8b2..dcc2725 100644
--- a/web/gui/src/main/webapp/app/fw/util/fn.js
+++ b/web/gui/src/main/webapp/app/fw/util/fn.js
@@ -117,6 +117,32 @@
return -1;
}
+ // search through array to find (the first occurrence of) item,
+ // returning its index if found; otherwise returning -1.
+ function inArray(item, array) {
+ var i;
+ if (isA(array)) {
+ for (i=0; i<array.length; i++) {
+ if (array[i] === item) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ // remove (the first occurrence of) the specified item from the given
+ // array, if any. Return true if the removal was made; false otherwise.
+ function removeFromArray(item, array) {
+ var found = false,
+ i = inArray(item, array);
+ if (i >= 0) {
+ array.splice(i, 1);
+ found = true;
+ }
+ return found;
+ }
+
angular.module('onosUtil')
.factory('FnService', ['$window', function (_$window_) {
$window = _$window_;
@@ -130,7 +156,9 @@
areFunctions: areFunctions,
areFunctionsNonStrict: areFunctionsNonStrict,
windowSize: windowSize,
- find: find
+ find: find,
+ inArray: inArray,
+ removeFromArray: removeFromArray
};
}]);
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index 7bd640d..c0e9228 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -111,8 +111,7 @@
<div id="quickhelp"></div>
<div id="veil"
resize
- ng-style="resizeWithOffset(0, 0)"
- ></div>
+ ng-style="resizeWithOffset(0, 0)"></div>
</div>
</body>
</html>
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index e5fc263..e189ef1 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -77,7 +77,9 @@
oblique = false, // whether we are in the oblique view
nodeLock = false, // whether nodes can be dragged or not (locked)
width, height, // the width and height of the force layout
- hovered; // the node over which the mouse is hovering
+ hovered, // the node over which the mouse is hovering
+ selections = {}, // what is currently selected
+ selectOrder = []; // the order in which we made selections
// SVG elements;
var linkG, linkLabelG, nodeG;
@@ -1323,15 +1325,77 @@
}
+ function updateDetailPanel() {
+ // TODO update detail panel
+ $log.debug("TODO: updateDetailPanel() ...");
+ }
+
+
+ // ==========================
+ // === SELECTION / DESELECTION
+
+ function selectObject(obj) {
+ var el = this,
+ ev = d3.event.sourceEvent,
+ n;
+
+ if (zoomingOrPanning(ev)) {
+ return;
+ }
+
+ if (el) {
+ n = d3.select(el);
+ } else {
+ node.each(function (d) {
+ if (d == obj) {
+ n = d3.select(el = this);
+ }
+ });
+ }
+ if (!n) return;
+
+ if (ev.shiftKey && n.classed('selected')) {
+ deselectObject(obj.id);
+ updateDetailPanel();
+ return;
+ }
+
+ if (!ev.shiftKey) {
+ deselectAll();
+ }
+
+ selections[obj.id] = { obj: obj, el: el };
+ selectOrder.push(obj.id);
+
+ n.classed('selected', true);
+ updateDeviceColors(obj);
+ updateDetailPanel();
+ }
+
+ function deselectObject(id) {
+ var obj = selections[id];
+ if (obj) {
+ d3.select(obj.el).classed('selected', false);
+ delete selections[id];
+ fs.removeFromArray(id, selectOrder);
+ updateDeviceColors(obj.obj);
+ }
+ }
+
+ function deselectAll() {
+ // deselect all nodes in the network...
+ node.classed('selected', false);
+ selections = {};
+ selectOrder = [];
+ updateDeviceColors();
+ updateDetailPanel();
+ }
+
// ==========================
// === MOUSE GESTURE HANDLERS
- function selectCb(d) {
- // this is the selected node
- $log.debug("\n\n\nSelect Object: ");
- $log.debug("d is ", d);
- $log.debug("this is ", this);
- $log.debug('\n\n');
+ function zoomingOrPanning(ev) {
+ return ev.metaKey || ev.altKey;
}
function atDragEnd(d) {
@@ -1345,8 +1409,7 @@
function dragEnabled() {
var ev = d3.event.sourceEvent;
// nodeLock means we aren't allowing nodes to be dragged...
- // meta or alt key pressed means we are zooming/panning...
- return !nodeLock && !(ev.metaKey || ev.altKey);
+ return !nodeLock && !zoomingOrPanning(ev);
}
// predicate that indicates when clicking is active
@@ -1406,7 +1469,7 @@
.on('tick', tick);
drag = sus.createDragBehavior(force,
- selectCb, atDragEnd, dragEnabled, clickEnabled);
+ selectObject, atDragEnd, dragEnabled, clickEnabled);
}
function resize(dim) {
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
index 1727aee..27b6ba3 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
@@ -201,7 +201,8 @@
it('should define api functions', function () {
expect(fs.areFunctions(fs, [
'isF', 'isA', 'isS', 'isO', 'contains',
- 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find'
+ 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find',
+ 'inArray', 'removeFromArray'
])).toBeTruthy();
});
@@ -260,4 +261,68 @@
it('should find Zevvv', function () {
expect(fs.find('Zevvv', dataset, 'name')).toEqual(4);
});
+
+
+ // === Tests for inArray()
+ var objRef = { x:1, y:2 },
+ array = [1, 3.14, 'hey', objRef, 'there', true],
+ array2 = ['b', 'a', 'd', 'a', 's', 's'];
+
+ it('should return -1 on non-arrays', function () {
+ expect(fs.inArray(1, {x:1})).toEqual(-1);
+ });
+ it('should not find HOO', function () {
+ expect(fs.inArray('HOO', array)).toEqual(-1);
+ });
+ it('should find 1', function () {
+ expect(fs.inArray(1, array)).toEqual(0);
+ });
+ it('should find pi', function () {
+ expect(fs.inArray(3.14, array)).toEqual(1);
+ });
+ it('should find hey', function () {
+ expect(fs.inArray('hey', array)).toEqual(2);
+ });
+ it('should find the object', function () {
+ expect(fs.inArray(objRef, array)).toEqual(3);
+ });
+ it('should find there', function () {
+ expect(fs.inArray('there', array)).toEqual(4);
+ });
+ it('should find true', function () {
+ expect(fs.inArray(true, array)).toEqual(5);
+ });
+
+ it('should find the first occurrence A', function () {
+ expect(fs.inArray('a', array2)).toEqual(1);
+ });
+ it('should find the first occurrence S', function () {
+ expect(fs.inArray('s', array2)).toEqual(4);
+ });
+ it('should not find X', function () {
+ expect(fs.inArray('x', array2)).toEqual(-1);
+ });
+
+ // === Tests for removeFromArray()
+ it('should ignore non-arrays', function () {
+ expect(fs.removeFromArray(1, {x:1})).toBe(false);
+ });
+ it('should keep the array the same, for non-match', function () {
+ var array = [1, 2, 3];
+ expect(fs.removeFromArray(4, array)).toBe(false);
+ expect(array).toEqual([1, 2, 3]);
+ });
+ it('should remove a value', function () {
+ var array = [1, 2, 3];
+ expect(fs.removeFromArray(2, array)).toBe(true);
+ expect(array).toEqual([1, 3]);
+ });
+ it('should remove the first occurrence', function () {
+ var array = ['x', 'y', 'z', 'z', 'y'];
+ expect(fs.removeFromArray('y', array)).toBe(true);
+ expect(array).toEqual(['x', 'z', 'z', 'y']);
+ expect(fs.removeFromArray('x', array)).toBe(true);
+ expect(array).toEqual(['z', 'z', 'y']);
+ });
+
});