Merge "When authorization header exists, do not introduce token (required for proxying)"
diff --git a/Changes b/Changes
index 6efd47c..b33b2e3 100755
--- a/Changes
+++ b/Changes
@@ -1,3 +1,8 @@
+0.35 2019-07-05
+        - Added EXPERIMENTAL proxy to API route.
+        - Remove deprecated api configuration
+          (requires api_path instead now).
+
 0.34 2019-06-26
         - Introduced guided tour (hebasta, #19).
         - Updated dependency on M::P::Notifications to
diff --git a/Makefile.PL b/Makefile.PL
index 19c606e..92c8b68 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -14,9 +14,9 @@
   },
   LICENSE      => 'freebsd',
   PREREQ_PM => {
-    'Mojolicious' => '8.06',
+    'Mojolicious' => '8.18',
     'Mojolicious::Plugin::TagHelpers::Pagination' => 0.07,
-    'Mojolicious::Plugin::TagHelpers::MailToChiffre' => 0.09,
+    'Mojolicious::Plugin::TagHelpers::MailToChiffre' => 0.10,
     'Mojolicious::Plugin::ClosedRedirect' => 0.14,
     'Mojolicious::Plugin::Notifications' => 1.03,
     'Mojolicious::Plugin::MailException' => 0.20,
diff --git a/dev/demo/all.html b/dev/demo/all.html
index 9b63bc4..8417903 100644
--- a/dev/demo/all.html
+++ b/dev/demo/all.html
@@ -33,7 +33,7 @@
 		 id="q-field"
 		 value="abcdefghijklmnopqrstuvwxyz"
 		 autofocus="autofocus" />
-	  <button type="submit"><span>Go</span></button>
+	  <button id="qsubmit" type="submit"><span>Go</span></button>
 	  <!-- <i class="fa fa-arrow-circle-down show-hint" onclick="hint.popUp()"></i> -->
 	</div>
 
@@ -250,7 +250,7 @@
 	<p ><strong>KorAP</strong> is a new Corpus Analysis Platform, suited for large, multiple annotated corpora and complex search queries independent of particular research questions.</p>
 	<p>This is the new frontend for KorAP, with currently no access to restricted corpora. For full access, please visit the <a href="http://korap.ids-mannheim.de/app">first frontend</a>.</p>
 
-    <p><strong>New to KorAP?</strong> Please check out our <a href="#" onclick="tourshow()"> guided tour </a> or our <a href="#">tutorial</a>!</p>
+    <p><strong>New to KorAP?</strong> Please check out our <a href="#" onclick="KorAP.tourshow()"> guided tour </a> or our <a href="#" onclick="KorAP.tourshowR()"> secondTour </a> our <a href="#">tutorial</a>!</p>
 
 	<p>KorAP is developed at the <a href="http://www.ids-mannheim.de">Institute for the German Language</a> and funded by the <a href="http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/">Leibniz Association</a>. The separated modules are being published as open source at <a href="http://github.com/KorAP">GitHub</a>.</p>
 
diff --git a/dev/demo/alldemo.js b/dev/demo/alldemo.js
index c32ce7e..6f9f60b 100644
--- a/dev/demo/alldemo.js
+++ b/dev/demo/alldemo.js
@@ -804,13 +804,6 @@
   };
 
 
-  tourshow =  function(){
-    var tourClass = require('tour/tours');
-    let tour = tourClass.guidedTour();
-    tour.start();
-  };
-  
-
   /**
    * Do some things at the beginning.
    */
diff --git a/dev/js/spec/tourSpec.js b/dev/js/spec/tourSpec.js
index 5e4dca6..f30cc32 100644
--- a/dev/js/spec/tourSpec.js
+++ b/dev/js/spec/tourSpec.js
@@ -4,71 +4,115 @@
  * @author Helge Stallkamp
  */
 
