MediaWiki:Common.js
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 );
}
} );
} );
}() );