Navigational Keyboard Shortcuts

Navigate through websites using keyboard buttons N/B for next/previous pages.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         Navigational Keyboard Shortcuts
// @namespace    https://github.com/kittenparry/
// @version      1.27.1
// @description  Navigate through websites using keyboard buttons N/B for next/previous pages.
// @author       kittenparry
// @match        *://*/*
// @grant        none
// @license      GPL-3.0-or-later
// ==/UserScript==

/* LIST:
 * archived.moe
 * google.com
 * imgfrog.pw
 * metal-tracker.com
 * mods.factorio.com
 * nexusmods.com
 * nyaa.si
 * opengameart.org
 * rarbg.to || rarbgproxy.org || rarbg2020.org || rarbgget.org
 * reddit.com
 * stargate.fandom.com
 * steamcommunity.com/workshop/
 * steamgifts.com
 * trakt.tv
 * tumblr.com
 * xkcd.com

 * NSFW:
 * 420chan.org
 * 8muses.com
 * *.booru.org
 * camshowhub.to
 * camvault.xyz
 * camwhores.tv
 * chaturbate.com
 * coedcherry.com
 * erome.com
 * f95zone.com
 * forum.sexy-egirls.com
 * forum.thotsbay.com
 * forums.sexy-youtubers.com
 * forums.socialmediagirls.com
 * hanime.tv
 * hentai-foundry.com
 * hongfire.com
 * javbus.com
 * kitty-kats.net
 * loverslab.com
 * meitulu.com
 * meituri.com
 * nhentai.net
 * nobodyhome.tv
 * pixietrixcomix.com
 * planetsuzy.org
 * pornbay.org
 * pornhub.com
 * recurbate.com
 * rec-tube.com
 * shadbase.com
 * simpcity.su
 * sinnercomics.com
 * thothub.tv
 * totempole666.com
 * xbunker.nu
 * xbunker.su
 * yiff.party/activity
 */

/* CHANGELOG:
 * 1.27.1: +xbunker.nu (xbunker.su alt/ rather updated domain)
 * 1.27:   +xbunker.su +simpcity.su +forums.socialmediagirls.com
 * 1.26.1: +rarbgget.org (rarbg.to alt)
 * 1.26:   +forum.thotsbay.com
 * 1.25:   +opengameart.org
 * 1.24:   +rarbg2020.org (rarbg.to alt) +xkcd.com +pixietrixcomix.com
 * 1.23:   +forums.sexy-youtubers.com +forum.sexy-egirls.com
 * 1.22:   +420chan.org
 * 1.21:   +totempole666.com
 * 1.20.1: fix archived.moe first (& likely last) page navigation
 * 1.20:   +loverslab.com | could probably be better with a special of its own similar to nexusmods
 * 1.19:   +pornhub.com
 * 1.18:   +google.com
 * 1.17:   +archived.moe
 * 1.16:   +kitty-kats.net
 * 1.15:   +mods.factorio.com
 * 1.14:   +imgfrog.pw
 * 1.13.1: lack of special handling similar to focus_input_key script (fik.user.js)
 * 1.13:   +*.booru.org
 * 1.12:   +camshowhub.to
 * 1.11.1: fix trakt.tv back keybind not working
 * 1.11:   +stargate.fandom.com | navigates the episodes (preceded by & followed by)
 * 1.10:   +trakt.tv +camvault.xyz | trakt.tv only works in episode/season views
 * 1.9.2:  additional functions to ease repetition & meituri/meitulu isn't special anymore
 * 1.9.1:  ability to navigate to first/last pages on pornbay.org
 * 1.9:    +rec-tube.com
 * 1.8:    +erome.com +recurbate.com +hanime.tv
 * 1.7.1:  +nobodyhome.tv instead of nobodyhome.ga (domain change)
 * 1.7:    +planetsuzy.org
 * 1.6.1:  +rarbgproxy.org as an alternative to rarbg.to
 * 1.6:    +chaturbate.com
 * 1.5:    +meituri.com +meitulu.com | they work the same way so a simple or will do
 * 1.4:    +javbus.com | switch to semantic versioning so incrementing minor instead of patch part (https://semver.org/)
 * 1.3.7:  +thothub.tv
 * 1.3.6:  +nexusmods.com | a special case similar to camwhores.tv
 * 1.3.5:  +yiff.party/activity | with some clunky mechanics
 * 1.3.4:  +steamcommunity.com/workshop/
 * 1.3.3:  +f95zone.to/latest/ | change the original link to .to as well
 * 1.3.2:  +metal-tracker.com | btn case i wanted to use in camwhores.tv
 * 1.3.1:  +shadbase.com
 * 1.3:    +camwhores.tv | element needs to be reassigned each time so it's in the function
 * 1.2.4:  +8muses.com
 * 1.2.3:  +nobodyhome.ga
 * 1.2.2:  +hongfire.com
 * 1.2.1:  prevent execution of code when not on these sites
 * 1.2:    +f95zone.com
 * 1.1:    +nyaa.si +coedcherry.com
 * 1.0:    initial | rarbg.com reddit.com steamgifts.com tumblr.com
 *           & hentai-foundry.com nhentai.net pornbay.org sinnercomics.com
 */

