// ==UserScript== // @name 8chan Style Script // @namespace 8chanSS // @match *://8chan.moe/* // @match *://8chan.se/* // @grant none // @version 1.1 // @author Anon // @run-at document-end // @description Script to style 8chan // @license MIT // @downloadURL none // ==/UserScript== // Header Catalog Links // Function to append /catalog.html to links function appendCatalogToLinks() { const navboardsSpan = document.getElementById('navBoardsSpan'); if (navboardsSpan) { const links = navboardsSpan.getElementsByTagName('a'); for (let link of links) { if (link.href && !link.href.endsWith('/catalog.html')) { link.href += '/catalog.html'; } } } } // Initial call to append links on page load appendCatalogToLinks(); // Set up a MutationObserver to watch for changes in the #navboardsSpan div const observer = new MutationObserver(appendCatalogToLinks); const config = { childList: true, subtree: true }; const navboardsSpan = document.getElementById('navBoardsSpan'); if (navboardsSpan) { observer.observe(navboardsSpan, config); } // Scroll to last read post // Function to save the scroll position const MAX_PAGES = 10; // Maximum number of pages to store scroll positions const currentPage = window.location.href; // Specify pages to exclude from scroll position saving const excludedPages = [ '*/catalog.html', // Add any other pages you want to exclude ]; // Function to save the scroll position for the current page function saveScrollPosition() { // Check if the current page is in the excluded pages list if (excludedPages.includes(currentPage)) { return; // Skip saving scroll position for excluded pages } const scrollPosition = window.scrollY; // Get the current vertical scroll position localStorage.setItem(`scrollPosition_${currentPage}`, scrollPosition); // Store it in localStorage with a unique key // Manage the number of stored scroll positions manageScrollStorage(); } // Function to restore the scroll position for the current page function restoreScrollPosition() { const savedPosition = localStorage.getItem(`scrollPosition_${currentPage}`); // Retrieve the saved position for the current page if (savedPosition) { window.scrollTo(0, parseInt(savedPosition, 10)); // Scroll to the saved position } } // Function to manage the number of stored scroll positions function manageScrollStorage() { const keys = Object.keys(localStorage).filter(key => key.startsWith('scrollPosition_')); // If the number of stored positions exceeds the limit, remove the oldest if (keys.length > MAX_PAGES) { // Sort keys by their creation time (assuming the order of keys reflects the order of storage) keys.sort((a, b) => { return localStorage.getItem(a) - localStorage.getItem(b); }); // Remove the oldest entries until we are within the limit while (keys.length > MAX_PAGES) { localStorage.removeItem(keys.shift()); } } } // Event listener to save scroll position before the page unloads window.addEventListener('beforeunload', saveScrollPosition); // Restore scroll position when the page loads window.addEventListener('load', restoreScrollPosition); // Toggle Announcement & Posting Form // Create the button const button = document.createElement('button'); button.style.margin = '10px'; const postingFormDiv = document.getElementById('postingForm'); const announcementDiv = document.getElementById('dynamicAnnouncement'); const panelMessageDiv = document.getElementById('panelMessage'); // Check if divs exist if (postingFormDiv && announcementDiv && panelMessageDiv) { // Insert the button before the announcement div postingFormDiv.parentNode.insertBefore(button, postingFormDiv); // Retrieve the visibility states from localStorage const isPostingFormVisible = localStorage.getItem('postingFormVisible') === 'true'; const isAnnouncementVisible = localStorage.getItem('announcementVisible') === 'true'; const isPanelMessageVisible = localStorage.getItem('panelMessageVisible') === 'true'; // Set the initial state of the divs and button based on stored values if (isPostingFormVisible) { postingFormDiv.style.display = 'block'; // Show the posting div } else { postingFormDiv.style.display = 'none'; // Hide the posting div } if (isAnnouncementVisible) { announcementDiv.style.display = 'block'; // Show the announcement div } else { announcementDiv.style.display = 'none'; // Hide the announcement div } if (isPanelMessageVisible) { panelMessageDiv.style.display = 'block'; // Show the panel message div } else { panelMessageDiv.style.display = 'none'; // Hide the panel message div } // Update button text based on the visibility of the announcement div button.textContent = (isPostingFormVisible && isAnnouncementVisible && isPanelMessageVisible) ? '-' : '+'; // Add click event listener to the button button.addEventListener('click', () => { // Toggle visibility of both divs const isCurrentlyVisible = postingFormDiv.style.display !== 'none' && announcementDiv.style.display !== 'none' && panelMessageDiv.style.display !== 'none'; if (isCurrentlyVisible) { postingFormDiv.style.display = 'none'; // Hide the posting div announcementDiv.style.display = 'none'; // Hide the announcement div panelMessageDiv.style.display = 'none'; // Hide the panel message div button.textContent = '+'; // Change button text localStorage.setItem('postingFormVisible', 'false'); // Save state localStorage.setItem('announcementVisible', 'false'); // Save state localStorage.setItem('panelMessageVisible', 'false'); // Save state } else { postingFormDiv.style.display = 'block'; // Hide the posting div announcementDiv.style.display = 'block'; // Show the announcement div panelMessageDiv.style.display = 'block'; // Show the panel message div button.textContent = '-'; // Change button text localStorage.setItem('postingFormVisible', 'true'); // Save state localStorage.setItem('announcementVisible', 'true'); // Save state localStorage.setItem('panelMessageVisible', 'true'); // Save state } }); } // Keyboard Shortcuts // QR (CTRL+Q) function toggleDiv(event) { // Check if Ctrl + Q is pressed if (event.ctrlKey && (event.key === 'q' || event.key === 'Q')) { const hiddenDiv = document.getElementById('quick-reply'); // Toggle QR if (hiddenDiv.style.display === 'none' || hiddenDiv.style.display === '') { hiddenDiv.style.display = 'block'; // Show the div } else { hiddenDiv.style.display = 'none'; // Hide the div } } } // Add an event listener for keydown events document.addEventListener('keydown', toggleDiv); // Custom CSS injection function addCustomCSS(css) { if (!css) return; const style = document.createElement('style'); style.type = 'text/css'; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); } // Get the current URL path const currentPath = window.location.pathname.toLowerCase(); const currentHost = window.location.hostname.toLowerCase(); // Apply CSS based on URL pattern // Thread page CSS if (/\/res\/[^/]+\.html$/.test(currentPath)) { const css = ` /* Quick Reply */ #quick-reply { display: block; padding: 0 !important; top: auto !important; bottom: 0; left: auto !important; position: fixed; right: 0 !important; } #qrbody { resize: vertical; max-height: 50vh; } .floatingMenu { padding: 0 !important; } #qrFilesBody { max-width: 300px; } /* Banner */ #bannerImage { width: 305px; right: 0; position: fixed; top: 26px; } .innerUtility.top { margin-top: 2em; background-color: transparent !important; color: var(--link-color) !important; } .innerUtility.top a { color: var(--link-color) !important; } /* Hover Posts */ img[style*="position: fixed"] { position: fixed !important; max-width: 80vw; max-height: 80vh !important; z-index: 200; } .quoteTooltip { z-index: 110; } /* (You) Replies */ .innerPost:has(.youName) { border-left: solid #68b723 5px; } .innerPost:has(.quoteLink.you) { border-left: solid #dd003e 5px; } /* Filename */ .originalNameLink { display: inline; overflow-wrap: anywhere; white-space: normal; } `; addCustomCSS(css); } if (/^8chan\.(se|moe)$/.test(currentHost)) { // General CSS for all pages const css = ` /* Margins */ #mainPanel { margin-left: 10px; margin-right: 305px; margin-top: 0; margin-bottom: 0; } .innerPost { margin-left: 40px; display: block; } /* Cleanup */ #footer, #actionsForm, #navTopBoardsSpan, .coloredIcon.linkOverboard, .coloredIcon.linkSfwOver, .coloredIcon.multiboardButton, #navLinkSpan>span:nth-child(9), #navLinkSpan>span:nth-child(11), #navLinkSpan>span:nth-child(13) { display: none; } /* Header */ #dynamicHeaderThread, .navHeader { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); } /* Thread Watcher */ #watchedMenu .floatingContainer { min-width: 330px; } .quoteTooltip .innerPost { overflow: hidden; } `; addCustomCSS(css); } // Catalog page CSS if (/\/catalog\.html$/.test(currentPath)) { const css = ` #dynamicAnnouncement { display: none; } #postingForm { margin: 2em auto; } `; addCustomCSS(css); }