User:Polygnotus/Scripts/NamespaceFilter.js

// 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 = '

Filter by namespace:

';

// 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();

}

})();

//