206 lines
8.5 KiB
JavaScript
206 lines
8.5 KiB
JavaScript
const statestore = require('../store/state.js')
|
|
|
|
const { addConnectionCredentials } = require('../utils/index.js')
|
|
|
|
module.exports = function (RED) {
|
|
function UiControlNode (config) {
|
|
const node = this
|
|
RED.nodes.createNode(this, config)
|
|
|
|
// which ui does this widget belong to
|
|
const ui = RED.nodes.getNode(config.ui)
|
|
|
|
function updateStore (all, items, msg, prop, value) {
|
|
items.forEach(function (item) {
|
|
const i = all[item]
|
|
if (i) {
|
|
// update the state store for each page
|
|
statestore.set(ui, i, msg, prop, value)
|
|
} else {
|
|
node.error("No item with the name '" + item + "' found", msg)
|
|
}
|
|
})
|
|
}
|
|
|
|
function emit (msg) {
|
|
ui.emit('ui-control:' + node.id, msg, node)
|
|
}
|
|
|
|
const evts = {
|
|
onInput: function (msg, send, done) {
|
|
const wNode = RED.nodes.getNode(node.id)
|
|
// handle the logic here of what to do when input is received
|
|
|
|
if (typeof msg.payload !== 'object') {
|
|
msg.payload = { page: msg.payload }
|
|
}
|
|
|
|
// switch to tab name (or number)
|
|
if ('tab' in msg.payload || 'page' in msg.payload) {
|
|
let page = msg.payload.page || msg.payload.tab
|
|
|
|
page = (page === undefined) ? '' : page
|
|
|
|
if (page === '-1' || page === '+1' || typeof (page) === 'number' || page === '') {
|
|
// special case for -1 and +1 to switch to previous/next tab
|
|
// number to pick specific index
|
|
// "" to refresh the page
|
|
// ensure consistency in payload format
|
|
msg.payload.page = page
|
|
emit(msg)
|
|
} else {
|
|
let pageFound = false
|
|
// check we have a valid tab/page name
|
|
RED.nodes.eachNode(function (n) {
|
|
if (n.type === 'ui-page') {
|
|
if (n.name === page) {
|
|
// ensure consistency in payload format
|
|
msg.payload.page = page
|
|
// send a message to the ui to switch to this tab
|
|
emit(msg)
|
|
pageFound = true
|
|
}
|
|
}
|
|
})
|
|
if (!pageFound) {
|
|
node.error("No page with the name '" + page + "' found")
|
|
}
|
|
}
|
|
}
|
|
|
|
// show/hide or enable/disable tabs
|
|
if ('tabs' in msg.payload || 'pages' in msg.payload) {
|
|
const pages = msg.payload.pages || msg.payload.tabs
|
|
// get a map of page name to page object
|
|
const allPages = {}
|
|
RED.nodes.eachNode((n) => {
|
|
if (n.type === 'ui-page') {
|
|
allPages[n.name] = n
|
|
}
|
|
})
|
|
|
|
// const pMap = RED.nodes.forEach
|
|
if ('show' in pages) {
|
|
updateStore(allPages, pages.show, msg, 'visible', true)
|
|
}
|
|
if ('hide' in pages) {
|
|
updateStore(allPages, pages.hide, msg, 'visible', false)
|
|
}
|
|
if ('enable' in pages) {
|
|
updateStore(allPages, pages.enable, msg, 'disabled', false)
|
|
}
|
|
if ('disable' in pages) {
|
|
updateStore(allPages, pages.disable, msg, 'disabled', false)
|
|
}
|
|
|
|
// ensure consistency in payload format
|
|
msg.payload.pages = pages
|
|
// send to front end in order to action there too
|
|
emit(msg)
|
|
}
|
|
|
|
// show or hide ui groups
|
|
if ('groups' in msg.payload || 'group' in msg.payload) {
|
|
const groups = msg.payload.groups || msg.payload.group
|
|
// get a map of group name to group object
|
|
const allGroups = {}
|
|
RED.nodes.eachNode((n) => {
|
|
if (n.type === 'ui-group') {
|
|
allGroups[n.name] = n
|
|
}
|
|
})
|
|
if ('show' in groups) {
|
|
const gs = groups.show.map((g) => {
|
|
const levels = g.split(':')
|
|
return levels.length > 1 ? levels[1] : g
|
|
})
|
|
updateStore(allGroups, gs, msg, 'visible', true)
|
|
}
|
|
if ('hide' in groups) {
|
|
const gh = groups.hide.map((g) => {
|
|
const levels = g.split(':')
|
|
return levels.length > 1 ? levels[1] : g
|
|
})
|
|
updateStore(allGroups, gh, msg, 'visible', false)
|
|
}
|
|
if ('enable' in groups) {
|
|
const ge = groups.enable.map((g) => {
|
|
const levels = g.split(':')
|
|
return levels.length > 1 ? levels[1] : g
|
|
})
|
|
updateStore(allGroups, ge, msg, 'disabled', false)
|
|
}
|
|
if ('disable' in groups) {
|
|
const gd = groups.disable.map((g) => {
|
|
const levels = g.split(':')
|
|
return levels.length > 1 ? levels[1] : g
|
|
})
|
|
updateStore(allGroups, gd, msg, 'disabled', true)
|
|
}
|
|
// ensure consistency in payload format
|
|
msg.payload.groups = groups
|
|
emit(msg)
|
|
|
|
// send specific visible/hidden commands via SocketIO here,
|
|
// so all logic stays server-side
|
|
|
|
wNode.send({ payload: 'input' })
|
|
}
|
|
|
|
if ('url' in msg.payload) {
|
|
emit(msg)
|
|
}
|
|
},
|
|
onSocket: {
|
|
connection: function (conn) {
|
|
if (config.events === 'all' || config.events === 'connect') {
|
|
const wNode = RED.nodes.getNode(node.id)
|
|
let msg = {
|
|
payload: 'connect'
|
|
}
|
|
msg = addConnectionCredentials(RED, msg, conn, ui)
|
|
wNode.send(msg)
|
|
}
|
|
},
|
|
disconnect: function (conn) {
|
|
if (config.events === 'all' || config.events === 'connect') {
|
|
const wNode = RED.nodes.getNode(node.id)
|
|
let msg = {
|
|
payload: 'lost'
|
|
}
|
|
msg = addConnectionCredentials(RED, msg, conn, ui)
|
|
wNode.send(msg)
|
|
}
|
|
},
|
|
'ui-control': function (conn, id, evt, payload) {
|
|
if (id === node.id && (config.events === 'all' || config.events === 'change')) {
|
|
// this message was sent by this particular node
|
|
if (evt === 'change') {
|
|
const wNode = RED.nodes.getNode(node.id)
|
|
let msg = {
|
|
payload: 'change',
|
|
tab: payload.page, // index of tab
|
|
name: payload.name // page name
|
|
}
|
|
msg = addConnectionCredentials(RED, msg, conn, ui)
|
|
wNode.send(msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// inform the dashboard UI that we are adding this node
|
|
ui.register(null, null, node, config, evts)
|
|
|
|
node.on('close', function (removed, done) {
|
|
if (removed) {
|
|
// handle node being removed
|
|
ui?.deregister(null, null, node)
|
|
}
|
|
done()
|
|
})
|
|
}
|
|
RED.nodes.registerType('ui-control', UiControlNode)
|
|
}
|