import { pecBoard, pec } from './pecslib.js'; // gui object classes? // Why am I doing this? class auxillery_dialog { constructor (title) { var me = this; this.root = document.createElement("div"); this.root.classList.add("dialog"); this.title = "string" == typeof title ? title : ""; var title_bar = document.createElement("div"); title_bar.classList.add("title_bar"); var titleText = document.createElement("span"); titleText.innerText = this.title; titleText.classList.add("title_text"); title_bar.appendChild(titleText); this.root.appendChild(title_bar); this.body = document.createElement("div"); this.root.appendChild(me.body); var close = document.createElement("span"); close.innerText = "<"; close.classList.add("close"); title_bar.appendChild(close); close.addEventListener("click", ()=>{ me.root.classList.remove("focused"); }); } } // Functions function systemMsg (message,level=0) { // abstraction? // Yes, but it allows us to easilly redirect error // output to where we want, for now, just the console... switch (level) { case 0: console.log("INFO: "+message); break; case 1: console.error("WARNING: "+message); break; case 2: console.error("ERROR: "+message); break; case 3: console.log("DEBUG: "+message); break; default: console.log(message); } // but later we will also have it push an error toast in the ui. } function toggleDarkmode () { var html = document.documentElement; if (html.classList.contains("darkmode")) { html.classList.remove("darkmode"); } else { html.classList.add("darkmode"); } } function inRange(target,min,max) { if (isNaN(target) || isNaN(min) || isNaN(max)) { return false; } return (min <= target && target <= max); } function invokeLibrary(e) { // Breaking this out of event so its not an // annon function, so we can swap it in and out var targetID = ((e.currentTarget.getAttribute("id")).split("pec"))[1]; if (isNaN(targetID)) { return systemMsg("invokeLibrary targetID not a number",2); } var targetPec = app.gui["pec"+targetID]; if (undefined == targetPec) { return systemMsg("invokeLibrary targetPec undefined: "+targetID,2); } if (!inRange(targetID,0,15)) { return systemMsg("invokeLibrary targetID out of range: "+targetPecIndex,2); } app.targetPec = targetPec; app.targetPecIndex = targetID; app.gui.library.root.classList.add("focused"); } function clearPec (index=null) { if (!inRange(index,0,15)) { return systemMsg("clearPec was given an out of range index",2); } var targetPec = app.gui["pec"+index]; if (undefined == targetPec) { return systemMsg("clearPec targetPec undefined",2); } targetPec.classList.add("empty"); targetPec.innerHTML = "
+
"; targetPec.addEventListener("click", invokeLibrary); } function setPec(pecTemplate=null, targetIndex=null) { if (null == pecTemplate) { return systemMsg("setPec was not provided data",2); } if (!(pecTemplate instanceof pec)) { return systemMsg("setPec was not provided a valid template",2); } if (isNaN(targetIndex)) { return systemMsg("setPec was not provided a valid targetIndex",2); } var targetPec = app.gui["pec"+targetIndex]; targetPec.removeEventListener("click", invokeLibrary); targetPec.innerHTML = ""; targetPec.classList.remove("empty"); var freshPec = new pec(pecTemplate.word, pecTemplate.image); targetPec.appendChild(freshPec.DOM); } function repopBoards (current=0) { //app.boards; //app.currentBoards; //app.menu; // Normally we only want <, but we want an extra // iteration here for the "add new board" element for (var i = 0; i <= app.boards.length; i++) { var boardTab = document.getElementById("board"+i); if (null == boardTab) { boardTab = document.createElement("span"); boardTab.setAttribute("id", "board"+i); boardTab.classList.add("board_tab"); app.gui.menu.appendChild(boardTab); } // Clear event listeners var oldTab = boardTab; boardTab = oldTab.cloneNode(true); oldTab.parentNode.replaceChild(boardTab, oldTab); // not sure how memory safe the above lines are var name = "Board "+i; if (i == app.boards.length) { name = "+"; boardTab.addEventListener("click",(e)=>{ var current = app.boards.findIndex(i=>i==app.currentBoard); app.boards.push(new pecBoard()); repopBoards(current); repopGrid(app.currentBoard); }); } else { if (current != i) { boardTab.classList.remove("active_board"); boardTab.addEventListener("click", (e)=>{ var sourceID = ((e.currentTarget.getAttribute("id")).split("board"))[1]; app.currentBoard = app.boards[sourceID]; repopBoards(sourceID); repopGrid(app.currentBoard); }); } else { boardTab.classList.add("active_board"); } if (undefined != app.boards[i].name) { name = app.boards[i].name; } } boardTab.innerText = name; } } function repopGrid (board=null) { if (null == board) { return systemMsg("repopGrid was not provided data",2); } if (!(board instanceof pecBoard)) { return systemMsg("repopGrid was not provided data of type pecBoard",2); } board.pecs.forEach((p,i)=>{ if (null == p) { return clearPec(i); } if (!(p instanceof pec)) { systemMsg("Invalid Pec found, scrubbing and returning to null",1); board.pecs[i] = null; return clearPec(i); } setPec(p,i); }); } function useThisPec (e) { var sourceID = ((e.currentTarget.getAttribute("id")).split("lPec"))[1]; app.gui.library.root.classList.remove("focused"); app.currentBoard.pecs[app.targetPecIndex] = app.library.splice(sourceID,1)[0]; repopLibrary(); repopGrid(app.currentBoard); } function createNewPec () { var name = app.gui.newpec_name.value; var image = app.gui.newpec_image.files[0]; app.library.push(new pec(name, URL.createObjectURL(image))); repopLibrary(); app.gui.newpec.root.classList.remove("focused"); } function updatePreview () { var name = app.gui.newpec_name.value; var image = app.gui.newpec_image.files[0]; app.gui.newpec_preview.innerHTML = ""; app.gui.newpec_preview.removeEventListener("click", createNewPec); app.gui.newpec_status.innerText = "Create your new pec by naming it and providing an image."; var correct = 0; if (["image/png","image/jpeg","image/svg"].includes(image?.type)) { var imageBox = document.createElement("img"); imageBox.src = URL.createObjectURL(image); imageBox.classList.add("pecImage"); app.gui.newpec_preview.appendChild(imageBox); correct++; } if ("" != name) { var nameBox = document.createElement("div"); nameBox.innerText = name; nameBox.classList.add("pecName"); app.gui.newpec_preview.appendChild(nameBox); correct++; } if (1 < correct) { app.gui.newpec_status.innerText = "Click/Tap the preview to save."; app.gui.newpec_preview.addEventListener("click", createNewPec); } } function addNewPec (e) { // clear out any previous values in form. app.gui.newpec_form.reset(); updatePreview(); // make form visible app.gui.newpec.root.classList.add("focused"); } function repopLibrary () { // clear library app.gui.library.body.innerHTML = ""; // recreate items from app.library app.library.forEach((p,i)=>{ var newPec = document.createElement("span"); newPec.classList.add("libSlot"); newPec.innerText = p.word; newPec.id = "lPec"+i; newPec.addEventListener("click", useThisPec); app.gui.library.body.appendChild(newPec); }); // add new pec button at end of list var newPec = document.createElement("span"); newPec.classList.add("libSlot"); newPec.innerText = " + "; newPec.id = "lPec"+(app.library.length + 1); newPec.addEventListener("click", addNewPec); app.gui.library.body.appendChild(newPec); } // Initialize gui window.app = {}; app.gui = {}; app.gui.main = document.createElement("div"); app.gui.main.id = "root"; document.body.appendChild(app.gui.main); app.gui.menu = document.createElement("nav"); app.gui.menu.id = "navigation"; app.gui.main.appendChild(app.gui.menu); app.gui.optionsButton = document.createElement("div"); app.gui.optionsButton.classList.add("options_button"); app.gui.optionsButton.innerHTML = "☰"; app.gui.menu.appendChild(app.gui.optionsButton); app.gui.view = document.createElement("div"); app.gui.view.id = "view"; app.gui.main.appendChild(app.gui.view); // Library popup app.gui.library = new auxillery_dialog("Library"); app.gui.library.body.classList.add("library_body"); app.gui.main.append(app.gui.library.root); // Options popup app.gui.options = new auxillery_dialog("Options"); app.gui.options.body.classList.add("options_body"); app.gui.main.append(app.gui.options.root); app.gui.optionsButton.addEventListener("click", ()=>{ app.gui.options.root.classList.add("focused") }); // New Pec popup app.gui.newpec = new auxillery_dialog("Create New Pec"); app.gui.newpec.body.classList.add("newpec_body"); app.gui.main.append(app.gui.newpec.root); // pre-populate grid with emptys for (var x = 0; x < 16; x++) { var name = "pec"+x; var thisPec = document.createElement("span"); app.gui[name] = thisPec; thisPec.classList.add("pecSlot"); thisPec.id = name; clearPec(x); app.gui.view.appendChild(thisPec); } // Create our initial board app.boards = []; app.boards.push(new pecBoard()); app.currentBoard = app.boards[0]; repopBoards(0); repopGrid(app.currentBoard); // Populate Library import { library as library } from './library.js'; app.library = library; repopLibrary(); // Populate Options app.options = {}; app.options.darkmode = false; // should always be an option app.options.audioFeedback = false; // read word aloude app.options.feedbackFloodLimit = 3; app.options.feedbackTimeout = 60; // 60 seconds // overload defaults with options stored in localstorage // build Options UI app.gui.options_darkmode = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_darkmode); app.gui.options_darkmode.innerHTML = ' '; app.gui.options_darkmode.addEventListener("change", toggleDarkmode); app.gui.options_audioFeedback = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_audioFeedback); app.gui.options_audioFeedback.innerHTML = ' '; app.gui.options_feedbackFloodLimit = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_feedbackFloodLimit); app.gui.options_feedbackFloodLimit.innerHTML = ' '; app.gui.options_feedbackTimeout = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_feedbackTimeout); app.gui.options_feedbackTimeout.innerHTML = ' '; app.gui.options_about = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_about); app.gui.options_about.innerHTML = 'App licenced