MediaWiki:Common.js: Difference between revisions
No edit summary |
Add Life Planning hero CSS; move all 8 cards to ID-based grid fixer |
||
| (16 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 256: | Line 306: | ||
})(); | })(); | ||
/* ================================================ */ | /* ================================================ */ | ||
/* ADMIN CONTROL PANEL | /* ADMIN CONTROL PANEL - AlphaX Wiki */ | ||
/* ================================================ */ | /* ================================================ */ | ||
(function() { | (function() { | ||
| Line 287: | Line 338: | ||
'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{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:hover{color:#FF6600}', | ||
'table.axcp-t thead th.sa::after{content:" | 'table.axcp-t thead th.sa::after{content:" u2191";color:#FF6600}', | ||
'table.axcp-t thead th.sd::after{content:" | '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 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:hover td{background:rgba(255,102,0,.04)}', | ||
| Line 315: | Line 366: | ||
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-load"><div style="font-size:28px;margin-bottom:14px">⚙</div><div>Loading article database | 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'); | ||
| Line 355: | Line 406: | ||
return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); | 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 setP(msg) { var el = document.getElementById('axcp-prog'); if (el) el.textContent = msg; } | ||
| Line 366: | Line 416: | ||
}); | }); | ||
fd.sort(function(a,b) { | fd.sort(function(a,b) { | ||
var av=a[sortCol], bv=b[sortCol]; | var av=a[sortCol]||0, bv=b[sortCol]||0; | ||
if (typeof av==='number'&&typeof bv==='number') return sortDir*(av-bv); | if (typeof av==='number'&&typeof bv==='number') return sortDir*(av-bv); | ||
return sortDir*String(av | return sortDir*String(av).localeCompare(String(bv)); | ||
}); | }); | ||
var mxW = Math.max.apply(null,[1].concat(fd.map(function(r){return r.words||0;}))); | var mxW = Math.max.apply(null,[1].concat(fd.map(function(r){return r.words||0;}))); | ||
| Line 391: | Line 441: | ||
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 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></div>'; | ||
h+='<div class="axcp-ctrl">'; | h+='<div class="axcp-ctrl">'; | ||
h+='<input class="axcp-inp" id="axcps" type="text" placeholder="Search articles or categories..." value="'+esc(filterText)+'">'; | h+='<input class="axcp-inp" id="axcps" type="text" placeholder="Search articles or categories..." value="'+esc(filterText)+'">'; | ||
| Line 405: | Line 454: | ||
h+='<button class="axcp-rbtn" id="axcpr">↻ Refresh Data</button>'; | h+='<button class="axcp-rbtn" id="axcpr">↻ Refresh Data</button>'; | ||
h+='</div>'; | h+='</div>'; | ||
h+='<div class="axcp-tw"><table class="axcp-t"><thead><tr>'; | h+='<div class="axcp-tw"><table class="axcp-t"><thead><tr>'; | ||
[{k:'title',l:'Article'},{k:'cat',l:'Category'},{k:'subcat',l:'Subcategory'}, | [{k:'title',l:'Article'},{k:'cat',l:'Category'},{k:'subcat',l:'Subcategory'}, | ||
{k:'words',l:'Words'},{k:'size',l:'Size'},{k:'status',l:'Status'}, | {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){ | ].forEach(function(c){ | ||
var cl=sortCol===c.k?(sortDir===1?'sa':'sd'):''; | var cl=sortCol===c.k?(sortDir===1?'sa':'sd'):''; | ||
| Line 416: | Line 464: | ||
}); | }); | ||
h+='</tr></thead><tbody>'; | h+='</tr></thead><tbody>'; | ||
if (!fd.length) { | if (!fd.length) { | ||
h+='<tr><td colspan=" | h+='<tr><td colspan="10" style="text-align:center;padding:50px;color:#333">No articles match your filters.</td></tr>'; | ||
} | } | ||
fd.forEach(function(r){ | fd.forEach(function(r){ | ||
| Line 430: | Line 477: | ||
h+='<td class="axcp-n">'+fKb(r.size)+'</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><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 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.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:' | 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+='<td class="axcp-n '+(r.links>5?'axcp-hi':'')+'">'+(r.links||0)+'</td>'; | ||
h+='</tr>'; | h+='</tr>'; | ||
| Line 462: | Line 508: | ||
function loadData() { | function loadData() { | ||
root.innerHTML='<div class="axcp-load"><div style="font-size:28px;margin-bottom:14px">⚙</div><div>Loading article database | 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=[]; | var pages=[], map={}, ids=[]; | ||
| Line 470: | Line 515: | ||
if(apc) u+='&apcontinue='+encodeURIComponent(apc); | if(apc) u+='&apcontinue='+encodeURIComponent(apc); | ||
return apiFetch(u).then(function(d){ | return apiFetch(u).then(function(d){ | ||
pages=pages.concat(d.query.allpages); | 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) { | function fetchInfoChunk(i) { | ||
if(i>=ids.length) return Promise.resolve(); | if(i>=ids.length) return Promise.resolve(); | ||
var chunk=ids.slice(i,i+50).join('|'); | var chunk=ids.slice(i,i+50).join('|'); | ||
return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=info|categories | 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){ | Object.keys(d.query.pages).forEach(function(pid){ | ||
var pg=d.query.pages[pid]; if(!map[pid])return; | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
map[pid].size=pg.length||0; | map[pid].size=pg.length||0; | ||
map[pid].touched=pg.touched||''; | map[pid].touched=pg.touched||''; | ||
map[pid].watchers=pg.watchers!=null?pg.watchers:0 | map[pid].watchers=pg.watchers!=null?Number(pg.watchers):0; | ||
if(pg.categories){pg.categories.forEach(function(c){ | if(pg.categories){pg.categories.forEach(function(c){ | ||
var cn=c.title.replace('Category:',''); | var cn=c.title.replace('Category:',''); | ||
if(KCATS.indexOf(cn)>=0){if(!map[pid].cat)map[pid].cat=cn;} | if(KCATS.indexOf(cn)>=0){if(!map[pid].cat)map[pid].cat=cn;} | ||
else if(cn.length<60&&cn.indexOf('stub')<0){if(!map[pid].subcat)map[pid].subcat=cn;} | else if(cn.length<60&&cn.toLowerCase().indexOf('stub')<0){if(!map[pid].subcat)map[pid].subcat=cn;} | ||
});} | });} | ||
}); | }); | ||
| Line 495: | Line 543: | ||
} | } | ||
// Step 3: revision counts | |||
function fetchRevChunk(i) { | function fetchRevChunk(i) { | ||
if(i>=ids.length) return Promise.resolve(); | if(i>=ids.length) return Promise.resolve(); | ||
var chunk=ids.slice(i,i+20).join('|'); | 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){ | 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){ | Object.keys(d.query.pages).forEach(function(pid){ | ||
var pg=d.query.pages[pid]; if(!map[pid])return; | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
| Line 507: | Line 557: | ||
} | } | ||
// Step 4: word counts + inbound links | |||
function fetchContentChunk(i) { | function fetchContentChunk(i) { | ||
if(i>=ids.length) return Promise.resolve(); | if(i>=ids.length) return Promise.resolve(); | ||
var chunk=ids.slice(i,i+8).join('|'); | 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){ | 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(d.query.pages).forEach(function(pid){ | ||
var pg=d.query.pages[pid]; if(!map[pid])return; | var pg=d.query.pages[pid]; if(!map[pid])return; | ||
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||''; | ||
// 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); | return fetchContentChunk(i+8); | ||
| Line 534: | Line 585: | ||
fetchPages(null).then(function(){ | fetchPages(null).then(function(){ | ||
setP('Step 2/4 | 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 | 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;}); | ids=pages.map(function(p){return p.pageid;}); | ||
return fetchInfoChunk(0); | return fetchInfoChunk(0); | ||
}).then(function(){ | }).then(function(){ | ||
setP('Step 3/4 | setP('Step 3/4 -- Counting revisions...'); | ||
return fetchRevChunk(0); | return fetchRevChunk(0); | ||
}).then(function(){ | }).then(function(){ | ||
setP('Step 4/4 | setP('Step 4/4 -- Calculating word counts & inbound links...'); | ||
return fetchContentChunk(0); | return fetchContentChunk(0); | ||
}).then(function(){ | }).then(function(){ | ||
| Line 554: | Line 605: | ||
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(); | |||
} | |||
})(); | })(); | ||