Compare commits

...

2 Commits

Author SHA1 Message Date
Caleb Fultz
7902212982 cleanup 2024-08-21 18:04:18 -04:00
Caleb Fultz
6d93ad6203 refactor 2024-08-21 18:03:33 -04:00
20 changed files with 708 additions and 603 deletions

273
app.js
View File

@ -1,273 +0,0 @@
// Define setTheme globally to handle theme changes
window.setTheme = function setTheme(theme) {
document.body.className = ''; // Clear existing themes
document.body.classList.add(theme);
// Save the selected theme to localStorage
localStorage.setItem('selectedTheme', theme);
};
let cardList = [];
let soundThreshold = 1.00; // Default sound threshold
// Make toggleMenu globally accessible
window.toggleMenu = function toggleMenu() {
const menuContent = document.getElementById('menu-content');
menuContent.classList.toggle('show');
};
document.addEventListener('DOMContentLoaded', () => {
const savedTheme = localStorage.getItem('selectedTheme');
if (savedTheme) {
setTheme(savedTheme);
}
const savedThreshold = localStorage.getItem('soundThreshold');
if (savedThreshold) {
soundThreshold = parseFloat(savedThreshold);
document.getElementById('sound-threshold').value = savedThreshold;
}
document.getElementById('add-card-button').addEventListener('click', addCard);
document.getElementById('download-csv-button').addEventListener('click', downloadCSV);
document.getElementById('card-name').addEventListener('input', async function() {
const query = this.value;
const suggestions = await fetchCardSuggestions(query);
const suggestionsDiv = document.getElementById('suggestions');
suggestionsDiv.innerHTML = '';
suggestions.forEach(suggestion => {
const div = document.createElement('div');
div.textContent = suggestion;
div.onclick = () => {
document.getElementById('card-name').value = suggestion;
suggestionsDiv.innerHTML = '';
};
suggestionsDiv.appendChild(div);
});
});
// Update sound threshold based on user input
document.getElementById('sound-threshold').addEventListener('input', (event) => {
soundThreshold = parseFloat(event.target.value);
localStorage.setItem('soundThreshold', soundThreshold); // Save the threshold to localStorage
});
// Check and apply dark mode
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (isDarkMode) {
document.body.classList.add('dark-mode');
}
});
async function fetchCardSuggestions(query) {
try {
const response = await fetch(`https://api.scryfall.com/cards/autocomplete?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data.data;
} catch (error) {
console.error('Fetch error:', error);
return [];
}
}
document.getElementById('card-name').addEventListener('input', async function() {
const query = this.value.trim();
if (query.length < 2) { // Only search for queries longer than 1 character
document.getElementById('suggestions').innerHTML = '';
return;
}
const suggestions = await fetchCardSuggestions(query);
const suggestionsDiv = document.getElementById('suggestions');
suggestionsDiv.innerHTML = ''; // Clear previous suggestions
suggestions.forEach(suggestion => {
const div = document.createElement('div');
div.textContent = suggestion;
div.onclick = () => {
document.getElementById('card-name').value = suggestion;
suggestionsDiv.innerHTML = ''; // Clear suggestions after selection
};
suggestionsDiv.appendChild(div);
});
});
async function fetchCardDetails(cardName, setCode) {
// Use the Scryfall 'named' endpoint to fetch the exact card by name and set code
const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`);
const data = await response.json();
console.log('Fetched Data from Scryfall:', data); // Debugging: Log the fetched data
// Return the relevant card details
return {
set: data.set.toUpperCase(),
card_id: data.collector_number,
mana_cost: data.mana_cost || 'N/A',
power: data.power || 'N/A',
toughness: data.toughness || 'N/A',
type: data.type_line.split('—')[0].trim(),
rarity: data.rarity || 'N/A',
price: data.prices.usd || '0.00',
imageUrl: data.image_uris ? data.image_uris.small : null,
name: data.name
};
}
async function addCard() {
const cardName = document.getElementById('card-name').value;
const setCodeInput = document.getElementById('set-code');
const cardIdInput = document.getElementById('card-id');
// Fetch card details using the card name and set code
let setCode = setCodeInput.value;
let cardId = cardIdInput.value;
// Always fetch card details
const cardDetails = await fetchCardDetails(cardName, setCode);
if (cardDetails) {
// If set code or card ID is not filled, auto-fill them
if (!setCode) {
setCode = cardDetails.set;
setCodeInput.value = setCode;
}
if (!cardId) {
cardId = cardDetails.card_id;
cardIdInput.value = cardId;
}
// Update card details with either user input or fetched data
updateCardDetails({
name: cardName,
set: setCode,
card_id: cardId,
mana_cost: cardDetails.mana_cost,
power: cardDetails.power,
toughness: cardDetails.toughness,
type: cardDetails.type,
rarity: cardDetails.rarity,
price: cardDetails.price,
imageUrl: cardDetails.imageUrl
});
} else {
alert("Card not found with the given name.");
}
}
function updateCardDetails(printing) {
document.getElementById('set-code').value = printing.set;
document.getElementById('card-id').value = printing.card_id;
const quantity = document.getElementById('quantity').value;
const foil = document.getElementById('foil').value;
const cardData = {
name: document.getElementById('card-name').value,
set: printing.set,
card_id: printing.card_id,
quantity: quantity,
foil: foil,
mana_type: printing.mana_cost,
power: printing.power,
toughness: printing.toughness,
type: printing.type,
rarity: printing.rarity
};
cardList.push(cardData);
// Update the textarea with the new card information
let csvContent = generateCSVContent();
document.getElementById('csv-output').value = csvContent;
// Show the card image, name, and price in the dissolving popup
showCardPopup(cardData.name, printing.imageUrl, printing.price);
}
function generateCSVContent() {
let csvContent = "Card Name,Set,Card ID,Quantity,Foil,Mana Type,Power,Toughness,Type,Rarity\n";
cardList.forEach(card => {
const escapedValues = [
card.name,
card.set,
card.card_id,
card.quantity,
card.foil,
card.mana_type,
card.power,
card.toughness,
card.type,
card.rarity
].map(value => `"${value.replace(/"/g, '""')}"`); // Escape any existing quotes by doubling them
csvContent += escapedValues.join(",") + "\n";
});
return csvContent;
}
async function showCardPopup(cardName, imageUrl, price) {
const cardPopup = document.getElementById('card-popup');
cardPopup.innerHTML = `
<div style="font-family: 'Sorts Mill Goudy', serif; text-align: center;">
<strong>${cardName}</strong>
</div>
<img src="${imageUrl}" alt="${cardName}">
<div style="font-family: 'Sorts Mill Goudy', serif; text-align: center; margin-top: 10px;">
Price: $${price}
</div>
`;
cardPopup.style.display = 'block';
cardPopup.style.opacity = 1;
if (parseFloat(price) >= soundThreshold) {
playSound();
}
// Dissolve the popup after a few seconds
setTimeout(() => {
cardPopup.style.opacity = 0;
setTimeout(() => {
cardPopup.style.display = 'none';
}, 500);
}, 3000);
}
function playSound() {
const audio = new Audio('cash-money.mp3');
audio.play();
}
function getRandomWordsFromFile() {
let randomWordsSelected = [];
for (let i = 0; i < 3; i++) { // Get three random words
const randomIndex = Math.floor(Math.random() * randomWords.length);
randomWordsSelected.push(randomWords[randomIndex]);
}
return randomWordsSelected.join('-');
}
async function downloadCSV() {
const csvContent = generateCSVContent();
const randomFileName = getRandomWordsFromFile();
// Create a blob with the CSV content
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
// Create a temporary link element
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `${randomFileName}.csv`);
document.body.appendChild(link);
// Trigger the download
link.click();
// Clean up and remove the link
document.body.removeChild(link);
}

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

