MediaWiki:Gadget-NoFunZone.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes.
- Internet Explorer: hold down the Ctrl key and click the Refresh or Reload button, or press Ctrl+F5.
- Firefox: hold down the Shift key while clicking Reload; alternatively press Ctrl+F5 or Ctrl-Shift-R.
- Opera, Konqueror and Safari users can just click the Reload button.
- Chrome: press Ctrl+F5 or Shift+F5
/* ---- WELCOME TO THE NO FUN ZONE! WHERE DREAMS GO TO DIE! ----
This gadget is disabled by default and completely optional! You can enable
it by going to Preferences > Gadgets, under "No Fun Allowed"
Things this gadget does to all user and user talk pages:
1) Removes all flying objects with infinite CSS animations.
2) Removes all background images from page body.
3) Unwraps any divs which style and contain more than 70% of page text.
4) Disables any Extension:CSS stylesheets which style the page.
5) Adds convenient toggle button in your personal toolbar.
6) Implants memetic agent inside brain which makes you grow kind of bored
of that whole "Moccassins" meme, inception-style.
*/
(function () {
'use strict';
var STORAGE_KEY = 'extcss-enabled';
// Storage helpers
function getEnabled() {
try {
return localStorage.getItem(STORAGE_KEY) !== '0';
} catch (e) {
return true; // default = enabled
}
}
function setEnabled(v) {
try {
localStorage.setItem(STORAGE_KEY, v ? '1' : '0');
} catch (e) {}
}
// Check if current page is in User or User talk namespace
function isUserNamespace() {
var ns = mw.config.get('wgNamespaceNumber') | 0;
return ns === 2 || ns === 3;
}
// Check if current page is a .js or .css page (or subpage thereof)
function isCodePage() {
var title = mw.config.get('wgPageName') || '';
return /\.(js|css)(\/|$)/i.test(title);
}
// Check if we should run on this page
function shouldRunOnPage() {
return isUserNamespace() && !isCodePage();
}
// Remove injected Extension:CSS styles
function removeExtensionCSS() {
if (!shouldRunOnPage()) return;
var selectors = [
'link[rel="stylesheet"][href^="/w/index.php?title"]',
'link[rel="stylesheet"][href^="data:text/css"]'
];
for (var s = 0; s < selectors.length; s++) {
var elements = document.querySelectorAll(selectors[s]);
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
if (el && el.parentNode) {
el.parentNode.removeChild(el);
}
}
}
}
// Remove wrapper divs which change base page color
function removePageColorChanger() {
if (!shouldRunOnPage()) return;
var contentArea = document.querySelector('.mw-parser-output');
if (!contentArea) return;
var children = contentArea.children;
// Heuristic: check the first few elements (within the top 5 elements)
for (var i = 0; i < Math.min(5, children.length); i++) {
var element = children[i];
// Only consider div elements
if (element.tagName !== 'DIV') continue;
// Check if this div contains a large portion of the page content
var divTextLength = element.textContent.length;
var totalTextLength = contentArea.textContent.length;
// If this div contains more than 70% of the page's text content,
// it's likely a wrapper div
if (totalTextLength > 0 && (divTextLength / totalTextLength) > 0.7) {
// Additional check: the div should have inline styles or classes
// that suggest it's for styling (background, color, etc.)
var hasStyleAttributes = element.hasAttribute('style') ||
element.className.length > 0;
if (hasStyleAttributes) {
// Unwrap this div while preserving its contents
if (element.parentNode) {
while (element.firstChild) {
element.parentNode.insertBefore(element.firstChild, element);
}
element.parentNode.removeChild(element);
}
break;
}
}
}
// Always also check for #pageColorChanger separately
var pcc = document.getElementById('pageColorChanger');
if (pcc && pcc.parentNode) {
while (pcc.firstChild) {
pcc.parentNode.insertBefore(pcc.firstChild, pcc);
}
pcc.parentNode.removeChild(pcc);
}
}
// Remove background image template
function removeBackgroundImage() {
if (!shouldRunOnPage()) return;
// Find all divs and check if they contain an img
var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; i++) {
var div = divs[i];
// Check if this div has z-index under -999
var zIndex = window.getComputedStyle(div).zIndex;
if (zIndex !== 'auto' && parseInt(zIndex, 10) < -999) {
// Check if it contains an img element
if (div.querySelector('img')) {
div.parentNode.removeChild(div);
}
}
}
}
// Remove div elements with infinite animations (on div itself or img children)
function removeInfiniteAnimations() {
if (!shouldRunOnPage()) return;
var divs = document.querySelectorAll('div');
for (var i = divs.length - 1; i >= 0; i--) {
var div = divs[i];
var hasInfiniteAnimation = false;
// Check computed style on the div (handles both longhand and shorthand)
var computedStyle = window.getComputedStyle(div);
if (computedStyle.animationIterationCount === 'infinite') {
hasInfiniteAnimation = true;
}
// Also check inline style attribute directly for 'infinite' keyword
// This catches cases like: animation: rotate 1.2s linear infinite
var inlineStyle = div.getAttribute('style');
if (inlineStyle && /\binfinite\b/i.test(inlineStyle)) {
hasInfiniteAnimation = true;
}
// Check img children for infinite animations
if (!hasInfiniteAnimation) {
var imgs = div.querySelectorAll('img');
for (var j = 0; j < imgs.length; j++) {
var img = imgs[j];
var imgComputedStyle = window.getComputedStyle(img);
if (imgComputedStyle.animationIterationCount === 'infinite') {
hasInfiniteAnimation = true;
break;
}
var imgInlineStyle = img.getAttribute('style');
if (imgInlineStyle && /\binfinite\b/i.test(imgInlineStyle)) {
hasInfiniteAnimation = true;
break;
}
}
}
if (hasInfiniteAnimation && div.parentNode) {
div.parentNode.removeChild(div);
}
}
}
// Toggle label text
function updateLabel(el) {
if (!el) return;
el.textContent = getEnabled() ? 'Userpage CSS: On' : 'Userpage CSS: Off';
// Vector skin blue styling
el.style.fontWeight = 'bold';
el.style.color = '#0645ad';
el.style.textDecoration = 'none';
el.style.cursor = 'pointer';
// Underline on hover
el.addEventListener('mouseenter', function() {
el.style.textDecoration = 'underline';
});
el.addEventListener('mouseleave', function() {
el.style.textDecoration = 'none';
});
}
// Notify helper (graceful fallback)
function notify(msg) {
if (mw.notify) {
mw.notify(msg, { autoHideSeconds: 5 });
} else {
// Fallback for when mw.notify isn't loaded yet
console.log('[ExtCSS] ' + msg);
}
}
// Create toggle control
function addToggle() {
if (!shouldRunOnPage()) return; // only show in User/User talk namespaces, not on code pages
if (!mw.config.get('wgUserName')) return; // skip anon
// Desktop portlet
var portletLink = document.getElementById('pt-extcss-toggle');
if (!portletLink && mw.util && mw.util.addPortletLink) {
// Try to find insertion point - prefer #pt-mytalk, fallback to #pt-notifications-notice
var insertBefore = document.getElementById('pt-mytalk') ||
document.getElementById('pt-notifications-notice');
portletLink = mw.util.addPortletLink(
'p-personal',
'#',
'',
'pt-extcss-toggle',
'Toggle Extension:CSS rendering on user & talk pages',
null,
insertBefore || null // insert before target if found, otherwise append to end
);
if (portletLink) {
updateLabel(portletLink);
portletLink.addEventListener('click', function (e) {
e.preventDefault();
var now = !getEnabled();
setEnabled(now);
updateLabel(portletLink);
if (now) {
// Turning CSS back on - reload page
notify('Extension:CSS enabled (reloading page...)');
setTimeout(function() {
location.reload();
}, 500);
} else {
// Turning CSS off
notify('Extension:CSS disabled');
removeInfiniteAnimations(); // Must run first while stylesheets still active
removeExtensionCSS();
removePageColorChanger();
removeBackgroundImage();
}
});
}
}
}
// Remove Extension:CSS on page load if disabled
mw.hook('wikipage.content').add(function () {
if (!getEnabled()) {
removeInfiniteAnimations(); // Must run first while stylesheets still active
removeExtensionCSS();
removePageColorChanger();
removeBackgroundImage();
}
});
// Initialize toggle ASAP
// Execute immediately if utilities are available
if (mw.util && mw.notify) {
if (document.readyState !== 'loading') {
addToggle();
} else {
document.addEventListener('DOMContentLoaded', addToggle, { once: true });
}
} else {
// Otherwise load utilities in background, then execute
mw.loader.using(['mediawiki.util', 'mediawiki.notify']).then(function () {
if (document.readyState !== 'loading') {
addToggle();
} else {
document.addEventListener('DOMContentLoaded', addToggle, { once: true });
}
});
}
// Safety net – reduced timeout
setTimeout(addToggle, 500);
})();