Use containerMenu as a base for HintMenu instead of regular menu

Change-Id: Ic1ed2e216a9c61aabf1f1cac41972b1a4e96a91a
diff --git a/Changes b/Changes
index a0f6a46..e5508c5 100755
--- a/Changes
+++ b/Changes
@@ -46,6 +46,8 @@
         - Fix wrong handling of utf8 input in login that can
           lead to server crash.
         - Fix query-by-match for multiple colons.
+        - HintMenu is now based on containerMenu instead of regular
+          menu (lerepp)
 
 0.42 2021-06-18
         - Added GitHub based CI for perl.
diff --git a/dev/demo/containermenudemo.js b/dev/demo/containermenudemo.js
index e3b0f99..dfb4df3 100644
--- a/dev/demo/containermenudemo.js
+++ b/dev/demo/containermenudemo.js
@@ -24,8 +24,9 @@
     },
 
     // enter or click
-    onclick : function () {
+    onclick : function (event) {
       console.log(this._name);
+      event.halt();
     },
 
     // right arrow
@@ -56,6 +57,7 @@
         .upgradeTo(this);
         //._init();
       obj.value="";
+      obj.defaultTextValue = "CI";
       return obj;
     },
     add : function (letter) {
@@ -67,25 +69,18 @@
     further : function () {
       this.value = this.value + this.value;
     },
+    isSelectable : function () {
+      return (this.value !== "");
+    },
+    chop : function () {
+      console.log("chop");
+      console.log(this.content(this.value));
+    },
     onclick : function () {
-      console.log('ContainerItem' + this.value);
+      console.log('ContainerItem ' + this.value);
       console.log(this._i);
       this._menu.limit(this._i);
       this._menu.show();
-    },
-    element : function () {
-      // already defined
-      if (this._el !== undefined) return this._el;
-      
-      // Create list item
-      const li = document.createElement("li");
-      li.innerHTML="CI";
-  
-      // Connect action
-      if (this["onclick"] !== undefined) {
-        li["onclick"] = this.onclick.bind(this);
-      };    
-      return this._el = li;
     }
   };
   //List of items.
@@ -224,6 +219,8 @@
   document.getElementById('menu').appendChild(menu.element());
   //document.getElementById('largemenu').appendChild(largeMenu.element());
 
+  menu.container().addItem({ value : "Dynamically added", defaultTextValue : "dynamic", _i : 5})
+
   menu.limit(3).show(3);
   menu.focus();
   
diff --git a/dev/demo/hintdemo.js b/dev/demo/hintdemo.js
index 413625c..4f2e12d 100644
--- a/dev/demo/hintdemo.js
+++ b/dev/demo/hintdemo.js
@@ -15,6 +15,14 @@
 
     KorAP.Hint = hintClass.create();
 
+    var newItem = {
+      onclick : function (e) { console.log("CI click"); console.log(this.element()); console.log("'" + this._menu.prefix() + "'" + " <-- If this returns the empty string when\
+ it should not, we know that the click event listener in container.js gets called too early and overwrites the prefix before we can read it.");},
+      chop : function () { console.log("chop"); },
+      defaultTextValue : "Text Example"
+    };
+    KorAP.Hint.menu("-").container().addItem(newItem); //must be added to a specific context menu.
+
     /**
      * Add query panel
      */
@@ -69,10 +77,13 @@
         }
       }]
     });
+    
+    console.log(KorAP.Hint);
   });
 });
 
 function demoAlert (pos, msg) {
   if (KorAP.hint !== undefined)
     KorAP.Hint.alert(pos, msg);
+    console.log(KorAP.Hint);
 }
