Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js

MediaWiki interface page
Revision as of 21:27, 20 February 2026 by QuadraticFour (talk | contribs)

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.
(function () {
    const theme = localStorage.getItem('lt-theme') || 'lt-theme-default';
    document.documentElement.classList.add(theme);
})();
mw.loader.using(['mediawiki.util']).then(function () {

    function injectThemeOption(container) {
    	
    	function createRadio(id, label) {
		    const radioWrapper = document.createElement('div');
		    radioWrapper.className = 'citizen-client-prefs-radio';
		
		    const input = document.createElement('input');
		    input.type = 'radio';
		    input.name = 'lt-theme-group';
		    input.id = id;
		    input.className = 'citizen-client-prefs-radio__input';
		
		    const icon = document.createElement('span');
		    icon.className = 'citizen-client-prefs-radio__icon';
		
		    const labelEl = document.createElement('label');
		    labelEl.className = 'citizen-client-prefs-radio__label';
		    labelEl.htmlFor = id;
		    labelEl.textContent = label;
		
		    radioWrapper.appendChild(input);
		    radioWrapper.appendChild(icon);
		    radioWrapper.appendChild(labelEl);
		
		    return radioWrapper;
		}

        if (document.getElementById('lt-theme-test')) return;

        // Portlet wrapper
        const wrapper = document.createElement('div');
        wrapper.id = 'lt-theme-test';
        wrapper.className = 'mw-portlet citizen-menu';

        // Heading
        const heading = document.createElement('div');
        heading.className = 'citizen-menu__heading';
        heading.textContent = 'Theme';

        // Content wrapper
        const content = document.createElement('div');
        content.className = 'citizen-menu__content';

        // UL
        const list = document.createElement('ul');
        list.className = 'citizen-menu__content-list';

        // LI
        const item = document.createElement('li');
        item.className = 'mw-list-item mw-list-item-js';

        // Inner div (matches Citizen structure)
        const innerDiv = document.createElement('div');

        // FORM (this is important for spacing)
        const form = document.createElement('form');

        form.appendChild(createRadio('lt-theme-default', '🎭 Velvet'));
		form.appendChild(createRadio('lt-theme-red', '🚇 Meta'));
		form.appendChild(createRadio('lt-theme-blue', '⚖️ Justice'));
		
		// === Persistence Logic ===

		// Check if theme exists
		let savedTheme = localStorage.getItem('lt-theme');
		
		if (!savedTheme) {
		    // First-time visitor → set default explicitly
		    savedTheme = 'lt-theme-default';
		    localStorage.setItem('lt-theme', savedTheme);
		}
		
		// Select the saved radio
		const savedInput = form.querySelector('#' + savedTheme);
		if (savedInput) {
		    savedInput.checked = true;
		}
		
		form.addEventListener('change', function (e) {
		    if (e.target && e.target.matches('.citizen-client-prefs-radio__input')) {
		
		        const newTheme = e.target.id;
		
		        document.documentElement.classList.remove(
		            'lt-theme-default',
		            'lt-theme-red',
		            'lt-theme-blue'
		        );
		
		        document.documentElement.classList.add(newTheme);
		
		        localStorage.setItem('lt-theme', newTheme);
		
		        // Re-run scrapbook engine after theme change
		        applyRansomEffect('.lt-infobox-title');
		        applyRansomEffect('.mw-page-title-main');
		        applyRansomEffect('h2');
		        applyRansomEffect('.mw-logo-wordmark');
		    }
		});

        // Assemble hierarchy
        innerDiv.appendChild(form);
        item.appendChild(innerDiv);
        list.appendChild(item);
        content.appendChild(list);
        wrapper.appendChild(heading);
        wrapper.appendChild(content);

        container.appendChild(wrapper);
    }

    const observer = new MutationObserver(function () {
        const container = document.querySelector('.citizen-preferences-content');
        if (container) {
            injectThemeOption(container);
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
    
    function floatWordmark() {
        const wordmarks = document.querySelectorAll('.mw-logo-wordmark');

        wordmarks.forEach(function (el) {
            if (el.dataset.floatingApplied) return;

            const text = el.textContent;
            el.textContent = '';

            text.split('').forEach(function (char, index) {
                const span = document.createElement('span');
                span.textContent = char === ' ' ? '\u00A0' : char;
                span.style.animationDelay = (index * 0.08) + 's';
                span.className = 'velvet-letter';
                el.appendChild(span);
            });

            el.dataset.floatingApplied = "true";
        });
    }
    floatWordmark();
    function toggleScrapbook(el) {
	
	    const isRed = document.documentElement.classList.contains('lt-theme-red');
	
	    const original = el.querySelector('.scrap-original');
	    const scrapbook = el.querySelector('.scrap-layer');
	
	    if (!original || !scrapbook) return;
	
	    if (isRed) {
	        original.style.display = 'none';
	        scrapbook.style.display = 'inline';
	    } else {
	        original.style.display = 'inline';
	        scrapbook.style.display = 'none';
	    }
	}
    function applyRansomEffect(selector) {
	
	    const elements = document.querySelectorAll(selector);
	
	    elements.forEach(function (el) {
	
	        const isRed = document.documentElement.classList.contains('lt-theme-red');

			if (el.dataset.scrapbookInitialized === "true") {
			
			    if (isRed) {
			        // Rebuild scrapbook layer for new randomization
			        const scrapbookLayer = el.querySelector('.scrap-layer');
			        if (scrapbookLayer) scrapbookLayer.remove();
			        el.dataset.scrapbookInitialized = "";
			    } else {
			        toggleScrapbook(el);
			        return;
			    }
			}
	
	        const originalText = el.textContent;
	        el.textContent = '';
	
	        // Create original layer
	        const originalLayer = document.createElement('span');
	        originalLayer.className = 'scrap-original';
	        originalLayer.textContent = originalText;
	
	        // Create scrapbook layer
	        const scrapbookLayer = document.createElement('span');
	        scrapbookLayer.className = 'scrap-layer';
	
	        const fonts = [
		    // Heavy Sans
		    "'Anton', sans-serif",
		    "'Bebas Neue', sans-serif",
		    "'Archivo Black', sans-serif",
		    "'Oswald', sans-serif",
		
		    // Serif (Ransom vibe)
		    "'Playfair Display', serif",
		    "'Libre Baskerville', serif",
		    "'Bitter', serif",
		    "'Cinzel', serif",
		
		    // Mono / Mechanical
		    "'Courier New', monospace",
		    "'IBM Plex Mono', monospace",
		
		    // Accent chaos
		    "'Black Ops One', sans-serif"
		];
	
	        originalText.split('').forEach(function (char) {
	
	            const span = document.createElement('span');
	
	            if (char === ' ') {
	                span.textContent = '\u00A0';
	                scrapbookLayer.appendChild(span);
	                return;
	            }
	
	            span.textContent = char;
	            span.className = 'scrapbook-letter box-black';
	
	            const randomFont = fonts[Math.floor(Math.random() * fonts.length)];
	            const randomRotate = (Math.random() * 6 - 3);
	            const randomSize = 1 + (Math.random() * 0.15 - 0.075);
	            const randomY = (Math.random() * 6 - 3);
	
	            span.style.fontFamily = randomFont;
	            span.style.transform =
	                'rotate(' + randomRotate + 'deg) ' +
	                'translateY(' + randomY + 'px) ' +
	                'scale(' + randomSize + ')';
	
	            const chance = Math.random();
	            if (chance < 0.15) {
	                span.className = 'scrapbook-letter box-white';
	            }
	
	            scrapbookLayer.appendChild(span);
	        });
	
	        el.appendChild(originalLayer);
	        el.appendChild(scrapbookLayer);
	
	        el.dataset.scrapbookInitialized = "true";
	
	        toggleScrapbook(el);
	    });
    }

    applyRansomEffect('.lt-infobox-title');
    applyRansomEffect('.mw-page-title-main');
    applyRansomEffect('h2');
    applyRansomEffect('.mw-logo-wordmark')

});