Fixed tree view with multi line labels
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index 82f1062..00821de 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -8,59 +8,61 @@
 
   KorAP.URL = KorAP.URL || 'http://korap.ids-mannheim.de/kalamar';
 
-  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;
+  KorAP.API = KorAP.API || {};
 
-      // { 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'];
-      }
+  KorAP.API.getMatchInfo = function (match, param, cb) {
 
-      // { spans : false, layer: [Array of KorAP.InfoLayer] }
-      else {
-	// TODO
-	url += '?spans=false';
-      }
+    // match is a KorAP.Match object
+    var url = KorAP.URL;
+    url += '/corpus';
+    url += '/' + match.corpusID;
+    url += '/' + match.docID;
+    url += '/' + match.textID;
+    url += '/' + match.matchID;
 
-      this.getJSON(url, cb);
-    },
-
-    getJSON : function (url, onload) {
-      var req = new XMLHttpRequest();
-
-      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();
+    // { 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';
+    }
+
+    KorAP.API.getJSON(url, cb);
   };
+
+  KorAP.API.getJSON = function (url, onload) {
+    var req = new XMLHttpRequest();
+
+    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();
+  }
 });
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index 5ef8dee..feab93f 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -13,7 +13,8 @@
 	     vcClass,
 	     tutClass,
 	     domReady,
-	     hintArray) {
+	     hintArray,
+	     alertifyClass) {
   domReady(function (event) {
     var obj = {};
 
@@ -130,7 +131,8 @@
     };
 
     // Initialize queries for document
-    obj.tutorial.initQueries(document);
+    if (obj.tutorial)
+      obj.tutorial.initQueries(document);
 
     /**
      * Init hint helper
@@ -148,10 +150,10 @@
     KorAP.log = function (type, msg) {
 
       // Use alertify to log errors
-      alertify.log(
+      alertifyClass.log(
 	(type === 0 ? '' : type + ': ') +
 	  msg,
-	'warn',
+	'error',
 	5000
       );
     };
diff --git a/dev/js/src/match/tree.js b/dev/js/src/match/tree.js
index 9722f94..e45d05f 100644
--- a/dev/js/src/match/tree.js
+++ b/dev/js/src/match/tree.js
@@ -9,7 +9,7 @@
   var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
 
   // Node size
-  var WIDTH  = 55, HEIGHT = 20;
+  var WIDTH  = 55, HEIGHT = 20, LINEHEIGHT = 14;
 
   // Create path for node connections 
   function _line (src, target) {
@@ -179,8 +179,7 @@
       var canvas = document.createElementNS(svgXmlns, 'svg');
       this._element = canvas;
 
-      canvas.setAttribute('height', g.graph().height);
-      canvas.setAttribute('width', g.graph().width);
+      var height = g.graph().height;
 
       // Create edges
       g.edges().forEach(
@@ -218,22 +217,52 @@
 	  // Add label
 	  if (v.label !== undefined) {
 	    var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
-	    text.setAttribute('x', v.x - v.width / 2);
-	    text.setAttribute('y', v.y - v.height / 2);
+	    var y = v.y - v.height / 2;
+	    text.setAttribute('y', y);
 	    text.setAttribute(
 	      '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.class === "leaf") {
+	      text.setAttribute('title', v.label);
 
+	      var labelPart = v.label.split(" ");
+	      var n = 0;
+	      for (var i = 0; i < labelPart.length; i++) {
+		if (labelPart[i].length === 0)
+		  continue;
+
+		var tspan = document.createElementNS(svgXmlns, 'tspan');
+		tspan.appendChild(document.createTextNode(labelPart[i]));
+		if (n !== 0)
+		  tspan.setAttribute('dy', LINEHEIGHT + 'pt');
+		else
+		  n = 1;
+		tspan.setAttribute('x', v.x - v.width / 2);
+		y += LINEHEIGHT;
+		text.appendChild(tspan);
+	      };
+
+	      y += LINEHEIGHT;
+
+	      // The text is below the canvas - readjust the height!
+	      if (y > height)
+		height = y;
+	    }
+	    else {
+	      var tspan = document.createElementNS(svgXmlns, 'tspan');
+	      tspan.appendChild(document.createTextNode(v.label));
+	      tspan.setAttribute('x', v.x - v.width / 2);
+	      text.appendChild(tspan);
+	    };
+	  };
 	  canvas.appendChild(group);
 	}
       );
 
+      canvas.setAttribute('width', g.graph().width);
+      canvas.setAttribute('height', height);
       return this._element;
     }
   };
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index f04b445..a19430a 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -489,6 +489,9 @@
     _changed : function () {
       this.__changed = true;
       
+      if (this._parent) {
+      };
+
       if (this._rewrites === undefined)
 	return;
 
diff --git a/dev/js/src/vc/docgroup.js b/dev/js/src/vc/docgroup.js
index 0db4b4c..da1f6ab 100644
--- a/dev/js/src/vc/docgroup.js
+++ b/dev/js/src/vc/docgroup.js
@@ -1,6 +1,9 @@
 /**
  * Document group criterion
  */
+/*
+ * TODO: Let the UPDATE event bubble up through parents!
+ */
 define([
   'vc/jsonld',
   'vc/unspecified',