MediaWiki:Gadget-PrettyLog.js

From wikishia

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.
/*
PrettyLog - reformat log pages. If the log contains file uploads, add small thumbnails of the
files. If the GalleryDetails gadget is also activated, make sure that it adds its sidebar link.

@Authors: [[User:Lupo]], 2009-01; [[User:Ilmari Karonen]], 2010-04, [[User:Perhelion]], 2018-07
@License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
<nowiki>
*/
/* global jQuery, mediaWiki, GalleryDetailsLoader */
(function ($, mw) {
'use strict';

if (mw.config.get('wgCanonicalSpecialPageName') !== 'Log') return;
$(function () {
	var server = mw.config.get('wgServer');
	var maxThumbWidth = 60;
	var maxThumbHeight = 60;
	var apiBatchSize = 50; // normal page 50 could be to much
	var list = $('ul.mw-logevent-loglines')[0];
	if (!list) return;
	$(list).addClass('mw-search-results');

	// That's all that's needed for the pretty layout!  All code below is for the image thumbnails.

	// Get list of upload log entries, abort early if there are none.
	var uploads = $(list).children('li.mw-logline-upload');

	if (!uploads || !uploads.length) return;

	// Find image links within each upload entry.  For simplicity, we assume that all links are in
	// canonical prefixed text form, and that all file links thus begin with ns6prefix.  Otherwise
	// we'd have to extract and normalize the namespace prefix and look for it in wgNamespaceIds.
	var ns6prefix = mw.config.get('wgFormattedNamespaces')[6] + ':',
		imageLocations = {},
		links, title,
		chunkTitlesToGet = [],
		images = [];

	// Callback function to show the thumbnails:
	function addThumbnails(result) {
		var i;
		if (result.error || !result.query || !result.query.pages) throw new Error('PrettyLog: unexpected API result:\n' + result.toSource());

		// hopefully we don't get any normalization, but just in case...
		if (result.query.normalized) {
			var norm = result.query.normalized;
			for (i = 0; i < norm.length; i++)
				imageLocations[norm[i].to] = imageLocations[norm[i].from].concat(imageLocations[norm[i].to] || []);

		}

		// now loop over the result and insert thumbs
		var pages = result.query.pages;
		for (var id in pages) {
			if (pages.hasOwnProperty(id)) {
				var title = pages[id].title;
				var className = '';

				if (!title) continue; // should not happen
 				// Don't show thumbnails for remote images (could happen if an image shadowing a Commons image is uploaded locally and deleted)
				if (pages[id].imagerepository && pages[id].imagerepository === 'shared') {
					// continue;
					className = ' error';
				}

				var info = pages[id].imageinfo;
				if (!info || !info.length) continue; // can happen if the image has been deleted, or if it wasn't an image at all
				info = info[0];

				if (!info.thumburl) {
					var fileext = title.slice(-4).toLowerCase();
					// KLUGE: for some reason, audio files sometimes get no thumburl; fix it here
					// TODO(?): there are more file type icons we could match to MIME types if needed
					// if (/^(audio\/|application\/ogg$)/.test(info.mime))
					fileext = $.inArray(fileext, ['djvu', '.mov', 'mpga', '.svg', '.ogg', '.pdf', '.psd', '.xcf']) !== -1 ? '-' + fileext.replace(/^\./, '') : '';
					info.thumburl = '/w/resources/assets/file-type-icons/fileicon' + fileext + '.png';
					info.thumbwidth = maxThumbWidth;
					info.thumbheight = maxThumbHeight; // all icons are currently 120x120
				}

				if (!info.thumbwidth || !info.thumbheight) continue; // can't happen?

				// if the returned thumb is too large for some reason, scale it proportionately so it fits
				if (info.thumbheight > maxThumbHeight) {
					info.thumbwidth *= maxThumbHeight / info.thumbheight;
					info.thumbheight = maxThumbHeight;
				}
				if (info.thumbwidth > maxThumbWidth) {
					info.thumbheight *= maxThumbWidth / info.thumbwidth;
					info.thumbwidth = maxThumbWidth;
				}

				// if the URL is local, strip the hostname prefix (avoids needless external link icons on some browsers)
				if (info.descriptionurl.indexOf(server + '/') === 0) info.descriptionurl = info.descriptionurl.substring(server.length);

				var loglines = imageLocations[title];

				if (!loglines) continue; // should not happen

				for (i = 0; i < loglines.length; i++) {
					// safety check: don't process the same line twice
					if (/^table$/i.test(loglines[i].firstChild.tagName)) continue;
					// create image and link elements for the thumbnail
					var img = document.createElement('img');
					img.setAttribute( 'loading', 'lazy' );
					img.src = info.thumburl;
					img.width = Math.round(info.thumbwidth);
					img.height = Math.round(info.thumbheight);
					var link = document.createElement('a');
					link.href = info.descriptionurl;
					link.title = title;
					link.className = 'image';
					link.appendChild(img);
					// transform the contents of this logline into a table
					var tbl = document.createElement('table');
					tbl.className = 'searchResultImage' + className;
					var tr = tbl.insertRow(-1);
					tr.setAttribute('valign', 'top');
					var td = document.createElement('td');
					td.width = maxThumbWidth + 10;
					td.setAttribute('align', 'center');
					td.appendChild(link);
					tr.appendChild(td);
					td = document.createElement('td');
					while (loglines[i].firstChild) td.appendChild(loglines[i].firstChild);
					tr.appendChild(td);

					loglines[i].appendChild(tbl);
				}
			}
		}
		// if [[MediaWiki:Gadget-GalleryDetails.js]] is enabled but inactive, rerun it now that we have some images
		if (window.GalleryDetailsLoader && !document.getElementById('t-gallerydetails') && GalleryDetailsLoader.initialized)
			GalleryDetailsLoader.initialize();
		if (chunkTitlesToGet.length) queryThumbnails();
	}

	function queryThumbnails() {
		var titles = chunkTitlesToGet.pop();
		if (titles) {
			$.get(mw.util.wikiScript('api'), {
				format: 'json',
				action: 'query',
				maxage: 3600,
				smaxage: 3600,
				prop: 'imageinfo',
				iiprop: 'url', // |mime
				iiurlwidth: maxThumbWidth,
				iiurlheight: maxThumbHeight,
				titles: titles
			}, addThumbnails);
		}
	}

	for (var i = 0; i < uploads.length; i++) {
		links = uploads[i].getElementsByTagName('a');
		for (var j = 0; j < links.length; j++) {
			title = links[j].title;
			if (!title || title.substring(0, ns6prefix.length) !== ns6prefix) continue;
			// Skip any redlinks, links in log summaries and links to uploaders' user/talk/contribs/etc. pages
			for (var e = links[j]; title && e && e !== uploads[i]; e = e.parentNode)
				if (/(^|\s)(new|comment|mw-userlink|mw-usertoollinks|mw-revdelundel-link|searchResultImage)(\s|$)/.test(e.className)) title = null;

			if (title) {
				if (!imageLocations[title]) imageLocations[title] = [];
				imageLocations[title].push(uploads[i]);
			}
		}
	}
	uploads = links = null; // we don't need these NodeLists anymore

	// Build array of unique image titles and URL-encode them.
	for (title in imageLocations)
		if (imageLocations.hasOwnProperty(title) && typeof title === 'string') images.push(title);

	// Make the queries in batches, to avoid tripping API and prevent freezing the browser or a 500 server error due to a overlong query.
	for (var n = 0; n < images.length; n += apiBatchSize)
		chunkTitlesToGet.push(images.slice(n, n + apiBatchSize).join('|'));

	if (chunkTitlesToGet.length) queryThumbnails();
});

// Declare CSS if external imported
if (mw.config.get('wgDBname') !== 'commonswiki') {
	mw.util.addCSS('.mw-search-results li{padding:.25em 1em;border-bottom:1px solid #d3daed}\n' +
	'.mw-search-results li:nth-child(odd){background:#f6f8fc}');
}
}(jQuery, mediaWiki));
// </nowiki>