Merge "Open hint and QL menu in the guided tour"
diff --git a/Changes b/Changes
index 440cf1e..1d3d800 100755
--- a/Changes
+++ b/Changes
@@ -1,4 +1,6 @@
-0.37 2019-09-25
+0.37 2019-10-16
+        - Removed deprecated 'kalamar_test_port' helper.
+        - Separated KalamarHelpers and KalamarPages.
 
 0.36 2019-09-19
         - Rename all cookies to be independent
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index f1a01b0..cc41eed 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -146,6 +146,7 @@
   foreach (
     'TagHelpers::MailToChiffre', # Obfuscate email addresses
     'KalamarHelpers',            # Specific Helpers for Kalamar
+    'KalamarPages',             # Page Helpers for Kalamar
     'KalamarErrors',             # Specific Errors for Kalamar
     'KalamarUser',               # Specific Helpers for Kalamar Users
     'ClientIP',                  # Get client IP from X-Forwarded-For
diff --git a/lib/Kalamar/Plugin/KalamarHelpers.pm b/lib/Kalamar/Plugin/KalamarHelpers.pm
index 4731e4d..4745860 100644
--- a/lib/Kalamar/Plugin/KalamarHelpers.pm
+++ b/lib/Kalamar/Plugin/KalamarHelpers.pm
@@ -1,14 +1,8 @@
 package Kalamar::Plugin::KalamarHelpers;
-use Mojo::Base 'Mojolicious::Plugin';
-use Mojo::JSON qw/decode_json true false/;
 use Mojo::ByteStream 'b';
-use Mojo::Util qw/xml_escape deprecated/;
+use Mojo::Base 'Mojolicious::Plugin';
+use Mojo::Util qw/deprecated/;
 
-# TODO:
-#   Add documentation plugin to programmatically
-#   create documentation navigation as a content_block
-#   so custom routes to custom templates can easily
-#   be configured
 
 sub register {
   my ($plugin, $mojo) = @_;
@@ -49,220 +43,6 @@
     }
   );
 
