/**
* @module Main
* @author Eric Udlis, Luke Houge
* @description Handles all responsive UI elements of the dashboard
*/
const { remote: REMOTE } = require('electron');
const ELECTRON_WINDOW = REMOTE.getCurrentWindow();
const CONFIG = require('./public/javascripts/config');
const CONFIG_CONSTANTS = require('./public/javascripts/config').constants;
const RATE = CONFIG_CONSTANTS.DATA_SEND_RATE;
/*
Modals
Purpose: code for opening a pop up modal box
*/
const SETTINGS_MODAL = document.querySelector('.settingsModal');
const SETTINGS_TRIGGER = document.getElementById('settingsTrigger');
const CLOSE_BUTTON = document.querySelector('.close-button');
/**
* Toggles hide/show of the settings modal
*/
function toggleSettingsModal() {
SETTINGS_MODAL.classList.toggle('show-modal');
fillConstants(); // eslint-disable-line no-use-before-define
}
if (SETTINGS_TRIGGER) SETTINGS_TRIGGER.addEventListener('click', toggleSettingsModal);
if (CLOSE_BUTTON) CLOSE_BUTTON.addEventListener('click', toggleSettingsModal);
// window.addEventListener('click', windowOnClick);
/*
Focus Clone
Purpose: Fill boxes at the top with live information from tables, for better visibility
*/
// Counters for Focus Header
let focusOne;
let focusTwo;
let focusThree;
let focusFour;
const settingsSubmit = document.getElementById('podSettingsSubmit');
// filling for focus clone
let x = 1; // counter for boxes filed so far
/**
* Clones the value from a table to the focus header
* @param {String} id ID of sensor to focus
*/
function clone(id) { // eslint-disable-line no-unused-vars
if (x === 1) {
// clone for box 1
focusOne = setInterval(() => {
const value = Number(document.getElementById(id).innerHTML); // gets value from table
// const fixedValue = value.toFixed(3);
document.getElementById('header_value_1').innerHTML = value; // sets the value tp the box
let name = id.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2'); // changes the ID from camel case to regular
name = name.charAt(0).toUpperCase() + name.slice(1); // Capitalizes first letter
document.getElementById('header_label_1').innerHTML = name; // sets that as the label for the box
}, RATE); // updates every 300 ms
x += 1;
} else if (x === 2) {
// clone for box 2
focusTwo = setInterval(() => {
const value = Number(document.getElementById(id).innerHTML);
// const value = value.toFixed(3);
document.getElementById('header_value_2').innerHTML = value;
let name = id.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2'); // changes the ID from camel case to regular
name = name.charAt(0).toUpperCase() + name.slice(1); // Capitalizes first letter
document.getElementById('header_label_2').innerHTML = name;
}, RATE);
x += 1;
} else if (x === 3) {
// clone for box 3
focusThree = setInterval(() => {
const value = Number(document.getElementById(id).innerHTML);
// const value = value.toFixed(3);
document.getElementById('header_value_3').innerHTML = value;
let name = id.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2'); // changes the ID from camel case to regular
name = name.charAt(0).toUpperCase() + name.slice(1); // Capitalizes first letter
document.getElementById('header_label_3').innerHTML = name;
}, RATE);
x += 1;
} else if (x === 4) {
// clone for box 4
focusFour = setInterval(() => {
const value = Number(document.getElementById(id).innerHTML);
// const value = value.toFixed(3);
document.getElementById('header_value_4').innerHTML = value;
let name = id.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2'); // changes the ID from camel case to regular
name = name.charAt(0).toUpperCase() + name.slice(1); // Capitalizes first letter
document.getElementById('header_label_4').innerHTML = name;
}, RATE);
x += 1;
} else if (x > 4) {
alert('Max of 4 values reached, please remove one and try again');
}
}
/**
* Clears the focus div
*/
function clear() { // eslint-disable-line no-unused-vars
for (let i = 1; i < 5; i += 1) {
clearInterval(focusOne);
clearInterval(focusTwo);
clearInterval(focusThree);
clearInterval(focusFour);
document.getElementById(`header_value_${String(i)}`).innerHTML = '';
document.getElementById(
`header_label_${String(i)}`,
).innerHTML = `Value ${String(i)}`;
}
x = 1;
}
/*
Tables
*/
const tableIDs = ['motion', 'braking_table', 'battery', 'motor']; // arrays for loop to iterate through
const divIDs = ['motion_div', 'braking_div', 'battery_pack_div', 'motor_div'];
/**
* Dynamically styles cells and table based on if value is within nominal range
*/
setInterval(() => {
let errorChecker = 0;
for (let u = 0; u < 4; u += 1) {
const table = document.getElementById(tableIDs[u]); // Calls table from array
for (let r = 1, n = table.rows.length; r < n; r += 1) { // iterates through rows in given table
const min = parseFloat(table.rows[r].cells[1].innerHTML);
const max = parseFloat(table.rows[r].cells[3].innerHTML);
const y = parseFloat(table.rows[r].cells[2].innerHTML);
if (y < min || y > max || y === 'Off') { // checks if too low
table.rows[r].cells[2].style.backgroundColor = '#FC6962';
errorChecker += 1; // adds to w, signifying that there is an error present in the table
} else {
table.rows[r].cells[2].style.backgroundColor = '#fff'; // else sets to white background
}
}
if (errorChecker !== 0) { // if there was an error in any row during one run of the for loop,
// meaning errorChecker is not 0 as it was created as,
// then change the class of the div that tavble is in to 'error',
// which will make the border color red
document.getElementById(divIDs[u]).className = 'error';
errorChecker = 0;
} else { // if there was not an error during the for loop in any row,
// then keep the class of the div as 'ok'
document.getElementById(divIDs[u]).className = 'ok';
errorChecker = 0;
}
}
}, RATE);
// Table Search Boxes
/**
* Takes the value of the table search box and filters table
* @param {String} tableID The name of the table to search
*/
function searchTable(tableID) { // eslint-disable-line no-unused-vars
let input; let filter; let table; let tr; let td;
input = document.getElementById(`${tableID}input`);
filter = input.value.toUpperCase();
table = document.getElementById(tableID);
tr = table.getElementsByTagName('tr');
for (let i = 0; i < tr.length; i += 1) {
td = tr[i].getElementsByTagName('td')[0];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) tr[i].style.display = '';
else tr[i].style.display = 'none';
}
}
}
/*
Settings form
Purpose: Read and Write to Config File
*/
// Submits Entries to File
if (settingsSubmit) {
settingsSubmit.addEventListener('click', () => {
let constsCache = {
dataSendRate: null,
renderInterval: null,
serverAddr: {
ip: null,
port: null,
},
hvBone: {
ip: null,
port: null,
},
lvBone: {
ip: null,
port: null,
},
};
constsCache.serverAddr.ip = document.getElementById('podIP').value;
constsCache.serverAddr.port = Number(document.getElementById('podPort').value);
constsCache.dataSendRate = Number(document.getElementById('scanningRate').value);
constsCache.hvBone.ip = document.getElementById('hvBoneIP').value;
constsCache.hvBone.port = Number(document.getElementById('hvBonePort').value);
constsCache.lvBone.ip = document.getElementById('lvBoneIP').value;
constsCache.lvBone.port = Number(document.getElementById('lvBonePort').value);
constsCache.renderInterval = Number(document.getElementById('renderInterval').value);
document.getElementById('formFeedback').innerHTML = CONFIG.writeJSON(constsCache);
ELECTRON_WINDOW.reload();
});
}
/**
* Fills constants in settings modal
*/
function fillConstants() { // eslint-disable-line no-unused-vars
CONFIG.updateConstants();
document.getElementById('formFeedback').innerHTML = 'Will restart for changes to take place.';
document.getElementById('podIP').value = String(CONFIG_CONSTANTS.serverAddr.ip);
document.getElementById('podPort').value = CONFIG_CONSTANTS.serverAddr.port;
document.getElementById('scanningRate').value = CONFIG_CONSTANTS.dataSendRate;
document.getElementById('lvBoneIP').value = CONFIG_CONSTANTS.lvBone.ip;
document.getElementById('lvBonePort').value = CONFIG_CONSTANTS.lvBone.port;
document.getElementById('hvBoneIP').value = CONFIG_CONSTANTS.hvBone.ip;
document.getElementById('hvBonePort').value = CONFIG_CONSTANTS.hvBone.port;
document.getElementById('renderInterval').value = CONFIG_CONSTANTS.renderInterval;
}
// Window Handling
document.getElementById('min-window').addEventListener('click', () => {
ELECTRON_WINDOW.minimize();
});
document.getElementById('max-window').addEventListener('click', () => {
if (!ELECTRON_WINDOW.isMaximized()) ELECTRON_WINDOW.maximize();
else ELECTRON_WINDOW.unmaximize();
});
document.getElementById('close-window').addEventListener('click', () => {
ELECTRON_WINDOW.close();
});
const FOCUS_DROPDOWN = new Dropdown('focusAddButton', 'Add Values', document.getElementById('focusBox'), true, clone); // eslint-disable-line
const FOCUS_CLEAR = new Dropdown('focusClear', 'Clear', document.getElementById('focusBox'), false);
FOCUS_CLEAR.onClick(clear);
/**
* Since Luke said this was impossible and could not be done, I had to do this myself
* As you can see, it is possible, not fun, not quick, but possible.
* This allows dropdowns to dissapear whenever you click out of them
* Never let anything hold you back, set your mind to whatever you want and you will achieve it
* ~ Eric Udlis 03/25/2020
*/
document.body.addEventListener('click', (e) => {
let isADropdown = false;
if (e.target.classList.contains('dropdown-content') || e.target.parentNode.classList.contains('dropdown-content')) isADropdown = true;
if (e.target.classList.contains('dropbtn') || e.target.parentNode.classList.contains('dropbtn')) isADropdown = true;
if (!isADropdown) {
Dropdown.getListOfDropdowns().forEach((dropdown) => {
if (dropdown.list) dropdown.list.classList.remove('show');
});
}
});