Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status
// ==UserScript==
// @name Challengers Tower - Inventory Export
// @description Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status
// @namespace https://challengerstower.com
// @version 1.7.0
// @author Zach
// @license MIT
// @match *://challengerstower.com/*
// @run-at document-end
// @grant none
// ==/UserScript==
function ctCopy(str) {
var ta = document.createElement("textarea");
ta.value = str;
ta.style.position = "fixed";
ta.style.left = "-9999px";
document.body.appendChild(ta);
ta.select();
var ok = false;
try { ok = document.execCommand("copy"); } catch(e) {}
document.body.removeChild(ta);
return ok;
}
function ctGetAlpineData(el) {
// Try different Alpine versions' internal property names
if(el._x_dataStack && el._x_dataStack[0]) return el._x_dataStack[0];
if(el.__x && el.__x.$data) return el.__x.$data;
if(el._x_data) return el._x_data;
try {
if(typeof Alpine !== "undefined" && Alpine.$data) return Alpine.$data(el);
} catch(e) {}
return null;
}
function ctFindGroups() {
var els = document.querySelectorAll("[x-data]");
for(var i=0; i<els.length; i++) {
var data = ctGetAlpineData(els[i]);
if(data && data.groups && data.groups.length > 0) {
return data.groups;
}
}
return null;
}
function ctScrapeDOM() {
// Fallback: scrape from rendered DOM, inventory section only
var results = [];
var seen = {};
// Find the inventory section by looking for the "Inventory" heading
var headings = document.querySelectorAll("h3");
var inventorySection = null;
for(var i=0; i<headings.length; i++) {
if(headings[i].textContent.trim() === "Inventory") {
inventorySection = headings[i].closest(".bg-white, .bg-gray-800");
break;
}
}
var container = inventorySection || document;
var nameEls = container.querySelectorAll("span.font-medium");
nameEls.forEach(function(el) {
var name = el.textContent.trim();
if(!name || seen[name]) return;
var card = el.closest(".bg-gray-100, .rounded-lg");
if(!card) return;
var cardText = card.textContent || "";
var tierMatch = cardText.match(/\bT([0-9]+)\b/);
var tier = tierMatch ? "T" + tierMatch[1] : "";
var isUlt = /\bULT\b/.test(cardText) ? " [ULT]" : "";
var equipped = /Equipped/i.test(cardText) ? " [Equipped]" : "";
var upgradeMatch = cardText.match(/T[0-9]+:\s*[0-9]+%/);
var progress = upgradeMatch ? " (" + upgradeMatch[0] + ")" : "";
seen[name] = true;
results.push(name + (tier ? " [" + tier + "]" : "") + isUlt + progress + equipped);
});
return results.length > 0 ? results : null;
}
function ctScrape() {
var groups = ctFindGroups();
if(groups) {
return groups.map(function(g) {
var tier = "T" + g.highest_tier;
var isUlt = g.item.ultimate ? " [ULT]" : "";
var count = g.total_count > 1 ? " x" + g.total_count : "";
var progress = g.maxed_out ? " [MAX]" : " (T" + g.next_tier + ": " + Math.round(g.progress) + "%)";
var equipped = g.equipped_tiers.length > 0 ? " [Equipped]" : "";
return g.item.name + " [" + tier + "]" + isUlt + count + progress + equipped;
});
}
// Fall back to DOM scraping
return ctScrapeDOM();
}
function ctAddButton() {
if(document.getElementById("ct-inv-export")) return;
if(!/\/items/.test(window.location.href)) return;
var btn = document.createElement("button");
btn.id = "ct-inv-export";
btn.textContent = "Export Inventory";
btn.style.cssText = "position:fixed;bottom:80px;right:16px;z-index:99999;padding:10px 16px;background:#4f8ef7;color:#fff;border:none;border-radius:8px;font-weight:bold;font-size:14px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.35);";
btn.onclick = function() {
var lines = ctScrape();
if(!lines) {
btn.textContent = "Not ready yet";
btn.style.background = "#e74c3c";
setTimeout(function() {
btn.textContent = "Export Inventory";
btn.style.background = "#4f8ef7";
}, 2000);
return;
}
var out = ["=== CT Inventory (" + lines.length + " items) ==="].concat(lines).join("\n");
console.log(out);
var ok = ctCopy(out);
btn.textContent = ok ? "Copied! (" + lines.length + ")" : "See console";
btn.style.background = ok ? "#2ecc71" : "#e74c3c";
setTimeout(function() {
btn.textContent = "Export Inventory";
btn.style.background = "#4f8ef7";
}, 3000);
};
document.body.appendChild(btn);
}
var ctAttempts = 0;
var ctPoll = setInterval(function() {
ctAttempts++;
if(!/\/items/.test(window.location.href)) {
clearInterval(ctPoll);
return;
}
if(document.body && (ctFindGroups() || ctAttempts >= 40)) {
ctAddButton();
clearInterval(ctPoll);
}
}, 500);