MediaWiki:Gadget-interwiki.js

From Wikimedia Incubator

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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * The interwiki gadget
 * 
 * This gadget does three separate but interrelated things related to interwiki
 * links:
 * * On Wiktionary entries, it automatically adds interwiki links to pages in
 *   existing Wiktionaries that have the same spelling, in the same way that
 *   the Cognate extension does on actual Wiktionaries.
 * * On all other pages (including pages outside test wikis), it changes the
 *   non-functional "add interlanguage link" links so that when you click them,
 *   a dialog to add interwiki links appears. This will then add the module-
 *   based {{INTERWIKI}} template to the page, adding interwiki links from
 *   Wikidata.
 * * On pages that already have the {{INTERWIKI}} template, it will add a link
 *   to Wikidata to the sidebar, and add a toolbar section "In other projects"
 *   like what pages on existing wikis have.
 * 
 * The gadget should work on both desktop and mobile, in all supported skins.
 * 
 * @author Jon Harald Søby
 * @author Nikki
 * @version 1.7.2 (2024-02-15)
 */

( function() {
	var conf = mw.config.get( [
			'skin',
			'wgAction',
			'wgArticleId',
			'wgCurRevisionId',
			'wgIsProbablyEditable',
			'wgNamespaceNumber',
			'wgPageContentLanguage',
			'wgPageName',
			'wgRevisionId',
			'wgULSBabelLanguages',
			'wgUserId',
			'wgUserLanguage',
			'wgWmincRealPagename',
			'wgWmincTestwikiProject'
		] ),
		babel = conf.wgULSBabelLanguages || [],
		fallbackLanguageChain = mw.language.getFallbackLanguageChain(),
		langPriList = babel.concat( fallbackLanguageChain );

	function capitalize( text ) {
		var userlang = conf.wgUserLanguage,
			lowercaseLangs = mw.config.get( 'wgWmincLowercaseLanguages' ) || []; // Not in conf because it loads later
		if ( lowercaseLangs && lowercaseLangs.includes( userlang ) ) {
			return text;
		} else {
			return text.charAt( 0 ).toUpperCase() + text.substring( 1 );
		}
	}

	function sort_languages( a, b ) {
		// Put Wiktionary links for languages in the user's Babel box first
		if ( babel.includes( a.lang ) && !babel.includes( b.lang ) )
			return -1;
		if ( babel.includes( b.lang ) && !babel.includes( a.lang ) )
			return 1;
		// Then sort alphabetically by language code
		return a.lang.toLocaleLowerCase().localeCompare( b.lang.toLocaleLowerCase() );
	}

	function analyzeInput( input ) {
		var langcode, dbsuffix, pagename;
		
		if ( /(.*wikidata.org\/.*[Qq][1-9]\d*.*|^[Qq][1-9]\d*$)/.test( input ) ) {
			// If the input is a Wikidata item ID or Wikidata item URL
			return {
				qid: input.match( /[Qq][1-9]\d*/ )[ 0 ]
			};
		} else if ( /^https?:\/\/(commons\.(m\.)?wikimedia|[a-z\-]+\.(m\.)?wik(ibooks|inews|ipedia|iquote|isource|iversity|ivoyage|tionary))\.org\//.test( input ) ) {
			// If the input is a URL to a sister project
			var domain = input.split( '/' )[ 2 ].replace( '.m.', '.' ).split( '.' );
			langcode = domain[ 0 ];
			dbsuffix = domain[ 1 ];
			if ( langcode === 'commons' ) {
				dbsuffix = 'wiki';
			}
			if ( /\.org\/wiki\/.+/.test( input ) ) {
				pagename = input.split( '/' )[ 4 ].split( '?' )[ 0 ].split( '#' )[ 0 ];
			} else if ( /\.org\/w\/index\.php/.test( input ) ) {
				pagename = mw.util.getParamValue( 'title', input );
			} else {
				return false;
			}
			if ( /^(b|wikibooks|n|wikinews|q|wikiquote|s|wikisource|v|wikiversity|voy|wikivoyage|wikt|wiktionary):/i.test( pagename ) ) {
				var iwProjectMapping = {
					'b': 'wikibooks',
					'n': 'wikinews',
					'q': 'wikiquote',
					's': 'wikisource',
					'v': 'wikiversity',
					'voy': 'wikivoyage',
					'wikt': 'wiktionary'
				};
				var iwPrefix = pagename.substring( 0, pagename.indexOf( ':' ) ).toLowerCase();
				dbsuffix = iwProjectMapping[ iwPrefix ] || iwPrefix;
				pagename = pagename.substring( pagename.indexOf( ':' ) );
			}
			dbsuffix = dbsuffix.replace( 'wikipedia', 'wiki' );
		} else if ( /^([a-z]{2,3}|simple)([\-_][a-z]+){0,2}:.+$/.test( input ) ) {
			// If the input is "old interwiki style" (like "en:Something")
			var incProjectMapping = {
				'b': 'wikibooks',
				'n': 'wikinews',
				'p': 'wiki',
				'q': 'wikiquote',
				's': 'wikisource',
				't': 'wiktionary',
				'v': 'wikiversity',
				'y': 'wikivoyage'
			};
			langcode = input.substring( 0, input.indexOf( ':' ) );
			if ( langcode === 'commons' ) {
				dbsuffix = 'wiki';
			} else {
				dbsuffix = incProjectMapping[ conf.wgWmincTestwikiProject ];
			}
			pagename = input.substring( input.indexOf( ':' ) + 1 );
		} else {
			return false;
		}
		
		pagename = mw.util.percentDecodeFragment( pagename );
		langcode = langcode.replace( /-/g, '_' ).replace( 'be_tarask', 'be_x_old' );
		
		return {
			dbname: langcode + dbsuffix,
			pagename: pagename
		};
	}
	
	if ( $( '#wminc-interwiki' ).length && /^Q\d+$/.test( $( '#wminc-interwiki' ).attr( 'data-wikidata-id' ) ) ) {
		// If there is an {{INTERWIKI}} template present, do this
		new mw.Api().loadMessagesIfMissing( [
			'accesskey-t-wikibase', // g
			'tooltip-t-wikibase', // Link to connected data repository item
			'wikibase-dataitem',
			'wikibase-otherprojects',
			'wikibase-otherprojects-commons',
			'wikibase-otherprojects-mediawiki',
			'wikibase-otherprojects-meta',
			'wikibase-otherprojects-sources',
			'wikibase-otherprojects-species',
			'wikibase-otherprojects-wikibooks',
			'wikibase-otherprojects-wikidata',
			'wikibase-otherprojects-wikimania',
			'wikibase-otherprojects-wikinews',
			'wikibase-otherprojects-wikipedia',
			'wikibase-otherprojects-wikiquote',
			'wikibase-otherprojects-wikisource',
			'wikibase-otherprojects-wikiversity',
			'wikibase-otherprojects-wikivoyage',
			'wikibase-otherprojects-wiktionary'
		] ).then( function() {
			var qid = $( '#wminc-interwiki' ).attr( 'data-wikidata-id' );
			mw.config.set( 'wgWikibaseItemId', qid );
			mw.hook( 'incubator.wikidata' ).fire( qid );
			mw.util.addPortletLink(
				'p-tb',
				'https://www.wikidata.org/wiki/Special:EntityPage/' + qid,
				mw.msg( 'wikibase-dataitem' ),
				't-wikibase',
				mw.msg( 'tooltip-t-wikibase' ),
				mw.msg( 'accesskey-t-wikibase' ),
				$( '.menu__item--page-actions-overflow-cite' ).closest( 'li' ) // Minerva specific, does nothing in other skins
			);
			if ( $( '.wminc-otherprojects' ).length ) {
				var $opportlet = $( '#p-coll-print_export' ).clone().attr( 'id', 'p-wikibase-otherprojects' );
				var $opportletheader = $opportlet.find( '.vector-menu-heading, h3' );
				$opportletheader.attr( 'id', 'p-wikibase-otherprojects-label' );
				if ( $opportletheader.find( '.vector-menu-heading-label' ).length ) {
					$opportletheader = $opportletheader.find( '.vector-menu-heading-label' );
				}
				$opportletheader.text( mw.msg( 'wikibase-otherprojects' ) );
				$opportlet.find( 'ul' ).empty();
				var insertportletafter = conf.skin === 'timeless' ? '#p-lang' : '#p-coll-print_export';
				$opportlet.insertAfter( insertportletafter );
				$( '.wminc-otherprojects' ).each( function() {
					var project = $( this ).attr( 'data-project' ),
						url = $( this ).attr( 'data-url' );
					mw.util.addPortletLink( 'p-wikibase-otherprojects', url, mw.msg( 'wikibase-otherprojects-' + project ) ).classList.add( 'wb-otherproject-link', 'wb-otherproject-' + project );
				});
			}
			mw.hook( 'mobileFrontend.languageSearcher.onOpen' ).add( function( langoverlay ) {
				$( langoverlay.$el ).find( 'span.title' ).each( function() {
					var title = $( this ).text();
					title = title.replace( /^(b|wikibooks|n|wikinews|q|wikiquote|s|wikisource|v|wikiversity|voy|wikivoyage|wikt|wiktionary):/i, '' );
					$( this ).text( title );
				});
			});
		});
	} else if (
		// We are on a Wiktionary project
		conf.wgWmincTestwikiProject === 't' &&
		// We are in the main namespace
		conf.wgNamespaceNumber === 0 &&
		// We are on an actual page (not a project's info page)
		conf.wgWmincRealPagename &&
		// We are viewing the page (not editing, deliting, etc.)
		conf.wgAction === 'view'
	) {
		// The API query only includes pages on other domains.
		// To avoid having to do a second query to find out whether
		// the page exists on the API's domain, we can instead take
		// advantage of aa.wiktionary.org being a closed wiki with no pages.
		var aaapi = new mw.ForeignApi( 'https://aa.wiktionary.org/w/api.php' ),
			lemma = conf.wgWmincRealPagename;

		// Add OOUI's pending class as a visual hint that the element will change
		$( '#p-lang, #p-lang-btn, #language-selector' ).addClass( 'oo-ui-pendingElement-pending' );

		aaapi.get( {
			'action': 'parse',
			'prop': 'langlinks',
			'text': '',
			'title': lemma,
		} ).then( function ( data ) {
			
			if ( !data.parse.langlinks.length ) {
				$( '#p-lang, #p-lang-btn, #language-selector' ).removeClass( 'oo-ui-pendingElement-pending' );
				return;
			}
			
			if ( conf.skin === 'vector-2022' ) {
				new mw.Api().loadMessagesIfMissing( [
					'vector-language-button-label',
					'vector-language-button-aria-label',
					'interlanguage-link-title'
				] ).then( function() {
					$( '#p-lang-btn' )	
						.removeClass( [ 'mw-portlet-empty',  'oo-ui-pendingElement-pending' ] )
						.addClass( [ 'mw-portlet', 'mw-portlet-lang' ] );
					$( '#p-lang-btn-checkbox' )
						.attr( 'aria-label', mw.msg( 'vector-language-button-aria-label', mw.language.convertNumber( data.parse.langlinks.length ) ) );
					$( '#p-lang-btn-label .vector-menu-heading-label, #p-lang-btn-sticky-header span:last-of-type' )
						.text( mw.msg( 'vector-language-button-label', mw.language.convertNumber( data.parse.langlinks.length ) ) );
					$.each( data.parse.langlinks.sort( sort_languages ), function( i, v ) {
						var $li = $( '<li>' )
							.addClass( 'interlanguage-link interwiki-' + v.lang + ' mw-list-item' );
						var $langlink = $( '<a>' )
							.addClass( 'interlanguage-link-target' )
							.attr( 'href', v.url )
							.attr( 'title', mw.msg( 'interlanguage-link-title', v[ '*' ], v.langname ) )
							.attr( 'hreflang', v.lang )
							.attr( 'lang', v.lang )
							.html( '<span>' + v.autonym + '</span>' )
							.appendTo( $li );
						$( '#p-lang-btn ul.vector-menu-content-list' ).append( $li );
					} );
				} );
			} else if ( conf.skin === 'minerva' ) {
				new mw.Api().loadMessagesIfMissing( [
					'parentheses',
					'mobile-frontend-languages-structured-overlay-all-languages-header',
					'mobile-frontend-languages-structured-overlay-suggested-languages-header'
				] ).then( function() {
					$( '#language-selector' ).removeClass( 'oo-ui-pendingElement-pending' );
					var $langselector = $( '#language-selector a' );
					$langselector.removeClass( 'disabled' );
					$langselector.attr( 'href', '#' ); // so that it goes to the language page instead of showing an error
					mw.hook( 'mobileFrontend.languageSearcher.onOpen' ).add( function( langoverlay ) {
						$( langoverlay.$el ).find( '.panel' ).remove();
						var $overlay = $( langoverlay.$el ).find( '.overlay-content-body' );
						var $suggestedLangHeader = $( '<h3>' )
							.addClass( 'list-header' )
							.text( mw.msg( 'mobile-frontend-languages-structured-overlay-suggested-languages-header' ) );
						var $suggestedLangList = $( '<ol>' ).addClass( 'site-link-list suggested-languages' );
						var suggestedLangCount = 0;
						var $otherLangHeader = $( '<h3>' )
							.addClass( 'list-header' )
							.text( mw.msg( 'mobile-frontend-languages-structured-overlay-all-languages-header' ) );
						var $otherLangList = $( '<ol>' ).addClass( 'site-link-list all-languages' );
						var otherLangCount = 0;
						
						$.each( data.parse.langlinks.sort( sort_languages ), function( i, v ) {
							var $li = $( '<li>' );
							var $langlink = $( '<a>' )
								.attr( 'lang', v.lang )
								.attr( 'hreflang', v.lang )
								.attr( 'href', v.url )
								.attr( 'dir', $.uls.data.getDir( v.lang ) )
								.append( $( '<span class="autonym">').text( v.autonym ) )
								.append( $( '<span class="title">').text( lemma ) );
							$li.append( $langlink );
							if ( babel.includes( v.lang ) ) {
								$suggestedLangList.append( $li );
								++suggestedLangCount;
							} else {
								$otherLangList.append( $li );
								++otherLangCount;
							}
						} );
						
						if ( suggestedLangCount ) {
							$suggestedLangHeader.append( ' ' + mw.msg( 'parentheses', mw.language.convertNumber( suggestedLangCount ) ) );
							$overlay.append( $suggestedLangHeader, $suggestedLangList );
						}
						if ( otherLangCount ) {
							$otherLangHeader.append( ' ' + mw.msg( 'parentheses', mw.language.convertNumber( otherLangCount ) ) );
							$overlay.append( $otherLangHeader, $otherLangList );
						}
					});
				});
			} else {
				$( '#p-lang' ).removeClass( 'oo-ui-pendingElement-pending' );
				$( '#p-lang ul.vector-menu-content-list' ).empty();
				$.each( data.parse.langlinks.sort( sort_languages ), function ( i, v ) {
					var autonym = v.autonym;
					if ( v.lang !== 'ka' ) {
						autonym = autonym.charAt( 0 ).toUpperCase() + autonym.slice( 1 );
					}
					mw.util.addPortletLink( 'p-lang', v.url, autonym );
				} );
			}
		} );
	} else {
		if (
			// Not in a test wiki
			!conf.wgWmincRealPagename ||
			// On a talk page
			conf.wgNamespaceNumber % 2 !== 0 ||
			// In the Module namespace
			( conf.wgNamespaceNumber === 828 && !/\/doc$/.test( conf.wgWmincRealPagename ) ) ||
			// In a test Wiktionary entry (interwikis there are handled by another part of this gadget)
			( conf.wgWmincTestwikiProject === 't' && conf.wgNamespaceNumber === 0 ) ||
			// The page doesn't exist
			conf.wgArticleId === 0 ||
			// You're editing the page, not simply viewing it
			conf.wgAction !== 'view' ||
			// The user is not logged in
			!conf.wgUserId ||
			// You are viewing an old revision of the page
			( conf.wgRevisionId !== conf.wgCurRevisionId ) ||
			// You can edit the page
			!conf.wgIsProbablyEditable
		) {
			// Do nothing
			return;
		} else {
			// If the following has a match, that means that there are old-style
			// interwiki links on the page. If that is the case, we will attempt
			// to switch them out with the {{INTERWIKI}} template instead.
			var replaceOldIw = !!$( '.mw-portlet-lang, #p-lang' ).find( '.interlanguage-link-target' ).length || !!$( '#language-selector a' ).attr( 'href' ),
				triggerSelector = '#wminc-addinterwiki a, #p-lang-btn, #p-lang-btn-sticky-header',
				oldIwHref;
			new mw.Api().getMessages(
				// This message is loaded in a special way because it should
				// preferrably be in the page's content language and not in the
				// user interface language.
				[
					'wminc-gadget-iw-editsummary'
				],
				{ amlang: conf.wgPageContentLanguage }
			).then( function( msgs ) {
				mw.messages.set( msgs );
			});
			new mw.Api().loadMessagesIfMissing( [
				'accesskey-publish',
				'accesskey-t-wikibase',
				'pipe-separator',
				'wikibase-addlinkstitle',
				'wikibase-linkitem-addlinks',
				'wminc-gadget-iw-add-wikidata',
				'wminc-gadget-iw-dialog-cancel',
				'wminc-gadget-iw-dialog-editpage',
				'wminc-gadget-iw-dialog-learn-more',
				'wminc-gadget-iw-dialog-publish',
				'wminc-gadget-iw-dialog-qid-placeholder',
				'wminc-gadget-iw-dialog-replace-editinstead',
				'wminc-gadget-iw-dialog-replace-text',
				'wminc-gadget-iw-dialog-title',
				'wminc-gadget-iw-dialog-text',
				'wminc-gadget-iw-dialog-wikidata-search',
				'wminc-gadget-iw-error-invalid',
				'wminc-gadget-iw-error-reload',
				'wminc-gadget-iw-error-title'
			] ).then( function() {
				var portlet = 'p-lang',
					addlinkmsg = 'wikibase-linkitem-addlinks';
				if ( conf.skin === 'vector-2022' ) {
					portlet = 'p-tb';
					addlinkmsg = 'wikibase-addlinkstitle';
				}
				
				if ( replaceOldIw ) {
					addlinkmsg = 'wminc-gadget-iw-add-wikidata';
					if ( conf.skin !== 'minerva' ) {
						triggerSelector = '#wminc-addinterwiki a';
						var oldIwSelector = $( '.mw-portlet-lang, #p-lang ' ).find( '.interlanguage-link-target' ),
							random = Math.floor( Math.random() * oldIwSelector.length ),
							chosenIw = oldIwSelector[ random ];
						for ( var i = 0; i < langPriList.length; i++ ) {
							var lang = langPriList.reverse()[ i ];
							if ( $( '.interlanguage-link-target[lang=' + lang + ']' ).length ) {
								chosenIw = $( '.interlanguage-link-target[lang=' + lang + ']' )[ 0 ];
							}
						}
						oldIwHref = chosenIw.href;
					} else {
						if ( $( '#language-selector a' ).hasClass( 'disabled' ) ) {
							triggerSelector = '#language-selector a';
							$( triggerSelector )
								.removeClass( [ 'mw-ui-icon-wikimedia-language-base20', 'disabled' ] )
								.addClass( 'gadget-iw-icon-addlang' );
						} else {
							$( 'body' ).append( '<a id="wminc-dummylink" style="display:none;"></a>' );
							triggerSelector = '#wminc-dummylink';
							mw.hook( 'mobileFrontend.languageSearcher.onOpen' ).add( function( langoverlay ) {
								var oldIws = langoverlay.$languageItems,
									random = Math.floor( Math.random() * oldIws.length ),
									chosenIw = oldIws[ random ];
								for ( var i = 0; i < langPriList.length; i++ ) {
									var lang = langPriList.reverse()[ i ];
									if ( $( langoverlay.$el ).find( 'a.' + lang ).length ) {
										chosenIw = $( langoverlay.$el ).find( 'a.' + lang )[ 0 ];
									}
								}
								oldIwHref = chosenIw.href;
								var $addwdlink = $( '<ol>' ).addClass( 'site-link-list' )
									.append( $( '<li id="wminc-addinterwiki">' )
										.append( $( '<a>' )
											.append( '<span class="mw-ui-icon gadget-iw-icon-addlang"></span>' )
											.append( '<span>' + mw.msg( addlinkmsg ) + '</span>' )
										.on( 'click', function() {
											$( '#wminc-dummylink' ).click();
										})
									)
								);
								$( langoverlay.$el ).find( '.overlay-content-body' ).prepend( $addwdlink );
							});
						}
					}
				}
				mw.util.addPortletLink(
					portlet,
					mw.util.getUrl( null, { action: 'edit' } ),
					mw.msg( addlinkmsg ),
					'wminc-addinterwiki',
					null,
					mw.msg( 'accesskey-t-wikibase' )
				);

				// Minerva specific
				if ( $( '#language-selector a' ).hasClass( 'disabled' ) ) {
					triggerSelector = '#language-selector a';
					$( triggerSelector )
						.removeClass( [ 'mw-ui-icon-wikimedia-language-base20', 'disabled' ] )
						.addClass( 'gadget-iw-icon-addlang' );
				}

				$( triggerSelector ).on( 'click', function( e ) {
					e.preventDefault();
					e.stopPropagation();
					
					function AddInterwikiDialog( config ) {
						AddInterwikiDialog.super.call( this, config );
					}
					OO.inheritClass( AddInterwikiDialog, OO.ui.ProcessDialog );
					AddInterwikiDialog.static.name = 'addInterwikiDialog';
					AddInterwikiDialog.static.title = mw.msg( 'wminc-gadget-iw-dialog-title' );
					
					var saveButton = new OO.ui.ActionWidget( {
						action: 'save',
						label: mw.msg( 'wminc-gadget-iw-dialog-publish' ),
						flags: [ 'primary', 'progressive' ],
						framed: true,
						tabIndex: replaceOldIw ? 1 : 2,
						accessKey: mw.msg( 'accesskey-publish' )
					});
					
					AddInterwikiDialog.static.actions = [
						saveButton,
						{
							label: mw.msg( 'wminc-gadget-iw-dialog-cancel' ),
							flags: 'safe',
							tabIndex: 3,
							icon: 'close',
							invisibleLabel: true
						}
					];
					
					var qidinput = new OO.ui.TextInputWidget( {
						placeholder: mw.msg( 'wminc-gadget-iw-dialog-qid-placeholder', 'Q12345' ),
						tabIndex: 1,
						validate: analyzeInput,
						spellcheck: false,
						accessKey: ','
					}).on( 'change', function( value ) {
						qidinput.getValidity().then( function() {
							saveButton.setDisabled( false );
						}, function() {
							saveButton.setDisabled( true );
						});
					}).on( 'enter', function() {
						qidinput.getValidity().then( function() {
							dialog.executeAction( 'save' );
						}, function() {
							saveButton.setDisabled( true );
						});
					});
					
					var editButton = new OO.ui.ButtonWidget( {
						label: capitalize( mw.msg( 'wminc-gadget-iw-dialog-editpage' ) ),
						title: capitalize( mw.msg( 'wminc-gadget-iw-dialog-editpage' ) ),
						flags: [ 'progressive', 'primary' ],
						href: mw.util.getUrl( null, { action: 'edit' } )
					});
					
					AddInterwikiDialog.prototype.initialize = function() {
						AddInterwikiDialog.super.prototype.initialize.apply( this, arguments );
						this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
						if ( replaceOldIw ) {
							this.content.$element
								.append( $( '<p>' )
									.text( mw.msg( 'wminc-gadget-iw-dialog-replace-text' ) )
								).append( $( '<p>' )
									.append( $( '<a>' )
										.attr( 'href', oldIwHref )
										.attr( 'target', '_blank' )
										.text( mw.util.percentDecodeFragment( oldIwHref ) ) )
								).append( $( '<p>' )
									.text( mw.msg( 'wminc-gadget-iw-dialog-replace-editinstead' ) )
								).append( editButton.$element );
						} else {
							this.content.$element
								.append( $( '<p>' ).text( mw.message( 'wminc-gadget-iw-dialog-text' ).parse() ) )
								.append( $( '<p>' ).css( 'text-align', 'end' )
									.append( $( '<a>' )
										.attr( 'href', mw.util.getUrl( 'Special:MyLanguage/Incubator:Wikidata' ) )
										.attr( 'target', '_blank' )
										.text( mw.msg( 'wminc-gadget-iw-dialog-learn-more' ) ) )
									.append( mw.msg( 'pipe-separator' ) )
									.append( $ ( '<a>' )
										.attr( 'href', 'https://www.wikidata.org/wiki/Special:Search/' + conf.wgWmincRealPagename )
										.attr( 'target', '_blank' )
										.text( mw.msg( 'wminc-gadget-iw-dialog-wikidata-search' ) )
									))
								.append( qidinput.$element );
						}
						this.$body.append( this.content.$element );
					};
					AddInterwikiDialog.prototype.getActionProcess = function( action ) {
						var dialog = this;
						if ( action && action === 'save' && ( replaceOldIw || qidinput.getValue().length ) ) {
							dialog.pushPending();
							var input;
							if ( replaceOldIw ) {
								input = analyzeInput( oldIwHref );
								if ( !input ) {
									dialog.close();
									mw.notify( mw.msg( 'wminc-gadget-iw-error-invalid' ), {
										title: mw.msg( 'wminc-gadget-iw-error-title' ),
										type: 'error',
										autoHide: false
									} );
									return;
								}
							} else {
								input = analyzeInput( qidinput.getValue() );
							}
							return new OO.ui.Process( function() {
								if ( input ) {
									var qidres;
									var wdapi = new mw.ForeignApi( 'https://www.wikidata.org/w/api.php' );
									if ( input.dbname ) {
										qidres = wdapi.get( {
											action: 'wbgetentities',
											sites: input.dbname,
											titles: input.pagename,
											props: null,
											normalize: 'yes'
										});
									} else {
										qidres = wdapi.get( {
											action: 'wbgetentities',
											ids: input.qid,
											props: null
										});
									}
									
									qidres.then( function( data ) {
										var qid = Object.keys( data.entities )[ 0 ];
										if ( qid === '-1' || data.entities[ qid ].hasOwnProperty( 'missing' ) ) {
											dialog.close();
											mw.notify( mw.msg( 'wminc-gadget-iw-error-invalid' ), {
												title: mw.msg( 'wminc-gadget-iw-error-title' ),
												type: 'error',
												autoHide: false
											} );
										} else {
											var oldIwRegex = /(\[\[ *((?!mw:|doi:|voy)[a-z]{2,3}|simple)(-[a-z]+){0,2} *: *.+?\]\][\u00AD ]*\n*)+/g,
												iwplaceholder = '!!IWGADGETREPLACEME!!',
												insert = '{{INTERWIKI|' + qid + '}}';
											new mw.Api().edit( conf.wgPageName, function( revision ) {
												var source = revision.content,
													tags = 'gadget-interwiki';
												source = source.replace( oldIwRegex, iwplaceholder );
												if ( conf.skin === 'minerva' ) {
													tags = 'gadget-interwiki|gadget-interwiki-mobile';
												}
												if ( /<noinclude>(.|\n)*?!!IWGADGETREPLACEME!!(.|\n)*?<\/noinclude>/gi.test( source ) ) {
													source = source.replace( iwplaceholder, insert + '\n' );
													// .replace only replaces the first occurence.
													// Just in case, let's replace any other occurences too.
													source = source.replace( new RegExp( iwplaceholder, 'g' ), '' ).trimEnd();
												} else if ( conf.wgNamespaceNumber === 10 ) {
													source = source.replace( new RegExp( iwplaceholder, 'g' ), '' );
													if ( /<\/noinclude>\s*$/i.test( source ) ) {
														source = source.replace( /<\/noinclude>\s*$/i, '\n\n' + insert + '\n</noinclude>' );
													} else {
														source = source.trimEnd() + '<noinclude>\n\n' + insert + '\n</noinclude>';
													}
												} else {
													source = source.replace( new RegExp( iwplaceholder, 'g' ), '' );
													source = source.trimEnd() + '\n\n' + insert;
												}
												return {
													text: source,
													tags: tags,
													summary: mw.msg( 'wminc-gadget-iw-editsummary', qid ),
													minor: true,
													baserevid: conf.wgRevisionId
												};
											}).then( function( data ) {
												location.reload();
											}).catch( function( err ) {
												dialog.close();
												mw.notify( $( '<p>' + err + '</p><p>' + mw.msg( 'wminc-gadget-iw-error-reload' ) + '</p>' ), {
													title: mw.msg( 'wminc-gadget-iw-error-title' ),
													type: 'error',
													autoHide: false
												});
											});
										}
									}).catch( function( err ) {
										dialog.close();
										mw.notify( mw.msg( 'wminc-gadget-iw-error-invalid' ), {
											title: mw.msg( 'wminc-gadget-iw-error-title' ),
											type: 'error',
											autoHide: false
										} );
									});
								}
							});
						} else if ( action ) {
							return new OO.ui.Process( function() {
								dialog.close( { action: action } );
							});
						}
						return AddInterwikiDialog.super.prototype.getActionProcess.call( this, action );
					};
					
					AddInterwikiDialog.prototype.getReadyProcess = function( data ) {
						return AddInterwikiDialog.parent.prototype.getReadyProcess.call( this, data ).next( function() {
							qidinput.focus();
						}, this );
					};
					
					var windowManager = new OO.ui.WindowManager();
					$( document.body ).append( windowManager.$element );
					
					var dialog = new AddInterwikiDialog();
					windowManager.addWindows( [ dialog ] );
					windowManager.openWindow( dialog );
				});
			});
		}
	}
})();