Added tutorial and session mechanism
diff --git a/public/js/demo/about.html b/public/js/demo/about.html
new file mode 100644
index 0000000..8381ae5
--- /dev/null
+++ b/public/js/demo/about.html
@@ -0,0 +1,102 @@
+ <!DOCTYPE html>
+<html>
+  <head>
+    <title>Tutorial demo</title>
+    <link type="text/css" rel="stylesheet" href="../../css/build/kalamar.css" />
+    <script src="../src/session.js"></script>
+    <script src="../src/tutorial.js"></script>
+  </head>
+  <body class="embedded">
+    <div id="sidebar" tabindex="0" class="active">
+      <h2>Tutorial</h2>
+      <nav>
+	<ul>
+	  <li>About ...
+	    <ul>
+	      <li>Kalamar</li>
+	      <li>Koral</li>
+	      <li>Kustvakt</li>
+	      <li>Krill</li>
+	      <!-- <li>Karang</li> -->
+	    </ul>
+	  </li>
+	  <li>Query Languages
+	    <ul>
+	      <li>Cosmas II</li>
+	      <li>Poliqarp</li>
+	      <li>Annis QL</li>
+	      <li>CQL</li>
+	      <li>RegExp</li>
+	    </ul>
+	  </li>
+	  <li>Data
+	    <ul>
+	      <li>Corpora
+		<ul>
+		  <li>DeReKo</li>
+		</ul>
+	      </li>
+	      <li>Annotations
+		<ul>
+		  <li>Connexor</li>
+		  <li>Mate</li>
+		  <li>TreeTagger</li>
+		</ul>
+	      </li>
+	    </ul>
+	  </li>
+	  <li>API
+	    <ul>
+	      <li>KoralQuery</li>
+	      <li>Search API</li>
+	      <li>Match Information API</li>
+	      <li>User API</li>
+	    </ul>
+	  </li>
+	</ul>
+      </nav>
+    </div>
+    <main class="tutorial">
+      <h2>KorAP-Tutorial</h2>
+      <section id="tut-intro">
+	<h3>Frontend Features</h3>
+	<p>This frontend ...</p>
+      </section>
+
+      <section id="tut-examples">
+	<h3>Example Queries</h3>
+	<p><strong>Poliqarp</strong>: Find all occurrences of the lemma &quot;baum&quot; as annotated by the <a href="#">default foundry</a>.</p>
+	<pre class="query tutorial" data-query="[base=Baum]" data-query-language="poliqarp"><code>[base=Baum]</code></pre>
+      </section>
+
+      <section id="tut-caveats">
+	<h3>Caveats</h3>
+	<p>Memcheck is not perfect; it occasionally produces false positives, and there are mechanisms for suppressing these (see Suppressing errors in the Valgrind User Manual). However, it is typically right 99% of the time, so you should be wary of ignoring its error messages. After all, you wouldn’t ignore warning messages produced by a compiler, right?</p>
+	<p>The suppression mechanism is also useful if Memcheck is reporting errors in library code that you cannot change. The default suppression set hides a lot of these, but you may come across more. Memcheck cannot detect every memory error your program has. For example, it can’t detect out-of-range reads or writes to arrays that are allocated statically or on the stack. But it should detect many errors that could crash your program (eg. cause a segmentation fault).</p>
+	<p>Try to make your program so clean that Memcheck reports no errors. Once you achieve this state, it is much easier to see when changes to the program cause Memcheck to report new errors. Experience from several years of Memcheck use shows that it is possible to make even huge programs run Memcheck-clean. For example, large parts of KDE, OpenOffice.org and Firefox are Memcheck-clean, or very close to it.</p>
+      </section>
+      <section id="tut-more-information">
+	<h3>More information</h3>
+	<p>Please consult the Valgrind FAQ and the Valgrind User Manual, which have much more information. Note that the other tools in the Valgrind distribution can be invoked with the <code>--tool</code> option.</p>
+      </section>
+      <section id="tut-overview">
+	<h3>An Overview of Valgrind</h3>
+	  <p>Valgrind is an instrumentation framework for building dynamic analysis tools. It comes with a set of tools each of which performs some kind of debugging, profiling, or similar task that helps you improve your programs. Valgrind’s architecture is modular, so new tools can be created easily and without disturbing the existing structure.</p>
+	  <p>A number of useful tools are supplied as standard.</p>
+	  <ol>
+	    <li><strong>Memcheck</strong> is a memory error detector. It helps you make your programs, particularly those written in C and C++, more correct.</li>
+	    <li><strong>Cachegrind</strong> is a cache and branch-prediction profiler. It helps you make your programs run faster.</li>
+	    <li><strong>Callgrind</strong> is a call-graph generating cache profiler. It has some overlap with Cachegrind, but also gathers some information that Cachegrind does not.</li>
+	    <li><strong>Helgrind</strong> is a thread error detector. It helps you make your multi-threaded programs more correct.</li>
+	    <li><strong>DRD</strong> is also a thread error detector. It is similar to Helgrind but uses different analysis techniques and so may find different problems.</li>
+	    <li><strong>Massif</strong> is a heap profiler. It helps you make your programs use less memory.</li>
+	    <li><strong>DHAT</strong> is a different kind of heap profiler. It helps you understand issues of block lifetimes, block utilisation, and layout inefficiencies.</li>
+	    <li><strong>SGcheck</strong> is an experimental tool that can detect overruns of stack and global arrays. Its functionality is complementary to that of Memcheck: SGcheck finds problems that Memcheck can’t, and vice versa.</li>
+	    <li><strong>BBV</strong> is an experimental SimPoint basic block vector generator. It is useful to people doing computer architecture research and development.</li>
+	  </ol>
+	  <p>There are also a couple of minor tools that aren’t useful to most users: Lackey is an example tool that illustrates some instrumentation basics; and Nulgrind is the minimal Valgrind tool that does no analysis or instrumentation, and is only useful for testing purposes. Valgrind is closely tied to details of the CPU and operating system, and to a lesser extent, the compiler and basic C libraries. Nonetheless, it supports a number of widely-used platforms, listed in full at <a href="http://www.valgrind.org/">valgrind.org</a>.</p>
+	  <p>Valgrind is built via the standard Unix ./configure, make, make install process; full details are given in the README file in the distribution.</p>
+      </section>
+    </main>
+  </body>
+</html>
diff --git a/public/js/demo/all.html b/public/js/demo/all.html
index 26337fa..7809da4 100644
--- a/public/js/demo/all.html
+++ b/public/js/demo/all.html
@@ -4,13 +4,19 @@
     <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/api.js"></script>
     <script src="../src/hint.js"></script>
     <script src="../src/match.js"></script>
     <script src="../src/menu.js"></script>
     <script src="../src/vc.js"></script>
