// ==UserScript== // @name Runbox Keyboard Shortcuts // @description Add keyboard shortcuts to Runbox Webmail // @author https://github.com/RunboxScripts // @namespace https://github.com/RunboxScripts // @version 3.8 // @downloadURL https://raw.githubusercontent.com/RunboxScripts/RunboxKeyboardShortcuts/master/RunboxKeyboardShortcuts.user.js // @updateURL https://raw.githubusercontent.com/RunboxScripts/RunboxKeyboardShortcuts/master/RunboxKeyboardShortcuts.meta.js // @match https://runbox.com/* // @grant none // @require https://raw.githubusercontent.com/ccampbell/mousetrap/master/mousetrap.min.js // @require https://raw.githubusercontent.com/ccampbell/mousetrap/master/plugins/global-bind/mousetrap-global-bind.min.js // @require https://raw.githubusercontent.com/dinbror/bpopup/master/jquery.bpopup.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/select2/3.3.2/select2.min.js // @require https://gist.githubusercontent.com/BrockA/2625891/raw/waitForKeyElements.js // ==/UserScript== // FUNCTIONS AND VARIABLES // Get Element by XPath function getElementByXpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } // Get Current View if (/\/addresses/.test(self.location.href)) { var rksRunboxView = 'contacts'; } else if (/\/compose/.test(self.location.href)) { var rksRunboxView = 'compose'; } else if (/\/forward/.test(self.location.href)) { var rksRunboxView = 'compose'; } else if (/\/reply/.test(self.location.href)) { var rksRunboxView = 'compose'; } else if (/\/send/.test(self.location.href)) { var rksRunboxView = 'compose'; } else if (/\/read/.test(self.location.href)) { var rksRunboxView = 'read'; } else { var rksRunboxView = 'list'; } // Prevent checkboxes from stealing focus function checkboxFocusReset() { $('input[type=checkbox]').mousedown(function(event) { event.preventDefault(); }); } // Refresh checkbox state upon Ajax refresh waitForKeyElements(".m1:first", checkboxFocusReset); // Append script CSS to head $('head').append (''); // ACTIONS // Compose message Mousetrap.bind('c', function() { if (rksRunboxView != 'compose') { $('#menuFoldersCompose a')[0].click(); } }); // Delete message Mousetrap.bind('#', function() { $('#menuDelete a')[0].click(); }); // Mark as read Mousetrap.bind('I', function() { if (rksRunboxView == 'list') { $('#menuRead a')[0].click(); } }); // Mark as unread Mousetrap.bind('U', function() { $('#menuUnread a')[0].click(); }); // Flag message Mousetrap.bind(['=','+'], function() { $('#menuFlag a')[0].click(); }); // Unflag message Mousetrap.bind('-', function() { $('#menuUnflag a')[0].click(); }); // Report spam Mousetrap.bind('!', function() { $('#menuSpam a')[0].click(); }); // Not Spam Mousetrap.bind('@', function() { $('#menuNotSpam a')[0].click(); }); // Reply to message Mousetrap.bind('r', function() { if (rksRunboxView == 'read') { $('#menuReply a')[0].click(); } }); // Reply to all Mousetrap.bind('a', function() { if (rksRunboxView == 'read') { $('#menuReplyAll a')[0].click(); } }); // Forward message Mousetrap.bind('f', function() { if (rksRunboxView == 'read') { $('#menuForward a')[0].click(); } }); // Show HTML Version Mousetrap.bind('h', function() { if (rksRunboxView == 'read') { getElementByXpath('//A[descendant::text()=\'Show HTML-version\']').click(); } }); // Empty Drafts Mousetrap.bind('e d', function() { $('.empty_trash')[0].click(); }); // Empty Spam Mousetrap.bind('e p', function() { $('.empty_trash')[1].click(); }); // Empty Trash Mousetrap.bind('e r', function() { $('.empty_trash')[2].click(); }); // Open keyboard shortcuts help Mousetrap.bind('?', function() { $('#rksPdiv').bPopup(); return false; }); // SORT/FILTER COMMANDS // Run only in list view if (rksRunboxView == 'list') { // Select all or none Mousetrap.bind('mod+a', function() { $('.checkall')[0].click(); return false; }); // Sort by flagged Mousetrap.bind('s 1', function() { $('.orderflag')[0].click(); }); // Sort by replied Mousetrap.bind('s 2', function() { $('.orderrepl')[0].click(); }); // Sort by from Mousetrap.bind('s 3', function() { $('.orderfrom')[0].click(); }); // Sort by subject Mousetrap.bind('s 4', function() { $('.ordersubj')[0].click(); }); // Sort by new/old Mousetrap.bind('s 5', function() { $('.ordernew')[0].click(); }); // Sort by date Mousetrap.bind('s 6', function() { $('.orderrecv')[0].click(); }); // Sort by size Mousetrap.bind('s 7', function() { $('.ordersize')[0].click(); }); // Navigate message list function ondivchange(div, i) { // div is the highlighted div // i is index of said div } waitForKeyElements('.mailrow:first:not(.mailrow:contains(There\ are\ no\ messages\ in\ this\ folder))', (function (callback) { callback = callback || function() { }; var divs = document.getElementById('mailmessages').getElementsByClassName('mailrow'),selectedDiv = 0,i; divs[selectedDiv].className = divs[selectedDiv].className + ' rksMailrowFocus'; // Select message Mousetrap.bind(['x','space'], function() { divs[selectedDiv].click(); return false; }); // Reply to focused message Mousetrap.bind('r', function() { divs[selectedDiv].getElementsByClassName('maillink')[0].click(); return false; }); // Replyall to focused message Mousetrap.bind('a', function() { openCompose('/mail/replyall?message=' + divs[selectedDiv].getElementsByClassName('message_id')[0].innerHTML, 750, 670, '_blank'); return false; }); // Forward focused message Mousetrap.bind('f', function() { openCompose('/mail/forward?message=' + divs[selectedDiv].getElementsByClassName('message_id')[0].innerHTML, 750, 670, '_blank'); return false; }); // Open message Mousetrap.bind(['enter','o'], function() { divs[selectedDiv].getElementsByClassName('subjectlink')[0].click(); return false; }); // Open message in new tab/window Mousetrap.bind(['shift+enter','shift+o'], function() { var rksMessageSubjectlink = divs[selectedDiv].getElementsByClassName('subjectlink')[0]; rksMessageSubjectlink.setAttribute('target', '_blank'); rksMessageSubjectlink.click(); rksMessageSubjectlink.removeAttribute('target'); return false; }); // Prevent default j/down/k/up actions Mousetrap.bind(['j','k','up','down'], function() { return false; }); // Bind j/down/k/up document.onkeydown = function(e) { var x = 0; if (e.keyCode == 74) // j x = 1; else if (e.keyCode == 40) // down x = 1; else if (e.keyCode == 75) // k x = -1; else if (e.keyCode == 38) // up x = -1; else return; $('.mailrow').removeClass('rksMailrowFocus'); selectedDivEval = ((selectedDiv + x) % divs.length); lastDiv = (divs.length - 1); selectedDiv = selectedDiv + x; if (selectedDivEval < 0 ) { selectedDiv = 0; } if (selectedDiv == divs.length) { selectedDiv = lastDiv; } if (divs.length == 1) { selectedDiv = 0; } divs[selectedDiv].className = divs[selectedDiv].className + ' rksMailrowFocus'; callback(divs[selectedDiv], selectedDiv); }; })); (ondivchange); } // NAVIGATION COMMANDS // Exclude from compose view if (rksRunboxView != 'compose') { // Refresh message list Mousetrap.bind('u', function() { $('.btn.menu_refresh')[0].click(); }); // Search mail Mousetrap.bind('/', function() { $('[name=s_new_string]').focus(); return false; }); // Next inbox section Mousetrap.bind(['.','mod+.'], function() { $('#menuNext a')[0].click(); }); // Previous inbox section Mousetrap.bind([',','mod+,'], function() { $('#menuPrev a')[0].click(); }); // Go to Contacts Mousetrap.bind('g c', function() { $('.main')[2].click(); }); // Go to All Mail Mousetrap.bind('g a', function() { $('#foldername_0').click(); }); // Go to Drafts Mousetrap.bind('g d', function() { getElementByXpath('//A[descendant::text()=\'Drafts\']').click(); }); // Go to Inbox Mousetrap.bind('g i', function() { if (rksRunboxView == 'contacts') { $('.main')[0].click(); } else { getElementByXpath('//A[descendant::text()=\'Inbox\']').click(); } }); // Go to Sent Mousetrap.bind('g s', function() { getElementByXpath('//A[descendant::text()=\'Sent\']').click(); }); // Go to Spam Mousetrap.bind('g p', function() { getElementByXpath('//A[descendant::text()=\'Spam\']').click(); }); // Go to Templates Mousetrap.bind('g t', function() { getElementByXpath('//A[descendant::text()=\'Templates\']').click(); }); // Go to Trash Mousetrap.bind('g r', function() { getElementByXpath('//A[descendant::text()=\'Trash\']').click(); }); } // COMPOSE COMMANDS // Run only in compose view if (rksRunboxView == 'compose') { // Append Select2 CSS to head $('head').append (""); // Load Select2 on compose window select boxes $('select.formfield[name=from]').select2({width:'408px'}); $('select.formfield[name=from_files],select.formfield[name=add_tag_id]').select2({width:'auto'}); // Change From address Mousetrap.bindGlobal('mod+shift+f', function() { $('#s2id_autogen1').select2('open'); return false; }); // Add To recipients Mousetrap.bindGlobal('mod+shift+t', function() { $('#to_').focus(); return false; }); // Add Cc recipients Mousetrap.bindGlobal('mod+shift+c', function() { $('#cc_').focus(); return false; }); // Add Bcc recipients Mousetrap.bindGlobal('mod+shift+b', function() { $('#bcc_').focus(); return false; }); // Edit subject Mousetrap.bindGlobal('mod+shift+s', function() { $('[name=subject]').focus(); return false; }); // Edit body Mousetrap.bindGlobal('mod+shift', function() { $('#editor').focus(); return false; }); // Save draft Mousetrap.bindGlobal('mod+s', function() { $('[name=save]').click(); return false; }); // Send message Mousetrap.bindGlobal('mod+enter', function() { if (confirm('Are sure you want to send this message?')) { $('[name=send]').click(); } return false; }); // Show all fields Mousetrap.bind('a', function() { $('.show_compose_fields')[0].click(); return false; }); // Hide "show all fields" row on click $('.show_compose_fields').click(function() { $('#row_show_all_fields').hide(); }); } // KEYBOARD SHORTCUTS HELP // Help popup content if (rksRunboxView == 'contacts') { var rksPcontent = '
| g + i | Go to Inbox |
| ? | Open keyboard shortcuts help |
These commands do not work inside the HTML text editor.
\| a | Show all fields |
| Ctrl + Shift + f ⌘ + Shift + f | Change “From:” address |
| Ctrl + Shift + t ⌘ + Shift + t | Add “To:” recipients (Does not work in Chrome) |
| Ctrl + Shift + c ⌘ + Shift + c | Add “CC:” recipients |
| Ctrl + Shift + b ⌘ + Shift + b | Add “BCC:” recipients |
| Ctrl + Shift + s ⌘ + Shift + s | Edit subject |
| Ctrl + Shift ⌘ + Shift | Edit body |
| Ctrl + s ⌘ + s | Save draft |
| Ctrl + Enter ⌘ + Enter | Send message |
| Actions | |
| c | Compose message |
| o or Enter | Open message |
| Shift + o or Shift + Enter | Open in new tab/window |
| r | Reply to message |
| a | Reply to all |
| f | Forward message |
| / | Search mail |
| Selections | |
| x or Space | Select message |
| Ctrl + a ⌘ + a | Select All or None |
| Shift + i | Mark as read |
| Shift + u | Mark as unread |
| + | Flag message(s) |
| - | Unflag message(s) |
| ! | Report spam |
| @ | Not spam |
| # | Delete message(s) |
| Navigation | |
| u | Refresh message list |
| j or Down | Next message in list |
| k or Up | Previous message in list |
| > | Next inbox or message page |
| < | Previous inbox or message page |
| Jumping | |
| g + a | Go to All mail |
| g + d | Go to Drafts |
| g + i | Go to Inbox |
| g + s | Go to Sent |
| g + t | Go to Templates |
| g + p | Go to Spam |
| g + r | Go to Trash |
| g + c | Go to Contacts |
| Sorting | |
| s + 1 | Sort by Flagged |
| s + 2 | Sort by Replied |
| s + 3 | Sort by From |
| s + 4 | Sort by Subject |
| s + 5 | Sort by New/Old |
| s + 6 | Sort by Date |
| s + 7 | Sort by Size |
| Cleaning | |
| e + r | Empty Trash |
| e + p | Empty Spam |
| e + d | Empty Drafts |
| Miscellaneous | |
| h | Show HTML version |
| ? | Open keyboard shortcuts help |