MediaWiki:Common.js: Difference between revisions

From AlphaX Wiki
Jump to navigation Jump to search
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 AlphaX Wiki           */
/* ADMIN CONTROL PANEL - AlphaX Wiki               */
/* Runs only on AlphaX:Admin_Control_Panel page */
/* ================================================ */
/* ============================================ */
(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() {


    // Inject styles
     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-header { 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-title { 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-subtitle { 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-stats-row { display: flex; gap: 12px; margin: 18px 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-stat { background: #0D0D0D; border: 1px solid #2E2E2E; border-radius: 12px; padding: 14px 20px; flex: 1; min-width: 100px; }
       '.axcp-sv{font-size:24px;font-weight:700;color:#FF6600;line-height:1}',
       .axcp-stat-val { font-size: 26px; 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-stat-label { 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-controls { display: flex; gap: 10px; padding: 14px 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-search { flex: 1; min-width: 180px; background: #1A1A1A; border: 1px solid #2E2E2E; border-radius: 8px; padding: 9px 14px; color: #fff; font-size: 13px; outline: none; }
       '.axcp-inp:focus{border-color:rgba(255,102,0,.5)}',
       .axcp-search::placeholder { color: #444; }
       '.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-search:focus { border-color: rgba(255,102,0,.5); }
       '.axcp-sel:focus{border-color:rgba(255,102,0,.5)}',
       .axcp-select { background: #1A1A1A; border: 1px solid #2E2E2E; border-radius: 8px; padding: 9px 12px; color: #fff; font-size: 12px; outline: none; cursor: pointer; max-width: 200px; }
       '.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-select:focus { border-color: rgba(255,102,0,.5); }
       '.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-count { 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:hover{opacity:.85}',
       .axcp-refresh-btn { background: linear-gradient(135deg,#FF6600,#FF8533); border: none; border-radius: 8px; color: #fff; padding: 9px 18px; font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap; letter-spacing: .03em; }
       '.axcp-tw{overflow-x:auto;padding:0 28px 32px;background:#0D0D0D}',
      .axcp-refresh-btn:hover { opacity: .85; }
       'table.axcp-t{width:100%;border-collapse:collapse;margin-top:14px;font-size:13px}',
       .axcp-table-wrap { overflow-x: auto; padding: 0 28px 32px; background: #0D0D0D; }
       '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-tbl { width: 100%; border-collapse: collapse; margin-top: 14px; font-size: 13px; }
       'table.axcp-t thead th:hover{color:#FF6600}',
       table.axcp-tbl thead th { background: #111; color: #555; text-transform: uppercase; letter-spacing: .08em; font-size: 10px; font-weight: 600; padding: 10px 12px; border-bottom: 1px solid #2E2E2E; cursor: pointer; white-space: nowrap; user-select: none; position: sticky; top: 0; z-index: 2; }
       'table.axcp-t thead th.sa::after{content:" u2191";color:#FF6600}',
       table.axcp-tbl thead th:hover { color: #FF6600; }
       'table.axcp-t thead th.sd::after{content:" u2193";color:#FF6600}',
       table.axcp-tbl thead th.s-asc::after { content: " "; color: #FF6600; }
       'table.axcp-t td{padding:8px 12px;border-bottom:1px solid rgba(255,255,255,.03);vertical-align:middle}',
       table.axcp-tbl thead th.s-desc::after { content: " "; color: #FF6600; }
       'table.axcp-t tr:hover td{background:rgba(255,102,0,.04)}',
       table.axcp-tbl td { padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,.03); vertical-align: middle; }
       'table.axcp-t tr:last-child td{border-bottom:none}',
       table.axcp-tbl tr:hover td { background: rgba(255,102,0,.04); }
       '.axcp-al{color:#fff;text-decoration:none;font-weight:500;font-size:13px}',
       table.axcp-tbl tr:last-child td { border-bottom: none; }
       '.axcp-al:hover{color:#FF6600}',
       .axcp-art-link { color: #fff; text-decoration: none; font-weight: 500; font-size: 13px; }
       '.axcp-cp{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap}',
       .axcp-art-link:hover { color: #FF6600; }
       '.axcp-st{display:inline-block;padding:2px 9px;border-radius:20px;font-size:11px;font-weight:600}',
       .axcp-cat-pill { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 11px; font-weight: 600; white-space: nowrap; }
       '.s-stu{background:rgba(255,60,60,.15);color:#ff7070;border:1px solid rgba(255,60,60,.25)}',
       .axcp-status { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 11px; font-weight: 600; }
       '.s-sho{background:rgba(255,166,0,.15);color:#ffb020;border:1px solid rgba(255,166,0,.25)}',
       .st-stub { background: rgba(255,60,60,.15); color: #ff7070; border: 1px solid rgba(255,60,60,.25); }
       '.s-med{background:rgba(61,220,132,.15);color:#3ddc84;border:1px solid rgba(61,220,132,.25)}',
       .st-short { background: rgba(255,166,0,.15); color: #ffb020; border: 1px solid rgba(255,166,0,.25); }
       '.s-lon{background:rgba(100,160,255,.15);color:#78b0ff;border:1px solid rgba(100,160,255,.25)}',
       .st-medium { background: rgba(61,220,132,.15); color: #3ddc84; border: 1px solid rgba(61,220,132,.25); }
       '.axcp-n{text-align:right;font-variant-numeric:tabular-nums;color:#777}',
       .st-long { background: rgba(100,160,255,.15); color: #78b0ff; border: 1px solid rgba(100,160,255,.25); }
       '.axcp-bw{background:#1A1A1A;border-radius:3px;height:5px;width:60px;display:inline-block;vertical-align:middle;margin-left:6px;overflow:hidden}',
       .axcp-num { text-align: right; font-variant-numeric: tabular-nums; color: #777; }
       '.axcp-bf{height:5px;border-radius:3px;background:linear-gradient(to right,#FF6600,#FF8533)}',
       .axcp-bar-wrap { background: #1A1A1A; border-radius: 3px; height: 5px; width: 60px; display: inline-block; vertical-align: middle; margin-left: 6px; overflow: hidden; }
       '.axcp-load{text-align:center;padding:80px 40px;color:#555;font-size:15px;background:#0D0D0D}',
       .axcp-bar-fill { height: 5px; border-radius: 3px; background: linear-gradient(to right, #FF6600, #FF8533); }
       '.axcp-prog{color:#FF6600;font-size:13px;margin-top:10px}',
       .axcp-loading { text-align: center; padding: 80px 40px; color: #555; font-size: 15px; background: #0D0D0D; }
       '.axcp-z{color:#333}',
       .axcp-progress { color: #FF6600; font-size: 13px; margin-top: 10px; }
       '.axcp-hi{color:#FF6600;font-weight:600}',
       .axcp-zero { color: #333; }
       '.axcp-wt{color:#3ddc84;font-weight:600}'
       .axcp-hi { color: #FF6600; font-weight: 600; }
     ].join('');
       .axcp-watch { color: #3ddc84; font-weight: 600; }
 
     `;
     var sEl = document.createElement('style');
     var styleEl = document.createElement('style');
     sEl.textContent = css;
     styleEl.textContent = css;
     document.head.appendChild(sEl);
     document.head.appendChild(styleEl);


    // Replace page content
     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-loading"><div style="font-size:28px;margin-bottom:16px;">⚙️</div><div>Loading article database&hellip;</div><div class="axcp-progress" id="axcp-prog">Fetching page list&hellip;</div></div></div>';
     contentEl.innerHTML = '<div id="axcp-root"><div class="axcp-load"><div style="font-size:28px;margin-bottom:14px">&#9881;</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 sortDir = 1;
    var filterText = '';
    var filterCat = '';


     var CAT_COLORS = {
     var CC = {
       'Sexual Health':           'rgba(255,100,100,.18)|#ff8080',
       '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':             'rgba(180,80,255,.18)|#c864ff',
       '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': 'rgba(255,220,60,.18)|#ffdc3c',
       'Fashion & Visual Signaling':         'rgba(255,220,60,.18)|#ffdc3c',
       'Community & Identity':     'rgba(61,220,132,.18)|#3ddc84',
       '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':           'rgba(80,200,255,.18)|#50c8ff'
       'Life Planning':                       'rgba(80,200,255,.18)|#50c8ff'
     };
     };
    var KCATS = Object.keys(CC);


    var KNOWN_CATS = Object.keys(CAT_COLORS);
     function catSty(cat) {
 
       var s = CC[cat] || 'rgba(120,120,120,.18)|#888', p = s.split('|');
     function catStyle(cat) {
       var s = CAT_COLORS[cat] || 'rgba(120,120,120,.18)|#888';
      var p = s.split('|');
       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 wordStatus(w) {
       if (w < 150)  return ['stu','Stub'];
       if (w < 150)  return ['stub',   'Stub'];
       if (w < 500)  return ['sho','Short'];
       if (w < 500)  return ['short', 'Short'];
       if (w < 1500) return ['med','Medium'];
       if (w < 1500) return ['medium', 'Medium'];
       return ['lon','Long'];
       return ['long', 'Long'];
     }
     }
 
     function fDate(iso) {
     function fmtDate(iso) {
       if (!iso) return '<span class="axcp-z">-</span>';
       if (!iso) return '<span class="axcp-zero"></span>';
       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 fmtKb(bytes) {
       return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
       if (!bytes) return '<span class="axcp-zero">—</span>';
      return (bytes/1024).toFixed(1)+' <span style="color:#444;font-size:11px">KB</span>';
     }
     }
 
     function setP(msg) { var el = document.getElementById('axcp-prog'); if (el) el.textContent = msg; }
     function esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }


     function render(data) {
     function render(data) {
       var filtered = data.filter(function(r) {
       var fd = data.filter(function(r) {
         var mt = !filterText || r.title.toLowerCase().indexOf(filterText) >= 0 || (r.cat||'').toLowerCase().indexOf(filterText) >= 0 || (r.subcat||'').toLowerCase().indexOf(filterText) >= 0;
         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();


       filtered.sort(function(a,b) {
       var h='';
        var av = a[sortCol], bv = b[sortCol];
      h+='<div class="axcp-hdr">';
        if (typeof av === 'number' && typeof bv === 'number') return sortDir*(av-bv);
      h+='<div class="axcp-ttl">&#9881; Admin Control Panel</div>';
        return sortDir*String(av||'').localeCompare(String(bv||''));
      h+='<div class="axcp-sub">AlphaX Wiki &bull; Article Database &bull; 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>';
      var maxW = Math.max.apply(null, [1].concat(filtered.map(function(r){return r.words||0;})));
       h+='<span class="axcp-cnt">'+fd.length+' articles</span>';
      var totalW = filtered.reduce(function(s,r){return s+(r.words||0);},0);
       h+='<button class="axcp-rbtn" id="axcpr">&#8635; Refresh Data</button>';
      var avgW  = filtered.length ? Math.round(totalW/filtered.length) : 0;
       h+='</div>';
      var totalR = filtered.reduce(function(s,r){return s+(r.revisions||0);},0);
       h+='<div class="axcp-tw"><table class="axcp-t"><thead><tr>';
      var nocat  = filtered.filter(function(r){return !r.cat;}).length;
       [{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'},
      // Build category list
      {k:'touched',l:'Last Edited'},{k:'revisions',l:'Revisions'},
      var cats = {};
      {k:'watchers',l:'Watchers'},{k:'links',l:'Inbound Links'}
      data.forEach(function(r){ if(r.cat) cats[r.cat]=1; });
       ].forEach(function(c){
      var catList = Object.keys(cats).sort();
         var cl=sortCol===c.k?(sortDir===1?'sa':'sd'):'';
 
         h+='<th class="'+cl+'" data-col="'+c.k+'">'+c.l+'</th>';
      var h = '';
 
      // Header
      h += '<div class="axcp-header">';
      h += '<div class="axcp-title">⚙️ Admin Control Panel</div>';
      h += '<div class="axcp-subtitle">AlphaX Wiki · Article Database · Live Data</div>';
      h += '<div class="axcp-stats-row">';
      h += '<div class="axcp-stat"><div class="axcp-stat-val">'+filtered.length+'</div><div class="axcp-stat-label">Articles</div></div>';
      h += '<div class="axcp-stat"><div class="axcp-stat-val">'+avgW.toLocaleString()+'</div><div class="axcp-stat-label">Avg Words</div></div>';
      h += '<div class="axcp-stat"><div class="axcp-stat-val">'+totalW.toLocaleString()+'</div><div class="axcp-stat-label">Total Words</div></div>';
      h += '<div class="axcp-stat"><div class="axcp-stat-val">'+totalR.toLocaleString()+'</div><div class="axcp-stat-label">Total Revisions</div></div>';
      h += '<div class="axcp-stat"><div class="axcp-stat-val">'+catList.length+'</div><div class="axcp-stat-label">Categories</div></div>';
      h += '<div class="axcp-stat"><div class="axcp-stat-val" style="color:'+(nocat>0?'#ff7070':'#3ddc84')+'">'+nocat+'</div><div class="axcp-stat-label">Uncategorised</div></div>';
      h += '</div></div>';
 
      // Controls
      h += '<div class="axcp-controls">';
      h += '<input class="axcp-search" id="axcp-s" type="text" placeholder="🔍 Search articles, categories…" value="'+esc(filterText)+'">';
      h += '<select class="axcp-select" id="axcp-cf"><option value="">All Categories ('+catList.length+')</option>';
      catList.forEach(function(c){ h+='<option value="'+esc(c)+'"'+(filterCat===c?' selected':'')+'>'+esc(c)+'</option>'; });
       h += '</select>';
      h += '<select class="axcp-select" id="axcp-sf"><option value="">All Statuses</option><option value="stub">Stub</option><option value="short">Short</option><option value="medium">Medium</option><option value="long">Long</option></select>';
       h += '<span class="axcp-count">'+filtered.length+' articles</span>';
       h += '<button class="axcp-refresh-btn" id="axcp-reload">Refresh</button>';
       h += '</div>';
 
      // Table
       h += '<div class="axcp-table-wrap"><table class="axcp-tbl">';
      h += '<thead><tr>';
       [
        {k:'title',l:'Article Title'},
        {k:'cat',l:'Category'},
        {k:'subcat',l:'Subcategory'},
        {k:'words',l:'Words'},
        {k:'size',l:'Size'},
        {k:'status',l:'Status'},
        {k:'created',l:'Published'},
        {k:'touched',l:'Last Edited'},
        {k:'revisions',l:'Revisions'},
        {k:'watchers',l:'Watchers'},
        {k:'links',l:'Inbound Links'}
       ].forEach(function(c) {
         var cls = sortCol===c.k ? (sortDir===1?'s-asc':'s-desc') : '';
         h += '<th class="'+cls+'" data-col="'+c.k+'">'+c.l+'</th>';
       });
       });
       h += '</tr></thead><tbody>';
       h+='</tr></thead><tbody>';
 
       if (!fd.length) {
       if (filtered.length === 0) {
         h+='<tr><td colspan="10" style="text-align:center;padding:50px;color:#333">No articles match your filters.</td></tr>';
         h += '<tr><td colspan="11" style="text-align:center;padding:50px;color:#444;">No articles match your filters.</td></tr>';
       }
       }
 
       fd.forEach(function(r){
       filtered.forEach(function(r) {
         var st=wSt(r.words||0);
         var st = wordStatus(r.words||0);
         var bp=mxW>0?Math.round(((r.words||0)/mxW)*60):0;
         var barPct = maxW > 0 ? Math.round(((r.words||0)/maxW)*60) : 0;
         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-art-link" 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>'+(r.cat ? '<span class="axcp-cat-pill" style="'+catStyle(r.cat)+'">'+esc(r.cat)+'</span>' : '<span class="axcp-zero"></span>')+'</td>';
         h+='<td style="color:#555;font-size:12px">'+esc(r.subcat||'-')+'</td>';
         h += '<td style="color:#666;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-num">'+(r.words||0).toLocaleString()+'<span class="axcp-bar-wrap"><span class="axcp-bar-fill" style="width:'+barPct+'px"></span></span></td>';
         h+='<td class="axcp-n">'+fKb(r.size)+'</td>';
         h += '<td class="axcp-num">'+fmtKb(r.size)+'</td>';
         h+='<td><span class="axcp-st s-'+st[0]+'">'+st[1]+'</span></td>';
         h += '<td><span class="axcp-status st-'+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:#666;white-space:nowrap;font-size:12px;">'+fmtDate(r.created)+'</td>';
         h+='<td class="axcp-n '+(r.revisions>20?'axcp-hi':'')+'">'+(r.revisions||0)+'</td>';
        h += '<td style="color:#666;white-space:nowrap;font-size:12px;">'+fmtDate(r.touched)+'</td>';
         h+='<td class="axcp-n '+(r.watchers>0?'axcp-wt':'axcp-z')+'">'+(r.watchers>0?'&#128065; '+r.watchers:'-')+'</td>';
         h += '<td class="axcp-num '+(r.revisions>20?'axcp-hi':'')+'">'+((r.revisions||0))+'</td>';
         h+='<td class="axcp-n '+(r.links>5?'axcp-hi':'')+'">'+(r.links||0)+'</td>';
         h += '<td class="axcp-num '+(r.watchers>0?'axcp-watch':'axcp-zero')+'">'+((r.watchers!=null&&r.watchers>0)? '👁 '+r.watchers : '0')+'</td>';
         h+='</tr>';
         h += '<td class="axcp-num '+(r.links>5?'axcp-hi':'')+'">'+((r.links||0))+'</td>';
         h += '</tr>';
       });
       });
      h+='</tbody></table></div>';
      root.innerHTML=h;


      h += '</tbody></table></div>';
       var si=document.getElementById('axcps');
      root.innerHTML = h;
       if(si)si.addEventListener('input',function(){filterText=this.value.toLowerCase();render(allData);});
 
       var ci=document.getElementById('axcpc');
      // Bind events
       if(ci)ci.addEventListener('change',function(){filterCat=this.value;render(allData);});
       var searchEl = document.getElementById('axcp-s');
       var fi=document.getElementById('axcpf');
       if (searchEl) searchEl.addEventListener('input', function(){ filterText = this.value.toLowerCase(); render(allData); });
       if(fi)fi.addEventListener('change',function(){filterSt=this.value;render(allData);});
 
       var ri=document.getElementById('axcpr');
       var catEl = document.getElementById('axcp-cf');
       if(ri)ri.addEventListener('click',function(){allData=[];loadData();});
       if (catEl) catEl.addEventListener('change', function(){ filterCat = this.value; render(allData); });
       document.querySelectorAll('table.axcp-t thead th').forEach(function(th){
 
         th.addEventListener('click',function(){
       var sfEl = document.getElementById('axcp-sf');
           var col=this.getAttribute('data-col');
       if (sfEl) sfEl.addEventListener('change', function() {
           if(sortCol===col){sortDir*=-1;}else{sortCol=col;sortDir=1;}
        var sv = this.value;
        if (!sv) { render(allData); return; }
        // Filter by status
        var prev = allData;
        var tmp = allData.filter(function(r){ return wordStatus(r.words||0)[0] === sv; });
        allData = prev;
        // Temporary local render
        var savedFilter = filterText;
        filterText = '';
        var fc = filterCat;
        filterCat = '';
        // just re-render with status override
        root.querySelector('#axcp-reload') && void 0;
        render(tmp);
        allData = prev;
        filterText = savedFilter;
        filterCat = fc;
      });
 
       var reloadEl = document.getElementById('axcp-reload');
       if (reloadEl) reloadEl.addEventListener('click', function(){ allData=[]; loadData(); });
 
       document.querySelectorAll('table.axcp-tbl 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);
           render(allData);
         });
         });
Line 503: Line 503:
     }
     }


     function setProgress(msg) {
     function apiFetch(url) {
       var el = document.getElementById('axcp-prog');
       return fetch(url).then(function(r){return r.json();});
      if (el) el.textContent = msg;
     }
     }


     async function loadData() {
     function loadData() {
       root.innerHTML = '<div class="axcp-loading"><div style="font-size:28px;margin-bottom:16px;">⚙️</div><div>Loading article database&hellip;</div><div class="axcp-progress" id="axcp-prog">Step 1/4 Fetching page list…</div></div>';
       root.innerHTML='<div class="axcp-load"><div style="font-size:28px;margin-bottom:14px">&#9881;</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=[];


       try {
       function fetchPages(apc) {
        // 1. Get all page IDs
         var u='/api.php?action=query&list=allpages&apnamespace=0&aplimit=500&format=json';
         var pages = [];
        if(apc) u+='&apcontinue='+encodeURIComponent(apc);
        var apcontinue = null;
        return apiFetch(u).then(function(d){
        do {
          if(!d||!d.query)return;
          var u = '/api.php?action=query&list=allpages&apnamespace=0&aplimit=500&format=json';
           pages=pages.concat(d.query.allpages||[]);
          if (apcontinue) u += '&apcontinue=' + encodeURIComponent(apcontinue);
           var cont=d.continue&&d.continue.apcontinue;
          var r = await fetch(u); var d = await r.json();
          if(cont)return fetchPages(cont);
           pages = pages.concat(d.query.allpages);
         });
           apcontinue = d.continue ? d.continue.apcontinue : null;
      }
        } while (apcontinue);
 
        setProgress('Step 2/4 — Fetching page info & categories for '+pages.length+' articles…');
 
         var map = {};
        pages.forEach(function(p){ map[p.pageid] = {title:p.title,cat:'',subcat:'',words:0,size:0,created:'',touched:'',revisions:0,watchers:null,links:0,status:''}; });
        var ids = pages.map(function(p){return p.pageid;});


        // 2. Info + categories + first revision (creation date)
      // Step 2: info + categories (no revision params to avoid conflicts)
         for (var i=0; i<ids.length; i+=50) {
      function fetchInfoChunk(i) {
          var chunk = ids.slice(i,i+50).join('|');
         if(i>=ids.length) return Promise.resolve();
          var r2 = await fetch('/api.php?action=query&pageids='+chunk+'&prop=info|categories|revisions&inprop=watchers&rvprop=timestamp&rvdir=newer&rvlimit=1&cllimit=15&format=json');
        var chunk=ids.slice(i,i+50).join('|');
           var d2 = await r2.json();
        return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=info|categories&inprop=watchers&cllimit=15&format=json').then(function(d){
           Object.keys(d2.query.pages).forEach(function(pid){
           if(!d||!d.query||!d.query.pages){return fetchInfoChunk(i+50);}
             var pg = d2.query.pages[pid]; if(!map[pid])return;
           Object.keys(d.query.pages).forEach(function(pid){
             map[pid].size   = pg.length || 0;
             var pg=d.query.pages[pid]; if(!map[pid])return;
             map[pid].touched = pg.touched || '';
             map[pid].size=pg.length||0;
             map[pid].watchers = (pg.watchers != null) ? pg.watchers : null;
             map[pid].touched=pg.touched||'';
            if (pg.revisions && pg.revisions[0]) map[pid].created = pg.revisions[0].timestamp || '';
             map[pid].watchers=pg.watchers!=null?Number(pg.watchers):0;
             if (pg.categories) {
             if(pg.categories){pg.categories.forEach(function(c){
              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 (KNOWN_CATS.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;}
                else if (cn.indexOf('stub')<0 && cn.indexOf('article')<0 && cn.length < 60) { if(!map[pid].subcat) map[pid].subcat = cn; }
            });}
              });
            }
           });
           });
         }
          return fetchInfoChunk(i+50);
 
         });
        setProgress('Step 3/4 — Counting revisions…');
      }


        // 3. Revision counts (rvlimit=max gives us up to 500; count them)
      // Step 3: revision counts
         for (var j=0; j<ids.length; j+=20) {
      function fetchRevChunk(i) {
          var chunk2 = ids.slice(j,j+20).join('|');
         if(i>=ids.length) return Promise.resolve();
          var r3 = await fetch('/api.php?action=query&pageids='+chunk2+'&prop=revisions&rvprop=ids&rvlimit=max&format=json');
        var chunk=ids.slice(i,i+20).join('|');
           var d3 = await r3.json();
        return apiFetch('/api.php?action=query&pageids='+chunk+'&prop=revisions&rvprop=ids&rvlimit=max&format=json').then(function(d){
           Object.keys(d3.query.pages).forEach(function(pid){
           if(!d||!d.query||!d.query.pages){return fetchRevChunk(i+20);}
             var pg = d3.query.pages[pid]; if(!map[pid])return;
           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);
         });
      }


        setProgress('Step 4/4 — Calculating word counts & inbound links…');
      // Step 4: word counts + inbound links
 
      function fetchContentChunk(i) {
        // 4. Word counts from wikitext + inbound links
         if(i>=ids.length) return Promise.resolve();
         for (var k=0; k<ids.length; k+=10) {
        var chunk=ids.slice(i,i+8).join('|');
          var chunk3 = ids.slice(k,k+10).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){
          var r4 = await fetch('/api.php?action=query&pageids='+chunk3+'&prop=revisions|linkshere&rvprop=content&rvslots=main&lhnamespace=0&lhlimit=max&format=json');
           if(!d||!d.query||!d.query.pages){return fetchContentChunk(i+8);}
           var d4 = await r4.json();
           Object.keys(d.query.pages).forEach(function(pid){
           Object.keys(d4.query.pages).forEach(function(pid){
             var pg=d.query.pages[pid]; if(!map[pid])return;
             var pg = d4.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 content = slot['*'] || slot.content || '';
             var raw=slot['*']||slot.content||'';
             var clean = content
            // Remove template names but keep parameter text
              .replace(/<!--[sS]*?-->/g,'')
             var c=raw.replace(/\{\{[A-Za-z][^|\}]*\|?/g,'').replace(/\}\}/g,' ');
              .replace(/<[^>]+>/g,' ')
            c=c.replace(/\[\[File:[^\]]+\]\]/gi,' ');
              .replace(/[[File:[^]]+]]/gi,'')
            c=c.replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1');
              .replace(/[[(?:[^]|]+|)?([^]]+)]]/g,'$1')
            c=c.replace(/<[^>]+>/g,' ').replace(/<!--[^>]*-->/g,' ');
              .replace(/{{[sS]*?}}/g,'')
            c=c.replace(/={2,}[^=]+=={2,}/g,' ');
              .replace(/={2,}([^=]+)={2,}/g,'$1')
            c=c.replace(/[|!=*#;:{}\/\[\]]/g,' ');
              .replace(/[|!*#;:]/g,' ')
            c=c.replace(/\s+/g,' ').trim();
              .replace(/https?://S+/g,'')
             map[pid].words=c?c.split(/\s+/).filter(function(w){return w.length>2;}).length:0;
              .replace(/s+/g,' ').trim();
             map[pid].words = clean ? clean.split(/s+/).filter(function(w){return w.length>1;}).length : 0;
           });
           });
         }
          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">&#9888; Error: '+e.message+'</div>';
         root.innerHTML = '<div class="axcp-loading" style="color:#ff7070;">⚠️ Error: '+e.message+'<br><br><button onclick="location.reload()" style="background:#FF6600;border:none;color:#fff;padding:10px 20px;border-radius:8px;cursor:pointer;font-size:14px;">Retry</button></div>';
       });
       }
     }
     }


     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();
  }
})();
})();

Latest revision as of 02:59, 22 April 2026

// 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 () {

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&amp;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">&#9881;</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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
    }
    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">&#9881; Admin Control Panel</div>';
      h+='<div class="axcp-sub">AlphaX Wiki &bull; Article Database &bull; 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">&#8635; 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?'&#128065; '+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">&#9881;</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">&#9888; 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();
  }
})();