MediaWiki:Common.js: Difference between revisions

No edit summary
Add Life Planning hero CSS; move all 8 cards to ID-based grid fixer
 
(13 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 -- AlphaX Wiki                */
/* 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:" ";color:#FF6600}',
       'table.axcp-t thead th.sa::after{content:" u2191";color:#FF6600}',
       'table.axcp-t thead th.sd::after{content:" ";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 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">&#9881;</div><div>Loading article database&hellip;</div><div class="axcp-prog" 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');
Line 355: Line 406:
       return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
       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 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||'').localeCompare(String(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 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">&#8635; Refresh Data</button>';
       h+='<button class="axcp-rbtn" id="axcpr">&#8635; 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:'created',l:'Published'},{k:'touched',l:'Last Edited'},
       {k:'touched',l:'Last Edited'},{k:'revisions',l:'Revisions'},
      {k:'revisions',l:'Revisions'},{k:'watchers',l:'Watchers'},{k:'links',l:'Inbound Links'}
      {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="11" style="text-align:center;padding:50px;color:#333">No articles match your filters.</td></tr>';
         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.created)+'</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?'&#128065; '+r.watchers:'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+='<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">&#9881;</div><div>Loading article database&hellip;</div><div class="axcp-prog" id="axcp-prog">Step 1/4 &mdash; Fetching page list&hellip;</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=[];
       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;
           if(d.continue&&d.continue.apcontinue) return fetchPages(d.continue.apcontinue);
           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|revisions&inprop=watchers&rvprop=timestamp&rvdir=newer&rvlimit=1&cllimit=15&format=json').then(function(d){
         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.revisions&&pg.revisions[0])map[pid].created=pg.revisions[0].timestamp||'';
             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 content=slot['*']||slot.content||'';
             var raw=slot['*']||slot.content||'';
             content=content
             // Remove template names but keep parameter text
              .replace(/\{\{[\s\S]*?\}\}/g,' ')
            var c=raw.replace(/\{\{[A-Za-z][^|\}]*\|?/g,'').replace(/\}\}/g,' ');
              .replace(/\[\[File:[^\]]+\]\]/gi,' ')
            c=c.replace(/\[\[File:[^\]]+\]\]/gi,' ');
              .replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1')
            c=c.replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1');
              .replace(/<[^>]+>/g,' ')
            c=c.replace(/<[^>]+>/g,' ').replace(/<!--[^>]*-->/g,' ');
              .replace(/<\!--[\s\S]*?-->/g,' ')
            c=c.replace(/={2,}[^=]+=={2,}/g,' ');
              .replace(/={2,}[^=]+=={2,}/g,' ')
            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=content?content.split(/s+/).filter(function(w){return w.length>1;}).length:0;
           });
           });
           return fetchContentChunk(i+8);
           return fetchContentChunk(i+8);
Line 534: Line 585:


       fetchPages(null).then(function(){
       fetchPages(null).then(function(){
         setP('Step 2/4 -- Fetching info & categories for '+pages.length+' articles...');
         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,created:'',touched:'',revisions:0,watchers:0,links: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);
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();
  }
})();
})();