MediaWiki:Gadget-LargerGallery.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-LargerGallery. |
- Report page listing warnings and errors.
/**
* Larger gallerie thumbnail images (in categories and galleries) with extra zoom feature
* Fulfilled [[phab:T3340]]
* @Author original Brian Wolff (User:Bawolff), archived in [[Commons:Village_pump/Archive/2016/08#Larger_thumbnails]]
* @Author User:Perhelion expanded/improved as Gadget, 03-2018; tested and further User:Speravir
* @Revision: 20:42, 1 August 2023 (UTC)
*/
// <nowiki>
// window.largerGallery = 1.5; // Custom autorun factor
// window.largerGalleryZoom = 1; // disable or change the hover zoom factor
/* eslint indent:["error","tab",{"outerIIFEBody":0}] */
/* eslint-disable vars-on-top, one-var, no-bitwise, valid-jsdoc, space-in-parens, computed-property-spacing, curly */
/* global jQuery, mediaWiki */
(function ($, mw) {
'use strict';
var oldMag,
nsNr = mw.config.get('wgNamespaceNumber'),
isCat = nsNr === 14,
special,
zoom = 0,
initial, // default image size
factor = 1,
galleries, // , ul.gallery
galleryBoxes;
function mouseenter(e) {
var $this = $(this || e.target),
$img = $this.find('img'),
img = $img[0];
if (!img) {
// no supported element, e.g. in case of a video
return;
}
e.stopPropagation();
$this.parent().height('+=0');
// Set higher resolution (1.5x)
if (img && img.srcset)
$img.attr('src', img.srcset.split(' ')[0]);
$img.css({
position: 'relative',
// if smaller as default width, a center must be emulated ("+=" is needed for -moz)
left: '+=' + String(($this.width() - img.width) / 2),
zIndex: 1003,
background: 'none',
transform: 'scale(' + zoom + ')',
transformOrigin: 'center 25%',
transition: 'transform .6s'
});
}
// Reset
function mouseleave(e) {
e.stopPropagation();
$(this || e.target)
.find('img')
.css({
position: 'relative',
left: '',
zIndex: 1,
background: '',
transform: '',
transition: 'transform .3s'
});
// [0].srcset = "" // not needed anymore, only maybe do set a new mag factor (which should very rarely the case!?)
}
/**
* { Change all galleries properties }
*
* @param $ {DOM LI/IMG} galleryboxes The galleryboxes
* @param {boolean} single not multiple elements
* @return {DOM IMG} { }
*/
function run(galleryboxes, single) {
var mag = Number(factor); // magnification factor (introduced by Speravir)
galleryboxes = galleryboxes || galleryBoxes;
zoom = window.largerGalleryZoom || 0;
// calculate new mag
if (oldMag && !single)
mag /= oldMag;
if (single) {
initial = 120;
oldMag = 0; // To set hover
}
// calculate relative to scale factor (max 2.5 x 1.4 = 3.5)
zoom = zoom || 2.5 - factor * 0.6;
zoom.toFixed(1);
/**
* Scale an image.
* @param {DOM IMG} img The image element
* @return {number} { The new image size }
*/
function scaleImage(img) {
var iW = img.width || $(img).width(),
max = 600, // initial * 5 as the default MW value for raster img, so it could maybe load faster
iWnew,
iHnew, // new dimensions
// responsive scaling factor
bW = img.getAttribute('data-file-width') || max, // max width
// Round the thumps to decadic numbers due the cache issue
decadicRound = function (number) {
return Math.round(number / 10) * 10;
};
if (/\.(svg|SVG)\.png$/.test(img.src)) { // SVG
var SVGm = bW / iW; // SVG mag
if (SVGm < 1) {
// relative scaling to fixed dimension
SVGm += -SVGm / mag + 1;
} else {
SVGm = mag;
}
// max = 512; // as the default MW value for SVG img, so it could maybe loaded faster
iWnew = iW * SVGm;
} else {
iWnew = Math.min(iW * mag, bW); // maximum
}
iWnew = Math.min(iWnew, max); // maximum
var proportion = img.height / iW;
if (proportion > 1) { // higher then wide (needed for possible reset)
iHnew = Math.max(120, proportion * iWnew); // minimum
iWnew = iHnew / proportion; // if was lesser then 120
} else {
iWnew = Math.max(120, iWnew); // minimum
iHnew = proportion * iWnew; // if was lesser then 120
}
var zW = Math.min(decadicRound(iWnew * 1.5), max); // maximum (default WM on file pages)
img.height = decadicRound(iHnew); // keep proportion;
iWnew = decadicRound(iWnew); // we need integer
var zW2 = Math.min(decadicRound(iWnew * 2), max); // not larger as possible
// img.srcset = img.src + " 1x, " + img.srcset || ''; // fallback if new image size not loaded?
img.src = img.src.replace(/\d+(px-[^/]*$)/, iWnew + '$1');
img.srcset = img.srcset ? img.srcset.replace(/\d+(px-[^/]* 1\.5x)/, zW + '$1').replace(/\d+(px-[^/]* 2x)/, zW2 + '$1') :
img.src.replace(/\d+(px-[^/]*$)/, zW + '$1') + ' 1.5x, ' + img.src.replace(/\d+(px-[^/]*$)/, zW2 + '$1') + ' 2x';
img.width = iWnew;
return iWnew;
}
if (special && !single) {
if (special === 'Search')
$('.mw-search-results').first().width(function (i, w) {
return w * Math.min(Math.max(mag, 0.66), 1.5);
}).css('maxWidth', 'none');
var i = galleryboxes.length;
while (i--) {
scaleImage(galleryboxes[i]);
}
} else {
if (galleries) galleries.hide(); // JS speedup >^2
galleryboxes.each(function () {
var $this = $(this),
child1 = $this.children('div').first(), // .thumb
child2 = child1.children('span');
if (!child2[0])
return; // caption e.q. or faulty thumb
var img = child2[0].querySelector('a img, video');
if (!img)
return; // non image file
// already there?
if (!oldMag && zoom) { // hover
child2.on('mouseenter', mouseenter).on('mouseleave', mouseleave);
} else {
child2.height(''); // clear old absolute
if (!zoom)
child2.off('mouseenter mouseleave');
}
var iWnew = scaleImage(img);
// box width not too small
var bW = (iWnew < initial * mag) ?
(initial * mag + Math.max(120, iWnew)) / 2 : // average
iWnew,
bH = Math.max(initial * mag, bW) / 2 - img.height / 2 + 15; // height
bW += 35; // static padding
$this.width(bW);
// child1.first().height(bW - 5);
child1.first().height('');
child1.first().width(bW);
// Default margin on max iW is 15, margin-width (needed for lesser hover)
child2.css('margin', bH + 'px ' + ((bW - 5 - iWnew) / 2) + 'px');
});
if (galleries) galleries.show();
}
if (!single) {
initial *= mag;
oldMag = factor;
}
}
/**
* Create select element and autorun if possible
*
* @param {string} targetSelect The target id for the element before inserting the select
* @param {jQuery object} targetGallery The target gallery
*/
function init(targetSelect, targetGallery) {
$(function () {
// only once
if (document.getElementById('largerGallery2')) return;
var customMag = window.largerGallery || 0, // magnification factor
// Then, build a select and bind the change-event
$select = $('<select>', {
id: 'largerGallery' + (targetSelect ? '2' : ''),
title: 'Larger-Gallery',
style: 'float:right;margin:2px 5px 0 .5em;'
}).on('change', function (e) {
e.preventDefault();
factor = this.value;
// Dynamic page
if (special === 0) galleryBoxes = galleries.find('li');
run();
}),
factors = [1, 1.25, 1.5, 1.66, 1.83, 2.08, 2.5], // official values: 120, 150, 180, 200, 220, 250, 300, 400px; linear would be: 1.25, 1.5, 1.75, 2, 2.25, 2.5
content = document.getElementById(isCat ? 'mw-category-media' : 'mw-content-text') || document.getElementById('bodyContent'),
mi = 0; // mag index
initial = 120;
oldMag = 0;
galleries = $('ul.mw-gallery-traditional, ul.mw-gallery-nolines'); // , .gallery
if (nsNr % 2)
galleries = $(); // Not on talk pages
if (targetGallery) {
galleries = targetGallery;
special = 0;
} else if (nsNr === -1) {
special = mw.config.get('wgCanonicalSpecialPageName');
if (special === 'Search' || special === 'Listfiles') {
galleries = $(content);
galleryBoxes = galleries.find('.image img, .mw-file-description img');
} else {
special = 0;
}
}
if (!special) galleryBoxes = galleries.find('li');
if (!galleryBoxes[0]) return; // Nothing to do
customMag = Number(customMag);
if (customMag && factors.indexOf(customMag) === -1) {
factors.push(customMag);
// We can use string sort as the numbers are only tens digits
factors.sort();
// Alignment to default values. Real add is disabled due cache issue
mi = factors.indexOf(customMag);
if (mi) {
// It's closer to the lower value?
if (mi < factors.length - 1) {
if (customMag - factors[mi - 1] < factors[mi + 1] - customMag)
customMag = factors[mi - 1];
else customMag = factors[mi + 1];
} // else { factors.pop(); } its too big
} // else { factors.shift(); } its too small
factors.splice(mi);
}
factors.forEach(function (i) {
$select.append($('<option>', {
value: i,
text: (i * 100).toFixed() + '%' // Fix bad floating point arithmetic
}));
});
// Set custom value for dropdown (not on special pages)
if (customMag > 1 && !special) {
$select[0].selectedIndex = factors.indexOf(customMag);
factor = customMag;
run(galleryBoxes); // autorun
}
// Insert dropdown field
if (targetSelect) {
targetSelect = document.getElementById(targetSelect);
targetSelect.parentNode.appendChild($select[0]);
} else {
isCat = isCat ? content.getElementsByTagName('H2')[0] : 0;
if (isCat) {
var p = $(isCat).next();
if (p && p.prop('tagName') === 'P')
p.prepend($select);
else
content.insertBefore($select[0], isCat.nextSibling);
} else {
if (!$('#contentSub:visible, #siteSub:visible').first().prepend($select)[0])
content.insertBefore($select[0], content.firstChild);
}
}
});
}
mw.libs.largerGallery = { init: init, run: run };
init();
}(jQuery, mediaWiki));
// </nowiki>