User:Polygnotus/Scripts/SourceTable4.js
Appearance
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/SourceTable4.
// <nowiki>
// Only run on AfD edit pages
var currentAction = 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');
} else if ($('#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');
} else if ($('.editButtons').length) {
$('<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
var assessmentState = {
sources: [],
assessments: []
};
function showSourceAssessmentPopup() {
var afdPageName = mw.config.get('wgPageName');
var articleName = extractArticleName(afdPageName);
console.log('Loading sources for:', articleName);
var editTextarea = document.getElementById('wpTextbox1');
if (!editTextarea) {
mw.notify('Error: Could not find edit textarea.', {type: 'error'});
return;
}
var existingState = parseExistingAssessment(editTextarea.value);
if (existingState) {
assessmentState = existingState;
console.log('Loaded existing state with', existingState.sources.length, 'sources');
displayPopup();
} else {
console.log('No existing assessment found, fetching article sources...');
new mw.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;
}
var page = 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;
}
var pageContent = page.revisions[0].content;
var references = extractReferences(pageContent);
if (references.length === 0) {
mw.notify('No references found in the article.', {type: 'warning'});
return;
}
assessmentState.sources = references;
assessmentState.assessments = references.map(function() {
return {
independent: '',
reliable: '',
significant: '',
gng: '',
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);
});
}
}
function displayPopup() {
$('#sourceAssessmentPopup').remove();
var popup = $('<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'
});
var content = $('<div>')
.css({
'background': 'white',
'padding': '20px',
'border-radius': '5px',
'width': '90%',
'max-height': '90%',
'overflow': 'auto',
'position': 'relative'
});
var closeButton = $('<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);
content.append($('<h2>').text('Source Assessment for WP:GNG').css('margin-top', '0'));
var table = $('<table>')
.attr('id', 'sourceAssessmentTable')
.css({
'width': '100%',
'border-collapse': 'collapse',
'margin': '20px 0'
});
var thead = $('<thead>');
var headerRow = $('<tr>');
['#', 'Source', 'Independent', 'Reliable', 'Significant Coverage', 'Count Toward GNG', 'Comments'].forEach(function(header) {
var th = $('<th>').text(header).css({
'border': '1px solid #ccc',
'padding': '8px',
'background': '#f0f0f0',
'text-align': 'left'
});
if (header === 'Count Toward GNG') {
th.css('width', '20px');
}
headerRow.append(th);
});
thead.append(headerRow);
table.append(thead);
var tbody = $('<tbody>');
assessmentState.sources.forEach(function(source, index) {
var row = createAssessmentRow(source, index);
tbody.append(row);
});
table.append(tbody);
content.append(table);
var buttonDiv = $('<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);
}
function createAssessmentRow(source, index) {
var assessment = assessmentState.assessments[index];
var row = $('<tr>').attr('data-index', index);
var numberCell = $('<td>').css({
'border': '1px solid #ccc',
'padding': '8px',
'text-align': 'center',
'font-weight': 'bold'
}).text(index + 1);
row.append(numberCell);
var sourceCell = $('<td>').css({
'border': '1px solid #ccc',
'padding': '8px',
'max-width': '300px',
'overflow': 'auto'
}).html(source);
row.append(sourceCell);
['independent', 'reliable', 'significant'].forEach(function(field) {
var cell = $('<td>').css({
'border': '1px solid #ccc',
'padding': '8px'
});
var radioGroup = $('<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) {
var radioId = 'radio_' + index + '_' + field + '_' + option.value;
var label = $('<label>').css({'display': 'flex', 'align-items': 'center', 'cursor': 'pointer'});
var radio = $('<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);
});
var gngCell = $('<td>').css({
'border': '1px solid #ccc',
'padding': '8px',
'text-align': 'center',
'width': '20px'
}).attr('data-gng', '');
row.append(gngCell);
var commentsCell = $('<td>').css({
'border': '1px solid #ccc',
'padding': '8px'
});
var commentsTextarea = $('<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);
updateRowStatus(row, index);
return row;
}
function updateRowStatus(row, index) {
var assessment = assessmentState.assessments[index];
var gngCell = row.find('td[data-gng]');
var allYes = 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">');
assessmentState.assessments[index].gng = 'yes';
} else if (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">');
assessmentState.assessments[index].gng = 'no';
} else {
row.css('background-color', 'white');
gngCell.html('');
assessmentState.assessments[index].gng = '';
}
}
function generateWikicode() {
var wikicode = '\n\n{{User:Polygnotus/Templates/table\n';
assessmentState.sources.forEach(function(source, index) {
var assessment = assessmentState.assessments[index];
var num = index + 1;
wikicode += '|' + num + '={{User:Polygnotus/Templates/row\n';
wikicode += ' |rownumber=' + num + '\n';
wikicode += ' |1=' + source + '\n';
wikicode += ' |independent=' + assessment.independent + '\n';
wikicode += ' |reliable=' + assessment.reliable + '\n';
wikicode += ' |significant=' + assessment.significant + '\n';
wikicode += ' |gng=' + assessment.gng + '\n';
wikicode += ' |comments=' + assessment.comments + '\n';
wikicode += '}}\n';
});
wikicode += '}}\n';
var editTextarea = document.getElementById('wpTextbox1');
if (editTextarea) {
var currentContent = editTextarea.value;
var pattern = /\n*\{\{User:Polygnotus\/Templates\/table[\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'});
}
}
function parseExistingAssessment(content) {
var startPattern = /\{\{User:Polygnotus\/Templates\/table/;
var startMatch = content.search(startPattern);
if (startMatch === -1) {
console.log('No existing assessment template found');
return null;
}
var braceCount = 0;
var inTemplate = false;
var templateEnd = -1;
for (var i = startMatch; i < content.length - 1; i++) {
if (content[i] === '{' && content[i + 1] === '{') {
braceCount++;
inTemplate = true;
i++;
} else if (content[i] === '}' && content[i + 1] === '}') {
braceCount--;
if (braceCount === 0 && inTemplate) {
templateEnd = i + 2;
break;
}
i++;
}
}
if (templateEnd === -1) {
console.log('Could not find end of template');
return null;
}
var fullTemplate = content.substring(startMatch, templateEnd);
console.log('Found template, length:', fullTemplate.length);
var sources = [];
var assessments = [];
// Parse numbered parameters that contain {{User:Polygnotus/Templates/row
var rowPattern = /\|(\d+)=\{\{User:Polygnotus\/Templates\/row\s*([\s\S]*?)\}\}/g;
var rowMatch;
while ((rowMatch = rowPattern.exec(fullTemplate)) !== null) {
var rowNum = parseInt(rowMatch[1]);
var rowContent = rowMatch[2];
// Parse parameters within this row
var params = {};
var paramPattern = /\|(\w+)=((?:[^|\n]|\n(?!\s*\|))*)/g;
var paramMatch;
while ((paramMatch = paramPattern.exec(rowContent)) !== null) {
var paramName = paramMatch[1];
var paramValue = paramMatch[2].trim();
params[paramName] = paramValue;
}
// Check for both |1= and |source= parameter names
var sourceValue = params['1'] || params['source'] || '';
if (sourceValue) {
sources.push(sourceValue);
assessments.push({
independent: params.independent || '',
reliable: params.reliable || '',
significant: params.significant || '',
gng: params.gng || '',
comments: params.comments || ''
});
}
}
if (sources.length === 0) {
console.log('No sources found in template');
return null;
}
console.log('Successfully parsed', sources.length, 'sources with assessments');
return {
sources: sources,
assessments: assessments
};
}
function extractArticleName(afdPageName) {
var articleName = afdPageName.replace('Wikipedia:Articles_for_deletion/', '');
var nominationMatch = articleName.match(/(.*) \((\d+)(st|nd|rd|th) nomination\)$/);
if (nominationMatch) {
articleName = nominationMatch[1];
}
return articleName;
}
function extractReferences(content) {
var references = [];
var index = 0;
while (index < content.length) {
// Find next <ref tag
var refStart = content.indexOf('<ref', index);
if (refStart === -1) break;
// Find the end of the opening tag
var tagEnd = content.indexOf('>', refStart);
if (tagEnd === -1) break;
var openingTag = content.substring(refStart, tagEnd + 1);
// Skip self-closing tags like <ref name="x" />
if (openingTag.endsWith('/>')) {
index = tagEnd + 1;
continue;
}
// Find the matching </ref> tag
var refEnd = content.indexOf('</ref>', tagEnd);
if (refEnd === -1) break;
// Extract content between opening and closing tags
var refContent = content.substring(tagEnd + 1, refEnd).trim();
// Skip empty refs
if (refContent) {
references.push(refContent);
}
// Move past this ref tag
index = refEnd + 6; // length of '</ref>'
}
console.log('Extracted', references.length, 'references');
return references;
}
// </nowiki>