-  # Documentation link
-  # TODO: Support opener mechanism, so the link will open the embedded
-  # documentation in case it's not there.
-  $mojo->helper(
-    doc_link_to => sub {
-      my $c = shift;
-      my $title = shift;
-      my $page = pop;
-      my $scope = shift;
-
-      ($page, my $fragment) = split '#', $page;
-
-      my $url = $c->doc->url($scope, $page);
-      $url->fragment($fragment) if $fragment;
-
-      return $c->link_to(
-        $title,
-        $url,
-        class => 'doc-link'
-      );
-    }
-  );
-
-  $mojo->helper(
-    doc_ext_link_to => sub {
-      my $c = shift;
-      return $c->link_to(@_, target => '_top');
-    }
-  );
-
-
-  # Documentation alert - Under Construction!
-  $mojo->helper(
-    doc_uc => sub {
-      my $c = shift;
-      return $c->tag('p', $c->loc('underConstruction'));
-    }
-  );
-
-  $mojo->helper(
-    doc_opener => sub {
-      my $c = shift;
-      my $cb = pop;
-      my $page = pop;
-      my $scope = shift;
-      my $url;
-      if ($page) {
-        $url = $c->doc->url($scope, $page);
-        $url->path->canonicalize;
-      }
-      else {
-        $url = $c->url_for('doc_start');
-      };
-      return $c->link_to($cb->($c), $url);
-    }
-  );
-
-
-  $mojo->helper(
-    'doc.url' => sub {
-      my $c = shift;
-      my $page = pop;
-      my $scope = shift;
-      if ($scope) {
-        return $c->url_with(
-          'doc2',
-          page => $page,
-          scope => $scope
-        );
-      };
-
-      return $c->url_with(
-        'doc1',
-        page => $page
-      );
-    }
-  );
-
-  # Documentation navigation helper
-  $mojo->helper(
-    doc_navi => sub {
-      my $c = shift;
-      my $items = pop;
-      my $scope = shift;
-
-      # Create unordered list
-      my $html = '<ul class="nav">'."\n";
-
-      # Embed all link tags
-      foreach (@$items) {
-
-        my ($active, $url) = 0;
-
-        # There is a fragment!
-        if (index($_->{id}, '#') == 0) {
-
-          my $part_scope = scalar($scope);
-          $part_scope =~ s!\/([^\/]+)$!!;
-          my $page = $1;
-          my $id = $_->{id};
-          $id =~ s/^#//;
-
-          $url = $c->doc->url($part_scope, $page);
-          $url->fragment($id);
-        }
-
-        # There is no fragment
-        else {
-
-          # The item is active
-          if ($c->stash('page') && $c->stash('page') eq $_->{id}) {
-            $active = 1;
-          };
-
-          # Generate url with query parameter inheritance
-          $url = $c->doc->url($scope, $_->{id});
-
-          # Canonicalize (for empty scopes)
-          $url->path->canonicalize;
-          $url->fragment('tutorial-top');
-        };
-
-        my @classes;
-        push(@classes, $_->{'class'}) if $_->{'class'};
-        push(@classes, 'active') if $active;
-
-
-        # New list item
-        $html .= '<li';
-        if (@classes) {
-          $html .= ' class="' . join(' ', @classes) . '"';
-        };
-        $html .= '>';
-
-        # Translate title
-        my $title = $c->loc('Nav_' . $_->{id}, $_->{title});
-
-        # Generate link
-        $html .= $c->link_to($title, $url);
-
-        # Set sub entries
-        if ($_->{items} && ref($_->{items}) eq 'ARRAY') {
-          $html .= "\n";
-          my $subscope = $scope ? scalar($scope) . '/' . $_->{id} : $_->{id};
-          $html .= $c->doc_navi($subscope, $_->{items});
-          $html .= "</li>\n";
-        }
-        else {
-          $html .= "</li>\n";
-        };
-      };
-      return $html . "</ul>\n";
-    }
-  );
-
-
-  # Create helper for queries in the tutorial
-  $mojo->helper(
-    doc_query => sub {
-      my ($c, $ql, $q, %param) = @_;
-
-      # Query is not supported in the corpus
-      if ($q =~ s/^\*\*\s*//) {
-        # Escape query for html embedding
-        $q = xml_escape $q;
-
-        return b(
-          '<pre class="query tutorial unsupported">' .
-            "<code>$q</code>" .
-            '<span title="' . $c->loc('notAvailInCorpus') . '">*</span>' .
-            '</pre>');
-      };
-
-      # Escape query for html embedding
-      $q = xml_escape $q;
-
-      # Return tag
-      b('<pre class="query tutorial" ' .
-          qq!data-query="$q" data-query-cutoff="! .
-          ($param{cutoff} ? 1 : 0) .
-          '"' .
-          qq! data-query-language="$ql">! .
-          '<code>' . $q . '</code>' .
-          '</pre>'
-        );
-    }
-  );
-
-
-  # Check for test port
-  $mojo->helper(
-    kalamar_test_port => sub {
-      my $c = shift;
-
-      # 2018-11-15
-      deprecated 'kalamar_test_port is deprecated and will be removed';
-
-      # Test port is defined in the stash
-      if (defined $c->stash('kalamar.test_port')) {
-        return $c->stash('kalamar.test_port');
-      };
-
-      # Check the port
-      if ($c->req->url->to_abs->port == 6666 ||
-            $c->app->mode =~ m/^development|test$/) {
-        $c->stash('kalamar.test_port' => 1);
-        return 1;
-      };
-
-      # No test port
-      $c->stash('kalamar.test_port' => 0);
-      return 0;
-    });
-
 
   # Establish 'search_results' taghelper
   # This is based on Mojolicious::Plugin::Search
@@ -393,104 +173,3 @@
 
 
 1;
