// created on 2022-12-30; updated on 2023-06-18 const DEFAULT_WORD = "word"; type RGB = [r: number, g: number, b: number]; // legacy type ColorHex = string; function getElem(id: string): HTMLElement { let elem = document.getElementById(id); if (!elem) throw new Error(`${id} missing!`); return elem; } const params = new URLSearchParams(window.location.search); function getQuery(query: string): string | null { return params.get(query); } function rgbToHex(rgb: RGB): ColorHex { let r = rgb[0].toString(16).padStart(2, "0"); let g = rgb[1].toString(16).padStart(2, "0"); let b = rgb[2].toString(16).padStart(2, "0"); return `${r}${g}${b}`; } function randomColor(): ColorHex { let num = Math.floor(Math.random() * 16777216); return num.toString(16).padStart(6, "0") } namespace Saving { export type CurrentSave = SaveV4; export type Save = SaveV2 | SaveV3 | SaveV4; interface SaveV2 { word: RGB[]; version: 2; } interface SaveV3 { colors: ColorHex[]; version: 3; } interface SaveV4 { word: string; colors: ColorHex[]; version: 4; } function v2to3(save: SaveV2): SaveV3 { let colors: ColorHex[] = []; for (const rgb of save.word) { colors.push(rgbToHex(rgb)); } return { colors, version: 3 }; } function v3to4(save: SaveV3): SaveV4 { return { word: DEFAULT_WORD, colors: save.colors, version: 4, }; } export function convertSave(save: string | Save): CurrentSave | null { let s: Save = typeof save === "string" ? JSON.parse(save) : save; if (typeof s["version"] !== "number") return null; switch (s["version"]) { case 2: return convertSave(v2to3(s)); case 3: return convertSave(v3to4(s)); case 4: return s; default: return null; } } } class Game { word: string; colors: ColorHex[]; paragraph: HTMLElement; //TODO: make all these its own class readonly clicker: HTMLElement; readonly resetb: HTMLElement; readonly editb: HTMLElement; constructor() { this.colors = []; this.word = DEFAULT_WORD; this.paragraph = getElem("p"); let word = getQuery("word"); let gotWordFromQuery = false; if (word) { this.word = word; gotWordFromQuery = true; } this.clicker = getElem("click!"); this.clicker.onclick = () => this.add(randomColor()); this.resetb = getElem("reset!"); this.resetb.onclick = () => this.reset(); this.editb = getElem("edit!"); this.editb.onclick = () => this.editWordPrompt(); let data = localStorage.getItem("word"); if (!data) return; let save = Saving.convertSave(data); if (!save || !(save.colors instanceof Array)) return; this.colors = save.colors; if (!gotWordFromQuery) this.word = save.word; else this.save(); this.draw(); } save() { localStorage.setItem( "word", JSON.stringify(<Saving.CurrentSave>{ word: this.word, colors: this.colors, version: 4, }) ); } showExtraButtons(show: boolean) { let style = getElem("hider"); if (show) style.innerText = ""; else style.innerText = ".hidden{display:none}"; } editWordPrompt() { let newWord = prompt("choose new word!", this.word); if (!newWord) return; this.word = newWord; this.save(); this.draw(); } get length(): number { return this.colors.length; } clearScreen() { this.showExtraButtons(false); this.paragraph.innerHTML = ""; this.clicker.innerHTML = "click here!"; } reset() { this.clearScreen(); this.word = DEFAULT_WORD; this.colors = []; this.save(); } draw() { this.clearScreen(); if (this.length > 0) this.showExtraButtons(true); for (const word of this.colors) { this.drawOne(word); } } drawOne(color: ColorHex) { this.paragraph.innerHTML += `<span style="color:#${color}">${this.word}</span>`; this.clicker.innerHTML = `click here! (clicked ${this.length} times)`; } add(color: ColorHex) { this.showExtraButtons(true); this.colors.push(color); this.drawOne(color); this.save(); } } var game; window.addEventListener("DOMContentLoaded", () => { window.game = new Game(); });