2025-08-18 16:50:40 -06:00
import { pecBoard , pec } from './pecslib.js' ;
2025-07-15 17:33:12 -06:00
// gui object classes?
// Why am I doing this?
class auxillery _dialog {
constructor ( title ) {
2025-07-21 15:52:20 -06:00
var me = this ;
2025-07-15 17:33:12 -06:00
this . root = document . createElement ( "div" ) ;
2025-07-21 15:52:20 -06:00
this . root . classList . add ( "dialog" ) ;
2025-07-15 17:33:12 -06:00
this . title = "string" == typeof title ? title : "" ;
var title _bar = document . createElement ( "div" ) ;
2025-08-12 16:08:02 -06:00
title _bar . classList . add ( "title_bar" ) ;
var titleText = document . createElement ( "span" ) ;
titleText . innerText = this . title ;
titleText . classList . add ( "title_text" ) ;
title _bar . appendChild ( titleText ) ;
2025-07-15 17:33:12 -06:00
this . root . appendChild ( title _bar ) ;
this . body = document . createElement ( "div" ) ;
2025-07-21 15:52:20 -06:00
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" ) ;
} ) ;
2025-07-15 17:33:12 -06:00
}
}
2025-08-12 16:09:08 -06:00
// 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.
}
2025-09-19 13:41:09 -06:00
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 ) ;
} ;
} ;
}
2025-08-18 19:54:13 -06:00
function toggleDarkmode ( ) {
var html = document . documentElement ;
2025-09-19 13:41:09 -06:00
if ( app . options . darkmode ) {
if ( ! ( html . classList . contains ( "darkmode" ) ) ) {
html . classList . add ( "darkmode" ) ;
}
2025-08-18 19:54:13 -06:00
} else {
2025-09-19 13:41:09 -06:00
if ( html . classList . contains ( "darkmode" ) ) {
html . classList . remove ( "darkmode" ) ;
}
2025-08-18 19:54:13 -06:00
}
}
2025-08-12 16:09:08 -06:00
function inRange ( target , min , max ) {
if ( isNaN ( target ) || isNaN ( min ) || isNaN ( max ) ) { return false ; }
return ( min <= target && target <= max ) ;
}
function invokeLibrary ( e ) {
2025-09-19 13:41:09 -06:00
if ( app . options . kidsmode ) { return ; }
2025-08-12 16:09:08 -06:00
// 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 = "<p>+</p>" ;
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" ) ;
2025-09-16 13:32:54 -06:00
var freshPec = new pec ( pecTemplate . word , pecTemplate . image ) ;
targetPec . appendChild ( freshPec . DOM ) ;
2025-08-12 16:09:08 -06:00
}
2025-08-18 15:18:56 -06:00
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 ) => {
2025-09-19 13:41:09 -06:00
if ( app . options . kidsmode ) { return ; }
2025-08-18 15:18:56 -06:00
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" ) ;
}
2025-09-19 13:41:09 -06:00
if ( undefined == app . boards [ i ] ) { app . boards [ i ] = new pecBoard ( ) ; }
2025-08-18 15:18:56 -06:00
if ( undefined != app . boards [ i ] . name ) {
name = app . boards [ i ] . name ;
}
}
boardTab . innerText = name ;
}
}
2025-08-12 16:09:08 -06:00
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" ) ;
2025-09-16 13:32:54 -06:00
app . currentBoard . pecs [ app . targetPecIndex ] = app . library . splice ( sourceID , 1 ) [ 0 ] ;
2025-09-19 13:41:09 -06:00
saveApp ( ) ;
2025-09-16 13:32:54 -06:00
repopLibrary ( ) ;
2025-08-12 16:09:08 -06:00
repopGrid ( app . currentBoard ) ;
}
2025-09-16 13:32:54 -06:00
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 ) ) ) ;
2025-09-19 13:41:09 -06:00
saveApp ( ) ;
2025-09-16 13:32:54 -06:00
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 ) ;
}
2025-09-19 13:41:09 -06:00
2025-07-07 16:09:29 -06:00
// 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 ) ;
2025-08-18 15:21:33 -06:00
app . gui . optionsButton = document . createElement ( "div" ) ;
app . gui . optionsButton . classList . add ( "options_button" ) ;
app . gui . optionsButton . innerHTML = "☰" ;
app . gui . menu . appendChild ( app . gui . optionsButton ) ;
2025-07-07 16:09:29 -06:00
app . gui . view = document . createElement ( "div" ) ;
app . gui . view . id = "view" ;
app . gui . main . appendChild ( app . gui . view ) ;
// Library popup
2025-07-15 17:33:12 -06:00
app . gui . library = new auxillery _dialog ( "Library" ) ;
2025-08-18 19:54:13 -06:00
app . gui . library . body . classList . add ( "library_body" ) ;
2025-08-12 16:08:02 -06:00
app . gui . main . append ( app . gui . library . root ) ;
2025-07-15 17:33:12 -06:00
// Options popup
app . gui . options = new auxillery _dialog ( "Options" ) ;
2025-08-18 19:54:13 -06:00
app . gui . options . body . classList . add ( "options_body" ) ;
2025-08-18 15:21:33 -06:00
app . gui . main . append ( app . gui . options . root ) ;
2025-09-19 13:41:09 -06:00
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 ) ;
} ) ;
2025-09-16 13:32:54 -06:00
// 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 ) ;
2025-07-07 16:09:29 -06:00
// pre-populate grid with emptys
for ( var x = 0 ; x < 16 ; x ++ ) {
var name = "pec" + x ;
2025-08-12 16:08:02 -06:00
var thisPec = document . createElement ( "span" ) ;
app . gui [ name ] = thisPec ;
thisPec . classList . add ( "pecSlot" ) ;
thisPec . id = name ;
clearPec ( x ) ;
2025-07-07 16:09:29 -06:00
app . gui . view . appendChild ( thisPec ) ;
}
2025-08-18 15:21:33 -06:00
// build Options UI
app . gui . options _darkmode = document . createElement ( "div" ) ;
app . gui . options . body . appendChild ( app . gui . options _darkmode ) ;
2025-08-18 19:54:13 -06:00
app . gui . options _darkmode . innerHTML = '<label for="darkmode-checkbox">Darkmode</label> <input id="darkmode-checkbox" type="checkbox" class="dm-indicator" />' ;
2025-09-19 13:41:09 -06:00
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 = '<label for="kidsmode-checkbox">Kids Mode (Disables adding pecs, boards, and accessing options (hold for 3 seconds to access)</label> <input id="kidsmode-checkbox" type="checkbox" class="toggle-switch" />' ;
app . gui . options _kidsmode . addEventListener ( "change" , ( e ) => {
app . options . kidsmode = e . target . checked ;
saveOptions ( ) ;
} ) ;
2025-08-18 19:54:13 -06:00
2025-08-18 15:21:33 -06:00
app . gui . options _audioFeedback = document . createElement ( "div" ) ;
app . gui . options . body . appendChild ( app . gui . options _audioFeedback ) ;
2025-08-18 19:54:13 -06:00
app . gui . options _audioFeedback . innerHTML = '<label for="audio-feedback-toggle">Read Allowed</label> <input id="audio-feedback-toggle" type="checkbox" class="toggle-switch" />' ;
2025-09-19 13:41:09 -06:00
app . gui . options _audioFeedback . addEventListener ( "change" , ( e ) => {
app . options . audioFeedback = e . target . checked ;
saveOptions ( ) ;
} ) ;
2025-08-18 19:54:13 -06:00
2025-08-18 15:21:33 -06:00
app . gui . options _feedbackFloodLimit = document . createElement ( "div" ) ;
app . gui . options . body . appendChild ( app . gui . options _feedbackFloodLimit ) ;
2025-08-18 19:54:13 -06:00
app . gui . options _feedbackFloodLimit . innerHTML = '<label for="flood-limit">Audio Flood Limit</label> <input id="flood-limit" type="number" min=1 step=2 max=10 />' ;
2025-09-19 13:41:09 -06:00
app . gui . options _feedbackFloodLimit . addEventListener ( "change" , ( e ) => {
app . options . feedbackFloodLimit = e . target . value ;
saveOptions ( ) ;
} ) ;
2025-08-18 19:54:13 -06:00
2025-08-18 15:21:33 -06:00
app . gui . options _feedbackTimeout = document . createElement ( "div" ) ;
app . gui . options . body . appendChild ( app . gui . options _feedbackTimeout ) ;
2025-08-18 19:54:13 -06:00
app . gui . options _feedbackTimeout . innerHTML = '<label for="flood-timeout">Audio Flood Timeout</label> <input id="flood-timeout" type="number" min=0 step=10 />' ;
2025-09-19 13:41:09 -06:00
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 = '<span id="reset-hard-reset" class="button">Completely Reset App (hold down for 3 seconds)</span><span id="reset-soft-reset" class="button">Clear All Boards</span>' ;
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
2025-08-18 21:15:09 -06:00
app . gui . options _about = document . createElement ( "div" ) ;
app . gui . options . body . appendChild ( app . gui . options _about ) ;
2025-09-16 13:32:54 -06:00
app . gui . options _about . innerHTML = 'App licenced <a href="LICENSE.txt" target="_blank"><img src="images/ui/gplv3-or-later.svg" height="30px" /></a><label>-</label> Cards from <a href="https://assistivecards.com" target="_blank"><img src="images/ui/ac.svg" height="30px" alt="Assistive Cards" /></a><label>-</label> Project Source <a href="https://labs.murkfall.net/bluesaxman/librePECS">labs.murkfall.net</a>' ;
2025-09-19 13:41:09 -06:00
// Build New Pec UI
2025-09-16 13:32:54 -06:00
app . gui . newpec . body . innerHTML = '<form id="new-pec-form"><label for="new-pec-name">Name</label><input id="new-pec-name" type="text" value="" /><br /><label for="new-pec-image">Picture</label><input id="new-pec-image" type="file" accept="image/png, image/jpeg, image/svg" value="" /><br /><div id="new-pec-preview"></div><br /><div id="new-pec-status">Create your new pec by naming it and providing an image.</div></form>' ;
app . gui . newpec _form = document . getElementById ( "new-pec-form" ) ;
app . gui . newpec _name = document . getElementById ( "new-pec-name" ) ;
app . gui . newpec _image = document . getElementById ( "new-pec-image" ) ;
app . gui . newpec _preview = document . getElementById ( "new-pec-preview" ) ;
app . gui . newpec _status = document . getElementById ( "new-pec-status" ) ;
app . gui . newpec _preview . classList . add ( "pecSlot" ) ;
app . gui . newpec _name . addEventListener ( "change" , updatePreview ) ;
app . gui . newpec _image . addEventListener ( "change" , updatePreview ) ;
2025-09-19 13:41:09 -06:00
// initialize and load app
initApp ( ) ;
loadApp ( ) ;