User:The Editor's Apprentice/randomlink.js

// This script adds a link titled "Random link" to the left sidebar above the standard "Random page" link. // Clicking the link takes you to a random page linked in the current page or from a given page or // set of pages.

// Settings // Use "randomlink_start" to choose a random link from a given page or set of pages rather than // the current page. This example uses Featured articles and // Featured pictures. //randomlink_start  = [ "Wikipedia:Featured articles", "Wikipedia:Featured pictures" ]; // Use "randomlink_hops" //randomlink_hops   = 2; //randomlink_exclude = /^List of/; randomlink_paged = true; // Get around the fact some special pages are paged //randomlink_open   = true;        // if you want to open in new windows //randomlink_debug  = true;        // set this, to debug randomlink_maxfrom  = 22000000;    // 10-20% less than max page id randomlink_maxoffset = 950;         // en.wikipedia is limited to 1000

// Forked from User:GregU's code at User:GregU/randomlink.js, using the November 29, 2021‎version as a base. // Forked as GregU has become inactive and no longer maintains the script. // Last changed by User:The Editor's Apprentice on August 27, 2022

function scrapeLinks( descend ) {   var topnode = document.getElementById('bodyContent') || document; var node   = document.getElementById('mw-subcategories'); var atags  = []; var links  = []; var nspat  = /^(Talk|User|Wik\w+|File|MediaWiki|Template|Help|Category|Portal|Book)( talk)?:/i;

var spec = document.getElementsByClassName(topnode, 'div', 'mw-spcontent'); if (spec.length == 1) topnode = spec[0];      // skip help links at top of specials

if (node && descend) topnode = node;          // pick a sub-category else for (var id in {'mw-pages':0, 'mw-category-media':0 }) { node = document.getElementById( id ); if (node) { var nodelist = node.getElementsByTagName('a'); for (var i=0; i < nodelist.length; i++) atags.push( nodelist[i] ); }       }

if (atags.length == 0) atags = topnode.getElementsByTagName('a');

nextlink: for (var i=0; i < atags.length; i++) { var link = atags[i]; var href = link.href; var title = link.title;

if (!href)                                // needed? continue; if (link.className.search(/\b(external|internal|extiw|image)\b/) >= 0) continue; if (href.search(/\/Special:|\?(?!.*redirect=|.*from=.*to=)/) >= 0) continue; if (mw.config.get('wgIsArticle') && mw.config.get('wgNamespaceNumber') != 14 && title.search(/^(Category|File):|^$|^#/) >= 0) continue; if (mw.config.get('wgIsArticle') && mw.config.get('wgNamespaceNumber') == 0 && title.search(nspat) >= 0) continue; if ((mw.config.get('wgAction') == "history") != $(link).hasClass("mw-userlink")) continue; if (link.hostname != location.hostname)   // commons.wikimedia.org on images continue; if (link.parentNode.id == "coordinates") continue;                             // coords too common, or help link if (typeof randomlink_exclude != "undefined" && title.search(randomlink_exclude) >= 0) continue;

// Exclude message boxes and the Metadata section on Image pages. // And also mw-usertool links and comments in page listings. // And also top links on Recent changes and watchlists. //       for (var n = link.parentNode; n != topnode; n = n.parentNode) { if (n.id.search(/^mw-watchlist-options|^recentchangestext/) >= 0) continue nextlink; if (n.className.search(/\b(usertool|summary|metadata|.mbox|comment|warn)/) >= 0) continue nextlink; }       links.push( link ); }

while (links.length && links[0].parentNode.id.search( /contentSub|jump-to|target/i ) >= 0) links.shift;                                 // breadcrumb links

if (links.length && links[0].title == "Wikipedia:FAQ/Categories") links.shift;                                 // "learn more" link in cats

return links; }

function randomLink( links, hops ) {   var continuing = (typeof links == "object" && links == null);

if (typeof links == "undefined" && typeof randomlink_start != "undefined") links = randomlink_start;

if (typeof hops == "undefined" && typeof randomlink_hops != "undefined") hops = randomlink_hops;

if (typeof hops == "undefined") hops = 1;

if (hops > 4)      // sanity check; 4 needed for Special:AllPages hops = 4;

if (typeof links == "string" &&       links.search(/^Special:(WhatLinksHere|RecentChangesLinked)$/i) >= 0) {       links += "/" + mw.config.get('wgPageName') + "?limit=250"; if (mw.config.get('wgNamespaceNumber') == 0) links += "&namespace=0";   // stay in article space if in it now }

// Random contrib: Toggles between user, page, user, page, ... //   if (typeof links == "string" && links == "Special:Contributions") if (mw.config.get('wgPageName').search(/^User:[^\/]+$/) >= 0) links += "/" + mw.config.get('wgPageName').slice(5) + "?namespace=0"; else links = mw.config.get('wgPageName') + "?action=history";

if (typeof links == "string") links = links.split("|");

if (typeof links == "object" && links != null) { for (var i=0; i < links.length; i++) links[i] = mw.config.get('wgArticlePath').replace("$1", links[i]).replace(/ /g, "_"); hops++; }   else { links = scrapeLinks( hops > 1 ); }

if (typeof randomlink_debug != "undefined" && randomlink_debug) { var msg = links.slice(0); if (msg.length > 36) msg.splice( 20, msg.length-35, "..." ); alert( links.length + " links:\n  " + msg.join("\n   ")); }

if (links.length == 0) return alert("I am unable to comply.");

var newpage = links[ Math.floor(links.length * Math.random) ].toString;

if (typeof randomlink_paged != "undefined" && randomlink_paged) newpage = pagedUrl( newpage );

if (mw.config.get('wgCanonicalNamespace') == "Category" && newpage.indexOf("/Category:") == -1) hops = 1;

if (--hops > 0) { newpage += (newpage.indexOf("?") >= 0) ? "&" : "?";       newpage += "random_hops=" + hops; }

// WikiProjects organize by talk pages, but let's end on the subject page. //   if ((mw.config.get('wgCanonicalNamespace') == "Category" || mw.config.get('wgCanonicalSpecialPageName') == "Whatlinkshere")             && continuing && hops <= 0) newpage = newpage.replace("/Talk:", "/").replace("_talk:", ":");

if (typeof randomlink_open != "undefined" && randomlink_open && !continuing) window.open( newpage ); else window.location = newpage; }

function pagedUrl( url ) {   var param = ""; var value; var alphabet = "!abcdefghijklmnoprstuvwy~";

if (url.indexOf("/Category:") >= 0) { value = alphabet.charAt(alphabet.length * Math.random).toUpperCase + alphabet.charAt(alphabet.length * Math.random); param = (value < "M" ? "from" : "until"); }   if (url.indexOf("Special:WhatLinksHere") >= 0) { param = "from"; value = Math.floor( randomlink_maxfrom * Math.random ); // Clustering will hurt randomness, but better than nothing }   if (url.search(/Special:\w+s\b/) >= 0) { param = "offset"; value = Math.floor( randomlink_maxoffset * Math.random ); }   if (param) { url += (url.indexOf("?") >= 0) ? "&" : "?";       url += param + "=" + encodeURIComponent(value); }   return url; }

addOnloadHook( function {   var hops = document.URL.match( /[?&]random_hops=(\d+)/ );    if (hops)  randomLink( null, hops[1] );

var where = 'this page'; if (typeof randomlink_start != 'undefined') where = randomlink_start.toString.slice(0,500); // Set up function for adding portlet (sidebar) links // Documented at https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.util-method-addPortletLink. $.when( mw.loader.using( 'mediawiki.util' ), $.ready ).then( function {    // General usage:    mw.util.addPortletLink( portletId, href, text /*, id, tooltip, accesskey, nextnode */ ); } );

// Add portlet (sidebar) link for following a random link on the left above the standard "Random page" link var newElement = mw.util.addPortletLink(	'p-navigation',	'javascript:randomLink',	'Random link',	'n-randomlink',	'Follow a randomly chosen link on ' + where,	'@',	'n-randompage' );

});