Items sorter with RW weapon filter

Allows you to sort your items, by RW or price.

  1. // ==UserScript==
  2. // @name Items sorter with RW weapon filter
  3. // @namespace Nurv@IronNerd.me
  4. // @description Allows you to sort your items, by RW or price.
  5. // @version 0.1
  6. // @author Nurv [669537]
  7. // @license Copyright IronNerd.me
  8. // @match https://www.torn.com/item.php*
  9.  
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. "use strict";
  14.  
  15.  
  16. function init() {
  17. const titleBarEl = document.querySelector(".title-black.hospital-dark.top-round.scroll-dark[role='heading'][aria-level='5']");
  18.  
  19. if (!titleBarEl) {
  20. console.error("Title bar element not found!");
  21. return;
  22. }
  23.  
  24. if (document.getElementById("sort-default")) {
  25. console.log("Sorting buttons already added.");
  26. return;
  27. }
  28.  
  29. const controlPanel = document.createElement("div");
  30. controlPanel.style.display = "inline-block";
  31. controlPanel.style.marginLeft = "20px";
  32. controlPanel.style.gap = "10px";
  33. controlPanel.style.verticalAlign = "middle";
  34.  
  35. const buttonStyle = `
  36. padding: 8px 12px;
  37. cursor: pointer;
  38. color: white;
  39. background-color: #555;
  40. border: none;
  41. border-radius: 4px;
  42. font-size: 14px;
  43. transition: background-color 0.3s;
  44. `;
  45.  
  46. const styleSheet = document.createElement("style");
  47. styleSheet.type = "text/css";
  48. styleSheet.innerText = `
  49. #sort-default:hover { background-color: #777; }
  50. #sort-rw:hover { background-color: #888; }
  51. #sort-price:hover { background-color: #999; }
  52.  
  53. #sort-default, #sort-rw, #sort-price {
  54. border: 1px solid #333;
  55. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  56. }
  57.  
  58. @media (max-width: 600px) {
  59. #sort-default, #sort-rw, #sort-price {
  60. font-size: 12px;
  61. padding: 6px 8px;
  62. }
  63.  
  64. .control-panel {
  65. flex-direction: column;
  66. align-items: flex-start;
  67. }
  68. }
  69. `;
  70. document.head.appendChild(styleSheet);
  71.  
  72. controlPanel.innerHTML = `
  73. <button id="sort-default" style="${buttonStyle}" aria-label="Sort items to default order">Default</button>
  74. <button id="sort-rw" style="${buttonStyle}" aria-label="Sort RW weapons">RW</button>
  75. <button id="sort-price" style="${buttonStyle}" aria-label="Sort items by price">Price</button>
  76. `;
  77.  
  78. const itemsSorterIcon = titleBarEl.querySelector(".items-sorter");
  79. if (itemsSorterIcon && itemsSorterIcon.parentNode) {
  80. itemsSorterIcon.parentNode.appendChild(controlPanel);
  81. console.log("Sorting buttons added to the page.");
  82. } else {
  83. console.error("Items sorter icon's parent not found.");
  84. return;
  85. }
  86.  
  87. const categoriesList = document.querySelector("#categoriesList");
  88. if (!categoriesList) {
  89. console.error("Categories list not found!");
  90. return;
  91. }
  92.  
  93. let sortState = "default";
  94. let parentElement;
  95. let itemsOriginal;
  96. let posOriginal;
  97. let loadedAll = false;
  98.  
  99. document.getElementById("sort-default").addEventListener("click", () => {
  100. resetSorting();
  101. });
  102.  
  103. document.getElementById("sort-rw").addEventListener("click", async () => {
  104. await handleSorting("rw");
  105. });
  106.  
  107. document.getElementById("sort-price").addEventListener("click", async () => {
  108. await handleSorting("price");
  109. });
  110.  
  111. categoriesList.addEventListener("click", () => {
  112. handleTabChange();
  113. });
  114.  
  115. async function handleSorting(type) {
  116. parentElement = document.querySelectorAll('[aria-hidden="false"]');
  117.  
  118. if (type === "price" && !document.querySelector(".tt-item-price")) {
  119. alert(
  120. "Inventory Sorter requires Torn Tools to work properly. Make sure you install it before using this script!"
  121. );
  122. return;
  123. }
  124.  
  125. if (!posOriginal && !loadedAll) {
  126. posOriginal = window.scrollY;
  127. await loadAllItems();
  128. }
  129.  
  130. if (!itemsOriginal || sortState === "default") {
  131. cacheOriginalItems();
  132. }
  133.  
  134. if (type === "rw") {
  135. sortItems(sortRW([...itemsOriginal]), parentElement);
  136. sortState = "rw";
  137. } else if (type === "price") {
  138. sortItems(sortPrices([...itemsOriginal]), parentElement);
  139. }
  140.  
  141. localStorage.setItem("sortState", sortState);
  142. }
  143.  
  144. function cacheOriginalItems() {
  145. itemsOriginal = Array.from(parentElement[0].childNodes).map((itemEl) => {
  146. const priceText = itemEl.querySelector(".tt-item-price")?.lastChild?.textContent || "0";
  147. const price = +priceText.replace(/[^0-9.-]+/g, "");
  148.  
  149. const imageWrap = itemEl.querySelector(".image-wrap");
  150. const isRW =
  151. imageWrap &&
  152. (imageWrap.classList.contains("glow-yellow") ||
  153. imageWrap.classList.contains("glow-orange") ||
  154. imageWrap.classList.contains("glow-red"));
  155.  
  156. let colorPriority = 4;
  157. if (imageWrap.classList.contains("glow-red")) colorPriority = 1;
  158. else if (imageWrap.classList.contains("glow-orange")) colorPriority = 2;
  159. else if (imageWrap.classList.contains("glow-yellow")) colorPriority = 3;
  160.  
  161. return { element: itemEl, price, isRW, colorPriority };
  162. });
  163. }
  164.  
  165. function sortRW(items) {
  166. return items.sort((a, b) => {
  167. if (a.isRW && !b.isRW) return -1;
  168. if (!a.isRW && b.isRW) return 1;
  169. return a.colorPriority - b.colorPriority;
  170. });
  171. }
  172.  
  173. function sortPrices(items) {
  174. if (sortState === "price-descending") {
  175. sortState = "price-ascending";
  176. return items.sort((a, b) => a.price - b.price);
  177. } else {
  178. sortState = "price-descending";
  179. return items.sort((a, b) => b.price - a.price);
  180. }
  181. }
  182.  
  183. function resetSorting() {
  184. if (itemsOriginal && parentElement) {
  185. sortItems(itemsOriginal, parentElement);
  186. }
  187. sortState = "default";
  188. localStorage.setItem("sortState", sortState);
  189. itemsOriginal = null;
  190. posOriginal = null;
  191. loadedAll = false;
  192. }
  193.  
  194. function sortItems(_items, _parentElement) {
  195. _items.forEach((item) => _parentElement[0].appendChild(item.element));
  196. }
  197.  
  198. function handleTabChange() {
  199. resetSorting();
  200. }
  201.  
  202. async function loadAllItems() {
  203. const loadMoreDesc = document.querySelector("#load-more-items-desc");
  204. if (!loadMoreDesc) {
  205. console.error("#load-more-items-desc element not found!");
  206. return;
  207. }
  208.  
  209. const text = loadMoreDesc.textContent;
  210.  
  211. if (text.toLowerCase().includes("full")) {
  212. window.scroll(0, posOriginal);
  213. loadedAll = true;
  214. return;
  215. }
  216.  
  217. if (text.toLowerCase().includes("load more")) {
  218. const lastItem = document.querySelector(".items-wrap").lastElementChild;
  219. if (lastItem) {
  220. lastItem.scrollIntoView();
  221. await new Promise((resolve) => setTimeout(resolve, 500));
  222. return loadAllItems();
  223. }
  224. }
  225. }
  226.  
  227. const savedSortState = localStorage.getItem("sortState");
  228. if (savedSortState && savedSortState !== "default") {
  229. handleSorting(savedSortState);
  230. }
  231.  
  232. if (window.matchMedia("(max-width: 600px)").matches) {
  233. titleBarEl.addEventListener("click", () => {
  234. if (sortState === "default") {
  235. handleSorting("rw");
  236. } else {
  237. resetSorting();
  238. }
  239. });
  240. }
  241. }
  242.  
  243. const observer = new MutationObserver((mutations, obs) => {
  244. const titleBarEl = document.querySelector(".title-black.hospital-dark.top-round.scroll-dark[role='heading'][aria-level='5']");
  245. if (titleBarEl) {
  246. init();
  247. obs.disconnect();
  248. }
  249. });
  250.  
  251. observer.observe(document.body, { childList: true, subtree: true });
  252. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址