Introduce display of relational structures (experimental)

Change-Id: I3f8f16ff477e0ff21d846a4a0799a8e48f3f43e4
diff --git a/dev/demo/all.html b/dev/demo/all.html
index 369ec2c..6cbfa65 100644
--- a/dev/demo/all.html
+++ b/dev/demo/all.html
@@ -175,7 +175,7 @@
 	      data-doc-id="WWW"
 	      data-text-id="03313"
 	      data-match-id="p102-103"
-	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans"
+	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans malt/d=rels"
 	      id="WPD-WWW.03313-p102-103"
 	      tabindex="6">
 	    <div class="meta">WPD/WWW/03313</div>
@@ -194,7 +194,7 @@
 	      data-doc-id="FFF"
 	      data-text-id="01460"
 	      data-match-id="p119-120"
-	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans"
+	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans malt/d=rels"
 	      id="WPD-FFF.01460-p119-120"
 	      tabindex="6">
 	    <div class="meta">WPD/III/78432</div>
@@ -211,7 +211,7 @@
 	      data-doc-id="HHH"
 	      data-text-id="06056"
 	      data-match-id="p2564-2565"
-	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans"
+	      data-available-info="base/s=spans corenlp/c=spans corenlp/ne=tokens corenlp/p=tokens corenlp/s=spans glemm/l=tokens mate/l=tokens mate/m=tokens mate/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens tt/s=spans xip/c=spans malt/d=rels"
 	      id="WPD-HHH.06056-p2564-2565"
 	      tabindex="6">
 	    <div class="meta">WPD/HHH/06056</div>
diff --git a/dev/demo/alldemo.js b/dev/demo/alldemo.js
index 5e0ec44..226ee28 100644
--- a/dev/demo/alldemo.js
+++ b/dev/demo/alldemo.js
@@ -137,6 +137,75 @@
   "<\/span>"+
   "<span class=\"context-right\"><\/span>";
 
