TORN: TornTools - Item Values

Display the value of items on various pages.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name             TORN: TornTools - Item Values
// @namespace        torntools.item-values
// @version          1.0.1
// @author           DeKleineKobini [2114440] and the TornTools team
// @description      Display the value of items on various pages.
// @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/item.php*
// @match            https://*.torn.com/bazaar.php*
// @match            https://*.torn.com/displaycase.php*
// @match            https://*.torn.com/factions.php*
// @match            https://*.torn.com/trade.php*
// @match            https://*.torn.com/itemuseparcel.php*
// @connect          torntools.tornplayground.eu
// @grant            GM.getValue
// @grant            GM.info
// @grant            GM.setValue
// @grant            GM.xmlHttpRequest
// @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;
	var OFFLOAD_SERVICE;
	var DATA_FETCHER;
	var ITEM_RESOLVER;
	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;
	}
	function setOffloadService(offloadService) {
		OFFLOAD_SERVICE = offloadService;
	}
	function setDataFetcher(dataFetcher) {
		DATA_FETCHER = dataFetcher;
	}
	function setStaticItemResolver(staticItemResolver) {
		ITEM_RESOLVER = staticItemResolver;
	}
	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;
	}
	_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);
	}
	function requireItemsLoaded() {
		return requireElement(".items-cont[aria-expanded=true] > li > .title-wrap");
	}
	function requireDOMContentLoaded() {
		return new Promise((resolve) => {
			if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", () => resolve(), { once: true });
			else resolve();
		});
	}
	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);
	svgImport(caret_down_fill_default);
	svgImport(caret_right_fill_default);
	svgImport(caret_up_fill_default);
	svgImport(info_fill_default);
	svgImport(funnel_fill_default);
	svgImport(funnel_x_fill_default);
	svgImport(gear_fill_default);
	svgImport(plus_fill_default);
	svgImport(stethoscope_fill_default);
	svgImport(table_fill_default);
	var mobile;
	var tablet;
	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 getSearchParameters(input) {
		if (!input) input = location.href;
		try {
			return new URL(input).searchParams;
		} catch {
			return new URL(location.href).searchParams;
		}
	}
	function getHashParameters(hash) {
		if (!hash) hash = location.hash;
		if (hash.startsWith("#/")) hash = hash.substring(2);
		else if (hash.startsWith("#") || hash.startsWith("/")) hash = hash.substring(1);
		if (!hash.startsWith("!")) hash = `?${hash}`;
		return new URLSearchParams(hash);
	}
	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 isElement(node) {
		return node && "nodeType" in node && node.nodeType === Node.ELEMENT_NODE && typeof node.className === "string";
	}
	function isSVGElement(node) {
		return node && "nodeType" in node && node.nodeType === Node.ELEMENT_NODE && "ownerSVGElement" in node;
	}
	var SCRIPT_TYPE = (() => {
		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));
	}
	var TO_MILLIS = {
		SECONDS: 1e3,
		MINUTES: 1e3 * 60,
		HOURS: 1e3 * 60 * 60,
		DAYS: 1e3 * 60 * 60 * 24
	};
	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 getCookie(cname) {
		const name = `${cname}=`;
		for (let cookie of decodeURIComponent(document.cookie).split(";")) {
			cookie = cookie.trimStart();
			if (cookie.includes(name)) return cookie.substring(name.length);
		}
		return "";
	}
	function toNumericVersion(version) {
		return parseInt(version.split(".").map((part) => part.padStart(3, "0")).join("").padEnd(9, "9"));
	}
	function isTabFocused() {
		return document.hasFocus();
	}
	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 storageListeners = {
		settings: [],
		filters: [],
		version: [],
		userdata: [],
		torndata: [],
		attackHistory: [],
		stakeouts: [],
		factionStakeouts: [],
		notes: [],
		factiondata: [],
		localdata: [],
		cache: [],
		api: [],
		npcs: [],
		stockdata: [],
		notificationHistory: [],
		notifications: [],
		quick: [],
		migrations: []
	};
	var databaseLoaded = false;
	var databaseLoadPromise = null;
	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
		};
		if (databaseLoadPromise) return await databaseLoadPromise;
		databaseLoadPromise = (async () => {
			const database = await ttStorage.get();
			populateDatabaseVariables(database);
			console.log("TT - Database loaded.", database);
			return database;
		})();
		try {
			const result = await databaseLoadPromise;
			databaseLoaded = true;
			databaseLoadPromise = null;
			return result;
		} catch (error) {
			databaseLoadPromise = null;
			throw error;
		}
	}
	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;
	}
	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;
	}
	var BADGE_TYPES = {
		default: {
			text: "",
			color: null
		},
		error: {
			text: "error",
			color: "#FF0000"
		},
		count: {
			text: async (options) => {
				if (options.events && options.messages) return `${options.events}/${options.messages}`;
				else if (options.events) return options.events.toString();
				else if (options.messages) return options.messages.toString();
				else return await getBadgeText() === "error" ? "error" : null;
			},
			color: async (options) => {
				if (options.events && options.messages) return "#1ed2ac";
				else if (options.events) return "#009eda";
				else if (options.messages) return "#84af03";
				else return await getBadgeText() === "error" ? "error" : null;
			}
		}
	};
	async function setBadge(type, partialOptions = {}) {
		if (SCRIPT_TYPE !== "BACKGROUND") return false;
		const options = {
			events: 0,
			messages: 0,
			...partialOptions
		};
		const badge = { ...BADGE_TYPES[type] };
		if (typeof badge.text === "function") badge.text = await badge.text(options);
		if (typeof badge.color === "function") badge.color = await badge.color(options);
		if (!badge.text) badge.text = "";
		browser.action.setBadgeText({ text: badge.text || "" });
		if (badge.color) browser.action.setBadgeBackgroundColor({ color: badge.color });
		return true;
	}
	function getBadgeText() {
		if (SCRIPT_TYPE !== "BACKGROUND") return Promise.resolve(null);
		return browser.action.getBadgeText({});
	}
	var REGEXES = {
		convertToNumber: /-?[\d,]+(\.\d+)?/,
		formatNumber: /\B(?=(\d{3})+(?!\d))/g
	};
	function formatNumber(number, partialOptions = {}) {
		const options = {
			shorten: false,
			formatter: void 0,
			decimals: 0,
			currency: false,
			forceOperation: false,
			roman: false,
			...partialOptions
		};
		if (typeof number !== "number") if (Number.isNaN(parseInt(number))) return number;
		else number = parseFloat(number);
		if (number === Number.POSITIVE_INFINITY) return "∞";
		if (options.decimals !== void 0) number = parseFloat(number.toFixed(options.decimals));
		if (options.formatter) return options.formatter.format(number);
		if (options.roman) {
			if (number === 0) return "";
			else if (number < 0) throw "Roman numbers can only be positive!";
			const ROMAN = [
				[1e3, "M"],
				[900, "CM"],
				[500, "D"],
				[400, "CD"],
				[100, "C"],
				[90, "XC"],
				[50, "L"],
				[40, "XL"],
				[10, "X"],
				[9, "IX"],
				[5, "V"],
				[4, "IV"],
				[1, "I"]
			];
			return toRoman(number);
			function toRoman(number) {
				if (number === 0) return "";
				for (const [value, character] of ROMAN) {
					if (number < value) continue;
					return character + toRoman(number - value);
				}
				return "N/A";
			}
		}
		const abstract = Math.abs(number);
		const operation = number < 0 ? "-" : options.forceOperation ? "+" : "";
		let text;
		if (options.shorten) {
			const version = options.shorten === true ? 1 : options.shorten;
			const decimals = options.decimals !== -1 ? options.decimals : 3;
			const words = (() => {
				switch (version) {
					case 1: return {
						thousand: "k",
						million: "mil",
						billion: "bill"
					};
					case 2:
					case 3: return {
						thousand: "k",
						million: "m",
						billion: "b"
					};
				}
			})();
			if (version === 1 || version === 2) {
				if (abstract >= 1e9) if (abstract % 1e9 === 0) text = (abstract / 1e9).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + words.billion;
				else text = (abstract / 1e9).toFixed(3) + words.billion;
				else if (abstract >= 1e6) if (abstract % 1e6 === 0) text = abstract / 1e6 + words.million;
				else text = (abstract / 1e6).toFixed(3) + words.million;
				else if (abstract >= 1e3) {
					if (abstract % 1e3 === 0) text = abstract / 1e3 + words.thousand;
				}
			} else if (abstract >= 1e9) if (abstract % 1e9 === 0) text = abstract / 1e9 + words.billion;
			else text = parseFloat((abstract / 1e9).toFixed(decimals)) + words.billion;
			else if (abstract >= 1e6) if (abstract % 1e6 === 0) text = abstract / 1e6 + words.million;
			else text = parseFloat((abstract / 1e6).toFixed(decimals)) + words.million;
			else if (abstract >= 1e3) {
				if (abstract % 1e3 === 0) text = abstract / 1e3 + words.thousand;
				else if (abstract % 100 === 0) text = abstract / 1e3 + words.thousand;
			}
		}
		if (!text) text = abstract.toString().replace(REGEXES.formatNumber, ",");
		return `${operation}${options.currency ? "$" : ""}${text}`;
	}
	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();
	}
	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);
	function getRFC() {
		const rfc = getCookie("rfc_v");
		if (!rfc) for (const cookie of document.cookie.split("; ")) {
			const parts = cookie.split("=");
			if (parts[0] === "rfc_v") return parts[1];
		}
		return rfc;
	}
	function getPage() {
		let page = location.pathname.substring(1);
		if (page.endsWith(".php")) page = page.substring(0, page.length - 4);
		else if (page.endsWith(".html")) page = page.substring(0, page.length - 3);
		switch (page) {
			case "index": {
				const _page = getSearchParameters().get("page");
				if (_page === "hunting") page = "hunting";
				else if (_page === "people") page = "abroad-people";
				else if (_page === "rehab") page = "rehab";
				else page = "home";
				break;
			}
			case "page": {
				const sid = getSearchParameters().get("sid").toLowerCase();
				if (sid === "list") page = getSearchParameters().get("type");
				else if (sid === "crimes") page = "crimes-v2";
				else if (sid === "holdemfull") page = "poker-fullscreen";
				else page = sid;
				break;
			}
			case "hospitalview":
				page = "hospital";
				break;
			case "jailview":
				page = "jail";
				break;
			case "pmarket":
				page = "points-market";
				break;
			case "amarket":
				page = "auction";
				break;
			case "crimes":
				page = "crimes-v1";
				break;
		}
		return page;
	}
	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 };
	}
	function millisToNewDay() {
		const now = Date.now();
		const newDate = new Date();
		newDate.setUTCHours(0, 0, 0);
		newDate.setUTCDate(newDate.getUTCDate() + 1);
		return newDate.getTime() - now;
	}
	function getUserDetails() {
		let id, name;
		if (!hasAPIData()) {
			const script = document.querySelector("script[uid][name]");
			if (!script) return { error: "Couldn't get details" };
			id = parseInt(script.getAttribute("uid"));
			name = script.getAttribute("name");
		} else {
			id = userdata.profile.id;
			name = userdata.profile.name;
		}
		return {
			id,
			name
		};
	}
	function isInfiniteUsageItem(id) {
		return [
			42,
			43,
			65,
			104
		].includes(id);
	}
	var CUSTOM_API_ERROR = {
		NO_NETWORK: "tt-no_network",
		NO_PERMISSION: "tt-no_permission",
		CANCELLED: "tt-cancelled"
	};
	var FETCH_PLATFORMS = {
		tornv2: "https://api.torn.com/v2/",
		torn_direct: "https://www.torn.com/",
		yata: "https://yata.yt/",
		tornstats: "https://www.tornstats.com/",
		torntools: "https://torntools.gregork.com/",
		nukefamily: "https://nuke.family/",
		uhc: "https://tornuhc.eu/",
		stig: "https://api.no1irishstig.co.uk/",
		prometheus: "https://prombot.co.uk:8443/",
		lzpt: "https://api.lzpt.io/",
		wtf: "https://what-the-f.de/",
		tornw3b: "https://weav3r.dev/",
		ffscouter: "https://ffscouter.com/",
		laekna: "https://laekna-revive-bot.onrender.com/",
		tornintel: "https://torn-intel.com/",
		playground_torntools: "https://torntools.tornplayground.eu/"
	};
	var TORN_API_PLATFORMS = ["tornv2"];
	var TEXT_RESPONSE_PLATFORMS = ["torn_direct", "laekna"];
	async function fetchData(location, partialOptions = {}) {
		const options = mergeOptions(partialOptions);
		if (options.relay && SCRIPT_TYPE !== "BACKGROUND" && !RUNTIME_INFORMATION.isUserscript()) return relayToBackground(location, options);
		const request = buildFetchRequest(location, options);
		let result;
		try {
			result = parseFetchResponse(await DATA_FETCHER.fetch(request.url, {
				method: request.method,
				...request.method === "POST" ? { body: request.body } : {},
				headers: request.headers,
				timeout: decideTimeoutTimer(location)
			}), location);
		} catch (error) {
			return await handleError(location, options, error);
		}
		if (!result.success) return await handleError(location, options, result);
		else if (isApiErrorResponse(result.data)) return await handleError(location, options, result.data);
		await handleTornApiState(location, options);
		return result.data;
	}
	function mergeOptions(partial) {
		return {
			section: void 0,
			id: void 0,
			selections: [],
			legacySelections: [],
			key: void 0,
			action: void 0,
			method: "GET",
			body: void 0,
			silent: false,
			includeKey: false,
			relay: false,
			params: {},
			...partial
		};
	}
	async function relayToBackground(location, options) {
		return OFFLOAD_SERVICE.fetchRelay(location, {
			...options,
			relay: false
		});
	}
	function decideTimeoutTimer(location) {
		switch (location) {
			case "yata": return 30 * TO_MILLIS.SECONDS;
			default: return 10 * TO_MILLIS.SECONDS;
		}
	}
	function buildFetchRequest(location, options) {
		const url = buildUrl(location, options);
		const headers = buildHeaders(location, options);
		if (options.method === "POST") return {
			url,
			method: options.method,
			body: buildBody(options),
			headers
		};
		else return {
			url,
			method: options.method,
			headers
		};
	}
	function buildUrl(location, options) {
		let path, pathSections, key;
		const params = new URLSearchParams();
		switch (location) {
			case "tornv2":
				path = `${options.section}/${options.id || ""}`;
				params.append("selections", [...options.selections, ...options.legacySelections].join(","));
				params.append("legacy", options.legacySelections.join(","));
				if (settings.apiUsage.comment) params.append("comment", settings.apiUsage.comment);
				break;
			case "torn_direct":
				path = options.action;
				params.set("rfcv", getRFC());
				break;
			case "tornstats":
				pathSections = [
					"api",
					"v2",
					options.key || api.tornstats.key || api.torn.key
				];
				if (options.section) pathSections.push(options.section);
				if (options.id) pathSections.push(options.id);
				path = pathSections.join("/");
				break;
			case "yata":
				pathSections = [
					"api",
					"v1",
					options.section
				];
				if (options.id) pathSections.push(options.id, "");
				if (options.includeKey) key = api.yata.key;
				path = pathSections.join("/");
				break;
			case "prometheus":
				path = ["api", options.section].join("/");
				break;
			case "tornw3b":
				path = ["api", options.section].join("/");
				break;
			case "ffscouter":
				path = [
					"api",
					"v1",
					options.section
				].join("/");
				key = api.ffScouter.key;
				break;
			case "tornintel":
				path = ["api", options.section].join("/");
				break;
			case "playground_torntools":
				path = ["api", options.section].join("/");
				break;
			default:
				path = options.section;
				break;
		}
		if (options.includeKey) params.append("key", options.key || key || api.torn.key);
		if (options.params) for (const [key, value] of Object.entries(options.params)) params.append(key, value.toString());
		return `${FETCH_PLATFORMS[location]}${path}${params.toString() ? `?${params}` : ""}`;
	}
	function buildHeaders(location, options) {
		const headers = {};
		if (location === "tornv2") headers["Authorization"] = `ApiKey ${options.key || api.torn.key}`;
		if (options.method === "POST") {
			if (!(options.body instanceof URLSearchParams)) headers["content-type"] = "application/json";
			if (location === "torn_direct") headers["x-requested-with"] = "XMLHttpRequest";
		}
		return headers;
	}
	function buildBody(options) {
		if (options.method !== "POST") return null;
		return options.body instanceof URLSearchParams ? options.body : JSON.stringify(options.body);
	}
	function parseFetchResponse(response, location) {
		try {
			return {
				data: JSON.parse(response.text),
				success: true
			};
		} catch {
			if (TEXT_RESPONSE_PLATFORMS.includes(location)) return {
				data: response.text,
				success: true
			};
			if (response.ok) return { success: true };
			return {
				success: false,
				error: new HTTPException(response.status)
			};
		}
	}
	async function handleError(location, options, result) {
		if (result instanceof DOMException) return handleTimeoutError(location, options);
		if (result.constructor.name === "TypeError") return handleNetworkError(location, options, result.message);
		return handleApiError(location, options, result);
	}
	async function handleTimeoutError(location, options) {
		const error = "Request cancelled because it took too long.";
		await handleTornApiState(location, options, error);
		throw {
			error,
			isLocal: false,
			code: CUSTOM_API_ERROR.CANCELLED
		};
	}
	async function handleTornApiState(location, options, error, online = false) {
		if (!TORN_API_PLATFORMS.includes(location) || options.silent || SCRIPT_TYPE !== "BACKGROUND") return;
		if (error) {
			await ttStorage.change({ api: { torn: {
				online,
				error
			} } });
			await setBadge("error");
		} else {
			await getBadgeText().then((value) => {
				if (value === "error") return setBadge("default");
			}).catch(() => console.error("TT - Couldn't get the badge text."));
			await ttStorage.change({ api: { torn: {
				online: true,
				error: ""
			} } });
		}
	}
	async function handleNetworkError(location, options, message) {
		let error = message;
		let isLocal = false;
		let code;
		if (error === "Failed to fetch") {
			isLocal = true;
			if (!RUNTIME_INFORMATION.isUserscript() && SCRIPT_TYPE === "BACKGROUND" && !await hasOrigins(FETCH_PLATFORMS[location])) {
				error = "Permission issues";
				code = CUSTOM_API_ERROR.NO_PERMISSION;
			} else {
				error = "Network issues";
				code = CUSTOM_API_ERROR.NO_NETWORK;
			}
		}
		await handleTornApiState(location, options, error);
		throw {
			error,
			isLocal,
			code
		};
	}
	async function hasOrigins(...origins) {
		return browser.permissions.contains({ origins });
	}
	async function handleApiError(location, options, result) {
		if (TORN_API_PLATFORMS.includes(location)) {
			let error, online;
			if (result.error instanceof HTTPException) {
				error = result.error.toString();
				online = false;
			} else {
				error = result.error.error;
				online = result.error.code !== 9 && !(result instanceof HTTPException);
			}
			await handleTornApiState(location, options, error, online);
			throw result.error instanceof HTTPException ? result.error.asObject() : result.error;
		}
		throw { error: result.error };
	}
	function isApiErrorResponse(data) {
		return !!data && typeof data === "object" && "error" in data;
	}
	var HTTPException = class HTTPException {
		code;
		constructor(code) {
			this.code = code;
		}
		get message() {
			return this.code in HTTPException.codes ? HTTPException.codes[this.code] : `Unknown code (${this.code})`;
		}
		asObject() {
			return {
				code: this.code,
				message: this.message,
				http: true
			};
		}
		toString() {
			return `HTTP ${this.code}: ${this.message}`;
		}
		static get codes() {
			return {
				200: "OK",
				201: "Created",
				202: "Accepted",
				203: "Non-Authoritative Information",
				204: "No Content",
				205: "Reset Content",
				206: "Partial Content",
				300: "Multiple Choices",
				301: "Moved Permanently",
				302: "Found",
				303: "See Other",
				304: "Not Modified",
				305: "Use Proxy",
				306: "Unused",
				307: "Temporary Redirect",
				400: "Bad Request",
				401: "Unauthorized",
				402: "Payment Required",
				403: "Forbidden",
				404: "Not Found",
				405: "Method Not Allowed",
				406: "Not Acceptable",
				407: "Proxy Authentication Required",
				408: "Request Timeout",
				409: "Conflict",
				410: "Gone",
				411: "Length Required",
				412: "Precondition Required",
				413: "Request Entry Too Large",
				414: "Request-URI Too Long",
				415: "Unsupported Media Type",
				416: "Requested Range Not Satisfiable",
				417: "Expectation Failed",
				418: "I'm a teapot",
				429: "Too Many Requests",
				500: "Internal Server Error",
				501: "Not Implemented",
				502: "Bad Gateway",
				503: "Service Unavailable",
				504: "Gateway Timeout",
				505: "HTTP Version Not Supported"
			};
		}
	};
	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 addFetchListener(callback) {
		SCRIPT_INJECTOR.injectFetch();
		window.addEventListener("tt-fetch", callback);
	}
	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);
	}
	var isInternalFaction = getSearchParameters().get("step") === "your";
	async function setupFactionsPage() {
		await requireDOMContentLoaded();
		if (isInternalFaction) {
			addXHRListener(({ detail: { page, xhr } }) => {
				if (page === "factions") {
					const step = new URLSearchParams(xhr.requestBody).get("step");
					if (step === "crimes") loadCrimes().catch((err) => console.warn(err));
					else if (step === "getMoneyDepositors") triggerCustomListener(EVENT_CHANNELS.FACTION_GIVE_TO_USER);
					else if (step === "upgradeConfirm") {
						if (document.querySelector(".faction-tabs .ui-tabs-active").dataset.case !== "upgrades") return;
						triggerCustomListener(EVENT_CHANNELS.FACTION_UPGRADE_INFO);
					}
				}
			});
			addFetchListener(({ detail: { page, fetch } }) => {
				if (page === "page") {
					const sid = new URL(fetch.url).searchParams.get("sid");
					if (sid === "factionsProfile") loadInfo().catch((err) => console.warn(err));
					else if (sid === "factionsNews") loadMain().catch((err) => console.warn(err));
					else if (sid === "factionsControlMembers") loadArmory().catch((err) => console.warn(err));
				}
			});
			await requireElement(".faction-tabs");
			document.querySelector(".faction-tabs li[data-case=armoury]").addEventListener("click", loadArmory);
			switch (getFactionSubpage()) {
				case "main":
					loadMain().catch((err) => console.warn(err));
					break;
				case "info":
					loadInfo().catch((err) => console.warn(err));
					break;
				case "crimes":
					loadCrimes().catch((err) => console.warn(err));
					break;
				case "armoury":
					loadArmory().catch((err) => console.warn(err));
					break;
				case "controls":
					loadControls().catch((err) => console.warn(err));
					break;
				default: break;
			}
			async function loadMain() {
				await requireElement(".announcement").then(() => triggerCustomListener(EVENT_CHANNELS.FACTION_MAIN)).catch(() => {});
			}
			async function loadInfo() {
				const root = await requireElement("#react-root-faction-info").catch(() => {});
				if (!root || root.classList.contains("tt-handled")) return;
				root.classList.add("tt-handled");
				await requireElement(".faction-description, .members-list");
				triggerCustomListener(EVENT_CHANNELS.FACTION_INFO);
				loadMemberTable();
			}
			async function loadCrimes() {
				requireElement("#faction-crimes .crimes-list").then(() => triggerCustomListener(EVENT_CHANNELS.FACTION_CRIMES)).catch(() => {});
				requireElement("#faction-crimes-root [class*='buttonsContainer___']", { maxCycles: 20 }).then(async (buttonsContainer) => {
					const list = await requireElement("#faction-crimes-root .page-head-delimiter + div:not([class])");
					await requireElement("[class*='loader___']", {
						parent: list,
						invert: true
					});
					list.classList.add("tt-oc2-list");
					triggerCustomListener(EVENT_CHANNELS.FACTION_CRIMES2);
					new MutationObserver(() => triggerCustomListener(EVENT_CHANNELS.FACTION_CRIMES2_REFRESH)).observe(list, { childList: true });
					buttonsContainer.querySelectorAll("button").forEach((button) => {
						const tabName = button.querySelector("[class*='tabName___']").textContent.trim();
						new MutationObserver(() => {
							if (!button.className.includes("active___")) return;
							triggerCustomListener(EVENT_CHANNELS.FACTION_CRIMES2_TAB, { tabName });
						}).observe(button, {
							attributes: true,
							attributeFilter: ["class"]
						});
					});
				}).catch(() => {});
			}
			async function loadArmory() {
				const tab = await requireElement("#faction-armoury-tabs > ul.torn-tabs > li[aria-selected='true']");
				await requireElement(":scope > .ajax-preloader", {
					invert: true,
					parent: document.getElementById(tab.getAttribute("aria-controls"))
				});
				if (!getCurrentSection()) return;
				triggerCustomListener(EVENT_CHANNELS.FACTION_ARMORY_TAB, { section: getCurrentSection() });
				new MutationObserver((mutations) => {
					if (!mutations.some((mutation) => {
						return Array.from(mutation.addedNodes).filter(isElement).some((node) => node.classList.contains("item-list") || node.tagName === "DIV" && node.classList.contains("p10") || node.id === "inventory-container");
					})) return;
					const mutation = mutations.find((mutation) => extractArmorySubcategory(mutation.target.id) !== null);
					if (!mutation) return;
					triggerCustomListener(EVENT_CHANNELS.FACTION_ARMORY_TAB, { section: extractArmorySubcategory(mutation.target.id) });
				}).observe(document.querySelector("#faction-armoury-tabs"), {
					childList: true,
					subtree: true
				});
				function getCurrentSection() {
					const controls = document.querySelector("#faction-armoury-tabs > ul.torn-tabs > li[aria-selected='true']")?.getAttribute("aria-controls");
					if (!controls) return null;
					return extractArmorySubcategory(controls);
				}
			}
			async function loadControls() {
				await requireElement(".control-tabs");
				const giveToUser = document.querySelector(".control-tabs > li[aria-controls='option-give-to-user']");
				if (giveToUser) {
					checkGiveToUser();
					giveToUser.addEventListener("click", () => checkGiveToUser());
				}
				function checkGiveToUser() {
					if (document.querySelector(".control-tabs > li[aria-controls='option-give-to-user']").getAttribute("aria-selected")) triggerCustomListener(EVENT_CHANNELS.FACTION_GIVE_TO_USER);
				}
			}
		} else if (!await isDestroyed()) loadMemberTable();
		let observer;
		function loadMemberTable() {
			const table = document.querySelector(".members-list .table-body");
			handleFilter();
			handleSorting();
			handleIconUpdates();
			async function handleFilter() {
				(await requireElement(".table-header input[class*='searchInput___']")).addEventListener("input", () => {
					disconnectObserver();
					if (table) {
						let handled = false;
						const timeout = setTimeout(() => {
							triggerCustomListener(EVENT_CHANNELS.FACTION_NATIVE_FILTER, { hasResults: true });
							disconnectObserver();
							handled = true;
						}, 250);
						observer = new MutationObserver((mutations) => {
							if (handled) return;
							if (!Array.from(mutations).filter((mutation) => Array.from(mutation.addedNodes).every((node) => !isElement(node) || !node.classList.contains(".tt-last-action"))).length) return;
							handled = true;
							triggerCustomListener(EVENT_CHANNELS.FACTION_NATIVE_FILTER, { hasResults: true });
							clearTimeout(timeout);
							disconnectObserver();
						});
						observer.observe(table, { childList: true });
					} else triggerCustomListener(EVENT_CHANNELS.FACTION_NATIVE_FILTER, { hasResults: false });
				});
				function disconnectObserver() {
					if (!observer) return;
					observer.disconnect();
					observer = void 0;
				}
			}
			async function handleSorting() {
				await requireElement(".members-list .table-header .c-pointer");
				for (const header of findAllElements(".members-list .table-header .c-pointer")) header.addEventListener("click", sortListener);
				function sortListener(event) {
					if (event.target.closest("button, input")) return;
					if (!findAllElements(".members-list .table-body .table-row").length) return;
					new MutationObserver((_mutations, observer) => {
						triggerCustomListener(EVENT_CHANNELS.FACTION_NATIVE_SORT);
						observer.disconnect();
					}).observe(document.querySelector(".members-list .table-body"), { childList: true });
				}
			}
			async function handleIconUpdates() {
				const memberTable = await requireElement(".members-list .table-body");
				new MutationObserver((records) => {
					if (records.length > 1) return;
					for (const record of records) {
						const firstRemovedNode = record.removedNodes?.[0];
						if (!firstRemovedNode?.matches("#iconTray")) continue;
						const oldIconsCount = firstRemovedNode.children.length;
						const newIconsCount = (record.addedNodes?.[0]).children.length;
						if (oldIconsCount > 0 && newIconsCount > 0 && oldIconsCount !== newIconsCount) {
							triggerCustomListener(EVENT_CHANNELS.FACTION_NATIVE_ICON_UPDATE);
							break;
						}
					}
				}).observe(memberTable, {
					childList: true,
					subtree: true
				});
			}
		}
	}
	function getFactionSubpage() {
		const hash = location.hash.replace("#/", "");
		if (!hash || hash.includes("war/")) return "main";
		const params = getHashParameters();
		if (params.has("tab")) return params.get("tab");
		if (hash.startsWith("#faction-")) return hash.substring(9);
		return "";
	}
	function extractArmorySubcategory(controls) {
		const params = new URLSearchParams(controls);
		if (params.has("sub")) return params.get("sub");
		if (controls.startsWith("armoury-")) return controls.replace("armoury-", "");
		return null;
	}
	async function isDestroyed() {
		return (await requireElement(".faction-info")).classList.contains("faction-destroyed");
	}
	var pendingActions = {};
	function setupItemPage() {
		addXHRListener(({ detail }) => {
			const { page, xhr } = detail;
			if (page !== "item") return;
			const params = new URLSearchParams(xhr.requestBody);
			const step = params.get("step");
			if ("json" in detail) {
				const { json } = detail;
				if (isUseItem(step, json)) {
					if (!json.success) return;
					if (params.get("step") !== "useItem") return;
					if (params.has("fac") && params.get("fac") !== "0") return;
					if (json.items) {
						if (json.items.itemAppear) json.items.itemAppear.filter((item) => !("isMoney" in item)).forEach((item) => {
							triggerCustomListener(EVENT_CHANNELS.ITEM_AMOUNT, {
								item: parseInt(item.ID),
								amount: parseInt(item.qty),
								reason: "usage"
							});
						});
						if (json.items.itemDisappear) for (const item of json.items.itemDisappear) triggerCustomListener(EVENT_CHANNELS.ITEM_AMOUNT, {
							item: parseInt(item.ID),
							amount: -parseInt(item.qty),
							reason: "usage"
						});
					} else if (!isInfiniteUsageItem(parseInt(params.get("itemID")))) triggerCustomListener(EVENT_CHANNELS.ITEM_AMOUNT, {
						item: parseInt(params.get("itemID")),
						amount: -1,
						reason: "usage"
					});
				} else if (isSendItemAction(step, json)) {
					if (!json.success) return;
					const actionId = "confirm" in json ? json.itemID : params.get("XID");
					const item = "confirm" in json ? params.get("itemID") : pendingActions[actionId].item;
					const amount = json.amount;
					if ("confirm" in json) pendingActions[actionId] = { item };
					else {
						delete pendingActions[actionId];
						triggerCustomListener(EVENT_CHANNELS.ITEM_AMOUNT, {
							item: parseInt(item),
							amount: -amount,
							reason: "sending"
						});
					}
				} else if (json && [
					"getCategoryList",
					"getNotAllItemsListWithoutGroups",
					"getItemsListByItemId",
					"getSearchList"
				].includes(step)) {
					const tab = getCurrentTab();
					if (!tab) return;
					new MutationObserver((_mutations, observer) => {
						if (document.querySelector("li.ajax-item-loader")) return;
						triggerCustomListener(EVENT_CHANNELS.ITEM_ITEMS_LOADED, {
							tab,
							initial: false
						});
						observer.disconnect();
					}).observe(tab, {
						subtree: true,
						childList: true
					});
				}
			} else if (step === "actionForm") {
				if (params.get("action") === "equip" && hasAPIData()) {
					const textElement = elementBuilder({
						type: "div",
						html: xhr.response
					}).querySelector("h5, [data-status]");
					if (textElement) {
						const regexResult = textElement.textContent.trim().match(/You (unequipped|equipped) your (.*)\./i);
						if (regexResult) {
							const itemName = regexResult[2];
							const equipAction = regexResult[1];
							const item = torndata.items.find((item) => item.name === itemName);
							if (!item) return;
							triggerCustomListener(EVENT_CHANNELS.ITEM_EQUIPPED, {
								equip: equipAction === "equipped",
								item: item.id
							});
						}
					}
				}
			}
		});
		requireItemsLoaded().then(() => {
			for (const icon of findAllElements("#categoriesList > li:not(.no-items):not(.m-show):not(.hide)")) icon.addEventListener("click", async () => {
				await requireItemsLoaded();
				triggerCustomListener(EVENT_CHANNELS.ITEM_SWITCH_TAB, { tab: icon.dataset.type });
			});
			triggerCustomListener(EVENT_CHANNELS.ITEM_ITEMS_LOADED, {
				tab: getCurrentTab(),
				initial: false
			});
		});
	}
	function getCurrentTab() {
		return document.querySelector("ul.items-cont.tab-menu-cont[style='display: block;'], ul.items-cont.tab-menu-cont:not([style])");
	}
	function isUseItem(step, _json) {
		return step === "useItem";
	}
	function isSendItemAction(step, _json) {
		return step === "sendItemAction";
	}
	function setupTradePage() {
		addXHRListener(({ detail: { page, xhr } }) => {
			if (page === "trade") triggerEvent(new URLSearchParams(xhr.requestBody).get("step"));
		});
		window.addEventListener("hashchange", () => {
			triggerEvent(getHashParameters().get("step"));
		});
	}
	function triggerEvent(step) {
		const active = [
			"view",
			"initiateTrade",
			"accept"
		].includes(step);
		triggerCustomListener(EVENT_CHANNELS.TRADE, {
			step,
			active
		});
	}
	_css("body.tt-mobile .tt-item-price-wrap{height:30px!important}body.tt-tablet .tt-item-price-wrap{height:34px!important}.tt-item-price-color,.tt-item-price{color:var(--tt-color-item-text)}.tt-item-price{white-space:pre;font-weight:400;width:unset!important;padding-left:10px!important;padding-right:10px!important}span.tt-item-price{float:right;line-height:34px;position:absolute;right:0}.tt-item-quantity{color:var(--tt-color-item-quantity);font-weight:700}body.tt-mobile .tt-item-price{float:unset;font-size:11px;position:absolute;top:10px;right:-10px}body.tt-tablet .tt-item-price{float:right;margin-top:-1px;font-size:12px;padding-right:5px!important}body.tt-tablet li[data-category=Primary] .tt-item-price,body.tt-tablet li[data-category=Secondary] .tt-item-price,body.tt-tablet li[data-category=Melee] .tt-item-price{transform:translate(-225px)}body.tt-tablet li[data-category=Defensive]:not([data-group]) .tt-item-price,body.tt-tablet li[data-category=Defensive][data-group=item] .tt-item-price{transform:translate(-155px)}body.tt-mobile li[data-equipped] .name-wrap{margin-top:-4px}body.tt-mobile .needed-item .tt-item-price,body.tt-tablet .needed-item .tt-item-price{right:0}body.tt-mobile .tt-item-price.price-total,body.tt-tablet .tt-item-price.price-total{float:left;font-size:12px;position:relative;top:0;left:0!important}body.tt-mobile .items-cont .name,body.tt-tablet .items-cont .name,body.tt-mobile .items-cont .qty,body.tt-tablet .items-cont .qty{position:relative;top:-3px}.thumbnails .tt-item-price-wrap{display:block!important}body.tt-mobile #bazaarRoot .tt-item-price-wrap{margin-bottom:1px}body.tt-mobile .items-wrap .tt-item-price-wrap{margin-bottom:4px}");
	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;
		}
	};
	var page = getPage();
	function initialiseItemValues() {
		switch (page) {
			case "bazaar":
			case "factions":
			case "itemuseparcel":
			case "trade":
				setupXHR({ addRelative: true });
				break;
			case "displaycase":
				setupXHR({
					ignoreUntradable: true,
					addRelative: true
				});
				break;
			case "item":
				CUSTOM_LISTENERS[EVENT_CHANNELS.ITEM_ITEMS_LOADED].push(itemListener);
				CUSTOM_LISTENERS[EVENT_CHANNELS.ITEM_SWITCH_TAB].push(itemListener);
				CUSTOM_LISTENERS[EVENT_CHANNELS.ITEM_AMOUNT].push(({ item, amount }) => {
					updateItemAmount(item, amount);
				});
				break;
		}
		function itemListener({ tab }) {
			if (!FEATURE_MANAGER.isEnabled(ItemValuesFeature)) return;
			showItemValues(tab);
		}
		function setupXHR(options = {}) {
			addXHRListener(({ detail: { page, xhr, json } }) => {
				if (!json || page !== "inventory") return;
				handleRequest(xhr, json, options);
			});
		}
		function handleRequest(xhr, json, options = {}) {
			const params = new URLSearchParams(xhr.requestBody);
			switch (params.get("step")) {
				case "getList":
				case "getListById":
					showInventoryList(params.get("type") || null, json.list, options).catch((error) => console.error("Couldn't show the item values.", error));
					break;
			}
		}
	}
	async function showInventoryList(type, items, partialOptions = {}) {
		const options = {
			ignoreUntradable: true,
			addRelative: false,
			...partialOptions
		};
		const list = getCurrentList();
		if (settings.pages.items.values) for (const item of items) {
			if (options.ignoreUntradable && parseInt(item.untradable)) continue;
			requireElement(`li[data-reactid*='$${item.armoryID}'] .name-wrap`, { parent: list }).then(async () => {
				await sleep(0);
				const itemRow = list.querySelector(`li[data-reactid*='$${item.armoryID}']`);
				const parent = itemRow.querySelector(".name-wrap");
				if (parent.querySelector(".tt-item-price")) if (type) return;
				else parent.querySelector(".tt-item-price").remove();
				if (options.addRelative) parent.parentElement.classList.add("relative");
				const price = parseInt(item.averageprice) || 0;
				const quantity = parseInt(item.Qty) || 1;
				const valueWrap = itemRow.querySelector(".info-wrap");
				if (valueWrap?.clientWidth && (!valueWrap.textContent.trim() || valueWrap.textContent.startsWith("$"))) {
					valueWrap.innerHTML = "";
					valueWrap.classList.add("tt-item-price-color");
					addValue(valueWrap, quantity, price);
				} else if (valueWrap?.clientWidth && (!isElement(valueWrap.nextSibling) || !valueWrap.nextSibling.childElementCount)) {
					valueWrap.style.setProperty("position", "relative");
					const priceElement = elementBuilder({
						type: "span",
						class: "tt-item-price"
					});
					addValue(priceElement, quantity, price);
					valueWrap.appendChild(priceElement);
				} else {
					const priceElement = elementBuilder({
						type: "span",
						class: "tt-item-price"
					});
					if (item.groupItem && quantity !== 1) priceElement.style.setProperty("padding-right", "98px", "important");
					addValue(priceElement, quantity, price);
					if (item.groupItem) if (quantity === 1) parent.insertAdjacentElement("afterend", priceElement);
					else parent.appendChild(priceElement);
					else parent.insertAdjacentElement("afterend", priceElement);
				}
			}).catch(() => {});
		}
		else for (const price of findAllElements(".tt-item-price, #category-wrap .tt-ignore")) price.remove();
		function getCurrentList() {
			return document.querySelector(".category-wrap ul.items-cont[style*='display:block;'], .category-wrap ul.items-cont[style*='display: block;']");
		}
	}
	function addValue(priceElement, quantity, price) {
		const totalPrice = quantity * price;
		if (totalPrice) {
			if (quantity > 1) {
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${formatNumber(price, { currency: true })} | `
				}));
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${quantity}x = `,
					class: "tt-item-quantity"
				}));
			}
			priceElement.appendChild(elementBuilder({
				type: "span",
				text: `${formatNumber(totalPrice, { currency: true })}`
			}));
		} else if (price === 0) priceElement.textContent = "N/A";
		else priceElement.textContent = `${formatNumber(price, { currency: true })}`;
	}
	function showItemValues(list) {
		if (!list.dataset) return;
		for (const item of findAllElements(":scope > li[data-item]", list)) {
			const id = parseInt(item.dataset.item);
			const price = ITEM_RESOLVER.getFullItem(id).value.market_price;
			const parent = mobile || tablet ? item.querySelector(".name-wrap") : item.querySelector(".bonuses-wrap") || item.querySelector(".name-wrap");
			const quantity = parseInt(item.querySelector(".item-amount.qty").textContent) || 1;
			const totalPrice = quantity * price;
			if (parent.querySelector(".tt-item-price")) continue;
			let priceElement;
			if (item.querySelector(".bonuses-wrap")) priceElement = elementBuilder({
				type: "li",
				class: "tt-item-price fl"
			});
			else {
				priceElement = elementBuilder({
					type: "span",
					class: "tt-item-price"
				});
				if (item.querySelector("button.group-arrow")) priceElement.style.setProperty("padding-right", "30px", "important");
			}
			if (totalPrice) if (quantity === 1) priceElement.appendChild(elementBuilder({
				type: "span",
				text: `${formatNumber(price, { currency: true })}`
			}));
			else {
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${formatNumber(price, { currency: true })} | `
				}));
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${quantity}x = `,
					class: "tt-item-quantity"
				}));
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${formatNumber(totalPrice, { currency: true })}`
				}));
			}
			else if (price === 0) priceElement.textContent = "N/A";
			else priceElement.textContent = `${formatNumber(price, { currency: true })}`;
			parent.appendChild(priceElement);
		}
	}
	function updateItemAmount(id, change) {
		for (const item of findAllElements(`.items-cont > li[data-item="${id}"]`)) {
			const priceElement = item.querySelector(".tt-item-price");
			if (!priceElement) continue;
			const quantityElement = priceElement.querySelector(".tt-item-quantity");
			if (!quantityElement) continue;
			const price = ITEM_RESOLVER.getFullItem(id).value.market_price;
			const newQuantity = parseInt(quantityElement.textContent.match(/(\d*)x = /i)[1]) + change;
			if (newQuantity === 1) {
				priceElement.innerHTML = "";
				priceElement.appendChild(elementBuilder({
					type: "span",
					text: `${formatNumber(price, { currency: true })}`
				}));
			} else {
				quantityElement.textContent = `${newQuantity}x = `;
				priceElement.querySelector("span:last-child").textContent = `${formatNumber(price * newQuantity, { currency: true })}`;
			}
		}
	}
	async function startValues() {
		if (page === "item") {
			await requireItemsLoaded();
			showItemValues(document.querySelector(".itemsList[aria-expanded='true']"));
		}
	}
	function removeValues() {
		for (const value of findAllElements(".tt-item-price")) if (value.classList.contains("price-total")) value.parentElement.remove();
		else value.remove();
	}
	var ItemValuesFeature = class extends Feature {
		constructor() {
			super("Item Values", "items");
		}
		precondition() {
			if (!getPageStatus().access) return false;
			if (page === "displaycase") {
				const userId = location.hash.startsWith("#display/") ? parseInt(location.hash.substring(9)) || false : false;
				const details = getUserDetails();
				if (userId && !details.error && userId !== details.id) return false;
			} else if (page === "bazaar") {
				const userId = parseInt(getSearchParameters().get("userId"));
				const details = getUserDetails();
				if (userId && !details.error && userId !== details.id) return false;
			} else if (page === "faction" && !isInternalFaction) return false;
			return true;
		}
		requirements() {
			if (page === "item" && !ITEM_RESOLVER.hasFullItems()) return "No API access.";
			return true;
		}
		isEnabled() {
			return settings.pages.items.values;
		}
		initialise() {
			initialiseItemValues();
		}
		async execute() {
			await startValues();
		}
		cleanup() {
			removeValues();
		}
		storageKeys() {
			return ["settings.pages.items.values"];
		}
		shouldTriggerEvents() {
			return true;
		}
	};
	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 {
		async change(object) {
			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 });
			}
		}
		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)}");
	var ScriptItemResolver = {
		itemsMap: {},
		loadItem(id) {
			return this.getFullItem(id) ?? this.getStaticItem(id);
		},
		getStaticItem(id) {
			return this.getFullItem(id);
		},
		hasFullItems: () => true,
		getFullItem(id) {
			if (!Object.keys(this.itemsMap).length) throw new Error("no items loaded");
			return id in this.itemsMap ? this.itemsMap[id] : null;
		},
		async loadItems() {
			if (ttCache.hasValue("static-data", "items-map")) {
				this.itemsMap = ttCache.get("static-data", "items-map");
				return;
			}
			const itemsMap = (await fetchData("playground_torntools", { section: "static-items" })).items.reduce((acc, item) => {
				acc[item.id] = item;
				return acc;
			}, {});
			this.itemsMap = itemsMap;
			ttCache.set({ "static-data": { "items-map": itemsMap } }, millisToNewDay());
		}
	};
	async function registerUserscriptContext(storagePrefix) {
		setTTStorage(new TTScriptStorage(storagePrefix));
		setFeatureManager(new ScriptFeatureManager());
		setScriptInjector(UserscriptScriptInjector);
		setRuntimeInformation(UserscriptRuntimeInformation);
		setRuntimeStorage(UserscriptRuntimeStorage);
		setOffloadService(ScriptOffloadService);
		setDataFetcher(ScriptDataFetcher);
		setStaticItemResolver(ScriptItemResolver);
		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;
		}
	};
	var ScriptOffloadService = {
		fetchRelay(_location, _options) {
			return Promise.reject(new Error("OffloadService is not available in script context. Use DataFetcher instead."));
		},
		initialize() {
			return Promise.resolve({ success: true });
		}
	};
	var ScriptDataFetcher = { fetch(url, options) {
		return new Promise((resolve, reject) => {
			try {
				const u = new URL(url);
				u.searchParams.append("pda-cache-busting", getUUID());
				url = u.toString();
			} catch {}
			GM.xmlHttpRequest({
				method: options?.method || "GET",
				url,
				headers: options?.headers,
				data: options?.method === "POST" ? typeof options.body === "string" ? options.body : JSON.stringify(options.body) : void 0,
				timeout: options?.timeout,
				onload: (response) => {
					if (!response) {
						reject(new Error("Request has no actual response. Likely something went wrong in the fetch implementation."));
						return;
					}
					resolve({
						text: response.responseText,
						status: response.status,
						ok: response.status >= 200 && response.status < 300
					});
				},
				onerror: (error) => {
					reject(error);
				},
				ontimeout: () => {
					reject(new DOMException("Request cancelled because it took too long.", "AbortError"));
				}
			});
		});
	} };
	(async () => {
		await registerUserscriptContext("tt_iv");
		await ScriptItemResolver.loadItems();
		await setupActivePage();
		const feature = new ItemValuesFeature();
		FEATURE_MANAGER.registerFeature(feature);
	})();
	async function setupActivePage() {
		switch (getPage()) {
			case "factions":
				await setupFactionsPage();
				return;
			case "item":
				setupItemPage();
				return;
			case "trade":
				setupTradePage();
				return;
		}
	}
})();