MediaWiki:Gadget-iw-links.js

Z Wikiźródeł, wolnej biblioteki

Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.

  • Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
  • Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
  • Internet Explorer / Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5
  • Opera: Naciśnij klawisze Ctrl+F5.
/*
 * @author: [[:pl:User:Beau]]
 */

/* Translatable strings */
mw.messages.set( {
	'iw-whl-not-found': 'Brak.',
	'iw-whl-error-unable-to-fetch': 'Nie udało się pobrać linkujących.',
	'iw-whl-error-empty-title': 'Podaj nazwę strony.',
	'iw-whl-fetching-links': 'Sprawdzanie linkujących...',
	'iw-whl-button-show': 'Pokaż linkujące z siostrzanych projektów',
	'iw-whl-sister-project-links': 'Linkujące z projektu $1',
	'iw-whl-current-project-links': 'Linkujące z bieżącego projektu',
	'iw-whl-project-wikipedia': 'Wikipedia',
	'iw-whl-project-wikisource': 'Wikiźródła',
	'iw-whl-project-wiktionary': 'Wikisłownik',
	'iw-whl-project-wikibooks': 'Wikibooks',
	'iw-whl-project-wikinews': 'Wikinews',
	'iw-whl-project-wikiquote': 'Wikicytaty',
	'iw-whl-project-wikiversity': 'Wikiversity'
} );

