GUI -- Implemented createToolbar in ToolbarService.
- Will log warnings if no toolbar id is given, there are no tools, and if the tool's ids are duplicated
- creates toolbar div and calls add button functions based on tool input
- Created skeleton dispatchers to ButtonService
- Wrote unit tests for current ToolbarService

Change-Id: I3d05158c5ce132cb94d465674949ade81ed12664
diff --git a/web/gui/src/main/webapp/app/fw/widget/toolbar.js b/web/gui/src/main/webapp/app/fw/widget/toolbar.js
index ee60985..86309c8 100644
--- a/web/gui/src/main/webapp/app/fw/widget/toolbar.js
+++ b/web/gui/src/main/webapp/app/fw/widget/toolbar.js
@@ -20,16 +20,40 @@
 (function () {
     'use strict';
 
-    var $log;
+    var $log, ps;
 
-    // rids will hold all the ids used with makeRadio so that you can create
-    // more radio buttons not in order
-    // TODO: implement this --^
-    var rids = {},
-        ridCtr = 0;
+    var toolBtnIds = {},
+        toolbarPanel,
+        toolbarDiv;
 
-    // toggle state is used in createToolbar (not needed in makeToggle) (??)
-    var toggle = false;
+    function init() {
+        toolBtnIds = {};
+    }
+
+    function addButton(btn) {
+        // pass in button object and pass separate the pieces in this function,
+        // to be passed to ButtonService
+        var btndiv = toolbarDiv.append('div').attr('class', 'btn');
+        // use ButtonService to create btn with other arguments and btndiv
+    }
+
+    function addToggle(tog) {
+        var togdiv = toolbarDiv.append('div').attr('class', 'tog');
+        // use ButtonService to create btn with other arguments and togdiv
+    }
+
+    function addRadio(rad) {
+        var raddiv = toolbarDiv.append('div').attr('class', 'rad');
+        // use ButtonService to create btn with other arguments and raddiv
+    }
+
+    function addSeparator() {
+        toolbarDiv.append('div')
+            .attr('class', 'sep')
+            .style({'width': '2px',
+                    'border-width': '1px',
+                    'border-style': 'solid'});
+    }
 
     function makeButton(id, gid, cb) {
         return {
@@ -49,10 +73,13 @@
         };
     }
 
-    function makeRadio(id, cb) {
+    function makeRadio(id, gid, cb) {
+        (id in toolBtnIds) ? toolBtnIds[id] += 1 : toolBtnIds[id] = 0;
         return {
             t: 'rad',
-            id: id + '-' + ridCtr,
+            id: id + '-' + toolBtnIds[id],
+            rid: toolBtnIds[id] + '',
+            gid: gid,
             cb: cb
         };
     }
@@ -63,21 +90,72 @@
         };
     }
 
-    function createToolbar() {
+    function createToolbar(tbarId, tools) {
+        var api;
 
+        if (!tbarId) {
+            $log.warn('createToolbar: no ID given');
+            return null;
+        }
+        if (!tools || tools.constructor !== Array || !tools.length) {
+            $log.warn('createToolbar: no tools given');
+            return null;
+        }
+
+        for(var i = 0; i < tools.length; i += 1) {
+            if (tools[i].t === 'rad' || tools[i].t === 'sep') {
+                continue;
+            } else {
+                if (tools[i].id in toolBtnIds) {
+                    $log.warn('createToolbar: item with id '
+                            + tools[i].id + ' already exists');
+                    return null;
+                }
+                toolBtnIds[tools[i].id] = 1;
+            }
+        }
+
+        // need to pass in an object with settings for where the toolbar will go
+        toolbarPanel = ps.createPanel(tbarId, {});
+        toolbarPanel.classed('toolbar', true);
+        toolbarDiv = toolbarPanel.el();
+
+        tools.forEach(function (tool) {
+            switch (tool['t']) {
+                case 'btn': addButton(tool); break;
+                case 'tog': addToggle(tool); break;
+                case 'rad': addRadio(tool); break;
+                default: addSeparator(); break;
+            }
+        });
+
+        // api functions to be returned defined here
+
+        function size(arr) {
+            return arr.length;
+        }
+
+        api = {
+            size: size
+        };
+
+        return api;
     }
 
     angular.module('onosWidget')
-        .factory('ToolbarService', ['$log', function (_$log_) {
-            $log = _$log_;
+        .factory('ToolbarService', ['$log', 'PanelService',
+            function (_$log_, _ps_) {
+                $log = _$log_;
+                ps = _ps_;
 
-            return {
-                makeButton: makeButton,
-                makeToggle: makeToggle,
-                makeRadio: makeRadio,
-                separator: separator,
-                createToolbar: createToolbar
-            };
-        }]);
+                return {
+                    init: init,
+                    makeButton: makeButton,
+                    makeToggle: makeToggle,
+                    makeRadio: makeRadio,
+                    separator: separator,
+                    createToolbar: createToolbar
+                };
+            }]);
 
 }());
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
index 8637258..079b236 100644
--- a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js
@@ -108,7 +108,7 @@
     it('should note when there is no panel to destroy', function () {
         spyOn($log, 'debug');
         ps.destroyPanel('bar');
-        expect($log.debug).toHaveBeenCalledWith('no panel to destroy:', 'bar')
+        expect($log.debug).toHaveBeenCalledWith('no panel to destroy:', 'bar');
     });
 
     it('should destroy the panel', function () {
@@ -117,7 +117,7 @@
         expect(floatPanelSelection().size()).toBe(1);
 
         ps.destroyPanel('foo');
-        expect($log.debug).toHaveBeenCalledWith('destroying panel:', 'foo')
+        expect($log.debug).toHaveBeenCalledWith('destroying panel:', 'foo');
         expect(floatPanelSelection().size()).toBe(0);
     });
 
