MediaWiki:Common.js

From Mona Mahall / Asli Serbest
Revision as of 09:15, 19 July 2026 by Cmfiy (talk | contribs) (Created page with "Any JavaScript here will be loaded for all users on every page load.: /* ========================================================================= aslimona.xyz — inline lightbox for gallery / thumbnail images Paste this ENTIRE file into: Special:MyLanguage/MediaWiki:Common.js (as an admin: go to https://aslimona.xyz/index.php/MediaWiki:Common.js click "Edit", paste, save.) Fixes the "click image -> File page -> back -> click next" problem: click...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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. */
/* =========================================================================
   aslimona.xyz — inline lightbox for gallery / thumbnail images
   Paste this ENTIRE file into: Special:MyLanguage/MediaWiki:Common.js
   (as an admin: go to https://aslimona.xyz/index.php/MediaWiki:Common.js
   click "Edit", paste, save.)

   Fixes the "click image -> File page -> back -> click next" problem:
   clicking any gallery or thumbnail image now opens a big in-page viewer
   with Prev/Next arrows and keyboard navigation, on every article,
   automatically. No per-page changes needed.
   ========================================================================= */
( function () {
	'use strict';

	function onReady( fn ) {
		if ( document.readyState !== 'loading' ) {
			fn();
		} else {
			document.addEventListener( 'DOMContentLoaded', fn );
		}
	}

	// Convert a MediaWiki thumbnail URL to the full-resolution original URL.
	// e.g. .../images/a/a1/Foo.jpg/800px-Foo.jpg           -> .../images/a/a1/Foo.jpg
	//      .../images/thumb/a/a1/Foo.jpg/800px-Foo.jpg     -> .../images/a/a1/Foo.jpg
	function originalFromThumb( src ) {
		var m = src.match( /^(.*\/thumb\/([0-9a-f])\/([0-9a-f]{2})\/([^\/]+))\/\d+px-[^\/]+$/i );
		if ( m ) {
			return src.replace( '/thumb/', '/' ).replace( /\/\d+px-[^\/]+$/, '' );
		}
		var m2 = src.match( /^(.*\/[0-9a-f]\/[0-9a-f]{2}\/[^\/]+)\/\d+px-[^\/]+$/i );
		if ( m2 ) {
			return m2[ 1 ];
		}
		return src;
	}

	function injectStyles() {
		var css = [
			'.aslimona-lb-overlay{position:fixed;inset:0;background:rgba(6,6,7,0.94);',
			'z-index:100000;display:flex;flex-direction:column;align-items:center;',
			'justify-content:center;padding:4vh 4vw;box-sizing:border-box;}',
			'.aslimona-lb-overlay[hidden]{display:none;}',
			'.aslimona-lb-img{max-width:92vw;max-height:80vh;object-fit:contain;',
			'border:1px solid #3a372f;background:#111;}',
			'.aslimona-lb-caption{color:#a89f8c;font-family:"IBM Plex Mono","Courier New",monospace;',
			'font-size:0.8rem;margin-top:14px;text-align:center;max-width:80vw;}',
			'.aslimona-lb-counter{color:#6e6858;font-family:"IBM Plex Mono","Courier New",monospace;',
			'font-size:0.7rem;margin-top:4px;letter-spacing:0.08em;}',
			'.aslimona-lb-close,.aslimona-lb-prev,.aslimona-lb-next{position:fixed;',
			'background:transparent;border:1px solid #3a372f;color:#e9e4d8;',
			'font-family:Georgia,serif;cursor:pointer;transition:background .2s,color .2s;}',
			'.aslimona-lb-close:hover,.aslimona-lb-prev:hover,.aslimona-lb-next:hover{',
			'background:#c9a15a;color:#0c0c0d;}',
			'.aslimona-lb-close{top:24px;right:24px;width:40px;height:40px;font-size:20px;',
			'line-height:38px;text-align:center;}',
			'.aslimona-lb-prev,.aslimona-lb-next{top:50%;transform:translateY(-50%);',
			'width:48px;height:64px;font-size:22px;}',
			'.aslimona-lb-prev{left:16px;}',
			'.aslimona-lb-next{right:16px;}',
			'@media (max-width:600px){',
			'.aslimona-lb-prev,.aslimona-lb-next{width:36px;height:52px;font-size:18px;}',
			'.aslimona-lb-close{width:34px;height:34px;font-size:17px;line-height:32px;}',
			'}'
		].join( '' );
		var style = document.createElement( 'style' );
		style.textContent = css;
		document.head.appendChild( style );
	}

	function buildOverlay() {
		var overlay = document.createElement( 'div' );
		overlay.className = 'aslimona-lb-overlay';
		overlay.hidden = true;

		var img = document.createElement( 'img' );
		img.className = 'aslimona-lb-img';
		overlay.appendChild( img );

		var caption = document.createElement( 'div' );
		caption.className = 'aslimona-lb-caption';
		overlay.appendChild( caption );

		var counter = document.createElement( 'div' );
		counter.className = 'aslimona-lb-counter';
		overlay.appendChild( counter );

		var closeBtn = document.createElement( 'button' );
		closeBtn.className = 'aslimona-lb-close';
		closeBtn.setAttribute( 'aria-label', 'Close' );
		closeBtn.textContent = '×';
		overlay.appendChild( closeBtn );

		var prevBtn = document.createElement( 'button' );
		prevBtn.className = 'aslimona-lb-prev';
		prevBtn.setAttribute( 'aria-label', 'Previous image' );
		prevBtn.textContent = '‹';
		overlay.appendChild( prevBtn );

		var nextBtn = document.createElement( 'button' );
		nextBtn.className = 'aslimona-lb-next';
		nextBtn.setAttribute( 'aria-label', 'Next image' );
		nextBtn.textContent = '›';
		overlay.appendChild( nextBtn );

		document.body.appendChild( overlay );

		return {
			overlay: overlay,
			img: img,
			caption: caption,
			counter: counter,
			closeBtn: closeBtn,
			prevBtn: prevBtn,
			nextBtn: nextBtn
		};
	}

	onReady( function () {
		var content = document.querySelector( '.mw-parser-output' );
		if ( !content ) {
			return;
		}

		// Collect every lightbox-able image link on the page, in document order:
		// - <gallery> thumbnails: li.gallerybox a.image
		// - inline [[File:x|thumb]]: div.thumb a.image
		var links = Array.prototype.slice.call(
			content.querySelectorAll( 'li.gallerybox a.image, div.thumb a.image, a.image' )
		);
		if ( !links.length ) {
			return;
		}
		// de-duplicate while preserving order
		var seen = {};
		links = links.filter( function ( a ) {
			if ( seen[ a.href ] ) {
				return false;
			}
			seen[ a.href ] = true;
			return true;
		} );

		var items = links.map( function ( a ) {
			var thumbImg = a.querySelector( 'img' );
			var full = thumbImg ? originalFromThumb( thumbImg.src ) : a.href;
			var captionEl = null;
			var gallerytext = a.closest( 'li.gallerybox' );
			if ( gallerytext ) {
				captionEl = gallerytext.querySelector( '.gallerytext' );
			} else {
				var thumbinner = a.closest( '.thumbinner' );
				if ( thumbinner ) {
					captionEl = thumbinner.querySelector( '.thumbcaption' );
				}
			}
			return {
				full: full,
				caption: captionEl ? captionEl.textContent.trim() : ( thumbImg ? thumbImg.alt : '' ),
				link: a
			};
		} );

		injectStyles();
		var ui = buildOverlay();
		var currentIndex = 0;

		function show( index ) {
			currentIndex = ( index + items.length ) % items.length;
			var item = items[ currentIndex ];
			ui.img.src = item.full;
			ui.caption.textContent = item.caption || '';
			ui.counter.textContent = ( currentIndex + 1 ) + ' / ' + items.length;
		}

		function open( index ) {
			show( index );
			ui.overlay.hidden = false;
			document.body.style.overflow = 'hidden';
		}

		function close() {
			ui.overlay.hidden = true;
			document.body.style.overflow = '';
		}

		items.forEach( function ( item, index ) {
			item.link.addEventListener( 'click', function ( ev ) {
				// let ctrl/cmd/shift/middle-clicks behave normally (open in new tab etc.)
				if ( ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.button === 1 ) {
					return;
				}
				ev.preventDefault();
				open( index );
			} );
		} );

		ui.closeBtn.addEventListener( 'click', close );
		ui.prevBtn.addEventListener( 'click', function () {
			show( currentIndex - 1 );
		} );
		ui.nextBtn.addEventListener( 'click', function () {
			show( currentIndex + 1 );
		} );
		ui.overlay.addEventListener( 'click', function ( ev ) {
			if ( ev.target === ui.overlay ) {
				close();
			}
		} );
		document.addEventListener( 'keydown', function ( ev ) {
			if ( ui.overlay.hidden ) {
				return;
			}
			if ( ev.key === 'Escape' ) {
				close();
			} else if ( ev.key === 'ArrowLeft' ) {
				show( currentIndex - 1 );
			} else if ( ev.key === 'ArrowRight' ) {
				show( currentIndex + 1 );
			}
		} );
	} );
}() );