Add pagination info service

Change-Id: I462f05581256575e9b52f474515cb8cb5e533d3b
diff --git a/Changes b/Changes
index aa474ca..27c9af0 100755
--- a/Changes
+++ b/Changes
@@ -29,6 +29,7 @@
         - Improve QueryParam response.
         - Fold all top-level navigation items.
         - Add plugin service to redirect to a certain location.
+        - Add support for pagination information to retrieve by plugins.
 
 0.42 2021-06-18
         - Added GitHub based CI for perl.
diff --git a/dev/js/runner/all.html b/dev/js/runner/all.html
index c1fc29c..e60e3a8 100644
--- a/dev/js/runner/all.html
+++ b/dev/js/runner/all.html
@@ -54,7 +54,8 @@
         'spec/stateSpec',
         'spec/pipeSpec',
         'spec/sessionSpec',
-        'spec/tutSpec'
+        'spec/tutSpec',
+        'spec/pageInfoSpec'
       ],
       function () {
         window.onload();
diff --git a/dev/js/spec/pageInfoSpec.js b/dev/js/spec/pageInfoSpec.js
new file mode 100644
index 0000000..4082bb4
--- /dev/null
+++ b/dev/js/spec/pageInfoSpec.js
@@ -0,0 +1,31 @@
+define(['pageInfo'], function (pageInfoClass) {
+
+  describe('KorAP.PageInfo', function () {
+    it('should be initializable', function () {
+      let pi = pageInfoClass.create();
+      expect(pi.total()).toEqual(0);
+      expect(pi.count()).toEqual(0);
+      expect(pi.page()).toEqual(0);
+    });
+
+    it('should be read the correct values', function () {
+
+      // Create pagination element for pagination information
+      let p = document.createElement('div');
+      p.setAttribute('id', 'pagination')
+      p.setAttribute('data-page',3);
+      p.setAttribute('data-total',30);
+      p.setAttribute('data-count',25);
+
+      document.body.appendChild(p);
+      
+      pi = pageInfoClass.create();
+      expect(pi.total()).toEqual(30);
+      expect(pi.count()).toEqual(25);
+      expect(pi.page()).toEqual(3);
+
+      // Recreate initial state
+      document.body.removeChild(p);
+    });
+  });
+});
diff --git a/dev/js/spec/pluginSpec.js b/dev/js/spec/pluginSpec.js
index 966d123..edffa59 100644
--- a/dev/js/spec/pluginSpec.js
+++ b/dev/js/spec/pluginSpec.js
@@ -623,7 +623,7 @@
       KorAP.Pipe = temp;
     });
 
-    it('should reply to query information requests', function () {
+    it('should reply to query information requests (queryform)', function () {
       var manager = pluginServerClass.create();
       var id = manager.addService({"name":'Service', "src":'about:blank'});
       expect(id).toMatch(/^id-/);
@@ -660,9 +660,41 @@
       expect(data.value["q"]).toEqual("[orth=Baum]");
       expect(data.value["ql"]).toEqual("poliqarp");
       expect(data.value["cq"]).toEqual("title = /[^b]ee.+?/");
+
       // Recreate initial state
       KorAP.vc = temp;
       document.body.removeChild(f);
     });
+
+    it('should reply to query information requests (pagination)', function () {
+      var manager = pluginServerClass.create();
+      var id = manager.addService({"name":'Service', "src":'about:blank'});
+      expect(id).toMatch(/^id-/);
+      
+      // Create pagination element for pagination information
+      let p = document.createElement('div');
+      p.setAttribute('id', 'pagination')
+      p.setAttribute('data-page',3);
+      p.setAttribute('data-total',30);
+      p.setAttribute('data-count',25);
+
+      document.body.appendChild(p);
+
+      let data = {
+        "originID" : id,
+        "action" : "get",
+        "key" : "Pagination"
+      };
+      manager._receiveMsg({
+        "data" : data
+      });
+      manager.destroy();
+      expect(data.value["count"]).toEqual(25);
+      expect(data.value["page"]).toEqual(3);
+      expect(data.value["total"]).toEqual(30);
+
+      // Recreate initial state
+      document.body.removeChild(p);
+    });
   });
 });
