Jump to content
Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js

MediaWiki interface page

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.
/* Pwani 2050: on the projects category, draw a grid of photo tiles per theme in place of the
   alphabetical listing. The category page carries an empty .okc-project-tiles marker with the
   plan's themes in order (generated from the topic schema); each tile shows the page title, the
   short name, over its {{Hero}} image, with the {{Infobox project}} theme and counties above it
   and the infobox's full name on hover. Styles are in MediaWiki:Common.css,
   which also hides the listing while the tiles load; if they cannot load, the listing comes back. */
( function () {
	var marker = document.querySelector( '#mw-content-text .okc-project-tiles' );
	if ( !marker || mw.config.get( 'wgAction' ) !== 'view' ) {
		return;
	}
	var content = document.getElementById( 'mw-content-text' );
	var category = mw.config.get( 'wgPageName' );
	var themes = ( marker.getAttribute( 'data-themes' ) || '' ).split( '|' ).filter( Boolean );

	// Theme names are accepted in any case, with "and" or "&" and with hyphens, as the infobox does.
	function norm( s ) {
		return s.toLowerCase().replace( /&/g, ' and ' ).replace( /[-_\s]+/g, ' ' ).trim();
	}
	var themeByNorm = {};
	themes.forEach( function ( t ) {
		themeByNorm[ norm( t ) ] = t;
	} );

	// One parameter from the first call of a template, with links reduced to their text.
	function param( text, template, name ) {
		var call = text.match( new RegExp( '\\{\\{\\s*' + template + '\\s*\\|([\\s\\S]*?)\\}\\}', 'i' ) );
		if ( !call ) {
			return '';
		}
		var m = call[ 1 ].match( new RegExp( '(?:^|\\|)\\s*' + name + '\\s*=([^|]*)', 'i' ) );
		return m ? m[ 1 ].replace( /\[\[(?:[^|\]]*\|)?([^\]]*)\]\]/g, '$1' ).trim() : '';
	}

	function fetchAll( api, params, collect ) {
		return api.get( params ).then( function ( data ) {
			collect( data );
			if ( data.continue ) {
				return fetchAll( api, $.extend( {}, params, data.continue ), collect );
			}
		} );
	}

	function fail() {
		content.classList.add( 'okc-project-tiles-failed' );
	}

	mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
		var api = new mw.Api();
		var projects = [];
		return fetchAll( api, {
			action: 'query',
			formatversion: 2,
			generator: 'categorymembers',
			gcmtitle: category,
			gcmnamespace: 0,
			gcmlimit: 50,
			prop: 'revisions',
			rvprop: 'content',
			rvslots: 'main'
		}, function ( data ) {
			( data.query && data.query.pages || [] ).forEach( function ( p ) {
				var text = p.revisions ? p.revisions[ 0 ].slots.main.content : '';
				var theme = param( text, 'Infobox project', 'theme' );
				projects.push( {
					title: p.title,
					name: param( text, 'Infobox project', 'name' ) || p.title,
					theme: themeByNorm[ norm( theme ) ] || theme,
					counties: param( text, 'Infobox project', 'counties' ),
					image: param( text, 'Hero', 'image' ).replace( /^(File|Image):/i, '' ),
					position: param( text, 'Hero', 'position' ).toLowerCase()
				} );
			} );
		} ).then( function () {
			var files = projects.filter( function ( p ) {
				return p.image;
			} ).map( function ( p ) {
				return 'File:' + p.image;
			} );
			var thumbs = {};
			var batches = [];
			for ( var i = 0; i < files.length; i += 50 ) {
				batches.push( api.get( {
					action: 'query',
					formatversion: 2,
					titles: files.slice( i, i + 50 ),
					prop: 'imageinfo',
					iiprop: 'url',
					iiurlwidth: 800
				} ).then( function ( data ) {
					var q = data.query || {};
					var canon = {};
					( q.normalized || [] ).forEach( function ( n ) {
						canon[ n.to ] = n.from;
					} );
					( q.pages || [] ).forEach( function ( p ) {
						if ( p.imageinfo ) {
							thumbs[ canon[ p.title ] || p.title ] = p.imageinfo[ 0 ].thumburl || p.imageinfo[ 0 ].url;
						}
					} );
				} ) );
			}
			return $.when.apply( $, batches ).then( function () {
				render( projects, thumbs );
			} );
		} );
	} ).catch( fail );

	function render( projects, thumbs ) {
		if ( !projects.length ) {
			fail();
			return;
		}
		var groups = {};
		projects.forEach( function ( p ) {
			var key = p.theme || '';
			( groups[ key ] = groups[ key ] || [] ).push( p );
		} );
		var order = themes.filter( function ( t ) {
			return groups[ t ];
		} );
		Object.keys( groups ).sort().forEach( function ( k ) {
			if ( order.indexOf( k ) === -1 && k ) {
				order.push( k );
			}
		} );
		if ( groups[ '' ] ) {
			order.push( '' );
		}

		marker.textContent = '';
		order.forEach( function ( theme ) {
			var section = document.createElement( 'section' );
			section.className = 'okc-tiles-group';
			var h = document.createElement( 'h2' );
			if ( theme ) {
				var link = document.createElement( 'a' );
				link.href = mw.util.getUrl( theme );
				link.textContent = theme;
				h.appendChild( link );
			} else {
				h.textContent = 'No theme yet';
			}
			section.appendChild( h );

			var grid = document.createElement( 'div' );
			grid.className = 'okc-tiles';
			groups[ theme ].sort( function ( a, b ) {
				return a.title.localeCompare( b.title );
			} ).forEach( function ( p ) {
				var tile = document.createElement( 'a' );
				tile.className = 'okc-tile';
				tile.href = mw.util.getUrl( p.title );
				tile.title = p.name;
				var src = p.image && thumbs[ 'File:' + p.image ];
				if ( src ) {
					tile.style.backgroundImage = 'url("' + src.replace( /"/g, '%22' ) + '")';
					tile.style.backgroundPosition = 'center ' +
						( p.position === 'top' || p.position === 'bottom' ? p.position : 'center' );
				}
				var meta = document.createElement( 'span' );
				meta.className = 'okc-tile-meta';
				meta.textContent = [ p.theme, p.counties ].filter( Boolean ).join( ' · ' );
				var name = document.createElement( 'span' );
				name.className = 'okc-tile-name';
				name.textContent = p.title;
				tile.appendChild( meta );
				tile.appendChild( name );
				grid.appendChild( tile );
			} );
			section.appendChild( grid );
			marker.appendChild( section );
		} );
		content.classList.add( 'okc-project-tiles-ready' );
	}
}() );