Consolidated: Auto-Scroll, Auto-Collapse, Unblur, Over18 Continue, Thumbnail Auto-Expand/Collapse, Moderator Comment Collapse, Infinite Scroll with Page Numbers + Image Embedder
// ==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);
});
})();