MediaWiki:Common.js: Difference between revisions
Add Admin Control Panel JS module — live article table with sort, filter, word count, categories, watchers, revisions |
Add Life Planning hero CSS; move all 8 cards to ID-based grid fixer |
||
| (17 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
// Knowledge Areas grid layout fixer | |||
(function() { | |||
function fixKnowledgeGrid() { | |||
var heroDiv = document.getElementById('ax-sexual-health-hero'); | |||
if (!heroDiv) return; | |||
var firstCard = heroDiv.closest('.ax-card'); | |||
if (!firstCard) return; | |||
var gridContainer = firstCard.parentElement; | |||
if (!gridContainer) return; | |||
gridContainer.style.display = 'grid'; | |||
gridContainer.style.gridTemplateColumns = 'repeat(2, 1fr)'; | |||
gridContainer.style.gap = '14px'; | |||
// Collect cards by hero IDs | |||
var heroIds = ['ax-sexual-health-hero','ax-dating-hero','ax-kink-hero','ax-culture-hero','ax-fashion-hero','ax-community-hero','ax-drugs-hero','ax-life-hero']; | |||
var allKnowledgeCards = []; | |||
heroIds.forEach(function(id) { | |||
var h = document.getElementById(id); | |||
if (h) { var c = h.closest('.ax-card'); if (c) allKnowledgeCards.push(c); } | |||
}); | |||
// Find remaining 3 knowledge cards by keyword match (Community, Drugs, Life Planning) | |||
// These have no hero images. Identify by checking they are NOT Start Learning or Featured | |||
var keywords = []; | |||
Array.from(document.querySelectorAll('.ax-card')).forEach(function(c) { | |||
if (allKnowledgeCards.indexOf(c) >= 0) return; | |||
var txt = c.innerText || ''; | |||
keywords.forEach(function(kw) { | |||
if (txt.indexOf(kw) >= 0 && allKnowledgeCards.indexOf(c) < 0) { | |||
allKnowledgeCards.push(c); | |||
} | |||
}); | |||
}); | |||
// Move all knowledge cards into grid | |||
allKnowledgeCards.forEach(function(c) { | |||
if (c.parentElement !== gridContainer) gridContainer.appendChild(c); | |||
}); | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', fixKnowledgeGrid); | |||
} else { | |||
fixKnowledgeGrid(); | |||
} | |||
})(); | |||
// Sexual Health hero image | |||
(function() { | |||
var style = document.createElement('style'); | |||
style.textContent = '#ax-sexual-health-hero { background-image: url("https://alphax.wiki/images/4/43/Sexual_Health_Hero.jpg"); background-size: cover; background-position: center center; } #ax-dating-hero { background-image: url("https://alphax.wiki/images/0/0b/Dating_Sex_Relationships_Hero.png"); background-size: cover; background-position: center center; } #ax-kink-hero { background-image: url("https://alphax.wiki/images/e/e5/Kink_BDSM_Hero.png"); background-size: cover; background-position: center center; } #ax-culture-hero { background-image: url("https://alphax.wiki/images/2/25/Culture_History_Politics_Hero.png"); background-size: cover; background-position: center center; } #ax-fashion-hero { background-image: url("https://alphax.wiki/images/4/4b/Fashion_Visual_Signaling_Hero.png"); background-size: cover; background-position: center top; } #ax-community-hero { background-image: url("https://alphax.wiki/images/e/ed/Community_Identity_Hero.png"); background-size: cover; background-position: center center; } #ax-drugs-hero { background-image: url("https://alphax.wiki/images/c/c7/Drugs_Party_Culture_Hero.jpg"); background-size: cover; background-position: center center; } #ax-life-hero { background-image: url("https://alphax.wiki/images/7/74/Life_Planning_Hero.jpg"); background-size: cover; background-position: center top; }'; | |||
document.head.appendChild(style); | |||
})(); | |||
(function () { | (function () { | ||
| Line 258: | Line 308: | ||
/* ============================================ */ | /* ================================================ */ | ||
/* ADMIN CONTROL PANEL | /* ADMIN CONTROL PANEL - AlphaX Wiki */ | ||
/* ================================================ */ | |||
/* ============================================ */ | |||
(function() { | (function() { | ||
if (mw.config.get('wgPageName') !== 'AlphaX:Admin_Control_Panel') return; | if (mw.config.get('wgPageName') !== 'AlphaX:Admin_Control_Panel') return; | ||
| Line 267: | Line 316: | ||
mw.loader.using(['mediawiki.api'], function() { | mw.loader.using(['mediawiki.api'], function() { | ||
var css = [ | |||
var css = | '#axcp-root *{box-sizing:border-box}', | ||
#axcp-root * { box-sizing: border-box | '#axcp-root{font-family:system-ui,-apple-system,sans-serif;background:#0D0D0D;color:#fff;margin:-8px -20px;padding:0;border-radius:12px;overflow:hidden}', | ||
#axcp-root { font-family: system-ui,-apple-system,sans-serif; background: #0D0D0D; color: #fff; margin: -8px -20px; padding: 0; border-radius: 12px; overflow: hidden | '.axcp-hdr{background:#1A1A1A;border-bottom:1px solid #2E2E2E;padding:24px 28px 20px}', | ||
.axcp- | '.axcp-ttl{font-size:22px;font-weight:700;color:#fff;margin-bottom:3px}', | ||
.axcp- | '.axcp-sub{font-size:11px;color:#555;text-transform:uppercase;letter-spacing:.1em}', | ||
.axcp- | '.axcp-sr{display:flex;gap:12px;margin:16px 0 0;flex-wrap:wrap}', | ||
.axcp- | '.axcp-sc{background:#0D0D0D;border:1px solid #2E2E2E;border-radius:12px;padding:14px 18px;flex:1;min-width:90px}', | ||
.axcp- | '.axcp-sv{font-size:24px;font-weight:700;color:#FF6600;line-height:1}', | ||
.axcp- | '.axcp-sl{font-size:10px;color:#555;text-transform:uppercase;letter-spacing:.08em;margin-top:4px}', | ||
.axcp- | '.axcp-ctrl{display:flex;gap:10px;padding:12px 28px;background:#111;border-bottom:1px solid #2E2E2E;flex-wrap:wrap;align-items:center}', | ||
.axcp- | '.axcp-inp{flex:1;min-width:160px;background:#1A1A1A;border:1px solid #2E2E2E;border-radius:8px;padding:8px 13px;color:#fff;font-size:13px;outline:none}', | ||
.axcp- | '.axcp-inp:focus{border-color:rgba(255,102,0,.5)}', | ||
.axcp- | '.axcp-sel{background:#1A1A1A;border:1px solid #2E2E2E;border-radius:8px;padding:8px 11px;color:#fff;font-size:12px;outline:none;cursor:pointer;max-width:200px}', | ||
'.axcp-sel:focus{border-color:rgba(255,102,0,.5)}', | |||
.axcp- | '.axcp-cnt{background:rgba(255,102,0,.12);border:1px solid rgba(255,102,0,.3);color:#FF6600;border-radius:6px;padding:4px 12px;font-size:12px;font-weight:600;white-space:nowrap}', | ||
.axcp- | '.axcp-rbtn{background:linear-gradient(135deg,#FF6600,#FF8533);border:none;border-radius:8px;color:#fff;padding:9px 16px;font-size:12px;font-weight:700;cursor:pointer}', | ||
.axcp- | '.axcp-rbtn:hover{opacity:.85}', | ||
.axcp- | '.axcp-tw{overflow-x:auto;padding:0 28px 32px;background:#0D0D0D}', | ||
'table.axcp-t{width:100%;border-collapse:collapse;margin-top:14px;font-size:13px}', | |||
.axcp- | 'table.axcp-t thead th{background:#111;color:#555;text-transform:uppercase;letter-spacing:.07em;font-size:10px;font-weight:600;padding:10px 12px;border-bottom:1px solid #2E2E2E;cursor:pointer;white-space:nowrap;user-select:none}', | ||
table.axcp- | 'table.axcp-t thead th:hover{color:#FF6600}', | ||
table.axcp- | 'table.axcp-t thead th.sa::after{content:" u2191";color:#FF6600}', | ||
table.axcp- | 'table.axcp-t thead th.sd::after{content:" u2193";color:#FF6600}', | ||
table.axcp- | 'table.axcp-t td{padding:8px 12px;border-bottom:1px solid rgba(255,255,255,.03);vertical-align:middle}', | ||
table.axcp- | 'table.axcp-t tr:hover td{background:rgba(255,102,0,.04)}', | ||
table.axcp- | 'table.axcp-t tr:last-child td{border-bottom:none}', | ||
table.axcp- | '.axcp-al{color:#fff;text-decoration:none;font-weight:500;font-size:13px}', | ||
table.axcp- | '.axcp-al:hover{color:#FF6600}', | ||
.axcp- | '.axcp-cp{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap}', | ||
.axcp- | '.axcp-st{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600}', | ||
.axcp- | '.s-stu{background:rgba(255,60,60,.15);color:#ff7070;border:1px solid rgba(255,60,60,.25)}', | ||
.axcp- | '.s-sho{background:rgba(255,166,0,.15);color:#ffb020;border:1px solid rgba(255,166,0,.25)}', | ||
. | '.s-med{background:rgba(61,220,132,.15);color:#3ddc84;border:1px solid rgba(61,220,132,.25)}', | ||
. | '.s-lon{background:rgba(100,160,255,.15);color:#78b0ff;border:1px solid rgba(100,160,255,.25)}', | ||
. | '.axcp-n{text-align:right;font-variant-numeric:tabular-nums;color:#777}', | ||
. | '.axcp-bw{background:#1A1A1A;border-radius:3px;height:5px;width:60px;display:inline-block;vertical-align:middle;margin-left:6px;overflow:hidden}', | ||
.axcp- | '.axcp-bf{height:5px;border-radius:3px;background:linear-gradient(to right,#FF6600,#FF8533)}', | ||
.axcp- | '.axcp-load{text-align:center;padding:80px 40px;color:#555;font-size:15px;background:#0D0D0D}', | ||
.axcp- | '.axcp-prog{color:#FF6600;font-size:13px;margin-top:10px}', | ||
.axcp- | '.axcp-z{color:#333}', | ||
.axcp- | '.axcp-hi{color:#FF6600;font-weight:600}', | ||
.axcp- | '.axcp-wt{color:#3ddc84;font-weight:600}' | ||
.axcp-hi { color: #FF6600; font-weight: 600 | ].join(''); | ||
.axcp- | |||
var sEl = document.createElement('style'); | |||
var | sEl.textContent = css; | ||
document.head.appendChild(sEl); | |||
document.head.appendChild( | |||
var contentEl = document.querySelector('#mw-content-text .mw-parser-output') || document.getElementById('mw-content-text'); | var contentEl = document.querySelector('#mw-content-text .mw-parser-output') || document.getElementById('mw-content-text'); | ||
contentEl.innerHTML = '<div id="axcp-root"><div class="axcp- | contentEl.innerHTML = '<div id="axcp-root"><div class="axcp-load"><div style="font-size:28px;margin-bottom:14px">⚙</div><div>Loading article database...</div><div class="axcp-prog" id="axcp-prog">Fetching page list...</div></div></div>'; | ||
var root = document.getElementById('axcp-root'); | var root = document.getElementById('axcp-root'); | ||
var allData = []; | var allData = []; | ||
var sortCol = 'title' | var sortCol = 'title', sortDir = 1, filterText = '', filterCat = '', filterSt = ''; | ||
var | var CC = { | ||
'Sexual Health': | 'Sexual Health': 'rgba(255,100,100,.18)|#ff8080', | ||
'Dating, Sex & Relationships': 'rgba(255,182,60,.18)|#ffb83c', | 'Dating, Sex & Relationships': 'rgba(255,182,60,.18)|#ffb83c', | ||
'Kink & BDSM': | 'Kink & BDSM': 'rgba(180,80,255,.18)|#c864ff', | ||
'Culture, History & Politics': 'rgba(80,140,255,.18)|#6496ff', | 'Culture, History & Politics': 'rgba(80,140,255,.18)|#6496ff', | ||
'Fashion & Visual Signaling': | 'Fashion & Visual Signaling': 'rgba(255,220,60,.18)|#ffdc3c', | ||
'Community & Identity': | 'Community & Identity': 'rgba(61,220,132,.18)|#3ddc84', | ||
'Drugs, Party Culture & Harm Reduction': 'rgba(255,120,40,.18)|#ff8028', | 'Drugs, Party Culture & Harm Reduction':'rgba(255,120,40,.18)|#ff8028', | ||
'Life Planning': | 'Life Planning': 'rgba(80,200,255,.18)|#50c8ff' | ||
}; | }; | ||
var KCATS = Object.keys(CC); | |||
function catSty(cat) { | |||
var s = CC[cat] || 'rgba(120,120,120,.18)|#888', p = s.split('|'); | |||
function | |||
var s = | |||
return 'background:'+p[0]+';color:'+p[1]+';border:1px solid '+p[1]+';'; | return 'background:'+p[0]+';color:'+p[1]+';border:1px solid '+p[1]+';'; | ||
} | } | ||
function wSt(w) { | |||
function | if (w < 150) return ['stu','Stub']; | ||
if (w < 150) return [' | if (w < 500) return ['sho','Short']; | ||
if (w < 500) return [' | if (w < 1500) return ['med','Medium']; | ||
if (w < 1500) return [' | return ['lon','Long']; | ||
return [' | |||
} | } | ||
function fDate(iso) { | |||
function | if (!iso) return '<span class="axcp-z">-</span>'; | ||
if (!iso) return '<span class="axcp- | |||
var d = new Date(iso); | var d = new Date(iso); | ||
return d.toLocaleDateString('en-GB', {day:'2-digit',month:'short',year:'numeric'}); | return d.toLocaleDateString('en-GB',{day:'2-digit',month:'short',year:'numeric'}); | ||
} | |||
function fKb(b) { | |||
if (!b) return '<span class="axcp-z">-</span>'; | |||
return (b/1024).toFixed(1)+' <span style="color:#444;font-size:10px">KB</span>'; | |||
} | } | ||
function esc(s) { | |||
function | return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); | ||
} | } | ||
function setP(msg) { var el = document.getElementById('axcp-prog'); if (el) el.textContent = msg; } | |||
function | |||
function render(data) { | function render(data) { | ||
var | var fd = data.filter(function(r) { | ||
var mt = !filterText || r.title.toLowerCase().indexOf(filterText) >= 0 || (r.cat | var mt = !filterText || r.title.toLowerCase().indexOf(filterText)>=0 || (r.cat||'').toLowerCase().indexOf(filterText)>=0; | ||
var mc = !filterCat || r.cat === filterCat; | var mc = !filterCat || r.cat === filterCat; | ||
return mt && mc; | var ms = !filterSt || wSt(r.words||0)[0] === filterSt; | ||
return mt && mc && ms; | |||
}); | |||
fd.sort(function(a,b) { | |||
var av=a[sortCol]||0, bv=b[sortCol]||0; | |||
if (typeof av==='number'&&typeof bv==='number') return sortDir*(av-bv); | |||
return sortDir*String(av).localeCompare(String(bv)); | |||
}); | }); | ||
var mxW = Math.max.apply(null,[1].concat(fd.map(function(r){return r.words||0;}))); | |||
var totW = fd.reduce(function(s,r){return s+(r.words||0);},0); | |||
var avgW = fd.length ? Math.round(totW/fd.length) : 0; | |||
var totR = fd.reduce(function(s,r){return s+(r.revisions||0);},0); | |||
var nocat = fd.filter(function(r){return !r.cat;}).length; | |||
var cats={}; | |||
data.forEach(function(r){if(r.cat)cats[r.cat]=1;}); | |||
var catList=Object.keys(cats).sort(); | |||
var h=''; | |||
h+='<div class="axcp-hdr">'; | |||
h+='<div class="axcp-ttl">⚙ Admin Control Panel</div>'; | |||
h+='<div class="axcp-sub">AlphaX Wiki • Article Database • Live Data</div>'; | |||
h+='<div class="axcp-sr">'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv">'+fd.length+'</div><div class="axcp-sl">Articles</div></div>'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv">'+avgW.toLocaleString()+'</div><div class="axcp-sl">Avg Words</div></div>'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv">'+totW.toLocaleString()+'</div><div class="axcp-sl">Total Words</div></div>'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv">'+totR.toLocaleString()+'</div><div class="axcp-sl">Total Revisions</div></div>'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv">'+catList.length+'</div><div class="axcp-sl">Categories</div></div>'; | |||
h+='<div class="axcp-sc"><div class="axcp-sv" style="color:'+(nocat>0?'#ff7070':'#3ddc84')+'">'+nocat+'</div><div class="axcp-sl">Uncategorised</div></div>'; | |||
h+='</div></div>'; | |||
h+='<div class="axcp-ctrl">'; | |||
h+='<input class="axcp-inp" id="axcps" type="text" placeholder="Search articles or categories..." value="'+esc(filterText)+'">'; | |||
h+='<select class="axcp-sel" id="axcpc"><option value="">All Categories</option>'; | |||
catList.forEach(function(c){h+='<option value="'+esc(c)+'"'+(filterCat===c?' selected':'')+'>'+esc(c)+'</option>';}); | |||
h+='</select>'; | |||
h+='<select class="axcp-sel" id="axcpf"><option value="">All Statuses</option>'; | |||
[['stu','Stub'],['sho','Short'],['med','Medium'],['lon','Long']].forEach(function(s){ | |||
h+='<option value="'+s[0]+'"'+(filterSt===s[0]?' selected':'')+'>'+s[1]+'</option>'; | |||
}); | }); | ||
h+='</select>'; | |||
h+='<span class="axcp-cnt">'+fd.length+' articles</span>'; | |||
h+='<button class="axcp-rbtn" id="axcpr">↻ Refresh Data</button>'; | |||
h+='</div>'; | |||
h+='<div class="axcp-tw"><table class="axcp-t"><thead><tr>'; | |||
[{k:'title',l:'Article'},{k:'cat',l:'Category'},{k:'subcat',l:'Subcategory'}, | |||
{k:'words',l:'Words'},{k:'size',l:'Size (KB)'},{k:'status',l:'Status'}, | |||
{k:'touched',l:'Last Edited'},{k:'revisions',l:'Revisions'}, | |||
{k:'watchers',l:'Watchers'},{k:'links',l:'Inbound Links'} | |||
].forEach(function(c){ | |||
var cl=sortCol===c.k?(sortDir===1?'sa':'sd'):''; | |||
h+='<th class="'+cl+'" data-col="'+c.k+'">'+c.l+'</th>'; | |||
h += ' | |||
h += '<span class="axcp- | |||
h += '<button class="axcp- | |||
h += '</div>'; | |||
h += '<div class="axcp- | |||
[ | |||
].forEach(function(c) { | |||
var | |||
h += '<th class="'+ | |||
}); | }); | ||
h += '</tr></thead><tbody>'; | h+='</tr></thead><tbody>'; | ||
if (!fd.length) { | |||
if ( | h+='<tr><td colspan="10" style="text-align:center;padding:50px;color:#333">No articles match your filters.</td></tr>'; | ||
h += '<tr><td colspan=" | |||
} | } | ||
fd.forEach(function(r){ | |||
var st=wSt(r.words||0); | |||
var st = | var bp=mxW>0?Math.round(((r.words||0)/mxW)*60):0; | ||
var | h+='<tr>'; | ||
h += '<tr>'; | h+='<td><a class="axcp-al" href="/wiki/'+encodeURIComponent((r.title||'').replace(/ /g,'_'))+'" target="_blank">'+esc(r.title)+'</a></td>'; | ||
h += '<td><a class="axcp- | h+='<td>'+(r.cat?'<span class="axcp-cp" style="'+catSty(r.cat)+'">'+esc(r.cat)+'</span>':'<span class="axcp-z">-</span>')+'</td>'; | ||
h += '<td>'+(r.cat ? '<span class="axcp- | h+='<td style="color:#555;font-size:12px">'+esc(r.subcat||'-')+'</td>'; | ||
h += '<td style="color:# | h+='<td class="axcp-n">'+(r.words||0).toLocaleString()+'<span class="axcp-bw"><span class="axcp-bf" style="width:'+bp+'px"></span></span></td>'; | ||
h += '<td class="axcp- | h+='<td class="axcp-n">'+fKb(r.size)+'</td>'; | ||
h += '<td class="axcp- | h+='<td><span class="axcp-st s-'+st[0]+'">'+st[1]+'</span></td>'; | ||
h += '<td><span class="axcp- | h+='<td style="color:#555;white-space:nowrap;font-size:12px">'+fDate(r.touched)+'</td>'; | ||
h += '<td style="color:# | h+='<td class="axcp-n '+(r.revisions>20?'axcp-hi':'')+'">'+(r.revisions||0)+'</td>'; | ||
h+='<td class="axcp-n '+(r.watchers>0?'axcp-wt':'axcp-z')+'">'+(r.watchers>0?'👁 '+r.watchers:'-')+'</td>'; | |||
h += '<td class="axcp- | h+='<td class="axcp-n '+(r.links>5?'axcp-hi':'')+'">'+(r.links||0)+'</td>'; | ||
h += '<td class="axcp- | h+='</tr>'; | ||
h += '<td class="axcp- | |||
h += '</tr>'; | |||
}); | }); | ||
h+='</tbody></table></div>'; | |||
root.innerHTML=h; | |||
var si=document.getElementById('axcps'); | |||
if(si)si.addEventListener('input',function(){filterText=this.value.toLowerCase();render(allData);}); | |||
var ci=document.getElementById('axcpc'); | |||
if(ci)ci.addEventListener('change',function(){filterCat=this.value;render(allData);}); | |||
var | var fi=document.getElementById('axcpf'); | ||
if ( | if(fi)fi.addEventListener('change',function(){filterSt=this.value;render(allData);}); | ||
var ri=document.getElementById('axcpr'); | |||
var | if(ri)ri.addEventListener('click',function(){allData=[];loadData();}); | ||
if ( | document.querySelectorAll('table.axcp-t thead th').forEach(function(th){ | ||
th.addEventListener('click',function(){ | |||
var | var col=this.getAttribute('data-col'); | ||
if ( | if(sortCol===col){sortDir*=-1;}else{sortCol=col;sortDir=1;} | ||
var | |||
if ( | |||
document.querySelectorAll('table.axcp- | |||
th.addEventListener('click', function(){ | |||
var col = this.getAttribute('data-col'); | |||
if (sortCol === col) { sortDir *= -1; } else { sortCol = col; sortDir = 1; } | |||
render(allData); | render(allData); | ||
}); | }); | ||
| Line 503: | Line 503: | ||
} | } | ||
function | function apiFetch(url) { | ||
return fetch(url).then(function(r){return r.json();}); | |||
} | } | ||
function loadData() { | |||
root.innerHTML = '<div class="axcp- | root.innerHTML='<div class="axcp-load"><div style="font-size:28px;margin-bottom:14px">⚙</div><div>Loading article database...</div><div class="axcp-prog" id="axcp-prog">Step 1/4 -- Fetching page list...</div></div>'; | ||
var pages=[], map={}, ids=[]; | |||
function fetchPages(apc) { | |||
var u='/api.php?action=query&list=allpages&apnamespace=0&aplimit=500&format=json'; | |||
if(apc) u+='&apcontinue='+encodeURIComponent(apc); | |||
return apiFetch(u).then(function(d){ | |||
if(!d||!d.query)return; | |||
pages=pages.concat(d.query.allpages||[]); | |||
var cont=d.continue&&d.continue.apcontinue; | |||
if(cont)return fetchPages(cont); | |||
pages = pages.concat(d.query.allpages); | }); | ||
} | |||
// Step 2: info + categories (no revision params to avoid conflicts) | |||
function fetchInfoChunk(i) { | |||
if(i>=ids.length) return Promise.resolve(); | |||
var chunk=ids.slice(i,i+50).join('|'); | |||
return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=info|categories&inprop=watchers&cllimit=15&format=json').then(function(d){ | |||
Object.keys( | if(!d||!d.query||!d.query.pages){return fetchInfoChunk(i+50);} | ||
var pg = | Object.keys(d.query.pages).forEach(function(pid){ | ||
map[pid].size | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
map[pid].touched = pg.touched || ''; | map[pid].size=pg.length||0; | ||
map[pid].watchers = | map[pid].touched=pg.touched||''; | ||
map[pid].watchers=pg.watchers!=null?Number(pg.watchers):0; | |||
if (pg.categories) { | if(pg.categories){pg.categories.forEach(function(c){ | ||
var cn=c.title.replace('Category:',''); | |||
if(KCATS.indexOf(cn)>=0){if(!map[pid].cat)map[pid].cat=cn;} | |||
else if(cn.length<60&&cn.toLowerCase().indexOf('stub')<0){if(!map[pid].subcat)map[pid].subcat=cn;} | |||
});} | |||
}); | }); | ||
} | return fetchInfoChunk(i+50); | ||
}); | |||
} | |||
// Step 3: revision counts | |||
function fetchRevChunk(i) { | |||
if(i>=ids.length) return Promise.resolve(); | |||
var chunk=ids.slice(i,i+20).join('|'); | |||
return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=revisions&rvprop=ids&rvlimit=max&format=json').then(function(d){ | |||
Object.keys( | if(!d||!d.query||!d.query.pages){return fetchRevChunk(i+20);} | ||
var pg = | Object.keys(d.query.pages).forEach(function(pid){ | ||
map[pid].revisions = (pg.revisions||[]).length; | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
map[pid].revisions=(pg.revisions||[]).length; | |||
}); | }); | ||
} | return fetchRevChunk(i+20); | ||
}); | |||
} | |||
// Step 4: word counts + inbound links | |||
function fetchContentChunk(i) { | |||
if(i>=ids.length) return Promise.resolve(); | |||
var chunk=ids.slice(i,i+8).join('|'); | |||
return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=revisions|linkshere&rvprop=content&rvslots=main&lhnamespace=0&lhlimit=max&format=json').then(function(d){ | |||
if(!d||!d.query||!d.query.pages){return fetchContentChunk(i+8);} | |||
Object.keys(d.query.pages).forEach(function(pid){ | |||
Object.keys( | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
var pg = | map[pid].links=(pg.linkshere||[]).length; | ||
map[pid].links = (pg.linkshere||[]).length; | if(!pg.revisions||!pg.revisions[0])return; | ||
if (!pg.revisions||!pg.revisions[0]) return; | if(!pg.revisions||!pg.revisions[0])return; | ||
var slot = pg.revisions[0].slots ? pg.revisions[0].slots.main : pg.revisions[0]; | var slot=pg.revisions[0].slots?pg.revisions[0].slots.main:pg.revisions[0]; | ||
var | var raw=slot['*']||slot.content||''; | ||
var | // Remove template names but keep parameter text | ||
var c=raw.replace(/\{\{[A-Za-z][^|\}]*\|?/g,'').replace(/\}\}/g,' '); | |||
c=c.replace(/\[\[File:[^\]]+\]\]/gi,' '); | |||
c=c.replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1'); | |||
c=c.replace(/<[^>]+>/g,' ').replace(/<!--[^>]*-->/g,' '); | |||
c=c.replace(/={2,}[^=]+=={2,}/g,' '); | |||
c=c.replace(/[|!=*#;:{}\/\[\]]/g,' '); | |||
c=c.replace(/\s+/g,' ').trim(); | |||
map[pid].words=c?c.split(/\s+/).filter(function(w){return w.length>2;}).length:0; | |||
map[pid].words = | |||
}); | }); | ||
} | return fetchContentChunk(i+8); | ||
}); | |||
} | |||
allData = Object.values(map); | fetchPages(null).then(function(){ | ||
setP('Step 2/4 -- Fetching page info & categories for '+pages.length+' articles...'); | |||
pages.forEach(function(p){map[p.pageid]={title:p.title,cat:'',subcat:'',words:0,size:0,touched:'',revisions:0,watchers:0,links:0};}); | |||
ids=pages.map(function(p){return p.pageid;}); | |||
return fetchInfoChunk(0); | |||
}).then(function(){ | |||
setP('Step 3/4 -- Counting revisions...'); | |||
return fetchRevChunk(0); | |||
}).then(function(){ | |||
setP('Step 4/4 -- Calculating word counts & inbound links...'); | |||
return fetchContentChunk(0); | |||
}).then(function(){ | |||
allData=Object.values(map); | |||
render(allData); | render(allData); | ||
}).catch(function(e){ | |||
} catch(e) { | root.innerHTML='<div class="axcp-load" style="color:#ff7070">⚠ Error: '+e.message+'</div>'; | ||
root.innerHTML = '<div class="axcp- | }); | ||
} | |||
} | } | ||
loadData(); | loadData(); | ||
}); | }); | ||
})(); | |||
// Category Grid Component - background images for ax-cat-grid | |||
(function() { | |||
// Map of card ID -> background image URL and fallback gradient | |||
var catImages = { | |||
'ax-cat-img-1': { url: 'https://alphax.wiki/images/4/43/Sexual_Health_Hero.jpg', pos: 'center center' }, | |||
'ax-cat-img-2': { url: 'https://alphax.wiki/images/e/e5/Kink_BDSM_Hero.png', pos: 'center center' }, | |||
'ax-cat-img-3': { gradient: 'linear-gradient(135deg, #0D1B2A 0%, #1B3A4B 40%, #0D2B3E 100%)' }, | |||
'ax-cat-img-4': { gradient: 'linear-gradient(135deg, #1A0D0D 0%, #3A1A0D 50%, #2B1A0D 100%)' }, | |||
'ax-cat-img-5': { url: 'https://alphax.wiki/images/0/0b/Dating_Sex_Relationships_Hero.png', pos: 'center 30%' }, | |||
'ax-cat-img-6': { gradient: 'linear-gradient(135deg, #1A0D1A 0%, #2E0D3A 50%, #1A0D2B 100%)' }, | |||
'ax-cat-img-7': { url: 'https://alphax.wiki/images/2/25/Culture_History_Politics_Hero.png', pos: 'center center' }, | |||
'ax-cat-img-8': { gradient: 'linear-gradient(135deg, #0D1A0D 0%, #0D2B1A 50%, #0D1A2B 100%)' } | |||
}; | |||
function applyCatImages() { | |||
Object.keys(catImages).forEach(function(id) { | |||
var el = document.getElementById(id); | |||
if (!el) return; | |||
var cfg = catImages[id]; | |||
if (cfg.url) { | |||
el.style.backgroundImage = 'url("' + cfg.url + '")'; | |||
el.style.backgroundSize = 'cover'; | |||
el.style.backgroundPosition = cfg.pos || 'center center'; | |||
} else if (cfg.gradient) { | |||
el.style.backgroundImage = cfg.gradient; | |||
} | |||
}); | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', applyCatImages); | |||
} else { | |||
applyCatImages(); | |||
} | |||
})(); | })(); | ||