diff --git a/dev/demo/querystoring.html b/dev/demo/querystoring.html
new file mode 100644
index 0000000..e1d51fd
--- /dev/null
+++ b/dev/demo/querystoring.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Hint demo</title>
+    <meta charset="utf-8" />
+    <script data-main="querystoringdemo.js" src="../js/lib/require.js" async="async"></script>
+    <link type="text/css" rel="stylesheet" href="../css/kalamar.css" />
+  </head>
+  <body>
+    <header>
+      <form id="searchform" autocomplete="off" action="/kalamar">
+	<div id="searchbar">
+	  <input type="search"
+		 placeholder="Find ..."
+		 name="q"
+		 id="q-field"
+		 value="mbfhjgfghfhgfhfghfghnhvrthrjvztrhhrnvnthvft"
+		 autofocus="autofocus" />
+	  <button type="submit"><span>Go</span></button>
+	</div>
+  <div id="vc-view"></div>
+      </form>
+    </header>
+
+    <a onclick="demoAlert(14, 'This is a warning')">Alert at 14</a>
+  </body>
+</html>
diff --git a/dev/demo/querystoringdemo.js b/dev/demo/querystoringdemo.js
new file mode 100644
index 0000000..6991865
--- /dev/null
+++ b/dev/demo/querystoringdemo.js
@@ -0,0 +1,184 @@
+requirejs.config({
+  baseUrl: '../js/src',
+  paths : {
+    'lib': '../lib'
+  }
+});
+
+var hint = undefined;
+
+require(['plugin/server','panel/query', 'hint','hint/foundries/cnx','lib/domReady','api'], function (pluginClass, queryPanelClass, hintClass, hintArray, domReady) {
+  KorAP.hintArray = hintArray;
+  KorAP.Hint = null;
+  
+  domReady(function() {
+    KorAP.URL="http://localhost:3000"
+    //console.log(KorAP.URL=http://localhost:3003);
+
+    KorAP.Hint = hintClass.create();
+
+    KorAP.restoreMinusMenuButton = { // Whenever used is at location 0
+      defaultTextValue : "Back",
+      onclick : function (e) {
+        this._menu.hint().unReadQueries();
+        //e.halt();
+      }
+    };
+
+    KorAP.triggerDeleteModeButton = { // Whenever used is at location 1
+      defaultTextValue : "Delete Queries",
+      onclick : function (e) {
+        if (this._menu.hint()._queryReadMode === "READ") {
+          this._menu.hint()._deleteTheseQueries = [];
+          this.content("Delete Selected Queries");
+          this._menu.hint()._queryReadMode = "DELETE";
+
+
+        } else if (this._menu.hint()._queryReadMode === "DELETE") {
+          this._menu.hint()._deleteTheseQueries.forEach(function (query){
+            KorAP.API.deleteQuery(qn,console.log);
+          },undefined);
+          this._menu.hint()._deleteTheseQueries = [];
+          this._menu.hint()._queryReadMode = "READ";
+          this.content("Delete Queries");
+
+        } else {
+          //ERROR
+          console.log("What?");
+        };
+        this.menu("-").show();
+        //e.halt();
+      }
+    };
+
+    JSONListToQueryTemplates= function(JSONList) {
+      var queryItemTemplates = [];
+      JSONList.forEach(function(query){ //I need to see what this actually looks like!
+        queryItemTemplates.push([query["qname"],query["q"],query["desc"]])
+      }, undefined); //no "this" needed
+      return queryItemTemplates
+    };
+
+
+    KorAP.SaveButton = { // Whenever used is at location 0
+      onclick : function (e) {
+        var query = {}
+        query.q = "this._menu._hint._inputField._el.value";
+        query.ql = "a";
+        query.desc = "b";
+        KorAP.API.putQuery("query1",query,console.log);
+        this.menu("-").show();
+        //e.halt();
+      },
+      defaultTextValue : "Save Query"
+    };
+
+    KorAP.DisplayQueriesButton = { // Whenever used is at location 1
+      onclick : function (e) {
+        KorAP.API.getQueryList(KorAP.Hint.readQueries,console.log);
+        this.menu("-").show();
+        //e.halt();
+      },
+      defaultTextValue : "Display Stored Queries"
+    };
+
+
+    KorAP.Hint.menu("-").container().addItem(KorAP.SaveButton); //must be added to a specific context menu.
+    KorAP.Hint.menu("-").container().addItem(KorAP.DisplayQueriesButton); //must be added to a specific context menu.
+
+    KorAP.Hint.readQueries = function (JSONList) {
+      console.log(JSONList);
+      //Need to check what JSONList looks like
+      // 0: _name
+      // 1: _action (query)
+      // 2: _desc
+      var queryItemTemplates = JSONListToQueryTemplates(JSONList);
+      queryItemTemplates.push(["ExampleName","value","descr"]);
+      this.menu("-").readItems(queryItemTemplates);
+      this._queryReadMode = "READ"; //alternative: "DELETE"
+      this._deleteTheseQueries = [];
+      // add first, remove later to avoid empty lists
+      this.menu("-").container().addItem(KorAP.restoreMinusMenuButton); 
+      this.menu("-").container().addItem(KorAP.triggerDeleteModeButton);
+      this.menu("-").container().removeItemByIndex(0); //Save Button
+      this.menu("-").container().removeItemByIndex(0); //Display Button
+      this.menu("-").show();
+    }.bind(KorAP.Hint);
+
+    KorAP.Hint.unReadQueries = function () {
+      KorAP.log("unread");
+      this.menu("-").readItems(KorAP.annotationHelper["-"]);
+      this._queryReadMode = "READ"; //alternative: "DELETE"
+      this._deleteTheseQueries = [];
+      // add first, remove later to avoid empty lists
+      this.menu("-").container().addItem(KorAP.SaveButton);
+      this.menu("-").container().addItem(KorAP.DisplayQueriesButton);
+      this.menu("-").container().removeItemByIndex(0); //restoreMinusMenuButton
+      this.menu("-").container().removeItemByIndex(0); //triggerDeleteModeButton
+      this.menu("-").show();
+
+    }.bind(KorAP.Hint);
+
+    /**
+     * Add query panel
+     */
+    var queryPanel = queryPanelClass.create();
+
+    // Get input field
+    var sform = document.getElementById("searchform");
+    var vcView = document.getElementById('vc-view')
+    if (sform && vcView) {
+      // The views are below the query bar
+      sform.insertBefore(queryPanel.element(),vcView);
+      KorAP.Panel = KorAP.Panel || {};
+      KorAP.Panel['query'] = queryPanel;
+    }
+
+    // Load plugin server
+    KorAP.Plugin = pluginClass.create();
+    KorAP.Plugin.register
+    // Register match plugin
+    KorAP.Plugin.register({
+      'name' : 'Example New',
+      'desc' : 'Some content about cats',
+      'embed' : [{
+        'panel' : 'query',
+        'title' : 'Translate',
+        'classes' : ['translate'],
+        'onClick' : {
+          "template" : "http://localhost:3003/demo/plugin-client.html"
+        }
+      },{
+        'panel' : 'query',
+        'title' : 'Glemm',
+        'classes' : ['glemm'],
+        'onClick' : {
+          "action":"toggle",
+          "template" : "http://localhost:3003/demo/plugin-client.html"
+        }
+      }]
+    });
+
+    // Register match plugin
+    KorAP.Plugin.register({
+      'name' : 'Glimpse',
+      'desc' : 'Shorten all queries',
+      'embed' : [{
+        'panel' : 'query',
+        'title' : 'Glimpse',
+        'classes' : ['glimpse'],
+        'onClick' : {
+          "action" : "toggle",
+          "template" : "http://localhost:3003/demo/plugin-client.html"
+        }
+      }]
+    });
+    
+    console.log(KorAP.Hint);
+  });
+});
+
+function demoAlert (pos, msg) {
+  if (KorAP.hint !== undefined)
+    KorAP.Hint.alert(pos, msg);
+}
diff --git a/dev/js/spec/containerMenuSpec.js b/dev/js/spec/containerMenuSpec.js
index 40d72e0..a6d85b7 100644
--- a/dev/js/spec/containerMenuSpec.js
+++ b/dev/js/spec/containerMenuSpec.js
@@ -45,31 +45,13 @@
         this.value = this.value + this.value;
       },
       onclick : function () {
-      },
-      element : function () {
-        // already defined
-        if (this._el !== undefined) return this._el;
-        
-        // Create list item
-        const li = document.createElement("li");
-        li.innerHTML="CI";
-    
-        // Connect action
-        if (this["onclick"] !== undefined) {
-          li["onclick"] = this.onclick.bind(this);
-        };    
-        return this._el = li;
       }
     };
 
     //List of items.
     var ExampleItemList = new Array;
