Greasy Fork is available in English.
Filter the (advanced) search userlist.
// ==UserScript==
// @name TORN: TornTools - Userlist Filter
// @namespace torntools.userlist-filter
// @version 1.0.0
// @author DeKleineKobini [2114440] and the TornTools team
// @description Filter the (advanced) search userlist.
// @license GPL-3.0-or-later
// @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @supportURL https://github.com/Mephiles/torntools_extension/issues
// @match https://*.torn.com/page.php?sid=UserList*
// @grant GM.getValue
// @grant GM.info
// @grant GM.setValue
// @grant GM_addStyle
// @grant unsafeWindow
// @run-at document-end
// @contributionURL https://buymeacoffee.com/dekleinekobini
// ==/UserScript==
(function() {
"use strict";
var s = new Set();
var _css = async (t) => {
if (s.has(t)) return;
s.add(t);
((c) => {
if (typeof GM_addStyle === "function") GM_addStyle(c);
else (document.head || document.documentElement).appendChild(document.createElement("style")).append(c);
})(t);
};
var FEATURE_MANAGER;
var ttStorage;
var SCRIPT_INJECTOR;
var RUNTIME_INFORMATION;
var RUNTIME_STORAGE;
function setFeatureManager(featureManager) {
FEATURE_MANAGER = featureManager;
}
function setTTStorage(storage) {
ttStorage = storage;
}
function setScriptInjector(scriptInjector) {
SCRIPT_INJECTOR = scriptInjector;
}
function setRuntimeInformation(runtimeInformation) {
RUNTIME_INFORMATION = runtimeInformation;
}
function setRuntimeStorage(runtimeStorage) {
RUNTIME_STORAGE = runtimeStorage;
}
_css(".tt-loading-placeholder{content:var(--default-preloader-url,url(https://www.torn.com/images/v2/main/ajax-loader.gif));margin:0 auto;padding:10px;display:none}.tt-loading-placeholder.active{display:block}");
function requireCondition(condition, partialOptions = {}) {
const options = {
delay: 50,
maxCycles: 100,
...partialOptions
};
const error = new Error("Maximum cycles reached.");
return new Promise((resolve, reject) => {
if (checkCondition()) return;
let counter = 0;
const checker = setInterval(() => {
if (checkCounter(counter++) || checkCondition()) return clearInterval(checker);
}, options.delay);
function checkCondition() {
const response = condition();
if (!response) return false;
if (typeof response === "boolean") if (response) resolve(true);
else reject();
else if (typeof response === "object") if (Object.hasOwn(response, "success")) if (response.success === true) resolve(response.value);
else reject(response.value);
else resolve(response);
return true;
}
function checkCounter(count) {
if (options.maxCycles <= 0) return false;
if (count > options.maxCycles) {
reject(error);
return true;
}
return false;
}
});
}
function requireElement(selector, attributes = {}) {
const options = {
invert: false,
parent: document,
...attributes
};
if (attributes.invert) return requireCondition(() => !options.parent.querySelector(selector), attributes);
else return requireCondition(() => options.parent.querySelector(selector), attributes);
}
(() => {
if (typeof window === "undefined" || window.location.href.endsWith("/_generated_background_page.html")) return "BACKGROUND";
else if (typeof browser === "object" && browser.action) return "POPUP";
else return "CONTENT";
})();
function sleep(millis) {
return new Promise((resolve) => setTimeout(resolve, millis));
}
function isIntNumber(number) {
if (number === null) return false;
if (number.match(/[a-zA-Z]/)) return false;
const _number = parseFloat(number.toString());
return !Number.isNaN(_number) && Number.isFinite(_number) && _number % 1 === 0;
}
function getUUID() {
return `_${Math.random().toString(36).substr(2, 9)}`;
}
function toNumericVersion(version) {
return parseInt(version.split(".").map((part) => part.padStart(3, "0")).join("").padEnd(9, "9"));
}
function isTabFocused() {
return document.hasFocus();
}
function svgImport(svgImport) {
if (typeof svgImport !== "string") return (attributes = {}) => createFallbackElement(attributes);
if (svgImport.startsWith("data:image/svg+xml")) {
const encodedData = svgImport.substring(19);
let svgContent;
try {
svgContent = decodeURIComponent(encodedData);
} catch (error) {
console.error("Failed to decode SVG data URL", error);
return (attributes = {}) => createFallbackElement(attributes);
}
return (attributes = {}) => createSvgElement(svgContent, attributes);
}
return (attributes = {}) => createSvgElement(svgImport, attributes);
}
function createFallbackElement(attributes) {
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", "24");
svg.setAttribute("height", "24");
svg.setAttribute("viewBox", "0 0 24 24");
Object.entries(attributes).filter(([, value]) => value !== false && value !== null && value !== void 0).map(([key, value]) => svg.setAttribute(key, String(value)));
const rect = document.createElementNS(svgNS, "rect");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("width", "24");
rect.setAttribute("height", "24");
rect.setAttribute("fill", "red");
svg.appendChild(rect);
return svg;
}
function createSvgElement(svgContent, attributes = {}) {
const fullAttributes = {
width: "size" in attributes ? attributes.size : "1em",
height: "size" in attributes ? attributes.size : "1em",
...attributes
};
const svg = elementBuilder({
type: "template",
html: svgContent.trim()
}).content.firstChild;
if (!isSVGElement(svg)) return createFallbackElement(fullAttributes);
Object.entries(fullAttributes).filter(([, value]) => value !== false && value !== null && value !== void 0).forEach(([key, value]) => svg.setAttribute(key, String(value)));
return svg;
}
var arrow_bend_up_left_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M236,200a12,12,0,0,1-24,0,84.09,84.09,0,0,0-84-84H61l27.52,27.51a12,12,0,0,1-17,17l-48-48a12,12,0,0,1,0-17l48-48a12,12,0,0,1,17,17L61,92h67A108.12,108.12,0,0,1,236,200Z'/%3e%3c/svg%3e";
var arrow_clockwise_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M244,56v48a12,12,0,0,1-12,12H184a12,12,0,1,1,0-24H201.1l-19-17.38c-.13-.12-.26-.24-.38-.37A76,76,0,1,0,127,204h1a75.53,75.53,0,0,0,52.15-20.72,12,12,0,0,1,16.49,17.45A99.45,99.45,0,0,1,128,228h-1.37A100,100,0,1,1,198.51,57.06L220,76.72V56a12,12,0,0,1,24,0Z'/%3e%3c/svg%3e";
var arrow_down_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M208.49,152.49l-72,72a12,12,0,0,1-17,0l-72-72a12,12,0,0,1,17-17L116,187V40a12,12,0,0,1,24,0V187l51.51-51.52a12,12,0,0,1,17,17Z'/%3e%3c/svg%3e";
var arrow_up_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M208.49,120.49a12,12,0,0,1-17,0L140,69V216a12,12,0,0,1-24,0V69L64.49,120.49a12,12,0,0,1-17-17l72-72a12,12,0,0,1,17,0l72,72A12,12,0,0,1,208.49,120.49Z'/%3e%3c/svg%3e";
var check_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z'/%3e%3c/svg%3e";
var check_circle_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M176.49,95.51a12,12,0,0,1,0,17l-56,56a12,12,0,0,1-17,0l-24-24a12,12,0,1,1,17-17L112,143l47.51-47.52A12,12,0,0,1,176.49,95.51ZM236,128A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Z'/%3e%3c/svg%3e";
var copy_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z'/%3e%3c/svg%3e";
var info_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z'/%3e%3c/svg%3e";
var spinner_gap_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M140,32V64a12,12,0,0,1-24,0V32a12,12,0,0,1,24,0Zm84,84H192a12,12,0,0,0,0,24h32a12,12,0,0,0,0-24Zm-42.26,48.77a12,12,0,1,0-17,17l22.63,22.63a12,12,0,0,0,17-17ZM128,180a12,12,0,0,0-12,12v32a12,12,0,0,0,24,0V192A12,12,0,0,0,128,180ZM74.26,164.77,51.63,187.4a12,12,0,0,0,17,17l22.63-22.63a12,12,0,1,0-17-17ZM76,128a12,12,0,0,0-12-12H32a12,12,0,0,0,0,24H64A12,12,0,0,0,76,128ZM68.6,51.63a12,12,0,1,0-17,17L74.26,91.23a12,12,0,0,0,17-17Z'/%3e%3c/svg%3e";
var warning_circle_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z'/%3e%3c/svg%3e";
var x_circle_bold_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M168.49,104.49,145,128l23.52,23.51a12,12,0,0,1-17,17L128,145l-23.51,23.52a12,12,0,0,1-17-17L111,128,87.51,104.49a12,12,0,0,1,17-17L128,111l23.51-23.52a12,12,0,0,1,17,17ZM236,128A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Z'/%3e%3c/svg%3e";
var airplane_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M240,136v32a8,8,0,0,1-8,8,7.61,7.61,0,0,1-1.57-.16L156,161v23.73l17.66,17.65A8,8,0,0,1,176,208v24a8,8,0,0,1-11,7.43l-37-14.81L91,239.43A8,8,0,0,1,80,232V208a8,8,0,0,1,2.34-5.66L100,184.69V161L25.57,175.84A7.61,7.61,0,0,1,24,176a8,8,0,0,1-8-8V136a8,8,0,0,1,4.42-7.16L100,89.06V44a28,28,0,0,1,56,0V89.06l79.58,39.78A8,8,0,0,1,240,136Z'/%3e%3c/svg%3e";
var arrows_out_cardinal_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M96,136H64v24a8,8,0,0,1-13.66,5.66l-32-32a8,8,0,0,1,0-11.32l32-32A8,8,0,0,1,64,96v24H96a8,8,0,0,1,0,16Zm0-72h24V96a8,8,0,0,0,16,0V64h24a8,8,0,0,0,5.66-13.66l-32-32a8,8,0,0,0-11.32,0l-32,32A8,8,0,0,0,96,64Zm141.66,58.34-32-32A8,8,0,0,0,192,96v24H160a8,8,0,0,0,0,16h32v24a8,8,0,0,0,13.66,5.66l32-32A8,8,0,0,0,237.66,122.34ZM160,192H136V160a8,8,0,0,0-16,0v32H96a8,8,0,0,0-5.66,13.66l32,32a8,8,0,0,0,11.32,0l32-32A8,8,0,0,0,160,192Z'/%3e%3c/svg%3e";
var bell_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M221.8,175.94C216.25,166.38,208,139.33,208,104a80,80,0,1,0-160,0c0,35.34-8.26,62.38-13.81,71.94A16,16,0,0,0,48,200H88.81a40,40,0,0,0,78.38,0H208a16,16,0,0,0,13.8-24.06ZM128,216a24,24,0,0,1-22.62-16h45.24A24,24,0,0,1,128,216Z'/%3e%3c/svg%3e";
var bell_slash_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M221.84,192v0a1.85,1.85,0,0,1-3,.28L83.27,43.19a4,4,0,0,1,.8-6A79.55,79.55,0,0,1,129.17,24C173,24.66,207.8,61.1,208,104.92c.14,34.88,8.31,61.54,13.82,71A15.89,15.89,0,0,1,221.84,192Zm-7.92,18.62a8,8,0,0,1-11.85,10.76L182.62,200H167.16a40,40,0,0,1-78.41,0H47.91a15.78,15.78,0,0,1-13.59-7.59,16.42,16.42,0,0,1-.09-16.68c5.55-9.73,13.7-36.64,13.7-71.73A79.42,79.42,0,0,1,58.79,63.85L42,45.38A8,8,0,1,1,53.84,34.62ZM150.59,200H105.32a24,24,0,0,0,45.27,0Z'/%3e%3c/svg%3e";
var caret_down_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z'/%3e%3c/svg%3e";
var caret_right_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M181.66,133.66l-80,80A8,8,0,0,1,88,208V48a8,8,0,0,1,13.66-5.66l80,80A8,8,0,0,1,181.66,133.66Z'/%3e%3c/svg%3e";
var caret_up_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M215.39,163.06A8,8,0,0,1,208,168H48a8,8,0,0,1-5.66-13.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,215.39,163.06Z'/%3e%3c/svg%3e";
var funnel_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M227.81,66.76l-.08.09L160,139.17v55.49A16,16,0,0,1,152.87,208l-32,21.34A16,16,0,0,1,96,216V139.17L28.27,66.85l-.08-.09A16,16,0,0,1,40,40H216a16,16,0,0,1,11.84,26.76Z'/%3e%3c/svg%3e";
var funnel_x_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M227.73,66.85,160,139.17v55.49A16,16,0,0,1,152.87,208l-32,21.34A16,16,0,0,1,96,216V139.17L28.27,66.85l-.08-.09A16,16,0,0,1,40,40H216a16,16,0,0,1,11.84,26.76ZM227.31,192l18.35-18.34a8,8,0,0,0-11.32-11.32L216,180.69l-18.34-18.35a8,8,0,0,0-11.32,11.32L204.69,192l-18.35,18.34a8,8,0,0,0,11.32,11.32L216,203.31l18.34,18.35a8,8,0,0,0,11.32-11.32Z'/%3e%3c/svg%3e";
var gear_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z'/%3e%3c/svg%3e";
var info_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z'/%3e%3c/svg%3e";
var plus_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM184,136H136v48a8,8,0,0,1-16,0V136H72a8,8,0,0,1,0-16h48V72a8,8,0,0,1,16,0v48h48a8,8,0,0,1,0,16Z'/%3e%3c/svg%3e";
var stethoscope_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M240,160a32,32,0,1,0-39.93,31,8,8,0,0,0-.07,1,32,32,0,0,1-32,32H144a32,32,0,0,1-32-32V151.48c31.47-4,56-31.47,56-64.31V40a8,8,0,0,0-8-8H136a8,8,0,0,0,0,16h16V87.17c0,26.58-21.25,48.49-47.36,48.83A48,48,0,0,1,56,88V48H72a8,8,0,0,0,0-16H48a8,8,0,0,0-8,8V88a64,64,0,0,0,56,63.49V192a48.05,48.05,0,0,0,48,48h24a48.05,48.05,0,0,0,48-48,8,8,0,0,0-.07-1A32,32,0,0,0,240,160Zm-32,8a8,8,0,1,1,8-8A8,8,0,0,1,208,168Z'/%3e%3c/svg%3e";
var table_fill_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM40,112H80v32H40Zm56,0H216v32H96ZM40,160H80v32H40Zm176,32H96V160H216v32Z'/%3e%3c/svg%3e";
var caret_down_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z'/%3e%3c/svg%3e";
var eye_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M247.31,124.76c-.35-.79-8.82-19.58-27.65-38.41C194.57,61.26,162.88,48,128,48S61.43,61.26,36.34,86.35C17.51,105.18,9,124,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208s66.57-13.26,91.66-38.34c18.83-18.83,27.3-37.61,27.65-38.4A8,8,0,0,0,247.31,124.76ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.47,133.47,0,0,1,25,128,133.33,133.33,0,0,1,48.07,97.25C70.33,75.19,97.22,64,128,64s57.67,11.19,79.93,33.25A133.46,133.46,0,0,1,231.05,128C223.84,141.46,192.43,192,128,192Zm0-112a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Z'/%3e%3c/svg%3e";
var eye_slash_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M53.92,34.62A8,8,0,1,0,42.08,45.38L61.32,66.55C25,88.84,9.38,123.2,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208a127.11,127.11,0,0,0,52.07-10.83l22,24.21a8,8,0,1,0,11.84-10.76Zm47.33,75.84,41.67,45.85a32,32,0,0,1-41.67-45.85ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.16,133.16,0,0,1,25,128c4.69-8.79,19.66-33.39,47.35-49.38l18,19.75a48,48,0,0,0,63.66,70l14.73,16.2A112,112,0,0,1,128,192Zm6-95.43a8,8,0,0,1,3-15.72,48.16,48.16,0,0,1,38.77,42.64,8,8,0,0,1-7.22,8.71,6.39,6.39,0,0,1-.75,0,8,8,0,0,1-8-7.26A32.09,32.09,0,0,0,134,96.57Zm113.28,34.69c-.42.94-10.55,23.37-33.36,43.8a8,8,0,1,1-10.67-11.92A132.77,132.77,0,0,0,231.05,128a133.15,133.15,0,0,0-23.12-30.77C185.67,75.19,158.78,64,128,64a118.37,118.37,0,0,0-19.36,1.57A8,8,0,1,1,106,49.79,134,134,0,0,1,128,48c34.88,0,66.57,13.26,91.66,38.35,18.83,18.83,27.3,37.62,27.65,38.41A8,8,0,0,1,247.31,131.26Z'/%3e%3c/svg%3e";
var plus_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z'/%3e%3c/svg%3e";
var question_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'/%3e%3c/svg%3e";
var trash_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z'/%3e%3c/svg%3e";
var x_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z'/%3e%3c/svg%3e";
var x_circle_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='currentColor'%3e%3cpath%20d='M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'/%3e%3c/svg%3e";
svgImport(caret_down_default);
svgImport(eye_default);
svgImport(eye_slash_default);
svgImport(plus_default);
svgImport(trash_default);
svgImport(question_default);
svgImport(x_default);
svgImport(x_circle_default);
svgImport(arrow_bend_up_left_bold_default);
svgImport(arrow_clockwise_bold_default);
svgImport(arrow_down_bold_default);
svgImport(arrow_up_bold_default);
svgImport(check_bold_default);
svgImport(check_circle_bold_default);
svgImport(copy_bold_default);
svgImport(info_bold_default);
svgImport(warning_circle_bold_default);
svgImport(x_circle_bold_default);
svgImport(spinner_gap_bold_default);
svgImport(arrows_out_cardinal_fill_default);
svgImport(airplane_fill_default);
svgImport(bell_fill_default);
svgImport(bell_slash_fill_default);
var PHFillCaretDown = svgImport(caret_down_fill_default);
svgImport(caret_right_fill_default);
svgImport(caret_up_fill_default);
svgImport(info_fill_default);
var PHFillFunnel = svgImport(funnel_fill_default);
var PHFillFunnelX = svgImport(funnel_x_fill_default);
svgImport(gear_fill_default);
svgImport(plus_fill_default);
svgImport(stethoscope_fill_default);
svgImport(table_fill_default);
function elementBuilder(options) {
if (typeof options === "string") return document.createElement(options);
else if (typeof options === "object") {
options = {
type: "div",
id: void 0,
class: void 0,
text: void 0,
html: void 0,
value: void 0,
href: void 0,
children: [],
attributes: {},
events: {},
style: {},
dataset: {},
...options
};
const newElement = document.createElement(options.type);
if (options.id) newElement.id = options.id;
if (options.class) newElement.className = Array.isArray(options.class) ? options.class.filter((name) => !!name).join(" ") : options.class.trim();
if (options.text !== void 0) newElement.textContent = options.text.toString();
if (options.html) newElement.innerHTML = options.html;
if (options.value && "value" in newElement) if (typeof options.value === "function") newElement.value = options.value();
else newElement.value = options.value;
if (options.href && "href" in newElement) newElement.href = options.href;
for (const child of options.children.filter((child) => !!child) || []) if (typeof child === "string") newElement.appendChild(document.createTextNode(child));
else newElement.appendChild(child);
if (options.attributes) {
let attributes = options.attributes;
if (typeof attributes === "function") attributes = attributes();
for (const attribute in attributes) newElement.setAttribute(attribute, attributes[attribute].toString());
}
for (const event in options.events) newElement.addEventListener(event, options.events[event]);
for (const key in options.style) newElement.style[key] = options.style[key];
for (const key in options.dataset) if (typeof options.dataset[key] === "object") newElement.dataset[key] = JSON.stringify(options.dataset[key]);
else newElement.dataset[key] = options.dataset[key].toString();
return newElement;
} else throw new Error("Invalid options provided to newElement.");
}
function findAllElements(selector, parent = document) {
return Array.from(parent.querySelectorAll(selector));
}
function findParent(element, options = {}) {
options = {
tag: void 0,
class: void 0,
partialClass: void 0,
id: void 0,
hasAttribute: void 0,
maxAttempts: -1,
currentAttempt: 1,
...options
};
if (!element?.parentElement) return void 0;
if (options.maxAttempts !== -1 && options.currentAttempt > options.maxAttempts) return void 0;
if (options.tag && element.parentElement.tagName === options.tag) return element.parentElement;
if (options.id && element.parentElement.id === options.id) return element.parentElement;
if (options.class && (Array.isArray(options.class) && options.class.some((c) => element.parentElement.classList.contains(c)) || !Array.isArray(options.class) && element.parentElement.classList.contains(options.class))) return element.parentElement;
if (options.partialClass && Array.from(element.parentElement.classList).some((c) => c.startsWith(options.partialClass))) return element.parentElement;
if (options.hasAttribute && element.parentElement.getAttribute(options.hasAttribute) !== null) return element.parentElement;
return findParent(element.parentElement, {
...options,
currentAttempt: options.currentAttempt + 1
});
}
function isSVGElement(node) {
return node && "nodeType" in node && node.nodeType === Node.ELEMENT_NODE && "ownerSVGElement" in node;
}
var EVENT_CHANNELS = function(EVENT_CHANNELS) {
EVENT_CHANNELS["FETCH"] = "tt-fetch";
EVENT_CHANNELS["XHR"] = "tt-xhr";
EVENT_CHANNELS["CHAT_MESSAGE"] = "chat-message";
EVENT_CHANNELS["CHAT_NEW"] = "chat-box-new";
EVENT_CHANNELS["CHAT_OPENED"] = "chat-box-opened";
EVENT_CHANNELS["CHAT_PEOPLE_MENU_OPENED"] = "chat-people-menu-opened";
EVENT_CHANNELS["CHAT_SETTINGS_MENU_OPENED"] = "chat-settings-menu-opened";
EVENT_CHANNELS["CHAT_REFRESHED"] = "chat-refreshed";
EVENT_CHANNELS["CHAT_RECONNECTED"] = "chat-reconnected";
EVENT_CHANNELS["CHAT_CLOSED"] = "chat-closed";
EVENT_CHANNELS["COMPANY_EMPLOYEES_PAGE"] = "company-employees-page";
EVENT_CHANNELS["COMPANY_STOCK_PAGE"] = "company-stock-page";
EVENT_CHANNELS["FACTION_ARMORY_TAB"] = "faction-armory-tab";
EVENT_CHANNELS["FACTION_CRIMES"] = "faction-crimes";
EVENT_CHANNELS["FACTION_CRIMES2"] = "faction-crimes2";
EVENT_CHANNELS["FACTION_CRIMES2_TAB"] = "faction-crimes2-tab";
EVENT_CHANNELS["FACTION_CRIMES2_REFRESH"] = "faction-crimes2-refresh";
EVENT_CHANNELS["FACTION_GIVE_TO_USER"] = "faction-give-to-user";
EVENT_CHANNELS["FACTION_UPGRADE_INFO"] = "faction-upgrade-info";
EVENT_CHANNELS["FACTION_INFO"] = "faction-info";
EVENT_CHANNELS["FACTION_MAIN"] = "faction-main";
EVENT_CHANNELS["FACTION_NATIVE_FILTER"] = "faction-filter_native";
EVENT_CHANNELS["FACTION_NATIVE_SORT"] = "faction-sort_native";
EVENT_CHANNELS["FACTION_NATIVE_ICON_UPDATE"] = "faction-icon_update_native";
EVENT_CHANNELS["FF_SCOUTER_GAUGE"] = "ff-scouter-gauge";
EVENT_CHANNELS["ITEM_AMOUNT"] = "item-amount";
EVENT_CHANNELS["ITEM_EQUIPPED"] = "item-equipped";
EVENT_CHANNELS["ITEM_ITEMS_LOADED"] = "item-items-loaded";
EVENT_CHANNELS["ITEM_SWITCH_TAB"] = "item-switch-tab";
EVENT_CHANNELS["HOSPITAL_SWITCH_PAGE"] = "hospital-switch-page";
EVENT_CHANNELS["JAIL_SWITCH_PAGE"] = "jail-switch-page";
EVENT_CHANNELS["USERLIST_SWITCH_PAGE"] = "userlist-switch-page";
EVENT_CHANNELS["TRAVEL_SELECT_TYPE"] = "travel-select-type";
EVENT_CHANNELS["TRAVEL_SELECT_COUNTRY"] = "travel-select-country";
EVENT_CHANNELS["TRAVEL_DESTINATION_UPDATE"] = "travel-destination-update";
EVENT_CHANNELS["TRAVEL_ABROAD__SHOP_LOAD"] = "TRAVEL_ABROAD__SHOP_LOAD";
EVENT_CHANNELS["TRAVEL_ABROAD__SHOP_REFRESH"] = "TRAVEL_ABROAD__SHOP_REFRESH";
EVENT_CHANNELS["FEATURE_ENABLED"] = "feature-enabled";
EVENT_CHANNELS["FEATURE_DISABLED"] = "feature-disabled";
EVENT_CHANNELS["STATE_CHANGED"] = "state-changed";
EVENT_CHANNELS["SHOP__LOAD"] = "SHOP__LOAD";
EVENT_CHANNELS["GYM_LOAD"] = "gym-load";
EVENT_CHANNELS["GYM_TRAIN"] = "gym-train";
EVENT_CHANNELS["CRIMES_LOADED"] = "crimes-loaded";
EVENT_CHANNELS["CRIMES_CRIME"] = "crimes-crime";
EVENT_CHANNELS["CRIMES2_HOME_LOADED"] = "crimes2-home-loaded";
EVENT_CHANNELS["CRIMES2_BURGLARY_LOADED"] = "crimes2-burglary-loaded";
EVENT_CHANNELS["CRIMES2_CRIME_LOADED"] = "crimes2-crime-loaded";
EVENT_CHANNELS["MISSION_LOAD"] = "mission-load";
EVENT_CHANNELS["MISSION_REWARDS"] = "mission-rewards";
EVENT_CHANNELS["TRADE"] = "trade";
EVENT_CHANNELS["PROFILE_FETCHED"] = "profile-fetched";
EVENT_CHANNELS["FILTER_APPLIED"] = "filter-applied";
EVENT_CHANNELS["STATS_ESTIMATED"] = "stats-estimated";
EVENT_CHANNELS["SWITCH_PAGE"] = "switch-page";
EVENT_CHANNELS["AUCTION_SWITCH_TYPE"] = "auction-switch-type";
EVENT_CHANNELS["ITEMMARKET_CATEGORY_ITEMS"] = "itemmarket-category-items";
EVENT_CHANNELS["ITEMMARKET_CATEGORY_ITEMS_UPDATE"] = "itemmarket-category-items-update";
EVENT_CHANNELS["ITEMMARKET_ITEMS"] = "itemmarket-items";
EVENT_CHANNELS["ITEMMARKET_ITEMS_UPDATE"] = "itemmarket-items-update";
EVENT_CHANNELS["ITEMMARKET_ITEM_DETAILS"] = "itemmarket-item-details";
EVENT_CHANNELS["WINDOW__FOCUS"] = "WINDOW__FOCUS";
return EVENT_CHANNELS;
}({});
var ANTI_SCRAPE_EVENTS = [
"TRAVEL_ABROAD__SHOP_LOAD",
"chat-message",
"chat-box-opened",
"chat-closed",
"chat-refreshed",
"chat-reconnected",
"itemmarket-category-items",
"itemmarket-category-items-update",
"itemmarket-items",
"itemmarket-items-update"
];
var CUSTOM_LISTENERS = (() => {
const listeners = {};
for (const channel of Object.values(EVENT_CHANNELS)) listeners[channel] = [];
return listeners;
})();
function addXHRListener(callback) {
SCRIPT_INJECTOR.injectXHR();
window.addEventListener("tt-xhr", callback);
}
function triggerCustomListener(channel, payload) {
if (ANTI_SCRAPE_EVENTS.includes(channel) && !isTabFocused()) return;
for (const listener of CUSTOM_LISTENERS[channel]) listener(payload);
}
function setupUserlistPage() {
addXHRListener(async ({ detail: { page, xhr } }) => {
if (page !== "page") return;
if (new URLSearchParams(xhr.requestBody).get("sid") !== "UserListAjax") return;
await requireElement(".user-info-list-wrap");
await requireElement(".user-info-list-wrap .ajax-placeholder", { invert: true });
triggerCustomListener(EVENT_CHANNELS.USERLIST_SWITCH_PAGE);
});
}
var TornToolsCache = class {
_cache;
constructor() {
this._cache = {};
}
set cache(value) {
this._cache = value || {};
}
get cache() {
return this._cache;
}
get(section, key) {
return this.getCacheValue(section, key)?.value;
}
async remove(section, key) {
if (!key) {
key = section;
section = null;
}
if (section && !this.hasValue(section, key) || !section && !this.hasValue(key.toString())) return;
if (section) delete this.cache[section][key];
else delete this.cache[key];
await ttStorage.set({ cache: this.cache });
}
hasValue(section, key) {
return this.getCacheValue(section, key) !== null;
}
getCacheValue(section, key) {
if (!key) {
key = section;
section = null;
}
let value = null;
if (section) {
if (section in this.cache && key in this.cache[section]) value = this.cache[section][key];
} else if (key in this.cache) value = this.cache[key];
if (value === null || !("value" in value)) return null;
if ("indefinite" in value) return value;
else return value.timeout > Date.now() ? value : null;
}
async set(object, ttl, section) {
return this._set(object, ttl, section);
}
setIndefinite(object, section) {
return this._set(object, null, section);
}
async _set(object, ttl, section) {
const timeout = ttl === null ? null : Date.now() + ttl;
if (section) {
if (!(section in this.cache)) this.cache[section] = {};
for (const [key, value] of Object.entries(object)) this.cache[section][key] = this.createCacheValue(value, timeout);
} else for (const [key, value] of Object.entries(object)) this.cache[key] = this.createCacheValue(value, timeout);
await ttStorage.set({ cache: this.cache });
}
createCacheValue(value, timeout) {
if (timeout === null) return {
value,
indefinite: true
};
else return {
value,
timeout
};
}
async clear(section) {
if (section) {
delete this.cache[section];
await ttStorage.set({ cache: this.cache });
} else ttStorage.set({ cache: {} }).then(() => this.cache = {});
}
async refresh() {
let hasChanged = false;
const now = Date.now();
refreshObject(this.cache);
for (const section in this.cache) if (!Object.keys(this.cache[section]).length) delete this.cache[section];
if (hasChanged) await ttStorage.set({ cache: this.cache });
function refreshObject(object) {
for (const key in object) {
const value = object[key];
if ("value" in value) {
const cacheValue = value;
if ("indefinite" in cacheValue || cacheValue.timeout > now) continue;
hasChanged = true;
delete object[key];
} else refreshObject(value);
}
}
}
};
var ttCache = new TornToolsCache();
var DefaultSetting = class {
type;
defaultValue;
constructor(type, defaultValue) {
this.type = type;
this.defaultValue = defaultValue;
}
};
var DEFAULT_STORAGE = {
version: {
current: new DefaultSetting("string", () => RUNTIME_INFORMATION.getVersion()),
initial: new DefaultSetting("string", () => RUNTIME_INFORMATION.getVersion()),
oldVersion: new DefaultSetting("string"),
showNotice: new DefaultSetting("boolean", true)
},
api: {
torn: {
key: new DefaultSetting("string"),
online: new DefaultSetting("boolean", true),
error: new DefaultSetting("string")
},
tornstats: { key: new DefaultSetting("string") },
yata: { key: new DefaultSetting("string") },
ffScouter: { key: new DefaultSetting("string") }
},
settings: {
updateNotice: new DefaultSetting("boolean", true),
featureDisplay: new DefaultSetting("boolean", true),
featureDisplayPosition: new DefaultSetting("string", "bottom-left"),
featureDisplayOnlyFailed: new DefaultSetting("boolean", false),
featureDisplayHideDisabled: new DefaultSetting("boolean", false),
featureDisplayHideEmpty: new DefaultSetting("boolean", true),
developer: new DefaultSetting("boolean", false),
formatting: {
tct: new DefaultSetting("boolean", false),
date: new DefaultSetting("string", "eu"),
time: new DefaultSetting("string", "eu")
},
sorting: { abroad: {
column: new DefaultSetting("string", ""),
order: new DefaultSetting("string", "none")
} },
notifications: {
sound: new DefaultSetting("string", "default"),
soundCustom: new DefaultSetting("string", ""),
tts: new DefaultSetting("boolean", false),
ttsVoice: new DefaultSetting("string", "default"),
ttsRate: new DefaultSetting("number", 1),
link: new DefaultSetting("boolean", true),
volume: new DefaultSetting("number", 100),
requireInteraction: new DefaultSetting("boolean", false),
types: {
global: new DefaultSetting("boolean", () => typeof Notification !== "undefined" && Notification.permission === "granted"),
events: new DefaultSetting("boolean", true),
messages: new DefaultSetting("boolean", true),
status: new DefaultSetting("boolean", true),
traveling: new DefaultSetting("boolean", true),
cooldowns: new DefaultSetting("boolean", true),
education: new DefaultSetting("boolean", true),
newDay: new DefaultSetting("boolean", true),
energy: new DefaultSetting("array", ["100%"]),
nerve: new DefaultSetting("array", ["100%"]),
happy: new DefaultSetting("array", ["100%"]),
life: new DefaultSetting("array", ["100%"]),
offline: new DefaultSetting("array", []),
chainTimerEnabled: new DefaultSetting("boolean", true),
chainBonusEnabled: new DefaultSetting("boolean", true),
leavingHospitalEnabled: new DefaultSetting("boolean", true),
landingEnabled: new DefaultSetting("boolean", true),
cooldownDrugEnabled: new DefaultSetting("boolean", true),
cooldownBoosterEnabled: new DefaultSetting("boolean", true),
cooldownMedicalEnabled: new DefaultSetting("boolean", true),
chainTimer: new DefaultSetting("array", []),
chainBonus: new DefaultSetting("array", []),
leavingHospital: new DefaultSetting("array", []),
landing: new DefaultSetting("array", []),
cooldownDrug: new DefaultSetting("array", []),
cooldownBooster: new DefaultSetting("array", []),
cooldownMedical: new DefaultSetting("array", []),
stocks: new DefaultSetting("object", {}),
missionsLimitEnabled: new DefaultSetting("boolean", false),
missionsLimit: new DefaultSetting("string", ""),
missionsExpireEnabled: new DefaultSetting("boolean", false),
missionsExpire: new DefaultSetting("array", []),
npcsGlobal: new DefaultSetting("boolean", true),
npcs: new DefaultSetting("array", []),
npcPlannedEnabled: new DefaultSetting("boolean", true),
npcPlanned: new DefaultSetting("array", []),
refillEnergyEnabled: new DefaultSetting("boolean", true),
refillEnergy: new DefaultSetting("string", ""),
refillNerveEnabled: new DefaultSetting("boolean", true),
refillNerve: new DefaultSetting("string", "")
}
},
apiUsage: {
comment: new DefaultSetting("string", "TornTools"),
delayEssential: new DefaultSetting("number", 30),
delayBasic: new DefaultSetting("number", 120),
delayPassive: new DefaultSetting("number", 3600),
delayStakeouts: new DefaultSetting("number", 30),
user: {
bars: new DefaultSetting("boolean", true),
cooldowns: new DefaultSetting("boolean", true),
travel: new DefaultSetting("boolean", true),
newevents: new DefaultSetting("boolean", true),
newmessages: new DefaultSetting("boolean", true),
refills: new DefaultSetting("boolean", true),
stocks: new DefaultSetting("boolean", true),
education: new DefaultSetting("boolean", true),
networth: new DefaultSetting("boolean", true),
inventory: new DefaultSetting("boolean", true),
jobpoints: new DefaultSetting("boolean", true),
merits: new DefaultSetting("boolean", true),
perks: new DefaultSetting("boolean", true),
icons: new DefaultSetting("boolean", true),
ammo: new DefaultSetting("boolean", true),
battlestats: new DefaultSetting("boolean", true),
crimes: new DefaultSetting("boolean", true),
workstats: new DefaultSetting("boolean", true),
skills: new DefaultSetting("boolean", true),
weaponexp: new DefaultSetting("boolean", true),
properties: new DefaultSetting("boolean", true),
calendar: new DefaultSetting("boolean", true),
organizedcrime: new DefaultSetting("boolean", true),
missions: new DefaultSetting("boolean", true),
personalstats: new DefaultSetting("boolean", true),
attacks: new DefaultSetting("boolean", true),
money: new DefaultSetting("boolean", true),
honors: new DefaultSetting("boolean", true),
medals: new DefaultSetting("boolean", true),
virus: new DefaultSetting("boolean", true)
}
},
themes: {
pages: new DefaultSetting("string", "default"),
containers: new DefaultSetting("string", "default")
},
hideIcons: new DefaultSetting("array", []),
hideCasinoGames: new DefaultSetting("array", []),
hideStocks: new DefaultSetting("array", []),
alliedFactions: new DefaultSetting("array", []),
customLinks: new DefaultSetting("array", []),
employeeInactivityWarning: new DefaultSetting("array", []),
factionInactivityWarning: new DefaultSetting("array", []),
userAlias: new DefaultSetting("array", []),
csvDelimiter: new DefaultSetting("string", ";"),
pages: {
global: {
alignLeft: new DefaultSetting("boolean", false),
hideLevelUpgrade: new DefaultSetting("boolean", false),
hideQuitButtons: new DefaultSetting("boolean", false),
hideTutorials: new DefaultSetting("boolean", false),
keepAttackHistory: new DefaultSetting("boolean", true),
miniProfileLastAction: new DefaultSetting("boolean", true),
reviveProvider: new DefaultSetting("string", ""),
pageTitles: new DefaultSetting("boolean", true),
stackingMode: new DefaultSetting("boolean", false),
noOutsideLinkAlert: new DefaultSetting("boolean", false),
urlFill: new DefaultSetting("boolean", true)
},
profile: {
avgpersonalstats: new DefaultSetting("boolean", false),
statusIndicator: new DefaultSetting("boolean", true),
idBesideProfileName: new DefaultSetting("boolean", true),
notes: new DefaultSetting("boolean", true),
showAllyWarning: new DefaultSetting("boolean", true),
ageToWords: new DefaultSetting("boolean", true),
disableAllyAttacks: new DefaultSetting("boolean", true),
box: new DefaultSetting("boolean", true),
boxStats: new DefaultSetting("boolean", true),
boxSpy: new DefaultSetting("boolean", true),
boxStakeout: new DefaultSetting("boolean", true),
boxAttackHistory: new DefaultSetting("boolean", true),
boxFetch: new DefaultSetting("boolean", true)
},
chat: {
fontSize: new DefaultSetting("number", 12),
searchChat: new DefaultSetting("boolean", true),
completeUsernames: new DefaultSetting("boolean", true),
highlights: new DefaultSetting("array", [{
name: "$player",
color: "#7ca900"
}]),
titleHighlights: new DefaultSetting("array", []),
tradeTimer: new DefaultSetting("boolean", true),
resizable: new DefaultSetting("boolean", true),
hideChatButton: new DefaultSetting("boolean", true),
hideChat: new DefaultSetting("boolean", false)
},
sidebar: {
notes: new DefaultSetting("boolean", true),
highlightEnergy: new DefaultSetting("boolean", true),
highlightNerve: new DefaultSetting("boolean", false),
ocTimer: new DefaultSetting("boolean", true),
oc2Timer: new DefaultSetting("boolean", true),
oc2TimerPosition: new DefaultSetting("boolean", false),
oc2TimerLevel: new DefaultSetting("boolean", true),
factionOCTimer: new DefaultSetting("boolean", false),
collapseAreas: new DefaultSetting("boolean", true),
settingsLink: new DefaultSetting("boolean", true),
hideGymHighlight: new DefaultSetting("boolean", false),
hideNewspaperHighlight: new DefaultSetting("boolean", false),
upkeepPropHighlight: new DefaultSetting("number", 0),
barLinks: new DefaultSetting("boolean", true),
pointsValue: new DefaultSetting("boolean", true),
npcLootTimes: new DefaultSetting("boolean", true),
npcLootTimesService: new DefaultSetting("string", "tornstats"),
cooldownEndTimes: new DefaultSetting("boolean", true),
companyAddictionLevel: new DefaultSetting("boolean", true),
showJobPointsToolTip: new DefaultSetting("boolean", true),
rwTimer: new DefaultSetting("boolean", true),
virusTimer: new DefaultSetting("boolean", false)
},
popup: {
dashboard: new DefaultSetting("boolean", true),
marketSearch: new DefaultSetting("boolean", true),
bazaarUsingExternal: new DefaultSetting("boolean", true),
calculator: new DefaultSetting("boolean", true),
stocksOverview: new DefaultSetting("boolean", true),
notifications: new DefaultSetting("boolean", true),
defaultTab: new DefaultSetting("string", "dashboard"),
showStakeouts: new DefaultSetting("boolean", true),
showIcons: new DefaultSetting("boolean", true),
fullBarTime: new DefaultSetting("boolean", false)
},
icon: {
global: new DefaultSetting("boolean", true),
energy: new DefaultSetting("boolean", true),
nerve: new DefaultSetting("boolean", true),
happy: new DefaultSetting("boolean", true),
life: new DefaultSetting("boolean", true),
chain: new DefaultSetting("boolean", true),
travel: new DefaultSetting("boolean", true)
},
education: {
greyOut: new DefaultSetting("boolean", true),
finishTime: new DefaultSetting("boolean", true)
},
jail: { filter: new DefaultSetting("boolean", true) },
bank: {
investmentInfo: new DefaultSetting("boolean", true),
investmentDueTime: new DefaultSetting("boolean", true)
},
home: {
networthDetails: new DefaultSetting("boolean", true),
effectiveStats: new DefaultSetting("boolean", true)
},
items: {
quickItems: new DefaultSetting("boolean", true),
values: new DefaultSetting("boolean", true),
drugDetails: new DefaultSetting("boolean", true),
marketLinks: new DefaultSetting("boolean", false),
highlightBloodBags: new DefaultSetting("string", "none"),
missingFlowers: new DefaultSetting("boolean", false),
missingPlushies: new DefaultSetting("boolean", false),
bookEffects: new DefaultSetting("boolean", true),
canGains: new DefaultSetting("boolean", true),
nerveGains: new DefaultSetting("boolean", true),
candyHappyGains: new DefaultSetting("boolean", true),
energyWarning: new DefaultSetting("boolean", true),
medicalLife: new DefaultSetting("boolean", true),
openedSupplyPackValue: new DefaultSetting("boolean", true),
hideRecycleMessage: new DefaultSetting("boolean", false),
hideTooManyItemsWarning: new DefaultSetting("boolean", false)
},
crimes: { quickCrimes: new DefaultSetting("boolean", true) },
companies: {
idBesideCompanyName: new DefaultSetting("boolean", false),
specials: new DefaultSetting("boolean", true),
autoStockFill: new DefaultSetting("boolean", true),
employeeEffectiveness: new DefaultSetting("number", 18)
},
travel: {
computer: new DefaultSetting("boolean", true),
table: new DefaultSetting("boolean", true),
cleanFlight: new DefaultSetting("boolean", false),
tabTitleTimer: new DefaultSetting("boolean", false),
travelProfits: new DefaultSetting("boolean", true),
fillMax: new DefaultSetting("boolean", true),
peopleFilter: new DefaultSetting("boolean", true),
landingTime: new DefaultSetting("boolean", true),
flyingTime: new DefaultSetting("boolean", true),
itemFilter: new DefaultSetting("boolean", true),
energyWarning: new DefaultSetting("boolean", true),
cooldownWarnings: new DefaultSetting("boolean", true),
autoTravelTableCountry: new DefaultSetting("boolean", false),
autoFillMax: new DefaultSetting("boolean", true),
efficientRehab: new DefaultSetting("boolean", true),
efficientRehabSelect: new DefaultSetting("boolean", false)
},
stocks: {
filter: new DefaultSetting("boolean", true),
acronyms: new DefaultSetting("boolean", true),
valueAndProfit: new DefaultSetting("boolean", true),
moneyInput: new DefaultSetting("boolean", true)
},
competitions: {
easterEggs: new DefaultSetting("boolean", false),
easterEggsAlert: new DefaultSetting("boolean", true)
},
events: { worth: new DefaultSetting("boolean", true) },
hospital: { filter: new DefaultSetting("boolean", true) },
auction: { filter: new DefaultSetting("boolean", true) },
api: {
autoFillKey: new DefaultSetting("boolean", true),
autoDemo: new DefaultSetting("boolean", false),
autoPretty: new DefaultSetting("boolean", true),
clickableSelections: new DefaultSetting("boolean", true)
},
forums: {
menu: new DefaultSetting("boolean", true),
hidePosts: new DefaultSetting("object", {}),
hideThreads: new DefaultSetting("object", {}),
highlightPosts: new DefaultSetting("object", {}),
highlightThreads: new DefaultSetting("object", {}),
ignoredThreads: new DefaultSetting("object", {}),
debugInfoBtn: new DefaultSetting("boolean", true),
onlyNewFeedButton: new DefaultSetting("boolean", true)
},
bazaar: {
itemsCost: new DefaultSetting("boolean", true),
worth: new DefaultSetting("boolean", true),
fillMax: new DefaultSetting("boolean", true),
maxBuyIgnoreCash: new DefaultSetting("boolean", false),
highlightSubVendorItems: new DefaultSetting("boolean", false)
},
trade: {
itemValues: new DefaultSetting("boolean", true),
openChat: new DefaultSetting("boolean", true)
},
displayCase: { worth: new DefaultSetting("boolean", true) },
shops: {
fillMax: new DefaultSetting("boolean", true),
maxBuyIgnoreCash: new DefaultSetting("boolean", false),
profit: new DefaultSetting("boolean", true),
filters: new DefaultSetting("boolean", true),
values: new DefaultSetting("boolean", true)
},
casino: {
netTotal: new DefaultSetting("boolean", true),
blackjack: new DefaultSetting("boolean", true),
highlow: new DefaultSetting("boolean", false),
highlowMovement: new DefaultSetting("boolean", true)
},
racing: {
winPercentage: new DefaultSetting("boolean", true),
upgrades: new DefaultSetting("boolean", true),
filter: new DefaultSetting("boolean", true)
},
faction: {
idBesideFactionName: new DefaultSetting("boolean", false),
csvRaidReport: new DefaultSetting("boolean", true),
csvRankedWarReport: new DefaultSetting("boolean", true),
csvWarReport: new DefaultSetting("boolean", true),
csvChainReport: new DefaultSetting("boolean", true),
csvChallengeContributions: new DefaultSetting("boolean", true),
openOc: new DefaultSetting("boolean", true),
highlightOwn: new DefaultSetting("boolean", true),
availablePlayers: new DefaultSetting("boolean", true),
recommendedNnb: new DefaultSetting("boolean", true),
ocNnb: new DefaultSetting("boolean", true),
ocTimes: new DefaultSetting("boolean", true),
ocLastAction: new DefaultSetting("boolean", true),
banker: new DefaultSetting("boolean", true),
showFullInfobox: new DefaultSetting("boolean", true),
foldableInfobox: new DefaultSetting("boolean", true),
numberMembers: new DefaultSetting("boolean", true),
warFinishTimes: new DefaultSetting("boolean", false),
memberFilter: new DefaultSetting("boolean", true),
armoryFilter: new DefaultSetting("boolean", true),
armoryWorth: new DefaultSetting("boolean", true),
upgradeRequiredRespect: new DefaultSetting("boolean", true),
memberInfo: new DefaultSetting("boolean", false),
rankedWarFilter: new DefaultSetting("boolean", true),
quickItems: new DefaultSetting("boolean", true),
stakeout: new DefaultSetting("boolean", true),
showFactionSpy: new DefaultSetting("boolean", true),
oc2Filter: new DefaultSetting("boolean", true),
warnCrime: new DefaultSetting("boolean", false),
rankedWarValue: new DefaultSetting("boolean", true),
totalChallengeContributions: new DefaultSetting("boolean", true)
},
property: {
value: new DefaultSetting("boolean", true),
happy: new DefaultSetting("boolean", true)
},
gym: {
specialist: new DefaultSetting("boolean", true),
disableStats: new DefaultSetting("boolean", true),
graph: new DefaultSetting("boolean", true),
steadfast: new DefaultSetting("boolean", true),
progress: new DefaultSetting("boolean", true)
},
missions: {
hints: new DefaultSetting("boolean", true),
rewards: new DefaultSetting("boolean", true)
},
attack: {
bonusInformation: new DefaultSetting("boolean", true),
timeoutWarning: new DefaultSetting("boolean", true),
fairAttack: new DefaultSetting("boolean", true),
weaponExperience: new DefaultSetting("boolean", true),
hideAttackButtons: new DefaultSetting("array", [])
},
city: {
items: new DefaultSetting("boolean", true),
combineDuplicates: new DefaultSetting("boolean", true)
},
joblist: { specials: new DefaultSetting("boolean", true) },
bounties: { filter: new DefaultSetting("boolean", true) },
userlist: { filter: new DefaultSetting("boolean", true) },
itemmarket: {
highlightCheapItems: new DefaultSetting("number|empty", ""),
highlightCheapItemsSound: new DefaultSetting("boolean", false),
leftBar: new DefaultSetting("boolean", false),
fillMax: new DefaultSetting("boolean", true)
},
competition: { filter: new DefaultSetting("boolean", true) },
museum: { autoFill: new DefaultSetting("boolean", true) },
enemies: { filter: new DefaultSetting("boolean", true) },
friends: { filter: new DefaultSetting("boolean", true) },
targets: { filter: new DefaultSetting("boolean", true) },
crimes2: {
burglaryFilter: new DefaultSetting("boolean", true),
value: new DefaultSetting("boolean", true)
}
},
scripts: {
noConfirm: {
itemEquip: new DefaultSetting("boolean", true),
tradeAccept: new DefaultSetting("boolean", false),
pointsMarketRemove: new DefaultSetting("boolean", false),
pointsMarketBuy: new DefaultSetting("boolean", false),
abroadItemBuy: new DefaultSetting("boolean", true)
},
achievements: {
show: new DefaultSetting("boolean", true),
completed: new DefaultSetting("boolean", false)
},
lastAction: {
factionMember: new DefaultSetting("boolean", false),
companyOwn: new DefaultSetting("boolean", false),
companyOther: new DefaultSetting("boolean", false)
},
statsEstimate: {
global: new DefaultSetting("boolean", true),
delay: new DefaultSetting("number", 1500),
cachedOnly: new DefaultSetting("boolean", true),
displayNoResult: new DefaultSetting("boolean", false),
maxLevel: new DefaultSetting("number", 100),
profiles: new DefaultSetting("boolean", true),
enemies: new DefaultSetting("boolean", true),
hof: new DefaultSetting("boolean", true),
attacks: new DefaultSetting("boolean", true),
userlist: new DefaultSetting("boolean", true),
bounties: new DefaultSetting("boolean", true),
factions: new DefaultSetting("boolean", true),
wars: new DefaultSetting("boolean", true),
abroad: new DefaultSetting("boolean", true),
competition: new DefaultSetting("boolean", true),
rankedWars: new DefaultSetting("boolean", true),
targets: new DefaultSetting("boolean", true)
},
ffScouter: {
miniProfile: new DefaultSetting("boolean", true),
profile: new DefaultSetting("boolean", true),
attack: new DefaultSetting("boolean", true),
factionList: new DefaultSetting("boolean", true),
gauge: new DefaultSetting("boolean", true)
}
},
external: {
tornstats: new DefaultSetting("boolean", false),
yata: new DefaultSetting("boolean", false),
prometheus: new DefaultSetting("boolean", false),
lzpt: new DefaultSetting("boolean", false),
tornw3b: new DefaultSetting("boolean", false),
ffScouter: new DefaultSetting("boolean", false),
tornintel: new DefaultSetting("boolean", false)
}
},
filters: {
hospital: {
enabled: new DefaultSetting("boolean", true),
timeStart: new DefaultSetting("number", 0),
timeEnd: new DefaultSetting("number", 100),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100),
faction: new DefaultSetting("string", ""),
activity: new DefaultSetting("array", []),
revivesOn: new DefaultSetting("boolean", false)
},
jail: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
faction: new DefaultSetting("string", "All"),
timeStart: new DefaultSetting("number", 0),
timeEnd: new DefaultSetting("number", 100),
levelStart: new DefaultSetting("number", 1),
levelEnd: new DefaultSetting("number", 100),
scoreStart: new DefaultSetting("number", 0),
scoreEnd: new DefaultSetting("number", 5e3),
bailCost: new DefaultSetting("number", -1)
},
racing: {
enabled: new DefaultSetting("boolean", true),
hideRaces: new DefaultSetting("array", []),
timeStart: new DefaultSetting("number", 0),
timeEnd: new DefaultSetting("number", 48),
driversMin: new DefaultSetting("number", 2),
driversMax: new DefaultSetting("number", 100),
lapsMin: new DefaultSetting("number", 1),
lapsMax: new DefaultSetting("number", 100),
track: new DefaultSetting("array", []),
name: new DefaultSetting("string", "")
},
containers: new DefaultSetting("object", {}),
travel: {
open: new DefaultSetting("boolean", false),
type: new DefaultSetting("string", "basic"),
categories: new DefaultSetting("array", []),
countries: new DefaultSetting("array", []),
hideOutOfStock: new DefaultSetting("boolean", false),
applySalesTax: new DefaultSetting("boolean", false),
sellAnonymously: new DefaultSetting("boolean", false)
},
abroadPeople: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
status: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100),
faction: new DefaultSetting("string", ""),
special: {
newPlayer: new DefaultSetting("string", "both"),
inCompany: new DefaultSetting("string", "both"),
inFaction: new DefaultSetting("string", "both"),
isDonator: new DefaultSetting("string", "both"),
hasBounties: new DefaultSetting("string", "both"),
bazaarOpen: new DefaultSetting("string", "both")
},
estimates: new DefaultSetting("array", []),
ffScoreMax: new DefaultSetting("number", null),
ffScoreMin: new DefaultSetting("number", null)
},
abroadItems: {
enabled: new DefaultSetting("boolean", true),
profitOnly: new DefaultSetting("boolean", false),
outOfStock: new DefaultSetting("boolean", false),
categories: new DefaultSetting("array", []),
taxes: new DefaultSetting("array", [])
},
trade: { hideValues: new DefaultSetting("boolean", false) },
gym: {
specialist1: new DefaultSetting("string", "none"),
specialist2: new DefaultSetting("string", "none"),
strength: new DefaultSetting("boolean", false),
speed: new DefaultSetting("boolean", false),
defense: new DefaultSetting("boolean", false),
dexterity: new DefaultSetting("boolean", false)
},
city: { highlightItems: new DefaultSetting("boolean", true) },
bounties: {
maxLevel: new DefaultSetting("number", 100),
hideUnavailable: new DefaultSetting("boolean", false)
},
userlist: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100),
special: {
fedded: new DefaultSetting("string", "both"),
fallen: new DefaultSetting("string", "both"),
traveling: new DefaultSetting("string", "both"),
newPlayer: new DefaultSetting("string", "both"),
onWall: new DefaultSetting("string", "both"),
inCompany: new DefaultSetting("string", "both"),
inFaction: new DefaultSetting("string", "both"),
isDonator: new DefaultSetting("string", "both"),
inHospital: new DefaultSetting("string", "both"),
inJail: new DefaultSetting("string", "both"),
earlyDischarge: new DefaultSetting("string", "both"),
hasBounties: new DefaultSetting("string", "both"),
bazaarOpen: new DefaultSetting("string", "both")
},
hospReason: {
attackedBy: new DefaultSetting("string", "both"),
muggedBy: new DefaultSetting("string", "both"),
hospitalizedBy: new DefaultSetting("string", "both"),
other: new DefaultSetting("string", "both")
},
estimates: new DefaultSetting("array", []),
ffScoreMax: new DefaultSetting("number", null),
ffScoreMin: new DefaultSetting("number", null)
},
stocks: {
enabled: new DefaultSetting("boolean", true),
name: new DefaultSetting("string", ""),
investment: {
owned: new DefaultSetting("string", "both"),
benefit: new DefaultSetting("string", "both"),
passive: new DefaultSetting("string", "both")
},
price: {
price: new DefaultSetting("string", "both"),
profit: new DefaultSetting("string", "both")
}
},
faction: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 1),
levelEnd: new DefaultSetting("number", 100),
lastActionStart: new DefaultSetting("number", 0),
lastActionEnd: new DefaultSetting("number", -1),
status: new DefaultSetting("array", []),
position: new DefaultSetting("string", ""),
special: {
fedded: new DefaultSetting("string", "both"),
fallen: new DefaultSetting("string", "both"),
newPlayer: new DefaultSetting("string", "both"),
inCompany: new DefaultSetting("string", "both"),
isDonator: new DefaultSetting("string", "both"),
isRecruit: new DefaultSetting("string", "both")
},
ffScoreMax: new DefaultSetting("number", null),
ffScoreMin: new DefaultSetting("number", null)
},
factionArmory: {
enabled: new DefaultSetting("boolean", true),
hideUnavailable: new DefaultSetting("boolean", false),
weapons: {
name: new DefaultSetting("string", ""),
category: new DefaultSetting("string", ""),
rarity: new DefaultSetting("string", ""),
weaponType: new DefaultSetting("string", ""),
damage: new DefaultSetting("string", ""),
accuracy: new DefaultSetting("string", ""),
weaponBonus: new DefaultSetting("array", [])
},
armor: {
name: new DefaultSetting("string", ""),
rarity: new DefaultSetting("string", ""),
defence: new DefaultSetting("string", ""),
set: new DefaultSetting("string", ""),
armorBonus: new DefaultSetting("string", "")
},
temporary: { name: new DefaultSetting("string", "") }
},
factionRankedWar: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
status: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 1),
levelEnd: new DefaultSetting("number", 100),
estimates: new DefaultSetting("array", []),
ffScoreMax: new DefaultSetting("number", null),
ffScoreMin: new DefaultSetting("number", null)
},
profile: {
relative: new DefaultSetting("boolean", false),
stats: new DefaultSetting("array", [])
},
competition: {
levelStart: new DefaultSetting("number", 1),
levelEnd: new DefaultSetting("number", 100),
estimates: new DefaultSetting("array", [])
},
shops: {
hideLoss: new DefaultSetting("boolean", false),
hideUnder100: new DefaultSetting("boolean", false)
},
auction: {
enabled: new DefaultSetting("boolean", true),
weapons: {
name: new DefaultSetting("string", ""),
category: new DefaultSetting("string", ""),
rarity: new DefaultSetting("string", ""),
weaponType: new DefaultSetting("string", ""),
damage: new DefaultSetting("string", ""),
accuracy: new DefaultSetting("string", ""),
weaponBonus: new DefaultSetting("array", []),
quality: new DefaultSetting("string", "")
},
armor: {
name: new DefaultSetting("string", ""),
rarity: new DefaultSetting("string", ""),
defence: new DefaultSetting("string", ""),
set: new DefaultSetting("string", ""),
armorBonus: new DefaultSetting("string", "")
},
items: {
name: new DefaultSetting("string", ""),
category: new DefaultSetting("string", ""),
rarity: new DefaultSetting("string", "")
}
},
enemies: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100),
estimates: new DefaultSetting("array", [])
},
friends: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100)
},
targets: {
enabled: new DefaultSetting("boolean", true),
activity: new DefaultSetting("array", []),
levelStart: new DefaultSetting("number", 0),
levelEnd: new DefaultSetting("number", 100),
estimates: new DefaultSetting("array", [])
},
burglary: {
targetName: new DefaultSetting("string", ""),
targetType: new DefaultSetting("array", [])
},
oc2: {
enabled: new DefaultSetting("boolean", true),
difficulty: new DefaultSetting("array", []),
status: new DefaultSetting("array", [])
}
},
userdata: new DefaultSetting("object", { date: -1 }),
torndata: new DefaultSetting("object", { date: -2 }),
stockdata: new DefaultSetting("object", {}),
factiondata: new DefaultSetting("object", {}),
localdata: {
tradeMessage: new DefaultSetting("number", 0),
popup: { calculatorItems: new DefaultSetting("array", []) },
vault: {
initialized: new DefaultSetting("boolean", false),
lastTransaction: new DefaultSetting("string", ""),
total: new DefaultSetting("number", 0),
user: {
initial: new DefaultSetting("number", 0),
current: new DefaultSetting("number", 0)
},
partner: {
initial: new DefaultSetting("number", 0),
current: new DefaultSetting("number", 0)
}
},
chatResize: new DefaultSetting("object", {}),
feedHidden: new DefaultSetting("object", {}),
threadsHiddenInFeed: new DefaultSetting("array", [])
},
stakeouts: new DefaultSetting("object", { list: [] }),
factionStakeouts: new DefaultSetting("object", { list: [] }),
attackHistory: {
fetchData: new DefaultSetting("boolean", true),
lastAttack: new DefaultSetting("number", 0),
history: new DefaultSetting("object", {})
},
notes: {
sidebar: {
text: new DefaultSetting("string", ""),
height: new DefaultSetting("string", "22px")
},
profile: new DefaultSetting("object", {})
},
quick: {
items: new DefaultSetting("array", []),
factionItems: new DefaultSetting("array", []),
crimes: new DefaultSetting("array", []),
jail: new DefaultSetting("array", [])
},
cache: new DefaultSetting("object", {}),
npcs: new DefaultSetting("object", {}),
notificationHistory: new DefaultSetting("array", []),
notifications: {
events: new DefaultSetting("object", {}),
messages: new DefaultSetting("object", {}),
newDay: new DefaultSetting("object", {}),
energy: new DefaultSetting("object", {}),
happy: new DefaultSetting("object", {}),
nerve: new DefaultSetting("object", {}),
life: new DefaultSetting("object", {}),
travel: new DefaultSetting("object", {}),
drugs: new DefaultSetting("object", {}),
boosters: new DefaultSetting("object", {}),
medical: new DefaultSetting("object", {}),
hospital: new DefaultSetting("object", {}),
chain: new DefaultSetting("object", {}),
chainCount: new DefaultSetting("object", {}),
stakeouts: new DefaultSetting("object", {}),
npcs: new DefaultSetting("object", {}),
offline: new DefaultSetting("object", {}),
missionsLimit: new DefaultSetting("object", {}),
missionsExpire: new DefaultSetting("object", {}),
refillEnergy: new DefaultSetting("object", {}),
refillNerve: new DefaultSetting("object", {})
},
migrations: new DefaultSetting("array", [])
};
function getDefaultStorage(defaultStorage) {
const newStorage = {};
for (const key in defaultStorage) if (typeof defaultStorage[key] === "object") {
const setting = defaultStorage[key];
if (setting instanceof DefaultSetting && "defaultValue" in setting) switch (typeof setting.defaultValue) {
case "function":
newStorage[key] = setting.defaultValue();
break;
case "boolean":
case "number":
case "string":
case "object":
newStorage[key] = setting.defaultValue;
break;
default:
newStorage[key] = setting.defaultValue;
break;
}
else newStorage[key] = getDefaultStorage(defaultStorage[key]);
} else newStorage[key] = defaultStorage[key];
return newStorage;
}
var MIGRATIONS = [
{
id: "9da14c73-0145-4b1d-90e3-0363a5b57499",
version: "9.0.0",
execute(_database, flags, _oldStorage) {
flags.updateUserdata = true;
}
},
{
id: "43fae1f2-5568-4ae5-b12f-f3625e1e58c6",
version: "9.0.0",
execute(database, _flags, _oldStorage) {
database.cache["personal-stats"] = {};
}
},
{
id: "b194a6d5-4230-4b03-8a8b-bebd7c431cc9",
version: "9.0.0",
execute(database, _flags, _oldStorage) {
database.settings.pages.api.autoDemo = false;
}
},
{
id: "b0f539ba-41f8-4eed-93e2-e8523f7c49a5",
version: "9.0.1",
execute(database, _flags, oldStorage) {
const oldCustomLinks = oldStorage?.settings?.customLinks ?? [];
database.settings.customLinks = oldCustomLinks.map((link) => {
return link.preset && link.preset !== "custom" ? {
newTab: link.newTab,
location: link.location,
name: link.name,
preset: link.preset
} : {
newTab: link.newTab,
location: link.location,
name: link.name,
href: link.href
};
});
}
},
{
id: "360b1f70-c78b-44c1-b217-24bd6b398bac",
version: "9.0.5",
execute(database, _flags, oldStorage) {
if (!oldStorage?.settings?.userAlias || Array.isArray(oldStorage.settings.userAlias)) return;
const oldUserAliases = oldStorage.settings.userAlias;
database.settings.userAlias = Object.entries(oldUserAliases).map(([id, { alias, name }]) => {
const idMatch = id.match(/^(\d+)$/);
return idMatch ? {
userId: parseInt(idMatch[0]),
userName: name,
alias
} : {
userId: -1,
userName: name,
alias,
incorrectId: id
};
});
}
},
{
id: "95c020eb-2c75-4bbe-8fe9-64f96f108f48",
version: "9.0.5",
execute(database, _flags, oldStorage) {
if (!oldStorage?.settings?.pages?.popup?.defaultTab) return;
if (oldStorage.settings.pages.popup.defaultTab === "stocks") database.settings.pages.popup.defaultTab = "stocksOverview";
else if (oldStorage.settings.pages.popup.defaultTab === "market") database.settings.pages.popup.defaultTab = "marketSearch";
}
},
{
id: "96356911-fecd-4b79-9825-ee5ad422c8fe",
version: "9.0.5",
execute(database, _flags, oldStorage) {
if (typeof oldStorage?.settings?.pages?.popup.hoverBarTime !== "boolean") return;
database.settings.pages.popup.fullBarTime = oldStorage.settings.pages.popup.hoverBarTime;
}
},
{
id: "7396191c-35a9-4d92-905a-0e411f9a6823",
version: "9.0.5",
execute(_database, _flags, _oldStorage) {
ttStorage.remove("usage");
}
},
{
id: "d3e6e03a-698d-4df4-9062-4d3c9ce9d479",
version: "9.0.5",
execute(database, _flags, oldStorage) {
if (!oldStorage?.filters?.travel?.categories?.includes("other")) return;
database.filters.travel.categories = [...oldStorage.filters.travel.categories, "defensive"];
}
},
{
id: "700848e9-ee48-42ce-b8b1-893cb471cfe4",
version: "9.0.6",
execute(_database, flags, _oldStorage) {
flags.clearCache = true;
}
},
{
id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
version: "9.0.6",
execute(database, _flags, oldStorage) {
const oldStakeouts = oldStorage?.stakeouts;
if (!oldStakeouts || typeof oldStakeouts !== "object") return;
const reservedKeys = new Set([
"order",
"date",
"list"
]);
const oldOrder = oldStakeouts.order ?? [];
const list = [];
Object.entries(oldStakeouts).filter((entry) => !reservedKeys.has(entry[0])).forEach(([id, data]) => {
const orderIndex = oldOrder.indexOf(id);
list.push({
...data,
id: parseInt(id),
order: orderIndex !== -1 ? orderIndex : Date.now()
});
});
database.stakeouts.list = list;
}
},
{
id: "b2c3d4e5-f6a7-8901-bcde-f12345678901",
version: "9.0.6",
execute(database, _flags, oldStorage) {
const oldFactionStakeouts = oldStorage?.factionStakeouts;
if (!oldFactionStakeouts || typeof oldFactionStakeouts !== "object") return;
const reservedKeys = new Set(["date", "list"]);
const list = [];
Object.entries(oldFactionStakeouts).filter((entry) => !reservedKeys.has(entry[0])).forEach(([id, data]) => {
list.push({
...data,
id: parseInt(id),
order: Date.now()
});
});
database.factionStakeouts.list = list;
}
}
];
async function executeMigrationScripts(storage, oldStorage) {
if (RUNTIME_INFORMATION.isUserscript()) return;
const migrations = MIGRATIONS.filter(({ version }) => toNumericVersion(version) >= toNumericVersion(storage.version.initial)).filter(({ id }) => !storage.migrations.map(({ id }) => id).includes(id));
const flags = {
updateUserdata: false,
updateFactiondata: false,
updateTorndata: false,
clearCache: false
};
migrations.reverse().filter((migration) => {
migration.execute(storage, flags, oldStorage);
storage.migrations.push({ id: migration.id });
});
if (flags.updateUserdata) storage.userdata.date = 0;
if (flags.updateFactiondata) storage.factiondata.date = 0;
if (flags.updateTorndata) storage.torndata.date = 0;
if (flags.clearCache) storage.cache = {};
}
var settings;
var filters;
var version;
var api;
var userdata;
var torndata;
var stakeouts;
var attackHistory;
var notes;
var factiondata;
var quick;
var localdata;
var npcs;
var notificationHistory;
var stockdata;
var factionStakeouts;
var notifications;
var migrations;
var databaseLoaded = false;
var databaseLoading = false;
var storageListeners = {
settings: [],
filters: [],
version: [],
userdata: [],
torndata: [],
attackHistory: [],
stakeouts: [],
factionStakeouts: [],
notes: [],
factiondata: [],
localdata: [],
cache: [],
api: [],
npcs: [],
stockdata: [],
notificationHistory: [],
notifications: [],
quick: [],
migrations: []
};
async function loadDatabase(force = false) {
if (databaseLoaded && !force) return {
settings,
filters,
version,
userdata,
stakeouts,
factionStakeouts,
notes,
factiondata,
localdata,
cache: ttCache.cache,
api,
npcs,
torndata,
notificationHistory,
attackHistory,
quick,
stockdata,
notifications,
migrations
};
else if (databaseLoaded && !settings || databaseLoading) {
await sleep(75);
return await loadDatabase(force);
}
databaseLoading = true;
const database = await ttStorage.get();
populateDatabaseVariables(database);
console.log("TT - Database loaded.", database);
databaseLoaded = true;
databaseLoading = false;
return database;
}
async function migrateDatabase(force = false) {
try {
const loadedStorage = await ttStorage.get();
if (!loadedStorage || !Object.keys(loadedStorage).length) {
console.log("TT - Fresh installation detected, setting up default storage.");
await ttStorage.reset();
await loadDatabase();
return;
}
const storedVersion = loadedStorage?.version?.current || "5.0.0";
const currentVersion = RUNTIME_INFORMATION.getVersion();
console.log(`TT - Migration check: ${storedVersion} -> ${currentVersion}`);
const migratedStorage = convertStorage(loadedStorage, DEFAULT_STORAGE);
await executeMigrationScripts(migratedStorage, loadedStorage);
migratedStorage.version.current = currentVersion;
await ttStorage.set(migratedStorage);
populateDatabaseVariables(migratedStorage);
console.log("TT - Database migration completed successfully.");
} catch (error) {
console.error("TT - Database migration failed:", error);
await loadDatabase();
}
}
function convertStorage(oldStorage, defaultStorage) {
const newStorage = {};
for (const key in defaultStorage) {
if (!oldStorage) oldStorage = {};
if (!(key in oldStorage)) oldStorage[key] = {};
const defaultValue = defaultStorage[key];
if (typeof defaultValue === "object" && defaultValue !== null) if (defaultValue instanceof DefaultSetting) newStorage[key] = migrateDefaultSetting(oldStorage[key], defaultValue);
else newStorage[key] = convertStorage(oldStorage[key], defaultValue);
else newStorage[key] = oldStorage[key] ?? defaultValue;
}
return newStorage;
}
function migrateDefaultSetting(oldValue, setting) {
if (isValidSettingValue(oldValue, setting)) return oldValue;
if (setting.defaultValue) return typeof setting.defaultValue === "function" ? setting.defaultValue() : setting.defaultValue;
return null;
}
function isValidSettingValue(value, setting) {
if (setting.type === "array") return Array.isArray(value);
return setting.type.split("|").some((type) => type === "empty" && value === "" || typeof value === type);
}
function populateDatabaseVariables(database) {
settings = database.settings;
filters = database.filters;
version = database.version;
api = database.api;
userdata = database.userdata;
torndata = database.torndata;
localdata = database.localdata;
stakeouts = database.stakeouts;
attackHistory = database.attackHistory;
notes = database.notes;
factiondata = database.factiondata;
quick = database.quick;
npcs = database.npcs;
stockdata = database.stockdata;
factionStakeouts = database.factionStakeouts;
notificationHistory = database.notificationHistory;
notifications = database.notifications;
migrations = database.migrations;
ttCache.cache = database.cache;
}
function initializeDatabaseListener() {
RUNTIME_STORAGE.addChangeListener((changes, area) => {
if (area === "local") for (const key in changes) {
switch (key) {
case "settings":
settings = changes.settings.newValue;
break;
case "filters":
filters = changes.filters.newValue;
break;
case "version":
version = changes.version.newValue;
break;
case "userdata":
userdata = changes.userdata.newValue;
break;
case "api":
api = changes.api.newValue;
break;
case "torndata":
torndata = changes.torndata.newValue;
break;
case "stakeouts":
stakeouts = changes.stakeouts.newValue;
break;
case "attackHistory":
attackHistory = changes.attackHistory.newValue;
break;
case "notes":
notes = changes.notes.newValue;
break;
case "factiondata":
factiondata = changes.factiondata.newValue;
break;
case "quick":
quick = changes.quick.newValue;
break;
case "localdata":
localdata = changes.localdata.newValue;
break;
case "cache":
ttCache.cache = changes.cache.newValue;
break;
case "npcs":
npcs = changes.npcs.newValue;
break;
case "stockdata":
stockdata = changes.stockdata.newValue;
break;
case "notificationHistory":
notificationHistory = changes.notificationHistory.newValue;
break;
case "notifications":
notifications = changes.notifications.newValue;
break;
case "factionStakeouts":
factionStakeouts = changes.factionStakeouts.newValue;
break;
}
if (storageListeners[key]) storageListeners[key].forEach((listener) => listener(changes[key].oldValue, changes[key].newValue));
}
});
}
function setLocaldata(data) {
localdata = data;
}
function setFilters(data) {
filters = data;
}
_css(".tt-textbox{border:1px solid var(--input-border-color);background:var(--input-background-color);width:75%;color:var(--input-color);border-radius:5px;padding:0 3px}");
function createTextbox(partialOptions) {
const options = {
description: null,
id: getUUID(),
type: "text",
attributes: {},
style: {},
...partialOptions
};
const textbox = elementBuilder({
type: "input",
class: "tt-textbox",
id: options.id,
attributes: {
...options.attributes,
type: options.type
},
style: options.style
});
let element;
if (options.description) {
element = elementBuilder({
type: "div",
class: "tt-textbox-wrapper"
});
if (typeof options.description === "string") {
element.appendChild(elementBuilder({
type: "label",
text: options.description,
attributes: { for: options.id }
}));
element.appendChild(textbox);
} else {
element.appendChild(elementBuilder({
type: "label",
text: options.description.before,
attributes: { for: options.id }
}));
element.appendChild(textbox);
element.appendChild(elementBuilder({
type: "label",
text: options.description.after,
attributes: { for: options.id }
}));
}
} else element = textbox;
let onChangeCallback;
function setValue(value) {
textbox.value = value;
}
function setNumberValue(value) {
if (value === null || Number.isNaN(parseInt(value.toString())) || !["string", "number"].includes(typeof value)) value = "";
textbox.value = value.toString();
}
function getValue() {
return textbox.value;
}
function onChange(callback) {
onChangeCallback = callback;
textbox.addEventListener("input", _onChangeListener);
}
function dispose() {
if (onChangeCallback) {
textbox.removeEventListener("input", _onChangeListener);
onChangeCallback = void 0;
}
}
function _onChangeListener() {
onChangeCallback();
}
return {
element,
setValue,
setNumberValue,
getValue,
onChange,
dispose
};
}
function hasAPIData() {
const hasKey = !!api?.torn?.key;
const hasError = !!api?.torn?.error && !api.torn.error.includes("Backend error") && api.torn.error !== "Network issues";
const hasUserdata = !!(userdata && Object.keys(userdata).length);
return hasKey && !hasError && hasUserdata;
}
_css(".tt-container.spacer{margin-bottom:10px}.tt-container .title{text-shadow:1px 1px 2px #000000a6;letter-spacing:1px;white-space:nowrap;height:30px;margin:initial;align-items:center;padding-left:10px;font-size:13px;display:flex}.tt-theme,.tt-container.tt-theme-background .title{color:var(--tt-theme-color);background:var(--tt-theme-background)}.tt-container.collapsible .title{cursor:pointer}.tt-container.spacer .title{margin-top:10px}.tt-container .title .text{width:-webkit-fill-available;width:-moz-available}.tt-container .title .icon{text-align:center;min-width:30px;margin:auto;font-size:16px;position:static!important}.tt-container.rounding.always-content .title,.tt-container.rounding .title:not(.collapsed){border-radius:5px 5px 0 0}.tt-container.rounding:not(.always-content) .title.collapsed{border-radius:5px}.tt-container .title.collapsed .icon{transform:rotate(-90deg)}.tt-container:not(.always-content) .title.collapsed+main,.tt-container.always-content .title.collapsed+main .hide-collapse{display:none!important}.tt-container .title .options{flex-direction:row-reverse;align-items:center;width:100%;margin-right:4px;display:flex}.tt-container .title .options>*{align-items:center;margin-right:4px;display:flex}.tt-container .title .options i{font-size:1rem}#sidebarroot .tt-container .title{border-top-right-radius:5px;border-bottom-right-radius:5px;align-items:center;height:22px}body.tt-tablet #sidebarroot .tt-container .title{height:34px}.tt-container>main{margin-top:initial!important}.tt-container:not(.compact)>main{padding:4px 4px 3px}.tt-container.rounding>main{border-radius:0 0 5px 5px}.tt-container>main.background{background-color:var(--default-bg-panel-color)}.tt-container.reset-styles{letter-spacing:0;color:var(--default-color)!important;font-family:Arial,serif!important}.tt-container input[type=checkbox]{accent-color:#6e8820}");
function roundNearest(number, multiple) {
return Math.round(number / multiple) * multiple;
}
function camelCase(text, lowerCamelCase = true) {
return (text.trim().charAt(0)[lowerCamelCase ? "toLowerCase" : "toUpperCase"]() + text.slice(1)).trim().replaceAll(" ", "");
}
function capitalizeText(text, partialOptions = {}) {
if (!{
everyWord: false,
...partialOptions
}.everyWord) return text[0].toUpperCase() + text.slice(1);
return text.trim().split(" ").map((word) => capitalizeText(word)).join(" ").trim();
}
function createContainer(title, partialOptions) {
const options = {
id: camelCase(title),
class: void 0,
showHeader: true,
onlyHeader: false,
collapsible: true,
applyRounding: true,
spacer: false,
contentBackground: true,
allowDragging: false,
flexContainer: false,
compact: false,
alwaysContent: false,
filter: false,
resetStyles: false,
...partialOptions
};
if (options.onlyHeader) options.collapsible = false;
const { container, collapsed } = _createContainer(title, options);
let parentElement;
if ("parentElement" in options) parentElement = options.parentElement;
else if ("nextElement" in options) parentElement = options.nextElement.parentElement;
else if ("previousElement" in options) parentElement = options.previousElement.parentElement;
else parentElement = document.querySelector(".content-wrapper");
if ("nextElement" in options) parentElement.insertBefore(container, options.nextElement);
else if ("previousElement" in options) parentElement.insertBefore(container, options.previousElement.nextSibling);
else parentElement.appendChild(container);
return {
container,
content: container.querySelector(":scope > main"),
options: container.querySelector(".options"),
collapsed
};
function _createContainer(title, options) {
if (document.querySelector(`#${options.id}`)) document.querySelector(`#${options.id}`).remove();
const containerClasses = ["tt-container"];
if (options.collapsible) containerClasses.push("collapsible");
if (options.applyRounding) containerClasses.push("rounding");
if (options.spacer) containerClasses.push("spacer");
if (options.compact) containerClasses.push("compact");
if (options.alwaysContent) containerClasses.push("always-content");
if (options.class) {
let classes;
if (typeof options.class === "string") classes = options.class.split(" ").filter((c) => !!c);
else classes = options.class.filter((c) => !!c);
containerClasses.push(...classes);
}
if (options.filter) containerClasses.push("tt-filter");
if (options.resetStyles) containerClasses.push("reset-styles");
const mainClasses = [];
if (options.contentBackground) mainClasses.push("background");
if (options.flexContainer) mainClasses.push("t-flex");
containerClasses.push("tt-theme-background");
const container = elementBuilder({
type: "div",
class: containerClasses.join(" "),
id: options.id
});
const collapsed = options.onlyHeader || options.collapsible && (options.id in filters.containers ? filters.containers[options.id] : false);
if (options.showHeader) container.appendChild(elementBuilder({
type: "div",
class: ["title", collapsed ? "collapsed" : null],
children: [
elementBuilder({
type: "div",
class: "text",
text: title
}),
elementBuilder({
type: "div",
class: "options"
}),
options.collapsible ? PHFillCaretDown({ class: "icon" }) : null
]
}));
if (!options.onlyHeader) container.appendChild(elementBuilder({
type: "main",
class: mainClasses
}));
if (options.collapsible) container.querySelector(".title").addEventListener("click", async () => {
container.querySelector(".title").classList.toggle("collapsed");
await ttStorage.change({ filters: { containers: { [options.id]: container.querySelector(".title").classList.contains("collapsed") } } });
});
if (options.allowDragging) {
const content = container.querySelector(":scope > main");
content.addEventListener("dragover", (event) => event.preventDefault());
content.addEventListener("drop", (event) => {
if (content.querySelector(".temp.item, .temp.quick-item")) content.querySelector(".temp.item, .temp.quick-item").classList.remove("temp");
event.preventDefault();
event.dataTransfer?.clearData();
});
}
return {
container,
collapsed
};
}
}
function findContainer(title, partialOptions = {}) {
const options = {
id: camelCase(title),
selector: void 0,
...partialOptions
};
if (!options.id) return null;
const container = document.querySelector(`#${options.id}`);
if (!container) return null;
if (options.selector) return container.querySelector(options.selector);
else return container;
}
function removeContainer(title, partialOptions = {}) {
const container = findContainer(title, partialOptions);
if (!container) return;
container.remove();
}
_css(".filter-container+*{margin-top:0!important}.filter-header{flex-direction:row-reverse;margin-bottom:10px;display:flex}.filter-header .statistic span{font-weight:700}.filter-content{flex-wrap:wrap;display:flex}.tt-mobile .filter-content .filter-wrap{flex:1 0 100%}.filter-wrap,.filter-subwrap{flex:1 0 auto;margin-bottom:10px}.filter-wrap .filter-heading{text-align:center;margin-bottom:5px;font-weight:700}.filter-slider{width:70%;margin:auto auto 10px}.filter-slider+.filter-slider-info{text-align:center;width:100%}.filter-wrap select{width:fit-content;margin:auto;display:block}.filter-hidden,.filter-hidden+.tt-user-info{display:none!important}.filter-multi-wrap{width:fit-content;margin:auto}.tt-mobile .filter-multi-wrap{width:80%;display:flex}.tt-mobile .filter-multi-wrap>*{flex:1 0 auto}.tt-checkbox-wrap input{cursor:pointer;height:13px;margin:1px 5px 0;position:relative;top:-2px}.tt-checkbox-wrap *,.tt-input-wrap *{vertical-align:top;display:inline-block}.filter-header .statistic{margin:5px 10px 0 0}.tt-filter{font-family:Arial}.tt-filter .statistics{text-align:right;width:100%;padding-top:5px;transform:translate(-10px)}.tt-filter .content{flex-wrap:wrap;justify-content:space-evenly;width:100%;padding:10px 0;display:flex}body.tt-mobile .tt-filter .content,body.tt-tablet .tt-filter .content{flex-flow:column;justify-content:center;align-items:center}body.tt-mobile .tt-filter .content .tt-checkbox-wrapper,body.tt-tablet .tt-filter .content .tt-checkbox-wrapper{margin:1px 0}.tt-filter .content .tt-yn-checkboxes>div{align-items:center;display:flex}.tt-filter .content .tt-yn-checkboxes label{margin-left:4px}.tt-filter .content>div{text-align:center;padding:0 10px}body.tt-mobile .tt-filter .content>div,body.tt-tablet .tt-filter .content>div{margin-bottom:12px}.tt-filter .content strong{margin-bottom:2px;display:block}.tt-filter .content .tt-slider{padding:0 10px}.tt-filter .content .slider-counter{margin-top:5px}.weaponBonus__section-class{grid-template:\"title title\"\"b1 v1\"\"b2 v2\"/1fr .5fr;gap:4px;display:grid}.weaponBonus__section-class strong{grid-area:title}.tt-filter-enabled-funnel{cursor:pointer;font-size:16px;margin-right:-4px!important}");
_css(".tt-checkbox-wrapper,.tt-checkbox-wrapper label{align-items:center;display:inline-flex}.tt-checkbox-wrapper>*{cursor:pointer}.tt-checkbox-wrapper.reverse-label label{flex-flow:row-reverse}.tt-checkbox-wrapper:not(.reverse-label) input{margin-right:2px}.tt-checkbox-wrapper.reverse-label input{margin-left:2px}");
function createCheckbox(partialOptions = {}) {
const options = {
description: "",
isHTML: false,
reverseLabel: false,
id: getUUID(),
class: "",
...partialOptions
};
const checkbox = elementBuilder({
type: "input",
id: options.id,
attributes: { type: "checkbox" }
});
let label;
if (typeof options.description === "object") label = elementBuilder({
type: "label",
children: [options.description]
});
else label = elementBuilder({
type: "label",
[options.isHTML ? "html" : "text"]: options.description
});
label.insertAdjacentElement("afterbegin", checkbox);
const checkboxWrapper = elementBuilder({
type: "div",
class: `tt-checkbox-wrapper ${options.reverseLabel ? "reverse-label" : ""} ${options.class}`,
children: [label],
events: { click(event) {
event.stopPropagation();
} }
});
let onChangeCallback;
function setChecked(isChecked) {
checkbox.checked = isChecked;
}
function isChecked() {
return checkbox.checked;
}
function onChange(callback) {
onChangeCallback = callback;
checkbox.addEventListener("change", _onChangeListener);
}
function dispose() {
if (onChangeCallback) {
checkbox.removeEventListener("change", _onChangeListener);
onChangeCallback = void 0;
}
}
function _onChangeListener() {
onChangeCallback();
}
return {
element: checkboxWrapper,
setChecked,
isChecked,
onChange,
dispose
};
}
_css(".tt-checkbox-list-wrapper{display:flex}.tt-checkbox-list-wrapper.tt-checkbox-list-column{flex-direction:column}.tt-checkbox-list-wrapper.tt-checkbox-list-row{flex-direction:row}.tt-checkbox-list-wrapper.tt-checkbox-list-row>.tt-checkbox-wrapper:not(:first-child){margin-left:10px}");
function createCheckboxList(partialOptions) {
const options = {
items: [],
orientation: "column",
reverseLabel: false,
useId: false,
...partialOptions
};
let selectedIds = {};
const checkboxes = {};
let selectionChangeCallback;
for (const item of options.items) {
const checkbox = options.useId ? createCheckbox({
description: item.description,
reverseLabel: options.reverseLabel,
id: item.id.toString()
}) : createCheckbox({
description: item.description,
reverseLabel: options.reverseLabel
});
checkbox.onChange(() => {
if (checkbox.isChecked()) selectedIds[item.id] = true;
else delete selectedIds[item.id];
if (selectionChangeCallback) selectionChangeCallback();
});
checkboxes[item.id] = checkbox;
}
const checkboxWrapper = elementBuilder({
type: "div",
class: ["tt-checkbox-list-wrapper", options.orientation === "row" ? "tt-checkbox-list-row" : "tt-checkbox-list-column"].join(" "),
children: Object.values(checkboxes).map((checkbox) => checkbox.element)
});
function setSelections(selectedItemIds) {
selectedIds = selectedItemIds.reduce((object, id) => {
object[id] = true;
return object;
}, {});
for (const id in checkboxes) checkboxes[id].setChecked(selectedIds[id] || false);
}
function getSelections() {
return Object.keys(selectedIds);
}
function onSelectionChange(callback) {
selectionChangeCallback = callback;
}
function dispose() {
Object.values(checkboxes).forEach((checkbox) => checkbox.dispose());
selectionChangeCallback = void 0;
}
return {
element: checkboxWrapper,
setSelections,
getSelections,
onSelectionChange,
dispose
};
}
_css(".tt-multi-select{border-radius:4px;flex-direction:column;max-height:100px;padding:5px;display:flex;overflow:hidden auto}body:not(.dark-mode) .tt-multi-select{background-color:#ddd}body.dark-mode .tt-multi-select{background-color:#444}.tt-multi-select label{cursor:pointer;align-items:center;margin-bottom:4px;display:flex}.tt-multi-select label[disabled]{cursor:not-allowed}.tt-multi-select input{cursor:pointer}.tt-multi-select span{margin-left:6px}");
function createSelect(options) {
let selectedOptionValue = options[0].value;
let shownOptions = options;
let onChangeCallback;
const select = elementBuilder({
type: "select",
children: _createOptionsElements(shownOptions)
});
function updateOptionsList(options, s = select) {
if (options.every((option) => option.value !== selectedOptionValue)) options.unshift(shownOptions.find((option) => option.value === selectedOptionValue));
const newOptions = _createOptionsElements(options);
while (s.firstChild) s.removeChild(s.firstChild);
const documentFragment = document.createDocumentFragment();
newOptions.forEach((newOption) => documentFragment.appendChild(newOption));
s.appendChild(documentFragment);
shownOptions = options;
setSelected(selectedOptionValue);
}
function setSelected(optionValue) {
const index = shownOptions.findIndex((option) => option.value === optionValue);
if (index === -1) return false;
if (shownOptions[index].disabled) return false;
selectedOptionValue = optionValue;
select.selectedIndex = index;
return true;
}
function getSelected() {
return select.value;
}
function onChange(callback) {
onChangeCallback = callback;
select.addEventListener("change", _onChangeListener);
}
function dispose() {
if (onChangeCallback) {
select.removeEventListener("change", _onChangeListener);
onChangeCallback = void 0;
}
}
function _createOptionsElements(optionsLst) {
return optionsLst.map((option) => elementBuilder({
type: "option",
attributes: {
value: option.value,
...option.value === selectedOptionValue ? { selected: "true" } : {},
...option.disabled ? { disabled: "true" } : {}
},
text: option.description
}));
}
function _onChangeListener() {
selectedOptionValue = select.value;
if (onChangeCallback) onChangeCallback();
}
return {
element: select,
updateOptionsList,
setSelected,
getSelected,
onChange,
dispose
};
}
function createMultiSelect(options) {
let selectedValues = Array.isArray(options.defaults) ? options.defaults : [];
let shownOptions = options.select;
let onChangeCallback;
const container = elementBuilder({
type: "div",
class: "tt-multi-select"
});
function renderOptions() {
container.innerHTML = "";
shownOptions.forEach((opt) => {
const wrapper = elementBuilder({ type: "label" });
if (opt.disabled) wrapper.setAttribute("disabled", "true");
const checkbox = elementBuilder({
type: "input",
value: opt.value,
attributes: { type: "checkbox" },
events: { change: () => {
if (checkbox.checked) {
if (!selectedValues.includes(opt.value)) selectedValues.push(opt.value);
} else selectedValues = selectedValues.filter((v) => v !== opt.value);
if (onChangeCallback) onChangeCallback();
} }
});
checkbox.checked = selectedValues.includes(opt.value);
checkbox.disabled = !!opt.disabled;
const text = elementBuilder({
type: "span",
text: opt.description
});
wrapper.appendChild(checkbox);
wrapper.appendChild(text);
container.appendChild(wrapper);
});
}
function setSelected(values) {
selectedValues = Array.isArray(values) ? values : [values];
renderOptions();
}
function getSelected() {
return [...selectedValues];
}
function onChange(callback) {
onChangeCallback = callback;
}
function updateOptionsList(newList) {
shownOptions = newList;
renderOptions();
}
renderOptions();
return {
element: container,
getSelected,
setSelected,
onChange,
updateOptionsList
};
}
_css(".tt-dual-range{user-select:none;background:#ddd;border-radius:50px;width:150px;height:12px;margin:auto;position:relative}.tt-dual-range .highlight{height:12px;width:calc(calc(var(--right) - var(--left)) + 21px);left:var(--left);z-index:1;background-color:#627e0d;border-radius:50px;position:absolute}.tt-dual-range .handle{z-index:2;cursor:grab;float:none;border-radius:50%;width:21px;height:21px;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 3px 6px #0000001a}.tt-dual-range .handle.focus{border:3px solid #888;width:15px;height:15px}body:not(.dark-mode) .tt-dual-range .handle{background-color:#fff}body.dark-mode .tt-dual-range .handle{background-color:#e8e8e8}.tt-dual-range .handle.left{left:var(--left)}.tt-dual-range .handle.right{left:var(--right)}.tt-dual-range .dump{width:0;overflow:hidden}.tt-dual-range .dump *{opacity:0}.tt-slider-wrapper{flex-direction:column;align-items:center;display:flex}.tt-slider-label{margin-top:10px}");
var DualRangeSlider = class {
options;
uuid;
startPos;
slider;
activeHandle;
handles;
moveTouchListener = this.moveTouch.bind(this);
moveListener = this.move.bind(this);
constructor(options = {}) {
this.options = {
min: 0,
max: 100,
step: 1,
valueLow: options.min || 0,
valueHigh: options.max || 100,
...options
};
this.uuid = getUUID();
this.startPos = 0;
this.handles = [];
this._createElement();
}
_createElement() {
this.slider = elementBuilder({
type: "div",
class: "tt-dual-range",
html: `
<label for="handle-left_${this.uuid}" class="handle left"></label>
<span class="highlight"></span>
<label for="handle-right_${this.uuid}" class="handle right"></label>
<div class="dump">
<input id="handle-left_${this.uuid}"/>
<input id="handle-right_${this.uuid}"/>
</div>
`
});
this.handles = findAllElements(".handle", this.slider);
this.handles.forEach((handle) => {
const input = this.slider.querySelector(`#${handle.getAttribute("for")}`);
handle.addEventListener("mousedown", this.startMove.bind(this));
handle.addEventListener("touchstart", this.startMoveTouch.bind(this));
handle.addEventListener("click", () => input.focus());
input.addEventListener("focus", () => handle.classList.add("focus"));
input.addEventListener("blur", () => handle.classList.remove("focus"));
input.addEventListener("keydown", this.moveKeyboard.bind(this));
});
this.updateValue(this.handles[0], this.options.valueLow);
this.updateValue(this.handles[1], this.options.valueHigh);
window.addEventListener("mouseup", this.stopMove.bind(this));
window.addEventListener("touchend", this.stopMove.bind(this));
window.addEventListener("touchcancel", this.stopMove.bind(this));
window.addEventListener("touchleave", this.stopMove.bind(this));
}
startMoveTouch(event) {
const handleRect = event.target.getBoundingClientRect();
this.startPos = event.touches[0].clientX - handleRect.x;
this.activeHandle = event.target;
window.addEventListener("touchmove", this.moveTouchListener);
}
startMove(event) {
this.startPos = event.offsetX;
this.activeHandle = event.target;
window.addEventListener("mousemove", this.moveListener);
}
moveKeyboard(event) {
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
const handle = this.slider.querySelector(`.handle[for="${event.target.id}"]`);
if (!handle) return;
let value = parseInt(handle.dataset.value);
if (event.key === "ArrowLeft") value -= this.options.step;
else if (event.key === "ArrowRight") value += this.options.step;
this.updateValue(handle, value);
}
moveTouch(event) {
this.move({ clientX: event.touches[0].clientX });
}
move(event) {
const parentRect = this.slider.getBoundingClientRect();
const handleRect = this.activeHandle.getBoundingClientRect();
const position = Math.max(Math.min(event.clientX - parentRect.x - this.startPos, parentRect.width - handleRect.width / 2), 0 - handleRect.width / 2);
this.updateValue(this.activeHandle, this.calculateValue((position + handleRect.width / 2) / parentRect.width));
}
calculateValue(percentage) {
return Math.round(percentage * (this.options.max - this.options.min) + this.options.min);
}
updateValue(handle, value) {
value = roundNearest(Math.max(Math.min(value, this.options.max), this.options.min), this.options.step);
handle.dataset.value = value.toString();
this.updateValues();
}
stopMove() {
window.removeEventListener("mousemove", this.moveListener);
window.removeEventListener("touchmove", this.moveTouchListener);
}
updateValues() {
const valueLeft = parseInt(this.handles[0].dataset.value);
const valueRight = parseInt(this.handles[1].dataset.value);
const low = Math.min(valueLeft, valueRight);
const high = Math.max(valueLeft, valueRight);
this.updateHighlight("left", low);
this.updateHighlight("right", high);
this.slider.dataset.low = low.toString();
this.slider.dataset.high = high.toString();
}
updateHighlight(side, value) {
const rangeWidth = this.slider.getBoundingClientRect().width || 150;
const handleWidth = this.handles[0].getBoundingClientRect().width || 21;
const percentage = (value - this.options.min) / (this.options.max - this.options.min);
this.slider.style.setProperty(`--${side}`, `${percentage * rangeWidth - handleWidth / 2}px`);
}
};
var LINKS = {
auction: "https://www.torn.com/amarket.php",
bank: "https://www.torn.com/bank.php",
bazaar: "https://www.torn.com/bazaar.php",
bounties: "https://www.torn.com/bounties.php#!p=main",
chain: "https://www.torn.com/factions.php?step=your#/war/chain",
church: "https://www.torn.com/church.php",
committee: "https://www.torn.com/committee.php",
companies: "https://www.torn.com/companies.php",
companyEmployees: "https://www.torn.com/companies.php#/option=employees",
crimes: "https://www.torn.com/crimes.php",
donator: "https://www.torn.com/donator.php",
education: "https://www.torn.com/page.php?sid=education",
events: "https://www.torn.com/events.php#/step=all",
faction: "https://www.torn.com/factions.php",
faction__ranked_war: "https://www.torn.com/factions.php?step=your&type=1#/war/rank",
faction_oc: "https://www.torn.com/factions.php?step=your#/tab=crimes",
gym: "https://www.torn.com/gym.php",
home: "https://www.torn.com/index.php",
homepage: "https://www.torn.com/index.php",
hospital: "https://www.torn.com/hospitalview.php",
itemmarket: "https://www.torn.com/page.php?sid=ItemMarket",
items: "https://www.torn.com/item.php",
items_booster: "https://www.torn.com/item.php#boosters-items",
items_candy: "https://www.torn.com/item.php#candy-items",
items_drug: "https://www.torn.com/item.php#drugs-items",
items_medical: "https://www.torn.com/item.php#medical-items",
jailview: "https://www.torn.com/jailview.php",
jobs: "https://www.torn.com/companies.php",
loan: "https://www.torn.com/loan.php",
messages: "https://www.torn.com/messages.php",
missions: "https://www.torn.com/loader.php?sid=missions",
organizedCrimes: "https://www.torn.com/factions.php?step=your#/tab=crimes",
pc: "https://www.torn.com/pc.php",
points: "https://www.torn.com/page.php?sid=points",
pointsmarket: "https://www.torn.com/pmarket.php",
properties: "https://www.torn.com/properties.php",
property_upkeep: "https://www.torn.com/properties.php#/p=options&tab=upkeep",
property_vault: "https://www.torn.com/properties.php#/p=options&tab=vault",
raceway: "https://www.torn.com/page.php?sid=racing",
staff: "https://www.torn.com/staff.php",
stocks: "https://www.torn.com/page.php?sid=stocks",
trade: "https://www.torn.com/trade.php",
travelagency: "https://www.torn.com/page.php?sid=travel"
};
LINKS.donator, LINKS.donator, LINKS.staff, LINKS.committee, LINKS.church, LINKS.jobs, LINKS.jobs, LINKS.jobs, LINKS.jobs, LINKS.jobs, LINKS.jobs, LINKS.companies, LINKS.companies, LINKS.companies, LINKS.faction, LINKS.faction, LINKS.faction, LINKS.faction, LINKS.faction, LINKS.education, LINKS.education, LINKS.bank, LINKS.bank, LINKS.travelagency, LINKS.property_vault, LINKS.loan, LINKS.auction, LINKS.bazaar, LINKS.itemmarket, LINKS.pointsmarket, LINKS.stocks, LINKS.stocks, LINKS.trade, LINKS.homepage, LINKS.raceway, LINKS.raceway, LINKS.faction_oc, LINKS.faction_oc, LINKS.faction_oc, LINKS.faction_oc, LINKS.bounties, LINKS.bank, LINKS.auction, LINKS.auction, LINKS.hospital, LINKS.hospital, LINKS.hospital, LINKS.jailview, LINKS.hospital, LINKS.items_booster, LINKS.items_booster, LINKS.items_booster, LINKS.items_booster, LINKS.items_booster, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.items_drug, LINKS.items_drug, LINKS.items_drug, LINKS.items_drug, LINKS.items_drug, LINKS.travelagency, LINKS.travelagency, LINKS.travelagency, LINKS.travelagency, LINKS.travelagency, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.items_medical, LINKS.property_upkeep, LINKS.property_upkeep, LINKS.property_upkeep;
[
{
id: 1,
reason: "Admin"
},
{
id: 4,
reason: "NPC"
},
{
id: 7,
reason: "NPC"
},
{
id: 9,
reason: "NPC"
},
{
id: 10,
reason: "NPC"
},
{
id: 15,
reason: "NPC"
},
{
id: 17,
reason: "NPC"
},
{
id: 19,
reason: "NPC"
},
{
id: 20,
reason: "NPC"
},
{
id: 21,
reason: "NPC"
}
].map(({ id }) => id);
var SPECIAL_FILTER_ICONS = {
traveling: ["icon71"],
isFedded: ["icon70"],
fedded: ["icon70"],
newPlayer: ["icon72"],
onWall: ["icon75", "icon76"],
inCompany: [
"icon21",
"icon22",
"icon23",
"icon24",
"icon25",
"icon26",
"icon27",
"icon73",
"icon83"
],
inFaction: [
"icon9",
"icon74",
"icon81"
],
isDonator: ["icon3", "icon4"],
inHospital: ["icon15"],
inJail: ["icon16"],
fallen: ["icon77"],
earlyDischarge: ["icon82"],
isRecruit: ["icon81"],
hasBounties: ["icon13"],
bazaarOpen: ["icon35"]
};
function is2FACheckPage() {
return !!document.querySelector(".content-wrapper.logged-out .two-factor-auth-container");
}
function getPageStatus() {
const infoMessage = document.querySelector(".content-wrapper .info-msg-cont");
if (infoMessage?.classList.contains("red")) {
if (infoMessage.textContent.includes("items in your inventory")) return { access: true };
else if (findParent(infoMessage, { class: "no-parcel-wrap" })?.style?.display === "none") return { access: true };
return {
access: false,
message: infoMessage.textContent
};
}
if (document.querySelector(".captcha")) return {
access: false,
message: "Captcha required"
};
else if (document.querySelector(".dirty-bomb")) return {
access: false,
message: "Dirty bomb screen"
};
else if (is2FACheckPage()) return {
access: false,
message: "2 Factor Authentication"
};
return { access: true };
}
var RANK_TRIGGERS = {
level: [
2,
6,
11,
26,
31,
50,
71,
100
],
crimes: [
100,
5e3,
1e4,
2e4,
3e4,
5e4
],
networth: [
5e6,
5e7,
5e8,
5e9,
5e10
],
stats: [
"under 2k",
"2k - 25k",
"20k - 250k",
"200k - 2.5m",
"2m - 25m",
"20m - 250m",
"over 200m"
]
};
var HOSPITALIZATION_REASONS = {
attackedBy: "Attacked by",
muggedBy: "Mugged by",
hospitalizedBy: "Hospitalized by",
other: [
"Attacked by",
"Mugged by",
"Hospitalized by"
]
};
[...hasAPIData() && userdata.faction ? [{
value: userdata.faction.tag,
description: userdata.faction.tag
}] : []];
function createFilterSection(options) {
if ("type" in options) {
if (options.type === "Activity") return createFilterSection({
title: "Activity",
checkboxes: [
{
id: "online",
description: "Online"
},
{
id: "idle",
description: "Idle"
},
{
id: "offline",
description: "Offline"
}
],
defaults: options.defaults,
callback: options.callback
});
else if (options.type === "HideRaces") return createFilterSection({
title: "Hide Races",
checkboxes: [
{
id: "full",
description: "Full"
},
{
id: "protected",
description: "Protected"
},
{
id: "incompatible",
description: "Incompatible"
},
{
id: "paid",
description: "With Fee"
}
],
defaults: options.defaults,
callback: options.callback
});
else if (options.type === "LevelAll") return createFilterSection({
title: "Level Filter",
noTitle: true,
slider: {
min: 0,
max: 100,
step: 1,
valueLow: options.typeData.valueLow,
valueHigh: options.typeData.valueHigh
},
callback: options.callback
});
else if (options.type === "LevelPlayer") return createFilterSection({
title: "Level Filter",
noTitle: true,
slider: {
min: 1,
max: 100,
step: 1,
valueLow: options.typeData.valueLow,
valueHigh: options.typeData.valueHigh
},
callback: options.callback
});
return null;
}
const ccTitle = `${camelCase(options.title)}__section-class`;
const section = elementBuilder({
type: "div",
class: ccTitle,
style: options.style
});
if (!options.noTitle) section.appendChild(elementBuilder({
type: "strong",
text: options.title
}));
if (isTextOptions(options)) {
const textbox = createTextbox({ type: typeof options.text === "string" ? options.text : "text" });
textbox.setValue(options.default);
textbox.onChange(options.callback);
section.appendChild(textbox.element);
return {
element: section,
getValue: () => textbox.getValue()
};
}
if (isCheckboxOptions(options)) {
const checkbox = createCheckbox({ description: options.checkbox });
checkbox.onChange(options.callback);
checkbox.setChecked(options.default);
section.appendChild(checkbox.element);
return {
element: section,
isChecked: (content) => content.querySelector(`.${ccTitle} input`)?.checked ?? false
};
}
if (isCheckboxesOptions(options)) {
const checkboxes = createCheckboxList({
items: options.checkboxes,
orientation: options.orientation ?? "column",
useId: true
});
checkboxes.onSelectionChange(options.callback);
checkboxes.setSelections(Array.isArray(options.defaults) ? options.defaults : []);
section.appendChild(checkboxes.element);
return {
element: section,
getSelections: (content) => findAllElements(`.${ccTitle} input:checked`, content).map((x) => x.getAttribute("id")?.toLowerCase().trim() ?? "")
};
}
if (isYNCheckboxesOptions(options)) {
options.ynCheckboxes.forEach((key) => {
const ccKey = camelCase(key);
const checkboxesDiv = elementBuilder({
type: "div",
class: ccKey
});
const yCheckbox = createCheckbox({
description: "Y:",
reverseLabel: true
});
const nCheckbox = createCheckbox({
description: "N:",
reverseLabel: true
});
const value = options.defaults[ccKey];
if (value === "yes" || value === "both") yCheckbox.setChecked(true);
if (value === "no" || value === "both") nCheckbox.setChecked(true);
yCheckbox.onChange(options.callback);
nCheckbox.onChange(options.callback);
checkboxesDiv.appendChild(yCheckbox.element);
checkboxesDiv.appendChild(nCheckbox.element);
checkboxesDiv.appendChild(elementBuilder({
type: "label",
text: key
}));
section.appendChild(checkboxesDiv);
});
section.classList.add("tt-yn-checkboxes");
return {
element: section,
getSelections
};
function getSelections(content) {
const selections = {};
for (const specialDiv of findAllElements(`.${ccTitle} > div`, content)) {
const checkboxes = findAllElements("input", specialDiv);
const yChecked = checkboxes[0].checked;
const nChecked = checkboxes[1].checked;
const key = specialDiv.className.split("__")[0];
if (yChecked && nChecked) selections[key] = "both";
else if (yChecked) selections[key] = "yes";
else if (nChecked) selections[key] = "no";
else selections[key] = "none";
}
return selections;
}
}
if (isMultiSelectOptions(options)) {
const multiSelect = createMultiSelect({
select: options.select.filter((opt) => opt.value !== ""),
defaults: Array.isArray(options.defaults) ? options.defaults : []
});
multiSelect.onChange(options.callback);
section.appendChild(multiSelect.element);
return {
element: section,
getSelected: () => multiSelect.getSelected(),
updateOptions: (newOptions) => multiSelect.updateOptionsList(newOptions)
};
}
if (isSelectOptions(options)) {
const select = createSelect(options.select);
select.setSelected(options.default ?? "");
select.onChange(options.callback);
section.appendChild(select.element);
return {
element: section,
getSelected: (content) => content.querySelector(`.${ccTitle} select`)?.value ?? "",
updateOptions: (newOptions, content) => select.updateOptionsList(newOptions, content.querySelector(`.${ccTitle} select`))
};
}
if (isSliderOptions(options)) {
const rangeSlider = new DualRangeSlider(options.slider);
section.appendChild(rangeSlider.slider);
section.appendChild(elementBuilder({
type: "div",
class: "slider-counter",
text: ""
}));
section.classList.add("tt-slider");
new MutationObserver(options.callback).observe(rangeSlider.slider, { attributes: true });
return {
element: section,
getStartEnd,
updateCounter
};
function getStartEnd(content) {
const rangeElement = content.querySelector(`.${ccTitle} .tt-dual-range`);
if (!rangeElement) return {
start: options.slider.valueLow,
end: options.slider.valueHigh
};
return {
start: rangeElement.dataset.low,
end: rangeElement.dataset.high
};
}
function updateCounter(string, content) {
const counter = content.querySelector(`.${ccTitle} .slider-counter`);
if (!counter) return;
counter.textContent = string;
}
}
return { element: section };
}
function isTextOptions(options) {
return "text" in options;
}
function isCheckboxOptions(options) {
return "checkbox" in options;
}
function isCheckboxesOptions(options) {
return "checkboxes" in options;
}
function isYNCheckboxesOptions(options) {
return "ynCheckboxes" in options;
}
function isMultiSelectOptions(options) {
return "select" in options && "multiSelect" in options;
}
function isSelectOptions(options) {
return "select" in options && !("type" in options);
}
function isSliderOptions(options) {
return "slider" in options;
}
function createStatistics(name = "entries", addBrackets = false, lowercase = false) {
const statistics = elementBuilder({
type: "div",
class: "statistics",
children: [
`${addBrackets ? "(" : ""}${lowercase ? "s" : "S"}howing `,
elementBuilder({
type: "strong",
class: "stat-count",
text: "X"
}),
" of ",
elementBuilder({
type: "strong",
class: "stat-total",
text: "Y"
}),
` ${name}${addBrackets ? ")" : "."}`
]
});
function updateStatistics(count, total, content) {
content.querySelector(".statistics .stat-count").textContent = count.toString();
content.querySelector(".statistics .stat-total").textContent = total.toString();
}
return {
element: statistics,
updateStatistics
};
}
function getSpecialIcons(li) {
return findAllElements(":scope li[id*='icon']", li).map((x) => x.id.split("_")[0]);
}
function createFilterEnabledFunnel(partialOptions = {}) {
const options = {
id: getUUID(),
class: "",
...partialOptions
};
const iconWrapper = elementBuilder({
type: "div",
class: ["tt-filter-enabled-funnel", options.class],
attributes: {
id: options.id,
title: "Disable this filter."
}
});
let onChangeCallback;
let enabled = false;
function updateIcon() {
iconWrapper.innerHTML = "";
iconWrapper.appendChild(enabled ? PHFillFunnel() : PHFillFunnelX());
iconWrapper.setAttribute("title", enabled ? "Disable this filter." : "Enable this filter.");
}
function setEnabled(isEnabled) {
enabled = isEnabled;
updateIcon();
}
function isEnabled() {
return enabled;
}
function onChange(callback) {
onChangeCallback = callback;
iconWrapper.addEventListener("click", _onClickListener);
}
function dispose() {
if (onChangeCallback) {
iconWrapper.removeEventListener("click", _onClickListener);
onChangeCallback = void 0;
}
}
function _onClickListener(event) {
event.stopPropagation();
enabled = !enabled;
updateIcon();
onChangeCallback(enabled);
}
updateIcon();
return {
element: iconWrapper,
setEnabled,
isEnabled,
onChange,
dispose
};
}
var Feature = class {
name;
scope;
executionTiming;
constructor(name, scope, executionTiming = "CONTENT_LOADED") {
this.name = name;
this.scope = scope;
this.executionTiming = executionTiming;
}
precondition() {
return true;
}
initialise() {}
execute(liveReload) {}
cleanup() {}
storageKeys() {
return [];
}
requirements() {
return true;
}
shouldTriggerEvents() {
return false;
}
shouldLiveReload() {
return false;
}
requiresScreenInformation() {
return true;
}
};
_css(".tt-stats-estimate-profile{float:right;color:var(--tt-color-light-green);padding-right:10px}.tt-stats-estimate{text-align:center;border-bottom:1px solid var(--default-panel-divider-outer-side-color);justify-content:center;align-items:center;min-height:11px;padding:5px;display:flex}body.tt-elimination .tt-stats-estimate{background-color:var(--elimination-team-list-background-color)}.tt-stats-estimate .tt-loading-placeholder{padding:0}.tt-stats-estimate-attacks-wrapper>*{margin-left:10px!important}.tt-mobile .tt-stats-estimate-attacks-wrapper>:first-child,.tt-mobile .tt-stats-estimate-attacks-wrapper>:nth-child(2){margin-left:0!important}.tt-mobile .tt-stats-estimate-attacks-wrapper>:nth-child(3){margin-left:5px!important}.tt-mobile .tt-stats-estimate-attacks-wrapper>:nth-child(4){margin-left:12px!important}.bounties-list .tt-stats-estimate{background:var(--newspaper-divider-horizontal-older-line-url) left top repeat-x}body.dark-mode .bounties-list .tt-stats-estimate{color:#aaa}.faction-war .tt-stats-estimate{border-bottom:unset}.tt-transparent-estimates .tt-stats-estimate{background-color:var(--default-bg-panel-color)}body[data-page=enemies] .tt-stats-estimate{background:var(--default-bg-panel-color)}");
var ESTIMATE_INSTANCES = {};
function hasStatsEstimatesLoaded(name) {
return name in ESTIMATE_INSTANCES && !ESTIMATE_INSTANCES[name].running;
}
function initialiseFilters() {
CUSTOM_LISTENERS[EVENT_CHANNELS.USERLIST_SWITCH_PAGE].push(async () => {
if (!FEATURE_MANAGER.isEnabled(UserlistFilterFeature)) return;
await filtering();
});
CUSTOM_LISTENERS[EVENT_CHANNELS.STATS_ESTIMATED].push(({ row }) => {
if (!FEATURE_MANAGER.isEnabled(UserlistFilterFeature)) return;
const content = findContainer("Userlist Filter", { selector: "main" });
const statsEstimates = localFilters["Stats Estimate"]?.getSelections(content);
if (!statsEstimates?.length) return;
filterRow(row, { statsEstimates }, true);
});
CUSTOM_LISTENERS[EVENT_CHANNELS.FF_SCOUTER_GAUGE].push(async () => {
if (!FEATURE_MANAGER.isEnabled(UserlistFilterFeature)) return;
if (!localFilters["FF Score Max"]?.getValue() && !localFilters["FF Score Min"]?.getValue()) return;
await filtering();
});
}
var localFilters = {};
async function addFilters() {
await requireElement(".userlist-wrapper .user-info-list-wrap");
const { content, options } = createContainer("Userlist Filter", {
class: "mt10",
nextElement: document.querySelector(".users-list-title"),
compact: true,
filter: true
});
const statistics = createStatistics("players");
content.appendChild(statistics.element);
localFilters["Statistics"] = { updateStatistics: statistics.updateStatistics };
const filterContent = elementBuilder({
type: "div",
class: "content"
});
const activityFilter = createFilterSection({
type: "Activity",
defaults: filters.userlist.activity,
callback: () => filtering()
});
filterContent.appendChild(activityFilter.element);
localFilters["Activity"] = { getSelections: activityFilter.getSelections };
const specialFilter = createFilterSection({
title: "Special",
ynCheckboxes: [
"Fedded",
"Fallen",
"Traveling",
"New Player",
"On Wall",
"In Company",
"In Faction",
"Is Donator",
"In Hospital",
"In Jail",
"Early Discharge",
"Has Bounties",
"Bazaar Open"
],
defaults: filters.userlist.special,
callback: () => filtering()
});
filterContent.appendChild(specialFilter.element);
localFilters["Special"] = { getSelections: specialFilter.getSelections };
const hospReasonFilter = createFilterSection({
title: "Hosp Reason",
ynCheckboxes: [
"Attacked By",
"Mugged By",
"Hospitalized By",
"Other"
],
defaults: filters.userlist.hospReason,
callback: () => filtering()
});
filterContent.appendChild(hospReasonFilter.element);
localFilters["Hosp Reason"] = { getSelections: hospReasonFilter.getSelections };
const levelFilter = createFilterSection({
type: "LevelAll",
typeData: {
valueLow: filters.userlist.levelStart,
valueHigh: filters.userlist.levelEnd
},
callback: () => filtering()
});
filterContent.appendChild(levelFilter.element);
localFilters["Level Filter"] = {
getStartEnd: levelFilter.getStartEnd,
updateCounter: levelFilter.updateCounter
};
if (settings.scripts.statsEstimate.global && settings.scripts.statsEstimate.userlist && hasAPIData()) {
const estimatesFilter = createFilterSection({
title: "Stats Estimates",
checkboxes: [
{
id: "none",
description: "none"
},
...RANK_TRIGGERS.stats.map((trigger) => ({
id: trigger,
description: trigger
})),
{
id: "n/a",
description: "N/A"
}
],
defaults: filters.userlist.estimates,
callback: () => filtering()
});
filterContent.appendChild(estimatesFilter.element);
localFilters["Stats Estimate"] = { getSelections: estimatesFilter.getSelections };
}
if (settings.scripts.ffScouter.gauge && settings.external.ffScouter && hasAPIData()) {
const ffScoreFilterMin = createFilterSection({
title: "FF Score Min",
text: "number",
default: filters.userlist.ffScoreMin?.toString(),
callback: () => filtering()
});
ffScoreFilterMin.element.querySelector("input").step = .1;
filterContent.appendChild(ffScoreFilterMin.element);
localFilters["FF Score Min"] = { getValue: ffScoreFilterMin.getValue };
const ffScoreFilterMax = createTextbox({ type: "number" });
ffScoreFilterMax.setValue(filters.userlist.ffScoreMax?.toString());
ffScoreFilterMax.onChange(filtering);
ffScoreFilterMax.element.step = "0.1";
ffScoreFilterMin.element.appendChild(elementBuilder({
type: "strong",
text: "FF Score Max"
}));
ffScoreFilterMin.element.append(ffScoreFilterMax.element);
localFilters["FF Score Max"] = { getValue: ffScoreFilterMax.getValue };
}
content.appendChild(filterContent);
const enabledFunnel = createFilterEnabledFunnel();
enabledFunnel.onChange(() => filtering());
enabledFunnel.setEnabled(filters.userlist.enabled);
options.appendChild(enabledFunnel.element);
localFilters.enabled = { isEnabled: enabledFunnel.isEnabled };
await filtering();
}
async function filtering() {
await requireElement(".user-info-list-wrap");
await requireCondition(() => {
return !document.querySelector(".user-info-list-wrap .ajax-placeholder, .user-info-list-wrap .ajax-preloader") || document.evaluate("//*[contains(@class, 'userlist-wrapper')][.//*[contains(text(), 'No users found')]]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}, {});
const content = findContainer("Userlist Filter", { selector: "main" });
const activity = localFilters["Activity"].getSelections(content);
const special = localFilters["Special"].getSelections(content);
const hospReason = localFilters["Hosp Reason"].getSelections(content);
const levels = localFilters["Level Filter"].getStartEnd(content);
const levelStart = parseInt(levels.start);
const levelEnd = parseInt(levels.end);
const statsEstimates = hasStatsEstimatesLoaded("Userlist") && settings.scripts.statsEstimate.global && settings.scripts.statsEstimate.userlist && hasAPIData() ? localFilters["Stats Estimate"]?.getSelections(content) : void 0;
localFilters["Level Filter"].updateCounter(`Level ${levelStart} - ${levelEnd}`, content);
const ffScoreMin = parseFloat(localFilters["FF Score Min"]?.getValue()) || null;
const ffScoreMax = parseFloat(localFilters["FF Score Max"]?.getValue()) || null;
await ttStorage.change({ filters: { userlist: {
enabled: localFilters.enabled.isEnabled(),
activity,
levelStart,
levelEnd,
special,
hospReason,
estimates: statsEstimates ?? filters.userlist.estimates,
ffScoreMax,
ffScoreMin
} } });
if (!localFilters.enabled.isEnabled()) {
findAllElements(".user-info-list-wrap > li.tt-hidden").forEach((row) => {
row.classList.remove("tt-hidden");
delete row.dataset.hideReason;
});
localFilters["Statistics"].updateStatistics(findAllElements(".user-info-list-wrap > li:not(.tt-hidden)").length, findAllElements(".user-info-list-wrap > li").length, content);
return;
}
for (const li of findAllElements(".user-info-list-wrap > li")) filterRow(li, {
activity,
special,
hospReason,
level: {
start: levelStart,
end: levelEnd
},
statsEstimates,
ffScoreMin,
ffScoreMax
}, false);
triggerCustomListener(EVENT_CHANNELS.FILTER_APPLIED, { filter: "Userlist Filter" });
localFilters["Statistics"].updateStatistics(findAllElements(".user-info-list-wrap > li:not(.tt-hidden)").length, findAllElements(".user-info-list-wrap > li").length, content);
}
function filterRow(row, filters, individual) {
if (row.querySelector(".ajax-preloader")) return;
if (filters.activity) {
if (filters.activity.length && !filters.activity.some((x) => x.trim() === row.querySelector("#iconTray li").getAttribute("title").match(/(?<=<b>).*(?=<\/b>)/g)[0].toLowerCase().trim())) {
hide("activity");
return;
}
}
if (filters.special) {
const match = Object.entries(filters.special).filter(([, value]) => value !== "both" && value !== "none").find(([key, value]) => {
const icons = getSpecialIcons(row);
const filterIcons = SPECIAL_FILTER_ICONS[key];
return value === "yes" && !icons.some((foundIcon) => filterIcons.includes(foundIcon)) || value === "no" && icons.some((foundIcon) => filterIcons.includes(foundIcon));
});
if (match) {
hide(`special-${match[0]}`);
return;
}
}
if (filters.hospReason) {
const match = Object.entries(filters.hospReason).filter(([, value]) => value !== "both" && value !== "none").find(([key, value]) => {
const isHospitalized = row.querySelector("li[title*='Hospital']");
if (isHospitalized) {
const hospitalizationReason = isHospitalized.getAttribute("title").split("<br>")[1];
if (key === "other") return value === "yes" && HOSPITALIZATION_REASONS[key].some((reason) => hospitalizationReason.match(reason)) || value === "no" && !HOSPITALIZATION_REASONS[key].some((reason) => hospitalizationReason.match(reason));
else return value === "yes" && !hospitalizationReason.includes(HOSPITALIZATION_REASONS[key]) || value === "no" && hospitalizationReason.includes(HOSPITALIZATION_REASONS[key]);
}
return false;
});
if (match) {
hide(`hospReason-${match[0]}`);
return;
}
}
if (filters.level) {
const level = parseInt(row.querySelector(".level .value").textContent);
if (filters.level.start && level < filters.level.start || filters.level.end !== 100 && level > filters.level.end) {
hide("level");
return;
}
}
if (filters.statsEstimates) {
if (filters.statsEstimates.length) {
const estimate = row.dataset.estimate?.toLowerCase() ?? "none";
if ((estimate !== "none" || !row.classList.contains("tt-estimated")) && !filters.statsEstimates.includes(estimate)) {
hide("stats-estimate");
return;
}
}
}
if (filters.ffScoreMax || filters.ffScoreMin) try {
const gauge = row.querySelector(".tt-ff-scouter-indicator.indicator-lines");
if (gauge) {
const ffFloat = parseFloat(gauge.getAttribute("data-ff-scout"));
if (!Number.isNaN(ffFloat)) {
if (filters.ffScoreMax && !Number.isNaN(filters.ffScoreMax) && ffFloat > filters.ffScoreMax) {
hide("ff-score");
return;
}
if (filters.ffScoreMin && !Number.isNaN(filters.ffScoreMin) && ffFloat < filters.ffScoreMin) {
hide("ff-score");
return;
}
}
}
} catch (error) {
console.error("TT - Failed to filter row by FF Score.", error);
}
show();
function show() {
row.classList.remove("tt-hidden");
delete row.dataset.hideReason;
if (row.nextElementSibling?.classList.contains("tt-stats-estimate")) row.nextElementSibling.classList.remove("tt-hidden");
if (individual) {
const content = findContainer("Userlist Filter", { selector: "main" });
localFilters["Statistics"].updateStatistics(findAllElements(".user-info-list-wrap > li:not(.tt-hidden)").length, findAllElements(".user-info-list-wrap > li").length, content);
}
}
function hide(reason) {
row.classList.add("tt-hidden");
row.dataset.hideReason = reason;
if (row.nextElementSibling?.classList.contains("tt-stats-estimate")) row.nextElementSibling.classList.add("tt-hidden");
if (individual) {
const content = findContainer("Userlist Filter", { selector: "main" });
localFilters["Statistics"].updateStatistics(findAllElements(".user-info-list-wrap > li:not(.tt-hidden)").length, findAllElements(".user-info-list-wrap > li").length, content);
}
}
}
function removeFilters() {
removeContainer("Userlist Filter");
findAllElements(".user-info-list-wrap > li.tt-hidden").forEach((x) => x.classList.remove("tt-hidden"));
}
var UserlistFilterFeature = class extends Feature {
constructor() {
super("Userlist Filter", "userlist");
}
precondition() {
return getPageStatus().access;
}
isEnabled() {
return settings.pages.userlist.filter;
}
initialise() {
initialiseFilters();
}
async execute() {
await addFilters();
}
cleanup() {
removeFilters();
}
storageKeys() {
return ["settings.pages.userlist.filter"];
}
};
var RequestListenerInjector = class {
injectListeners;
id;
constructor(injectListeners) {
this.injectListeners = injectListeners;
this.id = capitalizeText(injectListeners.name);
}
inject() {
if (this.isInjected()) return;
this.injectListeners();
this.setInjected();
}
isInjected() {
return document.documentElement.dataset[`tt${this.id}`] === "true";
}
setInjected() {
document.documentElement.dataset[`tt${this.id}`] = "true";
}
};
function injectFetchListeners() {
const oldFetch = SCRIPT_INJECTOR.getWindow().fetch;
SCRIPT_INJECTOR.getWindow().fetch = (input, init) => new Promise((resolve, reject) => {
oldFetch(input, init).then(async (response) => {
const page = response.url.substring(response.url.indexOf("torn.com/") + 9, response.url.indexOf(".php"));
let json = {};
try {
json = await response.clone().json();
} catch {}
let body = null;
if (init) {
body = init.body;
if (body !== null && typeof body === "object" && body?.constructor?.name === "FormData") {
const newBody = {};
for (const [key, value] of [...body]) if (isIntNumber(value)) newBody[key] = parseFloat(value);
else newBody[key] = value;
body = newBody;
}
}
const url = response.url || input;
const detail = {
page,
json,
text: await response.clone().text(),
fetch: {
url,
body,
status: response.status
}
};
window.dispatchEvent(new CustomEvent("tt-fetch", { detail }));
resolve(response);
}).catch((error) => {
reject(error);
});
});
}
function injectXhrListeners() {
const oldXHROpen = window.XMLHttpRequest.prototype.open;
const oldXHRSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function(method, url) {
let params = this["params"] ?? {};
if ("xhrOpenAdjustments" in window && typeof window.xhrOpenAdjustments === "object") for (const key in window.xhrOpenAdjustments) {
if (typeof window.xhrOpenAdjustments[key] !== "function") continue;
const adjustments = window.xhrOpenAdjustments[key]({ ...this }, method, url);
method = adjustments.method;
url = adjustments.url;
params = {
...params,
...adjustments.params || {}
};
}
this["method"] = method;
this["url"] = url;
this["params"] = params;
this.addEventListener("readystatechange", function() {
if (this.readyState > 3 && this.status === 200) {
const page = this.responseURL.substring(this.responseURL.indexOf("torn.com/") + 9, this.responseURL.indexOf(".php"));
let json, uri;
if (isJsonString(this.response)) json = JSON.parse(this.response);
else uri = getUrlParams(this.responseURL);
let text;
if (this.responseType === "" || this.responseType === "text") text = this.responseText;
window.dispatchEvent(new CustomEvent("tt-xhr", { detail: {
page,
json,
uri,
xhr: {
requestBody: this["requestBody"],
response: this.response,
responseType: this.responseType,
responseText: text,
responseURL: this.responseURL
}
} }));
}
});
arguments[0] = method;
arguments[1] = url;
return oldXHROpen.apply(this, arguments);
};
window.XMLHttpRequest.prototype.send = function(body) {
this["params"] = this["params"] ?? {};
if ("xhrSendAdjustments" in window && typeof window.xhrSendAdjustments === "object") for (const key in window.xhrSendAdjustments) {
if (typeof window.xhrSendAdjustments[key] !== "function") continue;
body = window.xhrSendAdjustments[key]({ ...this }, body);
}
this["requestBody"] = body;
arguments[0] = body;
return oldXHRSend.apply(this, arguments);
};
}
function getUrlParams(url, prop) {
if (!url) url = location.href;
const definitions = decodeURIComponent(url.slice(url.indexOf("?") + 1)).split("&");
const params = {};
definitions.forEach((val) => {
const parts = val.split("=", 2);
params[parts[0]] = parts[1];
});
return prop && prop in params ? params[prop] : params;
}
function isJsonString(str) {
if (!str || str === "") return false;
try {
JSON.parse(str);
} catch {
return false;
}
return true;
}
var TornToolsStorage = class {
change(object) {
return new Promise(async (resolve) => {
const keys = Object.keys(object);
for (const key of keys) {
const data = this.recursive(await this.get(key), object[key]);
await this.set({ [key]: data });
}
resolve();
});
}
recursive(parent, toChange) {
for (const key in toChange) if (parent && typeof parent === "object" && !Array.isArray(parent[key]) && key in parent && typeof toChange[key] === "object" && !Array.isArray(toChange[key]) && toChange[key] !== null) parent[key] = this.recursive(parent[key], toChange[key]);
else if (parent && typeof parent === "object") {
const value = toChange[key];
parent[key] = Array.isArray(value) ? Array.from(value) : value;
} else parent = { [key]: toChange[key] };
return parent;
}
};
var TTScriptStorage = class extends TornToolsStorage {
prefix;
constructor(prefix) {
super();
this.prefix = prefix;
}
storageKey(key) {
return key === "cache" ? key : `${this.prefix}_${key}`;
}
async get(key) {
if (Array.isArray(key)) return await Promise.all(key.map((k) => this.storageKey(k)).map((k) => GM.getValue(k)));
else if (key) return await GM.getValue(this.storageKey(key));
else {
const storageKeys = Object.keys(DEFAULT_STORAGE);
const storageValues = await this.get(storageKeys);
return storageKeys.reduce((total, k, i) => {
total[k] = storageValues[i];
return total;
}, {});
}
}
async set(object) {
await Promise.all(Object.entries(object).map(([key, value]) => {
UserscriptRuntimeStorage.callback({ [key]: {
newValue: value,
oldValue: null
} }, "local");
return GM.setValue(this.storageKey(key), value);
}));
}
remove(_key) {
throw new Error("Method not implemented.");
}
clear() {
throw new Error("Method not implemented.");
}
reset(_key) {
throw new Error("Method not implemented.");
}
getSize() {
throw new Error("Method not implemented.");
}
};
_css(".tt-hidden{display:none!important}.tt-black-overlay{z-index:100;background-color:#00000059;width:100%;height:100%;position:fixed;top:0;left:0}.no-margin{margin:0}.tt-delimiter{border-top:#ccc;border-left:none;border-right:none;border-top:1px solid var(--sidebar-horizontal-divider-bg-color);border-bottom:#fff;border-bottom:1px solid var(--sidebar-horizontal-divider-shadow-color);height:0;margin-bottom:5px;overflow:hidden}.tt-overlay{z-index:1000000;background-color:#00000059;width:100%;height:100%;position:fixed;top:0;left:0}.tt-overlay-item,.tt-overlay-item-notbroken{z-index:999999999;position:relative}.tt-overlay-item .tt-overlay-ignore{z-index:0;pointer-events:none}.tt-overlay-item .tt-overlay-ignore:before{content:\"\";z-index:1000000;background-color:#00000059;width:100%;height:100%;position:absolute;top:0;left:0}.relative{position:relative}.flex-break{border:0;height:0;margin:0;flex-basis:100%!important}.mt10{margin-top:10px}.mb10{margin-bottom:10px}.t-flex{display:flex}[class*=torn-icon-]{vertical-align:middle;background:url(https://www.torn.com/images/v2/city/location_icons_34x34px.svg) no-repeat;width:34px;height:34px;display:inline-block}.torn-icon-item-market{background-position:-68px -34px}.tt-sidebar-area{margin-top:2px;overflow:hidden}.tt-sidebar-area>div{cursor:pointer;vertical-align:top;background-color:var(--default-bg-panel-color);border-top-right-radius:5px;border-bottom-right-radius:5px;position:relative;overflow:hidden}.tt-sidebar-area a{color:var(--default-content-font-color);justify-content:flex-start;align-items:center;height:100%;text-decoration:none;display:flex;overflow:hidden}.tt-sidebar-area a span{float:none;vertical-align:middle;margin-left:10px;display:inline-block}.tt-button-link{cursor:pointer;color:var(--default-blue-color)}.tt-btn{background-color:var(--tt-color-light-green);color:#000;border-radius:6px;width:fit-content}.tt-btn:not([disabled]){cursor:pointer}.tt-btn[disabled]{cursor:not-allowed;opacity:.4}.tt-msg-box{background:var(--info-msg-grey-gradient);box-shadow:var(--info-msg-box-shadow);color:var(--info-msg-font-color);border-radius:5px;margin-top:10px;font-size:0;line-height:16px}.tt-msg-box .tt-msg-div{background:var(--info-msg-horizontal-gradient);border-radius:5px;justify-content:flex-start;display:flex}.tt-msg-box .tt-msg{vertical-align:middle;background-color:var(--default-bg-panel-active-color);background:var(--info-msg-delimiter-gradient);border-radius:0 5px 5px 0;flex-grow:1;width:1px;height:auto}.tt-msg-box .tt-content{vertical-align:middle;color:var(--info-msg-font-color);background-color:var(--default-bg-panel-active-color);background:var(--info-msg-bg-gradient);border-radius:0 5px 5px 0;padding:10px;font-size:13px;position:relative}.tt-message-box{color:var(--info-msg-font-color);box-shadow:var(--info-msg-box-shadow);border-radius:5px;margin-top:10px;font-size:13px;display:flex}.tt-message-box .tt-message-icon-wrap{background:var(--info-msg-grey-gradient);border-radius:5px 0 0 5px;width:34px}.tt-message-box .tt-message-icon{background:var(--info-msg-horizontal-gradient);border-radius:5px 0 0 5px;justify-content:center;width:34px;height:100%;display:flex}.tt-message-box .tt-svg{width:34px;height:34px}.tt-message-box .tt-message-wrap{background-color:var(--default-bg-panel-active-color);background:var(--info-msg-bg-gradient);border-radius:0 5px 5px 0;flex-grow:1;align-items:center;padding:10px;display:flex}.tt-message-box .tt-message{flex-grow:1}.tt-svg{width:128px;height:128px}.tt-svg .tt-svg-upper{stroke:#000;fill:#000}.tt-svg .tt-svg-lower{stroke:#568725;fill:#568725}#sidebarroot .pill{cursor:pointer;background-color:var(--default-bg-panel-color);min-height:22px;color:var(--default-font-color);border-top-right-radius:5px;border-bottom-right-radius:5px;align-items:center;margin-top:2px;text-decoration:none;display:flex;overflow:hidden}#sidebarroot .pill:not([icon]){box-sizing:border-box;padding-top:5px;padding-bottom:5px}#sidebarroot .pill:not([icon]),#sidebarroot .pill[icon] span{height:100%;color:var(--default-font-color);justify-content:flex-start;align-items:center;padding-left:8px;text-decoration:none;display:flex;overflow:hidden}body.tt-tablet #sidebarroot .pill{min-height:34px}body[data-layout=hospital] #sidebarroot .pill{margin-top:0;margin-bottom:1px}#sidebarroot .pill:hover{background-color:var(--default-bg-panel-active-color)!important}.tt-sidebar-information{flex-direction:column;display:flex}.tt-sidebar-information .title{color:inherit;margin:inherit;font-weight:700;text-decoration:none}.tt-sidebar-information .countdown.short{color:var(--tt-color-red)}.tt-sidebar-information .countdown.medium{color:var(--tt-color-orange)}.tt-top-icons{gap:10px;display:flex}");
_css(":root{--tt-color-green:#00a500;--tt-color-light-green:#acea00;--tt-color-red:#d83500;--tt-color-green--20:#00a50033;--tt-color-green--30:#00a5004d;--tt-color-green--40:#00a50066;--tt-background-torn-gray:repeating-linear-gradient(90deg, #627e0d, #627e0d 2px, #6e8820 0, #6e8820 4px);--tt-background-green:repeating-linear-gradient(90deg, #627e0d, #627e0d 2px, #6e8820 0, #6e8820 4px);--tt-background-alternative:repeating-linear-gradient(90deg, #242424, #242424 2px, #2e2e2e 0, #2e2e2e 4px)}body:not(.dark-mode){--tt-color-blue:blue;--tt-color-orange:orange;--tt-color-item-text:#678c00;--tt-color-item-quantity:black;--tt-background-popup:#f1f1f1;--tt-shadow-popup:unset}body.dark-mode{--tt-color-blue:#058cff;--tt-color-orange:gold;--tt-color-item-text:#9c0;--tt-color-item-quantity:#ddd;--tt-background-popup:#444;--tt-shadow-popup:0 0 10px black}.tt-color-green{color:var(--tt-color-green)}.tt-color-red{color:var(--tt-color-red)}");
async function registerUserscriptContext(storagePrefix) {
setTTStorage(new TTScriptStorage(storagePrefix));
setFeatureManager(new ScriptFeatureManager());
setScriptInjector(UserscriptScriptInjector);
setRuntimeInformation(UserscriptRuntimeInformation);
setRuntimeStorage(UserscriptRuntimeStorage);
await migrateDatabase(true);
initializeDatabaseListener();
const [localdata, filters, cache] = await ttStorage.get([
"localdata",
"filters",
"cache"
]);
setLocaldata(localdata ? localdata : getDefaultStorage(DEFAULT_STORAGE.localdata));
setFilters(filters ? filters : getDefaultStorage(DEFAULT_STORAGE.filters));
ttCache.cache = cache ? cache : getDefaultStorage(DEFAULT_STORAGE.cache);
initializeScriptTheme();
}
function initializeScriptTheme() {
document.documentElement.style.setProperty("--tt-theme-color", "#fff");
document.documentElement.style.setProperty("--tt-theme-background", "var(--tt-background-green)");
}
var ScriptFeatureManager = class {
createPopup() {}
isEnabled() {
return true;
}
registerFeature(feature) {
feature.initialise();
feature.execute();
}
};
var fetchListenerInjector = new RequestListenerInjector(injectFetchListeners);
var xhrListenerInjector = new RequestListenerInjector(injectXhrListeners);
var UserscriptScriptInjector = {
getWindow() {
return unsafeWindow;
},
injectFetch() {
fetchListenerInjector.inject();
},
injectXHR() {
xhrListenerInjector.inject();
}
};
var UserscriptRuntimeInformation = {
getVersion() {
return GM.info.version;
},
isUserscript() {
return true;
}
};
var UserscriptRuntimeStorage = {
callback: () => {},
addChangeListener(callback) {
this.callback = callback;
}
};
(async () => {
await registerUserscriptContext("tt_ulf");
setupUserlistPage();
const feature = new UserlistFilterFeature();
FEATURE_MANAGER.registerFeature(feature);
})();
})();