MediaWiki:Common.js
Aus Bayernflora
Version vom 10. August 2015, 12:13 Uhr von Andreas Plank (Diskussion | Beiträge) (switch nonsense für MediaWiki:QuizFelixRiester.js: switch(mw.config.QuizFelixRiester.js) → switch(mw.config.get( 'wgPageName' )))
Hinweis: Leeren Sie nach dem Speichern den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Internet Explorer: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
- Opera: Extras → Internetspuren löschen … → Individuelle Auswahl → Den kompletten Cache löschen
// <syntaxhighlight lang="javascript">
/* Das folgende JavaScript wird für alle Benutzer geladen. */
// This JavaScript will be loaded for all users on every page load.
// The first version was imported from en.wikipedia.org, for authors see http://en.wikipedia.org/w/index.php?title=MediaWiki:Common.js&action=history
// Strongly modified (remove, add) since.
/*
* dependencies:
* @requires: MediaWiki:Common.js/edit.js
* @requires: MediaWiki:Edittools.js
* @requires: MediaWiki:Gadget-HeadingLink.js
* @requires: MediaWiki:JKey.js
* @requires: MediaWiki:JKeyRenumberingTool.js
* @requires: MediaWiki:JKeyTextToLeadTemplateTool.js
* @requires: MediaWiki:JKeyWikiEditorHelp.js
* @requires: MediaWiki:Jquery.zoomImage.js
* @requires: MediaWiki:ModifyTalkPageRedlink2AddNewSection.js
* @requires: MediaWiki:Mw-customcollapsible.js
* @requires: MediaWiki:SearchTools.js
* @requires: MediaWiki:WikiEditor-insert-file-with-preview.js
* @requires: MediaWiki:WikiEditor-insert-Zitat.js
*/
/*global jQuery, document, screen, window, location, navigator, unescape, Image, clearTimeout, addOnloadHook, importScript, setTimeout, appendCSS, mw */
/* = settings for JSLint */
/* should go into mw.config: wgPageName, wgServer, wgScript, wgAction, wgCanonicalNamespace */
"use strict"; // set ECMAScript 5 Strict Mode
/**
* @description Social media integration; using async script inserted here:
*/
if ((mw.config.get('wgAction') === 'view')
&& (mw.config.get('wgCanonicalNamespace') !== 'Special')) {
$("h1#firstHeading:first").prepend(
"<!-- SOCIAL MEDIA START REMOVED, GET AGAIN FROM OLD VERSION IF IE8-9 ARE WORKING AGAIN!!!-->"
+ "<!-- SOCIAL MEDIA END -->"
);
}
/**
* @description 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"
/**
* @description: helper function to escape jQuery IDs
* @param {string} myid HTML ID
* @returns {@exp;myid@call;replace|String}
*/
function jqueryEscapeId(myid) {
if(myid.substr(0, 1) === "#"){
return myid.replace(/(:|\.)/g,'\\$1');
} else {
return '#' + myid.replace(/(:|\.)/g,'\\$1');
}
}
/**
* @description Footnotes as unformatted tooltip - from it.wikipedia.org under same license
*
* @requires http://wiki.bayernflora.de/w/skins/common/wikibits.js
* @returns {undefined}/**
*/
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;
}
}
}
}
})
/** @description Editing-page-specific: see also below JKeyWikiEditorHelp.js */
if (mw.config.get('wgAction') === "edit" || mw.config.get('wgAction') === "submit" || mw.config.get('wgCanonicalSpecialPageName') === "Upload") {
if (typeof EditTools === 'undefined') {
importScript('MediaWiki:Edittools.js');
}
if (typeof $.wikiEditor === 'undefined') {
importScript("MediaWiki:Common.js/edit.js"); // TODO remove or adjust? AP 2011-08-25
}
}
/**
* @namespace resource string dictionary
*
* Note: Commons uses collapse/expand ▲/▼, but this looks better in strict box
* layouts that in the free-wrapping key statements
*
* Nomenclature proposal: if an extra plugin is used, strings can be designated as
* “plugin_toolTipSomthing” otherwise just “toolTipSomthing” (global string). So it’s more clear if
* somebody wants to deactivate a plugin and remove strings from the resource dictionary.
* @augments $
* @type object
*/
$.jI18n = {
en: {
ClueTip_newWindow : "(New Window …)",
ClueTip_toolTipClose : "Click to close",
ClueTip_toolTipNewWindow : "(click to open content in a new window or tab)",
ClueTip_toolTipNoContentLoadable:"<i>No content could be loaded</i>",
CollapseBox_captionCollapse : " (show less) ",
CollapseBox_captionExpand : " (more...) ",
CollapseBox_toolTipCollapse : "(click to hide information below)",
CollapseBox_toolTipExpand : "(click to show more information below)",
HeadingLink_toolTipHeadingLink: "Click to show (permanent) link to this headline", // MediaWiki:Gadget-HeadingLink
HeadingLink_toolTipHeadingLinkHelp: "(1) Normal link to this head line or (2) the permanent link with version number:",// MediaWiki:Gadget-HeadingLink
ImageZoom1st_iconCloseWindow : "http://upload.wikimedia.org/wikipedia/commons/8/87/Close_icon_default.jpg",
ImageZoom1st_iconCloseWindowHover : "http://upload.wikimedia.org/wikipedia/commons/d/d0/Close_icon_hover.jpg",
ImageZoom1st_imageMetadataLink : "(Information about Creator, License and Copyright)",
ImageZoom1st_toolTipImageZooming : "Images can be enlarged by clicking on it",
ImageZoom1st_zoomNotPossible : "(This image can not be further enlarged)",
// see MediaWiki:Jquery.zoomImage.js
ImageZoom2nd_iconMagnifier: "http://species-id.net/o/media/f/f7/Iviewer.zoom_in.gif",
ImageZoom2nd_iconMagnifierHover: "http://species-id.net/o/media/5/5c/Iviewer.zoom_out.gif",
ImageZoom2nd_iconLoader: "http://upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
ImageZoom2nd_toolTipLoad : "(click to load largest available image; this may take considerable time to load)",
ImageZoom2nd_textZoomOrig: "Zooming facility",
MoveTOC_toolTipFloatleft : "floating on the left side",
MoveTOC_toolTipFloatright: "floating on the right side",
MoveTOC_toolTipNavigatePagetop : "Top of page",
MoveTOC_toolTipUnfloat: "back to default position",
jKey_expandAll : "Show all extras",
jKey_iconOverview : "http://upload.wikimedia.org/wikipedia/commons/thumb/2/22/View-pause_Gion_simple.svg/20px-View-pause_Gion_simple.svg.png",
jKey_iconResume : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
jKey_iconStart1st : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
jKey_iconStartNew : "http://upload.wikimedia.org/wikipedia/commons/thumb/0/05/View-refresh_Gion_simple.svg/20px-View-refresh_Gion_simple.svg.png"
},
de: {
ClueTip_newWindow : "(Neues Fenster …)",
ClueTip_toolTipClose : "Zum Schließen klicken",
ClueTip_toolTipNewWindow : "(klicken um Inhalt in neuem Fenster oder Reiter zu öffnen)",
ClueTip_toolTipNoContentLoadable:"<i>Leider konnte der Inhalt nicht geladen werden.</i>",
CollapseBox_captionCollapse : " (weniger anzeigen) ",
CollapseBox_captionExpand : " (mehr...) ",
CollapseBox_toolTipCollapse : "(klicken um Zusatzinformationen zu verbergen)",
CollapseBox_toolTipExpand : "(klicken um Zusatzinformationen anzuzeigen)",
HeadingLink_toolTipHeadingLink: "Klicken um (permanenten) Link dieser Überschrift anzuzeigen",// MediaWiki:Gadget-HeadingLink
HeadingLink_toolTipHeadingLinkHelp: "(1) Link zu dieser Überschrift oder (2) Link mit Versionsnummer:",// MediaWiki:Gadget-HeadingLink
ImageZoom1st_imageMetadataLink : "(Informationen zu Autor, Lizenz und Copyright)",
ImageZoom1st_toolTipImageZooming : "Bilder können durch Anklicken vergrößert betrachtet werden",
ImageZoom1st_zoomNotPossible : "(Dieses Bild kann nicht weiter vergrößert werden)",
// see MediaWiki:Jquery.zoomImage.js
ImageZoom2nd_toolTipLoad : "(klicken um Originalbild nachzuladen; bei großen Bildern kann dies u. U. langsam sein)",
ImageZoom2nd_textZoomOrig: "Vergrößerungsfunktion",
MoveTOC_toolTipFloatleft : "Links schwebend",
MoveTOC_toolTipFloatright: "Rechts schwebend",
MoveTOC_toolTipNavigatePagetop : "Zum Seitenanfang",
MoveTOC_toolTipUnfloat: "Zurück zur Normalposition",
jKey_expandAll : "Alle Zusatzinformationen zeigen"
},
it: {
ClueTip_toolTipClose : "Clicca per chiudere",
CollapseBox_captionCollapse : " (mostra di meno) ",
CollapseBox_captionExpand : " (più...) ",
ImageZoom1st_imageMetadataLink : "(Informazione sull'Autore, Licenza e Copyright)",
ImageZoom1st_toolTipImageZooming : "Le immagini possono essere ingrandite cliccandoci sopra",
ImageZoom1st_zoomNotPossible : "(Al momento non è possibilie ingrandire questa immagine)", // TODO translation see en version
jKey_expandAll : "Mostra tutti informazione" //REVISE
}
};
/**
* @description Get resource string (text, image URLs) for a given language, based on a string-key
* If no resource is defined in a given language for a resource key, the resource for "en" will be returned,
* if this is missing as well an error message.
* @augments $
* @requires mw.config for getting global variables
* @param {string} resourceKey key for the resource
* @returns {String}
*/
$.resource = function (resourceKey) {
var lang = 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.");
};
/**
* @description Create html string for link with image and/or text content
* * @requires $.resource()
* @param {string} txtResourceKey resource keys (multilingual {@link $.resource()}
* @param {html} txtContent displayed content of a link
* @param {url} href
* @param {string} attributes string of combined other attributes of link element; must use ' as inner quotes, and \" inside event functions
* @returns {@exp;txtResourceKey@pro;length|String|@exp;txtContent@pro;length@exp;txtResourceKey@pro;length}
*/
$.linkBuilder = function (txtResourceKey, txtContent, href, attributes) {
return (txtResourceKey.length ? "<a "
+ " href='" + href + "' "
+ " " + (attributes.length ? attributes : "") + ">"
+ $.resource(txtResourceKey)
+ "</a>" : (txtContent.length ? "<a "
+ " href='" + href + "' "
+ " " + (attributes.length ? attributes : "") + ">"
+ txtContent +
"</a>" : "")
);
};
/**
* @requires $.resource()
* @param {string} imgResourceKey resource key {@link $.resource()}
* @param {string} txtResourceKey resource key {@link $.resource()}
* @param {string} attributes HTML
* @returns {String}
*/
$.imglinkBuilder = function (imgResourceKey, txtResourceKey, attributes) {
return (imgResourceKey.length ? "<a "
+ " href='#'" + (attributes.length ? " " + attributes : "") + "><img src='" + $.resource(imgResourceKey) + "' /></a> " : "")
+ $.linkBuilder(txtResourceKey, "", "#", attributes);
};
/**
* @description return a random integer
* @param {integer} min
* @param {integer} max
* @returns {@exp;@call;parseInt}
*/
$.random = function (min, max) { // NO CHECKS: if(min>max) {return -1;} if(min==max) {return min;}
return (min + parseInt(Math.random() * (max - min + 1), 10));
};
///////////////////////
// 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
*/
/**
* @description Highlight a single element that is target of the link-object caller (e.g. <a href=...>)
*
* @requires resetHighlight()
* @requires jqueryEscapeId()
* @param {type} caller
* @returns {undefined}
*/
function highlightTarget(caller) {
var target = $(jqueryEscapeId(caller.hash)); // 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);
}
}
/**
* @description Stop highlighting
* @requires highlightTarget()
* @requires jqueryEscapeId()
* @param {string} hash ID of the target, could be 'a.34:'
* @param {string} backColor background color
* @param {string} txtDeco CSS style text decoration
* @returns {undefined}
*/
function resetHighlight(hash, backColor, txtDeco) {
if (hash) { // reset
var tStyle = $(jqueryEscapeId(hash)).get(0).style;
tStyle.backgroundColor = backColor;
tStyle.textDecoration = (txtDeco === "") ? "none" : txtDeco;
}
}
/**
* @description Add onclick events to all page-internal links
*
* @requires resetHighlight()
* @requires highlightTarget()
* @requires jqueryEscapeId()
* @returns {undefined}
*/
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); };
}
}
}
/**
* @description collapse all collapsible key tables on wiki page
* using the MediaWiki mw-customtoggle mechanism on all CSS class of
* class="decisiontree"
* @requires MediaWiki:JKey.js
* @param {boolean} shallExpandThisKey
* @param {selector} caller
* @returns {undefined}
*/
function toggleAllCollapsible(shallExpandThisKey, caller) {
var $pseudolinks = $(caller).closest(".decisiontree")
.find("span.pseudolink");
// debug log messages
if ($pseudolinks.length) {
$pseudolinks.each(function () {
var thisClasses = $(this).attr('class'),
// get last string position/index
thisClassesStopPos = thisClasses.indexOf(" ", thisClasses.indexOf("mw-customtoggle")),
// get mw-customtoggle-Key class
thisClassMwCustomtoggle = thisClasses.substring(
thisClasses.indexOf("mw-customtoggle"), // from index
thisClassesStopPos === -1 ? thisClasses.length : thisClassesStopPos // to index
),
// extract correspoding id
thisIdToToggle = thisClassMwCustomtoggle.replace("mw-customtoggle", "mw-customcollapsible"),
// check clicked status using class mw-collapsed
thisIsCollapsed = $('#' + thisIdToToggle).hasClass('mw-collapsed'),
doExpandThis;
if (shallExpandThisKey) {
doExpandThis = thisIsCollapsed ? true : false;
} else {
doExpandThis = thisIsCollapsed ? false : true;
}
if (doExpandThis) {
$(this).trigger('click');
}
});
}// pseudolink found
}// toggleAllCollapsible()
////////////////////////////////////
// Cluetip hover and click popups //
////////////////////////////////////
/**
* @description Utility for Cluetip, Modal layer, Image Zoom:
* Create appendable jquery object, fnAction = function bound to click.
* NOTE All functions called within createButton() should return false
* to prevent appending a # to the URL from clicking <a href='#'></a>
* @param {string} kindOfButton type of button ('zoomImg', 'close')
* @param {function} fnAction function to bind on that link
* @returns {@exp;@exp;@call;$@pro;append@pro;h@call;over@call;@call;click|@exp;@call;$@pro;append@pro;h@call;over@call;@call;click}
*/
function createButton(kindOfButton, fnAction) {
switch (kindOfButton) {
case "zoomImg":
return $("<a href='#' title='"+$.resource('ImageZoom2nd_toolTipLoad')+"' />")
.append(
"<img src='"+$.resource("ImageZoom2nd_iconMagnifier")+"' align='middle' style='border:1px solid gray;'>" +
'<span style="position:absolute;left:20px;top:0px;white-space:nowrap;">'+$.resource("ImageZoom2nd_textZoomOrig")+'</span>'
) // text after img seems to be inline only with position:absolute
.hover(
function() { $(this).find("img:first").attr({src: $.resource("ImageZoom2nd_iconMagnifierHover"), style :'border:1px solid black;'}); },
function() { $(this).find("img:first").attr({src: $.resource("ImageZoom2nd_iconMagnifier"), style :'border:1px solid gray;'}); })
.click(fnAction);
break;
case "close":
default:
return $("<a href='#' title='"+$.resource('ClueTip_toolTipClose')+"'/>")
.append("<img src='"+$.resource("ImageZoom1st_iconCloseWindow")+"' />")
.hover(
function() { $(this).find("img:first").attr("src", $.resource("ImageZoom1st_iconCloseWindowHover")); },
function() { $(this).find("img:first").attr("src", $.resource("ImageZoom1st_iconCloseWindow")); })
.click(fnAction);
}// end switch case
}
///////////////////////////////
// HoverIntent START
/**
* @description hoverIntent is a plug-in that attempts to determine the user's intent
* see http://offene-naturfuehrer.de/wiki/MediaWiki:HoverIntent.js for docu, creators and license
* @augments $.fn
* @requires jQuery 1.1.2+
* @param {handler} f handler in
* @param {handler} g handler out
* @returns {@exp;@call;@call;mouseout}
*/
$.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
/**
* @description Cluetip Plugin (modified)
* MIT and GPL licenses
* http://plugins.learningjquery.com/cluetip/ and http://www.offene-naturfuehrer.de/wiki/MediaWiki:Cluetip.js for details
* @version 1.0.6 modified for Off.Naturführer.
* @requires jQuery
* @requires jqueryEscapeId()
* @requires createButton()
* @augments object $.fn
* @returns {undefined}
*/
(function($) {
$.cluetip = {version: '1.0.6-ON'};
var $cluetip, $cluetipInner, $cluetipOuter, $cluetipTitle, $cluetipArrows, $cluetipWait, $dropShadow, imgCount,
insertionElement = 'body';
$.fn.cluetip = function(js, options) {
if (typeof js == 'object') {
options = js;
js = null;
}
if (js == 'destroy') {
return this.removeData('thisInfo').unbind('.cluetip');
}
return this.each(function(index) {
var link=this, $this=$(this);
// support metadata plugin (v1.0 and 2.0)
var opts = $.extend(
true,
{},
$.fn.cluetip.defaults,
options || {},
$.metadata ? $this.metadata() : ( $.meta ? $this.data() : {} )
);
// start out with no contents (for ajax activation)
var cluetipContents = false;
var cluezIndex = +opts.cluezIndex;
$this.data('thisInfo', {title: link.title, zIndex: cluezIndex});
var isActive = false, closeOnDelay = 0;
// create the cluetip divs
if (!$('#cluetip').length) {
$(['<div id="cluetip">',
'<div id="cluetip-outer">',
'<h3 id="cluetip-title"></h3>',
'<div id="cluetip-inner"></div>',
'</div>',
'<div id="cluetip-extra"></div>',
'<div id="cluetip-arrows" class="cluetip-arrows"></div>',
'</div>'].join(''))
.appendTo(insertionElement).hide();
$cluetip = $('#cluetip').css({position: 'absolute'});
$cluetipOuter = $('#cluetip-outer').css({position: 'relative', zIndex: cluezIndex});
$cluetipInner = $('#cluetip-inner');
$cluetipTitle = $('#cluetip-title');
$cluetipArrows = $('#cluetip-arrows');
$cluetipWait = $('<div id="cluetip-waitimage"></div>')
.css({position: 'absolute'}).insertBefore($cluetip).hide();
}
var dropShadowSteps = (opts.dropShadow) ? +opts.dropShadowSteps : 0;
if (!$dropShadow) {
$dropShadow = $([]);
for (var i=0; i < dropShadowSteps; i++) {
$dropShadow = $dropShadow.add($('<div></div>').css({zIndex: cluezIndex-1, opacity:0.1, top: 1+i, left: 1+i}));
}
$dropShadow.css({position: 'absolute', backgroundColor: '#000'})
.prependTo($cluetip);
}
var tipAttribute = $this.attr(opts.attribute);
if (!tipAttribute && !opts.splitTitle && !js) {
return true;
}
// if hideLocal is set to true, on DOM ready hide the local content that will be displayed in the clueTip
if (opts.local && opts.localPrefix) {tipAttribute = opts.localPrefix + tipAttribute;}
if (opts.local && opts.hideLocal) { $(tipAttribute + ':first').hide(); }
var tOffset = parseInt(opts.topOffset, 10), lOffset = parseInt(opts.leftOffset, 10);
// vertical measurement variables
var tipHeight, wHeight,
sTop, linkTop, posY, tipY, mouseY, baseline,
defHeight = isNaN(parseInt(opts.height, 10)) ? 'auto' : (/\D/g).test(opts.height) ? opts.height : opts.height + 'px';
// horizontal measurement variables
var tipInnerWidth = parseInt(opts.width, 10) || 275,
tipWidth = tipInnerWidth + (parseInt($cluetip.css('paddingLeft'),10)||0) + (parseInt($cluetip.css('paddingRight'),10)||0) + dropShadowSteps,
linkWidth = this.offsetWidth,
linkLeft, posX, mouseX, winWidth;
// parse the title
var tipParts;
var tipTitle = (opts.attribute != 'title') ? $this.attr(opts.titleAttribute) : '';
if (opts.splitTitle) {
if (tipTitle === undefined) {tipTitle = '';}
tipParts = tipTitle.split(opts.splitTitle);
tipTitle = tipParts.shift();
}
if (opts.escapeTitle) {
tipTitle = tipTitle.replace(/&/g,'&').replace(/>/g,'>').replace(/</g,'<');
}
function returnFalse() { return false; }
//activate clueTip
var activate = function(event) {
var pY;
if (!opts.onActivate($this)) {
return false;
}
isActive = true;
$cluetip.removeClass().css({width: tipInnerWidth});
if (tipAttribute == $this.attr('href')) {
$this.css('cursor', opts.cursor);
}
if (opts.hoverClass) {
$this.addClass(opts.hoverClass);
}
linkTop = posY = $this.offset().top;
linkLeft = $this.offset().left;
mouseX = event.pageX;
mouseY = event.pageY;
if (link.tagName.toLowerCase() != 'area') {
sTop = $(document).scrollTop();
winWidth = $(window).width();
}
// position clueTip horizontally
if (opts.positionBy == 'fixed') {
posX = linkWidth + linkLeft + lOffset;
$cluetip.css({left: posX});
} else {
posX = (linkWidth > linkLeft && linkLeft > tipWidth) || linkLeft + linkWidth + tipWidth + lOffset > winWidth ? linkLeft - tipWidth - lOffset : linkWidth + linkLeft + lOffset;
if (link.tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse
if (mouseX + 20 + tipWidth > winWidth) {
$cluetip.addClass(' cluetip-jtip');
posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'),10) + parseInt($cluetipInner.css('marginRight'),10) : mouseX - (tipWidth/2);
} else {
posX = mouseX + lOffset;
}
}
pY = posX < 0 ? event.pageY + tOffset : event.pageY;
$cluetip.css({
left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0),
zIndex: $this.data('thisInfo').zIndex
});
$cluetipArrows.css({zIndex: $this.data('thisInfo').zIndex+1});
}
wHeight = $(window).height();
// load a string from cluetip method's first argument
if (js) {
if (typeof js == 'function') {
js = js.call(link);
}
$cluetipInner.html(js);
cluetipShow(pY);
}
// load the title attribute only (or user-selected attribute).
// clueTip title is string before 1st delimiter, subsequent delim place clueTip body text on separate lines
else if (tipParts) {
var tpl = tipParts.length;
$cluetipInner.html(tpl ? tipParts[0] : '');
if (tpl > 1) {
for (var i=1; i < tpl; i++){
$cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>');
}
}
cluetipShow(pY);
}
// load external file via ajax
else if (!opts.local && tipAttribute.indexOf('#') !== 0) {
if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) {
$cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />');
cluetipShow(pY);
} else if (cluetipContents && opts.ajaxCache) {
$cluetipInner.html(cluetipContents);
// highlight target having a <span id=".."> see also ajaxSettings success:
var idTarget = link.toString().split('#')[1];
if(idTarget!=undefined){
$cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert');
}
cluetipShow(pY);
} else {
var optionBeforeSend = opts.ajaxSettings.beforeSend,
optionError = opts.ajaxSettings.error,
optionSuccess = opts.ajaxSettings.success,
optionComplete = opts.ajaxSettings.complete;
var ajaxSettings = {
cache: false, // force requested page not to be cached by browser
url: tipAttribute,
beforeSend: function(xhr) {
if (optionBeforeSend) {optionBeforeSend.call(link, xhr, $cluetip, $cluetipInner);}
$cluetipOuter.children().empty();
$cluetipWait.css({top: mouseY+20, left: mouseX+20, zIndex: $this.data('thisInfo').zIndex-1}).show();
},
error: function(xhr, textStatus) {
if (isActive) {
if (optionError) {
optionError.call(link, xhr, textStatus, $cluetip, $cluetipInner);
} else {
$cluetipInner.html($.resource('ClueTip_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('ClueTip_toolTipNewWindow') +"' >" + $.resource('ClueTip_newWindow') + "</a>").show();
if (opts.sticky) { // Close text modified directly
var $closeLink = $('<div id="cluetip-close"/>').append(createButton("close", function() {cluetipClose();return false;}));
if(opts.closePosition == 'bottom') {
$closeLink.appendTo($cluetipInner);
} else if(opts.closePosition == 'title'){
$closeLink.prependTo($cluetipTitle);
} else {
$closeLink.prependTo($cluetipInner);
}
if (opts.mouseOutClose) {
$cluetip.bind('mouseleave.cluetip', function() {cluetipClose();});
} else {
$cluetip.unbind('mouseleave.cluetip');
}
}
// now that content is loaded, finish the positioning
var direction = '';
$cluetipOuter.css({zIndex: $this.data('thisInfo').zIndex, overflow: defHeight == 'auto' ? 'visible' : 'auto'});
tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(),$cluetip.height()) : parseInt(defHeight,10);
$cluetipInner.css({
overflow: defHeight == 'auto' ? 'visible' : 'auto',
height: tipHeight - 45 //minus title: should be set automatically but $cluetipTitle.height() and height variants always get 0 or 0px
});
tipY = posY;
baseline = sTop + wHeight;
if (opts.positionBy == 'fixed') {
tipY = posY - opts.dropShadowSteps + tOffset;
} else if ( (posX < mouseX && Math.max(posX, 0) + tipWidth > mouseX) || opts.positionBy == 'bottomTop') {
if (posY + tipHeight + tOffset > baseline && mouseY - sTop > tipHeight + tOffset) {
tipY = mouseY - tipHeight - tOffset;
direction = 'top';
} else {
tipY = mouseY + tOffset;
direction = 'bottom';
}
} else if ( posY + tipHeight + tOffset > baseline ) {
tipY = (tipHeight >= wHeight) ? sTop : baseline - tipHeight - tOffset;
} else if ($this.css('display') == 'block' || link.tagName.toLowerCase() == 'area' || opts.positionBy == "mouse") {
tipY = bpY - tOffset;
} else {
tipY = posY - opts.dropShadowSteps;
}
if (direction == '') {
direction = (posX < linkLeft) ? 'left' : 'right';
}
$cluetip.css({top: tipY + 'px'}).removeClass().addClass('clue-' + direction + '-jtip').addClass(' cluetip-jtip');
if (opts.arrows) { // set up arrow positioning to align with element
var bgY = (posY - tipY - opts.dropShadowSteps);
$cluetipArrows.css({top: (/(left|right)/.test(direction) && posX >=0 && bgY > 0) ? bgY + 'px' : /(left|right)/.test(direction) ? 0 : ''}).show();
} else {
$cluetipArrows.hide();
}
// (first hide, then) ***SHOW THE CLUETIP***
$dropShadow.hide();
$cluetip.hide()[opts.fx.open](opts.fx.openSpeed || 0);
if (opts.dropShadow) { $dropShadow.css({height: tipHeight, width: tipInnerWidth, zIndex: $this.data('thisInfo').zIndex-1}).show(); }
if ($.fn.bgiframe) { $cluetip.bgiframe(); }
// trigger the optional onShow function
opts.onShow.call(link, $cluetip, $cluetipInner);
};
// INACTIVATION
var inactivate = function(event) {
isActive = false;
$cluetipWait.hide();
if (!opts.sticky || (/click|toggle/).test(opts.activation) ) {
cluetipClose();
clearTimeout(closeOnDelay);
}
if (opts.hoverClass) {
$this.removeClass(opts.hoverClass);
}
};
// close cluetip and reset some things
var cluetipClose = function() {
$cluetipOuter
.parent().hide().removeClass();
opts.onHide.call(link, $cluetip, $cluetipInner);
$this.removeClass('cluetip-clicked');
if (tipTitle) {
$this.attr(opts.titleAttribute, tipTitle);
}
$this.css('cursor','');
if (opts.arrows) {
$cluetipArrows.css({top: ''});
}
};
$(document).bind('hideCluetip', function(e) {
cluetipClose();
});
// BIND EVENTS
// activate by click
if ( (/click|toggle/).test(opts.activation) ) {
$this.bind('click.cluetip', function(event) {
if ($cluetip.is(':hidden') || !$this.is('.cluetip-clicked')) {
activate(event);
$('.cluetip-clicked').removeClass('cluetip-clicked');
$this.addClass('cluetip-clicked');
} else {
inactivate(event);
}
this.blur();
return false;
});
// activate by focus; inactivate by blur
} else if (opts.activation == 'focus') {
$this.bind('focus.cluetip', function(event) {
activate(event);
});
$this.bind('blur.cluetip', function(event) {
inactivate(event);
});
// activate by hover
} else {
// clicking is returned false if clickThrough option is set to false
$this[opts.clickThrough ? 'unbind' : 'bind']('click', returnFalse);
$this.hoverIntent({ // hoverintent now REQUIRED!
sensitivity: opts.hoverIntent.sensitivity,
interval: opts.hoverIntent.interval,
timeout: opts.hoverIntent.timeout,
over: function(event) {activate(event);},
out: function(event) {inactivate(event); $this.unbind('mousemove.cluetip');}
});
$this.bind('mouseover.cluetip', function(event) {
$this.attr('title','');
}).bind('mouseleave.cluetip', function(event) {
$this.attr('title', $this.data('thisInfo').title);
});
}
});
};
/**
* @description default settings of $.fn.cluetip
* 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"
*
* @augments $.fn.cluetip
* @requires jQuery
*/
$.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
/////////////////////
/**
* @requires cluetip plugin
* @returns {undefined}
*/
function initCluetips() {
var jPopup = $('span.cluetip a');
if (jPopup.length) { // only if at least one popup exists
var ctHover = {
arrows: true,
height: 275,
width: 400,
fx: {open:'fadeIn', openSpeed:'3'}, // open can be 'show' or 'slideDown' or 'fadeIn'
titleAttribute: 'suppress-title-display',
positionBy: 'bottomTop',
sticky: true,
mouseOutClose: true,
closePosition: 'title'
},
ctClick = {
activation: 'click',
height: 275,
width: 400,
fx: {open:'fadeIn', openSpeed:'3'}, // open can be 'show' or 'slideDown' or 'fadeIn'
titleAttribute: 'suppress-title-display',
sticky: true,
closePosition: 'title',
onShow: function () {// make title draggable
if (typeof mw != "undefined"){
mw.loader.using("jquery.ui.draggable", function () {
$( "#cluetip" ).draggable({handle: "#cluetip-title"});
$("#cluetip-title").css({cursor:"move"});
});
}
}
};
// Design: Without js, a normal link should exist. With js, normal click should only call cluetip, which in turn offers opening in new window.
jPopup.each(function() {
var jLink=$(this), jSpan=jLink.parent();
jLink.attr("resource", jSpan.attr("resource"));
if (jSpan.hasClass("cluetip-hover")) {
jLink.cluetip(ctHover);
} else if (jSpan.hasClass("cluetip-click")) {
jLink.cluetip(ctClick);
}
});
} // END if popup found
}
////////////////////////////////////
// Modal Layer base functionality //
////////////////////////////////////
// for program flow see [[MediaWiki:ModalLayer and image zoom docu]]
/**
* @description: Hide (= close) modal layer (Note: cyclical dependency with next method unavoidable)
* @requires modalLayer_KeyDown()
* @returns {Boolean}
*/
function modalLayer_Hide() {
$(document).unbind("keydown", modalLayer_KeyDown);
$("#modal-fg").fadeOut(function() {
$("#modal-bg").hide();
$(this).empty().hide();
});
return false;
}
/**
* @description: Close (hide) modal layer on escape, backspace and arrow left key
* @requires modalLayer_Hide()
* @param {event} e the keyboard event object
* @returns {undefined}
*/
function modalLayer_KeyDown(e) {
if ((e.keyCode === 8) || (e.keyCode === 27) || (e.keyCode === 37)) { modalLayer_Hide(); }
}
/**
* @description: Create modal layer and execute fnRender
* @param {function} fnRender custom function to display something
* @param {object} paramsObj generic parameters passed to "fnRender"
* @returns {undefined}
*/
function modalLayer_Create(fnRender, paramsObj) {
// find existing or create background & layer
var modalBG = $("#modal-bg"),
modalFG = $("#modal-fg");
if (modalBG.length === 0) { // first time init
if (typeof(document.body.style.maxHeight) === "undefined") { // if IE 6
$("body","html").css({height: "100%", width: "100%"});
}
// add styles (IE6 hack not possible as element style!)
$("head").append("<style type=\"text/css\">#modal-bg {position:fixed; z-index:100; top:0px;left:0px; height:100%;width:100%; background:black; opacity:0.8; filter:alpha(opacity=80); display:none;}\n" +
"#modal-fg {position:fixed; z-index:101; top:50%;left:50%; padding:3px; border:2px solid #E0E0E0; background-color:white; display:none;}\n" + // IE6 hack: add (very!) slow IE-CSS-expression only for IE < 7
($.browser.msie && $.browser.version < 7 ? "* html #modal-bg {position: absolute; height:expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');}\n* html #modal-fg {position: absolute; margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');}\n" : "") +
"#modal-fg img {display:block;}\n</style>");
modalBG = $("<div id='modal-bg'/>"); // #### in old code version: HEIGHT was changed to: $(document).height()-- necessary for some browsers??? Trying without!
modalFG = $("<div id='modal-fg'/>");
modalBG.click(function() { modalLayer_Hide(); });
$("body").append(modalBG).append(modalFG);
} // END first time init
$(document).keydown(modalLayer_KeyDown);
// generic view port functionality: close icon and background click
modalFG
.append( $('<div style="position:absolute;right:4px;"/>')
.append(createButton("close", modalLayer_Hide)) );
modalBG
.append('<img src="' + $.resource('ImageZoom2nd_iconLoader') + '" id="loaderIcon" style="position:absolute;top:50%;left:50%;"/>')
.show();
fnRender(paramsObj); // Execute custom logic, example: modalLayer_ZoomImage
}
/**
* @description Show modal layer based on a image
*
* @requires createButton()
* @requires modalLayer_InitShowIviewerZoomImage()
* @requires modalLayer_Hide()
* @requires $.resource()
*
* @param {object} newImg new image object
* @param {object} oriImg original image object
* @returns {undefined}
*/
function modalLayer_ShowImage(newImg, oriImg) {
var title = oriImg.title,
imgWidth = newImg.width,
imgHeight = newImg.height,
modalBG = $("#modal-bg"),
modalFG = $("#modal-fg");
modalBG.find("#loaderIcon").remove();
if (imgWidth===0) { // Only IE, only if newImg = oriImg.clone(): cloned image in IE has no width
imgWidth = oriImg.width;
imgHeight = oriImg.height;
}
// extend height of modal layer for no-zoom msg
var zoomIsPossible = (imgWidth != oriImg.width),
layerHeight = imgHeight + 105 + ((!zoomIsPossible) ? 60 : 0),
layerWidth = Math.max(300, imgWidth + 70); // reserve minimal text width
// delete alt text & add click function to hide modal
$(newImg).removeAttr("alt")
.attr("title", title.replace($.resource("ImageZoom1st_toolTipImageZooming"),$.resource("ClueTip_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("ImageZoom1st_toolTipImageZooming")+")","")+"<br />")
// URL to metadata page from "a[href]" around img
.append($.linkBuilder("ImageZoom1st_imageMetadataLink", "", $(oriImg).closest("a").attr("href"), "target='_blank'"))
.append( !zoomIsPossible ? "<br/><br/><span style='color:red;'>" + $.resource("ImageZoom1st_zoomNotPossible") + "</span>" : "")
);
// take away IE6 modifications
if ( !($.browser.msie && $.browser.version < 7)) {
modalFG.css({"margin-top": -((layerHeight + 8) / 2)}); // 8 from other margin-top
}
modalFG.fadeIn(50);
}
/**
* @description: load script and functionality on demand for jQuery plugin iviewer
* @requires MediaWiki:Jquery.zoomImage.js with function modalLayer_ShowIviewerZoomImage()
* @requires mw.config.get
* @requires modalLayer_ShowIviewerZoomImage()
* @returns {Boolean}
*/
function modalLayer_InitShowIviewerZoomImage() {
if (typeof modalLayer_ShowIviewerZoomImage !== "function") {
$.getScript(mw.config.get('wgServer') + mw.config.get('wgScript') + "?title=MediaWiki:Jquery.zoomImage.js&action=raw&ctype=text/javascript",
function(){modalLayer_ShowIviewerZoomImage();});
} else {
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
* @param {object} paramsObj object containing "caller" = ref to a link including an img
* @requires $.random()
* @requires modalLayer_ShowImage()
* @returns {undefined}
*/
function modalLayer_ZoomImage(paramsObj) {
var oriImg = $(paramsObj.caller).find("img").get(0), // caller is typically a[href]
urlParts = oriImg.src.split("/");
if ((oriImg.src.search(/\/thumb\//) === -1) ||
(urlParts[urlParts.length - 1].search(/px-/) === -1)) {
// no larger picture possible, use existing
modalLayer_ShowImage($(oriImg).clone().get(0), oriImg);
} else { // images with "/thumb/" in path can be enlarged using URL-based resize
var stdThumbWidths = [1600,1400,1280,1024,900,800,700,640,600,550,480,400,350,320,300,250,200,180,150,120,100,80],
maxHeight = $(window).height() - 105, // 70 for additional text; 35 for space at top and bottom
maxWidth = $(window).width() - 50; // 50 for space left & right
// smallest possible of upscaling factor for height, width, multiply back to get max possible width
var maxScaledWidth = oriImg.width * Math.min(maxHeight/oriImg.height, maxWidth/oriImg.width),
maxScaledHeight = oriImg.height * maxScaledWidth / oriImg.width;
// reduce to next smaller standard thumb width (mediawiki preview settings plus additions)
for (var i = 0; i < 22; i++) {
if (stdThumbWidths[i] < maxScaledWidth) {
maxScaledWidth = stdThumbWidths[i];
maxScaledHeight = oriImg.height * maxScaledWidth / oriImg.width;
break;
}
}
urlParts[urlParts.length - 1] = maxScaledWidth + "px-" + urlParts[urlParts.length - 2];
var newImg = new Image(maxScaledWidth, maxScaledHeight);
// Load image. load/error occur asynchronously, need independent calls
// appending "random()" seems necessary for IE6-8, else "zoom image, close, zoom again" fails. REASON?
$(newImg)
.attr("src", urlParts.join("/")+"&rnd="+$.random(0,10000))
.load(function() { // load succeeded: create modal layer after loading image, else values (width, etc.) are 0
modalLayer_ShowImage(newImg, oriImg);
})
.error(function() { // Error loading thumb, main reason: thumbs must be smaller than ori size.
// Currently assuming this reason, loading full original image; BETTER: test using API:
// 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
*
* @requires modalLayer_Create()
* @requires modalLayer_ZoomImage()
* @param {caller} caller reference to a link around image, function to call
* @returns {False}
*/
function zoomImage(caller) {
modalLayer_Create(modalLayer_ZoomImage, {caller: caller});
return false; // cancel default event
}
/**
* @description: Add a modal zoom functionality to all images linking to own metadata
* the CSS
* class=no-image-popup or class=no-popup-image OR
* class=no-image-zoom or class=no-zoom-image prevents an image from being zoomable, e.g.
* [[File:Example.jpg|caption text|thumb|class=no-popup-image]]
* @requires $.resource()
* @requires zoomImage()
* @returns {undefined}
*/
function initImageZooming() {
$("a[href].image img:not(.no-image-popup,.no-popup-image,.no-image-zoom,.no-zoom-image)").each(function() {
var jParent = $(this).parent(),
metaURL = jParent.attr("href"),
urlParts = this.src.split("/"),
imgFileName = (this.src.search(/\/thumb\//) != -1) ? urlParts[urlParts.length - 2] : urlParts[urlParts.length - 1];
// Is file name also in metadata page link? Else abort (e.g. for |link=parameter| wiki-images)
// Problem: a.href and img.scr inconsistently! use encoded or non-encoded versions of e.g. () or "," -> unescape
if (unescape(metaURL).indexOf(unescape(imgFileName)) == -1) { return; }
// pass along the image
jParent.click(function() { return zoomImage(this); });
// set or change title, set alt to title
var newTitle = this.alt + ((this.alt.length === 0) ? "" : " ") + "(" + $.resource("ImageZoom1st_toolTipImageZooming") + ")";
$(this).attr({title:newTitle, alt:newTitle});
});
}
// END Modal Layer/Img Zoom
////////////////////////////
// jKey Source
importScript("MediaWiki:JKey.js");
// WikiEditor Helper tools
if (mw.config.get('wgAction') === "edit"
|| mw.config.get('wgAction') === "submit" ) {
importScript("MediaWiki:JKeyWikiEditorHelp.js"); // load help for the wikiEditor
importScript("MediaWiki:WikiEditor-insert-Zitat.js");// Wizard to insert template:Zitat
importScript("MediaWiki:WikiEditor-insert-file-with-preview.js");// Wizard to insert file showing a preview
}
/**
* @description fix WikiEditor Helper tools for Extension:LiquidThreads
* @returns {null}
*/
function initFixEditorIcons4LiquidThreads () {
if($(".lqt-talkpage").length) {
mw.loader.using("ext.liquidThreads", function () {
// Extension LiquidThreads has a bug and does not load all i18n messages so do it manually
mw.loader.using('jquery.wikiEditor.dialogs.config', function () {
// mw.notify('jquery.wikiEditor.dialogs.config loaded');
$(".lqt-talkpage-header .lqt_start_discussion a, li.lqt-command.lqt-command-reply a").on("click", function () {
setTimeout(function() {
// console.log("MediaWiki:Common.js: import tools Zitat, file preview ... ");
importScript("MediaWiki:WikiEditor-insert-Zitat.js");
importScript("MediaWiki:WikiEditor-insert-file-with-preview.js");
setTimeout(function() {
// trigger wikiEditor-toolbar-doneInitialSections assigned in MediaWiki:WikiEditor-insert-Zitat.js, MediaWiki:WikiEditor-insert-file-with-preview.js
$('#wpTextbox1').trigger('wikiEditor-toolbar-doneInitialSections');
// console.log("MediaWiki:Common.js. trigger('wikiEditor-toolbar-doneInitialSections') done (4000ms delay) ... ");
}, 2000);
}, 2000);
});
});// jquery.wikiEditor.dialogs.config
});// ext.liquidThreads
}
return null;
}// initFixEditorIcons4LiquidThreads()
/**
* @description click-text modifications for mw-customcollapsible triggering from outside of mw-collapsible
*
* @returns {undefined}
*/
importScript("MediaWiki:Mw-customcollapsible.js");
/**
* @description create jQuery UI Tabs for Semantic forms
* on CSS class="use-jquery-ui-tabs"
*
* @requires https://www.mediawiki.org/wiki/Extension:Semantic_Forms
* @requires jQuery-UI mw.loader.using( 'jquery.ui.tabs', function () {});
* @returns {undefined}
*/
function initUiTabsInForms() {
console.log("initUiTabsInForms");
var use_jquery_ui_tabs= $(".use-jquery-ui-tabs");
var use_jquery_ui_tabs_vertical= $(".use-jquery-ui-tabs-vertical");
if (use_jquery_ui_tabs.length || use_jquery_ui_tabs_vertical.length) {
mw.loader.using( 'jquery.ui.tabs', function () {
var use_jquery_ui_tabs= $(".use-jquery-ui-tabs");// Google Chrome Fix
var use_jquery_ui_tabs_vertical= $(".use-jquery-ui-tabs-vertical");// Google Chrome Fix
console.log("initUiTabsInForms mw.loader.using");
if ($(".use-jquery-ui-tabs").length) {
$(".use-jquery-ui-tabs").tabs({event: "mouseover"});
console.log("initUiTabsInForms tabs");
}
if (use_jquery_ui_tabs_vertical.length) {
use_jquery_ui_tabs_vertical.tabs({event: "mouseover"}).addClass( "ui-tabs-vertical ui-helper-clearfix" );
use_jquery_ui_tabs_vertical.find( "li" ).removeClass( "ui-corner-top" ).addClass( "ui-corner-left" );
console.log("initUiTabsInForms tabs_vertical");
}
});
}
}
/**
* @description: formats background of form elements created by template:Hidden
* @requires https://www.mediawiki.org/wiki/Extension:Semantic_Forms
* @requires CSS class indicateHiddenInputs or other fields
* @requires CSS class indicateFilledFormElements
* @returns {undefined}
*/
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()
/**
* @description init moveable table of contents
* offers floating the table of contents to the left or right side (will make part of the page invisible!)
*
* @requires $.resource()
* @requires moveTOC()
* @returns {undefined}
*/
function initmoveTOC() {
$('#togglelink').after(' | <span style="cursor:pointer;color:blue;" title="'+
$.resource('MoveTOC_toolTipFloatleft') +'" onclick="moveTOC(\'0px\')" >◄</span> <span style="cursor:pointer;color:blue;" title="'+
$.resource('MoveTOC_toolTipFloatright') +'" onclick="moveTOC(\'right\')" >►</span> <span id="restorestatic" style="display:none;">| <span style="cursor:pointer;" title="'+
$.resource('MoveTOC_toolTipUnfloat') +'" onclick="moveTOC(\'restore\')" >×</span></span> ');
}// end initmoveTOC
/**
* @description init moveable table of contents
* @requires initmoveTOC()
* @param {string} position may be string "right", or "restore", everthing else is interpreted as "left"
* @returns {undefined}
*/
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('MoveTOC_toolTipNavigatePagetop') +'">↑ </a>');
}
switch (position) {
case "right":
offsetX = $("#content").width() - 140;
offsetY = - 55;
break;
case "restore":
TOC.unwrap();
$('#toTop').remove();
$('#restorestatic').hide('slow');
$("#toctitle h2").show('slow');
break;
default:
offsetY = -110;
offsetX = 0 - $("#mw-panel").width() - 14 ; // padding#content
break;
}// end switch
if (position != "restore") {
$("#toctitle h2").hide('slow');
$('#restorestatic').show();
$('#moveTOC')
.css({"position":"absolute"})
.animate({'max-width':"13em"})
.animate({
'left': $(window).scrollLeft()+ offsetX +"px",
'top' : $(window).scrollTop() + offsetY +"px"
});
// emulate position fixed see CSS .navigation-left .navigation-right
$(window).scroll(function(){
$('#moveTOC')
.animate({
'left': $(window).scrollLeft()+ offsetX +"px",
'top' : $(window).scrollTop() + offsetY +"px"
},{queue: false, duration: 400} // queue: don't wait
);
});
} // end positioning unless restore
}// end function moveTOC()
///////////////////////////////////////////////////////////////
// specific to http://offene-naturfuehrer.de
///////////////////////////////////////////////////////////////
/**
* @description: collapsible parts: div and tr → Template:Hidden
* main difference: initCollapsebox() uses a switcher defined by the Wikitemplate
* and is not using a javascript resource title.
* * the only advanced feature it has: collpsible table rows
* * otherwise use http://www.mediawiki.org/wiki/RL/DM#jQuery.makeCollapsible
* @requires $.resources()
* @requires $.jl18n.en.CollapseBox_toolTipExpand, $.jl18n.en.CollapseBox_toolTipCollapse
* @returns {Boolean}
*/
function initCollapsebox() {
/* is nested in: div.collapsebox
└ div.switcher
└ div.collapsecontent */
var hasSwitcher = jQuery("div.switcher .show, div.switcher .hide");
if(hasSwitcher.length){
jQuery.each(hasSwitcher, function(index){// add tooltip
hasSwitcher[index].title = hasSwitcher[index].className === "show"? jQuery.resource("CollapseBox_toolTipExpand") : jQuery.resource("CollapseBox_toolTipCollapse");
});
jQuery("div.collapsebox div.switcher").on('click',
function() {
jQuery(this).nextAll("div.collapsecontent:first").slideToggle(250);
/* $(this).toggle() does not work in live as toggle is a bind()
therefore toggle must be bound to a different DOM element */
jQuery(this).find(".show, .hide").toggle();
});
/* is in a table: tr.collapsebox
└ div.switcher
tr.collapsecontent */
jQuery("tr.collapsebox div.switcher").on('click',
function() {
jQuery(this).closest("tr.collapsebox").nextAll("tr.collapsecontent:first").toggle();
jQuery(this).find(".show, .hide").toggle();
});
// TODO is a better generic check possible?
if (mw.config.get('wgPageName') === "Spezial:Suche") {
if (!$(".mw-search-results").length) {
// open box with additional search possibilities
$(".switcher .show").trigger('click');
}
}
return true;
}
return false;
}// END initCollapsebox()
///////////////////////
// specific to http://wiki.bayernflora.de
// Page-specific scripts:
switch (mw.config.get( 'wgPageName' )) { // Minimize the pages on which the code will be loaded
// vielleicht später auch woanders, d.h. als vorgewählte Suche auf Taxaseiten beispielsweise?
case "Sandkasten":
case "Spezial:Suche":
case "Steckbriefe":
case "Verbreitungskarten":
case "Dokumente":
importScript("MediaWiki:SearchTools.js");
break;
case "Intern:Bastelseite":
importScript("MediaWiki:QuizFelixRiester.js");
break;
// case "Hilfe:Nummerierungen_im_jKey_abändern_(Lead_Nummern)":
// importScript("MediaWiki:JKeyRenumberingTool.js");
// break;
// case "Hilfe:Konvertierung_geschachtelt-eingerückter_Schlüssel_in_das_ON-Format":
// case "Testseite":
// importScript("MediaWiki:JKeyTextToLeadTemplateTool.js");
// break;
}
/*
mediaWiki.loader.state({"site":"ready"}); is appended and takes care of
the document ready event. If $(document).ready() is used, for forms
$ is then unknown. Scope or closure problem? (AP 2011-02-20)
*/
// When document is completely loaded
$(document).ready(function() {
initImageZooming();
initTargetHighlighting(); // page-internal jumps
initmoveTOC(); // TOC CSS position fixed or static
// specific to http://offene-naturfuehrer.de
importScript("MediaWiki:ModifyTalkPageRedlink2AddNewSection.js");
initFixEditorIcons4LiquidThreads();
}); // end $(document).ready()
initCluetips();
// page specific
if(mw.config.get( 'wgAction' )==="formedit" || mw.config.get( 'wgCanonicalSpecialPageName' )==="FormEdit"){
// initConfirmDeleteSubform();
initMarkAllFilledFormElements();
}
// specific to http://offene-naturfuehrer.de
initCollapsebox(); //collapsible parts
/**
* @description modify MediaWiki:Sidebar
* @param {string} action one of add, remove
* @param {string} section name of the section in the sidebar
* @param {string} name name of the link
* @param {string} link URL of the link
* @returns {unresolved}
*/
function ModifySidebar( action, section, name, link ) {
try {
switch ( section ) {
case 'languages':
var target = 'p-lang';
break;
case 'toolbox':
var target = 'p-tb';
break;
case 'navigation':
var target = 'p-navigation';
break;
default:
var target = 'p-' + section;
break;
}
if ( action == 'add' ) {
var node = document.getElementById( target )
.getElementsByTagName( 'div' )[0]
.getElementsByTagName( 'ul' )[0];
var aNode = document.createElement( 'a' );
var liNode = document.createElement( 'li' );
aNode.appendChild( document.createTextNode( name ) );
aNode.setAttribute( 'href', link );
liNode.appendChild( aNode );
liNode.className = 'plainlinks';
node.appendChild( liNode );
}
if ( action == 'remove' ) {
var list = document.getElementById( target )
.getElementsByTagName( 'div' )[0]
.getElementsByTagName( 'ul' )[0];
var listelements = list.getElementsByTagName( 'li' );
for ( var i = 0; i < listelements.length; i++ ) {
if (
listelements[i].getElementsByTagName( 'a' )[0].innerHTML == name ||
listelements[i].getElementsByTagName( 'a' )[0].href == link
)
{
list.removeChild( listelements[i] );
}
}
}
} catch( e ) {
// let's just ignore what's happened
return;
}
}
/**
* @description apply modification of the sidebar
* @requires ModifySidebar()
* @returns {undefined}
*/
function CustomizeModificationsOfSidebar() {
// adds [[Special:CategoryTree]] to toolbox
ModifySidebar( 'add', 'toolbox', 'Kategoriebaum', mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace(/\$1/, "Special:CategoryTree") );
}
/**
* @description Allows add-on scripts to add onload functions
* @requires http://wiki.bayernflora.de/w/skins/common/wikibits.js
*/
addOnloadHook( CustomizeModificationsOfSidebar );
// </syntaxhighlight>
//Slideshow
$.fn.slideshow = ( function() {
return this.each( function() {
var $ss = $(this), $sl = $ss.children( '.slide' ), $actions;
if ( $sl.length < 2 ) {
return;
}
$sl.slice(1).hide();
$actions = $('<div class="slide-actions"><span class="slide-prev"></span><span class="slide-next"></span></div>');
$ss.data( 'slides', { 'at': 0, 'total': $sl.length }).append( $actions ).click( function(e) {
var $where = $( e.target ), $ss, $sl, data;
if ( $where.is( '.slide-prev' ) ) {
e.stopPropagation();
$ss = $(this); $sl = $ss.children( '.slide' ); data = $ss.data( 'slides' );
if ( data.at > 0 ) {
--data.at;
$sl.eq( data.at + 1).fadeOut(1000).end().eq( data.at ).delay(1000).fadeIn(1000);
$ss.data( 'slides', data );
}
} else if ( $where.is( '.slide-next' ) ) {
e.stopPropagation();
$ss = $(this); $sl = $ss.children( '.slide' ); data = $ss.data( 'slides' );
if ( data.at < data.total - 1 ) {
++data.at;
$sl.eq( data.at - 1).fadeOut(1000).end().eq( data.at ).delay(1000).fadeIn(1000);
$ss.data( 'slides', data );
}
}
});
});
});
$(document).ready( function() { $( '.slides' ).slideshow(); } );