Major redesign of JS and Sass assets
diff --git a/public/js/demo/all.html b/public/js/demo/all.html
new file mode 100644
index 0000000..aba76c4
--- /dev/null
+++ b/public/js/demo/all.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS demo</title>
+    <meta charset="utf-8" />
+    <script src="../lib/dagre/dagre.min.js"></script>
+    <script src="../src/util.js"></script>
+    <script src="../src/match.js"></script>
+    <script src="../src/menu.js"></script>
+    <script src="../src/matchInfo.js"></script>
+
+    <script src="./all.js"></script>
+
+    <link type="text/css" rel="stylesheet" href="../../css/build/kalamar.css" />
+  </head>
+  <body>
+    <div id="kalamar-bg"></div>
+    <header>
+      <a href="/" class="logo"><h1><span>KorAP - Corpus Analysis Platform</span></h1></a>
+      <form autocomplete="off" action="/kalamar">
+	<div id="searchbar">
+	  <input type="search"
+		 placeholder="Find ..."
+		 name="q"
+		 id="q-field"
+		 autofocus="autofocus" />
+	  <button type="submit"><span>Go</span></button>
+<!--
+	  <i class="fa fa-arrow-circle-down show-hint" onclick="hint.popUp()"></i>
+-->
+	</div>
+	with <span class="select">
+<!-- Change this to js-menu -->
+	  <select name="ql" id="ql-field">
+	    <option value="poliqarp">Poliqarp</option>
+	    <option value="cosmas2">Cosmas II</option>
+	    <option value="annis">Annis</option>
+	    <option value="cql">CQL v1.2</option>
+	  </select>
+	</span>
+	<div id="button-right">
+	  <input type="checkbox"
+		 value="1"
+		 name="cutoff"
+		 class="checkbox"
+		 id="q-cutoff-field" />
+	  <label for="q-cutoff-field"><span></span>Glimpse</label>
+
+	  <!-- Todo: open tutorial - like openTutorial() -->
+	  <a href="/about" title="Tutorial" class="tutorial"><span>Tutorial</span></a>
+
+	</div>
+      </form>
+    </header>
+    <main>
+      <div class="resultinfo">
+	<div id="pagination">
+	  <a rel="prev"><span><span>&lt;</span></span></a>
+	  <a rel="self"><span>1</span></a>
+	  <a href="#2"><span>2</span></a>
+	  <a href="#3"><span>3</span></a>
+	  <a class="ellipsis"><span><span>...</span></span></a>
+	  <a href="#52230"><span>52230</span></a>
+	  <a rel="next" href="#2"><span><span>&gt;</span></span></a>
+	</div>
+	<p class="found">Found <span id="total-results">unknown amount of matches</span> in 59.93ms</p>
+      </div>
+
+      <div id="search">
+	<ol class="align-left">
+	  <li data-corpus-id="WPD"
+	      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"
+	      id="WPD-WWW.03313-p102-103">
+	    <div>
+	      <div class="snippet startMore endMore"><span class="context-left">In diesem Beispiel ist zu sehen, dass die beiden Variablen a und b lediglich ihre Werte an die Funktion </span><span class="match">test</span><span class="context-right"> übergeben, aber im Gegensatz zu einem Referenzparamter dabei unverändert bleiben.</span></div>
+	      <!-- only inject via javascript! -->
+	    </div>
+	    <p class="ref"><strong>Wertparameter</strong> by Hubi,Zwobot,4; published on 2005-03-28 as WWW.03313 (WPD)</p>
+	    <!-- only inject via javascript! -->
+	    <!--
+	    <ul class="action right">
+	      <li class="close" title="Close"><a href="#">Close hit<i class="fa fa-toggle-up"></i></a></li>
+	      <li class="open" title="Open in new tab"><a href="#WPD-WWW.03313-p102-103" target="_blank"><i class="fa fa-external-link-square"></i></a></li>
+	      <li onclick="showTable(this)" title="Annotations"><i class="fa fa-info-circle"></i></li>
+	    </ul>
+-->
+	  </li>
+	  <li data-corpus-id="WPD"
+	      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"
+	      id="WPD-FFF.01460-p119-120">
+	    <div>
+	      <div class="snippet startMore endMore"><span class="context-left">Der Fehler unterläuft häufig bei der direkten Übersetzung aus dem Englischen, wenn im originalen Ausdruck die beiden Wortteile verschiedene Wörter sind und sich das Adjektiv wahlweise auf das erste oder zweite Wort bezieht. Ein Beispiel ist multiples Testproblem für multiple </span><span class="match">test</span><span class="context-right"> problem.</span></div>
+	      <div class="tokenInfo"></div>
+	    </div>
+	    <p class="ref"><strong>Fehlbezogenes Adjektiv</strong> by Joni2,Peterlustig,BWBot; published on 2005-03-28 as FFF.01460 (WPD)</p>
+	    <ul class="action right">
+	      <li class="close" title="Close"><a href="#"><i class="fa fa-toggle-up"></i></a></li>
+	      <li class="open" title="Open in new tab"><a href="#WPD-FFF.01460-p119-120" target="_blank"><i class="fa fa-external-link-square"></i></a></li>
+	      <li onclick="showTable(this)" title="Annotations"><i class="fa fa-info-circle"></i></li>
+	    </ul>
+	  </li>
+
+	  <li data-corpus-id="WPD"
+	      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"
+	      id="WPD-HHH.06056-p2564-2565">
+	    <div>
+	      <div class="snippet startMore endMore"><span class="context-left">HDTV Samples from European (and other) broadcasters and </span><span class="match">test</span><span class="context-right"> transmissions in Europe</span></div>
+	      <div class="tokenInfo"></div>
+	    </div>
+	    <p class="ref"><strong>High Definition Television</strong> by ArtMechanic,TheK,Andreas -horn- Hornig; published on 2005-03-28 as HHH.06056 (WPD)</p>
+	    <ul class="action right">
+	      <li class="close" title="Close"><a href="#"><i class="fa fa-toggle-up"></i></a></li>
+	      <li class="open" title="Open in new tab"><a href="#WPD-HHH.06056-p2564-2565" target="_blank"><i class="fa fa-external-link-square"></i></a></li>
+	      <li onclick="showTable(this)" title="Annotations"><i class="fa fa-info-circle"></i></li>
+	    </ul>
+	  </li>
+      </div>
+      <div id="menu"></div>
+
+      <div class="intro">
+	<p><strong>KorAP</strong> is a new Corpus Analysis Platform, suited for large, multiple annotated corpora and complex search queries independent of particular research questions.</p>
+	<p>This is the new frontend for KorAP, with currently no access to restricted corpora. For full access, please visit the <a href="http://korap.ids-mannheim.de/app">first frontend</a>.</p>
+
+	<p><strong>New to KorAP?</strong> Please check out our <a href="#">tutorial</a>!</p>
+
+	<p>KorAP is developed at the <a href="http://www.ids-mannheim.de">Institute for German Language</a> and funded by the <a href="http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/">Leibniz Association</a>. The separated modules are being published as open source at <a href="http://github.com/KorAP">GitHub</a>.</p>
+      </div>
+      <div id="logos">
+	<div>
+	  <a href="http://www.ids-mannheim.de/" class="logo"><p id="ids-logo"><span>Developed at the Institute for German Language (IDS)</span></p></a>
+	  <a href="http://www.leibniz-gemeinschaft.de/" class="logo"><p id="leibniz-logo"><span>Funded by the Leibniz Association</span></p></a>
+	</div>
+      </div>
+
+    </main>
+  </body>
+</html>
diff --git a/public/js/demo/all.js b/public/js/demo/all.js
new file mode 100644
index 0000000..b0871be
--- /dev/null
+++ b/public/js/demo/all.js
@@ -0,0 +1,168 @@
+var snippet = "<span title=\"cnx/l:meist\">" +
+  "  <span title=\"cnx/p:ADV\">" +
+  "    <span title=\"cnx/syn:@PREMOD\">" +
+  "      <span title=\"mate/l:meist\">" +
+  "        <span title=\"mate/p:ADV\">" +
+  "          <span title=\"opennlp/p:ADV\">meist</span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span title=\"cnx/l:deutlich\">" +
+  "  <span title=\"cnx/p:A\">" +
+  "    <span title=\"cnx/syn:@PREMOD\">" +
+  "      <span title=\"mate/l:deutlich\">" +
+  "        <span title=\"mate/m:degree:pos\">" +
+  "          <span title=\"mate/p:ADJD\">" +
+  "            <span title=\"opennlp/p:ADJD\">deutlich</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span title=\"cnx/l:fähig\">" +
+  "  <span title=\"cnx/l:leistung\">" +
+  "    <span title=\"cnx/p:A\">" +
+  "      <span title=\"cnx/syn:@NH\">" +
+  "        <span title=\"mate/l:leistungsfähig\">" +
+  "          <span title=\"mate/m:degree:comp\">" +
+  "            <span title=\"mate/p:ADJD\">" +
+  "              <span title=\"opennlp/p:ADJD\">leistungsfähiger</span>" +
+  "            </span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>";
+
+var treeSnippet =
+  "<span class=\"context-left\"></span>" +
+  "<span class=\"match\">" +
+  "  <span title=\"xip/c:MC\">" +
+  "    <span title=\"xip/c:TOP\">" +
+  "      <span title=\"xip/c:PP\">" +
+  "        <span title=\"xip/c:PREP\">Mit</span>" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:DET\">dieser</span>" +
+  "          <span title=\"xip/c:NPA\">" +
+  "            <span title=\"xip/c:NOUN\">Methode</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:VERB\">ist</span>" +
+  "      <span title=\"xip/c:NP\">" +
+  "        <span title=\"xip/c:PRON\">es</span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:AP\">" +
+  "        <span title=\"xip/c:ADV\">nun</span>" +
+  "        <span title=\"xip/c:ADJ\">möglich</span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:ADV\">z. B.</span>" +
+  "      <span title=\"xip/c:NPA\">" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:NOUN\">Voice</span>" +
+  "        </span>" +
+  "      </span>" + "(" +
+  "      <span title=\"xip/c:INS\">" +
+  "        <span title=\"xip/c:NPA\">" +
+  "          <span title=\"xip/c:NP\">" +
+  "            <span title=\"xip/c:NOUN\">Sprache</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" + ")" +
+  "      <span title=\"xip/c:VERB\">bevorzugt</span>" +
+  "      <span title=\"xip/c:PP\">" +
+  "        <span title=\"xip/c:PREP\">in</span>" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:PRON\">der</span>" +
+  "        </span>" +
+  "        <span title=\"xip/c:NPA\">" +
+  "          <span title=\"xip/c:NP\">" +
+  "            <span title=\"xip/c:NOUN\">Bridge</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:INFC\">" +
+  "        <span title=\"xip/c:INS\">" +
+  "          <span title=\"xip/c:VERB\">weiterzugeben</span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span class=\"context-right\"></span>";
+
+/*
+var available =[
+  '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'
+];
+*/
+/*
+var match = {
+  'corpusID' : 'WPD',
+  'docID' : 'UUU',
+  'textID' : '01912',
+  'matchID' : 'p121-122'
+};
+*/
+
+var menuContent = [
+    ['cnx/c', 'cnx', 'c'],
+    ['mate/c', 'mate', 'c'],
+    ['base/c', 'base', 'c'],
+    ['xip/c', 'xip', 'c'],
+    ['tt/c', 'tt', 'c']
+];
+
+// Parse and show the table
+// Override getMatchInfo API call
+KorAP.API.getMatchInfo = function(match, callObj) {
+  if (callObj["spans"] !== undefined && callObj["spans"] === true) {
+    return { "snippet": treeSnippet };
+  }
+  else {
+    return { "snippet": snippet };
+  }
+};
+
+/**
+ * Do some things at the beginning.
+ */
+window.onload = function () {
+
+  // Decorate actions
+  KorAP.init();
+
+  var menu = KorAP.MatchTreeMenu.create(
+    undefined,
+    menuContent
+  );
+
+  // Don't hide!!!
+  menu.hide = function () {};
+  document.getElementById('menu').appendChild(menu.element());
+  menu.limit(3);
+  menu.show();
+  menu.focus();
+  /*
+  var e = KorAP.MatchInfo.create(match, available);
+  document.getElementById('WPD-WWW.03313-p102-103').children[0].appendChild(e.element());
+  e.addTree('cnx', 'c');
+  */
+};
diff --git a/public/js/demo/matchInfo.html b/public/js/demo/match.html
similarity index 94%
rename from public/js/demo/matchInfo.html
rename to public/js/demo/match.html
index c79642b..373b0ee 100644
--- a/public/js/demo/matchInfo.html
+++ b/public/js/demo/match.html
@@ -1,14 +1,19 @@
 <!DOCTYPE html>
 <html>
   <head>
