MediaWiki:Gadget-PopupsFix.js

From Wikidata
Jump to navigation Jump to search

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.
/**
 * This script is a hack to make popups on items work.
 *
 * @author [[User:Bene*]]
 */
( function( $, mw ) {
	'use strict';
	

	/**
	 * Property which is used to find the thumbnail of an item.
	 */
	var imageProperty = 'P18';

	// dont enable popups for all users, only add it when it is already loaded
	var state = mw.loader.getState( 'ext.popups.desktop' );
	if ( !state || !state.match( /loading|loaded|ready/ ) ) {
		return;
	}

	mw.loader.using( [ 'ext.popups.desktop' ], function() {

		var newLineIdentifier = '[br-' + Math.random() + ']',
			beforeIdIdentifier = '[before-id-' + Math.random() + ']',
			afterIdIdentifier = '[after-id-' + Math.random() + ']';

		/**
		 * Creates a Wikidata specific popup.
		 *
		 * @param {jQuery.Promise} deferred
		 * @param {string} title
		 * @param {string} href
		 */
		function popupWikidata( deferred, title, href ) {
			var id = title.replace( 'Property:', '' ),
				lang = mw.config.get( 'wgUserLanguage' );

			// first request to get the entity's labels, descriptions and claims
			mw.popups.render.currentRequest = mw.popups.api.get( {
				action: 'wbgetentities',
				props: 'labels|descriptions|claims',
				redirects: 'yes',
				languages: lang,
				languagefallback: true,
				ids: id
			} );
	 
			mw.popups.render.currentRequest.fail( deferred.reject );
			mw.popups.render.currentRequest.done( function ( re ) {
				mw.popups.render.currentRequest = undefined;

				if (
					!re.success ||
					!re.entities ||
					!re.entities[id] ||
					!re.entities[id].labels ||
					!re.entities[id].labels[lang] ||
					!re.entities[id].descriptions ||
					!re.entities[id].descriptions[lang]
				) {
					deferred.reject();
					return;
				}

				var entity = re.entities[id];

				// hack: fake request because of bad api design
				var fakedRe = {
					extract: entity.labels[lang].value + beforeIdIdentifier + id + afterIdIdentifier +
						newLineIdentifier + entity.descriptions[lang].value,
					title: entity.labels[lang].value
				};

				var file = '';

				if ( entity.claims && entity.claims[imageProperty] ) {
					file = '|File:' + entity.claims[imageProperty][0].mainsnak.datavalue.value;
				}

				// second request to get the revision of the entity and the thumbnail if an image was found
				mw.popups.render.currentRequest = mw.popups.api.get( {
					action: 'query',
					prop: 'imageinfo|revisions',
					iiprop: 'url|size',
					iilimit: 1,
					iiurlwidth: 300 * $.bracketedDevicePixelRatio(), // scaledThumbSize
					rvprop: 'timestamp',
					rvlimit: 1,
					titles: title + file
				} );

				mw.popups.render.currentRequest.fail( deferred.reject );
				mw.popups.render.currentRequest.done( function ( re ) {
					mw.popups.render.currentRequest = undefined;

					if (
						!re.query ||
						!re.query.pages
					) {
						deferred.reject();
						return;
					}

					var pageId;
					for ( var i in re.query.pages ) {
						if ( i > 0 ) {
							pageId = i;
						}
					}

					fakedRe.revisions = re.query.pages[pageId].revisions;

					if ( re.query.pages[-1] && re.query.pages[-1].imageinfo ) {
						fakedRe.thumbnail = {
							source:  re.query.pages[-1].imageinfo[0].thumburl,
							width: re.query.pages[-1].imageinfo[0].thumbwidth,
							height: re.query.pages[-1].imageinfo[0].thumbheight
						};
					}

					mw.popups.render.cache[ href ] = {};
					mw.popups.render.cache[ href ].popup = mw.popups.render.renderers.article.createPopup( fakedRe, href );
					mw.popups.render.cache[ href ].getOffset = mw.popups.render.renderers.article.getOffset;
					mw.popups.render.cache[ href ].getClasses = mw.popups.render.renderers.article.getClasses;
					mw.popups.render.cache[ href ].process = mw.popups.render.renderers.article.processPopup;
					deferred.resolve();
				} );
			} );
		}

		/**
		 * Code stolen from the popup extension itself to create the default popup.
		 *
		 * @param {jQuery.Promise} deferred
		 * @param {string} title
		 * @param {string} href
		 */
		function popupDefault( deferred, title, href ) {
			mw.popups.render.currentRequest = mw.popups.api.get( {
				action: 'query',
				prop: 'extracts|pageimages|revisions',
				formatversion: 2,
				redirects: true,
				exintro: true,
				exsentences: 2,
				// there is an added geometric limit on .mwe-popups-extract
				// so that text does not overflow from the card
				explaintext: true,
				piprop: 'thumbnail',
				pithumbsize: 300 * $.bracketedDevicePixelRatio(), // scaledThumbSize
				rvprop: 'timestamp',
				titles: title,
				smaxage: 300,
				maxage: 300,
				uselang: 'content'
			} );

			mw.popups.render.currentRequest.fail( deferred.reject );
			mw.popups.render.currentRequest.done( function ( re ) {
				mw.popups.render.currentRequest = undefined;

				if (
					!re.query ||
					!re.query.pages ||
					!re.query.pages[0].extract ||
					re.query.pages[0].extract === ''
				) {
					// Restore the title attribute and set flag
					if ( link.data( 'dont-empty-title' ) !== true ) {
						link
							.attr( 'title', link.data( 'title' ) )
							.removeData( 'title' )
							.data( 'dont-empty-title', true );
					}
					deferred.reject();
					return;
				}

				mw.popups.render.cache[ href ] = {};
				mw.popups.render.cache[ href ].popup = mw.popups.render.renderers.article.createPopup( re.query.pages[0], href );
				mw.popups.render.cache[ href ].getOffset = mw.popups.render.renderers.article.getOffset;
				mw.popups.render.cache[ href ].getClasses = mw.popups.render.renderers.article.getClasses;
				mw.popups.render.cache[ href ].process = mw.popups.render.renderers.article.processPopup;
				deferred.resolve();
			} );
		}

		/**
		 * Send an API request and cache the jQuery element
		 *
		 * @param {jQuery} link
		 * @return {jQuery.Promise}
		 */
		mw.popups.render.renderers.article.init = function ( link ) {
			var href = link.attr( 'href' ),
				title = mw.popups.getTitle( href ),
				deferred = $.Deferred();

			if ( !title ) {
				return deferred.reject().promise();
			}

			if ( title.match( /^Q[0-9]+/ ) || title.match( /^Property:P[0-9]+/ ) ) {
				popupWikidata( deferred, title, href );
			} else {
				popupDefault( deferred, title, href );
			}
	
			return deferred.promise();
		};

		var getProcessedElements = mw.popups.render.renderers.article.getProcessedElements;

		mw.popups.render.renderers.article.getProcessedElements = function( extract, title ) {
			var elements = getProcessedElements.apply( this, [ extract, title ] );
			var html = $( '<span>' ).append( elements ).html()
				.replace( newLineIdentifier, '</p><p>' )
				.replace( beforeIdIdentifier, ' <small>(' )
				.replace( afterIdIdentifier, ')</small>');

			return '<p>' + html + '</p>';
		};

		mw.hook( 'wikipage.content').add( function( $content ) {
			var $elements = $content.find( 'a[href^="/wiki/Property:"]' );
			mw.popups.removeTooltip( $elements );
			mw.popups.setupTriggers( $elements );
		} );

	} );
		
} )( jQuery, mediaWiki );