Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
//[[ВП:Гаджетfk/Упрощение ссылок]]

// Toolbar buttons

var addOldToolbarButton = function() {
	var $toolbar = $( '#gadget-toolbar' );
	if ( !$toolbar.length ) {
		$toolbar = $( '#toolbar' );
	}
	$( '<div>' )
		.addClass( 'mw-toolbar-editbutton' )
		.attr( 'alt', 'Раскодировщик URL' )
		.attr( 'title', 'Раскодировать URL перед курсором или все URL в выделенном тексте' )
		.css( 'background-image', 'url(//upload.wikimedia.org/wikipedia/commons/6/63/Link_go_toolbar.png)' )
		.appendTo( $toolbar )
		.on( 'click', urlDecoderRun );
};

var addNewToolbarButton = function() {
	$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
		'section': 'main',
		'group': 'format',
		'tools': {
			'urldecoder': {
				label: 'Раскодировать URL перед курсором или все URL в выделенном тексте',
				type: 'button',
				icon: '//upload.wikimedia.org/wikipedia/commons/0/01/Link_go_remake.png',
				action: {
					type: 'callback',
					execute: function() {
						urlDecoderRun();
					}
				}
			}
		}
	} );
};

if ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 ) {
	mw.loader.using( [ 'user.options', 'jquery.textSelection' ], function () {
		if ( mw.user.options.get( 'usebetatoolbar' ) === 1 ) {
			if ( mw.user.options.get( 'showtoolbar' ) === 1 ) {
				$.when(
					mw.loader.using( ['ext.wikiEditor', 'schema.Edit'] ),
					$.ready
				).then( addNewToolbarButton );
			}
		} else {
			mw.loader.using( 'mediawiki.toolbar', function() {
				$( addOldToolbarButton );
			});
		}
	} );
}

mw.hook( 'ext.lqt.textareaCreated' ).add( addNewToolbarButton );


