Add 'setWidget' action for plugin registration

Change-Id: I1ecdc14b7f906e2d3e15f3775af1dc98fd5bf2f8
diff --git a/Changes b/Changes
index bd74833..283e582 100755
--- a/Changes
+++ b/Changes
@@ -21,6 +21,7 @@
           assets.
         - Add associates() method to state objects.
         - Add minimize() method to views.
+        - Add "setWidget" action for plugin registration.
 
         WARNING: If you relied on the former default API endpoint
           being http://localhost:9999/, this will break your
diff --git a/dev/demo/export.js b/dev/demo/export.js
index ef1c313..3db7bf4 100644
--- a/dev/demo/export.js
+++ b/dev/demo/export.js
@@ -31,7 +31,7 @@
       'icon' : "\uf019",
       'classes' : [ 'button-icon', 'plugin' ],
       'onClick' : {
-        'action' : 'addWidget',
+        'action' : 'setWidget',
         'template' : 'http://localhost:7777/export'
       }
     } ]
diff --git a/dev/js/spec/pluginSpec.js b/dev/js/spec/pluginSpec.js
index cabeae9..36d6f64 100644
--- a/dev/js/spec/pluginSpec.js
+++ b/dev/js/spec/pluginSpec.js
@@ -150,6 +150,88 @@
       expect(manager.buttonGroup('result').length).toEqual(1);
       manager.destroy();
     });
