MediaWiki:Common.js: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| (108 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
/* Das folgende JavaScript wird für alle Benutzer geladen. */ | /* Das folgende JavaScript wird für alle Benutzer geladen. */ | ||
/* ADOS Whisky-Ratings – RatePage Frontend (ES5, Widgets + Stats + Summary, Doppel-Init-Schutz; ANON VOTING ERLAUBT) */ | /* ADOS Whisky-Ratings – RatePage Frontend (ES5, Widgets + Stats + Summary, Doppel-Init-Schutz; ANON VOTING ERLAUBT) */ | ||
mw.loader.using(['mediawiki.api', 'mediawiki.user']).then(function () { | mw.loader.using(['mediawiki.api', 'mediawiki.user']).then(function () { | ||
| Zeile 395: | Zeile 396: | ||
}); | }); | ||
| Zeile 457: | Zeile 454: | ||
/* ========== Kategorien rekursiv einsammeln (inkl. Subkats, Namespaces) ========== */ | /* ========== Kategorien rekursiv einsammeln (inkl. Subkats, Namespaces) ========== */ | ||
function fetchCategoryMembersRecursiveSingleResolved(api, catTitle, limit, outSet, pages, nsStr){ | function fetchCategoryMembersRecursiveSingleResolved(api, catTitle, limit, outSet, pages, nsStr){ | ||
var visited = {}, queue = [catTitle]; | var visited = {}, queue = [catTitle]; | ||
| Zeile 467: | Zeile 463: | ||
list: 'categorymembers', | list: 'categorymembers', | ||
cmtitle: title, | cmtitle: title, | ||
cmtype: 'page|subcat', | cmtype: 'page|subcat', | ||
cmlimit: Math.min(200, limit), | cmlimit: Math.min(200, limit), | ||
| Zeile 576: | Zeile 571: | ||
/* ========== Render ========== */ | /* ========== Render ========== */ | ||
function renderTopN(container, rows, N, minVotes) { | function renderTopN(container, rows, N, minVotes) { | ||
var keep = (container.getAttribute && container.getAttribute('data-keep-status') === 'true'); | var keep = (container.getAttribute && container.getAttribute('data-keep-status') === 'true'); | ||
var statusBox = keep ? container.querySelector('.whisky-top5__status') : null; | var statusBox = keep ? container.querySelector('.whisky-top5__status') : null; | ||
| Zeile 636: | Zeile 630: | ||
container.setAttribute('data-top5-init','1'); | container.setAttribute('data-top5-init','1'); | ||
var rawCats = container.getAttribute('data-categories') || container.getAttribute('data-category') || ''; | var rawCats = container.getAttribute('data-categories') || container.getAttribute('data-category') || ''; | ||
var parts = rawCats.split(/\n|;/), rootCats = [], i; | var parts = rawCats.split(/\n|;/), rootCats = [], i; | ||
| Zeile 646: | Zeile 639: | ||
var minVotes = parseInt(container.getAttribute('data-min-votes') || '1', 10); | var minVotes = parseInt(container.getAttribute('data-min-votes') || '1', 10); | ||
var includeHidden = (container.getAttribute('data-include-hidden') === 'true'); | var includeHidden = (container.getAttribute('data-include-hidden') === 'true'); | ||
var nsStr = container.getAttribute('data-namespaces') || '0|14'; | var nsStr = container.getAttribute('data-namespaces') || '0|14'; | ||
var rawC = container.getAttribute('data-contests') || 'NASE,GESCHMACK,ABGANG,GESAMTEINDRUCK'; | var rawC = container.getAttribute('data-contests') || 'NASE,GESCHMACK,ABGANG,GESAMTEINDRUCK'; | ||
| Zeile 657: | Zeile 650: | ||
status('Sammle Seiten …'); | status('Sammle Seiten …'); | ||
fetchCategoryMembersRecursiveMulti(rootCats, lim, status, nsStr).then(function(members){ | fetchCategoryMembersRecursiveMulti(rootCats, lim, status, nsStr).then(function(members){ | ||
status('Gefundene Seiten gesamt: ' + (members ? members.length : 0) + ' – lade Bewertungen …', true); | status('Gefundene Seiten gesamt: ' + (members ? members.length : 0) + ' – lade Bewertungen …', true); | ||
| Zeile 702: | Zeile 693: | ||
}); | }); | ||
| Zeile 711: | Zeile 700: | ||
var el = this, val = parseFloat(el.getAttribute('data-rating') || '0'); | var el = this, val = parseFloat(el.getAttribute('data-rating') || '0'); | ||
if (isNaN(val)) val = 0; | if (isNaN(val)) val = 0; | ||
val = Math.max(0, Math.min(5, val)); | val = Math.max(0, Math.min(5, val)); | ||
el.style.setProperty('--stars', (val).toString()); | el.style.setProperty('--stars', (val).toString()); | ||
el.setAttribute('aria-label', val + ' von 5 Sternen'); | el.setAttribute('aria-label', val + ' von 5 Sternen'); | ||
| Zeile 720: | Zeile 707: | ||
}); | }); | ||
// Force light color-scheme at document level (helps Mobile Safari) | // Force light color-scheme at document level (helps Mobile Safari) | ||
| Zeile 737: | Zeile 722: | ||
// - | function isFlagTrue(v){ | ||
if (v == null) return false; | |||
v = String(v).trim().toLowerCase(); | |||
return v === 'true' || v === '1' || v === 'yes'; | |||
} | |||
// Gesamtzahl unter der Legende einfügen (im Diagramm-Block, nicht im Canvas!) | |||
function addTotalBelowLegend(chart, block) { | |||
try { | |||
if (!chart || !block) return; | |||
const total = chart.data.datasets.reduce((sum, ds) => | |||
sum + (ds.data || []).reduce((a, b) => a + (parseFloat(b) || 0), 0) | |||
, 0); | |||
const oldInfo = block.querySelector(':scope > .chart-total-info'); | |||
if (oldInfo) oldInfo.remove(); | |||
const info = document.createElement('div'); | |||
info.className = 'chart-total-info'; | |||
info.textContent = 'Gesamte Anzahl aller eigenen Abfüllungen: ' + total; | |||
info.style.textAlign = 'center'; | |||
info.style.fontWeight = 'bold'; | |||
info.style.fontSize = '1.05em'; | |||
info.style.marginTop = '0.5rem'; | |||
info.style.marginBottom = '0.5rem'; | |||
info.style.color = '#444'; | |||
block.appendChild(info); | |||
} catch(e) { console.warn('[addTotalBelowLegend]', e); } | |||
. | |||
} | } | ||
/* === ADOS Multi-Serien-Chart (Chart.js) ============================= * | |||
* Lädt Chart.js sicher (asynchron) und baut Diagramme aus Cargo-Tabellen. | |||
var | * Benötigt im Artikel: <div class="ados-chart-multi"> + Cargo-Query als |format=table | ||
* ==================================================================== */ | |||
(function () { | |||
var _chartReady = null; | |||
function ensureChartJS() { | |||
if (_chartReady) return _chartReady; | |||
_chartReady = new Promise(function (resolve, reject) { | |||
if (window.Chart) return resolve(); | |||
var s = document.createElement('script'); | |||
s.src = 'https://cdn.jsdelivr.net/npm/chart.js'; | |||
s.async = true; | |||
s.onload = function(){ resolve(); }; | |||
s.onerror = function(){ console.error('Chart.js konnte nicht geladen werden'); reject(); }; | |||
document.head.appendChild(s); | |||
}); | |||
return _chartReady; | |||
} | |||
var ADOS_COLORS = { | |||
'A Dream of Scotland': '#C2410C', | |||
'A Dream of Ireland': '#15803D', | |||
'A Dream of... – Der Rest der Welt': '#1D4ED8', | |||
'Friendly Mr. Z': '#9333EA', | |||
'Die Whisky Elfen': '#0891B2', | |||
'The Fine Art of Whisky': '#CA8A04' | |||
}; | |||
var COLOR_CYCLE = ['#2563eb','#16a34a','#f97316','#dc2626','#a855f7','#0ea5e9','#f59e0b','#10b981']; | |||
function toYear(x){ | |||
var n = parseInt(String(x).replace(/[^\d]/g,''),10); | |||
return isFinite(n) ? n : null; | |||
} | |||
function getColor(name, used){ | |||
if (ADOS_COLORS[name]) return ADOS_COLORS[name]; | |||
var i = used.size % COLOR_CYCLE.length; | |||
used.add(name); | |||
return COLOR_CYCLE[i]; | |||
} | |||
function buildDatasetsFromTable(tbl){ | |||
var rows = Array.from(tbl.querySelectorAll('tr')); | |||
if (rows.length < 2) return { labels:[], datasets:[] }; | |||
var yearsSet = new Set(); | |||
var bySeries = new Map(); | |||
var | rows.slice(1).forEach(function(tr){ | ||
var | var tds = tr.querySelectorAll('td,th'); | ||
if (tds.length < 3) return; | |||
var y = toYear(tds[0].textContent.trim()); | |||
var s = tds[1].textContent.trim(); | |||
var v = parseFloat(tds[2].textContent.replace(',','.')) || 0; | |||
if (y == null) return; | |||
yearsSet.add(y); | |||
if (!bySeries.has(s)) bySeries.set(s, new Map()); | |||
bySeries.get(s).set(y, v); | |||
}); | |||
var years = Array.from(yearsSet).sort(function(a,b){return a-b;}); | |||
var used = new Set(); | |||
var labels = years.map(String); | |||
var | var datasets = Array.from(bySeries.entries()).map(function(entry){ | ||
var | var name = entry[0], yearMap = entry[1]; | ||
var data = years.map(function(y){ return yearMap.get(y) || 0; }); | |||
var color = getColor(name, used); | |||
return { | |||
label: name, | |||
data: data, | |||
borderColor: color, | |||
backgroundColor: color + '80', | |||
tension: 0.25, | |||
pointRadius: 3 | |||
}; | |||
}); | |||
return { labels: labels, datasets: datasets }; | |||
} | |||
function renderOne(block){ | |||
if (block.dataset.rendered === '1') return; | |||
var el = block.nextElementSibling, tbl = null, wrapToHide = null; | |||
while (el) { | |||
if (/^H[1-6]$/.test(el.tagName) || (el.classList && el.classList.contains('ados-chart-multi'))) break; | |||
if (el.tagName === 'TABLE') { | |||
tbl = el; | |||
} else if (el.querySelector) { | |||
var t = el.querySelector('table'); | |||
if (t) tbl = t; | |||
} | } | ||
if ( | if (tbl) { | ||
wrapToHide = tbl.parentElement; | |||
break; | |||
} | } | ||
el = el.nextElementSibling; | |||
} | |||
if (!tbl) return; | |||
var out = buildDatasetsFromTable(tbl); | |||
if (!out.labels.length || !out.datasets.length) return; | |||
var hide = (block.dataset.hideTable || '').toLowerCase() === 'true'; | |||
if (hide) { | |||
var onlyTable = wrapToHide && wrapToHide.children.length === 1 && wrapToHide.firstElementChild === tbl; | |||
if (onlyTable) { | |||
wrapToHide.setAttribute('aria-hidden','true'); | |||
wrapToHide.style.display = 'none'; | |||
} else { | |||
tbl.setAttribute('aria-hidden','true'); | |||
tbl.style.display = 'none'; | |||
} | |||
} | } | ||
} | |||
var wrap = document.createElement('div'); | |||
wrap.style.position = 'relative'; | |||
wrap.style.width = '100%'; | |||
wrap.style.height = block.dataset.height || (window.matchMedia('(min-width:768px)').matches ? '450px' : '300px'); | |||
var canvas = document.createElement('canvas'); | |||
wrap.appendChild(canvas); | |||
block.innerHTML = ''; | |||
block.appendChild(wrap); | |||
var type = (block.dataset.type || 'line').toLowerCase(); | |||
var title = block.dataset.title || ''; | |||
var cumulative = (block.dataset.cumulative || '').toLowerCase() === 'true'; | |||
if (cumulative) { | |||
out.datasets = out.datasets.map(function(ds){ | |||
var acc = 0; | |||
return Object.assign({}, ds, { | |||
data: ds.data.map(function(v){ acc += v; return acc; }) | |||
}); | |||
}); | |||
} | |||
ensureChartJS().then(function(){ | |||
const chart = new Chart(canvas.getContext('2d'), { | |||
type: type, | |||
data: { labels: out.labels, datasets: out.datasets }, | |||
options: { | |||
responsive: true, | |||
maintainAspectRatio: false, | |||
interaction: { mode: 'nearest', intersect: false }, | |||
plugins: { | |||
legend: { position: 'bottom', labels: { font: { size: 13 }, boxWidth: 20 } }, | |||
title: { display: !!title, text: title, font: { size: 16 } }, | |||
tooltip:{ backgroundColor: 'rgba(0,0,0,0.8)', titleFont: {size:14}, bodyFont: {size:13} } | |||
}, | |||
scales: { | |||
x: { ticks: { font: { size: 12 } } }, | |||
y: { beginAtZero: true, ticks: { precision: 0, font: { size: 12 } } } | |||
} | |||
} | } | ||
}); | |||
const hideTotal = (block.dataset.hideTotal || '').toLowerCase() === 'true'; | |||
const oldInfo = block.querySelector(':scope > .chart-total-info'); | |||
if (oldInfo) oldInfo.remove(); | |||
if (!hideTotal) { | |||
addTotalBelowLegend(chart, block); | |||
if (window.ResizeObserver) { | |||
const obs = new ResizeObserver(() => addTotalBelowLegend(chart, block)); | |||
obs.observe(chart.canvas); | |||
chart.$adosTotalObserver = obs; | |||
} | } | ||
} | |||
block.dataset.rendered = '1'; | |||
}); | |||
} | |||
function boot($scope){ | |||
var blocks = ($scope && $scope[0] ? $scope[0] : document).querySelectorAll('.ados-chart-multi'); | |||
if (!blocks.length) return; | |||
ensureChartJS().then(function(){ blocks.forEach(renderOne); }); | |||
} | |||
if (window.mw && mw.hook) { | |||
mw.hook('wikipage.content').add(boot); | |||
} else { | |||
(document.readyState === 'loading') | |||
? document.addEventListener('DOMContentLoaded', function(){ boot(); }) | |||
: boot(); | |||
} | |||
})(); | |||
// ==========================Scan================================== | |||
mw.loader.using('mediawiki.util').then(function () { | |||
if (mw.config.get('wgPageName') !== 'LabelScan') return; | |||
mw.loader.load('/index.php?title=MediaWiki:Gadget-LabelScan.js&action=raw&ctype=text/javascript'); | |||
mw.loader.load('/index.php?title=MediaWiki:Gadget-LabelScan.css&action=raw&ctype=text/css', 'text/css'); | |||
}); | |||
// ==========================ScanApp================================== | |||
/* ==== PWA: Manifest + Service Worker + Install-Button (ES5) ==== */ | |||
/* Manifest einbinden */ | |||
(function () { | |||
var link = document.createElement("link"); | |||
link.rel = "manifest"; | |||
link.href = "/app/labelscan/manifest.webmanifest"; | |||
document.head.appendChild(link); | |||
})(); | |||
/* Service Worker registrieren (nur wenn vorhanden) */ | |||
(function () { | |||
if ("serviceWorker" in navigator) { | |||
navigator.serviceWorker.register("/app/labelscan/sw.js")["catch"](function () {}); | |||
} | |||
})(); | |||
/* Install-Button steuern (Button-ID: ados-install) */ | |||
(function () { | |||
var installPrompt = null; | |||
window.addEventListener("beforeinstallprompt", function (e) { | |||
try { e.preventDefault(); } catch (ex) {} | |||
installPrompt = e; | |||
var btn = document.getElementById("ados-install"); | |||
if (btn) btn.style.display = "inline-block"; | |||
}); | |||
function onReady(fn){ if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", fn); else fn(); } | |||
onReady(function () { | |||
var btn = document.getElementById("ados-install"); | |||
if (!btn) return; | |||
btn.addEventListener("click", function () { | |||
if (!installPrompt) return; | |||
try { installPrompt.prompt(); } catch (ex) {} | |||
installPrompt = null; | |||
btn.style.display = "none"; | |||
}); | |||
}); | |||
})(); | |||
if ('serviceWorker' in navigator) { | |||
navigator.serviceWorker.register('/app/labelscan/sw.js').catch(function(){}); | |||
} | |||
// ============================================================ | |||
mw.loader.using('mediawiki.util').then(function () { | |||
function checkNeuBadges() { | |||
var badges = document.querySelectorAll('.ados-neu-badge'); | |||
var now = new Date(); | |||
badges.forEach(function (badge) { | |||
var expiry = badge.getAttribute('data-expiry'); | |||
if (!expiry) return; | |||
var expiryDate = new Date(expiry + "T23:59:59"); | |||
if (now > expiryDate) { | |||
badge.style.display = "none"; | |||
} | } | ||
}); | |||
} | |||
if (document.readyState === "loading") { | |||
document.addEventListener("DOMContentLoaded", checkNeuBadges); | |||
} else { | |||
checkNeuBadges(); | |||
} | } | ||
}); | }); | ||
// ============================================================ | |||
mw.loader.load('/wiki/MediaWiki:WhiskybaseBatch.js?action=raw&ctype=text/javascript'); | |||