Difference between revisions of "MediaWiki:Common.js"
From Biowikifarm Metawiki
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 | ||
− | // | + | /* |
+ | * 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: |
− | if ( | + | // 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 ( | + | if (wgAction === "edit" || wgAction === "submit" || wgCanonicalSpecialPageName === "Upload") { |
− | /** | + | if (typeof EditTools === 'undefined') { |
− | + | importScript('MediaWiki:Edittools.js'); | |
− | + | } | |
− | + | if (typeof $.wikiEditor === 'undefined') { | |
− | var | + | importScript("MediaWiki:Common.js/edit.js"); // TODO remove or adjust? AP 2011-08-25 |
− | var | + | } |
+ | } | ||
+ | |||
+ | $.jI18n = {/* resource string dictionary | ||
+ | Note: Commons uses collapse/expand ▲/▼, but this looks better in strict box | ||
+ | layouts that in the free-wrapping key statements | ||
+ | |||
+ | Nomenclature proposal: if an extra plugin is used, strings can be designated as | ||
+ | “plugin_toolTipSomthing” otherwise just “toolTipSomthing” (global string). So it’s more clear if | ||
+ | somebody wants to deactivate a plugin and remove strings from the resource dictionary. | ||
+ | */ | ||
+ | en: { | ||
+ | captionCollapse : " (show less) ", | ||
+ | captionExpand : " (more...) ", | ||
+ | expandAll : "Show all extras", | ||
+ | iconCloseWindowHover : "http://upload.wikimedia.org/wikipedia/commons/d/d0/Close_icon_hover.jpg", | ||
+ | iconCloseWindow : "http://upload.wikimedia.org/wikipedia/commons/8/87/Close_icon_default.jpg", | ||
+ | iconOverview : "http://upload.wikimedia.org/wikipedia/commons/thumb/2/22/View-pause_Gion_simple.svg/20px-View-pause_Gion_simple.svg.png", | ||
+ | iconResume : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png", | ||
+ | iconStart1st : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png", | ||
+ | iconStartNew : "http://upload.wikimedia.org/wikipedia/commons/thumb/0/05/View-refresh_Gion_simple.svg/20px-View-refresh_Gion_simple.svg.png", | ||
+ | imageMetadataLink : "(Information about Creator, License and Copyright)", | ||
+ | newWindow : "(New Window …)", | ||
+ | toolTipClose : "Click to close", | ||
+ | toolTipCollapse : "(click to hide information below)", | ||
+ | toolTipExpand : "(click to show more information below)", | ||
+ | toolTipImageZooming : "Images can be enlarged by clicking on it", | ||
+ | toolTipNavigatePagetop : "Top of page", | ||
+ | toolTipNewWindow : "(click to open content in a new window or tab)", | ||
+ | toolTipFloatleft : "floating on the left side", | ||
+ | toolTipFloatright: "floating on the right side", | ||
+ | toolTipUnfloat: "back to default position", | ||
+ | toolTipNoContentLoadable:"<i>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 : " (weniger anzeigen) ", | ||
+ | captionExpand : " (mehr...) ", | ||
+ | expandAll : "Alle Zusatzinformationen zeigen", | ||
+ | imageMetadataLink : "(Informationen zu Autor, Lizenz und Copyright)", | ||
+ | newWindow : "(Neues Fenster …)", | ||
+ | toolTipClose : "Zum Schließen klicken", | ||
+ | toolTipCollapse : "(klicken um Zusatzinformationen zu verbergen)", | ||
+ | toolTipExpand : "(klicken um Zusatzinformationen anzuzeigen)", | ||
+ | toolTipHeadingLink: "Klicken um (permanenten) Link dieser Überschrift anzuzeigen",// MediaWiki:Gadget-HeadingLink | ||
+ | toolTipHeadingLinkHelp: "(1) Link zu dieser Überschrift oder (2) Link mit Versionsnummer:",// MediaWiki:Gadget-HeadingLink | ||
+ | toolTipImageZooming : "Bilder können durch Anklicken vergrößert betrachtet werden", | ||
+ | toolTipNavigatePagetop : "Zum Seitenanfang", | ||
+ | toolTipNewWindow : "(klicken um Inhalt in neuem Fenster oder Reiter zu öffnen)", | ||
+ | toolTipFloatleft : "Links schwebend", | ||
+ | toolTipFloatright: "Rechts schwebend", | ||
+ | toolTipUnfloat: "Zurück zur Normalposition", | ||
+ | toolTipNoContentLoadable:"<i>Leider konnte der Inhalt nicht geladen werden.</i>", | ||
+ | zoomNotPossible : "(Dieses Bild kann nicht weiter vergrößert werden)", | ||
+ | // see MediaWiki:zoomImage.js | ||
+ | ZoomImage_toolTipLoad : "(klicken um Originalbild nachzuladen; bei großen Bildern kann dies u. U. langsam sein)", | ||
+ | ZoomImage_textZoomOrig: "Vergrößerungsfunktion" | ||
+ | }, | ||
+ | it: { | ||
+ | captionCollapse : " (mostra di meno) ", | ||
+ | captionExpand : " (più...) ", | ||
+ | expandAll : "Mostra tutti informazione", //REVISE | ||
+ | imageMetadataLink : "(Informazione sull'Autore, Licenza e Copyright)", | ||
+ | toolTipClose : "Clicca per chiudere", | ||
+ | toolTipImageZooming : "Le immagini possono essere ingrandite cliccandoci sopra", | ||
+ | zoomNotPossible : "(Al momento non è possibilie ingrandire questa immagine)" // TODO translation see en version | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | |||
+ | /* | ||
+ | * Description: Get resource string (text, image URLs) for a given language, based on a string-key | ||
+ | * If no resource is defined in a given language for a resource key, the resource for "en" will be returned, if this is missing as well an error message. | ||
+ | * resourceKey: key for the resource (string) | ||
+ | */ | ||
+ | $.resource = function (resourceKey) { | ||
+ | var lang = wgUserLanguage.split("-")[0]; // language: "pt-BR", "de-formal", etc. | ||
+ | return ($.jI18n[lang] && $.jI18n[lang][resourceKey] ? | ||
+ | $.jI18n[lang][resourceKey] : | ||
+ | ($.jI18n.en[resourceKey]) ? $.jI18n.en[resourceKey] : "MISSING RESOURCE: no $.jI18n.en." + resourceKey + " defined."); | ||
+ | }; | ||
+ | |||
+ | /* | ||
+ | * Descriptions: Create html string for link with image and/or text content | ||
+ | * attributes: string of combined other attributes of link element; must use ' as inner quotes, and \" inside event functions | ||
+ | * imgResourceKey, txtResourceKey: resource keys; 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> " : "") | ||
+ | + $.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 + non-words (\W) */ | ||
+ | // OLD: shallExpand = (jLink.text().replace(/( |\W)/g,'') == $.resource("captionExpand").replace(/( |\W)/g,'')); | ||
+ | // *** TRYING: | ||
+ | shallExpand = (jLink.text().replace(/(\W)/g,'') == $.resource("captionExpand").replace(/(\W)/g,'')); | ||
} | } | ||
− | + | jLink | |
− | + | .attr("title", $.resource( shallExpand ? "toolTipCollapse" : "toolTipExpand" )) | |
− | + | .html($.resource( shallExpand ? "captionCollapse" : "captionExpand" )); | |
− | + | jTable.find("tr:not(tr:first)").toggle(shallExpand); | |
− | + | // show/hide all in set, tr of nested tables are included | |
− | + | } | |
− | + | return false; | |
− | + | } | |
− | + | ||
− | + | ||
− | + | ||
− | // } | + | |
− | } | + | |
+ | 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,'&').replace(/>/g,'>').replace(/</g,'<'); | ||
+ | } | ||
+ | |||
+ | function returnFalse() { return false; } | ||
+ | |||
+ | //activate clueTip | ||
+ | var activate = function(event) { | ||
+ | var pY; | ||
+ | if (!opts.onActivate($this)) { | ||
+ | return false; | ||
+ | } | ||
+ | isActive = true; | ||
+ | $cluetip.removeClass().css({width: tipInnerWidth}); | ||
+ | if (tipAttribute == $this.attr('href')) { | ||
+ | $this.css('cursor', opts.cursor); | ||
+ | } | ||
+ | if (opts.hoverClass) { | ||
+ | $this.addClass(opts.hoverClass); | ||
+ | } | ||
+ | linkTop = posY = $this.offset().top; | ||
+ | linkLeft = $this.offset().left; | ||
+ | mouseX = event.pageX; | ||
+ | mouseY = event.pageY; | ||
+ | if (link.tagName.toLowerCase() != 'area') { | ||
+ | sTop = $(document).scrollTop(); | ||
+ | winWidth = $(window).width(); | ||
+ | } | ||
+ | // position clueTip horizontally | ||
+ | if (opts.positionBy == 'fixed') { | ||
+ | posX = linkWidth + linkLeft + lOffset; | ||
+ | $cluetip.css({left: posX}); | ||
+ | } else { | ||
+ | posX = (linkWidth > linkLeft && linkLeft > tipWidth) || linkLeft + linkWidth + tipWidth + lOffset > winWidth ? linkLeft - tipWidth - lOffset : linkWidth + linkLeft + lOffset; | ||
+ | if (link.tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse | ||
+ | if (mouseX + 20 + tipWidth > winWidth) { | ||
+ | $cluetip.addClass(' cluetip-jtip'); | ||
+ | posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'),10) + parseInt($cluetipInner.css('marginRight'),10) : mouseX - (tipWidth/2); | ||
+ | } else { | ||
+ | posX = mouseX + lOffset; | ||
+ | } | ||
+ | } | ||
+ | pY = posX < 0 ? event.pageY + tOffset : event.pageY; | ||
+ | $cluetip.css({ | ||
+ | left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0), | ||
+ | zIndex: $this.data('thisInfo').zIndex | ||
+ | }); | ||
+ | $cluetipArrows.css({zIndex: $this.data('thisInfo').zIndex+1}); | ||
+ | } | ||
+ | wHeight = $(window).height(); | ||
+ | |||
+ | // load a string from cluetip method's first argument | ||
+ | if (js) { | ||
+ | if (typeof js == 'function') { | ||
+ | js = js.call(link); | ||
+ | } | ||
+ | $cluetipInner.html(js); | ||
+ | cluetipShow(pY); | ||
+ | } | ||
+ | |||
+ | // load the title attribute only (or user-selected attribute). | ||
+ | // clueTip title is string before 1st delimiter, subsequent delim place clueTip body text on separate lines | ||
+ | else if (tipParts) { | ||
+ | var tpl = tipParts.length; | ||
+ | $cluetipInner.html(tpl ? tipParts[0] : ''); | ||
+ | if (tpl > 1) { | ||
+ | for (var i=1; i < tpl; i++){ | ||
+ | $cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>'); | ||
+ | } | ||
+ | } | ||
+ | cluetipShow(pY); | ||
+ | } | ||
+ | // load external file via ajax | ||
+ | else if (!opts.local && tipAttribute.indexOf('#') !== 0) { | ||
+ | if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) { | ||
+ | $cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />'); | ||
+ | cluetipShow(pY); | ||
+ | } else if (cluetipContents && opts.ajaxCache) { | ||
+ | $cluetipInner.html(cluetipContents); | ||
+ | // highlight target having a <span id=".."> see also ajaxSettings success: | ||
+ | var idTarget = link.toString().split('#')[1]; | ||
+ | if(idTarget!=undefined){ | ||
+ | $cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert'); | ||
+ | } | ||
+ | cluetipShow(pY); | ||
+ | } else { | ||
+ | var optionBeforeSend = opts.ajaxSettings.beforeSend, | ||
+ | optionError = opts.ajaxSettings.error, | ||
+ | optionSuccess = opts.ajaxSettings.success, | ||
+ | optionComplete = opts.ajaxSettings.complete; | ||
+ | var ajaxSettings = { | ||
+ | cache: false, // force requested page not to be cached by browser | ||
+ | url: tipAttribute, | ||
+ | beforeSend: function(xhr) { | ||
+ | if (optionBeforeSend) {optionBeforeSend.call(link, xhr, $cluetip, $cluetipInner);} | ||
+ | $cluetipOuter.children().empty(); | ||
+ | $cluetipWait.css({top: mouseY+20, left: mouseX+20, zIndex: $this.data('thisInfo').zIndex-1}).show(); | ||
+ | }, | ||
+ | error: function(xhr, textStatus) { | ||
+ | if (isActive) { | ||
+ | if (optionError) { | ||
+ | optionError.call(link, xhr, textStatus, $cluetip, $cluetipInner); | ||
+ | } else { | ||
+ | $cluetipInner.html($.resource('toolTipNoContentLoadable')); | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | success: function(data, textStatus) { | ||
+ | cluetipContents = opts.ajaxProcess.call(link, data); | ||
+ | if (isActive) { | ||
+ | if (optionSuccess) {optionSuccess.call(link, data, textStatus, $cluetip, $cluetipInner);} | ||
+ | $cluetipInner.html(cluetipContents); | ||
+ | // highlight target having a <span id=".."> | ||
+ | var idTarget = link.toString().split('#')[1]; | ||
+ | if(idTarget!=undefined){ | ||
+ | $cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert'); | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | complete: function(xhr, textStatus) { | ||
+ | if (optionComplete) {optionComplete.call(link, xhr, textStatus, $cluetip, $cluetipInner);} | ||
+ | imgCount = $('#cluetip-inner img').length; | ||
+ | if (imgCount && !$.browser.opera) { | ||
+ | $('#cluetip-inner img').bind('load error', function() { | ||
+ | imgCount--; | ||
+ | if (imgCount<1) { | ||
+ | $cluetipWait.hide(); | ||
+ | if (isActive) { cluetipShow(pY); } | ||
+ | } | ||
+ | }); | ||
+ | } else { | ||
+ | $cluetipWait.hide(); | ||
+ | if (isActive) { cluetipShow(pY); } | ||
+ | } | ||
+ | } | ||
+ | }; | ||
+ | var ajaxMergedSettings = $.extend(true, {}, opts.ajaxSettings, ajaxSettings); | ||
+ | $.ajax(ajaxMergedSettings); | ||
+ | } | ||
+ | |||
+ | // load an element from the same page | ||
+ | } else if (opts.local) { | ||
+ | |||
+ | var $localContent = $(tipAttribute + (/#\S+$/.test(tipAttribute) ? '' : ':eq(' + index + ')')).clone(true).show(); | ||
+ | $cluetipInner.html($localContent); | ||
+ | cluetipShow(pY); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // get dimensions and options for cluetip and prepare it to be shown | ||
+ | var cluetipShow = function(bpY) { | ||
+ | $cluetip.addClass('cluetip-jtip'); | ||
+ | if (opts.truncate) { | ||
+ | var $truncloaded = $cluetipInner.text().slice(0,opts.truncate) + '...'; | ||
+ | $cluetipInner.html($truncloaded); | ||
+ | } | ||
+ | if (opts.showTitle) { | ||
+ | $cluetipTitle.show().html(tipTitle ? tipTitle : ' '); | ||
+ | } else { | ||
+ | $cluetipTitle.hide(); | ||
+ | } | ||
+ | // INSERTED CODE: get href (= real wiki page link) and add as link in title ("(New Window)"). Cluetip now on link itself! | ||
+ | // Not found how to get access in onShow() to "this"; thus inserted here. | ||
+ | $cluetipTitle.prepend("<a href='"+ $this.attr("href") + "' target='_blank' title='"+ $.resource('toolTipNewWindow') +"' >" + $.resource('newWindow') + "</a>").show(); | ||
+ | if (opts.sticky) { // Close text modified directly | ||
+ | var $closeLink = $('<div id="cluetip-close"/>').append(createButton("close", function() {cluetipClose();return false;})); | ||
+ | if(opts.closePosition == 'bottom') { | ||
+ | $closeLink.appendTo($cluetipInner); | ||
+ | } else if(opts.closePosition == 'title'){ | ||
+ | $closeLink.prependTo($cluetipTitle); | ||
+ | } else { | ||
+ | $closeLink.prependTo($cluetipInner); | ||
+ | } | ||
+ | if (opts.mouseOutClose) { | ||
+ | $cluetip.bind('mouseleave.cluetip', function() {cluetipClose();}); | ||
+ | } else { | ||
+ | $cluetip.unbind('mouseleave.cluetip'); | ||
+ | } | ||
+ | } | ||
+ | // now that content is loaded, finish the positioning | ||
+ | var direction = ''; | ||
+ | $cluetipOuter.css({zIndex: $this.data('thisInfo').zIndex, overflow: defHeight == 'auto' ? 'visible' : 'auto', 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"); | ||
− | + | if (wgAction == "edit" || wgAction == "submit" ) { | |
− | importScript("MediaWiki: | + | 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') +'">↑ </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 : " (show less) ",
captionExpand : " (more...) ",
expandAll : "Show all extras",
iconCloseWindowHover : "http://upload.wikimedia.org/wikipedia/commons/d/d0/Close_icon_hover.jpg",
iconCloseWindow : "http://upload.wikimedia.org/wikipedia/commons/8/87/Close_icon_default.jpg",
iconOverview : "http://upload.wikimedia.org/wikipedia/commons/thumb/2/22/View-pause_Gion_simple.svg/20px-View-pause_Gion_simple.svg.png",
iconResume : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStart1st : "http://upload.wikimedia.org/wikipedia/commons/thumb/4/49/View-playback_Gion_simple.svg/20px-View-playback_Gion_simple.svg.png",
iconStartNew : "http://upload.wikimedia.org/wikipedia/commons/thumb/0/05/View-refresh_Gion_simple.svg/20px-View-refresh_Gion_simple.svg.png",
imageMetadataLink : "(Information about Creator, License and Copyright)",
newWindow : "(New Window …)",
toolTipClose : "Click to close",
toolTipCollapse : "(click to hide information below)",
toolTipExpand : "(click to show more information below)",
toolTipImageZooming : "Images can be enlarged by clicking on it",
toolTipNavigatePagetop : "Top of page",
toolTipNewWindow : "(click to open content in a new window or tab)",
toolTipFloatleft : "floating on the left side",
toolTipFloatright: "floating on the right side",
toolTipUnfloat: "back to default position",
toolTipNoContentLoadable:"<i>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 : " (weniger anzeigen) ",
captionExpand : " (mehr...) ",
expandAll : "Alle Zusatzinformationen zeigen",
imageMetadataLink : "(Informationen zu Autor, Lizenz und Copyright)",
newWindow : "(Neues Fenster …)",
toolTipClose : "Zum Schließen klicken",
toolTipCollapse : "(klicken um Zusatzinformationen zu verbergen)",
toolTipExpand : "(klicken um Zusatzinformationen anzuzeigen)",
toolTipHeadingLink: "Klicken um (permanenten) Link dieser Überschrift anzuzeigen",// MediaWiki:Gadget-HeadingLink
toolTipHeadingLinkHelp: "(1) Link zu dieser Überschrift oder (2) Link mit Versionsnummer:",// MediaWiki:Gadget-HeadingLink
toolTipImageZooming : "Bilder können durch Anklicken vergrößert betrachtet werden",
toolTipNavigatePagetop : "Zum Seitenanfang",
toolTipNewWindow : "(klicken um Inhalt in neuem Fenster oder Reiter zu öffnen)",
toolTipFloatleft : "Links schwebend",
toolTipFloatright: "Rechts schwebend",
toolTipUnfloat: "Zurück zur Normalposition",
toolTipNoContentLoadable:"<i>Leider konnte der Inhalt nicht geladen werden.</i>",
zoomNotPossible : "(Dieses Bild kann nicht weiter vergrößert werden)",
// see MediaWiki:zoomImage.js
ZoomImage_toolTipLoad : "(klicken um Originalbild nachzuladen; bei großen Bildern kann dies u. U. langsam sein)",
ZoomImage_textZoomOrig: "Vergrößerungsfunktion"
},
it: {
captionCollapse : " (mostra di meno) ",
captionExpand : " (più...) ",
expandAll : "Mostra tutti informazione", //REVISE
imageMetadataLink : "(Informazione sull'Autore, Licenza e Copyright)",
toolTipClose : "Clicca per chiudere",
toolTipImageZooming : "Le immagini possono essere ingrandite cliccandoci sopra",
zoomNotPossible : "(Al momento non è possibilie ingrandire questa immagine)" // TODO translation see en version
}
};
/*
* Description: Get resource string (text, image URLs) for a given language, based on a string-key
* If no resource is defined in a given language for a resource key, the resource for "en" will be returned, if this is missing as well an error message.
* resourceKey: key for the resource (string)
*/
$.resource = function (resourceKey) {
var lang = wgUserLanguage.split("-")[0]; // language: "pt-BR", "de-formal", etc.
return ($.jI18n[lang] && $.jI18n[lang][resourceKey] ?
$.jI18n[lang][resourceKey] :
($.jI18n.en[resourceKey]) ? $.jI18n.en[resourceKey] : "MISSING RESOURCE: no $.jI18n.en." + resourceKey + " defined.");
};
/*
* Descriptions: Create html string for link with image and/or text content
* attributes: string of combined other attributes of link element; must use ' as inner quotes, and \" inside event functions
* imgResourceKey, txtResourceKey: resource keys; 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> " : "")
+ $.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 + non-words (\W) */
// OLD: shallExpand = (jLink.text().replace(/( |\W)/g,'') == $.resource("captionExpand").replace(/( |\W)/g,''));
// *** TRYING:
shallExpand = (jLink.text().replace(/(\W)/g,'') == $.resource("captionExpand").replace(/(\W)/g,''));
}
jLink
.attr("title", $.resource( shallExpand ? "toolTipCollapse" : "toolTipExpand" ))
.html($.resource( shallExpand ? "captionCollapse" : "captionExpand" ));
jTable.find("tr:not(tr:first)").toggle(shallExpand);
// show/hide all in set, tr of nested tables are included
}
return false;
}
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,'&').replace(/>/g,'>').replace(/</g,'<');
}
function returnFalse() { return false; }
//activate clueTip
var activate = function(event) {
var pY;
if (!opts.onActivate($this)) {
return false;
}
isActive = true;
$cluetip.removeClass().css({width: tipInnerWidth});
if (tipAttribute == $this.attr('href')) {
$this.css('cursor', opts.cursor);
}
if (opts.hoverClass) {
$this.addClass(opts.hoverClass);
}
linkTop = posY = $this.offset().top;
linkLeft = $this.offset().left;
mouseX = event.pageX;
mouseY = event.pageY;
if (link.tagName.toLowerCase() != 'area') {
sTop = $(document).scrollTop();
winWidth = $(window).width();
}
// position clueTip horizontally
if (opts.positionBy == 'fixed') {
posX = linkWidth + linkLeft + lOffset;
$cluetip.css({left: posX});
} else {
posX = (linkWidth > linkLeft && linkLeft > tipWidth) || linkLeft + linkWidth + tipWidth + lOffset > winWidth ? linkLeft - tipWidth - lOffset : linkWidth + linkLeft + lOffset;
if (link.tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse
if (mouseX + 20 + tipWidth > winWidth) {
$cluetip.addClass(' cluetip-jtip');
posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'),10) + parseInt($cluetipInner.css('marginRight'),10) : mouseX - (tipWidth/2);
} else {
posX = mouseX + lOffset;
}
}
pY = posX < 0 ? event.pageY + tOffset : event.pageY;
$cluetip.css({
left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0),
zIndex: $this.data('thisInfo').zIndex
});
$cluetipArrows.css({zIndex: $this.data('thisInfo').zIndex+1});
}
wHeight = $(window).height();
// load a string from cluetip method's first argument
if (js) {
if (typeof js == 'function') {
js = js.call(link);
}
$cluetipInner.html(js);
cluetipShow(pY);
}
// load the title attribute only (or user-selected attribute).
// clueTip title is string before 1st delimiter, subsequent delim place clueTip body text on separate lines
else if (tipParts) {
var tpl = tipParts.length;
$cluetipInner.html(tpl ? tipParts[0] : '');
if (tpl > 1) {
for (var i=1; i < tpl; i++){
$cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>');
}
}
cluetipShow(pY);
}
// load external file via ajax
else if (!opts.local && tipAttribute.indexOf('#') !== 0) {
if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) {
$cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />');
cluetipShow(pY);
} else if (cluetipContents && opts.ajaxCache) {
$cluetipInner.html(cluetipContents);
// highlight target having a <span id=".."> see also ajaxSettings success:
var idTarget = link.toString().split('#')[1];
if(idTarget!=undefined){
$cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert');
}
cluetipShow(pY);
} else {
var optionBeforeSend = opts.ajaxSettings.beforeSend,
optionError = opts.ajaxSettings.error,
optionSuccess = opts.ajaxSettings.success,
optionComplete = opts.ajaxSettings.complete;
var ajaxSettings = {
cache: false, // force requested page not to be cached by browser
url: tipAttribute,
beforeSend: function(xhr) {
if (optionBeforeSend) {optionBeforeSend.call(link, xhr, $cluetip, $cluetipInner);}
$cluetipOuter.children().empty();
$cluetipWait.css({top: mouseY+20, left: mouseX+20, zIndex: $this.data('thisInfo').zIndex-1}).show();
},
error: function(xhr, textStatus) {
if (isActive) {
if (optionError) {
optionError.call(link, xhr, textStatus, $cluetip, $cluetipInner);
} else {
$cluetipInner.html($.resource('toolTipNoContentLoadable'));
}
}
},
success: function(data, textStatus) {
cluetipContents = opts.ajaxProcess.call(link, data);
if (isActive) {
if (optionSuccess) {optionSuccess.call(link, data, textStatus, $cluetip, $cluetipInner);}
$cluetipInner.html(cluetipContents);
// highlight target having a <span id="..">
var idTarget = link.toString().split('#')[1];
if(idTarget!=undefined){
$cluetipInner.find(jqueryEscapeId('#'+idTarget)).addClass('alert');
}
}
},
complete: function(xhr, textStatus) {
if (optionComplete) {optionComplete.call(link, xhr, textStatus, $cluetip, $cluetipInner);}
imgCount = $('#cluetip-inner img').length;
if (imgCount && !$.browser.opera) {
$('#cluetip-inner img').bind('load error', function() {
imgCount--;
if (imgCount<1) {
$cluetipWait.hide();
if (isActive) { cluetipShow(pY); }
}
});
} else {
$cluetipWait.hide();
if (isActive) { cluetipShow(pY); }
}
}
};
var ajaxMergedSettings = $.extend(true, {}, opts.ajaxSettings, ajaxSettings);
$.ajax(ajaxMergedSettings);
}
// load an element from the same page
} else if (opts.local) {
var $localContent = $(tipAttribute + (/#\S+$/.test(tipAttribute) ? '' : ':eq(' + index + ')')).clone(true).show();
$cluetipInner.html($localContent);
cluetipShow(pY);
}
};
// get dimensions and options for cluetip and prepare it to be shown
var cluetipShow = function(bpY) {
$cluetip.addClass('cluetip-jtip');
if (opts.truncate) {
var $truncloaded = $cluetipInner.text().slice(0,opts.truncate) + '...';
$cluetipInner.html($truncloaded);
}
if (opts.showTitle) {
$cluetipTitle.show().html(tipTitle ? tipTitle : ' ');
} else {
$cluetipTitle.hide();
}
// INSERTED CODE: get href (= real wiki page link) and add as link in title ("(New Window)"). Cluetip now on link itself!
// Not found how to get access in onShow() to "this"; thus inserted here.
$cluetipTitle.prepend("<a href='"+ $this.attr("href") + "' target='_blank' title='"+ $.resource('toolTipNewWindow') +"' >" + $.resource('newWindow') + "</a>").show();
if (opts.sticky) { // Close text modified directly
var $closeLink = $('<div id="cluetip-close"/>').append(createButton("close", function() {cluetipClose();return false;}));
if(opts.closePosition == 'bottom') {
$closeLink.appendTo($cluetipInner);
} else if(opts.closePosition == 'title'){
$closeLink.prependTo($cluetipTitle);
} else {
$closeLink.prependTo($cluetipInner);
}
if (opts.mouseOutClose) {
$cluetip.bind('mouseleave.cluetip', function() {cluetipClose();});
} else {
$cluetip.unbind('mouseleave.cluetip');
}
}
// now that content is loaded, finish the positioning
var direction = '';
$cluetipOuter.css({zIndex: $this.data('thisInfo').zIndex, overflow: defHeight == 'auto' ? 'visible' : 'auto', 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') +'">↑ </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>