MediaWiki:MediaUploader.js
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.
( function () {
'use strict';
// Wait for the ext.uploadWizard module to be ready before patching prototypes
mw.loader.using( [ 'ext.uploadWizard', 'oojs-ui-core', 'oojs-ui-widgets' ] ).then( function () {
var uw = mw.uploadWizard;
if ( !uw || !uw.deed || !uw.deed.ThirdParty || !mw.UploadWizardDetails ) {
return;
}
// Define Bahá'í source definitions
var BAHAI_SOURCES = {
bwn: {
label: 'Bahá’í World News Service (news.bahai.org)',
template: 'bwn',
license: '{{Baha\'i World News Service}}',
fields: [
{ key: 'article', label: 'Article number (e.g., 1868)', placeholder: '1868' }
],
getCategory: function ( vals ) {
return vals.article ? 'Category:BWNS ' + vals.article : null;
}
},
bns: {
label: 'Bahá’í News (1924–1990)',
template: 'bns',
license: '{{Bn-excerpt}}',
fields: [
{ key: 'issue', label: 'Issue number (e.g., 161)', placeholder: '161' },
{ key: 'page', label: 'Page number (e.g., 6)', placeholder: '6' }
],
getCategory: function ( vals ) {
return vals.issue ? 'Category:Baha\'i News No ' + vals.issue : null;
}
},
bws: {
label: 'The Bahá’í World (1925–2024)',
template: 'bws',
license: '{{Baha\'i World excerpt}}',
fields: [
{ key: 'volume', label: 'Volume number (e.g., 16)', placeholder: '16' },
{ key: 'page', label: 'Page number (e.g., 28)', placeholder: '28' }
],
getCategory: function ( vals ) {
return vals.volume ? 'Category:BW Volume ' + vals.volume : null;
}
},
ab: {
label: 'The American Bahá’í (1970–2001)',
template: 'ab',
license: '{{Abn-copyright}}',
fields: [
{ key: 'volume', label: 'Volume number (e.g., 1)', placeholder: '1' },
{ key: 'issue', label: 'Issue number (e.g., 4)', placeholder: '4' },
{ key: 'page', label: 'Page number (e.g., 1)', placeholder: '1' }
],
getCategory: function ( vals ) {
return ( vals.volume && vals.issue ) ? 'Category:AB Volume ' + vals.volume + ' No ' + vals.issue : null;
}
},
sw: {
label: 'Star of the West',
template: 'sw',
license: '{{SOTW excerpt}}',
fields: [
{ key: 'volume', label: 'Volume number (e.g., 1)', placeholder: '1' },
{ key: 'issue', label: 'Issue number (e.g., 2)', placeholder: '2' },
{ key: 'page', label: 'Page number (e.g., 9)', placeholder: '9' }
],
getCategory: function ( vals ) {
return vals.volume ? 'Category:SW Volume ' + vals.volume + ' pictures' : null;
}
}
};
// =========================================================================
// 1. Patch uw.deed.ThirdParty.prototype.setFormFields
// =========================================================================
var origSetFormFields = uw.deed.ThirdParty.prototype.setFormFields;
uw.deed.ThirdParty.prototype.setFormFields = function ( $selector ) {
var deed = this;
origSetFormFields.call( this, $selector );
this.bahaiSourceType = null;
this.bahaiParamInputs = {};
// Build radio options
var radioOptions = [];
Object.keys( BAHAI_SOURCES ).forEach( function ( key ) {
radioOptions.push( new OO.ui.RadioOptionWidget( {
data: key,
label: BAHAI_SOURCES[ key ].label
} ) );
} );
radioOptions.push( new OO.ui.RadioOptionWidget( {
data: 'other',
label: 'Other source (enter URL or publication below)'
} ) );
this.bahaiRadioSelect = new OO.ui.RadioSelectWidget( {
items: radioOptions
} );
var $bahaiContainer = $( '<div>' )
.addClass( 'bahai-mediauploader-source-selector' )
.css( { marginBottom: '1em' } );
var $paramsContainer = $( '<div>' )
.addClass( 'bahai-mediauploader-source-params' )
.css( {
marginTop: '0.75em',
marginLeft: '1.75em',
padding: '0.75em 1em',
borderLeft: '3px solid var(--border-color-ui-widget, #a2a9b1)',
backgroundColor: 'var(--background-color-ui-subtle, #f8f9fa)'
} )
.hide();
$bahaiContainer.append(
this.bahaiRadioSelect.$element,
$paramsContainer
);
// Insert radio selector right above the original Source input widget
this.sourceInput.$element.before( $bahaiContainer );
// Grab wrappers for Source help text, Author, and License so we can toggle them
var $sourceHelp = this.sourceInputField.$element.find( '.mediauploader-details-help' );
var $authorWrapper = this.authorInputField.$element.closest( '.mediauploader-thirdparty-fields' );
var $licenseWrapper = this.licenseInputField.$element.closest( '.mediauploader-thirdparty-license' );
// Helper to sync parameter inputs into the underlying sourceInput
function updateBahaiSourceValue() {
var type = deed.bahaiSourceType;
if ( !type || type === 'other' ) {
return;
}
var spec = BAHAI_SOURCES[ type ];
var values = [];
var valMap = {};
var allFilled = true;
spec.fields.forEach( function ( f ) {
var val = deed.bahaiParamInputs[ f.key ] ? deed.bahaiParamInputs[ f.key ].getValue().trim() : '';
if ( !val ) {
allFilled = false;
}
values.push( val );
valMap[ f.key ] = val;
} );
// Show or hide the duplicate check warning
if ( deed.bahaiCategoryWarning ) {
var catTitle = spec.getCategory ? spec.getCategory( valMap ) : null;
if ( catTitle ) {
var catUrl = mw.util.getUrl( catTitle );
var $msg = $( '<span>' ).append(
'Most images from this source have already been uploaded. Please check ',
$( '<a>' )
.attr( { href: catUrl, target: '_blank', rel: 'noopener' } )
.css( 'font-weight', 'bold' )
.text( catTitle ),
' to make sure your image does not already exist before continuing.'
);
deed.bahaiCategoryWarning.setLabel( $msg );
deed.bahaiCategoryWarning.toggle( true );
} else {
deed.bahaiCategoryWarning.toggle( false );
}
}
if ( allFilled ) {
deed.sourceInput.setValue( '{{' + spec.template + '|' + values.join( '|' ) + '}}' );
} else {
// Keep empty so validation catches incomplete parameters
deed.sourceInput.setValue( '' );
}
}
// Handle radio button selection
this.bahaiRadioSelect.on( 'choose', function ( item ) {
var key = item.getData();
deed.bahaiSourceType = key;
deed.bahaiParamInputs = {};
$paramsContainer.empty();
// Clear previous validation errors when switching
deed.sourceInputField.setErrors( [] );
if ( key === 'other' ) {
$paramsContainer.hide();
$sourceHelp.show();
deed.sourceInput.$element.show();
if ( !deed.sourceInput.getValue().indexOf( '{{' ) ) {
deed.sourceInput.setValue( '' );
}
if ( deed.authorInput.getValue() === 'N/A' ) {
deed.authorInput.setValue( '' );
}
$authorWrapper.show();
$licenseWrapper.show();
} else {
var spec = BAHAI_SOURCES[ key ];
$sourceHelp.hide();
deed.sourceInput.$element.hide();
$authorWrapper.hide();
$licenseWrapper.hide();
// Set dummy author to satisfy internal methods if called
deed.authorInput.setValue( 'N/A' );
// Build specific inputs for this Bahá'í source
spec.fields.forEach( function ( f ) {
var input = new OO.ui.TextInputWidget( {
placeholder: f.placeholder
} );
input.on( 'change', updateBahaiSourceValue );
deed.bahaiParamInputs[ f.key ] = input;
var fieldLayout = new OO.ui.FieldLayout( input, {
label: f.label,
align: 'top'
} );
$paramsContainer.append( fieldLayout.$element );
} );
// Add warning widget for checking existing category
deed.bahaiCategoryWarning = new OO.ui.MessageWidget( {
type: 'warning',
inline: false
} );
deed.bahaiCategoryWarning.$element.css( 'margin-top', '1em' );
deed.bahaiCategoryWarning.toggle( false );
$paramsContainer.append( deed.bahaiCategoryWarning.$element );
$paramsContainer.show();
updateBahaiSourceValue();
}
} );
// Override sourceInput.getErrors to provide clear feedback for Bahá'í source fields
var origGetErrors = this.sourceInput.getErrors;
this.sourceInput.getErrors = function () {
if ( !deed.bahaiSourceType ) {
return $.Deferred().resolve( [
mw.message( 'mediauploader-error-blank' )
] ).promise();
}
if ( deed.bahaiSourceType !== 'other' ) {
var spec = BAHAI_SOURCES[ deed.bahaiSourceType ];
var missing = [];
spec.fields.forEach( function ( f ) {
var val = deed.bahaiParamInputs[ f.key ] ? deed.bahaiParamInputs[ f.key ].getValue().trim() : '';
if ( !val ) {
missing.push( f.label );
}
} );
if ( missing.length > 0 ) {
return $.Deferred().resolve( [
mw.message( 'mediauploader-error-blank' )
] ).promise();
}
return $.Deferred().resolve( [] ).promise();
}
return origGetErrors.call( this );
};
// Initially hide the raw source input, help text, author, and license until the user selects "other"
$sourceHelp.hide();
this.sourceInput.$element.hide();
$authorWrapper.hide();
$licenseWrapper.hide();
};
// =========================================================================
// 2. Patch uw.deed.ThirdParty.prototype.getFields & getLicenseWikiText
// =========================================================================
var origGetFields = uw.deed.ThirdParty.prototype.getFields;
uw.deed.ThirdParty.prototype.getFields = function () {
if ( this.bahaiSourceType && this.bahaiSourceType !== 'other' ) {
// Only validate the source field when a Bahá'í source is chosen
return [ this.sourceInputField ];
}
return origGetFields.call( this );
};
var origGetLicenseWikiText = uw.deed.ThirdParty.prototype.getLicenseWikiText;
uw.deed.ThirdParty.prototype.getLicenseWikiText = function () {
if ( this.bahaiSourceType && this.bahaiSourceType !== 'other' ) {
return BAHAI_SOURCES[ this.bahaiSourceType ].license;
}
return origGetLicenseWikiText.call( this );
};
// =========================================================================
// 3. Patch mw.UploadWizardDetails for Step 4 ("Describe") UI & Wikitext
// =========================================================================
function isBahaiSourceDeed( upload ) {
var deed = upload && upload.deedChooser && upload.deedChooser.deed;
return Boolean(
deed &&
deed.name === 'thirdparty' &&
deed.bahaiSourceType &&
deed.bahaiSourceType !== 'other'
);
}
// Adjust Step 4 fields (Rename "Description" -> "Caption" and hide "Date")
var origBuildInterface = mw.UploadWizardDetails.prototype.buildInterface;
mw.UploadWizardDetails.prototype.buildInterface = function () {
origBuildInterface.apply( this, arguments );
if ( isBahaiSourceDeed( this.upload ) ) {
var captionFieldKey = mw.UploadWizard.config.content.captionField || 'description';
if ( this.fieldWrapperMap[ captionFieldKey ] ) {
this.fieldWrapperMap[ captionFieldKey ].setLabel( 'Caption' );
}
if ( this.fieldWrapperMap.date ) {
this.fieldWrapperMap.date.toggle( false );
// Ensure hidden date field does not block validation or trigger warnings
this.fieldMap.date.getErrors = function () {
return $.Deferred().resolve( [] ).promise();
};
this.fieldMap.date.getWarnings = function () {
return $.Deferred().resolve( [] ).promise();
};
}
}
};
// Output {{cs}} instead of {{Information}} when a Bahá'í source is selected
var origGetWikiText = mw.UploadWizardDetails.prototype.getWikiText;
mw.UploadWizardDetails.prototype.getWikiText = function () {
if ( !isBahaiSourceDeed( this.upload ) ) {
return origGetWikiText.apply( this, arguments );
}
var deed = this.upload.deedChooser.deed;
var captionFieldKey = mw.UploadWizard.config.content.captionField || 'description';
var caption = '';
if ( this.fieldMap[ captionFieldKey ] ) {
// Use getCaption() if available so it doesn't wrap in {{en|1=...}} unless desired
if ( typeof this.fieldMap[ captionFieldKey ].getCaption === 'function' ) {
caption = this.fieldMap[ captionFieldKey ].getCaption().trim();
} else {
caption = this.fieldMap[ captionFieldKey ].getWikiText().trim();
}
}
var source = deed.getSourceWikiText( this.upload ).trim();
var license = deed.getLicenseWikiText().trim();
var categories = this.fieldMap.categories ? this.fieldMap.categories.getWikiText().trim() : '';
var wikiText = '== File info ==\n' +
'{{cs\n' +
'| caption = ' + caption + '\n' +
'| source = ' + source + '\n' +
'}}\n\n' +
'== File license ==\n' +
license;
if ( categories ) {
wikiText += '\n\n' + categories;
}
return wikiText;
};
} );
}() );