mirror of
https://github.com/Jetsparrow/steam-janitor.git
synced 2026-01-21 06:06:09 +03:00
v0.0.2-alpha
- Renamed (planning to do guides too) - Added filter toggle to switch between filtering and displaying hidden content - General tidying up
This commit is contained in:
parent
8d9ee97005
commit
3625ea3bda
13
README.md
13
README.md
@ -1,14 +1,17 @@
|
|||||||
# Steam Workshop Janitor
|
# Steam Janitor
|
||||||
_A userscript that makes browsing through thousands of mods viable_
|
_A userscript that makes browsing through thousands of mods viable_
|
||||||
|
|
||||||
## [Install 0.0.1](https://github.com/Jetsparrow/steam-workshop-janitor/raw/main/steam-workshop-janitor.user.js)
|
## [Install 0.0.2](https://github.com/Jetsparrow/steam-janitor/raw/main/steam-janitor.user.js)
|
||||||
|
_Requires a userscript extension for your browser, like [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en)_
|
||||||
|
|
||||||
**N.B.: this is a very crude, hacked together proof of concept. It doesn't even have a way to undo the filtering yet.**
|
**N.B.: this is a very crude alpha. Developed and tested in Google Chrome**
|
||||||
|
|
||||||
## Filter mods
|
## Filter mods
|
||||||
|
|
||||||
Adds a little "filter mod" X button to any workshop browse page. Use it, and you will no longer see the hundreds of miscellaneous translations to laguages you don't know, obsolete mods, etc.
|
Adds a little "hide" button to any workshop browse page. Use it, and you will no longer see the hundreds of miscellaneous translations to laguages you don't know, obsolete mods, or gachimuchi memes.
|
||||||
|
|
||||||
|
The userscripts adds a "filter" checkbox to the top paging controls, allowing you to switch between the filtered and unfiltered view.
|
||||||
|
|
||||||
## Endless scrolling
|
## Endless scrolling
|
||||||
|
|
||||||
After the filter runs its course, you will be left with barely any mods. Wndless scrolling fixes that.
|
After the filter runs its course, you will be left with barely any mods. Endless scrolling fixes that.
|
||||||
|
|||||||
192
steam-janitor.user.js
Normal file
192
steam-janitor.user.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @name Steam Janitor
|
||||||
|
// @description Hide unwanted user content in browse view, endless scrolling
|
||||||
|
// @match *://*.steamcommunity.com/workshop/browse/*
|
||||||
|
// @run-at document-end
|
||||||
|
// @version 0.0.2
|
||||||
|
// @grant GM_setValue
|
||||||
|
// @grant GM_getValue
|
||||||
|
// @grant GM_listValues
|
||||||
|
// @updateURL https://raw.githubusercontent.com/Jetsparrow/steam-janitor/main/steam-janitor.js
|
||||||
|
// @downloadURL https://raw.githubusercontent.com/Jetsparrow/steam-janitor/main/steam-janitor.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 selectors = {
|
||||||
|
PAGING_INFO: ".workshopBrowsePagingWithBG",
|
||||||
|
ITEM_CONTAINER: ".workshopBrowseItems .workshopItemPreviewHolder",
|
||||||
|
ITEMS_CONTAINER: ".workshopBrowseItems",
|
||||||
|
ITEMS_HOVERS: ".workshopBrowseItems script",
|
||||||
|
NEXT_BUTTON: ".workshopBrowsePagingControls .pagebtn:last-child",
|
||||||
|
PAGINATOR: ".workshopBrowsePagingControls",
|
||||||
|
FOOTER: ".workshopBrowsePaging"
|
||||||
|
};
|
||||||
|
|
||||||
|
const unhiddenClass = "janitorItem";
|
||||||
|
const hiddenFilteredClass = "janitorItemHidden";
|
||||||
|
const hiddenUnfilteredClass = "janitorItemHiddenUnfiltered";
|
||||||
|
const hideButtonClass = "janitorHideButton";
|
||||||
|
const showButtonClass = "janitorShowButton";
|
||||||
|
|
||||||
|
const janitorCss = `
|
||||||
|
.${hiddenFilteredClass} { display:none !important; }
|
||||||
|
.${hiddenUnfilteredClass} {opacity: 0.25;}
|
||||||
|
.${hiddenUnfilteredClass} .${showButtonClass} { display:inline !important; }
|
||||||
|
.${hiddenUnfilteredClass} .${hideButtonClass} { display:none !important; }
|
||||||
|
.${unhiddenClass} .${showButtonClass} { display:none !important; }
|
||||||
|
.${unhiddenClass} .${hideButtonClass} { display:inline !important; }
|
||||||
|
`;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
const updateHiddenClass = (doc, modId) => {
|
||||||
|
var container = doc.getElementById(modId)?.parentElement?.parentElement;
|
||||||
|
if (!container) return;
|
||||||
|
const d = loadModData(modId);
|
||||||
|
container.classList.remove(unhiddenClass);
|
||||||
|
container.classList.remove(hiddenFilteredClass);
|
||||||
|
container.classList.remove(hiddenUnfilteredClass);
|
||||||
|
|
||||||
|
if (!d.hide) container.classList.add(unhiddenClass);
|
||||||
|
else if (window.janitorFilterEnabled) container.classList.add(hiddenFilteredClass);
|
||||||
|
else container.classList.add(hiddenUnfilteredClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
const setHidden = (doc, modId, isHidden) => {
|
||||||
|
var d = loadModData(modId);
|
||||||
|
d.hide = isHidden;
|
||||||
|
saveModData(modId, d);
|
||||||
|
updateHiddenClass(doc, modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addHideButtons = (doc, container, id) => {
|
||||||
|
const hide = htmlToElement(doc, `<a class="${hideButtonClass}">hide</a>`)
|
||||||
|
hide.onclick = () => {setHidden(document, id, true);};
|
||||||
|
container.prepend(hide);
|
||||||
|
|
||||||
|
const show = htmlToElement(doc, `<a class="${showButtonClass}">show</a>`)
|
||||||
|
show.onclick = () => {setHidden(document, id, false);};
|
||||||
|
container.prepend(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
const processContainers = (doc) => {
|
||||||
|
for (var el of doc.querySelectorAll(selectors.ITEM_CONTAINER)){
|
||||||
|
const container = el.parentElement.parentElement;
|
||||||
|
const id = el.id;
|
||||||
|
updateHiddenClass(doc, id);
|
||||||
|
|
||||||
|
if (container.janitorButtonsAdded) continue;
|
||||||
|
container.janitorButtonsAdded = true;
|
||||||
|
addHideButtons(doc, container, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadNextPage = (url) => {
|
||||||
|
fetch(url, { credentials: "same-origin" })
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const newDoc = parser.parseFromString(html, "text/html");
|
||||||
|
processContainers(newDoc);
|
||||||
|
|
||||||
|
const newMods = newDoc.querySelectorAll(selectors.ITEM_CONTAINER);
|
||||||
|
const modContainer = document.querySelector(selectors.ITEMS_CONTAINER);
|
||||||
|
for (const mod of newMods) {
|
||||||
|
const container = mod.parentElement.parentElement;
|
||||||
|
modContainer.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scripts = newDoc.querySelectorAll(selectors.ITEMS_HOVERS);
|
||||||
|
for (const newScript of scripts){
|
||||||
|
const matches = newScript.innerHTML.match(/(sharedfile_\d+)/);
|
||||||
|
if (matches.length < 1) continue;
|
||||||
|
const data = loadModData(matches[0]);
|
||||||
|
if (data.hide) continue;
|
||||||
|
eval("try{ "+ newScript.innerHTML + " } catch {} ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextUrl = newDoc.querySelector(selectors.NEXT_BUTTON)?.getAttribute("href");
|
||||||
|
const footer = document.getElementById("footer");
|
||||||
|
if (nextUrl) onVisible(footer, loadNextPage.bind(null, nextUrl));
|
||||||
|
window.history.pushState("", "", url);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggleFilter(checkbox) {
|
||||||
|
window.janitorFilterEnabled = checkbox.checked;
|
||||||
|
GM_setValue("janitorFilterEnabled", checkbox.checked);
|
||||||
|
const doc = checkbox.ownerDocument;
|
||||||
|
processContainers(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const load = () => {
|
||||||
|
|
||||||
|
console.log(GM_listValues());
|
||||||
|
|
||||||
|
addGlobalStyle(document, janitorCss);
|
||||||
|
|
||||||
|
const filterToggleRoot = document.querySelector(selectors.PAGING_INFO);
|
||||||
|
const toggle = htmlToElement(document, `
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="janitorFilterToggle">
|
||||||
|
<label for="janitorFilterToggle">Filter</label>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
const checkbox = toggle.children[0];
|
||||||
|
checkbox.checked = window.janitorFilterEnabled = GM_getValue("janitorFilterEnabled", true);
|
||||||
|
checkbox.onclick = () => toggleFilter(checkbox);
|
||||||
|
filterToggleRoot.prepend(toggle);
|
||||||
|
processContainers(document);
|
||||||
|
|
||||||
|
const nextUrl = document.querySelector(selectors.NEXT_BUTTON)?.getAttribute("href");
|
||||||
|
if (!nextUrl) {
|
||||||
|
console.error(`Could not find nextUrl through "${selectors.NEXT_BUTTON}"`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadNextPage(nextUrl);
|
||||||
|
document.querySelector(selectors.PAGINATOR)?.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
})();
|
||||||
@ -1,156 +0,0 @@
|
|||||||
// ==UserScript==
|
|
||||||
// @name Steam Workshop Janitor
|
|
||||||
// @description Hide unwanted mods in the browse view, endless scrolling
|
|
||||||
// @match *://*.steamcommunity.com/workshop/browse/*
|
|
||||||
// @run-at document-end
|
|
||||||
// @version 0.0.1
|
|
||||||
// @grant GM_setValue
|
|
||||||
// @grant GM_getValue
|
|
||||||
// @updateURL https://raw.githubusercontent.com/Jetsparrow/steam-workshop-janitor/main/steam-workshop-janitor.js
|
|
||||||
// @downloadURL https://raw.githubusercontent.com/Jetsparrow/steam-workshop-janitor/main/steam-workshop-janitor.js
|
|
||||||
// ==/UserScript==
|
|
||||||
|
|
||||||
const selectors = {
|
|
||||||
ITEM_CONTAINER: ".workshopBrowseItems .workshopItemPreviewHolder",
|
|
||||||
ITEMS_CONTAINER: ".workshopBrowseItems",
|
|
||||||
ITEMS_HOVERS: ".workshopBrowseItems script",
|
|
||||||
NEXT_BUTTON: ".workshopBrowsePagingControls .pagebtn:last-child",
|
|
||||||
PAGINATOR: ".workshopBrowsePagingControls"
|
|
||||||
};
|
|
||||||
|
|
||||||
function htmlToElement(doc, html) {
|
|
||||||
var template = doc.createElement('template');
|
|
||||||
html = html.trim(); // Never return a text node of whitespace as the result
|
|
||||||
template.innerHTML = html;
|
|
||||||
return template.content.firstChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
const on_visible = (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);
|
|
||||||
};
|
|
||||||
|
|
||||||
function defaultModData()
|
|
||||||
{
|
|
||||||
var res = new Object();
|
|
||||||
res.hide = false;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
function loadModData(id){
|
|
||||||
var j = GM_getValue("modid:" + id, "");
|
|
||||||
return j == "" ? defaultModData() : JSON.parse(j);
|
|
||||||
}
|
|
||||||
function saveModData(id, data){
|
|
||||||
GM_setValue("modid:" + id, JSON.stringify(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hideId(id){
|
|
||||||
var d = loadModData(id);
|
|
||||||
d.hide = true;
|
|
||||||
saveModData(id, d);
|
|
||||||
var elem = document.getElementById(id);
|
|
||||||
if (elem){
|
|
||||||
elem.parentElement.parentElement.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sweepHidden(doc){
|
|
||||||
var containers = doc.querySelectorAll(selectors.ITEM_CONTAINER);
|
|
||||||
for (var container of containers)
|
|
||||||
{
|
|
||||||
var id = container.id;
|
|
||||||
var data = loadModData(id);
|
|
||||||
if (data.hide){
|
|
||||||
container.parentElement.parentElement.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addButtonToElement(doc, elem){
|
|
||||||
var janitorButton = htmlToElement(doc, `<a>X</a>`)
|
|
||||||
let id = elem.id;
|
|
||||||
janitorButton.onclick = function(){hideId(id);};
|
|
||||||
elem.parentElement.parentElement.prepend(janitorButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addButtons(doc){
|
|
||||||
var containers = doc.querySelectorAll(selectors.ITEM_CONTAINER);
|
|
||||||
for (var container of containers){
|
|
||||||
addButtonToElement(doc, container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const load_next = (url) => {
|
|
||||||
fetch(url, { credentials: "same-origin" })
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(html => {
|
|
||||||
const parser = new DOMParser();
|
|
||||||
const newDoc = parser.parseFromString(html, "text/html");
|
|
||||||
|
|
||||||
sweepHidden(newDoc);
|
|
||||||
addButtons(newDoc);
|
|
||||||
|
|
||||||
const newPaginator = newDoc.querySelector(selectors.PAGINATOR);
|
|
||||||
const oldPaginator = document.querySelector(selectors.PAGINATOR);
|
|
||||||
const pagParent = oldPaginator.parentElement;
|
|
||||||
oldPaginator.remove();
|
|
||||||
pagParent.append(newPaginator);
|
|
||||||
|
|
||||||
const newMods = newDoc.querySelectorAll(selectors.ITEM_CONTAINER);
|
|
||||||
const lastMod = newMods[newMods.length - 1];
|
|
||||||
|
|
||||||
const modContainer = document.querySelector(selectors.ITEMS_CONTAINER);
|
|
||||||
for (var mod of newMods) {
|
|
||||||
var container = mod.parentElement.parentElement;
|
|
||||||
modContainer.appendChild(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
const scripts = newDoc.querySelectorAll(selectors.ITEMS_HOVERS);
|
|
||||||
for (var newScript of scripts){
|
|
||||||
var matches = newScript.innerHTML.match(/(sharedfile_\d+)/);
|
|
||||||
if (matches.length < 1) continue;
|
|
||||||
var data = loadModData(matches[0]);
|
|
||||||
if (data.hide) continue;
|
|
||||||
eval("try{ "+ newScript.innerHTML + " } catch {} ");
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextButton = document.querySelector(selectors.NEXT_BUTTON);
|
|
||||||
|
|
||||||
if (lastMod && nextButton) {
|
|
||||||
const nextUrl = nextButton.getAttribute("href");
|
|
||||||
on_visible(lastMod, load_next.bind(null, nextUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
window.history.pushState("", "", url);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
(() => {
|
|
||||||
const load = () => {
|
|
||||||
const next_button = document.querySelector(selectors.NEXT_BUTTON);
|
|
||||||
|
|
||||||
if (!next_button) {
|
|
||||||
console.error(`Could not find "${selectors.NEXT_BUTTON}"`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sweepHidden(document);
|
|
||||||
addButtons(document);
|
|
||||||
|
|
||||||
const nextUrl = next_button.getAttribute("href");
|
|
||||||
load_next(nextUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
load();
|
|
||||||
})();
|
|
||||||
Loading…
Reference in New Issue
Block a user