User:Polygnotus/Scripts/NamespaceFilter.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.
// Very janky and not intended for real world use, does not make API calls just filters whatever is there on the page (so use limit=5000).
// I have requested something better:
// https://meta.wikimedia.org/wiki/Community_Wishlist/Wishes/Add_Namespace_filter_to_all_Special:_pages_(where_applicable)

(function() {
    'use strict';

    const STORAGE_KEY = 'wikipediaNamespaceFilter';
    const ARTICLE_NAMES_ONLY_KEY = 'wikipediaArticleNamesOnly';

    function addStyles() {
        const style = document.createElement('style');
        style.textContent = `
            #namespace-filter {
                margin: 10px 0;
                padding: 10px;
                border: 1px solid #a2a9b1;
                background-color: #f8f9fa;
            }
            #namespace-filter label {
                margin-right: 10px;
                display: inline-block;
            }
            #all-none-options, #display-options {
                margin-bottom: 10px;
            }
            #all-none-options button {
                margin-right: 10px;
            }
            .article-name-only .external {
                display: none;
            }
        `;
        document.head.appendChild(style);
    }

    function getNamespaces() {
        const namespaces = new Set(['Main']);
        document.querySelectorAll('ol.special li a:nth-child(2)').forEach(link => {
            const text = link.textContent;
            const colonIndex = text.indexOf(':');
            if (colonIndex === -1) {
                namespaces.add('Main');
            } else {
                const namespace = text.substring(0, colonIndex);
                if (namespace) {
                    namespaces.add(namespace);
                }
            }
        });
        return Array.from(namespaces).sort((a, b) => a === 'Main' ? -1 : b === 'Main' ? 1 : a.localeCompare(b));
    }

    function createFilterUI(namespaces) {
        const filterDiv = document.createElement('div');
        filterDiv.id = 'namespace-filter';
        filterDiv.innerHTML = '<h4>Filter by namespace:</h4>';

        // Add All/None options
        const allNoneDiv = document.createElement('div');
        allNoneDiv.id = 'all-none-options';
        const allButton = document.createElement('button');
        allButton.textContent = 'Select All';
        allButton.addEventListener('click', () => setAllCheckboxes(true));
        const noneButton = document.createElement('button');
        noneButton.textContent = 'Select None';
        noneButton.addEventListener('click', () => setAllCheckboxes(false));
        allNoneDiv.appendChild(allButton);
        allNoneDiv.appendChild(noneButton);
        filterDiv.appendChild(allNoneDiv);

        // Add Article Names Only option
        const displayOptionsDiv = document.createElement('div');
        displayOptionsDiv.id = 'display-options';
        const articleNamesOnlyLabel = document.createElement('label');
        const articleNamesOnlyCheckbox = document.createElement('input');
        articleNamesOnlyCheckbox.type = 'checkbox';
        articleNamesOnlyCheckbox.id = 'article-names-only';
        articleNamesOnlyCheckbox.checked = localStorage.getItem(ARTICLE_NAMES_ONLY_KEY) === 'true';
        articleNamesOnlyCheckbox.addEventListener('change', toggleArticleNamesOnly);
        articleNamesOnlyLabel.appendChild(articleNamesOnlyCheckbox);
        articleNamesOnlyLabel.appendChild(document.createTextNode('Show Article Names Only'));
        displayOptionsDiv.appendChild(articleNamesOnlyLabel);
        filterDiv.appendChild(displayOptionsDiv);

        const savedFilters = getSavedFilters();

        namespaces.forEach(namespace => {
            const label = document.createElement('label');
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.value = namespace;
            checkbox.checked = savedFilters ? savedFilters.includes(namespace) : true;
            checkbox.addEventListener('change', () => {
                filterResults();
                saveFilters();
            });
            label.appendChild(checkbox);
            label.appendChild(document.createTextNode(namespace));
            filterDiv.appendChild(label);
        });

        const ol = document.querySelector('ol.special');
        ol.parentNode.insertBefore(filterDiv, ol);

        // Apply initial article names only setting
        toggleArticleNamesOnly();
    }

    function setAllCheckboxes(checked) {
        document.querySelectorAll('#namespace-filter input[type="checkbox"]').forEach(cb => {
            if (cb.id !== 'article-names-only') {
                cb.checked = checked;
            }
        });
        filterResults();
        saveFilters();
    }

    function filterResults() {
        const checkedNamespaces = Array.from(document.querySelectorAll('#namespace-filter input:checked:not(#article-names-only)')).map(cb => cb.value);
        document.querySelectorAll('ol.special li').forEach(li => {
            const link = li.querySelector('a:nth-child(2)');
            const text = link.textContent;
            const colonIndex = text.indexOf(':');
            const namespace = colonIndex === -1 ? 'Main' : text.substring(0, colonIndex);
            if (checkedNamespaces.includes(namespace)) {
                li.style.display = '';
            } else {
                li.style.display = 'none';
            }
        });
    }

    function toggleArticleNamesOnly() {
        const isChecked = document.getElementById('article-names-only').checked;
        document.querySelector('ol.special').classList.toggle('article-name-only', isChecked);
        localStorage.setItem(ARTICLE_NAMES_ONLY_KEY, isChecked);
    }

    function saveFilters() {
        const checkedNamespaces = Array.from(document.querySelectorAll('#namespace-filter input:checked:not(#article-names-only)')).map(cb => cb.value);
        localStorage.setItem(STORAGE_KEY, JSON.stringify(checkedNamespaces));
    }

    function getSavedFilters() {
        const savedFilters = localStorage.getItem(STORAGE_KEY);
        return savedFilters ? JSON.parse(savedFilters) : null;
    }

    function init() {
        const namespaces = getNamespaces();
        if (namespaces.length > 0) {
            addStyles();
            createFilterUI(namespaces);
            filterResults(); // Apply filters on initial load
        }
    }

    // Run the script when the page is loaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
// </nowiki>