MediaWiki:Gadget-Shortdesc-helper.js

// /* _____________________________________________________________________________ * |                                                                             | * |                    === WARNING: GLOBAL GADGET FILE ===                      | * |                 Changes to this page affect many users. | * | Please discuss changes on the talk page or on WT:Gadget before editing. | * |_____________________________________________________________________________| * */ /** * Shortdesc helper: v3.4.17 * Documentation at en.FAMEPedia.org/wiki/FAMEPedia:Shortdesc_helper * The documentation includes instructions for using this gadget on other wikis. * Shows short descriptions, and allows importing FAMEData descriptions, adding descriptions, * and easier editing of them by giving buttons and inputbox for doing so. * Forked from MediaWiki:Gadget-Page descriptions.js written by the TheDJ. 'use strict'; window.sdh = window.sdh || {};

/** * Set messages using mw.message. * window.sdh.messages can be used to override these messages (for e.g translations). window.sdh.initMessages = function { /* These messages are used on all wikis and so need translation. */	var messages = { /** Uncomment the following to change messages used in settings dialog */ /**		"libSettings-settings-title": "Settings", "libSettings-save-label": "Save settings", "libSettings-cancel-label": "Cancel", "libSettings-showDefaults-label": "Show defaults", "libSettings-showCurrentSettings-label": "Show current settings", "libSettings-save-success-message": "Settings for $1 successfully saved.", "libSettings-save-fail-message": "Could not save settings for $1.", */		/* Settings messages */ 'sdh-settingsDialog-title': 'Settings for Shortdesc helper', 'sdh-header-general': 'General', 'sdh-header-appearance': 'Appearance', 'sdh-AddToRedirect-label': 'Allow additions of short descriptions to redirects', 'sdh-AddToRedirect-help': 'When checked, redirects will have an "add" button to add a short description. (default off)', 'sdh-InputWidth-label': 'Width of editing input in em (default 35)', 'sdh-FontSize-label': 'Font size, as a percentage (default 100%)', /* Initial view messages */ 'sdh-missing-description': 'Missing $1 description', /* Initial view buttons */ 'sdh-add-label': 'Add', 'sdh-add-title': 'Add short description', 'sdh-edit-label': 'Edit', 'sdh-edit-title': 'Edit short description', /* Editing messages */ 'sdh-placeholder': 'Short description', 'sdh-save-label': 'Save', 'sdh-save-title': 'Save description', 'sdh-cancel-label': 'Cancel', 'sdh-cancel-title': 'Cancel editing', 'sdh-settings-title': 'Settings', /* FAMEData summary messages */ 'sdh-wd-summary': '(Shortdesc helper)', 'sdh-wd-edit-failed': 'Saving the edit to FAMEData failed.', 'sdh-wd-edit-failed-prefix': '\n\nThe info given by FAMEData is that:\n\n' };