89
css/base.css Normal file
View File

@ -0,0 +1,89 @@
/* base.css */
:root {
--background-color: #f0f0f0;
--text-color: #333;
--card-background: #ffffff;
--button-background: #007acc;
--button-hover: #005fa3;
}
body {
font-family: 'Sorts Mill Goudy', serif;
background-color: var(--background-color);
color: var(--text-color);
margin: 0;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: var(--card-background);
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
h1 {
text-align: center;
margin-bottom: 20px;
}
form div {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input, select, textarea {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
background-color: var(--card-background);
color: var(--text-color);
}
button {
display: block;
width: 100%;
padding: 10px;
background-color: var(--button-background);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
button:hover {
background-color: var(--button-hover);
}
#csv-output {
margin-top: 20px;
height: 200px;
background-color: var(--card-background);
color: var(--text-color);
}
footer {
text-align: center;
font-size: 0.5rem;
color: #666;
margin-top: 0px;
padding: 1px;
}
footer a {
color: #007acc;
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}

106
css/components.css Normal file
View File

@ -0,0 +1,106 @@
/* components.css */
.mana-icon {
font-family: 'mana', sans-serif;
font-size: 24px;
cursor: pointer;
padding: 5px;
margin-right: 10px;
}
.mana-icon:hover {
opacity: 0.8;
}
.menu {
position: relative;
display: inline-block;
}
.hamburger-menu {
cursor: pointer;
font-size: 30px;
color: var(--text-color);
}
.menu-content {
display: none; /* Ensure it's hidden by default */
position: absolute;
top: 40px;
left: 0;
background-color: var(--background-color);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
padding: 10px;
z-index: 1;
min-width: 150px;
border-radius: 4px;
}
.menu-content.show {
display: block; /* Display menu when toggled */
}
.zoom-modal {
display: none; /* Ensure modal is hidden by default */
position: fixed;
z-index: 1000;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.8);
}
.zoomed-image {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
}
/* Dark mode styles */
/* Dark mode styles */
body.dark-mode .menu-content {
background-color: #333;
}
body.dark-mode .hamburger-menu,
body.dark-mode .menu-content label,
body.dark-mode .menu-content input {
color: white;
}
.card-popup {
position: fixed;
bottom: 20px;
right: 20px;
max-width: 20%;
z-index: 1000;
background-color: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
opacity: 0;
transition: opacity 0.5s ease-in-out;
display: none;
}
.card-popup img {
width: 100%;
height: auto;
border-radius: 4px;
}
@media (max-width: 600px) {
.card-popup {
max-width: 40%;
}
}
@media (max-width: 400px) {
.card-popup {
max-width: 60%;
}
}

102
css/themes.css Normal file
View File

@ -0,0 +1,102 @@
/* Light and Dark mode base theme */
:root {
--background-color: #f0f0f0;
--text-color: #333;
--card-background: #ffffff;
--button-background: #007acc;
--button-hover: #005fa3;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #1e1e1e;
--text-color: #ffffff;
--card-background: #2e2e2e;
--button-background: #1f6fb2;
--button-hover: #145b8c;
}
}
/* Mana Color Themes */
.theme-white {
--background-color: #f9f8f6;
--text-color: #1e1e1e;
--card-background: #ffffff;
--button-background: #e5e5e5;
--button-hover: #cccccc;
}
.theme-blue {
--background-color: #d7e7f9;
--text-color: #002d72;
--card-background: #e0f2ff;
--button-background: #007acc;
--button-hover: #005fa3;
}
.theme-black {
--background-color: #333333;
--text-color: #ffffff;
--card-background: #444444;
--button-background: #1f1f1f;
--button-hover: #000000;
}
.theme-red {
--background-color: #fbe4e4;
--text-color: #8b0000;
--card-background: #ffe5e5;
--button-background: #d32f2f;
--button-hover: #b71c1c;
}
.theme-green {
--background-color: #e7f4e7;
--text-color: #004d00;
--card-background: #e0ffe0;
--button-background: #388e3c;
--button-hover: #2e7d32;
}
/* Dark Mode Variants for Mana Themes */
@media (prefers-color-scheme: dark) {
.theme-white {
--background-color: #1e1e1e;
--text-color: #ffffff;
--card-background: #333333;
--button-background: #555555;
--button-hover: #777777;
}
.theme-blue {
--background-color: #001f3f;
--text-color: #ffffff;
--card-background: #002d72;
--button-background: #005fa3;
--button-hover: #007acc;
}
.theme-black {
--background-color: #000000;
--text-color: #e5e5e5;
--card-background: #1c1c1c;
--button-background: #333333;
--button-hover: #555555;
}
.theme-red {
--background-color: #330000;
--text-color: #ffaaaa;
--card-background: #8b0000;
--button-background: #b71c1c;
--button-hover: #d32f2f;
}
.theme-green {
--background-color: #002200;
--text-color: #ccffcc;
--card-background: #004d00;
--button-background: #2e7d32;
--button-hover: #388e3c;
}
}