+    <script src="../src/session.js"></script>
+    <script src="../src/tutorial.js"></script>
+    <script src="../src/util.js"></script>
     <script src="./all.js"></script>
     <link type="text/css" rel="stylesheet" href="../../css/build/kalamar.css" />
+    <script>
+      KorAP.URL = 'http://localhost:3000';
+    </script>
   </head>
   <body>
     <div id="kalamar-bg"></div>
@@ -28,8 +34,9 @@
 	  <i class="fa fa-arrow-circle-down show-hint" onclick="hint.popUp()"></i>
 -->
 	</div>
-	<div id="vc"></div>
-	in Wikipedia
+	<div id="vc-view"></div>
+	in <input type="hidden" id="vc-name" name="vc-name" value="Wikipedia" />
+	<input type="text" name="vc" id="vc" value="corpusID = Wikipedia" />
 	with <span class="select">
 <!-- Change this to js-menu -->
 	  <select name="ql" id="ql-field">
@@ -48,11 +55,16 @@
 	  <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>
-
+	  <a href="about.html" title="Tutorial" class="tutorial" id="view-tutorial"><span>Tutorial</span></a>
 	</div>
       </form>
     </header>
+
+    <div id="sidebar" tabindex="0">
+      <h2>Tutorial</h2>
+      <p>Hui</p>
+    </div>
+
     <main>
       <div class="resultinfo">
 	<div id="pagination">
@@ -73,10 +85,10 @@
 	      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"
+	      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"
 	      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>
+	      <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><mark><mark class="class-2 level-1">Dies </mark><mark class="class-1 level-0"><mark class="class-2 level-1"><mark class="class-3 level-2">ist</mark> ein</mark> Test</mark></mark><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>
@@ -93,7 +105,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"
+	      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"
 	      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>
@@ -111,7 +123,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"
+	      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"
 	      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>
diff --git a/public/js/demo/all.js b/public/js/demo/all.js
index dc1e051..0a72f73 100644
--- a/public/js/demo/all.js
+++ b/public/js/demo/all.js
@@ -95,6 +95,46 @@
   "</span>" +
   "<span class=\"context-right\"></span>";
 
