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: Difference between revisions

MediaWiki interface page
No edit summary
No edit summary
Line 19: Line 19:
cmlimit: 500,
cmlimit: 500,
cmsort: 'sortkey',
cmsort: 'sortkey',
formatversion: 2,
format: 'json'
format: 'json'
} );
} );
Line 43: Line 44:
}
}


/* ---------- Instant-create forms (Country, City, Person, Case) ---------- */
/* ---------- Instant-create core (used by typed-name forms AND letter clicks) ---------- */


function fallbackToManualEditor( newTitle, preloadTitle, $status ) {
function fallbackToManualEditor( newTitle, preloadTitle, $status, reason ) {
console.error( 'GAR instant-create fell back to manual editor. Reason: ' + reason + '. Title: ' + newTitle );
$status.text( ' Opening the normal editor instead...' );
$status.text( ' Opening the normal editor instead...' );
window.location.href = mw.util.getUrl( newTitle, { action: 'edit', preload: preloadTitle } );
window.location.href = mw.util.getUrl( newTitle, { action: 'edit', preload: preloadTitle } );
Line 51: Line 53:


function safeReplacement( str ) {
function safeReplacement( str ) {
// Prevents literal "$" sequences a user types from being
// misread as regex backreferences by String.replace().
return str.replace( /\$/g, '$$$$' );
return str.replace( /\$/g, '$$$$' );
}
}
Line 85: Line 85:
action: 'query',
action: 'query',
titles: newTitle,
titles: newTitle,
formatversion: 2,
format: 'json'
format: 'json'
} ).then( function ( data ) {
} ).then( function ( data ) {
var pages = data.query.pages;
var pages = ( data.query && data.query.pages ) || [];
var exists = false;
var exists = false;
for ( var id in pages ) {
pages.forEach( function ( page ) {
if ( !pages[ id ].missing ) {
if ( !page.missing ) {
exists = true;
exists = true;
}
}
}
} );


if ( exists ) {
if ( exists ) {
Line 107: Line 108:
rvprop: 'content',
rvprop: 'content',
rvslots: 'main',
rvslots: 'main',
formatversion: 2,
format: 'json'
format: 'json'
} ).then( function ( tplData ) {
} ).then( function ( tplData ) {
var tplPages = tplData.query.pages;
var tplPages = ( tplData.query && tplData.query.pages ) || [];
var rawText = '';
var rawText = '';
for ( var id in tplPages ) {
tplPages.forEach( function ( page ) {
var revs = tplPages[ id ].revisions;
if ( page.revisions && page.revisions[ 0 ] && page.revisions[ 0 ].slots && page.revisions[ 0 ].slots.main ) {
if ( revs && revs[ 0 ] && revs[ 0 ].slots && revs[ 0 ].slots.main ) {
rawText = page.revisions[ 0 ].slots.main.content;
rawText = revs[ 0 ].slots.main[ '*' ];
}
}
} );
if ( !rawText ) {
fallbackToManualEditor( newTitle, preloadTitle, $status, 'preload template came back empty' );
return;
}
}


Line 125: Line 131:
text: finalText,
text: finalText,
createonly: true,
createonly: true,
formatversion: 2,
summary: 'Auto-created'
summary: 'Auto-created'
} ).then( function ( editResult ) {
} ).then( function ( editResult ) {
Line 130: Line 137:
window.location.href = mw.util.getUrl( newTitle );
window.location.href = mw.util.getUrl( newTitle );
} else {
} else {
fallbackToManualEditor( newTitle, preloadTitle, $status );
fallbackToManualEditor( newTitle, preloadTitle, $status, 'edit call did not report Success' );
}
}
}, function () {
}, function ( code, err ) {
fallbackToManualEditor( newTitle, preloadTitle, $status );
fallbackToManualEditor( newTitle, preloadTitle, $status, 'edit call failed: ' + code );
} );
} );
}, function () {
}, function ( code, err ) {
fallbackToManualEditor( newTitle, preloadTitle, $status );
fallbackToManualEditor( newTitle, preloadTitle, $status, 'template fetch failed: ' + code );
} );
} );
}, function () {
}, function ( code, err ) {
fallbackToManualEditor( newTitle, preloadTitle, $status );
fallbackToManualEditor( newTitle, preloadTitle, $status, 'existence check failed: ' + code );
} );
} );
} );
} );
}
}
/* ---------- Typed-name forms (Platform, Country, City, Person, Case) ---------- */