+var relSnippet =
+    "<span class=\"context-left\"></span>" +
+    "<span class=\"match\">" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p199\">" +
+    "    <span xlink:title=\"malt/d:ADV\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">dann</span>" +
+    "  </span>" +
+    " zog " +
+    "  <span xlink:title=\"malt/d:SUBJ\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">ich</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p202\">" +
+    "    <span xlink:title=\"malt/d:OBJA\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">mich</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:PP\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">gegen</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p204\">" +
+    "    <span xlink:title=\"malt/d:DET\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p204\">das</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:PN\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p202\">Regiment</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p206\">" +
+    "    <span xlink:title=\"malt/d:AVZ\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">zurück</span>" +
+    "  </span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p207\">" +
+    "    <span xlink:title=\"malt/d:KON\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p199\">und</span>" +
+    "  </span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p208\">" +
+    "    <span xlink:title=\"malt/d:CJ\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p206\">war</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:AUX\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p207\">bemüht</span>" +
+    "," +
+    "  <span xlink:title=\"malt/d:DET\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p211\">einige</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p211\">" +
+    "    <span xlink:title=\"malt/d:ATTR\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p211\">genaue</span>" +
+    "  </span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p212\">" +
+    "    <span xlink:title=\"malt/d:OBJA\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p215\">Umrisse</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:OBJP\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p215\">aufs</span>" +
+    "  <span xlink:title=\"malt/d:PN\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p212\">Papier</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p215\">" +
+    "    <span xlink:title=\"malt/d:PART\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p215\">zu</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:OBJI\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p208\">bringen</span>" +
+    ", um mir " +
+    "  <span xml:id=\"token-GOE/AGA/01784-p219\">" +
+    "    <span xlink:title=\"malt/d:DET\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p219\">die</span>" +
+    "  </span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p220\">Bezüge</span>" +
+    "  <span xlink:title=\"malt/d:KON\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p219\">und</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p222\">" +
+    "    <span xlink:title=\"malt/d:DET\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p222\">die</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:CJ\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p220\">Distanzen</span>" +
+    "  <mark>" +
+    "    <span xlink:title=\"malt/d:DET\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p225\">der</span>" +
+    "  </mark>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p225\">" +
+    "    <span xlink:title=\"malt/d:ATTR\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p225\">landschaftlichen</span>" +
+    "  </span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p226\">" +
+    "    <span xlink:title=\"malt/d:GMOD\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p222\">Gegenstände</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:KON\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p219\">desto</span>" +
+    "  <span xlink:title=\"malt/d:ADV\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p229\">besser</span>" +
+    "  <span xml:id=\"token-GOE/AGA/01784-p229\">" +
+    "    <span xlink:title=\"malt/d:PART\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p229\">zu</span>" +
+    "  </span>" +
+    "  <span xlink:title=\"malt/d:CJ\" xlink:type=\"simple\" xlink:href=\"#token-GOE/AGA/01784-p226\">imprimieren</span>" +
+    "</span>" +
+    "<span class=\"context-right\"></span>";
+
+
 var menuContent = [
     ['cnx/c', 'cnx', 'c'],
     ['mate/c', 'mate', 'c'],
@@ -546,8 +615,11 @@
   KorAP.API.getMatchInfo = function(match, callObj, cb) {
 
     console.log(match);
-    
-    if (callObj["spans"] !== undefined && callObj["spans"] === true) {
+
+    if (callObj["foundry"] === "malt" && callObj["layer"] === "d") {
+      cb({ "snippet": relSnippet });
+    }
+    else if (callObj["spans"] !== undefined && callObj["spans"] === true) {
       cb({ "snippet": treeSnippet2 });
     }
     else {
diff --git a/dev/js/spec/matchSpec.js b/dev/js/spec/matchSpec.js
index 68bd981..806aee8 100644
--- a/dev/js/spec/matchSpec.js
+++ b/dev/js/spec/matchSpec.js
@@ -430,7 +430,7 @@
     it('should parse into a tree (async) 1', function (done) {
       var info = matchClass.create(match).info();
       expect(info).toBeTruthy();
-      info.getTree(undefined, undefined, function (treem) {
+      info.getTree(undefined, undefined, "spans", function (treem) {
         tree = treem;
         done();
       });
@@ -475,7 +475,7 @@
 
     it('should add a tree view async 1', function (done) {
       expect(info).toBeTruthy();
-      info.addTree('mate', 'beebop', function () {
+      info.addTree('mate', 'beebop', "spans", function () {
         done();
       });
     });
@@ -565,7 +565,7 @@
     it('should be rendered async 1', function (done) {
       var info = matchClass.create(match).info();
       expect(info).toBeTruthy();
-      info.getTree(undefined, undefined, function (y) {
+      info.getTree(undefined, undefined, "spans", function (y) {
         tree = y;
         done();
       });
diff --git a/dev/js/src/match/info.js b/dev/js/src/match/info.js
index cb01593..7880a63 100644
--- a/dev/js/src/match/info.js
+++ b/dev/js/src/match/info.js
@@ -5,16 +5,16 @@
   'match/infolayer',
   'match/table',
   'match/tree',
+  'match/relations',
   'match/treemenu',
   'match/querycreator',
-  'match/relations',
   'util'
 ], function (infoLayerClass,
 	           matchTableClass,
 	           matchTreeClass,
+             matchRelClass,
 	           matchTreeMenuClass,
-             matchQueryCreator,
-             matchRelClass) {
+             matchQueryCreator) {
   
   // Override 
   KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
@@ -139,7 +139,7 @@
     /**
      * Retrieve and parse snippet for tree representation
      */
-    getTree : function (foundry, layer, cb) {
+    getTree : function (foundry, layer, type, cb) {
       var focus = [];
       
       // TODO: Support and cache multiple trees
@@ -154,7 +154,17 @@
           if (matchResponse["snippet"] !== undefined) {
             // Todo: This should be cached somehow
 
-            cb(matchTreeClass.create(matchResponse["snippet"]));
+            if (type === "spans") {
+              cb(matchTreeClass.create(matchResponse["snippet"]));
+            }
+            else if (type === "rels") {
+              cb(matchRelClass.create(matchResponse["snippet"]));              
+            }
+
+            // Unknown tree type
+            else {
+              cb(null);
+            };
           }
           else {
             cb(null);
@@ -182,7 +192,7 @@
     /**
      * Add a new tree view to the list
      */
-    addTree : function (foundry, layer, cb) {
+    addTree : function (foundry, layer, type, cb) {
       var matchtree = document.createElement('div');
       matchtree.classList.add('matchtree');
       
@@ -213,7 +223,7 @@
       tree.classList.add('loading');
 
       // Get tree data async
-      this.getTree(foundry, layer, function (treeObj) {
+      this.getTree(foundry, layer, type, function (treeObj) {
 
         tree.classList.remove('loading');
 
@@ -224,30 +234,35 @@
         }
         else {
           tree.appendChild(treeObj.element());
+          treeObj.show();
           // Reposition the view to the center
           // (This may in a future release be a reposition
           // to move the root into the center or the actual
           // match)
 
-          var dl = document.createElement('li');
-          dl.className = 'download';
-          dl.addEventListener(
-            'click', function (e) {
+          // This is currently not supported by relations
+          if (type === "spans") {
+            var dl = document.createElement('li');
+            dl.className = 'download';
+            dl.addEventListener(
+              'click', function (e) {
 
-              var a = document.createElement('a');
-              a.setAttribute('href-lang', 'image/svg+xml');
-              a.setAttribute('href', 'data:image/svg+xml;base64,'+treeObj.toBase64());
-              a.setAttribute('download', 'tree.svg');
-              a.target = '_blank';
-
-              document.body.appendChild(a);
-              a.click();
-              document.body.removeChild(a)
-
-              e.halt();
-            }
-          );
-          actions.appendChild(dl);
+                var a = document.createElement('a');
+                a.setAttribute('href-lang', 'image/svg+xml');
+                a.setAttribute('href', 'data:image/svg+xml;base64,'+treeObj.toBase64());
+                a.setAttribute('download', 'tree.svg');
+                a.target = '_blank';
+                
+                document.body.appendChild(a);
+                a.click();
+                document.body.removeChild(a)
+                e.halt();
+              }
+            );
+            
+            actions.appendChild(dl);
+          };
+          
           treeObj.center();
         };
   
@@ -293,11 +308,9 @@
       for (i in spans) {
         treeLayers.push(spans[i]);
       };
-      /*
       for (i in rels) {
         treeLayers.push(rels[i]);
       };
-      */
 
       // Get spans
       treeLayers = treeLayers.sort(
@@ -327,7 +340,8 @@
         menuList.push([
           span.foundry + '/' + span.layer,
           span.foundry,
-          span.layer
+          span.layer,
+          span.type
         ]);
       };
 
diff --git a/dev/js/src/match/relations.js b/dev/js/src/match/relations.js
index 942acee..e3bed9d 100644
--- a/dev/js/src/match/relations.js
+++ b/dev/js/src/match/relations.js
@@ -527,6 +527,26 @@
       this._sortedAnchors = lengthSort(sortedAnchors, true);
     },
 
+    /**
+     * Center the viewport of the canvas
+     * TODO:
+     *   This is identical to tree
+     */
+    center : function () {
+      if (this._element === undefined)
+       return;
+
+      var treeDiv = this._element.parentNode;
+
+      var cWidth = parseFloat(window.getComputedStyle(this._element).width);
+      var treeWidth = parseFloat(window.getComputedStyle(treeDiv).width);
+      // Reposition:
+      if (cWidth > treeWidth) {
+       var scrollValue = (cWidth - treeWidth) / 2;
+       treeDiv.scrollLeft = scrollValue;
+      };
+    },
+
 
     // Show the element
     show : function () {
diff --git a/dev/js/src/match/tree.js b/dev/js/src/match/tree.js
index dc4f229..88f0542 100644
--- a/dev/js/src/match/tree.js
+++ b/dev/js/src/match/tree.js
@@ -162,8 +162,15 @@
       return this;
     },
 
+    // Dummy method to be compatible with relTree
+    show : function () {
+      return;
+    },
+
     /**
      * Center the viewport of the canvas
+     * TODO:
+     *   This is identical to relations
      */
     center : function () {
       if (this._element === undefined)
diff --git a/dev/js/src/match/treeitem.js b/dev/js/src/match/treeitem.js
index c284de3..ad457b1 100644
--- a/dev/js/src/match/treeitem.js
+++ b/dev/js/src/match/treeitem.js
@@ -42,14 +42,23 @@
     },
 
     /**
+     * The type attribute of the menu item.
+     * Is either "spans" or "rels".
+     */
+    type : function () {
+      return this._type;
+    },
+    
+    /**
      * Override click action of the menu item.
      */
     onclick : function (e) {
       var menu = this.menu();
       menu.hide();
       e.halt();
-      if (menu.info() !== undefined)
-	      menu.info().addTree(this._foundry, this._layer);
+      if (menu.info() !== undefined) {
+	      menu.info().addTree(this._foundry, this._layer, this._type);
+      };
     },
 
     // Initialize tree menu item.
@@ -60,6 +69,7 @@
       this._name    = params[0];
       this._foundry = params[1];
       this._layer   = params[2];
+      this._type    = params[3];
       this._content = document.createTextNode(this._name);
       this._lcField = ' ' + this.content().textContent.toLowerCase();
       return this;
diff --git a/dev/js/src/match/treemenu.js b/dev/js/src/match/treemenu.js
index 2ca0731..4c9539d 100644
--- a/dev/js/src/match/treemenu.js
+++ b/dev/js/src/match/treemenu.js
@@ -15,10 +15,10 @@
      * @param params The match menu items
      *   as an array of arrays.
      */
-    create : function (info, params) {
+    create : function (info, list) {
       var obj = Object.create(menuClass)
 	        .upgradeTo(this)
-	        ._init(params, {itemClass : itemClass});
+	        ._init(list, {itemClass : itemClass});
       obj.limit(6);
       obj._info = info;