MediaWiki:Common.js: Difference between revisions

No edit summary
No edit summary
Line 256: Line 256:


})();
})();


/* ================================================ */
/* ================================================ */
/* ADMIN CONTROL PANEL -- AlphaX Wiki                */
/* ADMIN CONTROL PANEL - AlphaX Wiki                */
/* ================================================ */
/* ================================================ */
(function() {
(function() {
Line 287: Line 288:
       '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 316:


     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 356:
       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 366:
       });
       });
       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 391:
       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 404:
       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 414:
       });
       });
       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 427:
         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 458:


     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 465:
         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 493:
       }
       }


      // 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 507:
       }
       }


      // 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;
Line 516: Line 518:
             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 c=slot['*']||slot.content||'';
             content=content
             c=c.replace(/\{\{[\s\S]*?\}\}/g,' ')
              .replace(/\{\{[\s\S]*?\}\}/g,' ')
               .replace(/\[\[File:[^\]]+\]\]/gi,' ')
               .replace(/\[\[File:[^\]]+\]\]/gi,' ')
               .replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1')
               .replace(/\[\[(?:[^\]|]+\|)?([^\]]+)\]\]/g,'$1')
Line 524: Line 525:
               .replace(/<\!--[\s\S]*?-->/g,' ')
               .replace(/<\!--[\s\S]*?-->/g,' ')
               .replace(/={2,}[^=]+=={2,}/g,' ')
               .replace(/={2,}[^=]+=={2,}/g,' ')
               .replace(/[|!*#;:[]]/g,' ')
               .replace(/[|!*#;:]/g,' ')
               .replace(/https?:\/\/\S+/g,' ')
               .replace(/https?:\/\/\S+/g,' ')
               .replace(/\s+/g,' ').trim();
               .replace(/\s+/g,' ').trim();
             map[pid].words=content?content.split(/s+/).filter(function(w){return w.length>1;}).length:0;
             map[pid].words=c?c.split(/\s+/).filter(function(w){return w.length>1;}).length:0;
           });
           });
           return fetchContentChunk(i+8);
           return fetchContentChunk(i+8);
Line 534: Line 535:


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