MediaWiki:Gadget-categories.js: Difference between revisions
MediaWiki interface page
More actions
No edit summary  |
(No difference)
|
Latest revision as of 11:19, 7 March 2026
/* =============================================================================
Gadget-categories.js
Runs on all Category:* pages.
- Strips path prefixes from labels
- Wraps link text in structured spans (name + meta)
- Injects search bar
- Batch API fetch: contributor count + last edited per page
============================================================================= */
( function () {
if ( mw.config.get( 'wgNamespaceNumber' ) !== 14 ) return;
$( function () {
var $pageLinks = $( '#mw-pages .mw-category-group ul li a' );
if ( !$pageLinks.length ) return;
// --- Wrap link content in structured spans ---------------------------
$pageLinks.each( function () {
var $a = $( this );
var label = $a.text().replace( /^.*\//, '' ); // strip path prefix
$a.empty()
.append( $( '<span>' ).addClass( 'cat-item-name' ).text( label ) )
.append( $( '<span>' ).addClass( 'cat-item-meta' ).text( '\u00a0' ) );
// \u00a0 = non-breaking space — keeps height while loading
} );
// --- Inject search bar above #mw-pages -------------------------------
var total = $pageLinks.length;
var $count = $( '<span>' ).addClass( 'cat-search-count' )
.text( total + ' page' + ( total === 1 ? '' : 's' ) );
var $input = $( '<input>' ).attr( {
type : 'text',
placeholder : 'Search this category...',
'class' : 'cat-search-input'
} );
var $wrap = $( '<div>' ).addClass( 'cat-search-wrap' )
.append( $input, $count );
$( '#mw-pages h2' ).after( $wrap );
$input.on( 'input', function () {
var query = $( this ).val().toLowerCase().trim();
var visible = 0;
$pageLinks.each( function () {
var $a = $( this );
var name = $a.find( '.cat-item-name' ).text().toLowerCase();
var show = !query || name.indexOf( query ) !== -1;
$a.closest( 'li' ).toggle( show );
if ( show ) visible++;
} );
// Hide empty letter groups
$( '#mw-pages .mw-category-group' ).each( function () {
var hasVisible = $( this ).find( 'li:visible' ).length > 0;
$( this ).toggle( hasVisible );
} );
$count.text(
query
? visible + ' / ' + total + ' page' + ( total === 1 ? '' : 's' )
: total + ' page' + ( total === 1 ? '' : 's' )
);
} );
// --- Batch API fetch -------------------------------------------------
var pageNames = $pageLinks.map( function () {
return $( this ).attr( 'href' )
? decodeURIComponent(
$( this ).attr( 'href' ).replace( /^.*\/wiki\//, '' )
).replace( /_/g, ' ' )
: null;
} ).get().filter( Boolean );
if ( !pageNames.length ) return;
var api = new mw.Api();
for ( var i = 0; i < pageNames.length; i += 50 ) {
( function ( batch ) {
api.get( {
action : 'query',
titles : batch.join( '|' ),
prop : 'revisions|contributors',
rvprop : 'timestamp',
rvlimit : 1,
pclimit : 500,
format : 'json'
} ).done( function ( data ) {
var pages = data.query && data.query.pages;
if ( !pages ) return;
$.each( pages, function ( _, page ) {
var title = page.title;
var ts = page.revisions && page.revisions[0] && page.revisions[0].timestamp || '';
var nContrib = ( page.contributors ? page.contributors.length : 0 )
+ ( page.anoncontributors || 0 );
// Match by href
var $a = $pageLinks.filter( function () {
var href = $( this ).attr( 'href' ) || '';
return decodeURIComponent( href.replace( /^.*\/wiki\//, '' ) )
.replace( /_/g, ' ' ) === title;
} );
if ( !$a.length ) return;
var dateStr = ts
? new Date( ts ).toLocaleDateString( 'en-GB', { year: 'numeric', month: 'short' } )
: '—';
var metaText = nContrib + ( nContrib === 1 ? ' contributor' : ' contributors' )
+ ' · ' + dateStr;
$a.find( '.cat-item-meta' )
.text( metaText )
.addClass( 'loaded' );
} );
} );
}( pageNames.slice( i, i + 50 ) ) );
}
} );
}() );