Difference between revisions of "MediaWiki:Common.js"

From Biowikifarm Metawiki
Jump to: navigation, search
m (fix var fn-name = function())
(updating to code from http://species-id.net/wiki/MediaWiki:Common.js - see there for creators and contributors)
Line 3: Line 3:
 
// Except for additions at the end, imported from en.wikipedia.org and modified (parts removed).
 
// Except for additions at the end, imported from en.wikipedia.org and modified (parts removed).
 
// for authors see http://en.wikipedia.org/w/index.php?title=MediaWiki:Common.js&action=history
 
// for authors see http://en.wikipedia.org/w/index.php?title=MediaWiki:Common.js&action=history
// NEW: targetHighlighting (Hagedorn) and collapsibleTables (wikipedia) moved to jKey.js!
+
/*
 +
* dependencies:
 +
*  MediaWiki:Edittools.js
 +
*  MediaWiki:Common.js/edit.js
 +
*  MediaWiki:JKey.js
 +
*  MediaWiki:JKeyWikiEditorHelp.js
 +
*/
  
 +
/*global jQuery, document, screen, window, location, navigator, unescape, Image, clearTimeout, wgPageName, wgServer, wgScript, wgContentLanguage, wgAction, wgArticlePath, addOnloadHook, importScript, setTimeout, appendCSS, ts_alternate_row_colors */ /* = settings for JSLint */
 +
"use strict"; // set ECMAScript 5 Strict Mode
  
/* Import more specific scripts if necessary */
+
// NO LONGER NEEDED:
if (wgAction == "edit" || wgAction == "submit" || wgPageName == "Special:Upload") { //scripts specific to editing pages
+
// Following should not be necessary, and without all works in Chrome and Firefox, however, not in IE (6, 8 tested)!
    importScript("MediaWiki:Common.js/edit.js");
+
// var $ = $j;  // in later mediawiki versions set to jQuery, or unnecessary
 +
 
 +
/* 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"
 +
 
 +
 
 +
 
 +
// Table sorting fixes
 +
// Disables code in table sorting routine to set classes on even/odd rows
 +
var ts_alternate_row_colors = false;
 +
 
 +
// 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;
 +
      }
 +
    }
 +
  }
 +
}
 +
});
  
/* Scripts specific to Internet Explorer */
+
// Editing-page-specific: see also below JKeyWikiEditorHelp.js
if (navigator.appName == "Microsoft Internet Explorer") {
+
if (wgAction === "edit" || wgAction === "submit" || wgCanonicalSpecialPageName === "Upload") {
     /** Internet Explorer bug fix **************************************************
+
  if (typeof EditTools === 'undefined') {
    Description: Fixes IE horizontal scrollbar bug
+
     importScript('MediaWiki:Edittools.js');
    Maintainers: [[User:Tom-]]?
+
  }
    */
+
  if (typeof $.wikiEditor === 'undefined') {
     var oldWidth;
+
    importScript("MediaWiki:Common.js/edit.js");  // TODO remove or adjust? AP 2011-08-25
     var docEl = document.documentElement;
+
  }
 +
}
 +
 
 +
$.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 :        "&nbsp;(show less)&nbsp;",
 +
    captionExpand :          "&nbsp;(more...)&nbsp;",
 +
    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>Sorry, the contents could not 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 :        "&nbsp;(weniger anzeigen)&nbsp;",
 +
    captionExpand :          "&nbsp;(mehr...)&nbsp;",
 +
    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 :        "&nbsp;(mostra di meno)&nbsp;",
 +
    captionExpand :          "&nbsp;(più...)&nbsp;",
 +
    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; txtContent: direct string
 +
* href: URI (for linkBuilder only)
 +
*/
 +
$.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>&nbsp;" : "")
 +
    + $.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;
 +
  }
 +
}
  
    var fixIEScroll = function() {
+
// Add onclick events to all page-internal links
        if (!oldWidth || docEl.clientWidth > oldWidth)
+
function initTargetHighlighting() {
            doFixIEScroll();
+
  for (var i=0, max=document.links.length; i < max; i++) {
        else
+
    var lnk = document.links[i];
            setTimeout(doFixIEScroll, 1);
+
    if ((lnk.pathname === location.pathname) && lnk.hash.length > 1) { // page internal link; exluding single "#"
        oldWidth = docEl.clientWidth;
+
      lnk.onclick = function() { highlightTarget(this); };
 
     }
 
     }
   
+
  }
    var doFixIEScroll = function() {
+
}
      docEl.style.overflowX = (docEl.scrollWidth - docEl.clientWidth < 4) ? "hidden" : "";
+
 
 +
/////////////////////
 +
// Collapse Tables //
 +
/////////////////////
 +
 
 +
// 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 &nbsp; + non-words (\W) */
 +
      // OLD: shallExpand = (jLink.text().replace(/(&nbsp;|\W)/g,'') == $.resource("captionExpand").replace(/(&nbsp;|\W)/g,''));
 +
      // *** TRYING:
 +
      shallExpand = (jLink.text().replace(/(\W)/g,'') == $.resource("captionExpand").replace(/(\W)/g,''));
 
     }
 
     }
      
+
     jLink
    document.attachEvent("onreadystatechange", fixIEScroll);
+
      .attr("title", $.resource( shallExpand ? "toolTipCollapse" : "toolTipExpand" ))
    document.attachEvent("onresize", fixIEScroll);
+
      .html($.resource( shallExpand ? "captionCollapse" : "captionExpand" ));
   