+var treeSnippet2 =
+  "<span class=\"context-left\"><\/span>"+
+  "<span class=\"match\">"+
+  "<span title=\"xip\/c:NPA\">"+
+  "<span title=\"xip\/c:NP\">"+
+  "<span title=\"xip\/c:NOUN\">HDTV<\/span>"+
+  "<\/span>"+
+  "<\/span> "+
+  "<span title=\"xip\/c:NPA\">" +
+  "<span title=\"xip\/c:NP\">"+
+  "<span title=\"xip\/c:NOUN\">Samples<\/span>"+
+  "<\/span>"+
+  "<\/span> "+
+  "<span title=\"xip\/c:ADV\">from<\/span> "+
+  "<span title=\"xip\/c:NPA\">"+
+  "<span title=\"xip\/c:NP\">"+
+  "<span title=\"xip\/c:NOUN\">European<\/span>"+
+  "<\/span>"+
+  "<\/span> ("+
+  "<span title=\"xip\/c:INS\">"+
+  "<span title=\"xip\/c:NPA\">"+
+  "<span title=\"xip\/c:NP\">"+
+  "<span title=\"xip\/c:NOUN\">and<\/span>"+
+  "<\/span>"+
+  "<\/span> "+
+  "<span title=\"xip\/c:ADV\">other<\/span>"+
+  "<\/span>) "+
+  "<span title=\"xip\/c:ADV\">broadcasters<\/span> "+
+  "<span title=\"xip\/c:NPA\">"+
+  "<span title=\"xip\/c:NP\">"+
+  "<span title=\"xip\/c:NOUN\">and<\/span>"+
+  "<\/span>"+
+  "<\/span> "+
+  "<span title=\"xip\/c:VERB\">test<\/span> "+
+  "<span title=\"xip\/c:ADV\">transmissions<\/span> "+
+  "<span title=\"xip\/c:PREP\">in<\/span> "+
+  "<span title=\"xip\/c:NOUN\">Europe<\/span>"+
+  "<\/span>"+
+  "<span class=\"context-right\"><\/span>";
+
 var menuContent = [
     ['cnx/c', 'cnx', 'c'],
     ['mate/c', 'mate', 'c'],
@@ -379,12 +419,12 @@
 
 // Parse and show the table
 // Override getMatchInfo API call
-KorAP.API.getMatchInfo = function(match, callObj) {
+KorAP.API.getMatchInfo = function(match, callObj, cb) {
   if (callObj["spans"] !== undefined && callObj["spans"] === true) {
-    return { "snippet": treeSnippet };
+    cb({ "snippet": treeSnippet2 });
   }
   else {
-    return { "snippet": snippet };
+    cb({ "snippet": snippet });
   }
 };
 
@@ -392,19 +432,19 @@
 /**
  * Do some things at the beginning.
  */
-window.onload = function () {
+document.addEventListener('DOMContentLoaded', function () {
 
   // Decorate actions
-  KorAP.init();
+  var init = KorAP.init();
 
   var menu = KorAP.MatchTreeMenu.create(
     undefined,
     menuContent
   );
 
-  var vc = KorAP.VirtualCollection.render(vcExample);
-  document.getElementById('vc').appendChild(vc.element());
+  // document.getElementById('vc-choose').click();
 
+//  init.tutorial.show();
 
   // Don't hide!!!
   menu.hide = function () {};
@@ -412,4 +452,4 @@
   menu.limit(3);
   menu.show();
   menu.focus();
-};
+});
diff --git a/public/js/demo/matchSort.html b/public/js/demo/matchSort.html
new file mode 100644
index 0000000..860560e
--- /dev/null
+++ b/public/js/demo/matchSort.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Match demo</title>
+    <meta charset="utf-8" />
+    <link type="text/css"
+	  rel="stylesheet"
+	  href="../../css/build/kalamar.css"></link>
+    <style type="text/css"
+	   rel="stylesheet">
+body {
+  background-color: #ffa500;
+}
+    </style>
+  </head>
+  <body>
+    <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>
+	  </div>
+	  <p class="ref"><strong>Wertparameter</strong> by Hubi,Zwobot,4; published on 2005-03-28 as WWW.03313 (WPD)</p>
+	</li>
+	<li data-corpus-id="WPD"
+	    data-doc-id="WWW"
+	    data-text-id="03313"
+	    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">
+	  <ol>
+	    <li data-match-id="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>
+	      </div>
+	      <p class="ref"><strong>Wertparameter</strong> by Hubi,Zwobot,4; published on 2005-03-28 as WWW.03313 (WPD)</p>
+	    </li>
+	    <li data-match-id="p108-109">
+	      <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>
+	      </div>
+	      <p class="ref"><strong>Wertparameter</strong> by Hubi,Zwobot,4; published on 2005-03-28 as WWW.03313 (WPD)</p>
+	    </li>
+	  </ol>
+	  <span class="matchMore">+5</span>
+	</li>
+      </ol>
+    </div>
+
+
+    <p>Sort by</p>
+    <p>click auf ein Item gibt ihm höchste Priorität, Klick auf asc/desc ändert die Sortierung, Klick auf ein aktives Feld deaktiviert. Sortiert wird nur nach Klick auf (okay) if prefix feld</p>
+    <ul class="menu sort">
+      <li class="active">CorpusID (asc|desc)</li>
+      <li class="active">Author (asc|desc)</li>
+      <li>Date</li>
+      <span class="pref right" style="right: 0; left: auto;">Sortieren</span>
+    </ul>
+
+  </body>
+</html>
diff --git a/public/js/demo/session.html b/public/js/demo/session.html
new file mode 100644
index 0000000..59cfe4f
--- /dev/null
+++ b/public/js/demo/session.html
@@ -0,0 +1,44 @@
+ <!DOCTYPE html>
+<html>
+  <head>
+    <title>Cookie demo</title>
+    <script src="../src/session.js"></script>
+    <script>
+
+var cookey;
+
+function add (val) {
+  var list = document.getElementById('number');
+  list.textContent += '-' + val;
+  cookey.set('n', list.textContent);
+};
+
+function removeCookie () {
+  cookey.clear();
+};
+
+document.addEventListener('DOMContentLoaded', function () {
+  cookey = KorAP.Session.create('peter');
+  document.getElementById('number').textContent = cookey.get('n') || '';
+
+  var elements =   document.getElementsByClassName('num');
+  for (var i = 0; i < elements.length; i++) {
+    elements[i].addEventListener(
+      'click',
+      function (e) {
+        add(this.textContent);
+      }
+    );
+  };
+});
+
+    </script>
+  </head>
+  <body>
+    <div id="number"></div>
+    <span class="num">1</span>
+    <span class="num">2</span>
+    <span class="num">3</span>
+    <div onclick="removeCookie()">remove</div>
+  </body>
+</html>
diff --git a/public/js/lib/highlight/highlight.pack.js b/public/js/lib/highlight/highlight.pack.js
new file mode 100644
index 0000000..6c71f03
--- /dev/null
+++ b/public/js/lib/highlight/highlight.pack.js
@@ -0,0 +1 @@
+var hljs=new function(){function k(v){return v.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(v){return v.nodeName.toLowerCase()}function i(w,x){var v=w&&w.exec(x);return v&&v.index==0}function d(v){return Array.prototype.map.call(v.childNodes,function(w){if(w.nodeType==3){return b.useBR?w.nodeValue.replace(/\n/g,""):w.nodeValue}if(t(w)=="br"){return"\n"}return d(w)}).join("")}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^language-/,"")});return v.filter(function(x){return j(x)||x=="no-highlight"})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(t(A)=="br"){z+=1}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset<y[0].offset)?w:y}return y[0].event=="start"?w:y}function A(H){function G(I){return" "+I.nodeName+'="'+k(I.value)+'"'}F+="<"+t(H)+Array.prototype.map.call(H.attributes,G).join("")+">"}function E(G){F+="</"+t(G)+">"}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=k(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+k(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};function E(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})}if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b=D.bK.split(" ").join("|")}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?\\b("+F.b+")\\b\\.?":F.b}).concat([D.tE]).concat([D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}};D.continuation={}}x(y)}function c(S,L,J,R){function v(U,V){for(var T=0;T<V.c.length;T++){if(i(V.c[T].bR,U)){return V.c[T]}}}function z(U,T){if(i(U.eR,T)){return U}if(U.eW){return z(U.parent,T)}}function A(T,U){return !J&&i(U.iR,T)}function E(V,T){var U=M.cI?T[0].toLowerCase():T[0];return V.k.hasOwnProperty(U)&&V.k[U]}function w(Z,X,W,V){var T=V?"":b.classPrefix,U='<span class="'+T,Y=W?"":"</span>";U+=Z+'">';return U+X+Y}function N(){var U=k(C);if(!I.k){return U}var T="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(U);while(V){T+=U.substr(X,V.index-X);var W=E(I,V);if(W){H+=W[1];T+=w(W[0],V[0])}else{T+=V[0]}X=I.lR.lastIndex;V=I.lR.exec(U)}return T+U.substr(X)}function F(){if(I.sL&&!f[I.sL]){return k(C)}var T=I.sL?c(I.sL,C,true,I.continuation.top):g(C);if(I.r>0){H+=T.r}if(I.subLanguageMode=="continuous"){I.continuation.top=T.top}return w(T.language,T.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(V,U){var T=V.cN?w(V.cN,"",true):"";if(V.rB){D+=T;C=""}else{if(V.eB){D+=k(U)+T;C=""}else{D+=T;C=U}}I=Object.create(V,{parent:{value:I}})}function G(T,X){C+=T;if(X===undefined){D+=Q();return 0}var V=v(X,I);if(V){D+=Q();P(V,X);return V.rB?0:X.length}var W=z(I,X);if(W){var U=I;if(!(U.rE||U.eE)){C+=X}D+=Q();do{if(I.cN){D+="</span>"}H+=I.r;I=I.parent}while(I!=W.parent);if(U.eE){D+=k(X)}C="";if(W.starts){P(W.starts,"")}return U.rE?0:X.length}if(A(X,I)){throw new Error('Illegal lexeme "'+X+'" for mode "'+(I.cN||"<unnamed>")+'"')}C+=X;return X.length||1}var M=j(S);if(!M){throw new Error('Unknown language: "'+S+'"')}m(M);var I=R||M;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,D,true)}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+="</span>"}}return{r:H,value:D,language:S,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:k(L)}}else{throw O}}}function g(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:k(y)};var w=v;x.forEach(function(z){if(!j(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function h(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"<br>")}return v}function p(z){var y=d(z);var A=r(z);if(A=="no-highlight"){return}var v=A?c(A,y,true):g(y);var w=u(z);if(w.length){var x=document.createElementNS("http://www.w3.org/1999/xhtml","pre");x.innerHTML=v.value;v.value=q(w,u(x),y)}v.value=h(v.value);z.innerHTML=v.value;z.className+=" hljs "+(!A&&v.language||"");z.result={language:v.language,re:v.r};if(v.second_best){z.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function e(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function j(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=g;this.fixMarkup=h;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=e;this.getLanguage=j;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("xml",function(a){var c="[A-Za-z0-9\\._:-]+";var d={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"};var b={eW:true,i:/</,r:0,c:[d,{cN:"attribute",b:c,r:0},{b:"=",r:0,c:[{cN:"value",v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html"],cI:true,c:[{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[b],starts:{e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},d,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ /><]+",r:0},b]}]}});hljs.registerLanguage("markdown",function(a){return{c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}|\t)",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].+?[\\)\\]]",rB:true,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:true,rE:true,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:true,eE:true},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:true,eE:true,}],r:10},{b:"^\\[.+\\]:",e:"$",rB:true,c:[{cN:"link_reference",b:"\\[",e:"\\]",eB:true,eE:true},{cN:"link_url",b:"\\s",e:"$"}]}]}});hljs.registerLanguage("css",function(a){var b="[a-zA-Z-][a-zA-Z0-9_-]*";var c={cN:"function",b:b+"\\(",e:"\\)",c:["self",a.NM,a.ASM,a.QSM]};return{cI:true,i:"[=/|']",c:[a.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:true,eE:true,r:0,c:[c,a.ASM,a.QSM,a.NM]}]},{cN:"tag",b:b,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[a.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[c,a.NM,a.QSM,a.ASM,a.CBLCLM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]}]}]}});hljs.registerLanguage("json",function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}});
\ No newline at end of file
diff --git a/public/js/spec/hintSpec.js b/public/js/spec/hintSpec.js
index 66c831f..bb736ce 100644
--- a/public/js/spec/hintSpec.js
+++ b/public/js/spec/hintSpec.js
@@ -389,7 +389,7 @@
 
   it('should be initializable', function () {
 
-    var menu = KorAP.HintMenu.create("cnx/", list);
+    var menu = KorAP.HintMenu.create(null, "cnx/", list);
     expect(menu.context()).toEqual('cnx/');
     expect(menu.element().nodeName).toEqual('UL');
     expect(menu.element().style.opacity).toEqual("0");
diff --git a/public/js/spec/matchSpec.js b/public/js/spec/matchSpec.js
index 0e638c4..f520557 100644
--- a/public/js/spec/matchSpec.js
+++ b/public/js/spec/matchSpec.js
@@ -140,6 +140,15 @@
 };
 
 
+// Override getMatchInfo API call
+KorAP.API.getMatchInfo = function (x, param, cb) {
+  if (param['spans'] === undefined || param['spans'] === false)
+    cb({ "snippet": snippet });
+  else
+    cb({ "snippet": treeSnippet });
+};
+
+
 describe('KorAP.InfoLayer', function () {
 
   it('should be initializable', function () {
@@ -279,33 +288,52 @@
 
 
 describe('KorAP.MatchInfo', function () {
-  it('should parse into a table', function () {
 
-    var m = KorAP.Match.create(match);
-    var info = m.info();
+  var m = KorAP.Match.create(match);
+  var info = m.info();
+
+  it('should contain a valid info', function () {
     expect(m._info).toEqual(info);
+  });
 
-    expect(info.getTable('base/s')).not.toBeTruthy();
+  var table1, table2;
 
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function () {
-      return { "snippet": snippet };
-    };
+  // Async preparation
+  it('should fail to load a table async', function (done) {
+    expect(info).toBeTruthy();
 
-    var table = info.getTable();
-    expect(table).toBeTruthy();
+    info.getTable([], function (tablen) {
+      table1 = tablen;
+      done();
+    });
+  });
 
-    expect(table.length()).toBe(3);
+  it('should\'nt be parsable (async)', function () {
+    expect(table1).not.toBeTruthy();
+  });
 
-    expect(table.getToken(0)).toBe("meist");
-    expect(table.getToken(1)).toBe("deutlich");
-    expect(table.getToken(2)).toBe("leistungsfähiger");
+  it('should load a working table async', function(done) {
+    expect(info).toBeTruthy();
+    info.getTable(undefined, function (tablem) {
+      table2 = tablem;
+      done();
+    });
+  });
+  
+  it('should parse into a table (async)', function () {
+    expect(table2).toBeTruthy();
 
-    expect(table.getValue(0, "cnx", "p")[0]).toBe("ADV");
-    expect(table.getValue(0, "cnx", "syn")[0]).toBe("@PREMOD");
+    expect(table2.length()).toBe(3);
 
-    expect(table.getValue(2, "cnx", "l")[0]).toBe("fähig");
-    expect(table.getValue(2, "cnx", "l")[1]).toBe("leistung");
+    expect(table2.getToken(0)).toBe("meist");
+    expect(table2.getToken(1)).toBe("deutlich");
+    expect(table2.getToken(2)).toBe("leistungsfähiger");
+
+    expect(table2.getValue(0, "cnx", "p")[0]).toBe("ADV");
+    expect(table2.getValue(0, "cnx", "syn")[0]).toBe("@PREMOD");
+
+    expect(table2.getValue(2, "cnx", "l")[0]).toBe("fähig");
+    expect(table2.getValue(2, "cnx", "l")[1]).toBe("leistung");
   });
 
 
@@ -352,26 +380,28 @@
     expect(infotable.children[1].classList.contains('addtree')).toBeTruthy();
   });
 
-
-  it('should parse into a tree', function () {
+  var tree;
+  it('should parse into a tree (async) 1', function (done) {
     var info = KorAP.Match.create(match).info();
+    expect(info).toBeTruthy();
+    info.getTree(undefined, undefined, function (treem) {
+      tree = treem;
+      done();
+    });
+  });
 
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function () {
-      return { "snippet": treeSnippet };
-    };
-
-    var tree = info.getTree();
+  it('should parse into a tree (async) 2', function () {
     expect(tree).toBeTruthy();
     expect(tree.nodes()).toEqual(49);
   });
 
 
+  var info, matchElement;
   it('should parse into a tree view', function () {
-    var matchElement = matchElementFactory();
+    matchElement = matchElementFactory();
     expect(matchElement.tagName).toEqual('LI');
 
-    var info = KorAP.Match.create(matchElement).info();
+    info = KorAP.Match.create(matchElement).info();
     info.toggle();
 
     // Match
@@ -392,14 +422,16 @@
     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 };
-    };
+  it('should add a tree view async 1', function (done) {
+    expect(info).toBeTruthy();
+    info.addTree('mate', 'beebop', function () {
+      done();
+    });
+  });
 
-    info.addTree('mate', 'beebop');
-
+  it('should add a tree view async 2', function () {
     // With added tree
     var infotable = matchElement.children[0].children[1];
     expect(infotable.tagName).toEqual('DIV');
@@ -417,21 +449,23 @@
     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 table;
+  it('should be retrieved async', function (done) {
     var info = KorAP.Match.create(match).info();
+    expect(info).toBeTruthy();
+    info.getTable(undefined, function (x) {
+      table = x;
+      done();
+    });
+  });
 
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function() {
-      return { "snippet": snippet };
-    };
-
-    var table = info.getTable();
+  it('should be rendered async', function () {
     var e = table.element();
 
     expect(e.nodeName).toBe('TABLE');
@@ -472,16 +506,18 @@
 });
 
 describe('KorAP.MatchTree', function () {
-  it('should be rendered', function () {
+  var tree;
+
+  it('should be rendered async 1', function (done) {
     var info = KorAP.Match.create(match).info();
+    expect(info).toBeTruthy();
+    info.getTree(undefined, undefined, function (y) {
+      tree = y;
+      done();
+    });
+  });
 
-    // Override getMatchInfo API call
-    KorAP.API.getMatchInfo = function() {
-      return { "snippet": treeSnippet };
-    };
-
-    var tree = info.getTree();
-
+  it('should be rendered async 2', function () {
     var e = tree.element();
     expect(e.nodeName).toEqual('svg');
     expect(e.getElementsByTagName('g').length).toEqual(48);
diff --git a/public/js/src/api.js b/public/js/src/api.js
new file mode 100644
index 0000000..ed33cc2
--- /dev/null
+++ b/public/js/src/api.js
@@ -0,0 +1,80 @@
+var KorAP = KorAP || {};
+
+(function (KorAP) {
+  "use strict";
+
+  // Default log message
+  KorAP.log = KorAP.log || function (type, msg) {
+    console.log(type + ": " + msg);
+  };
+
+  KorAP.URL = KorAP.URL || 'http://korap.ids-mannheim.de/kalamar';
+
+  // TODO: https://github.com/honza/140medley/blob/master/140medley.js
+  // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
+  // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
+  // r.addEventListener("progress", updateProgress, false);
+  // http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
+  // http://stackoverflow.com/questions/6112744/load-javascript-on-demand
+
+  KorAP.API = {
+    getMatchInfo : function (match, param, cb) {
+      // match is a KorAP.Match object
+
+      var url = KorAP.URL;
+      url += '/corpus';
+      url += '/' + match.corpusID;
+      url += '/' + match.docID + '.' + match.textID; // TODO
+      url += '/' + match.matchID;
+
+      // { spans: true, layer:x, foundry : y}
+      if (param['spans'] == true) {
+	url += '?spans=true';
+	if (param['foundry'] !== undefined)
+	  url += '&foundry=' + param['foundry'];
+	if (param['layer'] !== undefined)
+	  url += '&layer=' + param['layer'];
+      }
+
+      // { spans : false, layer: [Array of KorAP.InfoLayer] }
+      else {
+	// TODO
+	url += '?spans=false';
+      }
+
+      this.getJSON(url, cb);
+    },
+    getJSON : function (url, onload) {
+      var req = new XMLHttpRequest();
+
+      console.log('Request url: ' + url);
+
+      req.open("GET", url, true);
+
+
+      req.setRequestHeader("Accept", "application/json");
+      req.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 
+      req.onreadystatechange = function () {
+	/*
+	  States:
+	  0 - unsent (prior to open)
+	  1 - opened (prior to send)
+	  2 - headers received
+	  3 - loading (responseText has partial data)
+	  4 - done
+	 */
+	if (this.readyState == 4) {
+	  if (this.status === 200)
+	    onload(JSON.parse(this.responseText));
+	  else
+	    KorAP.log(this.status, this.statusText);
+	}
+      };
+      req.ontimeout = function () {
+	KorAP.log(0, 'Request Timeout');
+      };
+      req.send();
+    }
+  };
+
+}(this.KorAP));
diff --git a/public/js/src/hint.js b/public/js/src/hint.js
index 8011bee..80631ec 100644
--- a/public/js/src/hint.js
+++ b/public/js/src/hint.js
@@ -52,7 +52,7 @@
 
       // Update position of the mirror
       var that = this;
-      window.resize = function () {
+      window.onresize = function () {
 	that.reposition();
       };
 
@@ -311,6 +311,7 @@
 
       var that = this;
 
+
       // Add event listener for key pressed down
       inputFieldElement.addEventListener(
 	"keypress", function (e) {
diff --git a/public/js/src/match.js b/public/js/src/match.js
index b974031..4e3786b 100644
--- a/public/js/src/match.js
+++ b/public/js/src/match.js
@@ -35,8 +35,10 @@
   KorAP.API = KorAP.API || {};
 
   // TODO: Make this async
-  KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
-
+  KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
+    KorAP.log(0, 'KorAP.API.getMatchInfo() not implemented')
+    return {};
+  };
 
   /**
    * Match object
@@ -302,7 +304,7 @@
     /**
      * Retrieve and parse snippet for table representation
      */
-    getTable : function (tokens) {
+    getTable : function (tokens, cb) {
       var focus = [];
 
       // Get all tokens
@@ -330,51 +332,56 @@
 
       // No tokens chosen
       if (focus.length == 0)
-	return;
+	cb(null);
 
       // Get info (may be cached)
       // TODO: Async
-      var matchResponse = KorAP.API.getMatchInfo(
+      KorAP.API.getMatchInfo(
 	this._match,
-	{ 'spans' : false, 'layer' : focus }
+	{ 'spans' : false, 'layer' : focus },
+
+	// Callback for retrieval
+	function (matchResponse) {
+	  // Get snippet from match info
+	  if (matchResponse["snippet"] !== undefined) {
+	    this._table = KorAP.MatchTable.create(matchResponse["snippet"]);
+	    cb(this._table);
+	  };
+	}.bind(this)
       );
 
-      // Get snippet from match info
-      if (matchResponse["snippet"] !== undefined) {
-	this._table = KorAP.MatchTable.create(matchResponse["snippet"]);
-	return this._table;
-      };
-
+/*
       // Todo: Store the table as a hash of the focus
-
       return null;
+*/
     },
 
 
     /**
      * Retrieve and parse snippet for tree representation
      */
-    getTree : function (foundry, layer) {
+    getTree : function (foundry, layer, cb) {
       var focus = [];
 
-      // TODO: Async
-      var matchResponse = KorAP.API.getMatchInfo(
+      // TODO: Support and cache multiple trees
+
+      KorAP.API.getMatchInfo(
 	this._match, {
 	  'spans' : true,
 	  'foundry' : foundry,
 	  'layer' : layer
-	}
+	},
+	function (matchResponse) {
+	  // Get snippet from match info
+	  if (matchResponse["snippet"] !== undefined) {
+	    // Todo: This should be cached somehow
+	    cb(KorAP.MatchTree.create(matchResponse["snippet"]));
+	  }
+	  else {
+	    cb(null);
+	  };
+	}.bind(this)
       );
-
-      // TODO: Support and cache multiple trees
-
-      // Get snippet from match info
-      if (matchResponse["snippet"] !== undefined) {
-	// Todo: This should be cached somehow
-	return KorAP.MatchTree.create(matchResponse["snippet"]);
-      };
-
-      return null;
     },
 
     /**
@@ -397,13 +404,7 @@
     /**
      * Add a new tree view to the list
      */
-    addTree : function (foundry, layer) {
-      var treeObj = this.getTree(foundry, layer);
-
-      // Something went wrong - probably log!!!
-      if (treeObj === null)
-	return;
-
+    addTree : function (foundry, layer, cb) {
       var matchtree = document.createElement('div');
       matchtree.classList.add('matchtree');
 
@@ -416,7 +417,7 @@
       var tree = matchtree.appendChild(
 	document.createElement('div')
       );
-      tree.appendChild(treeObj.element());
+
       this._element.insertBefore(matchtree, this._element.lastChild);
 
       var close = tree.appendChild(document.createElement('em'));
@@ -427,11 +428,24 @@
 	}
       );
 
-      // 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)
-      treeObj.center();
+      // Get tree data async
+      this.getTree(foundry, layer, function (treeObj) {
+	// Something went wrong - probably log!!!
+	if (treeObj === null) {
+	  tree.appendChild(document.createTextNode('No data available.'));
+	}
+	else {
+	  tree.appendChild(treeObj.element());
+	  // 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)
+	  treeObj.center();
+	}
+
+	if (cb !== undefined)
+	  cb(treeObj);
+      });
     },
 
     /**
@@ -449,9 +463,15 @@
       // Append default table
       var matchtable = document.createElement('div');
       matchtable.classList.add('matchtable');
-      matchtable.appendChild(this.getTable().element());
       info.appendChild(matchtable);
 
+      // Create the table asynchronous
+      this.getTable(undefined, function (table) {
+	if (table !== null) {
+	  matchtable.appendChild(table.element());
+	};
+      });
+
       // Get spans
       var spanLayers = this._match.getSpans().sort(
 	function (a, b) {
@@ -917,23 +937,33 @@
 	  var rect = group.appendChild(document.createElementNS(svgXmlns, 'rect'));
 	  rect.setAttributeNS(null, 'x', v.x - v.width / 2);
 	  rect.setAttributeNS(null, 'y', v.y - v.height / 2);
-	  rect.setAttributeNS(null, 'width', v.width);
-	  rect.setAttributeNS(null, 'height', v.height);
 	  rect.setAttributeNS(null, 'rx', 5);
 	  rect.setAttributeNS(null, 'ry', 5);
+	  rect.setAttributeNS(null, 'width', v.width);
+	  rect.setAttributeNS(null, 'height', v.height);
+
+	  if (v.class === 'root' && v.label === undefined) {
+	    rect.setAttributeNS(null, 'width', v.height);
+	    rect.setAttributeNS(null, 'x', v.x - v.height / 2);
+	    rect.setAttributeNS(null, 'class', 'empty');
+	  };
 
 	  // Add label
-	  var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
-	  text.setAttributeNS(null, 'x', v.x - v.width / 2);
-	  text.setAttributeNS(null, 'y', v.y - v.height / 2);
-	  text.setAttributeNS(
-	    null,
-	    'transform',
-	    'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
-	  );
-	  var tspan = document.createElementNS(svgXmlns, 'tspan');
-	  tspan.appendChild(document.createTextNode(v.label));
-	  text.appendChild(tspan);
+	  if (v.label !== undefined) {
+	    var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
+	    text.setAttributeNS(null, 'x', v.x - v.width / 2);
+	    text.setAttributeNS(null, 'y', v.y - v.height / 2);
+	    text.setAttributeNS(
+	      null,
+	      'transform',
+	      'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
+	    );
+
+	    var tspan = document.createElementNS(svgXmlns, 'tspan');
+	    tspan.appendChild(document.createTextNode(v.label));
+	    text.appendChild(tspan);
+	  };
+
 	  canvas.appendChild(group);
 	}
       );
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index 3267b69..1cf3455 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -154,6 +154,7 @@
       e.style.outline = 0;
       e.setAttribute('tabindex', 0);
       e.classList.add('menu');
+      e.classList.add('roll');
       e.appendChild(this._prefix.element());
 
       // This has to be cleaned up later on
diff --git a/public/js/src/session.js b/public/js/src/session.js
new file mode 100644
index 0000000..a10ce81
--- /dev/null
+++ b/public/js/src/session.js
@@ -0,0 +1,80 @@
+/**
+ * Simple cookie based session library that stores
+ * values in JSON encoded cookies.
+ *
+ * @author Nils Diewald
+ */
+var KorAP = KorAP || {};
+
+(function (KorAP) {
+  "use strict";
+
+  
+  KorAP.Session = {
+
+    /**
+     * Create a new session.
+     * Expects a name or defaults to 'korap'
+     */
+    create : function (name) {
+      var obj = Object.create(KorAP.Session);
+      if (name === undefined)
+	name = 'korap';
+      obj._name = name.toLowerCase();
+      obj._hash = {};
+      obj._parse();
+      return obj;
+    },
+
+    /**
+     * Get a value based on a key.
+     * The value can be complex, as the value is stored as JSON.
+     */
+    get : function (key) {
+      return this._hash[key.toLowerCase()];
+    },
+
+    /**
+     * Set a value based on a key.
+     * The value can be complex, as the value is stored as JSON.
+     */
+    set : function (key, value) {
+      this._hash[key] = value;
+      this._store();
+    },
+
+    /**
+     * Clears the session by removing the cookie
+     */
+    clear : function () {
+      document.cookie = this._name + '=; expires=-1';
+    },
+
+    /* Store cookie */
+    _store : function () {
+      /*
+	var date = new Date();
+	date.setYear(date.getFullYear() + 1);
+      */
+      document.cookie =
+	this._name + '=' + encodeURIComponent(JSON.stringify(this._hash)) + ';';
+    },
+
+    /* Parse cookie */
+    _parse : function () {
+      var c = document.cookie;
+      var part = document.cookie.split(';');
+      for(var i = 0; i < part.length; i++) {
+        var pair = part[i].split('=');
+        var name = pair[0].trim().toLowerCase();
+	if (name === this._name) {
+	  if (pair.length === 1 || pair[1].length === 0)
+	    return;
+          this._hash = JSON.parse(decodeURIComponent(pair[1]));
+	  return;
+	};
+      };
+    }
+  }
+
+}(this.KorAP));
diff --git a/public/js/src/tutorial.js b/public/js/src/tutorial.js
new file mode 100644
index 0000000..03cf903
--- /dev/null
+++ b/public/js/src/tutorial.js
@@ -0,0 +1,133 @@
+/**
+ * Open and close a tutorial page.
+ * The current page is stored and retrieved in a session cookie.
+ */
+// Requires session.js
+var KorAP = KorAP || {};
+
+// Todo: add query mechanism!
+
+(function (KorAP) {
+  "use strict";
+
+  // Localization values
+  var loc   = (KorAP.Locale = KorAP.Locale || {} );
+  loc.CLOSE = loc.CLOSE || 'Close';
+
+  KorAP.Tutorial = {
+
+    /**
+     * Create new tutorial object.
+     * Accepts an element to bind the tutorial window to.
+     */
+    create : function (obj) {
+      return Object.create(KorAP.Tutorial)._init(obj);
+    },
+
+    // Initialize Tutorial object
+    _init : function (obj) {
+      this._session = KorAP.Session.create();
+      this._show = obj;
+      this.start = obj.getAttribute('href');
+      obj.removeAttribute('href');
+      var that = this;
+      obj.onclick = function () {
+	that.show();
+      };
+
+      // Injects a tutorial div to the body
+      var div = document.createElement('div');
+      div.setAttribute('id', 'tutorial');
+      div.style.display = 'none';
+      document.getElementsByTagName('body')[0].appendChild(div);
+      this._iframe = null;
+
+      this._element = div;
+      return this;
+    },
+
+    show : function () {
+      var element = this._element;
+      if (element.style.display === 'block')
+	return;
+
+      if (this._iframe === null) {
+	this._iframe = document.createElement('iframe');
+	this._iframe.setAttribute('src', this.getPage() || this.start);
+
+	var ul = document.createElement('ul');
+	ul.classList.add('action', 'right');
+
+	// 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);
+	close.onclick = function () {
+	  element.style.display = 'none';
+	};
+
+	// Add open in new window button
+	// Add scroll to top 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);
+	*/
+
+	ul.appendChild(close);
+
+	element.appendChild(ul);
+	element.appendChild(this._iframe);
+      };
+
+      element.style.display = 'block';
+    },
+
+    /**
+     * Close tutorial window.
+     */
+    hide : function () {
+      this._element.display.style = 'none';
+    },
+
+    /**
+     * Set a page to be the current tutorial page.
+     * Expects either a string or an element.
+     */
+    setPage : function (obj) {
+      var page = obj;
+      if (typeof page != 'string') {
+	page = window.location.pathname + window.location.search;
+	for (i = 1; i < 5; i++) {
+	  if (obj.nodeName === 'SECTION') {
+	    if (obj.hasAttribute('id'))
+	      page += '#' + obj.getAttribute('id');
+	    break;
+	  }
+	  else if (obj.nodeName === 'PRE' && obj.hasAttribute('id')) {
+	    page += '#' + obj.getAttribute('id');
+	    break;
+	  }
+	  else {
+	    obj = obj.parentNode;
+	  };
+	};
+      };
+      this._session.set('tutpage', page);
+    },
+
+    /**
+     * Get the current tutorial URL
+     */
+    getPage : function () {
+      this._session.get('tutpage');
+    },
+  }
+}(this.KorAP));
diff --git a/public/js/src/util.js b/public/js/src/util.js
index 3adf5a0..ecaad6f 100644
--- a/public/js/src/util.js
+++ b/public/js/src/util.js
@@ -36,6 +36,7 @@
    * Initialize user interface elements
    */
   KorAP.init = function () {
+    var obj = Object.create(KorAP.init);
 
     /**
      * Add actions to match entries
@@ -81,9 +82,43 @@
     };
 
     /**
-     * Init hint helper
+     * Init vc
      */
-    KorAP.Hint.create();
+    var input = document.getElementById('vc');
+    if (input) {
+      input.style.display = 'none';
+      var vcname = document.createElement('span');
+      vcname.setAttribute('id', 'vc-choose');
+      vcname.appendChild(
+	document.createTextNode(
+	  document.getElementById('vc-name').value
+	)
+      );
+      input.parentNode.insertBefore(vcname, input);
+      
+      vcname.onclick = function () {
+	var vc = KorAP.VirtualCollection.render(vcExample);
+	var view = document.getElementById('vc-view');
+	view.appendChild(vc.element());
+      };
+    };
+
+    /**
+     * Init Tutorial view
+     */
+    obj.tutorial = KorAP.Tutorial.create(
+      document.getElementById('view-tutorial')
+    );
+
+    /**
+     * Init hint helper
+     * has to be final because of
+     * reposition
+     */
+// Todo: Pass an element, so this works with
+// tutorial pages as well!
+    obj.hint = KorAP.Hint.create();
+    return obj;
   };
 
 }(this.KorAP));