tarot/tarot.js

172 lines
4.0 KiB
JavaScript

import cardDB from './cards.js';
// TODO reset state button
class Tarot {
constructor() {
this.cardDB = cardDB();
this.deck = new CardPile();
for (const cardName in this.cardDB) {
this.deck.add(cardName);
}
this.table = new CardPile();
const deckElem = document.querySelector("#deck");
const tableElem = document.querySelector("#table");
const infoElem = document.querySelector("#info");
const resetElem = document.querySelector("#reset");
// TODO bring those top level functions in as methods
this.renderers = [
new Renderer(
deckElem,
deckRender(this.deck)),
new Renderer(
tableElem,
tableRender(this, this.table)),
new Renderer(
infoElem,
infoRender(tableElem, deckElem))
];
// handlers
// TODO make it feel similar to the renderers? or just have a general way to list out events
// that should trigger a render
tableElem.addEventListener("mouseenter", this.render.bind(this));
tableElem.addEventListener("mouseleave", this.render.bind(this));
deckElem.addEventListener("click", this.drawCard.bind(this));
resetElem.addEventListener("click", this.reset.bind(this));
}
reset() {
let card = this.table.drawRandom();
while (card != null) {
this.deck.add(card);
card = this.table.drawRandom();
}
this.render();
}
drawCard() {
let cardName = this.deck.drawRandom();
if (cardName === null) {
this.render();
return;
}
this.table.add(cardName);
this.render();
}
render() {
for (const renderer of this.renderers) {
renderer.render();
}
}
}
function infoRender(tableElem) {
return function(elem) {
const cardElem = tableElem.querySelector(".card:hover");
if (cardElem == null) {
elem.innerHTML = "";
return
}
elem.innerHTML = cardElem.querySelector("span").innerHTML;
}
}
function tableRender(tarot, table) {
return function(elem) {
const cardsOut = elem.querySelectorAll('.card');
for (let cardElem of cardsOut) {
if (!table.has(cardElem.getAttribute('data-cardName'))) {
cardElem.remove();
}
}
for (let cardName of table.cards) {
const found = elem.querySelector(`[data-cardName="${cardName}"]`);
if (found != null) {
continue;
}
let bp = document.querySelector(".card.blueprint");
let cardElem = bp.cloneNode(true);
cardElem.classList.remove("blueprint");
cardElem.setAttribute("data-cardName", cardName)
cardElem.setAttribute("style", "float:left; width: 150px; height: 250px; border: 1px solid pink;");
cardElem.querySelector("span").innerHTML = cardName;
cardElem.addEventListener("mouseenter", tarot.render.bind(tarot));
elem.append(cardElem);
}
}
}
function deckRender(deck) {
return function(elem) {
const width = deck.size;
elem.setAttribute("style", `float:left; height: 250px; width: 150px; border-left: 1px solid white; border-top: 1px solid white; border-bottom: ${width}px solid white; border-right: ${width}px solid white`);
}
}
function deckClick(deckElem, deck) {
return function() {
console.log(deckElem, deck);
}
}
class Renderer {
constructor(element, renderFn) {
this.element = element;
this.renderFn = renderFn;
}
render() {
this.renderFn(this.element);
}
}
class CardPile {
constructor() {
this.cards = new Set();
}
add(cardName) {
this.cards.add(cardName);
}
has(cardName) {
return this.cards.has(cardName);
}
get size() {
return this.cards.size;
}
drawRandom() {
const size = this.cards.size
if (size === 0) {
return null;
}
const index = Math.floor(Math.random() * Math.floor(size));
let i = 0;
let cardName = "";
for (let card of this.cards) {
if (i === index) {
cardName = card;
break
}
i++;
}
this.cards.delete(cardName);
return cardName;
}
}
export default Tarot;