-// 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')
} 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')
// 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
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(/^<class '(str|int|float|bool)'>$/g)) {
+ literal = false
+ }
+ if (value == '<class \'bool\'>' || 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 == '<class \'str\'>' ||
+ (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 == '<class \'int\'>' || value == '<class \'float\'>' ||
+ typeof(value) == 'number') {
+ input.setAttribute('type', 'number')
+ if (value == '<class \'int\'>') {
+ input.setAttribute('step', '1')
+ } else if (value == '<class \'float\'>') {
+ input.setAttribute('step', 'any')
+ }
+ } else if (value == '<class \'str\'>' || 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 == '<class \'str\'>' ||
+ (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')
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 '<class ...>':
- } else if (value == '<class \'str\'>') {
- 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 == '<class \'int\'>') {
- 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 == '<class \'float\'>') {
- 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 == '<class \'bool\'>') {
- 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)
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 = '<h3>=></h3>'
- 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 = '<h3><=</h3>'
- 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 = '<h3>=></h3>'
+ 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 = '<h3><=</h3>'
+ 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)
}
})