function urlDecoderRun() { //main function

	//2nd-lvl WMF domains; old secure link:  .../wikipedia/mediawiki, .../wikipedia/foundation 
	var wmDomain = {
		mediawiki: 'mw',
		wikidata: 'd',
		wikimediafoundation: 'foundation'
	};

	//2nd-lvl WMF domains with multiple languages; old secure link:  wikinews/en 
	var wmDomainM = {
		wikipedia: 'w',
		wikibooks: 'b',
		wikinews: 'n',
		wikiquote: 'q',
		wikisource: 's',
		wikiversity: 'v',
		wikivoyage: 'voy',
		wiktionary: 'wikt'
	};

	//3rd-lvl WMF domains on .wikimedia.org; for some reason old secure link is  wikipedia/*
	var wmSubDomains = /^(meta|commons|incubator|species|strategy)$/;

	var httpRegExp = '(https?:\\/\\/[^\\]\\[\\n\\r<>" ]+)', // any chars except []<>" and \n and spaces
		localPrefix = WMPrefixes( unSecure( mw.config.get( 'wgServer' ).replace( /^\/\//,'http://' ) + mw.config.get( 'wgScript' ) ) ),
		newText,
		isBeforeCursor,
		colonNS,
		tbox = $( '#wpTextbox1' ).focus(),
		oldText = tbox.textSelection( 'getSelection' );

	if ( oldText ) { //there was selection

		var rx = RegExp( '(\\[{0,2})' + httpRegExp + '([^\\]\\[\\n\\r]*?\\]\\]?)?', 'ig' );
		newText = oldText.replace( rx, simplifyMatched );

		if ( window.urlDecoderIntLinks ) {
			var ut = '(' + mw.config.get( 'wgFormattedNamespaces' )[3].replace( / /g, '_' ) + '|user_talk)'; //both localized and canonical 'user_talk'
			ut = RegExp( '\\[\\[' + ut.toLowerCase() + ':[^#]+$', 'i' );
			newText = newText.replace( /\[\[[^\]\|\n]+/g, function ( lnk ) {
				return ut.test(lnk) ? lnk : decodeAnchor( lnk ); // skip user_talk, usually found in signatures
			} );
		}

		if ( newText == oldText) {
			return;
		}

	} else { //process text before cursor

		isBeforeCursor = true;

		//move back enough characters
		var caretPos = tbox.textSelection( 'getCaretPosition' ),
			beginPos = caretPos - 2000;
		if ( beginPos < 0 ) {
			beginPos = 0;
		}
		tbox.textSelection( 'setSelection', { start: beginPos, end: caretPos } );
		oldText = tbox.textSelection( 'getSelection' );
		tbox.textSelection( 'setSelection', { start: caretPos, end: caretPos } );

		//try to find http in oldText
		var rx = new RegExp( '(\\[{0,2})' + httpRegExp + '( +[^\\]\n]+)?\\]{0,2}$', 'i' ),
			ma = rx.exec( oldText ); // result: (whole string)' '[', 'http:...', ' name]'
		if ( !ma ) {
			return;
		}
		oldText = ma[0];
		if ( ma[3] ) { //link with name: automatically add brackets
			newText = simplifyMatched( ma[0], '[', ma[2], ma[3] + ']' );
		} else { //just url: add closing bracket only if there is leading bracket
			newText = simplifyMatched( ma[0], ma[1], ma[2], ma[1] ? ']' : '');
		}

		if ( oldText == newText ) {
			return;
		}
		tbox.textSelection( 'setSelection', { start: caretPos - oldText.length, end: caretPos } );

	}

	//replace text
	tbox.textSelection( 'encapsulateSelection', { replace: true, peri: newText } );

	//end of main code
	return;





	//---FUNCTIONS


	function simplifyMatched ( str, bracket, url, rest ) { //arguments: (whole string), '[', url, ' name]'
		if ( !bracket ) {//no brackets, just url
			var trail = RegExp(
					'[' +
					',;\\\\\.:!\\?' + //trailing punctuation, per Parser.php
					( /\(/.test( url ) ? '' : '\\)' ) + //also closing bracket without opening bracket
					']+$' +
					"|''+$" //or possible bold/italic at the end of url
				)
				.exec( url );
			if ( trail ) {
				url = url.substring( 0, url.length - trail[0].length ); //move these out of url
			}
			return decodeUrl( url ) + str.substring( url.length );

		} else if ( rest ) { //both brackets and possibly name
			return decodeUrl( url, rest.replace( /\]+$|^ +| +$/g, '' ) ); //trim ending brackets and spaces in 'name]'
		} else {
			return str; //probably broken wikicode in selected text
		}
	}



	function decodeUrl ( url, name ) { //url -> %-decoded -> [[link|name]] (if possible); name is optional

		var decodingFailed; //need to skip some strange percent-encoded URIs
		url = unSecure( url );

		//percent-decoding
		if ( url.indexOf('%') !== -1 ) {
			try {
				url = decodeURI( url );
				url = url.replace( /%(3B|2F|2C|3A)/g, decodeURIComponent ); //decode ;/,:
				url = url.replace( /[ <>"\[\]\n\r|]/g, encodeURIComponent ); //" some disallowed chars, and pipe can screw template params
			} catch ( e ) {
				decodingFailed = true;
			}
		}

		if ( isBeforeCursor ) { //user-defined conversion to eng keywords
			for ( var n in window.urlDecoderEngNames ) {
				url = url.replace( RegExp( '(title=|wiki\/)(' + window.urlDecoderEngNames[n] + ':)' ), '$1' + n + ':' );
			}
		}

		//try converting to internal link
		var link;
		if ( !decodingFailed && !/(\}\}|\|)$/.test( url ) ) { //trailing | or }}  could mean a part of a template, skip to be safe
			link = toWikilink( url );
		}

		//user-defined function
		if ( window.urlDecoderCustom ) {
			url = urlDecoderCustom( url );
			if ( !/^(https?:\/\/|\{\{)/.test( url ) ) {
				link = url; //was converted to internal link
			}
		}

		//return internal link
		if ( link ) {
			link = link.replace( /%(3f|26|22)/ig, decodeURIComponent ); //decode ?&"
			if ( ( mw.config.get( 'wgNamespaceNumber') == 0 ||
				mw.config.get( 'wgNamespaceNumber' ) == 14) &&
				isBeforeCursor
			) {
				link = link.replace( /^:/, '' ); //probably user adding interwiki
			}
			return '[[' + link + ( name ? '|' + name : '' ) + ']]';
		}

		//or return external link
		if ( typeof name == 'string' ) {
			if ( isBeforeCursor ) {
				url = url.replace( /''/g, '%27%27' ); //techically '' should stop URL, but more likely it's part of it
			}
			return '[' + url + ( name ? ' ' + name : '' ) + ']'; //empty name
		} else {
			return url;
		}

	}



	function toWikilink ( url ) { // 'http://xx.wikipedia.org/wiki/YY'   ->   xx:YY

		//add bugzilla to user-defined prefixes
		var urlDecoderPrefixes = $.extend(
			window.urlDecoderPrefixes,
			{ 'https://bugzilla.wikimedia.org/show_bug.cgi?id=' : 'mediazilla', 
			'wikidata.org/wiki/' : 'd',
			'phabricator.wikimedia.org/' : 'phab'
			}
		);

		//apply user-defined prefixes
		for ( var key in urlDecoderPrefixes ) {
			if ( url.toLowerCase().indexOf( key ) !== -1 ) {
				return urlDecoderPrefixes[key] + ':' + url.substring( url.indexOf( key ) + key.length );
			}
		}

		//check if we can convert to internal link with WM prefixes
		var ma = /^(https?:\/\/[^\/]+)\/wiki\/([^?]+)$/.exec( url ); // 1:'http://domain.org'  2:part after /wiki/
		if ( !ma ) {
			return null;
		}
		var linkPrefix = WMPrefixes( ma[1] );
		if ( !linkPrefix) {
			return null;
		}

		//convert to internal
		var title = decodeAnchor( ma[2] ),
			prefixes = '';
		if ( linkPrefix[0] && ( linkPrefix[0] !== localPrefix[0] ) ) {
			prefixes = linkPrefix[0];
		}
		if ( linkPrefix[1] && ( linkPrefix[1] !== localPrefix[1] ) ) {
			prefixes += ':' + linkPrefix[1];
		}
		if ( prefixes || isColonNeeded( title ) ) {
			prefixes += ':'; //colon after prefix or leading colon on cat/file link
		}
		return prefixes + title;

	}




	function decodeAnchor ( link ) { //simplify internal link: replace %20 and _ then decode anchor
		link = link.replace( /(_|%20)/g, ' ' ).replace( /^ +| +$/g, '' );
		var parts = link.split( '#' );
		if ( parts.length !== 2 ) {
			return link; //no anchor
		}
		var anchor = parts[1],
			hidIdx = -1,
			hidden = [];
		//decode 4, 3 and 2-byte: http://en.wikipedia.org/wiki/UTF-8
		anchor = anchor.replace( /\.F[0-4]\.[89AB][\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, deChar );
		anchor = anchor.replace( /\.E[\dA-F]\.[89AB][\dA-F]\.[89AB][\dA-F]/g, deChar );
		anchor = anchor.replace( /\.[CD][\dA-F]\.[89AB][\dA-F]/g, deChar );
		anchor = anchor.replace( //hide IPs
			/(?:^|[^0-9A-F\.])(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/,
			function ( s ) {
				hidden[++hidIdx] = s;
				return '\x01' + hidIdx + '\x02';
			}
		);
		//decode 1-byte chars: all symbols except  -.:_  and []{} prohibited in links
		anchor = anchor.replace( /\.[2-7][0-9A-F]/g, function ( hhh ) {
			var ch = deChar( hhh );
			if ( '!"#$%&\'()*+,/;<=>?@\\^`~'.indexOf( ch ) >= 0 ) {
				return ch;
			} else {
				return hhh;
			}
		} );
		//unhide IPs and return
		for ( var i = hidIdx; i >= 0; i-- ) {
			anchor = anchor.replace( '\x01'+i+'\x02', hidden[i] );
		}
		if ( anchor.indexOf( "''" ) !== -1 ) {
			return link; //cannot have double '' in link
		} else {
			return parts[0] + '#' + anchor;
		}

		function deChar ( ss ) {
			try {
				ss = decodeURIComponent( ss.replace( /\.([0-9A-F][0-9A-F])/g, '%$1' ) );
			} catch ( e ) {}
			return ss;
		}
	}





	function WMPrefixes ( url ) {  // http: //en.wikipedia.org/wiki/...  -> [ 'w',  'en']
		var dd = /^https?:\/\/([a-z\.]+)\.org/.exec( url.toLowerCase() );
		if ( !dd ) {
			return null;
		}
		dd = dd[1].split( '.' ); //domains, e.g. ['en','wikipedia']
		if ( dd.length > 2 ) {
			return null; //too many subdomains, possibly mobile site XX.m.wikipedia.org/
		}

		var lang = '',
			proj = '',
			domain = dd.pop(),
			subdomain = dd.pop();
		if ( subdomain == 'www' ) {
			subdomain = '';
		}

		if ( domain == 'wikimedia' ) {  // *.wikimedia.org
			if ( !subdomain ) {
				proj = 'foundation';
			} else if ( wmSubDomains.test( subdomain ) ) {
				proj = subdomain;
			} else {
				return null;
			}

		} else if ( ( proj = wmDomain[domain] ) && !subdomain ) { // mediawiki.org & wikimediafoundation.org
			//done: proj is set 

		} else if ( proj = wmDomainM[domain] ) { //multi-lang domains
			if ( !subdomain ) {
				//done: e.g. 'wikisource.org'
			} else if ( proj == 'w' && subdomain == 'test' ) {
				proj = 'testwiki';
			} else if ( subdomain.length >= 2 ) {
				lang = subdomain;
			} else {
				return null;
			}

		} else {
			return null; //unrecognized domain
		}

		return [ proj, lang ];
	}



	function unSecure ( url ) {
		var mm = /https:\/\/secure\.wikimedia\.org\/(\w+)\/(\w+)\/([^\]\|\n\r ]+)/i.exec( url );
		if ( !mm ) {
			return url;
		}
		var domain = mm[1].toLowerCase(), sub = mm[2].toLowerCase();

		if ( !wmDomainM[domain] ) {
			return url; //domain not recognized
		}
		
		if ( domain == 'wikipedia' ) { //handle some special cases
			switch ( sub ) { 
				case 'mediawiki':  sub = 'www'; domain = 'mediawiki'; break;
				case 'foundation': sub = ''; domain = 'wikimediafoundation'; break;
				case 'sources':    sub = ''; domain = 'wikisource'; break;
				default: 
					if ( wmSubDomains.test( sub ) ) { 
						domain = 'wikimedia'; // .../wikipedia/meta -> meta.wikimedia.org
					}
					//otherwise: consider it language: .../wikipedia/en
			}
		}
		
		return 'http://' + ( sub ? sub + '.' : '' ) + domain + '.org/' + mm[3];
	}

	function isColonNeeded ( pg ) {
		if ( !/:/.test( pg ) ) {
			return false;
		}
		if ( !colonNS ) { //define list of all possible category and file namespaces
			var list = [ 'file', 'category' ]; //canonical aliases
			for ( var name in mw.config.get( 'wgNamespaceIds' ) ) {
				if ( ( mw.config.get( 'wgNamespaceIds' )[name] == 6 ||
					mw.config.get( 'wgNamespaceIds' )[name] == 14 ) &&
					$.inArray( name, list ) == -1
				) { 
					list.push( name );
				}
			}
			colonNS = RegExp( '^(' + list.join( '|' ) + ') *:', 'i' );
		}
		return colonNS.test( $.trim( pg ) );
	}

}