// FlexDash raw HTML node for Node-RED -- Send raw HTML into a widget
// Copyright ©2023 by Thorsten von Eicken, see LICENSE
import { parse_html, fromJSON, HtmlArray, HtmlElement } from "./reflect-html"
import { Request, Response } from "express"
import type { Node, NodeDef, NodeMessageInFlow } from "node-red"
interface FDHtmlNodeDef extends NodeDef {
html_seq: number
plugin: any
}
interface Command {
selector?: string | unknown
command?: string | unknown
args?: unknown[]
}
interface FDHtmlNodeMessage extends NodeMessageInFlow, Command {
commands?: Command[] | unknown
}
export default function (RED: any) {
function flexdashHtml(this: Node, config: FDHtmlNodeDef) {
RED.nodes.createNode(this, config)
const plugin = RED.plugins.get("flexdash")
// Initialize the widget and get a handle onto the FlexDash widget API.
const widget = RED.plugins.get("flexdash").initWidget(this, config, "RawHTML")
if (!widget) return // missing config node, thus no FlexDash to hook up to, nothing to do here
let html_seq = 0
// API call to get the current DOM
RED.httpAdmin.get("/_fd_html/preview", (req: Request, res: Response) => {
console.log("GET /_fd_html/preview", req.query)
res.set("Content-Type", "application/json")
const data = JSON.stringify({ seq: html_seq, html: widget.get("html") })
console.log(data)
res.send(data)
})
this.on("input", (msg: FDHtmlNodeMessage) => {
let updated = false
let html
// if we have a string payload then parse that as HTML and notify flow editors
if (typeof msg.payload == "string") {
html = parse_html(msg.payload)
updated = true
} else {
const json = widget.get("html")
html = new HtmlArray(...json.map(fromJSON))
}
let sel = html
//console.log("INPUT", msg, html)
// process array of commands as well as single command
const commands = Array.isArray(msg.commands) ? msg.commands : []
if (msg.command)
commands.unshift({ selector: msg.selector, command: msg.command, args: msg.args })
for (const cmd of commands) {
if (typeof cmd.command == "string" && typeof html[cmd.command] == "function") {
// process any selector
if (typeof cmd.selector == "string") {
if (cmd.selector == "") sel = html
if (cmd.selector != ".") sel = html.select(cmd.selector)
}
// fix-up arguments
let args = Array.isArray(cmd.args) ? cmd.args : []
// for commands that expect HTML elements, convert the args to HtmlElement
if (["append", "prepend", "replaceWith"].includes(cmd.command)) {
args = args.map(fromJSON)
}
console.log(`CMD ${cmd.command}(${JSON.stringify(args)}) on ${sel.length}`)
// perform the command
;(sel[cmd.command] as any)(...args)
updated = true
//console.log("RESULT", JSON.stringify(html, null, 2))
}
}
if (updated) {
widget.set(
"html",
html.map(h => h.toJSON())
)
html_seq = Date.now()
RED.comms.publish("fd-html-preview", { seq: html_seq })
}
})
}
RED.nodes.registerType("flexdash html", flexdashHtml)
RED.plugins.get("node-red-vue").createVueTemplate("flexdash html", __filename)
}