4chan Blur-and-Hide Filter (Persistent Unblur)

This script blurs all 4chan posts

目前為 2025-07-16 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name      4chan Blur-and-Hide Filter (Persistent Unblur)
// @match     https://boards.4chan.org/*
// @grant     none
// @description This script blurs all 4chan posts
// @license MIT
// @version 0.0.1.20250716073752
// @namespace https://greasyfork.org/users/1495343
// ==/UserScript==  

(function() {
  const bannedPatterns = [/nsfw/i, /nsfl/i, /gore/i, /loli/i, /rape/i, /blood/i];
  const blurAmount     = '30px';

  // Inject blur CSS
  const style = document.createElement('style');
  style.textContent = `
    .safelBlur {
      filter: blur(${blurAmount});
      transition: filter .2s ease;
      cursor: pointer;
    }
    .safelBlur.unblur { filter: none; }
  `;
  document.head.appendChild(style);

  // Key prefix for localStorage
  const STORAGE_KEY = '4chan_safel_unblur_';

  // Determine storage key for a media URL
  function keyFor(src) {
    return STORAGE_KEY + src;
  }

  // Process a single post container
  function processPost(post) {
    // Hide posts matching banned patterns
    const msgEl = post.querySelector('.postMessage');
    const text  = msgEl && msgEl.innerText ? msgEl.innerText : '';
    for (let rx of bannedPatterns) {
      if (rx.test(text)) {
        post.style.display = 'none';
        return;
      }
    }

    // Blur each image/video, but auto-unblur if previously approved
    const mediaElems = post.querySelectorAll('img, video');
    mediaElems.forEach(media => {
      const src = media.src || (media.querySelector('source')?.src);
      if (!src) return;

      if (!media.classList.contains('safelBlur')) {
        media.classList.add('safelBlur');

        // Restore unblur state if stored
        if (localStorage.getItem(keyFor(src))) {
          media.classList.add('unblur');
        }

        // Toggle blur on click and update storage
        media.addEventListener('click', () => {
          media.classList.toggle('unblur');
          if (media.classList.contains('unblur')) {
            localStorage.setItem(keyFor(src), 'true');
          } else {
            localStorage.removeItem(keyFor(src));
          }
        });
      }
    });
  }

  // Apply filter to all current posts
  function runFilter() {
    document.querySelectorAll('.postContainer').forEach(processPost);
  }

  // Initial run + observe for new posts
  runFilter();
  new MutationObserver(runFilter)
    .observe(document.body, { childList: true, subtree: true });
})();