-    ExampleItemList.push(OwnContainerItemClass.create());
-    ExampleItemList.push(OwnContainerItemClass.create());
-    ExampleItemList[0].value = "CIValue1 ";
-    ExampleItemList[1].value = "CIValue2 ";
-    ExampleItemList[0].element().innerHTML = "CIText1 ";
-    ExampleItemList[1].element().innerHTML = "CIText2 ";
+    ExampleItemList.push({value : "CIValue1" , defaultTextValue : "CIText1 "});
+    ExampleItemList.push({value : "CIValue2" , defaultTextValue : "CIText2 "});
 
     //Own container class.
     const OwnContainerClass = {
@@ -228,9 +210,9 @@
         expect(liElements[2].classList.contains("no-more")).toBe(false);
 
         var items = menu.container().items;
-        expect(items[0].element().innerHTML).toEqual("CIText1 ");
-        expect(items[1].element().innerHTML).toEqual("CIText2 ");
-        expect(items[2].element().innerHTML).toEqual("");
+        expect(items[0].content().nodeValue).toEqual("CIText1 ");
+        expect(items[1].content().nodeValue).toEqual("CIText2 ");
+        expect(items[2].element().innerHTML).toEqual(""); //prefix!
         expect(items[3]).toBe(undefined);
       });
 
@@ -2115,11 +2097,11 @@
         expect(container.element().nodeName).toEqual("UL");
         expect(container.element().classList.contains("menu")).toBeTruthy();
         expect(container.element().classList.contains("visible")).toBeFalsy();
-        expect(menu._prefix).toEqual(container._prefix);
+        expect(menu._prefix).toEqual(container._cItemPrefix);
         expect(container.length()).toEqual(1);
         expect(container.length()).toEqual(container.items.length);
         expect(container.liveLength()).toEqual(0);
-        expect(container.item(0)).toEqual(container._prefix);
+        expect(container.item(0)).toEqual(container._cItemPrefix);
         expect(container.active()).toBeFalsy();
         expect(directElementChildrenByTagName(menu.element(),"pref")).toEqual([]);
         expect(container.element().getElementsByClassName("pref").length).toEqual(1);
@@ -2132,5 +2114,191 @@
         expect(container.active()).toBeTruthy();
       });
 
