mirror of
https://forge.murkfall.net/bluesaxman/librePECS.git
synced 2026-03-13 02:04:20 -06:00
258 lines
9.2 KiB
JavaScript
258 lines
9.2 KiB
JavaScript
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 = "<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");
|
|
targetPec.appendChild(pecTemplate.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.gui.library.body.childNodes.entries().forEach(p=>{
|
|
p[1].getAttribute("id") == "lPec"+sourceID ? p[1].style.display = "none" : null;
|
|
});
|
|
app.currentBoard.pecs[app.targetPecIndex] = app.library[sourceID];
|
|
repopGrid(app.currentBoard);
|
|
}
|
|
|
|
// 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") });
|
|
// 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;
|
|
// add more here
|
|
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);
|
|
});
|
|
|
|
// 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 = '<label for="darkmode-checkbox">Darkmode</label> <input id="darkmode-checkbox" type="checkbox" class="dm-indicator" />';
|
|
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 = '<label for="audio-feedback-toggle">Read Allowed</label> <input id="audio-feedback-toggle" type="checkbox" class="toggle-switch" />';
|
|
|
|
app.gui.options_feedbackFloodLimit = document.createElement("div");
|
|
app.gui.options.body.appendChild(app.gui.options_feedbackFloodLimit);
|
|
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 />';
|
|
|
|
app.gui.options_feedbackTimeout = document.createElement("div");
|
|
app.gui.options.body.appendChild(app.gui.options_feedbackTimeout);
|
|
app.gui.options_feedbackTimeout.innerHTML = '<label for="flood-timeout">Audio Flood Timeout</label> <input id="flood-timeout" type="number" min=0 step=10 />';
|
|
|
|
app.gui.options_about = document.createElement("div");
|
|
app.gui.options.body.appendChild(app.gui.options_about);
|
|
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>';
|