function buildForm( $container ) {
function buildForm( $container ) {
Line 155: Line 164:


var extraInputs = {};
var extraInputs = {};
var plainExtraInputs = [];
if ( extraFieldsRaw ) {
if ( extraFieldsRaw ) {
extraFieldsRaw.split( ';' ).forEach( function ( pair ) {
extraFieldsRaw.split( ';' ).forEach( function ( pair ) {
Line 165: Line 176:
} else {
} else {
$extra = $( '<input>' ).attr( 'type', 'text' ).attr( 'placeholder', label ).css( 'width', '100%' ).css( 'margin-bottom', '6px' );
$extra = $( '<input>' ).attr( 'type', 'text' ).attr( 'placeholder', label ).css( 'width', '100%' ).css( 'margin-bottom', '6px' );
plainExtraInputs.push( $extra );
}
}
extraInputs[ key ] = $extra;
extraInputs[ key ] = $extra;
Line 177: Line 189:
$container.empty().append( $wrapper );
$container.empty().append( $wrapper );


$button.on( 'click', function () {
function submit() {
var name = $.trim( $input.val() );
var name = $.trim( $input.val() );
if ( !name ) {
if ( !name ) {
Line 187: Line 199:
}
}
runInstantCreate( prefix + name, preloadTitle, fields, $status );
runInstantCreate( prefix + name, preloadTitle, fields, $status );
}
$button.on( 'click', submit );
// Enter submits from the name field or any plain (non-textarea) extra field.
$input.on( 'keydown', function ( e ) {
if ( e.which === 13 ) {
e.preventDefault();
submit();
}
} );
plainExtraInputs.forEach( function ( $extra ) {
$extra.on( 'keydown', function ( e ) {
if ( e.which === 13 ) {
e.preventDefault();
submit();
}
} );
} );
}
/* ---------- Letter-grid clicks (A-Z navigation hops) ---------- */
function bindInstantLetter( $el ) {
$el.on( 'click', function () {
var title = $el.data( 'title' );
var preload = $el.data( 'preload' );
var $status = $( '<span>' ).addClass( 'gar-instant-status' );
$el.after( $status );
$el.off( 'click' );
$el.css( 'cursor', 'default' );
runInstantCreate( title, preload, {}, $status );
} );
} );
}
}
Line 198: Line 242:
$content.find( '.gar-instant-create' ).each( function () {
$content.find( '.gar-instant-create' ).each( function () {
buildForm( $( this ) );
buildForm( $( this ) );
} );
$content.find( '.gar-instant-letter' ).each( function () {
bindInstantLetter( $( this ) );
} );
} );
} );
} );
}() );
}() );

Revision as of 18:42, 27 June 2026

