From: Benjamin Braatz Date: Sun, 7 Mar 2021 19:21:21 +0000 (+0100) Subject: Refactor debug Javascript code (for DRY-ness, ...) X-Git-Tag: v0.3.0~38 X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=a431da29a44fd717fb9388d24de95bb32f512d3e;p=graphit%2Fcontrolpi-wsserver.git Refactor debug Javascript code (for DRY-ness, ...) --- diff --git a/web/controlpi-debug.js b/web/controlpi-debug.js index e9e39dd..8c6eebc 100644 --- a/web/controlpi-debug.js +++ b/web/controlpi-debug.js @@ -1,11 +1,3 @@ -// Open Websocket back to ControlPi we were loaded from: -const websocket = new WebSocket("ws://" + window.location.host) - -// When Websocket is ready request all clients from bus: -websocket.addEventListener('open', function (event) { - websocket.send(JSON.stringify({target: '', command: 'get clients'})) -}) - // Create section for client: function createForClient(client) { const section = document.createElement('section') @@ -46,9 +38,19 @@ function createForMessage(message, isMessage) { } else { const table = document.createElement('table') for (const key in message) { - if (isMessage && key == 'sender') { - // Ignore 'sender' for messages (redundant in client heading): - continue + if (isMessage) { + if (key == 'sender') { + // Ignore 'sender' for last received messages + // (information redundantly present in client heading): + continue + } else if (key == 'state') { + // Set background according to state: + if (message[key]) { + div.classList.add('green') + } else { + div.classList.add('red') + } + } } // Append table row for key-value pair: const tr = document.createElement('tr') @@ -75,9 +77,15 @@ function createForMessage(message, isMessage) { // Resize input fields to current input: function resizeInput() { const tmp = document.createElement('span') - // Hack: Should get this from CSS class rather than hardcode it: + // HACK: Should get this from CSS rather than hardcode it: tmp.setAttribute('style', 'font-size: 0.8rem;') - const tmpContent = document.createTextNode(this.value) + var value = this.value + // HACK: 'number' fields for floats do not return trailing dot in + // value, so we add one unconditionally: + if (this.type == 'number' && this.step == 'any' && !value.includes('.')) { + value += '.' + } + const tmpContent = document.createTextNode(value) tmp.appendChild(tmpContent) document.body.appendChild(tmp); const width = tmp.getBoundingClientRect().width @@ -85,6 +93,97 @@ function resizeInput() { this.style.width = width + 'px' } +// Create form input (or select) for key-value pairs in receive templates: +function inputsForKeyValue(key, value) { + result = [] + literal = true + if (value.match(/^$/g)) { + literal = false + } + if (value == '' || typeof(value) == 'boolean') { + // Create select with true and false options for Boolean: + const select = document.createElement('select') + select.setAttribute('name', key) + const optionTrue = document.createElement('option') + optionTrue.setAttribute('value', 'true') + const optionTrueContent = document.createTextNode('true') + optionTrue.appendChild(optionTrueContent) + select.appendChild(optionTrue) + const optionFalse = document.createElement('option') + optionFalse.setAttribute('value', false) + const optionFalseContent = document.createTextNode('false') + optionFalse.appendChild(optionFalseContent) + if (literal) { + // Select set value and disable other value + // for literal Boolean: + if (value) { + optionTrue.setAttribute('selected', '') + optioniFalse.setAttribute('disabled', '') + } else { + optionFalse.setAttribute('selected', '') + optionTrue.setAttribute('disabled', '') + } + } + select.appendChild(optionFalse) + result.push(select) + } else { + // Create input for everything except Booleans: + if (value == '' || + (literal && typeof(value) == 'string' && key != 'command')) { + // Quote strings: + const openquote = document.createTextNode('"') + result.push(openquote) + } + const input = document.createElement('input') + // Set type of input: + if (key == 'command') { + input.setAttribute('type', 'hidden') + } else if (value == '' || value == '' || + typeof(value) == 'number') { + input.setAttribute('type', 'number') + if (value == '') { + input.setAttribute('step', '1') + } else if (value == '') { + input.setAttribute('step', 'any') + } + } else if (value == '' || typeof(value) == 'string') { + input.setAttribute('type', 'text') + } + // Set key as name of input: + input.setAttribute('name', key) + // Set value of input, readonly or required: + if (key == 'command') { + input.setAttribute('value', value) + } else if (literal) { + input.setAttribute('value', value) + input.setAttribute('readonly', '') + } else { + input.setAttribute('value', '') + input.setAttribute('required', '') + } + if (key != 'command') { + // Resize input field at every change and at beginning: + input.addEventListener('input', resizeInput) + resizeInput.call(input) + } + result.push(input) + if (value == '' || + (literal && typeof(value) == 'string' && key != 'command')) { + // Quote strings: + const closequote = document.createTextNode('"') + result.push(closequote) + } + if (key == 'command') { + // Add submit button for 'command' key: + const submit = document.createElement('input') + submit.setAttribute('type', 'submit') + submit.setAttribute('value', value) + result.push(submit) + } + } + return result +} + // Create div, form and table for command template: function createForCommand(template) { const div = document.createElement('div') @@ -121,113 +220,8 @@ function createForCommand(template) { keyTd.appendChild(keyTdContent) tr.appendChild(keyTd) const valueTd = document.createElement('td') - value = template[key] - // Create submit button for command itself: - if (key == 'command') { - const hidden = document.createElement('input') - hidden.setAttribute('type', 'hidden') - hidden.setAttribute('name', key) - hidden.setAttribute('value', value) - valueTd.appendChild(hidden) - const submit = document.createElement('input') - submit.setAttribute('type', 'submit') - submit.setAttribute('value', value) - valueTd.appendChild(submit) - // Create appropriate input fields for '': - } else if (value == '') { - const openquote = document.createTextNode('"') - valueTd.appendChild(openquote) - const input = document.createElement('input') - input.setAttribute('type', 'text') - input.setAttribute('name', key) - input.setAttribute('value', '') - input.setAttribute('required', '') - input.addEventListener('input', resizeInput) - resizeInput.call(input) - valueTd.appendChild(input) - const closequote = document.createTextNode('"') - valueTd.appendChild(closequote) - } else if (value == '') { - const input = document.createElement('input') - input.setAttribute('type', 'number') - input.setAttribute('step', '1') - input.setAttribute('name', key) - input.setAttribute('value', '') - input.setAttribute('required', '') - input.addEventListener('input', resizeInput) - resizeInput.call(input) - valueTd.appendChild(input) - } else if (value == '') { - const input = document.createElement('input') - input.setAttribute('type', 'number') - input.setAttribute('step', 'any') - input.setAttribute('name', key) - input.setAttribute('value', '') - input.setAttribute('required', '') - input.addEventListener('input', resizeInput) - resizeInput.call(input) - valueTd.appendChild(input) - } else if (value == '') { - const select = document.createElement('select') - select.setAttribute('name', key) - const optionTrue = document.createElement('option') - optionTrue.setAttribute('value', true) - const optionTrueContent = document.createTextNode('true') - optionTrue.appendChild(optionTrueContent) - select.appendChild(optionTrue) - const optionFalse = document.createElement('option') - optionFalse.setAttribute('value', false) - const optionFalseContent = document.createTextNode('false') - optionFalse.appendChild(optionFalseContent) - select.appendChild(optionFalse) - valueTd.appendChild(select) - // Create readonly input fields for literals: - } else if (typeof(value) == 'string') { - const openquote = document.createTextNode('"') - valueTd.appendChild(openquote) - const input = document.createElement('input') - input.setAttribute('type', 'text') - input.setAttribute('name', key) - input.setAttribute('value', value) - input.setAttribute('readonly', '') - input.addEventListener('input', resizeInput) - resizeInput.call(input) - valueTd.appendChild(input) - const closequote = document.createTextNode('"') - valueTd.appendChild(closequote) - } else if (typeof(value) == 'number') { - const input = document.createElement('input') - input.setAttribute('type', 'number') - input.setAttribute('name', key) - input.setAttribute('value', value) - input.setAttribute('readonly', '') - input.addEventListener('input', resizeInput) - resizeInput.call(input) - valueTd.appendChild(input) - } else if (typeof(value) == 'boolean') { - const select = document.createElement('select') - select.setAttribute('name', key) - const optionTrue = document.createElement('option') - optionTrue.setAttribute('value', 'true') - if (value) { - optionTrue.setAttribute('selected', '') - } else { - optionTrue.setAttribute('disabled', '') - } - const optionTrueContent = document.createTextNode('true') - optionTrue.appendChild(optionTrueContent) - select.appendChild(optionTrue) - const optionFalse = document.createElement('option') - optionFalse.setAttribute('value', 'false') - if (value) { - optionTrue.setAttribute('disabled', '') - } else { - optionTrue.setAttribute('selected', '') - } - const optionFalseContent = document.createTextNode('false') - optionFalse.appendChild(optionFalseContent) - select.appendChild(optionFalse) - valueTd.appendChild(select) + for (const element of inputsForKeyValue(key, template[key])) { + valueTd.appendChild(element) } tr.appendChild(valueTd) table.appendChild(tr) @@ -237,66 +231,72 @@ function createForCommand(template) { return div } -// When receiving message from ControlPi through Websocket: -websocket.addEventListener('message', function (event) { - const message = JSON.parse(event.data) - if (message.sender == '') { - // When message is from bus: - if (message.event == 'unregistered') { - // On deregistration delete section if it exists: - const clientElement = document.getElementById(message.client) - if (clientElement != null) { - clientElement.remove() - } - } else { - // On registration or initial answers for all clients: - const clientElement = document.getElementById(message.client) - if (clientElement == null) { - // Create section for client if not existent: - const main = document.getElementById('ControlPi Debug') - main.appendChild(createForClient(message.client)) - } - const receiveContainer = document.getElementById(message.client + ' Receives') - receiveContainer.innerHTML = '