View File

@ -1,28 +1,33 @@
<!-- index.html -->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MTG Card CSV Generator</title> <title>MTG Card CSV Generator</title>
<link rel="stylesheet" href="styles.css">
<!-- Stylesheets -->
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/themes.css">
<link rel="stylesheet" href="css/components.css">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link href="https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy&display=swap" rel="stylesheet">
<link rel="icon" href="icon.png" type="image/png"> <link rel="icon" href="assets/icon.png" type="image/png">
<link href="//cdn.jsdelivr.net/npm/mana-font@latest/css/mana.css" rel="stylesheet" type="text/css" /> <link href="https://cdn.jsdelivr.net/npm/mana-font@latest/css/mana.css" rel="stylesheet" type="text/css" />
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="menu"> <div class="menu">
<div class="hamburger-menu" onclick="toggleMenu()"></div> <div class="hamburger-menu" id="hamburger-menu"></div>
<div id="menu-content" class="menu-content"> <div id="menu-content" class="menu-content">
<label>Select Theme:</label> <label>Select Theme:</label>
<div id="theme-selector"> <div id="theme-selector">
<span class="mana-icon" onclick="setTheme('')">&#xe623;</span> <!-- Colorless --> <span class="mana-icon" data-theme="" onclick="setTheme('')">&#xe623;</span> <!-- Colorless -->
<span class="mana-icon" onclick="setTheme('theme-white')">&#xe600;</span> <!-- White --> <span class="mana-icon" data-theme="theme-white" onclick="setTheme('theme-white')">&#xe600;</span> <!-- White -->
<span class="mana-icon" onclick="setTheme('theme-blue')">&#xe601;</span> <!-- Blue --> <span class="mana-icon" data-theme="theme-blue" onclick="setTheme('theme-blue')">&#xe601;</span> <!-- Blue -->
<span class="mana-icon" onclick="setTheme('theme-black')">&#xe602;</span> <!-- Black --> <span class="mana-icon" data-theme="theme-black" onclick="setTheme('theme-black')">&#xe602;</span> <!-- Black -->
<span class="mana-icon" onclick="setTheme('theme-red')">&#xe603;</span> <!-- Red --> <span class="mana-icon" data-theme="theme-red" onclick="setTheme('theme-red')">&#xe603;</span> <!-- Red -->
<span class="mana-icon" onclick="setTheme('theme-green')">&#xe604;</span> <!-- Green --> <span class="mana-icon" data-theme="theme-green" onclick="setTheme('theme-green')">&#xe604;</span> <!-- Green -->
</div> </div>
<label for="sound-threshold">Sound Threshold (USD):</label> <label for="sound-threshold">Sound Threshold (USD):</label>
<input type="number" id="sound-threshold" min="0.01" step="0.01" value="1.00"> <input type="number" id="sound-threshold" min="0.01" step="0.01" value="1.00">
@ -30,6 +35,7 @@
</div> </div>
<h1>MTG Card CSV Generator</h1> <h1>MTG Card CSV Generator</h1>
<form id="card-form"> <form id="card-form">
<div> <div>
<label for="card-name">Card Name:</label> <label for="card-name">Card Name:</label>
@ -57,9 +63,23 @@
</div> </div>
<button type="button" id="add-card-button">Add Card</button> <button type="button" id="add-card-button">Add Card</button>
</form> </form>
<div class="action-buttons">
<button id="download-csv-button">Download CSV</button> <button id="download-csv-button">Download CSV</button>
<button id="save-collection-button">Save Collection</button>
<button id="clear-collection-button">Clear Collection</button>
</div>
<textarea id="csv-output" readonly></textarea> <textarea id="csv-output" readonly></textarea>
<div id="card-popup" class="card-popup"></div>
<div id="card-popup" class="card-popup">
<img id="card-popup-image" src="" alt="Card Image">
</div>
<div id="zoom-modal" class="zoom-modal">
<span id="close-modal" class="close-modal">&times;</span>
<img id="zoomed-image" class="zoomed-image" src="" alt="">
</div>
</div> </div>
<footer> <footer>
@ -69,10 +89,21 @@
</p> </p>
</footer> </footer>
<script type="module" src="app.js"></script> <!-- Scripts -->
<script type="module" src="js/theme.js"></script>
<script type="module" src="js/main.js"></script>
<script type="module" src="js/card.js"></script>
<script type="module" src="js/storage.js"></script>
<script type="module" src="js/csv.js"></script>
<script type="module" src="js/popup.js"></script>
<script> <script>
if (typeof navigator.serviceWorker !== 'undefined') { if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js') navigator.serviceWorker.register('js/sw.js').then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function(error) {
console.error('Service Worker registration failed:', error);
});
} }
</script> </script>
</body> </body>

