Adds a input to quickly filter cards by name. Separate by commas to see multiple cards. Click `/` to quickly focus on input
// ==UserScript==
// @name Filter 17Lands data
// @namespace Violentmonkey Scripts
// @match https://www.17lands.com/*
// @grant none
// @version 1.4.0
// @author rsromanowski
// @license MIT
// @description Adds a input to quickly filter cards by name. Separate by commas to see multiple cards. Click `/` to quickly focus on input
// @require https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2
// @require https://cdn.jsdelivr.net/npm/@violentmonkey/shortcut@1
// ==/UserScript==
// URL
// https://www.17lands.com/card_data
// ?expansion=SOS
// &format=PremierDraft
// &user_group=top
// &color=w~u~g~multicolor~colorless
// &deck_color=G
// &rarity=common~uncommon
// &start=2026-04-21
// &end=2026-04-28
// Dropwdonws:
// color,rarity - button no id
// dates, no id
//
// Filters:
// container > container .card-performance-checkbox
// inputs hidden and readonly. ui checkbox, checked class toggled
function init() {
switch (window.location.pathname) {
case "/card_data":
initCardData();
break;
case "/card_data/details":
initCardDetails();
break;
default:
console.log(`Nothing to do on page: ${window.location.pathname}`);
break;
}
}
function initCardData() {
function findComponents() {
const expansion = document.querySelector("select#expansion");
const format = document.querySelector("select#format");
const users = document.querySelector("select#user-group");
const deckColors = document.querySelector("select#deck_color");
console.log(
`Dropdowns: expansion=${expansion}, format=${format}, users=${users}, deckColors=${deckColors}`,
);
}
findComponents();
function prettify() {
const toolbar =
document.querySelector("div > div > select").parentElement.parentElement;
toolbar.style.cssText += " justify-content: space-between";
const divs = document.querySelectorAll("#app > div > div");
divs.forEach((d) => {
d.classList.add("container");
});
}
function filterTable(filter) {
const columns = [
{ name: "Name", index: 0, isFilter: true },
{ name: "Color", index: 1, isFilter: false },
{ name: "Rarity", index: 2, isFilter: false },
];
const filterColumns = columns.filter((c) => c.isFilter).map((c) => c.index);
const rarityRegex = /r[:=]([curm])/i;
const colorRegex = /c[:=]([wubrgcm])/i;
// Currently only one table on page
// Headers are in thead
const rows = document.querySelectorAll(`table > tbody > tr`);
const clauses = filter.split(",");
// const rarityClauses = clauses.filter((c) => rarityRegex.test(c));
// const colorClauses = clauses.filter((c) => colorRegex.test(c));
const nameClauses = clauses.filter(
(c) => !rarityRegex.test(c) && !colorRegex.test(c),
);
const orFilter = nameClauses
.filter((f) => f.trim().length > 0)
.map((f) => f.trim())
.join("|");
const regex = new RegExp(`${orFilter}`, "i");
const isFoundInTds = (td) => regex.test(td.innerHTML);
const isFound = (childrenArr) => childrenArr.some(isFoundInTds);
const setTrStyleDisplay = ({ style, children }) => {
style.display = isFound([
...filterColumns.map((c) => children[c]), // <-- filter Columns
])
? ""
: "none";
};
rows.forEach(setTrStyleDisplay);
}
function createQueryInput() {
const div = document.querySelector(".container:first-of-type");
const i = document.createElement("input");
i.id = "q";
i.className = "form-control";
i.placeholder =
'Press \'/\' to focus. Search by card names. i.e. "Tome Blast" or "tome,scatter"';
div.appendChild(i);
i.addEventListener("input", (event) => {
filterTable(event.target.value);
});
// Select all text of focus for quick overwrite
i.addEventListener("focus", function () {
this.select();
});
}
VM.observe(document.body, () => {
const table = document.querySelector("table");
if (table) {
createQueryInput();
prettify();
return true; // disconnect observer
}
});
VM.shortcut.register("/", () => {
document.getElementById("q").focus();
});
}
// Doesn't work since dropdown changed
function initCardDetails() {
VM.shortcut.register("k", () => {
const dropdown = document.getElementById("card");
dropdown.selectedIndex = Math.max(0, dropdown.selectedIndex - 1);
dropdown.dispatchEvent(new window.Event("change", { bubbles: true }));
});
VM.shortcut.register("j", () => {
const dropdown = document.getElementById("card");
dropdown.selectedIndex = Math.min(
dropdown.childElementCount - 1,
dropdown.selectedIndex + 1,
);
dropdown.dispatchEvent(new window.Event("change", { bubbles: true }));
});
}
init();