Add role() method to state object

Change-Id: Ie98f15c83d94c6cb1915942bba6c2ba17ae321e0
diff --git a/Changes b/Changes
index 75dc6b0..470ead6 100755
--- a/Changes
+++ b/Changes
@@ -1,5 +1,6 @@
 0.40 2020-10-09
         - Modernize ES and fix in-loops.
+        - add roll() method to state object.
 
 0.39 2020-10-07
         - Add information on secret file to Readme.
diff --git a/dev/js/spec/stateSpec.js b/dev/js/spec/stateSpec.js
index 4f71859..fec337c 100644
--- a/dev/js/spec/stateSpec.js
+++ b/dev/js/spec/stateSpec.js
@@ -103,5 +103,24 @@
       expect(obj1.x).toBeFalsy();
       expect(obj2.x).toBeFalsy();
     });
+
+    it('should roll', function () {
+      let s = stateClass.create(['der','alte','mann']);
+
+      expect(s.get()).toEqual('der');
+      s.roll();
+      expect(s.get()).toEqual('alte');
+      s.roll();
+      expect(s.get()).toEqual('mann');
+      s.roll();
+      expect(s.get()).toEqual('der');
+      s.roll();
+      expect(s.get()).toEqual('alte');
+
+      s.set('alte');
+      expect(s.get()).toEqual('alte');
+      s.roll();
+      expect(s.get()).toEqual('mann');
+    });
   });
 });
diff --git a/dev/js/src/buttongroup.js b/dev/js/src/buttongroup.js
index 65ea860..29453c3 100644
--- a/dev/js/src/buttongroup.js
+++ b/dev/js/src/buttongroup.js
@@ -106,12 +106,6 @@
      * 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);
@@ -147,11 +141,7 @@
         e.halt();
         
         // Toggle state
-        if (state.get()) {
-          state.set(false)
-        } else {
-          state.set(true);
-        }
+        state.roll();
       });
 
       return b;
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
index e47cfbf..96b5cb7 100644
--- a/dev/js/src/plugin/server.js
+++ b/dev/js/src/plugin/server.js
@@ -158,14 +158,7 @@
 
               // The associated service is existent
               if (services[this.button['widgetID']]) {
-              
-                // TODO:
-                //   Use roll() when existing
-                if (s.get()) {
-                  s.set(false);
-                } else {
-                  s.set(true);
-                };
+                s.roll();
                 return;
               }
 
@@ -211,7 +204,7 @@
           if (onClick["action"] && onClick["action"] == "setWidget") {
 
             // Create a boolean state value, that initializes to true == opened
-            obj['state'] = stateClass.create(true);
+            obj['state'] = stateClass.create([true, false]);
           };
           
           // Add to dynamic button list (e.g. for matches)
@@ -233,8 +226,12 @@
         // TODO There is no possibility to add icons to an plugin toggle button right now. 
         else if (onClick["action"] == "toggle") {
 
-          // Todo: Initially false
-          let state = stateClass.create(false);
+          // TODO:
+          //   Accept a "value" list here for toggling, which should
+          //   also allow for "rolling" through states via CSS classes
+          //   as 'toggle-true', 'toggle-false' etc.
+
+          let state = stateClass.create([true, false]);
 
           // TODO:
           //   Lazy registration (see above!)
diff --git a/dev/js/src/state.js b/dev/js/src/state.js
index a5ab7d8..69e2d54 100644
--- a/dev/js/src/state.js
+++ b/dev/js/src/state.js
@@ -8,9 +8,9 @@
  */
 /*
  * TODO:
- *   Add a "roll" parameter, like "roll":["left","right","center"]
- *   and a roll() method, that will switch through the states in the list
- *   for flexible toggling.
+ *   Require names for states, that should be quite short, so they
+ *   can easily be serialized and kept between page turns (via cookie
+ *   and/or query param)
  */
 define(function () {
 
@@ -26,13 +26,21 @@
     },
 
     // Initialize
-    _init : function (value) {
+    _init : function (values) {
       this._assoc = [];
-      this.value = value;
+      if (values == undefined) {
+        this.values = [false,true];
+      }
+      else if (Array.isArray(values)) {
+        this.values = values;
+      }
+      else {
+        this.values = [values];
+      }
+      this.value = this.values[0];
       return this;
     },
 
-
     /**
      * Associate the state with some objects.
      */
@@ -78,6 +86,21 @@
      */
     clear : function () {
       return this._assoc = [];
+    },
+
+    /**
+     * Roll to the next value.
+     * This may be used for toggling.
+     */
+    roll : function () {
+      let next = 0;
+      for (let i = 0; i < this.values.length - 1; i++) {
+        if (this.value == this.values[i]) {
+          next = i+1;
+          break;
+        };
+      };
+      this.set(this.values[next]);
     }
   }
 });
diff --git a/package.json b/package.json
index 9b7a8a5..c23ee60 100755
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "Kalamar",
   "description": "Mojolicious-based Frontend for KorAP",
   "license": "BSD-2-Clause",
-  "version": "0.40.0",
+  "version": "0.40.1",
   "pluginVersion": "0.2.2",
   "engines": {
     "node": ">=6.0.0"