From 9a5d9a83ab10b89437f05c8d0b6a83e429792451 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Wed, 29 Apr 2015 02:24:03 -0400
Subject: [PATCH] Implemented UI element to jump to specified page of the messages list (#1485235)

---
 skins/larry/templates/mail.html   |    1 
 CHANGELOG                         |    1 
 skins/classic/templates/mail.html |    3 
 skins/classic/functions.js        |    2 
 skins/classic/common.css          |   32 ++++++++++
 skins/larry/styles.css            |   20 ++++++
 program/js/app.js                 |   87 +++++++++++++++++++++++++++++
 skins/larry/ui.js                 |    2 
 8 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 0a24125..48c69c7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,7 @@
 - Plugin API: Added message_part_body hook
 - Plugin API: Added message_ready hook
 - Plugin API: Add special onload() method to execute plugin actions before startup (session and GUI initialization)
+- Implemented UI element to jump to specified page of the messages list (#1485235)
 - Add option to place signature at bottom of the quoted text even in top-posting mode [sig_below]
 - Fix handling of %-encoded entities in mailto: URLs (#1490346)
 - Fix zipped messages downloads after selecting all messages in a folder (#1490339)
diff --git a/program/js/app.js b/program/js/app.js
index b4b125f..295a511 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -3255,6 +3255,91 @@
     this.set_alttext('delete', label);
   };
 
+  // Initialize input element for list page jump
+  this.init_pagejumper = function(element)
+  {
+    $(element).addClass('rcpagejumper')
+      .on('focus', function(e) {
+        // create and display popup with page selection
+        var i, html = '';
+
+        for (i = 1; i <= ref.env.pagecount; i++)
+          html += '<li>' + i + '</li>';
+
+        html = '<ul class="toolbarmenu">' + html + '</ul>';
+
+        if (!ref.pagejump) {
+          ref.pagejump = $('<div id="pagejump-selector" class="popupmenu"></div>')
+            .appendTo(document.body)
+            .on('click', 'li', function() {
+              if (!ref.busy)
+                $(element).val($(this).text()).change();
+            });
+        }
+
+        if (ref.pagejump.data('count') != i)
+          ref.pagejump.html(html);
+
+        ref.pagejump.attr('rel', '#' + this.id).data('count', i);
+
+        // display page selector
+        ref.show_menu('pagejump-selector', true, e);
+        $(this).keydown();
+      })
+      // keyboard navigation
+      .on('keydown keyup', function(e) {
+        var current, selector = $('#pagejump-selector'),
+          ul = $('ul', selector),
+          list = $('li', ul),
+          height = ul.height(),
+          p = parseInt(this.value);
+
+        if (e.type == 'keydown') {
+          // arrow-down
+          if (e.which == 40) {
+            if (!selector.is(':visible'))
+              return ref.show_menu('pagejump-selector', true, e);
+
+            if (list.length > p)
+              this.value = (p += 1);
+          }
+          // arrow-up
+          else if (e.which == 38) {
+            if (!selector.is(':visible'))
+              return ref.show_menu('pagejump-selector', true, e);
+
+            if (p > 1 && list.length > p - 1)
+              this.value = (p -= 1);
+          }
+          // enter
+          else if (e.which == 13) {
+            return $(this).change();
+          }
+        }
+
+        $('li.selected', ul).removeClass('selected');
+
+        if ((current = $(list[p - 1])).length) {
+          current.addClass('selected');
+          $('#pagejump-selector').scrollTop(((ul.height() / list.length) * (p - 1)) - selector.height() / 2);
+        }
+      })
+      .on('change', function(e) {
+        // go to specified page
+        var p = parseInt(this.value);
+        if (p && p != ref.env.current_page && !ref.busy) {
+          ref.hide_menu('pagejump-selector');
+          ref.list_page(p);
+        }
+      });
+  };
+
+  // Update page-jumper state on list updates
+  this.update_pagejumper = function()
+  {
+    $('input.rcpagejumper').val(this.env.current_page).prop('disabled', this.env.pagecount < 2);
+  };
+
   /*********************************************************/
   /*********       mailbox folders methods         *********/
   /*********************************************************/
@@ -6673,6 +6758,8 @@
   {
     this.enable_command('nextpage', 'lastpage', this.env.pagecount > this.env.current_page);
     this.enable_command('previouspage', 'firstpage', this.env.current_page > 1);
+
+    this.update_pagejumper();
   };
 
   // mark a mailbox as selected and set environment variable
diff --git a/skins/classic/common.css b/skins/classic/common.css
index 6d5a333..e97c30d 100644
--- a/skins/classic/common.css
+++ b/skins/classic/common.css
@@ -481,6 +481,22 @@
   background-position: -33px -11px;
 }
 
+#rcmcountdisplay
+{
+  float: left;
+  margin-right: 10px;
+}
+
+#countcontrols #pagejumper
+{
+  margin: 0 5px;
+  float: right;
+  text-align: center;
+  padding: 0;
+  cursor: default;
+  font-size: 10px;
+}
+
 .splitter
 {
   user-select: none;
@@ -1120,7 +1136,8 @@
   cursor: pointer;
 }
 
-#rcmKSearchpane ul li.selected
+#rcmKSearchpane ul li.selected,
+#pagejump-selector ul li.selected
 {
   color: #ffffff;
   background-color: #CC3333;
@@ -1279,6 +1296,19 @@
   color: #999;
 }
 
+#pagejump-selector
+{
+  max-height: 250px;
+  overflow-x: hidden;
+}
+
+#pagejump-selector ul li
+{
+  min-width: 45px;
+  padding: 2px 5px;
+  cursor: default;
+}
+
 
 /*** folder selector ***/
 
diff --git a/skins/classic/functions.js b/skins/classic/functions.js
index 5a5ad39..9227001 100644
--- a/skins/classic/functions.js
+++ b/skins/classic/functions.js
@@ -1027,6 +1027,8 @@
           .addEventListener('afterimport-messages', function(){ rcmail_ui.show_popup('uploadform', false); });
       }
 
+      rcmail.init_pagejumper('#pagejumper');
+
       // fix message list header on window resize (#1490213)
       if (bw.ie && rcmail.message_list)
         $(window).resize(function() {
diff --git a/skins/classic/templates/mail.html b/skins/classic/templates/mail.html
index 7f1e1b8..b7af014 100644
--- a/skins/classic/templates/mail.html
+++ b/skins/classic/templates/mail.html
@@ -79,9 +79,10 @@
     <roundcube:endif />
     </div>
     <div id="countcontrols" class="pagenav">
+        <roundcube:object name="messageCountDisplay" />
         <roundcube:button command="lastpage" type="link" class="buttonPas lastpage" classAct="button lastpage" classSel="button lastpageSel" title="lastpage" content=" " />
         <roundcube:button command="nextpage" type="link" class="buttonPas nextpage" classAct="button nextpage" classSel="button nextpageSel" title="nextpage" content=" " />
-        <roundcube:object name="messageCountDisplay" style="padding:0 .5em; float:right" />
+        <input id="pagejumper" class="pagejumper" type="text" size="3" disabled="disabled" title="<roundcube:label name="currpage" />" />
         <roundcube:button command="previouspage" type="link" class="buttonPas prevpage" classAct="button prevpage" classSel="button prevpageSel" title="previouspage" content=" " />
         <roundcube:button command="firstpage" type="link" class="buttonPas firstpage" classAct="button firstpage" classSel="button firstpageSel" title="firstpage" content=" " />
     </div>
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index 2f3cefd..327fd37 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -430,6 +430,14 @@
 	top: -2px;
 }
 
+.pagenav .pagejumper {
+	text-align: center;
+	background: #f8f8f8;
+	padding: 3px 0;
+	background: linear-gradient(to bottom, #dddddd 0%, #f8f8f8 100%);
+	cursor: default;
+}
+
 a.iconbutton {
 	display: inline-block;
 	width: 20px;
@@ -2340,6 +2348,7 @@
 ul.toolbarmenu li a.active:hover,
 ul.toolbarmenu li a.active:focus,
 #rcmKSearchpane ul li.selected,
+#pagejump-selector ul li.selected,
 select.decorated option:hover,
 select.decorated option[selected='selected'] {
 	background-color: #00aad6;
@@ -2457,6 +2466,17 @@
 	background-position: 0 -2150px;
 }
 
+#pagejump-selector {
+	max-height: 250px;
+	overflow-x: hidden;
+}
+
+#pagejump-selector ul li {
+	min-width: 45px;
+	padding: 2px 5px;
+	cursor: default;
+}
+
 #snippetslist {
 	max-width: 200px;
 }
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index 2c4e0f2..bda3efb 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -128,6 +128,7 @@
 		<span class="pagenavbuttons">
 		<roundcube:button command="firstpage" type="link" class="button firstpage disabled" classAct="button firstpage" classSel="button firstpage pressed" innerClass="inner" title="firstpage" label="first" />
 		<roundcube:button command="previouspage" type="link" class="button prevpage disabled" classAct="button prevpage" classSel="button prevpage pressed" innerClass="inner" title="previouspage" label="previous" />
+		<input id="pagejumper" class="pagejumper" type="text" size="3" disabled="disabled" title="<roundcube:label name="currpage" />" />
 		<roundcube:button command="nextpage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextpage" label="next" />
 		<roundcube:button command="lastpage" type="link" class="button lastpage disabled" classAct="button lastpage" classSel="button lastpage pressed" innerClass="inner" title="lastpage" label="last" />
 		</span>
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index a1c00eb..95efccf 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -231,6 +231,8 @@
         if (previewframe)
           mailviewsplit.init();
 
+        rcmail.init_pagejumper('#pagejumper');
+
         rcmail.addEventListener('setquota', update_quota)
           .addEventListener('enable-command', enable_command)
           .addEventListener('afterimport-messages', show_uploadform);

--
Gitblit v1.9.1