MediaWiki:ImageZoom.js
From GFBio Public Wiki
Note: After saving, 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: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Clear the cache in Tools → Preferences
/**
* @namespace resource string dictionary
*
* Note: Commons uses collapse/expand ▲/▼, but this looks better in strict box
* layouts that in the free-wrapping key statements
*
* Nomenclature proposal: if an extra plugin is used, strings can be designated as
* “plugin_toolTipSomthing” otherwise just “toolTipSomthing” (global string). So it’s more clear if
* somebody wants to deactivate a plugin and remove strings from the resource dictionary.
* @requires MediaWiki:Jquery.zoomImage.js, MediaWiki:Common.css for #modal-fg, .iviewer_zoom_in, .iviewer_zoom_out, .iviewer_zoom_zero, .iviewer_zoom_fit
* @requires jQuery
* @augments $
* @type object
*/
$.jI18n = {
en: {
captionCollapse : " (show less) ",
captionExpand : " (more...) ",
expandAll : "Show all extras",
iconCloseWindowHover : "https://upload.wikimedia.org/wikipedia/commons/d/d0/Close_icon_hover.jpg",
iconCloseWindow : "https://upload.wikimedia.org/wikipedia/commons/8/87/Close_icon_default.jpg",
iconOverview : "https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/View-pause_Gion_simple.svg/20px-View-pause_Gion_simple.svg.png",
iconResume : "https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStart1st : "https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStartNew : "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/View-refresh_Gion_simple.svg/20px-View-refresh_Gion_simple.svg.png",
imageMetadataLink : "(Information about Creator, License and Copyright)",
newWindow : "(New Window …)",
toolTipClose : "Click to close",
toolTipCollapse : "(click to hide information below)",
toolTipExpand : "(click to show more information below)",
toolTipImageZooming : "Images can be enlarged by clicking on it",
toolTipNavigatePagetop : "Top of page",
toolTipNewWindow : "(click to open content in a new window or tab)",
toolTipFloatleft : "floating on the left side",
toolTipFloatright: "floating on the right side",
toolTipUnfloat: "back to default position",
toolTipNoContentLoadable:"<i>No content could be loaded</i>",
//toolTipHeadingLink: "Click to show (permanent) link to this headline", // MediaWiki:Gadget-HeadingLink
//toolTipHeadingLinkHelp: "(1) Normal link to this head line or (2) the permanent link with version number:",// MediaWiki:Gadget-HeadingLink
zoomNotPossible : "(This image can not be further enlarged)",
// see MediaWiki:Jquery.zoomImage.js
ZoomImage_iconMagnifier: "http://www.species-id.net/o/media/f/f7/Iviewer.zoom_in.gif",
ZoomImage_iconMagnifierHover: "http://www.species-id.net/o/media/5/5c/Iviewer.zoom_out.gif",
ZoomImage_iconLoader: "https://upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
ZoomImage_toolTipLoad : "(click to load largest available image; this may take considerable time to load)",
ZoomImage_textZoomOrig: "Zooming facility"
},
de: {
captionCollapse : " (weniger anzeigen) ",
captionExpand : " (mehr...) ",
expandAll : "Alle Zusatzinformationen zeigen",
imageMetadataLink : "(Informationen zu Autor, Lizenz und Copyright)",
newWindow : "(Neues Fenster …)",
toolTipClose : "Zum Schließen klicken",
toolTipCollapse : "(klicken um Zusatzinformationen zu verbergen)",
toolTipExpand : "(klicken um Zusatzinformationen anzuzeigen)",
//toolTipHeadingLink: "Klicken um (permanenten) Link dieser Überschrift anzuzeigen",// MediaWiki:Gadget-HeadingLink
//toolTipHeadingLinkHelp: "(1) Link zu dieser Überschrift oder (2) Link mit Versionsnummer:",// MediaWiki:Gadget-HeadingLink
toolTipImageZooming : "Bilder können durch Anklicken vergrößert betrachtet werden",
toolTipNavigatePagetop : "Zum Seitenanfang",
toolTipNewWindow : "(klicken um Inhalt in neuem Fenster oder Reiter zu öffnen)",
toolTipFloatleft : "Links schwebend",
toolTipFloatright: "Rechts schwebend",
toolTipUnfloat: "Zurück zur Normalposition",
toolTipNoContentLoadable:"<i>Leider konnte der Inhalt nicht geladen werden.</i>",
zoomNotPossible : "(Dieses Bild kann nicht weiter vergrößert werden)",
// see MediaWiki:zoomImage.js
ZoomImage_toolTipLoad : "(klicken um Originalbild nachzuladen; bei großen Bildern kann dies u. U. langsam sein)",
ZoomImage_textZoomOrig: "Vergrößerungsfunktion"
},
it: {
captionCollapse : " (mostra di meno) ",
captionExpand : " (più...) ",
expandAll : "Mostra tutti informazione", //REVISE
imageMetadataLink : "(Informazione sull'Autore, Licenza e Copyright)",
toolTipClose : "Clicca per chiudere",
toolTipImageZooming : "Le immagini possono essere ingrandite cliccandoci sopra",
zoomNotPossible : "(Al momento non è possibilie ingrandire questa immagine)" // TODO translation see en version
}
};
/**
* @description Get resource string (text, image URLs) for a given language, based on a string-key
* If no resource is defined in a given language for a resource key, the resource for "en" will be returned,
* if this is missing as well an error message.
* @augments $
* @requires mw.config for getting global variables
* @param {string} resourceKey key for the resource
* @returns {String}
*/
$.resource = function (resourceKey) {
var lang = mw.config.get('wgUserLanguage').split("-")[0]; // language: "pt-BR", "de-formal", etc.
return ($.jI18n[lang] && $.jI18n[lang][resourceKey] ?
$.jI18n[lang][resourceKey] :
($.jI18n.en[resourceKey]) ? $.jI18n.en[resourceKey] : "MISSING RESOURCE: no $.jI18n.en." + resourceKey + " defined.");
};
/**
* @descriptions: Create html string for link with image and/or text content
* @param {string} txtResourceKey resource keys (multilingual {@link $.resource()}
* @param {html} txtContent displayed content of a link
* @param {url} href
* @param {string} attributes string of combined other attributes of link element; must use ' as inner quotes, and \" inside event functions
* @returns {@exp;txtResourceKey@pro;length|String|@exp;txtContent@pro;length@exp;txtResourceKey@pro;length}
*/
$.linkBuilder = function (txtResourceKey, txtContent, href, attributes) {
return (txtResourceKey.length ? "<a "
+ " href='" + href + "' "
+ " " + (attributes.length ? attributes : "") + ">"
+ $.resource(txtResourceKey)
+ "</a>" : (txtContent.length ? "<a "
+ " href='" + href + "' "
+ " " + (attributes.length ? attributes : "") + ">"
+ txtContent +
"</a>" : "")
);
};
/**
* @description return a random integer
* @param {integer} min
* @param {integer} max
* @returns {@exp;@call;parseInt}
*/
$.random = function (min, max) { // NO CHECKS: if(min>max) {return -1;} if(min==max) {return min;}
return (min + parseInt(Math.random() * (max - min + 1), 10));
};
/**
* @description Utility for Cluetip, Modal layer, Image Zoom:
* Create appendable jquery object, fnAction = function bound to click.
* NOTE All functions called within createButton() should return false
* to prevent appending a # to the URL from clicking <a href='#'></a>
* @param {string} kindOfButton type of button ('zoomImg', 'close')
* @param {function} fnAction function to bind on that link
* @returns {@exp;@exp;@call;$@pro;append@pro;h@call;over@call;@call;click|@exp;@call;$@pro;append@pro;h@call;over@call;@call;click}
*/
function createButton(kindOfButton, fnAction) {
switch (kindOfButton) {
case "zoomImg":
return $("<a href='#' title='"+$.resource('ZoomImage_toolTipLoad')+"' />")
.append(
"<img src='"+$.resource("ZoomImage_iconMagnifier")+"' align='middle' style='border:1px solid gray;'>" +
'<span style="position:absolute;left:20px;top:0px;white-space:nowrap;">'+$.resource("ZoomImage_textZoomOrig")+'</span>'
) // text after img seems to be inline only with position:absolute
.hover(
function() { $(this).find("img:first").attr({src: $.resource("ZoomImage_iconMagnifierHover"), style :'border:1px solid black;'}); },
function() { $(this).find("img:first").attr({src: $.resource("ZoomImage_iconMagnifier"), style :'border:1px solid gray;'}); })
.click(fnAction);
break;
case "close":
default:
return $("<a href='#' title='"+$.resource('toolTipClose')+"'/>")
.append("<img src='"+$.resource("iconCloseWindow")+"' />")
.hover(
function() { $(this).find("img:first").attr("src", $.resource("iconCloseWindowHover")); },
function() { $(this).find("img:first").attr("src", $.resource("iconCloseWindow")); })
.click(fnAction);
}// end switch case
}
////////////////////////////////////
// Modal Layer base functionality //
////////////////////////////////////
// for program flow see [[MediaWiki:ModalLayer and image zoom docu]]
/**
* @description: Hide (= close) modal layer (Note: cyclical dependency with next method unavoidable)
* @requires modalLayer_KeyDown()
* @returns {Boolean}
*/
function modalLayer_Hide() {
$(document).unbind("keydown", modalLayer_KeyDown);
$("#modal-fg").fadeOut(function() {
$("#modal-bg").hide();
$(this).empty().hide();
});
return false;
}
/**
* @description: Close (hide) modal layer on escape, backspace and arrow left key
* @requires modalLayer_Hide()
* @param {event} e the keyboard event object
* @returns {undefined}
*/
function modalLayer_KeyDown(e) {
if ((e.keyCode == 8) || (e.keyCode == 27) || (e.keyCode == 37)) { modalLayer_Hide(); }
}
/**
* @description: Create modal layer and execute fnRender
* @param {function} fnRender custom function to display something
* @param {object} paramsObj generic parameters passed to "fnRender"
* @returns {undefined}
*/
function modalLayer_Create(fnRender, paramsObj) {
// find existing or create background & layer
var modalBG = $("#modal-bg"),
modalFG = $("#modal-fg");
if (modalBG.length === 0) { // first time init
if (typeof(document.body.style.maxHeight) === "undefined") { // if IE 6
$("body","html").css({height: "100%", width: "100%"});
}
// add styles (IE6 hack not possible as element style!)
$("head").append("<style type=\"text/css\">#modal-bg {position:fixed; z-index:100; top:0px;left:0px; height:100%;width:100%; background:black; opacity:0.8; filter:alpha(opacity=80); display:none;}\n" +
"#modal-fg {position:fixed; z-index:101; top:50%;left:50%; padding:3px; border:2px solid #E0E0E0; background-color:white; display:none;}\n" + // IE6 hack: add (very!) slow IE-CSS-expression only for IE < 7
($.browser.msie && $.browser.version < 7 ? "* html #modal-bg {position: absolute; height:expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');}\n* html #modal-fg {position: absolute; margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');}\n" : "") +
"#modal-fg img {display:block;}\n</style>");
modalBG = $("<div id='modal-bg'/>"); // #### in old code version: HEIGHT was changed to: $(document).height()-- necessary for some browsers??? Trying without!
modalFG = $("<div id='modal-fg'/>");
modalBG.click(function() { modalLayer_Hide(); });
$("body").append(modalBG).append(modalFG);
} // END first time init
$(document).keydown(modalLayer_KeyDown);
// generic view port functionality: close icon and background click
modalFG
.append( $('<div style="position:absolute;right:4px;"/>')
.append(createButton("close", modalLayer_Hide)) );
modalBG
.append('<img src="' + $.resource('ZoomImage_iconLoader') + '" id="loaderIcon" style="position:absolute;top:50%;left:50%;"/>')
.show();
fnRender(paramsObj); // Execute custom logic, example: modalLayer_ZoomImage
}
/**
* @description Show modal layer based on a image
*
* @requires createButton()
* @requires modalLayer_InitShowIviewerZoomImage()
* @requires modalLayer_Hide()
* @requires $.resource()
*
* @param {object} newImg new image object
* @param {object} oriImg original image object
* @returns {undefined}
*/
function modalLayer_ShowImage(newImg, oriImg) {
var title = oriImg.title,
imgWidth = newImg.width,
imgHeight = newImg.height,
modalBG = $("#modal-bg"),
modalFG = $("#modal-fg");
modalBG.find("#loaderIcon").remove();
if (imgWidth===0) { // Only IE, only if newImg = oriImg.clone(): cloned image in IE has no width
imgWidth = oriImg.width;
imgHeight = oriImg.height;
}
// extend height of modal layer for no-zoom msg
var zoomIsPossible = (imgWidth !== oriImg.width),
layerHeight = imgHeight + 105 + ((!zoomIsPossible) ? 60 : 0),
layerWidth = Math.max(300, imgWidth + 70); // reserve minimal text width
// delete alt text & add click function to hide modal
$(newImg).removeAttr("alt")
.attr("title", title.replace($.resource("toolTipImageZooming"),$.resource("toolTipClose")))
.click( function() {modalLayer_Hide();});
modalFG
.css({width: layerWidth + "px", height: layerHeight + "px", "margin-left": -(layerWidth/2)})
.append( zoomIsPossible ? $('<div style="position:absolute;left:4px;" id="iviewer_zoom_icon"/>').append(createButton("zoomImg", modalLayer_InitShowIviewerZoomImage)) :"" )
// append content to foreground; wrap image with <div>
.append($("<div id='modal-fg-wrapper' style='margin-left:" +
(layerWidth-imgWidth) / 2 + "px; margin-top:35px;'/>")
.append(newImg)// add image
)
// + caption
.append($("<div id='zoomcaption' class='zoomcaption' style='text-align:center; margin:8px 2px 2px 2px; font-weight:bold;'/>")
.append(title.replace("("+$.resource("toolTipImageZooming")+")","")+"<br />")
// URL to metadata page from "a[href]" around img
.append($.linkBuilder("imageMetadataLink", "", $(oriImg).closest("a").attr("href"), "target='_blank'"))
.append( !zoomIsPossible ? "<br/><br/><span style='color:red;'>" + $.resource("zoomNotPossible") + "</span>" : "")
);
// take away IE6 modifications
if ( !($.browser.msie && $.browser.version < 7)) {
modalFG.css({"margin-top": -((layerHeight + 8) / 2)}); // 8 from other margin-top
}
modalFG.fadeIn(50);
}
/**
* @description: load script and functionality on demand for jQuery plugin iviewer
* @requires MediaWiki:Jquery.zoomImage.js with function modalLayer_ShowIviewerZoomImage()
* @requires mw.config.get
* @requires modalLayer_ShowIviewerZoomImage()
* @returns {Boolean}
*/
function modalLayer_InitShowIviewerZoomImage() {
if (typeof modalLayer_ShowIviewerZoomImage !== "function") {
$.getScript(mw.config.get('wgServer')
+ mw.config.get('wgScript')
+ "?title=MediaWiki:Jquery.zoomImage.js&action=raw&ctype=text/javascript"
, function(){modalLayer_ShowIviewerZoomImage();}
);
} else {
if (modalLayer_ShowIviewerZoomImage) {
modalLayer_ShowIviewerZoomImage ();
} else {
console.log("Error: Function modalLayer_ShowIviewerZoomImage() not found to make the image scalable.");
}
}
return false;// needed for click on <a href'#'></a> → no # appended to the URL
}
/**
* @description: custom function to be passed to modal layer zooming an image
* @param {object} paramsObj object containing "caller" = ref to a link including an img
* @requires $.random()
* @requires modalLayer_ShowImage()
* @returns {undefined}
*/
function modalLayer_ZoomImage(paramsObj) {
var oriImg = $(paramsObj.caller).find("img").get(0), // caller is typically a[href]
urlParts = oriImg.src.split("/");
if ((oriImg.src.search(/\/thumb\//) === -1) ||
(urlParts[urlParts.length - 1].search(/px-/) === -1)) {
// no larger picture possible, use existing
modalLayer_ShowImage($(oriImg).clone().get(0), oriImg);
} else { // images with "/thumb/" in path can be enlarged using URL-based resize
var stdThumbWidths = [1600,1400,1280,1024,900,800,700,640,600,550,480,400,350,320,300,250,200,180,150,120,100,80],
maxHeight = $(window).height() - 105, // 70 for additional text; 35 for space at top and bottom
maxWidth = $(window).width() - 50; // 50 for space left & right
// smallest possible of upscaling factor for height, width, multiply back to get max possible width
var maxScaledWidth = oriImg.width * Math.min(maxHeight/oriImg.height, maxWidth/oriImg.width),
maxScaledHeight = oriImg.height * maxScaledWidth / oriImg.width;
// reduce to next smaller standard thumb width (mediawiki preview settings plus additions)
for (var i = 0; i < 22; i++) {
if (stdThumbWidths[i] < maxScaledWidth) {
maxScaledWidth = stdThumbWidths[i];
maxScaledHeight = oriImg.height * maxScaledWidth / oriImg.width;
break;
}
}
urlParts[urlParts.length - 1] = maxScaledWidth + "px-" + urlParts[urlParts.length - 2];
var newImg = new Image(maxScaledWidth, maxScaledHeight);
// Load image. load/error occur asynchronously, need independent calls
// appending "random()" seems necessary for IE6-8, else "zoom image, close, zoom again" fails. REASON?
$(newImg)
.attr("src", urlParts.join("/")+"&rnd="+$.random(0,10000))
.load(function() { // load succeeded: create modal layer after loading image, else values (width, etc.) are 0
modalLayer_ShowImage(newImg, oriImg);
})
.error(function() { // Error loading thumb, main reason: thumbs must be smaller than ori size.
// Currently assuming this reason, loading full original image; BETTER: test using API:
// https://commons.wikimedia.org/w/api.php?action=query&titles=Image:Lamium_purpureum_scan.jpg&prop=imageinfo&iiprop=size
urlParts.pop(); // remove last part (e.g. 800px-xyz.jpg)
// set default width and height in case of thumbnail generation failure with huge images
newImg = new Image(maxScaledWidth, maxScaledHeight); // load original image
$(newImg)
.attr("src", urlParts.join("/").replace("/thumb", "")) // remove "/thumb" from url to get full. DO NOT ADD random here!
.load(function() {modalLayer_ShowImage(newImg, oriImg);}) // load succeeded
// 2nd level fail -> load from wikimedia.org
.error(function() {
// set default width and height in case of thumbnail generation failure with huge images
newImg = new Image(maxScaledWidth, maxScaledHeight);
$(newImg)
.attr("src", "https://commons.wikimedia.org/w/thumb.php?f="+urlParts[urlParts.length-1]+"&width="+maxScaledWidth+"px"+"&rnd="+$.random(0,10000))
.load(function() {modalLayer_ShowImage(newImg, oriImg);}) // load succeeded
// 3rd level fail -> load unchanged wiki page thumb
.error(function() {modalLayer_ShowImage($(oriImg).clone().get(0), oriImg);});
});
}); // end first level error
}
}
/**
* @description: Show image in modal layer
*
* @requires modalLayer_Create()
* @requires modalLayer_ZoomImage()
* @param {caller} caller reference to a link around image, function to call
* @returns {False}
*/
function zoomImage(caller) {
modalLayer_Create(modalLayer_ZoomImage, {caller: caller});
return false; // cancel default event
}
/**
* @description: Add a modal zoom functionality to all images linking to own metadata
* the CSS
* class=no-image-popup or class=no-popup-image OR
* class=no-image-zoom or class=no-zoom-image prevents an image from being zoomable, e.g.
* [[File:Example.jpg|caption text|thumb|class=no-popup-image]]
* @requires $.resource()
* @requires zoomImage()
* @returns {undefined}
*/
function initImageZooming() {
$("a[href].image img:not(.no-image-popup,.no-popup-image,.no-image-zoom,.no-zoom-image)").each(function() {
var jParent = $(this).parent(),
metaURL = jParent.attr("href"),
urlParts = this.src.split("/"),
imgFileName = (this.src.search(/\/thumb\//) !== -1) ? urlParts[urlParts.length - 2] : urlParts[urlParts.length - 1];
// Is file name also in metadata page link? Else abort (e.g. for |link=parameter| wiki-images)
// Problem: a.href and img.scr inconsistently! use encoded or non-encoded versions of e.g. () or "," -> unescape
if (unescape(metaURL).indexOf(unescape(imgFileName)) === -1) { return; }
// pass along the image
jParent.click(function() { return zoomImage(this); });
// set or change title, set alt to title
var newTitle = this.alt + ((this.alt.length === 0) ? "" : " ") + "(" + $.resource("toolTipImageZooming") + ")";
$(this).attr({title:newTitle, alt:newTitle});
});
}
// END Modal Layer/Img Zoom
////////////////////////////
/* Load when documetn is ready */
$(document).ready(function() {
initImageZooming();
});
// </syntaxhighlight>