GUI --Base Tooltip Service created.
      - tips will appear above the mouse when element is hovered over
      - tips disappear after mouse leaves element
      - positioning of tooltip is still WIP, testing of it was run in Sample View

Change-Id: Ic3be1235bf8d2d4bbf473f965e3a1f8c79ce9fc6
diff --git a/web/gui/src/main/webapp/app/fw/widget/button.js b/web/gui/src/main/webapp/app/fw/widget/button.js
index c9980ee..0f97fdd 100644
--- a/web/gui/src/main/webapp/app/fw/widget/button.js
+++ b/web/gui/src/main/webapp/app/fw/widget/button.js
@@ -21,7 +21,7 @@
     'use strict';
 
     // injected refs
-    var $log, fs, is;
+    var $log, fs, is, tts;
 
     // configuration
     var btnSize = 25,
@@ -49,6 +49,11 @@
         return btnSize + 2 * btnPadding;
     }
 
+    function addTooltip(elem, tooltip) {
+        elem.on('mouseover', function () { tts.showTooltip(this, tooltip); });
+        elem.on('mouseout', function () { tts.cancelTooltip(this); });
+    }
+
 
     // === BUTTON =================================================
 
@@ -64,6 +69,7 @@
             cbFnc = fs.isF(cb) || noop;
 
         is.loadIcon(btnDiv, gid, btnSize, true);
+        if (tooltip) { addTooltip(btnDiv, tooltip); }
 
         btnDiv.on('click', cbFnc);
 
@@ -91,6 +97,7 @@
 
         is.loadIcon(togDiv, gid, btnSize, true);
         togDiv.classed('selected', sel);
