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 19:19, 13 April 2026 by Novacide (talk | contribs) (Created page with "Any JavaScript here will be loaded for all users on every page load.: * * eve.wiki — MediaWiki:Common.js * EVE-specific JS for Citizen skin * No external dependencies — vanilla JS only: ( function () { 'use strict'; /* ============================================================ 1. WAVE ACCORDION Finds all .evewiki-wave elements and wires up click toggles. To use in wikitext: wrap content in <div class="evewiki-wave"> <div class="ev...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.
/* Any JavaScript here will be loaded for all users on every page load. */
/**
 * eve.wiki — MediaWiki:Common.js
 * EVE-specific JS for Citizen skin
 * No external dependencies — vanilla JS only
 */

( function () {
	'use strict';

	/* ============================================================
	   1. WAVE ACCORDION
	   Finds all .evewiki-wave elements and wires up click toggles.
	   To use in wikitext: wrap content in <div class="evewiki-wave">
	     <div class="evewiki-wave-header">...</div>
	     <div class="evewiki-wave-body">...</div>
	   </div>
	   ============================================================ */
	function initWaveAccordions() {
		var waves = document.querySelectorAll( '.evewiki-wave' );
		waves.forEach( function ( wave ) {
			var header = wave.querySelector( '.evewiki-wave-header' );
			if ( !header ) return;

			// Open first wave by default
			if ( wave === waves[ 0 ] ) {
				wave.classList.add( 'is-open' );
			}

			header.addEventListener( 'click', function () {
				wave.classList.toggle( 'is-open' );
			} );

			// Keyboard support
			header.setAttribute( 'tabindex', '0' );
			header.setAttribute( 'role', 'button' );
			header.addEventListener( 'keydown', function ( e ) {
				if ( e.key === 'Enter' || e.key === ' ' ) {
					e.preventDefault();
					wave.classList.toggle( 'is-open' );
				}
			} );
		} );
	}

	/* ============================================================
	   2. SEARCH HOTKEY
	   Press / to focus search (standard wiki UX pattern)
	   ============================================================ */
	function initSearchHotkey() {
		var searchInput = document.querySelector(
			'#searchInput, .citizen-search__input, input[name="search"]'
		);
		if ( !searchInput ) return;

		document.addEventListener( 'keydown', function ( e ) {
			var tag = document.activeElement ? document.activeElement.tagName : '';
			if ( e.key === '/' && tag !== 'INPUT' && tag !== 'TEXTAREA' ) {
				e.preventDefault();
				searchInput.focus();
				searchInput.select();
			}
			if ( e.key === 'Escape' && document.activeElement === searchInput ) {
				searchInput.blur();
			}
		} );
	}

	/* ============================================================
	   3. ACTIVE NAV HIGHLIGHT
	   Adds .evewiki-nav-active to sidebar links matching current page path
	   ============================================================ */
	function initActiveNav() {
		var path = window.location.pathname;
		var links = document.querySelectorAll(
			'.citizen-nav a, #citizen-sidebar a, .citizen-sidebar a'
		);
		links.forEach( function ( link ) {
			var href = link.getAttribute( 'href' );
			if ( href && href !== '/' && path.indexOf( href ) === 0 ) {
				link.closest( 'li' )?.classList.add( 'citizen-nav__item--active' );
			}
		} );
	}

	/* ============================================================
	   4. INFOBOX IMAGE LAZY EXPAND
	   Click on infobox portrait to open full-size in overlay
	   ============================================================ */
	function initInfoboxPortrait() {
		var portraits = document.querySelectorAll( '.infobox img, .evewiki-portrait img' );
		portraits.forEach( function ( img ) {
			img.style.cursor = 'zoom-in';
			img.addEventListener( 'click', function () {
				var overlay = document.createElement( 'div' );
				overlay.style.cssText = [
					'position:fixed', 'inset:0', 'z-index:99999',
					'background:rgba(0,0,0,.85)',
					'display:flex', 'align-items:center', 'justify-content:center',
					'cursor:zoom-out'
				].join(';');

				var full = document.createElement( 'img' );
				full.src = img.src;
				full.style.cssText = 'max-width:90vw;max-height:90vh;object-fit:contain;border:1px solid #233042;';

				overlay.appendChild( full );
				document.body.appendChild( overlay );

				overlay.addEventListener( 'click', function () {
					document.body.removeChild( overlay );
				} );

				document.addEventListener( 'keydown', function esc( e ) {
					if ( e.key === 'Escape' ) {
						if ( document.body.contains( overlay ) ) {
							document.body.removeChild( overlay );
						}
						document.removeEventListener( 'keydown', esc );
					}
				} );
			} );
		} );
	}

	/* ============================================================
	   5. SORTABLE TABLE ENHANCEMENTS
	   Adds row hover highlight class for wikitables
	   ============================================================ */
	function initTableEnhancements() {
		var tables = document.querySelectorAll( '.wikitable, .evewiki-table' );
		tables.forEach( function ( table ) {
			var rows = table.querySelectorAll( 'tbody tr' );
			rows.forEach( function ( row ) {
				row.addEventListener( 'mouseenter', function () {
					row.style.background = '#1c2230';
				} );
				row.addEventListener( 'mouseleave', function () {
					row.style.background = '';
				} );
			} );
		} );
	}

	/* ============================================================
	   6. COPY-TO-CLIPBOARD FOR CODE BLOCKS
	   Adds a small copy button to <pre> blocks
	   ============================================================ */
	function initCodeCopy() {
		var pres = document.querySelectorAll( '.mw-parser-output pre' );
		pres.forEach( function ( pre ) {
			pre.style.position = 'relative';

			var btn = document.createElement( 'button' );
			btn.textContent = 'Copy';
			btn.style.cssText = [
				'position:absolute', 'top:6px', 'right:6px',
				'background:#131720', 'border:1px solid #233042',
				'color:#93a4b5', 'font-family:Rajdhani,sans-serif',
				'font-size:10px', 'font-weight:600', 'letter-spacing:1px',
				'text-transform:uppercase', 'padding:3px 8px',
				'cursor:pointer', 'transition:color .12s,border-color .12s'
			].join(';');

			btn.addEventListener( 'mouseenter', function () {
				btn.style.color = '#4fc3e8';
				btn.style.borderColor = '#4fc3e8';
			} );
			btn.addEventListener( 'mouseleave', function () {
				btn.style.color = '#93a4b5';
				btn.style.borderColor = '#233042';
			} );

			btn.addEventListener( 'click', function () {
				navigator.clipboard.writeText( pre.textContent.trim() ).then( function () {
					btn.textContent = 'Copied!';
					btn.style.color = '#3acc7a';
					setTimeout( function () {
						btn.textContent = 'Copy';
						btn.style.color = '#93a4b5';
					}, 2000 );
				} );
			} );

			pre.appendChild( btn );
		} );
	}

	/* ============================================================
	   7. HOMEPAGE-ONLY: CATEGORY CARD HOVER GLOW
	   Only runs on Main_Page
	   ============================================================ */
	function initHomepageEffects() {
		if ( mw.config.get( 'wgPageName' ) !== 'Main_Page' ) return;

		var cards = document.querySelectorAll( '.evewiki-hp-card' );
		cards.forEach( function ( card ) {
			card.addEventListener( 'mouseenter', function () {
				card.style.borderTopColor = '#4fc3e8';
				card.style.boxShadow = '0 0 20px rgba(79,195,232,.08)';
			} );
			card.addEventListener( 'mouseleave', function () {
				card.style.borderTopColor = '';
				card.style.boxShadow = '';
			} );
		} );
	}

	/* ============================================================
	   INIT — run after DOM ready
	   ============================================================ */
	function init() {
		initWaveAccordions();
		initSearchHotkey();
		initActiveNav();
		initInfoboxPortrait();
		initTableEnhancements();
		initCodeCopy();
		initHomepageEffects();
	}

	if ( document.readyState === 'loading' ) {
		document.addEventListener( 'DOMContentLoaded', init );
	} else {
		init();
	}

}() );