+
     jTable.find("tr:not(tr:first)").toggle(shallExpand);
    // In print IE (7?) does not like line-height
+
     // show/hide all in set, tr of nested tables are included
    appendCSS( '@media print { sup, sub, p, .documentDescription { line-height: normal; }}');
+
  }
   
+
  return false;
    //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"
+
  
 +
function toggleAllCollapsible(shallExpand) { // all collapsible tables on wiki page
 +
  $("table span.collapseButton a").each(function() {toggleCollapse(this, shallExpand);} );
 +
  $("span.toggleAllExtras input[type=checkbox]").each(function() {this.checked = shallExpand;} ); // in case of multiple keys
 +
}
  
 +
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);
 +
}
  
/** Table sorting fixes
 
  *  Description: Disables code in table sorting routine to set classes on even/odd rows
 
  */
 
ts_alternate_row_colors = false;
 
  
// jkey source
+
////////////////////////////////////
 +
// 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,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;');
 +
      }
 +
 
 +
      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', height: defHeight});
 +
      tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(),$cluetip.height()) : parseInt(defHeight,10);
 +
      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'
 +
    };
 +
    // 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(wgServer + 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");
 
importScript("MediaWiki:JKey.js");
//moveable TOCs
+
  if (wgAction == "edit" || wgAction == "submit" ) {
importScript("MediaWiki:MoveTOC.js");
+
  importScript("MediaWiki:JKeyWikiEditorHelp.js"); // load help for the wikiEditor
$j(document).ready(function() {
+
  }
   initmovingTOC(); // TOC CSS position fixed or static → MediaWiki:Gadget-moveTOC.js
+
 
});
+
 
 +
/*
 +
  function initMarkAllFilledFormElements()
 +
  Description: indicates 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') +'">↑&nbsp;</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()
 +
 
 +
 
 +
 
 +
/*
 +
  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
 +
}); // end $(document).ready()
 +
  initCollapseButtons(); // strongly changes page layout: execute first
 +
  initCluetips();
 +
  // page specific
 +
  if(wgAction==="formedit" || wgCanonicalSpecialPageName ==="FormEdit"){
 +
  // initConfirmDeleteSubform();
 +
    initMarkAllFilledFormElements();
 +
  }
 
// </source>
 
// </source>

Revision as of 20:54, 5 November 2011

// <source lang="javascript">
// This JavaScript will be loaded for all users on every page load.
// Except for additions at the end, imported from en.wikipedia.org and modified (parts removed).
// for authors see http://en.wikipedia.org/w/index.php?title=MediaWiki:Common.js&action=history
/*
 * dependencies:
 *   MediaWiki:Edittools.js
 *   MediaWiki:Common.js/edit.js
 *   MediaWiki:JKey.js
 *   MediaWiki:JKeyWikiEditorHelp.js
 */

/*global jQuery, document, screen, window, location, navigator, unescape, Image, clearTimeout, wgPageName, wgServer, wgScript, wgContentLanguage, wgAction, wgArticlePath, addOnloadHook, importScript, setTimeout, appendCSS, ts_alternate_row_colors */ /* = settings for JSLint */
"use strict"; // set ECMAScript 5 Strict Mode

// NO LONGER NEEDED:
// Following should not be necessary, and without all works in Chrome and Firefox, however, not in IE (6, 8 tested)!
// var $ = $j;  // in later mediawiki versions set to jQuery, or unnecessary

/* 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"



// Table sorting fixes
// Disables code in table sorting routine to set classes on even/odd rows
var ts_alternate_row_colors = false;

// 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 (wgAction === "edit" || wgAction === "submit" || 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 :        "&nbsp;(show less)&nbsp;",
    captionExpand :          "&nbsp;(more...)&nbsp;",
    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>Sorry, the contents could not 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 :        "&nbsp;(weniger anzeigen)&nbsp;",
    captionExpand :          "&nbsp;(mehr...)&nbsp;",
    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 :        "&nbsp;(mostra di meno)&nbsp;",
    captionExpand :          "&nbsp;(più...)&nbsp;",
    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; txtContent: direct string
 * href: URI (for linkBuilder only)
 */
$.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>&nbsp;" : "")
    + $.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 //
/////////////////////

// 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 &nbsp; + non-words (\W) */
      // OLD: shallExpand = (jLink.text().replace(/(&nbsp;|\W)/g,'') == $.resource("captionExpand").replace(/(&nbsp;|\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;
}

function toggleAllCollapsible(shallExpand) { // all collapsible tables on wiki page
  $("table span.collapseButton a").each(function() {toggleCollapse(this, shallExpand);} );
  $("span.toggleAllExtras input[type=checkbox]").each(function() {this.checked = shallExpand;} ); // in case of multiple keys
}

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,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;');
      }

      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', height: defHeight});
      tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(),$cluetip.height()) : parseInt(defHeight,10);
      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'
    };
    // 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(wgServer + 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 (wgAction == "edit" || wgAction == "submit" ) {
   importScript("MediaWiki:JKeyWikiEditorHelp.js"); // load help for the wikiEditor
  }


/*
  function initMarkAllFilledFormElements()
  Description: indicates 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') +'">↑&nbsp;</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()



/*
   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
}); // end $(document).ready()
  initCollapseButtons(); // strongly changes page layout: execute first
  initCluetips();
  // page specific
  if(wgAction==="formedit" || wgCanonicalSpecialPageName ==="FormEdit"){
   // initConfirmDeleteSubform();
    initMarkAllFilledFormElements();
  }
// </source>