Jump to content

User:Polygnotus/Scripts/SourceTable4.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <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>