Jump to content

User:The Editor's Apprentice/randomlink.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// 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 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'
);

});