import "./style.scss"; import Alpine from "alpinejs"; import cardSvgs from "./cardSvgs"; // Some helpful constants const suits = { C: "Clubs", D: "Diamonds", H: "Hearts", S: "Spades", }; const ranks = { A: "Ace", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", T: "Ten", J: "Jack", Q: "Queen", K: "King", }; const deck = Object.keys(suits).flatMap((suit) => { return Object.keys(ranks).map((cval) => { return cval + suit; }); }); // Do some SVG processing const cardSvgTitle = (ckey) => { return deck.includes(ckey) ? `${ranks[ckey[0]]} of ${suits[ckey[1]]}` : "Unknown Card"; }; Object.keys(cardSvgs).forEach((ckey) => { var cardDoc = new DOMParser().parseFromString( cardSvgs[ckey], "image/svg+xml" ); var svgRoot = cardDoc.documentElement; svgRoot.removeAttribute("height"); svgRoot.removeAttribute("width"); svgRoot.removeAttribute("class"); var titleEl = cardDoc.createElementNS( svgRoot.lookupNamespaceURI(null), "title" ); var titleText = document.createTextNode(cardSvgTitle(ckey)); titleEl.appendChild(titleText); svgRoot.insertBefore(titleEl, svgRoot.firstElementChild); cardSvgs[ckey] = new XMLSerializer().serializeToString( cardDoc.documentElement ); }); // Keep some constants in global store for components Alpine.store("global", { deck, cardsToSolve: Array(deck.length).fill(0), }); // card preview component data Alpine.data("playingCardsPreview", () => ({ cardSvgs, cardsBySlice(start, length) { return this.$store.global.cardsToSolve.slice(start, length); }, cardSvg(card) { return card === 0 ? this.cardSvgs["2B"] : this.cardSvgs[card]; }, })); // input component data Alpine.data("cardsInputForm", () => ({ // "constants" for validation etc nonAlphaNumRegEx: /[\W_]+/g, // input validation inputValue: "", validCards: [], dupedCards: [], invalidCards: [], validMessages: [], invalidMessages: [], get isFormValid() { return this.isValidCardsLengthInRange && this.invalidMessages.length === 0; }, get isValidCardsLengthTooSmall() { return this.validCards.length < 34; }, get isValidCardsLengthTooBig() { return this.validCards.length > this.$store.global.deck.length; }, get isValidCardsLengthInRange() { return !this.isValidCardsLengthTooSmall && !this.isValidCardsLengthTooBig; }, validateCardsInput() { // reset arrays and parse the input this.validCards = []; this.invalidCards = []; this.dupedCards = []; this.validMessages = []; this.invalidMessages = []; // handle if input has alphanum chars - treat them as delimeters // if no alphanum chars, split by 2 chars except for 0 let userCards = this.nonAlphaNumRegEx.test(this.inputValue) ? this.inputValue .toUpperCase() .replace(this.nonAlphaNumRegEx, " ") .split(" ") .filter((c) => c) : this.inputValue .toUpperCase() .split(/0|(..)/g) .filter((c) => c !== "") .map((c) => (!c ? "0" : c)); // check the input userCards.forEach((card) => { if (card === "0") { // user marking a slot with an unknown card this.validCards.push(card); } else if (this.validCards.includes(card)) { // this card was already seen in user's input, now it's a duplicate this.dupedCards.push(card); } else if (this.$store.global.deck.includes(card)) { // not a duplicate, and in the reference deck? Valid, add to valid cards this.validCards.push(card); } else { // not a dupe, but not in reference deck: invalid, add to invalid cards this.invalidCards.push(card); } }); // set validation messages based on length if (this.isValidCardsLengthTooSmall) { this.invalidMessages.push(`Must enter at least 34 cards`); } else if (this.isValidCardsLengthTooBig) { this.invalidMessages.push( `Must not enter more than ${this.$store.global.deck.length} cards` ); } if (this.validCards.slice(this.validCards.length - 34).includes("0")) { this.invalidMessages.push( `Stock + bottom row (last 34 cards) must not contain unknown ('0') cards` ); } // set other validation messages if (this.dupedCards.length > 0) { let s = this.dupedCards.length > 1 ? "s" : ""; this.invalidMessages.push( `${this.dupedCards.length} duplicate card${s}: ${this.dupedCards.join( " " )}` ); } if (this.invalidCards.length > 0) { let s = this.invalidCards.length > 1 ? "s" : ""; this.invalidMessages.push( `${this.invalidCards.length} invalid card${s}: ${this.invalidCards.join( " " )}` ); } if (this.validCards.length > 0) { let s = this.validCards.length > 1 ? "s" : ""; this.validMessages.push( `${this.validCards.length} valid card${s}: ${this.validCards.join(" ")}` ); } // set the game cards to try solving, based on current input this.$store.global.cardsToSolve = Array( this.$store.global.deck.length - this.validCards.length ) .fill(0) .concat(this.validCards) .map((c) => (c === "0" ? 0 : c)); }, })); window.Alpine = Alpine; Alpine.start();