( function () {
	'use strict';

	/* ---------- Live auto-updating grid (Platforms, Countries, Cities, etc.) ---------- */

	function renderAutoGrid( $container ) {
		var category = $container.data( 'category' );
		if ( !category ) {
			return;
		}

		$container.text( 'Loading...' );

		mw.loader.using( 'mediawiki.api' ).then( function () {
			return new mw.Api().get( {
				action: 'query',
				list: 'categorymembers',
				cmtitle: 'Category:' + category,
				cmlimit: 500,
				cmsort: 'sortkey',
				formatversion: 2,
				format: 'json'
			} );
		} ).then( function ( data ) {
			var members = ( data.query && data.query.categorymembers ) || [];
			$container.empty();

			if ( members.length === 0 ) {
				$container.append( $( '<p>' ).text( 'Nothing has been added here yet.' ) );
				return;
			}

			members.forEach( function ( member ) {
				var title = member.title;
				var displayName = title.split( ':' ).slice( 1 ).join( ':' ).split( '/' ).pop();
				var $link = $( '<a>' )
					.attr( 'href', mw.util.getUrl( title ) )
					.text( displayName );
				$container.append( $link );
			} );
		}, function () {
			$container.text( 'Could not load this list right now.' );
		} );
	}

	/* ---------- Instant-create core (used by typed-name forms AND letter clicks) ---------- */

	function fallbackToManualEditor( newTitle, preloadTitle, $status, reason ) {
		console.error( 'GAR instant-create fell back to manual editor. Reason: ' + reason + '. Title: ' + newTitle );
		$status.text( ' Opening the normal editor instead...' );
		window.location.href = mw.util.getUrl( newTitle, { action: 'edit', preload: preloadTitle } );
	}

	function safeReplacement( str ) {
		return str.replace( /\$/g, '$$$$' );
	}

	function applyExtraFields( rawText, fields ) {
		var text = rawText;

		if ( fields.image ) {
			text = text.replace( /\|image(\s*)=\s*\n/, function ( match, spacing ) {
				return '|image' + spacing + '= ' + safeReplacement( fields.image ) + '\n';
			} );
		}

		if ( fields.evidence ) {
			text = text.replace( '<Add Picture Here>', safeReplacement( fields.evidence ) );
		}

		if ( fields.description ) {
			text = text.replace( '<Write Text Here>', safeReplacement( fields.description ) );
		}

		return text;
	}

	function runInstantCreate( newTitle, preloadTitle, fields, $status ) {
		$status.text( ' Creating...' );

		mw.loader.using( 'mediawiki.api' ).then( function () {
			var api = new mw.Api();

			api.get( {
				action: 'query',
				titles: newTitle,
				formatversion: 2,
				format: 'json'
			} ).then( function ( data ) {
				var pages = ( data.query && data.query.pages ) || [];
				var exists = false;
				pages.forEach( function ( page ) {
					if ( !page.missing ) {
						exists = true;
					}
				} );

				if ( exists ) {
					$status.text( ' Already exists, opening it...' );
					window.location.href = mw.util.getUrl( newTitle );
					return;
				}

				api.get( {
					action: 'query',
					titles: preloadTitle,
					prop: 'revisions',
					rvprop: 'content',
					rvslots: 'main',
					formatversion: 2,
					format: 'json'
				} ).then( function ( tplData ) {
					var tplPages = ( tplData.query && tplData.query.pages ) || [];
					var rawText = '';
					tplPages.forEach( function ( page ) {
						if ( page.revisions && page.revisions[ 0 ] && page.revisions[ 0 ].slots && page.revisions[ 0 ].slots.main ) {
							rawText = page.revisions[ 0 ].slots.main.content;
						}
					} );

					if ( !rawText ) {
						fallbackToManualEditor( newTitle, preloadTitle, $status, 'preload template came back empty' );
						return;
					}

					var finalText = applyExtraFields( rawText, fields );

					api.postWithToken( 'csrf', {
						action: 'edit',
						title: newTitle,
						text: finalText,
						createonly: true,
						formatversion: 2,
						summary: 'Auto-created'
					} ).then( function ( editResult ) {
						if ( editResult && editResult.edit && editResult.edit.result === 'Success' ) {
							window.location.href = mw.util.getUrl( newTitle );
						} else {
							fallbackToManualEditor( newTitle, preloadTitle, $status, 'edit call did not report Success' );
						}
					}, function ( code, err ) {
						fallbackToManualEditor( newTitle, preloadTitle, $status, 'edit call failed: ' + code );
					} );
				}, function ( code, err ) {
					fallbackToManualEditor( newTitle, preloadTitle, $status, 'template fetch failed: ' + code );
				} );
			}, function ( code, err ) {
				fallbackToManualEditor( newTitle, preloadTitle, $status, 'existence check failed: ' + code );
			} );
		} );
	}

	/* ---------- Typed-name forms (Platform, Country, City, Person, Case) ---------- */

	function buildForm( $container ) {
		var prefix = $container.data( 'prefix' );
		var preloadTitle = $container.data( 'preload' );
		var placeholder = $container.data( 'placeholder' ) || 'Enter a name';
		var buttonLabel = $container.data( 'buttonlabel' ) || 'Add';
		var extraFieldsRaw = $container.data( 'extrafields' ) || '';

		var $input = $( '<input>' ).attr( 'type', 'text' ).attr( 'placeholder', placeholder ).css( 'width', '100%' ).css( 'margin-bottom', '6px' );
		var $wrapper = $( '<div>' ).append( $input );

		var extraInputs = {};
		var plainExtraInputs = [];

		if ( extraFieldsRaw ) {
			extraFieldsRaw.split( ';' ).forEach( function ( pair ) {
				var parts = pair.split( ':' );
				var key = $.trim( parts[ 0 ] );
				var label = $.trim( parts[ 1 ] || key );
				var $extra;
				if ( key === 'description' ) {
					$extra = $( '<textarea>' ).attr( 'placeholder', label ).attr( 'rows', 3 ).css( 'width', '100%' ).css( 'margin-bottom', '6px' );
				} else {
					$extra = $( '<input>' ).attr( 'type', 'text' ).attr( 'placeholder', label ).css( 'width', '100%' ).css( 'margin-bottom', '6px' );
					plainExtraInputs.push( $extra );
				}
				extraInputs[ key ] = $extra;
				$wrapper.append( $( '<div>' ).append( $extra ) );
			} );
		}

		var $button = $( '<button>' ).text( buttonLabel );
		var $status = $( '<span>' ).addClass( 'gar-instant-status' );
		$wrapper.append( $button, ' ', $status );

		$container.empty().append( $wrapper );

		function submit() {
			var name = $.trim( $input.val() );
			if ( !name ) {
				return;
			}
			var fields = {};
			for ( var key in extraInputs ) {
				fields[ key ] = $.trim( extraInputs[ key ].val() );
			}
			runInstantCreate( prefix + name, preloadTitle, fields, $status );
		}

		$button.on( 'click', submit );

		// Enter submits from the name field or any plain (non-textarea) extra field.
		$input.on( 'keydown', function ( e ) {
			if ( e.which === 13 ) {
				e.preventDefault();
				submit();
			}
		} );
		plainExtraInputs.forEach( function ( $extra ) {
			$extra.on( 'keydown', function ( e ) {
				if ( e.which === 13 ) {
					e.preventDefault();
					submit();
				}
			} );
		} );
	}

	/* ---------- Letter-grid clicks (A-Z navigation hops) ---------- */

	function bindInstantLetter( $el ) {
		$el.on( 'click', function () {
			var title = $el.data( 'title' );
			var preload = $el.data( 'preload' );
			var $status = $( '<span>' ).addClass( 'gar-instant-status' );
			$el.after( $status );
			$el.off( 'click' );
			$el.css( 'cursor', 'default' );
			runInstantCreate( title, preload, {}, $status );
		} );
	}

	/* ---------- Wire everything up once the page content is ready ---------- */

	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		$content.find( '.gar-auto-grid' ).each( function () {
			renderAutoGrid( $( this ) );
		} );
		$content.find( '.gar-instant-create' ).each( function () {
			buildForm( $( this ) );
		} );
		$content.find( '.gar-instant-letter' ).each( function () {
			bindInstantLetter( $( this ) );
		} );
	} );
}() );