check_nav_key_press = (e, prev, next, special) => {
	var type = e.target.getAttribute('type');
	var tag = e.target.tagName.toLowerCase();
	if (type != 'text' && tag != 'textarea' && type != 'search') {
		if (e.keyCode == 66) {
			if (special == 'camwhores') {
				document.querySelector('li[class="page-current"]').previousElementSibling.firstElementChild.click();
			} else if (special == 'nexusmods') {
				document.querySelector('li[class="prev"]').firstElementChild.click();
			} else if (special == 'hanime') {
				sel_and_click('.pagination__navigation', 0);
			} else if (special == 'btn' && prev != undefined) {
				document.querySelector(prev).click();
			} else if (special == 'url' && prev != undefined) {
				window.location = prev;
			} else if (special == '') {
				window.location = get_query_href(prev);
			}
		} else if (e.keyCode == 78) {
			if (special == 'camwhores') {
				document.querySelector('li[class="page-current"]').nextElementSibling.firstElementChild.click();
			} else if (special == 'nexusmods') {
				document.querySelector('li[class="next"]').firstElementChild.click();
			} else if (special == 'hanime') {
				sel_and_click('.pagination__navigation', 3);
			} else if (special == 'btn' && next != undefined) {
				document.querySelector(next).click();
			} else if (special == 'url' && next != undefined) {
				window.location = next;
			} else if (special == '') {
				window.location = get_query_href(next);
			}
		}
	}
};

// return the first result of a given tag with the text
// eg. ('a', 'prev') returns the anchor with 'prev' innerHTML
find_els_with_text = (tag, text) => {
	var els = document.getElementsByTagName(tag);
	var found = [];
	for (var i = 0; i < els.length; i++) {
		if (els[i].innerHTML == text) {
			found.push(els[i]);
		}
	}

	return found[0].href;
};

// return the href of given selector
get_query_href = (sel) => {
	return document.querySelector(sel).href;
};

// click the idxth element of given selector
sel_and_click = (sel, idx) => {
	document.querySelectorAll(sel)[idx].click();
};

// return the href of given selector at idxth
get_sel_href = (sel, idx) => {
	return document.querySelectorAll(sel)[idx].href;
};

/* probably need a better way than simply .includes()
 * pqsel: previous query selector
 * nqsel: next query selector
 * nav_spcl: used when url is given instead of selector
 *	 check steamgifts example
 *	 also these include try/catch if they don't exist yet (first/last page)
 *	 !this section really looks ugly and repetitive
 */

var cur_loc = window.location.href;

