Old Reddit AutoTools

Consolidated: Auto-Scroll, Auto-Collapse, Unblur, Over18 Continue, Thumbnail Auto-Expand/Collapse, Moderator Comment Collapse, Infinite Scroll with Page Numbers + Image Embedder

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Old Reddit AutoTools
// @namespace    http://tampermonkey.net/
// @version      1.3.6
// @description  Consolidated: Auto-Scroll, Auto-Collapse, Unblur, Over18 Continue, Thumbnail Auto-Expand/Collapse, Moderator Comment Collapse, Infinite Scroll with Page Numbers + Image Embedder
// @author       Crates
// @license      MIT
// @match        https://old.reddit.com/*
// @match        https://www.reddit.com/*
// @match        http://old.reddit.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==
(function() {
    'use strict';

    // Track state for infinite scroll
    let isLoading = false;
    let currentPage = 1;
    let postCounter = 0;
    let prefetchedPage = null;
    let prefetchedDoc = null;

    // ========================================
    // UTILITY: Throttle function
    // ========================================
    function throttle(func, limit) {
        let inThrottle;
        return function(...args) {
            if (!inThrottle) {
                func.apply(this, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    }

    // ========================================
    // 1. OVER18 AUTO-CONTINUE
    // ========================================
    function clickContinue() {
        // Method 1: By name attribute (original)
        const buttonsByName = document.getElementsByName('over18');
        for (let button of buttonsByName) {
            if (button.value === "yes") {
                button.click();
                return true;
            }
        }

        // Method 2: Direct button selector with value
        const continueBtn = document.querySelector('button[name="over18"][value="yes"]');
        if (continueBtn) {
            continueBtn.click();
            return true;
        }

        // Method 3: By class (for new Reddit style)
        const primaryBtn = document.querySelector('.c-btn-primary[name="over18"]');
        if (primaryBtn) {
            primaryBtn.click();
            return true;
        }

        // Method 4: Input element (old style)
        const inputBtn = document.querySelector('input[name="over18"][value="yes"]');
        if (inputBtn) {
            inputBtn.click();
            return true;
        }

        // Method 5: Any button/input with "continue" text on over18 pages
        if (window.location.href.includes('over18')) {
            const allButtons = document.querySelectorAll('button[type="submit"], input[type="submit"]');
            for (let btn of allButtons) {
                const text = (btn.textContent || btn.value || '').toLowerCase();
                if (text.includes('continue') || text.includes('yes')) {
                    btn.click();
                    return true;
                }
            }
        }

        return false;
    }

    // ========================================
    // 2. AUTO-COLLAPSE MODERATOR COMMENTS
    // ========================================
    function collapseModeratorComments() {
        document.querySelectorAll('.thing.comment.stickied:not(.collapsed):not([data-auto-collapsed]), .thing.comment:has(.tagline .moderator):not(.collapsed):not([data-auto-collapsed]), .thing.comment:has(.tagline .admin):not(.collapsed):not([data-auto-collapsed])').forEach(comment => {
            const expandBtn = comment.querySelector(':scope > .entry .expand');
            if (expandBtn) {
                comment.setAttribute('data-auto-collapsed', 'true');
                expandBtn.click();
            }
        });
    }

    // ========================================
    // 3. AUTO-EXPAND POST ON COMMENT PAGES
    // ========================================
    function autoExpandPost() {
        if (!document.querySelector('.commentarea')) return;
        const expando = document.querySelector('.thing.link .expando-button.collapsed');
        if (expando) expando.click();
    }

    // ========================================
    // 4. CONSTRAIN OVERSIZED EMBEDS (portrait videos, etc.)
    // ========================================
    function constrainEmbeds() {
        document.querySelectorAll('.expando iframe:not([data-resized])').forEach(iframe => {
            const w = parseInt(iframe.getAttribute('width'));
            const h = parseInt(iframe.getAttribute('height'));
            if (!w || !h) return;

            iframe.setAttribute('data-resized', 'true');

            const maxH = window.innerHeight * 0.65;
            if (h > maxH) {
                const scale = maxH / h;
                iframe.setAttribute('width', Math.round(w * scale));
                iframe.setAttribute('height', Math.round(maxH));
            }
        });
    }

    // ========================================
    // 4.1. NEW: EMBED PLACEHOLDER IMAGES (<image>)
    // ========================================
    function embedPlaceholderImages() {
        const links = document.querySelectorAll('a');
        links.forEach(link => {
            if ((link.textContent.trim() === '<image>' || link.innerText.trim() === '<image>') && !link.dataset.processed) {
                link.dataset.processed = "true";
                const imageUrl = link.href;
                const img = document.createElement('img');
                img.src = imageUrl;

                // Reasonable Size Styling
                img.style.maxWidth = '600px';
                img.style.maxHeight = '500px';
                img.style.width = 'auto';
                img.style.height = 'auto';
                img.style.display = 'block';
                img.style.marginTop = '8px';
                img.style.marginBottom = '8px';
                img.style.borderRadius = '3px';
                img.style.border = '1px solid #ccc';
                img.style.cursor = 'zoom-in';

                // Click to toggle full size
                img.onclick = (e) => {
                    e.preventDefault();
                    if (img.style.maxWidth === '600px') {
                        img.style.maxWidth = '100%';
                        img.style.maxHeight = 'none';
                        img.style.cursor = 'zoom-out';
                    } else {
                        img.style.maxWidth = '600px';
                        img.style.maxHeight = '500px';
                        img.style.cursor = 'zoom-in';
                    }
                };

                link.textContent = '';
                link.appendChild(img);
            }
        });
    }

    // ========================================
    // 5. NUMBER POSTS AND ADD PAGE BREAKS
    // ========================================
    function numberPosts() {
        const posts = document.querySelectorAll('.thing.link:not([data-numbered])');
        if (posts.length === 0) return;

        posts.forEach(post => {
            postCounter++;
            post.setAttribute('data-numbered', 'true');
            post.setAttribute('data-post-number', postCounter);

            const rank = post.querySelector('.rank');
            if (rank) {
                rank.textContent = postCounter;
            }
        });
    }

    // ========================================
    // 6. ADD PAGE SEPARATOR
    // ========================================
    function addPageSeparator(pageNum) {
        const siteTable = document.querySelector('.sitetable.linklisting');
        if (!siteTable) return;

        const separator = document.createElement('div');
        separator.className = 'page-separator';
        separator.setAttribute('data-page', pageNum);
        separator.innerHTML = `
            <div class="page-separator-line"></div>
            <div class="page-separator-label">Page ${pageNum}</div>
            <div class="page-separator-line"></div>
        `;
        siteTable.appendChild(separator);
    }

    // ========================================
    // 7. INFINITE SCROLL (with prefetching)
    // ========================================
    function prefetchNextPage() {
        const nextButton = document.querySelector('.next-button a');
        if (!nextButton || prefetchedDoc) return;

        const url = nextButton.href;
        if (prefetchedPage === url) return;

        prefetchedPage = url;
        fetch(url)
            .then(response => response.text())
            .then(html => {
                const parser = new DOMParser();
                prefetchedDoc = parser.parseFromString(html, 'text/html');
            })
            .catch(() => {
                prefetchedPage = null;
            });
    }

    function loadNextPage() {
        if (isLoading) return;

        const nextButton = document.querySelector('.next-button a');
        if (!nextButton) return;

        isLoading = true;

        const siteTable = document.querySelector('.sitetable.linklisting');
        if (!siteTable) {
            isLoading = false;
            return;
        }

        function appendPosts(doc) {
            currentPage++;
            addPageSeparator(currentPage);

            const newPosts = doc.querySelectorAll('.thing.link');
            newPosts.forEach(post => {
                const clonedPost = post.cloneNode(true);
                clonedPost.removeAttribute('data-numbered');
                siteTable.appendChild(clonedPost);
            });

            const newNextButton = doc.querySelector('.next-button a');
            if (newNextButton) {
                nextButton.href = newNextButton.href;
                prefetchedDoc = null;
                prefetchedPage = null;
                prefetchNextPage();
            } else {
                const navButtons = document.querySelector('.nav-buttons');
                if (navButtons) {
                    navButtons.innerHTML = '<span style="color: #888; padding: 10px;">No more posts</span>';
                }
            }

            numberPosts();
            embedPlaceholderImages(); // Ensure images embed on new pages
            isLoading = false;
        }

        if (prefetchedDoc && prefetchedPage === nextButton.href) {
            appendPosts(prefetchedDoc);
            prefetchedDoc = null;
            prefetchedPage = null;
            return;
        }

        fetch(nextButton.href)
            .then(response => response.text())
            .then(html => {
                const parser = new DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                appendPosts(doc);
            })
            .catch(err => {
                console.error('Infinite scroll error:', err);
                isLoading = false;
            });
    }

    function setupInfiniteScroll() {
        if (document.querySelector('.commentarea')) return;

        prefetchNextPage();

        const handleScroll = throttle(() => {
            const scrollPosition = window.innerHeight + window.scrollY;
            const pageHeight = document.documentElement.scrollHeight;

            if (scrollPosition >= pageHeight - 1500) {
                prefetchNextPage();
            }

            if (scrollPosition >= pageHeight - 800) {
                loadNextPage();
            }
        }, 150);

        window.addEventListener('scroll', handleScroll);

        numberPosts();
    }

    // ========================================
    // 8. CLICK-BASED LOGIC
    // ========================================
    let internalClick = false;

    document.addEventListener('click', function(e) {
        const thumbnail = e.target.closest('a.thumbnail');
        if (thumbnail) {
            e.preventDefault();
            const thing = thumbnail.closest('.thing');
            if (thing) {
                const expandoButton = thing.querySelector('.expando-button');
                if (expandoButton) {
                    internalClick = true;
                    expandoButton.click();
                    internalClick = false;
                }
            }
            return;
        }

        const expandoButton = e.target.closest('.expando-button');
        if (expandoButton && expandoButton.classList.contains('collapsed')) {
            if (e.isTrusted || internalClick) {
                document.querySelectorAll('.expando-button.expanded').forEach(btn => {
                    if (btn !== expandoButton) btn.click();
                });

                const thing = expandoButton.closest('.thing');
                if (thing) {
                    setTimeout(() => {
                        thing.scrollIntoView({ behavior: 'smooth', block: 'start' });
                    }, 50);
                }
            }

            setTimeout(() => {
                const nsfwBtn = document.querySelector('button.expando-gate__show-once');
                if (nsfwBtn) nsfwBtn.click();
            }, 100);
        }
    }, true);

    // ========================================
    // 9. MUTATION OBSERVER (Optimized)
    // ========================================
    let observerTimeout = null;

    const observer = new MutationObserver((mutations) => {
        clickContinue();
        const nsfwBtn = document.querySelector('button.expando-gate__show-once');
        if (nsfwBtn) nsfwBtn.click();

        if (observerTimeout) return;

        observerTimeout = setTimeout(() => {
            collapseModeratorComments();
            constrainEmbeds();
            embedPlaceholderImages(); // Catch dynamic image links
            numberPosts();
            observerTimeout = null;
        }, 100);
    });

    // ========================================
    // 10. INITIALIZATION
    // ========================================
    function init() {
        observer.observe(document.documentElement, { childList: true, subtree: true });

        clickContinue();
        setTimeout(clickContinue, 100);
        setTimeout(clickContinue, 300);
        setTimeout(clickContinue, 500);
        setTimeout(clickContinue, 1000);

        collapseModeratorComments();
        autoExpandPost();
        constrainEmbeds();
        embedPlaceholderImages();
        setupInfiniteScroll();

        const style = document.createElement('style');
        style.textContent = `
            .page-separator {
                clear: both;
                width: 100%;
                display: flex;
                align-items: center;
                gap: 14px;
                margin: 20px 0;
                padding: 0 10px;
            }
            .page-separator-line {
                flex: 1;
                height: 1px;
                background: linear-gradient(90deg, transparent, #c0d0e0 15%, #c0d0e0 85%, transparent);
            }
            .page-separator-label {
                font-size: 11px;
                font-weight: 600;
                letter-spacing: 1.5px;
                text-transform: uppercase;
                color: #8aa8c7;
                white-space: nowrap;
            }
            .thing.link {
                border-bottom: 1px solid #e5e5e5 !important;
                padding-bottom: 10px !important;
                margin-bottom: 10px !important;
            }
            .infinite-scroll-loading {
                clear: both;
                width: 100%;
            }
            .rank {
                min-width: 30px !important;
            }
        `;
        document.head.appendChild(style);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    window.addEventListener('load', () => {
        clickContinue();
        setTimeout(clickContinue, 500);
    });
})();