MediaWiki:Common.js: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| Zeile 608: | Zeile 608: | ||
/* --- Whisky Top-5 ( | /* --- Whisky Top-5 (multi-root, recursive, with self-diagnostics) --------- */ | ||
mw.loader.using(['mediawiki.api']).then(function () { | mw.loader.using(['mediawiki.api']).then(function () { | ||
function get(obj, path) { | function get(obj, path) { | ||
var cur = obj, i; | var cur = obj, i; | ||
for (i = 0; i < path.length; i++) { | for (i = 0; i < path.length; i++) { if (!cur || typeof cur !== 'object') return; cur = cur[path[i]]; } | ||
return cur; | return cur; | ||
} | } | ||
function parseWeights(raw, contests) { | function parseWeights(raw, contests) { | ||
var map = {}, parts = (raw || '').split(','), i; | var map = {}, parts = (raw || '').split(','), i; | ||
for (i = 0; i < parts.length; i++) { | for (i = 0; i < parts.length; i++) { | ||
var kv = parts[i].split(':'); | var kv = parts[i].split(':'); if (kv.length !== 2) continue; | ||
var k = kv[0].replace(/^\s+|\s+$/g, ''), v = parseFloat(kv[1]); | |||
if (!isNaN(v)) map[k] = v; | |||
} | } | ||
for (i = 0; i < contests.length; i++) if (typeof map[contests[i]] !== 'number') map[contests[i]] = 1; | |||
return map; | return map; | ||
} | } | ||
function fetchCategoryMembersRecursiveSingle(rootCat, limit, outSet, pages) { | function fetchCategoryMembersRecursiveSingle(rootCat, limit, outSet, pages) { | ||
var api = new mw.Api(); | var api = new mw.Api(); | ||
| Zeile 645: | Zeile 634: | ||
function fetchOne(catTitle, cmcontinue) { | function fetchOne(catTitle, cmcontinue) { | ||
var | var p = { | ||
action: 'query', | action: 'query', list: 'categorymembers', | ||
cmtitle: catTitle, cmnamespace: '0|14', cmtype: 'page|subcat', | |||
cmtitle: catTitle, | cmlimit: Math.min(200, limit), format: 'json' | ||
cmlimit: Math.min(200, limit), | |||
}; | }; | ||
if (cmcontinue) | if (cmcontinue) p.cmcontinue = cmcontinue; | ||
return api.get( | return api.get(p).then(function (d) { | ||
var cms = get( | var cms = get(d, ['query', 'categorymembers']) || [], i, it; | ||
for (i = 0; i < cms.length; i++) { | for (i = 0; i < cms.length; i++) { | ||
it = cms[i]; | it = cms[i]; | ||
if (it.ns === 0) { | if (it.ns === 0) { | ||
var pid = String(it.pageid); | var pid = String(it.pageid); | ||
if (!outSet[pid] && pages.length < limit) { | if (!outSet[pid] && pages.length < limit) { outSet[pid] = true; pages.push({ pageid: pid, title: it.title }); } | ||
} else if (it.ns === 14) { | |||
} else if (it.ns === 14) { | |||
var sub = it.title; | var sub = it.title; | ||
if (!visited[sub]) { | if (!visited[sub]) { visited[sub] = true; queue.push(sub); } | ||
} | } | ||
} | } | ||
var cont = get( | var cont = get(d, ['continue', 'cmcontinue']); | ||
if (cont && pages.length < limit) return fetchOne(catTitle, cont); | if (cont && pages.length < limit) return fetchOne(catTitle, cont); | ||
}); | }); | ||
} | } | ||
| Zeile 692: | Zeile 669: | ||
} | } | ||
function fetchCategoryMembersRecursiveMulti(rootCats, limit) { | function fetchCategoryMembersRecursiveMulti(rootCats, limit) { | ||
var pages = [] | var pages = [], outSet = {}, i = 0; | ||
function next() { | |||
if (i >= rootCats.length || pages.length >= limit) return Promise.resolve(pages); | |||
var cat = rootCats[i++]; if (!cat) return next(); | |||
function | return fetchCategoryMembersRecursiveSingle(cat, limit, outSet, pages).then(next); | ||
if (i >= rootCats.length || pages.length >= limit) | |||
var cat = rootCats[i++]; | |||
return fetchCategoryMembersRecursiveSingle(cat, limit, outSet, pages).then( | |||
} | } | ||
return next(); | |||
return | |||
} | } | ||
function fetchRatingsForContest(pageIds, contest, includeHidden) { | function fetchRatingsForContest(pageIds, contest, includeHidden) { | ||
var api = new mw.Api() | var api = new mw.Api(), res = {}, i, chunk = 50, chunks = []; | ||
for (i = 0; i < pageIds.length; i += chunk) chunks.push(pageIds.slice(i, i + chunk)); | for (i = 0; i < pageIds.length; i += chunk) chunks.push(pageIds.slice(i, i + chunk)); | ||
| Zeile 721: | Zeile 687: | ||
var ids = chunks[idx]; | var ids = chunks[idx]; | ||
return api.get({ | return api.get({ | ||
action: 'query', | action: 'query', prop: 'pagerating', pageids: ids.join('|'), | ||
prcontest: contest, format: 'json' | |||
prcontest: contest, | |||
}).then(function (d) { | }).then(function (d) { | ||
var pages = get(d, ['query', 'pages']) || {} | var pages = get(d, ['query', 'pages']) || {}, pid, pr, hist, k, total, sum, s, c; | ||
for (pid in pages) if (Object.prototype.hasOwnProperty.call(pages, pid)) { | for (pid in pages) if (Object.prototype.hasOwnProperty.call(pages, pid)) { | ||
if (!res[pid]) res[pid] = { avg: null, total: 0 }; | |||
pr = pages[pid].pagerating; | pr = pages[pid].pagerating; | ||
if (pr && (includeHidden || !('canSee' in pr) || pr.canSee !== 0)) { | if (pr && (includeHidden || !('canSee' in pr) || pr.canSee !== 0)) { | ||
hist | hist = pr.pageRating || {}; total = 0; sum = 0; | ||
for (k in hist) if (Object.prototype.hasOwnProperty.call(hist, k)) { | for (k in hist) if (Object.prototype.hasOwnProperty.call(hist, k)) { | ||
s = parseInt(k, 10); c = parseInt(hist[k], 10); | s = parseInt(k, 10); c = parseInt(hist[k], 10); | ||
| Zeile 745: | Zeile 706: | ||
}, function () { return step(idx + 1); }); | }, function () { return step(idx + 1); }); | ||
} | } | ||
return step(0); | return step(0); | ||
} | } | ||
function computeOverall(entry, contests, weights) { | function computeOverall(entry, contests, weights) { | ||
var wSum = 0, wAvgSum = 0, present = 0, totalVotes = 0, i, sc, w; | var wSum = 0, wAvgSum = 0, present = 0, totalVotes = 0, i, sc, w; | ||
for (i = 0; i < contests.length; i++) { | for (i = 0; i < contests.length; i++) { | ||
sc = entry.scores[contests[i]]; | sc = entry.scores[contests[i]]; | ||
if (sc && sc.avg !== null) { | if (sc && sc.avg !== null) { w = (typeof weights[contests[i]] === 'number') ? weights[contests[i]] : 1; wSum += w; wAvgSum += sc.avg * w; present++; } | ||
if (sc && sc.total) totalVotes += sc.total; | if (sc && sc.total) totalVotes += sc.total; | ||
} | } | ||
| Zeile 766: | Zeile 720: | ||
} | } | ||
function renderTopN(container, rows, N, minVotes) { | function renderTopN(container, rows, N, minVotes) { | ||
rows = rows.filter(function (r) { | rows = rows.filter(function (r) { return (r.overall !== null) && (r.totalVotes >= minVotes); }); | ||
rows.sort(function (a, b) { | rows.sort(function (a, b) { | ||
if (a.overall === null && b.overall !== null) return 1; | if (a.overall === null && b.overall !== null) return 1; | ||
| Zeile 779: | Zeile 729: | ||
return a.title.localeCompare(b.title); | return a.title.localeCompare(b.title); | ||
}); | }); | ||
rows = rows.slice(0, N); | rows = rows.slice(0, N); | ||
var frag = document.createDocumentFragment() | var frag = document.createDocumentFragment(), i, r, item, rank, name, a, right, mini, track, fill, val, votes; | ||
for (i = 0; i < rows.length; i++) { | for (i = 0; i < rows.length; i++) { | ||
r = rows[i]; | r = rows[i]; | ||
item = document.createElement('div'); item.className = 'whisky-top5__item'; | |||
item = document.createElement('div'); | rank = document.createElement('div'); rank.className = 'whisky-top5__rank'; rank.textContent = (i + 1); | ||
name = document.createElement('div'); name.className = 'whisky-top5__name'; | |||
a = document.createElement('a'); a.href = mw.util.getUrl(r.title); a.textContent = r.title; name.appendChild(a); | |||
rank = document.createElement('div'); | right = document.createElement('div'); right.style.minWidth = '160px'; | ||
mini = document.createElement('div'); mini.className = 'whisky-mini'; | |||
track = document.createElement('div'); track.className = 'whisky-mini__track'; | |||
fill = document.createElement('div'); fill.className = 'whisky-mini__fill'; fill.style.width = Math.max(0, Math.min(100, (r.overall / 10) * 100)) + '%'; | |||
name = document.createElement('div'); | val = document.createElement('span'); val.className = 'whisky-mini__val'; | ||
a = document.createElement('a'); | |||
right = document.createElement('div'); | |||
mini = document.createElement('div'); | |||
track = document.createElement('div'); | |||
fill = document.createElement('div'); | |||
val = document.createElement('span'); | |||
val.textContent = (r.overall.toFixed ? r.overall.toFixed(1) : (Math.round(r.overall * 10) / 10)); | val.textContent = (r.overall.toFixed ? r.overall.toFixed(1) : (Math.round(r.overall * 10) / 10)); | ||
track.appendChild(fill); | track.appendChild(fill); mini.appendChild(track); mini.appendChild(val); | ||
votes = document.createElement('div'); votes.className = 'whisky-top5__votes'; votes.textContent = r.totalVotes + ' Stimmen'; | |||
right.appendChild(mini); right.appendChild(votes); | |||
item.appendChild(rank); item.appendChild(name); item.appendChild(right); | |||
votes = document.createElement('div'); | |||
right.appendChild(mini); | |||
item.appendChild(rank); | |||
frag.appendChild(item); | frag.appendChild(item); | ||
} | } | ||
while (container.firstChild) container.removeChild(container.firstChild); | while (container.firstChild) container.removeChild(container.firstChild); | ||
if (rows.length) | if (rows.length) container.appendChild(frag); else container.textContent = 'Noch keine Bewertungen vorhanden.'; | ||
} | } | ||
function bootTop5(root) { | function bootTop5(root) { | ||
var nodes = (root || document).querySelectorAll('.whisky-top5'); | var nodes = (root || document).querySelectorAll('.whisky-top5'); | ||
| Zeile 849: | Zeile 762: | ||
container.setAttribute('data-top5-init', '1'); | container.setAttribute('data-top5-init', '1'); | ||
// | // Kategorien einlesen (Zeilenumbruch oder Semikolon-getrennt) | ||
var | var rawCats = container.getAttribute('data-categories') || container.getAttribute('data-category') || ''; | ||
var parts = | var parts = rawCats.split(/\n|;/), rootCats = [], i; | ||
for (i = 0; i < parts.length; i++) { var nm = parts[i].replace(/^\s+|\s+$/g, ''); if (nm) rootCats.push(nm); } | |||
for ( | |||
var lim = parseInt(container.getAttribute('data-limit') || '2000', 10); | var lim = parseInt(container.getAttribute('data-limit') || '2000', 10); | ||
| Zeile 866: | Zeile 774: | ||
var rawC = container.getAttribute('data-contests') || 'NASE,GESCHMACK,ABGANG,GESAMTEINDRUCK'; | var rawC = container.getAttribute('data-contests') || 'NASE,GESCHMACK,ABGANG,GESAMTEINDRUCK'; | ||
var cParts = rawC.split(','), contests = [], seen = {}; | var cParts = rawC.split(','), contests = [], seen = {}; | ||
for (i = 0; i < cParts.length; i++) { | for (i = 0; i < cParts.length; i++) { var c = cParts[i].replace(/^\s+|\s+$/g, ''); if (c && !seen[c]) { contests.push(c); seen[c] = 1; } } | ||
var weights = parseWeights(container.getAttribute('data-weights') || '', contests); | var weights = parseWeights(container.getAttribute('data-weights') || '', contests); | ||
container.textContent = ' | function status(msg) { container.textContent = msg; } | ||
if (!rootCats.length) { status('Keine Kategorien angegeben.'); return; } | |||
status('Sammle Seiten …'); | |||
// 1) | // 1) Seiten sammeln | ||
fetchCategoryMembersRecursiveMulti(rootCats, lim).then(function (members) { | fetchCategoryMembersRecursiveMulti(rootCats, lim).then(function (members) { | ||
if (!members || !members.length) { | status('Gefundene Seiten: ' + (members ? members.length : 0) + ' – lade Bewertungen …'); | ||
if (!members || !members.length) { status('Keine passenden Seiten gefunden.'); return; } | |||
var pageIds = [], byId = {}, i; | var pageIds = [], byId = {}, i; | ||
for (i = 0; i < members.length; i++) { | for (i = 0; i < members.length; i++) { pageIds.push(members[i].pageid); byId[members[i].pageid] = { pageid: members[i].pageid, title: members[i].title, scores: {} }; } | ||
// Wrapper, damit wir bei 0 Stimmen automatisch mit includeHidden=true nachladen können | |||
function buildAndRender() { | |||
var rows = [], pid, e, withVotes = 0; | |||
for (pid in byId) if (Object.prototype.hasOwnProperty.call(byId, pid)) { | |||
e = byId[pid]; computeOverall(e, contests, weights); rows.push(e); | |||
if (e.totalVotes > 0 && e.overall !== null) withVotes++; | |||
} | |||
if (withVotes === 0 && !includeHidden) { | |||
// Auto-Retry mit includeHidden=true | |||
includeHidden = true; | |||
status('Keine sichtbaren Stimmen – zweiter Versuch (versteckte Ergebnisse mitzählen) …'); | |||
return loopContest(0).then(buildAndRender); | |||
} | |||
renderTopN(container, rows, cnt, minVotes); | |||
} | } | ||
function loopContest(idx) { | function loopContest(idx) { | ||
if (idx >= contests.length) return Promise.resolve(); | if (idx >= contests.length) return Promise.resolve(); | ||
| Zeile 892: | Zeile 811: | ||
return fetchRatingsForContest(pageIds, contest, includeHidden).then(function (map) { | return fetchRatingsForContest(pageIds, contest, includeHidden).then(function (map) { | ||
var pid; | var pid; | ||
for (pid in map) if (Object.prototype.hasOwnProperty.call(map, pid)) | for (pid in map) if (Object.prototype.hasOwnProperty.call(map, pid)) byId[pid].scores[contest] = map[pid]; | ||
return loopContest(idx + 1); | return loopContest(idx + 1); | ||
}); | }); | ||
} | } | ||
loopContest(0).then( | loopContest(0).then(buildAndRender).catch(function () { status('Topliste konnte nicht geladen werden.'); }); | ||
}).catch(function () { status('Topliste konnte nicht geladen werden.'); }); | |||
}).catch(function () { | |||
})(nodes[n]); | })(nodes[n]); | ||
| Zeile 920: | Zeile 824: | ||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', function () { bootTop5(document); }); | document.addEventListener('DOMContentLoaded', function () { bootTop5(document); }); | ||
} else { | } else { bootTop5(document); } | ||
mw.hook('wikipage.content').add(function ($c) { if ($c && $c[0]) bootTop5($c[0]); }); | mw.hook('wikipage.content').add(function ($c) { if ($c && $c[0]) bootTop5($c[0]); }); | ||
}); | }); | ||