Last active
September 3, 2024 19:21
-
-
Save ihatecsv/09f3a9df30d13f6bcdd945069f4e51c6 to your computer and use it in GitHub Desktop.
Scryfall Forge MTG and Mage buttons
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Scryfall to Forge MTG and Mage | |
// @namespace http://tampermonkey.net/ | |
// @version 1.0.11 | |
// @description Fetch Forge MTG and Mage card data on Scryfall card pages. | |
// @match https://scryfall.com/card/* | |
// @grant GM_xmlhttpRequest | |
// @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/java.min.js | |
// @resource HLJS_CSS https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css | |
// @grant GM_addStyle | |
// @grant GM_getResourceText | |
// @author ihatecsv | |
// ==/UserScript== | |
(function () { | |
"use strict"; | |
// Add highlight.js CSS | |
GM_addStyle(GM_getResourceText("HLJS_CSS")); | |
const STATE = { | |
forgeData: "", | |
mageData: "", | |
buttonContainer: null, | |
forgeToggleButton: null, | |
mageToggleButton: null, | |
githubButton: null, | |
cardName: "", | |
modal: null, | |
modalContent: null, | |
closeButton: null, | |
forgeSize: 0, | |
mageSize: 0, | |
forgeRetryState: 0, // 0: letter folders, 1: upcoming, 2: rebalanced | |
}; | |
function formatFileSize(bytes) { | |
return bytes < 1024 ? `${bytes} B` : `${(bytes / 1024).toFixed(1)} KB`; | |
} | |
function getGithubUrl(source, raw = false) { | |
const { cardName } = STATE; | |
let url; | |
if (source === "forge") { | |
const firstLetter = cardName.charAt(0).toLowerCase(); | |
let formattedCardName; | |
if (STATE.forgeRetryState === 2) { | |
// For rebalanced cards, keep the 'a-' prefix and replace other non-alphanumeric characters with underscores | |
formattedCardName = | |
cardName.slice(0, 2) + cardName.slice(2).replace(/[^a-z0-9]/g, "_"); | |
} else { | |
formattedCardName = cardName.toLowerCase().replace(/[^a-z0-9]/g, "_"); | |
} | |
if (STATE.forgeRetryState === 0) { | |
url = `https://github.com/Card-Forge/forge/blob/master/forge-gui/res/cardsfolder/${firstLetter}/${formattedCardName}.txt`; | |
} else if (STATE.forgeRetryState === 1) { | |
url = `https://github.com/Card-Forge/forge/blob/master/forge-gui/res/cardsfolder/upcoming/${formattedCardName}.txt`; | |
} else { | |
url = `https://github.com/Card-Forge/forge/blob/master/forge-gui/res/cardsfolder/rebalanced/${formattedCardName}.txt`; | |
} | |
} else { | |
const basicLands = ["plains", "island", "swamp", "mountain", "forest"]; | |
if (basicLands.includes(cardName.toLowerCase())) { | |
const capitalizedCardName = | |
cardName.charAt(0).toUpperCase() + cardName.slice(1).toLowerCase(); | |
url = `https://github.com/magefree/mage/blob/master/Mage/src/main/java/mage/cards/basiclands/${capitalizedCardName}.java`; | |
} else { | |
const camelCaseCardName = cardName | |
.split("-") | |
.map( | |
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() | |
) | |
.join(""); | |
url = `https://github.com/magefree/mage/blob/master/Mage.Sets/src/mage/cards/${camelCaseCardName | |
.charAt(0) | |
.toLowerCase()}/${camelCaseCardName}.java`; | |
} | |
} | |
return raw | |
? url | |
.replace("github.com", "raw.githubusercontent.com") | |
.replace("/blob", "") | |
: url; | |
} | |
function fetchCardData(source) { | |
const url = getGithubUrl(source, true); | |
const button = | |
source === "forge" ? STATE.forgeToggleButton : STATE.mageToggleButton; | |
const dataKey = `${source}Data`; | |
const sizeKey = `${source}Size`; | |
button.disabled = true; | |
button.style.cursor = "wait"; | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: url, | |
onload: function (response) { | |
if (response.status === 200) { | |
STATE[dataKey] = response.responseText; | |
STATE[sizeKey] = response.responseText.length; | |
button.textContent = `${ | |
source === "forge" ? "Forge" : "XMage" | |
} (${formatFileSize(STATE[sizeKey])})`; | |
button.style.backgroundColor = "#343242"; | |
button.disabled = false; | |
button.style.cursor = "pointer"; | |
} else if (source === "forge" && STATE.forgeRetryState < 2) { | |
// Retry with the next folder for Forge | |
STATE.forgeRetryState++; | |
fetchCardData("forge"); | |
} else { | |
console.log(`Card not found in ${source} repo`); | |
button.style.backgroundColor = "#870000"; | |
button.disabled = true; | |
button.style.cursor = "not-allowed"; | |
} | |
}, | |
onerror: function (error) { | |
console.error(`Error fetching ${source} data:`, error); | |
button.style.backgroundColor = "#c85309"; | |
button.disabled = true; | |
button.style.cursor = "not-allowed"; | |
}, | |
}); | |
} | |
function createButton(text, onClick, styles = {}) { | |
const button = document.createElement("button"); | |
button.textContent = text; | |
button.addEventListener("click", onClick); | |
Object.assign(button.style, { | |
color: "#fff", | |
backgroundColor: "#808080", | |
border: "none", | |
cursor: "wait", | |
padding: "0", | |
transition: "all 0.3s ease", | |
...styles, | |
}); | |
button.disabled = true; | |
return button; | |
} | |
function addToggleButtons() { | |
const cardTextOracle = document.querySelector(".prints"); | |
if (cardTextOracle && !STATE.forgeToggleButton && !STATE.mageToggleButton) { | |
STATE.buttonContainer = document.createElement("div"); | |
Object.assign(STATE.buttonContainer.style, { | |
display: "flex", | |
justifyContent: "space-between", | |
marginBottom: "25px", | |
}); | |
STATE.forgeToggleButton = createButton( | |
"Forge", | |
() => showModal("forge"), | |
{ width: "49%", height: "24px" } | |
); | |
STATE.mageToggleButton = createButton("XMage", () => showModal("mage"), { | |
width: "49%", | |
height: "24px", | |
}); | |
STATE.buttonContainer.appendChild(STATE.forgeToggleButton); | |
STATE.buttonContainer.appendChild(STATE.mageToggleButton); | |
cardTextOracle.insertBefore( | |
STATE.buttonContainer, | |
cardTextOracle.firstChild | |
); | |
fetchCardData("forge"); | |
fetchCardData("mage"); | |
} | |
} | |
function createModal() { | |
STATE.modal = document.createElement("div"); | |
STATE.modalContent = document.createElement("div"); | |
STATE.closeButton = document.createElement("span"); | |
Object.assign(STATE.modal.style, { | |
display: "none", | |
position: "fixed", | |
zIndex: "1000", | |
left: "0", | |
top: "0", | |
width: "100%", | |
height: "100%", | |
backgroundColor: "rgba(0,0,0,0.4)", | |
overflow: "auto", | |
}); | |
Object.assign(STATE.modalContent.style, { | |
backgroundColor: "#fefefe", | |
margin: "5% auto", | |
padding: "20px", | |
border: "1px solid #888", | |
width: "80%", | |
maxWidth: "800px", | |
maxHeight: "80%", | |
overflowY: "auto", | |
position: "relative", | |
borderRadius: "15px", | |
}); | |
Object.assign(STATE.closeButton.style, { | |
color: "#aaa", | |
float: "right", | |
fontSize: "28px", | |
fontWeight: "bold", | |
cursor: "pointer", | |
}); | |
STATE.closeButton.textContent = "×"; | |
STATE.closeButton.onclick = closeModal; | |
STATE.modalContent.appendChild(STATE.closeButton); | |
STATE.modal.appendChild(STATE.modalContent); | |
document.body.appendChild(STATE.modal); | |
STATE.modal.onclick = function (event) { | |
if (event.target === STATE.modal) { | |
closeModal(); | |
} | |
}; | |
} | |
function showModal(source) { | |
if (!STATE.modal) { | |
createModal(); | |
} | |
const data = source === "forge" ? STATE.forgeData : STATE.mageData; | |
const size = source === "forge" ? STATE.forgeSize : STATE.mageSize; | |
const header = document.createElement("h2"); | |
header.textContent = `${ | |
source === "forge" ? "Forge" : "XMage" | |
} Data (${formatFileSize(size)})`; | |
header.style.marginTop = "0"; | |
header.style.marginBottom = "15px"; | |
const preElement = document.createElement("pre"); | |
Object.assign(preElement.style, { | |
margin: "0", | |
whiteSpace: "no-wrap", | |
wordBreak: "break-word", | |
overflowX: "auto", | |
maxHeight: "400px", | |
backgroundColor: "#f0f0f0", | |
padding: "8px", | |
borderRadius: "8px", | |
marginTop: "10px", | |
border: "1px solid #ccc", | |
}); | |
preElement.textContent = data; | |
if (source === "mage") { | |
preElement.className = "language-java"; | |
hljs.highlightElement(preElement); | |
} | |
STATE.modalContent.innerHTML = ""; | |
STATE.modalContent.appendChild(STATE.closeButton); | |
STATE.modalContent.appendChild(header); | |
STATE.modalContent.appendChild(preElement); | |
STATE.githubButton = document.createElement("a"); | |
STATE.githubButton.textContent = "View on GitHub"; | |
STATE.githubButton.href = getGithubUrl(source); | |
STATE.githubButton.target = "_blank"; | |
Object.assign(STATE.githubButton.style, { | |
display: "inline-block", | |
padding: "8px 16px", | |
backgroundColor: "#24292e", | |
color: "#fff", | |
textDecoration: "none", | |
borderRadius: "4px", | |
fontSize: "14px", | |
marginTop: "10px", | |
textAlign: "center", | |
cursor: "pointer", | |
}); | |
STATE.modalContent.appendChild(STATE.githubButton); | |
STATE.modal.style.display = "block"; | |
document.body.style.overflow = "hidden"; | |
} | |
function closeModal() { | |
if (STATE.modal) { | |
STATE.modal.style.display = "none"; | |
document.body.style.overflow = "auto"; | |
} | |
} | |
STATE.cardName = window.location.pathname.split("/").pop(); | |
addToggleButtons(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment