Modernize ES for-loops and remove problematic for-in loops

This slightly modifies the behaviour of errors (see init.js)

Change-Id: I1aab691d5b7e8167b6213378bdd9139c133202cd
diff --git a/Changes b/Changes
index 607cd2f..75dc6b0 100755
--- a/Changes
+++ b/Changes
@@ -1,4 +1,5 @@
-0.40 2020-10-07
+0.40 2020-10-09
+        - Modernize ES and fix in-loops.
 
 0.39 2020-10-07
         - Add information on secret file to Readme.
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index a92d73d..7a85dc0 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -167,9 +167,9 @@
           return;
         };
 	      if (json !== null && json["errors"] !== null) {
-	        for (var i in json["errors"]) {
-	          KorAP.log(json["errors"][i][0], json["errors"][i][1] || "Unknown");
-	        };
+	        json["errors"].forEach(
+            e => KorAP.log(e[0], e[1] || "Unknown")
+	        );
 	      }
         else if (this.status !== 200) {
         	KorAP.log(this.status, this.statusText);
diff --git a/dev/js/src/hint/foundries/corenlp.js b/dev/js/src/hint/foundries/corenlp.js
index d4279b7..f718679 100644
--- a/dev/js/src/hint/foundries/corenlp.js
+++ b/dev/js/src/hint/foundries/corenlp.js
@@ -1,31 +1,33 @@
-define(["hint/foundries","hint/foundries/stts","hint/foundries/negranodes","hint/foundries/negraedges"],
-       function (ah, sttsArray, negraNodesArray, negraEdgesArray) {
-  var namedEntities = [
-    ["I-LOC",  "I-LOC ",  "Location"],
-    ["I-MISC", "I-MISC ", "Miscellaneous"],
-    ["I-ORG",  "I-ORG ",  "Organization"],
-    ["I-PER",  "I-PER ",  "Person"]
-  ];
+define(
+  ["hint/foundries","hint/foundries/stts","hint/foundries/negranodes","hint/foundries/negraedges"],
+  function (ah, sttsArray, negraNodesArray, negraEdgesArray) {
+    var namedEntities = [
+      ["I-LOC",  "I-LOC ",  "Location"],
+      ["I-MISC", "I-MISC ", "Miscellaneous"],
+      ["I-ORG",  "I-ORG ",  "Organization"],
+      ["I-PER",  "I-PER ",  "Person"]
+    ];
 
-  ah["-"].push(
-    ["CoreNLP", "corenlp/", "Constituency, Named Entities, Part-of-Speech"]
-  );
+    ah["-"].push(
+      ["CoreNLP", "corenlp/", "Constituency, Named Entities, Part-of-Speech"]
+    );
 
-  ah["corenlp/"] = [
-    ["Constituency", "c="],
-    ["Named Entity", "ne=" , "Combined"],
-    ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
-    ["Named Entity", "ne_hgc_175m_600=",    "ne_hgc_175m_600"],
-    ["Part-of-Speech", "p="]
-  ];
+    ah["corenlp/"] = [
+      ["Constituency", "c="],
+      ["Named Entity", "ne=" , "Combined"],
+      ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
+      ["Named Entity", "ne_hgc_175m_600=",    "ne_hgc_175m_600"],
+      ["Part-of-Speech", "p="]
+    ];
 
-  ah["corenlp/ne="] = namedEntities;
-  ah["corenlp/ne_dewac_175m_600="] = namedEntities;
-  ah["corenlp/ne_hgc_175m_600="] = namedEntities;
-  ah["corenlp/p="] = sttsArray;
-  ah["corenlp/c="] = negraNodesArray;
+    ah["corenlp/ne="] = namedEntities;
+    ah["corenlp/ne_dewac_175m_600="] = namedEntities;
+    ah["corenlp/ne_hgc_175m_600="] = namedEntities;
+    ah["corenlp/p="] = sttsArray;
+    ah["corenlp/c="] = negraNodesArray;
 
-  for (var i in negraNodesArray) {
-    ah["corenlp/c=" + negraNodesArray[i][0] + '-'] = negraEdgesArray;
-  };
-});
+    negraNodesArray.forEach(
+      i => ah["corenlp/c=" + i[0] + '-'] = negraEdgesArray
+    );
+  }
+);
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index f9eaa13..4482316 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -262,22 +262,20 @@
       };
 
       if (KorAP.koralQuery["errors"]) {
-        var errors = KorAP.koralQuery["errors"];
-        for (var i in errors) {
+        KorAP.koralQuery["errors"].forEach(function(e) {
 
           // Malformed query
-          if (errors[i][0] === 302 && errors[i][2]) {
+          if (e[0] === 302 && e[2]) {
             obj.hint = hintClass.create();
-            obj.hint.alert(errors[i][2], errors[i][1]);
-            break;
+            obj.hint.alert(e[2], e[1]);
           }
 
           // no query
-          else if (errors[i][0] === 301) {
+          else if (e[0] === 301) {
             obj.hint = hintClass.create();
-            obj.hint.alert(0, errors[i][1]);      
+            obj.hint.alert(0, e[1]);      
           }
-        }
+        });
       };
     };
 
diff --git a/dev/js/src/match.js b/dev/js/src/match.js
index 92062cc..7ce2652 100644
--- a/dev/js/src/match.js
+++ b/dev/js/src/match.js
@@ -85,10 +85,9 @@
       else {
 
         // Iterate over allowed match terms
-        for (var i in _matchTerms) {
-          var term = _matchTerms[i];
+        _matchTerms.forEach(function(term) {
           this[term] = match[term] !== undefined ? match[term] : undefined;
-        };
+        }, this);
       };
       
       this._avail = {
diff --git a/dev/js/src/match/meta.js b/dev/js/src/match/meta.js
index c4cd63f..78b1430 100644
--- a/dev/js/src/match/meta.js
+++ b/dev/js/src/match/meta.js
@@ -56,12 +56,9 @@
       };
       
       // TODO: Meta fields should be separated
-      const keys = Object.keys(posInMetaArray);
-
       // Sort all meta keys alphabetically
-      for (let i in keys.sort()) {
-        let k = keys[i];                             // This is the title
-        let field = fields[posInMetaArray[keys[i]]]; // This is the object
+      Object.keys(posInMetaArray).sort().forEach(function(k) {
+        let field = fields[posInMetaArray[k]]; // This is the object
 
         // Ignore internal IDs
         if (k !== "UID" &&
@@ -111,7 +108,7 @@
           
           metaDL.appendChild(metaL);
         };
-      };
+      });
 
       // Add corpusByMatch assistant
       this._corpusByMatch = cbmClass.create(this._element);
diff --git a/dev/js/src/match/querycreator.js b/dev/js/src/match/querycreator.js
index 8c60ff8..fa4917b 100644
--- a/dev/js/src/match/querycreator.js
+++ b/dev/js/src/match/querycreator.js
@@ -405,13 +405,9 @@
       };
 
       // Set query language field
-      var qlf = this._ql.options;
-      for (var i in qlf) {
-	      if (qlf[i].value == 'poliqarp') {
-	        qlf[i].selected = true;
-          break;
-	      };
-      };
+      const ql = this._ql.options.find(e => e.value == 'poliqarp');
+      if (ql)
+        ql.selected = true;
 
       // Insert to query bar
       this._q.value = this.toString();
diff --git a/dev/js/src/match/table.js b/dev/js/src/match/table.js
index 3ad2731..9078780 100644
--- a/dev/js/src/match/table.js
+++ b/dev/js/src/match/table.js
@@ -104,8 +104,7 @@
     _parse : function (children, mark) {
 
       // Get all children
-      for (var i in children) {
-        var c = children[i];
+      children.forEach(function(c) {
 
         // Create object on position unless it exists
         if (this._info[this._pos] === undefined) {
@@ -180,7 +179,7 @@
             this._token[this._pos++] = c.nodeValue;
           };
         };
-      };
+      }, this);
 
       delete this._info[this._pos];
     },
@@ -250,7 +249,7 @@
       tr.addCell('th', undefined, 'Layer');
 
       // Add tokens
-      for (var i in this._token) {
+      Object.keys(this._token).forEach(function(i) {
         let surface = this.getToken(i);
         var c = tr.addCell('th', undefined, surface);
         if (this._mark[i]) {
@@ -267,7 +266,7 @@
         if (surface.length > 20) {
           c.setAttribute("title", surface)
         }
-      };
+      }, this);
       
       var tbody = table.addE('tbody');
 
diff --git a/dev/js/src/match/treearc.js b/dev/js/src/match/treearc.js
index 6f0690b..2acda9b 100644
--- a/dev/js/src/match/treearc.js
+++ b/dev/js/src/match/treearc.js
@@ -61,8 +61,7 @@
       // Iterate over edge lists

       // TODO:

       //   Support spans for anchors!

-      for (var i in edges) {

-        var edge = edges[i];

+      edges.forEach(function(edge) {

 

         // Check the target identifier

         var targetID = edge.targetID;

@@ -88,7 +87,7 @@
           // console.log(relation);

           this.addRel(relation);

         };

-      };

+      }, this);

 

       // Reset parsing memory

       this.temp = {};

