// ==UserScript==
// @name         Desuarchive Word Filter
// @namespace    -ACK!
// @version      1.0
// @description  Word filter for Desuarchive posts, toggle with Shift+F or button
// @author       NiggersTongueMyAnus
// @match        https://desuarchive.org/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const style = document.createElement('style');
    style.textContent = `
        .filter-toggle-container {
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 10001;
            font-family: Arial, sans-serif;
        }
        .filter-toggle-btn {
            padding: 6px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            background: #6c757d;
            color: #fff;
            border: none;
            transition: background 0.2s;
        }
        .filter-toggle-btn:hover {
            background: #5a6268;
        }
    `;
    document.head.appendChild(style);

    // Create filter toggle button
    const filterContainer = document.createElement('div');
    filterContainer.className = 'filter-toggle-container';
    const filterBtn = document.createElement('button');
    filterBtn.className = 'filter-toggle-btn';
    filterBtn.textContent = 'Filter: On';
    filterContainer.appendChild(filterBtn);
    document.body.appendChild(filterContainer);

    // Word filter for posts and their replacement
    const targetWords = ['Akemi', 'Ack', '!Akemi', 'ack', 'ACK'];
    const replacementPhrases = ['I love you', 'Marry me', 'Steamy night!', 'Have SEX with me', '*kisses you*', 'I KNEEL'];
    let filterEnabled = true;
    const originalTexts = new Map(); // Store original text node values

    function getRandomReplacement() {
        return replacementPhrases[Math.floor(Math.random() * replacementPhrases.length)];
    }

    function filterTextNode(textNode) {
        if (!textNode.nodeValue || !textNode.parentNode) return;
        if (!filterEnabled) {
            // Restore original text if filter is disabled
            if (originalTexts.has(textNode)) {
                textNode.nodeValue = originalTexts.get(textNode);
            }
            return;
        }
        // Store original text if not already stored
        if (!originalTexts.has(textNode)) {
            originalTexts.set(textNode, textNode.nodeValue);
        }
        let text = textNode.nodeValue;
        let modified = false;

        targetWords.forEach((word) => {
            // Regex to match whole words/phrases
            const regex = new RegExp(`\\b${word.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}\\b`, 'g');
            if (regex.test(text)) {
                text = text.replace(regex, getRandomReplacement());
                modified = true;
            }
        });

        if (modified) {
            textNode.nodeValue = text;
            //console.log('Filtered text node:', text);
        }
    }

    function filterPostText(postTextElement) {
        if (!postTextElement) return;

        function processNode(node) {
            // Skip greentext spans with backlinks
            if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('greentext')) {
                if (node.querySelector('a.backlink')) {
                    return; // Skip backlink greentext to prevent breaking format
                }
            }

            if (node.nodeType === Node.TEXT_NODE) {
                filterTextNode(node);
            } else if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
                node.childNodes.forEach(processNode);
            }
        }

        processNode(postTextElement);
    }

    // Apply filter to existing posts
    function applyFilterToPosts() {
        const postWrappers = document.querySelectorAll('.post_wrapper');
        postWrappers.forEach((wrapper) => {
            const postText = wrapper.querySelector('.text');
            if (postText) {
                filterPostText(postText);
            }
        });
        //console.log(`Applied filter to ${postWrappers.length} posts`);
    }

    // Toggle filter
    function toggleFilter() {
        filterEnabled = !filterEnabled;
        filterBtn.textContent = `Filter: ${filterEnabled ? 'On' : 'Off'}`;
        //console.log(`Filter toggled to: ${filterEnabled ? 'On' : 'Off'}`);
        applyFilterToPosts();
    }

    // Shift+F to toggle filter
    filterBtn.addEventListener('click', toggleFilter);
    document.addEventListener('keydown', (e) => {
        if (e.shiftKey && e.key.toLowerCase() === 'f') {
            e.preventDefault();
            toggleFilter();
        }
    });

    // Initial filter application
    applyFilterToPosts();
    // Observe for new posts to filter
    const postObserver = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.matches('.post_wrapper')) {
                            const postText = node.querySelector('.text');
                            if (postText) {
                                filterPostText(postText);
                            }
                        } else {
                            const wrappers = node.querySelectorAll('.post_wrapper');
                            wrappers.forEach((wrapper) => {
                                const postText = wrapper.querySelector('.text');
                                if (postText) {
                                    filterPostText(postText);
                                }
                            });
                        }
                    }
                });
            }
        });
    });

    postObserver.observe(document.body, {
        childList: true,
        subtree: true
    });
    //console.log('Post MutationObserver set up for .post_wrapper');
})();
Edit Report
Pub: 23 Apr 2025 20:40 UTC
Views: 10