2018-12-28 10:46:01 -05:00
|
|
|
<template>
|
|
|
|
<div>
|
2019-01-04 16:11:27 -05:00
|
|
|
<headful :title="'Editor|' + deckInfo.meta.name"> </headful>
|
2018-12-28 10:46:01 -05:00
|
|
|
<div id="controls">
|
|
|
|
<div>
|
|
|
|
<button type="button" @click="upload"> Save Deck </button>
|
2019-01-06 10:20:42 -05:00
|
|
|
<Loader :loading="uploading"></Loader>
|
2019-01-06 09:54:04 -05:00
|
|
|
Download:
|
2019-01-10 22:12:28 -05:00
|
|
|
<a class="download" :href="downloadJSON(deckInfo)"
|
2019-01-07 08:51:32 -05:00
|
|
|
:download="deckInfo.meta.name + '.input.json'">
|
2019-01-06 09:54:04 -05:00
|
|
|
Input JSON
|
2019-01-07 08:51:32 -05:00
|
|
|
</a>
|
2019-01-10 22:12:28 -05:00
|
|
|
<a class="download" :href="downloadJSON(makeTTSJSON())"
|
2019-01-07 08:51:32 -05:00
|
|
|
:download="deckInfo.meta.name + '.tts.json'">
|
|
|
|
Tabletop Sim Output JSON
|
|
|
|
</a>
|
|
|
|
<a class="download" :href="`/decks/${deckID}.png`"
|
|
|
|
:download="deckInfo.meta.name + '.png'">
|
2019-01-06 09:54:04 -05:00
|
|
|
Deck PNG
|
2019-01-07 08:51:32 -05:00
|
|
|
</a>
|
2018-12-28 10:46:01 -05:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<label> Upload JSON: WARNING: WILL CLEAR DECK
|
2019-01-09 20:39:25 -05:00
|
|
|
<input ref="jsonUpload" type="file">
|
2018-12-28 10:46:01 -05:00
|
|
|
</label>
|
2019-01-09 20:39:25 -05:00
|
|
|
<button type="button" @click="jsonUpload"> Load </button>
|
2018-12-28 10:46:01 -05:00
|
|
|
</div>
|
|
|
|
|
2019-01-06 09:54:04 -05:00
|
|
|
<div>
|
|
|
|
<label> Deck Name: <input type="text" v-model="deckInfo.meta.name"> </label>
|
|
|
|
<label> Deck Type:
|
|
|
|
<select v-model="deckInfo.meta.type">
|
|
|
|
<option value="hero">hero</option>
|
|
|
|
<option value="villain">villain</option>
|
|
|
|
<option value="environment">environment</option>
|
|
|
|
</select>
|
|
|
|
</label>
|
|
|
|
</div>
|
2018-12-28 10:46:01 -05:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div id="cardEditor" v-if="selected">
|
|
|
|
<button class="close-editor" @click="selected = null">X</button>
|
2019-01-04 17:04:30 -05:00
|
|
|
<div v-for="(type, prop) in selected.props">
|
2018-12-28 10:46:01 -05:00
|
|
|
<label> {{ prop }}
|
2019-01-06 09:59:34 -05:00
|
|
|
<input v-if="type === 'image'" type="file" accept="image/*"
|
2019-01-04 17:04:30 -05:00
|
|
|
@change="fileUploaded(prop, $event)" />
|
2019-01-06 09:59:34 -05:00
|
|
|
<textarea v-else-if="type === 'textarea'" v-model="selected.card[prop]"> </textarea>
|
|
|
|
<input v-else :type="type" v-model="selected.card[prop]"/>
|
2018-12-28 10:46:01 -05:00
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2019-01-12 16:56:37 -05:00
|
|
|
<Deck ref="deck" :deckInfo="deckInfo" v-model="selected"> </Deck>
|
2018-12-28 10:46:01 -05:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2019-01-03 15:12:16 -05:00
|
|
|
import yaml from 'js-yaml';
|
2019-01-09 19:40:39 -05:00
|
|
|
import html2canvas from 'html2canvas';
|
2019-01-04 17:04:30 -05:00
|
|
|
import Deck from './Deck.vue';
|
2019-01-06 10:20:42 -05:00
|
|
|
import Loader from './Loader.vue';
|
2019-01-10 22:12:28 -05:00
|
|
|
import tts_templates from './template/tts/*.json';
|
2018-12-28 10:46:01 -05:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'Editor',
|
2019-01-06 10:20:42 -05:00
|
|
|
components: {Deck, Loader},
|
2018-12-28 10:46:01 -05:00
|
|
|
|
2019-01-04 17:04:30 -05:00
|
|
|
props: ['deckID'],
|
2018-12-28 10:46:01 -05:00
|
|
|
data() {
|
|
|
|
return {
|
2019-01-06 10:20:42 -05:00
|
|
|
uploading: false,
|
2018-12-28 10:46:01 -05:00
|
|
|
selected: null,
|
2019-01-01 11:43:57 -05:00
|
|
|
deckInfo: {meta: {name: "", type: ""},
|
|
|
|
cards: {}},
|
2018-12-28 10:46:01 -05:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
created() {
|
2019-01-04 17:04:30 -05:00
|
|
|
if (this.deckID !== 'new') {
|
|
|
|
fetch('/decks/' + this.deckID + '.json')
|
2019-01-01 11:43:57 -05:00
|
|
|
.then(r => r.json())
|
2019-01-06 09:48:51 -05:00
|
|
|
.then(j => this.deckInfo = j.deck)
|
2019-01-01 11:43:57 -05:00
|
|
|
.catch((err) => console.log('did not get old JSON, starting new deck'));
|
2019-01-04 17:04:30 -05:00
|
|
|
}
|
|
|
|
|
2018-12-28 10:46:01 -05:00
|
|
|
/* window.addEventListener(
|
|
|
|
* 'beforeunload', e => e.returnValue = "Unsaved changes blah blah"); */
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
// deck JSON uploader
|
|
|
|
jsonUpload(event) {
|
2019-01-09 20:39:25 -05:00
|
|
|
let files = this.$refs.jsonUpload.files;
|
2018-12-28 10:46:01 -05:00
|
|
|
let reader = new FileReader();
|
2019-01-03 15:12:16 -05:00
|
|
|
reader.onload = e => this.deckInfo = yaml.safeLoad(e.target.result);
|
2018-12-28 10:46:01 -05:00
|
|
|
reader.readAsText(files[0]);
|
|
|
|
},
|
|
|
|
|
2019-01-04 17:04:30 -05:00
|
|
|
fileUploaded(event, prop) {
|
|
|
|
let reader = new FileReader();
|
|
|
|
reader.onload = e => {
|
|
|
|
this.selected.card[prop] = e.target.result;
|
|
|
|
};
|
|
|
|
reader.readAsDataURL(event.target.files[0]);
|
2018-12-28 10:46:01 -05:00
|
|
|
},
|
|
|
|
|
2019-01-10 22:12:28 -05:00
|
|
|
downloadJSON(json) {
|
|
|
|
return 'data:application/json;charset=utf-8,' +
|
|
|
|
encodeURIComponent(JSON.stringify(json))
|
|
|
|
},
|
|
|
|
|
|
|
|
makeTTSJSON() {
|
|
|
|
// make a copy
|
|
|
|
let deckOut = JSON.parse(JSON.stringify(tts_templates['deck']));
|
|
|
|
deckOut.ObjectStates[0].Nickname = this.deckInfo.meta.name;
|
|
|
|
|
|
|
|
let index = 100;
|
|
|
|
deckOut.ObjectStates[0].ContainedObjects = Object
|
|
|
|
.keys(this.deckInfo.cards)
|
|
|
|
.flatMap(cardType => this.deckInfo.cards[cardType].flatMap(card => {
|
|
|
|
let cardOut = {...JSON.parse(JSON.stringify(tts_templates['card'])),
|
|
|
|
Nickname: card.name,
|
|
|
|
Description: card.keywords,
|
|
|
|
CardID: index};
|
|
|
|
|
|
|
|
deckOut.ObjectStates[0].DeckIDs.push(
|
|
|
|
...Array(card.count || 1).fill(index));
|
|
|
|
index++;
|
|
|
|
|
|
|
|
if(card.back) {
|
|
|
|
cardOut.States = {
|
|
|
|
"2": {...JSON.parse(JSON.stringify(tts_templates['card'])),
|
|
|
|
Nickname: card.back.name,
|
|
|
|
Description: card.back.keywords,
|
|
|
|
CardID: index}};
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
return Array(card.count || 1).fill(cardOut);
|
|
|
|
}))
|
|
|
|
|
|
|
|
let cardCount = index - 100;
|
|
|
|
let columns = Math.ceil(Math.sqrt(cardCount));
|
|
|
|
Object.assign(
|
|
|
|
deckOut.ObjectStates[0].CustomDeck['1'],
|
|
|
|
{NumWidth: columns,
|
|
|
|
NumHeight: Math.ceil(cardCount / columns),
|
|
|
|
FaceURL: `${location.origin}/decks/${this.deckID}.png`,
|
|
|
|
BackURL: "http://cloud-3.steamusercontent.com/ugc/156906385556221451/CE2C3AFE1759790CB0B532FFD636D05A99EC91F4/"});
|
|
|
|
|
|
|
|
return deckOut;
|
|
|
|
},
|
|
|
|
|
2018-12-28 10:46:01 -05:00
|
|
|
upload() {
|
2019-01-06 10:20:42 -05:00
|
|
|
this.uploading = true;
|
2019-01-09 19:40:39 -05:00
|
|
|
|
2019-01-09 23:23:20 -05:00
|
|
|
// TODO: remove this nasty hack
|
|
|
|
function bindStyles(doc) {
|
|
|
|
console.log(doc);
|
|
|
|
// get existing styles from CSS...
|
|
|
|
let style = Array.from(document.styleSheets[0].rules)
|
|
|
|
.map(rule => rule.cssText)
|
|
|
|
.join('\n');
|
|
|
|
// ...and jam them into a <style> in each foreignObject
|
|
|
|
doc.querySelectorAll('foreignObject')
|
|
|
|
.forEach(o => {
|
|
|
|
let styleElement = o.appendChild(document.createElement('style'));
|
|
|
|
styleElement.innerHTML = style;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-01-09 19:40:39 -05:00
|
|
|
let node = this.$refs.deck.$el;
|
2019-01-10 15:47:12 -05:00
|
|
|
html2canvas(node, {scale: 2,
|
2019-01-09 23:23:20 -05:00
|
|
|
backgroundColor: 'black',
|
|
|
|
onclone: bindStyles})
|
2019-01-09 19:40:39 -05:00
|
|
|
.then(canvas => canvas.toDataURL("image/png"))
|
|
|
|
.then(image =>
|
|
|
|
// POST the inputed json to the server
|
|
|
|
fetch('/upload', {
|
|
|
|
method: 'post',
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
body: JSON.stringify({
|
|
|
|
deck: this.deckInfo,
|
|
|
|
_id: this.deckID === 'new' ? undefined : this.deckID,
|
|
|
|
image: image,
|
|
|
|
})}))
|
2019-01-04 17:04:30 -05:00
|
|
|
.then(r => r.json())
|
2019-01-06 10:20:42 -05:00
|
|
|
.then(j => {
|
|
|
|
this.$router.replace('/edit/' + j.id);
|
|
|
|
this.uploading = false;
|
|
|
|
})
|
2019-01-04 17:04:30 -05:00
|
|
|
.catch(err => console.log('Failed to upload' + err));
|
2018-12-28 10:46:01 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
#cardEditor {
|
|
|
|
position: fixed;
|
|
|
|
top: 0;
|
|
|
|
right: 0;
|
|
|
|
background-color: gray;
|
|
|
|
padding: 10px;
|
|
|
|
border-radius: 3px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.close-editor {
|
|
|
|
float: right;
|
|
|
|
}
|
2019-01-07 08:51:32 -05:00
|
|
|
|
|
|
|
a.download {
|
|
|
|
display: inline-block;
|
|
|
|
margin-bottom: .2em;
|
|
|
|
background-color: #1976D2;
|
|
|
|
color: white;
|
|
|
|
text-decoration: none;
|
|
|
|
padding: .2em .4em;
|
|
|
|
}
|
|
|
|
|
|
|
|
a.download:hover {
|
|
|
|
background-color: #1565C0;
|
|
|
|
}
|
2018-12-28 10:46:01 -05:00
|
|
|
</style>
|