MediaWiki:ImageZoom.js

/** * @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:"No content could be loaded", //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:"Leider konnte der Inhalt nicht geladen werden.", 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 ? ""    + $.resource(txtResourceKey)    + "" : (txtContent.length ? ""      + txtContent +       "" : "")  ); }; /** * @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  * @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 $("") .append(       "" +        ' '+$.resource("ZoomImage_textZoomOrig")+' '      ) // 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 $("") .append("") .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("#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 "); modalBG = $(" "); // #### in old code version: HEIGHT was changed to: $(document).height-- necessary for some browsers??? Trying without! modalFG = $(" "); 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( $(' ')   .append(createButton("close", modalLayer_Hide)) ); modalBG .append('') .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 ? $('').append(createButton("zoomImg", modalLayer_InitShowIviewerZoomImage)) :"" ) // append content to foreground; wrap image with .append($("")       .append(newImg)// add image      ) // + caption .append($("")       .append(title.replace("("+$.resource("toolTipImageZooming")+")","")+" ")      // URL to metadata page from "a[href]" around img        .append($.linkBuilder("imageMetadataLink", "", $(oriImg).closest("a").attr("href"), "target='_blank'"))        .append( !zoomIsPossible ? " " + $.resource("zoomNotPossible") + " " : "")      ); // 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> → 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. * * @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; }); //