您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Filters posts on any lemmy instance by text in the title. It can also auto-open image posts, unblur thumbnails, and other things.
当前为
// ==UserScript== // @name Lemmy post utilities - Filter posts by title // @namespace Violentmonkey Scripts // @description Filters posts on any lemmy instance by text in the title. It can also auto-open image posts, unblur thumbnails, and other things. // @match https://*lemmy*.*/ // @include https://burggit.moe/* // @include https://lemmit.online/* // @include https://yiffit.net/* // @include https://reddthat.com/* // @include https://sh.itjust.works/* // @grant GM_getValue // @grant GM_setValue // @version 2.0.1 // @icon data:image/png;base64,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 // @author Xynoth // @license GPT-3 // @date 14/7/2023, 20:15:03 // ==/UserScript== //---------------------------------------- // CONSTANTS //---------------------------------------- // Key constants const BLOCKED_TAGS_KEY = "blockedTags"; const BLOCKED_CASE_SENSITIVE_TAGS_KEY = "blockedCaseSensitiveTags"; const AUTO_OPEN_MEDIA_POSTS_KEY = "autoOpenMediaPosts"; const UNBLUR_THUMBNAILS_KEY = "unblurThumbs"; const SHOW_FILTERED_STUBS_KEY = "showFilteredStubs"; const COMMAS_AS_SEPARATORS_KEY = "useCommasAsSeparators"; const CASE_SENSITIVE_KEY = "caseSensitiveTag"; const WIDGET_HEIGHT_KEY = "widgetHeight"; const FIX_BROKEN_VIDEO_PREVIEWS_KEY = "fixBrokenVideoPreviews"; const MARK_POSTS_AS_NSFW_KEY = "markNewPostsAsNSFW"; // Constant selectors const postsContainerClass = "post-listings"; const profileContainerClass = "person-details"; const searchContainerClass = "search" const postContainer = "post-listing"; const loadingSpinnerSelector = ".icon.spin"; // This is the "creators" select in the search page const postCommunityContainer = ".community-link"; const postCommunityNameContainer = `${postCommunityContainer} > span`; const postSrcLink = `div:nth-of-type(2) p a`; const fixedPreviewContainerClass = "fixed-preview"; const fixedPreviewVideoClass = "fixed-preview-video"; const postAsNSFW = "#post-nsfw[type='checkbox']"; const createPostContainerId = "createPostForm"; const editPostClass = "post-form"; // GUI main elements const settingsWidgetId = "settings-widget"; const blockedTagsDialogId = "tags-dialog"; const dialogBgId = "dialog-bg"; const settingsWidgetContainerId = "widget-container"; const blockTagListId = "blocked-insensitive-tag-list"; const csBlockTagListId = "blocked-sensitive-tags-list"; const openedPostChecker = "already-opened"; const filteredPostChecker = "filter-checked"; const processedPageChecker = "processed-page"; // CSS Color constants const caseInsensitiveTagColor = "#dd2222"; const caseSensitiveTagColor = "#2052b3"; const primaryBtnColor = "#0052cc"; const primaryBtnHoverColor = "#0066ff"; const primaryBtnActiveColor = "#0047b3"; const widgetBgColor = "#1a1a1b"; const errorToastColor = "#f94b4b"; const errorToastBgColor = "#2f0808"; // Image constants const logoImg = GM_info.script.icon; // HTML content constants const bottomWidgetContent = ` <div id="${settingsWidgetContainerId}"> <h1><span id="widget-logo"></span>Lemmy post utilities</h1> <div class="form-entry"> <label>Blocked tags: </label> <div class="btn-container"> <button id="block-tag-btn" type="button">🖊</button> </div> </div> <div class="form-entry"> <label> Show stubs on filter: </label> <div class="btn-container"> <span class="switch" id="show-stub-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div class="form-entry"> <label> Fix broken video previews: </label> <div class="btn-container"> <span class="switch" id="fix-video-previews-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div class="form-entry"> <label> Auto open media previews: </label> <div class="btn-container"> <span class="switch" id="auto-open-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div class="form-entry"> <label> Unblur NSFW thumbnails: </label> <div class="btn-container"> <span class="switch" id="unblur-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div class="form-entry"> <label> Mark new posts as NSFW: </label> <div class="btn-container"> <span class="switch" id="default-nsfw-posts-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> </div> `; const tagsDialogContent = ` <div id="blocked-tags-dialog-container"> <div id ="blocked-tags-dialog-head"> <h1>Blocked tags editor</h1> <button type="button" class="close-dialog-btn">⨯</button> </div> <div> <p>Any tag added here will hide any post that contains the word in its title.</p> <p>These are the blocked tags you have for this instance:</p> <div id="blocked-tags-field-container"> <div id="blocked-tags-field"> <p id="empty-blocked-tags">You haven't blocked anything yet.</p> <ul id="${blockTagListId}" class="blocked-tags-list" hidden> </ul> <hr id="blocked-tags-separator" hidden> <ul id="${csBlockTagListId}" class="blocked-tags-list" hidden> </ul> </div> <div id="blocked-tags-field-legend"> <span id="tag-case-insensitive-legend" class="tag-legend"> <span class="tag-color-legend"></span> <small class="tag-label-legend">Case insensitive tag</small> </span> <span id="tag-case-sensitive-legend" class="tag-legend"> <span class="tag-color-legend"></span> <small class="tag-label-legend">Case sensitive tag</small> </span> </div> </div> <div class="switch-container"> <label> Use commas as tag separators: </label> <div class="btn-container"> <span class="switch" id="use-commas-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div class="switch-container"> <label> Add tags as case sensitive: </label> <div class="btn-container"> <span class="switch" id="case-sensitive-swt"> <input type="checkbox"> <span class="slider round"></span> </span> </div> </div> <div id="tag-input-container"> <input type="text" id="tag-input" placeholder="Add your tags"> <button type="button" id="tag-save-btn">Save</button> </div> <label id="blocked-tags-input-error" hidden>Tag must be longer than 1 non-whitespace character!</label> </div> </div> `; const tagContent = ` <span title="TOOLTIP-CONTENT"> <label>TAG-NAME</label> <button type="button">⨯</button> </span> `; const filteredPostStubContent = ` <div class="hidden-post-stub-meta-container"> <p>This post was hidden because it contained the tag 'TAG'.</p> <span class="hidden-post-stub-btn-container"> <button class="show-hidden-post-title-btn" type="button">Show title</button> <button class="show-hidden-post-btn" type="button">Show post</button> </span> </div> <span class="stub-hidden-post-title" hidden> <br> <p>Post title was '<b>POST-TITLE</b>' from <a class="stub-hidden-post-community-link">POST-COMMUNITY</a> community.</p> </span> `; const videoSourceContent = ` <source src="VIDEO-SOURCE" type="video/VIDEO-TYPE"> `; // CSS to add to style elements const initialCSS = getInitialCSS(); const guiCSS = () => ` /* Fix for stubs leaving empty space at the bottom of the page for some reason */ html { overflow-y: auto; } body { overflow-y: clip; } /* The settings widget */ #${settingsWidgetId} { display: flex; float: right; bottom: 0; right: 0.8rem; max-width: 15rem; position: fixed; transform: translateY(${getData(WIDGET_HEIGHT_KEY) ?? storeData(WIDGET_HEIGHT_KEY, "21rem")}); transition: 200ms ease-in-out transform; } #${settingsWidgetId}:hover { transform: translateY(1px); } /* The container for the widget */ #widget-container { flex-direction: column; border: 1px solid #333; font-size: 0.9rem; background-color: ${widgetBgColor}; width: 100%; padding: 1rem; border-radius: .5rem .5rem 0 0; margin-top: 50px; /* This is the space that will allow showing the popup when hovering over it */ } #widget-container:hover { margin-top: 0; } /* Widget title */ #widget-container > h1 { font-size: 1rem; font-weight: bold; height: 1rem; } #widget-logo { display: inline-block; background-image: url('${logoImg}'); height: 1rem; width: 1rem; background-size: contain; background-repeat: no-repeat; margin-right: 0.5rem; } /* Widget block button */ #block-tag-btn { appearance: none; color: #ddd; background: rgba(255,255,255,0.1); border: none; border-radius: 2rem; padding: .3rem .5rem; } /* The layout for each label + form control */ .form-entry { display: inline-flex; } .form-entry:not(:first-child) { margin-top: 0.8rem; } .form-entry > label { display: flex; align-items: center; justify-content: left; width: 10rem; } .btn-container { display: grid; place-items: center; } /* The dialog to filter tags */ #${blockedTagsDialogId} { appearance: none; border: none; background-color: ${widgetBgColor}; padding: 1rem; border-radius: 0.5rem; box-shadow: 0 0 20px 5px rgba(255,255,255,0.2); position: fixed; top: 50%; bottom: 50%; z-index: 1001; } #dialog-bg { display: none; background-color: rgba(0,0,0,.5); /* For browsers that don't support backdrop-filter */ position: fixed; height: 100vh; width: 100vw; backdrop-filter: blur(2px); top: 0; left: 0; z-index: 1000; } #blocked-tags-separator { width: 100%; margin-left: auto; margin-right: auto; border-top: 2px solid #666; margin-top: .5rem; margin-bottom: .5rem; } #blocked-tags-dialog-head { display: flex; } #blocked-tags-dialog-head > h1 { font-size: 1.5rem; margin-bottom: 1rem; color: #fff; } #tag-input-container { margin-top: 1rem; margin-bottom: 1rem; display: flex; } #blocked-tags-input-error { color: ${errorToastColor}; font-size: 0.8rem; background-color: ${errorToastBgColor}; padding: .4rem; border-radius: .5rem; font-weight: bold; } #blocked-tags-field-container { margin-bottom: 1rem; } #blocked-tags-field { display: grid; padding: 1rem; max-height: 20rem; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; place-items: center; border: 1px solid #555; border-radius: 0.3rem; } #empty-blocked-tags { color: #666; margin: 0; padding: 1rem; } #tag-input { border: none; flex-grow: 100; background-color: #333; border-radius: .5rem; padding: .5rem 1rem; width: 80%; margin-right: 1rem; color: #ddd; } #tag-save-btn { appearance: none; border: none; background-color: ${primaryBtnColor}; color: #ddd; padding: .5rem 1rem; border-radius: 0.3rem; } #tag-save-btn:hover { background-color: ${primaryBtnHoverColor}; color: #fff; } #tag-save-btn:active { background-color: ${primaryBtnActiveColor}; color: #fff; } #tag-case-insensitive-legend > span { background-color: ${caseInsensitiveTagColor}; } #tag-case-sensitive-legend > span { background-color: ${caseSensitiveTagColor}; } #blocked-tags-field-legend { display: inline-flex; align-items: center; justify-content: center; width: 100%; } .close-dialog-btn { appearance: none; color: #fff; font-weight: bold; background-color: transparent; border: none; padding: 0.2rem 0.6rem; position: absolute; right: 0; top: 0; transition: 200ms all; } .close-dialog-btn:hover { border-radius: 6rem; background-color: rgba(255,255,255,0.2); } .switch-container { width: 100%; display: flex; } .switch-container > label { width: 16rem; margin-right: 1rem; } .blocked-tags-list { display: flex; flex-wrap: wrap; max-width: 35.5rem; margin-bottom: 0; padding: 0; } .blocked-tags-list > li { display: inline-flex; list-style-type: none; } .tag-legend { margin-right: 1rem; } .tag-color-legend { display: inline-block; width: 7px; height: 7px; } .tag-label-legend { font-size: 0.6rem; } .blocked-tag { border-radius: 0.4rem; font-size: 0.8rem; color: #fff; margin: .2rem; cursor: grab; } .blocked-tag[data-dragged-item] { cursor: grabbing; opacity: 0.7; } .blocked-tag label { padding-left: .5rem; white-space: pre-wrap; cursor: grab; } .blocked-tag button { appearance: none; color: #fff; background: transparent; border: none; border-radius: .4rem; } .blocked-tag button:hover { background-color: rgba(255,255,255,0.2); } .case-insensitive-tag { background-color: ${caseInsensitiveTagColor}; } .case-sensitive-tag { background-color: ${caseSensitiveTagColor}; } /* The filtered post stubs */ .filtered-post-stub p { margin-bottom: 0; } .hidden-post-stub-meta-container { display: flex; } .hidden-post-stub-btn-container { margin-left: auto; } .hidden-post-stub-btn-container > button { appearance: none; background-color: transparent; border: none; cursor: pointer; color: #3498db; margin-right: 1rem; } .hide-post-btn { appearance: none; color: #f22; font-weight: bold; font-size: 2rem; line-height: 1rem; width: 1.8rem; background-color: transparent; border: none; padding: 0.2rem 0.6rem; height: 0; float: right; transition: 200ms all; } /* The switch - the box around the slider */ .switch { position: relative; display: inline-block; width: 30px; height: 16px; } /* Hide default HTML checkbox */ .switch input { opacity: 0; width: 0; height: 0; } /* The slider */ .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #777; -webkit-transition: .4s; transition: .4s; } .slider:before { position: absolute; content: ""; height: 12px; width: 12px; left: 2px; bottom: 2px; background-color: white; -webkit-transition: .4s; transition: .4s; } input:checked + .slider { background-color: #2196F3; } input:focus + .slider { box-shadow: 0 0 1px #2196F3; } input:checked + .slider:before { -webkit-transform: translateX(12px); -ms-transform: translateX(12px); transform: translateX(12px); } /* Rounded sliders */ .slider.round { border-radius: 34px; } .slider.round:before { border-radius: 50%; } `; const unblurCSS = ` /* Unblurs thumbnails */ .img-blur { filter: none !important; } ` //---------------------------------------- // GUI AND INITIAL SETUP //---------------------------------------- // Load initial settings and store defaults if they didn't exist console.info(`Loading data for domain '${document.location.host}'.`); const blockedTitleTags = getData(BLOCKED_TAGS_KEY) ?? storeData(BLOCKED_TAGS_KEY, []); const csBlockedTitleTags = getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY) ?? storeData(BLOCKED_CASE_SENSITIVE_TAGS_KEY, []); const expandMediaPosts = getData(AUTO_OPEN_MEDIA_POSTS_KEY) ?? storeData(AUTO_OPEN_MEDIA_POSTS_KEY, false); const unblurThumbnails = getData(UNBLUR_THUMBNAILS_KEY) ?? storeData(UNBLUR_THUMBNAILS_KEY, false); const useCommasAsSeparators = getData(COMMAS_AS_SEPARATORS_KEY) ?? storeData(COMMAS_AS_SEPARATORS_KEY, true); const showStubsForFilteredPosts = getData(SHOW_FILTERED_STUBS_KEY) ?? storeData(SHOW_FILTERED_STUBS_KEY, true); const fixPostVideoPreviews = getData(FIX_BROKEN_VIDEO_PREVIEWS_KEY) ?? storeData(FIX_BROKEN_VIDEO_PREVIEWS_KEY, true); const caseSensitiveTags = getData(CASE_SENSITIVE_KEY) ?? storeData(CASE_SENSITIVE_KEY, false); const markNewPostsAsNSFW = getData(MARK_POSTS_AS_NSFW_KEY) ?? storeData(MARK_POSTS_AS_NSFW_KEY, false); if (blockedTitleTags.length > 0 || csBlockedTitleTags.length > 0) console.info("Waiting for page to fully load to block tags: ", blockedTitleTags, csBlockedTitleTags); // Add the GUI to control the settings to the document const settingWidget = addElement(document.body, "DIV", settingsWidgetId, bottomWidgetContent); const tagsDialog = addElement(document.body, "DIALOG", blockedTagsDialogId, tagsDialogContent); const dialogBg = addElement(document.body, "DIV", dialogBgId); // Add initial CSS changes updateCSS(); // Load tags into the dialog window updateVisibleTags(); // Reflect boolean values of settings on GUI document.querySelector("#show-stub-swt > input").checked = showStubsForFilteredPosts; document.querySelector("#auto-open-swt > input").checked = expandMediaPosts; document.querySelector("#unblur-swt > input").checked = unblurThumbnails; document.querySelector("#use-commas-swt > input").checked = useCommasAsSeparators; document.querySelector("#case-sensitive-swt > input").checked = caseSensitiveTags; document.querySelector("#fix-video-previews-swt > input").checked = fixPostVideoPreviews; document.querySelector("#default-nsfw-posts-swt > input").checked = markNewPostsAsNSFW; // --------- Add event listeners --------- // Blocked list dialog button document.getElementById("block-tag-btn").onclick = () => { openDialog(tagsDialog); } // Close dialog button tagsDialog.getElementsByClassName("close-dialog-btn")[0].onclick = () => { closeDialog(tagsDialog); } // Close dialog on clicking outside the dialog dialogBg.onclick = (event) => { event.stopPropagation(); closeDialog(tagsDialog); } // Show stubs on filtering posts document.getElementById("show-stub-swt").onclick = (event) => { const showStubs = storeData(SHOW_FILTERED_STUBS_KEY, toggleCheckbox(event)); const postsContainer = getPostContainer(); if (postsContainer) postsContainer.getElementsByClassName(postContainer).forEach(post => { let siblingNode = post.nextElementSibling; if (showStubs && post.hasAttribute("hidden")) { // Show sibling separator as well if (siblingNode && siblingNode.tagName == "HR") siblingNode.removeAttribute("hidden"); // Show post with the stub post.removeAttribute("hidden"); } else if (post.getElementsByClassName("filtered-post-stub")[0]) { // Remove separator if it exists if (siblingNode && siblingNode.tagName == "HR") siblingNode.setAttribute("hidden", true); // Hide post completelly post.setAttribute("hidden", true); } }) } // Fix some broken video previews document.getElementById("fix-video-previews-swt").onclick = (event) => { const fixVideoPreviews = storeData(FIX_BROKEN_VIDEO_PREVIEWS_KEY, toggleCheckbox(event)); const fixedPreviewContainers = document.getElementsByClassName(fixedPreviewContainerClass); const fixedPreviewVideos = document.getElementsByClassName(fixedPreviewVideoClass); const postsContainer = getPostContainer(); const wasProcessed = postsContainer.id === processedPageChecker; if (!postsContainer) return; if (!wasProcessed) postsContainer.setAttribute("id", processedPageChecker); if (fixVideoPreviews) { if (fixedPreviewContainers.length > 0 || fixedPreviewVideos.length > 0) { for(let i = 0; i < fixedPreviewContainers.length; i++) { fixedPreviewContainers[i].querySelector("picture").setAttribute("hidden", true); fixedPreviewVideos[i].removeAttribute("hidden"); } } else { fixBrokenVideoPreviews(postsContainer); } } else if (fixedPreviewContainers.length > 0 || fixedPreviewVideos.length > 0) { for(let i = 0; i < fixedPreviewContainers.length; i++) { fixedPreviewContainers[i].querySelector("picture").removeAttribute("hidden"); fixedPreviewVideos[i].setAttribute("hidden", true); } } } // Auto-open tags on page reload document.getElementById("auto-open-swt").onclick = (event) => { const openMediaPosts = storeData(AUTO_OPEN_MEDIA_POSTS_KEY, toggleCheckbox(event)); const postsContainer = getPostContainer(); const wasProcessed = postsContainer.id === processedPageChecker; if (!postsContainer) return; if (!wasProcessed) postsContainer.setAttribute("id", processedPageChecker); // Open posts if they weren't already if (openMediaPosts) { openPosts(postsContainer); } } // Unblur setting document.getElementById("unblur-swt").onclick = (event) => { storeData(UNBLUR_THUMBNAILS_KEY, toggleCheckbox(event)); // Update CSS of site after having changed the setting updateCSS(); } // Mark new posts as NSFW by default document.getElementById("default-nsfw-posts-swt").onclick = (event) => { const markAsNSFW = storeData(MARK_POSTS_AS_NSFW_KEY, toggleCheckbox(event)); const NSFWCheckbox = document.querySelector(postAsNSFW); const createPostForm = document.getElementById(createPostContainerId); if (NSFWCheckbox && !NSFWCheckbox.checked && markAsNSFW && createPostForm) NSFWCheckbox.click(); } // Auto-open tags on page reload document.getElementById("use-commas-swt").onclick = (event) => { storeData(COMMAS_AS_SEPARATORS_KEY, toggleCheckbox(event)); } // Auto-open tags on page reload document.getElementById("case-sensitive-swt").onclick = (event) => { storeData(CASE_SENSITIVE_KEY, toggleCheckbox(event)); } // Accept pressing enter while in some input to send the data document.getElementById("tag-input").onkeydown = (event) => { if(event.keyCode === 13){ document.getElementById("tag-save-btn").click(); } } // Event for tag save on button click or enter on input of tag blocking document.getElementById("tag-save-btn").onclick = () => { const tagInput = document.getElementById("tag-input"); const errorEl = document.getElementById("blocked-tags-input-error"); let tagsToSubmit = document.getElementById("tag-input").value; if (tagsToSubmit.trim().length > 1) { const isCaseSensitive = getData(CASE_SENSITIVE_KEY); const splitOnCommas = getData(COMMAS_AS_SEPARATORS_KEY); let tagsAsArray = getTagsAsArray(tagsToSubmit, splitOnCommas); let tagsKey; // Hide the error message if it was visible if (errorEl.hasAttribute("hidden")) errorEl.setAttribute("hidden", true); if (isCaseSensitive) tagsKey = BLOCKED_CASE_SENSITIVE_TAGS_KEY; else tagsKey = BLOCKED_TAGS_KEY; const oldTags = getData(tagsKey); tagsAsArray = tagsAsArray.filter(tag => !oldTags.includes(tag)); if (tagsAsArray.length > 0) { // Get the old array of tags and concatenate the new one const allTags = oldTags.concat(tagsAsArray); // Store the new array of tags and update the tags to show storeData(tagsKey, allTags); addTagsToDialog(tagsAsArray, tagsKey); // Hide empty tags element if it was visible and show the tags const blockedTagsListContainer = document.getElementById(blockTagListId); const csBlockedTagsListContainer = document.getElementById(csBlockTagListId); // Update tag section visibility if required checkForEmptyTags(getData(BLOCKED_TAGS_KEY), getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY)); } // Clear input tagInput.value = ""; } else { errorEl.removeAttribute("hidden"); } } //---------------------------------------- // MAIN METHODS //---------------------------------------- // Wait for page to fully load to start doing things window.onload = () => { const baseContainer = document.getElementById("app"); let NSFWCheckbox = document.querySelector(postAsNSFW); let createPostForm = document.getElementById(createPostContainerId); let searchContainer = document.getElementsByClassName(searchContainerClass)[0]; let postsContainer = getPostContainer(); if (NSFWCheckbox && !NSFWCheckbox.checked && markNewPostsAsNSFW && createPostForm) NSFWCheckbox.click(); // Make sure that the widget height is correct updateWidgetHeightCSS(); // If there is a posts container in the page if (postsContainer) { // Perform first filter if (blockedTitleTags.length > 0 || csBlockedTitleTags.length > 0) { console.info("Page loaded, filtering tags..."); filterPosts(postsContainer); } // Open remaining posts if enabled if (expandMediaPosts) openPosts(postsContainer); // Fix video previews if there was any post if (fixPostVideoPreviews) fixBrokenVideoPreviews(postsContainer); document.getElementsByClassName(postsContainer.className)[0].setAttribute("id", processedPageChecker); } // Observe the changes of the page to know when to rethrow the filter method when the user changes the page const observer = new MutationObserver((e) => { if (document.getElementById(processedPageChecker)) return; const blockedTags = getData(BLOCKED_TAGS_KEY); const csBlockedTags = getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY); const autoOpenMedia = getData(AUTO_OPEN_MEDIA_POSTS_KEY); const fixVideoPreviews = getData(FIX_BROKEN_VIDEO_PREVIEWS_KEY); const markAsNSFW = getData(MARK_POSTS_AS_NSFW_KEY); searchContainer = document.getElementsByClassName(searchContainerClass)[0]; NSFWCheckbox = document.querySelector(postAsNSFW); createPostForm = document.getElementById(createPostContainerId); postsContainer = getPostContainer(); // If on the creation post page, mark as NSFW the post if (NSFWCheckbox && !NSFWCheckbox.checked && markAsNSFW && createPostForm) NSFWCheckbox.click(); for (let i = 0; i < e.length; i++) { if (e[i].target.getElementsByClassName(editPostClass)[0]) return; let postEdit = e[i].target.getElementsByClassName("form-control")[0]; let filteredPostStub = e[i].target.getElementsByClassName("filtered-post-stub")[0]; let filteredPostBtn = e[i].target.getElementsByClassName("hide-post-btn")[0]; // Prevent filtering to retrigger if editting a post if (postEdit && searchContainer) return; // If a post was already filtered don't add stubs again if (filteredPostBtn || filteredPostStub) continue; // Perform actions if on one of the pages if (postsContainer) { if (blockedTags.length > 0 || csBlockedTags.length > 0) { console.info("Page reloaded, filtering new posts..."); filterPosts(postsContainer); } if (autoOpenMedia) openPosts(postsContainer); if (fixVideoPreviews) fixBrokenVideoPreviews(postsContainer); // Mark page as already processed document.getElementsByClassName(postsContainer.className)[0].setAttribute("id", processedPageChecker); break; } } }); observer.observe(baseContainer, {subtree: true, childList: true}); } // Gets initial CSS content function getInitialCSS() { let style = document.head.getElementsByTagName("style")[0]; if (!style) { style = document.createElement('style'); document.head.appendChild(style); return ""; } return style.innerHTML; } // Gets the CSS from the script to be in effect function getEffectiveCSS() { let fullCSS = guiCSS(); // Check each setting that changes CSS apart from the GUI if (getData(UNBLUR_THUMBNAILS_KEY)) fullCSS += unblurCSS; return fullCSS; } // Splits tags on commas if necessary function getTagsAsArray(tagsString, splitOnCommas=true) { if (splitOnCommas) { return tagsString.split(","); } return [tagsString]; } // Gets the current page posts container function getPostContainer() { return document.getElementsByClassName(postsContainerClass)[0] || document.getElementsByClassName(profileContainerClass)[0] || document.getElementsByClassName(searchContainerClass)[0] || document.querySelector(".post"); } // Adds an element to the document function addElement(parentEl, elementTag, elementId, html="", idAsClass=false) { let p = parentEl; let newElement = document.createElement(elementTag); if (idAsClass) newElement.className = elementId; else newElement.setAttribute('id', elementId); newElement.innerHTML = html; p.appendChild(newElement); return newElement; } // Adds the new tags to the dialog function addTagsToDialog(tagArray, tagsType) { let tagsContainerSelector = csBlockTagListId; if (tagsType === BLOCKED_TAGS_KEY) tagsContainerSelector = blockTagListId; const blockedTagsListEl = document.getElementById(tagsContainerSelector); tagArray.forEach(tag => { addTagElement(tag, tagsType, blockedTagsListEl); }); } // Adds a tag element to the dialog function addTagElement(tag, tagType, tagListElement) { // Get the specific css class for tag type let tagSpecificClass = "case-sensitive-tag"; if (tagType === BLOCKED_TAGS_KEY) tagSpecificClass = "case-insensitive-tag"; // Add the element to the dialog and bind the button event const newTag = addElement(tagListElement, "LI", `blocked-tag ${tagSpecificClass}`, tagContent .replace("TAG-NAME", tag) .replace("TAG-TYPE", tagType) .replace("TOOLTIP-CONTENT", `'${tag}' (${tagSpecificClass})`), true); newTag.getElementsByTagName("button")[0].onclick = () => { onRemoveBlockedTag(tagType, tag, newTag); } // Mark element as draggable newTag.setAttribute("draggable", true); // Handle drag & drop events newTag.ondragstart = (e) => { newTag.setAttribute("data-dragged-item", true); } newTag.ondragover = (e) => { e.preventDefault(); } newTag.ondragend = () => { newTag.removeAttribute("data-dragged-item"); } newTag.ondrop = (e) => { const target = e.target; const targetTag = target.nodeName === "LABEL" || target.nodeName == "BUTTON" ? target.parentNode.parentNode : target.parentNode; const draggedItem = document.querySelector('li[data-dragged-item]'); const nextTagName = targetTag.getElementsByTagName("label")[0].innerHTML; const movedTagName = draggedItem.getElementsByTagName("label")[0].innerHTML; let movedTagParentId = targetTag.parentNode.id; let draggedTagType = BLOCKED_TAGS_KEY; if (draggedItem.className.includes("case-sensitive-tag")) draggedTagType = BLOCKED_CASE_SENSITIVE_TAGS_KEY; // Only accept dropping it in the same area if (draggedItem.parentNode.id === movedTagParentId) { // Update order of tags let tags = getData(draggedTagType); // We need to change a bit the logic depending on the position of the element if (tags.indexOf(nextTagName) > tags.indexOf(movedTagName)) { tags.splice(tags.indexOf(nextTagName) + 1, 0, null); targetTag.parentNode.insertBefore(draggedItem, targetTag.nextSibling); } else { tags.splice(tags.indexOf(nextTagName), 0, null); targetTag.parentNode.insertBefore(draggedItem, targetTag); } tags.splice(tags.indexOf(movedTagName), 1); tags[tags.indexOf(null)] = movedTagName; storeData(draggedTagType, tags); } newTag.removeAttribute('data-dragged-item'); } } // Updates the CSS of the site function updateCSS() { let style = document.head.getElementsByTagName("style")[0]; if (!style) { style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); return; } // Update the CSS with the initial one + the settings CSS style.innerHTML = initialCSS + getEffectiveCSS(); } // Update the visible part of the settings widget function updateWidgetHeightCSS() { const widget = document.getElementById(settingsWidgetContainerId); const widgetHeight = widget.getBoundingClientRect().height; const visibleTopHeight = 10; if (widgetHeight > 0 && widgetHeight - visibleTopHeight > 0) { const oldHeight = getData(WIDGET_HEIGHT_KEY); let updatedHeight = (widgetHeight - visibleTopHeight) + "px"; if (oldHeight != updatedHeight) { storeData(WIDGET_HEIGHT_KEY, (widgetHeight - visibleTopHeight) + "px"); updateCSS(); } } } // Updates the visible tags in the blocked tags dialog function updateVisibleTags() { const csBlockedTags = getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY); const blockedTags = getData(BLOCKED_TAGS_KEY); const blockedTagsListEl = document.getElementById(blockTagListId); const csBlockedTagsListEl = document.getElementById(csBlockTagListId); // Reset list blockedTagsListEl.innerHTML = ""; csBlockedTagsListEl.innerHTML = ""; blockedTags.forEach(tag => { addTagElement(tag, BLOCKED_TAGS_KEY, blockedTagsListEl); }); csBlockedTags.forEach(tag => { addTagElement(tag, BLOCKED_CASE_SENSITIVE_TAGS_KEY, csBlockedTagsListEl); }); checkForEmptyTags(blockedTags, csBlockedTags); } // Toggles checkboxes on sliders inside the onclick event function toggleCheckbox(event) { let checkbox = event.target.parentNode.getElementsByTagName("input")[0]; checkbox.checked = !checkbox.checked; return checkbox.checked; } // Opens a dialog box function openDialog(dialogSelector) { dialogSelector.show(); dialogBg.style.display = "block"; } // Closes the dialog box function closeDialog(dialogSelector) { dialogSelector.close(); dialogBg.style.display = "none"; document.getElementById("blocked-tags-input-error").setAttribute("hidden", true); } // Adds the notice for no blocked tag if necessary function checkForEmptyTags(blockedTags, csBlockedTags) { const emptyTagsEl = document.getElementById("empty-blocked-tags"); const blockedTagsListEl = document.getElementById(blockTagListId); const csBlockedTagsListEl = document.getElementById(csBlockTagListId); const tagsSeparatorEl = document.getElementById("blocked-tags-separator"); const hasAnyBlockedTag = blockedTags.length > 0; const hasAnyCsBlockedTag = csBlockedTags.length > 0; const hasAnyTag = hasAnyBlockedTag || hasAnyCsBlockedTag; const isBlockedTagsListHidden = blockedTagsListEl.hasAttribute("hidden"); const isCsBlockedTagsListHidden = csBlockedTagsListEl.hasAttribute("hidden"); // Check if any has tags if (hasAnyTag) { emptyTagsEl.setAttribute("hidden", true); if (hasAnyBlockedTag) blockedTagsListEl.removeAttribute("hidden"); else blockedTagsListEl.setAttribute("hidden", true); if (hasAnyCsBlockedTag) csBlockedTagsListEl.removeAttribute("hidden"); else csBlockedTagsListEl.setAttribute("hidden", true); // Either both have tags or at least one has tags if (hasAnyBlockedTag && hasAnyCsBlockedTag) tagsSeparatorEl.removeAttribute("hidden"); else tagsSeparatorEl.setAttribute("hidden", true); // If it has no tags } else if (!hasAnyTag) { emptyTagsEl.removeAttribute("hidden"); blockedTagsListEl.setAttribute("hidden", true); tagsSeparatorEl.setAttribute("hidden", true); csBlockedTagsListEl.setAttribute("hidden", true); } } // Checks if the page is loading function checkIfPageIsLoading(postsContainer, posts) { const searchContainer = document.getElementsByClassName(searchContainerClass)[0]; const emptyResultsContainer = document.querySelector(loadingSpinnerSelector); return posts.length === 0 && postsContainer === searchContainer && emptyResultsContainer; } // Removes a tag from the view and the storeData function onRemoveBlockedTag(tagType, tagToRemove, tagElement) { // Remove the actual tag element tagElement.parentNode.removeChild(tagElement); // Update the storage let allTags = getData(tagType); let tagIndex = allTags.indexOf(tagToRemove); if (tagIndex != -1) allTags.splice(tagIndex, 1); storeData(tagType, allTags); // Toggle visibility of the elements where the tag was removed if they are empty checkForEmptyTags(getData(BLOCKED_TAGS_KEY), getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY)); } // Filters posts by words from the title as specified by the user function filterPosts(postsContainer) { let posts = postsContainer.getElementsByClassName(postContainer); if (checkIfPageIsLoading(postsContainer, posts)) { let intervalCount = 0; // Check for posts in 1 second intervals const checkInterval = setInterval(() => { posts = document.getElementsByClassName(postContainer); // Finish interval checking the search finished or 30 seconds passed if (posts.length > 0 || intervalCount > 30 || !document.querySelector(loadingSpinnerSelector)) { clearInterval(checkInterval); filterPostsInPage(posts); } intervalCount++; }, 1000); return; } filterPostsInPage(posts); } // Filters the posts in the current page function filterPostsInPage(postsList) { const showStubs = getData(SHOW_FILTERED_STUBS_KEY); const blockedTags = getData(BLOCKED_TAGS_KEY); const csBlockedTags = getData(BLOCKED_CASE_SENSITIVE_TAGS_KEY); Array.from(postsList).forEach(post => { if (post.getElementsByClassName(editPostClass)[0]) return; post.setAttribute(filteredPostChecker, true); const communityEl = post.querySelector(postCommunityNameContainer); const communityLinkEl = post.querySelector(postCommunityContainer); let titleEl = post.querySelector(".post-title h1 span"); let community; let communityLink; let title; // Make sure we get a valid title selector if (!titleEl) titleEl = post.querySelector(".post-title h1 a"); title = titleEl.innerHTML; // Make sure community info is in post, otherwise get from page assuming the user // is in the community view instead, where the community isn't in the posts but the page if (!communityEl) { community = document.querySelector(postCommunityNameContainer).innerHTML; communityLink = document.querySelector(postCommunityContainer).href; } else { community = communityEl.innerHTML; communityLink = communityLinkEl.href; } const postInfo = { post: post, title: title, communityName: community, communityLink: communityLink } // Filter posts using case insensitive tags if (blockedTags.length > 0) { for (let bt = 0; bt < blockedTitleTags.length; bt++) { let tag = blockedTitleTags[bt] if (title.toLowerCase().includes(tag.toLowerCase())) { removePost(postInfo, tag, showStubs); return; } } } // Filter posts using case sensitive tags if (csBlockedTags.length > 0) { for (let bt = 0; bt < blockedTitleTags.length; bt++) { let tag = blockedTitleTags[bt] if (title.includes(tag)) { removePost(postInfo, tag, showStubs); return; } } } }) } // Filters out a post function removePost(postInfo, tag, showStubs) { const post = postInfo.post; // Hide the content of the children and add a stub by default addFilterStubToPost(postInfo, tag) hidePostChildren(post); // If the setting to show stubs is disabled we hide the post completelly if (!showStubs) { // Removes the posts from the body completelly and logs it to the console console.info(`Removing post with title ${postInfo.title} from community ${postInfo.communityName} because it contains tag ${tag}.`); post.setAttribute("hidden", true); let siblingNode = post.nextElementSibling; // Remove separator if it exists if (siblingNode && siblingNode.tagName == "HR") siblingNode.setAttribute("hidden", true); } } // Hides all elements of the post children function hidePostChildren(post) { for (let i = 0; i < post.children.length; i++) { let child = post.children[i]; if (child.className === "filtered-post-stub" || child.className === "hide-post-btn") continue; child.style.height = "0"; child.style.width = "0"; child.style.visibility = "hidden"; } } // Adds a filter stub after filtering a post function addFilterStubToPost(postInfo, tag) { let stub = addElement(postInfo.post, "DIV", "filtered-post-stub", filteredPostStubContent.replace("TAG", tag) .replace("POST-TITLE", postInfo.title) .replace("POST-COMMUNITY", postInfo.communityName), true); stub.getElementsByClassName("stub-hidden-post-community-link")[0].href = postInfo.communityLink; // Add actions for the buttons stub.getElementsByClassName("show-hidden-post-title-btn")[0].onclick = () => { stub.getElementsByClassName("stub-hidden-post-title")[0].removeAttribute("hidden"); stub.getElementsByClassName("show-hidden-post-title-btn")[0].setAttribute("hidden", true); } stub.getElementsByClassName("show-hidden-post-btn")[0].onclick = () => { stub.setAttribute("hidden", true); // Make sure the title button element is hidden stub.getElementsByClassName("stub-hidden-post-title")[0].setAttribute("hidden", true); stub.getElementsByClassName("show-hidden-post-title-btn")[0].removeAttribute("hidden"); // Prepend the post re-remover if it didn't exist yet let postRemoveEl = postInfo.post.getElementsByClassName("hide-post-btn")[0]; if (!post.getElementsByClassName("hide-post-btn")[0]) { postRemoveEl = document.createElement("BUTTON"); postRemoveEl.className = "hide-post-btn"; postRemoveEl.innerHTML = "🗶"; postRemoveEl.setAttribute("type", "button"); post.prepend(postRemoveEl); postRemoveEl.onclick = () => { stub.removeAttribute("hidden"); postRemoveEl.setAttribute("hidden", true); hidePostChildren(postInfo.post); } } else postRemoveEl.removeAttribute("hidden"); for (let i = 0; i < postInfo.post.children.length; i++) { postInfo.post.children[i].style = ""; } } } // Opens media posts so that the image or video is shown for all posts in the current page of the timeline function openPosts(postsContainer) { let posts = postsContainer.getElementsByClassName(postContainer); if (checkIfPageIsLoading(postsContainer, posts)) { let intervalCount = 0; // Check for posts in 1 second intervals const checkInterval = setInterval(() => { posts = document.getElementsByClassName(postContainer); // Finish interval checking the search finished or 30 seconds passed if (posts.length > 0 || intervalCount > 30 || !document.querySelector(loadingSpinnerSelector)) { clearInterval(checkInterval); clickPostsThumbnail(posts); } intervalCount++; }, 1000); return; } clickPostsThumbnail(posts) } // Clicks the thumbnail of the post to open it function clickPostsThumbnail(posts) { Array.from(posts).forEach(post => { const postHasMedia = post.querySelector("picture, video"); let clickableThumbnail = post.querySelector("button.thumbnail"); if (postHasMedia) { if (!clickableThumbnail) clickableThumbnail = post.querySelector("a.text-body[href$='.mp4'] .thumbnail, a.text-body[href$='.webm'] .thumbnail, a.text-body[href^='https://www.redgifs.com/watch'] .thumbnail") if (clickableThumbnail && !post.getElementsByClassName("filtered-post-stub")[0]) clickableThumbnail.click(); post.setAttribute(openedPostChecker, true); } }) } // Fixes imgur previews showing as jpg instead of the actual video function fixBrokenVideoPreviews(postsContainer) { let posts = postsContainer.getElementsByClassName(postContainer); if (checkIfPageIsLoading(postsContainer, posts)) { let intervalCount = 0; // Check for posts in 1 second intervals const checkInterval = setInterval(() => { posts = document.getElementsByClassName(postContainer); // Finish interval checking the search finished or 30 seconds passed if (posts.length > 0 || intervalCount > 30 || !document.querySelector(loadingSpinnerSelector)) { clearInterval(checkInterval); updateVideoPreviews(posts); } intervalCount++; }, 1000); return; } updateVideoPreviews(posts); } // Updates the video previews when needed so that they work again function updateVideoPreviews(posts) { Array.from(posts).forEach(post => { const postSourceLinkEl = post.querySelector(postSrcLink); const imageLinkContainer = post.querySelectorAll("div:nth-of-type(3) > a:not(.btn)"); if (postSourceLinkEl && imageLinkContainer.length > 0) { const postSourceLink = postSourceLinkEl.href; let newSrc = postSourceLink; if (postSourceLink.includes("i.imgur.com") && postSourceLink.endsWith(".gifv")) newSrc = postSourceLink.replace(".gifv", ".mp4"); if (postSourceLink.includes("i.redd.it") && postSourceLink.endsWith(".gif")) newSrc = imageLinkContainer[0].href; // Only apply changes if the src was suposed to be different if (newSrc != postSourceLink) { imageLinkContainer.forEach(imageContainer => { let pictureContainer = imageContainer.querySelector("picture"); if (pictureContainer) { pictureContainer.setAttribute("hidden", true); let videoElement = addElement(post.querySelector("div:nth-child(3).my-2"), "VIDEO", `embed-responsive-item ${fixedPreviewVideoClass} col-12`, videoSourceContent.replace("VIDEO-SOURCE", newSrc) .replace("VIDEO-TYPE", newSrc.split(".").at(-1)), true) videoElement.setAttribute("controls", ""); videoElement.parentNode.classList.add("embed-responsive", fixedPreviewContainerClass); } }); } } }); } //---------------------------------------- // STORAGE METHODS //---------------------------------------- // Composes the key with the current instance name to store data per-instance function composeInstanceKey(key) { return document.location.host + "->" + key; } // Saves data to the storage of the userscript function storeData(key, value) { const instanceKey = composeInstanceKey(key); let treatedValue = value; if (typeof value === "array" || typeof value === "object") treatedValue = JSON.stringify(value); GM_setValue(instanceKey, treatedValue); return treatedValue; } // Gets data from the userscript storage function getData(key) { const instanceKey = composeInstanceKey(key); let value = GM_getValue(instanceKey); if (value === null || value === undefined) return null; if (typeof value === "string") { let isValueArray = value.startsWith("[") && value.endsWith("]"); let isValueObject = value.startsWith("{") && value.endsWith("}"); if (isValueArray || isValueObject) return JSON.parse(value); } return value; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址