Jump to content

User:HaeB/WRN import adjustments.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// To be used on a page imported from enwiki, for converting internal links to interwiki links pointing back to the originally linked page (excluding File: and Media: links). Adds a button in source editing mode (only for "Research:Newsletter" pages) that triggers the conversion and opens "Show changes", for the user to review and save the result. 
// By [[User:HaeB]] (using ChatGPT o1-preview and Claude 3.5 Sonnet (new))

(function () {
    'use strict';
    
    let interwikiPrefixes = []; // To store the fetched interwiki prefixes
    
    // The interwiki prefix to be added to (formerly) internal wikilinks (e.g., :w: for Wikipedia):
    const targetInterwikiPrefix = ':w:';
    
    // Interwiki prefixes that point to the (new) current wiki:
    const sourceInterwikiPrefixes = ['m', 'meta']; 
    // todo: consistent use with/without colons
    
    // Load the necessary modules
    mw.loader.using(['oojs-ui', 'mediawiki.util', 'mediawiki.api']).then(function () {
        // Check if the page title starts with "Research:Newsletter" and we're in editing mode
        const pageTitle = mw.config.get('wgPageName');
        if (!pageTitle.startsWith('Research:Newsletter') || mw.config.get('wgAction') !== 'edit') return;
        
        // Fetch interwiki prefixes from the API
        fetchInterwikiPrefixes().then(function () {
            // Create a button in the toolbar once prefixes are fetched
            const button = new OO.ui.ButtonWidget({
                label: 'Convert links and templates',
                icon: 'edit',
                flags: ['progressive']
            });
            button.on('click', function () {
                performConversions();
            });
            // Add the button to the toolbar in the edit page
            $('#wpTextbox1').before(button.$element);
        });
    });

    // Function to fetch interwiki prefixes from the API
    function fetchInterwikiPrefixes() {
        const api = new mw.Api();
        return api.get({
            action: 'query',
            meta: 'siteinfo',
            siprop: 'interwikimap'
        }).then(function (data) {
            // Filter out the 'wikipedia' prefix to avoid conflicts with the Wikipedia namespace
            interwikiPrefixes = data.query.interwikimap
                .map(entry => entry.prefix.toLowerCase())
                .filter(prefix => prefix !== 'wikipedia');
        });
    }

    // Helper function to parse template parameters while respecting nested structures
    function parseTemplateParams(paramsString) {
        const params = {};
        let currentKey = '';
        let currentValue = '';
        let inValue = false;
        let nestLevel = 0;  // Track nesting level of [[...]] and {{...}}
        let captureBuffer = '';
        
        // Helper to add completed parameter to params object
        function addParam() {
            if (currentKey) {
                const key = currentKey.trim();
                const value = currentValue.trim();
                if (key && value) {
                    params[key] = value;
                }
            }
            currentKey = '';
            currentValue = '';
            inValue = false;
        }

        // Process character by character
        for (let i = 0; i < paramsString.length; i++) {
            const char = paramsString[i];
            const nextChar = paramsString[i + 1] || '';
            
            // Handle nested structures
            if (char === '[' && nextChar === '[') {
                nestLevel++;
                captureBuffer += '[[';
                i++; // Skip next [
                continue;
            }
            if (char === ']' && nextChar === ']') {
                nestLevel--;
                captureBuffer += ']]';
                i++; // Skip next ]
                continue;
            }
            if (char === '{' && nextChar === '{') {
                nestLevel++;
                captureBuffer += '{{';
                i++; // Skip next {
                continue;
            }
            if (char === '}' && nextChar === '}') {
                nestLevel--;
                captureBuffer += '}}';
                i++; // Skip next }
                continue;
            }

            // Only process pipes and equals when not in nested structure
            if (nestLevel === 0) {
                if (char === '|') {
                    if (inValue) {
                        currentValue += captureBuffer;
                    } else {
                        currentKey = captureBuffer;
                    }
                    captureBuffer = '';
                    addParam();
                    continue;
                }
                if (char === '=' && !inValue) {
                    currentKey = captureBuffer;
                    captureBuffer = '';
                    inValue = true;
                    continue;
                }
            }
            
            captureBuffer += char;
        }

        // Add final parameter
        if (captureBuffer) {
            if (inValue) {
                currentValue = captureBuffer;
            } else {
                currentKey = captureBuffer;
            }
            addParam();
        }

        return params;
    }

    // Function to convert image templates to File syntax
    function convertImageTemplates(content) {
        // Regular expressions for both template types
        const templateRegexes = [
            /\{\{Wikipedia:Wikipedia Signpost\/Templates\/Inline image\s*\|((?:[^{}]|\{\{[^{}]*\}\})*)\}\}/g,
            /\{\{Wikipedia:Wikipedia Signpost\/Templates\/Filler image-v2\s*\n?\s*\|((?:[^{}]|\{\{[^{}]*\}\})*)\}\}/g
        ];
        
        let newContent = content;
        
        // Process each template type
        templateRegexes.forEach(regex => {
            newContent = newContent.replace(regex, function(match, params) {
                // Parse parameters using the new parser
                const paramMap = parseTemplateParams(params);

                // Construct File syntax
                let fileString = '[[File:';
                // Add image name
                if (paramMap.image) {
                    fileString += paramMap.image.replace(/^(?:File:|Media:)/i, '');
                }
                
                // Add standard parameters
                fileString += '|thumb';
                if (paramMap.size) {
                    fileString += '|' + paramMap.size;
                }
                if (paramMap.align) {
                    fileString += '|' + paramMap.align;
                }
                if (paramMap.alt) {
                    fileString += '|alt=' + paramMap.alt;
                }
                if (paramMap.caption) {
                    fileString += '|' + paramMap.caption;
                }
                
                fileString += ']]';
                return fileString;
            });
        });
        
        return newContent;
    }

    // Function to perform all conversions
    function performConversions() {
        const editor = document.getElementById('wpTextbox1');
        if (!editor) return;

        // Define interwiki regex dynamically based on fetched prefixes
        const interwikiRegex = new RegExp(`^\\s*:?(${interwikiPrefixes.join('|')}):`, 'i');
        
        // Regular expression to match wikilinks, 
        // excluding File:, Media: and within-page section (anchor) links
        const linkRegex = /\[\[(?!\s*(?:File|Media):|\s*#)([^\]|]+)(\|[^\]]+)?\]\]/g;
        
        // Regular expression to match and remove interwiki prefixes pointing to the (new) source wiki
        const sourcePrefixesPattern = sourceInterwikiPrefixes.join('|');
        const currentWikiInterwikiPrefixRegex = new RegExp(`(\\[\\[\\s*):?(?:${sourcePrefixesPattern}):`, 'gi');

        // Get content and perform both conversions
        let newContent = editor.value;
        
        // First convert the templates
        newContent = convertImageTemplates(newContent);
        
        // Then convert internal links to interwiki links
        newContent = newContent.replace(linkRegex, function (match, p1, p2) {
            // Trim whitespace from the link target
            const linkTarget = p1.trim();
            // If the link already has an interwiki prefix, skip it
            if (interwikiRegex.test(linkTarget)) {
                return match;
            }
            // Construct the new link
            if (p2) {
                // Piped link: [[Target|Display]]
                return `[[${targetInterwikiPrefix}${linkTarget}${p2}]]`;
            } else {
                // Unpiped link: [[Target]]
                return `[[${targetInterwikiPrefix}${linkTarget}|]]`;
            }
        });
        
        // Lastly, remove interwiki prefixes pointing to the (new) source wiki,
        // converting those interwiki links into internal wikilinks
        newContent = newContent.replace(currentWikiInterwikiPrefixRegex, '$1');

        editor.value = newContent;
        // Click the "Show changes" button to preview changes
        document.getElementById('wpDiff').click();
    }
})();