+        if (tooltip) { addTooltip(togDiv, tooltip); }
 
         function _toggle(b, nocb) {
             sel = (b === undefined) ? !sel : !!b;
@@ -180,6 +187,7 @@
             rbdiv.classed('selected', initSel);
             rbdiv.on('click', rbclick);
             is.loadIcon(rbdiv, btn.gid, btnSize, true);
+            if (btn.tooltip) { addTooltip(rbdiv, btn.tooltip); }
             angular.extend(btn, {
                 el: rbdiv,
                 id: rid,
@@ -243,12 +251,13 @@
 
     angular.module('onosWidget')
     .factory('ButtonService',
-        ['$log', 'FnService', 'IconService',
+        ['$log', 'FnService', 'IconService', 'TooltipService',
 
-        function (_$log_, _fs_, _is_) {
+        function (_$log_, _fs_, _is_, _tts_) {
             $log = _$log_;
             fs = _fs_;
             is = _is_;
+            tts = _tts_;
 
             return {
                 button: button,
diff --git a/web/gui/src/main/webapp/app/fw/widget/tooltip.css b/web/gui/src/main/webapp/app/fw/widget/tooltip.css
new file mode 100644
index 0000000..f967793
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/widget/tooltip.css
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ ONOS GUI -- Tooltip Service -- CSS file
+ */
+
+#tooltip {
+    text-align: center;
+    font-size: 80%;
+    border: 1px solid;
+    padding: 5px;
+    position: absolute;
+    z-index: 5000;
+    display: none;
+    pointer-events: none;
+}
+
+/* colors subject to change */
+
+.light #tooltip {
+    background-color: #ddd;
+    color: #444;
+    border-color: #ccc;
+}
+
+.dark #tooltip {
+    background-color: #151515;
+    color: #B2B2B2;
+    border-color: #252525;
+}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/widget/tooltip.js b/web/gui/src/main/webapp/app/fw/widget/tooltip.js
new file mode 100644
index 0000000..b558b27
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/widget/tooltip.js
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ ONOS GUI -- Widget -- Tooltip Service
+ */
+
+(function () {
+    'use strict';
+
+    // injected references
+    var $log, $timeout, fs;
+
+    // constants
+    var hoverHeight = 35,
+        hoverDelay = 500,
+        exitDelay = 100;
+
+    // internal state
+    var tooltip, currElemId;
+
+    function init() {
+        tooltip = d3.select('#tooltip');
+        tooltip.html('');
+    }
+
+    // === API functions ------------------------------------------------
+
+    function showTooltip(el, msg) {
+        if (!el || !msg) {
+            return;
+        }
+        var elem = d3.select(el),
+            mouseX = d3.event.pageX,
+            mouseY = d3.event.pageY;
+        currElemId = elem.attr('id');
+
+        tooltip.transition()
+            .delay(hoverDelay)
+            .each('start', function () {
+                d3.select(this).style('display', 'none');
+            })
+            .each('end', function () {
+                d3.select(this).style({
+                    display: 'inline-block',
+                    left: mouseX + 'px',
+                    top: (mouseY - hoverHeight) + 'px'
+                })
+                    .text(msg);
+            });
+    }
+
+    function cancelTooltip(el) {
+        if (!el) {
+            return;
+        }
+        var elem = d3.select(el);
+
+        if (elem.attr('id') === currElemId) {
+            tooltip.transition()
+                .delay(exitDelay)
+                .style({
+                    display: 'none'
+                })
+                .text('');
+        }
+    }
+
+    angular.module('onosWidget')
+        .factory('TooltipService', ['$log', '$timeout', 'FnService',
+
+        function (_$log_, _$timeout_, _fs_) {
+            $log = _$log_;
+            $timeout = _$timeout_;
+            fs = _fs_;
+
+            init();
+
+            return {
+                showTooltip: showTooltip,
+                cancelTooltip: cancelTooltip
+            };
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/sample/sample.js b/web/gui/src/main/webapp/app/view/sample/sample.js
index c726e90..bb7417c 100644
--- a/web/gui/src/main/webapp/app/view/sample/sample.js
+++ b/web/gui/src/main/webapp/app/view/sample/sample.js
@@ -86,16 +86,16 @@
 
             toolbar = tbs.createToolbar(tbid);
             rset = [
-                { gid: 'checkMark', cb: checkFn },
+                { gid: 'checkMark', cb: checkFn, tooltip: 'rbtn tooltip' },
                 { gid: 'xMark', cb: xMarkFn },
-                { gid: 'bird', cb: birdFn }
+                { gid: 'bird', cb: birdFn, tooltip: 'hello' }
             ];
 
-            toolbar.addButton('demo-button', 'crown', btnFn);
-            toolbar.addToggle('demo-toggle', 'chain', false, togFn);
+            toolbar.addButton('demo-button', 'crown', btnFn, 'yay a tooltip');
+            toolbar.addToggle('demo-toggle', 'chain', false, togFn, 'another tooltip');
             toolbar.addSeparator();
             toolbar.addRadioSet('demo-radio', rset);
-            toolbar.hide();
+            toolbar.show();
 
             checkFn();
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 192c18d..06ddc0c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -20,6 +20,7 @@
  */
 
 (function () {
+    'use strict';
 
     // injected references
     var $log, tbs, api;
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 149ad6e..1bd5f33 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -59,6 +59,7 @@
     <script src="app/fw/widget/widget.js"></script>
     <script src="app/fw/widget/table.js"></script>
     <script src="app/fw/widget/toolbar.js"></script>
+    <script src="app/fw/widget/tooltip.js"></script>
     <script src="app/fw/widget/button.js"></script>
 
     <script src="app/fw/layer/layer.js"></script>
@@ -81,6 +82,7 @@
     <link rel="stylesheet" href="app/fw/nav/nav.css">
     <link rel="stylesheet" href="app/fw/widget/button.css">
     <link rel="stylesheet" href="app/fw/widget/toolbar.css">
+    <link rel="stylesheet" href="app/fw/widget/tooltip.css">
 
     <!-- This is where contributed javascript will get injected -->
     <!-- {INJECTED-JAVASCRIPT-START} -->
@@ -124,6 +126,7 @@
 
     <div id="floatpanels"></div>
     <div id="alerts"></div>
+    <div id="tooltip"></div>
     <div id="flash"></div>
     <div id="quickhelp"></div>
     <div id="veil"
diff --git a/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js b/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js
new file mode 100644
index 0000000..f5a8cef
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ ONOS GUI -- Widget -- Tooltip Service - Unit Tests
+ */
+describe('factory: fw/widget/tooltip.js', function () {
+    var $log, fs, tts, d3Elem;
+
+    beforeEach(module('onosWidget', 'onosUtil'));
+
+    beforeEach(inject(function (_$log_, FnService, TooltipService) {
+        $log = _$log_;
+        fs = FnService;
+        tts = TooltipService;
+    }));
+
+    beforeEach(function () {
+        d3Elem = d3.select('body').append('div').attr('id', 'tooltip');
+    });
+
+    afterEach(function () {
+        d3.select('#tooltip').remove();
+    });
+
+    it('should define TooltipService', function () {
+        expect(tts).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(tts, [
+            'showTooltip', 'cancelTooltip'
+        ])).toBeTruthy();
+    });
+
+    it('should not accept undefined arguments', function () {
+        var btn = d3.select('body').append('div');
+        expect(tts.showTooltip()).toBeFalsy();
+        expect(tts.showTooltip(btn)).toBeFalsy();
+
+        expect(tts.cancelTooltip()).toBeFalsy();
+    });
+
+    // testing mouse events is tough
+
+    xit('should show a tooltip', function () {
+        var btn = d3.select('body').append('div').attr('id', 'foo');
+        // each is used to trigger a "mouse" event, providing this, d, and i
+        btn.each(function () {
+            tts.showTooltip(this, 'yay a tooltip');
+        });
+    });
+});