MediaWiki:Common.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (โ-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (โ-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Hero logo image styling - white + orange glow
(function() {
var s = document.createElement('style');
s.textContent = '#ax-hero-logo img { filter: hue-rotate(200deg) saturate(8) brightness(1.6) drop-shadow(0 0 20px rgba(255,120,0,0.9)) drop-shadow(0 0 50px rgba(255,80,0,0.5)); max-width:520px; width:80%; height:auto; display:inline-block; }';
document.head.appendChild(s);
})();
// 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/c/c2/Sexual_health.png"); background-size: cover; background-position: center center; } #ax-dating-hero { background-image: url("https://alphax.wiki/images/6/6c/Dating_and_relationships.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 () {
const KEY = "alphax_entry_consent";
const ANALYTICS_KEY = "alphax_analytics_consent";
const MAX_AGE = 30 * 24 * 60 * 60 * 1000;
const GA_ID = "G-XXXXXXXXXX";
const saved = localStorage.getItem(KEY);
if (saved && (Date.now() - Number(saved) < MAX_AGE)) {
const analyticsConsent = localStorage.getItem(ANALYTICS_KEY);
if (analyticsConsent === "granted") {
loadGoogleAnalytics();
}
return;
}
const style = document.createElement("style");
style.innerHTML = `
#age-overlay{
position:fixed;
inset:0;
background:rgba(0,0,0,0.85);
backdrop-filter:blur(8px);
display:flex;
align-items:center;
justify-content:center;
z-index:999999;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial;
}
#age-box{
background:#0f0f12;
border:1px solid rgba(255,255,255,0.08);
border-radius:20px;
padding:40px 34px;
max-width:520px;
width:90%;
color:white;
box-shadow:0 25px 80px rgba(0,0,0,0.6);
text-align:center;
}
#age-box h2{
margin-top:0;
font-size:32px;
margin-bottom:18px;
}
#age-text{
color:#cfcfd6;
line-height:1.6;
margin-bottom:28px;
}
#checks{
text-align:left;
margin-bottom:28px;
}
#checks label{
display:block;
margin:12px 0;
font-size:15px;
color:#e4e4ea;
cursor:pointer;
line-height:1.5;
}
#checks input{
margin-right:10px;
}
#optional-title{
margin-top:22px;
margin-bottom:8px;
font-size:13px;
font-weight:700;
letter-spacing:0.04em;
text-transform:uppercase;
color:#9ca3af;
}
#buttons{
display:flex;
gap:14px;
justify-content:center;
margin-top:10px;
}
#enter{
background:#34d27a;
color:#05110b;
border:none;
padding:14px 26px;
border-radius:10px;
font-size:16px;
font-weight:700;
cursor:pointer;
opacity:0.4;
}
#enter.active{
opacity:1;
}
#exit{
background:#e74c3c;
border:none;
color:white;
padding:14px 22px;
border-radius:10px;
font-size:16px;
cursor:pointer;
}
#links{
margin-top:22px;
font-size:13px;
opacity:0.8;
}
#links a{
color:#bbb;
margin:0 10px;
text-decoration:none;
}
#links a:hover{
color:white;
}
`;
document.head.appendChild(style);
const overlay = document.createElement("div");
overlay.id = "age-overlay";
overlay.innerHTML = `
<div id="age-box">
<h2>18+ Access</h2>
<div id="age-text">
This website contains adult educational content.<br>
You must confirm the following to enter.
</div>
<div id="checks">
<label>
<input type="checkbox" id="c1">
I confirm that I am at least 18 years old
</label>
<label>
<input type="checkbox" id="c2">
I agree to the Terms & Conditions
</label>
<label>
<input type="checkbox" id="c3">
I agree to the Privacy Policy
</label>
<div id="optional-title">Optional</div>
<label>
<input type="checkbox" id="c4">
Allow anonymous analytics to help improve the website
</label>
</div>
<div id="buttons">
<button id="enter">Enter</button>
<button id="exit">Exit</button>
</div>
<div id="links">
<a href="/index.php?title=Terms_and_Conditions">T&C</a>
<a href="/index.php?title=Datenschutz">Privacy Policy</a>
</div>
</div>
`;
document.body.appendChild(overlay);
const c1 = document.getElementById("c1");
const c2 = document.getElementById("c2");
const c3 = document.getElementById("c3");
const c4 = document.getElementById("c4");
const enter = document.getElementById("enter");
function checkAll(){
if (c1.checked && c2.checked && c3.checked) {
enter.classList.add("active");
enter.disabled = false;
} else {
enter.classList.remove("active");
enter.disabled = true;
}
}
c1.onchange = checkAll;
c2.onchange = checkAll;
c3.onchange = checkAll;
enter.disabled = true;
function loadGoogleAnalytics() {
if (window.__gaLoaded) return;
window.__gaLoaded = true;
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
window.gtag = gtag;
gtag("consent", "default", {
analytics_storage: "granted"
});
const script = document.createElement("script");
script.async = true;
script.src = "https://www.googletagmanager.com/gtag/js?id=" + encodeURIComponent(GA_ID);
document.head.appendChild(script);
gtag("js", new Date());
gtag("config", GA_ID, {
anonymize_ip: true
});
}
enter.onclick = function(){
localStorage.setItem(KEY, String(Date.now()));
if (c4.checked) {
localStorage.setItem(ANALYTICS_KEY, "granted");
loadGoogleAnalytics();
} else {
localStorage.setItem(ANALYTICS_KEY, "denied");
}
overlay.remove();
};
document.getElementById("exit").onclick = function(){
window.location.href = "https://google.com";
};
})();
/* ================================================ */
/* ADMIN CONTROL PANEL - AlphaX Wiki */
/* ================================================ */
(function() {
if (mw.config.get('wgPageName') !== 'AlphaX:Admin_Control_Panel') return;
mw.loader.using(['mediawiki.api'], function() {
var css = [
'#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-hdr{background:#1A1A1A;border-bottom:1px solid #2E2E2E;padding:24px 28px 20px}',
'.axcp-ttl{font-size:22px;font-weight:700;color:#fff;margin-bottom:3px}',
'.axcp-sub{font-size:11px;color:#555;text-transform:uppercase;letter-spacing:.1em}',
'.axcp-sr{display:flex;gap:12px;margin:16px 0 0;flex-wrap:wrap}',
'.axcp-sc{background:#0D0D0D;border:1px solid #2E2E2E;border-radius:12px;padding:14px 18px;flex:1;min-width:90px}',
'.axcp-sv{font-size:24px;font-weight:700;color:#FF6600;line-height:1}',
'.axcp-sl{font-size:10px;color:#555;text-transform:uppercase;letter-spacing:.08em;margin-top:4px}',
'.axcp-ctrl{display:flex;gap:10px;padding:12px 28px;background:#111;border-bottom:1px solid #2E2E2E;flex-wrap:wrap;align-items:center}',
'.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-inp:focus{border-color:rgba(255,102,0,.5)}',
'.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-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-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-rbtn:hover{opacity:.85}',
'.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}',
'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-t thead th:hover{color:#FF6600}',
'table.axcp-t thead th.sa::after{content:" u2191";color:#FF6600}',
'table.axcp-t thead th.sd::after{content:" u2193";color:#FF6600}',
'table.axcp-t td{padding:8px 12px;border-bottom:1px solid rgba(255,255,255,.03);vertical-align:middle}',
'table.axcp-t tr:hover td{background:rgba(255,102,0,.04)}',
'table.axcp-t tr:last-child td{border-bottom:none}',
'.axcp-al{color:#fff;text-decoration:none;font-weight:500;font-size:13px}',
'.axcp-al:hover{color:#FF6600}',
'.axcp-cp{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap}',
'.axcp-st{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600}',
'.s-stu{background:rgba(255,60,60,.15);color:#ff7070;border:1px solid rgba(255,60,60,.25)}',
'.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-bf{height:5px;border-radius:3px;background:linear-gradient(to right,#FF6600,#FF8533)}',
'.axcp-load{text-align:center;padding:80px 40px;color:#555;font-size:15px;background:#0D0D0D}',
'.axcp-prog{color:#FF6600;font-size:13px;margin-top:10px}',
'.axcp-z{color:#333}',
'.axcp-hi{color:#FF6600;font-weight:600}',
'.axcp-wt{color:#3ddc84;font-weight:600}'
].join('');
var sEl = document.createElement('style');
sEl.textContent = css;
document.head.appendChild(sEl);
var contentEl = document.querySelector('#mw-content-text .mw-parser-output') || document.getElementById('mw-content-text');
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 allData = [];
var sortCol = 'title', sortDir = 1, filterText = '', filterCat = '', filterSt = '';
var CC = {
'Sexual Health': 'rgba(255,100,100,.18)|#ff8080',
'Dating, Sex & Relationships': 'rgba(255,182,60,.18)|#ffb83c',
'Kink & BDSM': 'rgba(180,80,255,.18)|#c864ff',
'Culture, History & Politics': 'rgba(80,140,255,.18)|#6496ff',
'Fashion & Visual Signaling': 'rgba(255,220,60,.18)|#ffdc3c',
'Community & Identity': 'rgba(61,220,132,.18)|#3ddc84',
'Drugs, Party Culture & Harm Reduction':'rgba(255,120,40,.18)|#ff8028',
'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('|');
return 'background:'+p[0]+';color:'+p[1]+';border:1px solid '+p[1]+';';
}
function wSt(w) {
if (w < 150) return ['stu','Stub'];
if (w < 500) return ['sho','Short'];
if (w < 1500) return ['med','Medium'];
return ['lon','Long'];
}
function fDate(iso) {
if (!iso) return '<span class="axcp-z">-</span>';
var d = new Date(iso);
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) {
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 render(data) {
var fd = data.filter(function(r) {
var mt = !filterText || r.title.toLowerCase().indexOf(filterText)>=0 || (r.cat||'').toLowerCase().indexOf(filterText)>=0;
var mc = !filterCat || r.cat === filterCat;
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+='</tr></thead><tbody>';
if (!fd.length) {
h+='<tr><td colspan="10" style="text-align:center;padding:50px;color:#333">No articles match your filters.</td></tr>';
}
fd.forEach(function(r){
var st=wSt(r.words||0);
var bp=mxW>0?Math.round(((r.words||0)/mxW)*60):0;
h+='<tr>';
h+='<td><a class="axcp-al" href="/wiki/'+encodeURIComponent((r.title||'').replace(/ /g,'_'))+'" target="_blank">'+esc(r.title)+'</a></td>';
h+='<td>'+(r.cat?'<span class="axcp-cp" style="'+catSty(r.cat)+'">'+esc(r.cat)+'</span>':'<span class="axcp-z">-</span>')+'</td>';
h+='<td style="color:#555;font-size:12px">'+esc(r.subcat||'-')+'</td>';
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-n">'+fKb(r.size)+'</td>';
h+='<td><span class="axcp-st s-'+st[0]+'">'+st[1]+'</span></td>';
h+='<td style="color:#555;white-space:nowrap;font-size:12px">'+fDate(r.touched)+'</td>';
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-n '+(r.links>5?'axcp-hi':'')+'">'+(r.links||0)+'</td>';
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 fi=document.getElementById('axcpf');
if(fi)fi.addEventListener('change',function(){filterSt=this.value;render(allData);});
var ri=document.getElementById('axcpr');
if(ri)ri.addEventListener('click',function(){allData=[];loadData();});
document.querySelectorAll('table.axcp-t thead th').forEach(function(th){
th.addEventListener('click',function(){
var col=this.getAttribute('data-col');
if(sortCol===col){sortDir*=-1;}else{sortCol=col;sortDir=1;}
render(allData);
});
});
}
function apiFetch(url) {
return fetch(url).then(function(r){return r.json();});
}
function loadData() {
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);
});
}
// 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){
if(!d||!d.query||!d.query.pages){return fetchInfoChunk(i+50);}
Object.keys(d.query.pages).forEach(function(pid){
var pg=d.query.pages[pid]; if(!map[pid])return;
map[pid].size=pg.length||0;
map[pid].touched=pg.touched||'';
map[pid].watchers=pg.watchers!=null?Number(pg.watchers):0;
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){
if(!d||!d.query||!d.query.pages){return fetchRevChunk(i+20);}
Object.keys(d.query.pages).forEach(function(pid){
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){
var pg=d.query.pages[pid]; if(!map[pid])return;
map[pid].links=(pg.linkshere||[]).length;
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 raw=slot['*']||slot.content||'';
// 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;
});
return fetchContentChunk(i+8);
});
}
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);
}).catch(function(e){
root.innerHTML='<div class="axcp-load" style="color:#ff7070">⚠ Error: '+e.message+'</div>';
});
}
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();
}
})();
// Add Impressum link to footer places row
(function() {
function addImpressumToFooter() {
// Find the footer-places list
var footerPlaces = document.getElementById('footer-places');
if (!footerPlaces) return;
// Check if Impressum link already exists
if (document.getElementById('footer-places-impressum')) return;
// Create the new list item
var li = document.createElement('li');
li.id = 'footer-places-impressum';
var a = document.createElement('a');
a.href = '/Impressum';
a.textContent = 'Impressum';
li.appendChild(a);
footerPlaces.appendChild(li);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addImpressumToFooter);
} else {
addImpressumToFooter();
}
})();
// ===== Permanent Favicon =====
(function() {
document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]').forEach(function(el) {
el.parentNode.removeChild(el);
});
var link = document.createElement('link');
link.rel = 'icon';
link.type = 'image/png';
link.href = 'https://alphax.wiki/images/2/26/Favicon.png';
document.head.appendChild(link);
})();
// =====================================================
// Users by Country Counter - Admin Page Widget
// =====================================================
(function() {
'use strict';
if (mw.config.get('wgPageName') !== 'User:Admin') return;
var langToCountry = {
'en': '๐บ๐ธ English-speaking', 'de': '๐ฉ๐ช Germany / German-speaking',
'fr': '๐ซ๐ท France / French-speaking', 'es': '๐ช๐ธ Spain / Spanish-speaking',
'pt': '๐ต๐น Portugal / Portuguese-speaking', 'it': '๐ฎ๐น Italy / Italian-speaking',
'nl': '๐ณ๐ฑ Netherlands / Dutch-speaking', 'pl': '๐ต๐ฑ Poland / Polish-speaking',
'ru': '๐ท๐บ Russia / Russian-speaking', 'ja': '๐ฏ๐ต Japan',
'zh': '๐จ๐ณ China / Chinese-speaking', 'ar': '๐ธ๐ฆ Arabic-speaking',
'tr': '๐น๐ท Turkey / Turkish-speaking', 'sv': '๐ธ๐ช Sweden / Swedish-speaking',
'da': '๐ฉ๐ฐ Denmark / Danish-speaking', 'fi': '๐ซ๐ฎ Finland / Finnish-speaking',
'nb': '๐ณ๐ด Norway / Norwegian-speaking', 'cs': '๐จ๐ฟ Czech Republic',
'hu': '๐ญ๐บ Hungary', 'ro': '๐ท๐ด Romania', 'el': '๐ฌ๐ท Greece / Greek-speaking',
'ko': '๐ฐ๐ท Korea / Korean-speaking', 'uk': '๐บ๐ฆ Ukraine / Ukrainian-speaking',
'he': '๐ฎ๐ฑ Israel / Hebrew-speaking', 'id': '๐ฎ๐ฉ Indonesia',
'vi': '๐ป๐ณ Vietnam', 'th': '๐น๐ญ Thailand', 'hi': '๐ฎ๐ณ India / Hindi-speaking',
'bn': '๐ง๐ฉ Bangladesh / Bengali-speaking', 'fa': '๐ฎ๐ท Iran / Persian-speaking'
};
function renderResults(langCounts, totalUsers) {
var container = document.getElementById('country-user-counter');
if (!container) return;
var sorted = Object.keys(langCounts)
.filter(function(l) { return langCounts[l] > 0; })
.sort(function(a, b) { return langCounts[b] - langCounts[a]; });
var html = '<style>';
html += '#cuc-wrap{font-family:sans-serif}';
html += '#cuc-stats{display:flex;gap:20px;margin-bottom:14px;flex-wrap:wrap}';
html += '.cuc-stat-card{background:#222;border:1px solid #444;border-radius:6px;padding:10px 18px;min-width:120px;text-align:center}';
html += '.cuc-stat-num{font-size:1.8em;font-weight:bold;color:#e07000}';
html += '.cuc-stat-lbl{font-size:0.8em;color:#aaa;margin-top:2px}';
html += '#cuc-table{width:100%;border-collapse:collapse;margin-top:4px}';
html += '#cuc-table th{background:#2a2a2a;color:#e07000;padding:8px 12px;text-align:left;border:1px solid #444;font-size:0.9em}';
html += '#cuc-table td{padding:7px 12px;border:1px solid #2e2e2e;color:#ddd;font-size:0.9em}';
html += '#cuc-table tr:nth-child(even) td{background:#1c1c1c}';
html += '#cuc-table tr:hover td{background:#252525}';
html += '.cuc-bar-wrap{background:#111;border-radius:3px;height:14px;overflow:hidden}';
html += '.cuc-bar{height:14px;background:linear-gradient(90deg,#e07000,#ff9a00);border-radius:3px;transition:width 0.4s ease}';
html += '.cuc-no-data{background:#1a1a1a;border:1px dashed #444;border-radius:6px;padding:16px;color:#aaa;line-height:1.6}';
html += '.cuc-no-data code{background:#222;padding:2px 6px;border-radius:3px;color:#e07000;font-size:0.9em}';
html += '#cuc-timestamp{color:#555;font-size:0.8em;margin-top:10px}';
html += '</style>';
html += '<div id="cuc-wrap">';
// Stats cards
html += '<div id="cuc-stats">';
html += '<div class="cuc-stat-card"><div class="cuc-stat-num">' + totalUsers + '</div><div class="cuc-stat-lbl">Total Users</div></div>';
html += '<div class="cuc-stat-card"><div class="cuc-stat-num">' + sorted.length + '</div><div class="cuc-stat-lbl">Countries / Regions</div></div>';
var locatedUsers = sorted.reduce(function(sum, l) { return sum + langCounts[l]; }, 0);
html += '<div class="cuc-stat-card"><div class="cuc-stat-num">' + locatedUsers + '</div><div class="cuc-stat-lbl">Users with Location</div></div>';
html += '</div>';
if (sorted.length === 0) {
html += '<div class="cuc-no-data">';
html += '<strong>๐ No country data available yet.</strong><br>';
html += 'This counter tracks users by country using the <strong>Babel extension</strong> language categories.<br>';
html += 'Users can declare their language/country by adding babel tags to their user page:<br><br>';
html += '<code>{{#babel:en|de|fr}}</code><br><br>';
html += 'Once users add babel tags, this counter will automatically display their country distribution.<br>';
html += 'Currently tracking <strong>' + totalUsers + ' registered users</strong> on this wiki.';
html += '</div>';
} else {
var maxCount = langCounts[sorted[0]] || 1;
html += '<table id="cuc-table"><thead><tr>';
html += '<th style="width:40px">#</th>';
html += '<th>Country / Region</th>';
html += '<th style="width:70px;text-align:center">Users</th>';
html += '<th style="width:200px">Distribution</th>';
html += '</tr></thead><tbody>';
sorted.forEach(function(lang, idx) {
var count = langCounts[lang];
var label = langToCountry[lang] || ('๐ ' + lang.toUpperCase());
var pct = Math.round((count / locatedUsers) * 100);
var barWidth = Math.round((count / maxCount) * 180);
html += '<tr>';
html += '<td style="color:#666;text-align:center">' + (idx + 1) + '</td>';
html += '<td>' + label + '</td>';
html += '<td style="text-align:center;font-weight:bold;color:#e07000">' + count + '</td>';
html += '<td><div class="cuc-bar-wrap"><div class="cuc-bar" style="width:' + barWidth + 'px"></div></div></td>';
html += '</tr>';
});
html += '</tbody></table>';
}
html += '<div id="cuc-timestamp">Last updated: ' + new Date().toLocaleString() + '</div>';
html += '</div>';
container.innerHTML = html;
}
function buildCounter() {
var container = document.getElementById('country-user-counter');
if (!container) return;
container.innerHTML = '<p style="color:#aaa;font-style:italic">โณ Loading user statistics by country...</p>';
var allUsers = [];
var langCounts = {};
var langs = Object.keys(langToCountry);
function fetchUsers(aufrom) {
var params = { action: 'query', list: 'allusers', aulimit: 500, format: 'json' };
if (aufrom) params.aufrom = aufrom;
return $.getJSON(mw.util.wikiScript('api'), params).then(function(data) {
var users = (data.query && data.query.allusers) || [];
allUsers = allUsers.concat(users);
if (data['continue'] && data['continue'].aufrom) return fetchUsers(data['continue'].aufrom);
});
}
function fetchLangCat(lang) {
return $.getJSON(mw.util.wikiScript('api'), {
action: 'query', list: 'categorymembers',
cmtitle: 'Category:User_' + lang,
cmtype: 'page', cmnamespace: 2, cmlimit: 500, format: 'json'
}).then(function(data) {
var m = (data.query && data.query.categorymembers) || [];
if (m.length > 0) langCounts[lang] = (langCounts[lang] || 0) + m.length;
}).catch(function() {});
}
fetchUsers().then(function() {
var d = $.Deferred().resolve().promise();
langs.forEach(function(lang) { d = d.then(function() { return fetchLangCat(lang); }); });
return d;
}).then(function() {
renderResults(langCounts, allUsers.length);
}).catch(function(e) {
container.innerHTML = '<p style="color:#f66">โ ๏ธ Error loading data: ' + String(e) + '</p>';
});
}
$(document).ready(function() { buildCounter(); });
})();
// =====================================================
// End: Users by Country Counter
// =====================================================
// ===== Article Overview Page - Category/Subcategory/Title/Words/Focus Table =====
(function() {
if (mw.config.get('wgPageName') !== 'Article_Overview') return;
var apiBase = mw.util.wikiScript('api');
var allRows = [];
function getCategoryMembers(catName, cmtype, continueParam, accumulated, callback) {
var params = {
action: 'query', list: 'categorymembers',
cmtitle: 'Category:' + catName,
cmlimit: 500, cmtype: cmtype, format: 'json'
};
if (continueParam) params.cmcontinue = continueParam;
$.getJSON(apiBase, params).done(function(data) {
var members = (data.query && data.query.categorymembers) ? data.query.categorymembers : [];
members.forEach(function(m){ accumulated.push(m); });
if (data.continue && data.continue.cmcontinue) {
getCategoryMembers(catName, cmtype, data.continue.cmcontinue, accumulated, callback);
} else {
callback(accumulated);
}
}).fail(function(){ callback(accumulated); });
}
function escHtml(str) {
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
}
function renderTable(rows) {
var tbody = document.getElementById('article-overview-body');
if (!tbody) return;
if (!rows || rows.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" style="padding:10px;text-align:center;color:#aaa;">No articles found.</td></tr>';
return;
}
var html = '';
rows.forEach(function(row, i) {
var bg = i % 2 === 0 ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.07)';
html += '<tr style="background:' + bg + ';">';
html += '<td style="padding:6px 10px;border:1px solid #444;">' + escHtml(row.category) + '</td>';
html += '<td style="padding:6px 10px;border:1px solid #444;">' + escHtml(row.sub) + '</td>';
html += '<td style="padding:6px 10px;border:1px solid #444;"><a href="' + mw.util.getUrl(row.title) + '">' + escHtml(row.title) + '</a></td>';
html += '<td style="padding:6px 10px;border:1px solid #444;text-align:right;">' + escHtml(String(row.words)) + '</td>';
html += '<td style="padding:6px 10px;border:1px solid #444;">' + escHtml(row.focus) + '</td>';
html += '</tr>';
});
tbody.innerHTML = html;
}
function buildTable() {
var status = document.getElementById('overview-status');
if (status) status.textContent = 'Fetching categoriesโฆ';
$.getJSON(apiBase, { action:'query', list:'allcategories', aclimit:500, format:'json' })
.done(function(data) {
var topCats = (data.query && data.query.allcategories) ? data.query.allcategories.map(function(c){ return c['*']; }) : [];
var rows = [];
var totalCats = topCats.length;
var processed = 0;
if (totalCats === 0) {
var tbody = document.getElementById('article-overview-body');
if (tbody) tbody.innerHTML = '<tr><td colspan="5" style="padding:10px;text-align:center;color:#aaa;">No categories found.</td></tr>';
if (status) status.textContent = '';
return;
}
function checkDone() {
processed++;
if (status) status.textContent = 'Loadingโฆ (' + processed + '/' + totalCats + ' categories processed)';
if (processed === totalCats) {
rows.sort(function(a,b){
var ca = a.category.toLowerCase(), cb = b.category.toLowerCase();
if (ca < cb) return -1; if (ca > cb) return 1;
var sa = a.sub.toLowerCase(), sb = b.sub.toLowerCase();
if (sa < sb) return -1; if (sa > sb) return 1;
return a.title.localeCompare(b.title);
});
allRows = rows;
renderTable(rows);
if (status) status.textContent = rows.length + ' article entries loaded.';
}
}
topCats.forEach(function(catName) {
getCategoryMembers(catName, 'page|subcat', null, [], function(members) {
var pages = members.filter(function(m){ return m.ns === 0; });
var subcats = members.filter(function(m){ return m.ns === 14; });
if (pages.length === 0 && subcats.length === 0) {
checkDone(); return;
}
var subTotal = pages.length + subcats.length;
var subProcessed = 0;
function subDone() {
subProcessed++;
if (subProcessed >= subTotal) checkDone();
}
pages.forEach(function(page) {
rows.push({ category: catName, sub: 'โ', title: page.title, words: 'n/a', focus: catName });
subDone();
});
subcats.forEach(function(subcat) {
var subName = subcat.title.replace('Category:','');
getCategoryMembers(subName, 'page', null, [], function(subPages) {
subPages.filter(function(m){ return m.ns === 0; }).forEach(function(page) {
rows.push({ category: catName, sub: subName, title: page.title, words: 'n/a', focus: catName });
});
subDone();
});
});
});
});
})
.fail(function() {
var tbody = document.getElementById('article-overview-body');
if (tbody) tbody.innerHTML = '<tr><td colspan="5" style="padding:10px;text-align:center;color:#f55;">Error loading data. Check API access.</td></tr>';
});
}
function downloadCSV() {
if (!allRows || allRows.length === 0) {
alert('No data to download yet. Please wait for the table to finish loading.');
return;
}
var csv = 'Category,Subcategory,Title,Words,Focus\n';
allRows.forEach(function(row) {
csv += ['category','sub','title','words','focus'].map(function(k){
return '"' + String(row[k]).replace(/"/g,'""') + '"';
}).join(',') + '\n';
});
var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url; a.download = 'article-overview.csv';
document.body.appendChild(a); a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
$(document).ready(function() {
var btn = document.getElementById('download-csv-btn');
if (btn) btn.addEventListener('click', downloadCSV);
buildTable();
});
})();