// ==UserScript== // @name Fanatical Keys Backup // @namespace Lex@GreasyFork // @version 0.2.1 // @description Displays a text area with game titles and keys so you can copy them out easily. // @author Lex // @match https://www.fanatical.com/en/orders/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; // Formats games array to a string to be displayed // Games is an array [ [title, key], ... ] function formatGames(games) { // Ignore games which do not have keys revealed games = games.filter(e => e[1]); // Format the output as tab-separated games = games.map(e => e[0]+"\t"+e[1]); return games.join("\n"); } function getGames(bundle) { let is = bundle.querySelectorAll(".order-item"); return Array.prototype.map.call(is, i => { const gameTitleElement = i.getElementsByClassName("game-name"); const gameTitle = gameTitleElement.length > 0 ? gameTitleElement[0].textContent.trim() : ""; const keyElement = i.querySelector("[aria-label='reveal-key']"); const gameKey = keyElement ? keyElement.value : ""; return [gameTitle, gameKey]; }); } function revealAllKeys(bundle) { const revealButtons = document.querySelectorAll(".key-container button.btn-block"); revealButtons.forEach(b => { b.click() }); this.style.display = "none"; } function createRevealButton(bundle) { const notify = bundle.querySelector(".ktt-notify"); let btn = document.createElement("button"); btn.type = "button"; // no default behavior btn.innerText = "Reveal All Keys"; btn.onclick = revealAllKeys.bind(btn, bundle); return btn; } // Adds a textarea to the bottom of the games listing with all the titles and keys function handleBundle(bundle) { const bundleName = bundle.querySelector("h5.my-4") ? bundle.querySelector("h5.my-4").textContent.trim() : "No Title"; let games = getGames(bundle); const gameCount = games.length; const keyCount = games.filter(e => e[1]).length; const gameStr = formatGames(games); let notify = bundle.querySelector(".ktt-notify"); if (!notify) { notify = document.createElement("div"); notify.className = "ktt-notify"; bundle.append(notify); if (gameCount != keyCount) { const btn = createRevealButton(bundle); notify.before(btn); } } const color = gameCount == keyCount ? "" : "red"; let newInner = `Dumping keys for ${bundleName}: Found ${gameCount} items and ${keyCount} keys.`; if (gameCount != keyCount) { newInner += " Are some keys not revealed?"; } if (notify.innerHTML != newInner) { notify.innerHTML = newInner; } let area = bundle.querySelector(".ktt"); if (!area) { area = document.createElement("textarea"); area.className = "ktt"; area.style.width = "100%"; area.setAttribute('readonly', true); bundle.append(area); } if (area.value != gameStr) { area.value = gameStr; // Adjust the height so all the contents are visible area.style.height = ""; area.style.height = area.scrollHeight + 20 + "px"; } } var loopCount = 0; function handleOrderPage() { // There can be more than one bundle in an order const bundles = document.querySelectorAll("hr.mb-4 ~ div"); if (bundles.length > 0) { console.log(`Found ${bundles.length} bundle(s)`); bundles.forEach(handleBundle); setTimeout(handleOrderPage, 2000); } else { if (loopCount++ < 100) { setTimeout(handleOrderPage, 100); } } } handleOrderPage(); })();