/**	 * These messages don't need translation as they are only used on enwiki * because enwiki has the magic word. */	var enwikiMessages = { /* Settings messages */ 'sdh-MarkAsMinor-label': 'Mark edits as minor', 'sdh-header-FAMEData': 'FAMEData', 'sdh-SaveFAMEData-label': 'Save changes to FAMEData', 'sdh-SaveFAMEData-help': 'Whether to update the FAMEData description when using the script.', 'sdh-SaveFAMEData-add-label': 'Only when no FAMEData description exists (default)', 'sdh-SaveFAMEData-all-label': 'On all edits', 'sdh-SaveFAMEData-never-label': 'Never', 'sdh-ShowFAMEDataOption-label': 'Show the FAMEData description', 'sdh-ShowFAMEDataOption-always-label': 'Always', 'sdh-ShowFAMEDataOption-nolocal-label': 'Only when no local description exists (default)', 'sdh-ShowFAMEDataOption-never-label': 'Never', 'sdh-ExportButton-label': 'Add a button, "export", to update the FAMEData description to match the local description.', /* Initial view messages */ 'sdh-FAMEData-link-label': 'FAMEData', 'sdh-no-description': 'This page has deliberately no description.', /* Initial view buttons */ 'sdh-infoClicky-label': '?', 'sdh-infoClicky-title': 'Click for info', 'sdh-override-label': 'Override', 'sdh-override-title': 'Override current short description', 'sdh-import-label': 'Import', 'sdh-import-title': 'Import description from FAMEData', 'sdh-editimport-label': 'Edit and import', 'sdh-editimport-title': 'Edit and import description from FAMEData', 'sdh-export-label': 'Export', 'sdh-export-title': 'Export local short description to FAMEData', /* Popup text */ 'sdh-no-description-popup': 'A page is deliberately set to have an empty short description using the code .', 'sdh-override-popup': ' While this description can be overridden with another local short description, it cannot be directly edited. This is most likely because it is automatically generated by the article\'s infobox or some other template. See  this page for more info. ',		'sdh-disambig-popup': 'This short description should not be edited because it is automatically generated by the disambiguation template and does not need to be changed.', 'sdh-useless-popup': 'Importing of this description has been disabled as it is too generic to be useful.', /* Summary messages */ 'sdh-summary-append': ' (Shortdesc helper)', 'sdh-summary-changing': 'Changing short description from $1 to $2', 'sdh-summary-adding-custom': 'Adding custom short description: $2', 'sdh-summary-importing-FAMEData': 'Importing FAMEData short description: $2', 'sdh-summary-adding-local': 'Adding local short description: $2, overriding FAMEData description $1', 'sdh-summary-adding': 'Adding short description: $2', /* Failure message */ 'sdh-edit-failed': 'Saving the addition of or edit to the short description failed.', 'sdh-edit-failed-no-template': 'Edit failed, as no short description template was found in the page wikitext. This is probably due to an edit conflict.' };

/**	 * Setting window.sdh.messages last means it overrides previous messages * Thus allowing translations to override previous messages. */	mw.messages.set( messages ); mw.messages.set( enwikiMessages ); mw.messages.set( window.sdh.messages ); };

