MediaWiki:Gadget-urldecoder.js
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- 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 ) );
}
}