- // ==UserScript==
- // @name Remove Blacklisted Images with Sidebar
- // @namespace http://tampermonkey.net/
- // @version 1.3
- // @description Adds a sidebar to manage and display blacklisted tags and hides or shows images based on the blacklist on rule34.xxx.
- // @author Dramorian
- // @match https://rule34.xxx/index.php?page=post*
- // @match https://rule34.xxx/index.php?page=favorites*
- // @match https://rule34.xxx/index.php?page=comment*
- // @exclude https://rule34.xxx/index.php?page=post&s=view*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
- // @grant none
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- const debug = true; // Set to false to disable logging
-
- function getTagBlacklist() {
- const cookieValue = getCookieValue('tag_blacklist');
- const blacklist = cookieValue ? decodeBlacklist(cookieValue) : [];
-
- if (debug) {
- console.log('Retrieved blacklist:', blacklist);
- }
-
- return blacklist;
- }
-
- function getCookieValue(name) {
- const cookie = document.cookie.split('; ').find(row => row.startsWith(`${name}=`));
- return cookie ? cookie.split('=')[1] : null;
- }
-
- function decodeBlacklist(encodedString) {
- return decodeURIComponent(encodedString).split('%20');
- }
-
- // Function to create and inject the sidebar
- function createSidebar() {
- const sidebarHTML = `
- <div id="blacklist-box">
- <div id="sidebar-header">
- <h2>Blacklisted</h2>
- <button id="toggle-header" aria-label="Toggle Sidebar">
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-right-fill" viewBox="0 0 16 16" class="toggle-icon">
- <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
- </svg>
- </button>
- </div>
- <div id="sidebar-content" style="display: none;">
- <ul id="blacklist-list" style="list-style: none;"></ul>
- </div>
- <div id="sidebar-footer">
- <button id="disable-all-blacklists">Disable All</button>
- <button id="re-enable-all-blacklists" style="display: none;">Re-enable All</button>
- </div>
- </div>
- `;
-
- const targetElement = findSidebarInsertionTarget();
- if (!targetElement) {
- console.error('Suitable element for sidebar insertion not found.');
- return;
- }
-
- insertSidebar(targetElement, sidebarHTML);
- updateSidebar();
- addSidebarEventListeners();
- addCollapsibleFunctionality();
- }
-
- function findSidebarInsertionTarget() {
- return document.querySelector('div.tag-search') || document.querySelector('#content > h1');
- }
-
- function insertSidebar(targetElement, sidebarHTML) {
- const sidebarContainer = document.createElement('div');
- sidebarContainer.innerHTML = sidebarHTML;
- targetElement.insertAdjacentElement('afterend', sidebarContainer);
- if (debug) {
- console.log(`Sidebar inserted after ${targetElement.tagName.toLowerCase()} element.`);
- }
- }
-
- function addCollapsibleFunctionality() {
- const toggleButton = document.getElementById('toggle-header');
- const icon = toggleButton.querySelector('svg');
-
- toggleButton.addEventListener('click', () => {
- const content = document.getElementById('sidebar-content');
- const isCollapsed = content.style.display === 'none';
- content.style.display = isCollapsed ? 'block' : 'none';
-
- // Apply rotation transform
- icon.style.transform = isCollapsed ? 'rotate(90deg)' : 'rotate(0deg)';
- icon.style.transition = 'transform 0.25s ease';
-
- if (debug) {
- console.log('Sidebar toggle button clicked. New display state:', content.style.display);
- }
- });
- }
-
- // Function to update the sidebar with current blacklist items
- function updateSidebar() {
- const blacklist = getTagBlacklist();
- const blacklistList = document.getElementById('blacklist-list');
- const header = document.querySelector('#blacklist-box h2');
-
- blacklistList.innerHTML = '';
-
- const detectedTags = blacklist.filter(isTagDetectedOnPage);
- const totalHiddenPosts = updateBlacklistList(detectedTags, blacklistList);
-
- updateSidebarVisibility(detectedTags.length, header, totalHiddenPosts);
- }
-
- function updateBlacklistList(detectedTags, blacklistList) {
- let totalHiddenPosts = 0;
-
- detectedTags.forEach(tag => {
- const isDisabled = isTagDisabled(tag);
- const hiddenCount = getInitialHiddenPostCount(tag);
- totalHiddenPosts += hiddenCount;
-
- const listItem = createBlacklistListItem(tag, isDisabled, hiddenCount);
- blacklistList.appendChild(listItem);
-
- if (debug) console.log(`Added tag to sidebar: ${tag}, Hidden count: ${hiddenCount}`);
- });
-
- if (debug) console.log('Sidebar updated. Total hidden posts:', totalHiddenPosts);
-
- // Re-attach event listeners after updating the sidebar
- addCheckboxEventListeners();
-
- return totalHiddenPosts;
- }
-
- function createBlacklistListItem(tag, isDisabled, hiddenCount) {
- const listItem = document.createElement('li');
- listItem.innerHTML = `
- <label>
- <input type="checkbox" class="blacklist-checkbox" data-tag="${encodeURIComponent(tag)}" ${isDisabled ? '' : 'checked'}>
- ${tag} <span class="count">${hiddenCount}</span>
- </label>
- `;
- return listItem;
- }
-
- function updateSidebarVisibility(tagCount, header, totalHiddenPosts) {
- const sidebar = document.getElementById('blacklist-box');
-
- if (tagCount === 0) {
- sidebar.style.display = 'none';
- header.textContent = 'Blacklisted (0)';
- if (debug) console.log('No blacklisted tags detected. Sidebar hidden.');
- } else {
- sidebar.style.display = '';
- header.textContent = `Blacklisted (${totalHiddenPosts})`;
- }
- }
-
- // Function to check if a tag is currently detected on the page
- function isTagDetectedOnPage(tag) {
- const elements = document.querySelectorAll('img[title], div[id^="p"] > div.col1.thumb > a > img');
- return Array.from(elements).some(el => {
- const title = el.getAttribute('title');
- const isDetected = title && containsExactTag(title, [tag]);
- if (debug && isDetected) console.log(`Tag detected on page: ${tag}`);
- return isDetected;
- });
- }
-
- // Function to check if a tag is currently disabled
- function isTagDisabled(tag) {
- const disabledTags = JSON.parse(localStorage.getItem('disabled_tags') || '[]');
- const isDisabled = disabledTags.includes(tag);
- if (debug) console.log(`Tag ${tag} is ${isDisabled ? 'disabled' : 'enabled'}`);
- return isDisabled;
- }
-
- // Function to toggle the filtering state of a tag
- function toggleTag(tag, isEnabled) {
- let disabledTags = getDisabledTags();
-
- disabledTags = isEnabled ? removeTag(disabledTags, tag) : addTag(disabledTags, tag);
-
- if (debug) logTagState(tag, isEnabled);
-
- localStorage.setItem('disabled_tags', JSON.stringify(disabledTags));
- applyTagFiltering();
- }
-
- function getDisabledTags() {
- return JSON.parse(localStorage.getItem('disabled_tags') || '[]');
- }
-
- function removeTag(tags, tag) {
- return tags.filter(t => t !== tag);
- }
-
- function addTag(tags, tag) {
- if (!tags.includes(tag)) {
- tags.push(tag);
- }
- return tags;
- }
-
- function logTagState(tag, isEnabled) {
- const action = isEnabled ? 'enabled' : 'disabled';
- console.log(`Tag ${tag} ${action}`);
- }
-
- // Function to get the initial count of hidden posts for a given tag
- function getInitialHiddenPostCount(tag) {
- const favoriteCount = countHiddenPosts('img[title]', tag);
- const commentCount = countHiddenPosts('div[id^="p"] > div.col1.thumb > a > img', tag);
-
- const totalCount = favoriteCount + commentCount;
-
- if (debug) console.log(`Initial hidden post count for tag ${tag}: ${totalCount}`);
- return totalCount;
- }
-
- function countHiddenPosts(selector, tag) {
- let count = 0;
- const images = document.querySelectorAll(selector);
- images.forEach(img => {
- const imgTitle = img.getAttribute('title');
- if (imgTitle && containsExactTag(imgTitle, [tag])) {
- count++;
- }
- });
- return count;
- }
-
- // Function to check if the title contains any exact blacklist tag
- function containsExactTag(title, blacklist) {
- return blacklist.some(tag => {
- const regex = new RegExp(`\\b${tag}\\b`, 'i');
- const contains = regex.test(title);
- if (debug && contains) console.log(`Title "${title}" contains tag "${tag}"`);
- return contains;
- });
- }
-
- function addSidebarEventListeners() {
- const disableAllButton = document.getElementById('disable-all-blacklists');
- const reEnableAllButton = document.getElementById('re-enable-all-blacklists');
-
- disableAllButton.addEventListener('click', (e) => handleBlacklistToggle(e, true));
- reEnableAllButton.addEventListener('click', (e) => handleBlacklistToggle(e, false));
-
- // Attach checkbox event listeners
- addCheckboxEventListeners();
- }
-
- function handleBlacklistToggle(event, disable) {
- event.preventDefault();
-
- if (disable) {
- const allTags = getTagBlacklist();
- localStorage.setItem('disabled_tags', JSON.stringify(allTags));
- if (debug) console.log('Disabled all blacklisted tags');
- toggleButtonVisibility('disable-all-blacklists', 're-enable-all-blacklists');
- } else {
- localStorage.removeItem('disabled_tags');
- if (debug) console.log('Re-enabled all blacklisted tags');
- toggleButtonVisibility('re-enable-all-blacklists', 'disable-all-blacklists');
- }
-
- updateSidebar(); // Update the sidebar to reflect the changes
- applyTagFiltering(); // Apply filtering immediately
- }
-
- function toggleButtonVisibility(hiddenButtonId, visibleButtonId) {
- document.getElementById(hiddenButtonId).style.display = 'none';
- document.getElementById(visibleButtonId).style.display = 'inline';
- }
-
- // Function to attach event listeners to checkboxes
- function addCheckboxEventListeners() {
- document.querySelectorAll('#blacklist-list .blacklist-checkbox').forEach(checkbox => {
- checkbox.addEventListener('change', function () {
- const tag = decodeURIComponent(this.getAttribute('data-tag'));
- const isEnabled = this.checked;
- toggleTag(tag, isEnabled);
- if (debug) console.log(`Checkbox for tag "${tag}" changed to ${isEnabled ? 'enabled' : 'disabled'}`);
- });
- });
- }
-
- // Function to apply tag filtering to the page
- function applyTagFiltering() {
- const disabledTags = JSON.parse(localStorage.getItem('disabled_tags') || '[]');
- const allTags = getTagBlacklist();
-
- if (debug) console.log('Applying tag filtering. Disabled tags:', disabledTags, 'All tags:', allTags);
-
- // Hide posts for tags that are checked (enabled)
- hidePosts(allTags.filter(tag => !disabledTags.includes(tag)));
-
- // Show posts for tags that are unchecked (disabled)
- showPosts(disabledTags);
- }
-
- // Function to show posts for tags that should be visible
- function showPosts(tags) {
- tags.forEach(tag => updatePostsVisibility(tag, '', ''));
- }
-
- // Function to hide posts for tags that should be hidden
- function hidePosts(tags) {
- if (tags.length === 0) return; // If no tags to hide, exit the function.
-
- tags.forEach(tag => updatePostsVisibility(tag, 'none', 'important'));
- }
-
- // Helper function to update post visibility
- function updatePostsVisibility(tag, displayValue, importantValue) {
- const elements = document.querySelectorAll(`img[title*="${tag}"], div[id^="p"] > div.col1.thumb > a > img`);
- elements.forEach(el => {
- const title = el.getAttribute('title');
- if (title && (displayValue === '' || containsExactTag(title, [tag]))) {
- const parent = el.closest('span') || el.closest('div[id^="p"]');
- if (parent) {
- parent.style.display = displayValue;
- parent.style.setProperty('display', displayValue, importantValue);
- if (debug) {
- const action = displayValue === 'none' ? 'Hiding' : 'Showing';
- console.log(`${action} post for tag "${tag}"`);
- }
- }
- }
- });
- }
-
-
- // Function to remove the effect of the blacklisted-sidebar
- // Native "Hidden" button that hides/reveals images
- //TODO: think of the better logic
- function removeBlacklistedSidebarEffect() {
- handleBlacklistCount();
- removeSidebarElement();
- }
-
- function handleBlacklistCount() {
- const blacklistCountElement = document.getElementById('blacklist-count');
- if (!blacklistCountElement) return;
-
- const postCount = parseInt(blacklistCountElement.textContent, 10);
-
- if (postCount > 0) {
- const hiddenLink = blacklistCountElement.closest('h5').querySelector('a');
- if (hiddenLink) {
- hiddenLink.click();
- if (debug) {
- console.log('Clicked on the "Hidden" to remove the blacklisted effect');
- }
- }
- }
- }
-
- function removeSidebarElement() {
- const blacklistSidebar = document.getElementById('blacklisted-sidebar');
- if (blacklistSidebar) {
- blacklistSidebar.remove();
- if (debug) {
- console.log('Removed the native blacklisted-sidebar element from the page');
- }
- }
- }
-
- removeBlacklistedSidebarEffect();
- createSidebar();
- applyTagFiltering();
- })();