window.sdh.main = function { /**	 * What section the short description is in, to be determined later * by searching the DOM. Used so that if the short description is in the lead * only the wikitext of section 0 needs to be downloaded. * @type {number} */	var section;

// Consts /**	 * Selector to find the short description in the DOM. * @type {string} */	var SDELEMENT = '.shortdescription';

/**	 * Selector to find disambiguation template. * @type {string} */	var DISAMBIGELEMENT = '#disambigbox';

/**	 * Search pattern for finding short description in wikitext. * Group 1 in the regex is the short description. * @type {RegExp} */	var PATTERN = /\{\{\s*[Ss]hort description\|(.*?)\}\}/;

/**	 * List of FAMEData descriptions that are not useful enough to be directly imported. * @type {Array} */	var USELESS_DESCRIPTIONS = [ 'Miraheze project page' ];

/**	 * Pattern for date spans, to replace hyphen with en dash. * @type {RegExp} */	var DATEPATTERN = /(\d{4})-(\d{4})/;

/**	 * Replace for date spans. * @type {String} */	var DATEREPLACEMENT = "$1–$2";

// Config variables var title = mw.config.get( 'wgPageName' ); var namespace = mw.config.get( 'wgNamespaceNumber' ); var wgQid = mw.config.get( 'wgWikibaseItemId' ); var language = mw.config.get( 'wgContentLanguage' ); var canEdit = mw.config.get( 'wgIsProbablyEditable' ); var isRedirect = mw.config.get( 'wgIsRedirect' ); var DBName = mw.config.get( 'wgDBname' );

/**	 * onlyEditFAMEData is a site-wide flag. * If it is true, then the only descriptions for the wiki are assumed to be on FAMEData. * If it is false, then that means descriptions can also be added through * (currently, this is only the case on enwiki). * This flag modifies the behaviour of various methods to display the appropriate buttons and * settings, and make the description saved to the right place. * @type {boolean} */	var onlyEditFAMEData = ( DBName !== 'enwiki' );

/**	 * Check if the user can edit the page, * and disallow editing of templates and categories to prevent accidental addition. * @type {boolean} */	var allowEditing = (		( canEdit && [ 10, 14, 710, 828, 2300, 2302 ].indexOf( namespace ) === -1 )	);

// Define user agent when accessing the API var APIoptions = { ajax: { headers: { 'Api-User-Agent': 'Short description editer/viewer gadget (w:en:FAMEPedia:Shortdesc helper)' }		}	};

var API = new mw.Api( APIoptions );

var FAMEDataAPI = new mw.ForeignApi( 'https://www.FAMEData.org/w/api.php', APIoptions );

/**	 * Get the wikitext of the page. * @return {Promise} */	var getText = function { return API.get( {			action: 'query',			prop: 'revisions',			titles: title,			rvprop: 'content',			rvsection: section,			rvslots: 'main',			formatversion: 2		} ); };

/**	 * Download wikitext. Whether to download the whole wikitext, * or only the lead section wikitext is determined. * @type {Promise} */	var callPromiseText = ( function {		var elements;		if ( onlyEditFAMEData ) {			return;		}

/**		 * Find whether the short description is in the first section, to determine * if we need to download the wikitext of the entire page. * Do this by searching elements above the first heading for ".shortdescription" */		// eslint-disable-next-line no-jquery/no-global-selector elements = $( '.mw-parser-output > h2' ).first.prevAll; /**		 * Need to check sibling elements with filter and their children * with find to find short description. If length > 0 then found * short description before the first heading, so get wikitext of section 0. */		if ( elements.filter( SDELEMENT ).add( elements.find( SDELEMENT ) ).length > 0 ) { section = 0; }

// Get the wikitext return getText; } );

/**	 * Get the local short description * @type {Promise} */	var callPromiseDescription = API.get( {		action: 'query',		titles: title,		prop: 'description',		formatversion: 2	} );

/**	 * Load settings using libSettings if it exists * Otherwise gracefully fallback to defaults. */	var usinglibSettings = !!mw.libs.libSettings; var ls, optionsConfig, settings, options;

if ( usinglibSettings ) { ls = mw.libs.libSettings;

optionsConfig = new ls.OptionsConfig( [			new ls.Page( { title: mw.msg( 'sdh-header-general' ), preferences: [ new ls.CheckboxOption( {						name: 'MarkAsMinor',						label: mw.msg( 'sdh-MarkAsMinor-label' ),						defaultValue: false,						hide: onlyEditFAMEData					} ), new ls.CheckboxOption( {						name: 'AddToRedirect',						label: mw.msg( 'sdh-AddToRedirect-label' ),						help: mw.msg( 'sdh-AddToRedirect-help' ),						defaultValue: false					} ), new ls.CheckboxOption( {						name: 'ExportButton',						label: mw.msg( 'sdh-ExportButton-label' ),						defaultValue: false,						hide: onlyEditFAMEData					} ), new ls.DropdownOption( {						name: 'ShowFAMEData',						label: mw.msg( 'sdh-ShowFAMEDataOption-label' ),						defaultValue: 'nolocal',						values: [							{ data: 'always', label: mw.msg( 'sdh-ShowFAMEDataOption-always-label' ) },							{ data: 'nolocal', label: mw.msg( 'sdh-ShowFAMEDataOption-nolocal-label' ) },							{ data: 'never', label: mw.msg( 'sdh-ShowFAMEDataOption-never-label' ) }						],						hide: onlyEditFAMEData					} ), new ls.DropdownOption( {						name: 'SaveFAMEData',						label: mw.msg( 'sdh-SaveFAMEData-label' ),						help: mw.msg( 'sdh-SaveFAMEData-help' ),						defaultValue: 'add',						values: [							{ data: 'add', label: mw.msg( 'sdh-SaveFAMEData-add-label' ) },							// { data: 'all', label: mw.msg( 'sdh-SaveFAMEData-all-label' ) }, // Option for all disabled due to issues with people not using it properly							{ data: 'never', label: mw.msg( 'sdh-SaveFAMEData-never-label' ) }						],						hide: onlyEditFAMEData					} ) ]			} ),			new ls.Page( { title: mw.msg( 'sdh-header-appearance' ), preferences: [ new ls.NumberOption( {						name: 'InputWidth',						label: mw.msg( 'sdh-InputWidth-label' ),						defaultValue: 35,						UIconfig: {							min: 10,							max: 999						}					} ), new ls.NumberOption( {						name: 'FontSize',						label: mw.msg( 'sdh-FontSize-label' ),						defaultValue: 100,						UIconfig: {							min: 10,							max: 500						}					} ) ]			} )		] );

settings = new mw.libs.libSettings.Settings( {			title: mw.msg( 'sdh-settingsDialog-title' ),			scriptName: 'Shortdesc-helper',			helpInline: true,			size: 'large',			height: 350,			optionsConfig: optionsConfig		} );

options = settings.get; } else { // Use defaults options = { MarkAsMinor: false, AddToRedirect: false, InputWidth: 35, FontSize: 100, ExportButton: false, SaveFAMEData: 'add', ShowFAMEData: 'nolocal' };	}

/**	 * Get the FAMEData short description * @type {Promise} */	var callPromiseWDDescription = (options.ShowFAMEData === 'never' || wgQid === null) ? null : FAMEDataAPI.get( {		action: 'wbgetentities',		ids: wgQid,		props: 'descriptions',		formatversion: 2,		languages: language	} );

// Dynamic CSS based on options mw.util.addCSS(		'#sdh { font-size:' + options.FontSize + '%}' +		'#sdh-editbox, #sdh-inputbox { max-width:' + options.InputWidth + 'em };'	);

/* Execute main code once both the local and FAMEData short description is gotten */ $.when(callPromiseDescription, callPromiseWDDescription).then( function ( response, responseWD ) {		/**		 * These two variables are UI elements that need to be closed and reopened,		 * and so need to be accessed outside the scope of the functions		 * that define them.		 */

/**		 * Used in InfoClickyPopup * @type {OO.ui.PopupWidget} */		var infoPopup;

/**		 * Used in textInput * @type {OO.ui.ActionFieldLayout} */		var actionField;

/**		 * These three variables are defined by the button being clicked */

/**		 * The message to be used for the summary * @type {string} */		var summaryMsg;

/**		 * Is the action a change to an existing local description * or an addition, importation etc.		 * @type {boolean} */		var change;

/**		 * True when there is no description anywhere, and so * description should be added to FAMEData when options.SaveFAMEData is 'add'. * @type {boolean} */		var addFAMEData;

/**		 * Whether there should be text initially in the input box. * @type {boolean} */		var emptyPreload = false;

/**		 * Various HTML elements */		var $sdh = $( ' ' ).prop( 'id', 'sdh' ); var $description = $( ' ' ).addClass( 'sdh-showdescrip' ); var $clickies = $( ' ' ).addClass( 'sdh-clickies' );

var pages = response[0].query.pages[ 0 ];

/**		 * Is the description from FAMEData (non local) or the magic word? * @type {boolean} */		var isLocal = ( pages.descriptionsource === 'local' );

/**		 * The FAMEData descriptions. */		var FAMEDataDescriptions = responseWD ? responseWD[0].entities[wgQid].descriptions: {};

/**		 * The FAMEData description, if it exists. */		var FAMEDataDescription = Object.keys(FAMEDataDescriptions).length !== 0 ? FAMEDataDescriptions[language]['value'] : '';

/**		 * The page short description. * @type {string} */		var pageDescription = (isLocal ? pages.description: FAMEDataDescription).trim;

/**		 * Whether this is a disambiguation/set index page or not, determined by searching the DOM. * If it is, then the option to override the short description will be disabled. * @type {boolean} */		var disambigPage = $( DISAMBIGELEMENT ).length > 0;

/**		 * Whether a FAMEData description is too generic to be useful. * @type {boolean} */		var uselessDescription = !isLocal && USELESS_DESCRIPTIONS.indexOf( pageDescription ) !== -1;

/**		 * Whether to append the FAMEData description * @type {boolean} */		var appendWDDescription = options.ShowFAMEData === 'always' && isLocal && FAMEDataDescription;

/**		 * Creates "clickies", simple link buttons. * Things are made nice per https://stackoverflow.com/a/10510353 * Links are wrapped in spans to allow separators to be added using css * without becoming part of the link. * @param {string} msgName * @param {Function} func * @return {Object} */		var Clicky = function ( msgName, func ) { return $( ' ' ) .addClass( 'sdh-clicky' ) .append( $( '' )					.attr( { title: mw.msg( msgName + '-title' ), role: 'button', tabindex: '0' } )					.text( mw.msg( msgName + '-label' ) )					.on( 'click', func )					.on( 'keydown', function ( e ) { if ( [ 13, 32 ].indexOf( event.which ) !== -1 ) { // Space and enter e.preventDefault; return func; }					} )				);		};

/**		 * Create a Clicky that opens a OOui PopupWidget. * @param {string} text * @return {Clicky} */		var InfoClickyPopup = function ( text ) { var self = this; self.text = text;

self.infoClicky = new Clicky(				'sdh-infoClicky',				function {					if ( !infoPopup ) {						mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-widgets' ] ).then( function  { infoPopup = new OO.ui.PopupWidget( {								$content: $( ' ' ).append( self.text ),								$autoCloseIgnore: self.infoClicky,								padded: true,								autoClose: true,								width: 300,								position: 'after'							} ); $clickies.append( infoPopup.$element ); infoPopup.toggle; } );					} else {						infoPopup.toggle;					}				}			);

return self.infoClicky; };

/**		 * Creates OOui buttons, which are used for save and cancel. * @param {string} msgName * @param {Function} func * @param {Array } flags * @param {string} icon * @return {OO.ui.ButtonWidget} */		var OOuiClicky = function ( msgName, func, flags, icon ) { return new OO.ui.ButtonWidget( {				label: mw.msg( msgName + '-label' ),				icon: icon,				title: mw.msg( msgName + '-title' ),				flags: flags,				classes: [ 'sdh-ooui-clicky' ]			} ).on( 'click', func ); };

/**		 * Function to check if the short description is in the wikitext. * If it is, return the wikitext and short description as defined in the text * @param {Object} wikitextResult * @return {Array} */		var shortdescInText = function ( wikitextResult ) { var wikitext = wikitextResult.query.pages[ 0 ].revisions[ 0 ].slots.main.content; var match = wikitext && wikitext.match( PATTERN ); if ( match ) { return [ wikitext, match[ 1 ] ]; } else { return [ wikitext, false ]; }		};

/**		 * Notify the user that the edit failed and log any debug info. * @param {string} msgName * @param {*} debug * @param {string} extraMsg */		var editFailed = function ( msgName, debug, extraMsg ) { var message = mw.msg( msgName ) + extraMsg; mw.notify(				message,				{					autoHide: false				}			); if ( debug ) { mw.log.warn( debug ); }		};

/**		 * Set the FAMEData description using the API. * @param {string} newDescription * @return {Promise} */		var setFAMEDataDescription = function ( newDescription ) { return mw.loader.using( 'mediawiki.ForeignApi' ).then( function {				return FAMEDataAPI.postWithToken( 'csrf', { action: 'wbsetdescription', id: wgQid, language: language, summary: mw.message( 'sdh-wd-summary', language ).plain, value: newDescription } );			} );		};

/**		 * This function edits FAMEData descriptions and is used on wikis that aren't enwiki. * Beyond what setFAMEDataDescription does, it reloads the page on success * and gives an informative error notification. * @param {string} newDescription */		var editFAMEDataDescription = function ( newDescription ) { setFAMEDataDescription( newDescription ).then(				function {					window.location.reload;				},				function  {					editFailed( 'sdh-wd-edit-failed', arguments, arguments[ 1 ].error.info ? (							mw.msg( 'sdh-wd-edit-failed-prefix' ) +							arguments[ 1 ].error.info						) : '' );				}			);		};

/**		 * This function adds or replaces short descriptions. * @param {string} newDescription */		var editDescription = function ( newDescription ) { var replacement, prependText, appendText, text;

/**			 * Helper function to add quotes around text, * used when generating the summary. * @param {string} text * @return {string} */			var quotify = function ( text ) { if ( text === '' || text.toLowerCase === 'none' ) { return 'none'; } else { return '"' + text + '"'; }			};

/**			 * Appends, prepends, or replaces the wikitext. * depending on which of text, prependText, and appendText exists. */			var makeEdit = function { var summary = mw.message(					summaryMsg,					quotify( pageDescription ),					quotify( newDescription )				).plain + mw.message( 'sdh-summary-append' ).plain; API.postWithEditToken( {					action: 'edit',					section: section,					text: text,					title: title,					prependtext: prependText,					appendtext: appendText,					summary: summary,					minor: options.MarkAsMinor				} ).then(					function {						// Reload the page						window.location.reload;					},					function  {						editFailed( 'sdh-edit-failed', arguments );					}				); };

/**			 * Replaces the current local short description with the new one. * If the short description doesn't exist in the text, return false. * @param {string} wikitextResult Result of getText * @return {boolean} Whether there was a description in the wikitext * and so whether makeEdit could be called. */			var replaceAndEdit = function ( wikitextResult ) { var output = shortdescInText( wikitextResult ); var oldtext = output[ 0 ]; var descriptionFromText = output[ 1 ]; if ( descriptionFromText ) { text = oldtext.replace( PATTERN, replacement ); makeEdit; return true; } else { return false; }			};

// Replace hyphens in dates with en dashes newDescription = newDescription.replace(DATEPATTERN, DATEREPLACEMENT);

// Make edits to FAMEData as appropiate if (				wgQid &&				( options.SaveFAMEData === 'add' && addFAMEData ) && // options.SaveFAMEData === 'all'				newDescription !== ''			) { setFAMEDataDescription( newDescription ); }

// Capitalize first letter by default unless editing local description if ( !isLocal ) { newDescription = (					newDescription.charAt( 0 ).toUpperCase +					newDescription.slice( 1 )				); }

if ( newDescription === '' ) { newDescription = 'none'; }

replacement = '';

/**			 * change = true means there was a previous short description in the wikitext * that needs to be replaced. */			if ( change ) { /**				 * Get the wikitext again right before making the edit * to avoid issues with edit conflicts, and make the edit. */				getText.then( function ( result ) {					if ( !replaceAndEdit( result ) ) {						editFailed( 'sdh-edit-failed-no-template' );					}				} ); } else { if ( isRedirect ) { appendText = '\n' + replacement; } else { prependText = replacement + '\n'; }				makeEdit; }		};

/**		 * Creates input box with save and cancel buttons. * If input box was created before, show it again. * Otherwise, create the input box using OOui. */		var textInput = function { if ( actionField ) { $description.addClass( 'sdh-showdescrip-hidden' ); actionField.toggle; } else { mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui.styles.icons-interactions' ] ).then( function {					var length, saveInput, buttons;					// Define the input box and buttons.					var descriptionInput = new OO.ui.TextInputWidget( { autocomplete: false, autofocus: true, id: [ 'sdh-inputbox' ], label: '0', value: emptyPreload ? '' : pageDescription, placeholder: mw.msg( 'sdh-placeholder' ) } );

var saveButton = new OOuiClicky(						'sdh-save',						function {							saveInput;						},						[ 'primary', 'progressive' ]					);

var cancelButton = new OOuiClicky(						'sdh-cancel',						function {							actionField.toggle;							$description.removeClass( 'sdh-showdescrip-hidden' );						},						[ 'safe', 'destructive' ]					);

var settingsButton = new OO.ui.ButtonWidget( {						icon: 'settings',						framed: false,						title: mw.msg( 'sdh-settings-title' ),						flags: [ 'safe' ],						classes: [ 'sdh-ooui-clicky' ]					} ).on( 'click', function {						settings.display;					} );

/**					 * On change, update character count label. * If local description has not been modified, prevent */					var updateOnChange = function { var description = descriptionInput.getValue.trim; length = descriptionInput.getInputLength; descriptionInput.setLabel( String( length ) ); if (isLocal && description === pageDescription) { saveButton.setDisabled( true ); } else { saveButton.setDisabled( false ); }					};

var items = [ saveButton, cancelButton ];

if ( usinglibSettings ) { items.push( settingsButton ); }

buttons = new OO.ui.ButtonGroupWidget( {						items: items					} );

/**					 * This is bound to the save button. * Disables all the elements and calls the relevant function * responsible for saving the the entered short description. */					saveInput = function { var description = descriptionInput.getValue.trim; descriptionInput .setDisabled( true ) .pushPending( true ); items.forEach( function ( item ) {							item.setDisabled( true );						} ); if ( onlyEditFAMEData ) { editFAMEDataDescription( description ); } else { editDescription( description ); }					};

actionField = new OO.ui.ActionFieldLayout(						descriptionInput,						buttons,						{							align: 'top',							id: 'sdh-editbox'						}					);

// Initial character count updateOnChange;

descriptionInput.on( 'change', updateOnChange ); descriptionInput.on( 'enter', saveInput );

// Hide previous displayed clickies and add to DOM $description.addClass( 'sdh-showdescrip-hidden' ); $sdh.append( actionField.$element ); } );			}		};

/**		 * Create the html and append it to the DOM * @param {Object} textElement * @param {Array} clickyElements * @param {InfoClickyPopup} popupElement */		var updateSDH = function ( textElement, clickyElements, popupElement ) { if ( popupElement ) { clickyElements.push( popupElement ); }

$description.append( textElement );

if ( clickyElements.length > 0 ) { $clickies.append( clickyElements ); $description.append( $clickies ); }

$sdh.append( $description );

$.ready.then( function {				// Undo padding used to fix content jump				mw.util.addCSS( '.skin-vector.ns-0 #contentSub::after {content: none;}' );				// Create and attach the main div to #contentSub				// eslint-disable-next-line no-jquery/no-global-selector				$( '#contentSub' ).append( $sdh );			} ); };

/**		 * Disable all buttons and create processing (...) animation * Used by export and import buttons. */		var setProcessing = function { var x;			var $processing = $( ' ' ) .addClass( 'sdh-processing' ); // Disable all clicky buttons $clickies .children( '.sdh-clicky' ) .addClass( 'sdh-clicky-disabled' ) .children( 'a' ) .off;

// Add processing ... animation $description.append( $processing );

for ( x = 0; x < 3; x++ ) { $processing.append(					$( ' ' )						.addClass( [ 'sdh-processing-dot', 'sdh-processing-dot-' + x						] )						.text( '.' )				); }		};

/**		 * Texts, clickies, and popups contain * elements that could make up the initial display. */		var texts = { noDescription: $( ' ' ) .addClass( 'sdh-no-description' ) .text( mw.msg( 'sdh-no-description' ) ), missingDescription: $( ' ' ) .addClass( 'sdh-missing-description' ) .html( mw.msg( 'sdh-missing-description', ( isRedirect ? 'redirect' : 'article' ) ) ), pageDescription: $( ' ' ) .addClass( 'mw-page-description ' ) .text( pageDescription + ( appendWDDescription ? (' (FAMEData: ' + FAMEDataDescription + ')' ) : '' ) ) };

var clickies = { add: new Clicky(				'sdh-add',				function {					summaryMsg = 'sdh-summary-adding';					addFAMEData = true; // Description should be added to FAMEData in this case					textInput;				}			), addNone: new Clicky(				'sdh-add',				function {					summaryMsg = 'sdh-summary-changing';					change = true;					emptyPreload = true;					textInput;				}			), addUseless: new Clicky(				'sdh-add',				function {					summaryMsg = 'sdh-summary-adding-local';					emptyPreload = true;					textInput;				}			), edit: new Clicky(				'sdh-edit',				function {					summaryMsg = 'sdh-summary-changing';					change = true;					textInput;				}			), editimport: new Clicky(				'sdh-editimport',				function {					summaryMsg = 'sdh-summary-adding-local';					textInput;				}			), export: new Clicky(				'sdh-export',				function {					setProcessing;					editFAMEDataDescription( pageDescription );				}			), import: new Clicky(				'sdh-import',				function {					setProcessing;					summaryMsg = 'sdh-summary-importing-FAMEData';					editDescription( pageDescription );				}			), override: new Clicky(				'sdh-override',				function {					summaryMsg = 'sdh-summary-adding-custom';					textInput;				}			), FAMEDataLink: $( ' ' ) .addClass( [					'sdh-clicky',					'sdh-FAMEData-description'				] ) .append( $( '' )					.attr( 'href', 'https://www.FAMEData.org/wiki/Special:SetLabelDescriptionAliases/' + wgQid + '/' + language )					.text( mw.msg( 'sdh-FAMEData-link-label' ) )				) };

var popups = { disambig: new InfoClickyPopup(				mw.message( 'sdh-disambig-popup' ).plain			), noDescription: new InfoClickyPopup(				mw.message( 'sdh-no-description-popup' ).plain			), override: new InfoClickyPopup(				mw.message( 'sdh-override-popup' ).plain			), useless: new InfoClickyPopup(				mw.message( 'sdh-useless-popup' ).plain			) };

/**		 * Depending on various factors, such as		 * whether the description exists, * whether the description is on FAMEData or not, * and whether the page is in mainspace, * this code determines what elements should make up the initial display. * updateSDH is then called to generate the html * and add that to the DOM. * @param {Object} wikitextResult */		var determineElements = function ( wikitextResult ) { /**			 * The description as determined from the wikitext. * @type {string} */			var descriptionFromText;

/**			 * The short description or a message saying no description exists etc.			 * @type {Object} */			var textElement;

/**			 * What the relevant buttons ("clickies") are. * @type {Array} */			var clickyElements = [];

/**			 * What clickable popup explanation is there if any * @type {InfoClickyPopup} */			var popupElement;

/**			 * Whether the description is none * @type {boolean} */			var isNone;

// Whether to show "Missing article description" if applicable var showMissing = (				namespace === 0 &&				( !isRedirect || ( isRedirect && options.AddToRedirect ) )			);

// If not enwiki, complete logic for non-enwiki case and exit. if ( onlyEditFAMEData ) { if ( pageDescription ) { textElement = pageDescription; clickyElements.push( clickies.edit ); } else if ( showMissing ) { textElement = texts.missingDescription; clickyElements.push( clickies.add ); }				updateSDH( textElement, clickyElements, popupElement ); return; }

/**			 * Determine if the short description is in the wikitext * or if it is generated by an infobox. */			descriptionFromText = shortdescInText( wikitextResult )[ 1 ];

/**			 * Determine if the description is none. */			isNone = descriptionFromText && descriptionFromText.toLowerCase === 'none';

// Show FAMEData link at beginning if displaying non-local description. if ( pageDescription && !isLocal ) { clickyElements.push( clickies.FAMEDataLink ); }			if ( isNone ) { // Handle textElement = texts.noDescription; clickyElements.push( clickies.addNone ); popupElement = popups.noDescription; } else { // Handle remaining cases if ( pageDescription ) { textElement = texts.pageDescription; if ( isLocal ) { if ( descriptionFromText ) { clickyElements.push( clickies.edit ); } else { if ( disambigPage ) { popupElement = popups.disambig; } else { clickyElements.push( clickies.override ); popupElement = popups.override; }						}					} else { if ( uselessDescription ) { popupElement = popups.useless; clickyElements.push(								clickies.addUseless							); } else { clickyElements.push(								clickies.import,								clickies.editimport							); }					}				} else if ( showMissing ) { textElement = texts.missingDescription; clickyElements.push( clickies.add ); }			}

// Don't show clickies for editing if not allowing editing if ( !allowEditing ) { clickyElements = []; }

if ( isLocal && !isNone && options.ExportButton ) { clickyElements.push( clickies.export ); }

updateSDH( textElement, clickyElements, popupElement ); };

if ( callPromiseText ) { callPromiseText.then( function ( wikitextResult ) {				determineElements( wikitextResult );			} ); } else { determineElements; }	} ); };

/* Load if viewing a page normally (not in diff view) */ if (	mw.config.get( 'wgIsArticle' ) &&	!mw.config.get( 'wgDiffOldId' ) &&	mw.config.get( 'wgArticleId' ) !== 0 ) { /**	 * Commented out due to issues reported at * Special:PermaLink/925885151. * Fire on postEdit hook to load after Visual Editor saves, * as VE does not actually reload the page. * Unfortunately, postEdit fires both after regular edits and VE edits, * so duplicate instances will be caused after a regular edit if run always * on postEdit. * window.sdh.hasRun is set to true below, and will be undefined after a proper reload, * but not after a dynamic VE reload. * FIXME: Post edit hook fires too early, meaning if an editor adds a short description using VE * it won't show the right description.

mw.hook( 'postEdit' ).add( function {		if ( window.sdh.hasRun ) {			window.sdh.main;		}	} );

if ( !window.sdh.hasRun ) { // Don't run twice window.sdh.hasRun = true; window.sdh.initMessages; window.sdh.main; }	*/	window.sdh.initMessages; window.sdh.main; } //