User:Polygnotus/Scripts/SourceTable3.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Polygnotus/Scripts/SourceTable3.
// AfD Source Assessment Tool //Features: //Continue where you left off //autopopulate the refs /* 2add: - give eachs ref an archive.org link by default if none exists? -numbering the wikitable - bug: comments aren't displayed yet */ /* [[User:Polygnotus/Scripts/SourceTable]] [[Wikipedia:User_scripts/Requests#Source_assessing]] [[Template:Source assess table]] used in [[Wikipedia:Articles for deletion/Nambiar Builders]] is a lot like [[User:Polygnotus/Scripts/SourceTable]]. I should also check out [[User:Awesome Aasim/xfdvote]]. See also [[Wikipedia:Notability_(organizations_and_companies)#How_to_apply_the_criteria]] -- [[Wikipedia_talk:WikiProject_Articles_for_creation#Source_Assessment_Tables]] https://en.wikipedia.org/wiki/Wikipedia:User_scripts/Requests/Archive_10#h-Source_assessing-20241206231600 There's also {{ORGCRIT assess table}}, which is a variation specific to WP:NCORP (organizations and companies notability criteria). */ // Only run on AfD edit pages varcurrentAction=mw.config.get('wgAction'); if(mw.config.get('wgPageName').startsWith('Wikipedia:Articles_for_deletion/')&& (currentAction==='edit'||currentAction==='submit')){ $(function(){ console.log('AfD Source Assessment script running...'); // Add button to page heading if($('#firstHeading').length){ $('<button>') .attr('id','assessSourcesButton') .text('Assess Sources') .css({ 'margin-left':'10px', 'margin-bottom':'5px', 'display':'inline-block', 'vertical-align':'middle', 'font-size':'15px', 'padding':'4px 8px' }) .click(function(e){ e.preventDefault(); showSourceAssessmentPopup(); }) .appendTo('#firstHeading'); console.log('Assess Sources button added'); }elseif($('#editform').length){ $('<button>') .attr('id','assessSourcesButton') .text('Assess Sources') .css({ 'margin':'10px 0', 'display':'block' }) .click(function(e){ e.preventDefault(); showSourceAssessmentPopup(); }) .prependTo('#editform'); console.log('Assess Sources button added to editform'); }elseif($('.editButtons').length){ // Fallback for preview pages $('<button>') .attr('id','assessSourcesButton') .text('Assess Sources') .css({ 'margin':'10px 0', 'display':'inline-block', 'padding':'4px 8px' }) .click(function(e){ e.preventDefault(); showSourceAssessmentPopup(); }) .prependTo('.editButtons'); console.log('Assess Sources button added to editButtons'); } }); } // Global state varassessmentState={ sources:[], assessments:[] }; functionshowSourceAssessmentPopup(){ // Extract article name varafdPageName=mw.config.get('wgPageName'); vararticleName=extractArticleName(afdPageName); console.log('Loading sources for:',articleName); // Always try to load state from existing wikicode in textbox first vareditTextarea=document.getElementById('wpTextbox1'); if(!editTextarea){ mw.notify('Error: Could not find edit textarea.',{type:'error'}); return; } varexistingState=parseExistingAssessment(editTextarea.value); if(existingState){ // Use existing state from textbox assessmentState=existingState; console.log('Loaded existing state with',existingState.sources.length,'sources'); console.log('First source assessments:',existingState.assessments[0]); if(existingState.assessments.length>1){ console.log('Second source assessments:',existingState.assessments[1]); } displayPopup(); }else{ // No existing assessment found, fetch article content to extract sources console.log('No existing assessment found, fetching article sources...'); newmw.Api().get({ action:'query', prop:'revisions', rvprop:'content', titles:articleName, formatversion:2 }).done(function(data){ try{ if(!data.query||!data.query.pages||data.query.pages.length===0){ mw.notify('Error: Could not retrieve article data.',{type:'error'}); return; } varpage=data.query.pages[0]; if(page.missing){ mw.notify('The article "'+articleName+'" could not be found.',{type:'error'}); return; } if(!page.revisions||page.revisions.length===0){ mw.notify('No revisions found for the article.',{type:'error'}); return; } varpageContent=page.revisions[0].content; varreferences=extractReferences(pageContent); if(references.length===0){ mw.notify('No references found in the article.',{type:'warning'}); return; } // Initialize state assessmentState.sources=references; assessmentState.assessments=references.map(function(){ return{ independent:'', reliable:'', significant:'', comments:'' }; }); displayPopup(); }catch(error){ mw.notify('An error occurred: '+error.message,{type:'error'}); console.error('Error:',error); } }).fail(function(error){ mw.notify('Failed to fetch article content.',{type:'error'}); console.error('API error:',error); }); } } functiondisplayPopup(){ // Remove existing popup if present $('#sourceAssessmentPopup').remove(); // Create popup overlay varpopup=$('<div>') .attr('id','sourceAssessmentPopup') .css({ 'position':'fixed', 'top':'0', 'left':'0', 'width':'100%', 'height':'100%', 'background':'rgba(0,0,0,0.5)', 'z-index':'10000', 'display':'flex', 'align-items':'center', 'justify-content':'center', 'overflow':'auto' }); // Create popup content varcontent=$('<div>') .css({ 'background':'white', 'padding':'20px', 'border-radius':'5px', 'width':'90%', 'max-height':'90%', 'overflow':'auto', 'position':'relative' }); // Add close button to top right varcloseButton=$('<button>') .text(×ばつ') .css({ 'position':'absolute', 'top':'10px', 'right':'10px', 'border':'none', 'background':'#f0f0f0', 'font-size':'24px', 'width':'30px', 'height':'30px', 'border-radius':'50%', 'cursor':'pointer', 'line-height':'1', 'padding':'0' }) .hover( function(){$(this).css('background','#e0e0e0');}, function(){$(this).css('background','#f0f0f0');} ) .click(function(){ popup.remove(); }); content.append(closeButton); // Add title content.append($('<h2>').text('Source Assessment for WP:GNG').css('margin-top','0')); // Create table vartable=$('<table>') .attr('id','sourceAssessmentTable') .css({ 'width':'100%', 'border-collapse':'collapse', 'margin':'20px 0' }); // Add header varthead=$('<thead>'); varheaderRow=$('<tr>'); ['#','Source','Independent','Reliable','Significant Coverage','Count Toward GNG','Comments'].forEach(function(header){ varth=$('<th>').text(header).css({ 'border':'1px solid #ccc', 'padding':'8px', 'background':'#f0f0f0', 'text-align':'left' }); // Set specific width for Count Toward GNG column if(header==='Count Toward GNG'){ th.css('width','20px'); } headerRow.append(th); }); thead.append(headerRow); table.append(thead); // Add body vartbody=$('<tbody>'); assessmentState.sources.forEach(function(source,index){ varrow=createAssessmentRow(source,index); tbody.append(row); }); table.append(tbody); content.append(table); // Add buttons varbuttonDiv=$('<div>').css({'text-align':'right','margin-top':'10px'}); $('<button>') .text('Generate Wikicode') .css({'margin-right':'10px','padding':'8px 16px'}) .click(function(){ generateWikicode(); }) .appendTo(buttonDiv); $('<button>') .text('Close') .css({'padding':'8px 16px'}) .click(function(){ popup.remove(); }) .appendTo(buttonDiv); content.append(buttonDiv); popup.append(content); $('body').append(popup); } functioncreateAssessmentRow(source,index){ varassessment=assessmentState.assessments[index]; varrow=$('<tr>').attr('data-index',index); // Number column varnumberCell=$('<td>').css({ 'border':'1px solid #ccc', 'padding':'8px', 'text-align':'center', 'font-weight':'bold' }).text(index+1); row.append(numberCell); // Source column varsourceCell=$('<td>').css({ 'border':'1px solid #ccc', 'padding':'8px', 'max-width':'300px', 'overflow':'auto' }).html(source); row.append(sourceCell); // Create radio button option boxes for Independent, Reliable, Significant Coverage ['independent','reliable','significant'].forEach(function(field){ varcell=$('<td>').css({ 'border':'1px solid #ccc', 'padding':'8px' }); varradioGroup=$('<div>').css({'display':'flex','flex-direction':'column','gap':'4px'}); [ {value:'yes',text:'Yes'}, {value:'no',text:'No'}, {value:'unknown',text:'Unknown'}, {value:'partial',text:'Partial'} ].forEach(function(option){ varradioId='radio_'+index+'_'+field+'_'+option.value; varlabel=$('<label>').css({'display':'flex','align-items':'center','cursor':'pointer'}); varradio=$('<input>') .attr('type','radio') .attr('name','radio_'+index+'_'+field) .attr('id',radioId) .val(option.value) .css({'margin-right':'4px'}); if(assessment[field]===option.value){ radio.attr('checked','checked'); } radio.change(function(){ assessmentState.assessments[index][field]=$(this).val().trim(); updateRowStatus(row,index); }); label.append(radio); label.append($('<span>').text(option.text)); radioGroup.append(label); }); cell.append(radioGroup); row.append(cell); }); // Count Toward GNG column vargngCell=$('<td>').css({ 'border':'1px solid #ccc', 'padding':'8px', 'text-align':'center', 'width':'20px' }).attr('data-gng',''); row.append(gngCell); // Comments column varcommentsCell=$('<td>').css({ 'border':'1px solid #ccc', 'padding':'8px' }); varcommentsTextarea=$('<textarea>') .css({'width':'100%','min-height':'40px'}) .val(assessment.comments) .on('input',function(){ assessmentState.assessments[index].comments=$(this).val().trim(); }); commentsCell.append(commentsTextarea); row.append(commentsCell); // Update initial status updateRowStatus(row,index); returnrow; } functionupdateRowStatus(row,index){ varassessment=assessmentState.assessments[index]; vargngCell=row.find('td[data-gng]'); varallYes=assessment.independent==='yes'&& assessment.reliable==='yes'&& assessment.significant==='yes'; if(allYes){ row.css('background-color','#d4edda'); gngCell.html('<img alt="Yes" src="//upload.wikimedia.org/wikipedia/commons/thumb/2/26/Check-green.svg/20px-Check-green.svg.png" width="13" height="13">'); gngCell.attr('data-gng','yes'); }elseif(assessment.independent||assessment.reliable||assessment.significant){ row.css('background-color','#f8d7da'); gngCell.html('<img alt="No" src="//upload.wikimedia.org/wikipedia/commons/thumb/4/48/Dark_Red_x.svg/20px-Dark_Red_x.svg.png" width="13" height="13">'); gngCell.attr('data-gng','no'); }else{ row.css('background-color','white'); gngCell.html(''); gngCell.attr('data-gng',''); } } functiongenerateWikicode(){ varwikicode='\n\n{{User:Polygnotus/Templates/SourceAssessTable\n'; assessmentState.sources.forEach(function(source,index){ varassessment=assessmentState.assessments[index]; varnum=index+1; // Escape pipe characters wikicode+='|src'+num+'='+source+'\n'; wikicode+='|num'+num+'='+num+'\n'; wikicode+='|ind'+num+'='+assessment.independent+'\n'; wikicode+='|rel'+num+'='+assessment.reliable+'\n'; wikicode+='|sig'+num+'='+assessment.significant+'\n'; wikicode+='|comments'+num+'='+assessment.comments+'\n'; }); wikicode+='}}\n'; // Insert into edit textarea vareditTextarea=document.getElementById('wpTextbox1'); if(editTextarea){ // Remove existing assessment if present varcurrentContent=editTextarea.value; varpattern=/\n*\{\{User:Polygnotus\/Templates\/SourceAssessTable[\s\S]*?\}\}\n*/; currentContent=currentContent.replace(pattern,''); editTextarea.value=currentContent+wikicode; $(editTextarea).trigger('change'); mw.notify('Source assessment added to edit area.',{type:'success'}); $('#sourceAssessmentPopup').remove(); }else{ mw.notify('Failed to find edit textarea.',{type:'error'}); } } functionparseExistingAssessment(content){ // Check if there's already an assessment in the content // We need to find the template and handle nested braces properly varstartPattern=/\{\{User:Polygnotus\/Templates\/SourceAssessTable/; varstartMatch=content.search(startPattern); if(startMatch===-1){ console.log('No existing assessment template found'); returnnull; } // Find the matching closing braces by counting varbraceCount=0; varinTemplate=false; vartemplateEnd=-1; for(vari=startMatch;i<content.length-1;i++){ if(content[i]==='{'&&content[i+1]==='{'){ braceCount++; inTemplate=true; i++;// Skip next character }elseif(content[i]==='}'&&content[i+1]==='}'){ braceCount--; if(braceCount===0&&inTemplate){ templateEnd=i+2; break; } i++;// Skip next character } } if(templateEnd===-1){ console.log('Could not find end of template'); returnnull; } varfullTemplate=content.substring(startMatch,templateEnd); console.log('Found template, length:',fullTemplate.length); // Extract just the parameters (between first newline after template name and closing braces) vartemplateNameEnd=fullTemplate.indexOf('\n'); if(templateNameEnd===-1){ console.log('Template has no parameters'); returnnull; } vartemplateContent=fullTemplate.substring(templateNameEnd+1,fullTemplate.length-2); varsources=[]; varassessments=[]; // Parse parameters - handle multiline values varparams={}; varcurrentParam=null; varcurrentValue=''; varlines=templateContent.split('\n'); for(vari=0;i<lines.length;i++){ varline=lines[i]; // Check if this line starts a new parameter varparamMatch=line.match(/^\|([a-zA-Z]+\d+)=(.*)$/); if(paramMatch){ // Save previous parameter if exists if(currentParam){ params[currentParam]=currentValue; } // Start new parameter currentParam=paramMatch[1]; currentValue=paramMatch[2]; }elseif(currentParam&&line.trim()!==''){ // Continue previous parameter value (multiline) currentValue+='\n'+line; } } // Don't forget to save the last parameter if(currentParam){ params[currentParam]=currentValue; } console.log('Parsed parameters:',params); // Determine number of sources varmaxNum=0; Object.keys(params).forEach(function(key){ varnumMatch=key.match(/\d+$/); if(numMatch){ varnum=parseInt(numMatch[0]); if(num>maxNum)maxNum=num; } }); console.log('Found',maxNum,'sources'); // Extract sources and assessments for(vari=1;i<=maxNum;i++){ if(params['src'+i]){ varsource=params['src'+i].replace(/\{\{!\}\}/g,'|').trim(); sources.push(source); assessments.push({ independent:(params['ind'+i]||'').trim(), reliable:(params['rel'+i]||'').trim(), significant:(params['sig'+i]||'').trim(), comments:(params['comments'+i]||'').trim() }); } } if(sources.length===0){ console.log('No sources found in template'); returnnull; } console.log('Successfully parsed',sources.length,'sources with assessments'); return{ sources:sources, assessments:assessments }; } functionextractArticleName(afdPageName){ vararticleName=afdPageName.replace('Wikipedia:Articles_for_deletion/',''); // Handle nomination numbers varnominationMatch=articleName.match(/(.*) \((\d+)(st|nd|rd|th) nomination\)$/); if(nominationMatch){ articleName=nominationMatch[1]; } returnarticleName; } functionextractReferences(content){ varreferences=[]; varindex=0; while(true){ varstartIndex=content.indexOf('<ref',index); if(startIndex===-1)break; varendIndex=content.indexOf('>',startIndex); if(endIndex===-1)break; vartagContent=content.substring(startIndex,endIndex+1); // Skip self-closing tags if(tagContent.endsWith('/>')){ index=endIndex+1; continue; } varrefEndIndex=content.indexOf('</ref>',endIndex); if(refEndIndex===-1)break; varrefContent=content.substring(endIndex+1,refEndIndex).trim(); // Skip empty refs if(refContent){ references.push(refContent); } index=refEndIndex+6; } console.log('Extracted',references.length,'references'); returnreferences; }