Add Keyboard Shortcut for Generic Next/Previous Page

Add CTRL+ArrowLeft and CTRL+ArrowRight for generic next/previous page. It will click the last found link/button whose text starts/ends with e.g. "Next", "Prev", or "Previous".

As of 2020-02-01. See the latest version.

// ==UserScript==
// @name        Add Keyboard Shortcut for Generic Next/Previous Page
// @namespace   AddKeyboardShortcutForGenericNextPreviousPage
// @version     1.0.7
// @license     GNU AGPLv3
// @author      jcunews
// @description Add CTRL+ArrowLeft and CTRL+ArrowRight for generic next/previous page. It will click the last found link/button whose text starts/ends with e.g. "Next", "Prev", or "Previous".
// @include     *://*/*
// @grant       none
// ==/UserScript==

/*
The link/button text more specifically, are those which starts with (non case sesitive) "Next", "Prev", "Previous";
or ends with "Prev", "Previous", "Next". e.g. "Next", "> Next", "Next Page", "Prev", "< Prev", "< Previous", etc.
but not "< Prev Page" because the word "prev" or "previous" is not at the start/end of text.

This script doesn't take into account of links whose contents is an image rather than text, or whose text is a CSS text contents.

If next/previous navigation link is specified in the HTML metadata, it will be used as a priority.
*/

(function(rxPrev, rxNext) {
  rxPrevious = /^prev(ious)?\b|\bprev(ious)?$/i;
  rxNext = /^next\b|\bnext$/i;
  rxCarousel = /carousel/i;

  addEventListener("keydown", function(ev, e) {

    function clickLink(rx, e, i, r) {
      e = document.querySelectorAll('a,button,input[type="button"],input[type="submit"]');
      for (i = e.length-1; i >= 0; i--) {
        if (
          (
            ((e[i].tagName === "A") && rx.test(e[i].getAttribute("rel"))) ||
            ((e[i].tagName === "INPUT") && rx.test(e[i].getAttribute("value"))) ||
            ((e[i].tagName !== "INPUT") && rx.test(e[i].textContent.trim()))
          ) && (!rxCarousel.test(e[i].className))
        ) {
          ev.preventDefault();
          e[i].click();
          return true;
        }
      }
      return false;
    }

    if (ev.ctrlKey && !ev.altKey && !ev.shiftKey) {
      if (document.activeElement && (
        (/^(INPUT|TEXTAREA)$/).test(document.activeElement.tagName) ||
        document.activeElement.isContentEditable)) return;
      switch (ev.key) {
        case "ArrowLeft": //previous
          if (e = document.querySelector('link[rel="prev"][href]')) {
            location.href = e.href;
            return;
          }
          if (clickLink(rxPrevious)) return;
          break;
        case "ArrowRight": //next
          if (e = document.querySelector('link[rel="next"][href]')) {
            location.href = e.href;
            return;
          }
          if (clickLink(rxNext)) return;
          break;
      }
    }
  });
})();