37
js/api.js Normal file
View File

@ -0,0 +1,37 @@
// api.js
export async function fetchCardSuggestions(query) {
try {
const response = await fetch(`https://api.scryfall.com/cards/autocomplete?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data.data;
} catch (error) {
console.error('Fetch error:', error);
return [];
}
}
export async function fetchCardDetails(cardName, setCode) {
const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`);
const data = await response.json();
if (data.object === 'error') {
return null;
}
return {
set: data.set.toUpperCase(),
card_id: data.collector_number,
mana_cost: data.mana_cost || 'N/A',
power: data.power || 'N/A',
toughness: data.toughness || 'N/A',
type: data.type_line.split('—')[0].trim(),
rarity: data.rarity || 'N/A',
price: data.prices.usd || '0.00',
imageUrl: data.image_uris ? data.image_uris.small : null,
name: data.name
};
}

68
js/card.js Normal file
View File

@ -0,0 +1,68 @@
// card.js
import { updateCardDetails, loadCollection } from './storage.js';
import { showCardPopup } from './popup.js';
export async function addCard(cardList) {
const cardName = document.getElementById('card-name').value;
const setCodeInput = document.getElementById('set-code');
const cardIdInput = document.getElementById('card-id');
let setCode = setCodeInput.value;
let cardId = cardIdInput.value;
const cardDetails = await fetchCardDetails(cardName, setCode);
if (cardDetails) {
if (!setCode) {
setCode = cardDetails.set;
setCodeInput.value = setCode;
}
if (!cardId) {
cardId = cardDetails.card_id;
cardIdInput.value = cardId;
}
const cardData = {
name: cardName,
set: setCode,
card_id: cardId,
quantity: document.getElementById('quantity').value,
foil: document.getElementById('foil').value,
mana_cost: cardDetails.mana_cost,
power: cardDetails.power,
toughness: cardDetails.toughness,
type: cardDetails.type,
rarity: cardDetails.rarity,
price: cardDetails.price
};
updateCardDetails(cardData);
cardList.push(cardData); // Add the card to the list
showCardPopup(cardData.name, cardDetails.imageUrl, cardDetails.price);
} else {
alert("Card not found with the given name.");
}
}
export async function fetchCardDetails(cardName, setCode) {
const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`);
const data = await response.json();
if (data.object === 'error') {
return null;
}
return {
set: data.set.toUpperCase(),
card_id: data.collector_number,
mana_cost: data.mana_cost || 'N/A',
power: data.power || 'N/A',
toughness: data.toughness || 'N/A',
type: data.type_line.split('—')[0].trim(),
rarity: data.rarity || 'N/A',
price: data.prices.usd || '0.00',
imageUrl: data.image_uris ? data.image_uris.small : null,
name: data.name
};
}

44
js/csv.js Normal file
View File

@ -0,0 +1,44 @@
// csv.js
import { getRandomWordsFromFile } from './randomWords.js';
export function generateCSVContent(cardList) {
let csvContent = "Card Name,Set,Card ID,Quantity,Foil,Mana Type,Power,Toughness,Type,Rarity\n";
cardList.forEach(card => {
const escapedValues = [
card.name,
card.set,
card.card_id,
card.quantity,
card.foil,
card.mana_cost,
card.power,
card.toughness,
card.type,
card.rarity
].map(value => `"${value.replace(/"/g, '""')}"`); // Escape any existing quotes by doubling them
csvContent += escapedValues.join(",") + "\n";
});
return csvContent;
}
export async function downloadCSV(cardList) {
const csvContent = generateCSVContent(cardList);
const randomFileName = getRandomWordsFromFile();
// Create a blob with the CSV content
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
// Create a temporary link element
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", `${randomFileName}.csv`);
document.body.appendChild(link);
// Trigger the download
link.click();
// Clean up and remove the link
document.body.removeChild(link);
}