+      it("should support dynamic changing of text content", function () {
+        var list = [
+          ["Constituency"],
+          ["Lemma"],
+          ["Morphology"],
+          ["Part-of-Speech"],
+          ["Syntax"]
+        ];
+        
+        var ExampleItemList2 = new Array;
+        ExampleItemList2.push({defaultTextValue : "CIText1 "});
+        ExampleItemList2.push({});
+        
+        var menu = OwnContainerMenu.create(list,ExampleItemList2);
+        var container = menu.container();
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(0).content("New1").nodeValue).toEqual("New1");
+        expect(container.item(1).content("New2").nodeValue).toEqual("New2");
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        
+        
+      });
+
+      it("should support dynamic adding of items", function () {
+
+        var list = [
+          ["Constituency"],
+          ["Lemma"],
+          ["Morphology"],
+          ["Part-of-Speech"],
+          ["Syntax"]
+        ];
+        
+        var ExampleItemList2 = new Array;
+        ExampleItemList2.push({defaultTextValue : "CIText1 "});
+        ExampleItemList2.push({});
+        
+        var menu = OwnContainerMenu.create(list,ExampleItemList2);
+        var container = menu.container();
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container.item(2)).toBeDefined();
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        container.addItem({defaultTextValue : "CIText2 "});
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2).content().nodeValue).toEqual("CIText2 ");
+        expect(container.item(3)).toBeDefined();
+        expect(container.item(3).content()).toEqual(undefined);
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        container.add("a");
+
+        menu.next();
+        menu.next();
+        menu.next();
+        menu.next();
+        menu.next();
+        expect(container.item(0).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(1).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(2).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(3).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(3).active()).toBeFalsy();
+        menu.prev();
+        expect(container.item(3).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(2).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(1).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(0).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(0).active()).toBeFalsy();
+
+        
+        
+      });
+
+      it("should support dynamic changing of text content", function () {
+        var list = [
+          ["Constituency"],
+          ["Lemma"],
+          ["Morphology"],
+          ["Part-of-Speech"],
+          ["Syntax"]
+        ];
+        
+        var ExampleItemList2 = new Array;
+        ExampleItemList2.push({defaultTextValue : "CIText1 "});
+        ExampleItemList2.push({});
+        
+        var menu = OwnContainerMenu.create(list,ExampleItemList2);
+        var container = menu.container();
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(0).content("New1").nodeValue).toEqual("New1");
+        expect(container.item(1).content("New2").nodeValue).toEqual("New2");
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        expect(container.item(2)).toBeDefined();
+        expect(container.item(2).content()).toEqual(undefined);
+        
+        
+      });
+
+      it("should support dynamic adding of items", function () {
+
+        var list = [
+          ["Constituency"],
+          ["Lemma"],
+          ["Morphology"],
+          ["Part-of-Speech"],
+          ["Syntax"]
+        ];
+        
+        var ExampleItemList2 = new Array;
+        ExampleItemList2.push({defaultTextValue : "CIText1 "});
+        ExampleItemList2.push({});
+        
+        var menu = OwnContainerMenu.create(list,ExampleItemList2);
+        var container = menu.container();
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2).content()).toEqual(undefined);
+        expect(container.item(2)).toBeDefined();
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        container.addItem({defaultTextValue : "CIText2 "});
+        expect(container.item(0).content().nodeValue).toEqual("CIText1 ");
+        expect(container.item(1).content().nodeValue).toEqual("");
+        expect(container.item(2).content().nodeValue).toEqual("CIText2 ");
+        expect(container.item(3)).toBeDefined();
+        expect(container.item(3).content()).toEqual(undefined);
+        expect(container._cItemPrefix.element().innerHTML).toEqual("");
+        container.add("a");
+
+        menu.next();
+        menu.next();
+        menu.next();
+        menu.next();
+        menu.next();
+        expect(container.item(0).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(1).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(2).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(3).active()).toBeTruthy();
+        menu.next();
+        expect(container.item(3).active()).toBeFalsy();
+        menu.prev();
+        expect(container.item(3).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(2).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(1).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(0).active()).toBeTruthy();
+        menu.prev();
+        expect(container.item(0).active()).toBeFalsy();
+
+        
+        
+      });
+
     });
   });
diff --git a/dev/js/spec/hintSpec.js b/dev/js/spec/hintSpec.js
index 3e95e55..e81033d 100644
--- a/dev/js/spec/hintSpec.js
+++ b/dev/js/spec/hintSpec.js
@@ -272,7 +272,6 @@
       hint.inputField().insert('der Baum corenlp/');
 
       var cont = hint.inputField().container();
-      
       expect(cont.getElementsByTagName('div').length).toBe(1);
       expect(cont.getElementsByTagName('ul').length).toBe(0);
       expect(cont.firstChild).toEqual(cont.firstChild);
@@ -281,34 +280,34 @@
       // There is a menu for corenlp/
       hint.show(false);
 
-      expect(hint.inputField().container().getElementsByTagName('ul').length).toEqual(1);
-      expect(hint.inputField().container().getElementsByTagName('li').length).toEqual(3);
+      expect(cont.getElementsByTagName('ul').length).toEqual(1+1); //+1 from containermenu (see container/container.js)
+      expect(cont.getElementsByTagName('li').length).toEqual(3);
       
       // Hide the menu and focus on the input
       hint.unshow();
       
-      expect(hint.inputField().container().getElementsByTagName('div').length).toEqual(1);
-      expect(hint.inputField().container().getElementsByTagName('li').length).toEqual(0);
+      expect(cont.getElementsByTagName('div').length).toEqual(1);
+      expect(cont.getElementsByTagName('li').length).toEqual(0);
 
       hint.unshow();
 
       hint.inputField().insert(' hhhh');
 
-      // show with context
+      // show with context if possible
       hint.show(false);
       
-      expect(hint.inputField().container().getElementsByTagName('div').length).toEqual(4);
-      expect(hint.inputField().container().getElementsByTagName('ul').length).toEqual(1);
-      expect(hint.inputField().container().getElementsByTagName('li').length).toEqual(2);
+      expect(cont.getElementsByTagName('div').length).toEqual(4);
+      expect(cont.getElementsByTagName('ul').length).toEqual(1+1);//+1 from containermenu (see container/container.js)
+      expect(cont.getElementsByTagName('li').length).toEqual(2);
 
       hint.unshow();
       hint.inputField().insert(' aaaa/');
 
-      // show with context
+      // show with context necessarily
       hint.show(true);
 
-      expect(hint.inputField().container().getElementsByTagName('div').length).toEqual(1);
-      expect(hint.inputField().container().getElementsByTagName('ul').length).toEqual(0);
+      expect(cont.getElementsByTagName('div').length).toEqual(1);
+      expect(cont.getElementsByTagName('ul').length).toEqual(0); //here not +1: context doesnt fit
     });
 
 
@@ -324,25 +323,25 @@
       hint.show(false);
 
       expect(hint.active()).toBeTruthy();
-      
-      expect(hint.inputField().container().getElementsByTagName('li')[0].firstChild.innerText).toEqual("Base Annotation");
+      var cont = hint.inputField().container();
+      expect(cont.getElementsByTagName('li')[0].firstChild.innerText).toEqual("Base Annotation");
 
       // Type in prefix
       hint.active().prefix("cor").show();
       expect(hint.active().prefix()).toEqual("cor");
 
       // Click first step
-      expect(hint.inputField().container().getElementsByTagName('li')[0].firstChild.firstChild.innerText).toEqual("Cor");
-      hint.inputField().container().getElementsByTagName('li')[0].click();
+      expect(cont.getElementsByTagName('li')[0].firstChild.firstChild.innerText).toEqual("Cor");
+      cont.getElementsByTagName('li')[0].click();
 
       expect(hint.active()).toBeTruthy();
       
       // Click second step