=>

' - for (const template of message.receives) { - if (template.command != null) { - const templateElement = createForCommand(template) - receiveContainer.appendChild(templateElement) - } else { - const templateElement = createForMessage(template) - receiveContainer.appendChild(templateElement) - } - } - const sendContainer = document.getElementById(message.client + ' Sends') - sendContainer.innerHTML = '

<=

' - for (const template of message.sends) { - const templateElement = createForMessage(template) - sendContainer.appendChild(templateElement) - } +// Remove (if unregistered) or create (if not existent) elements for +// clients and update interface information: +function processBusMessage(message) { + // When message is from bus: + if (message['event'] == 'unregistered') { + // On deregistration delete client element if it exists: + const clientElement = document.getElementById(message['client']) + if (clientElement != null) { + clientElement.remove() } } else { - // When message is from client: - const clientElement = document.getElementById(message.sender) + // On registration or 'get clients' answer: + const clientElement = document.getElementById(message['client']) if (clientElement == null) { - // Create section for client if not existent: + // Create element for client if not existent: const main = document.getElementById('ControlPi Debug') - main.appendChild(createForClient(message.sender)) + main.appendChild(createForClient(message['client'])) } - // Update last received message: - const lastContainer = document.getElementById(message.sender + ' Last') - lastContainer.innerHTML = '' - const messageElement = createForMessage(message, true) - lastContainer.appendChild(messageElement) - if (message.state != null) { - // If this is some kind of state set background - // green or red depending on it: - if (message.state) { - messageElement.classList.remove('red') - messageElement.classList.add('green') + // Crate message elements for receives interface: + const receiveContainer = document.getElementById(message['client'] + ' Receives') + receiveContainer.innerHTML = '