93
js/main.js Normal file
View File

@ -0,0 +1,93 @@
// main.js
import { addCard } from './card.js';
import { downloadCSV } from './csv.js';
import { saveCollection, loadCollection, clearCollection } from './storage.js';
import { setTheme, applySavedTheme } from './theme.js';
import { fetchCardSuggestions } from './api.js';
document.addEventListener('DOMContentLoaded', () => {
applySavedTheme();
let cardList = loadCollection();
document.getElementById('add-card-button').addEventListener('click', () => {
addCard(cardList);
updateCSVContent(cardList);
});
document.getElementById('download-csv-button').addEventListener('click', () => downloadCSV(cardList));
document.getElementById('save-collection-button').addEventListener('click', () => saveCollection(cardList));
document.getElementById('clear-collection-button').addEventListener('click', () => {
if (confirm("Are you sure you want to clear your collection? This action cannot be undone.")) {
cardList = []; // Reset the card list
clearCollection(cardList);
updateCSVContent(cardList);
}
});
document.getElementById('card-name').addEventListener('input', async function() {
const query = this.value.trim();
if (query.length < 2) {
document.getElementById('suggestions').innerHTML = '';
return;
}
const suggestions = await fetchCardSuggestions(query);
const suggestionsDiv = document.getElementById('suggestions');
suggestionsDiv.innerHTML = '';
suggestions.forEach(suggestion => {
const div = document.createElement('div');
div.textContent = suggestion;
div.onclick = () => {
document.getElementById('card-name').value = suggestion;
suggestionsDiv.innerHTML = '';
};
suggestionsDiv.appendChild(div);
});
});
document.getElementById('sound-threshold').addEventListener('input', (event) => {
const soundThreshold = parseFloat(event.target.value);
localStorage.setItem('soundThreshold', soundThreshold);
});
document.getElementById('hamburger-menu').addEventListener('click', () => {
const menuContent = document.getElementById('menu-content');
menuContent.classList.toggle('show');
});
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (isDarkMode) {
document.body.classList.add('dark-mode');
}
});
// Update the CSV content in the textarea
function updateCSVContent(cardList) {
// Sort the cardList by set and then by card ID
cardList.sort((a, b) => {
if (a.set < b.set) return -1;
if (a.set > b.set) return 1;
return parseInt(a.card_id) - parseInt(b.card_id);
});
let csvContent = "Card Name,Set,Card ID,Quantity,Foil,Mana Type,Power,Toughness,Type,Rarity\n";
cardList.forEach(card => {
const escapedValues = [
card.name || '',
card.set || '',
card.card_id || '',
card.quantity || '',
card.foil || '',
card.mana_type || '',
card.power || '',
card.toughness || '',
card.type || '',
card.rarity || ''
].map(value => `"${(value !== undefined ? value : '').replace(/"/g, '""')}"`);
csvContent += escapedValues.join(",") + "\n";
});
document.getElementById('csv-output').value = csvContent;
}

