Fix query-by-match for multi-colon values

Change-Id: Idc16bb535f4ec07d5aaee9f9d241d9eee6c6bf6e
diff --git a/Changes b/Changes
index 3351bc5..a0f6a46 100755
--- a/Changes
+++ b/Changes
@@ -1,4 +1,4 @@
-0.43 2021-11-05
+0.43 2021-11-16
         - New menu class that has an entry at the very end,
           similar to the input text prefix,
           that is always available. (lerepp)
@@ -45,6 +45,7 @@
         - Support default value for plugin toggle embeddings.
         - Fix wrong handling of utf8 input in login that can
           lead to server crash.
+        - Fix query-by-match for multiple colons.
 
 0.42 2021-06-18
         - Added GitHub based CI for perl.
diff --git a/dev/js/spec/queryCreatorSpec.js b/dev/js/spec/queryCreatorSpec.js
index 780a7bf..c3e041d 100644
--- a/dev/js/spec/queryCreatorSpec.js
+++ b/dev/js/spec/queryCreatorSpec.js
@@ -88,6 +88,7 @@
     "            <div>gender:masc</div>" +
     "            <div>number:sg</div>" +
     "            <div>morphemes:.::_SORSZ \\ZERO::NOM 'period::PUNCT'</div>" +
+    "            <div>morphemes:ZERO::NOM</div>" +
     "          </td>" +
     "          <td>ADJA</td>" +
     "          <td></td>" +
@@ -505,8 +506,8 @@
     });
 
     it('should create and-groups for key-value terms', function () {
-      var matchTable = matchTableComplexFactory();
-      var qc = qcClass.create(matchTable);
+      const matchTable = matchTableComplexFactory();
+      const qc = qcClass.create(matchTable);
 
       // Nothing to show
       expect(qc.toString()).toEqual("");
@@ -515,7 +516,7 @@
       expect(qc.shown()).toBe(false);
       expect(qc.element().className).toEqual("query fragment");
 
-      var cell = matchTable.querySelector("thead > tr > th:nth-child(5)");
+      let cell = matchTable.querySelector("thead > tr > th:nth-child(5)");
       expect(cell.innerString()).toEqual("lebende und sterbende");
       expect(cell.classList.contains("chosen")).toBe(false);
       cell.click();
@@ -537,7 +538,6 @@
       cell.click();
       expect(cell.classList.contains("chosen")).toBe(true);
       expect(qc.toString()).toEqual("[orth='lebende und sterbende'][corenlp/p=case:nom]");
-      var cell = cell;
 
       cell = matchTable.querySelector("tbody > tr:nth-child(1) > td:nth-child(6) > div:nth-child(3)");
       expect(cell.innerString()).toEqual('number:sg');
@@ -545,7 +545,7 @@
       cell.click();
       expect(cell.classList.contains("chosen")).toBe(true);
       expect(qc.toString()).toEqual("[orth='lebende und sterbende'][corenlp/p=case:nom & corenlp/p=number:sg]");
-      var cell2 = cell;
+      let cell2 = cell;
 
       cell = matchTable.querySelector("tbody > tr:nth-child(1) > td:nth-child(6) > div:nth-child(2)");
       expect(cell.innerString()).toEqual('gender:mascgender:fem');
@@ -553,7 +553,7 @@
       cell.click();
       expect(cell.classList.contains("chosen")).toBe(true);
       expect(qc.toString()).toEqual("[orth='lebende und sterbende'][(corenlp/p=gender:fem | corenlp/p=gender:masc) & corenlp/p=case:nom & corenlp/p=number:sg]");
-      var cell3 = cell;
+      let cell3 = cell;
 
       // Remove cell again
       cell = cell2;
@@ -616,7 +616,7 @@
 
       // TODO:
       // This does not respect keys vs values at the moment, but neither does Koral
-      var cell = matchTable.querySelector("tbody > tr:nth-child(2) > td > div:nth-child(4)");
+      let cell = matchTable.querySelector("tbody > tr:nth-child(2) > td > div:nth-child(4)");
       expect(cell.innerString()).toEqual("morphemes:.::_SORSZ \\ZERO::NOM 'period::PUNCT'");
       expect(cell.classList.contains("chosen")).toBe(false);
       cell.click();
@@ -643,6 +643,16 @@
       cell.click()
       expect(cell.classList.contains("chosen")).toBe(false);
       expect(qc.toString()).toEqual("");
+
+      cell = matchTable.querySelector("tbody > tr:nth-child(2) > td > div:nth-child(5)");
+      expect(cell.innerString()).toEqual("morphemes:ZERO::NOM");
+      expect(cell.classList.contains("chosen")).toBe(false);
+      cell.click();
+      expect(cell.classList.contains("chosen")).toBeTruthy();
+      expect(qc.toString()).toEqual("[opennlp/p='morphemes:ZERO::NOM']");
+      cell.click()
+      expect(cell.classList.contains("chosen")).toBe(false);
+      expect(qc.toString()).toEqual("");
     });
 
     it('should respect marked elements', function () {
diff --git a/dev/js/src/match/querycreator.js b/dev/js/src/match/querycreator.js
index 09d91b8..f4b819f 100644
--- a/dev/js/src/match/querycreator.js
+++ b/dev/js/src/match/querycreator.js
@@ -20,10 +20,10 @@
   const loc = KorAP.Locale;
   loc.NEW_QUERY = loc.NEW_QUERY || 'New Query';
 
-  const esc = RegExp("[ \.\'\\\\\|\&]");
+  const esc = RegExp("^(?:[^ \:\.\'\\\\\|\&]+:)?[^ \:\.\'\\\\\|\&]+$");
   
   function _getKeyValue (keyValue) {
-    if (keyValue.match(esc) != null) {
+    if (keyValue.match(esc) === null) {
       return "'" + keyValue.replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'";
     };
     return keyValue;