diff --git a/web/gui/src/main/webapp/tests/app/fw/widget/toolbar-spec.js b/web/gui/src/main/webapp/tests/app/fw/widget/toolbar-spec.js
index 2269642..30eec65 100644
--- a/web/gui/src/main/webapp/tests/app/fw/widget/toolbar-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/widget/toolbar-spec.js
@@ -18,26 +18,82 @@
  ONOS GUI -- Widget -- Toolbar Service - Unit Tests
  */
 describe('factory: fw/widget/toolbar.js', function () {
-    var $log, fs, tbs;
+    var $log, fs, tbs, ps,
+        d3Elem;
 
-    beforeEach(module('onosWidget', 'onosUtil'));
+    beforeEach(module('onosWidget', 'onosUtil', 'onosLayer'));
 
-    beforeEach(inject(function (_$log_, FnService, ToolbarService) {
+    beforeEach(inject(function (_$log_, FnService,
+                                ToolbarService, PanelService) {
         $log = _$log_;
         fs = FnService;
         tbs = ToolbarService;
+        ps = PanelService;
     }));
 
+    beforeEach(function () {
+        d3Elem = d3.select('body').append('div').attr('id', 'floatpanels');
+        tbs.init();
+        ps.init();
+    });
+
+    afterEach(function () {
+        d3.select('#floatpanels').remove();
+        tbs.init();
+        ps.init();
+    });
+
     it('should define ToolbarService', function () {
         expect(tbs).toBeDefined();
     });
 
     it('should define api functions', function () {
         expect(fs.areFunctions(tbs, [
-            'makeButton', 'makeToggle', 'makeRadio', 'separator', 'createToolbar'
+            'init', 'makeButton', 'makeToggle', 'makeRadio', 'separator',
+            'createToolbar'
         ])).toBeTruthy();
     });
 
+    function toolbarSelection() {
+        return d3Elem.selectAll('.toolbar');
+    }
+
+    it('should have no toolbars to start', function () {
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should log a warning if no ID is given', function () {
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar();
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: no ID given');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should log a warning if no tools are given', function () {
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar(true);
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: no tools given');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should log a warning if tools are not an array', function () {
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar(true, {});
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: no tools given');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should log a warning if tools array is empty', function () {
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar(true, []);
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: no tools given');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
     it("should verify makeButton's returned object", function () {
         var button = tbs.makeButton('foo', 'glyph-bar', function () {});
 
@@ -57,16 +113,48 @@
     });
 
     it("should verify makeRadio's returned object", function () {
-        // TODO: finish this
+        var rFoo0 = tbs.makeRadio('foo', 'glyph-foo0', function () {});
+        var rFoo1 = tbs.makeRadio('foo', 'glyph-foo1', function () {});
+        var rFoo2 = tbs.makeRadio('foo', 'glyph-foo2', function () {});
+        var rBar0 = tbs.makeRadio('bar', 'glyph-bar0', function () {});
+        var rBar1 = tbs.makeRadio('bar', 'glyph-bar1', function () {});
+        var rFoo3 = tbs.makeRadio('foo', 'glyph-foo3', function () {});
 
-        //var rFoo1 = tbs.makeRadio('foo', function () {});
-        //var rFoo2 = tbs.makeRadio('foo', function () {});
-        //var rFoo3 = tbs.makeRadio('foo', function () {});
-        //var rBar1 = tbs.makeRadio('bar', function () {});
-        //
-        //expect(radio1.t).toBe('rad');
-        //expect(radio1.id).toBe('foo');
-        //expect(fs.isF(radio1.cb)).toBeTruthy();
+        expect(rFoo0.t).toBe('rad');
+        expect(rFoo0.id).toBe('foo-0');
+        expect(rFoo0.rid).toBe('0');
+        expect(rFoo0.gid).toBe('glyph-foo0');
+        expect(fs.isF(rFoo0.cb)).toBeTruthy();
+
+        expect(rFoo1.t).toBe('rad');
+        expect(rFoo1.id).toBe('foo-1');
+        expect(rFoo1.rid).toBe('1');
+        expect(rFoo1.gid).toBe('glyph-foo1');
+        expect(fs.isF(rFoo1.cb)).toBeTruthy();
+
+        expect(rFoo2.t).toBe('rad');
+        expect(rFoo2.id).toBe('foo-2');
+        expect(rFoo2.rid).toBe('2');
+        expect(rFoo2.gid).toBe('glyph-foo2');
+        expect(fs.isF(rFoo2.cb)).toBeTruthy();
+
+        expect(rFoo3.t).toBe('rad');
+        expect(rFoo3.id).toBe('foo-3');
+        expect(rFoo3.rid).toBe('3');
+        expect(rFoo3.gid).toBe('glyph-foo3');
+        expect(fs.isF(rFoo3.cb)).toBeTruthy();
+
+        expect(rBar0.t).toBe('rad');
+        expect(rBar0.id).toBe('bar-0');
+        expect(rBar0.rid).toBe('0');
+        expect(rBar0.gid).toBe('glyph-bar0');
+        expect(fs.isF(rBar0.cb)).toBeTruthy();
+
+        expect(rBar1.t).toBe('rad');
+        expect(rBar1.id).toBe('bar-1');
+        expect(rBar1.rid).toBe('1');
+        expect(rBar1.gid).toBe('glyph-bar1');
+        expect(fs.isF(rBar1.cb)).toBeTruthy();
     });
 
     it("should verify separator's returned object", function () {
@@ -74,4 +162,106 @@
         expect(separator.t).toBe('sep');
     });
 
+    it('should log a warning if btn id is already in use', function () {
+        var tools = [
+            tbs.makeButton('id0', 'glyph-id0', function () {}),
+            tbs.makeButton('id0', 'glyph-id0', function () {})
+        ];
+
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('someId', tools);
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: item with id ' +
+                                                'id0 already exists');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should log a warning if tog id is already in use', function () {
+        var tools = [
+            tbs.makeToggle('id0', 'glyph-id0', function () {}),
+            tbs.makeToggle('id1', 'glyph-id1', function () {}),
+            tbs.makeToggle('id0', 'glyph-id0', function () {})
+        ];
+
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('someId', tools);
+        expect(tbar).toBeNull();
+        expect($log.warn).toHaveBeenCalledWith('createToolbar: item with id ' +
+                                                'id0 already exists');
+        expect(toolbarSelection().size()).toBe(0);
+    });
+
+    it('should create a toolbar', function () {
+        // need to create a button so it does not throw errors
+        var tools = [
+            tbs.makeButton('btn0', 'glyph0', function () {})
+        ];
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('test', tools);
+        expect($log.warn).not.toHaveBeenCalled();
+        expect(toolbarSelection().size()).toBe(1);
+    });
+
+    it('should test multiple separators in a row', function () {
+        var tools = [
+            tbs.separator(),
+            tbs.separator(),
+            tbs.separator()
+        ];
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('test', tools);
+        expect($log.warn).not.toHaveBeenCalled();
+        expect(toolbarSelection().size()).toBe(1);
+    });
+
+    it('should create a button div', function () {
+        var tools = [
+            tbs.makeButton('btn0', 'glyph0', function () {})
+        ];
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('test', tools);
+        expect($log.warn).not.toHaveBeenCalled();
+        expect(toolbarSelection().size()).toBe(1);
+
+        expect(d3Elem.select('.btn')).toBeTruthy();
+    });
+
+    it('should create a toggle div', function () {
+        var tools = [
+            tbs.makeToggle('tog0', 'glyph0', function () {})
+        ];
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('test', tools);
+        expect($log.warn).not.toHaveBeenCalled();
+        expect(toolbarSelection().size()).toBe(1);
+
+        expect(d3Elem.select('.tog')).toBeTruthy();
+    });
+
+    it('should create a radio btn div', function () {
+        var tools = [
+            tbs.makeRadio('rad0', 'glyph0', function () {})
+        ];
+        spyOn($log, 'warn');
+        var tbar = tbs.createToolbar('test', tools);
+        expect($log.warn).not.toHaveBeenCalled();
+        expect(toolbarSelection().size()).toBe(1);
+
+        expect(d3Elem.select('.rad')).toBeTruthy();
+    });
+
+
+    it('should create a separator div', function () {
+        var tools = [
+            tbs.separator()
+        ];
+        tbs.createToolbar('test', tools);
+
+        var sepDiv = d3Elem.select('.sep');
+        expect(sepDiv).toBeTruthy();
+        expect(sepDiv.style('width')).toBe('2px');
+        expect(sepDiv.style('border-width')).toBe('1px');
+        expect(sepDiv.style('border-style')).toBe('solid');
+    });
+
 });