MediaWiki:Common.js: Unterschied zwischen den Versionen
Aus Bayernflora
(Die Seite wurde neu angelegt: „→Das folgende JavaScript wird für alle Benutzer geladen.: // <source lang="javascript"> // This JavaScript will be loaded for all users on every page load…“) |
|||
Zeile 1: | Zeile 1: | ||
− | |||
// <source lang="javascript"> | // <source lang="javascript"> | ||
// This JavaScript will be loaded for all users on every page load. | // This JavaScript will be loaded for all users on every page load. |
Version vom 23. Oktober 2013, 08:28 Uhr
// <source lang="javascript">
// This JavaScript will be loaded for all users on every page load.
// The first version was imported from en.wikipedia.org, for authors see http://en.wikipedia.org/w/index.php?title=MediaWiki:Common.js&action=history
// Strongly modified (remove, add) since.
/*
* dependencies:
MediaWiki:Edittools.js
MediaWiki:Common.js/edit.js
MediaWiki:JKey.js
MediaWiki:Mw-customcollapsible.js
MediaWiki:JKeyWikiEditorHelp.js
MediaWiki:SearchTools.js
MediaWiki:JKeyRenumberingTool.js
MediaWiki:Jquery.zoomImage.js
MediaWiki:JKeyTextToLeadTemplateTool.js
*/
/*global jQuery, document, screen, window, location, navigator, unescape, Image, clearTimeout, addOnloadHook, importScript, setTimeout, appendCSS, mw */
/* = settings for JSLint */
/* should go into mw.config: wgPageName, wgServer, wgScript, wgAction, wgCanonicalNamespace */
"use strict"; // set ECMAScript 5 Strict Mode
/* Social media integration; using async script inserted here: */
if ((mw.config.get('wgAction') === 'view')
&& (mw.config.get('wgCanonicalNamespace') !== 'Special')) {
$("h1#firstHeading:first").prepend(
"<!-- SOCIAL MEDIA START REMOVED, GET AGAIN FROM OLD VERSION IF IE8-9 ARE WORKING AGAIN!!!-->"
+ "<!-- SOCIAL MEDIA END -->"
);
}
/* Scripts specific to Internet Explorer */
// THIS SCRIPT IS PROBABLY WORKING, BUT NOT CLEAN JS, see JSLint
if (navigator.appName === "Microsoft Internet Explorer") {
/* Internet Explorer ***bug fix*** Fixes horizontal scrollbar bug */
var oldWidth, docEl = document.documentElement;
var fixIEScroll2 = function() {
docEl.style.overflowX = (docEl.scrollWidth - docEl.clientWidth < 4) ? "hidden" : "";
}
var fixIEScroll = function() {
if (!oldWidth || docEl.clientWidth > oldWidth) {
fixIEScroll2();
} else {
setTimeout(fixIEScroll2, 1);
}
oldWidth = docEl.clientWidth;
}
document.attachEvent("onreadystatechange", fixIEScroll);
document.attachEvent("onresize", fixIEScroll);
// In print IE (7?) does not like line-height
appendCSS( '@media print { sup, sub, p, .documentDescription { line-height: normal; }}');
//Import scripts specific to Internet Explorer 6
// THIS IS A PNG transparency FIX, here commented out:
// if (navigator.appVersion.substr(22, 1) == "6") {
// importScript("MediaWiki:Common.js/IE60Fixes.js");
// }
} // END "Microsoft Internet Explorer"
// helper function to escape jQuery IDs
function jqueryEscapeId(myid) {
if(myid.substr(0, 1) === "#"){
return myid.replace(/(:|\.)/g,'\\$1');
} else {
return '#' + myid.replace(/(:|\.)/g,'\\$1');
}
}
// Footnotes as unformatted tooltip - from it.wikipedia.org under same license
addOnloadHook ( function () {
var sups = document.getElementsByTagName("sup");
for (var i=0; i < sups.length; i++) {
var note_id = sups[i].childNodes[0].href;
if (note_id && (note_id.indexOf("#") != -1)) {
note_id = document.getElementById(note_id.substr(note_id.indexOf("#")+1));
if (note_id) {
if (document.all) {
sups[i].title = note_id.innerText;
sups[i].childNodes[0].title = note_id.innerText;
} else {
sups[i].title = note_id.textContent;
}
}
}
}
})
// Editing-page-specific: see also below JKeyWikiEditorHelp.js
if (mw.config.get('wgAction') === "edit" || mw.config.get('wgAction') === "submit" || mw.config.get('wgCanonicalSpecialPageName') === "Upload") {
if (typeof EditTools === 'undefined') {
importScript('MediaWiki:Edittools.js');
}
if (typeof $.wikiEditor === 'undefined') {
importScript("MediaWiki:Common.js/edit.js"); // TODO remove or adjust? AP 2011-08-25
}
}
$.jI18n = {/* 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.
*/
en: {
captionCollapse : " (show less) ",
captionExpand : " (more...) ",
expandAll : "Show all extras",
iconCloseWindowHover : "http://upload.wikimedia.org/wikipedia/commons/d/d0/Close_icon_hover.jpg",
iconCloseWindow : "http://upload.wikimedia.org/wikipedia/commons/8/87/Close_icon_default.jpg",
iconOverview : "http://upload.wikimedia.org/wikipedia/commons/thumb/2/22/View-pause_Gion_simple.svg/20px-View-pause_Gion_simple.svg.png",
iconResume : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStart1st : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStartNew : "http://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: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: "http://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.
* resourceKey: key for the resource (string)
*/
$.resource = function (resourceKey) {
var lang = 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
* attributes: string of combined other attributes of link element; must use ' as inner quotes, and \" inside event functions
* imgResourceKey, txtResourceKey: resource keys (multilingual);
* txtContent: string literal (untranslated)
* href: URI (available for linkBuilder only, images only per js in attributes)
*/
$.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>" : "")
);
};
$.imglinkBuilder = function (imgResourceKey, txtResourceKey, attributes) {
return (imgResourceKey.length ? "<a "
+ " href='#'" + (attributes.length ? " " + attributes : "") + "><img src='" + $.resource(imgResourceKey) + "' /></a> " : "")
+ $.linkBuilder(txtResourceKey, "", "#", attributes);
};
$.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));
};
///////////////////////
// Highlight targets //
///////////////////////
/* Description: Highlight all targets of page-internal links; generic function but
* especially useful in long internally linked tables like identification keys (see Template:Key_Start)
* NOTE: background-color animation is not easliy done by jQuery it needs either UI or a colorplugin
*/
// Highlight a single element that is target of the link-object caller (e.g. <a href=...>)
function highlightTarget(caller) {
var target = $(caller.hash.replace(/([.:])/g, '\\$1')); // hash could be 'a.34:', jquery needs 'a\.34\:'
if (target.length) {
var tStyle = target.get(0).style,
resetString = "resetHighlight(\"" + caller.hash + "\",\"" + tStyle.backgroundColor + "\",\"" + tStyle.textDecoration + "\")";
tStyle.backgroundColor = "#EAEAEA";
window.setTimeout(resetString, 2000);
}
}
// Stop highlighting
function resetHighlight(hash, backColor, txtDeco) {
if (hash) { // reset
var tStyle = $(hash.replace(/([.:])/g, '\\$1')).get(0).style;
tStyle.backgroundColor = backColor;
tStyle.textDecoration = (txtDeco === "") ? "none" : txtDeco;
}
}
// Add onclick events to all page-internal links
function initTargetHighlighting() {
for (var i=0, max=document.links.length; i < max; i++) {
var lnk = document.links[i];
if ((lnk.pathname === location.pathname) && lnk.hash.length > 1) { // page internal link; exluding single "#"
lnk.onclick = function() { highlightTarget(this); };
}
}
}
/////////////////////
// Collapse Tables //
/////////////////////
// ***** CAN THIS BE DELETED? (except the toggleAllCollapsible)
// Description: Allows tables to be collapsed, showing only the header row.
// Similar to Wikipedia code; rewritten for jquery
// Description expand or collapse table
// caller: collapse/expand link inside a table.
// shallExpand: optional boolean; if absent visibility will be toggled
function toggleCollapse(caller, shallExpand) {
var jLink = $(caller),
jTable = jLink.closest("table");
if (jTable.length && jLink.length) {
if (shallExpand===null) {
/* called without parameter, determine direction by link text
Note: Opera 10 has problems with encodings: an "&Acute;" character gets in, breaking comparison;
fix: delete + non-words (\W) */
// OLD: shallExpand = (jLink.text().replace(/( |\W)/g,'') == $.resource("captionExpand").replace(/( |\W)/g,''));
// *** TRYING:
shallExpand = (jLink.text().replace(/(\W)/g,'') == $.resource("captionExpand").replace(/(\W)/g,''));
}
jLink
.attr("title", $.resource( shallExpand ? "toolTipCollapse" : "toolTipExpand" ))
.html($.resource( shallExpand ? "captionCollapse" : "captionExpand" ));
jTable.find("tr:not(tr:first)").toggle(shallExpand);
// show/hide all in set, tr of nested tables are included
}
return false;
}
// collapse all collapsible key tables on wiki page
// using the MediaWiki mw-customtoggle mechanism
function toggleAllCollapsible(shallExpandThisKey, caller) {
var $pseudolinks = $(caller).closest(".decisiontree")
.find("span.pseudolink");
// debug log messages
if ($pseudolinks.length) {
$pseudolinks.each(function () {
var thisClasses = $(this).attr('class'),
// get last string position/index
thisClassesStopPos = thisClasses.indexOf(" ", thisClasses.indexOf("mw-customtoggle")),
// get mw-customtoggle-Key class
thisClassMwCustomtoggle = thisClasses.substring(
thisClasses.indexOf("mw-customtoggle"), // from index
thisClassesStopPos === -1 ? thisClasses.length : thisClassesStopPos // to index
),
// extract correspoding id
thisIdToToggle = thisClassMwCustomtoggle.replace("mw-customtoggle", "mw-customcollapsible"),
// check clicked status using class mw-collapsed
thisIsCollapsed = $('#' + thisIdToToggle).hasClass('mw-collapsed'),
doExpandThis;
if (shallExpandThisKey) {
doExpandThis = thisIsCollapsed ? true : false;
} else {
doExpandThis = thisIsCollapsed ? false : true;
}
if (doExpandThis) {
$(this).trigger('click');
}
});
}// pseudolink found
}// toggleAllCollapsible()
// ***** CAN THIS BE DELETED?
function initCollapseButtons() {
// fix unknown $ e.g. click-tip Form:Artportrait
if ( typeof $ === 'undefined'){ var $ = $j;}
var autoCollapse = 2, // CONSTANT
idx = 0,
linkstring = '<span class="collapseButton noprint">'+ $.linkBuilder("captionCollapse", "", "#", "title='"+ $.resource("toolTipCollapse") +"' onclick='return toggleCollapse(this,null);'") +'</span>';
var eachTable = function() { // is closure relative to idx, linkstring
var jTable = $(this),
jTH = jTable.find("tr th"); // add collapse button only if header row present
if (jTH.length) { // init expand-links
jTH.append(linkstring);
var jLink = jTH.find("span.collapseButton a");
jLink.get(0).style.color = jTH.get(0).style.color;
// collapse if requested
if (jTable.hasClass("collapsed") ||
(idx >= autoCollapse && jTable.hasClass("autocollapse")) ||
// also collapse inner if innercollapse and is inside outercollapse
(jTable.hasClass("innercollapse") && jTable.closest(".outercollapse").length)) {
toggleCollapse(jLink.get(0),null);
}
idx++;
}
};
$("table.collapsible").each(eachTable);
}
////////////////////////////////////
// Cluetip hover and click popups //
////////////////////////////////////
// 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>
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
}
// HoverIntent START
// see http://www.offene-naturfuehrer.de/wiki/MediaWiki:HoverIntent.js for docu, creators and license
$.fn.hoverIntent = function(f,g) {
var cfg = { // default configuration options: Cluetip overrides!
sensitivity: 4, // mouseover is called if mouse is moved less pixels; default 7. Cluetip overrides!
interval: 250, // comparison interval, also influences initial delay until detected; default 100.
timeout: 0
};
cfg = $.extend(cfg, g ? {over:f, out:g} : f ); // override options with user-supplied object
// current and previous X/Y position of mouse
var cX, cY, pX, pY;
// private function for getting mouse position
var track = function(ev) {
cX = ev.pageX;
cY = ev.pageY;
};
// private function comparing current and previous mouse position
var compare = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
// compare mouse positions to see if they've crossed the threshold
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
$(ob).unbind("mousemove",track);
// set hoverIntent state to true (so mouseOut can be called)
ob.hoverIntent_s = 1;
return cfg.over.apply(ob,[ev]);
} else { // set previous coordinates for next time
pX = cX; pY = cY;
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
}
};
// private function delaying the mouseOut function
var delay = function(ev,ob) {
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
ob.hoverIntent_s = 0;
return cfg.out.apply(ob,[ev]);
};
// private function handling mouseover AND mouseout
var handleHover = function(e) {
// next three lines from jQuery.hover: ignore children onMouseOver/onMouseOut
var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
while ( p && p != this ) { try { p = p.parentNode; } catch(err) { p = this; } }
if ( p == this ) { return false; }
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = $.extend({},e);
var ob = this;
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// else e.type == "onmouseover"
if (e.type == "mouseover") {
pX = ev.pageX; pY = ev.pageY; // set previous X/Y pos based on initial entry point
$(ob).bind("mousemove",track); // update current X/Y pos based on mousemove
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// else e.type == "onmouseout"
} else {
// unbind expensive mousemove event
$(ob).unbind("mousemove",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
};
// bind functions to event listeners
return this.mouseover(handleHover).mouseout(handleHover);
};
// HoverIntent END
//////////////////////////////////////////////////
// Modified Cluetip Plugin
// Version 1.0.6 modified for Off.Naturführer. MIT and GPL licenses
// http://plugins.learningjquery.com/cluetip/ and http://www.offene-naturfuehrer.de/wiki/MediaWiki:Cluetip.js for details
(function($) {
$.cluetip = {version: '1.0.6-ON'};
var $cluetip, $cluetipInner, $cluetipOuter, $cluetipTitle, $cluetipArrows, $cluetipWait, $dropShadow, imgCount,
insertionElement = 'body';
$.fn.cluetip = function(js, options) {
if (typeof js == 'object') {
options = js;
js = null;
}
if (js == 'destroy') {
return this.removeData('thisInfo').unbind('.cluetip');
}
return this.each(function(index) {
var link=this, $this=$(this);
// support metadata plugin (v1.0 and 2.0)
var opts = $.extend(
true,
{},
$.fn.cluetip.defaults,
options || {},
$.metadata ? $this.metadata() : ( $.meta ? $this.data() : {} )
);
// start out with no contents (for ajax activation)
var cluetipContents = false;
var cluezIndex = +opts.cluezIndex;
$this.data('thisInfo', {title: link.title, zIndex: cluezIndex});
var isActive = false, closeOnDelay = 0;
// create the cluetip divs
if (!$('#cluetip').length) {
$(['<div id="cluetip">',
'<div id="cluetip-outer">',
'<h3 id="cluetip-title"></h3>',
'<div id="cluetip-inner"></div>',
'</div>',
'<div id="cluetip-extra"></div>',
'<div id="cluetip-arrows" class="cluetip-arrows"></div>',
'</div>'].join(''))
.appendTo(insertionElement).hide();
$cluetip = $('#cluetip').css({position: 'absolute'});
$cluetipOuter = $('#cluetip-outer').css({position: 'relative', zIndex: cluezIndex});
$cluetipInner = $('#cluetip-inner');
$cluetipTitle = $('#cluetip-title');
$cluetipArrows = $('#cluetip-arrows');
$cluetipWait = $('<div id="cluetip-waitimage"></div>')
.css({position: 'absolute'}).insertBefore($cluetip).hide();
}
var dropShadowSteps = (opts.dropShadow) ? +opts.dropShadowSteps : 0;
if (!$dropShadow) {
$dropShadow = $([]);
for (var i=0; i < dropShadowSteps; i++) {
$dropShadow = $dropShadow.add($('<div></div>').css({zIndex: cluezIndex-1, opacity:0.1, top: 1+i, left: 1+i}));
}
$dropShadow.css({position: 'absolute', backgroundColor: '#000'})
.prependTo($cluetip);
}
var tipAttribute = $this.attr(opts.attribute);
if (!tipAttribute && !opts.splitTitle && !js) {
return true;
}
// if hideLocal is set to true, on DOM ready hide the local content that will be displayed in the clueTip
if (opts.local && opts.localPrefix) {tipAttribute = opts.localPrefix + tipAttribute;}
if (opts.local && opts.hideLocal) { $(tipAttribute + ':first').hide(); }
var tOffset = parseInt(opts.topOffset, 10), lOffset = parseInt(opts.leftOffset, 10);
// vertical measurement variables
var tipHeight, wHeight,
sTop, linkTop, posY, tipY, mouseY, baseline,
defHeight = isNaN(parseInt(opts.height, 10)) ? 'auto' : (/\D/g).test(opts.height) ? opts.height : opts.height + 'px';
// horizontal measurement variables
var tipInnerWidth = parseInt(opts.width, 10) || 275,
tipWidth = tipInnerWidth + (parseInt($cluetip.css('paddingLeft'),10)||0) + (parseInt($cluetip.css('paddingRight'),10)||0) + dropShadowSteps,
linkWidth = this.offsetWidth,
linkLeft, posX, mouseX, winWidth;
// parse the title
var tipParts;
var tipTitle = (opts.attribute != 'title') ? $this.attr(opts.titleAttribute) : '';
if (opts.splitTitle) {
if (tipTitle === undefined) {tipTitle = '';}
tipParts = tipTitle.split(opts.splitTitle);
tipTitle = tipParts.shift();
}
if (opts.escapeTitle) {
tipTitle = tipTitle.replace(/&/g,'&').replace(/>/g,'>').replace(/</g,'<');
}
function returnFalse() { return false; }
//activate clueTip
var activate = function(event) {
var pY;
if (!opts.onActivate($this)) {
return false;
}
isActive = true;
$cluetip.removeClass().css({width: tipInnerWidth});
if (tipAttribute == $this.attr('href')) {
$this.css('cursor', opts.cursor);
}
if (opts.hoverClass) {
$this.addClass(opts.hoverClass);
}
linkTop = posY = $this.offset().top;
linkLeft = $this.offset().left;
mouseX = event.pageX;
mouseY = event.pageY;
if (link.tagName.toLowerCase() != 'area') {
sTop = $(document).scrollTop();
winWidth = $(window).width();
}
// position clueTip horizontally
if (opts.positionBy == 'fixed') {
posX = linkWidth + linkLeft + lOffset;
$cluetip.css({left: posX});
} else {
posX = (linkWidth > linkLeft && linkLeft > tipWidth) || linkLeft + linkWidth + tipWidth + lOffset > winWidth ? linkLeft - tipWidth - lOffset : linkWidth + linkLeft + lOffset;
if (link.tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse
if (mouseX + 20 + tipWidth > winWidth) {
$cluetip.addClass(' cluetip-jtip');
posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'),10) + parseInt($cluetipInner.css('marginRight'),10) : mouseX - (tipWidth/2);
} else {
posX = mouseX + lOffset;
}
}
pY = posX < 0 ? event.pageY + tOffset : event.pageY;
$cluetip.css({
left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0),
zIndex: $this.data('thisInfo').zIndex
});
$cluetipArrows.css({zIndex: $this.data('thisInfo').zIndex+1});
}
wHeight = $(window).height();
// load a string from cluetip method's first argument
if (js) {
if (typeof js == 'function') {
js = js.call(link);
}
$cluetipInner.html(js);
cluetipShow(pY);
}
// load the title attribute only (or user-selected attribute).
// clueTip title is string before 1st delimiter, subsequent delim place clueTip body text on separate lines
else if (tipParts) {
var tpl = tipParts.length;
$cluetipInner.html(tpl ? tipParts[0] : '');
if (tpl > 1) {
for (var i=1; i < tpl; i++){
$cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>');
}
}
cluetipShow(pY);
}
// load external file via ajax
else if (!opts.local && tipAttribute.indexOf('#') !== 0) {
if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) {
$cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />');
cluetipShow(pY);
} else if (cluetipContents && opts.ajaxCache) {
$cluetipInner.html(cluetipContents);
// highlight target having a <span id=".."> see also ajaxSettings success:
var idTarget = link.toString().split('#')[1];
if(idTarget!=undefined){
$cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert');
}
cluetipShow(pY);
} else {
var optionBeforeSend = opts.ajaxSettings.beforeSend,
optionError = opts.ajaxSettings.error,
optionSuccess = opts.ajaxSettings.success,
optionComplete = opts.ajaxSettings.complete;
var ajaxSettings = {
cache: false, // force requested page not to be cached by browser
url: tipAttribute,
beforeSend: function(xhr) {
if (optionBeforeSend) {optionBeforeSend.call(link, xhr, $cluetip, $cluetipInner);}
$cluetipOuter.children().empty();
$cluetipWait.css({top: mouseY+20, left: mouseX+20, zIndex: $this.data('thisInfo').zIndex-1}).show();
},
error: function(xhr, textStatus) {
if (isActive) {
if (optionError) {
optionError.call(link, xhr, textStatus, $cluetip, $cluetipInner);
} else {
$cluetipInner.html($.resource('toolTipNoContentLoadable'));
}
}
},
success: function(data, textStatus) {
cluetipContents = opts.ajaxProcess.call(link, data);
if (isActive) {
if (optionSuccess) {optionSuccess.call(link, data, textStatus, $cluetip, $cluetipInner);}
$cluetipInner.html(cluetipContents);
// highlight target having a <span id="..">
var idTarget = link.toString().split('#')[1];
if(idTarget!=undefined){
$cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert');
}
}
},
complete: function(xhr, textStatus) {
if (optionComplete) {optionComplete.call(link, xhr, textStatus, $cluetip, $cluetipInner);}
imgCount = $('#cluetip-inner img').length;
if (imgCount && !$.browser.opera) {
$('#cluetip-inner img').bind('load error', function() {
imgCount--;
if (imgCount<1) {
$cluetipWait.hide();
if (isActive) { cluetipShow(pY); }
}
});
} else {
$cluetipWait.hide();
if (isActive) { cluetipShow(pY); }
}
}
};
var ajaxMergedSettings = $.extend(true, {}, opts.ajaxSettings, ajaxSettings);
$.ajax(ajaxMergedSettings);
}
// load an element from the same page
} else if (opts.local) {
var $localContent = $(tipAttribute + (/#\S+$/.test(tipAttribute) ? '' : ':eq(' + index + ')')).clone(true).show();
$cluetipInner.html($localContent);
cluetipShow(pY);
}
};
// get dimensions and options for cluetip and prepare it to be shown
var cluetipShow = function(bpY) {
$cluetip.addClass('cluetip-jtip');
if (opts.truncate) {
var $truncloaded = $cluetipInner.text().slice(0,opts.truncate) + '...';
$cluetipInner.html($truncloaded);
}
if (opts.showTitle) {
$cluetipTitle.show().html(tipTitle ? tipTitle : ' ');
} else {
$cluetipTitle.hide();
}
// INSERTED CODE: get href (= real wiki page link) and add as link in title ("(New Window)"). Cluetip now on link itself!
// Not found how to get access in onShow() to "this"; thus inserted here.
$cluetipTitle.prepend("<a href='"+ $this.attr("href") + "' target='_blank' title='"+ $.resource('toolTipNewWindow') +"' >" + $.resource('newWindow') + "</a>").show();
if (opts.sticky) { // Close text modified directly
var $closeLink = $('<div id="cluetip-close"/>').append(createButton("close", function() {cluetipClose();return false;}));
if(opts.closePosition == 'bottom') {
$closeLink.appendTo($cluetipInner);
} else if(opts.closePosition == 'title'){
$closeLink.prependTo($cluetipTitle);
} else {
$closeLink.prependTo($cluetipInner);
}
if (opts.mouseOutClose) {
$cluetip.bind('mouseleave.cluetip', function() {cluetipClose();});
} else {
$cluetip.unbind('mouseleave.cluetip');
}
}
// now that content is loaded, finish the positioning
var direction = '';
$cluetipOuter.css({zIndex: $this.data('thisInfo').zIndex, overflow: defHeight == 'auto' ? 'visible' : 'auto'});
tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(),$cluetip.height()) : parseInt(defHeight,10);
$cluetipInner.css({
overflow: defHeight == 'auto' ? 'visible' : 'auto',
height: tipHeight - 45 //minus title: should be set automatically but $cluetipTitle.height() and height variants always get 0 or 0px
});
tipY = posY;
baseline = sTop + wHeight;
if (opts.positionBy == 'fixed') {
tipY = posY - opts.dropShadowSteps + tOffset;
} else if ( (posX < mouseX && Math.max(posX, 0) + tipWidth > mouseX) || opts.positionBy == 'bottomTop') {
if (posY + tipHeight + tOffset > baseline && mouseY - sTop > tipHeight + tOffset) {
tipY = mouseY - tipHeight - tOffset;
direction = 'top';
} else {
tipY = mouseY + tOffset;
direction = 'bottom';
}
} else if ( posY + tipHeight + tOffset > baseline ) {
tipY = (tipHeight >= wHeight) ? sTop : baseline - tipHeight - tOffset;
} else if ($this.css('display') == 'block' || link.tagName.toLowerCase() == 'area' || opts.positionBy == "mouse") {
tipY = bpY - tOffset;
} else {
tipY = posY - opts.dropShadowSteps;
}
if (direction == '') {
direction = (posX < linkLeft) ? 'left' : 'right';
}
$cluetip.css({top: tipY + 'px'}).removeClass().addClass('clue-' + direction + '-jtip').addClass(' cluetip-jtip');
if (opts.arrows) { // set up arrow positioning to align with element
var bgY = (posY - tipY - opts.dropShadowSteps);
$cluetipArrows.css({top: (/(left|right)/.test(direction) && posX >=0 && bgY > 0) ? bgY + 'px' : /(left|right)/.test(direction) ? 0 : ''}).show();
} else {
$cluetipArrows.hide();
}
// (first hide, then) ***SHOW THE CLUETIP***
$dropShadow.hide();
$cluetip.hide()[opts.fx.open](opts.fx.openSpeed || 0);
if (opts.dropShadow) { $dropShadow.css({height: tipHeight, width: tipInnerWidth, zIndex: $this.data('thisInfo').zIndex-1}).show(); }
if ($.fn.bgiframe) { $cluetip.bgiframe(); }
// trigger the optional onShow function
opts.onShow.call(link, $cluetip, $cluetipInner);
};
// INACTIVATION
var inactivate = function(event) {
isActive = false;
$cluetipWait.hide();
if (!opts.sticky || (/click|toggle/).test(opts.activation) ) {
cluetipClose();
clearTimeout(closeOnDelay);
}
if (opts.hoverClass) {
$this.removeClass(opts.hoverClass);
}
};
// close cluetip and reset some things
var cluetipClose = function() {
$cluetipOuter
.parent().hide().removeClass();
opts.onHide.call(link, $cluetip, $cluetipInner);
$this.removeClass('cluetip-clicked');
if (tipTitle) {
$this.attr(opts.titleAttribute, tipTitle);
}
$this.css('cursor','');
if (opts.arrows) {
$cluetipArrows.css({top: ''});
}
};
$(document).bind('hideCluetip', function(e) {
cluetipClose();
});
// BIND EVENTS
// activate by click
if ( (/click|toggle/).test(opts.activation) ) {
$this.bind('click.cluetip', function(event) {
if ($cluetip.is(':hidden') || !$this.is('.cluetip-clicked')) {
activate(event);
$('.cluetip-clicked').removeClass('cluetip-clicked');
$this.addClass('cluetip-clicked');
} else {
inactivate(event);
}
this.blur();
return false;
});
// activate by focus; inactivate by blur
} else if (opts.activation == 'focus') {
$this.bind('focus.cluetip', function(event) {
activate(event);
});
$this.bind('blur.cluetip', function(event) {
inactivate(event);
});
// activate by hover
} else {
// clicking is returned false if clickThrough option is set to false
$this[opts.clickThrough ? 'unbind' : 'bind']('click', returnFalse);
$this.hoverIntent({ // hoverintent now REQUIRED!
sensitivity: opts.hoverIntent.sensitivity,
interval: opts.hoverIntent.interval,
timeout: opts.hoverIntent.timeout,
over: function(event) {activate(event);},
out: function(event) {inactivate(event); $this.unbind('mousemove.cluetip');}
});
$this.bind('mouseover.cluetip', function(event) {
$this.attr('title','');
}).bind('mouseleave.cluetip', function(event) {
$this.attr('title', $this.data('thisInfo').title);
});
}
});
};
// OPTIONS
// Each can be explicitly overridden by changing its value
// $.fn.cluetip.defaults.width = 200;
// Each can also be overridden by passing an options map to the cluetip method
// $('a.example').cluetip({width: 200});
// would change the default width to 200 for clueTips invoked by a link with class of "example"
$.fn.cluetip.defaults = { // set up default options
width: 400, // The width of the clueTip
height: 'auto', // The height of the clueTip
cluezIndex: 97, // Sets the z-index style property of the clueTip
positionBy: 'auto', // Sets the type of positioning: 'auto', 'mouse','bottomTop', 'fixed'
topOffset: 24, // Number of px to offset clueTip from top of invoking element
leftOffset: 12, // Number of px to offset clueTip from left of invoking element
local: false, // Whether to use content from the same page for the clueTip's body
localPrefix: null, // string to be prepended to the tip attribute if local is true
hideLocal: true, // If local is true, this determines whether local content to be shown in clueTip should be hidden at its original location
attribute: 'resource', // the attribute to be used for fetching the clueTip's body content -- rel is sanitized by wiki!!!
titleAttribute: 'title', // the attribute to be used for fetching the clueTip's title
splitTitle: '', // A char to split the title attribute into title and divs within clueTip body. Example: |
escapeTitle: false, // whether to html escape the title attribute
showTitle: true, // show title bar of the clueTip, even if title attribute not set
hoverClass: '', // class applied to the invoking element onmouseover and removed onmouseout
cursor: 'help',
arrows: false, // if true, displays arrow on appropriate side of clueTip
dropShadow: true, // set to false if you don't want the drop-shadow effect on the clueTip
dropShadowSteps: 6, // adjusts the size of the drop shadow
sticky: false, // keep visible until manually closed
mouseOutClose: false, // close when clueTip is moused out
activation: 'hover', // set to 'click' to force user to click to show clueTip
// set to 'focus' to show on focus of a form element and hide on blur
clickThrough: false, // if true, and activation is not 'click', then clicking on link will take user to the link's href,
// even if href and tipAttribute are equal
closePosition: 'top', // location of close text for sticky cluetips; can be 'top' or 'bottom' or 'title'
closeText: 'X', // text (or HTML) to to be clicked to close sticky clueTips
truncate: 0, // number of characters to truncate clueTip's contents. if 0, no truncation occurs
// effect and speed for opening clueTips
fx: {
open: 'show', // can be 'show' or 'slideDown' or 'fadeIn'
openSpeed: ''
},
hoverIntent: { // settings for hoverIntent
sensitivity: 4,
interval: 300,
timeout: 0
},
// short-circuit function to run just before clueTip is shown.
onActivate: function(e) {return true;},
// function to run just after clueTip is shown.
onShow: function(ct, ci){},
// function to run just after clueTip is hidden.
onHide: function(ct, ci){},
// whether to cache results of ajax request to avoid unnecessary hits to server
ajaxCache: true,
// process data retrieved via xhr before it's displayed
ajaxProcess: function(data) {
data = data.replace(/<(script|style|title)[^<]+<\/(script|style|title)>/gm, '').replace(/<(link|meta)[^>]+>/g,'');
return data;
},
// can pass in standard $.ajax() parameters. Callback functions, such as beforeSend,
// will be queued first within the default callbacks.
// The only exception is error, which overrides the default
ajaxSettings: {
// error: function(ct, ci) { /* override default error callback */ }
// beforeSend: function(ct, ci) { /* called first within default beforeSend callback }
dataType: 'html'
}
};
})($);
// END Cluetip Plugin Code
/////////////////////
function initCluetips() {
var jPopup = $('span.cluetip a');
if (jPopup.length) { // only if at least one popup exists
var ctHover = {
arrows: true,
height: 275,
width: 400,
fx: {open:'fadeIn', openSpeed:'3'}, // open can be 'show' or 'slideDown' or 'fadeIn'
titleAttribute: 'suppress-title-display',
positionBy: 'bottomTop',
sticky: true,
mouseOutClose: true,
closePosition: 'title'
},
ctClick = {
activation: 'click',
height: 275,
width: 400,
fx: {open:'fadeIn', openSpeed:'3'}, // open can be 'show' or 'slideDown' or 'fadeIn'
titleAttribute: 'suppress-title-display',
sticky: true,
closePosition: 'title',
onShow: function () {// make title draggable
if (typeof mw != "undefined"){
mw.loader.using("jquery.ui.draggable", function () {
$( "#cluetip" ).draggable({handle: "#cluetip-title"});
$("#cluetip-title").css({cursor:"move"});
});
}
}
};
// Design: Without js, a normal link should exist. With js, normal click should only call cluetip, which in turn offers opening in new window.
jPopup.each(function() {
var jLink=$(this), jSpan=jLink.parent();
jLink.attr("resource", jSpan.attr("resource"));
if (jSpan.hasClass("cluetip-hover")) {
jLink.cluetip(ctHover);
} else if (jSpan.hasClass("cluetip-click")) {
jLink.cluetip(ctClick);
}
});
} // END if popup found
}
////////////////////////////////////
// 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)
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
// e: the keyboard event object
function modalLayer_KeyDown(e) {
if ((e.keyCode == 8) || (e.keyCode == 27) || (e.keyCode == 37)) { modalLayer_Hide(); }
}
// Description: Create modal layer and execute fnRender
// fnRender: custom function to display something
// paramsObj: generic parameters passed to "fnRender"
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
}
//////////////////////
// Modal Image zoom
// Description: Create modal layer and execute fnRender
// fnRender: custom function to display something
// paramsObj: generic parameters passed to "fnRender"
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
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 {
modalLayer_ShowIviewerZoomImage ();
}
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
// paramsObj: object containing "caller" = ref to a link including an img
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:
// http://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", "http://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
// caller: reference to a link around image
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
function initImageZooming() {
$("a[href].image img").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
////////////////////////////
// jKey Source
importScript("MediaWiki:JKey.js");
if (mw.config.get('wgAction') == "edit" || mw.config.get('wgAction') == "submit" ) {
importScript("MediaWiki:JKeyWikiEditorHelp.js"); // load help for the wikiEditor
}
// click-text modifications for mw-customcollapsible triggering from outside of mw-collapsible
importScript("MediaWiki:Mw-customcollapsible.js");
/*
function initMarkAllFilledFormElements()
Description: formats background of form elements created by template:Hidden
having class indicateHiddenInputs or other fields
having class indicateFilledFormElements
*/
function initMarkAllFilledFormElements(){
/*
structure of template:Hidden:
div.collapsebox
├ div.switcher (float)
├ div.collapsetitle
└ div.collapsecontent
tr.collapsebox
└ th/td
├ div.switcher (float)
└ div.collapsetitle
tr.collapsecontent
*/
var jDivHiddenFormTexts = $(
// select only input type text + textarea with values
"div.collapsecontent.indicateHiddenInputs * :text[value!=]" +
", div.collapsecontent.indicateHiddenInputs * textarea[value!=]"
);
var jTrHiddenFormTexts = $(
// select only input type text + textarea with values
"tr.collapsecontent.indicateHiddenInputs *:not(.collapsecontent) :text[value!=]" +
", tr.collapsecontent.indicateHiddenInputs *:not(.collapsecontent) textarea[value!=]"
);
var jIndicateFilledFormElements = $(
".indicateFilledFormElements:text[value!=]" +
", textarea[value!=].indicateFilledFormElements "
);
/*
pale orange:
hsv → 36 20 99 36 10 99 36 05 99
#fce8ca #fcf2e3 #fcf7f0
pale yellow
hsv → 50 18 100 50 10 100
#fff7d0 #fffbe6
*/
var bgcolor = {'background-color':'#fffbe6'};
var bgcolor_darker = {'background-color':'#fff7d0'};
// hidden elements
// <div>
//indicate the fields itself
jDivHiddenFormTexts
.css(bgcolor); //pale orange
// parent switcher
jDivHiddenFormTexts
.parentsUntil('.collapsebox')
.parent()
.find('div.switcher')
.css(bgcolor_darker); //pale orange
// <tr>
//indicate the fields itself
jTrHiddenFormTexts
.css({'background-color':'#fce8ca'}); //pale orange
jTrHiddenFormTexts.each(function(){
if($.trim($(this).text())){// make sure there is really text
$(this).parentsUntil('.collapsebox', '.formtable')
.find('div.switcher')
.css(bgcolor_darker); //pale orange
}
});
//<select>
$(
'.collapsecontent.indicateHiddenInputs select option[value!=]:selected' +
', select.indicateFilledFormElements option[value!=]:selected'
).parent().css(bgcolor);
// all visible form elements
jIndicateFilledFormElements
.css(bgcolor); //pale orange
}// END initMarkAllFilledFormElements()
///////////////////////////
// moveable table of contents
//////////////////////////
// offers floating the table of contents to the left or right side (will make part of the page invisible!)
function initmoveTOC() {
$('#togglelink').after(' | <span style="cursor:pointer;color:blue;" title="'+
$.resource('toolTipFloatleft') +'" onclick="moveTOC(\'0px\')" >◄</span> <span style="cursor:pointer;color:blue;" title="'+
$.resource('toolTipFloatright') +'" onclick="moveTOC(\'right\')" >►</span> <span id="restorestatic" style="display:none;">| <span style="cursor:pointer;" title="'+
$.resource('toolTipUnfloat') +'" onclick="moveTOC(\'restore\')" >×</span></span> ');
}// end initmoveTOC
function moveTOC(position) {
/* position may be string "right", or "restore", everthing else is interpreted as "left" */
var offsetX, offsetY;
var TOC = $('#toc');
if ($('#moveTOC').length === 0) {
TOC.wrapAll('<div id="moveTOC"></div>');
}
if ($('#toTop').length === 0) {
$("#toctitle h2").after('<a href="#mw-head" id="toTop" title="'+$.resource('toolTipNavigatePagetop') +'">↑ </a>');
}
switch (position) {
case "right":
offsetX = $("#content").width() - 140;
offsetY = - 55;
break;
case "restore":
TOC.unwrap();
$('#toTop').remove();
$('#restorestatic').hide('slow');
$("#toctitle h2").show('slow');
break;
default:
offsetY = -110;
offsetX = 0 - $("#mw-panel").width() - 14 ; // padding#content
break;
}// end switch
if (position != "restore") {
$("#toctitle h2").hide('slow');
$('#restorestatic').show();
$('#moveTOC')
.css({"position":"absolute"})
.animate({'max-width':"13em"})
.animate({
'left': $(window).scrollLeft()+ offsetX +"px",
'top' : $(window).scrollTop() + offsetY +"px"
});
// emulate position fixed see CSS .navigation-left .navigation-right
$(window).scroll(function(){
$('#moveTOC')
.animate({
'left': $(window).scrollLeft()+ offsetX +"px",
'top' : $(window).scrollTop() + offsetY +"px"
},{queue: false, duration: 400} // queue: don't wait
);
});
} // end positioning unless restore
}// end function moveTOC()
///////////////////////////////////////////////////////////////
// specific to http://offene-naturfuehrer.de
///////////////////////////////////////////////////////////////
// collapsible parts: div and tr → Template:Hidden
// may be fused later with toggleCollapse
// main difference: initCollapsebox() uses a switcher defined by the Wikitemplate
// and is not using a javascript resource title.
// * the only advanced feature it has: collpsible table rows
// * otherwise use http://www.mediawiki.org/wiki/RL/DM#jQuery.makeCollapsible
// DEPENDENCIES resources: $.jl18n.en.toolTipExpand and $.jl18n.en.toolTipCollapse
// specific to http://offene-naturfuehrer.de
function initCollapsebox() {
/* is nested in: div.collapsebox
└ div.switcher
└ div.collapsecontent */
var hasSwitcher = jQuery("div.switcher .show, div.switcher .hide");
if(hasSwitcher.length){
jQuery.each(hasSwitcher, function(index){// add tooltip
hasSwitcher[index].title = hasSwitcher[index].className === "show"? jQuery.resource("toolTipExpand") : jQuery.resource("toolTipCollapse");
});
jQuery("div.collapsebox div.switcher").live('click',
function() {
jQuery(this).nextAll("div.collapsecontent:first").slideToggle(250);
/* $(this).toggle() does not work in live as toggle is a bind()
therefore toggle must be bound to a different DOM element */
jQuery(this).find(".show, .hide").toggle();
});
/* is in a table: tr.collapsebox
└ div.switcher
tr.collapsecontent */
jQuery("tr.collapsebox div.switcher").live('click',
function() {
jQuery(this).closest("tr.collapsebox").nextAll("tr.collapsecontent:first").toggle();
jQuery(this).find(".show, .hide").toggle();
});
// TODO is a better generic check possible?
if (mw.config.get('wgPageName') === "Spezial:Suche") {
if (!$(".mw-search-results").length) {
// open box with additional search possibilities
$(".switcher .show").trigger('click');
}
}
return true;
}
return false;
}// END initCollapsebox()
///////////////////////
// Table Filter/Sort //
///////////////////////
// specific to http://offene-naturfuehrer.de
// TODO ?replace by jQuery plugin datatable?
function initTableFilterSort(){ // see MediaWiki:SortTableFilter.js
/* Note: problem is auto-inserted <tbody></tbody> by the browser: must be removed and
replaced by thead + tbody. jQuery.unwrap() was not successful */
// add possibly more classes from http://www.javascripttoolbox.com/lib/table/documentation.php
// Note: only applies to non-nested tables
var jAutotables = $('table.table-autosort, table.table-autofilter, table-autostripe,table-sorted-asc,table-sorted-desc, table-filtered');
if (jAutotables.length) {
// Note: in MediaWiki:SortTableFilter.js 'InputFilterTitle' was not recognized
$.extend(true, $.jI18n, {
en: {
AutoSortTitle : 'Click to sort',
FilterAllLabel: 'Filter: All',
InputFilterTitle: 'Filter text (case sensitive, uses reg. expressions)'
},
de: {
AutoSortTitle : 'Zum Sortieren klicken',
FilterAllLabel: 'Zeige: alle',
InputFilterTitle: 'Text filtern (GROß/klein!, nutzt reg. Ausdrücke)'
}
});
// get sortable/filterable here already otherwise multiple th-filters
$.getScript(mw.config.get( 'wgServer' ) + mw.config.get( 'wgScript' ) + "?title=MediaWiki:SortTableFilter.js&action=raw&ctype=text/javascript",
function(){return true;});
// modify tables to introduce thead structure
jAutotables.each(function(index){ //TODO simplify code? if()…
// There may or may not be a tbody around tr. NOTE: $.unwrap() does not work here!
// Memo: find('tr th') finds th, .parent() retrieves tr! .wrapAll will wrap inside DOM, not in return value! .detach() returns detached
// OK in FF 3.6 and IE 7-8, not in IE6 (like the old code)
// FURTHER WORK: Ideally, all normal tr td should remain in a tbody.
var jThis = $(this),
jThead = jThis.find('tr th').parent().detach(),
jTfoot = jThis.find('tr[class=tfoot] td').parent().detach(),// remove it from the DOM
jTbody = jThis.find('tbody:first');
if (jTbody.length===0) {
jThis.children().wrapAll('<tbody/>');
jTbody = jThis.find('tbody:first');
}
jTbody.before($("<thead/>").append(jThead));
if (jTfoot.length) {
jTbody.after($("<tfoot/>").append(jTfoot));
}
// th with class="input" gives an input field instead of selections
$(this).find('th[class=input]').append('<input name="filter" title="'+$.resource('InputFilterTitle')+'" size="8" onkeyup="Table.filter(this,this)">');
});// end each()
} // END if any autotable
}
///////////////////////
// specific to http://offene-naturfuehrer.de
// Page-specific scripts:
switch (mw.config.get( 'wgPageName' )) { // Minimize the pages on which the code will be loaded
// vielleicht später auch woanders, d.h. als vorgewählte Suche auf Taxaseiten beispielsweise?
case "Datenquellen":
case "Online-Überprüfung_von_Bestimmungen":
case "Tipps_zur_Bildersuche":
case "Spezial:Suche":
importScript("MediaWiki:SearchTools.js");
break;
case "Hilfe:Nummerierungen_im_jKey_abändern_(Lead_Nummern)":
importScript("MediaWiki:JKeyRenumberingTool.js");
break;
case "Hilfe:Konvertierung_geschachtelt-eingerückter_Schlüssel_in_das_ON-Format":
case "Testseite":
importScript("MediaWiki:JKeyTextToLeadTemplateTool.js");
break;
}
// Page-specific scripts more flexible:
if (mw.config.get('wgPageName').match(/^Basismerkmale_für/i)
|| mw.config.get('wgPageName').match(/^Vorlage:Character_State/i)
|| mw.config.get('wgPageName').match(/^Vorlage:Character_Definition/i)
) {
importScript("MediaWiki:ToolGetRandomId.js");
}
/*
mediaWiki.loader.state({"site":"ready"}); is appended and takes care of
the document ready event. If $(document).ready() is used, for forms
$ is then unknown. Scope or closure problem? (AP 2011-02-20)
*/
// When document is completely loaded
$(document).ready(function() {
initImageZooming();
initTargetHighlighting(); // page-internal jumps
initmoveTOC(); // TOC CSS position fixed or static
// specific to http://offene-naturfuehrer.de
initTableFilterSort(); // tests internally whether table with corresponding classes exist
}); // end $(document).ready()
// ***** CAN initCollapseButtons BE DELETED?
// TEST: COMMENTED OUT
// initCollapseButtons(); // strongly changes page layout: execute first
initCluetips();
// page specific
if(mw.config.get( 'wgAction' )==="formedit" || mw.config.get( 'wgCanonicalSpecialPageName' )==="FormEdit"){
// initConfirmDeleteSubform();
initMarkAllFilledFormElements();
}
// specific to http://offene-naturfuehrer.de
initCollapsebox(); //collapsible parts
// </source>