window.whatLinksHereGadget = {
	/** Version of the gadget */
	version: 7,
	/** List of families for Polish projects. Should obtain this list using sitematrix api call. */
	families: [
		{
		name: 'wikipedia',
		prefix: 'w',
		title: mw.msg( 'iw-whl-project-wikipedia' )
	},
		{
		name: 'wikisource',
		prefix: 's',
		title: mw.msg( 'iw-whl-project-wikisource' )
	},
		{
		name: 'wiktionary',
		prefix: 'wikt',
		title: mw.msg( 'iw-whl-project-wiktionary' )
	},
		{
		name: 'wikibooks',
		prefix: 'b',
		title: mw.msg( 'iw-whl-project-wikibooks' )
	},
		{
		name: 'wikinews',
		prefix: 'n',
		title: mw.msg( 'iw-whl-project-wikinews' )
	},
		{
		name: 'wikiquote',
		prefix: 'q',
		title: mw.msg( 'iw-whl-project-wikiquote' )
	}/*,
		{
			name: 'wikiversity',
			prefix: 'v',
			title: mw.msg('iw-whl-project-wikiversity')
		}*/

		],
	/** A family of the wiki the gadget is running on */
	family: null,
	/** A prefix of the wiki the gadget is running on */
	prefix: null,
	/** A url template for article links */
	articleUrl: null,
	/** A url template for api links */
	apiUrl: null,
	ajax: true,
	/** An identifier of last sent request */
	lastRequestId: 0,
	/** An array requestId => requestCallback */
	callbacks: {},

	/** Sets up the gadget */
	init: function() {
		var that = this;
		var server = mw.config.get( 'wgServer' );

		var m = server.match( /^(?:https?:)?\/\/(.+)\.([^.]+)\.org$/ );
		if ( m ) {
			this.family = m[2];
			this.articleUrl = "//" + m[1] + ".$1.org/wiki/$2";
			this.apiUrl = "//" + m[1] + ".$1.org/w/api.php";
			this.ajax = false;
		}

		if ( !this.family ) {
			return;
		}

		for ( var i in this.families ) {
			var f = this.families[i];
			if ( f.name == this.family ) {
				this.prefix = f.prefix;
				break;
			}
		}
		if ( !this.prefix ) {
			return;
		}

		if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == "Whatlinkshere" ) {
			var $fieldset = jQuery( '#namespace' ).parents( 'fieldset' ).find( '.mw-htmlform-submit-buttons' );
			if ( !$fieldset.length ) {
				return;
			}

			var buttonIwl = new OO.ui.ButtonWidget( { 
			label: mw.msg( 'iw-whl-button-show' )
			} );
            buttonIwl.$element.on('click', function() {
				that.show();
			} );
			$fieldset.append(buttonIwl.$element );
		}
	},

	/** Executes the callback function for all families except the current one */
	forEachFamily: function( callback ) {
		for ( var i in this.families ) {
			var f = this.families[i];
			// Ignore current wiki
			if ( f.name == this.family ) {
				continue;
			}

			callback( f );
		}
	},

	/** Shows the list of links */
	show: function() {
		var target = jQuery.trim( jQuery( '#mw-whatlinkshere-target' ).children('input').val() ).replace( / /g, '_' );

		if ( target == '' ) {
			alert( mw.msg( 'iw-whl-error-empty-title' ) );
			return;
		}

		if ( !this.uiVisible ) {

			this.uiVisible = true;
			var $p = jQuery( 'fieldset' ).parent( 'form' ).parent().next('p');
			$p.after( jQuery( '<h4/>' ).append( mw.msg( 'iw-whl-current-project-links' ) ) );

			var $list = jQuery( '<div />' );

			this.forEachFamily( function( family ) {
				$list.append(
					'<h4 id="header-links-' + family.name + '">' + mw.msg( 'iw-whl-sister-project-links', family.title ) + '</h2>',
					'<div id="links-' + family.name + '" />'
				);
			} );

			$p.after( $list );
		}

		var that = this;

		this.forEachFamily( function( family ) {
			var $header = jQuery( '#header-links-' + family.name );
			var $body = jQuery( '#links-' + family.name );

			$header.show();
			$body.empty().append( mw.msg( 'iw-whl-fetching-links' ) ).show();

			that.fetchLinks( target, family, function( list ) {
				if ( list == null ) {
					$body.empty().append( '<p>' + mw.msg( 'iw-whl-error-unable-to-fetch' ) + '</p>' );
					return;
				}
				if ( !list.length ) {
					$header.hide();
					$body.hide();
					return;
				}
				var $ul = jQuery( "<ul/>" );
				var url = that.articleUrl.replace( /\$1/, family.name );

				for ( var i in list ) {
					var title = list[i];

					var $link = jQuery( '<a/>' )
						.append( document.createTextNode( title.replace( /_/g, ' ' ) ) )
						.attr( 'href', url.replace( /\$2/, title ) );

					$ul.append( jQuery( '<li/>' ).append( $link ) );
				}
				$body.empty().append( $ul );
			} );
		} );
	},

	/**
	 * Fetches namespace information for the wiki.
	 * @param callback A function to be called when information is ready.
	 */
	fetchNamespaces: function( callback ) {
		if ( !this.namespaces ) {
			this.namespaces = jQuery.Deferred();
			var request = {
				action: 'query',
				meta: 'siteinfo',
				siprop: 'namespaces|namespacealiases',
				format: 'json'
			};
			var that = this;
			jQuery.getJSON( mw.util.wikiScript( 'api' ), request, function( data ) {
				if ( !data || !data.query || !data.query.namespaces || !data.query.namespacealiases ) {
					that.namespaces.reject();
				}
				var info = data.query.namespaces;

				for ( var i in info ) {
					var namespace = info[i];
					namespace.aliases = [];

					if ( namespace['*'] != null ) {
						namespace.aliases.push( namespace['*'] );
						delete namespace['*'];
					}

					if ( namespace.canonical != null ) {
						namespace.aliases.push( namespace.canonical );
						delete namespace.canonical;
					}
				}

				for ( var i in data.query.namespacealiases ) {
					var entry = data.query.namespacealiases[i];
					var namespace = info[entry.id];
					namespace.aliases.push( entry['*'] );
				}

				that.namespaces.resolve( info );
			} );
		}
		this.namespaces.then( callback );
	},

	/**
	 * Returns a string with the first character capitalized.
	 * @param str A string to be modified.
	 */
	ucfirst: function( str ) {
		if ( !str.length ) {
			return "";
		}
		return str.charAt( 0 ).toUpperCase() + str.substr( 1 );
	},

	/**
	 * Returns a string with the first character lowercased.
	 * @param str A string to be modified.
	 */
	lcfirst: function( str ) {
		if ( !str.length ) {
			return "";
		}
		return str.charAt( 0 ).toLowerCase() + str.substr( 1 );
	},

	/**
	 * Fetches links to a page.
	 * @param title A title of the page to which links refer to.
	 * @param family A family object representing the wiki to check.
	 * @param callback A function to be called when information is ready.
	 */
	fetchLinks: function( title, family, callback ) {
		var that = this;

		this.fetchNamespaces( function( namespaces ) {
			// Split namespace from page name
			var parsedTitle = that.parseTitle( title );
			// A namespace information of checked page.
			var namespace = namespaces[parsedTitle.ns];
			// Generate names without a namespace
			var names = {};
			if ( namespace['case'] == 'first-letter' ) {
				names[that.ucfirst( parsedTitle.name )] = 1;
				names[that.lcfirst( parsedTitle.name )] = 1;
			}
			names[parsedTitle.name] = 1;
			// Generate titles to check
			var titles = {};
			for ( var i in namespace.aliases ) {
				var alias = namespace.aliases[i];
				var prefix = ( alias == "" ) ? "" : alias + ':';

				for ( var sufix in names ) {
					titles[that.ucfirst( prefix ) + sufix] = 1;
					titles[that.lcfirst( prefix ) + sufix] = 1;
				}
			}
			titles[title] = 1;

			var requestCallback;
			var sendRequest;

			var queue = [];
			for ( var t in titles ) {
				queue.push( t );
			}
			var results = {};

			requestCallback = function( data ) {
				if ( data && data.query && data.query.iwbacklinks ) {
					for ( var i in data.query.iwbacklinks ) {
						results[data.query.iwbacklinks[i].title] = 1;
					}
				}

				if ( queue.length ) {
					sendRequest();
				} else {
					var list = [];
					for ( var r in results ) {
						list.push( r );
					}
					callback( list.sort() );
				}
			};

			var url = that.apiUrl.replace( /\$1/, family.name );
			sendRequest = function() {
				var request = {
					action: 'query',
					list: 'iwbacklinks',
					format: 'json',
					iwblprefix: that.prefix,
					iwblprop: 'iwtitle',
					iwbllimit: 'max',
					iwbltitle: queue.shift()
				};

				if ( that.ajax ) {
					jQuery.getJSON( url, request, requestCallback );
				} else {
					// Fallback for insecure users
					that.lastRequestId++;
					var id = that.lastRequestId;
					request['requestid'] = id;
					request['callback'] = 'whatLinksHereGadget.processResponse';
					that.callbacks[id] = requestCallback;
					url += '?' + jQuery.param( request );

					mw.loader.load( url );
				}
			};

			sendRequest();
		} );
	},

	/**
	 * Parses title of a specifed page.
	 * @param title A title of the page.
	 */
	parseTitle: function( title ) {
		var matches = title.match( /^([^:]+):(.+)$/ );
		if ( matches ) {
			var key = matches[1].toLowerCase();
			var namespaces = mw.config.get( 'wgNamespaceIds' );
			var result = namespaces[key];
			if ( result != null ) {
				return {
					'ns': result,
					'name': matches[2]
				};
			}
		}
		return {
			'ns': 0,
			'name': title
		};
	},

	/**
	 * Processes responses from the api by calling a callback function
	 * assigned to the request.
	 */
	processResponse: function( data ) {
		if ( !data || !data.requestid ) {
			return;
		}
		var callback = this.callbacks[data.requestid];
		delete this.callbacks[data.requestid];
		callback( data );
	}
};

jQuery( document ).ready( function() {
	whatLinksHereGadget.init();
} );