4
js/menu.js Normal file
View File

@ -0,0 +1,4 @@
export function toggleMenu() {
const menuContent = document.getElementById('menu-content');
menuContent.classList.toggle('show');
}

40
js/popup.js Normal file
View File

@ -0,0 +1,40 @@
// popup.js
export async function showCardPopup(cardName, imageUrl, price) {
const soundThreshold = parseFloat(localStorage.getItem('soundThreshold')) || 1.00;
const cardPopup = document.getElementById('card-popup');
cardPopup.innerHTML = `
<div style="font-family: 'Sorts Mill Goudy', serif; text-align: center;">
<strong>${cardName}</strong>
</div>
<img src="${imageUrl}" alt="${cardName}">
<div style="font-family: 'Sorts Mill Goudy', serif; text-align: center; margin-top: 10px;">
Price: $${price}
</div>
`;
cardPopup.style.display = 'block';
cardPopup.style.opacity = 1;
// Check if the price exceeds the sound threshold and play sound
if (parseFloat(price) >= soundThreshold) {
playSound(); // Play the sound if the price exceeds the threshold
}
// Dissolve the popup after a few seconds
setTimeout(() => {
cardPopup.style.opacity = 0;
setTimeout(() => {
cardPopup.style.display = 'none';
}, 500);
}, 3000);
}
function playSound() {
const audio = new Audio('assets/cash-money.mp3');
audio.play().then(() => {
console.log('Sound played successfully');
}).catch(error => {
console.error('Error playing sound:', error);
});
}

32
js/randomWords.js Normal file
View File

