mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-04-25 08:04:33 -05:00
551 lines
20 KiB
TypeScript
551 lines
20 KiB
TypeScript
const deckListPage = document.getElementById('deckListPage')!;
|
|
const deckListBackButton = document.getElementById('deckListBackButton') as HTMLLinkElement;
|
|
const deckViewBackButton = document.getElementById('deckViewBackButton') as HTMLLinkElement;
|
|
const deckEditorDeckViewSection = document.getElementById('deckEditorDeckViewSection')!;
|
|
const deckNameLabel = document.getElementById('deckName')!;
|
|
const deckViewSize = document.getElementById('deckViewSize')!;
|
|
const deckList = document.getElementById('deckList')!;
|
|
const deckCardListView = document.getElementById('deckCardListView')!;
|
|
const addDeckControls = document.getElementById('addDeckControls')!;
|
|
const newDeckButton = document.getElementById('newDeckButton') as HTMLButtonElement;
|
|
const importDeckButton = document.getElementById('importDeckButton') as HTMLButtonElement;
|
|
|
|
const deckViewMenu = document.getElementById('deckViewMenu')!;
|
|
const deckViewMenuButton = document.getElementById('deckViewMenuButton') as HTMLButtonElement;
|
|
const deckSleevesButton = document.getElementById('deckSleevesButton') as HTMLButtonElement;
|
|
const deckEditButton = document.getElementById('deckEditButton') as HTMLButtonElement;
|
|
const deckListTestButton = document.getElementById('deckListTestButton') as HTMLButtonElement;
|
|
const deckExportButton = document.getElementById('deckExportButton') as HTMLButtonElement;
|
|
const deckRenameButton = document.getElementById('deckRenameButton') as HTMLButtonElement;
|
|
const deckCopyButton = document.getElementById('deckCopyButton') as HTMLButtonElement;
|
|
const deckDeleteButton = document.getElementById('deckDeleteButton') as HTMLButtonElement;
|
|
|
|
const deckExportDialog = document.getElementById('deckExportDialog') as HTMLDialogElement;
|
|
const deckExportCopyButton = document.getElementById('deckExportCopyButton') as HTMLButtonElement;
|
|
const deckExportTextBox = document.getElementById('deckExportTextBox') as HTMLTextAreaElement;
|
|
|
|
const deckExportAllButton = document.getElementById('deckExportAllButton') as HTMLButtonElement;
|
|
|
|
const deckSleevesDialog = document.getElementById('deckSleevesDialog') as HTMLDialogElement;
|
|
const deckSleevesForm = document.getElementById('deckSleevesForm') as HTMLFormElement;
|
|
const deckSleevesButtons = deckSleevesForm.getElementsByTagName('input');
|
|
const deckSleevesOkButton = document.getElementById('deckSleevesOkButton') as HTMLButtonElement;
|
|
|
|
const deckImportDialog = document.getElementById('deckImportDialog') as HTMLDialogElement;
|
|
const deckImportForm = document.getElementById('deckImportForm') as HTMLFormElement;
|
|
const deckImportTextBox = document.getElementById('deckImportTextBox') as HTMLTextAreaElement;
|
|
const deckImportTextButton = document.getElementById('deckImportTextButton') as HTMLInputElement;
|
|
const deckImportTextSection = document.getElementById('deckImportTextSection')!;
|
|
const deckImportScreenshotButton = document.getElementById('deckImportScreenshotButton') as HTMLInputElement;
|
|
const deckImportScreenshotSection = document.getElementById('deckImportScreenshotSection')!;
|
|
const deckImportScreenshotInstructionsButton = document.getElementById('deckImportScreenshotInstructionsButton') as HTMLButtonElement;
|
|
const deckImportScreenshotInstructionsButtonPC = document.getElementById('deckImportScreenshotInstructionsButtonPC') as HTMLInputElement;
|
|
const deckImportScreenshotInstructionsButtonMobile = document.getElementById('deckImportScreenshotInstructionsButtonMobile') as HTMLInputElement;
|
|
const deckImportScreenshotInstructions = document.getElementById('deckImportScreenshotInstructions')!;
|
|
const deckImportScreenshotInstructionsPC = document.getElementById('deckImportScreenshotInstructionsPC')!;
|
|
const deckImportScreenshotInstructionsMobile = document.getElementById('deckImportScreenshotInstructionsMobile')!;
|
|
const deckImportFileBox = document.getElementById('deckImportFileBox') as HTMLInputElement;
|
|
const deckImportErrorBox = document.getElementById('deckImportErrorBox')!;
|
|
const deckImportOkButton = document.getElementById('deckImportOkButton') as HTMLButtonElement;
|
|
|
|
const deckButtons = new CheckButtonGroup<SavedDeck>(deckList);
|
|
|
|
let deckListTouchMode = false;
|
|
let draggingDeckButton: Element | null = null;
|
|
|
|
function showDeckList() {
|
|
showPage('deckList');
|
|
deselectDeck();
|
|
deckButtons.deselect();
|
|
}
|
|
|
|
deckList.addEventListener('touchstart', deckListEnableTouchMode);
|
|
|
|
function deckListEnableTouchMode() {
|
|
if (deckListTouchMode) return;
|
|
deckListTouchMode = true;
|
|
deckListPage.classList.add('touchmode');
|
|
for (var b of deckButtons.buttons) {
|
|
b.buttonElement.draggable = false;
|
|
(b.buttonElement.getElementsByClassName('handle')[0] as HTMLElement).draggable = true;
|
|
}
|
|
}
|
|
|
|
deckListBackButton.addEventListener('click', e => {
|
|
e.preventDefault();
|
|
showPage('preGame');
|
|
|
|
if (canPushState) {
|
|
try {
|
|
history.pushState(null, '', '.');
|
|
} catch {
|
|
canPushState = false;
|
|
}
|
|
}
|
|
if (location.hash)
|
|
location.hash = '';
|
|
});
|
|
|
|
deckViewBackButton.addEventListener('click', e => {
|
|
e.preventDefault();
|
|
clearChildren(deckCardListView);
|
|
deselectDeck();
|
|
deckListPage.classList.remove('showingDeck');
|
|
deckViewMenu.classList.remove('showing');
|
|
});
|
|
|
|
function saveDecks() {
|
|
const json = JSON.stringify(decks.filter(d => !d.isReadOnly), [ 'name', 'cards', 'sleeves', 'upgrades' ]);
|
|
localStorage.setItem('decks', json);
|
|
}
|
|
|
|
{
|
|
const decksString = localStorage.getItem('decks');
|
|
if (decksString) {
|
|
for (const deck of JSON.parse(decksString)) {
|
|
decks.push(SavedDeck.fromJson(deck));
|
|
}
|
|
} else {
|
|
const lastDeckString = localStorage.getItem('lastDeck');
|
|
const lastDeck = lastDeckString?.split(/\+/)?.map(s => parseInt(s));
|
|
if (lastDeck && lastDeck.length == 15) {
|
|
decks.push(new SavedDeck('Custom Deck', 0, lastDeck, new Array(15).fill(1), false));
|
|
saveDecks();
|
|
}
|
|
localStorage.removeItem('lastDeck');
|
|
}
|
|
|
|
for (let i = 0; i < decks.length; i++) {
|
|
createDeckButton(decks[i]);
|
|
}
|
|
}
|
|
|
|
function createDeckButton(deck: SavedDeck) {
|
|
const buttonElement = document.createElement('button');
|
|
buttonElement.className = 'deckButton';
|
|
buttonElement.type = 'button';
|
|
buttonElement.dataset.sleeves = deck.sleeves.toString();
|
|
const button = new CheckButton(buttonElement);
|
|
deckButtons.add(button, deck);
|
|
buttonElement.addEventListener('click', () => {
|
|
selectedDeck = deckButtons.value;
|
|
selectDeck();
|
|
});
|
|
buttonElement.addEventListener('dragstart', e => {
|
|
if (e.dataTransfer == null) return;
|
|
const index = decks.indexOf(deck);
|
|
draggingDeckButton = buttonElement;
|
|
e.dataTransfer.effectAllowed = 'copyMove';
|
|
e.dataTransfer.setData('text/plain', serialiseDecks([ deck ]));
|
|
e.dataTransfer.setData('application/tableturf-deck-index', index.toString());
|
|
buttonElement.classList.add('dragging');
|
|
});
|
|
buttonElement.addEventListener('dragend', e => {
|
|
buttonElement.classList.remove('dragging');
|
|
if (draggingDeckButton != null && e.currentTarget == draggingDeckButton) {
|
|
const index = deckButtons.entries.findIndex(el => el.button.buttonElement == e.currentTarget) + 1;
|
|
deckList.insertBefore(draggingDeckButton, index >= deckButtons.entries.length ? null : deckButtons.entries[index].button.buttonElement);
|
|
draggingDeckButton = null;
|
|
}
|
|
});
|
|
buttonElement.addEventListener('dragenter', e => e.preventDefault());
|
|
buttonElement.addEventListener('dragover', deckButton_dragover);
|
|
buttonElement.addEventListener('drop', deckButton_drop);
|
|
|
|
const handle = document.createElement('div');
|
|
handle.className = 'handle';
|
|
buttonElement.appendChild(handle);
|
|
buttonElement.appendChild(document.createTextNode(deck.name));
|
|
(deckListTouchMode ? handle : buttonElement).draggable = true;
|
|
|
|
return button;
|
|
}
|
|
|
|
function deckButton_dragover(e: DragEvent) {
|
|
e.preventDefault();
|
|
if (e.dataTransfer == null) return;
|
|
if (draggingDeckButton != null) {
|
|
e.dataTransfer.dropEffect = 'move';
|
|
if (e.currentTarget != draggingDeckButton && e.currentTarget != deckList) {
|
|
// Move the deck being dragged into the new position as a preview.
|
|
for (let el = draggingDeckButton.nextElementSibling; el != null; el = el.nextElementSibling) {
|
|
if (el == e.currentTarget) {
|
|
deckList.insertBefore(draggingDeckButton, el.nextElementSibling);
|
|
return;
|
|
}
|
|
}
|
|
deckList.insertBefore(draggingDeckButton, e.currentTarget as Node);
|
|
}
|
|
} else if (e.dataTransfer.types.includes('text/plain'))
|
|
e.dataTransfer.dropEffect = 'copy';
|
|
}
|
|
|
|
function deckButton_drop(e: DragEvent) {
|
|
e.preventDefault();
|
|
if (e.dataTransfer == null) return;
|
|
if (draggingDeckButton != null) {
|
|
const index = deckButtons.entries.findIndex(el => el.button.buttonElement == draggingDeckButton);
|
|
let newIndex = 0;
|
|
for (let el = deckList.firstElementChild; el != null; el = el.nextElementSibling) {
|
|
if (el == draggingDeckButton) break;
|
|
newIndex++;
|
|
}
|
|
if (index < 0 || newIndex == index) return;
|
|
console.log(`Moving deck ${index} to ${newIndex}.`);
|
|
|
|
deckButtons.move(index, newIndex);
|
|
const deck = decks[index];
|
|
decks.splice(index, 1);
|
|
decks.splice(newIndex, 0, deck);
|
|
saveDecks();
|
|
draggingDeckButton = null;
|
|
return;
|
|
}
|
|
const text = e.dataTransfer.getData('text/plain');
|
|
if (text) {
|
|
try {
|
|
importDecks(parseDecksForImport(text));
|
|
} catch (ex: any) {
|
|
alert(`Couldn't import dropped deck data: ${ex}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
function importDecks(decksToImport: DeckFullExport) {
|
|
let newSelectedDeck: SavedDeck | null = null;
|
|
|
|
// Merge custom cards.
|
|
const customCardNumbers: {[key: number]: number} = { };
|
|
if (decksToImport.customCards) {
|
|
console.log('Merging custom cards...');
|
|
for (const key in decksToImport.customCards) {
|
|
const incomingCard = Card.fromJson(decksToImport.customCards[key]);
|
|
const existingCard = cardDatabase.customCards.find(c => c.isTheSameAs(incomingCard));
|
|
if (existingCard) {
|
|
console.log(`Incoming card (${key}) ${incomingCard.name} matches existing (${existingCard.number}).`);
|
|
customCardNumbers[key] = existingCard.number;
|
|
} else {
|
|
const newNumber = CUSTOM_CARD_START - cardDatabase.customCards.length;
|
|
console.log(`Adding incoming card (${key}) ${incomingCard.name} as (${newNumber}).`);
|
|
incomingCard.number = newNumber;
|
|
cardDatabase.customCards.push(incomingCard);
|
|
addCardToGallery(incomingCard);
|
|
cardDatabase.customCardsModified = true;
|
|
customCardNumbers[key] = incomingCard.number;
|
|
}
|
|
}
|
|
console.log('Done merging custom cards.');
|
|
saveCustomCards();
|
|
}
|
|
|
|
// Import decks.
|
|
for (const el of decksToImport.decks) {
|
|
let deck;
|
|
if (Array.isArray(el))
|
|
deck = new SavedDeck(`Imported Deck ${decks.length + 1}`, 0, el, new Array(15).fill(1), false);
|
|
else {
|
|
deck = el;
|
|
deck.sleeves ??= 0;
|
|
deck.upgrades ??= new Array(15).fill(1);
|
|
deck.isReadOnly = false;
|
|
if (!deck.name) deck.name = `Imported Deck ${decks.length + 1}`;
|
|
}
|
|
for (let i = 0; i < deck.cards.length; i++) {
|
|
if (deck.cards[i] <= CUSTOM_CARD_START)
|
|
deck.cards[i] = customCardNumbers[deck.cards[i]];
|
|
}
|
|
createDeckButton(deck);
|
|
decks.push(deck);
|
|
newSelectedDeck ??= deck;
|
|
}
|
|
if (newSelectedDeck) {
|
|
selectedDeck = newSelectedDeck;
|
|
deckButtons.deselect();
|
|
deckButtons.entries.find(e => e.value == newSelectedDeck)!.button.checked = true;
|
|
deckButtons.value = newSelectedDeck;
|
|
selectDeck();
|
|
saveDecks();
|
|
}
|
|
}
|
|
|
|
newDeckButton.addEventListener('click', () => {
|
|
selectedDeck = new SavedDeck(`Deck ${decks.length + 1}`, 0, new Array(15).fill(0), new Array(15).fill(1), false);
|
|
createDeckButton(selectedDeck);
|
|
decks.push(selectedDeck);
|
|
editDeck();
|
|
});
|
|
importDeckButton.addEventListener('click', () => {
|
|
deckImportErrorBox.hidden = true;
|
|
deckImportTextSection.hidden = true;
|
|
deckImportScreenshotSection.hidden = true;
|
|
deckImportScreenshotInstructions.hidden = true;
|
|
deckImportScreenshotInstructionsPC.hidden = true;
|
|
deckImportScreenshotInstructionsMobile.hidden = true;
|
|
deckImportTextButton.checked = false;
|
|
deckImportScreenshotButton.checked = false;
|
|
deckImportScreenshotInstructionsButtonPC.checked = false;
|
|
deckImportScreenshotInstructionsButtonMobile.checked = false;
|
|
deckImportScreenshotInstructionsButton.innerText = 'Show instructions';
|
|
deckImportDialog.showModal();
|
|
});
|
|
deckImportForm.addEventListener('submit', e => {
|
|
if (e.submitter == deckImportOkButton) {
|
|
try {
|
|
importDecks(parseDecksForImport(deckImportTextBox.value));
|
|
} catch (ex: any) {
|
|
e.preventDefault();
|
|
deckImportErrorBox.innerText = ex.message;
|
|
deckImportErrorBox.hidden = false;
|
|
deckImportTextBox.focus();
|
|
}
|
|
}
|
|
});
|
|
|
|
function parseDecksForImport(s: string) : DeckFullExport {
|
|
let isKoishiShareUrl = false;
|
|
const pos = s.indexOf('deck=');
|
|
if (pos >= 0) {
|
|
isKoishiShareUrl = true;
|
|
s = decodeURIComponent(s.substring(pos + 5));
|
|
}
|
|
const data = JSON.parse(s);
|
|
if (Array.isArray(data)) {
|
|
if (data.length > 0 && typeof(data[0]) == 'number') {
|
|
if (data.length > 15 || data.find(i => typeof(i) != 'number' || !cardDatabase.isValidCardNumber(isKoishiShareUrl ? i : i + 1)))
|
|
throw new SyntaxError('Invalid deck data');
|
|
if (isKoishiShareUrl)
|
|
return { decks: [ data ] }; // tableturf.koishi.top share URL
|
|
else
|
|
return { decks: [ data.map(n => n + 1) ] }; // Tooltip export data
|
|
} else {
|
|
for (const deck of data) {
|
|
if (typeof(deck) != 'object' || !Array.isArray(deck.cards) || deck.cards.length > 15 || (deck.cards as any[]).find((i => typeof(i) != 'number' || !cardDatabase.isValidCardNumber(i))))
|
|
throw new SyntaxError('Invalid JSON deck');
|
|
}
|
|
return { decks: data.map(SavedDeck.fromJson) }; // Our export data without custom cards
|
|
}
|
|
} else if (typeof(data) == 'object') {
|
|
if ('decks' in data) {
|
|
// Our export data with custom cards
|
|
const fullExport = data as DeckFullExport;
|
|
fullExport.decks = fullExport.decks.map(SavedDeck.fromJson);
|
|
return fullExport;
|
|
} else {
|
|
// Our old export data
|
|
if (!Array.isArray(data.cards) || data.cards.length > 15 || (data.cards as any[]).find((i => typeof(i) != 'number' || !cardDatabase.isValidOfficialCardNumber(i))))
|
|
throw new SyntaxError('Invalid JSON deck');
|
|
return { decks: [ SavedDeck.fromJson(data) ] };
|
|
}
|
|
} else
|
|
throw new SyntaxError('Invalid JSON deck');
|
|
}
|
|
|
|
deckViewMenuButton.addEventListener('click', () => {
|
|
deckViewMenu.classList.toggle('showing');
|
|
});
|
|
|
|
deckViewMenu.addEventListener('click', () => {
|
|
deckViewMenu.classList.remove('showing');
|
|
});
|
|
|
|
deckSleevesButton.addEventListener('click', () => {
|
|
if (selectedDeck == null) return;
|
|
deckSleevesButtons[selectedDeck.sleeves].checked = true;
|
|
deckSleevesDialog.showModal();
|
|
});
|
|
deckSleevesForm.addEventListener('submit', e => {
|
|
if (e.submitter == deckSleevesOkButton && selectedDeck != null) {
|
|
let i = 0;
|
|
for (const button of deckSleevesButtons) {
|
|
if (button.checked) {
|
|
selectedDeck.sleeves = i;
|
|
deckButtons.entries[decks.indexOf(selectedDeck)].button.buttonElement.dataset.sleeves = i.toString();
|
|
saveDecks();
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
});
|
|
|
|
deckEditButton.addEventListener('click', editDeck);
|
|
|
|
deckListTestButton.addEventListener('click', () => {
|
|
testStageButtons.deselect();
|
|
testStageSelectionDialog.showModal();
|
|
});
|
|
|
|
function selectDeck() {
|
|
clearChildren(deckCardListView);
|
|
if (selectedDeck == null) return;
|
|
|
|
let size = 0;
|
|
|
|
deckNameLabel.innerText = selectedDeck.name;
|
|
for (const cardNumber of selectedDeck.cards) {
|
|
if (cardNumber) {
|
|
const card = cardDatabase.get(cardNumber);
|
|
size += card.size;
|
|
|
|
const button = new CardButton(card);
|
|
button.buttonElement.disabled = true;
|
|
deckCardListView.appendChild(button.buttonElement);
|
|
}
|
|
}
|
|
|
|
deckListTestButton.disabled = false;
|
|
deckExportButton.disabled = false;
|
|
deckCopyButton.disabled = false;
|
|
deckSleevesButton.disabled = selectedDeck.isReadOnly ?? false;
|
|
deckEditButton.disabled = selectedDeck.isReadOnly ?? false;
|
|
deckRenameButton.disabled = selectedDeck.isReadOnly ?? false;
|
|
deckDeleteButton.disabled = selectedDeck.isReadOnly ?? false;
|
|
deckViewSize.innerText = size.toString();
|
|
deckListPage.classList.add('showingDeck');
|
|
}
|
|
|
|
function deselectDeck() {
|
|
selectedDeck = null;
|
|
deckButtons.deselect();
|
|
clearChildren(deckCardListView);
|
|
deckNameLabel.innerText = '\u00a0';
|
|
deckViewSize.innerText = '0';
|
|
deckListTestButton.disabled = true;
|
|
deckExportButton.disabled = true;
|
|
deckCopyButton.disabled = true;
|
|
deckSleevesButton.disabled = true;
|
|
deckEditButton.disabled = true;
|
|
deckRenameButton.disabled = true;
|
|
deckDeleteButton.disabled = true;
|
|
deckListPage.classList.remove('showingDeck');
|
|
}
|
|
|
|
function deckExportJsonReplacer(key: string, value: any) {
|
|
switch (key) {
|
|
case 'isReadOnly':
|
|
case 'number':
|
|
case 'altNumber':
|
|
case 'artFileName':
|
|
case 'specialCost':
|
|
case 'size':
|
|
case 'textScale':
|
|
case 'isVariantOf':
|
|
case 'minX':
|
|
case 'minY':
|
|
case 'maxX':
|
|
case 'maxY':
|
|
return undefined;
|
|
case 'line1':
|
|
case 'line2':
|
|
return value ?? undefined; // Omit null values.
|
|
case 'imageUrl':
|
|
// Custom cards store image data here, so include it if it is a data URI.
|
|
return (<string> value).startsWith('data:') ? value : undefined;
|
|
default:
|
|
return value;
|
|
}
|
|
}
|
|
|
|
function serialiseDecks(decks: SavedDeck[]) {
|
|
let customCards: {[key: number]: Card} | null = null;
|
|
for (const deck of decks) {
|
|
for (const number of deck.cards) {
|
|
if (number <= CUSTOM_CARD_START) {
|
|
customCards ??= { };
|
|
customCards[number] = cardDatabase.customCards[CUSTOM_CARD_START - number];
|
|
}
|
|
}
|
|
}
|
|
return JSON.stringify(customCards != null ? { decks, customCards } : decks, deckExportJsonReplacer);
|
|
}
|
|
|
|
deckExportButton.addEventListener('click', () => {
|
|
if (selectedDeck == null) return;
|
|
const json = serialiseDecks([ selectedDeck ]);
|
|
deckExportTextBox.value = json;
|
|
deckExportCopyButton.innerText = 'Copy';
|
|
deckExportDialog.showModal();
|
|
});
|
|
deckExportCopyButton.addEventListener('click', () => {
|
|
navigator.clipboard.writeText(deckExportTextBox.value);
|
|
deckExportCopyButton.innerText = 'Copied';
|
|
});
|
|
|
|
deckRenameButton.addEventListener('click', () => {
|
|
if (selectedDeck == null) return;
|
|
const name = prompt(`What will you rename ${selectedDeck.name} to?`, selectedDeck.name)?.trim();
|
|
if (name) {
|
|
selectedDeck.name = name;
|
|
deckNameLabel.innerText = name;
|
|
deckButtons.entries[decks.indexOf(selectedDeck)].button.buttonElement.lastChild!.nodeValue = name;
|
|
saveDecks();
|
|
}
|
|
});
|
|
|
|
deckCopyButton.addEventListener('click', () => {
|
|
if (selectedDeck == null) return;
|
|
importDecks({ decks: [ new SavedDeck(`${selectedDeck.name} - Copy`, selectedDeck.sleeves, Array.from(selectedDeck.cards), Array.from(selectedDeck.upgrades), false) ] });
|
|
});
|
|
|
|
deckDeleteButton.addEventListener('click', () => {
|
|
if (selectedDeck == null) return;
|
|
if (!confirm(`Are you sure you want to delete ${selectedDeck.name}?`)) return;
|
|
|
|
const index = decks.indexOf(selectedDeck);
|
|
if (index >= 0) decks.splice(index, 1);
|
|
deckButtons.removeAt(index);
|
|
deselectDeck();
|
|
saveDecks();
|
|
});
|
|
|
|
deckImportTextButton.addEventListener('input', () => {
|
|
deckImportTextSection.hidden = false;
|
|
deckImportScreenshotSection.hidden = true;
|
|
});
|
|
deckImportScreenshotButton.addEventListener('input', () => {
|
|
deckImportTextSection.hidden = true;
|
|
deckImportScreenshotSection.hidden = false;
|
|
});
|
|
|
|
deckImportScreenshotInstructionsButton.addEventListener('click', () => {
|
|
if (deckImportScreenshotInstructions.hidden) {
|
|
deckImportScreenshotInstructions.hidden = false;
|
|
deckImportScreenshotInstructionsButton.innerText = 'Hide instructions';
|
|
} else {
|
|
deckImportScreenshotInstructions.hidden = true;
|
|
deckImportScreenshotInstructionsButton.innerText = 'Show instructions';
|
|
}
|
|
});
|
|
deckImportScreenshotInstructionsButtonPC.addEventListener('input', () => {
|
|
deckImportScreenshotInstructionsPC.hidden = false;
|
|
deckImportScreenshotInstructionsMobile.hidden = true;
|
|
});
|
|
deckImportScreenshotInstructionsButtonMobile.addEventListener('input', () => {
|
|
deckImportScreenshotInstructionsPC.hidden = true;
|
|
deckImportScreenshotInstructionsMobile.hidden = false;
|
|
});
|
|
|
|
deckImportFileBox.addEventListener('change', async () => {
|
|
if (deckImportFileBox.files && deckImportFileBox.files.length > 0) {
|
|
try {
|
|
const bitmaps = await Promise.all(Array.from(deckImportFileBox.files, f => createImageBitmap(f)));
|
|
importDecks({ decks: bitmaps.map(getCardListFromImageBitmap) });
|
|
deckImportDialog.close();
|
|
} catch (ex: any) {
|
|
deckImportErrorBox.innerText = ex.message;
|
|
deckImportErrorBox.hidden = false;
|
|
}
|
|
deckImportFileBox.value = '';
|
|
}
|
|
});
|
|
|
|
deckExportAllButton.addEventListener('click', () => {
|
|
const json = serialiseDecks(decks.filter(d => !d.isReadOnly));
|
|
deckExportTextBox.value = json;
|
|
deckExportCopyButton.innerText = 'Copy';
|
|
deckExportDialog.showModal();
|
|
});
|
|
|
|
if (!canPushState)
|
|
deckListBackButton.href = '#';
|