User:GrabUp/External Links Remover.js

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>

(function() { 
    function removeExternalLinks() {
        const validNamespaces = [0, 100, 2, 118];
        if (!validNamespaces.includes(mw.config.get('wgNamespaceNumber'))) {
            alert("This tool can only be used on article, sandbox, user, or draft pages.");
            return;
        }

        const confirmationContainer = document.createElement('div');
        confirmationContainer.innerHTML = `
            <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(30, 30, 30, 0.9); color: white; border: 2px solid #ccc; padding: 30px; z-index: 1000; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);">
                <p style="font-size: 18px; font-weight: bold;">Are you sure you want to remove external links from this article?</p>
                <button id="confirm-yes" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px;">Yes</button>
                <button id="confirm-no" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 4px;">No</button>
            </div>
        `;
        document.body.appendChild(confirmationContainer);

        document.getElementById('confirm-yes').onclick = function() {
            document.body.removeChild(confirmationContainer); 
            promptForComment(); 
        };

        document.getElementById('confirm-no').onclick = function() {
            document.body.removeChild(confirmationContainer);
        };
    }

    function promptForComment() {
        const commentContainer = document.createElement('div');
        commentContainer.innerHTML = `
            <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(30, 30, 30, 0.9); color: white; border: 2px solid #ccc; padding: 30px; z-index: 1000; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);">
                <p style="font-size: 18px; font-weight: bold;">Would you like to add additional edit summaries? If yes, please enter below:</p>
                <textarea id="edit-summary" rows="6" cols="50" placeholder="Enter your comments here..." style="width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ccc;"></textarea>
                <br><br>
                <button id="submit-comment" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px;">Submit</button>
                <button id="skip-comment" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px;">Skip</button>
                <button id="cancel" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 4px;">Cancel</button>
            </div>
        `;
        document.body.appendChild(commentContainer);

        document.getElementById('submit-comment').onclick = function() {
            const additionalSummaries = document.getElementById('edit-summary').value.trim();
            let editSummary = 'Removed external links, Using [[User:GrabUp/External_Links_Remover| External Links Remover]]'; 
            if (additionalSummaries) {
                editSummary += ' | ' + additionalSummaries.split('|').map(summary => summary.trim()).join(' | '); 
            }
            processEdit(editSummary);
            document.body.removeChild(commentContainer); 
        };

        document.getElementById('skip-comment').onclick = function() {
            const editSummary = 'Removed external links, Using [[User:GrabUp/External_Links_Remover| External Links Remover]]'; 
            processEdit(editSummary);
            document.body.removeChild(commentContainer); 
        };

        document.getElementById('cancel').onclick = function() {
            document.body.removeChild(commentContainer); 
        };
    }

    function processEdit(editSummary) {
        const api = new mw.Api();
        api.get({
            action: 'query',
            prop: 'revisions',
            rvprop: 'content',
            titles: mw.config.get('wgPageName'),
            formatversion: 2
        }).done(function(data) {
            const page = data.query.pages[0];
            if (!page || !page.revisions) {
                alert("Failed to fetch page content.");
                return;
            }
            let text = page.revisions[0].content;

            const externalLinkPattern = /(?:\[(http[s]?:\/\/[^\s]+)\s*([^\]]+)?\]|(http[s]?:\/\/[^\s]+))/g;
            const refPattern = /<ref[^>]*>[\s\S]*?<\/ref>/g;
            const citeWebPattern = /{{\s*(Cite\s+web|Cite\s+news|Cite\s+book|Cite\s+journal).*?}}/g; 
            const urlTemplatePattern = /{{\s*(URL|url)\s*\|\s*[^\}]+\s*}}/g;   
            const sectionHeadingPattern = /(==\s*(References|External links)\s*==\n?)/gi;

            const refPlaceholders = [];
            const citePlaceholders = [];
            const urlPlaceholders = [];

            text = text.replace(refPattern, match => {
                refPlaceholders.push(match);
                return `REF_PLACEHOLDER_${refPlaceholders.length - 1}`;
            });

            text = text.replace(citeWebPattern, match => {
                citePlaceholders.push(match);
                return `CITE_PLACEHOLDER_${citePlaceholders.length - 1}`;
            });

            text = text.replace(urlTemplatePattern, match => {
                urlPlaceholders.push(match);
                return `URL_PLACEHOLDER_${urlPlaceholders.length - 1}`;
            });

            let linkRemoved = false;

            const externalLinksSectionIndex = text.search(/==\s*External links\s*==/i);
            if (externalLinksSectionIndex !== -1) {
                let beforeExternalLinks = text.slice(0, externalLinksSectionIndex);
                const afterExternalLinks = text.slice(externalLinksSectionIndex);

                beforeExternalLinks = beforeExternalLinks.replace(externalLinkPattern, (match, p1, p2) => {
                    linkRemoved = true;
                    return `<span style="background-color: yellow;">${p2 ? p2.trim() : ''}</span>`;
                });

                text = beforeExternalLinks + afterExternalLinks;
            } else {
                text = text.replace(externalLinkPattern, (match, p1, p2) => {
                    linkRemoved = true;
                    return `<span style="background-color: yellow;">${p2 ? p2.trim() : ''}</span>`;
                });
            }

            refPlaceholders.forEach((ref, index) => {
                text = text.replace(`REF_PLACEHOLDER_${index}`, ref);
            });
            citePlaceholders.forEach((cite, index) => {
                text = text.replace(`CITE_PLACEHOLDER_${index}`, cite);
            });
            urlPlaceholders.forEach((url, index) => {
                text = text.replace(`URL_PLACEHOLDER_${index}`, url);
            });

            text = text.replace(sectionHeadingPattern, (match, heading) => heading.trim() + '\n');

            if (linkRemoved) {
                showPreview(text.trim(), editSummary);
            } else {
                const noLinksContainer = document.createElement('div');
                noLinksContainer.innerHTML = `
                    <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(30, 30, 30, 0.9); color: white; border: 2px solid #ccc; padding: 30px; z-index: 1000; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);">
                        <p style="font-size: 18px; font-weight: bold;">No external links found to remove</p>
                        <button id="close-no-links" style="padding: 10px 15px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px;">OK</button>
                    </div>
                `;
                document.body.appendChild(noLinksContainer);
                document.getElementById('close-no-links').onclick = function() {
                    document.body.removeChild(noLinksContainer);
                };
            }
        });
    }