-define(['tour/tours', 'vc'], function(tourClass, vcClass){
+define(['tour/tours', 'vc', 'session'], function(tourClass, vcClass, sessionClass){
   const loc   = KorAP.Locale;
  
   //TODO Read this file from the file system, see https://korap.ids-mannheim.de/gerrit/#/c/KorAP/Kalamar/+/2241/
-  var introKorAP  = "<form autocomplete='off' action='/' id='searchform'>" +
-  "<div id='searchbar'>" +
-    "<input autocapitalize='off' autocomplete='off ' autocorrect='off' autofocus='autofocus' id='q-field' name='q' placeholder='Find ...' spellcheck='false' type='search'>" +
-    "<button type='submit' title='Go!'><span>Go!</span></button>" +
-  "</div>"+
-  "<!-- Search in the following virtual collection -->"+
-  "<div id='vc-view'></div>" +
-  "in" +
-  "<input id='collection' name='collection' type='text'>" +
-  "with" +
-  "<span class='select'>" +
-    "<select id='ql-field' name='ql'><option value='poliqarp'>Poliqarp</option><option value='cosmas2'>Cosmas II</option><option value='annis'>Annis QL</option><option value='cql'>CQL v1.2</option><option value='fcsql'>FCSQL</option></select>" +
-  "</span>" +
-  "<div class='button right'>" +
-    "<input checked class='checkbox' id='q-cutoff-field' name='cutoff' type='checkbox' value='1'>"+
-    "<label for='q-cutoff-field' title='Just show the first matches in arbitrary order'><span id='glimpse'></span>Glimpse</label>" +
-    "<a class='tutorial' href='/doc' id='view-tutorial' tabindex='-1' title='Tutorial'><span>Tutorial</span></a>" +
-  "</div>" +
-  "<div class='clear'></div>" +
-  "</form>";
+  var introKorAP =
+  	"<form autocomplete='off' action='/' id='searchform'>" + 
+    "<div id='searchbar' class=''>" +
+      "<input autocapitalize='off' autocomplete='off' autocorrect='off' autofocus='autofocus' id='q-field' name='q' placeholder='Finde ...' spellcheck='false' type='search'>" +
+      "<button type='submit' id='qsubmit' title='Los!'><span>Los!</span></button>" + 
+    "</div>" + 
+    "<!-- Search in the following virtual collection -->"+
+    "<div id='vc-view'></div>" +
+    "in" +
+    "<span id='vc-choose' class='select'><span>allen Korpora</span></span>" +
+    "<input id='collection' name='collection' type='text' style='display: none;'>" +
+      "mit" +
+      "<span class='select'>" +
+        "<select id='ql-field' name='ql' style='display: none;'>" +
+            "<option value='poliqarp'>Poliqarp</option>" +
+            "<option value='cosmas2'>Cosmas II</option>" +
+            "<option value='annis'>Annis QL</option>" +
+            "<option value='cql'>CQL v1.2</option>" +
+            "<option value='fcsql'>FCSQL</option>" +
+        "</select>" +
+      "<span style='display: inline;'> Poliqarp</span>" +
+        "<ul style='outline: currentcolor none 0px;' tabindex='0' class='menu roll'>" +
+          "<span class='pref'></span>" +
+          "<div class='lengthField'>" +
+            "<span>Poliqarp--</span>" +
+            "<span>Cosmas II--</span>" +
+            "<span>Annis QL--</span>" +
+            "<span>CQL v1.2--</span>" +
+            "<span>FCSQL--</span>" +
+            "</div><div class='ruler' style='display: none;'><span></span><div></div>" +
+          "</div>" +
+         "</ul>" +
+       "</span>" +
+       "<div class='button right'>" +
+         "<input checked='' class='checkbox' id='q-cutoff-field' name='cutoff' type='checkbox' value='1'>" +
+           "<label for='q-cutoff-field' title='Zeige nur die ersten Treffer in beliebiger Reihenfolge'><span id='glimpse'></span>Glimpse</label>" +
+           "<a class='tutorial' id='view-tutorial' tabindex='-1' title='Einführung'><span>Einführung</span></a>"+
+       "</div>" +
+       "<div class='clear'></div>"+
+       "</form>" + 
+       "<div class='hint mirror' style='height: 0px; left: 238px; top: 36px; width: 1272px; padding-left: 2px; margin-left: 0px; border-left-width: 2px; border-left-style: solid; font-size: 14.6667px; font-family: Noto Sans;'>" +
+         "<span></span>" +
+           "<div id='hint' class=''>" +
+           "<div style='display: none;' class='alert hint'></div>" +
+           "<ul style='outline: currentcolor none 0px;' tabindex='0' class='menu roll hint'>" +
+             "<span class='pref'></span>" +
+             "<div class='lengthField'>" +
+               "<span>Base Annotation--</span>" +
+               "<span class='desc'>Structure--</span>" +
+               "<span>DeReKo--</span><span class='desc'>Structure--</span>"+
+             "</div>"
+             "<div class='ruler' style='display: none;'><span></span><div></div></div>"
+            "</ul>" +
+           "</div>" +
+          "</div>"; 
 
   let template = document.createElement('template');
   html = introKorAP.trim(); // Do not return a text node of whitespace as the result
   template.innerHTML = html;
-  intrkorap = template.content.firstChild;
-
+  intrkorap = template.content;
   
   //TODO Add hint and vc-choose, they are not part of the generated file
   describe('KorAP.GuidedTour', function(){
     it('IDs and classes, that are needed for the guided tour should be in existence', function(){
       expect(intrkorap.querySelector('#searchbar')).not.toBeNull();
       expect(intrkorap.querySelector('#q-field')).not.toBeNull();
-      //expect(intrkorap.querySelector('#hint')).not.toBeNull();
-      //expect(intrkorap.querySelector('#vc-choose')).not.toBeNull();     
+      expect(intrkorap.querySelector('#hint')).not.toBeNull();
+      expect(intrkorap.querySelector('#vc-choose')).not.toBeNull();     
       expect(intrkorap.querySelector('#vc-view')).not.toBeNull();
       expect(intrkorap.querySelector('#ql-field').parentNode).not.toBeNull();
       expect(intrkorap.querySelector('#glimpse')).not.toBeNull();
       expect(intrkorap.querySelector('#view-tutorial')).not.toBeNull();
-      expect(intrkorap.querySelector('#searchbar button[type=submit]')).not.toBeNull();
+      expect(intrkorap.querySelector('#qsubmit')).not.toBeNull();
     });
     
    
+  
    it('Guided Tour should be started and display steps and labels in the right order', function(){
-     let testTour = tourClass.guidedTour(intrkorap);
-     testTour.start();
-     let totalSteps = testTour.stepCount;
- 
+     let searchTour = tourClass.gTstartSearch(intrkorap);
+     searchTour.start();
+     let totalSteps = searchTour.stepCount;
      expect(document.querySelector(".introjs-tooltiptext").textContent).toEqual(loc.TOUR_sear1);
      expect(document.querySelector(".introjs-skipbutton").textContent).toEqual(loc.TOUR_lskip);
      expect(document.querySelector(".introjs-prevbutton").textContent).toEqual(loc.TOUR_lprev);
      expect(document.querySelector(".introjs-nextbutton").textContent).toEqual(loc.TOUR_lnext);
-     testTour.exit();
+     searchTour.exit();
      
      for(let i = 2; i <= totalSteps; i++){
-       testTour.goToStepNumber(i);
-       expect(document.querySelector(".introjs-tooltiptext").textContent).toEqual(testTour.testIntros[i-1]);
+       searchTour.goToStepNumber(i);
+       expect(document.querySelector(".introjs-tooltiptext").textContent).toEqual(searchTour.testIntros[i-1]);
        if(i == totalSteps){
-         expect(document.querySelector('.introjs-donebutton').textContent).toEqual(loc.TOUR_ldone);
-       }
-       testTour.exit();
-     }   
+         expect(document.querySelector(".introjs-donebutton").textContent).toEqual(loc.TOUR_seargo);
+         expect(document.querySelector(".introjs-prevbutton").textContent).toEqual(loc.TOUR_lprev);
+         expect(document.querySelector(".introjs-nextbutton").classList.contains("introjs-disabled")).toBe(true);
+       } 
+       searchTour.exit();
+     } 
+     
+     let resultTour = tourClass.gTshowResults(intrkorap);
+     KorAP.session = sessionClass.create('KalamarJSDem'); 
+     resultTour.start();
+     expect(document.querySelector(".introjs-tooltiptext").textContent).toEqual(loc.TOUR_result);
+     expect(document.querySelector(".introjs-donebutton").textContent).toEqual(loc.TOUR_ldone);   
+     resultTour.exit();
+    
    }); 
   });
 }    
diff --git a/dev/js/src/app/de.js b/dev/js/src/app/de.js
index 332ee07..142c276 100644
--- a/dev/js/src/app/de.js
+++ b/dev/js/src/app/de.js
@@ -1,3 +1,3 @@
 window.KorAP = window.KorAP || {};
 KorAP.Locale = KorAP.Locale || {};
-require(['loc/de','init']);
+require(['loc/dereko', 'loc/de', 'init']);
diff --git a/dev/js/src/app/en.js b/dev/js/src/app/en.js
index e851239..3521c84 100644
--- a/dev/js/src/app/en.js
+++ b/dev/js/src/app/en.js
@@ -1,3 +1,3 @@
 window.KorAP = window.KorAP || {};
 KorAP.Locale = KorAP.Locale || {};
-require(['init']);
+require(['loc/dereko', 'init']);
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index b78e870..ec11cf5 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -65,7 +65,16 @@
 
   KorAP.vc = vcClass.create(vcArray); 
   
+  KorAP.tourshow =  function(){
+    tourClass.gTstartSearch().start();
+  };
+ 
+  KorAP.tourshowR = function(){
+    tourClass.gTshowResults().start();
+  };
+    
   domReady(function (event) {
+      
     var obj = {};
 
     // What should be visible in the beginning?
@@ -343,7 +352,13 @@
         };
       });
     };