@@ -100,8 +99,7 @@
     _parse : function (parent, children, mark) {

 

       // Iterate over all child nodes

-      for (var i in children) {

-        var c = children[i];

+      children.forEach(function(c) {

 

         // Element node

         if (c.nodeType == 1) {

@@ -230,8 +228,8 @@
               this.temp['pos']++;

             };

           };

-        }

-      };

+        };

+      }, this);

 

       // Todo: define edges here!

     },

@@ -612,13 +610,7 @@
 

       // Add sorted arcs and anchors

       this._sortedArcs    = lengthSort(sortedArcs, false);

-

-      // Translate map to array (there is probably a better JS method)

-      var sortedAnchors = [];

-      for (var i in anchors) {

-        sortedAnchors.push(anchors[i]);

-      };

-      this._sortedAnchors = lengthSort(sortedAnchors, true);

+      this._sortedAnchors = lengthSort(Object.keys(anchors), true);

     },

 

     /**

@@ -671,18 +663,18 @@
       ws.style.textAnchor = "start";

       

       var lastRight = 0;

-      for (var node_i in this._tokens) {

+      this._tokens.forEach(function(node_i) {

         // Append svg

         // var x = text.appendChild(this._c("text"));

         var tspan = text.appendChild(this._c("tspan"));

-        tspan.appendChild(d.createTextNode(this._tokens[node_i]));

+        tspan.appendChild(d.createTextNode(node_i));

         tspan.setAttribute("text-anchor", "middle");

         

         this._tokenElements.push(tspan);

 

         // Add whitespace!

         tspan.setAttribute("dx", this.tokenSep);

-      };

+      }, this);

 

       // Get some global position data that may change on resize

       var globalBoundingBox = this._rect(g);

@@ -702,15 +694,14 @@
         this._sortArcs();

 

       // 1. Draw all anchors

-      var i;

-      for (i in this._sortedAnchors) {

-        this._drawAnchor(this._sortedAnchors[i]);

-      };

+      this._sortedAnchors.forEach(

+        i => this._drawAnchor(i)

+      );

 

       // 2. Draw all arcs

-      for (i in this._sortedArcs) {

-        this._drawArc(this._sortedArcs[i]);

-      };

+      this._sortedArcs.forEach(

+        i => this._drawArc(i)

+      );

 

       // Resize the svg with some reasonable margins

       var width = this._rect(text).width;

diff --git a/dev/js/src/match/treehierarchy.js b/dev/js/src/match/treehierarchy.js
index e9c6122..0a7bf49 100644
--- a/dev/js/src/match/treehierarchy.js
+++ b/dev/js/src/match/treehierarchy.js
@@ -109,8 +109,7 @@
 
     // Parse the snippet
     _parse : function (parent, children, mark) {
-      for (var i in children) {
-       var c = children[i];
+      children.forEach(function(c) {
 
        // Element node
        if (c.nodeType == 1) {
@@ -164,7 +163,7 @@
 
            this._addEdge(parent, id);
          };
-      };
+      }, this);
       return this;
     },
 
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index 3c2fff8..d0ce49a 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -175,14 +175,14 @@
 
       var i = 0;
       // Initialize item list based on parameters
-      for (i in list) {
-        var obj = this._itemClass.create(list[i]);
+      list.forEach(function(i){
+        var obj = this._itemClass.create(i);
 
         // This may become circular
         obj["_menu"] = this;
-        this._lengthField.add(list[i]);
+        this._lengthField.add(i);
         this._items.push(obj);
-      };
+      }, this);
 
       this._slider.length(this.liveLength())
         .limit(this._limit)
@@ -296,9 +296,9 @@
         delete this._element["menu"]; 
 
       // Remove circular reference to "this" in items
-      for (var i = 0; i < this._items.length; i++) {
-        delete this._items[i]["_menu"];
-      };
+      this._items.forEach(function(i) {
+        delete i["_menu"];
+      });
 
       // Remove circular reference to "this" in prefix
       delete this._prefix['_menu'];
@@ -854,11 +854,11 @@
 
     // Unmark all items
     _unmark : function () {
-      for (var i in this._list) {
-        var item = this._items[this._list[i]];
+      this._list.forEach(function(it){
+        var item = this._items[it];
         item.lowlight();
-        item.active(false);  
-      };
+        item.active(false);
+      }, this);
     },
 
     // Set boundary for viewport
@@ -903,7 +903,7 @@
         var shown = 0;
         var i;
 
-        for (i in this._list) {
+        for (let i = 0; i < this._list.length; i++) {
 
           // Don't show - it's before offset
           shown++;
diff --git a/dev/js/src/panel.js b/dev/js/src/panel.js
index eee7a00..f9fb979 100644
--- a/dev/js/src/panel.js
+++ b/dev/js/src/panel.js
@@ -104,11 +104,10 @@
      * Delete a closed view from panel
      */
     delView : function (view) {
-      for (i in this.views) {
-        if (this.views[i] === view) {
-          this.views[i] = undefined;
-        }
-      }
+      this.views.forEach(function(e, i, a) {
+        if (e === view)
+          a[i] = undefined;
+      });
     },
 
     /**
diff --git a/dev/js/src/panel/match.js b/dev/js/src/panel/match.js
index c901cee..a6c50db 100644
--- a/dev/js/src/panel/match.js
+++ b/dev/js/src/panel/match.js
@@ -87,9 +87,7 @@
         if (matchButtons) {
 
           // Add all matchbuttons in order
-          for (i in matchButtons) {
-            a.add.apply(a, matchButtons[i]);
-          }
+          matchButtons.forEach(m => a.add.apply(a, m));
         };
       };
 
@@ -142,13 +140,8 @@
       var spans = match.getSpans();
       var rels = match.getRels();
       
-      var i;
-      for (i in spans) {
-        treeLayers.push(spans[i]);
-      };
-      for (i in rels) {
-        treeLayers.push(rels[i]);
-      };
+      spans.forEach(i => treeLayers.push(i));
+      rels.forEach(i => treeLayers.push(i));
 
       // Get spans
       treeLayers = treeLayers.sort(
diff --git a/dev/js/src/panel/query.js b/dev/js/src/panel/query.js
index f0f1920..7f66f9f 100644
--- a/dev/js/src/panel/query.js
+++ b/dev/js/src/panel/query.js
@@ -27,9 +27,7 @@
         var queryButtons = KorAP.Plugin.buttonGroup("query");
 
         // Add all matchbuttons in order
-        for (i in queryButtons) {
-          a.add.apply(a, queryButtons[i]);
-        };
+        queryButtons.forEach(i => a.add.apply(a, i));
 
         KorAP.Plugin.clearButtonGroup("query")
       };
diff --git a/dev/js/src/panel/result.js b/dev/js/src/panel/result.js
index d221081..3c5dc31 100644
--- a/dev/js/src/panel/result.js
+++ b/dev/js/src/panel/result.js
@@ -30,9 +30,7 @@
         var resultButtons = KorAP.Plugin.buttonGroup("result");
 
         // Add all result buttons in order
-        for (i in resultButtons) {
-          this.actions.add.apply(this.actions, resultButtons[i]);
-        };
+       resultButtons.forEach(i => this.actions.add.apply(this.actions, i));
 
         KorAP.Plugin.clearButtonGroup("result");
       };
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
index 76ac11d..e47cfbf 100644
--- a/dev/js/src/plugin/server.js
+++ b/dev/js/src/plugin/server.js
@@ -125,8 +125,7 @@
  
       // Embed all embeddings of the plugin
       var that = this;
-      for (let i in obj["embed"]) {
-        let embed = obj["embed"][i];
+      obj["embed"].forEach(function(embed) {
 
         if (typeof embed !== 'object')
           throw new Error("Embedding of plugin is no object");
@@ -274,7 +273,7 @@
 
           plugin["services"].push(id);
         };
-      };
+      }, this);
     },
 
     // TODO:
@@ -322,7 +321,7 @@
         
         // Every second increase the limits of all registered services
         this._timer = window.setInterval(function () {
-          for (var i in limits) {
+          for (let i = 0; i < limits.length; i++) {
             if (limits[i]++ >= maxMessages) {
               limits[i] = maxMessages;
             }
@@ -558,16 +557,16 @@
     // Destructor, just for testing scenarios
     destroy : function () {
       limits = {};
-      for (let s in services) {
-        services[s].close();
-      };
+      Object.keys(services).forEach(
+        s => services[s].close()
+      );
       services = {};
-      for (let b in buttons) {
-        buttons[b] = [];
-      };
-      for (let b in buttonsSingle) {
-        buttonsSingle[b] = [];
-      };
+      Object.keys(buttons).forEach(
+        b => buttons[b] = []
+      );
+      Object.keys(buttonsSingle).forEach(
+        b => buttonsSingle[b] = []
+      );
 
       if (this._element) {
         let e = this._element;
diff --git a/dev/js/src/selectMenu.js b/dev/js/src/selectMenu.js
index 9f447ca..4db1dcd 100644
--- a/dev/js/src/selectMenu.js
+++ b/dev/js/src/selectMenu.js
@@ -50,9 +50,7 @@
 	      obj._container.addEventListener('click', obj.showSelected.bind(obj));
 
 	      // Add index information to each item
-	      for (i in obj._items) {
-	        obj._items[i]._index = i;
-	      };
+	      obj._items.forEach((e,i) => e._index = i);
 
 	      // This is only domspecific
 	      obj.element().addEventListener('blur', function (e) {
@@ -89,8 +87,7 @@
        */
       selectValue : function (vParam) {
         var qlf = this._select.options;
-        var i;
-        for (i in qlf) {
+        for (let i = 0; i < qlf.length; i++) {
 	        if (qlf[i].value == vParam) {
             this.hide();
             this.select(i);
diff --git a/dev/js/src/state.js b/dev/js/src/state.js
index 78cd4cc..a5ab7d8 100644
--- a/dev/js/src/state.js
+++ b/dev/js/src/state.js
@@ -54,9 +54,7 @@
     set : function (value) {
       if (value != this.value) {
         this.value = value;
-        for (let i in this._assoc) {
-          this._assoc[i].setState(value);
-        }
+        this._assoc.forEach(i => i.setState(value));
       };
     },
 
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 6cd3c00..764ed96 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -233,10 +233,10 @@
 
       // Check recursively
       else if (obj.ldType() === 'docGroup') {
-        for (var i in obj.operands()) {
-          if (this.wasRewritten(obj.getOperand(i))) {
-            return true;
-          }
+
+        // If there was a rewritten object
+        if (obj.operands().find(op => this.wasRewritten(op)) !== undefined) {
+          return true;
         };
       };
 
diff --git a/dev/js/src/vc/docgroup.js b/dev/js/src/vc/docgroup.js
index 3a22034..af49191 100644
--- a/dev/js/src/vc/docgroup.js
+++ b/dev/js/src/vc/docgroup.js
@@ -117,14 +117,13 @@
 
 	      // Flatten group
 	      if (docGroup.operation() === this.operation()) {
-	        for (var op in docGroup.operands()) {
-	          op = docGroup.getOperand(op);
+          docGroup.operands().forEach(function(op) {
 	          var dupl = this._duplicate(op);
 	          if (dupl === null) {
 	            this._operands.push(op);
 	            op.parent(this);
 	          };
-	        };
+	        }, this);
 	        docGroup._operands = [];
 	        docGroup.destroy();
 	        return this;
@@ -275,11 +274,10 @@
 	          this._operands.splice(i, 1);
 
 	          // Inject new operands
-	          for (var op in newOp.operands().reverse()) {
-	            op = newOp.getOperand(op);
+            newOp.operands().reverse().forEach(function(op) {
 	            this._operands.splice(i, 0, op);
 	            op.parent(this);
-	          };
+	          }, this);
 	          // Prevent destruction of operands
 	          newOp._operands = [];
 	          newOp.destroy();
@@ -337,10 +335,7 @@
       };
 
       // Add all documents
-      for (var i in json["operands"]) {
-	      var operand = json["operands"][i];
-	      this.append(operand);
-      };
+      json["operands"].forEach(i => this.append(i));
       
       return this;
     },
diff --git a/dev/js/src/vc/fragment.js b/dev/js/src/vc/fragment.js
index f0c1397..ccc121e 100644
--- a/dev/js/src/vc/fragment.js
+++ b/dev/js/src/vc/fragment.js
@@ -142,9 +142,7 @@
         root.setAttribute('class','docGroup');
         root.setAttribute('data-operation', 'and');
 
-        for (let i in this._operands) {
-          root.appendChild(_doc(this._operands[i]));
-        };
+        this._operands.forEach(i => root.appendChild(_doc(i)));
       }
       else if (l == 1) {
         root = _doc(this._operands[0]);
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
index 0f0d92a..f2f4b2f 100644
--- a/dev/js/src/vc/menu.js
+++ b/dev/js/src/vc/menu.js
@@ -44,11 +44,9 @@
      * VCs and small key lists.
      */
     typeOf : function (key) {
-      for (i in this._items) {
-        if (this._items[i].key() === key) {
-          return this._items[i].type();
-        }
-      };
+      const found = this._items.find(i => i.key() === key);
+      if (found)
+        return found.type();
     }
   };
 });
diff --git a/dev/js/src/vc/rewritelist.js b/dev/js/src/vc/rewritelist.js
index 9a87e4e..75f3d27 100644
--- a/dev/js/src/vc/rewritelist.js
+++ b/dev/js/src/vc/rewritelist.js
@@ -37,9 +37,8 @@
       this._element = document.createElement('div');
       this._element.setAttribute('class', 'rewrite');
       var comments = [];
-      for (var x in this._list) {
-	      var rewrite = this._list[x];
-
+      this._list.forEach(function(rewrite) {
+	    
         // This is a blind element
 	      var span = document.createElement('span');
 
@@ -60,7 +59,7 @@
         comments.push(rewriteText + ' (' + rewrite.operation() + ')');
         
 	      this._element.appendChild(span);
-      };
+      }, this);
       this._element.setAttribute("title", comments.join("\n"))
       return this._element;
     }