-      expect(hint.inputField().container().getElementsByTagName('li')[0].firstChild.innerText).toEqual("Named Entity");
-      hint.inputField().container().getElementsByTagName('li')[0].click()
+      expect(cont.getElementsByTagName('li')[0].firstChild.innerText).toEqual("Named Entity");
+      cont.getElementsByTagName('li')[0].click()
 
       // Invisible menu
-      expect(hint.inputField().container().getElementsByTagName('li')[0]).toBeUndefined();
+      expect(cont.getElementsByTagName('li')[0]).toBeUndefined();
 
       // Inactive menu
       expect(hint.active()).toBeFalsy();
@@ -420,10 +419,16 @@
 
       // Type in prefix
       hint.active().prefix("cor").show();
+      expect(hint.active()._prefix.value()).toBe("cor");
       expect(hint.active().prefix()).toEqual("cor");
-
       expect(input.value).toEqual("");
+      expect(hint.active()._prefix["isSelectable"]).not.toBeNull();
+      expect(hint._menuCollection['-']._prefix["isSelectable"]).not.toBeNull();
+      expect(hint.active()._prefix).toBe(hint._menuCollection['-']._prefix);
+      expect(hint.active()._prefix.element()).toBe(hint._menuCollection['-']._prefix.element());
       hint.active()._prefix.element().click();
+      
+      
       expect(input.value).toEqual("cor");
       expect(hint.active()).toBeFalsy();
 
@@ -433,6 +438,7 @@
       
     });
 
+
     
     xit('should remove all menus on escape');
   });
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index dcc84b7..5efdf5a 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -143,8 +143,9 @@
     window.dispatchEvent(reqE);
     
     req.setRequestHeader("Accept", "application/json");