if (cur_loc.includes('archived.moe')) {
	var nav_spcl = 'url';
	try {
		var pqsel = document.querySelector('li[class="prev"]').firstElementChild.href;
	} catch (e) {}
	try {
		var nqsel = document.querySelector('li[class="next"]').firstElementChild.href;
	} catch (e) {}
} else if (cur_loc.includes('google.com')) {
	var nav_spcl = 'url';
	try {
		var nqsel = get_sel_href('.pn', 1);
	} catch (e) {
		// if the first page
		var nqsel = get_sel_href('.pn', 0);
	}
	try {
		var pqsel = get_sel_href('.pn', 0);
		// if the first page
		if (nqsel == pqsel) {
			pqsel = null;
		}
	} catch (e) {}
} else if (cur_loc.includes('imgfrog.pw')) {
	var pqsel = 'a[data-pagination="prev"]';
	var nqsel = 'a[data-pagination="next"]';
} else if (cur_loc.includes('metal-tracker.com')) {
	var nav_spcl = 'btn';
	var pqsel = 'li[class="previous"]';
	var nqsel = 'li[class="next"]';
} else if (cur_loc.includes('mods.factorio.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = find_els_with_text('a', '« Previous');
	} catch (e) {}
	try {
		var nqsel = find_els_with_text('a', 'Next »');
	} catch (e) {}
} else if (cur_loc.includes('nexusmods.com')) {
	var nav_spcl = 'nexusmods';
	var pqsel = '';
	var nqsel = '';
} else if (cur_loc.includes('nyaa.si')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('opengameart.org')) {
	var pqsel = 'a[title="Go to previous page"]';
	var nqsel = 'a[title="Go to next page"]';
} else if (cur_loc.includes('rarbg.to') || cur_loc.includes('rarbgproxy.org') || (cur_loc.includes('rarbg2020.org')) || (cur_loc.includes('rarbgget.org'))) {
	var pqsel = 'a[title="previous page"]';
	var nqsel = 'a[title="next page"]'
} else if (cur_loc.includes('reddit.com')) {
	var pqsel = 'a[rel="nofollow prev"]';
	var nqsel = 'a[rel="nofollow next"]';
} else if (cur_loc.includes('stargate.fandom.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = document.querySelector('div[data-source="preceded_by"]').lastElementChild.firstElementChild.href;
	} catch (e) {}
	try {
		var nqsel = document.querySelector('div[data-source="followed_by"]').lastElementChild.firstElementChild.href;
	} catch (e) {}
} else if (cur_loc.includes('steamcommunity.com/workshop/')) {
	var nav_spcl = 'url';
	try {
		var pqsel = get_sel_href('.pagebtn', 0);
	} catch (e) {}
	try {
		var nqsel = get_sel_href('.pagebtn', 1);
	} catch (e) {}
} else if (cur_loc.includes('steamgifts.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = document.querySelector('i[class="fa fa-angle-left"]').parentNode.href;
	} catch (e) {}
	try {
		var nqsel = document.querySelector('i[class="fa fa-angle-right"]').parentNode.href;
	} catch (e) {}
} else if (cur_loc.includes('trakt.tv')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('tumblr.com')) {
	var pqsel = 'a[id="previous_page_link"]';
	var nqsel = 'a[id="next_page_link"]';
} else if (cur_loc.includes('xkcd.com')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
	/* * * * * * * *
	 * * * * * * * *
	 * NSFW BELOW  *
	 * * * * * * * *
	 * * * * * * * */
} else if (cur_loc.includes('420chan.org')) {
	var nav_spcl = 'url';
	try {
		var pqsel = find_els_with_text('a', '« Previous');
	} catch (e) {}
	try {
		var nqsel = find_els_with_text('a', 'Next »');
	} catch (e) {}
} else if (cur_loc.includes('8muses.com')) {
	var pqsel = 'a[class="pageNav-jump pageNav-jump--prev"]';
	var nqsel = 'a[class="pageNav-jump pageNav-jump--next"]';
} else if (cur_loc.includes('.booru.org')) {
	var pqsel = 'a[alt="back"]';
	var nqsel = 'a[alt="next"]';
} else if (cur_loc.includes('camshowhub.to')) {
	var pqsel = 'a[title="Previous page"]';
	var nqsel = 'a[title="Next page"]';
} else if (cur_loc.includes('camvault.xyz')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('camwhores.tv')) {
	var nav_spcl = 'camwhores';
	var pqsel = '';
	var nqsel = '';
} else if (cur_loc.includes('chaturbate.com')) {
	var pqsel = 'a[class="prev endless_page_link"]';
	var nqsel = 'a[class="next endless_page_link"]';
} else if (cur_loc.includes('coedcherry.com')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('erome.com')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('f95zone.to/latest/')) {
	// something else for "/pages/latest/"
	var pqsel = 'a[class="nav_prev"]';
	var nqsel = 'a[class="nav_next"]';
} else if (cur_loc.includes('f95zone.to')) {
	// for only threads/forums
	// something else is required for /pages/latest/
	var pqsel = 'a[class="pageNav-jump pageNav-jump--prev"]';
	var nqsel = 'a[class="pageNav-jump pageNav-jump--next"]';
} else if (cur_loc.includes('hanime.tv')) {
	var nav_spcl = 'hanime'
	var pqsel = '';
	var nqsel = '';
} else if (cur_loc.includes('hentai-foundry.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = document.querySelector('li[class="previous"]').firstChild.href;
	} catch (e) {}
	try {
		var nqsel = document.querySelector('li[class="next"]').firstChild.href;
	} catch (e) {}
} else if (cur_loc.includes('hongfire.com')) {
	var pqsel = 'a[class="js-pagenav-button js-pagenav-prev-button b-button b-button--secondary js-shrink-event-child"]';
	var nqsel = 'a[class="js-pagenav-button js-pagenav-next-button b-button b-button--secondary js-shrink-event-child"]';
} else if (cur_loc.includes('javbus.com')) {
	var pqsel = 'a[id="pre"]';
	var nqsel = 'a[id="next"]';
} else if (cur_loc.includes('kitty-kats.net')) {
	var pqsel = 'a[class="pageNav-jump pageNav-jump--prev"]';
	var nqsel = 'a[class="pageNav-jump pageNav-jump--next"]';
} else if (cur_loc.includes('loverslab.com')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('meituri.com') || cur_loc.includes('meitulu.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = get_sel_href('.a1', 0);
	} catch (e) {}
	try {
		var nqsel = get_sel_href('.a1', 1);
	} catch (e) {}
} else if (cur_loc.includes('nhentai.net')) {
	var pqsel = 'a[class="previous"]';
	var nqsel = 'a[class="next"]';
} else if (cur_loc.includes('nobodyhome.tv')) {
	var pqsel = 'a[class="pagination_previous"]';
	var nqsel = 'a[class="pagination_next"]';
} else if (cur_loc.includes('pixietrixcomix.com')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('planetsuzy.org')) {
	var pqsel = 'a[rel="prev"]';
	var nqsel = 'a[rel="next"]';
} else if (cur_loc.includes('pornbay.org')) {
	var nav_spcl = 'url';
	try {
		try {
			var pqsel = get_query_href('a[class="pager pager_prev"]');
		} catch (e) {
			var pqsel = get_query_href('a[class="pager pager_first"]');
		}
	} catch (e) {}
	try {
		try {
			var nqsel = get_query_href('a[class="pager pager_next"]');
		} catch (e) {
			var nqsel = get_query_href('a[class="pager pager_last"]');
		}
	} catch (e) {}
} else if (cur_loc.includes('pornhub.com')) {
	var nav_spcl = 'url';
	try {
		var pqsel = document.querySelector('li[class="page_previous alpha"]').firstElementChild.href;
	} catch (e) {}
	try {
		var nqsel = document.querySelector('li[class="page_next omega"]').firstElementChild.href;
	} catch (e) {}
} else if (cur_loc.includes('recurbate.com')) {
	var pqsel = 'a[aria-label="Previous"]';
	var nqsel = 'a[aria-label="Next"]';
} else if (cur_loc.includes('rec-tube.com')) {
	var nav_spcl = 'url';
	try {
		try {
			var pqsel = find_els_with_text('a', 'Prev Page');
		} catch (e) {
			var pqsel = find_els_with_text('a', 'First Page')
		}
	} catch (e) {}
	try {
		try {
			var nqsel = find_els_with_text('a', 'Next Page');
		} catch (e) {
			var nqsel = find_els_with_text('a', 'Last Page')
		}
	} catch (e) {}
} else if (cur_loc.includes('shadbase.com')) {
	var pqsel = 'a[class="navi navi-prev"]';
	var nqsel = 'a[class="navi navi-next"]';
} else if (cur_loc.includes('sinnercomics.com')) {
	var pqsel = 'a[class="comic-nav-base comic-nav-previous"]';
	var nqsel = 'a[class="comic-nav-base comic-nav-next"]';
} else if (cur_loc.includes('thothub.tv') || cur_loc.includes('forums.sexy-youtubers.com') || cur_loc.includes('forum.sexy-egirls.com') || cur_loc.includes('forum.thotsbay.com') || cur_loc.includes('xbunker.su') || cur_loc.includes('simpcity.su') || cur_loc.includes('forums.socialmediagirls.com') || cur_loc.includes('xbunker.nu')) {
	var pqsel = 'a[class="pageNav-jump pageNav-jump--prev"]';
	var nqsel = 'a[class="pageNav-jump pageNav-jump--next"]';
} else if (cur_loc.includes('totempole666.com')) {
	var pqsel = 'a[class="navi comic-nav-previous navi-prev"]';
	var nqsel = 'a[class="navi comic-nav-next navi-next"]';
} else if (cur_loc.includes('yiff.party/activity')) {
	var nav_spcl = 'url';
	try {
		var pqsel = find_els_with_text('a', 'prev');
	} catch (e) {}
	try {
		var nqsel = find_els_with_text('a', 'next');
	} catch (e) {}
}

if (pqsel != undefined || nqsel != undefined) {
	try {
		if (!nav_spcl) {
			var nav_spcl = '';
		}
		window.addEventListener('keydown', (e) => check_nav_key_press(e, pqsel, nqsel, nav_spcl), false);
	} catch (e) {}
}