User:HarJIT/portletnotifyshim.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
//// A script to fix some userscript or gadget compatibility issues with non-Wikimedia MediaWiki wikis.
// This may be useful if your Wikia, ShoutWiki or other non-Wikimedia user-JS loads Wikipedia, Commons, 
// etc. userscripts or gadgets and they run into incompatibilities.
//
// Feature shims carried out by this script:
//
//  * Implements mw.util.addPortletLink(...) toolbar/toolbox addition functionality for third-party 
//    MediaWiki skins which either do not supply a addPortletLink implementation, or supply one which
//    has not been ported to work with that skin.
//  * Implements mw.notify and re-implements mw.util.jsMessage (jsMsg) for Wikia. (Note: because Wikia
//    seems to have phagocytosed their mw.notify support from Gamepedia as of late, this part probably
//    no longer actually does anything.)
//
// Note the following caveats:
//
//  * Use at your own risk (I am not selling this and guarantee neither safety nor fitness).
//  * Although it is possible to use this when logged out, i.e. from Greasemonkey, certain
//    functionality (notably the Oasis toolbar) may only exist to begin with if you're logged in.
//    Similarly, p-cactions only exists in CologneBlue if you have edit perms on the current page.
//  * Portlets presently working: p-personal, p-tb, p-cactions, p-navigation.
//  * Skins which presently should be working: Aether, Aurora, Carbon, CarbonGreen, Chameleon,
//    Cosmos, Exvius, FandomDesktop, GreyStuff, Hydra, HydraDark, Monaco, Nimbus, Nostalgia, Oasis,
//    Onyx, Plurple, PlurpleDark, Standard, Truglass.
//  * Note that this has only been tested on certain deployed versions of those skins.
//    * Last tested December 2019 on Wikia, Shoutwiki, DoomWiki, Halopedia, Uncyclopedia.
//    * Last tested January 2020 on FTBWiki.org, Nostalgia Wikipedia.
//    * Last tested April 2021 on Gamepedia, TFWiki.net.
//  * Skins which this shouldn't be necessary for:
//    * Wikimedia skins: Monobook, Vector, Chick, CologneBlue, Modern, MySkin, Timeless.
//    * Others which already include their own compatible implementations of addPortletLink:
//      Bouquet, DeskMessMirrored, Dusk, DuskToDawn, Gamepress, MonacoBook, Simple
//      (or anything which is simply a Monobook modification).
//  * Known skins not currently provided for:
//    * Skins used for mobile sites: Citizen, FandomMobile, Minerva (including MinervaNeue),
//      WikiaMobile, WPTouch.
//    * addPortletLink absent: […]
//    * addPortletLink inadequate: […]
//
// Skin-specific caveats:
//
//  * Oasis:
//    * p-tb are added to "my tools" menu, very-short-label p-cactions add to the tool bar itself.
//    * Labels longer than 3 characters will be added to the "my tools" menu either way, because:
//    * The small-screen overflow system does not work on labels added to the bar (they just
//      disappear). Having tried to get this working, I've given up.
//    * These added options disappear until refresh if you use the customise feature on the
//      toolbar. Not sure what I can do about that.
//    * Too many p-navigation items just extend off the edge of the screen, such that they cannot
//      be accessed.
//  * FandomDesktop: includes several copies of the p-navigation equivalent, which become visible
//    at different screen sizes, and depending on whether the viewport is panned to the top of the
//    page or scrolled further down it. Only the link from one of these can be returned, so any
//    application of event handlers by invoking code will only apply to that one. Caveat about
//    the Oasis toolbar customisation feature probably applies here also.
//  * Onyx: lacking a "my tools" menu, p-tb and p-cactions are both added to the tool bar itself.
//  * Aurora: since the page/source/talk/history use custom icons, both p-tb and p-cactions are 
//    added to the "Page tools" menu from the bar to the right, not the actions bar to the left.
//  * Nostalgia: since some of the portlets' functions are duplicated in multiple parts of the
//    page (e.g. the p-cactions edit/talk/history in both header and footer), links often have to
//    be added in multiple locations. Only one of these can be returned, so anything which the
//    invoking code does to the portlet links after they're added (e.g. custom event handlers)
//    will only be applied to that one.
//
// Basic usage, has some limitations (cannot guarantee running before dependent code):
// mw.loader.load("//meta.wikimedia.org/w/index.php?action=raw&ctype=text/javascript&title=User:HarJIT/portletnotifyshim.js");
//
// It's more reliable to put dependent code in a callback:
// $.getScript("//meta.wikimedia.org/w/index.php?action=raw&ctype=text/javascript&title=User:HarJIT/portletnotifyshim.js", function(){
//     // do or load stuff that creates tabs ...
// });
//
// Canonical: [[m:User:HarJIT/portletnotifyshim.js]]
// Unstable: [[m:User:HarJIT/portletnotifyshim.js/sb.js]]

