From 933c275243a9d62f7fb7ef6dcbcd8ee5e52878cd Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Thu, 12 Oct 2017 02:27:17 -0400 Subject: [PATCH] Add support for hero and villain decks Should definitely have been smaller commits, but oh well, too lazy to split them up now important note: switched editor from XMLHttpRequest to fetch --- js/editor.js | 112 +++++++++--------- server.js | 4 +- template/environment/input.json | 1 + template/hero/character-back.svg | 2 +- .../{character-front.svg => character.svg} | 0 template/hero/input.json | 12 ++ template/villain/input.json | 35 ++++++ template/villain/instructions.svg | 24 ++-- 8 files changed, 122 insertions(+), 68 deletions(-) rename template/hero/{character-front.svg => character.svg} (100%) diff --git a/js/editor.js b/js/editor.js index 9d98679..be37ba6 100644 --- a/js/editor.js +++ b/js/editor.js @@ -10,10 +10,13 @@ document.title = "Editor|" + deckName; window.addEventListener("load", () => { // load deck input json - getJSON("deck.input.json", json => { - deckJSON = json; - makeSVGs(deckJSON); - }); + fetch("deck.input.json") + .then(data => data.json()) + .then(json => { + deckJSON = json; + makeSVGs(deckJSON); + }) + .catch(error => console.error(error)); // deck JSON uploader document.querySelector('#jsonUpload').addEventListener('change', event => { @@ -96,55 +99,54 @@ function downloadFile(file, name) { document.body.removeChild(dl); } -function getJSON(filename, callback) { - let xhr = new XMLHttpRequest(); - xhr.addEventListener("load", () => { - if (xhr.status === 200) { - callback(JSON.parse(xhr.responseText)); - } - }); - xhr.open("GET", filename); - xhr.send(); -} - function getSVGTemplate(name, callback) { - let xhr = new XMLHttpRequest(); - xhr.addEventListener("load", () => { - let respSVG = xhr.responseXML.children[0]; - callback(respSVG); - }); - xhr.open("GET", "/template/" + name + ".svg"); - xhr.send(); + return fetch("/template/" + name + ".svg") + .then(response => response.text()) + .then(str => (new window.DOMParser()).parseFromString(str, "text/xml").activeElement); } -function makeSVGs(deckJSON) { +async function makeSVGs(deckJSON) { document.querySelector('#deckName').value = deckJSON.name || ""; document.querySelector('#deckType').value = deckJSON.type || ""; let deck = document.querySelector('#deck'); deck.innerHTML = ""; - setDeckTemplate(deckJSON.type, () => { - Object.entries(template.cardTypes).forEach(cardType => { - getSVGTemplate(deckJSON.type + "/" + cardType[0], templateSVG => { - deck.style.width = Math.ceil(Math.sqrt(deckJSON.deck.length)) * - parseInt(templateSVG.getAttribute("width")) + "pt"; - deck.style.height = Math.ceil(Math.sqrt(deckJSON.deck.length)) * - parseInt(templateSVG.getAttribute("height")) + "pt"; + let template = await fetch(`/template/${deckJSON.type}/input.json`) + .then(data => data.json()); - // build card SVGs - deckJSON[cardType[0]].forEach( - card => makeCardSVG(deck, cardType[1], templateSVG, card)); - }); + let cardCount = Object.entries(template.cardTypes) + .map(ct => deckJSON[ct[0]].length * (ct[1].back ? 2 : 1)) + .reduce((sum, current) => sum + current, 0); + + // note: needs to be a for loop because it needs to be synchronous + // and also have await + // Although I suppose I could prefetch the SVGs and then do the rest... + for (let cardType of Object.entries(template.cardTypes)) { + let backSVG; + if (cardType[1].back) { + let backTemplate = cardType[1].back.template || (cardType[0] + "-back"); + backSVG = await getSVGTemplate(deckJSON.type + "/" + backTemplate); + } + let templateSVG = await getSVGTemplate(deckJSON.type + "/" + cardType[0]); + console.log(templateSVG); + + // build card SVGs + deckJSON[cardType[0]].forEach(card => { + makeCardSVG(deck, cardType[1], templateSVG, card); + + // if there is a back, build it too + if (cardType[1].back) { + makeCardSVG(deck, cardType[1].back, backSVG, card, back=true); + } }); - }); -} -function setDeckTemplate(type, callback) { - getJSON("/template/" + type + "/input.json", json => { - template = json; - callback(); - }); + // set div width/height based on number of cards + deck.style.width = Math.ceil(Math.sqrt(cardCount)) * + parseInt(templateSVG.getAttribute("width")) + "pt"; + deck.style.height = Math.ceil(Math.sqrt(cardCount)) * + parseInt(templateSVG.getAttribute("height")) + "pt"; + }; } function setForm(cardTemplate, card) { @@ -170,21 +172,20 @@ function setForm(cardTemplate, card) { }); } -function makeCardSVG(deck, cardInputTemplate, templateSVG, card) { +function makeCardSVG(deck, cardInputTemplate, templateSVG, card, back=false) { + let propSource = (back && card.back) ? card.back : card; let cardSVG = deck.appendChild(templateSVG.cloneNode(true)); cardSVG.addEventListener('click', () => { selected = {svg: cardSVG, json: card}; setForm(cardInputTemplate, card); }, true); - Object.keys(cardInputTemplate.inputs).forEach( - prop => wrapSVGText(cardSVG.querySelector('#' + prop), String(card[prop] || ""))); - Object.entries(cardInputTemplate.hide).forEach(hidable => { - if (hidable[1] in card) { - cardSVG.querySelector('#' + hidable[0]).setAttribute('display', ''); - } - else { - cardSVG.querySelector('#' + hidable[0]).setAttribute('display', 'none'); - } + Object.keys(cardInputTemplate.inputs).forEach(prop => { + let inputProp = propSource[prop] || card[prop] || ""; + wrapSVGText(cardSVG.querySelector('#' + prop), String(inputProp)); + }); + Object.entries(cardInputTemplate.hide || []).forEach(hidable => { + cardSVG.querySelector('#' + hidable[0]) + .setAttribute('display', hidable[1] in propSource ? '' : 'none'); }); } @@ -193,10 +194,11 @@ function upload() { // POST the generated SVGs to the server let data = (new XMLSerializer()).serializeToString(deck); - let xhr = new XMLHttpRequest(); - xhr.open('POST', "upload"); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.send(JSON.stringify({body: data, json: deckJSON})); + fetch('upload', { + method: 'post', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({body: data, json: deckJSON}) + }); } function wrapSVGText(e, string) { diff --git a/server.js b/server.js index a88fac9..2cd8045 100644 --- a/server.js +++ b/server.js @@ -50,12 +50,14 @@ const server = http.createServer((req, res) => { case "card.json": case "deck.json": case "environment/input.json": + case "hero/input.json": + case "villain/input.json": sendFile(res, "template/" + item, 'application/json'); break; case "environment/deck.svg": case "hero/deck.svg": case "hero/character-back.svg": - case "hero/character-front.svg": + case "hero/character.svg": case "villain/deck.svg": case "villain/character.svg": case "villain/instructions.svg": diff --git a/template/environment/input.json b/template/environment/input.json index a11ea81..1439eb8 100644 --- a/template/environment/input.json +++ b/template/environment/input.json @@ -1,6 +1,7 @@ { "cardTypes": { "deck": { + "meta": ["count"], "inputs": { "name": "text", "keywords": "text", diff --git a/template/hero/character-back.svg b/template/hero/character-back.svg index de47764..be5ee60 100644 --- a/template/hero/character-back.svg +++ b/template/hero/character-back.svg @@ -4,7 +4,7 @@ - + { Text Here { Text Here { Text Here diff --git a/template/hero/character-front.svg b/template/hero/character.svg similarity index 100% rename from template/hero/character-front.svg rename to template/hero/character.svg diff --git a/template/hero/input.json b/template/hero/input.json index 664c666..1b83ec4 100644 --- a/template/hero/input.json +++ b/template/hero/input.json @@ -1,5 +1,17 @@ { "cardTypes": { + "character": { + "back": { + "inputs": { + "incapacitated": "text" + } + }, + "inputs": { + "hp": "number", + "power": "text", + "powerText": "textarea" + } + }, "deck": { "inputs": { "name": "text", diff --git a/template/villain/input.json b/template/villain/input.json index 664c666..4d18ced 100644 --- a/template/villain/input.json +++ b/template/villain/input.json @@ -1,5 +1,40 @@ { "cardTypes": { + "character": { + "back": { + "template": "character", + "inputs": { + "name": "text", + "hp": "number", + "title": "text" + } + }, + "inputs": { + "name": "text", + "hp": "number", + "title": "text" + } + }, + "instructions": { + "back": { + "template": "instructions", + "inputs": { + "name": "text", + "title": "text", + "gameplay": "textarea" + }, + "hide": { + "setupBox": "setup" + } + }, + "inputs": { + "name": "text", + "title": "text", + "setup": "textarea", + "gameplay": "textarea", + "advanced": "textarea" + } + }, "deck": { "inputs": { "name": "text", diff --git a/template/villain/instructions.svg b/template/villain/instructions.svg index e82ea63..b1e552a 100644 --- a/template/villain/instructions.svg +++ b/template/villain/instructions.svg @@ -9,16 +9,18 @@ Villain Name - Villain Title - Art By - Text Here - - - - Text Here - SetUp + + Villain Title + + + + Text Here + SetUp + + Game Play + Text Here + Advanced + Text Here + Art By - Game Play - Advanced - Text Here