-    <title>MatchInfo demo</title>
+    <title>Match demo</title>
     <meta charset="utf-8" />
     <script src="../lib/dagre/dagre.min.js"></script>
     <script src="../src/menu.js"></script>
-    <script src="../src/matchInfo.js"></script>
-    <link type="text/css" rel="stylesheet" href="../../css/font-awesome.min.css"></link>
-    <link type="text/css" rel="stylesheet" href="../../css/build/kalamar.css"></link>
-    <style type="text/css" rel="stylesheet">
+    <script src="../src/match.js"></script>
+    <link type="text/css"
+	  rel="stylesheet"
+	  href="../../css/font-awesome.min.css"></link>
+    <link type="text/css"
+	  rel="stylesheet"
+	  href="../../css/build/kalamar.css"></link>
+    <style type="text/css"
+	   rel="stylesheet">
 
 body {
   background-color: #ffa500;
@@ -146,7 +151,8 @@
   'corpusID' : 'WPD',
   'docID' : 'UUU',
   'textID' : '01912',
-  'pos' : 'p121-122'
+  'pos' : 'p121-122',
+  'available' : available
 };
 
 // Parse and show the table
@@ -160,13 +166,14 @@
   }
 };
 
-var e = KorAP.MatchInfo.create(match, available);
+var e = KorAP.Match.create(match).open();
 
 document.getElementsByTagName('body')[0].appendChild(e.element());
 
-
 e.addTree('cnx', 'c');
 