diff --git a/dev/js/src/pageInfo.js b/dev/js/src/pageInfo.js
new file mode 100644
index 0000000..3f8f9a9
--- /dev/null
+++ b/dev/js/src/pageInfo.js
@@ -0,0 +1,21 @@
+"use strict";
+
+define(function () {
+  return {
+    create : function () {
+      const o = Object.create(this);
+      const p = document.getElementById('pagination');
+      o.ds = p ? p.dataset : {};
+      return o;
+    },
+    page : function () {
+      return parseInt(this.ds["page"] || 0);
+    },
+    total : function () {
+      return parseInt(this.ds["total"] || 0);
+    },
+    count : function () {
+      return parseInt(this.ds["count"] || 0);
+    }
+  }
+});
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
index 817c0c9..d5295f7 100644
--- a/dev/js/src/plugin/server.js
+++ b/dev/js/src/plugin/server.js
@@ -9,7 +9,7 @@
  */
 "use strict";
 
-define(['plugin/widget', 'plugin/service', 'state', 'util'], function (widgetClass, serviceClass, stateClass) {
+define(['plugin/widget', 'plugin/service', 'state', 'pageInfo', 'util'], function (widgetClass, serviceClass, stateClass, pageInfoClass) {
 
   KorAP.Panel = KorAP.Panel || {};
 
@@ -475,7 +475,7 @@
           };
         }
 
-        // Get Query information from from
+        // Get Query information from form
         else if (d.key == 'QueryForm') {
           let doc = document;
           let v = d["value"] = {};
@@ -502,6 +502,15 @@
           v["q"] = p.get('q');
           v["ql"] = p.get('ql');
           v["cq"] = p.get('cq');
+        }
+
+        // Get pagination information
+        else if (d.key == 'Pagination') {
+          const pi = pageInfoClass.create();
+          let v = d["value"] = {};
+          v["page"] = pi.page();
+          v["total"] = pi.total();
+          v["count"] = pi.count();
         };
 
         // data needs to be mirrored
diff --git a/t/query.t b/t/query.t
index 1dc3d92..b82d769 100644
--- a/t/query.t
+++ b/t/query.t
@@ -75,6 +75,9 @@
   ->text_is('li:nth-of-type(1) p.ref time[datetime=1982]', 1982)
   ->text_is('li:nth-of-type(1) p.ref span.sigle', '[GOE/AGI/00000]')
   ->header_isnt('X-Kalamar-Cache', 'true')
+  ->attr_is('#pagination','data-page','1')
+  ->attr_is('#pagination','data-total','3')
+  ->attr_is('#pagination','data-count','25')
   ->tx->res->dom->at('#error')
   ;
 is(defined $err ? $err->text : '', '');
@@ -170,6 +173,11 @@
 
   # Not searched for "der" before
   ->content_unlike(qr!${q}cutOff${q}:true!)
+
+  ->attr_is('#pagination','data-page','1')
+  ->attr_is('#pagination','data-total','7291')
+  ->attr_is('#pagination','data-count','2')
+
   ->tx->res->dom->at('#error')
   ;
 is(defined $err ? $err->text : '', '');
@@ -197,6 +205,10 @@
   ->content_like(qr!${q}itemsPerPage${q}:2!)
   ->content_like(qr!${q}startIndex${q}:2!)
 
+  ->attr_is('#pagination','data-page','2')
+  ->attr_is('#pagination','data-total','7291')
+  ->attr_is('#pagination','data-count','2')
+
   # No caching
   ->header_isnt('X-Kalamar-Cache', 'true')
   ->content_like(qr!${q}cutOff${q}:true!)
diff --git a/templates/search.html.ep b/templates/search.html.ep
index be4ced8..df52efd 100644
--- a/templates/search.html.ep
+++ b/templates/search.html.ep
@@ -1,6 +1,11 @@
 % layout 'main', schematype => 'SearchResultsPage';
 
-<div id="pagination" class="button-group button-panel"><%= pagination(stash('start_page'), stash('total_pages'), url_with->query({'p' => '{page}'})) =%></div>
+<div id="pagination"
+     class="button-group button-panel"
+     data-page="<%= stash('start_page') %>"
+     data-total="<%= stash('total_pages') %>"
+     data-count="<%= stash('items_per_page') %>"
+     ><%= pagination(stash('start_page'), stash('total_pages'), url_with->query({'p' => '{page}'})) =%></div>
 
 <div id="resultinfo" <% if (stash('results')->size) { %> class="found"<%} %>>
 % my $found = stash('total_results') // 0;