@ -0,0 +1,32 @@
// randomWords.js
export const randomWords = [
"nonstop", "detailed", "aspiring", "shock", "play", "bashful", "long", "quarter",
"six", "charge", "dock", "crabby", "dime", "wry", "story", "wiry", "rampant",
"return", "dare", "open", "shame", "many", "brush", "babies", "stem", "squeeze",
"judge", "heavenly", "defeated", "optimal", "invention", "object", "change",
"sink", "verdant", "jaded", "adjoining", "muddled", "switch", "helpless",
"motionless", "skirt", "shrug", "trip", "haircut", "oven", "lip", "habitual",
"yielding", "bag", "wheel", "attach", "ticket", "visit", "reflect", "suppose",
"present", "wound", "voyage", "real", "aunt", "religion", "redundant", "necessary",
"fail", "flower", "unpack", "join", "gamy", "tired", "welcome", "rightful", "jeans",
"obscene", "spring", "basket", "battle", "utter", "descriptive", "caring", "fry",
"resonant", "supply", "geese", "pets", "impulse", "scintillating", "tame", "release",
"tail", "depend", "lively", "nondescript", "punishment", "meek", "crooked",
"representative", "twist", "manage", "bored", "grotesque", "demonic", "camp",
"temporary", "coil", "passenger", "appliance", "clam", "smoggy", "tasteless", "guess",
"verse", "drab", "peep", "business", "paper", "female", "admire", "way", "moor",
"breezy", "opposite", "comparison", "tank", "suit", "ludicrous", "minister", "stiff",
"whine", "request", "camera", "internal", "improve", "unnatural", "decisive", "exist",
"grip", "electric", "bathe", "scandalous", "steer", "humdrum", "action", "rot",
"roll", "quartz", "amused", "sidewalk", "roll", "curve"
];
export function getRandomWordsFromFile() {
let randomWordsSelected = [];
for (let i = 0; i < 3; i++) {
const randomIndex = Math.floor(Math.random() * randomWords.length);
randomWordsSelected.push(randomWords[randomIndex]);
}
return randomWordsSelected.join('-');
}

24
js/storage.js Normal file
View File

@ -0,0 +1,24 @@
// storage.js
export function saveCollection(cardList) {
localStorage.setItem('cardList', JSON.stringify(cardList));
}
export function loadCollection() {
const savedCollection = localStorage.getItem('cardList');
if (savedCollection) {
return JSON.parse(savedCollection);
}
return [];
}
export function updateCardDetails(cardData) {
const cardList = loadCollection();
cardList.push(cardData);
saveCollection(cardList);
}
export function clearCollection(cardList) {
cardList.length = 0; // Clear the cardList array
saveCollection(cardList); // Save the empty list to localStorage
}

View File

19
js/theme.js Normal file
View File

@ -0,0 +1,19 @@
export function setTheme(theme) {
// Remove all theme classes first
document.body.classList.remove('theme-white', 'theme-blue', 'theme-black', 'theme-red', 'theme-green');
// Add the selected theme class if it exists
if (theme) {
document.body.classList.add(theme);
}
// Save the selected theme to localStorage
localStorage.setItem('selectedTheme', theme);
}
export function applySavedTheme() {
const savedTheme = localStorage.getItem('selectedTheme');
if (savedTheme) {
setTheme(savedTheme);
}
}

View File

