// ==UserScript== // @name Steam Janitor // @namespace jetsparrow-steam-janitor // @author Jetsparrow // @description Hide unwanted user content in browse view, endless scrolling, downloads // @match *://*.steamcommunity.com/workshop/browse/* // @run-at document-end // @version 0.0.4 // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_download // @connect ggntw.com // @downloadURL https://jetsparrow.github.io/steam-janitor/steam-janitor.user.js // ==/UserScript== const addGlobalStyle = (doc, css) => { let head = doc.getElementsByTagName('head')[0]; if (!head) return null; let style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); return style; } const htmlToElement = (doc, html) => { var template = doc.createElement('template'); template.innerHTML = html.trim(); return template.content.firstChild; } const onVisible = (element, callback) => { const observer = new IntersectionObserver( (entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { callback(); observer.unobserve(entry.target); } }); }, { rootMargin: "0px 0px 200px 0px" } ); observer.observe(element); }; const selector = { PAGING_INFO: ".workshopBrowsePagingWithBG", ITEM_CONTAINER: ".workshopBrowseItems .workshopItemPreviewHolder", ITEMS_CONTAINER: ".workshopBrowseItems", ITEMS_HOVERS: ".workshopBrowseItems script", NEXT_BUTTON: ".workshopBrowsePagingControls .pagebtn:last-child", PAGINATOR: ".workshopBrowsePagingControls", PAGE_INFO: ".workshopBrowsePagingInfo", FOOTER: ".workshopBrowsePaging", }; const elemId = { scrollTarget: "footer", filterToggleCheckbox: "janitorFilterToggleCheckbox", filterToggleOn: "janitorFilterOnIcon", filterToggleOff: "janitorFilterOffIcon", }; const cssClass = { unhidden: "janitorItem", hiddenFiltered: "janitorItemHidden", hiddenUnfiltered: "janitorItemHiddenUnfiltered", hideButton: "janitorHideButton", showButton: "janitorShowButton", filterToggle: "janitorFilterToggle", downloadButton: "janitorDownloadButton" }; const resource = { iconEyeOpen:"https://jetsparrow.github.io/steam-janitor/res/filter_toggle_open.png", iconEyeClosed:"https://jetsparrow.github.io/steam-janitor/res/filter_toggle_closed.png", btnHide:"https://jetsparrow.github.io/steam-janitor/res/janitor_hide.png", btnHideHover:"https://jetsparrow.github.io/steam-janitor/res/janitor_hide_hover.png", btnUnhide:"https://jetsparrow.github.io/steam-janitor/res/janitor_unhide.png", btnUnhideHover:"https://jetsparrow.github.io/steam-janitor/res/janitor_unhide_hover.png", btnDownload:"https://jetsparrow.github.io/steam-janitor/res/janitor_download.png", btnDownloadHover:"https://jetsparrow.github.io/steam-janitor/res/janitor_download_hover.png", }; const janitorCss = ` .${cssClass.filterToggle} * { vertical-align: middle; } .${cssClass.hiddenFiltered} {display:none !important; } .${cssClass.hiddenUnfiltered} img {opacity: 0.25;} .${cssClass.hideButton} {width:25px; height:25px;} .${cssClass.hiddenUnfiltered} .${cssClass.hideButton}:hover {background-image:url("${resource.btnUnhideHover}")} .${cssClass.hiddenUnfiltered} .${cssClass.hideButton} {background-image:url("${resource.btnUnhide}")} .${cssClass.unhidden} .${cssClass.hideButton}:hover {background-image:url("${resource.btnHideHover}")} .${cssClass.unhidden} .${cssClass.hideButton} {background-image:url("${resource.btnHide}")} .workshopItem .${cssClass.hideButton} { visibility: hidden; } .workshopItem:hover .${cssClass.hideButton} { visibility: visible; position: absolute; top: 4px; right: 6px;} .${cssClass.downloadButton} {width:25px; height:25px;} .${cssClass.downloadButton}:hover {background-image:url("${resource.btnDownloadHover}")} .${cssClass.downloadButton} {background-image:url("${resource.btnDownload}")} .workshopItem .${cssClass.downloadButton} { visibility: hidden; } .workshopItem:hover .${cssClass.downloadButton} { visibility: visible; position: absolute; top: 4px; right: 35px;} `; const setting = { filterEnabled: "janitorFilterEnabled" }; const defaultModData = () => { let d = new Object(); d.hide = false; return d; } const loadModData = (modId) => { var j = GM_getValue("modid:" + modId, ""); return j == "" ? defaultModData() : JSON.parse(j); } const saveModData = (modId, data) => GM_setValue("modid:" + modId, JSON.stringify(data)); // Update classes on mod container when model changes const updateHiddenClass = (doc, modId, filterOn) => { var container = doc.getElementById(modId)?.parentElement?.parentElement; if (!container) return; const d = loadModData(modId); container.classList.remove(cssClass.unhidden); container.classList.remove(cssClass.hiddenFiltered); container.classList.remove(cssClass.hiddenUnfiltered); if (!d.hide) container.classList.add(cssClass.unhidden); else if (filterOn) container.classList.add(cssClass.hiddenFiltered); else container.classList.add(cssClass.hiddenUnfiltered); } // When pressing the hide/unhide button on mod container const toggleHidden = (doc, modId) => { var d = loadModData(modId); d.hide = !d.hide; saveModData(modId, d); const filterOn = GM_getValue(setting.filterEnabled); updateHiddenClass(doc, modId, filterOn); } // When pressing the download button on mod container const downloadMod = (doc, modId) => { const id = modId.replace("sharedfile_", ""); GM_xmlhttpRequest( { url:"https://api.ggntw.com/steam.request", method: "POST", data: JSON.stringify( { url:"https://steamcommunity.com/sharedfiles/filedetails/?id=" + id }), headers: { "Content-type": "application/json; charset=UTF-8" }, onload: function(response) { if (response.status>299) { throw new Error(`HTTP error! Status: ${response.status}`); } const res = JSON.parse(response.responseText); console.log(res); const name = res.url.substring(res.url.lastIndexOf('/') + 1); GM_download(res.url, name); } }); } // Add hide butons to mod container const addJanitorButtons = (doc, container, id) => { const hideButton = htmlToElement(doc, `