"use strict"; import Luci from './luci.js'; import Antifurto from './antifurto.js'; import { scenariToggleListener } from "./toggles.js"; import { showAlert, makeElementBounce } from "./alerts.js"; // JSON templeate for scenario const scenarioTemplate = { "nome": "", "data": "", "id": "" }; class Scenari { /** * the div where everything related to the Scenari is stored */ static scenariContainer = document.getElementById('scenariContainer'); /** * this is the select element where to add all the scenarios */ static addNameHere = document.getElementById('p'); /** * contains the toggle that is currently active */ static scenarioAttivoToggle = null; /** * counts how many scenarios have been added */ static index = 0; /** * the array where all of the user's scenari are stored */ static scenariBucket = []; constructor() { Scenari.scenariContainer = document.getElementById('scenariContainer'); Scenari.addNameHere = document.getElementById('p'); Scenari.init(); Scenari.antifurtoModalLauncherListener(); Scenari.anyModalListener(); } /** * sets up the listener for the button in the top of the page * i'm also leaving here what I haven't yet implemented */ static init() { Scenari.fillTable(); const scenariBtn = document.getElementById('mainButtonContainer').children[1]; scenariBtn.addEventListener('click', () => { Scenari.toggleContainer(true); Luci.toggleContainer(false); Antifurto.toggleContainer(false); }, false); const registerBtn = document.getElementById('scenari-registra'); registerBtn.addEventListener('click', event => { event.preventDefault(); // TODO choose how to register the scenario }, false); } /** * used for the antitheft button (ON/OFF): before opening the connected modal, change its text then open it * @see Scenari.antifurtoModalSubmitListener */ static antifurtoModalLauncherListener() { const antifurtoBtn = document.getElementById('scenari-stato-antifurto'); const okBtn = document.getElementById('attiva-antifurto-btn'); const modalBody = document.querySelector('#antifurto-modal .modal-body'); const modalLauncher = document.getElementById('lancia-antifurto-modal'); const registerBtn = document.getElementById('scenari-registra'); antifurtoBtn.addEventListener('click', () => { if (registerBtn.innerText !== "Registra") {//sto registrando showAlert("Attenzione", "Stai registrando uno scenario, termina la registrazione per attivare l'antifurto.", false); makeElementBounce(registerBtn); return; } if (Scenari.scenariBucket.length === 0){ showAlert('Nessun scenario disponibile', 'Registrare uno o più scenari per poter attivarne uno',false); // make the registerBtn bounce for 3 seconds const registerBtn = document.getElementById('scenari-registra'); makeElementBounce(registerBtn); return; } const children = modalBody.children; // p, p /* <p>Stai per abilitare l'antifurto.</p> <p>Vuoi continuare?</p> */ if (antifurtoBtn.innerText === 'ON') { // the antitheft is on children[0].innerText = "Stai per disabilitare l'antifurto."; okBtn.innerText = 'Disattiva'; } else { // the antitheft is off children[0].innerText = "Stai per abilitare l'antifurto."; okBtn.innerText = 'Attiva'; } // launch the modal modalLauncher.click(); // related: Scenari.antifurtoModalSubmitListener(); }, false); // TODO server get antifurto status and set it correctly // toggleAntifurto(response); } /** * sets up various listeners for the modals * @see Scenari.attivaScenarioModal * @see Scenari.registraModal * @see Scenari.antifurtoModalSubmitListener */ static anyModalListener() { Scenari.anteprimaModal(); Scenari.attivaScenarioModal(); Scenari.registraModal(); Scenari.antifurtoModalSubmitListener(); } /** * listens to the submit button in the modal for the antitheft and activates/deactivates the antitheft * this modal is shown when the user clicks on the antitheft button * @see Scenari.antifurtoModalLauncherListener */ static antifurtoModalSubmitListener() { const antifurtoOkBtn = document.getElementById('attiva-antifurto-btn'); const closeBtn = document.querySelector('#antifurto-modal .btn.btn-danger'); const antifurtoBtn = document.getElementById('scenari-stato-antifurto'); antifurtoOkBtn.addEventListener('click', () => { // close modal closeBtn.click(); // switch the antitheft status button const isActivatingAntitheft = antifurtoBtn.innerText === 'OFF'; // Scenari.toggleAntifurto(isActivatingAntitheft); if (isActivatingAntitheft) Scenari.activateRandomScenario(); else Scenari.deactivateActiveScenario();//cannot use toggleScenario because I don't have the scenarioID // Antifurto.activate(isActivatingAntitheft) already taken care of }, false); } /** * listens for the submit of the anteprima modal */ static anteprimaModal() { const anterprimaModal = document.getElementById('anteprima-modal'); const okBtn = anterprimaModal.querySelector('#anteprima-scenari-btn'); const modalBody = anterprimaModal.querySelector('.modal-body'); const closeBtn = anterprimaModal.querySelector('.btn.btn-danger'); okBtn.addEventListener('click', () => {//activate the scenario // close the modal closeBtn.click(); // get the scenario from the modal body const scenarioID = modalBody.querySelector('.invisible').innerText; // find the scenario's row on the page // const scenarioRow = scenarioTable.querySelector(`#${scenarioName}`); // const toggle = scenarioTable.querySelector(`#${scenarioName} .toggle-button`); Scenari.toggleScenario(scenarioID, okBtn.innerText === 'Attiva'); }, false); } /** * listenes for the submit button in the modal for activating/deactivating a scenario when chosen from the toggles * @see scenariToggleListener */ static attivaScenarioModal() { const modalBtn = document.getElementById('attiva-scenari-btn'); const closeBtn = document.querySelector('#attiva-scenari-modal .btn.btn-danger'); modalBtn.addEventListener('click', () => { // close the modal by clicking on the close button closeBtn.click(); // I have to get to the toggle and activate it from the value of the select const scenarioID = Scenari.addNameHere.value; const toggle = document.querySelector(`#${scenarioID} .toggle-button`); // has the user chosen to activate the scenario? // const isActivatingNewScenario = ; // Scenari.toggleToggle(toggle,isActivatingNewScenario); // Scenari.toggleAntifurto(isActivatingNewScenario); Scenari.toggleScenario(scenarioID, toggle !== Scenari.scenarioAttivoToggle); }, false); } /** * this is for the button "registra"/"termina" and the related modals * @see Scenari.registraBtnListener * @see Scenari.submitRecordModalListener * @see Scenari.endRecordingListener */ static registraModal() { const scenariModal = document.getElementById('scenari-modal'); const registraBtn = document.getElementById('scenari-registra'); const okBtn = scenariModal.querySelector('.btn.btn-info'); const modalBody = scenariModal.querySelector('.modal-body'); const htmlForModalBody = `<p>Stai per attivare la registrazione dei comandi.</p> <p>Dal momento in cui premerai sul bottone azzurro, inizierai a registrare tutte le interazioni con le tue luci.</p> <p>Inoltre disabiliterai eventuali scenari attivi.</p> <p>Una volta che sarai soddisfatto del risultato, torna su questa pagina per terminare la registrazione.</p> <p>Inizare la registrazione?</p>` const modalLauncher = document.getElementById('registra-modal-launcer'); // const okBtn = scenariModal.querySelector('.btn.btn-info'); // const registraBtn = document.getElementById('scenari-registra'); const closeScenariModalBtn = scenariModal.querySelector('.btn.btn-danger'); const endRecordingModalLauncher = document.getElementById('termina-registrazione-launcher'); const endRecordingForm = document.getElementById('termina-registrazione-form'); // const modalBody = scenariModal.querySelector('.modal-body'); // htmlForModalBody = ... const closeRecordingBtn = document.getElementById('termina-registrazione-modal').querySelector('.btn.btn-danger'); const campoNome = document.getElementById('nome-nuovo-scenario'); Scenari.registraBtnListener(registraBtn, okBtn, modalBody, htmlForModalBody, modalLauncher); Scenari.submitRecordModalListener(okBtn, registraBtn, closeScenariModalBtn, endRecordingModalLauncher); Scenari.endRecordingListener(endRecordingForm, modalBody, htmlForModalBody, closeRecordingBtn, campoNome); } /** * listens for the actual "registra"/"termina" button on the page and edits the related modal accordingly * @param {HTMLButtonElement} registraBtn the button on the page * @param {HTMLButtonElement} okBtn the submit button in the modal * @param {HTMLElement} modalBody the body of the modal which is to be changed * @param {string} htmlForModalBody a string containing the html to be inserted in the modal body * @param {HTMLButtonElement} modalLauncher the button which launches the modal * @see Scenari.registraModal */ static registraBtnListener(registraBtn, okBtn, modalBody, htmlForModalBody, modalLauncher) { registraBtn.addEventListener('click', () => { const isAboutToRecord = registraBtn.innerText === 'Registra'; // change text of okBtn okBtn.innerText = isAboutToRecord ? 'Registra' : 'Termina'; // change inner text of the modal if (isAboutToRecord) { modalBody.innerHTML = htmlForModalBody; } else { modalBody.innerHTML = ` <p>Stai per terminare la registrazione dei comandi.</p> <p>Terminare la registrazione?</p>`; } // launch the modal modalLauncher.click(); // related: Scenari.submitRecordModalListener }, false); } /** * listens for the submit of the modal which is launched when the user clicks on the "registra"/"termina" button * @param {HTMLButtonElement} okBtn the submit button in the modal * @param {HTMLButtonElement} registraBtn the button on the page (to understand if the user is about to record or not) * @param {HTMLButtonElement} closeScenariModalBtn the button used to close the modal * @param {HTMLButtonElement} endRecordingModalLauncher the button which launches the next modal (used if the user has finished recording) * @see Scenari.registraModal */ static submitRecordModalListener(okBtn, registraBtn, closeScenariModalBtn, endRecordingModalLauncher) { okBtn.addEventListener('click', () => { const isAboutToRecord = registraBtn.innerText === 'Registra'; // close modal closeScenariModalBtn.click(); // get the current time // const timeBegin = new Date(); // if the user ended the recording, show another modal to ask for the name of the scenario if (isAboutToRecord) { registraBtn.innerText = 'Termina'; // TODO server send a note to start recording Scenari.deactivateActiveScenario(); } else { registraBtn.innerText = 'Registra'; // TODO server send a note to stop recording endRecordingModalLauncher.click(); // related: Scenari.endRecordingListener } }, false); } /** * listens for the submit of the modal which is launched when the user is done recording a scenario * @param {HTMLFormElement} endRecordingForm the form used to submit the name of the scenario * @param {HTMLElement} modalBody the body of the modal which is to be changed * @param {string} htmlForModalBody a string containing the html to be inserted in the modal body * @param {HTMLButtonElement} closeRecordingBtn the button used to close the modal * @param {HTMLInputElement} campoNome the input field used to insert the name of the scenario by the user * @see Scenari.registraModal * @see Scenari.mostraNuovoScenario */ static endRecordingListener(endRecordingForm, modalBody, htmlForModalBody, closeRecordingBtn, campoNome) { endRecordingForm.addEventListener('submit', event => { event.preventDefault(); // reset the other modal and button modalBody.innerHTML = htmlForModalBody; // close the modal closeRecordingBtn.click(); const name = campoNome.value; const scenario = Scenari.createScenarioFromName(name) Scenari.mostraNuovoScenario(scenario); // clear the field for the next use campoNome.value = ''; // TODO server send a note to save the scenario }, false); } /** * takes care of launching a modal to preview the actions of the chosen scenario; it also listens for the submit of the launched modal * @param {HTMLButtonElement} anteprimaBtn the button used to launch the modal * @param {scenarioTemplate} scenario the JSON scenario to be previewed * @see Scenari.correctlySetToggle */ static anteprimaListener(anteprimaBtn, scenario) { const scenarioID = scenario.id; const anterprimaModal = document.getElementById('anteprima-modal'); const modalBody = anterprimaModal.querySelector('.modal-body'); const modalLauncher = document.getElementById('anteprima-modal-launcher'); const okBtn = anterprimaModal.querySelector('#anteprima-scenari-btn'); const scenarioTable = document.getElementById('table-row-scenari'); const registerBtn = document.getElementById('scenari-registra'); anteprimaBtn.addEventListener('click', () => { if (registerBtn.innerText !== "Registra") {//sto registrando showAlert("Attenzione", "Stai registrando uno scenario, termina la registrazione per mostrare l'anteprima.", false); makeElementBounce(registerBtn); return; } // TODO server get the scenario's data // show the scenario in the modal (choose how to display it) modalBody.innerHTML = ` <p>${scenario.nome}</p> <p>${scenario.data}</p> <span class='invisible'>${scenarioID}</span>`;//FIXME remove this test (be sure to keep the last span as is) // if the scenario is already active, change the okBtn text // to find if it's active, find the relative toggle // and check if it's active // const scenarioRow = scenarioTable.querySelector(`#${name}`); const toggle = scenarioTable.querySelector(`#${scenarioID} .toggle-button`); okBtn.innerText = toggle.classList.contains('active') ? 'Disattiva' : 'Attiva'; // launch the modal modalLauncher.click(); }, false); } // utilility functions /** * displays the given scenario on the screen (below every other one) and hooks up the listeners; then it increments the index * @param {scenarioTemplate} scenario the JSON scenario to be displayed * @see Scenari.createScenarioRow * @see Scenari.anteprimaListener * @see scenariToggleListener */ static mostraNuovoScenario(scenario) { const row = Scenari.createScenarioRow(scenario); const scenarioID = scenario.id; const tableBody = document.getElementById('table-row-scenari'); tableBody.appendChild(row); const anteprimaBtn = row.querySelector('.badge.rounded-pill'); Scenari.anteprimaListener(anteprimaBtn, scenario); // add the name of the scenario as option in the select of the modal const option = document.createElement('option'); option.value = scenarioID; option.innerText = scenario.nome; Scenari.addNameHere.appendChild(option); const toggle = row.querySelector('.toggle-button'); scenariToggleListener(toggle, scenarioID); // Scenari.index++; } /** * creates a table entry for the given scenario and returns it * @param {scenarioTemplate} scenario the JSON scenario to be displayed * @returns {HTMLTableRowElement} the row containing the given scenario */ static createScenarioRow(scenario) { const row = document.createElement('tr'); row.id = scenario.id; row.innerHTML = ` <th scope="row">${scenario.nome.trim()}</th> <td> <span>${scenario.data}</span> </td> <td> <h5> <span class="badge rounded-pill bg-secondary">anteprima</span> </h5> </td> <td> <div class="switch-container no-box-sizing"> <div id="toggle-${scenario.id}" class="toggle-button no-box-sizing"> <div class="inner-circle no-box-sizing"></div> </div> </div> <span id="launch-modal-${scenario.id}" data-bs-toggle="modal" data-bs-target="#attiva-scenari-modal"></span> </td>`; // the span is used by the toggle to launch the modal return row; } /** * changes the antifurto button to the given state * @param {Boolean} activating true if the antitheft is being activated, false otherwise * @param {Boolean} fromAntifurto usually false or undefined; true only if the function is called from the scenari */ static correctlySetAntifurto(activating,fromAntifurto = false) { if(fromAntifurto !== true) Antifurto.activate(activating,true); const antifurtoBtn = document.getElementById('scenari-stato-antifurto'); if (activating) { if (antifurtoBtn.innerText === 'ON') return; antifurtoBtn.innerText = 'ON'; antifurtoBtn.classList.remove('btn-danger'); antifurtoBtn.classList.add('btn-success'); } else { if (antifurtoBtn.innerText === 'OFF') return; antifurtoBtn.innerText = 'OFF'; antifurtoBtn.classList.remove('btn-success'); antifurtoBtn.classList.add('btn-danger'); } } /** * turns off the active scenario if there is one; useful if you don't have the scenarioID */ static deactivateActiveScenario() { if (Scenari.scenarioAttivoToggle == null) return; // Scenari.scenarioAttivoToggle.classList.remove('active'); // Scenari.scenarioAttivoToggle = null; // // TODO server deactivate the scenario const toggleID = Scenari.scenarioAttivoToggle.id; const scenarioID = toggleID.substring(toggleID.indexOf('-') + 1); Scenari.toggleScenario(scenarioID, false); } /** * activates/deactivates the scenario with the given name * @param {string} scenarioID the ID of the scenario to be activated/deactivated * @param {Boolean} activating true if the scenario should be activated, false otherwise */ static toggleScenario(scenarioID, activating) { // find the scenario's row on the page const scenarioRow = document.getElementById(`${scenarioID}`); const toggle = scenarioRow.querySelector('.toggle-button'); Scenari.correctlySetToggle(toggle); Scenari.correctlySetAntifurto(activating); // if (activating) { // // TODO server activate the scenario // } else { // // TODO server deactivate the scenario // } } /** * takes care of activating or deactivating the scenario's toggle based on the given boolean * @param {HTMLElement} toggle the toggle to be activated or deactivated * @see Scenari.correctlySetAntifurto */ static correctlySetToggle(toggle) { const isActivatingNewScenario = toggle !== Scenari.scenarioAttivoToggle; Scenari.scenarioAttivoToggle?.classList.remove('active'); if (isActivatingNewScenario) { toggle.classList.add('active'); Scenari.scenarioAttivoToggle = toggle; } else { toggle.classList.remove('active'); Scenari.scenarioAttivoToggle = null; } } /** * activates a random scenario from the ones available */ static activateRandomScenario() { if (Scenari.scenariBucket.length === 0){ showAlert('Nessun scenario disponibile', 'Registrare uno o più scenari per poter attivarne uno',false); // make the registerBtn bounce for 3 seconds const registerBtn = document.getElementById('scenari-registra'); makeElementBounce(registerBtn); return; } const randomIndex = Math.floor(Math.random() * Scenari.index); const scenarioID = `scenario-${randomIndex}`; // const table = document.getElementById('table-row-scenari'); // // enable a random scenario // const randomScenario = table.children[Math.floor(Math.random() * table.children.length)]; // const scenarioID = randomScenario.id; Scenari.toggleScenario(scenarioID, true); } /** * creates the scenario and updates index * @param {string} scenarioName the string of the scenario to create * @returns {scenarioTemplate} the JSON scenario */ static createScenarioFromName(scenarioName) { const scenario = Object.assign({}, scenarioTemplate); scenario.nome = scenarioName; scenario.data = new Date().toLocaleDateString();// assign at scenario.data the string YYYY/MM/DD from the current date scenario.id = `scenario-${Scenari.index}`; Scenari.index++; Scenari.scenariBucket.push(scenario); return scenario; } /** * fills the table with the scenarios available for the user in the server * @see Scenari.mostraNuovoScenario */ static fillTable() { // TODO server get all scenarios const scenari = []; for (const scenario of scenari) { Scenari.mostraNuovoScenario(scenario) // TODO server if scenario is active, change the toggle to active // also set Scenari.scenarioAttivoToggle to its toggle } } /** * toggle the visibility of the scenari container * @param {Boolean} visible true if the container should be shown, false otherwise */ static toggleContainer(visible) { if (visible) { Scenari.scenariContainer.classList.remove('invisible'); if(Scenari.scenariBucket.length === 0) { // make the register button bounce const registerBtn = document.getElementById('scenari-registra'); makeElementBounce(registerBtn); } } else { Scenari.scenariContainer.classList.add('invisible'); } } } export default Scenari;