=>

' + for (const template of message.receives) { + if (template['command'] != null) { + receiveContainer.appendChild(createForCommand(template)) } else { - messageElement.classList.remove('green') - messageElement.classList.add('red') + receiveContainer.appendChild(createForMessage(template)) } } + // Create message elements for sends interface: + const sendContainer = document.getElementById(message['client'] + ' Sends') + sendContainer.innerHTML = '

<=

' + for (const template of message.sends) { + sendContainer.appendChild(createForMessage(template)) + } + } +} + +// Create element for client (if not existent) +// and update last received message: +function processClientMessage(message) { + const clientElement = document.getElementById(message['sender']) + if (clientElement == null) { + // Create section for client if not existent: + const main = document.getElementById('ControlPi Debug') + main.appendChild(createForClient(message.sender)) + } + // Update last received message: + const lastContainer = document.getElementById(message['sender'] + ' Last') + lastContainer.innerHTML = '' + lastContainer.appendChild(createForMessage(message, true)) +} + +// Open Websocket back to ControlPi we were loaded from: +const websocket = new WebSocket("ws://" + window.location.host) + +// When Websocket is ready request all clients from bus: +websocket.addEventListener('open', function (event) { + websocket.send(JSON.stringify({target: '', command: 'get clients'})) +}) + +// When receiving message from ControlPi through Websocket: +websocket.addEventListener('message', function (event) { + const message = JSON.parse(event.data) + if (message['sender'] == '') { + processBusMessage(message) + } else { + processClientMessage(message) } })