MediaWiki:Gadget-PrettyLog.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
This user script seems to have a documentation page at MediaWiki:Gadget-PrettyLog and an accompanying .css page at MediaWiki:Gadget-PrettyLog.css. |
/*
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>