-
-
-__END__
-
-=pod
-
-=encoding utf8
-
-=head1 NAME
-
-Kalamar::Plugin::KalamarHelpers
-
-
-=head1 DESCRIPTION
-
-L<Kalamar::Plugin::KalamarHelpers> makes Kalamar specific
-helpers for Mojolicious available.
-
-
-=head1 HELPERS
-
-=head2 doc_link_to
-
-  %# In templates
-  %= doc_link_to 'Kalamar', 'korap', 'kalamar'
-
-Create a link to the documentation. Accepts a name, a scope, and a page.
-
-
-=head2 doc_ext_link_to
-
-  %# In templates
-  %= doc_ext_link_to 'GitHub', "https://github.com/KorAP/Koral"
-
-Creates a link to an external page, that will be opened in the top frame,
-in case it's in an embedded frame (used in the tutorial).
-
-=head2 doc_uc
-
-  %# In templates
-  %= doc_uc
-
-Generates an C<Under Construction> notification.
-
-
-=head2 doc_opener
-
-Currently not used.
-
-
-=head2 doc_navi
-
-Returns an HTML representation of the documentation navigation,
-based on active navigation items.
-
-
-=head2 doc_query
-
-  %# In templates
-  %= doc_query poliqarp => 'Baum'
-
-Creates an interactive query view for documentation purposes.
-
-
-=head2 kalamar_test_port
-
-  # In controllers
-  if ($c->kalamar_test_port) {
-    $c->app->log->debug('Kalamar runs on test port');
-  };
-
-Returns a C<true> value in case Kalamar runs on test port C<6666>.
-
-
-=head2 korap_overview
-
-  %# In templates
-  %= korap_overview 'kalamar'
-
-Returns a modified and relatified overview svg image to be embedded
-as an object in templates.
-
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2015-2018, L<IDS Mannheim|http://www.ids-mannheim.de/>
-Author: L<Nils Diewald|http://nils-diewald.de/>
-
-Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
-Corpus Analysis Platform at the
-L<Leibniz Institute for the German Language (IDS)|http://ids-mannheim.de/>,
-member of the
-L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
-and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
-funded by the
-L<Federal Ministry of Education and Research (BMBF)|http://www.bmbf.de/en/>.
-
-Kalamar is free software published under the
-L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE>.
-
-=cut
diff --git a/lib/Kalamar/Plugin/KalamarPages.pm b/lib/Kalamar/Plugin/KalamarPages.pm
new file mode 100644
index 0000000..858ca91
--- /dev/null
+++ b/lib/Kalamar/Plugin/KalamarPages.pm
@@ -0,0 +1,289 @@
+package Kalamar::Plugin::KalamarPages;
+use Mojo::Base 'Mojolicious::Plugin';
+use Mojo::JSON qw/decode_json true false/;
+use Mojo::ByteStream 'b';
+use Mojo::Util qw/xml_escape deprecated/;
+
+
+# TODO:
+#   Add documentation plugin to programmatically
+#   create documentation navigation as a content_block
+#   so custom routes to custom templates can easily
+#   be configured
+sub register {
+  my ($plugin, $mojo) = @_;
+
+  # Documentation link
+  # TODO: Support opener mechanism, so the link will open the embedded
+  # documentation in case it's not there.
+  $mojo->helper(
+    doc_link_to => sub {
+      my $c = shift;
+      my $title = shift;
+      my $page = pop;
+      my $scope = shift;
+
+      ($page, my $fragment) = split '#', $page;
+
+      my $url = $c->doc->url($scope, $page);
+      $url->fragment($fragment) if $fragment;
+
+      return $c->link_to(
+        $title,
+        $url,
+        class => 'doc-link'
+      );
+    }
+  );
+
+  $mojo->helper(
+    doc_ext_link_to => sub {
+      my $c = shift;
+      return $c->link_to(@_, target => '_top');
+    }
+  );
+
+
+  # Documentation alert - Under Construction!
+  $mojo->helper(
+    doc_uc => sub {
+      my $c = shift;
+      return $c->tag('p', $c->loc('underConstruction'));
+    }
+  );
+
+  $mojo->helper(
+    doc_opener => sub {
+      my $c = shift;
+      my $cb = pop;
+      my $page = pop;
+      my $scope = shift;
+      my $url;
+      if ($page) {
+        $url = $c->doc->url($scope, $page);
+        $url->path->canonicalize;
+      }
+      else {
+        $url = $c->url_for('doc_start');
+      };
+      return $c->link_to($cb->($c), $url);
+    }
+  );
+
+
+  $mojo->helper(
+    'doc.url' => sub {
+      my $c = shift;
+      my $page = pop;
+      my $scope = shift;
+      if ($scope) {
+        return $c->url_with(
+          'doc2',
+          page => $page,
+          scope => $scope
+        );
+      };
+
+      return $c->url_with(
+        'doc1',
+        page => $page
+      );
+    }
+  );
+
+  # Documentation navigation helper
+  $mojo->helper(
+    doc_navi => sub {
+      my $c = shift;
+      my $items = pop;
+      my $scope = shift;
+
+      # Create unordered list
+      my $html = '<ul class="nav">'."\n";
+
+      # Embed all link tags
+      foreach (@$items) {
+
+        my ($active, $url) = 0;
+
+        # There is a fragment!
+        if (index($_->{id}, '#') == 0) {
+
+          my $part_scope = scalar($scope);
+          $part_scope =~ s!\/([^\/]+)$!!;
+          my $page = $1;
+          my $id = $_->{id};
+          $id =~ s/^#//;
+
+          $url = $c->doc->url($part_scope, $page);
+          $url->fragment($id);
+        }
+
+        # There is no fragment
+        else {
+
+          # The item is active
+          if ($c->stash('page') && $c->stash('page') eq $_->{id}) {
+            $active = 1;
+          };
+
+          # Generate url with query parameter inheritance
+          $url = $c->doc->url($scope, $_->{id});
+
+          # Canonicalize (for empty scopes)
+          $url->path->canonicalize;
+          $url->fragment('tutorial-top');
+        };
+
+        my @classes;
+        push(@classes, $_->{'class'}) if $_->{'class'};
+        push(@classes, 'active') if $active;
+
+
+        # New list item
+        $html .= '<li';
+        if (@classes) {
+          $html .= ' class="' . join(' ', @classes) . '"';
+        };
+        $html .= '>';
+
+        # Translate title
+        my $title = $c->loc('Nav_' . $_->{id}, $_->{title});
+
+        # Generate link
+        $html .= $c->link_to($title, $url);
+
+        # Set sub entries
+        if ($_->{items} && ref($_->{items}) eq 'ARRAY') {
+          $html .= "\n";
+          my $subscope = $scope ? scalar($scope) . '/' . $_->{id} : $_->{id};
+          $html .= $c->doc_navi($subscope, $_->{items});
+          $html .= "</li>\n";
+        }
+        else {
+          $html .= "</li>\n";
+        };
+      };
+      return $html . "</ul>\n";
+    }
+  );
+
+
+  # Create helper for queries in the tutorial
+  $mojo->helper(
+    doc_query => sub {
+      my ($c, $ql, $q, %param) = @_;
+
+      # Query is not supported in the corpus
+      if ($q =~ s/^\*\*\s*//) {
+        # Escape query for html embedding
+        $q = xml_escape $q;
+
+        return b(
+          '<pre class="query tutorial unsupported">' .
+            "<code>$q</code>" .
+            '<span title="' . $c->loc('notAvailInCorpus') . '">*</span>' .
+            '</pre>');
+      };
+
+      # Escape query for html embedding
+      $q = xml_escape $q;
+
+      # Return tag
+      b('<pre class="query tutorial" ' .
+          qq!data-query="$q" data-query-cutoff="! .
+          ($param{cutoff} ? 1 : 0) .
+          '"' .
+          qq! data-query-language="$ql">! .
+          '<code>' . $q . '</code>' .
+          '</pre>'
+        );
+    }
+  );
+
+}
+
+1;
+
+
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+Kalamar::Plugin::KalamarHelpers
+
+
+=head1 DESCRIPTION
+
+L<Kalamar::Plugin::KalamarHelpers> makes Kalamar specific
+helpers for Mojolicious available.
+
+
+=head1 HELPERS
+
+=head2 doc_link_to
+
+  %# In templates
+  %= doc_link_to 'Kalamar', 'korap', 'kalamar'
+
+Create a link to the documentation. Accepts a name, a scope, and a page.
+
+
+=head2 doc_ext_link_to
+
+  %# In templates
+  %= doc_ext_link_to 'GitHub', "https://github.com/KorAP/Koral"
+
+Creates a link to an external page, that will be opened in the top frame,
+in case it's in an embedded frame (used in the tutorial).
+
+=head2 doc_uc
+
+  %# In templates
+  %= doc_uc
+
+Generates an C<Under Construction> notification.
+
+
+=head2 doc_opener
+
+Currently not used.
+
+
+=head2 doc_navi
+
+Returns an HTML representation of the documentation navigation,
+based on active navigation items.
+
+
+=head2 doc_query
+
+  %# In templates
+  %= doc_query poliqarp => 'Baum'
+
+Creates an interactive query view for documentation purposes.
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015-2019, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Author: L<Nils Diewald|http://nils-diewald.de/>
+
+Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
+Corpus Analysis Platform at the
+L<Leibniz Institute for the German Language (IDS)|http://ids-mannheim.de/>,
+member of the
+L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
+and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
+funded by the
+L<Federal Ministry of Education and Research (BMBF)|http://www.bmbf.de/en/>.
+
+Kalamar is free software published under the
+L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE>.
+
+=cut
diff --git a/t/docnavi.t b/t/docnavi.t
index 576db84..dc1e18a 100644
--- a/t/docnavi.t
+++ b/t/docnavi.t
@@ -15,7 +15,7 @@
 $app->routes->get('/doc/*scope/:page')->to(cb => sub {})->name('doc2');
 
 # Load plugin to test
-$app->plugin('KalamarHelpers');
+$app->plugin('KalamarPages');
 
 my $languages = [qw/en de/];
 $app->plugin('Localize' => {