Still some problems with non-blocking search
diff --git a/Makefile.PL b/Makefile.PL
index f0c800b..790ed1d 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -15,19 +15,19 @@
   LICENSE      => 'artistic_2',
   PREREQ_PM => {
     'Mojolicious' => '5.00',
-#    'Mojolicious::Plugin::Oro::Account' => 0.07,
     'Mojolicious::Plugin::TagHelpers::Pagination' => 0.05,
     'Mojolicious::Plugin::Notifications' => 0.04,
     'Mojolicious::Plugin::MailException' => 0.18,
     'Mojolicious::Plugin::CHI' => 0.09,
+
+    # Currently only on GitHub
+    'Mojolicious::Plugin::Search' => 0.04,
     'Cache::FastMmap' => 0,
     'Mojolicious::Plugin::Number::Commify' => '0.022',
-#    'JSON::XS' => 0.0,
     'Mojolicious::Plugin::AssetPack' => 0.23,
     'JavaScript::Minifier::XS' => 0.09,
     'CSS::Minifier::XS' => 0.09,
     'CSS::Sass' => '0.8.1'
-#    'Mojolicious::Plugin::Search' => 0.01,
   },
   test => {
     TESTS => 't/*.t'
diff --git a/korap.conf b/korap.conf
index 6552606..b279d4c 100644
--- a/korap.conf
+++ b/korap.conf
@@ -10,7 +10,8 @@
     JSON => 1
   },
   Search => {
-    engine => 'Korap::Plugin::KorapSearch'
+    engine => 'Korap::API',
+    api => 'http://10.0.10.13:7070/api/v0.1/'
   },
   CHI => {
     session_cache => {
diff --git a/lib/Korap.pm b/lib/Korap.pm
index 49922b2..05135f2 100644
--- a/lib/Korap.pm
+++ b/lib/Korap.pm
@@ -26,8 +26,9 @@
 	      TagHelpers::Pagination
 	      Notifications
 	      Number::Commify
-	      KorapSearch
+	      Search
 	      KorapInfo
+	      KorapHelpers
 	      KorapTagHelpers
 	     /) {
     # Oro::Account
@@ -35,7 +36,8 @@
     $self->plugin($_);
   };
 
-  $self->plugin(AssetPack => { minify => 1 });
+#  $self->plugin(AssetPack => { minify => 1 });
+  $self->plugin('AssetPack');
   $self->plugin('AssetPack::LibSass');
   $self->plugin('MailException' => $self->config('MailException'));
 
@@ -60,10 +62,11 @@
 
   $self->asset(
     'korap.js' => (
-#      '/js/d3.v3.min.js',
+      '/js/d3.v3.min.js',
 #      '/js/dagre-d3.min.js',
-#      '/js/translateTree.js',
-#      '/js/jquery-2.0.0.min.js',
+      '/js/dagre-d3.js',
+      '/js/translateTree.js',
+      '/js/jquery-2.0.0.min.js', # Temp
       '/js/tutorialCookie.js',
       '/js/translateTable.js',
       '/js/hint.js',
@@ -103,15 +106,28 @@
   $collection->to('info#about_collection');
   my $collection_id = $collection->bridge('/:collection_id');
   # stats
-  $collection_id->search;
+};
+
+
+1;
+
+
+__END__
+
+
+
+# No shortcut!
+
+#  $collection_id->search;
 
   # Corpus data
   my $corpus_res = $r->route('/corpus');
   my $corpus = $corpus_res->route('/:corpus_id');
+
   # Todo: Stats
-  $corpus->search->name('search_corpus');
-  my $doc = $corpus->route('/#doc_id');
-  $doc->search->name('search_document');
+#  $corpus->search->name('search_corpus');
+#  my $doc = $corpus->route('/#doc_id');
+#  $doc->search->name('search_document');
 
   # Match data
   my $match = $doc->route('/:match_id');
diff --git a/lib/Korap/API.pm b/lib/Korap/API.pm
new file mode 100644
index 0000000..59ea869
--- /dev/null
+++ b/lib/Korap/API.pm
@@ -0,0 +1,273 @@
+package Korap::API;
+use Mojo::Base 'Mojolicious::Plugin';
+use Scalar::Util 'blessed';
+use strict;
+use warnings;
+
+# KorAP Search engine for Mojolicious::Plugin::Search
+
+# Todo: Add fixtures
+# Todo: Support search in corpus and virtualcollection
+
+
+# Register the plugin
+sub register {
+  my ($plugin, $mojo, $index_class, $param) = @_;
+  $param ||= {};
+
+  # Add attributes to the index class
+  $index_class->attr(api => $param->{api});
+  $index_class->attr([qw/cutoff
+			 query_language
+			 time_exceeded
+			 api_request
+			 _api_cache
+			 api_response
+			 benchmark
+			 query_jsonld/]);
+  $index_class->attr(no_cache => 0);
+};
+
+
+# Search the index
+sub search {
+  my $self = shift;
+  my $index = shift;
+
+  my $c = $index->controller;
+
+  # No query defined
+  return unless $index->query;
+
+  # If there is a callback, do async
+  my $cb = pop if ref $_[-1] && ref $_[-1] eq 'CODE';
+
+  my %param = @_;
+
+  # Set cutoff from param
+  $index->cutoff(delete $param{cutoff});
+
+  # Set query language
+  $index->query_language(delete $param{query_language} // 'poliqarp');
+
+  # TODO: This should also be a query parameter
+  $index->no_cache(1) if $param{no_cache} or $c->param('no_cache');
+
+  my %query;
+  $query{q}      = $index->query;
+  $query{ql}     = $index->query_language;
+  $query{page}   = $index->start_page;
+  $query{count}  = $index->items_per_page;
+  $query{cutoff} = 'true' if $index->cutoff;
+
+  # Todo: support corpus and collection
+  # Create query url
+  my $url = Mojo::URL->new($index->api);
+  $url->path('search');
+  $url->query(\%query);
+
+  # Cache based on URL
+  $index->_api_cache('total-' . $url->to_string);
+
+  # Set context based on parameter
+  $url->query({ context => $c->param('context') // 'paragraph' });
+
+  # Check cache for total results
+  my $total_results;
+
+  if (!$index->no_cache &&
+	defined ($total_results = $c->chi->get($index->_api_cache))) {
+
+    # Set total results from cache
+    $index->total_results($total_results);
+    $c->app->log->debug('Get total result from cache');
+
+    # Set cutoff unless already set
+    $url->query({cutoff => 'true'}) unless defined $index->cutoff;
+  };
+
+  # Set api request for debugging
+  $index->api_request($url->to_string);
+
+  # Create new user agent and set timeout to 2 minutes
+  my $ua = Mojo::UserAgent->new;
+  $ua->inactivity_timeout(120);
+
+  # Denugging
+  $c->app->log->debug('Search for ' . $index->api_request);
+
+  # Search non-blocking
+  if ($cb) {
+
+    # Non-blocking request
+    $ua->get(
+      $url->to_string => sub {
+	my ($ua, $tx) = @_;
+	unless ($tx->success) {
+	  if (my $e = $tx->error) {
+	    warn 'Problem: ' . $e->{message};
+	    return $cb->($index);
+	  };
+	};
+
+	$self->_process_response($index, pop);
+	return $cb->($index);
+      });
+
+    Mojo::IOLoop->wait unless Mojo::IOLoop->is_running;
+  }
+
+  # Search blocking
+  else {
+    my $tx = $ua->get($url);
+    return $self->_process_response($index, $tx);
+  };
+};
+
+
+sub _process_response {
+  my ($self, $index, $tx) = @_;
+  my $c = $index->controller;
+
+  # An error has occurded
+  if (my $e = $tx->error) {
+    $c->notify(
+      error =>
+	($e->{code} ? $e->{code} . ': ' : '') .
+	  $e->{message} . ' (remote)'
+	);
+    return;
+  };
+
+  # Response was fine
+  if (my $res = $tx->success) {
+
+    # Set api response for debugging
+    $index->api_response($res->body) if $c->korap_test_port;
+
+    # Json failure
+    my $json;
+    unless ($json = $res->json) {
+      $c->notify(error => 'JSON response is invalid');
+      return;
+    };
+
+    # Reformat benchmark counter
+    my $benchmark = $json->{benchmark};
+    if ($benchmark && $benchmark =~ s/\s+(m)?s$//) {
+      $benchmark = sprintf("%.2f", $benchmark) . ($1 ? $1 : '') . 's';
+    };
+    # Set benchmark
+    $index->benchmark($benchmark);
+
+    # Set time exceeded
+    if ($json->{timeExceeded} && $json->{timeExceeded} eq Mojo::JSON::true) {
+      $index->time_exceeded(1);
+    };
+
+    # Set result values
+    $index->items_per_page($json->{itemsPerPage});
+    $index->query_jsonld($json->{request}->{query});
+    $index->results(_map_matches($json->{matches}));
+
+    # Total results not set by stash
+    if ($index->total_results == -1) {
+
+      if ($json->{totalResults} && $json->{totalResults} > -1) {
+	$c->app->log->debug('Cache total result');
+	$c->chi->set($index->_api_cache => $json->{totalResults}, '120min');
+	$index->total_results($json->{totalResults});
+      };
+    };
+
+    # Add warnings (Legacy)
+    if ($json->{warning}) {
+      $json->{warning} =~ s/;\s+null$//;
+      $c->notify(warn => $json->{warning});
+    };
+
+    $self->_notify_on_error($c, 0, $json);
+  }
+
+  # Request failed
+  else {
+    $self->_notify_on_error($c, 1, $tx->res);
+  };
+  return 1;
+};
+
+
+
+sub _notify_on_error {
+  my ($self, $c, $failure, $res) = @_;
+  my $json = $res;
+
+  my $log = $c->app->log;
+
+  if (blessed $res) {
+    if (blessed $res ne 'Mojo::JSON') {
+      $json = $res->json;
+    };
+  };
+
+  if ($json) {
+    if ($json->{error}) {
+      # Temp
+      $json->{error} =~ s/;\s+null$//;
+      $c->notify(error => $json->{error});
+      return;
+    }
+
+    # New error messages
+    elsif ($json->{errstr}) {
+      # Temp
+      $json->{errstr} =~ s/;\s+null$//;
+      $c->notify(error => $json->{errstr});
+      return;
+    }
+
+    # policy service error messages
+    elsif ($json->{status}) {
+      $c->notify(error => 'Middleware error ' . $json->{status});
+      return;
+    };
+  };
+
+  if ($failure) {
+    $c->notify(error => (
+      ($res->{code}    ? $res->{code} . ': ' : '') .
+      ($res->{message} ? $res->{message}     : 'Unknown error') .
+      ' (remote)'
+    ));
+  };
+};
+
+
+sub _map_matches {
+  return () unless $_[0];
+  map {
+    $_->{ID} =~ s/^match\-[^!]+![^-]+-//;
+    $_->{docID} =~ s/^[^_]+_//;
+    $_;
+  } @{ shift() };
+};
+
+
+1;
+
+__END__
+
+=pod
+
+Additionally supported query parameters:
+- query_language
+- cutoff
+- no_cache
+
+Additional index attributes:
+- api
+- time_exceeded
+- api_request
+- api_response
+- benchmark
+- query_jsonld
diff --git a/lib/Korap/Plugin/KorapHelpers.pm b/lib/Korap/Plugin/KorapHelpers.pm
new file mode 100644
index 0000000..45cf3cb
--- /dev/null
+++ b/lib/Korap/Plugin/KorapHelpers.pm
@@ -0,0 +1,25 @@
+package Korap::Plugin::KorapHelpers;
+use Mojo::Base 'Mojolicious::Plugin';
+
+sub register {
+  my ($plugin, $mojo) = @_;
+
+  $mojo->helper(
+    korap_test_port => sub {
+      my $c = shift;
+      if (defined $c->stash('korap.test_port')) {
+	return $c->stash('korap.test_port');
+      };
+
+      if ($c->req->url->to_abs->port == 6666 ||
+	    $c->app->mode =~ m/^development|test$/) {
+	$c->stash('korap.test_port' => 1);
+	return 1;
+      };
+
+      $c->stash('korap.test_port' => 0);
+      return 0;
+    });
+};
+
+1;
diff --git a/lib/Korap/Plugin/KorapSearch.pm b/lib/Korap/Plugin/KorapSearch.pm
index 0ad520e..7aab520 100644
--- a/lib/Korap/Plugin/KorapSearch.pm
+++ b/lib/Korap/Plugin/KorapSearch.pm
@@ -156,7 +156,7 @@
       $c->stash('search.itemsPerPage' => $count);
 
       # Only set this when on test port
-      if ($c->stash('test_port')) {
+      if ($c->korap_test_port) {
 	$c->stash('search.apirequest' => $url->to_string);
       };
 
diff --git a/lib/Korap/Plugin/KorapTagHelpers.pm b/lib/Korap/Plugin/KorapTagHelpers.pm
index 0cd0364..2c1cd6c 100644
--- a/lib/Korap/Plugin/KorapTagHelpers.pm
+++ b/lib/Korap/Plugin/KorapTagHelpers.pm
@@ -26,7 +26,7 @@
 
       # Tutorial wasn't embedded - but opened for testing
       elsif ($c->param('testing') &&
-	       $c->stash('test_port') &&
+	       $c->korap_test_port &&
 		 $param{tests}) {
 
 	my $tests = $param{tests} // [];
diff --git a/lib/Korap/Search.pm b/lib/Korap/Search.pm
index 5918fd9..5980b3f 100644
--- a/lib/Korap/Search.pm
+++ b/lib/Korap/Search.pm
@@ -1,6 +1,9 @@
 package Korap::Search;
 use Mojo::Base 'Mojolicious::Controller';
 
+# Add X-Forwarded-For to user agent call everywhere
+
+
 # This action will render a template
 sub remote {
   my $c = shift;
@@ -10,11 +13,6 @@
   my $api = $c->config('KorAP')->{'api-0.1'};
 
   # Todo: Support query building directly
-
-  $c->stash(test_port => (
-    $c->req->url->to_abs->port == 6666 ||
-      $c->app->mode =~ m/^development|test$/) ? 1 : 0);
-
   if ((scalar $c->param('action') // '') eq 'inspect') {
     my $url = Mojo::URL->new($api)->path('search');
 
diff --git a/lib/Korap/Tutorial.pm b/lib/Korap/Tutorial.pm
index e8726ee..f9d99da 100644
--- a/lib/Korap/Tutorial.pm
+++ b/lib/Korap/Tutorial.pm
@@ -4,11 +4,6 @@
 sub page {
   my $c = shift;
 
-  # Redundant in Search
-  $c->stash(test_port => (
-    $c->req->url->to_abs->port == 6666 ||
-      $c->app->mode =~ m/^development|test$/) ? 1 : 0);
-
   if ($c->param('embedded')) {
     $c->layout('snippet');
     $c->stash(embedded => 1);
diff --git a/public/js/ajax.js b/public/js/ajax.js
index 2af0bc0..72ef1f7 100644
--- a/public/js/ajax.js
+++ b/public/js/ajax.js
@@ -3,6 +3,8 @@
 // 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
 var Ajax = {
   getJSON : function (url, onload) {
     var r = new XMLHttpRequest();
diff --git a/public/js/translateTree.js b/public/js/translateTree.js
index e7ce83d..6ac4378 100644
--- a/public/js/translateTree.js
+++ b/public/js/translateTree.js
@@ -90,8 +90,12 @@
 function showTree (o, foundry, layer) {
   var match = o.parentNode.parentNode;
 
+console.log("Match: " + match);
+
   var tree = d3.select(match).select("div > div.treeInfo");
 
+console.log("Tree: " + tree);
+
   if (tree.classed("active")) {
     tree.classed("active", false);
     return;
@@ -102,6 +106,8 @@
   };
 
   var corpusID = match.getAttribute('data-corpus-id');
+console.log(corpusID);
+
   var docID    = match.getAttribute('data-doc-id');
   var matchID  = match.getAttribute('data-match-id');
   var url      =
@@ -113,16 +119,38 @@
     '&layer=' + layer +
     '&spans=true';
 
+  var wrapper = new String("<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 svg = tree.append("svg");
+    var svgGroup = svg.append("svg:g");
+
+  var treething = translateTree(wrapper);
+console.log(treething);  
+
+    var layout = renderer.run(treething, svgGroup);
+    // 10 pixel padding
+    var w = layout.graph().width;
+    var h = layout.graph().height;
+    svg.attr("width", w + 10);
+    svg.attr("height", h + 10);
+    svgGroup.attr("transform", "translate(5, 5)");
+    tree.classed("active", true);
+
+/*
+
   jQuery.getJSON(url, function (res) {
     var svg = tree.append("svg");
     var svgGroup = svg.append("svg:g");
     var treething = translateTree(res['snippet']);
-
     var layout = renderer.run(treething, svgGroup);
-
-    svg.attr("width", layout.graph().width + 40)
-      .attr("height", layout.graph().height + 40);
-
+    // 10 pixel padding
+    var w = layout.graph().width;
+    var h = layout.graph().height;
+    svg.attr("width", w + 10);
+    svg.attr("height", h + 10);
+    svgGroup.attr("transform", "translate(5, 5)");
     tree.classed("active", true);
   });
+
+*/
 };
diff --git a/public/sass/matchinfo.scss b/public/sass/matchinfo.scss
index ac5830e..8c0b087 100644
--- a/public/sass/matchinfo.scss
+++ b/public/sass/matchinfo.scss
@@ -23,6 +23,11 @@
     font-size: 10pt;
     padding: 3pt 10pt; /* wie in ol > li.active p */
     /* All cells */
+    > tr:nth-child(odd) {
+      td {
+        background-color: lighten($dark-orange, 25%);
+      }
+    }
     > tr {
       > * {
 	padding: 1pt 6pt;
diff --git a/public/sass/query.scss b/public/sass/query.scss
index 4735d0f..b5abe2f 100644
--- a/public/sass/query.scss
+++ b/public/sass/query.scss
@@ -7,7 +7,7 @@
   overflow-x: hidden;
   color: $darkest-orange;
   background-color: $light-orange;
-  text-shadow: $light-shadow;
+//  text-shadow: $light-shadow;
   border: {
     color: $dark-orange;
     style: solid;
diff --git a/t/basic.t b/t/basic.t
index abb2332..b24e2c0 100644
--- a/t/basic.t
+++ b/t/basic.t
@@ -4,6 +4,8 @@
 use Test::Mojo;
 
 my $t = Test::Mojo->new('Korap');
-$t->get_ok('/')->status_is(200)->content_like(qr/Go to/i);
+$t->get_ok('/')
+  ->status_is(200)
+  ->content_like(qr/Go to/i);
 
 done_testing();
diff --git a/t/search-engine.t b/t/search-engine.t
new file mode 100644
index 0000000..e813e6f
--- /dev/null
+++ b/t/search-engine.t
@@ -0,0 +1,84 @@
+use Mojo::Base -strict;
+use lib '../lib', 'lib';
+use Test::More;
+use Test::Mojo;
+
+my $t = Test::Mojo->new('Korap');
+
+$t->app->routes->get('/searchtest')->to(
+  cb => sub {
+    my $c = shift;
+    $c->render(inline => <<'TEMPLATE');
+%= search query => 'baum', start_page => param('p'), no_cache => 1, begin
+<h1><%= search->query %></h1>
+<p id="api"><%= search->api %></p>
+<p id="cutoff"><%= search->cutoff %></p>
+<p id="ql"><%= search->query_language %></p>
+<p id="no_cache"><%= search->no_cache %></p>
+<p id="start_page"><%= search->start_page %></p>
+<p id="total_results"><%= search->total_results %></p>
+<p id="api_request"><%= search->api_request %></p>
+%=  search_results begin
+  <li><%= $_->{ID} %></li>
+%   end
+% end
+TEMPLATE
+  }
+);
+
+my $exttemplate = <<'EXTTEMPLATE';
+<h1><%= search->query %></h1>
+<p id="api"><%= search->api %></p>
+<p id="cutoff"><%= search->cutoff %></p>
+<p id="ql"><%= search->query_language %></p>
+<p id="no_cache"><%= search->no_cache %></p>
+<p id="start_page"><%= search->start_page %></p>
+<p id="total_results"><%= search->total_results %></p>
+<p id="api_request"><%= search->api_request %></p>
+%=  search_results begin
+  <li><%= $_->{ID} %></li>
+%   end
+EXTTEMPLATE
+
+
+$t->app->routes->get('/searchasync')->to(
+  cb => sub {
+    my $c = shift;
+    $c->search(
+      query => 'baum',
+      start_page => $c->param('p'),
+      no_cache => 1,
+      cb => sub {
+	return $c->render(inline => $exttemplate);
+      }
+    );
+  }
+);
+
+$t->get_ok('/searchasync')
+  ->status_is(200)
+  ->text_is('.notify-error', '')
+  ->text_is('h1', 'baum')
+  ->text_is('#api', 'http://10.0.10.13:7070/api/v0.1/')
+  ->text_is('#cutoff', '')
+  ->text_is('#ql', 'poliqarp')
+  ->text_is('#no_cache', 1)
+  ->text_is('#start_page', 1)
+  ->text_is('#total_results', 3)
+  ->text_is('li', 'p265-266');
+
+
+$t->get_ok('/searchtest')
+  ->status_is(200)
+  ->text_is('.notify-error', '')
+  ->text_is('h1', 'baum')
+  ->text_is('#api', 'http://10.0.10.13:7070/api/v0.1/')
+  ->text_is('#cutoff', '')
+  ->text_is('#ql', 'poliqarp')
+  ->text_is('#no_cache', 1)
+  ->text_is('#start_page', 1)
+  ->text_is('#total_results', 3)
+  ->text_is('li', 'p265-266');
+
+
+done_testing;
diff --git a/templates/match.html.ep b/templates/match.html.ep
index 5ec0742..e6e6866 100644
--- a/templates/match.html.ep
+++ b/templates/match.html.ep
@@ -1,32 +1,37 @@
 %# ID, title, corpusID, author, pubDate, textClass
-<li data-corpus-id="<%= $match->{corpusID} %>"
-    data-doc-id="<%= $match->{docID} %>"
-    data-match-id="<%= $match->{ID} %>"
-    id="<%= $match->{corpusID} %>-<%= $match->{docID} %>-<%= $match->{ID} %>"
-<% if (current_route eq 'match') { %>class="active"<% } %>
-    >
+<li data-corpus-id="<%= $match->{corpusID} %>" \
+data-doc-id="<%= $match->{docID} %>" \
+data-match-id="<%= $match->{ID} %>" \
+id="<%= $match->{corpusID} %>-<%= $match->{docID} %>-<%= $match->{ID} %>"\
+<% if (current_route eq 'match') { %> class="active"<% } =%>
+>
+%#
+%# -- Match information (snippet, morph table, tree information etc.)
   <div>
     <div class="snippet"><%== $match->{snippet} %></div>
     <div class="tokenInfo"></div>
-%#    <div class="treeInfo"></div>
+    <div class="treeInfo"></div>
   </div>
-  <p>
+%#
+%# -- Reference string
+  <p>\
 % if ($match->{title}) {
-    <strong><%= $match->{title} %></strong>
+<strong><%= $match->{title} %></strong>\
 % };
-    <%= $match->{author} ? ' by ' . $match->{author}  : '' %><% if ($match->{title} || $match->{author}) { %>;<% } %>
-    published on <%= date_format $match->{pubDate} %>
-    as <%= $match->{docID} %> (<%= $match->{corpusID} %>)
-  </p>
+<%= $match->{author} ? ' by ' . $match->{author}  : '' %><% if ($match->{title} || $match->{author}) { %>;<% } =%>
+ published on <%= date_format $match->{pubDate} %>\
+ as <%= $match->{docID} %> (<%= $match->{corpusID} %>)\
+</p>
+%#
+%# -- Action buttons
   <ul class="action right">
 % if (current_route ne 'match') {
-    <li class="close" title="Close"><a href="#"><i class="fa fa-toggle-up"></a></i></li>
-%#    <li class="open" title="Open in new tab"><%= link_to 'match', { corpus_id => $match->{corpusID}, doc_id => $match->{docID}, match_id => $match->{ID} }, target => '_blank', begin %><i class="fa fa-external-link-square"></i><% end %></li>
+    <li class="close" title="Close"><a href="#"><i class="fa fa-toggle-up"></i></a></li>
+%#  <li class="open" title="Open in new tab"><%= link_to 'match', { corpus_id => $match->{corpusID}, doc_id => $match->{docID}, match_id => $match->{ID} }, target => '_blank', begin %><i class="fa fa-external-link-square"></i><% end %></li>
     <li class="open" title="Open in new tab"><a href="#<%= $match->{corpusID} %>-<%= $match->{docID} %>-<%= $match->{ID} %>" 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>
-%#   <li onclick="showTree(this, 'xip', 'c')" title="Tree Visualizations"><i class="fa fa-sitemap"></i></li>
+    <li onclick="showTree(this, 'xip', 'c')" title="Tree Visualizations"><i class="fa fa-sitemap"></i></li>
 %#    <li title="Remember"><i class="fa fa-star-o"></i></li>
   </ul>
 </li>
diff --git a/templates/search.html.ep b/templates/search.html.ep
index 4d5c2c3..2bf4f80 100644
--- a/templates/search.html.ep
+++ b/templates/search.html.ep
@@ -1,6 +1,5 @@
 % if (param 'q') {
 % content 'main' => begin
-%=  search begin
 %     unless (param 'snippet') {
 <div style="clear: both">
 %       my $url = url_with->query(['p' => '{page}']);
@@ -31,7 +30,6 @@
 </ol>
 </div>
 % };
-%   end
 % end
 
 % content 'javascript' => begin