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 [[Wikipedia:Featured articles]] and// [[Wikipedia: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 debugrandomlink_maxfrom   = 22000000;    // 10-20% less than max page idrandomlink_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, 2022function 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" linkvar newElement = mw.util.addPortletLink('p-navigation','javascript:randomLink()','Random link','n-randomlink','Follow a randomly chosen link on ' + where,'@','n-randompage');});