From 76b355dca015d1ce3378e6869458f46cefd8e56c Mon Sep 17 00:00:00 2001 From: bluesaxman Date: Fri, 19 Sep 2025 13:41:09 -0600 Subject: [PATCH] added persistance for options, implimented some options --- main.js | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 240 insertions(+), 24 deletions(-) diff --git a/main.js b/main.js index 51174d8..0741dfb 100644 --- a/main.js +++ b/main.js @@ -51,12 +51,188 @@ function systemMsg (message,level=0) { // abstraction? // but later we will also have it push an error toast in the ui. } +function initDatabase (e) { + var db = e.target.result; + var objectStore = db.createObjectStore("pecs", {keyPath: "word"}); + objectStore.createIndex("word", "word", {unique: true}); + objectStore.createIndex("locate", "locate"); +} + +function saveApp () { + var idbHandle = window.indexedDB.open("masterLibrary"); + idbHandle.onerror = (e) => { + systemMsg("Error loading database",2); + }; + idbHandle.onupgradeneeded = initDatabase; + idbHandle.onsuccess = (e) => { + var masterLibrary = idbHandle.result; + var saving = masterLibrary.transaction("pecs","readwrite",{durability:"strict"}); + var pecStore = saving.objectStore("pecs"); + library.forEach(pecItem=>{ + var action = pecStore.get(pecItem.word); + action.onsuccess = (e)=>{ + var record = action.result; + if (record == undefined) { + record = { + word: pecItem.word, + image: pecItem.image, + locate: "library" + }; + } else { + record.locate = "library"; + } + pecStore.put(record); + }; + }); + for (var boardID = 0; boardID < app.boards.length; boardID++) { + var curBoard = app.boards[boardID]; + for (var index = 0; index < curBoard.pecs.length; index++) { + var curPec = curBoard.pecs[index]; + if (null == curPec) { continue; } //don't store nulls + var action = pecStore.get(curPec.word); + action.curPec = curPec; + action.boardID = boardID; + action.pecIndex = index; + action.onsuccess = (e)=>{ + var record = e.target.result; + if (record == undefined) { + record = { + word: e.target.curPec.word, + image: e.target.curPec.image, + locate: e.target.boardID + "-" + e.target.pecIndex + }; + } else { + record.locate = e.target.boardID + "-" + e.target.pecIndex; + } + pecStore.put(record); + }; + } + } + saving.oncomplete = (e)=>{ + masterLibrary.close(); + saveOptions(); + loadApp(); + }; + }; +} + +function saveOptions () { + localStorage.setItem("librepecSettings", JSON.stringify(app.options)); +} + +function loadOptions () { + var options = JSON.parse(localStorage.getItem("librepecSettings")); + if (null != options) { app.options = options; } + var darkmode = document.getElementById("darkmode-checkbox"); + var kidsmode = document.getElementById("kidsmode-checkbox"); + var feedback = document.getElementById("audio-feedback-toggle"); + var floodLimit = document.getElementById("flood-limit"); + var floodTimeout = document.getElementById("flood-timeout"); + darkmode.checked = (undefined == app.options?.darkmode) ? false : (app.options.darkmode ? true : false); + toggleDarkmode(darkmode.checked); + kidsmode.checked = (undefined == app.options?.kidsmode) ? false : (app.options.kidsmode ? true : false); + feedback.checked = (undefined == app.options?.audioFeedback) ? false : (app.options.audioFeedback ? true : false); + floodLimit.value = (undefined == app.options?.feedbackFloodLimit) ? 3 : app.options.feedbackFloodLimit; + floodTimeout.value = (undefined == app.options?.feedbackTimeout) ? 60 : app.options.feedbackTimeout; +} + +import { library as library } from './library.js'; + +function initApp () { + // Create our initial board + app.boards = []; + app.boards.push(new pecBoard()); + app.currentBoard = app.boards[0]; + repopBoards(0); + repopGrid(app.currentBoard); + + // Populate Library + app.library = library; + repopLibrary(); + + // Populate Options + app.options = {}; + app.options.darkmode = false; // should always be an option + app.options.kidsMode = false; // disables most interactions + app.options.audioFeedback = false; // read word aloude + app.options.feedbackFloodLimit = 3; + app.options.feedbackTimeout = 60; // 60 seconds + // overload defaults with options stored in localstorage +} + +function hardResetApp () { + alert("App ahs been hard reset"); + initApp(); + saveApp(); +} + +function loadAppPec (item) { + var locate = item.locate; + if ("library" == locate) { + app.library.push(new pec(item.word, item.image)); + } else { + locate = locate.split("-").map(n=>parseInt(n)); + if (!(app.boards[locate[0]] instanceof pecBoard)) { + app.boards[locate[0]] = new pecBoard(); + } + app.boards[locate[0]].pecs[locate[1]] = new pec(item.word, item.image); + } +} + +function loadApp () { + var idbHandle = window.indexedDB.open("masterLibrary"); + idbHandle.onerror = (e) => { + systemMsg("Error loading database",2); + }; + idbHandle.onupgradeneeded = initDatabase; + window.currentBoardID = app.boards.indexOf(app.currentBoard); + currentBoardID = isNaN(currentBoardID) ? 0 : currentBoardID; + idbHandle.onsuccess = (e) => { + var masterLibrary = idbHandle.result; + var loading = masterLibrary.transaction("pecs","readonly",{durability:"strict"}); + var pecStore = loading.objectStore("pecs"); + var lengthCheck = pecStore.count(); + lengthCheck.onsuccess = (e)=>{ + if (0 < e.target.result) { + app.library = []; // Clear out library cache + app.boards = []; // Clear out boards cache + app.boards.push(new pecBoard()); + app.currentBoard = app.boards[0]; + var cursor = pecStore.openCursor(); + cursor.onsuccess = (e) => { + var c = e.target.result; + if (c) { + loadAppPec(c.value); + c.continue(); + } + }; + } else { + // DB is empty, reinit app because + // we have nothing to load + hardResetApp(); + } + }; + loading.oncomplete = (e) => { + masterLibrary.close(); + loadOptions(); + app.currentBoard = app.boards[currentBoardID]; + repopLibrary(); + repopBoards(); + repopGrid(app.currentBoard); + }; + }; +} + function toggleDarkmode () { var html = document.documentElement; - if (html.classList.contains("darkmode")) { - html.classList.remove("darkmode"); + if (app.options.darkmode) { + if (!(html.classList.contains("darkmode"))) { + html.classList.add("darkmode"); + } } else { - html.classList.add("darkmode"); + if (html.classList.contains("darkmode")) { + html.classList.remove("darkmode"); + } } } @@ -66,6 +242,7 @@ function inRange(target,min,max) { } function invokeLibrary(e) { + if (app.options.kidsmode) { return; } // 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]; @@ -122,6 +299,7 @@ function repopBoards (current=0) { if (i == app.boards.length) { name = "+"; boardTab.addEventListener("click",(e)=>{ + if (app.options.kidsmode) { return; } var current = app.boards.findIndex(i=>i==app.currentBoard); app.boards.push(new pecBoard()); repopBoards(current); @@ -139,6 +317,7 @@ function repopBoards (current=0) { } else { boardTab.classList.add("active_board"); } + if (undefined == app.boards[i]) { app.boards[i] = new pecBoard(); } if (undefined != app.boards[i].name) { name = app.boards[i].name; } @@ -165,6 +344,7 @@ 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]; + saveApp(); repopLibrary(); repopGrid(app.currentBoard); } @@ -173,6 +353,7 @@ 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))); + saveApp(); repopLibrary(); app.gui.newpec.root.classList.remove("focused"); } @@ -233,6 +414,7 @@ function repopLibrary () { app.gui.library.body.appendChild(newPec); } + // Initialize gui window.app = {}; @@ -258,7 +440,25 @@ app.gui.main.append(app.gui.library.root); 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") }); +app.gui.optionsButton.addEventListener("click", ()=>{ + if (app.options.kidsmode) { return; } + loadOptions(); + app.gui.options.root.classList.add("focused"); +}); +app.gui.optionsButton.addEventListener("mousedown", ()=>{ + if (!app.options.kidsmode) { return; } + app.gui.optionsButton.classList.add("holding"); + app.optionHold = setTimeout(()=>{ + app.gui.optionsButton.classList.remove("holding"); + loadOptions(); + app.gui.options.root.classList.add("focused"); + },3000); +}); +app.gui.optionsButton.addEventListener("mouseup", ()=>{ + if (!app.options.kidsmode) { return; } + app.gui.optionsButton.classList.remove("holding"); + clearTimeout(app.optionHold); +}); // New Pec popup app.gui.newpec = new auxillery_dialog("Create New Pec"); app.gui.newpec.body.classList.add("newpec_body"); @@ -273,48 +473,61 @@ for (var x = 0; x < 16; x++) { 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_darkmode.addEventListener("change", (e)=>{ + app.options.darkmode = e.target.checked; + toggleDarkmode(); + saveOptions(); +}); + +app.gui.options_kidsmode = document.createElement("div"); +app.gui.options.body.appendChild(app.gui.options_kidsmode); +app.gui.options_kidsmode.innerHTML = ' '; +app.gui.options_kidsmode.addEventListener("change", (e) => { + app.options.kidsmode = e.target.checked; + saveOptions(); +}); app.gui.options_audioFeedback = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_audioFeedback); app.gui.options_audioFeedback.innerHTML = ' '; +app.gui.options_audioFeedback.addEventListener("change", (e) => { + app.options.audioFeedback = e.target.checked; + saveOptions(); +}); app.gui.options_feedbackFloodLimit = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_feedbackFloodLimit); app.gui.options_feedbackFloodLimit.innerHTML = ' '; +app.gui.options_feedbackFloodLimit.addEventListener("change", (e) => { + app.options.feedbackFloodLimit = e.target.value; + saveOptions(); +}); app.gui.options_feedbackTimeout = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_feedbackTimeout); app.gui.options_feedbackTimeout.innerHTML = ' '; +app.gui.options_feedbackTimeout.addEventListener("change", (e) => { + app.options.feedbackTimeout = e.target.value; + saveOptions(); +}); + +app.gui.options_reset = document.createElement("div"); +app.gui.options.body.appendChild(app.gui.options_reset); +app.gui.options_reset.innerHTML = 'Completely Reset App (hold down for 3 seconds)Clear All Boards'; +app.gui.options_reset_hard = document.getElementById("reset-hard-reset"); +app.gui.options_reset_soft = document.getElementById("reset-soft-reset"); +// Add functionality for resets here app.gui.options_about = document.createElement("div"); app.gui.options.body.appendChild(app.gui.options_about); app.gui.options_about.innerHTML = 'App licenced Cards from Assistive Cards Project Source labs.murkfall.net'; +// Build New Pec UI app.gui.newpec.body.innerHTML = '



Create your new pec by naming it and providing an image.
'; app.gui.newpec_form = document.getElementById("new-pec-form"); app.gui.newpec_name = document.getElementById("new-pec-name"); @@ -325,3 +538,6 @@ app.gui.newpec_preview.classList.add("pecSlot"); app.gui.newpec_name.addEventListener("change", updatePreview); app.gui.newpec_image.addEventListener("change", updatePreview); +// initialize and load app +initApp(); +loadApp();