Items sorter with RW weapon filter

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Items sorter with RW weapon filter
// @namespace    [email protected]
// @description  Allows you to sort your items, by RW or price.
// @version      0.1
// @author       Nurv [669537]
// @license      Copyright IronNerd.me
// @match        https://www.torn.com/item.php*

// ==/UserScript==

(function () {
  "use strict";


  function init() {
    const titleBarEl = document.querySelector(".title-black.hospital-dark.top-round.scroll-dark[role='heading'][aria-level='5']");

    if (!titleBarEl) {
      console.error("Title bar element not found!");
      return;
    }

    if (document.getElementById("sort-default")) {
      console.log("Sorting buttons already added.");
      return;
    }

    const controlPanel = document.createElement("div");
    controlPanel.style.display = "inline-block";
    controlPanel.style.marginLeft = "20px";
    controlPanel.style.gap = "10px";
    controlPanel.style.verticalAlign = "middle";

    const buttonStyle = `
      padding: 8px 12px;
      cursor: pointer;
      color: white;
      background-color: #555;
      border: none;
      border-radius: 4px;
      font-size: 14px;
      transition: background-color 0.3s;
    `;

    const styleSheet = document.createElement("style");
    styleSheet.type = "text/css";
    styleSheet.innerText = `
      #sort-default:hover { background-color: #777; }
      #sort-rw:hover { background-color: #888; }
      #sort-price:hover { background-color: #999; }

      #sort-default, #sort-rw, #sort-price {
        border: 1px solid #333;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
      }

      @media (max-width: 600px) {
        #sort-default, #sort-rw, #sort-price {
          font-size: 12px;
          padding: 6px 8px;
        }

        .control-panel {
          flex-direction: column;
          align-items: flex-start;
        }
      }
    `;
    document.head.appendChild(styleSheet);

    controlPanel.innerHTML = `
      <button id="sort-default" style="${buttonStyle}" aria-label="Sort items to default order">Default</button>
      <button id="sort-rw" style="${buttonStyle}" aria-label="Sort RW weapons">RW</button>
      <button id="sort-price" style="${buttonStyle}" aria-label="Sort items by price">Price</button>
    `;

    const itemsSorterIcon = titleBarEl.querySelector(".items-sorter");
    if (itemsSorterIcon && itemsSorterIcon.parentNode) {
      itemsSorterIcon.parentNode.appendChild(controlPanel);
      console.log("Sorting buttons added to the page.");
    } else {
      console.error("Items sorter icon's parent not found.");
      return;
    }

    const categoriesList = document.querySelector("#categoriesList");
    if (!categoriesList) {
      console.error("Categories list not found!");
      return;
    }

    let sortState = "default";
    let parentElement;
    let itemsOriginal;
    let posOriginal;
    let loadedAll = false;

    document.getElementById("sort-default").addEventListener("click", () => {
      resetSorting();
    });

    document.getElementById("sort-rw").addEventListener("click", async () => {
      await handleSorting("rw");
    });

    document.getElementById("sort-price").addEventListener("click", async () => {
      await handleSorting("price");
    });

    categoriesList.addEventListener("click", () => {
      handleTabChange();
    });

    async function handleSorting(type) {
      parentElement = document.querySelectorAll('[aria-hidden="false"]');

      if (type === "price" && !document.querySelector(".tt-item-price")) {
        alert(
          "Inventory Sorter requires Torn Tools to work properly. Make sure you install it before using this script!"
        );
        return;
      }

      if (!posOriginal && !loadedAll) {
        posOriginal = window.scrollY;
        await loadAllItems();
      }

      if (!itemsOriginal || sortState === "default") {
        cacheOriginalItems();
      }

      if (type === "rw") {
        sortItems(sortRW([...itemsOriginal]), parentElement);
        sortState = "rw";
      } else if (type === "price") {
        sortItems(sortPrices([...itemsOriginal]), parentElement);
      }

      localStorage.setItem("sortState", sortState);
    }

    function cacheOriginalItems() {
      itemsOriginal = Array.from(parentElement[0].childNodes).map((itemEl) => {
        const priceText = itemEl.querySelector(".tt-item-price")?.lastChild?.textContent || "0";
        const price = +priceText.replace(/[^0-9.-]+/g, "");

        const imageWrap = itemEl.querySelector(".image-wrap");
        const isRW =
          imageWrap &&
          (imageWrap.classList.contains("glow-yellow") ||
            imageWrap.classList.contains("glow-orange") ||
            imageWrap.classList.contains("glow-red"));

        let colorPriority = 4;
        if (imageWrap.classList.contains("glow-red")) colorPriority = 1;
        else if (imageWrap.classList.contains("glow-orange")) colorPriority = 2;
        else if (imageWrap.classList.contains("glow-yellow")) colorPriority = 3;

        return { element: itemEl, price, isRW, colorPriority };
      });
    }

    function sortRW(items) {
      return items.sort((a, b) => {
        if (a.isRW && !b.isRW) return -1;
        if (!a.isRW && b.isRW) return 1;
        return a.colorPriority - b.colorPriority;
      });
    }

    function sortPrices(items) {
      if (sortState === "price-descending") {
        sortState = "price-ascending";
        return items.sort((a, b) => a.price - b.price);
      } else {
        sortState = "price-descending";
        return items.sort((a, b) => b.price - a.price);
      }
    }

    function resetSorting() {
      if (itemsOriginal && parentElement) {
        sortItems(itemsOriginal, parentElement);
      }
      sortState = "default";
      localStorage.setItem("sortState", sortState);
      itemsOriginal = null;
      posOriginal = null;
      loadedAll = false;
    }

    function sortItems(_items, _parentElement) {
      _items.forEach((item) => _parentElement[0].appendChild(item.element));
    }

    function handleTabChange() {
      resetSorting();
    }

    async function loadAllItems() {
      const loadMoreDesc = document.querySelector("#load-more-items-desc");
      if (!loadMoreDesc) {
        console.error("#load-more-items-desc element not found!");
        return;
      }

      const text = loadMoreDesc.textContent;

      if (text.toLowerCase().includes("full")) {
        window.scroll(0, posOriginal);
        loadedAll = true;
        return;
      }

      if (text.toLowerCase().includes("load more")) {
        const lastItem = document.querySelector(".items-wrap").lastElementChild;
        if (lastItem) {
          lastItem.scrollIntoView();
          await new Promise((resolve) => setTimeout(resolve, 500));
          return loadAllItems();
        }
      }
    }

    const savedSortState = localStorage.getItem("sortState");
    if (savedSortState && savedSortState !== "default") {
      handleSorting(savedSortState);
    }

    if (window.matchMedia("(max-width: 600px)").matches) {
      titleBarEl.addEventListener("click", () => {
        if (sortState === "default") {
          handleSorting("rw");
        } else {
          resetSorting();
        }
      });
    }
  }

  const observer = new MutationObserver((mutations, obs) => {
    const titleBarEl = document.querySelector(".title-black.hospital-dark.top-round.scroll-dark[role='heading'][aria-level='5']");
    if (titleBarEl) {
      init();
      obs.disconnect();
    }
  });

  observer.observe(document.body, { childList: true, subtree: true });
})();