MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
mNo edit summary |
mNo edit summary |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
( function () { | ( function () { | ||
'use strict'; | 'use strict'; | ||
var DEFAULTS = { | var DEFAULTS = { | ||
minBytes: | minBytes: 1500, | ||
dailyStable: false, | dailyStable: false, | ||
blurbChars: 200, | |||
minImageWidth: 80, | |||
maxPages: 6 | |||
}; | }; | ||
var FURNITURE_SELECTOR = '.badge'; | |||
function init() { | function init() { | ||
var panels = document.querySelectorAll( '.lhc-article' ); | var panels = document.querySelectorAll( '.lhc-article' ); | ||
| Line 19: | Line 22: | ||
} ); | } ); | ||
} | } | ||
function start( api, panel ) { | function start( api, panel ) { | ||
if ( panel.hasAttribute( 'data-random' ) || | if ( panel.hasAttribute( 'data-random' ) || | ||
panel.hasAttribute( 'data-category' ) ) { | panel.hasAttribute( 'data-category' ) ) { | ||
return pickRandom( api, panel ).catch( function () { | return pickRandom( api, panel ).catch( function () {} ); | ||
} | } | ||
var page = resolvePage( panel ); | var page = resolvePage( panel ); | ||
if ( page ) { | if ( page ) { | ||
| Line 32: | Line 34: | ||
} | } | ||
} | } | ||
function resolvePage( panel ) { | function resolvePage( panel ) { | ||
var explicit = panel.getAttribute( 'data-page' ); | var explicit = panel.getAttribute( 'data-page' ); | ||
| Line 48: | Line 50: | ||
return title; | return title; | ||
} | } | ||
function pickRandom( api, panel ) { | function pickRandom( api, panel ) { | ||
var categories = splitList( panel.getAttribute( 'data-category' ) ); | var categories = splitList( panel.getAttribute( 'data-category' ) ); | ||
var exclude = panel.getAttribute( 'data-exclude-category' ); | var exclude = panel.getAttribute( 'data-exclude-category' ); | ||
var minBytes = parseInt( panel.getAttribute( 'data-min-bytes' ), 10 ); | var minBytes = parseInt( panel.getAttribute( 'data-min-bytes' ), 10 ); | ||
if ( isNaN( minBytes ) ) { | if ( isNaN( minBytes ) ) { | ||
minBytes = DEFAULTS.minBytes; | minBytes = DEFAULTS.minBytes; | ||
} | } | ||
var listing = categories.length ? | var listing = categories.length ? | ||
Promise.all( categories.map( function ( c ) { | Promise.all( categories.map( function ( c ) { | ||
| Line 63: | Line 65: | ||
} ) ).then( merge ) : | } ) ).then( merge ) : | ||
listAll( api, minBytes ); | listAll( api, minBytes ); | ||
return Promise.all( [ listing, listExcluded( api, exclude ) ] ) | return Promise.all( [ listing, listExcluded( api, exclude ) ] ) | ||
.then( function ( results ) { | .then( function ( results ) { | ||
var titles = results[ 0 ]; | var titles = results[ 0 ]; | ||
var skip = results[ 1 ]; | var skip = results[ 1 ]; | ||
var candidates = titles.filter( function ( t ) { | var candidates = titles.filter( function ( t ) { | ||
return !skip[ t ]; | return !skip[ t ]; | ||
| Line 74: | Line 76: | ||
return a.localeCompare( b ); | return a.localeCompare( b ); | ||
} ); | } ); | ||
if ( !candidates.length ) { | if ( !candidates.length ) { | ||
mw.log.warn( '[lhc-article] no candidates found' ); | mw.log.warn( '[lhc-article] no candidates found' ); | ||
return; | return; | ||
} | } | ||
return loadArticle( | return loadArticle( | ||
api, panel, candidates[ pickIndex( candidates.length ) ] | api, panel, candidates[ pickIndex( candidates.length ) ] | ||
| Line 85: | Line 87: | ||
} ); | } ); | ||
} | } | ||
function splitList( value ) { | function splitList( value ) { | ||
if ( !value ) { | if ( !value ) { | ||
| Line 96: | Line 98: | ||
} ); | } ); | ||
} | } | ||
function merge( lists ) { | function merge( lists ) { | ||
var seen = {}; | var seen = {}; | ||
| Line 110: | Line 112: | ||
return out; | return out; | ||
} | } | ||
function listCategory( api, category, minBytes ) { | function listCategory( api, category, minBytes ) { | ||
return api.get( { | return api.get( { | ||
| Line 136: | Line 138: | ||
} ); | } ); | ||
} | } | ||
function listAll( api, minBytes ) { | function listAll( api, minBytes ) { | ||
var titles = []; | var titles = []; | ||
function fetch( cont, round ) { | function fetch( cont, round ) { | ||
var params = { | var params = { | ||
| Line 155: | Line 157: | ||
} ); | } ); | ||
} | } | ||
return api.get( params ).then( function ( data ) { | return api.get( params ).then( function ( data ) { | ||
( ( data.query && data.query.pages ) || [] ) | ( ( data.query && data.query.pages ) || [] ) | ||
| Line 161: | Line 163: | ||
titles.push( p.title ); | titles.push( p.title ); | ||
} ); | } ); | ||
if ( data.continue && round < DEFAULTS.maxPages ) { | if ( data.continue && round < DEFAULTS.maxPages ) { | ||
return fetch( data.continue, round + 1 ); | return fetch( data.continue, round + 1 ); | ||
| Line 168: | Line 170: | ||
} ); | } ); | ||
} | } | ||
return fetch( null, 1 ); | return fetch( null, 1 ); | ||
} | } | ||
function listExcluded( api, category ) { | function listExcluded( api, category ) { | ||
if ( !category ) { | if ( !category ) { | ||
| Line 194: | Line 196: | ||
} ); | } ); | ||
} | } | ||
function pickIndex( n ) { | function pickIndex( n ) { | ||
if ( !DEFAULTS.dailyStable ) { | if ( !DEFAULTS.dailyStable ) { | ||
| Line 208: | Line 210: | ||
return h % n; | return h % n; | ||
} | } | ||
function loadArticle( api, panel, title ) { | function loadArticle( api, panel, title ) { | ||
return api.get( { | return api.get( { | ||
| Line 223: | Line 225: | ||
} | } | ||
var doc = new DOMParser().parseFromString( html, 'text/html' ); | var doc = new DOMParser().parseFromString( html, 'text/html' ); | ||
stripFurniture( doc ); | |||
render( panel, { | render( panel, { | ||
title: data.parse.title || title, | title: data.parse.title || title, | ||
image: extractImage( doc ), | image: extractImage( doc ), | ||
| Line 231: | Line 235: | ||
} ); | } ); | ||
} | } | ||
function stripFurniture( doc ) { | |||
var junk = doc.querySelectorAll( FURNITURE_SELECTOR ); | |||
Array.prototype.forEach.call( junk, function ( el ) { | |||
if ( el.parentNode ) { | |||
el.parentNode.removeChild( el ); | |||
} | |||
} ); | |||
} | |||
function extractImage( doc ) { | function extractImage( doc ) { | ||
var | var all = doc.querySelectorAll( 'img' ); | ||
var img = null; | |||
for ( var i = 0; i < all.length; i++ ) { | |||
if ( imageWidth( all[ i ] ) >= DEFAULTS.minImageWidth ) { | |||
img = all[ i ]; | |||
break; | |||
} | |||
} | |||
if ( !img ) { | if ( !img ) { | ||
return null; | return null; | ||
| Line 252: | Line 271: | ||
return src; | return src; | ||
} | } | ||
function imageWidth( img ) { | |||
return parseInt( img.getAttribute( 'width' ), 10 ) || 0; | |||
} | |||
function extractBlurb( doc ) { | function extractBlurb( doc ) { | ||
var | var tables = doc.querySelectorAll( 'table, .infobox, .thumb' ); | ||
Array.prototype.forEach.call( tables, function ( el ) { | |||
if ( el.parentNode ) { | |||
el.parentNode.removeChild( el ); | |||
Array.prototype.forEach.call( | } | ||
el.parentNode.removeChild( el ); | |||
} ); | } ); | ||
var paras = doc.querySelectorAll( 'p' ); | var paras = doc.querySelectorAll( 'p' ); | ||
for ( var i = 0; i < paras.length; i++ ) { | for ( var i = 0; i < paras.length; i++ ) { | ||
| Line 271: | Line 293: | ||
return null; | return null; | ||
} | } | ||
function truncate( text, max ) { | function truncate( text, max ) { | ||
if ( text.length <= max ) { | if ( text.length <= max ) { | ||
| Line 280: | Line 302: | ||
return ( space > 0 ? cut.slice( 0, space ) : cut ) + '…'; | return ( space > 0 ? cut.slice( 0, space ) : cut ) + '…'; | ||
} | } | ||
function render( panel, article ) { | function render( panel, article ) { | ||
var href = mw.util.getUrl( article.title ); | var href = mw.util.getUrl( article.title ); | ||
var wrap = document.createElement( 'div' ); | var wrap = document.createElement( 'div' ); | ||
wrap.className = 'lhc-ra'; | wrap.className = 'lhc-ra'; | ||
if ( article.image ) { | if ( article.image ) { | ||
var link = document.createElement( 'a' ); | var link = document.createElement( 'a' ); | ||
link.href = href; | link.href = href; | ||
link.className = 'lhc-ra-img'; | |||
var img = document.createElement( 'img' ); | var img = document.createElement( 'img' ); | ||
img.src = article.image; | img.src = article.image; | ||
| Line 296: | Line 319: | ||
wrap.appendChild( link ); | wrap.appendChild( link ); | ||
} | } | ||
var heading = document.createElement( 'div' ); | var heading = document.createElement( 'div' ); | ||
heading.className = 'lhc-ra-title'; | heading.className = 'lhc-ra-title'; | ||
| Line 304: | Line 327: | ||
heading.appendChild( titleLink ); | heading.appendChild( titleLink ); | ||
wrap.appendChild( heading ); | wrap.appendChild( heading ); | ||
if ( article.blurb ) { | if ( article.blurb ) { | ||
var p = document.createElement( 'p' ); | var p = document.createElement( 'p' ); | ||
| Line 311: | Line 334: | ||
wrap.appendChild( p ); | wrap.appendChild( p ); | ||
} | } | ||
var more = document.createElement( 'a' ); | var more = document.createElement( 'a' ); | ||
more.href = href; | more.href = href; | ||
| Line 317: | Line 340: | ||
more.textContent = 'Read the full article →'; | more.textContent = 'Read the full article →'; | ||
wrap.appendChild( more ); | wrap.appendChild( more ); | ||
panel.textContent = ''; | panel.textContent = ''; | ||
panel.appendChild( wrap ); | panel.appendChild( wrap ); | ||
} | } | ||
mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () { | mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () { | ||
if ( document.readyState !== 'loading' ) { | if ( document.readyState !== 'loading' ) { | ||
| Line 329: | Line 352: | ||
} | } | ||
} ); | } ); | ||
}() ); | }() ); | ||
Latest revision as of 06:51, 15 August 2026
( function () {
'use strict';
var DEFAULTS = {
minBytes: 1500,
dailyStable: false,
blurbChars: 200,
minImageWidth: 80,
maxPages: 6
};
var FURNITURE_SELECTOR = '.badge';
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 ) {
if ( panel.hasAttribute( 'data-random' ) ||
panel.hasAttribute( 'data-category' ) ) {
return pickRandom( api, panel ).catch( function () {} );
}
var page = resolvePage( panel );
if ( page ) {
return loadArticle( api, panel, page ).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 ) {
var categories = splitList( panel.getAttribute( 'data-category' ) );
var exclude = panel.getAttribute( 'data-exclude-category' );
var minBytes = parseInt( panel.getAttribute( 'data-min-bytes' ), 10 );
if ( isNaN( minBytes ) ) {
minBytes = DEFAULTS.minBytes;
}
var listing = categories.length ?
Promise.all( categories.map( function ( c ) {
return listCategory( api, c, minBytes );
} ) ).then( merge ) :
listAll( api, minBytes );
return Promise.all( [ listing, listExcluded( api, exclude ) ] )
.then( function ( results ) {
var titles = results[ 0 ];
var skip = results[ 1 ];
var candidates = titles.filter( function ( t ) {
return !skip[ t ];
} ).sort( function ( a, b ) {
return a.localeCompare( b );
} );
if ( !candidates.length ) {
mw.log.warn( '[lhc-article] no candidates found' );
return;
}
return loadArticle(
api, panel, candidates[ pickIndex( candidates.length ) ]
);
} );
}
function splitList( value ) {
if ( !value ) {
return [];
}
return value.split( '|' ).map( function ( s ) {
return s.trim();
} ).filter( function ( s ) {
return s.length > 0;
} );
}
function merge( lists ) {
var seen = {};
var out = [];
lists.forEach( function ( list ) {
list.forEach( function ( title ) {
if ( !seen[ title ] ) {
seen[ title ] = true;
out.push( title );
}
} );
} );
return out;
}
function listCategory( api, category, 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 ) || [];
if ( !pages.length ) {
mw.log.warn( '[lhc-article] empty category: ' + category );
}
return pages.filter( function ( p ) {
return p.length >= minBytes;
} ).map( function ( p ) {
return p.title;
} );
} ).catch( function () {
mw.log.warn( '[lhc-article] lookup failed: ' + category );
return [];
} );
}
function listAll( api, minBytes ) {
var titles = [];
function fetch( cont, round ) {
var params = {
action: 'query',
generator: 'allpages',
gapnamespace: 0,
gapfilterredir: 'nonredirects',
gapminsize: minBytes,
gaplimit: 'max',
formatversion: 2
};
if ( cont ) {
Object.keys( cont ).forEach( function ( k ) {
params[ k ] = cont[ k ];
} );
}
return api.get( params ).then( function ( data ) {
( ( data.query && data.query.pages ) || [] )
.forEach( function ( p ) {
titles.push( p.title );
} );
if ( data.continue && round < DEFAULTS.maxPages ) {
return fetch( data.continue, round + 1 );
}
return titles;
} );
}
return fetch( null, 1 );
}
function listExcluded( api, category ) {
if ( !category ) {
return Promise.resolve( {} );
}
return api.get( {
action: 'query',
list: 'categorymembers',
cmtitle: 'Category:' + category,
cmnamespace: 0,
cmlimit: 'max',
formatversion: 2
} ).then( function ( data ) {
var skip = {};
( ( data.query && data.query.categorymembers ) || [] )
.forEach( function ( p ) {
skip[ p.title ] = true;
} );
return skip;
} ).catch( function () {
return {};
} );
}
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' );
stripFurniture( doc );
render( panel, {
title: data.parse.title || title,
image: extractImage( doc ),
blurb: extractBlurb( doc )
} );
} );
}
function stripFurniture( doc ) {
var junk = doc.querySelectorAll( FURNITURE_SELECTOR );
Array.prototype.forEach.call( junk, function ( el ) {
if ( el.parentNode ) {
el.parentNode.removeChild( el );
}
} );
}
function extractImage( doc ) {
var all = doc.querySelectorAll( 'img' );
var img = null;
for ( var i = 0; i < all.length; i++ ) {
if ( imageWidth( all[ i ] ) >= DEFAULTS.minImageWidth ) {
img = all[ i ];
break;
}
}
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 imageWidth( img ) {
return parseInt( img.getAttribute( 'width' ), 10 ) || 0;
}
function extractBlurb( doc ) {
var tables = doc.querySelectorAll( 'table, .infobox, .thumb' );
Array.prototype.forEach.call( tables, function ( el ) {
if ( el.parentNode ) {
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;
link.className = 'lhc-ra-img';
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 );
}
} );
}() );