GUI -- Reworking sample view to be more interesting.
diff --git a/web/gui/src/main/webapp/sample2.js b/web/gui/src/main/webapp/sample2.js
index 7222452..6b4329f 100644
--- a/web/gui/src/main/webapp/sample2.js
+++ b/web/gui/src/main/webapp/sample2.js
@@ -15,7 +15,7 @@
  */
 
 /*
- Alternate Sample module file to illustrate framework integration.
+ Sample module file to illustrate framework integration.
 
  @author Simon Hunt
  */
@@ -23,9 +23,24 @@
 (function (onos) {
     'use strict';
 
-    var svg;
+    var pi = Math.PI,
+        svg,
+        dotG,
+        nCircles = 12,
+        circleData = [],
+        dotId = 0,
+        angle = 360 / nCircles,
+        baseAngle = -90 - angle,
+        groupRadius = 120,
+        dotRadius = 24,
+        dotMoveMs = 800,
+        dotAppearMs = 300,
+        dotEase = 'elastic',
+        colorScale = d3.scale.linear()
+            .domain([-pi/2, 2*pi/4, 3*pi/2])
+            .range(['green', 'goldenrod', 'blue']);
 
-
+    // set the size of the SVG layer to match that of the view
     function sizeSvg(view) {
         svg.attr({
             width: view.width(),
@@ -33,63 +48,169 @@
         });
     }
 
-    // NOTE: view is a view-token data structure:
-    // {
-    //     vid: 'view-id',
-    //     nid: 'nav-id',
-    //     $div: ...      // d3 selection of dom view div.
-    // }
-
     // gets invoked only the first time the view is loaded
     function preload(view, ctx) {
+        // prepare our SVG layer...
         svg = view.$div.append('svg');
         sizeSvg(view);
+        dotG = svg.append('g').attr('id', 'dots');
     }
 
+    // gets invoked just before our view is loaded
     function reset(view) {
-        // clear our svg of all objects
-        svg.html('');
+        // clear dot group and reset circle data
+        dotG.html('');
+        circleData = [];
+        // also clear text, if any
+        svg.selectAll('text').remove();
     }
 
-    function load(view, ctx) {
-        var fill = 'red',
-            stroke = 'black',
-            ctxText = ctx ? 'Context is "' + ctx + '"' : 'No Context';
+    function updateCirclePositions(view, addNew) {
+        var w = view.width(),
+            h = view.height(),
+            ox = w / 2,
+            oy = h / 2;
 
-        svg.append('circle')
+        // reposition existing dots
+        circleData.forEach(function (c, i) {
+            var inc = addNew ? 1 : 0,
+                theta = ((i + inc) * angle + baseAngle) * pi/180,
+                dx = Math.cos(theta) * groupRadius,
+                dy = Math.sin(theta) * groupRadius,
+                x = ox + dx,
+                y = oy + dy;
+            if (!addNew && i === 0) {
+                x = ox;
+                y = oy;
+            }
+            c.cx = x;
+            c.cy = y;
+            c.rgb = colorScale(theta);
+        });
+
+        if (addNew) {
+            // introduce a new dot
+            circleData.unshift({
+                cx: ox,
+                cy: oy,
+                id: dotId++
+            });
+        }
+
+        // +1 to account for the circle in the center..
+        if (circleData.length > nCircles + 1) {
+            circleData.splice(nCircles + 1, 1);
+        }
+    }
+
+    function doCircles(view) {
+        var ox = view.width() / 2,
+            oy = view.height() / 2,
+            stroke = 'black',
+            fill = 'red',
+            hoverFill = 'magenta';
+
+        // move existing circles, and add a new one
+        updateCirclePositions(view, true);
+
+        var circ = dotG.selectAll('circle')
+            .data(circleData, function (d) { return d.id; });
+
+        // operate on existing elements
+        circ.on('mouseover', null)
+            .on('mouseout', null)
+            .on('click', null)
+            .transition()
+            .duration(dotMoveMs)
+            .ease(dotEase)
             .attr({
-                cx: view.width() / 2,
-                cy: view.height() / 2,
-                r: 30
+                cx: function (d) { return d.cx; },
+                cy: function (d) { return d.cy; }
+            })
+            .style({
+                cursor: 'default',
+                fill: function (d) { return d.rgb; }
+            });
+
+        // operate on entering elements
+        circ.enter()
+            .append('circle')
+            .attr({
+                cx: function (d) { return d.cx; },
+                cy: function (d) { return d.cy; },
+                r: 0
             })
             .style({
                 fill: fill,
                 stroke: stroke,
-                'stroke-width': 3.5
-            });
-
-        svg.append('text')
-            .text(ctxText)
-            .attr({
-                x: 20,
-                y: '1.5em'
+                'stroke-width': 3.5,
+                cursor: 'pointer',
+                opacity: 0
             })
-            .style({
-                fill: 'darkgreen',
-                'font-size': '20pt'
-            });
+            .on('mouseover', function (d) {
+                d3.select(this).style('fill', hoverFill);
+            })
+            .on('mouseout', function (d) {
+                d3.select(this).style('fill', fill);
+            })
+            .on('click', function (d) {
+                setTimeout(function() {
+                    doCircles(view, true);
+                }, 10);
+            })
+            .transition()
+            .delay(dotMoveMs)
+            .duration(dotAppearMs)
+            .attr('r', dotRadius)
+            .style('opacity', 1);
+
+        // operate on exiting elements
+        circ.exit()
+            .transition()
+            .duration(750)
+            .style('opacity', 0)
+            .attr({
+                cx: ox,
+                cy: oy,
+                r: groupRadius - dotRadius
+            })
+            .remove();
+    }
+
+    function load(view, ctx) {
+        var ctxText = ctx ? 'Context is "' + ctx + '"' : '';
+
+        // display our view context
+        if (ctxText) {
+            svg.append('text')
+                .text(ctxText)
+                .attr({
+                    x: 20,
+                    y: '1.5em'
+                })
+                .style({
+                    fill: 'darkgreen',
+                    'font-size': '20pt'
+                });
+        }
+
+        doCircles(view);
     }
 
     function resize(view, ctx) {
         sizeSvg(view);
-        svg.selectAll('circle')
-            .attr({
-                cx: view.width() / 2,
-                cy: view.height() / 2
+        updateCirclePositions(view);
+
+        // move exiting dots into new positions, relative to view size
+        var circ = dotG.selectAll('circle')
+            .data(circleData, function (d) { return d.id; });
+        circ.attr({
+                cx: function (d) { return d.cx; },
+                cy: function (d) { return d.cy; }
             });
     }
 
-    // == register views here, with links to lifecycle callbacks
+    // == register our view here, with links to lifecycle callbacks
 
     onos.ui.addView('sample', {
         preload: preload,
@@ -98,5 +219,4 @@
         resize: resize
     });
 
-
 }(ONOS));
diff --git a/web/gui/src/main/webapp/sampleAlt2.js b/web/gui/src/main/webapp/sampleAlt2.js
index f0e662e..60cbe9d 100644
--- a/web/gui/src/main/webapp/sampleAlt2.js
+++ b/web/gui/src/main/webapp/sampleAlt2.js
@@ -15,7 +15,7 @@
  */
 
 /*
- Sample module file to illustrate framework integration.
+ Alternate sample module file to illustrate framework integration.
 
  @author Simon Hunt
  */
@@ -33,13 +33,6 @@
         });
     }
 
-    // NOTE: view is a view-token data structure:
-    // {
-    //     vid: 'view-id',
-    //     nid: 'nav-id',
-    //     $div: ...      // d3 selection of dom view div.
-    // }
-
     // gets invoked only the first time the view is loaded
     function preload(view, ctx) {
         svg = view.$div.append('svg');
@@ -52,8 +45,8 @@
     }
 
     function load(view, ctx) {
-        var fill = 'blue',
-            stroke = 'grey';
+        var fill = 'teal',
+            stroke = 'black';
 
         svg.append('circle')
             .attr({
@@ -64,7 +57,8 @@
             .style({
                 fill: fill,
                 stroke: stroke,
-                'stroke-width': 3.5
+                'stroke-width': 1.5,
+                opacity: 0.5
             });
     }