if(function(){

// No onload mechanism as (a) this must run before all onload callbacks which might use addPortletLink
// and (b) it isn't needed as the DOM is only used upon addPortletLink actually being called.

var $ = jQuery; // No reason to suspect PrototypeJS to be loaded, but be sure nonetheless.

//////////////////////////////////////////////////////////////////////////////////////////////////////
// addPortletLink

// Favour the one in mw.util (sometimes they differ, e.g. on tfwiki.net, in which case the mw.util one is the correct one).
var save_mwutiladdPortletLink = (typeof addPortletLink != "undefined" && addPortletLink != null)?(addPortletLink):(function(){});
save_mwutiladdPortletLink = (typeof mw.util.addPortletLink != "undefined" && mw.util.addPortletLink != null)?(mw.util.addPortletLink):(save_mwutiladdPortletLink);

var genAnc = function (ar) {
    var anc = document.createElement("a");
    anc.setAttribute("href", ar[1]); // href (ar[0], ar[1], ar[2] were formal params)
    if (typeof ar[3] != "undefined" && ar[3] !== null) {
        anc.setAttribute("data-name", ar[3]);
        anc.setAttribute("id", ar[3]);
    };
    if (typeof ar[4] != "undefined" && ar[4] !== null) {
        anc.setAttribute("title", ar[4]);
    };
    if (typeof ar[5] != "undefined" && ar[5] !== null) {
        anc.setAttribute("accesskey", ar[5]);
    };
    return anc;
};

if (mw.config.get("skin") == "oasis" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        var labeltext = document.createTextNode(text);
        if (portletId == "p-cactions" || portletId == "p-tb") {
            ovf = document.createElement("li");
            ovf.setAttribute("class", "overflow");
            var mtm = document.getElementById("my-tools-menu");
            if (text.length < 4 && portletId == "p-cactions") {
                mtm.parentNode.parentNode.insertBefore(ovf, mtm.parentNode);
            } else {
                mtm.appendChild(ovf);
            };
            anc = genAnc(arguments);
        } else if (portletId == "p-navigation") {
            var udrop = document.getElementsByClassName("wds-community-header__local-navigation")[0];
            var ulst = udrop.getElementsByClassName("wds-tabs")[0];
            var ivf = document.createElement("li");
            ulst.appendChild(ivf);
            ivf.setAttribute("class", "wds-tabs__tab");
            ovf = document.createElement("div");
            ivf.appendChild(ovf);
            ovf.setAttribute("class", "wds-tabs__tab-label");
            anc = genAnc(arguments);
            anc.setAttribute("class", "wds-global-navigation__dropdown-link");
            var spa = document.createElement("span");
            spa.appendChild(labeltext);
            labeltext = spa;
        } else if (portletId == "p-personal") {
            ovf = document.createElement("li");
            var udrop = document.getElementsByClassName("wds-global-navigation__user-menu")[0];
            var ulst = udrop.getElementsByClassName("wds-list")[0];
            ulst.appendChild(ovf);
            anc = genAnc(arguments);
            anc.setAttribute("class", "wds-global-navigation__dropdown-link");
        } else {
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        ovf.appendChild(anc);
        anc.appendChild(labeltext);
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "fandomdesktop" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        var labeltext = document.createTextNode(text);
        if (portletId == "p-cactions") {
            ovf = document.createElement("li");
            ovf.setAttribute("class", "overflow");
            var mtm = document.querySelector(".page-header__actions ul");
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
            ovf.appendChild(anc);
            anc.appendChild(labeltext);
        } else if (portletId == "p-tb") {
            ovf = document.createElement("li");
            ovf.setAttribute("class", "overflow");
            var mtm = document.getElementById("my-tools-menu");
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
            ovf.appendChild(anc);
            anc.appendChild(labeltext);
        } else if (portletId == "p-navigation") {
            var disputes = arguments;
            $(".fandom-community-header__local-navigation .wds-tabs").toArray().map(function (ulst) {
                var moreMenus = ulst.getElementsByClassName("more-menu");
                if (moreMenus.length) {
                    var myul = moreMenus[0].getElementsByClassName("wds-list")[0];
                    ovf = document.createElement("li");
                    myul.appendChild(ovf);
                    anc = genAnc(disputes);
                    ovf.appendChild(anc);
                    anc.appendChild(document.createTextNode(text));
                } else {
                    var ivf = document.createElement("div");
                    ulst.appendChild(ivf);
                    ivf.setAttribute("class", "wds-dropdown");
                    ovf = document.createElement("div");
                    ivf.appendChild(ovf);
                    ovf.setAttribute("class", "wds-tabs__tab-label first-level-item");
                    anc = genAnc(disputes);
                    ovf.appendChild(anc);
                    var spa = document.createElement("span");
                    anc.appendChild(spa);
                    spa.appendChild(document.createTextNode(text));
                }
                console.log(myul || ulst);
            });
        } else if (portletId == "p-personal") {
            ovf = document.createElement("li");
            var udrop = document.querySelector(".global-navigation__bottom > .wds-dropdown .wds-dropdown__content");
            var ulst = udrop.getElementsByClassName("wds-list")[0];
            ulst.appendChild(ovf);
            anc = genAnc(arguments);
            ovf.appendChild(anc);
            anc.appendChild(labeltext);
        } else {
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "aurora" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        if (portletId == "p-cactions" || portletId == "p-tb") {
            portletId = "p-tools";
        }
        ovf = document.createElement("li");
        var ulst = document.getElementById(portletId);
        ulst.appendChild(ovf);
        anc = genAnc(arguments);
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "truglass" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var truglassAria = function () {
        if (!mw.config.get("HarJIT-tabshim-aria")) {
            mw.config.set("HarJIT-tabshim-aria", 1);
            // Yes really, truglass uses layout tables.
            $("table.fullwidth").toArray().map(function (e) {
                e.setAttribute("role", "presentation");
            });
            $("div.sbmodule").toArray().map(function (e) {
                e.setAttribute("role", "navigation");
            });
            document.getElementById("realcontent").setAttribute("role", "main");
        }
    }
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        if (portletId == "p-cactions") {
            ovf = document.createElement("span");
            var mtm = $(".cbar td").toArray()[0]
            mtm.appendChild(document.createTextNode("\t"));
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
        } else if (portletId == "p-tb") {
            ovf = document.createElement("li");
            var mtm = $("#sbm-toolbox ul").toArray()[0];
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
        } else if (portletId == "p-navigation") {
            ovf = document.createElement("li");
            var mtm = $("#sbm-navigation ul").toArray()[0];
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
        } else if (portletId == "p-personal") {
            ovf = document.createElement("li");
            var ulst = $("#ptools ul").toArray()[0];
            ulst.appendChild(ovf);
            anc = genAnc(arguments);
        } else {
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
    $(document).ready(truglassAria);
} else if (mw.config.get("skin") == "greystuff" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc; var ulst;
        ovf = document.createElement("li");
        if (portletId == "p-cactions") {
            ulst = $("#p-actions ul").toArray()[0];
        } else if (portletId == "p-tb") {
            ulst = $("#p-tbx ul").toArray()[0];
        } else if (portletId == "p-navigation") {
            ulst = $("#p-Navigation ul").toArray()[0];
        } else if (portletId == "p-personal") {
            ulst = $("#p-personal ul").toArray()[0];
        } else {
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        ulst.appendChild(ovf);
        anc = genAnc(arguments);
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "cosmos" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        if (portletId == "p-cactions") {
            // Not supported by default addPortletLink under Cosmos.
            ovf = document.createElement("li");
            ovf.setAttribute("class", "overflow");
            var mtm = document.getElementById("cosmos-actionsList-list");
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
        } else if (portletId == "p-navigation") {
            // Cosmos calls it p-Navigation not p-navigation for some reason.
            return save_mwutiladdPortletLink.apply(this, ["p-Navigation", href, text]);
        } else {
            // Includes p-tb/p-personal, which default addPortletLink under Cosmos supports.
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "onyx" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf; var anc;
        if (portletId == "p-cactions" || portletId == "p-tb") {
            ovf = document.createElement("li");
            ovf.setAttribute("class", "overflow");
            var mtm = document.getElementById("onyx-tools-list");
            mtm.appendChild(ovf);
            anc = genAnc(arguments);
        } else if (portletId == "p-navigation") {
            ovf = document.createElement("li");
            var ulst = document.getElementById("onyx-navigation-list");
            ulst.insertBefore(ovf, ulst.firstChild); // ulst is a backward stacking flexbox.
            anc = genAnc(arguments);
        } else if (portletId == "p-personal") {
            ovf = document.createElement("li");
            var ulst = document.getElementById("onyx-personalTools-list");
            ulst.appendChild(ovf);
            anc = genAnc(arguments);
        } else {
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if ((mw.config.get("skin") == "plurple" || mw.config.get("skin") == "plurpledark") && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Annoyingly, none of them seem to have unique IDs (though several have the ID #mw-panel, which is not how IDs are supposed to work)
    // Accordingly this will probably break on other L10Ns.
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        if (portletId == "p-cactions") {
            var ulst = document.querySelector("#content header .views ul");
        } else if (portletId == "p-personal") {
            var ulst = document.querySelector("ul[aria-label=User]");
        } else {
            // Including p-tb and p-navigation
            return save_mwutiladdPortletLink.apply(this, arguments);
        };
        var ovf = document.createElement("li");
        ulst.appendChild(ovf);
        var anc = genAnc(arguments);
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "monaco" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf;
        if (portletId == "p-cactions") {
            ovf = document.createElement("li");
            var mtm = document.getElementById("page_controls");
            mtm.appendChild(ovf);
        } else if (portletId == "p-navigation") {
            // Some versions have the menu items in an ul (DoomWiki), some as straight divs (ShoutWiki)
            var mtm = $("#navigation ul").toArray()[0] || $("#navigation").toArray()[0];
            ovf = document.createElement((mtm.nodeName.toLowerCase() == "ul")?("li"):("div"));
            mtm.appendChild(ovf);
            ovf.setAttribute("class", "menu-item");
        } else if (portletId == "p-tb") {
            ovf = document.createElement("li");
            var uls = $("#link_box ul").toArray();
            var mul = uls[0];
            while (uls.length - 1) {
                uls.splice(0, 1);
                if ($(mul).children().length > $(uls[0]).children().length) {
                    mul = uls[0];
                };
            };
            mul.appendChild(ovf);
        } else if (portletId == "p-personal") {
            ovf = document.createElement("li");
            var ubar = document.getElementById("userData");
            if (document.getElementById("headerButtonUser") !== null) {
                ubar.insertBefore(ovf, document.getElementById("headerButtonUser").parentNode);
            } else {
                ubar.appendChild(ovf);
            };
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
        var anc = genAnc(arguments);
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
}  else if ((mw.config.get("skin") == "hydra" || mw.config.get("skin") == "hydradark") && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        if (portletId == "p-personal") {
            var ovf = document.createElement("li");
            var mtm = $("#netbar .user .dropdown").toArray()[0];
            mtm.appendChild(ovf);
            var anc = genAnc(arguments);
            ovf.appendChild(anc);
            anc.appendChild(document.createTextNode(text));
            return ovf;
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "exvius" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var ovf = document.createElement("li");
        if (portletId == "p-cactions") {
            var mtm = $("#header-tools .sub-menu").toArray()[0];
            mtm.insertBefore(ovf, $("#header-tools .sub-menu .label").toArray()[1]);
        } else if (portletId == "p-navigation") {
            var mtm = $("#menu").toArray()[0];
            mtm.appendChild(ovf);
        } else if (portletId == "p-tb") {
            var mtm = $("#header-tools .sub-menu").toArray()[0];
            mtm.appendChild(ovf);
        } else if (portletId == "p-personal") {
            var mtm = $("#netbar .user .dropdown").toArray()[0];
            mtm.appendChild(ovf);
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
        var anc = genAnc(arguments);
        ovf.appendChild(anc);
        anc.appendChild(document.createTextNode(text));
        return ovf;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "nimbus" && !mw.config.get("HarJIT-tabshim-loaded")) {
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var spa; var anc;
        if (typeof arguments[3] != "undefined" && arguments[3] == "utcdate") {
            return save_mwutiladdPortletLink.apply(this, arguments); // Sorry...
        } else if (portletId == "p-navigation") {
            var mtm = document.getElementById("menu");
            var last_anc = $(mtm).children().toArray(); // Skipping text nodes.
            last_anc = last_anc[last_anc.length-1];
            last_anc.setAttribute("class",
                     last_anc.getAttribute("class").split("border-fix").join(""));
            ovf = document.createElement("div");
            mtm.appendChild(ovf);
            ovf.setAttribute("class", "menu-item border-fix");
            // Nimbus hardcodes mouseover colour in callbacks, might be changed by site: just don't bother.
            // This differs from Monaco, which does it robustly using a :hover CSS selector.
            spa = anc = genAnc(arguments);
            ovf.appendChild(anc);
        } else if (portletId == "p-cactions") {
            // Some installations of Nimbus (Shoutwiki) seem to have the article-more-container (akin to Vector) while
            // others (Halopedia) do not (akin to Monobook)
            var cnt = document.getElementById("article-more-container") || document.getElementById("article-tabs");
            var last_anc = $(cnt).children().toArray(); // Skipping text nodes.
            var cleared = null;
            if (last_anc[last_anc.length-1].nodeName.toLowerCase() == "div") {
                cleared = last_anc[last_anc.length-1];
                cnt.removeChild(cleared);
                last_anc = last_anc.slice(0, -1);
            }
            last_anc = last_anc[last_anc.length-1];
            last_anc.setAttribute("class",
                     last_anc.getAttribute("class").split("border-fix").join(""));
            anc = genAnc(arguments);
            anc.setAttribute("class", "border-fix tab-off");
            cnt.appendChild(anc);
            if (cleared) {
                cnt.appendChild(cleared);
            }
            if (document.getElementById("article-more-container")) {
                spa = anc;
            } else {
                spa = document.createElement("span");
                anc.appendChild(spa);
            }
        } else if (portletId == "p-tb") {
            var cnt = document.getElementById("other-links");
            var chi = $(cnt).children().toArray(); // Skipping text nodes.
            anc = genAnc(arguments);
            cnt.appendChild(anc);
            var clr;
            if (chi[chi.length-3].getAttribute("class") == "cleared") {
                // i.e. ends in a sandwich of one
                clr = chi[chi.length-1]; // move the last one forward
            } else {
                clr = document.createElement("div");
                clr.setAttribute("class", "cleared");
            };
            cnt.appendChild(clr);
            spa = anc;
        } else if (portletId == "p-personal") {
            var cnt = document.getElementById("wiki-login");
            anc = genAnc(arguments);
            anc.setAttribute("class", "mw-skin-nimbus-button positive-button");
            cnt.insertBefore(anc, $("#wiki-login .negative-button").toArray()[0]);
            spa = document.createElement("span");
            anc.appendChild(spa);
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
        spa.appendChild(document.createTextNode(text));
        return anc;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if ((mw.config.get("skin") == "carbon" ||
            mw.config.get("skin") == "carbongreen") && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Carbon (at least on ftbwiki.org) actually has a copy of mw.util.addPortletLink in place already,
    // but not one ported to work with the Carbon skin itself. We simply need to patch it.
    // I could do this just by patching the DOM, but that would entail overwriting existing IDs
    // which is probably not a good idea.
    document.getElementById("navigation").parentNode.setAttribute("id", "p-navigation");
    document.getElementById("toolbox").parentNode.setAttribute("id", "p-tb");
    var usermenuid = document.getElementById("user_link").parentNode.getAttribute("id");
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var newargs = Array.from(arguments).slice();
        if (portletId == "p-personal") {
            newargs[0] = usermenuid;
        } else if (portletId == "p-cactions") {
            newargs[0] = "admin_bar";
        }
        return save_mwutiladdPortletLink.apply(this, newargs);
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "strappingads3" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Similarly with strappingads3 (wiki.aidancbrady.com)
    var usermenuid = Array.from(document.querySelectorAll("#searchform+ul li.dropdown")).pop().getAttribute("id");
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var newargs = Array.from(arguments).slice();
        if (portletId == "p-cactions") {
            newargs[0] = "p-namespaces";
        } else if (portletId == "p-tb") {
            newargs[0] = "p-toolbox";
        } else if (portletId == "p-navigation") {
            newargs[0] = "page-header";
        } else if (portletId == "p-personal") {
            newargs[0] = usermenuid;
        }
        return save_mwutiladdPortletLink.apply(this, newargs);
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "chameleon" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Similar approach to the other FTBWiki.org ones (Carbon, CarbonGreen).
    document.getElementById("p-navigation").parentNode.setAttribute("id", "p-navigation-outer"); // See below
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var newargs = Array.from(arguments).slice();
        if (portletId == "p-cactions") {
            newargs[0] = "p-views";
        } else if (portletId == "p-navigation") {
            // Because the #p-navigation ID is on the UL itself, the existing addPortletLink implementation
            // doesn't find an UL amongst its immediate descendents and appends one itself.
            newargs[0] = "p-navigation-outer";
        }
        return save_mwutiladdPortletLink.apply(this, newargs);
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "minerva" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // i.e. when running with ?useskin=minerva and with the advanced mode enabled.
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var newargs = Array.from(arguments).slice();
        if (portletId == "p-cactions") {
            newargs[0] = "p-tb";
        }
        var myli = save_mwutiladdPortletLink.apply(this, newargs);
        if (portletId != "p-navigation" && portletId != "p-interaction") {
            myli.setAttribute("class", "toggle-list-item");
            myli.firstChild.setAttribute("class", "toggle-list-item__label");
        } else {
            // Could alternatively just set toggle-list-item__label, but trying to match existing.
            myli.firstChild.appendChild(document.createElement("span"));
            myli.firstChild.lastChild.appendChild(myli.firstChild.firstChild);
        }
        return myli;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "aether" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // A skin used on KDE Community wiki. Misses out the IDs on several portlets without apporent reason.
    // Also, puts the IDs on headings, not bodies.
    document.querySelector(".menu-box:nth-of-type(2)").firstElementChild.setAttribute("id", "p-cactions");
    document.querySelector(".menu-box:last-of-type").firstElementChild.setAttribute("id", "p-personal");
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var newargs = Array.from(arguments).slice();
        if (document.getElementById(portletId)) {
            document.getElementById(portletId).nextElementSibling.setAttribute("id", "PORTLET-BODY-OF-" + portletId);
            newargs[0] = "PORTLET-BODY-OF-" + portletId;
        }
        return save_mwutiladdPortletLink.apply(this, newargs);
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "nostalgia" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Has the basic addPortletLink but it's completely incompatible with the Nostalgia skin.
    // It doesn't even use the same concept of operating as Monobook (not HTML lists but pipe-separated,
    // several familiar portlets' functions and/or content duplicated in several places...)
    // Caveat: sometimes has to add two links and can only return one (of course).
    var toolbar_insert = $("a[rel=archives]")[0].nextSibling;
    var toolbox_insert = $("#footer br")[0];
    var toolbox_insert_for_toolbar = $("a[rel=archives]")[1].nextSibling;
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var anc;
        if (portletId == "p-cactions") {
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            toolbox_insert_for_toolbar.parentNode.insertBefore(document.createTextNode(" |\n"), toolbox_insert_for_toolbar);
            toolbox_insert_for_toolbar.parentNode.insertBefore(anc, toolbox_insert_for_toolbar);
            anc = genAnc(arguments); // Need a second copy of the node.
            anc.appendChild(document.createTextNode(text));
            toolbar_insert.parentNode.insertBefore(document.createTextNode(" |\n"), toolbar_insert);
            toolbar_insert.parentNode.insertBefore(anc, toolbar_insert);
        } else if (portletId == "p-navigation") {
            var nav_insert = document.getElementById("searchform").previousSibling;
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            nav_insert.parentNode.insertBefore(document.createTextNode(" |\n"), nav_insert);
            nav_insert.parentNode.insertBefore(anc, nav_insert);
            nav_insert = $('a[title="Special:RecentChanges"]')[0].nextSibling;
            anc = genAnc(arguments); // Need a second copy of the node.
            anc.appendChild(document.createTextNode(text));
            nav_insert.parentNode.insertBefore(document.createTextNode(" |\n"), nav_insert);
            nav_insert.parentNode.insertBefore(anc, nav_insert);
        } else if (portletId == "p-tb") {
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            toolbox_insert.parentNode.insertBefore(document.createTextNode(" |\n"), toolbox_insert);
            toolbox_insert.parentNode.insertBefore(anc, toolbox_insert);
        } else if (portletId == "p-personal") {
            var user_insert = document.getElementById("specialpages").previousSibling;
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            user_insert.parentNode.insertBefore(document.createTextNode(" |\n"), user_insert);
            user_insert.parentNode.insertBefore(anc, user_insert);
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
        return anc;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else if (mw.config.get("skin") == "standard" && !mw.config.get("HarJIT-tabshim-loaded")) {
    // Not a dissimilar deal to Nostalgia, being the other pre-Monobook skin.
    var toolbar_insert = $("a[rel=archives]")[0].nextSibling;
    var toolbox_insert_for_toolbar = $("a[rel=archives]")[1].nextSibling;
    var mynew_mwutiladdPortletLink = function (portletId, href, text) {
        var anc;
        if (portletId == "p-cactions") {
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            toolbox_insert_for_toolbar.parentNode.insertBefore(document.createTextNode(" |\n"), toolbox_insert_for_toolbar);
            toolbox_insert_for_toolbar.parentNode.insertBefore(anc, toolbox_insert_for_toolbar);
            anc = genAnc(arguments); // Need a second copy of the node.
            anc.appendChild(document.createTextNode(text));
            toolbar_insert.parentNode.insertBefore(document.createTextNode(" |\n"), toolbar_insert);
            toolbar_insert.parentNode.insertBefore(anc, toolbar_insert);
        } else if (portletId == "p-navigation") {
            var nav_insert = $("#quickbar hr").toArray()[1];
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            nav_insert.parentNode.insertBefore(anc, nav_insert);
            nav_insert.parentNode.insertBefore(document.createElement("br"), nav_insert);
        } else if (portletId == "p-tb") {
            var quickbar = document.getElementById("quickbar");
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            quickbar.appendChild(anc);
            quickbar.appendChild(document.createElement("br"));
        } else if (portletId == "p-personal") {
            var user_insert = $("#topbar td[align=right] br").toArray();
            user_insert = user_insert[user_insert.length - 1];
            anc = genAnc(arguments);
            anc.appendChild(document.createTextNode(text));
            user_insert.parentNode.insertBefore(document.createTextNode(" |\n"), user_insert);
            user_insert.parentNode.insertBefore(anc, user_insert);
        } else {
           return save_mwutiladdPortletLink.apply(this, arguments);
        };
        return anc;
    };
    window.addPortletLink = mw.util.addPortletLink = mynew_mwutiladdPortletLink;
    mw.config.set("HarJIT-tabshim-loaded", 1);
} else {
    // Make sure both are the same.
    window.addPortletLink = mw.util.addPortletLink = save_mwutiladdPortletLink;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////
// mw.notify, mw.util.jsMessage, jsMsg

if (typeof jsMsg == "undefined" && typeof mw.util.jsMessage != "undefined") {
    window.jsMsg = mw.util.jsMessage;
};

if (((window.location.href+"").indexOf("wikia.org") >= 0 || (window.location.href+"").indexOf("fandom.com") >= 0) && 
            typeof mw.notify == "undefined") {
    if (mw.config.get("skin") == "oasis") {
        // Default jsMsg uses a div obscured by the top banner in Oasis/Wikia (so message text is
        // not visible) and does not match the banner messages used by the Wikia software:
        // replace it with a rewrite that does.
        //
        // With reference to:
        //
        // The confirmation message inserted in Oasis once user preferences are saved.
        // The EU cookies warning when logged out.
        //
        // Invocation aims to be compatible with jsMsg aka mw.util.jsMessage.  API docs:
        //  <1> https://www.mediawiki.org/wiki/ResourceLoader/Modules?oldid=2399938#jsMessage
        //  <2> https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.util-method-jsMessage
        //
        // Return value aims at compatibility with that of mw.notify:
        //  <3> https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Notification_
        //
        window.jsMsg = mw.util.jsMessage = function (text) {
            var style = "";
            if (typeof text == "undefined" || text === null || text == "") {
                return; // Following <1>, but messages are user-dismissible so largely N/A.
            };
            if ((arguments.length > 1) && arguments[1] !== null) {
                style = arguments[1]; // Optional argument, see <1>
            };
            var a = document.getElementsByClassName("banner-notifications-placeholder")[0];
            var b = document.createElement('span');
            //var c = document.getElementById('WikiHeader');
            var d = document.createElement("div");
            var e = document.createElement("div");
            var lic = document.createElement("div");
            var f = document.createElementNS("http://www.w3.org/2000/svg", "svg");
            var g = document.createElementNS("http://www.w3.org/2000/svg", "text");
            var r = {"pause": function(){}, "resume": function(){}}; // <3> but not auto-dismiss
            d.setAttribute("class", "wds-banner-notification__container");
            // Otherwise defaults to transparent, odd when scrolling.
            mw.util.addCSS(".jsmsg-custom-banner-notification { "+
                           "background: white; "+
                           "border: 1px solid silver; "+
                           "border-top: 0; }");
            if (style == "info") {
                style = "notify"; // Same purpose, the latter is the one used.
            };
            if (["notify","confirm","warn","error"].indexOf(style) >= 0) {
                // OLD: Oasis seems to use the class names "confirm", "warn" and "error" directly on
                // div.banner-notification elements, rather than e.g. mw-js-message-warn.
                // NOW: wds-(alert, warning, success, message)
                style = {"notify": "message", "confirm": "success",
                         "warn": "warning", "error": "alert"}[style];
                e.setAttribute("class", "wds-banner-notification wds-"+style); // Sets bg/border colour
            } else if (style == "") {
                e.setAttribute("class", "wds-banner-notification wds-message");
            } else {
                e.setAttribute("class", "wds-banner-notification jsmsg-custom-banner-notification"+
                                        " mw-js-message-" + style);
            };
            d.appendChild(e);
            lic.setAttribute("class", "wds-banner-notification__icon");
            e.appendChild(lic);
            f.setAttribute("width", "12");
            f.setAttribute("height", "12");
            f.setAttribute("viewBox", "0 0 12 12");
            r.close = f.onclick = function () { d.parentNode.style.height = (parseInt(d.parentNode.style.height || "0px") - 48) + "px"; d.parentNode.removeChild(d); }; // <3>
            g.style["font-size"] = "24px";
            g.setAttribute("x", "0");
            g.setAttribute("y", "12");
            g.appendChild(document.createTextNode("×"));
            f.appendChild(g);
            b.setAttribute("class", "wds-banner-notification__text");
            e.appendChild(b);
            f.setAttribute("class", "wds-icon wds-icon-tiny wds-banner-notification__close");
            e.appendChild(f);
            // Following <2> in the variety of object types accepted.
            if (typeof text == "string") {
                text = $.parseHTML(text);
            };
            $(b).append(text);
            a.append(d); // insertBefore(d, c);
            a.style.height = (parseInt(a.style.height || "0px") + 48) + "px";
            return r;
        };
    };
    // Invocations to mw.notify can be redirected to jsMsg without loss
    // of basic functionality:
    mw.notify = function (a) {
        if (arguments.length-1 && typeof arguments[1].title != "undefined") {
            a = "<b>" + arguments[1].title + "</b><br />" + a;
        };
        var r;
        if (arguments.length-1 && typeof arguments[1].type != "undefined") {
            r = jsMsg(a, arguments[1].type);
        } else {
            r = jsMsg(a);
        };
        if (r && typeof r.close == "undefined") {
            r = {"close": function(){ jsMsg(""); }, "pause": function(){}, 
                 "resume": function(){} };
        };
        return r;
    };
    //
    // Make mw.loader.using ignore dependency on mediawiki.notify:
    var save_mwloaderusing = mw.loader.using;
    var my_mwloaderusing = function (modules) {
        var i; for (i = 0; i < modules.length; i += 1){
            if (modules[i] == "mediawiki.notify") {
                modules.splice(i, 1);
                i -= 1;
            };
        };
        return save_mwloaderusing.apply(this, arguments);
    };
    mw.loader.using = my_mwloaderusing;
} else if (mw.notify && typeof jsMsg == "undefined") {
    // Inverse case: compatibility on wiki sites without legacy Wikibits.
    window.jsMsg = function (s) { return mw.notify(s); };
};

}()){};

// End: [[m:User:HarJIT/portletnotifyshim.js]]