Filter OCs by level on crimes tab
// ==UserScript==
// @name OC Level Filter
// @namespace BBSmalls
// @version 1.0.0
// @description Filter OCs by level on crimes tab
// @author BBSmalls [3908857]
// @match https://www.torn.com/factions.php*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const STORAGE_KEY = 'oc_level_range';
function loadRange() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
return raw ? JSON.parse(raw) : { min: 1, max: 10 };
} catch (_) { return { min: 1, max: 10 }; }
}
function saveRange(min, max) {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ min, max }));
}
function applyFilter() {
const range = loadRange();
const cards = document.querySelectorAll('[data-oc-id]');
const min = parseInt(range.min);
const max = parseInt(range.max);
let count = 0;
cards.forEach(card => {
const el = card.querySelector('[class^="levelValue___"]');
const level = el ? parseInt(el.textContent.match(/\d+/)?.[0], 10) : null;
const show = level === null || (level >= min && level <= max);
card.style.display = show ? '' : 'none';
if (show) count++;
});
const counter = document.getElementById('oc-filter-counter');
if (counter) counter.textContent = `Showing: ${count}`;
}
function buildFilterBar() {
const range = loadRange();
const bar = document.createElement('div');
bar.id = 'oc-level-filter-bar';
bar.style.cssText = `display: flex; align-items: center; justify-content: center; gap: 20px; padding: 10px; margin-bottom: 10px; background: rgba(0,0,0,0.15); border-radius: 4px; color: #ccc; font-size: 12px;`;
const dropdownWrapper = document.createElement('div');
dropdownWrapper.style.cssText = 'display: flex; align-items: center; gap: 15px;';
const createSelect = (labelText, val, isMax) => {
const wrapper = document.createElement('div');
wrapper.style.cssText = 'display: flex; align-items: center; gap: 5px;';
wrapper.innerHTML = `<span>${labelText}:</span>`;
const select = document.createElement('select');
select.style.cssText = 'padding: 2px; border-radius: 3px; background: #222; color: #fff; border: 1px solid #555;';
for (let i = 1; i <= 10; i++) {
const opt = document.createElement('option');
opt.value = i; opt.textContent = i;
if (i == val) opt.selected = true;
select.appendChild(opt);
}
select.addEventListener('change', () => {
const current = loadRange();
const newRange = isMax ? { min: current.min, max: select.value } : { min: select.value, max: current.max };
saveRange(newRange.min, newRange.max);
applyFilter();
});
wrapper.appendChild(select);
return wrapper;
};
dropdownWrapper.appendChild(createSelect('Min Level', range.min, false));
dropdownWrapper.appendChild(createSelect('Max Level', range.max, true));
bar.appendChild(dropdownWrapper);
const counter = document.createElement('span');
counter.id = 'oc-filter-counter';
bar.appendChild(counter);
return bar;
}
setInterval(() => {
// Only run if we are on the crimes tab
if (!window.location.hash.includes('tab=crimes')) {
const existingBar = document.getElementById('oc-level-filter-bar');
if (existingBar) existingBar.remove();
return;
}
// Inject if missing
const container = document.querySelector('[class^="buttonsContainer___"]');
const targetHr = container?.nextElementSibling;
if (targetHr && targetHr.tagName === 'HR' && !document.getElementById('oc-level-filter-bar')) {
targetHr.after(buildFilterBar());
applyFilter();
}
// Re-filter if cards were reset
const cards = document.querySelectorAll('[data-oc-id]');
if (cards.length > 0 && document.getElementById('oc-level-filter-bar')) {
const needsFilter = Array.from(cards).some(c => c.style.display === '');
if (needsFilter) applyFilter();
}
}, 1000);
})();