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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* <nowiki>
* DisableCode
* @file DisableCode is a JavaScript userscript that adds a number of buttons to
* the user's "My Tools" menu that permit the addition/removal of query strings
* in the URL. These query strings are used to deactivate various user/site
* JavaScript/CSS code for testing purposes.
* @author Eizen <dev.wikia.com/eizen>
* @author Parkour2906 <dev.fandom.com/User:Parkour2906>
*/
;(function (module, window, $, mw) {
"use strict";
// Respect prior double-load protection convention
if (!window || !$ || !mw || module.isLoaded || window.isDisableCodeLoaded) {
return;
}
module.isLoaded = window.isDisableCodeLoaded = true;
// Namespace protected properties
Object.defineProperties(this, {
/**
* @description The <code>Selectors</code> pseudo-enum is used to store the
* <code>string</code> names of custom selectors applied to the script's
* generated HTML elements and extant selectors of element already existing
* on the page for targetting purposes.
*
* @readonly
* @enum {Object}
*/
Selectors: {
enumerable: true,
writable: false,
configurable: false,
value: Object.freeze({
CLASS_OVERFLOW: "overflow",
CLASS_LIST: "disableCode-li",
CLASS_LINK: "disableCode-a",
ID_BAR: "WikiaBar",
ID_MY_TOOLS: "my-tools-menu",
ID_LIST: "disableCode",
ID_LINK_PREFIX: "disableCode-",
})
},
/**
* @description The <code>Params</code> pseudo-enum is used to store
* individual <code>Object</code>s related to each of the types of query
* strings available for insertion into the URL. These include strings for
* activation/deactivation of user CSS and JavaScipt and a catchall
* "safemode" setting that deactivates all custom code on the viewed wiki.
*
* @readonly
* @enum {Object}
*/
Params: {
enumerable: true,
writable: false,
configurable: false,
value: Object.freeze({
SAFE_MODE: Object.freeze({
QUERY: "safemode",
VALUE: 1,
MESSAGE: "safemode"
}),
USERJS: Object.freeze({
QUERY: "useuserjs",
VALUE: 0,
MESSAGE: "userJS"
}),
USERCSS: Object.freeze({
QUERY: "useusercss",
VALUE: 0,
MESSAGE: "userCSS"
}),
SITEJS: Object.freeze({
QUERY: "usesitejs",
VALUE: 0,
MESSAGE: "siteJS"
}),
SITECSS: Object.freeze({
QUERY: "usesitecss",
VALUE: 0,
MESSAGE: "siteCSS"
})
})
},
/**
* @description The <code>Utility</code> pseudo-enum is a catchall enum used
* store various values that see use in the script. These include the
* name of the script in <code>string</code> form, the I18n-js messages
* cache value, and the name of the hook that is fired at the end of the
* script initialization.
*
* @readonly
* @enum {Object}
*/
Utility: {
enumerable: true,
writable: false,
configurable: false,
value: Object.freeze({
SCRIPT: "DisableCode",
CACHE_VERSION: 2,
HOOK_NAME: "dev.disableCode"
}),
},
});
/**
* @description As the name implies, the <code>assembleLink</code> method is
* used to construct a simple custom link element for inclusion into the
* containing list element in the "My Tools" dropdown menu. It applies a
* number of custom selectors and attaches the desired href location before
* determining the display text by means of I18n-js.
*
* @function
* @param {string} paramMessage - The name of the I18n-js message to display
* @param {string} paramHref - The link location to which the button points
* @returns {string} - The assembled <code>string</code> HTML output
*/
this.assembleLink = function (paramMessage, paramHref) {
return mw.html.element("a", {
"id": this.Selectors.ID_LINK_PREFIX + paramMessage.toLowerCase(),
"class": this.Selectors.CLASS_LINK,
"href": paramHref,
"title": this.i18n.msg(paramMessage).plain()
}, this.i18n.msg(paramMessage).plain());
};
/**
* @description The <code>defineResetLocation</code> method is used to remove
* any vestigial query strings added by this script from the URL. The
* resultant target URL is the href of the "reset" button's link element. The
* idea of using regex for this part was initially developed by Parkour2906,
* with Eizen simplifying the regex slightly.
*
* @function
* @returns {string} - The href to which the "reset" button will point
*/
this.defineResetLocation = function () {
return this.config.wgArticlePath.replace("$1", this.config.wgPageName +
window.location.search.replace(
new RegExp("([?&])" + Object.values(this.Params).map(function (entry) {
return entry.QUERY + "[^&]*(?:&|$)";
}).join("|"), "gmi"), "$1"
)
);
};
/**
* @description The <code>init</code> function serves as the beating heart of
* the script, serving to configure i18n data, define the script-global
* scope's own properties, build and add the list container element to the
* "My Tools" menu, fire the hook, and establish an <code>exports</code>
* property for the <code>module</code>.
* <br />
* <br />
* In the script's original implementation, a custom "My Tools" clone was
* added to the toolbar. However, Parkour2906 scrapped this (admittedly
* janky) approach in favor of adding the various buttons to one sole overflow
* list element that would constitute a single entry in the "My Tools" menu,
* a design choice retained by Eizen.
*
* @function
* @param {Object} paramLang - i18n <code>Object</code> belonging to I18n-js
* @returns {void}
*/
this.init = function (paramLang) {
// Declarations
var content, target, search, link, resetLink, resetItem, listItems;
// Add i18n data as local property
(this.i18n = paramLang).useContentLang();
// Cache globals as object property
this.config = mw.config.get([
"wgArticlePath",
"wgPageName"
]);
// Definitions
target = "#" + this.Selectors.ID_BAR + " #" + this.Selectors.ID_MY_TOOLS;
search = (window.location.search.length) ? "&" : "?";
// Build tools list element and populate with buttons
resetLink = this.assembleLink("reset", this.defineResetLocation());
resetItem = mw.html.element("li", {
"class": this.Selectors.CLASS_OVERFLOW + " " + this.Selectors.CLASS_LIST
}, new mw.html.Raw(resetLink));
// Map params to individual li
listItems = Object.values(this.Params).map(function (paramEntry) {
link = this.assembleLink(paramEntry.MESSAGE,
this.config.wgArticlePath.replace("$1", this.config.wgPageName +
window.location.search + search + paramEntry.QUERY + "=" +
paramEntry.VALUE));
return mw.html.element("li", {
"class": this.Selectors.CLASS_OVERFLOW + " " + this.Selectors.CLASS_LIST
}, new mw.html.Raw(link));
}.bind(this)).join("");
content = resetItem + listItems;
// Add to "My Tools" menu
$(target).prepend(content);
// Expose public methods for external debugging
Object.defineProperty(module, "exports", {
enumerable: true,
writable: false,
configurable: false,
value: Object.freeze({
observeScript: window.console.dir.bind(this, this),
})
});
// Attach hook once complete
mw.hook(this.Utility.HOOK_NAME).fire(module);
};
// Attach hook listener, load script's messages, then pass to init
mw.hook("dev.i18n").add(function (i18n) {
$.when(
i18n.loadMessages(this.Utility.SCRIPT, {
cacheVersion: this.Utility.CACHE_VERSION,
}),
$.ready
)
.done(this.init.bind(this))
.fail(window.console.error.bind(null, this.Utility.SCRIPT));
}.bind(this));
// Import I18n-js if not already loaded
if (!window.dev || !window.dev.i18n) {
window.importArticle({
type: "script",
article: "u:dev:MediaWiki:I18n-js/code.js"
});
}
}.call(Object.create(null), (this.dev = this.dev || {}).disableCode =
this.dev.disableCode || {}, this, this.jQuery, this.mediaWiki));