MediaWiki:Gadget-NoFunZone.js

From Uncyclopedia, the content-free encyclopedia
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);

})();