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) }