+
+    it('should accept valid registrations for addWidget', function () {
+      let p = KorAP.Panel["result"] = panelClass.create();
+      
+      let manager = pluginServerClass.create();
+
+      manager.register({
+        name : 'Check',
+        embed : [{
+          panel : 'result',
+          title : 'Add',
+          onClick : {
+            template : 'about:blank',
+            action : 'addWidget'
+          }
+        }]
+      });
+
+      let b = p.actions.element().firstChild;
+      expect(b.hasAttribute("data-icon")).toBeFalsy();
+      expect(b.hasAttribute("cls")).toBeFalsy();
+      expect(b.getAttribute("title")).toEqual("Add");
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(0);
+
+      b.click();
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(1);
+      expect(p.element().querySelectorAll("div.view.widget").length).toEqual(1);
+      expect(p.element().querySelectorAll("div.view.show.widget").length).toEqual(1);
+
+      b.click();
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(2);
+      expect(p.element().querySelectorAll("div.view.widget").length).toEqual(2);
+      expect(p.element().querySelectorAll("div.view.show.widget").length).toEqual(2);
+      
+      manager.destroy();
+
+      KorAP.Panel["result"] = undefined;
+    });
+
+
+    it('should accept valid registrations for setWidget', function () {
+      let p = KorAP.Panel["result"] = panelClass.create();
+      
+      let manager = pluginServerClass.create();
+
+      manager.register({
+        name : 'Check',
+        embed : [{
+          panel : 'result',
+          title : 'Add',
+          onClick : {
+            template : 'about:blank',
+            action : 'setWidget'
+          }
+        }]
+      });
+
+      let b = p.actions.element().firstChild;
+      expect(b.hasAttribute("data-icon")).toBeFalsy();
+      expect(b.hasAttribute("cls")).toBeFalsy();
+      expect(b.getAttribute("title")).toEqual("Add");
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(0);
+
+      b.click();
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(1);
+      expect(p.element().querySelectorAll("div.view.show.widget").length).toEqual(1);
+
+      b.click();
+
+      expect(p.element().querySelectorAll("iframe").length).toEqual(1);
+      expect(p.element().querySelectorAll("div.view.widget").length).toEqual(1);
+      expect(p.element().querySelectorAll("div.view.show.widget").length).toEqual(0);
+      
+      manager.destroy();
+
+      KorAP.Panel["result"] = undefined;
+    });
   });
   
   describe('KorAP.Plugin.Widget', function () {
diff --git a/dev/js/src/buttongroup.js b/dev/js/src/buttongroup.js
index e71d082..65ea860 100644
--- a/dev/js/src/buttongroup.js
+++ b/dev/js/src/buttongroup.js
@@ -59,9 +59,13 @@
           b.classList.add.apply(b.classList, data['cls']);
         };
      
-        if (data['icon'] !== undefined){ 
+        if (data['icon'] !== undefined) { 
           b.setAttribute('data-icon', data['icon']);
         };
+
+        if (data['state'] !== undefined) {
+          b['state'] = data['state'];
+        }
       };
      
       b.addE('span').addT(title);
@@ -102,6 +106,12 @@
      * Add button that can toggle a state.
      * The state has to be a state object.
      */
+    /*
+     * TODO:
+     *   Do not add a state object here, but embed the
+     *   state in the data and "roll()" through the different
+     *   state options instead.
+     */
     addToggle : function (title, data, state) {
       let b = this._element.addE('span');
       b.setAttribute('title',title);
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
index f862770..ce36354 100644
--- a/dev/js/src/plugin/server.js
+++ b/dev/js/src/plugin/server.js
@@ -136,44 +136,85 @@
         let icon = embed["icon"];
         
         if (!panel || !(buttons[panel] || buttonsSingle[panel]))
-          throw new Error("Panel for plugin is invalid");        
+          throw new Error("Panel for plugin is invalid"); 
 
         // The embedding will open a widget
-        if (!onClick["action"] || onClick["action"] == "addWidget") {
-
+        if (!onClick["action"] ||
+            onClick["action"] == "addWidget" ||
+            onClick["action"] == "setWidget") {
           
           let cb = function (e) {
 
             // "this" is bind to the panel
+            // "this".button is the button
+            // "that" is the server object
 
             // Get the URL of the widget
             let url = onClick["template"];
             // that._interpolateURI(onClick["template"], this.match);
 
+            // The button has a state and the state is associated to the
+            // a intermediate object to toggle the view
+            if ('state' in this.button && this.button.state.associates() > 0) {
+
+              // TODO:
+              //   Use roll() when existing
+              let s = this.button.state;
+              if (s.get()) {
+                s.set(false);
+              } else {
+                s.set(true);
+              };
+              return;
+            };
+
             // Add the widget to the panel
             let id = that.addWidget(this, name, url);
             plugin["widgets"].push(id);
+            
+            // If a state exists, associate with a mediator object
+            if ('state' in this.button) {
+              this.button.state.associate({
+                setState : function (value) {
+                  // Minimize the widget
+                  if (value == false) {
+                    services[id].minimize();
+                  }
+                  else {
+                    services[id].show();                    
+                  };
+                }
+              });
+            }
           };
 
-          // TODO:
-          //   Create button class to be stored and loaded in button groups!
 
+          // Button object
+          let obj = {'cls':embed["classes"], 'icon': icon }
+
+          if (onClick["action"] && onClick["action"] == "setWidget") {
+
+            // Create a boolean state value, that initializes to true == opened
+            obj['state'] = stateClass.create(true);
+          };
+          
           // Add to dynamic button list (e.g. for matches)
           if (buttons[panel]) {
-            buttons[panel].push([title, {'cls':embed["classes"], 'icon': icon }, cb]);
+            buttons[panel].push([title, obj, cb]);
           }
 
           // Add to static button list (e.g. for query) already loaded
           else if (KorAP.Panel[panel]) {
-            KorAP.Panel[panel].actions.add(title, {'cls':embed["classes"], 'icon':icon}, cb);
+            KorAP.Panel[panel].actions.add(title, obj, cb);
           }
 
           // Add to static button list (e.g. for query) not yet loaded
           else {
-            buttonsSingle[panel].push([title, {'cls':embed["classes"], 'icon':icon}, cb]);
+            buttonsSingle[panel].push([title, obj, cb]);
           }
         }
-        //TODO There is no possibility to add icons to an plugin toggle button right now. 
+
+        // TODO There is no possibility to add icons to an plugin toggle button right now. 
         else if (onClick["action"] == "toggle") {
 
           // Todo: Initially false
@@ -288,13 +329,9 @@
       // Create a new service
       let service = serviceClass.create(name, src, id);
       
-      // TODO!
-      // Store the service based on the identifier
       services[id] = service;
       limits[id] = maxMessages;
 
-      // widget._mgr = this;
-
       // Add service to panel
       this.element().appendChild(
         service.load()
diff --git a/package.json b/package.json
index d05776c..2b0888e 100755
--- a/package.json
+++ b/package.json
@@ -2,8 +2,8 @@
   "name": "Kalamar",
   "description": "Mojolicious-based Frontend for KorAP",
   "license": "BSD-2-Clause",
-  "version": "0.39.2",
-  "pluginVersion": "0.2.1",
+  "version": "0.39.3",
+  "pluginVersion": "0.2.2",
   "engines": {
     "node": ">=6.0.0"
   },