+
+
 /*
 var t = KorAP.MatchInfo.create(match, available).getTable();
 document.getElementsByClassName('matchtable')[0]
diff --git a/public/js/demo/menu.html b/public/js/demo/menu.html
new file mode 100644
index 0000000..985be65
--- /dev/null
+++ b/public/js/demo/menu.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Menu demo</title>
+    <meta charset="utf-8" />
+    <script src="../src/menu.js"></script>
+<!--
+    <link href="../../css/vc.css" rel="stylesheet" type="text/css"></link>
+-->
+    <link href="../../css/build/kalamar.css" rel="stylesheet" type="text/css"></link>
+    <style type="text/css" rel="stylesheet">
+.info {
+  background-color: #ddd;
+  color: black;
+  padding: 1em;
+  font-family: mono;
+}
+body {
+  margin: 0;
+  background-color: #ddd;
+}
+    </style>
+  </head>
+  <body>
+    <div style="position: absolute; background-color: #7ba400; height: 50px; width: 100%;"></div>
+
+    <div id="menu" class="vc"></div>
+
+    <main>
+      <div>
+	<div id="pagination"><a rel="prev"><span><i class="fa fa-caret-left"></i></span></a><a rel="self"><span>1</span></a><a href="/kalamar?q=der+%5Bmate/m%3Dnumber:sg%5D&amp;ql=poliqarp&amp;p=2"><span>2</span></a><a href="/kalamar?q=der+%5Bmate/m%3Dnumber:sg%5D&amp;ql=poliqarp&amp;p=3"><span>3</span></a><span><i class="fa fa-ellipsis-h"></i></span><a href="/kalamar?q=der+%5Bmate/m%3Dnumber:sg%5D&amp;ql=poliqarp&amp;p=52230"><span>52230</span></a><a rel="next" href="/kalamar?q=der+%5Bmate/m%3Dnumber:sg%5D&amp;ql=poliqarp&amp;p=2"><span><i class="fa fa-caret-right"></i></span></a></div>
+      </div>
+    </main>
+
+    <script>
+KorAP.OwnMenuItem = {
+  create : function (params) {
+    return Object.create(KorAP.MenuItem).upgradeTo(KorAP.OwnMenuItem)._init(params);
+  },
+  content : function (content) {
+    if (arguments.length === 1) {
+      this._content = content;
+    };
+    return this._content;
+  },
+
+  // enter or click
+  onclick : function () {
+    console.log(this._name);
+  },
+
+  // right arrow
+  further : function () {
+    console.log("Further: " + this._name);
+  },
+  _init : function (params) {
+    if (params[0] === undefined)
+      throw new Error("Missing parameters");
+
+    this._name = params[0];
+    this._content = document.createTextNode(this._name);
+    this._lcField = ' ' + this.content().textContent.toLowerCase();
+
+    return this;
+  }
+};
+
+KorAP.OwnPrefix = {
+  create : function () {
+    return Object.create(KorAP.MenuPrefix)
+      .upgradeTo(KorAP.OwnPrefix)
+      ._init();
+  },
+  onclick : function () {
+    console.log('Prefix: ' + this.value());
+  }
+}
+
+KorAP.OwnMenu = {
+  create : function (params) {
+    return Object.create(KorAP.Menu)
+      .upgradeTo(KorAP.OwnMenu)
+      ._init(KorAP.OwnMenuItem, KorAP.OwnPrefix, params);
+  }
+};
+
+var menu = KorAP.OwnMenu.create([
+  ['Titel', 'title', 'string'],
+  ['Untertitel', 'subTitle', 'string'],
+  ['Veröffentlichungsdatum', 'pubDate', 'date'],
+  ['Länge', 'length', 'integer'],
+  ['Autor', 'author', 'string'],
+  ['Genre', 'genre', 'string'],
+  ['corpusID', 'corpusID', 'string'],
+  ['docID', 'docID', 'string'],
+  ['textID', 'textID', 'string']
+]);
+
+    document.getElementById('menu').appendChild(menu.element());
+
+    menu.limit(3);
+    menu.show('');
+    menu.focus();
+
+    </script>
+  </body>
+</html>
diff --git a/public/js/runner/matchInfo.html b/public/js/runner/match.html
similarity index 85%
rename from public/js/runner/matchInfo.html
rename to public/js/runner/match.html
index 7b20a7e..553fae7 100644
--- a/public/js/runner/matchInfo.html
+++ b/public/js/runner/match.html
@@ -10,8 +10,8 @@
   <script src="../lib/jasmine-2.1.1/boot.js"></script>
   <script src="../lib/dagre/dagre.min.js"></script>
   <script src="../src/menu.js"></script>
-  <script src="../src/matchInfo.js"></script>
-  <script src="../spec/matchInfoSpec.js"></script>
+  <script src="../src/match.js"></script>
+  <script src="../spec/matchSpec.js"></script>
 </head>
 <body>
 </body>
diff --git a/public/js/spec/matchInfoSpec.js b/public/js/spec/matchInfoSpec.js
deleted file mode 100644
index b228413..0000000
--- a/public/js/spec/matchInfoSpec.js
+++ /dev/null
@@ -1,338 +0,0 @@
-var available = [
-  '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'
-];
-
-var match = {
-  'corpusID' : 'WPD',
-  'docID' : 'UUU',
-  'textID' : '01912',
-  'pos' : 'p121-122'
-};
-
-var snippet = "<span title=\"cnx/l:meist\">" +
-  "  <span title=\"cnx/p:ADV\">" +
-  "    <span title=\"cnx/syn:@PREMOD\">" +
-  "      <span title=\"mate/l:meist\">" +
-  "        <span title=\"mate/p:ADV\">" +
-  "          <span title=\"opennlp/p:ADV\">meist</span>" +
-  "        </span>" +
-  "      </span>" +
-  "    </span>" +
-  "  </span>" +
-  "</span>" +
-  "<span title=\"cnx/l:deutlich\">" +
-  "  <span title=\"cnx/p:A\">" +
-  "    <span title=\"cnx/syn:@PREMOD\">" +
-  "      <span title=\"mate/l:deutlich\">" +
-  "        <span title=\"mate/m:degree:pos\">" +
-  "          <span title=\"mate/p:ADJD\">" +
-  "            <span title=\"opennlp/p:ADJD\">deutlich</span>" +
-  "          </span>" +
-  "        </span>" +
-  "      </span>" +
-  "    </span>" +
-  "  </span>" +
-  "</span>" +
-  "<span title=\"cnx/l:fähig\">" +
-  "  <span title=\"cnx/l:leistung\">" +
-  "    <span title=\"cnx/p:A\">" +
-  "      <span title=\"cnx/syn:@NH\">" +
-  "        <span title=\"mate/l:leistungsfähig\">" +
-  "          <span title=\"mate/m:degree:comp\">" +
-  "            <span title=\"mate/p:ADJD\">" +
-  "              <span title=\"opennlp/p:ADJD\">leistungsfähiger</span>" +
-  "            </span>" +
-  "          </span>" +
-  "        </span>" +
-  "      </span>" +
-  "    </span>" +
-  "  </span>" +
-  "</span>";
-
-var treeSnippet =
-  "<span class=\"context-left\"></span>" +
-  "<span class=\"match\">" +
-  "  <span title=\"xip/c:MC\">" +
-  "    <span title=\"xip/c:TOP\">" +
-  "      <span title=\"xip/c:PP\">" +
-  "        <span title=\"xip/c:PREP\">Mit</span>" +
-  "        <span title=\"xip/c:NP\">" +
-  "          <span title=\"xip/c:DET\">dieser</span>" +
-  "          <span title=\"xip/c:NPA\">" +
-  "            <span title=\"xip/c:NOUN\">Methode</span>" +
-  "          </span>" +
-  "        </span>" +
-  "      </span>" +
-  "      <span title=\"xip/c:VERB\">ist</span>" +
-  "      <span title=\"xip/c:NP\">" +
-  "        <span title=\"xip/c:PRON\">es</span>" +
-  "      </span>" +
-  "      <span title=\"xip/c:AP\">" +
-  "        <span title=\"xip/c:ADV\">nun</span>" +
-  "        <span title=\"xip/c:ADJ\">möglich</span>" +
-  "      </span>" +
-  "      <span title=\"xip/c:ADV\">z. B.</span>" +
-  "      <span title=\"xip/c:NPA\">" +
-  "        <span title=\"xip/c:NP\">" +
-  "          <span title=\"xip/c:NOUN\">Voice</span>" +
-  "        </span>" +
-  "      </span>" + "(" +
-  "      <span title=\"xip/c:INS\">" +
-  "        <span title=\"xip/c:NPA\">" +
-  "          <span title=\"xip/c:NP\">" +
-  "            <span title=\"xip/c:NOUN\">Sprache</span>" +
-  "          </span>" +
-  "        </span>" +
-  "      </span>" + ")" +
-  "      <span title=\"xip/c:VERB\">bevorzugt</span>" +
-  "      <span title=\"xip/c:PP\">" +
-  "        <span title=\"xip/c:PREP\">in</span>" +
-  "        <span title=\"xip/c:NP\">" +
-  "          <span title=\"xip/c:PRON\">der</span>" +
-  "        </span>" +
-  "        <span title=\"xip/c:NPA\">" +
-  "          <span title=\"xip/c:NP\">" +
-  "            <span title=\"xip/c:NOUN\">Bridge</span>" +
-  "          </span>" +
-  "        </span>" +
-  "      </span>" +
-  "      <span title=\"xip/c:INFC\">" +
-  "        <span title=\"xip/c:INS\">" +
-  "          <span title=\"xip/c:VERB\">weiterzugeben</span>" +
-  "        </span>" +
-  "      </span>" +
-  "    </span>" +
-  "  </span>" +
-  "</span>" +
-  "<span class=\"context-right\"></span>";
-
-describe('KorAP.InfoLayer', function () {
-  it('should be initializable', function () {
-    expect(
-      function() { KorAP.InfoLayer.create() }
-    ).toThrow(new Error("Missing parameters"));
-
-    expect(
-      function() { KorAP.InfoLayer.create("base") }
-    ).toThrow(new Error("Missing parameters"));
-
-    var layer = KorAP.InfoLayer.create("base", "s");
-    expect(layer).toBeTruthy();
-    expect(layer.foundry).toEqual("base");
-    expect(layer.layer).toEqual("s");
-    expect(layer.type).toEqual("tokens");
-
-    layer = KorAP.InfoLayer.create("cnx", "syn", "spans");
-    expect(layer).toBeTruthy();
-    expect(layer.foundry).toEqual("cnx");
-    expect(layer.layer).toEqual("syn");
-    expect(layer.type).toEqual("spans");
-  });
-});
-
-describe('KorAP.Match', function () {
-  var match = {
-    'corpusID' : 'WPD',
-    'docID' : 'UUU',
-    'textID' : '01912',
-    'pos' : 'p121-122'
-  };
-
-  it('should be initializable', function () {
-    var mInfo = KorAP.Match.create(match);
-    expect(mInfo.corpusID).toEqual("WPD");
-  });
-});
-
-describe('KorAP.MatchInfo', function () {
-  it('should be initializable', function () {
-    expect(function() {
-      KorAP.MatchInfo.create()
-    }).toThrow(new Error('Missing parameters'));
-
-    expect(function() {
-      KorAP.MatchInfo.create(available)
-    }).toThrow(new Error('Missing parameters'));
-
-    expect(KorAP.MatchInfo.create(match, available)).toBeTruthy();
-
-    // /corpus/WPD/UUU.01912/p121-122/matchInfo?spans=false&foundry=*
-    var info = KorAP.MatchInfo.create(match, available);
-
-    // Spans:
-    var spans = info.getSpans();
-    expect(spans[0].foundry).toEqual("base");
-    expect(spans[0].layer).toEqual("s");
-
-    expect(spans[1].foundry).toEqual("corenlp");
-    expect(spans[1].layer).toEqual("c");
-
-    expect(spans[2].foundry).toEqual("corenlp");
-    expect(spans[2].layer).toEqual("s");
-
-    expect(spans[spans.length-1].foundry).toEqual("tt");
-    expect(spans[spans.length-1].layer).toEqual("s");
-
-    // Tokens:
-    var tokens = info.getTokens();
-    expect(tokens[0].foundry).toEqual("corenlp");
-    expect(tokens[0].layer).toEqual("ne");
-
-    expect(tokens[1].foundry).toEqual("corenlp");
-    expect(tokens[1].layer).toEqual("p");
-
-    expect(tokens[tokens.length-1].foundry).toEqual("tt");
-    expect(tokens[tokens.length-1].layer).toEqual("p");
-  });
-
-
-  it('should parse into a table', function () {
-    var info = KorAP.MatchInfo.create(match, available);
-
-    expect(info.getTable('base/s')).not.toBeTruthy();
-
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function () {
-      return { "snippet": snippet };
-    };
-
-    var table = info.getTable();
-    expect(table).toBeTruthy();
-
-    expect(table.length()).toBe(3);
-
-    expect(table.getToken(0)).toBe("meist");
-    expect(table.getToken(1)).toBe("deutlich");
-    expect(table.getToken(2)).toBe("leistungsfähiger");
-
-    expect(table.getValue(0, "cnx", "p")[0]).toBe("ADV");
-    expect(table.getValue(0, "cnx", "syn")[0]).toBe("@PREMOD");
-
-    expect(table.getValue(2, "cnx", "l")[0]).toBe("fähig");
-    expect(table.getValue(2, "cnx", "l")[1]).toBe("leistung");
-  });
-
-
-  it('should parse into a tree', function () {
-    var info = KorAP.MatchInfo.create(match, available);
-
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function () {
-      return { "snippet": treeSnippet };
-    };
-
-    var tree = info.getTree();
-    expect(tree).toBeTruthy();
-    expect(tree.nodes()).toEqual(49);
-  });
-});
-
-describe('KorAP.MatchTable', function () {
-  it('should be rendered', function () {
-    var info = KorAP.MatchInfo.create(match, available);
-
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function() {
-      return { "snippet": snippet };
-    };
-
-    var table = info.getTable();
-    var e = table.element();
-
-    expect(e.nodeName).toBe('TABLE');
-    expect(e.children[0].nodeName).toBe('THEAD');
-    var tr = e.children[0].children[0];
-    expect(tr.nodeName).toBe('TR');
-    expect(tr.children[0].nodeName).toBe('TH');
-
-    expect(tr.children[0].firstChild.nodeValue).toBe('Foundry');
-    expect(tr.children[1].firstChild.nodeValue).toBe('Layer');
-    expect(tr.children[2].firstChild.nodeValue).toBe('meist');
-    expect(tr.children[3].firstChild.nodeValue).toBe('deutlich');
-    expect(tr.children[4].firstChild.nodeValue).toBe('leistungsfähiger');
-
-    // first row
-    tr = e.children[1].children[0];
-    expect(tr.nodeName).toBe('TR');
-    expect(tr.getAttribute('tabindex')).toEqual('0');
-    expect(tr.children[0].nodeName).toBe('TH');
-    expect(tr.children[0].firstChild.nodeValue).toEqual('cnx');
-    expect(tr.children[1].firstChild.nodeValue).toEqual('l');
-    expect(tr.children[2].firstChild.nodeValue).toEqual('meist');
-    expect(tr.children[3].firstChild.nodeValue).toEqual('deutlich');
-    expect(tr.children[4].firstChild.nodeValue).toEqual('fähig');
-    expect(tr.children[4].lastChild.nodeValue).toEqual('leistung');
-
-    // second row
-    tr = e.children[1].children[1];
-    expect(tr.nodeName).toBe('TR');
-    expect(tr.getAttribute('tabindex')).toEqual('0');
-    expect(tr.children[0].nodeName).toBe('TH');
-    expect(tr.children[0].firstChild.nodeValue).toEqual('cnx');
-    expect(tr.children[1].firstChild.nodeValue).toEqual('p');
-    expect(tr.children[2].firstChild.nodeValue).toEqual('ADV');
-    expect(tr.children[3].firstChild.nodeValue).toEqual('A');
-    expect(tr.children[4].firstChild.nodeValue).toEqual('A');
-  });
-});
-
-describe('KorAP.MatchTree', function () {
-  it('should be rendered', function () {
-    var info = KorAP.MatchInfo.create(match, available);
-
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function() {
-      return { "snippet": treeSnippet };
-    };
-
-    var tree = info.getTree();
-
-    var e = tree.element();
-    expect(e.nodeName).toEqual('svg');
-    expect(e.getElementsByTagName('g').length).toEqual(48);
-  });
-});
-
-describe('KorAP.MatchTreeItem', function () {
-  it('should be initializable', function () {
-    var mi = KorAP.MatchTreeItem.create(['cnx/c', 'cnx', 'c'])
-    expect(mi.element().firstChild.nodeValue).toEqual('cnx/c');
-    expect(mi.lcField()).toEqual(' cnx/c');
-    expect(mi.foundry()).toEqual('cnx');
-    expect(mi.layer()).toEqual('c');
-  });
-});
-
-describe('KorAP.MatchTreeMenu', function () {
-  it('should be initializable', function () {
-    var menu = KorAP.MatchTreeMenu.create([
-      ['cnx/c', 'cnx', 'c'],
-      ['xip/c', 'xip', 'c']
-    ]);
-
-    expect(menu.itemClass()).toEqual(KorAP.MatchTreeItem);
-    expect(menu.element().nodeName).toEqual('UL');
-    expect(menu.element().style.opacity).toEqual("0");
-    expect(menu.limit()).toEqual(6);
-  });
-});
-
-// table = view.toTable();
-// table.sortBy('');
-// table.element();
-// tree = view.toTree();
-// tree.element();
-
diff --git a/public/js/spec/matchSpec.js b/public/js/spec/matchSpec.js
new file mode 100644
index 0000000..dc54dc6
--- /dev/null
+++ b/public/js/spec/matchSpec.js
@@ -0,0 +1,522 @@
+var available = [
+  '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'
+];
+
+var match = {
+  'corpusID' : 'WPD',
+  'docID' : 'UUU',
+  'textID' : '01912',
+  'matchID' : 'p121-122',
+  'available' : available
+};
+
+var snippet = "<span title=\"cnx/l:meist\">" +
+  "  <span title=\"cnx/p:ADV\">" +
+  "    <span title=\"cnx/syn:@PREMOD\">" +
+  "      <span title=\"mate/l:meist\">" +
+  "        <span title=\"mate/p:ADV\">" +
+  "          <span title=\"opennlp/p:ADV\">meist</span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span title=\"cnx/l:deutlich\">" +
+  "  <span title=\"cnx/p:A\">" +
+  "    <span title=\"cnx/syn:@PREMOD\">" +
+  "      <span title=\"mate/l:deutlich\">" +
+  "        <span title=\"mate/m:degree:pos\">" +
+  "          <span title=\"mate/p:ADJD\">" +
+  "            <span title=\"opennlp/p:ADJD\">deutlich</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span title=\"cnx/l:fähig\">" +
+  "  <span title=\"cnx/l:leistung\">" +
+  "    <span title=\"cnx/p:A\">" +
+  "      <span title=\"cnx/syn:@NH\">" +
+  "        <span title=\"mate/l:leistungsfähig\">" +
+  "          <span title=\"mate/m:degree:comp\">" +
+  "            <span title=\"mate/p:ADJD\">" +
+  "              <span title=\"opennlp/p:ADJD\">leistungsfähiger</span>" +
+  "            </span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>";
+
+var treeSnippet =
+  "<span class=\"context-left\"></span>" +
+  "<span class=\"match\">" +
+  "  <span title=\"xip/c:MC\">" +
+  "    <span title=\"xip/c:TOP\">" +
+  "      <span title=\"xip/c:PP\">" +
+  "        <span title=\"xip/c:PREP\">Mit</span>" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:DET\">dieser</span>" +
+  "          <span title=\"xip/c:NPA\">" +
+  "            <span title=\"xip/c:NOUN\">Methode</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:VERB\">ist</span>" +
+  "      <span title=\"xip/c:NP\">" +
+  "        <span title=\"xip/c:PRON\">es</span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:AP\">" +
+  "        <span title=\"xip/c:ADV\">nun</span>" +
+  "        <span title=\"xip/c:ADJ\">möglich</span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:ADV\">z. B.</span>" +
+  "      <span title=\"xip/c:NPA\">" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:NOUN\">Voice</span>" +
+  "        </span>" +
+  "      </span>" + "(" +
+  "      <span title=\"xip/c:INS\">" +
+  "        <span title=\"xip/c:NPA\">" +
+  "          <span title=\"xip/c:NP\">" +
+  "            <span title=\"xip/c:NOUN\">Sprache</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" + ")" +
+  "      <span title=\"xip/c:VERB\">bevorzugt</span>" +
+  "      <span title=\"xip/c:PP\">" +
+  "        <span title=\"xip/c:PREP\">in</span>" +
+  "        <span title=\"xip/c:NP\">" +
+  "          <span title=\"xip/c:PRON\">der</span>" +
+  "        </span>" +
+  "        <span title=\"xip/c:NPA\">" +
+  "          <span title=\"xip/c:NP\">" +
+  "            <span title=\"xip/c:NOUN\">Bridge</span>" +
+  "          </span>" +
+  "        </span>" +
+  "      </span>" +
+  "      <span title=\"xip/c:INFC\">" +
+  "        <span title=\"xip/c:INS\">" +
+  "          <span title=\"xip/c:VERB\">weiterzugeben</span>" +
+  "        </span>" +
+  "      </span>" +
+  "    </span>" +
+  "  </span>" +
+  "</span>" +
+  "<span class=\"context-right\"></span>";
+
+
+function matchElementFactory () {
+  var me = document.createElement('li');
+
+  me.setAttribute(
+    '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');
+
+  me.setAttribute('data-corpus-id', 'WPD');
+  me.setAttribute('data-doc-id', 'FFF');
+  me.setAttribute('data-text-id', '01460');
+  me.setAttribute('data-match-id', 'p119-120');
+  me.innerHTML = '<div><div class="snippet">check</div></div><p class="ref">me</p>';
+  return me;
+};
+
+
+describe('KorAP.InfoLayer', function () {
+
+  it('should be initializable', function () {
+    expect(
+      function() { KorAP.InfoLayer.create() }
+    ).toThrow(new Error("Missing parameters"));
+
+    expect(
+      function() { KorAP.InfoLayer.create("base") }
+    ).toThrow(new Error("Missing parameters"));
+
+    var layer = KorAP.InfoLayer.create("base", "s");
+    expect(layer).toBeTruthy();
+    expect(layer.foundry).toEqual("base");
+    expect(layer.layer).toEqual("s");
+    expect(layer.type).toEqual("tokens");
+
+    layer = KorAP.InfoLayer.create("cnx", "syn", "spans");
+    expect(layer).toBeTruthy();
+    expect(layer.foundry).toEqual("cnx");
+    expect(layer.layer).toEqual("syn");
+    expect(layer.type).toEqual("spans");
+  });
+});
+
+
+describe('KorAP.Match', function () {
+  var match = {
+    'corpusID'  : 'WPD',
+    'docID'     : 'UUU',
+    'textID'    : '01912',
+    'matchID'   : 'p121-122',
+    'available' : available
+  };
+
+  it('should be initializable by Object', function () {
+    expect(function() {
+      KorAP.Match.create()
+    }).toThrow(new Error('Missing parameters'));
+
+    expect(KorAP.Match.create(match)).toBeTruthy();
+
+    var m = KorAP.Match.create(match);
+    expect(m.corpusID).toEqual("WPD");
+    expect(m.docID).toEqual("UUU");
+    expect(m.textID).toEqual("01912");
+    expect(m.matchID).toEqual("p121-122");
+
+    // /corpus/WPD/UUU.01912/p121-122/matchInfo?spans=false&foundry=*
+    var m = KorAP.Match.create(match);
+
+    // Spans:
+    var spans = m.getSpans();
+    expect(spans[0].foundry).toEqual("base");
+    expect(spans[0].layer).toEqual("s");
+
+    expect(spans[1].foundry).toEqual("corenlp");
+    expect(spans[1].layer).toEqual("c");
+
+    expect(spans[2].foundry).toEqual("corenlp");
+    expect(spans[2].layer).toEqual("s");
+
+    expect(spans[spans.length-1].foundry).toEqual("tt");
+    expect(spans[spans.length-1].layer).toEqual("s");
+
+    // Tokens:
+    var tokens = m.getTokens();
+    expect(tokens[0].foundry).toEqual("corenlp");
+    expect(tokens[0].layer).toEqual("ne");
+
+    expect(tokens[1].foundry).toEqual("corenlp");
+    expect(tokens[1].layer).toEqual("p");
+
+    expect(tokens[tokens.length-1].foundry).toEqual("tt");
+    expect(tokens[tokens.length-1].layer).toEqual("p");
+  });
+
+
+  it('should be initializable by Node', function () {
+    var m = KorAP.Match.create(matchElementFactory());
+    expect(m.corpusID).toEqual("WPD");
+    expect(m.docID).toEqual("FFF");
+    expect(m.textID).toEqual("01460");
+    expect(m.matchID).toEqual("p119-120");
+
+    // Spans:
+    var spans = m.getSpans();
+    expect(spans[0].foundry).toEqual("base");
+    expect(spans[0].layer).toEqual("s");
+
+    expect(spans[1].foundry).toEqual("corenlp");
+    expect(spans[1].layer).toEqual("c");
+
+    expect(spans[2].foundry).toEqual("corenlp");
+    expect(spans[2].layer).toEqual("s");
+
+    expect(spans[spans.length-1].foundry).toEqual("tt");
+    expect(spans[spans.length-1].layer).toEqual("s");
+
+    // Tokens:
+    var tokens = m.getTokens();
+    expect(tokens[0].foundry).toEqual("corenlp");
+    expect(tokens[0].layer).toEqual("ne");
+
+    expect(tokens[1].foundry).toEqual("corenlp");
+    expect(tokens[1].layer).toEqual("p");
+
+    expect(tokens[tokens.length-1].foundry).toEqual("tt");
+    expect(tokens[tokens.length-1].layer).toEqual("p");
+
+  });
+
+  it('should react to gui actions', function () {
+    var e = matchElementFactory();
+
+    expect(e.classList.contains('active')).toBe(false);
+    expect(e["_match"]).toBe(undefined);
+
+    var m = KorAP.Match.create(e);
+
+    expect(e.classList.contains('active')).toBe(false);
+    expect(e["_match"]).not.toBe(undefined);
+
+    // Open the match
+    m.open();
+
+    expect(e.classList.contains('active')).toBe(true);
+    expect(e["_match"]).not.toBe(undefined);
+
+    // Close the match
+    m.close();
+    expect(e.classList.contains('active')).toBe(false);
+    expect(e["_match"]).not.toBe(undefined);
+
+  });
+});
+
+
+describe('KorAP.MatchInfo', function () {
+  it('should parse into a table', function () {
+
+    var m = KorAP.Match.create(match);
+    var info = m.info();
+    expect(m._info).toEqual(info);
+
+    expect(info.getTable('base/s')).not.toBeTruthy();
+
+    // Override getMatchInfo API call
+    KorAP.API.getMatchInfo = function () {
+      return { "snippet": snippet };
+    };
+
+    var table = info.getTable();
+    expect(table).toBeTruthy();
+
+    expect(table.length()).toBe(3);
+
+    expect(table.getToken(0)).toBe("meist");
+    expect(table.getToken(1)).toBe("deutlich");
+    expect(table.getToken(2)).toBe("leistungsfähiger");
+
+    expect(table.getValue(0, "cnx", "p")[0]).toBe("ADV");
+    expect(table.getValue(0, "cnx", "syn")[0]).toBe("@PREMOD");
+
+    expect(table.getValue(2, "cnx", "l")[0]).toBe("fähig");
+    expect(table.getValue(2, "cnx", "l")[1]).toBe("leistung");
+  });
+
+
+  it('should parse into a table view', function () {
+    var matchElement = matchElementFactory();
+    expect(matchElement.tagName).toEqual('LI');
+
+    // Match
+    expect(matchElement.children[0].tagName).toEqual('DIV');
+
+    // snippet
+    expect(matchElement.children[0].children[0].tagName).toEqual('DIV');
+    expect(matchElement.children[0].children[0].classList.contains('snippet')).toBeTruthy();
+    expect(matchElement.children[0].children[0].firstChild.nodeValue).toEqual('check');
+
+    // reference
+    expect(matchElement.children[1].classList.contains('ref')).toBeTruthy();
+    expect(matchElement.children[1].firstChild.nodeValue).toEqual('me');
+
+    // not yet
+    expect(matchElement.children[0].children[1]).toBe(undefined);
+
+    var info = KorAP.Match.create(matchElement).info();
+
+    // Match
+    expect(matchElement.children[0].tagName).toEqual('DIV');
+
+    // snippet
+    expect(matchElement.children[0].children[0].tagName).toEqual('DIV');
+    expect(matchElement.children[0].children[0].classList.contains('snippet')).toBeTruthy();
+    expect(matchElement.children[0].children[0].firstChild.nodeValue).toEqual('check');
+
+    // reference
+    expect(matchElement.children[1].classList.contains('ref')).toBeTruthy();
+    expect(matchElement.children[1].firstChild.nodeValue).toEqual('me');
+
+    // now
+    var infotable = matchElement.children[0].children[1];
+    expect(infotable.tagName).toEqual('DIV');
+    expect(infotable.classList.contains('matchinfo')).toBeTruthy();
+
+    expect(infotable.children[0].classList.contains('matchtable')).toBeTruthy();
+    expect(infotable.children[1].classList.contains('addtree')).toBeTruthy();
+  });
+
+
+  it('should parse into a tree', function () {
+    var info = KorAP.Match.create(match).info();
+
+    // Override getMatchInfo API call
+    KorAP.API.getMatchInfo = function () {
+      return { "snippet": treeSnippet };
+    };
+
+    var tree = info.getTree();
+    expect(tree).toBeTruthy();
+    expect(tree.nodes()).toEqual(49);
+  });
+
+
+  it('should parse into a tree view', function () {
+    var matchElement = matchElementFactory();
+    expect(matchElement.tagName).toEqual('LI');
+
+    var info = KorAP.Match.create(matchElement).info();
+
+    // Match
+    expect(matchElement.children[0].tagName).toEqual('DIV');
+
+    // snippet
+    expect(matchElement.children[0].children[0].tagName).toEqual('DIV');
+    expect(matchElement.children[0].children[0].classList.contains('snippet')).toBeTruthy();
+    expect(matchElement.children[0].children[0].firstChild.nodeValue).toEqual('check');
+
+    // reference
+    expect(matchElement.children[1].classList.contains('ref')).toBeTruthy();
+    expect(matchElement.children[1].firstChild.nodeValue).toEqual('me');
+
+    // now
+    var infotable = matchElement.children[0].children[1];
+    expect(infotable.tagName).toEqual('DIV');
+    expect(infotable.classList.contains('matchinfo')).toBeTruthy();
+    expect(infotable.children[0].classList.contains('matchtable')).toBeTruthy();
+    expect(infotable.children[1].classList.contains('addtree')).toBeTruthy();
+
+    // Override getMatchInfo API call
+    KorAP.API.getMatchInfo = function () {
+      return { "snippet": treeSnippet };
+    };
+
+    info.addTree('mate', 'beebop');
+
+    // With added tree
+    var infotable = matchElement.children[0].children[1];
+    expect(infotable.tagName).toEqual('DIV');
+    expect(infotable.classList.contains('matchinfo')).toBeTruthy();
+    expect(infotable.children[0].classList.contains('matchtable')).toBeTruthy();
+    expect(infotable.children[1].classList.contains('addtree')).toBe(false);
+
+    var tree = infotable.children[1];
+    expect(tree.tagName).toEqual('DIV');
+    expect(tree.classList.contains('matchtree')).toBeTruthy();
+    expect(tree.children[0].tagName).toEqual('H6');
+    expect(tree.children[0].children[0].tagName).toEqual('SPAN');
+    expect(tree.children[0].children[0].firstChild.nodeValue).toEqual('mate');
+    expect(tree.children[0].children[1].tagName).toEqual('SPAN');
+    expect(tree.children[0].children[1].firstChild.nodeValue).toEqual('beebop');
+
+    expect(tree.children[1].tagName).toEqual('DIV');
+    
+  });
+});
+
+
+describe('KorAP.MatchTable', function () {
+  it('should be rendered', function () {
+    var info = KorAP.Match.create(match).info();
+
+    // Override getMatchInfo API call
+    KorAP.API.getMatchInfo = function() {
+      return { "snippet": snippet };
+    };
+
+    var table = info.getTable();
+    var e = table.element();
+
+    expect(e.nodeName).toBe('TABLE');
+    expect(e.children[0].nodeName).toBe('THEAD');
+    var tr = e.children[0].children[0];
+    expect(tr.nodeName).toBe('TR');
+    expect(tr.children[0].nodeName).toBe('TH');
+
+    expect(tr.children[0].firstChild.nodeValue).toBe('Foundry');
+    expect(tr.children[1].firstChild.nodeValue).toBe('Layer');
+    expect(tr.children[2].firstChild.nodeValue).toBe('meist');
+    expect(tr.children[3].firstChild.nodeValue).toBe('deutlich');
+    expect(tr.children[4].firstChild.nodeValue).toBe('leistungsfähiger');
+
+    // first row
+    tr = e.children[1].children[0];
+    expect(tr.nodeName).toBe('TR');
+    expect(tr.getAttribute('tabindex')).toEqual('0');
+    expect(tr.children[0].nodeName).toBe('TH');
+    expect(tr.children[0].firstChild.nodeValue).toEqual('cnx');
+    expect(tr.children[1].firstChild.nodeValue).toEqual('l');
+    expect(tr.children[2].firstChild.nodeValue).toEqual('meist');
+    expect(tr.children[3].firstChild.nodeValue).toEqual('deutlich');
+    expect(tr.children[4].firstChild.nodeValue).toEqual('fähig');
+    expect(tr.children[4].lastChild.nodeValue).toEqual('leistung');
+
+    // second row
+    tr = e.children[1].children[1];
+    expect(tr.nodeName).toBe('TR');
+    expect(tr.getAttribute('tabindex')).toEqual('0');
+    expect(tr.children[0].nodeName).toBe('TH');
+    expect(tr.children[0].firstChild.nodeValue).toEqual('cnx');
+    expect(tr.children[1].firstChild.nodeValue).toEqual('p');
+    expect(tr.children[2].firstChild.nodeValue).toEqual('ADV');
+    expect(tr.children[3].firstChild.nodeValue).toEqual('A');
+    expect(tr.children[4].firstChild.nodeValue).toEqual('A');
+  });
+});
+
+describe('KorAP.MatchTree', function () {
+  it('should be rendered', function () {
+    var info = KorAP.Match.create(match).info();
+
+    // Override getMatchInfo API call
+    KorAP.API.getMatchInfo = function() {
+      return { "snippet": treeSnippet };
+    };
+
+    var tree = info.getTree();
+
+    var e = tree.element();
+    expect(e.nodeName).toEqual('svg');
+    expect(e.getElementsByTagName('g').length).toEqual(48);
+  });
+});
+
+
+describe('KorAP.MatchTreeItem', function () {
+  it('should be initializable', function () {
+    var mi = KorAP.MatchTreeItem.create(['cnx/c', 'cnx', 'c'])
+    expect(mi.element().firstChild.nodeValue).toEqual('cnx/c');
+    expect(mi.lcField()).toEqual(' cnx/c');
+    expect(mi.foundry()).toEqual('cnx');
+    expect(mi.layer()).toEqual('c');
+  });
+});
+
+
+describe('KorAP.MatchTreeMenu', function () {
+  it('should be initializable', function () {
+    var menu = KorAP.MatchTreeMenu.create(undefined, [
+      ['cnx/c', 'cnx', 'c'],
+      ['xip/c', 'xip', 'c']
+    ]);
+
+    expect(menu.itemClass()).toEqual(KorAP.MatchTreeItem);
+    expect(menu.element().nodeName).toEqual('UL');
+    expect(menu.element().style.opacity).toEqual("0");
+    expect(menu.limit()).toEqual(6);
+    menu.show();
+    expect(menu.item(0).active()).toBe(true);
+  });
+});
+
+// table = view.toTable();
+// table.sortBy('');
+// table.element();
+// tree = view.toTree();
+// tree.element();
+
diff --git a/public/js/spec/menuSpec.js b/public/js/spec/menuSpec.js
index 7419f83..6692753 100644
--- a/public/js/spec/menuSpec.js
+++ b/public/js/spec/menuSpec.js
@@ -1051,4 +1051,7 @@
 
   xit('should be page downable');
   xit('should be page upable');
+
+  xit('should scroll to a chosen value')
+  xit('should highlight a chosen value')
 });
diff --git a/public/js/src/matchInfo.js b/public/js/src/match.js
similarity index 77%
rename from public/js/src/matchInfo.js
rename to public/js/src/match.js
index 30bd7b5..f8fdd91 100644
--- a/public/js/src/matchInfo.js
+++ b/public/js/src/match.js
@@ -1,11 +1,11 @@
 /**
- * Visualize annotations.
+ * Get information on matches,
+ * generate annotation tables and trees.
  *
  * @author Nils Diewald
  */