-
+ 
+    
+    //Starts the guided tour at the next page
+    if(KorAP.session.get("tour")){
+      tourClass.gTshowResults().start();
+    }
+    
     /**
      * Init hint helper
      * has to be final because of
@@ -360,11 +375,4 @@
     return obj;
   });
   
-
-  tourshow =  function(){
-    let tour = tourClass.guidedTour();
-    tour.start();
-  };
-   
-  
 });
diff --git a/dev/js/src/loc/de.js b/dev/js/src/loc/de.js
index 76f72bc..5cd7b68 100644
--- a/dev/js/src/loc/de.js
+++ b/dev/js/src/loc/de.js
@@ -52,7 +52,7 @@
   
   //Guided Tour: Steps
   loc.TOUR_sear1 = "Geben Sie die Suchanfrage hier ein.";
-  loc.TOUR_sear2 = "Zum Beispiel die Suche nach 'Baum'";
+  loc.TOUR_sear2 = "Zum Beispiel die Suche nach '"+ loc.TOUR_Qexample +  "'";
   loc.TOUR_searAnnot ="Für die Suche nach Annotationen steht der Annotationsassistent zur Verfügung.";
   loc.TOUR_annotAss = "Der Annotationsassistent erleichert die Formulierung von Suchanfragen mit Annotationen.";
   loc.TOUR_vccho1 = "Öffnen des Korpusassistenten";
@@ -62,4 +62,8 @@
   loc.TOUR_glimpse = "Beim Wählen dieser Option wird festgelegt ob nur die ersten Treffer in zufälliger Reihenfolge ausgewählt werden.";
   loc.TOUR_help = "Hier finden Sie Hilfe zu KorAP.";
   loc.TOUR_seargo = "Suchanfrage starten";
+  
+  //Guided Tour: explain the result
+  loc.TOUR_result = "Viel Spaß mit KorAP!";
+  
 });
diff --git a/dev/js/src/loc/dereko.js b/dev/js/src/loc/dereko.js
new file mode 100644
index 0000000..1bd7a57
--- /dev/null
+++ b/dev/js/src/loc/dereko.js
@@ -0,0 +1,10 @@
+/**
+ * Corpus specific localization, here for DeReKo (http://www1.ids-mannheim.de/kl/projekte/korpora.html)
+ * 
+ * @author Helge Stallkamp
+ */
+
+define(function () {
+  const loc = KorAP.Locale;
+  loc.TOUR_Qexample = loc.TOUR_Qexample || "laufen";
+});
\ No newline at end of file
diff --git a/dev/js/src/tour/tours.js b/dev/js/src/tour/tours.js
index f33c56d..b4302e0 100644
--- a/dev/js/src/tour/tours.js
+++ b/dev/js/src/tour/tours.js
@@ -5,19 +5,19 @@
  */
 
 define(['lib/intro', 'vc'], function(introClass, vcClass) {
-  
+
   //needed for localization of labels and contents of the tour
   const loc   = KorAP.Locale;
-  
+
   //labels for nextStep, previousStep, done and abort
   loc.TOUR_lskip = loc.TOUR_lskip || "Abort";
   loc.TOUR_lprev = loc.TOUR_lprev || "Back";
   loc.TOUR_lnext = loc.TOUR_lnext || "Next";
   loc.TOUR_ldone = loc.TOUR_ldone || "Done";
-  
-  //localization tours
+
+  //localization guided tour gTstartSearch
   loc.TOUR_sear1 = loc.TOUR_sear1 || "Enter your search enquiry here.";
-  loc.TOUR_sear2 = loc.TOUR_sear2 || "For example the search for 'Baum'...";
+  loc.TOUR_sear2 = loc.TOUR_sear2 || "For example the search for '" +  loc.TOUR_Qexample + "'";
   loc.TOUR_searAnnot = loc.TOUR_searAnnot || "Annotation helper: By clicking here, the annotations of the differents layers are displayed and can be selected.";
   loc.TOUR_annotAss =  loc.TOUR_annotAss || "The annoation assistant helps to formulate queries with annotations";
   loc.TOUR_vccho1 = loc.TOUR_vccho1 || "Choose corpus by clicking here.";  
@@ -27,35 +27,39 @@
   loc.TOUR_help = loc.TOUR_help || "Help and more information about KorAP.";
   loc.TOUR_glimpse = loc.TOUR_glimpse || "Select to show only the first hits in arbitrary order.";
   loc.TOUR_seargo = loc.TOUR_seargo || "Start the search.";
+  //localization guided Tour gTshowResults
+  loc.TOUR_result = loc.TOUR_result || "Have fun with KorAP!";
   
   //localization of button labels
-   let labelOptions = {
-          'skipLabel': loc.TOUR_lskip, 
-          'prevLabel': loc.TOUR_lprev,
-          'nextLabel': loc.TOUR_lnext,
-          'doneLabel': loc.TOUR_ldone,
-          'showStepNumbers': false,
+  let labelOptions = {
+      'skipLabel': loc.TOUR_lskip, 
+      'prevLabel': loc.TOUR_lprev,
+      'nextLabel': loc.TOUR_lnext,
+      'doneLabel': loc.TOUR_ldone,
+      'showStepNumbers': false,
   };
-  
-  let intro = introClass();
-  intro.setOptions(labelOptions);
-  
-  
+
   return{
-    
+
     /** 
-     * Guided Tour: Definition of steps 
+     * Guided Tour gTstartSearch: Explains the search functionality
      */
-    guidedTour:function(elparam){
-      
+    gTstartSearch:function(elparam){
+
+      let intro = introClass();
+      intro.setOptions(labelOptions);
+      intro.setOption('doneLabel', loc.TOUR_seargo);
+     
+      //for testing purposes
       var doe = document;
       if(elparam){
         doe = elparam;
       }
-  
+
       let input = doe.querySelector("#q-field");
       input.value="";
       
+
       //steps of the example tour
       let Steps =[
         {
@@ -72,82 +76,121 @@
           element: '#hint',
           intro: loc.TOUR_searAnnot,
           position: 'bottom'
-         },
-         {
+        },
+        {
           element:'#vc-choose',
           intro: loc.TOUR_vccho1,
           position: "bottom",
-         },
-         {
-           element:'#vc-view',
-           intro: loc.TOUR_vccho2,
-           position: "bottom",
-         }, 
-         {
-           element: doe.querySelector('#ql-field').parentNode,
-           intro: loc.TOUR_qlfield,
-           position: "bottom",
-         },  
-         {
-           element:'#glimpse',
-           intro: loc.TOUR_glimpse,
-           position: "bottom",
-         }, 
-         {
-           element:'#view-tutorial',
-           intro: loc.TOUR_help,
-           position: "bottom",
-         },
-         {
-           element: doe.querySelector('#searchbar button[type=submit]'),
-           intro: loc.TOUR_seargo,
-           position: "bottom",
-         },
-       ];
-   
+        },
+        {
+          element:'#vc-view',
+          intro: loc.TOUR_vccho2,
+          position: "bottom",
+        }, 
+        {
+          element: doe.querySelector('#ql-field').parentNode,
+          intro: loc.TOUR_qlfield,
+          position: "bottom",
+        },  
+        {
+          element:'#glimpse',
+          intro: loc.TOUR_glimpse,
+          position: "bottom",
+        }, 
+        {
+          element:'#view-tutorial',
+          intro: loc.TOUR_help,
+          position: "bottom",
+        },
+        {
+          element: '#qsubmit',
+          intro: loc.TOUR_seargo,
+          position: "bottom",
+        },
+        ];
+
       //pass in the Steps array created earlier
       intro.setOptions({steps: Steps});
-   
+
       //total number of steps, needed for jasmine tests
-      //See also: introJS.totalSteps() merge requested: //github.com/usablica/intro.js/pull/268/files
+      //TODO see also: introJS.totalSteps() merge requested: //github.com/usablica/intro.js/pull/268/files
       intro.stepCount = Steps.length;
-    
+
       //array of intro content needed for efficient testing
       intro.testIntros = [];
-        for(let i = 0; i< Steps.length; i++){
+
+      for(let i = 0; i< Steps.length; i++){
         intro.testIntros.push(Steps[i].intro);
-        }
-  
-        //changes before executing the single steps
-        intro.onbeforechange(function(targetedElement){
-          switch(targetedElement.id){
-          case "searchbar": 
-            if(this._currentStep == 1){ 
-              input = doe.querySelector('#q-field');
-              input.value="Baum";
-            }   
-            break;
+      }
+
+      //changes before executing the single steps
+      intro.onbeforechange(function(targetedElement){
+        switch(targetedElement.id){
+        case "searchbar": 
+          if(this._currentStep == 1){ 
+            input = doe.querySelector('#q-field');
+            input.value= loc.TOUR_Qexample;
+          }   
+          break;
+
+        case "vc-view":  
+          vchoo = doe.querySelector("#vc-choose");
+          vcv = doe.querySelector("#vc-view");  
+          KorAP._delete.apply(KorAP.vc.root());
+          KorAP.vc.root().key("creationDate").update();
+          KorAP.vc.root().value("1820").update();  
+          if(!(vcv.querySelector(".active"))){
+            vchoo.click();
+          }   
+          break;   
           
-          case "vc-view":  
-            vchoo = doe.querySelector("#vc-choose");
-            vcv = doe.querySelector("#vc-view");  
-            KorAP._delete.apply(KorAP.vc.root());
-            KorAP.vc.root().key("creationDate").update();
-            KorAP.vc.root().value("1820").update();  
-            if(!(vcv.querySelector(".active"))){
-              vchoo.click();
-            }   
-            break;     
-      } 
-    });
-    
-    // Execute at the end of the tour (By clicking at the done-Button)
-    intro.oncomplete(function(){
-      input.value="";
-      KorAP._delete.apply(KorAP.vc.root());
+        /*
+        * Sets button labels for the last step of the tour
+        * Because Kalamar is a multipage webapplication, this tours starts by
+        * completion the gTshowResults Tour. Therefore, the skip-button is removed
+        * and the label of the done button changed.
+         */
+         
+        case "qsubmit":
+            intro.setOption('hideNext', true);
+          break;
+        } 
       });
-    
-    return intro;
+
+
+      // Execute at the end of the tour (By clicking at the done-Button)
+      intro.oncomplete(function(){
+        KorAP.session.set("tour", true);
+        let form = doe.getElementById("searchform");
+        form.submit();
+        //input.value="";
+        //KorAP._delete.apply(KorAP.vc.root()); 
+      });
+
+      return intro;
     },
+
+
+    /* Guided Tour to explain the different views of the results */     
+    gTshowResults: function(elparam){  
+      if(elparam){
+        doe = elparam;
+      }
+      let tourR = introClass();
+      let StepsSR = [
+        {     
+          intro: loc.TOUR_result,
+        }
+        ]
+      tourR.setOptions({steps:StepsSR});
+      tourR.setOptions(labelOptions);
+      
+      tourR.onbeforechange(function (){ 
+        KorAP.session.set("tour", false);
+      });
+      
+      return tourR;
+    },  
+
   }
 });
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 7856e91..c9e9374 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -8,7 +8,7 @@
 use List::Util 'none';
 
 # Minor version - may be patched from package.json
-our $VERSION = '0.34';
+our $VERSION = '0.35';
 
 # Supported version of Backend API
 our $API_VERSION = '1.0';
@@ -58,9 +58,29 @@
 
   $self->log->info('Mode is ' . $self->mode);
 
-  # Specific path prefixing
+  # Get configuration
   my $conf = $self->config('Kalamar');
-  if ($conf && $conf->{proxy_prefix}) {
+  unless ($conf) {
+    $self->config(Kalamar => {});
+    $conf = $self->config('Kalamar');
+  };
+
+  # Check for API endpoint and set the endpoint accordingly
+  if ($conf->{api}) {
+
+    # The api endpoint should be defined as a separated path
+    # and version string
+    $self->log->warn(
+      'Kalamar.api is no longer supported in configurations '.
+        'in favor of Kalamar.api_path'
+      );
+  };
+
+  unless ($conf->{api_path} || $ENV{KALAMAR_API}) {
+    $self->log->warn('Kalamar-api_path not defined in configuration');
+  };
+
+  if ($conf->{proxy_prefix}) {
 
     for ($self->sessions) {
       $_->cookie_path($conf->{proxy_prefix});
@@ -78,28 +98,9 @@
       });
   };
 
-  # Check for API endpoint and set the endpoint accordingly
-  if ($conf->{api}) {
-
-    # The api endpoint should be defined as a separated path
-    # and version string
-    $self->log->info(
-      'Kalamar.api is deprecated in configurations '.
-        'in favor of Kalamar.api_path'
-      );
-  }
-
-  # Set from environment variable
-  elsif ($ENV{'KALAMAR_API'}) {
-    $conf->{api} = $ENV{'KALAMAR_API'};
-  }
-
   # API is not yet set - define
-  else {
-
-    $conf->{api} =
-      Mojo::URL->new($conf->{api_path})->path('v' . ($conf->{api_version} // $API_VERSION) . '/')->to_string;
-  };
+  $conf->{api_path} //= $ENV{KALAMAR_API};
+  $conf->{api_version} //= $API_VERSION;
 
   # Add development path
   if ($self->mode eq 'development') {
@@ -211,7 +212,7 @@
 
   $self->config(navi => $navi);
 
-  $self->log->info('API expected at ' . $self->config->{Kalamar}->{api});
+  $self->log->info('API expected at ' . $self->korap->api);
 
   # Establish routes with authentification
   my $r = $self->routes;
@@ -234,6 +235,10 @@
   $r->get('/contact')->to('documentation#contact');
   $r->get('/contact')->mail_to_chiffre('documentation#contact');
 
+  # API proxy route
+  # $r->any('/api/v#apiv' => [apiv => ['1.0']])->to('Proxy#pass');
+  # $r->any('/api/v#apiv/*path' => [apiv => ['1.0']])->to('Proxy#pass');
+
   # Match route
   # Corpus route
   my $corpus = $r->get('/corpus')->to('search#corpus_info')->name('corpus');
@@ -266,7 +271,7 @@
 
 =head2 COPYRIGHT AND LICENSE
 
-Copyright (C) 2015-2018, L<IDS Mannheim|http://www.ids-mannheim.de/>
+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/>
diff --git a/lib/Kalamar/Controller/Proxy.pm b/lib/Kalamar/Controller/Proxy.pm
new file mode 100644
index 0000000..ed9acef
--- /dev/null
+++ b/lib/Kalamar/Controller/Proxy.pm
@@ -0,0 +1,53 @@
+package Kalamar::Controller::Proxy;
+use Mojo::Base 'Mojolicious::Controller';
+
+# Pass proxy command to API
+sub pass {
+  my $c = shift;
+
+  my $apiv = $c->stash('apiv');
+  my $path = $c->stash('path') // '';
+
+  # Get the original request
+  my $req = $c->req;
+
+  # Clone and clean request headers
+  my $headers = $req->headers->clone->dehop;
+
+  # Set Proxy information
+  $headers->header('X-Proxy' => 'Kalamar');
+
+  # Set clean X-Forwarded-For header
+  $headers->header(
+    'X-Forwarded-For' => $c->client_ip
+  );
+
+  # Get parameters of the request
+  my $params = $req->query_params->clone;
+  my $api_url;
+
+  # Get API request for proxying
+  my $url = Mojo::URL->new($c->korap->api($apiv))->path($path)->query($params);
+
+  # Resend headers
+  my $tx = $c->kalamar_ua->build_tx(
+    $req->method => $url => $headers->to_hash => $req->body
+  );
+
+  # Pipe through system (which means
+  # an authorization header may be set)
+  $c->app->plugins->emit_hook(
+    before_korap_request => ($c, $tx)
+  );
+
+  # return $c->render(text => 'okay ' . $url);
+
+  $c->proxy->start_p($tx)->wait;
+  # $tx->res->content->once(
+  #   body => sub {
+  #     $c->res->headers->header('X-Proxy' => 'Kalamar');
+  #   }
+  # );
+};
+
+1;
diff --git a/lib/Kalamar/Plugin/KalamarHelpers.pm b/lib/Kalamar/Plugin/KalamarHelpers.pm
index a85f2ae..f97f453 100644
--- a/lib/Kalamar/Plugin/KalamarHelpers.pm
+++ b/lib/Kalamar/Plugin/KalamarHelpers.pm
@@ -13,6 +13,14 @@
 sub register {
   my ($plugin, $mojo) = @_;
 
+  # Get config
+  my $conf = $mojo->config('Kalamar');
+
+  # Define API_URL
+  my $api_url = Mojo::URL->new(
+    $conf->{api_path} // '-'
+  )->path('v' . ($conf->{api_version} // '-') . '/')->to_string;
+
   # Embed the korap architecture image
   $mojo->helper(
     korap_overview => sub {
@@ -290,7 +298,19 @@
   # Get the KorAP API endpoint
   $mojo->helper(
     'korap.api' => sub {
-      return shift->config('Kalamar')->{api};
+      my $c = shift;
+
+      # TODO:
+      #   May clone a Mojo::URL object instead
+      return $api_url unless @_;
+
+      my $apiv = shift;
+      if (!$apiv || $apiv eq $conf->{api_version}) {
+        return $api_url;
+      };
+
+      # Return newly created API URL
+      return Mojo::URL->new($conf->{api_path})->path('v' . $apiv . '/')->to_string;
     }
   );
 
diff --git a/lib/Kalamar/Plugin/KalamarUser.pm b/lib/Kalamar/Plugin/KalamarUser.pm
index 9915e45..c1a6b0c 100644
--- a/lib/Kalamar/Plugin/KalamarUser.pm
+++ b/lib/Kalamar/Plugin/KalamarUser.pm
@@ -11,6 +11,9 @@
 #   This Plugin will be removed in favour of
 #   Kalamar::Plugin::Auth!
 
+# TODO:
+#   Replace plugin-api with korap->api!
+
 sub register {
   my ($plugin, $mojo, $param) = @_;
 
diff --git a/package.json b/package.json
index e06a30c..088ed25 100755
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "Kalamar",
   "description": "Mojolicious-based Frontend for KorAP",
   "license": "BSD-2-Clause",
-  "version": "0.34.1",
+  "version": "0.35.1",
   "pluginVersion": "0.1",
   "repository": {
     "type": "git",
diff --git a/t/corpus_info.t b/t/corpus_info.t
index e174bf0..ecbf2dd 100644
--- a/t/corpus_info.t
+++ b/t/corpus_info.t
@@ -7,7 +7,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar');
diff --git a/t/corpus_info_cache.t b/t/corpus_info_cache.t
index 916a2d7..a11fb8b 100644
--- a/t/corpus_info_cache.t
+++ b/t/corpus_info_cache.t
@@ -7,7 +7,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 # New test with new cache
diff --git a/t/docembed.t b/t/docembed.t
index 24503f7..8224fa7 100644
--- a/t/docembed.t
+++ b/t/docembed.t
@@ -6,7 +6,7 @@
 
 my $t = Test::Mojo->new('Kalamar' => {
   Kalamar => {
-    api => 'xyz',
+    api_path => 'xyz',
     navi_ext => [
       {
         title => 'Privacy',
diff --git a/t/fixtures.t b/t/fixtures.t
index 598b918..6508cfc 100644
--- a/t/fixtures.t
+++ b/t/fixtures.t
@@ -8,23 +8,23 @@
 
 my $t = Test::Mojo->new($mock_server);
 
-$t->get_ok('/')
+$t->get_ok('/v1.0')
   ->status_is(200)
   ->content_is('Fake server available');
 
-$t->get_ok('/search?ql=cosmas3')
+$t->get_ok('/v1.0/search?ql=cosmas3')
   ->status_is(400)
   ->json_is('/errors/0/0',"307")
   ->json_is('/errors/0/1',"cosmas3 is not a supported query language!")
   ;
 
-$t->get_ok('/search?q=server_fail')
+$t->get_ok('/v1.0/search?q=server_fail')
   ->status_is(500)
   ->text_is('#error', '')
   ->content_like(qr!Oooops!)
   ;
 
-$t->get_ok('/search?q=[orth=das&ql=poliqarp&offset=0&count=25')
+$t->get_ok('/v1.0/search?q=[orth=das&ql=poliqarp&offset=0&count=25')
   ->status_is(400)
   ->text_is('#error', '')
   ->json_is('/errors/0/0',302)
@@ -33,7 +33,7 @@
   ->json_is('/errors/1/1','Could not parse query >>> [orth=das <<<.')
   ;
 
-$t->get_ok('/search?q=baum&ql=poliqarp&offset=0&count=25')
+$t->get_ok('/v1.0/search?q=baum&ql=poliqarp&offset=0&count=25')
   ->status_is(200)
   ->text_is('#error', '')
   ->json_is('/meta/count', 25)
@@ -41,7 +41,7 @@
   ->json_is('/matches/0/docSigle', "GOE/AGI")
   ;
 
-$t->get_ok('/corpus/WPD15/232/39681/p2133-2134/matchInfo?spans=false&foundry=*')
+$t->get_ok('/v1.0/corpus/WPD15/232/39681/p2133-2134/matchInfo?spans=false&foundry=*')
   ->status_is(200)
   ->json_is('/textSigle', 'WPD15/232/39681')
   ;
diff --git a/t/match_info.t b/t/match_info.t
index 59f12ed..486fe11 100644
--- a/t/match_info.t
+++ b/t/match_info.t
@@ -7,7 +7,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar');
diff --git a/t/plugin/auth-oauth.t b/t/plugin/auth-oauth.t
index bd1dee8..17c3cda 100644
--- a/t/plugin/auth-oauth.t
+++ b/t/plugin/auth-oauth.t
@@ -8,7 +8,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar' => {
@@ -34,7 +34,7 @@
 # Configure fake backend
 $fake_backend->pattern->defaults->{app}->log($t->app->log);
 
-$t->get_ok('/api')
+$t->get_ok('/realapi/v1.0')
   ->status_is(200)
   ->content_is('Fake server available');
 
diff --git a/t/plugin/auth.t b/t/plugin/auth.t
index 6b5818d..e310988 100644
--- a/t/plugin/auth.t
+++ b/t/plugin/auth.t
@@ -8,7 +8,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar' => {
@@ -29,7 +29,7 @@
 # Configure fake backend
 $fake_backend->pattern->defaults->{app}->log($t->app->log);
 
-$t->get_ok('/api')
+$t->get_ok('/realapi/v1.0')
   ->status_is(200)
   ->content_is('Fake server available');
 
diff --git a/t/proxy.t b/t/proxy.t
new file mode 100644
index 0000000..f754f11
--- /dev/null
+++ b/t/proxy.t
@@ -0,0 +1,77 @@
+use Mojo::Base -strict;
+use Test::More;
+use Test::Mojo;
+use Mojo::File qw/path/;
+use Data::Dumper;
+
+
+#####################
+# Start Fake server #
+#####################
+my $mount_point = '/realapi/';
+$ENV{KALAMAR_API} = $mount_point;
+
+my $t = Test::Mojo->new('Kalamar' => {
+  Kalamar => {
+    plugins => ['Auth']
+  }
+});
+
+# Mount fake backend
+# Get the fixture path
+my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, 'server');
+my $fake_backend = $t->app->plugin(
+  Mount => {
+    $mount_point =>
+      $fixtures_path->child('mock.pl')
+  }
+);
+# Configure fake backend
+$fake_backend->pattern->defaults->{app}->log($t->app->log);
+
+# Globally set server
+$t->app->ua->server->app($t->app);
+
+my $r = $t->app->routes;
+
+# API proxy route
+$r->any('/api/v#apiv' => [apiv => ['1.0']])->to('Proxy#pass');
+$r->any('/api/v#apiv/*path' => [apiv => ['1.0']])->to('Proxy#pass');
+
+$t->get_ok('/realapi/v1.0')
+  ->status_is(200)
+  ->content_is('Fake server available')
+  ;
+
+$t->get_ok('/api/v1.0/')
+  ->status_is(200)
+  ->content_is('Fake server available')
+  ;
+
+$t->get_ok('/api/v1.0/search?ql=cosmas3')
+  ->status_is(400)
+  ->json_is('/errors/0/0','307')
+  ;
+
+$t->post_ok('/api/v1.0/oauth2/token' => {} => form => {
+  username => 'test',
+  password => 'pass'
+})
+  ->status_is(400)
+  ->json_is('/errors/0/1','Grant Type unknown')
+  ;
+
+$t->post_ok('/api/v1.0/oauth2/token' => {} => form => {
+  grant_type => 'password',
+  client_id => 2,
+  client_secret => 'k414m4r-s3cr3t',
+  username => 'test',
+  password => 'pass'
+})
+  ->status_is(200)
+  ->json_is('/token_type', 'Bearer')
+  ;
+
+
+done_testing;
+__END__
diff --git a/t/query.t b/t/query.t
index 2ce7a90..81c4ac8 100644
--- a/t/query.t
+++ b/t/query.t
@@ -7,7 +7,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar');
diff --git a/t/server/mock.pl b/t/server/mock.pl
index 359f78c..2c7f43b 100644
--- a/t/server/mock.pl
+++ b/t/server/mock.pl
@@ -65,13 +65,13 @@
 
 
 # Base page
-get '/' => sub {
+get '/v1.0/' => sub {
   shift->render(text => 'Fake server available');
 };
 
 
 # Search fixtures
-get '/search' => sub {
+get '/v1.0/search' => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('q');
@@ -133,7 +133,7 @@
 };
 
 # Textinfo fixtures
-get '/corpus/:corpusId/:docId/:textId' => sub {
+get '/v1.0/corpus/:corpusId/:docId/:textId' => sub {
   my $c = shift;
 
   my $file = join('_', (
@@ -152,7 +152,7 @@
 
 
 # Matchinfo fixtures
-get '/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
+get '/v1.0/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
   my $c = shift;
 
   my $file = join('_', (
@@ -172,7 +172,7 @@
 
 
 # Statistics endpoint
-get '/statistics' => sub {
+get '/v1.0/statistics' => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('corpusQuery');
@@ -193,7 +193,7 @@
 ############
 
 # Request API token
-get '/auth/logout' => sub {
+get '/v1.0/auth/logout' => sub {
   my $c = shift;
 
   if (my $auth = $c->req->headers->header('Authorization')) {
@@ -210,7 +210,7 @@
 
 
 # Request API token
-get '/auth/apiToken' => sub {
+get '/v1.0/auth/apiToken' => sub {
   my $c = shift;
 
   # Get auth header
@@ -275,10 +275,12 @@
 
 
 # Request API token
-post '/oauth2/token' => sub {
+post '/v1.0/oauth2/token' => sub {
   my $c = shift;
 
-  if ($c->param('grant_type') eq 'password') {
+  my $grant_type = $c->param('grant_type') // 'undefined';
+
+  if ($grant_type eq 'password') {
 
     # Check for wrong client id
     if ($c->param('client_id') ne '2') {
@@ -346,7 +348,7 @@
   }
 
   # Refresh token
-  elsif ($c->param('grant_type') eq 'refresh_token') {
+  elsif ($grant_type eq 'refresh_token') {
     return $c->render(
       status => 200,
       json => {
@@ -361,10 +363,11 @@
   # Unknown token grant
   else {
     return $c->render(
+      status => 400,
       json => {
         "errors" => [
           [
-            0, "Grant Type unknown", $c->param("grant_type")
+            0, "Grant Type unknown", $grant_type
           ]
         ]
       }
diff --git a/t/text_info.t b/t/text_info.t
index 49be159..82e6a97 100644
--- a/t/text_info.t
+++ b/t/text_info.t
@@ -7,7 +7,7 @@
 #####################
 # Start Fake server #
 #####################
-my $mount_point = '/api/';
+my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
 my $t = Test::Mojo->new('Kalamar');
diff --git a/templates/de/intro.html.ep b/templates/de/intro.html.ep
index 3806197..fd65a08 100644
--- a/templates/de/intro.html.ep
+++ b/templates/de/intro.html.ep
@@ -9,7 +9,7 @@
   
   <p><strong>Neu bei KorAP?</strong> Dann starten Sie doch mit unserer <%= doc_opener 'korap', begin %>Einführung<% end %>!</p>
 
-  <!--<p><strong>Neu bei KorAP?</strong> Dann starten Sie doch mit unserer <a href="#" onclick="tourshow()"> Tour</a> oder mit der <%= doc_opener 'korap', begin %>Einführung<% end %>!</p> -->
+  <!-- <p><strong>Neu bei KorAP?</strong> Dann starten Sie doch mit unserer <a href="#" onclick="KorAP.tourshow()"> Tour</a> oder mit der <%= doc_opener 'korap', begin %>Einführung<% end %>!</p> -->
   %= include 'announcement', lang => 'de'
 
   <p>KorAP wird am <a href="http://www.ids-mannheim.de">Leibniz-Institut für Deutsche Sprache</a> in Mannheim entwickelt, Mitglied der <a href="http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/">Leibniz Gemeinschaft</a>. Die einzelnen Module werden als Open Source auf <a href="http://github.com/KorAP">GitHub</a> veröffentlicht.</p>
diff --git a/templates/intro.html.ep b/templates/intro.html.ep
index 91761db..e9895e6 100644
--- a/templates/intro.html.ep
+++ b/templates/intro.html.ep
@@ -8,7 +8,7 @@
 
   <p><strong>New to KorAP?</strong> Please check out our <%= doc_opener 'korap', begin %>tutorial<% end %>!</p>
   
-  <!-- <p><strong>New to KorAP?</strong> Please check out our <a href="#" onclick="tourshow()">guided tour</a> or our <%= doc_opener 'korap', begin %>tutorial<% end %>!</p> -->    
+ <!-- <p><strong>New to KorAP?</strong> Please check out our <a href="#" onclick="KorAP.tourshow()">guided tour</a> or our <%= doc_opener 'korap', begin %>tutorial<% end %>!</p> -->
   %= include 'announcement', lang => 'en'
 
   <p>KorAP is developed at the <a href="http://www.ids-mannheim.de">Leibniz Institute for the German Language</a>, member of the <a href="http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/">Leibniz Association</a>. The separated modules are being published as open source at <a href="http://github.com/KorAP">GitHub</a>.</p>
diff --git a/templates/partial/header.html.ep b/templates/partial/header.html.ep
index d007f94..8992b0f 100644
--- a/templates/partial/header.html.ep
+++ b/templates/partial/header.html.ep
@@ -6,7 +6,7 @@
   <form autocomplete="off" action="<%= url_for 'index' %>" id="searchform">
     <div id="searchbar">
       %= search_field 'q', id => 'q-field', autofocus => 'autofocus', placeholder => loc('searchplaceholder'), spellcheck => 'false', autocomplete => 'off', autocorrect => 'off', autocapitalize => 'off'
-      <button type="submit" title="<%= loc 'go' %>"><span><%= loc 'go' %></span></button>
+      <button type="submit" id="qsubmit" title="<%= loc 'go' %>"><span><%= loc 'go' %></span></button>
     </div>
 
     <!-- Search in the following virtual collection -->