MediaWiki:Common.js: Unterschied zwischen den Versionen
Aus quickguide.bitcointoolz.com
Zur Navigation springenZur Suche springen
(Die Seite wurde neu angelegt: „→Das folgende JavaScript wird für alle Benutzer geladen.: // Funktion, um Benutzergruppen zu erkennen mw.loader.using('mediawiki.user', function () { mw.user.getGroups().done(function (groups) { // Überprüfe, ob der Benutzer ein Admin ist if (groups.indexOf('sysop') === -1) { // Wenn der Benutzer kein Admin ist, verstecke den Link zu den Spezialseiten $('a[href*="Special:SpecialPages"]').parent().hide();…“) |
Marko (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| (379 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
console.log('[Common.js] My Common.js is running'); | |||
/* Das folgende JavaScript wird für alle Benutzer geladen. */ | /* Das folgende JavaScript wird für alle Benutzer geladen. */ | ||
// Funktion, ob JavaScript geladen wird | |||
//console.log('Common.js wird geladen'); | |||
// Funktion, um Benutzergruppen zu erkennen | // Funktion, um Benutzergruppen zu erkennen | ||
mw.loader.using('mediawiki.user', function () { | mw.loader.using('mediawiki.user', function () { | ||
mw.user.getGroups().done(function (groups) { | mw.user.getGroups().done(function (groups) { | ||
// Überprüfe, ob der Benutzer ein Admin ist | // Überprüfe, ob der Benutzer ein Admin ist (sysop-Gruppe) | ||
if (groups.indexOf('sysop') === -1) { | if (groups.indexOf('sysop') === -1) { | ||
// | // Verstecke den Link zu den Spezialseiten | ||
$('a[href*="Special:SpecialPages"]').parent().hide(); | $('a[href*="Special:SpecialPages"]').parent().hide(); | ||
// Verstecke den Link zu den Seiteninformationen | |||
$('a[href*="Special:PageInformation"]').parent().hide(); | |||
// Verstecke den Link zu Änderungen an verlinkten Seiten | |||
$('#t-recentchangeslinked').hide(); | |||
// Verstecke den Link zur Druckversion | |||
$('#t-print').hide(); | |||
// Verstecke den Link zu Links auf diese Seite | |||
$('#t-whatlinkshere').hide(); | |||
// Verstecke den Link zu den Spezialseiten (deutsch) | |||
$('a[href*="Spezial:Spezialseiten"]').parent().hide(); | |||
// Verstecke den Link zu den Seiteninformationen (deutsch) | |||
$('a[href*="Spezial:Seiteninformationen"]').parent().hide(); | |||
// Verstecke den Link zu Änderungen an verlinkten Seiten (deutsch) | |||
$('a[href*="Spezial:Änderungen an verlinkten Seiten"]').parent().hide(); | |||
// Verstecke den Link zur Druckversion (deutsch) | |||
$('#t-print').hide(); | |||
// Verstecke den Link zu Links auf diese Seite (deutsch) | |||
$('a[href*="Spezial:Linkliste"]').parent().hide(); | |||
// Verstecke den Link zum permanenten Link (deutsch) | |||
$('a[href*="Spezial:Permanenter_Link"]').parent().hide(); | |||
// Verstecke den Link zum permanenten Link (deutsch) | |||
$('a[href*="oldid"]').parent().hide(); | |||
// Verstecke den Link zu Letzte Änderungen (deutsch) | |||
$('a[href*="Spezial:Letzte_Änderungen"]').parent().hide(); | |||
$('#n-recentchanges').hide(); // Verstecke den Link zu Letzte Änderungen (ID) | |||
} | |||
}); | |||
}); | |||
// ******************************************************************************************************************** | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// ******************************************************************************************************************** | |||
// *START************************ Fügt den CSS-Code für das schlagende Herz hinzu ************************************* | |||
mw.loader.using( 'mediawiki.util', function () { | |||
var css = '.red-heart {' + | |||
'color: #FB0000;' + | |||
'font-size: 17px;' + | |||
'animation: heartbeat 1.8s infinite ease-in-out;' + | |||
'display: inline-block;' + | |||
'vertical-align: middle;' + | |||
'}' + | |||
'@keyframes heartbeat {' + | |||
'0%, 100% {' + | |||
'transform: scale(0.8) translateY(-1px);' + | |||
'opacity: 0.5;' + | |||
'}' + | |||
'25%, 75% {' + | |||
'transform: scale(1.0) translateY(-1px);' + | |||
'opacity: 1;' + | |||
'}' + | |||
'}'; | |||
var style = document.createElement('style'); | |||
style.type = 'text/css'; | |||
if (style.styleSheet) { | |||
style.styleSheet.cssText = css; | |||
} else { | |||
style.appendChild(document.createTextNode(css)); | |||
} | |||
document.getElementsByTagName('head')[0].appendChild(style); | |||
}); | |||
// ************************************************************* | |||
mw.loader.using('mediawiki.util', function () { | |||
// Erstelle das neue Element für den Text unter dem Logo | |||
var logoText = document.createElement('div'); | |||
logoText.innerHTML = '<strong>We <span class="red-heart" style="position: relative; top: -3px;">❤️</span><span style="color: #ff5000; font-size: 17px;"> ₿</span>itcoin</strong>'; // Setzt das ₿-Symbol in Orange und größer | |||
logoText.style.textAlign = 'center'; // Zentriert den Text | |||
logoText.style.marginTop = '-20px'; // Fügt Abstand zum Logo hinzu | |||
logoText.style.marginLeft = '-5px'; // Verschiebt den Text 10px nach links | |||
// Füge das Element unter dem Logo hinzu | |||
var logoContainer = document.getElementById('p-logo'); | |||
if (logoContainer) { | |||
logoContainer.appendChild(logoText); | |||
} | |||
}); | |||
// ***************************************** | |||
mw.loader.using('mediawiki.util', function () { | |||
// Finde den Container des Logos (#p-logo) | |||
var logoContainer = document.getElementById('p-logo'); | |||
// Wenn das Logo existiert, verschiebe es um 5px nach oben und 10px nach rechts | |||
if (logoContainer) { | |||
logoContainer.style.marginTop = '-5px'; // Verschiebt das Logo um -5px nach oben | |||
logoContainer.style.marginLeft = '5px'; // Verschiebt das Logo um 5px nach rechts | |||
} | |||
}); | |||
// *ENDE************************* Fügt den CSS-Code für das schlagende Herz hinzu ************************************* | |||
// ******************************************************************************************************************** | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// ******************************************************************************************************************** | |||
// *START*********************************** ₿-Rakete "To the Moon" *************************************************** | |||
(function($, mw) { | |||
$(function() { | |||
// Überprüfen, ob der "Nach oben"-Container bereits existiert | |||
if ($('#back-to-top-container').length === 0) { | |||
// Erstelle den Container für den Button und die Animationen | |||
var $backToTopContainer = $('<div id="back-to-top-container"></div>').css({ | |||
position: 'fixed', // Fixiert den Container am Bildschirm | |||
bottom: '20px', // Abstand vom unteren Bildschirmrand | |||
right: '20px', // Abstand vom rechten Bildschirmrand | |||
width: '50px', // Breite des Containers | |||
textAlign: 'center', // Zentriert den Inhalt horizontal | |||
zIndex: '1000' // Platzierung über anderen Elementen | |||
}).appendTo('body'); | |||
// Initialer Text "To the Top" | |||
var $backToTopText = $('<div id="back-to-top-text">To <br>the<br>Top</div>').css({ | |||
fontSize: '12pt', // Schriftgröße des Textes | |||
color: 'rgba(255, 96, 0, 0.8)', // Schriftfarbe mit Transparenz | |||
backgroundColor: 'rgba(255, 255, 255, 0.8)', // Hintergrundfarbe mit Transparenz | |||
padding: '5px', // Innenabstand des Textcontainers | |||
borderRadius: '15px', // Abgerundete Ecken | |||
marginBottom: '10px', // Abstand nach unten | |||
marginLeft: '-3px', // Leichte Verschiebung nach links | |||
position: 'relative', // Relative Positionierung | |||
zIndex: '1' // Niedriger z-index für die z-Achsen-Ordnung | |||
}).appendTo($backToTopContainer); | |||
// Bitcoin-Symbol für die Animation | |||
var $bitcoin = $('<div id="bitcoin">₿</div>').css({ | |||
position: 'absolute', // Absolute Positionierung für freie Bewegung | |||
bottom: '75px', // Startposition von unten | |||
left: '50%', // Horizontale Zentrierung | |||
transform: 'translateX(-50%)', // Korrekte Zentrierung | |||
fontSize: '28px', // Schriftgröße | |||
fontWeight: 'bold', // Fettschrift | |||
color: '#FF6000', // Bitcoin Orange | |||
opacity: '0', // Unsichtbar bis zur Animation | |||
zIndex: '2' // Höherer z-index für Vordergrund | |||
}).appendTo($backToTopContainer); | |||
// Raketen-Symbol für die Animation | |||
var $rocket = $('<div id="rocket">🚀</div>').css({ | |||
position: 'absolute', // Absolute Positionierung für freie Bewegung | |||
bottom: '42px', // Startposition von unten | |||
left: '50%', // Horizontale Zentrierung | |||
transform: 'translateX(-50%) rotate(-45deg)', // Zentrierung und Drehung | |||
fontSize: '24px', // Schriftgröße | |||
opacity: '0', // Unsichtbar bis zur Animation | |||
zIndex: '2' // Höherer z-index für Vordergrund | |||
}).appendTo($backToTopContainer); | |||
// "Nach oben"-Button | |||
var $backToTopButton = $('<div id="back-to-top">▲</div>').css({ | |||
width: '50px', // Breite des Buttons | |||
height: '50px', // Höhe des Buttons | |||
backgroundColor: 'rgba(255, 96, 0, 0.5)', // Hintergrundfarbe mit Transparenz | |||
color: '#fff', // Schriftfarbe | |||
lineHeight: '50px', // Vertikale Zentrierung des Pfeils | |||
fontSize: '22px', // Schriftgröße des Pfeils | |||
fontWeight: 'bold', // Schriftart des Pfeils | |||
borderRadius: '50%', // Runde Form | |||
cursor: 'pointer', // Hand-Cursor bei Hover | |||
opacity: '0.6', // Anfangs-Transparenz | |||
textAlign: 'center', // Zentrierung des Pfeils | |||
position: 'relative', // Relative Positionierung | |||
zIndex: '1' // Gleicher z-index wie der Text | |||
}).appendTo($backToTopContainer); | |||
// Klick-Ereignis für den Button (scrollt nach oben) | |||
$backToTopButton.on('click', function() { | |||
$('html, body').animate({ scrollTop: 0 }, 'slow'); | |||
}); | |||
// Variable, um den Animationsstatus zu verfolgen | |||
var isAnimating = false; | |||
// Hover-Ereignis für den Button | |||
$backToTopButton.hover( | |||
function() { | |||
$(this).css('opacity', '0.9'); | |||
// Prüfen, ob bereits eine Animation läuft | |||
if (!isAnimating) { | |||
isAnimating = true; // Animation wird gestartet | |||
// Text zu "To the Moon" ändern | |||
$backToTopText.fadeOut('slow', function() { | |||
$backToTopText.html('„To <br>the<br>Moon!“').fadeIn('slow'); | |||
}); | |||
// Bitcoin-Symbol animieren | |||
$bitcoin.stop(true, true).css({ bottom: '75px', opacity: '1' }).animate( | |||
{ bottom: '485px', opacity: '0' }, | |||
3000 | |||
); | |||
// Rakete animieren | |||
$rocket.stop(true, true).css({ bottom: '42px', opacity: '1' }).animate( | |||
{ bottom: '360px', opacity: '0' }, | |||
3000, | |||
function() { | |||
// Nach Abschluss der Raketenanimation | |||
$rocket.css({ bottom: '42px', opacity: '0' }); | |||
$bitcoin.css({ bottom: '75px', opacity: '0' }); | |||
// Text langsam zurück zu "To the Top" ändern | |||
$backToTopText.fadeOut('slow', function() { | |||
$backToTopText.html('To <br>the<br>Top').fadeIn('slow'); | |||
}); | |||
isAnimating = false; // Animation ist beendet | |||
} | |||
); | |||
} | |||
}, | |||
function() { | |||
// Zurücksetzen der Button-Transparenz beim Verlassen des Hover-Effekts | |||
$(this).css('opacity', '0.6'); | |||
} | |||
); | |||
// Button anzeigen | |||
$backToTopButton.show(); | |||
} | } | ||
}); | }); | ||
})(jQuery, mediaWiki); | |||
// *ENDE************************************ ₿-Rakete "To the Moon" *************************************************** | |||
// ******************************************************************************************************************** | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// ******************************************************************************************************************** | |||
// *START********************* Script zum täglichen Abruf und Einfügen des Bitcoin-Kurses 2024-10-05 ****************** | |||
$(document).ready(function() { | |||
console.log("Document is ready - Starting Bitcoin price script"); | |||
// Variable zum Zwischenspeichern der zuletzt abgerufenen Bitcoin-Kursdaten | |||
var cachedBitcoinData = null; | |||
var lastUpdateTime = null; | |||
// Funktion zum Abrufen des Bitcoin-Kurses von der CoinGecko API mit Ratenbegrenzung | |||
function fetchWithRateLimit(url) { | |||
console.log("[fetchWithRateLimit] URL: ", url); | |||
const maxRequestsPerMinute = 3; | |||
if (!window.requestCount) { | |||
window.requestCount = 0; | |||
} | |||
if (!window.requestTimer) { | |||
window.requestTimer = setInterval(function () { | |||
window.requestCount = 0; // Reset alle 60 Sekunden | |||
console.log("[fetchWithRateLimit] Request count reset"); | |||
}, 60000); | |||
} | |||
if (window.requestCount >= maxRequestsPerMinute) { | |||
console.warn("[fetchWithRateLimit] Max requests per minute reached"); | |||
return Promise.reject(new Error('Maximale Anfragenanzahl erreicht. Bitte warten.')); | |||
} | |||
window.requestCount++; | |||
console.log("[fetchWithRateLimit] Request count incremented: ", window.requestCount); | |||
return fetch(url).then(function(response) { | |||
console.log("[fetchWithRateLimit] Fetch response status: ", response.status); | |||
if (!response.ok) { | |||
throw new Error('Fehler bei der Anfrage: ' + response.statusText); | |||
} | |||
return response; | |||
}).catch(function(error) { | |||
console.error('[fetchWithRateLimit] Fehler bei der Anfrage:', error); | |||
throw error; // Fehler weiterreichen, um sicherzustellen, dass sie ordnungsgemäß behandelt werden | |||
}); | |||
} | |||
// Funktion zum Abrufen des Bitcoin-Kurses über die CoinGecko-API | |||
function getBitcoinPrice(callback) { | |||
console.log("[getBitcoinPrice] Fetching Bitcoin price from CoinGecko..."); | |||
fetchWithRateLimit('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd,eur') | |||
.then(function(response) { | |||
console.log("[getBitcoinPrice] Response received"); | |||
if (!response.ok) { | |||
if (response.status === 429) { | |||
throw new Error('[getBitcoinPrice] Zu viele Anfragen - Bitte versuche es später erneut'); | |||
} else { | |||
throw new Error('[getBitcoinPrice] Netzwerkantwort war nicht ok'); | |||
} | |||
} | |||
return response.json(); | |||
}) | |||
.then(function(data) { | |||
console.log("[getBitcoinPrice] Response data: ", data); | |||
if (data && data.bitcoin && typeof data.bitcoin.usd === 'number' && typeof data.bitcoin.eur === 'number') { | |||
console.log("[getBitcoinPrice] Bitcoin price successfully fetched"); | |||
cachedBitcoinData = data; | |||
lastUpdateTime = formatTime(); | |||
callback(null, data); | |||
} else { | |||
throw new Error("[getBitcoinPrice] Unerwartete Datenstruktur von der API"); | |||
} | |||
}) | |||
.catch(function(error) { | |||
console.error('[getBitcoinPrice] Fehler beim Abrufen des ₿itcoin-Kurses:', error); | |||
callback(new Error('[getBitcoinPrice] Abruf fehlgeschlagen'), null); | |||
}); | |||
} | |||
// Funktion zum Formatieren der Kurswerte in Euro und Dollar | |||
function formatCurrency(value, currency) { | |||
console.log("[formatCurrency] Formatting value: ", value, " Currency: ", currency); | |||
return value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' ' + currency; | |||
} | |||
// Funktion zum Formatieren von Datum und Zeit | |||
function formatTime() { | |||
var now = new Date(); | |||
var year = now.getFullYear(); | |||
var month = (now.getMonth() + 1).toString().padStart(2, '0'); | |||
var day = now.getDate().toString().padStart(2, '0'); | |||
var hours = now.getHours().toString().padStart(2, '0'); | |||
var minutes = now.getMinutes().toString().padStart(2, '0'); | |||
var formattedTime = year + '-' + month + '-' + day + '; ' + hours + ':' + minutes + ' Uhr'; | |||
console.log("[formatTime] Formatted time: ", formattedTime); | |||
return formattedTime; | |||
} | |||
// Funktion zum Setzen des Bitcoin-Kurses im MediaWiki | |||
function setBitcoinPrice() { | |||
console.log("[setBitcoinPrice] Setting Bitcoin price..."); | |||
var placeholders = document.querySelectorAll('.bitcoin-price-placeholder'); | |||
if (placeholders.length === 0) { | |||
console.warn("[setBitcoinPrice] Kein Platzhalter für Bitcoin-Kurs gefunden, Abbruch..."); | |||
return; | |||
} | |||
placeholders.forEach(function (placeholder) { | |||
placeholder.textContent = 'Lade ₿itcoin-Kurs...'; | |||
}); | |||
getBitcoinPrice(function (error, data) { | |||
if (!error && data && data.bitcoin) { | |||
var bitcoinPriceUSD = data.bitcoin.usd; | |||
var bitcoinPriceEUR = data.bitcoin.eur; | |||
var currentTime = formatTime(); | |||
var formattedPrice = "<strong>₿itcoin-Kurs: $ " + formatCurrency(bitcoinPriceUSD, '') + " = " + formatCurrency(bitcoinPriceEUR, '€') + "</strong>"; | |||
formattedPrice += "<br><span style=\"font-size:10px; color: #555555;\">(Stand: " + currentTime + " von <a href='https://www.coingecko.com/' target='_blank' style='color:black;'>CoinGecko</a>; ohne Gewähr)</span>"; | |||
placeholders.forEach(function (placeholder) { | |||
placeholder.innerHTML = formattedPrice; | |||
}); | |||
} else { | |||
console.error("[setBitcoinPrice] Fehler beim Abrufen des Bitcoin-Kurses, Daten nicht verfügbar"); | |||
placeholders.forEach(function (placeholder) { | |||
placeholder.textContent = 'Fehler beim Abrufen des ₿itcoin-Kurses. Bitte später erneut versuchen.'; | |||
}); | |||
} | |||
}); | |||
} | |||
// Funktion zum Starten des täglichen Updates des Bitcoin-Kurses | |||
function startDailyBitcoinUpdate() { | |||
console.log("[startDailyBitcoinUpdate] Starting daily Bitcoin update..."); | |||
setBitcoinPrice(); | |||
var now = new Date(); | |||
var nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0); | |||
var timeToMidnight = nextMidnight - now; | |||
console.log("[startDailyBitcoinUpdate] Time to midnight: ", timeToMidnight); | |||
setTimeout(function () { | |||
console.log("[startDailyBitcoinUpdate] Updating Bitcoin price at midnight..."); | |||
setBitcoinPrice(); | |||
setInterval(setBitcoinPrice, 24 * 60 * 60 * 1000); | |||
}, timeToMidnight); | |||
} | |||
// Funktion zum Anzeigen des Bitcoin-Kurses als Tooltip beim Überfahren des Logos | |||
function addTooltipForLogo() { | |||
function applyTooltipStyles() { | |||
var tooltip = document.getElementById('bitcoin-price-tooltip'); | |||
if (tooltip) { | |||
tooltip.style.position = 'absolute'; | |||
tooltip.style.display = 'none'; | |||
tooltip.style.border = '1px solid black'; | |||
tooltip.style.backgroundColor = '#696969'; | |||
tooltip.style.borderRadius = '5%'; | |||
tooltip.style.padding = '10px'; | |||
tooltip.style.color = 'white'; | |||
tooltip.style.zIndex = '1000'; | |||
tooltip.style.pointerEvents = 'none'; | |||
console.log("[applyTooltipStyles] Tooltip styles applied"); | |||
} | |||
} | |||
var logoElement = document.querySelector('#p-logo a, #p-logo img'); | |||
if (!logoElement) { | |||
console.error('[addTooltipForLogo] Logo-Element nicht gefunden.'); | |||
return; | |||
} | |||
var tooltip = document.createElement('div'); | |||
tooltip.id = 'bitcoin-price-tooltip'; | |||
tooltip.className = 'bitcoin-tooltip'; | |||
document.body.appendChild(tooltip); | |||
applyTooltipStyles(); | |||
logoElement.addEventListener('mouseenter', function(event) { | |||
console.log("[addTooltipForLogo] Mouse entered logo"); | |||
if (cachedBitcoinData) { | |||
updateTooltipContent(event, cachedBitcoinData); | |||
tooltip.style.display = 'block'; | |||
} else { | |||
getBitcoinPrice(function (error, data) { | |||
if (!error && data) { | |||
updateTooltipContent(event, data); | |||
tooltip.style.display = 'block'; | |||
} | |||
}); | |||
} | |||
}); | |||
logoElement.addEventListener('mousemove', function(event) { | |||
tooltip.style.left = (event.pageX + 10) + 'px'; | |||
tooltip.style.top = (event.pageY + 10) + 'px'; | |||
console.log("[addTooltipForLogo] Tooltip position updated"); | |||
}); | |||
logoElement.addEventListener('mouseleave', function() { | |||
console.log("[addTooltipForLogo] Mouse left logo"); | |||
tooltip.style.display = 'none'; | |||
}); | |||
function updateTooltipContent(event, data) { | |||
var bitcoinPriceUSD = data.bitcoin.usd; | |||
var bitcoinPriceEUR = data.bitcoin.eur; | |||
var tooltipContent = "<div style='text-align:center;'>" + | |||
"<span style='font-size:18px; font-weight:bold;'>₿itcoin-Kurs: $ </span>" + | |||
"<span style='font-size:18px; color:orange;'>" + bitcoinPriceUSD.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + "</span>" + | |||
"<span style='font-size:18px; color:white; padding: 0 5px;'> = </span>" + | |||
"<span style='font-size:18px; color:orange;'>" + bitcoinPriceEUR.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + "</span>" + | |||
"<span style='font-size:18px; font-weight:bold;'> € </span></div>" + | |||
"<br><div style='font-size:10px; text-align:right;'>(Stand: " + lastUpdateTime + " von <a href='https://www.coingecko.com/' target='_blank' style='color:black;'>CoinGecko</a>; ohne Gewähr)</div>"; | |||
tooltip.innerHTML = tooltipContent; | |||
tooltip.style.left = (event.pageX + 10) + 'px'; | |||
tooltip.style.top = (event.pageY + 10) + 'px'; | |||
console.log("[updateTooltipContent] Tooltip content updated"); | |||
} | |||
} | |||
// Starte das tägliche Update, um den Bitcoin-Kurs automatisch zu aktualisieren | |||
startDailyBitcoinUpdate(); | |||
// Füge die Tooltip-Funktionalität zum Logo hinzu | |||
addTooltipForLogo(); | |||
}); | }); | ||
// *ENDE********************* Script zum täglichen Abruf und Einfügen des Bitcoin-Kurses 2024-10-05 ******************* | |||
// ******************************************************************************************************************** | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// ******************************************************************************************************************** | |||
// *START************************** Übersetzungen mit Google-API OHNE iframe-Einbindungen ***************************** | |||
// ******************************************************************************************************************** | |||
// Google API Übersetzung Dropdown mit Benutzerspracheinstellungen | |||
var selectImportant, selectAll; | |||
// Funktion zur Erstellung der Dropdown-Menüs für wichtige und alle weiteren Sprachen | |||
function createTranslateDropdowns() { | |||
// Container für die Dropdown-Menüs erstellen | |||
var container = document.createElement('div'); | |||
container.id = 'translate_container'; | |||
container.style.position = 'fixed'; // Fixierte Position, damit der Container immer sichtbar bleibt | |||
container.style.top = '5px'; // Abstand vom oberen Rand des Fensters | |||
container.style.left = '0px'; // Abstand vom linken Rand des Fensters | |||
container.style.width = '95%'; // Passt die Breite des Containers an, um auf mobilen Geräten sichtbar zu sein | |||
container.style.maxWidth = '380px'; // Maximale Breite für größere Bildschirme | |||
container.style.height = 'auto'; // Höhe wird automatisch basierend auf dem Inhalt angepasst | |||
container.style.backgroundColor = 'rgba(255, 255, 255, 0.8)'; // Halbtransparente Hintergrundfarbe für bessere Lesbarkeit | |||
container.style.border = '1px solid #aaa'; // Rahmen um den Container in hellem Grau (#aaa) | |||
container.style.padding = '5px'; // Innenabstand innerhalb des Containers (5px auf allen Seiten) | |||
container.style.zIndex = '1000'; // Stellt sicher, dass der Container über anderen Elementen auf der Seite liegt | |||
container.style.display = 'flex'; // Flexbox für einfaches Layout der Dropdown-Elemente | |||
container.style.flexDirection = 'row'; // Elemente im Container werden horizontal nebeneinander angezeigt | |||
container.style.flexWrap = 'wrap'; // Wenn die Breite nicht ausreicht, werden die Elemente umgebrochen | |||
container.style.justifyContent = 'space-between'; // Die Dropdowns werden mit gleichem Abstand dazwischen verteilt | |||
document.body.appendChild(container); | |||
// Wrapper für die wichtigen Sprachen erstellen | |||
var importantWrapper = document.createElement('div'); | |||
importantWrapper.style.display = 'inline-block'; | |||
importantWrapper.style.marginBottom = '0px'; // Kein Abstand nach unten | |||
// Label für wichtige Sprachen erstellen | |||
var importantLabel = document.createElement('label'); | |||
importantLabel.textContent = 'Translate to:'; // Beschriftung für das Dropdown-Menü | |||
importantLabel.classList.add('no-translate'); // Klasse, damit das Label nicht übersetzt wird | |||
importantLabel.style.fontWeight = 'bold'; // Fettschrift für bessere Lesbarkeit | |||
importantLabel.style.marginRight = '2px'; // Abstand zwischen Label und Dropdown | |||
importantLabel.style.fontSize = '0.8em'; | |||
importantLabel.style.fontFamily = 'Arial, sans-serif'; | |||
importantWrapper.appendChild(importantLabel); | |||
// Dropdown für wichtige Sprachen erstellen | |||
selectImportant = createDropdown('important_languages', 'Main Languages', getImportantLanguages()); | |||
selectImportant.style.width = '100px'; // Breite des Dropdowns | |||
selectImportant.style.padding = '2px'; // Innenabstand innerhalb des Dropdowns | |||
selectImportant.style.marginRight = '2px'; // Abstand zwischen Dropdown und weiteren Elementen | |||
selectImportant.style.borderRadius = '1px'; // Leichte Abrundung der Ecken | |||
selectImportant.style.fontFamily = 'Arial, sans-serif'; | |||
selectImportant.style.fontSize = '0.8em'; | |||
importantWrapper.appendChild(selectImportant); | |||
container.appendChild(importantWrapper); | |||
// Wrapper für alle Sprachen erstellen | |||
var allWrapper = document.createElement('div'); | |||
allWrapper.style.display = 'inline-block'; | |||
allWrapper.style.marginLeft = '5px'; // Abstand links zwischen dem Wrapper und anderen Elementen | |||
allWrapper.style.marginBottom = '0px'; // Kein Abstand nach unten | |||
// Label für alle Sprachen erstellen | |||
var allLabel = document.createElement('label'); | |||
allLabel.textContent = 'All:'; // Beschriftung für das Dropdown-Menü | |||
allLabel.classList.add('no-translate'); // Klasse, damit das Label nicht übersetzt wird | |||
allLabel.style.fontWeight = 'bold'; // Fettschrift für bessere Lesbarkeit | |||
allLabel.style.marginRight = '2px'; // Abstand zwischen Label und Dropdown | |||
allLabel.style.fontSize = '0.8em'; | |||
allLabel.style.fontFamily = 'Arial, sans-serif'; | |||
allWrapper.appendChild(allLabel); | |||
// Dropdown für alle Sprachen erstellen | |||
selectAll = createDropdown('all_languages', 'Select Of All', getAllLanguages()); | |||
selectAll.style.width = '115px'; // Breite des Dropdowns | |||
selectAll.style.padding = '2px'; // Innenabstand innerhalb des Dropdowns | |||
selectAll.style.marginRight = '4px'; // Abstand rechts vom Dropdown zum nächsten Element | |||
selectAll.style.borderRadius = '2px'; // Leichte Abrundung der Ecken | |||
selectAll.style.fontFamily = 'Arial, sans-serif'; | |||
selectAll.style.fontSize = '0.8em'; | |||
allWrapper.appendChild(selectAll); | |||
container.appendChild(allWrapper); | |||
// Event Listener für Sprachänderungen hinzufügen | |||
selectImportant.addEventListener('change', function () { | |||
if (selectImportant.value !== 'de') { | |||
handleLanguageChange(selectImportant.value) | |||
.then(function() { | |||
localStorage.setItem('selectedLanguage', selectImportant.value); // Speichert die ausgewählte Sprache lokal | |||
syncDropdowns(selectImportant.value); // Synchronisiert die Dropdowns | |||
}) | |||
.catch(function(error) { | |||
console.error("[selectImportant] Fehler bei der Sprachänderung: ", error); | |||
}); | |||
} else { | |||
resetToDefaultLanguage(); // Setzt die Sprache auf Deutsch zurück, wenn 'de' ausgewählt ist | |||
} | |||
}); | |||
selectAll.addEventListener('change', function () { | |||
if (selectAll.value !== 'de') { | |||
handleLanguageChange(selectAll.value) | |||
.then(function() { | |||
localStorage.setItem('selectedLanguage', selectAll.value); // Speichert die ausgewählte Sprache lokal | |||
syncDropdowns(selectAll.value); // Synchronisiert die Dropdowns | |||
}) | |||
.catch(function(error) { | |||
console.error("[selectAll] Fehler bei der Sprachänderung: ", error); | |||
}); | |||
} else { | |||
resetToDefaultLanguage(); // Setzt die Sprache auf Deutsch zurück, wenn 'de' ausgewählt ist | |||
} | |||
}); | |||
} | |||
// Erstellt ein Dropdown-Menü | |||
function createDropdown(id, placeholderText, languages) { | |||
var select = document.createElement('select'); | |||
select.id = id; | |||
select.style.padding = '2px'; // Innenabstand des Dropdowns | |||
select.style.marginRight = '4px'; // Abstand rechts vom Dropdown, um Platz zwischen Elementen zu schaffen | |||
select.style.borderRadius = '0'; // Keine abgerundeten Ecken, um das Dropdown rechteckig zu halten | |||
select.style.fontFamily = 'Arial, sans-serif'; // Konsistente Schriftart | |||
select.style.fontSize = '0.8em'; // Schriftgröße reduziert für ein kompakteres Layout | |||
// Platzhalteroption erstellen | |||
var placeholderOption = document.createElement('option'); | |||
placeholderOption.value = ''; | |||
placeholderOption.textContent = placeholderText; // Platzhaltertext, der angezeigt wird, bevor eine Auswahl getroffen wird | |||
placeholderOption.disabled = true; // Platzhalteroption kann nicht ausgewählt werden | |||
placeholderOption.selected = true; // Platzhalter ist standardmäßig ausgewählt | |||
select.appendChild(placeholderOption); | |||
// Optionen für Sprachen hinzufügen | |||
for (var i = 0; i < languages.length; i++) { | |||
var lang = languages[i]; | |||
var option = document.createElement('option'); | |||
option.value = lang.lang; | |||
option.textContent = lang.name; // Name der Sprache, die im Dropdown angezeigt wird | |||
option.style.fontFamily = 'Arial, sans-serif'; | |||
option.style.fontSize = '1.0em'; | |||
select.appendChild(option); | |||
} | |||
return select; | |||
} | |||
// Wichtige Sprachen - Rückgabe einer Liste der wichtigsten Sprachen | |||
function getImportantLanguages() { | |||
return [ | |||
{ lang: 'de', name: '⟶ German (orig.)' }, | |||
{ lang: 'en', name: 'English' }, | |||
{ lang: 'es', name: 'Spanish' }, | |||
{ lang: 'zh-CN', name: 'Chinese (Simp.)' }, | |||
{ lang: 'fr', name: 'French' }, | |||
{ lang: 'pt', name: 'Portuguese' }, | |||
{ lang: 'ru', name: 'Russian' }, | |||
{ lang: 'ja', name: 'Japanese' }, | |||
{ lang: 'ko', name: 'Korean' }, | |||
{ lang: 'it', name: 'Italian' } | |||
]; | |||
} | |||
// Alle Sprachen - Rückgabe einer Liste aller unterstützten Sprachen | |||
function getAllLanguages() { | |||
return [ | |||
{ lang: 'af', name: 'Afrikaans' }, { lang: 'am', name: 'Amharic' }, { lang: 'ar', name: 'Arabic' }, | |||
{ lang: 'az', name: 'Azerbaijani' }, { lang: 'be', name: 'Belarusian' }, { lang: 'bg', name: 'Bulgarian' }, | |||
{ lang: 'bn', name: 'Bengali' }, { lang: 'bs', name: 'Bosnian' }, { lang: 'ca', name: 'Catalan' }, | |||
{ lang: 'zh-CN', name: 'Chinese (Simp.)' }, { lang: 'zh-TW', name: 'Chinese (Trad.)' }, | |||
{ lang: 'hr', name: 'Croatian' }, { lang: 'cs', name: 'Czech' }, { lang: 'da', name: 'Danish' }, | |||
{ lang: 'nl', name: 'Dutch' }, { lang: 'en', name: 'English' }, { lang: 'et', name: 'Estonian' }, | |||
{ lang: 'tl', name: 'Filipino' }, { lang: 'fi', name: 'Finnish' }, { lang: 'fr', name: 'French' }, | |||
{ lang: 'de', name: '⟶ German (orig.)' }, { lang: 'el', name: 'Greek' }, { lang: 'gu', name: 'Gujarati' }, | |||
{ lang: 'he', name: 'Hebrew' }, { lang: 'hi', name: 'Hindi' }, { lang: 'hu', name: 'Hungarian' }, | |||
{ lang: 'is', name: 'Icelandic' }, { lang: 'id', name: 'Indonesian' }, { lang: 'it', name: 'Italian' }, | |||
{ lang: 'ja', name: 'Japanese' }, { lang: 'kn', name: 'Kannada' }, { lang: 'ko', name: 'Korean' }, | |||
{ lang: 'lv', name: 'Latvian' }, { lang: 'lt', name: 'Lithuanian' }, { lang: 'ms', name: 'Malay' }, | |||
{ lang: 'ml', name: 'Malayalam' }, { lang: 'mr', name: 'Marathi' }, { lang: 'ne', name: 'Nepali' }, | |||
{ lang: 'no', name: 'Norwegian' }, { lang: 'fa', name: 'Persian' }, { lang: 'pl', name: 'Polish' }, | |||
{ lang: 'pt', name: 'Portuguese' }, { lang: 'pa', name: 'Punjabi' }, { lang: 'ro', name: 'Romanian' }, | |||
{ lang: 'ru', name: 'Russian' }, { lang: 'sr', name: 'Serbian' }, { lang: 'sk', name: 'Slovak' }, | |||
{ lang: 'sl', name: 'Slovenian' }, { lang: 'es', name: 'Spanish' }, { lang: 'sw', name: 'Swahili' }, | |||
{ lang: 'sv', name: 'Swedish' }, { lang: 'ta', name: 'Tamil' }, { lang: 'te', name: 'Telugu' }, | |||
{ lang: 'th', name: 'Thai' }, { lang: 'tr', name: 'Turkish' }, { lang: 'uk', name: 'Ukrainian' }, | |||
{ lang: 'ur', name: 'Urdu' }, { lang: 'vi', name: 'Vietnamese' }, { lang: 'cy', name: 'Welsh' }, | |||
{ lang: 'zu', name: 'Zulu' } | |||
]; | |||
} | |||
// Sprachänderungsfunktion mit Google Translate API | |||
function handleLanguageChange(selectedLang) { | |||
return new Promise(function(resolve, reject) { | |||
var googleTranslateApiUrl = "https://translation.googleapis.com/language/translate/v2"; | |||
var apiKey = "AIzaSyBe4xJokSKneAvA_7_aaU984vS2R8gSFtM"; | |||
if (selectedLang !== "" && selectedLang !== "de") { | |||
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false); | |||
var nodes = []; | |||
var node; | |||
// Durchlaufen aller Textknoten im Dokument | |||
while ((node = walker.nextNode())) { | |||
if (node.nodeValue.trim() !== "") { | |||
nodes.push(node); // Füge alle Textknoten hinzu, die nicht leer sind | |||
} | |||
} | |||
var maxTextLength = 500; // Maximal erlaubte Textlänge für eine Anfrage | |||
var textGroups = []; | |||
var currentGroup = []; | |||
var currentLength = 0; | |||
// Gruppiere die Textknoten basierend auf der maximalen Textlänge | |||
nodes.forEach(function (textNode) { | |||
if (currentLength + textNode.nodeValue.length > maxTextLength) { | |||
textGroups.push(currentGroup); | |||
currentGroup = []; | |||
currentLength = 0; | |||
} | |||
currentGroup.push(textNode); | |||
currentLength += textNode.nodeValue.length; | |||
}); | |||
if (currentGroup.length > 0) { | |||
textGroups.push(currentGroup); | |||
} | |||
// Sende jede Textgruppe zur Übersetzung | |||
var groupPromises = textGroups.map(function (group) { | |||
var textToTranslateArray = group.map(function (textNode) { | |||
return textNode.nodeValue; | |||
}); | |||
return fetch(googleTranslateApiUrl + "?key=" + apiKey, { | |||
method: "POST", | |||
headers: { | |||
"Content-Type": "application/json" | |||
}, | |||
body: JSON.stringify({ | |||
q: textToTranslateArray, | |||
source: "de", | |||
target: selectedLang, | |||
format: "text" | |||
}) | |||
}).then(function (response) { | |||
if (!response.ok) { | |||
throw new Error("Netzwerkantwort war nicht ok"); | |||
} | |||
return response.json(); | |||
}).then(function (data) { | |||
if (data.data && data.data.translations) { | |||
for (var i = 0; i < data.data.translations.length; i++) { | |||
group[i].nodeValue = " " + data.data.translations[i].translatedText + " "; // Leerzeichen erhalten | |||
} | |||
console.log("Übersetzung erfolgreich"); | |||
} else { | |||
throw new Error("Fehlerhafte Antwort von der Übersetzungs-API"); | |||
} | |||
}); | |||
}); | |||
Promise.all(groupPromises).then(function() { | |||
resolve(); | |||
}).catch(function (error) { | |||
console.error("Fehler bei der Übersetzung: ", error); | |||
reject(error); | |||
}); | |||
} else { | |||
resolve(); | |||
} | |||
}); | |||
} | |||
// Setzt die Sprache zurück auf die Standardsprache Deutsch | |||
function resetToDefaultLanguage() { | |||
localStorage.removeItem('selectedLanguage'); // Entfernt die gespeicherte Sprache | |||
window.location.reload(); // Lädt die Seite neu, um die Standard-Sprache anzuwenden | |||
} | |||
// Synchronisiert beide Dropdowns | |||
function syncDropdowns(selectedLang) { | |||
if (selectImportant) selectImportant.value = selectedLang; | |||
if (selectAll) selectAll.value = selectedLang; | |||
} | |||
// Google Translate Script laden und Dropdown-Menü erstellen | |||
function initializeTranslationFeature() { | |||
createTranslateDropdowns(); // Erstellt die Dropdown-Menüs | |||
var savedLanguage = localStorage.getItem('selectedLanguage'); // Holt die gespeicherte Sprache | |||
if (savedLanguage && savedLanguage !== 'de') { | |||
handleLanguageChange(savedLanguage).then(function() { | |||
syncDropdowns(savedLanguage); // Synchronisiert die Dropdowns | |||
}).catch(function(error) { | |||
console.error("[initializeTranslationFeature] Fehler bei der Sprachänderung: ", error); | |||
}); | |||
} else { | |||
syncDropdowns('de'); // Setzt die Dropdowns auf die Standardsprache | |||
} | |||
} | |||
// Document ready | |||
$(document).ready(function () { | |||
document.body.style.paddingTop = '75px'; // Abstand vom oberen Rand der Seite, um sicherzustellen, dass der Inhalt nicht überlagert wird | |||
initializeTranslationFeature(); // Initialisiert die Übersetzungsfunktion | |||
}); | |||
// *ENDE*************************** Übersetzungen mit Google-API OHNE iframe-Einbindungen ***************************** | |||
// ******************************************************************************************************************** | |||
// ******************************************************************************************************************** | |||
// *START********************* Script zum dynamischen Aufbau des E-Mail-Share-Links 2025-05-28 ********************* | |||
mw.loader.using('mediawiki.util', function() { | |||
$(function(){ | |||
var title = mw.config.get('wgTitle'), | |||
page = mw.config.get('wgPageName'), | |||
server = mw.config.get('wgServer'), | |||
script = mw.config.get('wgScriptPath'), | |||
fullUrl = server + script + '/index.php?title=' + encodeURIComponent(page), | |||
subject = encodeURIComponent( | |||
'Entdecke den tollen Bitcoin-Wiki-Artikel zum Thema »' + title + '«' | |||
), | |||
body = encodeURIComponent( | |||
'Hey, du Krypto-Fan! Entdecke jetzt den tollen Bitcoin-Wiki-Artikel zum Thema »' | |||
+ title | |||
+ '« unter ' | |||
+ fullUrl | |||
), | |||
mailto = 'mailto:info@example.org?subject=' + subject + '&body=' + body, | |||
link = '<a href="' + mailto + '">Jetzt per E-Mail teilen✉</a>'; | |||
$('#share-email-placeholder').html(link); | |||
}); | |||
}); | |||
// *ENDE********************* Script zum dynamischen Aufbau des E-Mail-Share-Links 2025-05-28 ******************* | |||
// ******************************************************************************************************************** | |||
/** | |||
* Unmined-Bitcoin-Skript für MediaWiki:Common.js | |||
* | |||
* Dieses Skript holt alle fünf Minuten die aktuelle Anzahl der bereits geminten Bitcoins von der CoinGecko-API (serverseitig via PHP-Proxy) | |||
* ab, berechnet daraus die verbleibende Anzahl bis zur Obergrenze von 21 000 000, formatiert das Ergebnis in deutscher Schreibweise | |||
* mit Punkten als Tausender-Trennzeichen und gibt es in einem DIV-Container im Wiki-Artikel aus. | |||
* | |||
* Ablauf: | |||
* 1. Sobald der DOM fertig geladen ist (jQuery(document).ready), wird geprüft, ob der DIV-Container mit der ID "unmined-btc-widget" existiert. | |||
* Ist der Container nicht vorhanden, bricht das Skript ab. | |||
* | |||
* 2. Über die Funktion fetchRemainingBTC() wird via jQuery.ajax eine JSON-Anfrage an den PHP-Proxy auf Deinem Server gestellt: | |||
* URL: https://test.toepperwien.de/unmined_bitcoin/unmined_btc.php | |||
* | |||
* Der PHP-Proxy: | |||
* • Ruft serverseitig die CoinGecko-API ab (https://api.coingecko.com/api/v3/coins/bitcoin). | |||
* • Berechnet auf dem Server den verbleibenden Bitcoin-Betrag (21 000 000 − circulating_supply) und gibt ein JSON zurück: | |||
* { | |||
* "circulating_supply": 19xxxxxx, | |||
* "remaining": 1xxxxxx | |||
* } | |||
* • Setzt in der HTTP-Antwort zwingend den Header Access-Control-Allow-Origin: *, | |||
* damit der Browser keine CORS-Fehler wirft. | |||
* | |||
* 3. Im success-Callback von jQuery.ajax wird das zurückgelieferte JSON geparst. Falls das JSON ein Feld "error" enthält, wird | |||
* „Fehler: <Fehlermeldung>“ ins DIV geschrieben. Andernfalls werden die Felder "circulating_supply" und "remaining" ausgelesen. | |||
* Der Wert „remaining“ wird dann in deutscher Tausender-Notation formatiert und in das DIV geschrieben als: | |||
* Noch <formatted> Bitcoin | |||
* | |||
* 4. Sollte die AJAX-Anfrage (z. B. beim Netzwerkausfall) scheitern, wird im error-Callback „Fehler beim Laden“ angezeigt. | |||
* | |||
* 5. Direkt nach DOM-ready wird im DIV zunächst „Lade Daten…“ angezeigt, dann der erste Aufruf von fetchRemainingBTC() durchgeführt. | |||
* Mit setInterval(fetchRemainingBTC, 10 * 60 * 1000) wird das Skript alle 10 Minuten (300 000 ms) erneut ausgeführt. | |||
*/ | |||
jQuery(function () { | |||
// Debug: Prüfen, ob Common.js geladen wird | |||
console.log('[UnminedBTC] Starting fetch script'); | |||
// 1) Container per jQuery abrufen | |||
// Dieser DIV muss im Artikel stehen: <div id="unmined-btc-widget">Lade Daten…</div> | |||
var $container = jQuery('#unmined-btc-widget'); | |||
if ($container.length === 0) { | |||
// Falls das DIV nicht existiert, brechen wir ab | |||
console.warn('[UnminedBTC] Kein Container #unmined-btc-widget gefunden, Abbruch.'); | |||
return; | |||
} | |||
// 2) Konstanten: Maximale Gesamtmenge aller Bitcoins | |||
var TOTAL_SUPPLY = 21000000; | |||
/** | |||
* formatThousand(num) | |||
* | |||
* Formatiert eine Zahl in deutsche Tausender-Notation mit Punkten als Trennzeichen. | |||
* Beispiel: 2500000 → "2.500.000" | |||
* | |||
* @param {number} num - Die zu formatierende Zahl (wird auf ganze Zahl abgerundet). | |||
* @return {string} - Die formatierte Zahl als String mit Punkt-Trennungen. | |||
*/ | |||
function formatThousand(num) { | |||
// Ganzzahl-String erzeugen | |||
var s = Math.floor(num).toString(); | |||
var parts = []; | |||
var rest = s; | |||
// Solange mehr als drei Ziffern übrig sind, letzten drei Ziffern extrahieren | |||
while (rest.length > 3) { | |||
var idx = rest.length - 3; | |||
parts.unshift(rest.slice(idx)); // Letzte 3 Ziffern ins Array vorne einfügen | |||
rest = rest.slice(0, idx); // Rest ohne die letzten 3 Ziffern | |||
} | |||
parts.unshift(rest); // Übrigen Teil (1–3 Ziffern) ebenfalls ins Array einfügen | |||
// Array mit Punkten verbinden, z. B. ["1", "126", "085"] → "1.126.085" | |||
return parts.join('.'); | |||
} | |||
/** | |||
* fetchRemainingBTC() | |||
* | |||
* Ruft per AJAX den eigenen PHP-Proxy auf, der serverseitig die CoinGecko-API | |||
* abfragt und die verbleibenden Bitcoins bis 21 000 000 berechnet. Anschließend | |||
* wird das Ergebnis ins DIV-Element (ID "unmined-btc-widget") geschrieben. | |||
* | |||
* Der PHP-Proxy liefert ein JSON-Objekt in folgender Struktur: | |||
* { | |||
* "circulating_supply": <Number>, | |||
* "remaining": <Number> | |||
* } | |||
* | |||
* Oder bei Fehlern: | |||
* { | |||
* "error": "<Fehlermeldung>" | |||
* } | |||
*/ | |||
function fetchRemainingBTC() { | |||
// 4a) URL zu deinem PHP-Proxy auf deinem Server | |||
// Der Proxy muss CORS erlauben (Access-Control-Allow-Origin: *) und gibt JSON zurück. | |||
var url = 'https://test.toepperwien.de/unmined_bitcoin/unmined_btc.php'; | |||
console.log('[UnminedBTC] AJAX-Request an URL:', url); | |||
// 4b) AJAX-Aufruf mit jQuery.ajax (ES5) | |||
jQuery.ajax({ | |||
url: url, | |||
dataType: 'json', // Erwartet JSON-Antwort | |||
timeout: 5000, // Timeout nach 5 Sekunden | |||
success: function (data) { | |||
console.log('[UnminedBTC] AJAX success, rohes JSON:', data); | |||
// 4c) Prüfen, ob der Proxy einen Fehler zurückliefert | |||
if (data.error) { | |||
console.error('[UnminedBTC] PHP-Proxy-Fehler:', data.error); | |||
// Zeige Fehlermeldung im DIV an | |||
$container.text('Fehler: ' + data.error); | |||
return; | |||
} | |||
// 4d) Auslesen von remaining und circulating_supply | |||
var remaining = data.remaining; | |||
var circ = data.circulating_supply; | |||
console.log('[UnminedBTC] circulating_supply:', circ, 'remaining:', remaining); | |||
// 4e) Validierung: remaining muss eine Zahl sein | |||
if (typeof remaining !== 'number' || isNaN(remaining)) { | |||
$container.text('Fehler: ungültige Daten'); | |||
return; | |||
} | |||
// 4f) Ausgabe im DIV: "<formatted> Bitcoin" | |||
// Beispiel: "Noch 1.126.085 Bitcoin" | |||
var formatted = formatThousand(remaining); | |||
$container.text(formatted); | |||
}, | |||
error: function (xhr, status, error) { | |||
console.error('[UnminedBTC] AJAX-Fehler:', status, error); | |||
// Zeige generische Fehlermeldung im DIV | |||
$container.text('Fehler beim Laden'); | |||
} | |||
}); | |||
} | |||
// 5) Initialisierung: Zuerst "Lade Daten…" ins DIV schreiben | |||
$container.text('Lade Daten…'); | |||
// 5a) Ersten Abruf sofort starten | |||
fetchRemainingBTC(); | |||
// 5b) Alle 10 Minuten (10 * 60 * 1000 ms) erneut abrufen | |||
setInterval(fetchRemainingBTC, 10 * 60 * 1000); | |||
}); | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
/* Anonymer Song-Zähler + Cover-Play (ohne Cookies/PII) */ | |||
(function () { | |||
function ajaxGET(url, done) { | |||
try { | |||
var xhr = new XMLHttpRequest(); | |||
xhr.open('GET', url, true); | |||
xhr.onreadystatechange = function () { | |||
if (xhr.readyState === 4) { | |||
var ok = (xhr.status >= 200 && xhr.status < 300); | |||
done(ok, ok ? xhr.responseText : null); | |||
} | |||
}; | |||
xhr.send(null); | |||
} catch (e) { done(false, null); } | |||
} | |||
function initSongBlocks() { | |||
var wrappers = document.querySelectorAll ? document.querySelectorAll('.wiki-song') : []; | |||
var playedOnce = window.__wikiSongPlayedOnce || (window.__wikiSongPlayedOnce = {}); | |||
for (var i = 0; i < wrappers.length; i++) { | |||
(function (wrap) { | |||
var id = wrap.getAttribute('data-song-id') || ''; | |||
var audio = wrap.querySelector ? wrap.querySelector('.wiki-song-audio') : null; | |||
var countEl = wrap.querySelector ? wrap.querySelector('[data-song-count]') : null; | |||
var coverBtn = wrap.querySelector ? wrap.querySelector('.wiki-song-cover-play') : null; | |||
// 1) aktuellen Zählerstand laden | |||
if (id && countEl) { | |||
ajaxGET('/audio/count.php?song=' + encodeURIComponent(id), function (ok, text) { | |||
if (!ok || !text) { return; } | |||
try { | |||
var ob = JSON.parse(text); | |||
if (ob && typeof ob.plays === 'number') { countEl.textContent = String(ob.plays); } | |||
} catch (e) {} | |||
}); | |||
} | |||
// 2) einmalig bumpen beim ersten Play | |||
if (id && audio) { | |||
audio.addEventListener('play', function () { | |||
wrap.classList && wrap.classList.add('is-playing'); | |||
if (!playedOnce[id]) { | |||
playedOnce[id] = true; | |||
ajaxGET('/audio/bump.php?song=' + encodeURIComponent(id), function () {}); | |||
if (countEl) { | |||
var n = parseInt(countEl.textContent, 10); | |||
if (!isNaN(n)) { countEl.textContent = String(n + 1); } | |||
} | |||
} | |||
}, false); | |||
audio.addEventListener('pause', function () { wrap.classList && wrap.classList.remove('is-playing'); }, false); | |||
audio.addEventListener('ended', function () { wrap.classList && wrap.classList.remove('is-playing'); }, false); | |||
} | |||
// 3) Cover-Button steuert Play/Pause | |||
if (coverBtn && audio) { | |||
coverBtn.addEventListener('click', function () { | |||
try { | |||
if (audio.paused) { audio.play(); } else { audio.pause(); } | |||
} catch (e) {} | |||
}, false); | |||
} | |||
})(wrappers[i]); | |||
} | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', initSongBlocks, false); | |||
} else { | |||
initSongBlocks(); | |||
} | |||
})(); | |||
/* Inline-Anzeige der Song-Aufrufe hinter "Wiki-Song" */ | |||
(function () { | |||
function ajaxGET(url, done) { | |||
try { | |||
var x = new XMLHttpRequest(); | |||
x.open('GET', url, true); | |||
x.onreadystatechange = function(){ if (x.readyState === 4) done(x.status>=200&&x.status<300, x.responseText); }; | |||
x.send(null); | |||
} catch (e) { done(false, null); } | |||
} | |||
function updateInlineCounts() { | |||
var nodes = document.querySelectorAll ? document.querySelectorAll('.wiki-song-inline-count') : []; | |||
if (!nodes.length) return; | |||
// ggf. mehrere IDs auf einer Seite unterstützen | |||
var byId = {}; | |||
for (var i=0;i<nodes.length;i++) { | |||
var id = nodes[i].getAttribute('data-song-id') || ''; | |||
if (!id) continue; | |||
(byId[id] = byId[id] || []).push(nodes[i]); | |||
} | |||
for (var id in byId) if (byId.hasOwnProperty(id)) { | |||
(function(songId, targets){ | |||
ajaxGET('/audio/count.php?song=' + encodeURIComponent(songId), function(ok, text){ | |||
var n = 0; | |||
if (ok && text) { try { var ob = JSON.parse(text); if (ob && typeof ob.plays === 'number') n = ob.plays|0; } catch(e){} } | |||
for (var j = 0; j < targets.length; j++) { | |||
var el = targets[j].querySelector ? targets[j].querySelector('.count') : null; | |||
if (el) { | |||
el.textContent = String(n); // nur die Zahl austauschen | |||
} else { | |||
// Fallback, falls mal kein .count drin ist: | |||
targets[j].innerHTML = '<em>Bislang ' + String(n) + ' Aufrufe</em>'; | |||
} | |||
} | |||
}); | |||
})(id, byId[id]); | |||
} | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', updateInlineCounts, false); | |||
} else { | |||
updateInlineCounts(); | |||
} | |||
})(); | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
Aktuelle Version vom 14. Oktober 2025, 14:48 Uhr
console.log('[Common.js] My Common.js is running');
/* Das folgende JavaScript wird für alle Benutzer geladen. */
// Funktion, ob JavaScript geladen wird
//console.log('Common.js wird geladen');
// Funktion, um Benutzergruppen zu erkennen
mw.loader.using('mediawiki.user', function () {
mw.user.getGroups().done(function (groups) {
// Überprüfe, ob der Benutzer ein Admin ist (sysop-Gruppe)
if (groups.indexOf('sysop') === -1) {
// Verstecke den Link zu den Spezialseiten
$('a[href*="Special:SpecialPages"]').parent().hide();
// Verstecke den Link zu den Seiteninformationen
$('a[href*="Special:PageInformation"]').parent().hide();
// Verstecke den Link zu Änderungen an verlinkten Seiten
$('#t-recentchangeslinked').hide();
// Verstecke den Link zur Druckversion
$('#t-print').hide();
// Verstecke den Link zu Links auf diese Seite
$('#t-whatlinkshere').hide();
// Verstecke den Link zu den Spezialseiten (deutsch)
$('a[href*="Spezial:Spezialseiten"]').parent().hide();
// Verstecke den Link zu den Seiteninformationen (deutsch)
$('a[href*="Spezial:Seiteninformationen"]').parent().hide();
// Verstecke den Link zu Änderungen an verlinkten Seiten (deutsch)
$('a[href*="Spezial:Änderungen an verlinkten Seiten"]').parent().hide();
// Verstecke den Link zur Druckversion (deutsch)
$('#t-print').hide();
// Verstecke den Link zu Links auf diese Seite (deutsch)
$('a[href*="Spezial:Linkliste"]').parent().hide();
// Verstecke den Link zum permanenten Link (deutsch)
$('a[href*="Spezial:Permanenter_Link"]').parent().hide();
// Verstecke den Link zum permanenten Link (deutsch)
$('a[href*="oldid"]').parent().hide();
// Verstecke den Link zu Letzte Änderungen (deutsch)
$('a[href*="Spezial:Letzte_Änderungen"]').parent().hide();
$('#n-recentchanges').hide(); // Verstecke den Link zu Letzte Änderungen (ID)
}
});
});
// ********************************************************************************************************************
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// ********************************************************************************************************************
// *START************************ Fügt den CSS-Code für das schlagende Herz hinzu *************************************
mw.loader.using( 'mediawiki.util', function () {
var css = '.red-heart {' +
'color: #FB0000;' +
'font-size: 17px;' +
'animation: heartbeat 1.8s infinite ease-in-out;' +
'display: inline-block;' +
'vertical-align: middle;' +
'}' +
'@keyframes heartbeat {' +
'0%, 100% {' +
'transform: scale(0.8) translateY(-1px);' +
'opacity: 0.5;' +
'}' +
'25%, 75% {' +
'transform: scale(1.0) translateY(-1px);' +
'opacity: 1;' +
'}' +
'}';
var style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
document.getElementsByTagName('head')[0].appendChild(style);
});
// *************************************************************
mw.loader.using('mediawiki.util', function () {
// Erstelle das neue Element für den Text unter dem Logo
var logoText = document.createElement('div');
logoText.innerHTML = '<strong>We <span class="red-heart" style="position: relative; top: -3px;">❤️</span><span style="color: #ff5000; font-size: 17px;"> ₿</span>itcoin</strong>'; // Setzt das ₿-Symbol in Orange und größer
logoText.style.textAlign = 'center'; // Zentriert den Text
logoText.style.marginTop = '-20px'; // Fügt Abstand zum Logo hinzu
logoText.style.marginLeft = '-5px'; // Verschiebt den Text 10px nach links
// Füge das Element unter dem Logo hinzu
var logoContainer = document.getElementById('p-logo');
if (logoContainer) {
logoContainer.appendChild(logoText);
}
});
// *****************************************
mw.loader.using('mediawiki.util', function () {
// Finde den Container des Logos (#p-logo)
var logoContainer = document.getElementById('p-logo');
// Wenn das Logo existiert, verschiebe es um 5px nach oben und 10px nach rechts
if (logoContainer) {
logoContainer.style.marginTop = '-5px'; // Verschiebt das Logo um -5px nach oben
logoContainer.style.marginLeft = '5px'; // Verschiebt das Logo um 5px nach rechts
}
});
// *ENDE************************* Fügt den CSS-Code für das schlagende Herz hinzu *************************************
// ********************************************************************************************************************
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// ********************************************************************************************************************
// *START*********************************** ₿-Rakete "To the Moon" ***************************************************
(function($, mw) {
$(function() {
// Überprüfen, ob der "Nach oben"-Container bereits existiert
if ($('#back-to-top-container').length === 0) {
// Erstelle den Container für den Button und die Animationen
var $backToTopContainer = $('<div id="back-to-top-container"></div>').css({
position: 'fixed', // Fixiert den Container am Bildschirm
bottom: '20px', // Abstand vom unteren Bildschirmrand
right: '20px', // Abstand vom rechten Bildschirmrand
width: '50px', // Breite des Containers
textAlign: 'center', // Zentriert den Inhalt horizontal
zIndex: '1000' // Platzierung über anderen Elementen
}).appendTo('body');
// Initialer Text "To the Top"
var $backToTopText = $('<div id="back-to-top-text">To <br>the<br>Top</div>').css({
fontSize: '12pt', // Schriftgröße des Textes
color: 'rgba(255, 96, 0, 0.8)', // Schriftfarbe mit Transparenz
backgroundColor: 'rgba(255, 255, 255, 0.8)', // Hintergrundfarbe mit Transparenz
padding: '5px', // Innenabstand des Textcontainers
borderRadius: '15px', // Abgerundete Ecken
marginBottom: '10px', // Abstand nach unten
marginLeft: '-3px', // Leichte Verschiebung nach links
position: 'relative', // Relative Positionierung
zIndex: '1' // Niedriger z-index für die z-Achsen-Ordnung
}).appendTo($backToTopContainer);
// Bitcoin-Symbol für die Animation
var $bitcoin = $('<div id="bitcoin">₿</div>').css({
position: 'absolute', // Absolute Positionierung für freie Bewegung
bottom: '75px', // Startposition von unten
left: '50%', // Horizontale Zentrierung
transform: 'translateX(-50%)', // Korrekte Zentrierung
fontSize: '28px', // Schriftgröße
fontWeight: 'bold', // Fettschrift
color: '#FF6000', // Bitcoin Orange
opacity: '0', // Unsichtbar bis zur Animation
zIndex: '2' // Höherer z-index für Vordergrund
}).appendTo($backToTopContainer);
// Raketen-Symbol für die Animation
var $rocket = $('<div id="rocket">🚀</div>').css({
position: 'absolute', // Absolute Positionierung für freie Bewegung
bottom: '42px', // Startposition von unten
left: '50%', // Horizontale Zentrierung
transform: 'translateX(-50%) rotate(-45deg)', // Zentrierung und Drehung
fontSize: '24px', // Schriftgröße
opacity: '0', // Unsichtbar bis zur Animation
zIndex: '2' // Höherer z-index für Vordergrund
}).appendTo($backToTopContainer);
// "Nach oben"-Button
var $backToTopButton = $('<div id="back-to-top">▲</div>').css({
width: '50px', // Breite des Buttons
height: '50px', // Höhe des Buttons
backgroundColor: 'rgba(255, 96, 0, 0.5)', // Hintergrundfarbe mit Transparenz
color: '#fff', // Schriftfarbe
lineHeight: '50px', // Vertikale Zentrierung des Pfeils
fontSize: '22px', // Schriftgröße des Pfeils
fontWeight: 'bold', // Schriftart des Pfeils
borderRadius: '50%', // Runde Form
cursor: 'pointer', // Hand-Cursor bei Hover
opacity: '0.6', // Anfangs-Transparenz
textAlign: 'center', // Zentrierung des Pfeils
position: 'relative', // Relative Positionierung
zIndex: '1' // Gleicher z-index wie der Text
}).appendTo($backToTopContainer);
// Klick-Ereignis für den Button (scrollt nach oben)
$backToTopButton.on('click', function() {
$('html, body').animate({ scrollTop: 0 }, 'slow');
});
// Variable, um den Animationsstatus zu verfolgen
var isAnimating = false;
// Hover-Ereignis für den Button
$backToTopButton.hover(
function() {
$(this).css('opacity', '0.9');
// Prüfen, ob bereits eine Animation läuft
if (!isAnimating) {
isAnimating = true; // Animation wird gestartet
// Text zu "To the Moon" ändern
$backToTopText.fadeOut('slow', function() {
$backToTopText.html('„To <br>the<br>Moon!“').fadeIn('slow');
});
// Bitcoin-Symbol animieren
$bitcoin.stop(true, true).css({ bottom: '75px', opacity: '1' }).animate(
{ bottom: '485px', opacity: '0' },
3000
);
// Rakete animieren
$rocket.stop(true, true).css({ bottom: '42px', opacity: '1' }).animate(
{ bottom: '360px', opacity: '0' },
3000,
function() {
// Nach Abschluss der Raketenanimation
$rocket.css({ bottom: '42px', opacity: '0' });
$bitcoin.css({ bottom: '75px', opacity: '0' });
// Text langsam zurück zu "To the Top" ändern
$backToTopText.fadeOut('slow', function() {
$backToTopText.html('To <br>the<br>Top').fadeIn('slow');
});
isAnimating = false; // Animation ist beendet
}
);
}
},
function() {
// Zurücksetzen der Button-Transparenz beim Verlassen des Hover-Effekts
$(this).css('opacity', '0.6');
}
);
// Button anzeigen
$backToTopButton.show();
}
});
})(jQuery, mediaWiki);
// *ENDE************************************ ₿-Rakete "To the Moon" ***************************************************
// ********************************************************************************************************************
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// ********************************************************************************************************************
// *START********************* Script zum täglichen Abruf und Einfügen des Bitcoin-Kurses 2024-10-05 ******************
$(document).ready(function() {
console.log("Document is ready - Starting Bitcoin price script");
// Variable zum Zwischenspeichern der zuletzt abgerufenen Bitcoin-Kursdaten
var cachedBitcoinData = null;
var lastUpdateTime = null;
// Funktion zum Abrufen des Bitcoin-Kurses von der CoinGecko API mit Ratenbegrenzung
function fetchWithRateLimit(url) {
console.log("[fetchWithRateLimit] URL: ", url);
const maxRequestsPerMinute = 3;
if (!window.requestCount) {
window.requestCount = 0;
}
if (!window.requestTimer) {
window.requestTimer = setInterval(function () {
window.requestCount = 0; // Reset alle 60 Sekunden
console.log("[fetchWithRateLimit] Request count reset");
}, 60000);
}
if (window.requestCount >= maxRequestsPerMinute) {
console.warn("[fetchWithRateLimit] Max requests per minute reached");
return Promise.reject(new Error('Maximale Anfragenanzahl erreicht. Bitte warten.'));
}
window.requestCount++;
console.log("[fetchWithRateLimit] Request count incremented: ", window.requestCount);
return fetch(url).then(function(response) {
console.log("[fetchWithRateLimit] Fetch response status: ", response.status);
if (!response.ok) {
throw new Error('Fehler bei der Anfrage: ' + response.statusText);
}
return response;
}).catch(function(error) {
console.error('[fetchWithRateLimit] Fehler bei der Anfrage:', error);
throw error; // Fehler weiterreichen, um sicherzustellen, dass sie ordnungsgemäß behandelt werden
});
}
// Funktion zum Abrufen des Bitcoin-Kurses über die CoinGecko-API
function getBitcoinPrice(callback) {
console.log("[getBitcoinPrice] Fetching Bitcoin price from CoinGecko...");
fetchWithRateLimit('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd,eur')
.then(function(response) {
console.log("[getBitcoinPrice] Response received");
if (!response.ok) {
if (response.status === 429) {
throw new Error('[getBitcoinPrice] Zu viele Anfragen - Bitte versuche es später erneut');
} else {
throw new Error('[getBitcoinPrice] Netzwerkantwort war nicht ok');
}
}
return response.json();
})
.then(function(data) {
console.log("[getBitcoinPrice] Response data: ", data);
if (data && data.bitcoin && typeof data.bitcoin.usd === 'number' && typeof data.bitcoin.eur === 'number') {
console.log("[getBitcoinPrice] Bitcoin price successfully fetched");
cachedBitcoinData = data;
lastUpdateTime = formatTime();
callback(null, data);
} else {
throw new Error("[getBitcoinPrice] Unerwartete Datenstruktur von der API");
}
})
.catch(function(error) {
console.error('[getBitcoinPrice] Fehler beim Abrufen des ₿itcoin-Kurses:', error);
callback(new Error('[getBitcoinPrice] Abruf fehlgeschlagen'), null);
});
}
// Funktion zum Formatieren der Kurswerte in Euro und Dollar
function formatCurrency(value, currency) {
console.log("[formatCurrency] Formatting value: ", value, " Currency: ", currency);
return value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' ' + currency;
}
// Funktion zum Formatieren von Datum und Zeit
function formatTime() {
var now = new Date();
var year = now.getFullYear();
var month = (now.getMonth() + 1).toString().padStart(2, '0');
var day = now.getDate().toString().padStart(2, '0');
var hours = now.getHours().toString().padStart(2, '0');
var minutes = now.getMinutes().toString().padStart(2, '0');
var formattedTime = year + '-' + month + '-' + day + '; ' + hours + ':' + minutes + ' Uhr';
console.log("[formatTime] Formatted time: ", formattedTime);
return formattedTime;
}
// Funktion zum Setzen des Bitcoin-Kurses im MediaWiki
function setBitcoinPrice() {
console.log("[setBitcoinPrice] Setting Bitcoin price...");
var placeholders = document.querySelectorAll('.bitcoin-price-placeholder');
if (placeholders.length === 0) {
console.warn("[setBitcoinPrice] Kein Platzhalter für Bitcoin-Kurs gefunden, Abbruch...");
return;
}
placeholders.forEach(function (placeholder) {
placeholder.textContent = 'Lade ₿itcoin-Kurs...';
});
getBitcoinPrice(function (error, data) {
if (!error && data && data.bitcoin) {
var bitcoinPriceUSD = data.bitcoin.usd;
var bitcoinPriceEUR = data.bitcoin.eur;
var currentTime = formatTime();
var formattedPrice = "<strong>₿itcoin-Kurs: $ " + formatCurrency(bitcoinPriceUSD, '') + " = " + formatCurrency(bitcoinPriceEUR, '€') + "</strong>";
formattedPrice += "<br><span style=\"font-size:10px; color: #555555;\">(Stand: " + currentTime + " von <a href='https://www.coingecko.com/' target='_blank' style='color:black;'>CoinGecko</a>; ohne Gewähr)</span>";
placeholders.forEach(function (placeholder) {
placeholder.innerHTML = formattedPrice;
});
} else {
console.error("[setBitcoinPrice] Fehler beim Abrufen des Bitcoin-Kurses, Daten nicht verfügbar");
placeholders.forEach(function (placeholder) {
placeholder.textContent = 'Fehler beim Abrufen des ₿itcoin-Kurses. Bitte später erneut versuchen.';
});
}
});
}
// Funktion zum Starten des täglichen Updates des Bitcoin-Kurses
function startDailyBitcoinUpdate() {
console.log("[startDailyBitcoinUpdate] Starting daily Bitcoin update...");
setBitcoinPrice();
var now = new Date();
var nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0);
var timeToMidnight = nextMidnight - now;
console.log("[startDailyBitcoinUpdate] Time to midnight: ", timeToMidnight);
setTimeout(function () {
console.log("[startDailyBitcoinUpdate] Updating Bitcoin price at midnight...");
setBitcoinPrice();
setInterval(setBitcoinPrice, 24 * 60 * 60 * 1000);
}, timeToMidnight);
}
// Funktion zum Anzeigen des Bitcoin-Kurses als Tooltip beim Überfahren des Logos
function addTooltipForLogo() {
function applyTooltipStyles() {
var tooltip = document.getElementById('bitcoin-price-tooltip');
if (tooltip) {
tooltip.style.position = 'absolute';
tooltip.style.display = 'none';
tooltip.style.border = '1px solid black';
tooltip.style.backgroundColor = '#696969';
tooltip.style.borderRadius = '5%';
tooltip.style.padding = '10px';
tooltip.style.color = 'white';
tooltip.style.zIndex = '1000';
tooltip.style.pointerEvents = 'none';
console.log("[applyTooltipStyles] Tooltip styles applied");
}
}
var logoElement = document.querySelector('#p-logo a, #p-logo img');
if (!logoElement) {
console.error('[addTooltipForLogo] Logo-Element nicht gefunden.');
return;
}
var tooltip = document.createElement('div');
tooltip.id = 'bitcoin-price-tooltip';
tooltip.className = 'bitcoin-tooltip';
document.body.appendChild(tooltip);
applyTooltipStyles();
logoElement.addEventListener('mouseenter', function(event) {
console.log("[addTooltipForLogo] Mouse entered logo");
if (cachedBitcoinData) {
updateTooltipContent(event, cachedBitcoinData);
tooltip.style.display = 'block';
} else {
getBitcoinPrice(function (error, data) {
if (!error && data) {
updateTooltipContent(event, data);
tooltip.style.display = 'block';
}
});
}
});
logoElement.addEventListener('mousemove', function(event) {
tooltip.style.left = (event.pageX + 10) + 'px';
tooltip.style.top = (event.pageY + 10) + 'px';
console.log("[addTooltipForLogo] Tooltip position updated");
});
logoElement.addEventListener('mouseleave', function() {
console.log("[addTooltipForLogo] Mouse left logo");
tooltip.style.display = 'none';
});
function updateTooltipContent(event, data) {
var bitcoinPriceUSD = data.bitcoin.usd;
var bitcoinPriceEUR = data.bitcoin.eur;
var tooltipContent = "<div style='text-align:center;'>" +
"<span style='font-size:18px; font-weight:bold;'>₿itcoin-Kurs: $ </span>" +
"<span style='font-size:18px; color:orange;'>" + bitcoinPriceUSD.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + "</span>" +
"<span style='font-size:18px; color:white; padding: 0 5px;'> = </span>" +
"<span style='font-size:18px; color:orange;'>" + bitcoinPriceEUR.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 }) + "</span>" +
"<span style='font-size:18px; font-weight:bold;'> € </span></div>" +
"<br><div style='font-size:10px; text-align:right;'>(Stand: " + lastUpdateTime + " von <a href='https://www.coingecko.com/' target='_blank' style='color:black;'>CoinGecko</a>; ohne Gewähr)</div>";
tooltip.innerHTML = tooltipContent;
tooltip.style.left = (event.pageX + 10) + 'px';
tooltip.style.top = (event.pageY + 10) + 'px';
console.log("[updateTooltipContent] Tooltip content updated");
}
}
// Starte das tägliche Update, um den Bitcoin-Kurs automatisch zu aktualisieren
startDailyBitcoinUpdate();
// Füge die Tooltip-Funktionalität zum Logo hinzu
addTooltipForLogo();
});
// *ENDE********************* Script zum täglichen Abruf und Einfügen des Bitcoin-Kurses 2024-10-05 *******************
// ********************************************************************************************************************
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// ********************************************************************************************************************
// *START************************** Übersetzungen mit Google-API OHNE iframe-Einbindungen *****************************
// ********************************************************************************************************************
// Google API Übersetzung Dropdown mit Benutzerspracheinstellungen
var selectImportant, selectAll;
// Funktion zur Erstellung der Dropdown-Menüs für wichtige und alle weiteren Sprachen
function createTranslateDropdowns() {
// Container für die Dropdown-Menüs erstellen
var container = document.createElement('div');
container.id = 'translate_container';
container.style.position = 'fixed'; // Fixierte Position, damit der Container immer sichtbar bleibt
container.style.top = '5px'; // Abstand vom oberen Rand des Fensters
container.style.left = '0px'; // Abstand vom linken Rand des Fensters
container.style.width = '95%'; // Passt die Breite des Containers an, um auf mobilen Geräten sichtbar zu sein
container.style.maxWidth = '380px'; // Maximale Breite für größere Bildschirme
container.style.height = 'auto'; // Höhe wird automatisch basierend auf dem Inhalt angepasst
container.style.backgroundColor = 'rgba(255, 255, 255, 0.8)'; // Halbtransparente Hintergrundfarbe für bessere Lesbarkeit
container.style.border = '1px solid #aaa'; // Rahmen um den Container in hellem Grau (#aaa)
container.style.padding = '5px'; // Innenabstand innerhalb des Containers (5px auf allen Seiten)
container.style.zIndex = '1000'; // Stellt sicher, dass der Container über anderen Elementen auf der Seite liegt
container.style.display = 'flex'; // Flexbox für einfaches Layout der Dropdown-Elemente
container.style.flexDirection = 'row'; // Elemente im Container werden horizontal nebeneinander angezeigt
container.style.flexWrap = 'wrap'; // Wenn die Breite nicht ausreicht, werden die Elemente umgebrochen
container.style.justifyContent = 'space-between'; // Die Dropdowns werden mit gleichem Abstand dazwischen verteilt
document.body.appendChild(container);
// Wrapper für die wichtigen Sprachen erstellen
var importantWrapper = document.createElement('div');
importantWrapper.style.display = 'inline-block';
importantWrapper.style.marginBottom = '0px'; // Kein Abstand nach unten
// Label für wichtige Sprachen erstellen
var importantLabel = document.createElement('label');
importantLabel.textContent = 'Translate to:'; // Beschriftung für das Dropdown-Menü
importantLabel.classList.add('no-translate'); // Klasse, damit das Label nicht übersetzt wird
importantLabel.style.fontWeight = 'bold'; // Fettschrift für bessere Lesbarkeit
importantLabel.style.marginRight = '2px'; // Abstand zwischen Label und Dropdown
importantLabel.style.fontSize = '0.8em';
importantLabel.style.fontFamily = 'Arial, sans-serif';
importantWrapper.appendChild(importantLabel);
// Dropdown für wichtige Sprachen erstellen
selectImportant = createDropdown('important_languages', 'Main Languages', getImportantLanguages());
selectImportant.style.width = '100px'; // Breite des Dropdowns
selectImportant.style.padding = '2px'; // Innenabstand innerhalb des Dropdowns
selectImportant.style.marginRight = '2px'; // Abstand zwischen Dropdown und weiteren Elementen
selectImportant.style.borderRadius = '1px'; // Leichte Abrundung der Ecken
selectImportant.style.fontFamily = 'Arial, sans-serif';
selectImportant.style.fontSize = '0.8em';
importantWrapper.appendChild(selectImportant);
container.appendChild(importantWrapper);
// Wrapper für alle Sprachen erstellen
var allWrapper = document.createElement('div');
allWrapper.style.display = 'inline-block';
allWrapper.style.marginLeft = '5px'; // Abstand links zwischen dem Wrapper und anderen Elementen
allWrapper.style.marginBottom = '0px'; // Kein Abstand nach unten
// Label für alle Sprachen erstellen
var allLabel = document.createElement('label');
allLabel.textContent = 'All:'; // Beschriftung für das Dropdown-Menü
allLabel.classList.add('no-translate'); // Klasse, damit das Label nicht übersetzt wird
allLabel.style.fontWeight = 'bold'; // Fettschrift für bessere Lesbarkeit
allLabel.style.marginRight = '2px'; // Abstand zwischen Label und Dropdown
allLabel.style.fontSize = '0.8em';
allLabel.style.fontFamily = 'Arial, sans-serif';
allWrapper.appendChild(allLabel);
// Dropdown für alle Sprachen erstellen
selectAll = createDropdown('all_languages', 'Select Of All', getAllLanguages());
selectAll.style.width = '115px'; // Breite des Dropdowns
selectAll.style.padding = '2px'; // Innenabstand innerhalb des Dropdowns
selectAll.style.marginRight = '4px'; // Abstand rechts vom Dropdown zum nächsten Element
selectAll.style.borderRadius = '2px'; // Leichte Abrundung der Ecken
selectAll.style.fontFamily = 'Arial, sans-serif';
selectAll.style.fontSize = '0.8em';
allWrapper.appendChild(selectAll);
container.appendChild(allWrapper);
// Event Listener für Sprachänderungen hinzufügen
selectImportant.addEventListener('change', function () {
if (selectImportant.value !== 'de') {
handleLanguageChange(selectImportant.value)
.then(function() {
localStorage.setItem('selectedLanguage', selectImportant.value); // Speichert die ausgewählte Sprache lokal
syncDropdowns(selectImportant.value); // Synchronisiert die Dropdowns
})
.catch(function(error) {
console.error("[selectImportant] Fehler bei der Sprachänderung: ", error);
});
} else {
resetToDefaultLanguage(); // Setzt die Sprache auf Deutsch zurück, wenn 'de' ausgewählt ist
}
});
selectAll.addEventListener('change', function () {
if (selectAll.value !== 'de') {
handleLanguageChange(selectAll.value)
.then(function() {
localStorage.setItem('selectedLanguage', selectAll.value); // Speichert die ausgewählte Sprache lokal
syncDropdowns(selectAll.value); // Synchronisiert die Dropdowns
})
.catch(function(error) {
console.error("[selectAll] Fehler bei der Sprachänderung: ", error);
});
} else {
resetToDefaultLanguage(); // Setzt die Sprache auf Deutsch zurück, wenn 'de' ausgewählt ist
}
});
}
// Erstellt ein Dropdown-Menü
function createDropdown(id, placeholderText, languages) {
var select = document.createElement('select');
select.id = id;
select.style.padding = '2px'; // Innenabstand des Dropdowns
select.style.marginRight = '4px'; // Abstand rechts vom Dropdown, um Platz zwischen Elementen zu schaffen
select.style.borderRadius = '0'; // Keine abgerundeten Ecken, um das Dropdown rechteckig zu halten
select.style.fontFamily = 'Arial, sans-serif'; // Konsistente Schriftart
select.style.fontSize = '0.8em'; // Schriftgröße reduziert für ein kompakteres Layout
// Platzhalteroption erstellen
var placeholderOption = document.createElement('option');
placeholderOption.value = '';
placeholderOption.textContent = placeholderText; // Platzhaltertext, der angezeigt wird, bevor eine Auswahl getroffen wird
placeholderOption.disabled = true; // Platzhalteroption kann nicht ausgewählt werden
placeholderOption.selected = true; // Platzhalter ist standardmäßig ausgewählt
select.appendChild(placeholderOption);
// Optionen für Sprachen hinzufügen
for (var i = 0; i < languages.length; i++) {
var lang = languages[i];
var option = document.createElement('option');
option.value = lang.lang;
option.textContent = lang.name; // Name der Sprache, die im Dropdown angezeigt wird
option.style.fontFamily = 'Arial, sans-serif';
option.style.fontSize = '1.0em';
select.appendChild(option);
}
return select;
}
// Wichtige Sprachen - Rückgabe einer Liste der wichtigsten Sprachen
function getImportantLanguages() {
return [
{ lang: 'de', name: '⟶ German (orig.)' },
{ lang: 'en', name: 'English' },
{ lang: 'es', name: 'Spanish' },
{ lang: 'zh-CN', name: 'Chinese (Simp.)' },
{ lang: 'fr', name: 'French' },
{ lang: 'pt', name: 'Portuguese' },
{ lang: 'ru', name: 'Russian' },
{ lang: 'ja', name: 'Japanese' },
{ lang: 'ko', name: 'Korean' },
{ lang: 'it', name: 'Italian' }
];
}
// Alle Sprachen - Rückgabe einer Liste aller unterstützten Sprachen
function getAllLanguages() {
return [
{ lang: 'af', name: 'Afrikaans' }, { lang: 'am', name: 'Amharic' }, { lang: 'ar', name: 'Arabic' },
{ lang: 'az', name: 'Azerbaijani' }, { lang: 'be', name: 'Belarusian' }, { lang: 'bg', name: 'Bulgarian' },
{ lang: 'bn', name: 'Bengali' }, { lang: 'bs', name: 'Bosnian' }, { lang: 'ca', name: 'Catalan' },
{ lang: 'zh-CN', name: 'Chinese (Simp.)' }, { lang: 'zh-TW', name: 'Chinese (Trad.)' },
{ lang: 'hr', name: 'Croatian' }, { lang: 'cs', name: 'Czech' }, { lang: 'da', name: 'Danish' },
{ lang: 'nl', name: 'Dutch' }, { lang: 'en', name: 'English' }, { lang: 'et', name: 'Estonian' },
{ lang: 'tl', name: 'Filipino' }, { lang: 'fi', name: 'Finnish' }, { lang: 'fr', name: 'French' },
{ lang: 'de', name: '⟶ German (orig.)' }, { lang: 'el', name: 'Greek' }, { lang: 'gu', name: 'Gujarati' },
{ lang: 'he', name: 'Hebrew' }, { lang: 'hi', name: 'Hindi' }, { lang: 'hu', name: 'Hungarian' },
{ lang: 'is', name: 'Icelandic' }, { lang: 'id', name: 'Indonesian' }, { lang: 'it', name: 'Italian' },
{ lang: 'ja', name: 'Japanese' }, { lang: 'kn', name: 'Kannada' }, { lang: 'ko', name: 'Korean' },
{ lang: 'lv', name: 'Latvian' }, { lang: 'lt', name: 'Lithuanian' }, { lang: 'ms', name: 'Malay' },
{ lang: 'ml', name: 'Malayalam' }, { lang: 'mr', name: 'Marathi' }, { lang: 'ne', name: 'Nepali' },
{ lang: 'no', name: 'Norwegian' }, { lang: 'fa', name: 'Persian' }, { lang: 'pl', name: 'Polish' },
{ lang: 'pt', name: 'Portuguese' }, { lang: 'pa', name: 'Punjabi' }, { lang: 'ro', name: 'Romanian' },
{ lang: 'ru', name: 'Russian' }, { lang: 'sr', name: 'Serbian' }, { lang: 'sk', name: 'Slovak' },
{ lang: 'sl', name: 'Slovenian' }, { lang: 'es', name: 'Spanish' }, { lang: 'sw', name: 'Swahili' },
{ lang: 'sv', name: 'Swedish' }, { lang: 'ta', name: 'Tamil' }, { lang: 'te', name: 'Telugu' },
{ lang: 'th', name: 'Thai' }, { lang: 'tr', name: 'Turkish' }, { lang: 'uk', name: 'Ukrainian' },
{ lang: 'ur', name: 'Urdu' }, { lang: 'vi', name: 'Vietnamese' }, { lang: 'cy', name: 'Welsh' },
{ lang: 'zu', name: 'Zulu' }
];
}
// Sprachänderungsfunktion mit Google Translate API
function handleLanguageChange(selectedLang) {
return new Promise(function(resolve, reject) {
var googleTranslateApiUrl = "https://translation.googleapis.com/language/translate/v2";
var apiKey = "AIzaSyBe4xJokSKneAvA_7_aaU984vS2R8gSFtM";
if (selectedLang !== "" && selectedLang !== "de") {
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
var nodes = [];
var node;
// Durchlaufen aller Textknoten im Dokument
while ((node = walker.nextNode())) {
if (node.nodeValue.trim() !== "") {
nodes.push(node); // Füge alle Textknoten hinzu, die nicht leer sind
}
}
var maxTextLength = 500; // Maximal erlaubte Textlänge für eine Anfrage
var textGroups = [];
var currentGroup = [];
var currentLength = 0;
// Gruppiere die Textknoten basierend auf der maximalen Textlänge
nodes.forEach(function (textNode) {
if (currentLength + textNode.nodeValue.length > maxTextLength) {
textGroups.push(currentGroup);
currentGroup = [];
currentLength = 0;
}
currentGroup.push(textNode);
currentLength += textNode.nodeValue.length;
});
if (currentGroup.length > 0) {
textGroups.push(currentGroup);
}
// Sende jede Textgruppe zur Übersetzung
var groupPromises = textGroups.map(function (group) {
var textToTranslateArray = group.map(function (textNode) {
return textNode.nodeValue;
});
return fetch(googleTranslateApiUrl + "?key=" + apiKey, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
q: textToTranslateArray,
source: "de",
target: selectedLang,
format: "text"
})
}).then(function (response) {
if (!response.ok) {
throw new Error("Netzwerkantwort war nicht ok");
}
return response.json();
}).then(function (data) {
if (data.data && data.data.translations) {
for (var i = 0; i < data.data.translations.length; i++) {
group[i].nodeValue = " " + data.data.translations[i].translatedText + " "; // Leerzeichen erhalten
}
console.log("Übersetzung erfolgreich");
} else {
throw new Error("Fehlerhafte Antwort von der Übersetzungs-API");
}
});
});
Promise.all(groupPromises).then(function() {
resolve();
}).catch(function (error) {
console.error("Fehler bei der Übersetzung: ", error);
reject(error);
});
} else {
resolve();
}
});
}
// Setzt die Sprache zurück auf die Standardsprache Deutsch
function resetToDefaultLanguage() {
localStorage.removeItem('selectedLanguage'); // Entfernt die gespeicherte Sprache
window.location.reload(); // Lädt die Seite neu, um die Standard-Sprache anzuwenden
}
// Synchronisiert beide Dropdowns
function syncDropdowns(selectedLang) {
if (selectImportant) selectImportant.value = selectedLang;
if (selectAll) selectAll.value = selectedLang;
}
// Google Translate Script laden und Dropdown-Menü erstellen
function initializeTranslationFeature() {
createTranslateDropdowns(); // Erstellt die Dropdown-Menüs
var savedLanguage = localStorage.getItem('selectedLanguage'); // Holt die gespeicherte Sprache
if (savedLanguage && savedLanguage !== 'de') {
handleLanguageChange(savedLanguage).then(function() {
syncDropdowns(savedLanguage); // Synchronisiert die Dropdowns
}).catch(function(error) {
console.error("[initializeTranslationFeature] Fehler bei der Sprachänderung: ", error);
});
} else {
syncDropdowns('de'); // Setzt die Dropdowns auf die Standardsprache
}
}
// Document ready
$(document).ready(function () {
document.body.style.paddingTop = '75px'; // Abstand vom oberen Rand der Seite, um sicherzustellen, dass der Inhalt nicht überlagert wird
initializeTranslationFeature(); // Initialisiert die Übersetzungsfunktion
});
// *ENDE*************************** Übersetzungen mit Google-API OHNE iframe-Einbindungen *****************************
// ********************************************************************************************************************
// ********************************************************************************************************************
// *START********************* Script zum dynamischen Aufbau des E-Mail-Share-Links 2025-05-28 *********************
mw.loader.using('mediawiki.util', function() {
$(function(){
var title = mw.config.get('wgTitle'),
page = mw.config.get('wgPageName'),
server = mw.config.get('wgServer'),
script = mw.config.get('wgScriptPath'),
fullUrl = server + script + '/index.php?title=' + encodeURIComponent(page),
subject = encodeURIComponent(
'Entdecke den tollen Bitcoin-Wiki-Artikel zum Thema »' + title + '«'
),
body = encodeURIComponent(
'Hey, du Krypto-Fan! Entdecke jetzt den tollen Bitcoin-Wiki-Artikel zum Thema »'
+ title
+ '« unter '
+ fullUrl
),
mailto = 'mailto:info@example.org?subject=' + subject + '&body=' + body,
link = '<a href="' + mailto + '">Jetzt per E-Mail teilen✉</a>';
$('#share-email-placeholder').html(link);
});
});
// *ENDE********************* Script zum dynamischen Aufbau des E-Mail-Share-Links 2025-05-28 *******************
// ********************************************************************************************************************
/**
* Unmined-Bitcoin-Skript für MediaWiki:Common.js
*
* Dieses Skript holt alle fünf Minuten die aktuelle Anzahl der bereits geminten Bitcoins von der CoinGecko-API (serverseitig via PHP-Proxy)
* ab, berechnet daraus die verbleibende Anzahl bis zur Obergrenze von 21 000 000, formatiert das Ergebnis in deutscher Schreibweise
* mit Punkten als Tausender-Trennzeichen und gibt es in einem DIV-Container im Wiki-Artikel aus.
*
* Ablauf:
* 1. Sobald der DOM fertig geladen ist (jQuery(document).ready), wird geprüft, ob der DIV-Container mit der ID "unmined-btc-widget" existiert.
* Ist der Container nicht vorhanden, bricht das Skript ab.
*
* 2. Über die Funktion fetchRemainingBTC() wird via jQuery.ajax eine JSON-Anfrage an den PHP-Proxy auf Deinem Server gestellt:
* URL: https://test.toepperwien.de/unmined_bitcoin/unmined_btc.php
*
* Der PHP-Proxy:
* • Ruft serverseitig die CoinGecko-API ab (https://api.coingecko.com/api/v3/coins/bitcoin).
* • Berechnet auf dem Server den verbleibenden Bitcoin-Betrag (21 000 000 − circulating_supply) und gibt ein JSON zurück:
* {
* "circulating_supply": 19xxxxxx,
* "remaining": 1xxxxxx
* }
* • Setzt in der HTTP-Antwort zwingend den Header Access-Control-Allow-Origin: *,
* damit der Browser keine CORS-Fehler wirft.
*
* 3. Im success-Callback von jQuery.ajax wird das zurückgelieferte JSON geparst. Falls das JSON ein Feld "error" enthält, wird
* „Fehler: <Fehlermeldung>“ ins DIV geschrieben. Andernfalls werden die Felder "circulating_supply" und "remaining" ausgelesen.
* Der Wert „remaining“ wird dann in deutscher Tausender-Notation formatiert und in das DIV geschrieben als:
* Noch <formatted> Bitcoin
*
* 4. Sollte die AJAX-Anfrage (z. B. beim Netzwerkausfall) scheitern, wird im error-Callback „Fehler beim Laden“ angezeigt.
*
* 5. Direkt nach DOM-ready wird im DIV zunächst „Lade Daten…“ angezeigt, dann der erste Aufruf von fetchRemainingBTC() durchgeführt.
* Mit setInterval(fetchRemainingBTC, 10 * 60 * 1000) wird das Skript alle 10 Minuten (300 000 ms) erneut ausgeführt.
*/
jQuery(function () {
// Debug: Prüfen, ob Common.js geladen wird
console.log('[UnminedBTC] Starting fetch script');
// 1) Container per jQuery abrufen
// Dieser DIV muss im Artikel stehen: <div id="unmined-btc-widget">Lade Daten…</div>
var $container = jQuery('#unmined-btc-widget');
if ($container.length === 0) {
// Falls das DIV nicht existiert, brechen wir ab
console.warn('[UnminedBTC] Kein Container #unmined-btc-widget gefunden, Abbruch.');
return;
}
// 2) Konstanten: Maximale Gesamtmenge aller Bitcoins
var TOTAL_SUPPLY = 21000000;
/**
* formatThousand(num)
*
* Formatiert eine Zahl in deutsche Tausender-Notation mit Punkten als Trennzeichen.
* Beispiel: 2500000 → "2.500.000"
*
* @param {number} num - Die zu formatierende Zahl (wird auf ganze Zahl abgerundet).
* @return {string} - Die formatierte Zahl als String mit Punkt-Trennungen.
*/
function formatThousand(num) {
// Ganzzahl-String erzeugen
var s = Math.floor(num).toString();
var parts = [];
var rest = s;
// Solange mehr als drei Ziffern übrig sind, letzten drei Ziffern extrahieren
while (rest.length > 3) {
var idx = rest.length - 3;
parts.unshift(rest.slice(idx)); // Letzte 3 Ziffern ins Array vorne einfügen
rest = rest.slice(0, idx); // Rest ohne die letzten 3 Ziffern
}
parts.unshift(rest); // Übrigen Teil (1–3 Ziffern) ebenfalls ins Array einfügen
// Array mit Punkten verbinden, z. B. ["1", "126", "085"] → "1.126.085"
return parts.join('.');
}
/**
* fetchRemainingBTC()
*
* Ruft per AJAX den eigenen PHP-Proxy auf, der serverseitig die CoinGecko-API
* abfragt und die verbleibenden Bitcoins bis 21 000 000 berechnet. Anschließend
* wird das Ergebnis ins DIV-Element (ID "unmined-btc-widget") geschrieben.
*
* Der PHP-Proxy liefert ein JSON-Objekt in folgender Struktur:
* {
* "circulating_supply": <Number>,
* "remaining": <Number>
* }
*
* Oder bei Fehlern:
* {
* "error": "<Fehlermeldung>"
* }
*/
function fetchRemainingBTC() {
// 4a) URL zu deinem PHP-Proxy auf deinem Server
// Der Proxy muss CORS erlauben (Access-Control-Allow-Origin: *) und gibt JSON zurück.
var url = 'https://test.toepperwien.de/unmined_bitcoin/unmined_btc.php';
console.log('[UnminedBTC] AJAX-Request an URL:', url);
// 4b) AJAX-Aufruf mit jQuery.ajax (ES5)
jQuery.ajax({
url: url,
dataType: 'json', // Erwartet JSON-Antwort
timeout: 5000, // Timeout nach 5 Sekunden
success: function (data) {
console.log('[UnminedBTC] AJAX success, rohes JSON:', data);
// 4c) Prüfen, ob der Proxy einen Fehler zurückliefert
if (data.error) {
console.error('[UnminedBTC] PHP-Proxy-Fehler:', data.error);
// Zeige Fehlermeldung im DIV an
$container.text('Fehler: ' + data.error);
return;
}
// 4d) Auslesen von remaining und circulating_supply
var remaining = data.remaining;
var circ = data.circulating_supply;
console.log('[UnminedBTC] circulating_supply:', circ, 'remaining:', remaining);
// 4e) Validierung: remaining muss eine Zahl sein
if (typeof remaining !== 'number' || isNaN(remaining)) {
$container.text('Fehler: ungültige Daten');
return;
}
// 4f) Ausgabe im DIV: "<formatted> Bitcoin"
// Beispiel: "Noch 1.126.085 Bitcoin"
var formatted = formatThousand(remaining);
$container.text(formatted);
},
error: function (xhr, status, error) {
console.error('[UnminedBTC] AJAX-Fehler:', status, error);
// Zeige generische Fehlermeldung im DIV
$container.text('Fehler beim Laden');
}
});
}
// 5) Initialisierung: Zuerst "Lade Daten…" ins DIV schreiben
$container.text('Lade Daten…');
// 5a) Ersten Abruf sofort starten
fetchRemainingBTC();
// 5b) Alle 10 Minuten (10 * 60 * 1000 ms) erneut abrufen
setInterval(fetchRemainingBTC, 10 * 60 * 1000);
});
// --------------------------------------------------------------------------------------------------------------------
/* Anonymer Song-Zähler + Cover-Play (ohne Cookies/PII) */
(function () {
function ajaxGET(url, done) {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var ok = (xhr.status >= 200 && xhr.status < 300);
done(ok, ok ? xhr.responseText : null);
}
};
xhr.send(null);
} catch (e) { done(false, null); }
}
function initSongBlocks() {
var wrappers = document.querySelectorAll ? document.querySelectorAll('.wiki-song') : [];
var playedOnce = window.__wikiSongPlayedOnce || (window.__wikiSongPlayedOnce = {});
for (var i = 0; i < wrappers.length; i++) {
(function (wrap) {
var id = wrap.getAttribute('data-song-id') || '';
var audio = wrap.querySelector ? wrap.querySelector('.wiki-song-audio') : null;
var countEl = wrap.querySelector ? wrap.querySelector('[data-song-count]') : null;
var coverBtn = wrap.querySelector ? wrap.querySelector('.wiki-song-cover-play') : null;
// 1) aktuellen Zählerstand laden
if (id && countEl) {
ajaxGET('/audio/count.php?song=' + encodeURIComponent(id), function (ok, text) {
if (!ok || !text) { return; }
try {
var ob = JSON.parse(text);
if (ob && typeof ob.plays === 'number') { countEl.textContent = String(ob.plays); }
} catch (e) {}
});
}
// 2) einmalig bumpen beim ersten Play
if (id && audio) {
audio.addEventListener('play', function () {
wrap.classList && wrap.classList.add('is-playing');
if (!playedOnce[id]) {
playedOnce[id] = true;
ajaxGET('/audio/bump.php?song=' + encodeURIComponent(id), function () {});
if (countEl) {
var n = parseInt(countEl.textContent, 10);
if (!isNaN(n)) { countEl.textContent = String(n + 1); }
}
}
}, false);
audio.addEventListener('pause', function () { wrap.classList && wrap.classList.remove('is-playing'); }, false);
audio.addEventListener('ended', function () { wrap.classList && wrap.classList.remove('is-playing'); }, false);
}
// 3) Cover-Button steuert Play/Pause
if (coverBtn && audio) {
coverBtn.addEventListener('click', function () {
try {
if (audio.paused) { audio.play(); } else { audio.pause(); }
} catch (e) {}
}, false);
}
})(wrappers[i]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSongBlocks, false);
} else {
initSongBlocks();
}
})();
/* Inline-Anzeige der Song-Aufrufe hinter "Wiki-Song" */
(function () {
function ajaxGET(url, done) {
try {
var x = new XMLHttpRequest();
x.open('GET', url, true);
x.onreadystatechange = function(){ if (x.readyState === 4) done(x.status>=200&&x.status<300, x.responseText); };
x.send(null);
} catch (e) { done(false, null); }
}
function updateInlineCounts() {
var nodes = document.querySelectorAll ? document.querySelectorAll('.wiki-song-inline-count') : [];
if (!nodes.length) return;
// ggf. mehrere IDs auf einer Seite unterstützen
var byId = {};
for (var i=0;i<nodes.length;i++) {
var id = nodes[i].getAttribute('data-song-id') || '';
if (!id) continue;
(byId[id] = byId[id] || []).push(nodes[i]);
}
for (var id in byId) if (byId.hasOwnProperty(id)) {
(function(songId, targets){
ajaxGET('/audio/count.php?song=' + encodeURIComponent(songId), function(ok, text){
var n = 0;
if (ok && text) { try { var ob = JSON.parse(text); if (ob && typeof ob.plays === 'number') n = ob.plays|0; } catch(e){} }
for (var j = 0; j < targets.length; j++) {
var el = targets[j].querySelector ? targets[j].querySelector('.count') : null;
if (el) {
el.textContent = String(n); // nur die Zahl austauschen
} else {
// Fallback, falls mal kein .count drin ist:
targets[j].innerHTML = '<em>Bislang ' + String(n) + ' Aufrufe</em>';
}
}
});
})(id, byId[id]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', updateInlineCounts, false);
} else {
updateInlineCounts();
}
})();
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------