GUI -- [ONOS-282] - clean up drag behavior API.

Change-Id: I562eedd6f075afdd8d35e109fda9c6cd1d594d82
diff --git a/web/gui/src/main/webapp/d3Utils.js b/web/gui/src/main/webapp/d3Utils.js
index accfcab..33bba5a 100644
--- a/web/gui/src/main/webapp/d3Utils.js
+++ b/web/gui/src/main/webapp/d3Utils.js
@@ -23,23 +23,40 @@
 (function (onos) {
     'use strict';
 
-    function createDragBehavior(force, selectCb, atDragEnd, requireMeta) {
+    function isF(f) {
+        return $.isFunction(f) ? f : null;
+    }
+
+    function createDragBehavior(force, selectCb, atDragEnd, enabled) {
         var draggedThreshold = d3.scale.linear()
                 .domain([0, 0.1])
                 .range([5, 20])
                 .clamp(true),
-            drag;
+            drag,
+            fSel = isF(selectCb),
+            fEnd = isF(atDragEnd),
+            fEnb = isF(enabled),
+            bad = [];
 
-        // TODO: better validation of parameters
-        if (!$.isFunction(selectCb)) {
-            alert('d3util.createDragBehavior(): selectCb is not a function')
+        function naf(what) {
+            return 'd3util.createDragBehavior(): '+ what + ' is not a function';
         }
-        if (!$.isFunction(atDragEnd)) {
-            alert('d3util.createDragBehavior(): atDragEnd is not a function')
+
+        if (!fSel) {
+            bad.push(naf('selectCb'));
         }
-        if (!$.isFunction(requireMeta)) {
-            alert('d3util.createDragBehavior(): requireMeta is not a function')
+        if (!fEnd) {
+            bad.push(naf('atDragEnd'));
         }
+        if (!fEnb) {
+            bad.push(naf('enabled'));
+        }
+
+        if (bad.length) {
+            alert(bad.join('\n'));
+            return null;
+        }
+
 
         function dragged(d) {
             var threshold = draggedThreshold(force.alpha()),
@@ -54,7 +71,7 @@
         drag = d3.behavior.drag()
             .origin(function(d) { return d; })
             .on('dragstart', function(d) {
-                if (requireMeta() ^ !d3.event.sourceEvent.metaKey) {
+                if (enabled()) {
                     d3.event.sourceEvent.stopPropagation();
 
                     d.oldX = d.x;
@@ -65,7 +82,7 @@
                 }
             })
             .on('drag', function(d) {
-                if (requireMeta() ^ !d3.event.sourceEvent.metaKey) {
+                if (enabled()) {
                     d.px = d3.event.x;
                     d.py = d3.event.y;
                     if (dragged(d)) {
diff --git a/web/gui/src/main/webapp/topo.js b/web/gui/src/main/webapp/topo.js
index 1620048..6673b28 100644
--- a/web/gui/src/main/webapp/topo.js
+++ b/web/gui/src/main/webapp/topo.js
@@ -206,7 +206,8 @@
         showHosts = false,
         showOffline = true,
         useDetails = true,
-        haveDetails = false;
+        haveDetails = false,
+        dragAllowed = true;
 
     // constants
     var hoverModeAll = 1,
@@ -2642,14 +2643,13 @@
         svg = view.$div.append('svg').attr('viewBox', viewBox);
         setSize(svg, view);
 
+        // load glyphs and filters...
         loadGlyphs(svg);
+        d3u.appendGlow(svg);
 
         zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
         setupZoomPan();
 
-        // add blue glow filter to svg layer
-        d3u.appendGlow(zoomPanContainer);
-
         // group for the topology
         topoG = zoomPanContainer.append('g')
             .attr('id', 'topo-G')
@@ -2691,6 +2691,14 @@
             }
         }
 
+        // predicate that indicates when dragging is active
+        function dragEnabled() {
+            // meta key pressed means we are zooming/panning (so disable drag)
+            return dragAllowed && !d3.event.sourceEvent.metaKey;
+            // dragAllowed will be set false when we are in oblique view
+            // or when we 'lock' node positions
+        }
+
         // set up the force layout
         network.force = d3.layout.force()
             .size(forceDim)
@@ -2704,10 +2712,16 @@
             .on('tick', tick);
 
         network.drag = d3u.createDragBehavior(network.force,
-            selectCb, atDragEnd, panZoom);
+            selectCb, atDragEnd, dragEnabled);
+
 
         // create mask layer for when we lose connection to server.
         // TODO: this should be part of the framework
+
+        function para(sel, text) {
+            sel.append('p').text(text);
+        }
+
         mask = view.$div.append('div').attr('id','topo-mask');
         para(mask, 'Oops!');
         para(mask, 'Web-socket connection to server closed...');
@@ -2731,10 +2745,6 @@
                 })
     }
 
-    function para(sel, text) {
-        sel.append('p').text(text);
-    }
-
 
     function load(view, ctx, flags) {
         // resize, in case the window was resized while we were not loaded