-// require menu.js
+// require menu.js, dagre
 /*
- * - Scroll with a static left legend.
  * - Highlight (at least mark as bold) the match
  * - Scroll to match vertically per default
  */
@@ -23,11 +23,13 @@
 
   // Localization values
   var loc   = (KorAP.Locale = KorAP.Locale || {} );
-  loc.ADDTREE = loc.ADDTREE || 'Add tree view';
+  loc.ADDTREE  = loc.ADDTREE  || 'Add tree view';
+  loc.SHOWINFO = loc.SHOWINFO || 'Show information';
+  loc.CLOSE    = loc.CLOSE    || 'Close';
 
   KorAP._AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
-  KorAP._TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
-  KorAP._matchTerms  = ["corpusID", "docID", "textID"];
+  KorAP._TermRE      = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+  KorAP._matchTerms  = ['corpusID', 'docID', 'textID', 'matchID', 'available'];
 
   // API requests
   KorAP.API = KorAP.API || {};
@@ -35,42 +37,75 @@
   // TODO: Make this async
   KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
 
-  KorAP.MatchInfo = {
+
+  /**
+   * Match object
+   */
+  KorAP.Match = {
 
     /**
      * Create a new annotation object.
      * Expects an array of available foundry/layer=type terms.
      * Supported types are 'spans', 'tokens' and 'rels'.
      */
-    create : function (match, available) {
-      if (arguments.length < 2)
-	throw new Error("Missing parameters");
-
-      return Object.create(KorAP.MatchInfo)._init(match, available);
+    create : function (match) {
+      return Object.create(KorAP.Match)._init(match);
     },
 
     /**
-     * Destroy this match information view.
+     * Initialize match.
      */
-    destroy : function () {
+    _init : function (match) {
+      this._element = null;
 
-      // Remove circular reference
-      if (this._treeMenu !== undefined)
-	delete this._treeMenu["info"];
+      // No match defined
+      if (arguments.length < 1 ||
+	  match === null ||
+	  match === undefined) {
+	throw new Error('Missing parameters');
+      }
 
-      this._treeMenu.destroy();
-      this._treeMenu = undefined;
-    },
+      // Match defined as a node
+      else if (match instanceof Node) {
+	this._element  = match;
 
-    _init : function (match, available) {
-      this._match = KorAP.Match.create(match);
+	// Circular reference !!
+	match["_match"] = this;
+
+	this.corpusID  = match.getAttribute('data-corpus-id'),
+	this.docID     = match.getAttribute('data-doc-id'),
+	this.textID    = match.getAttribute('data-text-id'),
+	this.matchID   = match.getAttribute('data-match-id')
+
+	// List of available annotations
+	this.available = match.getAttribute('data-available-info').split(' ');
+      }
+
+      // Match as an object
+      else {
+
+	// Iterate over allowed match terms
+	for (var i in KorAP._matchTerms) {
+	  var term = KorAP._matchTerms[i];
+	  if (match[term] !== undefined) {
+	    this[term] = match[term];
+	  }
+	  else {
+	    this[term] = undefined;
+	  }
+	};
+      };
+
       this._available = {
 	tokens : [],
-	spans : [],
-	rels : []
+	spans  : [],
+	rels   : []
       };
-      for (var i = 0; i < available.length; i++) {
-	var term = available[i];
+
+      // Iterate over info layers
+      for (var i = 0; i < this.available.length; i++) {
+	var term = this.available[i];
+
 	// Create info layer objects
 	try {
 	  var layer = KorAP.InfoLayer.create(term);
@@ -80,6 +115,7 @@
 	  continue;
 	};
       };
+
       return this;
     },
 
@@ -107,16 +143,160 @@
       return this._available.rels;
     },
 
+    /**
+     * Open match
+     */
+    open : function () {
+
+      // Add actions unless it's already activated
+      var element = this._element;
+
+      // There is an element to open
+      if (this._element === undefined || this._element === null)
+	return false;
+
+      // The element is already opened
+      if (element.classList.contains('active'))
+	return false;
+      
+      // Add active class to element
+      element.classList.add('active');
+
+      // Create action buttons
+      var ul = document.createElement('ul');
+      ul.classList.add('action', 'right');
+      element.appendChild(ul);
+
+      // Use localization
+      var loc = KorAP.Locale;
+
+      // Add close button
+      var close = document.createElement('li');
+      close.appendChild(document.createElement('span'))
+	.appendChild(document.createTextNode(loc.CLOSE));
+      close.classList.add('close');
+      close.setAttribute('title', loc.CLOSE);
+
+      // Add info button
+      var info = document.createElement('li');
+      info.appendChild(document.createElement('span'))
+	.appendChild(document.createTextNode(loc.SHOWINFO));
+      info.classList.add('info');
+      info.setAttribute('title', loc.SHOWINFO);
+
+      var that = this;
+
+      // Close match
+      close.addEventListener('click', function (e) {
+	e.halt();
+	that.close()
+      });
+
+      // Add information, unless it already exists
+      info.addEventListener('click', function (e) {
+	e.halt();
+	that.info();
+      });
+
+      ul.appendChild(close);
+      ul.appendChild(info);
+
+      return true;
+    },
+
 
     /**
-     * Get table object.
+     * Close info view
+     */
+    close : function () {
+      this._element.classList.remove('active');
+
+/*
+      if (this._info !== undefined) {
+	this._info.destroy();
+      };
+*/
+    },
+
+
+
+    /**
+     * Get and open associated match info.
+     */
+    info : function () {
+
+      // Create match info
+      if (this._info === undefined)
+	this._info = KorAP.MatchInfo.create(this);
+
+      // There is an element to append
+      if (this._element === undefined ||
+	  this._element === null)
+	return this._info;
+
+      // Info is already activated
+      if (this._info._elemet !== undefined)
+	return this._info;
+
+      // Append element to match
+      this._element.children[0].appendChild(
+	this._info.element()
+      );
+
+      return this._info;
+    },
+
+
+    /**
+     * Get match element.
+     */
+    element : function () {
+
+      // May be null
+      return this._element;
+    }
+  };
+
+
+
+  /**
+   * Information about a match.
+   */
+  KorAP.MatchInfo = {
+
+    /**
+     * Create new object
+     */
+    create : function (match) {
+      return Object.create(KorAP.MatchInfo)._init(match);
+    },
+
+    /**
+     * Initialize object
+     */
+    _init : function (match) {
+      this._match = match;
+      return this;
+    },
+
+
+    /**
+     * Get match object
+     */
+    match : function () {
+      return this._match;
+    },
+
+
+    /**
+     * Retrieve and parse snippet for table representation
      */
     getTable : function (tokens) {
       var focus = [];
 
       // Get all tokens
       if (tokens === undefined) {
-	focus = this.getTokens();
+	focus = this._match.getTokens();
       } 
 
       // Get only some tokens
@@ -154,10 +334,15 @@
 	return this._table;
       };
 
+      // Todo: Store the table as a hash of the focus
+
       return null;
     },
 
-    // Parse snippet for table visualization
+
+    /**
+     * Retrieve and parse snippet for tree representation
+     */
     getTree : function (foundry, layer) {
       var focus = [];
 
@@ -182,6 +367,23 @@
     },
 
     /**
+     * Destroy this match information view.
+     */
+    destroy : function () {
+
+      // Remove circular reference
+      if (this._treeMenu !== undefined)
+	delete this._treeMenu["info"];
+
+      this._treeMenu.destroy();
+      this._treeMenu = undefined;
+      this._match = undefined;
+
+      // Element destroy
+    },
+
+
+    /**
      * Add a new tree view to the list
      */
     addTree : function (foundry, layer) {
@@ -225,6 +427,7 @@
      * Create match information view.
      */
     element : function () {
+
       if (this._element !== undefined)
 	return this._element;
 
@@ -238,7 +441,8 @@
       matchtable.appendChild(this.getTable().element());
       info.appendChild(matchtable);
 
-      var spanLayers = this.getSpans().sort(
+      // Get spans
+      var spanLayers = this._match.getSpans().sort(
 	function (a, b) {
 	  if (a.foundry < b.foundry) {
 	    return -1;
@@ -272,6 +476,7 @@
       // Create tree menu
       var treemenu = this.treeMenu(menuList);
       var span = info.appendChild(document.createElement('p'));
+      span.classList.add('addtree');
       span.appendChild(document.createTextNode(loc.ADDTREE));
 
       var treeElement = treemenu.element();
@@ -285,8 +490,15 @@
       this._element = info;
 
       return info;
+
     },
 
+
+    /**
+     * Get tree menu.
+     * There is only one menu rendered
+     * - no matter how many trees exist
+     */
     treeMenu : function (list) {
       if (this._treeMenu !== undefined)
 	return this._treeMenu;
@@ -295,23 +507,7 @@
     }
   };
 
-  KorAP.Match = {
-    create : function (match) {
-      return Object.create(KorAP.Match)._init(match);
-    },
-    _init : function (match) {
-      for (var i in KorAP._matchTerms) {
-	var term = KorAP._matchTerms[i];
-	if (match[term] !== undefined) {
-	  this[term] = match[term];
-	}
-	else {
-	  this[term] = undefined;
-	}
-      };
-      return this;
-    },
-  };
+
 
   /**
    *
@@ -765,7 +961,8 @@
       var menu = this.menu();
       menu.hide();
       e.halt();
-      menu.info().addTree(this._foundry, this._layer);
+      if (menu.info() !== undefined)
+	menu.info().addTree(this._foundry, this._layer);
     },
     
     _init : function (params) {
@@ -791,6 +988,7 @@
 	.upgradeTo(KorAP.MatchTreeMenu)
 	._init(KorAP.MatchTreeItem, undefined, params);
       obj.limit(6);
+
       obj._info = info;
 
       // This is only domspecific
diff --git a/public/js/src/util.js b/public/js/src/util.js
new file mode 100644
index 0000000..0f0f114
--- /dev/null
+++ b/public/js/src/util.js
@@ -0,0 +1,134 @@
+/**
+ * These are utility functions for the frontend
+ */
+
+// Add toggleClass method similar to jquery
+HTMLElement.prototype.toggleClass = function (c1, c2) {
+  var cl = this.classList;
+  if (cl.contains(c1)) {
+    cl.add(c2);
+    cl.remove(c1);
+  }
+  else {
+    cl.remove(c2);
+    cl.add(c1);
+  };
+};
+
+
+// Don't let events bubble up
+if (Event.halt === undefined) {
+  // Don't let events bubble up
+  Event.prototype.halt = function () {
+    this.stopPropagation();
+    this.preventDefault();
+  };
+};
+
+var KorAP = KorAP || {};
+
+
+(function (KorAP) {
+  "use strict";
+
+  KorAP.init = function () {
+
+    /**
+     * Add actions to match entries
+     */
+    var inactiveLi = document.querySelectorAll('#search > ol > li:not(.active)');
+    var i = 0;
+    for (i = 0; i < inactiveLi.length; i++) {
+      inactiveLi[i].addEventListener('click', function () {
+
+	if (this._match !== undefined) {
+	  this._match.open();
+	  console.log('already open');
+	}
+	else {
+	  KorAP.Match.create(this).open();
+	  console.log('newly open');
+	}
+
+	
+      });
+    };
+
+    /**
+     * Toggle the alignment (left <=> right)
+     */
+    if (i > 0) {
+      var br = document.getElementById('button-right');
+      if (br !== undefined) {
+	var toggle = document.createElement('a');
+	toggle.setAttribute('title', 'toggle Alignment');
+	// Todo: Reuse old alignment from cookie!
+	var cl = toggle.classList;
+	cl.add('align');
+	cl.add('right');
+	toggle.addEventListener(
+	  'click',
+	  function (e) {
+	    var ol = document.querySelector('#search > ol');
+	    ol.toggleClass("align-left", "align-right");
+	    this.toggleClass("left", "right");
+	  });
+	toggle.appendChild(document.createElement('span'))
+	  .appendChild(document.createTextNode('Alignment'));
+	br.appendChild(toggle);
+      };
+    };
+  };
+
+  /*
+  function _openMatch (e) {
+    e.halt();
+    this.classList.add("active");
+    var matchElement = this;
+
+    // Todo: Add object to element
+    var ul = document.createElement('ul');
+    ul.classList.add('action', 'right');
+    matchElement.appendChild(ul);
+
+    // Todo:: Localize!
+    var close = document.createElement('li');
+    close.appendChild(document.createElement('span'))
+      .appendChild(document.createTextNode('Close'));
+    close.classList.add('close');
+    close.setAttribute('title', 'Close');
+
+    close.addEventListener('click', function (ie) {
+      ie.halt();
+      var match = matchElement['_match'];
+      match.destroy();
+      matchElement.classList.remove('active');
+      matchElement.removeChild(ul);
+    });
+
+    // Todo:: Localize!
+    var info = document.createElement('li');
+    info.appendChild(document.createElement('span'))
+      .appendChild(document.createTextNode('Info'));
+    info.classList.add('info');
+    info.setAttribute('title', 'Information');
+
+    // Add information, unless it already exists
+    info.addEventListener('click', function (ie) {
+      ie.halt();
+      KorAP.Match.create(matchElement).addInfo();
+    });
+
+    ul.appendChild(close);
+    ul.appendChild(info);
+  };
+*/
+
+  /**
+  function _closeMatch (e) {
+    e.halt();
+    this.parentNode.parentNode.classList.remove("active");
+  };
+  */
+
+}(this.KorAP));