function showPreview(previewContent, editSummary) {
    // Create a preview container with a black background and high-contrast text.
    const previewContainer = document.createElement('div');
    previewContainer.innerHTML = `
        <div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.95); color: white; border: 2px solid #ccc; padding: 30px; z-index: 1000; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); width: 80%; max-height: 80%; display: flex; flex-direction: column;">
            <h3 style="font-size: 18px; font-weight: bold;">Preview Changes</h3>
            <pre style="background: #000; color: #ffffff; padding: 15px; border-radius: 4px; flex-grow: 1; overflow-y: auto; white-space: pre-wrap;">
                ${previewContent.replace(/background-color: yellow;/g, 'background-color: blue;')}
            </pre>
            <div style="text-align: center; margin-top: 10px;">
                <button id="confirm-save" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px;">Save</button>
                <button id="cancel-preview" style="margin: 5px; padding: 10px 15px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 4px;">Cancel</button>
            </div>
        </div>
    `;
    document.body.appendChild(previewContainer);

    // Confirm and cancel buttons functionality
    document.getElementById('confirm-save').onclick = function() {
        const finalContent = previewContent.replace(/<span style="background-color: yellow;">(.*?)<\/span>/g, '$1');
        const api = new mw.Api();
        api.postWithEditToken({
            action: 'edit',
            title: mw.config.get('wgPageName'),
            text: finalContent, // Save without any highlighting
            summary: editSummary
        }).done(function(data) {
            const newRevId = data.edit.newrevid;
            window.location.href = mw.util.getUrl(mw.config.get('wgPageName'), { diff: newRevId });
        }).fail(function() {
            alert("Failed to save changes.");
        });
        document.body.removeChild(previewContainer);
    };

    document.getElementById('cancel-preview').onclick = function() {
        document.body.removeChild(previewContainer);
    };
}

    mw.loader.using('mediawiki.api', function() {
        mw.util.addPortletLink(
            'p-cactions',
            '#',
            'Remove External Links',
            'ca-remove-external-links',
            'Removes all external links from the article'
        );
        document.getElementById('ca-remove-external-links').onclick = removeExternalLinks;
    });
})();






// </nowiki>