MediaWiki:Common.js: Difference between revisions

From Legendary Hardcore Wiki
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
Line 3: Line 3:
'use strict';
'use strict';
   
   
var CONFIG = {
var DEFAULTS = {
category: 'Groups',
minBytes: 3000,
minBytes: 3000,
dailyStable: false,
dailyStable: true,
  blurbChars: 320
  blurbChars: 200
};
};
   
   
function init() {
function init() {
var container = document.getElementById( 'lhc-random-article' );
var panels = document.querySelectorAll( '.lhc-article' );
if ( !container ) {
if ( !panels.length ) {
return;
return;
}
}
var api = new mw.Api();
Array.prototype.forEach.call( panels, function ( panel ) {
start( api, panel );
} );
}
function start( api, panel ) {
var page = resolvePage( panel );
if ( page ) {
return loadArticle( api, panel, page ).catch( function () {
} );
}
var category = panel.getAttribute( 'data-category' );
if ( !category ) {
return;
}
return pickRandom( api, panel, category ).catch( function () {} );
}
function resolvePage( panel ) {
var explicit = panel.getAttribute( 'data-page' );
if ( explicit ) {
return explicit;
}
var link = panel.querySelector( 'a[title]' );
if ( !link ) {
return null;
}
var title = link.getAttribute( 'title' );
if ( /\(page does not exist\)$/.test( title ) ) {
return null;
}
return title;
}
   
   
var api = new mw.Api();
function pickRandom( api, panel, category ) {
var minBytes = parseInt( panel.getAttribute( 'data-min-bytes' ), 10 );
if ( isNaN( minBytes ) ) {
minBytes = DEFAULTS.minBytes;
}
   
   
api.get( {
return api.get( {
action: 'query',
action: 'query',
generator: 'categorymembers',
generator: 'categorymembers',
gcmtitle: 'Category:' + CONFIG.category,
gcmtitle: 'Category:' + category,
gcmnamespace: 0,
gcmnamespace: 0,
gcmtype: 'page',
gcmtype: 'page',
Line 31: Line 70:
   
   
var candidates = pages.filter( function ( p ) {
var candidates = pages.filter( function ( p ) {
return p.length >= CONFIG.minBytes;
return p.length >= minBytes;
} ).sort( function ( a, b ) {
} ).sort( function ( a, b ) {
// Stable order matters for the daily-seed pick.
// Stable order matters for the daily-seed pick.
Line 38: Line 77:
   
   
if ( !candidates.length ) {
if ( !candidates.length ) {
return fail( container );
mw.log.warn( '[lhc-article] no candidates in ' + category );
return;
}
}
   
   
var pick = candidates[ pickIndex( candidates.length ) ];
return loadArticle(
return loadArticle( api, container, pick.title );
api, panel, candidates[ pickIndex( candidates.length ) ].title
} ).catch( function () {
);
fail( container );
} );
} );
}
}
   
   
function pickIndex( n ) {
function pickIndex( n ) {
if ( !CONFIG.dailyStable ) {
if ( !DEFAULTS.dailyStable ) {
return Math.floor( Math.random() * n );
return Math.floor( Math.random() * n );
}
}
var d = new Date();
var d = new Date();
var key = '' + d.getUTCFullYear() + d.getUTCMonth() + d.getUTCDate();
var key = d.getUTCFullYear() + '-' +
( d.getUTCMonth() + 1 ) + '-' + d.getUTCDate();
var h = 2166136261;
var h = 2166136261;
for ( var i = 0; i < key.length; i++ ) {
for ( var i = 0; i < key.length; i++ ) {
Line 61: Line 101:
}
}
   
   
function loadArticle( api, container, title ) {
function loadArticle( api, panel, title ) {
return api.get( {
return api.get( {
action: 'parse',
action: 'parse',
Line 72: Line 112:
var html = data.parse && data.parse.text;
var html = data.parse && data.parse.text;
if ( !html ) {
if ( !html ) {
return fail( container );
return;
}
}
var doc = new DOMParser().parseFromString( html, 'text/html' );
var doc = new DOMParser().parseFromString( html, 'text/html' );
render( panel, {
render( container, {
// Use the resolved title, so redirects show their target.
title: title,
title: data.parse.title || title,
image: extractImage( doc ),
image: extractImage( doc ),
blurb: extractBlurb( doc )
blurb: extractBlurb( doc )
Line 97: Line 136:
return null;
return null;
}
}
// Parser output uses protocol-relative or root-relative paths.
if ( src.indexOf( '//' ) === 0 ) {
if ( src.indexOf( '//' ) === 0 ) {
return location.protocol + src;
return location.protocol + src;
Line 106: Line 144:
return src;
return src;
}
}
 
function extractBlurb( doc ) {
function extractBlurb( doc ) {
var junk = doc.querySelectorAll(
var junk = doc.querySelectorAll(
Line 120: Line 158:
var text = paras[ i ].textContent.replace( /\s+/g, ' ' ).trim();
var text = paras[ i ].textContent.replace( /\s+/g, ' ' ).trim();
if ( text.length > 40 ) {
if ( text.length > 40 ) {
return truncate( text, CONFIG.blurbChars );
return truncate( text, DEFAULTS.blurbChars );
}
}
}
}
Line 135: Line 173:
}
}
   
   
function render( container, article ) {
function render( panel, article ) {
var href = mw.util.getUrl( article.title );
var href = mw.util.getUrl( article.title );
   
   
Line 147: Line 185:
img.src = article.image;
img.src = article.image;
img.alt = article.title;
img.alt = article.title;
img.className = 'lhc-ra-image';
link.appendChild( img );
link.appendChild( img );
wrap.appendChild( link );
wrap.appendChild( link );
}
}
var body = document.createElement( 'div' );
body.className = 'lhc-ra-body';
   
   
var heading = document.createElement( 'div' );
var heading = document.createElement( 'div' );
Line 161: Line 195:
titleLink.textContent = article.title;
titleLink.textContent = article.title;
heading.appendChild( titleLink );
heading.appendChild( titleLink );
body.appendChild( heading );
wrap.appendChild( heading );
   
   
if ( article.blurb ) {
if ( article.blurb ) {
Line 167: Line 201:
p.className = 'lhc-ra-blurb';
p.className = 'lhc-ra-blurb';
p.textContent = article.blurb;
p.textContent = article.blurb;
body.appendChild( p );
wrap.appendChild( p );
}
}
   
   
Line 174: Line 208:
more.className = 'lhc-ra-more';
more.className = 'lhc-ra-more';
more.textContent = 'Read the full article →';
more.textContent = 'Read the full article →';
body.appendChild( more );
wrap.appendChild( more );
   
   
wrap.appendChild( body );
panel.textContent = '';
panel.appendChild( wrap );
container.textContent = '';
container.appendChild( wrap );
}
 
function fail( container ) {
var a = document.createElement( 'a' );
a.href = mw.util.getUrl(
'Special:RandomInCategory/' + CONFIG.category
);
a.textContent = 'Show me a random article';
container.textContent = '';
container.appendChild( a );
}
}
   
   

Revision as of 04:33, 14 August 2026

/* Any JavaScript here will be loaded for all users on every page load. */
( function () {
	'use strict';
 
	var DEFAULTS = {
		minBytes: 3000,
 		dailyStable: true,
 		blurbChars: 200
	};
 
	function init() {
		var panels = document.querySelectorAll( '.lhc-article' );
		if ( !panels.length ) {
			return;
		}
		var api = new mw.Api();
		Array.prototype.forEach.call( panels, function ( panel ) {
			start( api, panel );
		} );
	}
 
	function start( api, panel ) {
		var page = resolvePage( panel );
 
		if ( page ) {
			return loadArticle( api, panel, page ).catch( function () {
			} );
		}
 
		var category = panel.getAttribute( 'data-category' );
		if ( !category ) {
			return;
		}
		return pickRandom( api, panel, category ).catch( function () {} );
	}
 
	function resolvePage( panel ) {
		var explicit = panel.getAttribute( 'data-page' );
		if ( explicit ) {
			return explicit;
		}
		var link = panel.querySelector( 'a[title]' );
		if ( !link ) {
			return null;
		}
		var title = link.getAttribute( 'title' );
		if ( /\(page does not exist\)$/.test( title ) ) {
			return null;
		}
		return title;
	}
 
	function pickRandom( api, panel, category ) {
		var minBytes = parseInt( panel.getAttribute( 'data-min-bytes' ), 10 );
		if ( isNaN( minBytes ) ) {
			minBytes = DEFAULTS.minBytes;
		}
 
		return api.get( {
			action: 'query',
			generator: 'categorymembers',
			gcmtitle: 'Category:' + category,
			gcmnamespace: 0,
			gcmtype: 'page',
			gcmlimit: 'max',
			prop: 'info',
			formatversion: 2
		} ).then( function ( data ) {
			var pages = ( data.query && data.query.pages ) || [];
 
			var candidates = pages.filter( function ( p ) {
				return p.length >= minBytes;
			} ).sort( function ( a, b ) {
				// Stable order matters for the daily-seed pick.
				return a.title.localeCompare( b.title );
			} );
 
			if ( !candidates.length ) {
				mw.log.warn( '[lhc-article] no candidates in ' + category );
				return;
			}
 
			return loadArticle(
				api, panel, candidates[ pickIndex( candidates.length ) ].title
			);
		} );
	}
 
	function pickIndex( n ) {
		if ( !DEFAULTS.dailyStable ) {
			return Math.floor( Math.random() * n );
		}
		var d = new Date();
		var key = d.getUTCFullYear() + '-' +
			( d.getUTCMonth() + 1 ) + '-' + d.getUTCDate();
		var h = 2166136261;
		for ( var i = 0; i < key.length; i++ ) {
			h = ( h ^ key.charCodeAt( i ) ) * 16777619 >>> 0;
		}
		return h % n;
	}
 
	function loadArticle( api, panel, title ) {
		return api.get( {
			action: 'parse',
			page: title,
			prop: 'text',
			section: 0,
			redirects: 1,
			formatversion: 2
		} ).then( function ( data ) {
			var html = data.parse && data.parse.text;
			if ( !html ) {
				return;
			}
			var doc = new DOMParser().parseFromString( html, 'text/html' );
			render( panel, {
				// Use the resolved title, so redirects show their target.
				title: data.parse.title || title,
				image: extractImage( doc ),
				blurb: extractBlurb( doc )
			} );
		} );
	}
 
	function extractImage( doc ) {
		var img = doc.querySelector(
			'.infobox img, .infobox-image img, table.infobox img'
		) || doc.querySelector( 'img' );
 
		if ( !img ) {
			return null;
		}
		var src = img.getAttribute( 'src' );
		if ( !src ) {
			return null;
		}
		if ( src.indexOf( '//' ) === 0 ) {
			return location.protocol + src;
		}
		if ( src.indexOf( '/' ) === 0 ) {
			return location.origin + src;
		}
		return src;
	}
 
	function extractBlurb( doc ) {
		var junk = doc.querySelectorAll(
			'table, .infobox, .navbox, .hatnote, .thumb, ' +
			'sup.reference, .mw-editsection, .noexcerpt, style'
		);
		Array.prototype.forEach.call( junk, function ( el ) {
			el.parentNode.removeChild( el );
		} );
 
		var paras = doc.querySelectorAll( 'p' );
		for ( var i = 0; i < paras.length; i++ ) {
			var text = paras[ i ].textContent.replace( /\s+/g, ' ' ).trim();
			if ( text.length > 40 ) {
				return truncate( text, DEFAULTS.blurbChars );
			}
		}
		return null;
	}
 
	function truncate( text, max ) {
		if ( text.length <= max ) {
			return text;
		}
		var cut = text.slice( 0, max );
		var space = cut.lastIndexOf( ' ' );
		return ( space > 0 ? cut.slice( 0, space ) : cut ) + '…';
	}
 
	function render( panel, article ) {
		var href = mw.util.getUrl( article.title );
 
		var wrap = document.createElement( 'div' );
		wrap.className = 'lhc-ra';
 
		if ( article.image ) {
			var link = document.createElement( 'a' );
			link.href = href;
			var img = document.createElement( 'img' );
			img.src = article.image;
			img.alt = article.title;
			link.appendChild( img );
			wrap.appendChild( link );
		}
 
		var heading = document.createElement( 'div' );
		heading.className = 'lhc-ra-title';
		var titleLink = document.createElement( 'a' );
		titleLink.href = href;
		titleLink.textContent = article.title;
		heading.appendChild( titleLink );
		wrap.appendChild( heading );
 
		if ( article.blurb ) {
			var p = document.createElement( 'p' );
			p.className = 'lhc-ra-blurb';
			p.textContent = article.blurb;
			wrap.appendChild( p );
		}
 
		var more = document.createElement( 'a' );
		more.href = href;
		more.className = 'lhc-ra-more';
		more.textContent = 'Read the full article →';
		wrap.appendChild( more );
 
		panel.textContent = '';
		panel.appendChild( wrap );
	}
 
	mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
		if ( document.readyState !== 'loading' ) {
			init();
		} else {
			document.addEventListener( 'DOMContentLoaded', init );
		}
	} );
 
}() );