More Keybinds

Adds some extra keystrokes to Firefox.

  1. // ==UserScript==
  2. // @name More Keybinds
  3. // @namespace MK
  4. // @description Adds some extra keystrokes to Firefox.
  5. // @version 1.2.5
  6. // @include *
  7. // @license ISC
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // jQuery might be present
  13. /* eslint-env jquery */
  14.  
  15. // Not all keys fire a keypress event (Chrome 2010), so we use keydown.
  16. document.addEventListener('keydown', keypressListener, false);
  17.  
  18. function keypressListener(evt) {
  19. var code = evt.keyCode || evt.which;
  20.  
  21. /*
  22. var modifierReport = "";
  23. modifierReport += ( evt.ctrlKey ? "Ctrl " : "" );
  24. modifierReport += ( evt.shiftKey ? "Shift " : "" );
  25. modifierReport += ( evt.altKey ? "Alt " : "" );
  26. GM_log("Caught keypress "+code+" with modifiers: "+modifierReport);
  27. */
  28.  
  29. // Do not intercept any of the keys below when the user is focused on an input or textarea.
  30. /*
  31. //var focusedElement = document.activeElement; // document.body if no input is focused
  32. var focusedElement = evt.target || event.srcElement;
  33. if (focusedElement) {
  34. var isInput = focusedElement.nodeName === 'INPUT' || focusedElement.nodeName === 'TEXTAREA';
  35. if (isInput) {
  36. return;
  37. }
  38. }
  39. */
  40. // From next_imageprevious_image.user.js:
  41. if (evt.target.tagName && evt.target.tagName.match(/input|select|textarea/i) || evt.target.getAttribute('contenteditable')==="true") {
  42. return;
  43. }
  44. // Unfortunately this doesn't catch all input boxes (e.g. YouTube live chat, amongst others)
  45.  
  46. // Actions
  47.  
  48. /*
  49. // Ctrl+Backspace goes Back
  50. if (code == 8 && evt.ctrlKey) {
  51. window.history.back();
  52. }
  53.  
  54. // The Delete key, because sometimes I map Ctrl+Backspace to emit a Delete, so I need that Delete to go back
  55. // But I disable this for sites where I really need the Delete key to work normally outside of form elements. Those sites are usually graphical editors.
  56. if (code == 46 && !document.location.host.match(/piktochart.com|miro.com|whimsical.com/)) {
  57. window.history.back();
  58. }
  59.  
  60. // Ctrl+Enter goes Forward
  61. if (code == 13 && evt.ctrlKey) {
  62. window.history.forward();
  63. }
  64. */
  65.  
  66. // macOS style (Cmd-BraceLeft / Cmd-BraceRight)
  67. // Not needed in Firefox, because Firefox responds to these by default
  68. if (!navigator.userAgent.match(/Firefox[/]/)) {
  69. if ((evt.ctrlKey || evt.altKey) && evt.key === '[') {
  70. window.history.back();
  71. }
  72. if ((evt.ctrlKey || evt.altKey) && evt.key === ']') {
  73. window.history.forward();
  74. }
  75. }
  76.  
  77. /* These conflict with selecting words in text!
  78.  
  79. // Ctrl+Shift+Left goes Back
  80. if (code == 37 && evt.ctrlKey && evt.shiftKey) {
  81. window.history.back();
  82. }
  83.  
  84. // Ctrl+Shift+Right goes Forward
  85. if (code == 39 && evt.ctrlKey && evt.shiftKey) {
  86. window.history.forward();
  87. }
  88.  
  89. */
  90.  
  91. // Ctrl+Shift+Up goes up in the URL path (removes the tail leaf)
  92. // Cmd+Alt+Up on macOS also works
  93. if ((code == 38 && evt.ctrlKey && evt.shiftKey) || (evt.code == 'ArrowUp' && evt.altKey && evt.metaKey)) {
  94. var newURL = document.location.href;
  95. if (newURL.slice(-1)=='/') {
  96. newURL = newURL.slice(0,-1);
  97. }
  98. document.location.href = document.location.href.replace(/[#/?][^#/?]*[/]*$/,'');
  99. }
  100.  
  101. if (!evt.ctrlKey && !evt.shiftKey && !evt.metaKey) {
  102. if (document.location.host !== "9gag.com" /* && document.location.host !== "github.com" */) {
  103. if (code === 'K'.charCodeAt(0)) {
  104. bestScrollBy(-getScrollAmount());
  105. }
  106.  
  107. if (code === 'J'.charCodeAt(0)) {
  108. bestScrollBy(+getScrollAmount());
  109. }
  110. }
  111. }
  112.  
  113. }
  114.  
  115. // Try to perform a smooth vertical scroll, but fall back to a jump scroll if neccessary
  116. function bestScrollBy(amount) {
  117. if (typeof window.scrollBy === 'function') {
  118. // If browser has native scrollBy, use that
  119. window.scrollBy({
  120. left: 0,
  121. top: amount,
  122. behavior: 'smooth'
  123. });
  124. } else if (typeof $ !== "undefined" && $.fn && $.fn.animate) {
  125. // Otherwise, if jQuery is present, use that
  126. queue(function(next){
  127. $("html,body").animate({scrollTop: $(document).scrollTop() + amount}, 200, "swing", ifBody(next));
  128. });
  129. } else {
  130. // Otherwise perform a jerky scroll
  131. // Does not do anything in Chrome:
  132. document.body.scrollTop += amount;
  133. // Works in Chrome/Firefox:
  134. document.documentElement.scrollTop += amount;
  135. }
  136. }
  137.  
  138. function getScrollAmount() {
  139. return window.innerHeight / 6;
  140. }
  141.  
  142. function ifBody(fn) {
  143. return function(){
  144. // jQuery calls complete once for each element, and we have two elements. Annoying!
  145. if (this === document.body) {
  146. fn();
  147. }
  148. };
  149. }
  150.  
  151. var actions = [];
  152. var running = false;
  153. function queue(action) {
  154. actions.push(action);
  155. if (!running) {
  156. dequeue();
  157. }
  158. }
  159. function dequeue() {
  160. if (actions.length > 0) {
  161. var nextAction = actions.shift();
  162. running = true;
  163. nextAction(dequeue);
  164. } else {
  165. running = false;
  166. }
  167. }