GUI -- Test events: unpinned the first node; increased a few link widths.
 - added alerts pane to framework.
 - added library registration mechanism to framework.
 - created d3Utils library
 - reimplemented drag behavior of nodes.

Change-Id: I501f4ab6eded8393948cede903573580599258b1
diff --git a/web/gui/src/main/webapp/d3Utils.js b/web/gui/src/main/webapp/d3Utils.js
new file mode 100644
index 0000000..51651fa
--- /dev/null
+++ b/web/gui/src/main/webapp/d3Utils.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Utility functions for D3 visualizations.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+    'use strict';
+
+    function createDragBehavior(force, selectCb, atDragEnd) {
+        var draggedThreshold = d3.scale.linear()
+                .domain([0, 0.1])
+                .range([5, 20])
+                .clamp(true),
+            drag;
+
+        // TODO: better validation of parameters
+        if (!$.isFunction(selectCb)) {
+            alert('d3util.createDragBehavior(): selectCb is not a function')
+        }
+        if (!$.isFunction(atDragEnd)) {
+            alert('d3util.createDragBehavior(): atDragEnd is not a function')
+        }
+
+        function dragged(d) {
+            var threshold = draggedThreshold(force.alpha()),
+                dx = d.oldX - d.px,
+                dy = d.oldY - d.py;
+            if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
+                d.dragged = true;
+            }
+            return d.dragged;
+        }
+
+        drag = d3.behavior.drag()
+            .origin(function(d) { return d; })
+            .on('dragstart', function(d) {
+                d.oldX = d.x;
+                d.oldY = d.y;
+                d.dragged = false;
+                d.fixed |= 2;
+            })
+            .on('drag', function(d) {
+                d.px = d3.event.x;
+                d.py = d3.event.y;
+                if (dragged(d)) {
+                    if (!force.alpha()) {
+                        force.alpha(.025);
+                    }
+                }
+            })
+            .on('dragend', function(d) {
+                if (!dragged(d)) {
+                    // consider this the same as a 'click' (selection of node)
+                    selectCb(d, this); // TODO: set 'this' context instead of param
+                }
+                d.fixed &= ~6;
+
+                // hook at the end of a drag gesture
+                atDragEnd(d, this); // TODO: set 'this' context instead of param
+            });
+
+        return drag;
+    }
+
+    function appendGlow(svg) {
+        // TODO: parameterize color
+
+        var glow = svg.append('filter')
+            .attr('x', '-50%')
+            .attr('y', '-50%')
+            .attr('width', '200%')
+            .attr('height', '200%')
+            .attr('id', 'blue-glow');
+
+        glow.append('feColorMatrix')
+            .attr('type', 'matrix')
+            .attr('values', '0 0 0 0  0 ' +
+            '0 0 0 0  0 ' +
+            '0 0 0 0  .7 ' +
+            '0 0 0 1  0 ');
+
+        glow.append('feGaussianBlur')
+            .attr('stdDeviation', 3)
+            .attr('result', 'coloredBlur');
+
+        glow.append('feMerge').selectAll('feMergeNode')
+            .data(['coloredBlur', 'SourceGraphic'])
+            .enter().append('feMergeNode')
+            .attr('in', String);
+    }
+
+    // === register the functions as a library
+    onos.ui.addLib('d3util', {
+        createDragBehavior: createDragBehavior,
+        appendGlow: appendGlow
+    });
+
+}(ONOS));