@ -8,12 +8,12 @@
"description": "A PWA for generating CSV files with Magic: The Gathering card details", "description": "A PWA for generating CSV files with Magic: The Gathering card details",
"icons": [ "icons": [
{ {
"src": "icon.png", "src": "assets/icon.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "icon.png", "src": "assets/icon.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }

View File

@ -8,7 +8,6 @@
<link href="https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy&display=swap" rel="stylesheet">
<link rel="icon" href="icon.png" type="image/png"> <link rel="icon" href="icon.png" type="image/png">
<link href="//cdn.jsdelivr.net/npm/mana-font@latest/css/mana.css" rel="stylesheet" type="text/css" /> <link href="//cdn.jsdelivr.net/npm/mana-font@latest/css/mana.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
</head> </head>
<body> <body>
@ -27,9 +26,5 @@
This app is not affiliated with Wizards of the Coast or Hasbro. All rights and trademarks are owned by their respective owners. This app is not affiliated with Wizards of the Coast or Hasbro. All rights and trademarks are owned by their respective owners.
</p> </p>
</footer> </footer>
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
<script>
mdc.autoInit(); // Initialize all MDC components
</script>
</body> </body>
</html> </html>

View File

@ -1,306 +0,0 @@
:root {
--background-color: #f0f0f0;
--text-color: #333;
--card-background: #ffffff;
--button-background: #007acc;
--button-hover: #005fa3;
}
.material-theme {
--background-color: #ffffff;
--text-color: #000000;
--card-background: #f8f8f8;
--button-background: #6200ee;
--button-hover: #3700b3;
}
.theme-white {
--background-color: #f9f8f6;
--text-color: #1e1e1e;
--card-background: #ffffff;
--button-background: #e5e5e5;
--button-hover: #cccccc;
}
.theme-blue {
--background-color: #d7e7f9;
--text-color: #002d72;
--card-background: #e0f2ff;
--button-background: #007acc;
--button-hover: #005fa3;
}
.theme-black {
--background-color: #333333;
--text-color: #ffffff;
--card-background: #444444;
--button-background: #1f1f1f;
--button-hover: #000000;
}
.theme-red {
--background-color: #fbe4e4;
--text-color: #8b0000;
--card-background: #ffe5e5;
--button-background: #d32f2f;
--button-hover: #b71c1c;
}
.theme-green {
--background-color: #e7f4e7;
--text-color: #004d00;
--card-background: #e0ffe0;
--button-background: #388e3c;
--button-hover: #2e7d32;
}
@media (prefers-color-scheme: dark) {
.theme-white {
--background-color: #1e1e1e;
--text-color: #ffffff;
--card-background: #333333;
--button-background: #555555;
--button-hover: #777777;
}
.theme-blue {
--background-color: #001f3f;
--text-color: #ffffff;
--card-background: #002d72;
--button-background: #005fa3;
--button-hover: #007acc;
}
.theme-black {
--background-color: #000000;
--text-color: #e5e5e5;
--card-background: #1c1c1c;
--button-background: #333333;
--button-hover: #555555;
}
.theme-red {
--background-color: #330000;
--text-color: #ffaaaa;
--card-background: #8b0000;
--button-background: #b71c1c;
--button-hover: #d32f2f;
}
.theme-green {
--background-color: #002200;
--text-color: #ccffcc;
--card-background: #004d00;
--button-background: #2e7d32;
--button-hover: #388e3c;
}
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #1e1e1e;
--text-color: #ffffff;
--card-background: #2e2e2e;
--button-background: #1f6fb2;
--button-hover: #145b8c;
}
}
.mana-icon {
font-family: 'mana', sans-serif;
font-size: 24px;
cursor: pointer;
padding: 5px;
margin-right: 10px;
}
.mana-icon:hover {
opacity: 0.8;
}
/* Ensure the menu follows the theme */
body.theme-white .menu-content {
background-color: var(--background-color);
color: var(--text-color);
}
body.theme-blue .menu-content {
background-color: var(--background-color);
color: var(--text-color);
}
body.theme-black .menu-content {
background-color: var(--background-color);
color: var(--text-color);
}
body.theme-red .menu-content {
background-color: var(--background-color);
color: var(--text-color);
}
body.theme-green .menu-content {
background-color: var(--background-color);
color: var(--text-color);
}
body {
font-family: 'Sorts Mill Goudy', serif;
background-color: var(--background-color);
color: var(--text-color);
margin: 0;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
background-color: var(--card-background);
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
h1 {
text-align: center;
margin-bottom: 20px;
}
form div {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input, select, textarea {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
background-color: var(--card-background);
color: var(--text-color);
}
button {
display: block;
width: 100%;
padding: 10px;
background-color: var(--button-background);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
button:hover {
background-color: var(--button-hover);
}
#csv-output {
margin-top: 20px;
height: 200px;
background-color: var(--card-background);
color: var(--text-color);
}
.card-popup {
position: fixed;
bottom: 20px;
right: 20px;
max-width: 20%;
z-index: 1000;
background-color: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
opacity: 0;
transition: opacity 0.5s ease-in-out;
display: none;
}
.card-popup img {
width: 100%;
height: auto;
border-radius: 4px;
}
@media (max-width: 600px) {
.card-popup {
max-width: 40%;
}
}
@media (max-width: 400px) {
.card-popup {
max-width: 60%;
}
}
.menu {
position: relative;
display: inline-block;
}
.hamburger-menu {
cursor: pointer;
font-size: 30px;
color: var(--text-color);
}
.menu-content {
display: none;
position: absolute;
top: 40px;
left: 0;
background-color: var(--background-color);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
padding: 10px;
z-index: 1;
min-width: 150px;
border-radius: 4px;
}
.menu-content label,
.menu-content input {
color: var(--text-color);
}
.menu-content.show {
display: block;
}
/* Dark mode styles */
body.dark-mode .menu-content {
background-color: #333;
}
body.dark-mode .hamburger-menu,
body.dark-mode .menu-content label,
body.dark-mode .menu-content input {
color: white;
}
footer {
text-align: center;
font-size: 0.5rem; /* Smaller font size */
color: #666; /* A lighter color for the text */
margin-top: 0px;
padding: 1px;
}
footer a {
color: #007acc; /* Link color matching the theme */
text-decoration: none;
}
footer a:hover {
text-decoration: underline; /* Underline on hover for emphasis */
}