Greasy Fork

8chan Style Script

Script to style 8chan

目前为 2025-04-18 提交的版本。查看 最新版本

// ==UserScript==
// @name        8chan Style Script
// @namespace   8chanSS
// @match       *://8chan.moe/*
// @match       *://8chan.se/*
// @grant       none
// @version     1.0
// @author      Anon
// @run-at      document-end
// @description Script to style 8chan
// @license     MIT
// ==/UserScript==

// Header Catalog Links
document.getElementById('navBoardsSpan').addEventListener('click', function () {
    var boardLinks = document.querySelectorAll('#navBoardsSpan a');
    var appendString = '\/catalog.html';

    boardLinks.forEach(function (link) {
        link.href += appendString;
    });
});

// Scroll to last read post
// Function to save the scroll position
function saveScrollPosition() {
    const scrollPosition = window.scrollY; // Get the current vertical scroll position
    localStorage.setItem('scrollPosition', scrollPosition); // Store it in localStorage
}

// Function to restore the scroll position
function restoreScrollPosition() {
    const savedPosition = localStorage.getItem('scrollPosition'); // Retrieve the saved position
    if (savedPosition) {
        window.scrollTo(0, parseInt(savedPosition, 10)); // Scroll to the saved position
    }
}

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