-    req.setRequestHeader("Content-Type", "application/json");
+    req.setRequestHeader("Content-Type", "application/json"); //https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type //this doesnt fit
     req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+    //req.setRequestHeader('Origin',"API");
     req.onreadystatechange = function () {
       /*
 	      States:
diff --git a/dev/js/src/container/container.js b/dev/js/src/container/container.js
index 02d6fba..0936e6c 100644
--- a/dev/js/src/container/container.js
+++ b/dev/js/src/container/container.js
@@ -7,7 +7,7 @@
 
 "use strict";
 define([
-  'container/containeritem'   //TODO why does this not work!!!
+  'container/containeritem'
 ], function (
   defaultContainerItemClass
 ) {
@@ -31,14 +31,31 @@
       } else {
         this._containerItemClass = defaultContainerItemClass;
       };
-      var el = document.createElement("ul");
-      el.style.outline = 0;
-      el.setAttribute('tabindex', 0);
-      el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
+      this._el = document.createElement("ul");
+      this._el.style.outline = 0;
+      this._el.setAttribute('tabindex', 0);
+      this._el.classList.add('menu', 'container'); //container class allows for more stylesheet changes
+      this._el.addEventListener("mousedown", function (e) {
+        // see https://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event
+        e.preventDefault();
+        // It used to be, that clicking on a item within the container (see container.js) would cause the container to gain focus
+        // thanks to mousedown default behaviour, which would mean the actual menu (ul menu roll containermenu hint) would not be in focus (I think? containermenu ul is its child
+        // afterall?). This would cause blur to be called, which (see hint/menu.js) would hide the current menu and its container, causing click to target a location
+        // the containeritem USED to be.
+        //https://w3c.github.io/uievents/#event-type-mousedown
+        //These default actions are thus not supported anymore.
+        
+      }.bind(this));
+      this._el.addEventListener("click", function (e) {
+        this._menu.prefix("");
+        this._menu.hint().inputField().insert("").update();
+        this._menu.element().blur();
+        this._menu.hint().unshow(); //hide the containermenu, not with hide but with blur, because blur would usually happen in default mousedown behaviour
+        e.halt(); // Question: my impression is, that this click event handler is called after all the others and thus this should be absolutely no problem.
+        // Are we sure there are no things that do not happen now thanks to this?
 
-      this._el = el;
-      this._prefix = undefined; //required for re-setting the menus pointer correctly
-      // after having upgraded a new item scss style to the prefix object.
+        //by default, click focuses its target. Maybe that is why e.halt() is necessary? (https://w3c.github.io/uievents/#event-type-click)
+      }.bind(this));
 
       this.items = new Array();
       //items are stored in the order they are added in. This includes the prefix.
@@ -54,24 +71,48 @@
       this._prefixPosition = undefined; //Required so that switching
       // to prefix by default is supported
 
+      this._menu = undefined // add later
+
 
       
       //t._el.classList.add('visible'); //Done by containermenu
+    },
 
-  },
-
+    /**
+     * Adds a static item to this container by creating a standard containerItem as specified when this container was created,
+     * then upgrading it to the item passed to this function, and calling element() and content(). For a full list of supported functions see
+     * containeritem.js .
+     * Example:
+     * 
+     * menu.container().addItem(
+     *  {defaultTextValue : "dynamic", onClick : function (e) { ... }
+     * )
+     * 
+     *  For a full demo see containermenudemo.js.
+     * 
+     * @param {Object} item An object with any number of functions like in containeritem.js or an attribute defaultTextValue,
+     * as well as any number of own properties.
+     * @returns the new use-ready containerItem
+     */
     addItem : function (item) {
+      //Call Order: First _containerItemClass is created and then upgraded To whatever object is passed to this function
+      //Then container calls first element() and then container()
       var cItem = this._containerItemClass.create().upgradeTo(item);
       cItem._menu = this._menu; //if not set then undefined, but thats OK
       this.items.push(cItem);
+      if (this._cItemPrefix !== undefined){ //this must be dynamic adding of CIs, move prefix to the back
+        this.items.splice(this.items.indexOf(this._cItemPrefix) , 1); //remove cItemPrefix
+        this.items.push(this._cItemPrefix); //and move it to the end;
+      };
       this._el.appendChild(cItem.element());
+      cItem.content(); // create its textNode
       return cItem;
     },
 
     addMenu : function (menu) {
       this._menu = menu;
-      if (this._prefix !== undefined) {
-        this._menu._prefix = this._prefix; // better than going via classList or something
+      if (this._cItemPrefix !== undefined) {
+        this._menu._prefix = this._cItemPrefix; // better than going via classList or something
       };
       for (let item of this.items) {
         item._menu=menu;
@@ -83,13 +124,16 @@
         return this.isSet(); //TODO check!
       }
       this._prefixPosition = this.items.length;
+      prefix.content = function (t) {}; //Does not need a textNode Child!
       var prefItem = this.addItem(prefix);
-      this._prefix = prefItem;
+      this._cItemPrefix = prefItem;
+      prefItem._el["onclick"] = prefItem.onclick.bind(prefItem);
       if (this._menu !== undefined){
         this._menu._prefix=prefItem;
       }
     },
     
+    //Taken from Branch 5133
     /**
     * Remove a containeritem from the container by identity. Should not be used with prefix.
     * If the active item is removed, this calls menu.next().
@@ -100,9 +144,9 @@
         KorAP.log(0,"Invalid item in containers removeItemByIndex: This containerItem is not contained", "container.js");
         return;
       };
-      if (item === this._prefix) {//CHANGE TO _cItemPrefix later!!!
+      if (item === this._cItemPrefix) {//CHANGE TO _cItemPrefix later!!!
         KorAP.log(0,"Tried to remove the prefix item by calling removeItem. Please cut all connections from the menu to prefix and then\
- the connection container._prefix before calling this function if you really want to remove the prefix.","container.js");
+ the connection container._cItemPrefix before calling this function if you really want to remove the prefix.","container.js");
         return;
       };
       if (item.active()) {
@@ -173,14 +217,14 @@
      * menus list empty while we had it selected.
      * This potentially requires adjusting this.position.
      */
-     makeActive : function () {
+    makeActive : function () {
       if (this.position === undefined) {
-        if (this._prefix.isSelectable()) {
+        if (this._cItemPrefix.isSelectable()) {
           this.position = this._prefixPosition; //make prefix active if it exists
           this.item().active(true);
         } else if (this.liveLength() > 0) {
           this.position = 0;
-          this._prefix.active(false); // usually the menu makes the prefix active anyway.
+          this._cItemPrefix.active(false); // usually the menu makes the prefix active anyway.
           this.item().active(true);
         }
       }
diff --git a/dev/js/src/container/containeritem.js b/dev/js/src/container/containeritem.js
index a9a209b..6d0ef59 100644
--- a/dev/js/src/container/containeritem.js
+++ b/dev/js/src/container/containeritem.js
@@ -31,9 +31,11 @@
 
   /**
    * Get/create the document element of the container item. Can be overwritten. Standard class: li
+   * If you wish to change the textNode please overwrite the content function instead.
    */
   element : function () {
-    // already defined
+    //Call Order: First this class is created and then upgraded To whatever object is passed to container
+    //Then container calls first element() and then container()
     if (this._el !== undefined) return this._el;
     
     // Create list item
@@ -47,7 +49,32 @@
   },
 
   /**
-   * Expected to be overwritten
+   * Get/create a TextNode with text "content". If content is left blank it gets set to this.defaultTextValue,
+   * or the empty string if it does not exists
+   * @param {String} content String to which to set the text
+   * @returns textNode with content or undefined
+   */
+  content : function (content) {
+    var newText; //set textNode to this
+    if (arguments.length === 1) { //new value!
+      newText = content;
+    } else { //use default
+      if (this.defaultTextValue === undefined) { //default default is ""
+        this.defaultTextValue = "";
+      }
+      newText = this.defaultTextValue;
+    };
+    if (this._content === undefined) { //no Element until now
+      this._content = document.createTextNode(newText); //create one
+      this.element().appendChild(this._content);
+    } else { //just change it
+      this._content.nodeValue = newText; // use nodeValue instead of _innerHTML
+    };
+    return this._content;
+  },
+
+  /**
+   * Expected to be overwritten. Default returns true always.
    * @returns whether the item is currently an option to be selected, or if it should just be skipped
    */
   isSelectable : function () {
diff --git a/dev/js/src/containermenu.js b/dev/js/src/containermenu.js
index 865c3ec..0958045 100644
--- a/dev/js/src/containermenu.js
+++ b/dev/js/src/containermenu.js
@@ -12,7 +12,7 @@
   'container/container',
   'util'
 ], function (defaultMenuClass,
-             defaultContainerClass) {
+            defaultContainerClass) {
 
   return {
     /**
@@ -41,13 +41,13 @@
       } else {
         obj._container = defaultContainerClass.create(containerList, params);
       }
-      obj.container().addMenu(obj);
+      obj.container().addMenu(obj); //this is your menu, container!
 
       // add entry to HTML element
       obj._el.appendChild(obj.container().element());
-      obj._el.removeChild(obj._prefix.element());
+      obj._el.removeChild(obj._prefix.element()); //different HTML element relationship required
       //Keep prefix as 'pref' style. The additional distance is fine.
-      obj.container().addPrefix(obj._prefix);
+      obj.container().addPrefix(obj._prefix); //creates containeritem as base for prefix that then is upgraded to prefix. Also ajust _menu chains.
       return obj;
     },
 
@@ -128,8 +128,17 @@
         // Click on prefix
         if (t.container().active()){
           t.container().enter(e);
+          //NEW: reset some things. These are reset for hint menu style items
+          // so I believe we need to do the same when pressing on items in the container
+          t.prefix("");
+          t.hint().inputField().insert("").update();
+          t.hide();
+          t.hint().unshow();
+          //for clicking this is done in container.js with an eventListener for click.
         } else { // Click on item
           t.liveItem(t.position).onclick(e);
+          //Above is already done: see file dev/js/src/hint/item.js
+
         };
         e.halt();
         break;
@@ -165,7 +174,7 @@
      * @param {string} Prefix for filtering the list
      */
     show : function (active) {
-      //There are only four new lines, marked with NEW
+      //There are only five new lines, marked with NEW
       const t = this;
 
       // show menu based on initial offset
@@ -177,7 +186,7 @@
       if (!t._initList()) {
 
         // The prefix is not active
-        t._prefix.active(true);        
+        //t._prefix.active(true);     //NEW: not used   
         t.container().makeActive(); //NEW Incase the own
         // list becomes empty we need to make container active for line 129 to work
 
@@ -254,7 +263,7 @@
         this._prefix.clear();
         this.onHide();
         this._el.classList.remove('visible');
-        this.container()._el.classList.remove('visible'); //NEW
+        this.container()._el.classList.remove('visible'); //NEW //apparently not necessary
       }
       // this._el.blur();
     },
@@ -367,6 +376,19 @@
         newItem.active(true);
       };
     },
+    
+    /**
+    * Upgrade this object to another object,
+    * while private data stays intact.
+    *
+    * @param {Object} An object with properties.
+    */
+    upgradeTo : function (props) {
+      for (var prop in props) {
+        this[prop] = props[prop];
+      };
+      return this;
+    },
 
     /**
      * Get the container object
diff --git a/dev/js/src/hint/menu.js b/dev/js/src/hint/menu.js
index 89f1d10..b28c03a 100644
--- a/dev/js/src/hint/menu.js
+++ b/dev/js/src/hint/menu.js
@@ -5,12 +5,12 @@
 "use strict";
 
 define([
-  'menu',
+  'containermenu',
 	'hint/item',
 	'hint/prefix',
 	'hint/lengthField'
 ], function (
-  menuClass,
+  containerMenuClass,
   itemClass,
   prefixClass,
   lengthFieldClass) {
@@ -21,13 +21,11 @@
      * Create new hint helper menu.
      */
     create : function (hint, context, params) {
-      const obj = Object.create(menuClass)
-	        .upgradeTo(this)
-	        ._init(params, {
-	          itemClass : itemClass,
-	          prefixClass : prefixClass,
-	          lengthFieldClass : lengthFieldClass
-	        });
+      const obj = containerMenuClass.create(params, {
+        itemClass : itemClass,
+        prefixClass : prefixClass,
+        lengthFieldClass : lengthFieldClass})
+	      .upgradeTo(this);
       obj._context = context;
       obj._el.classList.add('hint');
       obj._hint = hint;
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
index 1a673f8..7f4d300 100644
--- a/dev/js/src/hint/prefix.js
+++ b/dev/js/src/hint/prefix.js
@@ -18,6 +18,7 @@
       const m = this.menu();
       const value = this.value();
       const h = m.hint();
+
       h.inputField().insert(value);
       h.active(null);
       m.hide();
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index 0c84c48..5cf8d09 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -491,7 +491,7 @@
      * @param {string} Prefix for filtering the list
      */
     show : function (active) {
-      //Upon change please also update alwaysmenu.js (only two lines new there)
+      //Upon change please also update alwaysmenu.js and containermenu.js (only two lines new there)
       const t = this;
 
       // show menu based on initial offset
diff --git a/dev/scss/header/containermenu.scss b/dev/scss/header/containermenu.scss
new file mode 100644
index 0000000..973bf6b
--- /dev/null
+++ b/dev/scss/header/containermenu.scss
@@ -0,0 +1,5 @@
+ul.containermenu {
+  > ul.container:not(.visible) {
+    display: none;
+  }
+}
diff --git a/dev/scss/header/header.scss b/dev/scss/header/header.scss
index 78fa532..76c583e 100644
--- a/dev/scss/header/header.scss
+++ b/dev/scss/header/header.scss
@@ -1,6 +1,7 @@
 @charset "utf-8";
 @import "../util";
 @import "hint";          // Hint specific menu list
+@import "containermenu"; // Container menu specific
 @import "searchbar";     // The search bar
 @import "vc";            // Virtual corpus builder
 @import "statistics";    // Statistics for VCs
@@ -141,4 +142,4 @@
   &:last-child {
     border-top-right-radius: 0;
   }
-}
\ No newline at end of file
+}
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index b6a5db0..2fb41b9 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -142,6 +142,13 @@
         'Access-Control-Allow-Methods' =>
           $h->header('Access-Control-Allow-Methods') // 'GET, POST, OPTIONS'
         );
+      #This is only for testing
+      $h->header('Access-Control-Allow-Origin' => '*'); #https://stackoverflow.com/questions/61161128/allow-cors-with-mojoliciouspluginsecurecors
+      $h->access_control_allow_origin('*');
+      $h->header('Access-Control-Allow-Methods' => 'GET, OPTIONS, POST, DELETE, PUT');
+      $h->header('Access-Control-Allow-Headers' => 'Content-Type' => 'application/json');
+      $h->header('Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Accept, Authorization, Content-Type');
+      #end test
     }
   );
 
diff --git a/lib/Kalamar/Plugin/CSP.pm b/lib/Kalamar/Plugin/CSP.pm
index 59c6c58..cd5dbec 100644
--- a/lib/Kalamar/Plugin/CSP.pm
+++ b/lib/Kalamar/Plugin/CSP.pm
@@ -17,7 +17,6 @@
 
   my $with_nonce = delete $param->{-with_nonce};
   my $disabled   = delete $param->{-disable};
-
   if ($disabled) {
     $app->log->warn('CSP DISABLED!','NEVER USE IN PRODUCTION!');
   };
@@ -49,7 +48,7 @@
   my $csp = \( generate(%directives) );
 
   # Add csp header
-  unless ($disabled) {
+    unless ($disabled) {
     $app->hook(
       before_dispatch => sub {
         my $c = shift;
@@ -92,7 +91,7 @@
 
   $app->helper(
     csp_nonce_tag => sub {
-      my $c = shift;
+      my $c = shift;      
       if ($disabled || !$c->content_block_ok('nonce_js')) {
         return '';
       };
diff --git a/lib/Kalamar/Plugin/QueryReference.pm b/lib/Kalamar/Plugin/QueryReference.pm
index bb1b390..1823c53 100644
--- a/lib/Kalamar/Plugin/QueryReference.pm
+++ b/lib/Kalamar/Plugin/QueryReference.pm
@@ -25,11 +25,12 @@
 
   $r->add_type('qname' => qr![-_\.a-zA-Z0-9]+!);
 
-
+  
   # List queries
   $r->get('/query')->to(
     cb => sub {
       my $c = shift;
+      #$c->res->headers->header('Access-Control-Allow-Origin' => '127.0.0.1:3000'); #Does not seem to be necessary
 
       # Use mock up
       if ($ENV{QUERY_REF_MOCKUP}) {
@@ -104,6 +105,7 @@
   $r->put('/query/<qname:qname>')->to(
     cb => sub {
       my $c = shift;
+      #$c->res->headers->header('Access-Control-Allow-Origin' => '127.0.0.1:3000'); #Does not seem to be necessary
       my $v = $c->validation;
 
       # Missing: definition
@@ -239,6 +241,7 @@
   $r->delete('/query/<qname:qname>')->to(
     cb => sub {
       my $c = shift;
+      #$c->res->headers->header('Access-Control-Allow-Origin' => '127.0.0.1:3000'); #Does not seem to be necessary
       my $qname = $c->stash('qname');
 
       # Use mock up
@@ -324,6 +327,7 @@
   $r->get('/query/<qname:qname>')->to(
     cb => sub {
       my $c = shift;
+      #$c->res->headers->header('Access-Control-Allow-Origin' => '127.0.0.1:3000'); #Does not seem to be necessary
       my $qname = $c->stash('qname');
 
       # Use mock up
diff --git a/t/plugin/csp.t b/t/plugin/csp.t
index aa6cd70..967af84 100644
--- a/t/plugin/csp.t
+++ b/t/plugin/csp.t
@@ -148,12 +148,16 @@
 $content =~ q!<script nonce="(.{20})"!;
 like($content, qr/nonce-\Q$1\E/);
 
+<<<<<<< HEAD
 
+=======
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu
 # Disable csp
 $t = Test::Mojo->new(Mojolicious::Lite->new);
 $t->app->plugin('Kalamar::Plugin::CSP' => {
   '-disable' => 1
 });
+<<<<<<< HEAD
 
 $t->app->routes->get('/' => sub {
   shift->render(text => 'hello world');
@@ -162,6 +166,13 @@
 $t->app->csp->add('script-src', '*');
 is($t->app->csp_nonce_tag,'');
 
+=======
+$t->app->routes->get('/' => sub {
+  shift->render(text => 'hello world');
+});
+$t->app->csp->add('script-src', '*');
+is($t->app->csp_nonce_tag,'');
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu
 $t->get_ok('/')
   ->status_is(200)
   ->content_is('hello world')
diff --git a/t/plugin/demo_server.t b/t/plugin/demo_server.t
index 1b2b524..da8c4ad 100644
--- a/t/plugin/demo_server.t
+++ b/t/plugin/demo_server.t
@@ -1,25 +1,43 @@
+<<<<<<< HEAD
 use Mojo::Base -strict;
 use Test::More;
 use Test::Mojo;
 
+=======
+
+use Mojo::Base -strict;
+use Test::More;
+use Test::Mojo;
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu
 # Test the documentation
 my $t = Test::Mojo->new('Kalamar' => {
   Kalamar => {
     plugins => ['DemoServer']
   }
 });
+<<<<<<< HEAD
 
+=======
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu
 $t->get_ok('/demo/all.html')
   ->status_is(200)
   ->content_type_is('text/html;charset=UTF-8')
   ->text_is('title', 'Complete Demo')
   ;
+<<<<<<< HEAD
 
+=======
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu
 $t->get_ok('/demo/alldemo.js')
   ->status_is(200)
   ->content_type_is('application/javascript')
   ->content_like(qr!'app/en'!)
   ;
+<<<<<<< HEAD
 
 done_testing;
 __END__
+=======
+done_testing;
+__END__
+>>>>>>> Use containerMenu as a base for HintMenu instead of regular menu