function showEditConfigNodeDialog(name,type,id,prefix,editContext) {
        if (buildingEditDialog) { return }
        buildingEditDialog = true;
        var adding = (id == "_ADD_");
        var node_def = RED.nodes.getType(type);
        var editing_config_node = RED.nodes.node(id);
        var activeEditPanes = [];

        if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return }

        var configNodeScope = ""; // default to global
        var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
        if (activeSubflow) {
            configNodeScope = activeSubflow.id;
        }
        if (editing_config_node == null) {
            editing_config_node = {
                id: RED.nodes.id(),
                _def: node_def,
                type: type,
                z: configNodeScope,
                users: []
            }
            for (var d in node_def.defaults) {
                if (node_def.defaults[d].value) {
                    editing_config_node[d] = JSON.parse(JSON.stringify(node_def.defaults[d].value));
                }
            }
            editing_config_node["_"] = node_def._;
        }
        editStack.push(editing_config_node);

        RED.view.state(RED.state.EDITING);
        var trayOptions = {
            title: getEditStackTitle(), //(adding?RED._("editor.addNewConfig", {type:type}):RED._("editor.editConfig", {type:type})),
            resize: function(dimensions) {
                $(".red-ui-tray-content").height(dimensions.height - 50);
                var form = $("#node-config-dialog-edit-form");
                var size = {width:form.width(),height:form.height()};
                activeEditPanes.forEach(function(pane) {
                    if (pane.resize) {
                        pane.resize.call(pane, size);
                    }
                })
            },
            open: function(tray, done) {
                var trayHeader = tray.find(".red-ui-tray-header");
                var trayBody = tray.find('.red-ui-tray-body');
                var trayFooter = tray.find(".red-ui-tray-footer");

                var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)

                var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
                    evt.preventDefault();
                    evt.stopPropagation();
                    RED.sidebar.help.show(editing_config_node.type);
                }).appendTo(trayFooterLeft);
                RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));

                $('<input id="node-config-input-node-disabled" type="checkbox">').prop("checked",!!editing_config_node.d).appendTo(trayFooterLeft).toggleButton({
                    enabledIcon: "fa-circle-thin",
                    disabledIcon: "fa-ban",
                    invertState: true
                })

                if (node_def.hasUsers !== false) {
                    // $('<span><i class="fa fa-info-circle"></i> <span id="red-ui-editor-config-user-count"></span></span>').css("margin-left", "10px").appendTo(trayFooterLeft);
                    $('<button type="button" class="red-ui-button"><i class="fa fa-user"></i><span id="red-ui-editor-config-user-count"></span></button>').on('click', function() {
                        RED.sidebar.info.outliner.search('uses:'+editing_config_node.id)
                        RED.sidebar.info.show()
                    }).appendTo(trayFooterLeft);
                }
                trayFooter.append('<span class="red-ui-tray-footer-right"><span id="red-ui-editor-config-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="red-ui-editor-config-scope"></select></span>');


                var nodeEditPanes = [ 'editor-tab-properties' ];
                if (!editing_config_node._def.defaults || !editing_config_node._def.defaults.hasOwnProperty('info'))  {
                    nodeEditPanes.push('editor-tab-description');
                }

                prepareEditDialog(trayBody, nodeEditPanes, editing_config_node, node_def, "node-config-input", null, function(_activeEditPanes) {
                    activeEditPanes = _activeEditPanes;
                    if (editing_config_node._def.exclusive) {
                        $("#red-ui-editor-config-scope").hide();
                    } else {
                        $("#red-ui-editor-config-scope").show();
                    }
                    $("#red-ui-editor-config-scope-warning").hide();

                    var nodeUserFlows = {};
                    editing_config_node.users.forEach(function(n) {
                        nodeUserFlows[n.z] = true;
                    });
                    var flowCount = Object.keys(nodeUserFlows).length;
                    var tabSelect = $("#red-ui-editor-config-scope").empty();
                    tabSelect.off("change");
                    tabSelect.append('<option value=""'+(!editing_config_node.z?" selected":"")+' data-i18n="sidebar.config.global"></option>');
                    tabSelect.append('<option disabled data-i18n="sidebar.config.flows"></option>');
                    RED.nodes.eachWorkspace(function(ws) {
                        var workspaceLabel = ws.label;
                        if (nodeUserFlows[ws.id]) {
                            workspaceLabel = "* "+workspaceLabel;
                        }
                        $('<option value="'+ws.id+'"'+(ws.id==editing_config_node.z?" selected":"")+'></option>').text(workspaceLabel).appendTo(tabSelect);
                    });
                    tabSelect.append('<option disabled data-i18n="sidebar.config.subflows"></option>');
                    RED.nodes.eachSubflow(function(ws) {
                        var workspaceLabel = ws.name;
                        if (nodeUserFlows[ws.id]) {
                            workspaceLabel = "* "+workspaceLabel;
                        }
                        $('<option value="'+ws.id+'"'+(ws.id==editing_config_node.z?" selected":"")+'></option>').text(workspaceLabel).appendTo(tabSelect);
                    });
                    if (flowCount > 0) {
                        tabSelect.on('change',function() {
                            var newScope = $(this).val();
                            if (newScope === '') {
                                // global scope - everyone can use it
                                $("#red-ui-editor-config-scope-warning").hide();
                            } else if (!nodeUserFlows[newScope] || flowCount > 1) {
                                // a user will loose access to it
                                $("#red-ui-editor-config-scope-warning").show();
                            } else {
                                $("#red-ui-editor-config-scope-warning").hide();
                            }
                        });
                    }
                    if (node_def.hasUsers !== false) {
                        $("#red-ui-editor-config-user-count").text(editing_config_node.users.length).parent().show();
                        RED.popover.tooltip($("#red-ui-editor-config-user-count").parent(), function() { return RED._('editor.nodesUse',{count:editing_config_node.users.length})});
                    }
                    trayBody.i18n();
                    trayFooter.i18n();
                    buildingEditDialog = false;
                    done();
                });
            },
            close: function() {
                RED.workspaces.refresh();

                activeEditPanes.forEach(function(pane) {
                    if (pane.close) {
                        pane.close.call(pane);
                    }
                })

                editStack.pop();
            },
            show: function() {
                if (editing_config_node) {
                    RED.sidebar.info.refresh(editing_config_node);
                    RED.sidebar.help.show(type, false);
                }
            }
        }
        trayOptions.buttons = [
            {
                id: "node-config-dialog-cancel",
                text: RED._("common.label.cancel"),
                click: function() {
                    var configType = type;
                    var configId = editing_config_node.id;
                    var configAdding = adding;
                    var configTypeDef = RED.nodes.getType(configType);
                    if (configTypeDef.oneditcancel) {
                        // TODO: what to pass as this to call
                        if (configTypeDef.oneditcancel) {
                            var cn = RED.nodes.node(configId);
                            if (cn) {
                                try {
                                    configTypeDef.oneditcancel.call(cn,false);
                                } catch(err) {
                                    console.log("oneditcancel",cn.id,cn.type,err.toString());
                                }
                            } else {
                                try {
                                    configTypeDef.oneditcancel.call({id:configId},true);
                                } catch(err) {
                                    console.log("oneditcancel",configId,configType,err.toString());
                                }
                            }
                        }
                    }
                    RED.tray.close();
                }
            },
            {
                id: "node-config-dialog-ok",
                text: adding ? RED._("editor.configAdd") : RED._("editor.configUpdate"),
                class: "primary",
                click: function() {
                    // TODO: Already defined
                    const configProperty = name;
                    const configType = type;
                    const configTypeDef = RED.nodes.getType(configType);

                    const wasChanged = editing_config_node.changed;
                    const editState = {
                        changes: {},
                        changed: false,
                        outputMap: null
                    };
                    
                    // Call `oneditsave` and search for changes
                    handleEditSave(editing_config_node, editState);

                    // Search for changes in the edit box (panes)
                    activeEditPanes.forEach(function (pane) {
                        if (pane.apply) {
                            pane.apply.call(pane, editState);
                        }
                    });

                    // TODO: Why?
                    editing_config_node.label = configTypeDef.label

                    // Check if disabled has changed
                    if ($("#node-config-input-node-disabled").prop('checked')) {
                        if (editing_config_node.d !== true) {
                            editState.changes.d = editing_config_node.d;
                            editState.changed = true;
                            editing_config_node.d = true;
                        }
                    } else {
                        if (editing_config_node.d === true) {
                            editState.changes.d = editing_config_node.d;
                            editState.changed = true;
                            delete editing_config_node.d;
                        }
                    }

                    // NOTE: must be undefined if no scope used
                    const scope = $("#red-ui-editor-config-scope").val() || undefined;

                    // Check if the scope has changed
                    if (editing_config_node.z !== scope) {
                        editState.changes.z = editing_config_node.z;
                        editState.changed = true;
                        editing_config_node.z = scope;
                    }

                    // Search for nodes that use this config node that are no longer
                    // in scope, so must be removed
                    const historyEvents = [];
                    if (scope) {
                        const newUsers = editing_config_node.users.filter(function (node) {
                            let keepNode = false;
                            let nodeModified = null;

                            for (const d in node._def.defaults) {
                                if (node._def.defaults.hasOwnProperty(d)) {
                                    if (node._def.defaults[d].type === editing_config_node.type) {
                                        if (node[d] === editing_config_node.id) {
                                            if (node.z === editing_config_node.z) {
                                                // The node is kept only if at least one property uses
                                                // this config node in the correct scope.
                                                keepNode = true;
                                            } else {
                                                if (!nodeModified) {
                                                    nodeModified = {
                                                        t: "edit",
                                                        node: node,
                                                        changes: { [d]: node[d] },
                                                        changed: node.changed,
                                                        dirty: node.dirty
                                                    };
                                                } else {
                                                    nodeModified.changes[d] = node[d];
                                                }

                                                // Remove the reference to the config node
                                                node[d] = "";
                                            }
                                        }
                                    }
                                }
                            }

                            // Add the node modified to the history
                            if (nodeModified) {
                                historyEvents.push(nodeModified);
                            }

                            // Mark as changed and revalidate this node
                            if (!keepNode) {
                                node.changed = true;
                                node.dirty = true;
                                validateNode(node);
                                RED.events.emit("nodes:change", node);
                            }

                            return keepNode;
                        });

                        // Check if users are changed
                        if (editing_config_node.users.length !== newUsers.length) {
                            editState.changes.users = editing_config_node.users;
                            editState.changed = true;
                            editing_config_node.users = newUsers;
                        }
                    }

                    if (editState.changed) {
                        // Set the congig node as changed
                        editing_config_node.changed = true;
                    }

                    // Now, validate the config node
                    validateNode(editing_config_node);

                    // And validate nodes using this config node too
                    const validatedNodes = new Set();
                    const userStack = editing_config_node.users.slice();

                    validatedNodes.add(editing_config_node.id);
                    while (userStack.length) {
                        const node = userStack.pop();
                        if (!validatedNodes.has(node.id)) {
                            validatedNodes.add(node.id);
                            if (node.users) {
                                userStack.push(...node.users);
                            }
                            validateNode(node);
                        }
                    }

                    let historyEvent = {
                        t: "edit",
                        node: editing_config_node,
                        changes: editState.changes,
                        changed: wasChanged,
                        dirty: RED.nodes.dirty()
                    };

                    if (historyEvents.length) {
                        // Need a multi events
                        historyEvent = {
                            t: "multi",
                            events: [historyEvent].concat(historyEvents),
                            dirty: historyEvent.dirty
                        };
                    }

                    if (!adding) {
                        // This event is triggered when the edit box is saved,
                        // regardless of whether there are any modifications.
                        RED.events.emit("editor:save", editing_config_node);
                    }

                    if (editState.changed) {
                        if (adding) {
                            RED.history.push({ t: "add", nodes: [editing_config_node.id], dirty: RED.nodes.dirty() });
                            // Add the new config node and trigger the `nodes:add` event
                            RED.nodes.add(editing_config_node);
                        } else {
                            RED.history.push(historyEvent);
                            RED.events.emit("nodes:change", editing_config_node);
                        }

                        RED.nodes.dirty(true);
                        RED.view.redraw(true);
                    }

                    RED.tray.close(function() {
                        var filter = null;
                        // when editing a config via subflow edit panel, the `configProperty` will not
                        // necessarily be a property of the editContext._def.defaults object
                        // Also, when editing via dashboard sidebar, editContext can be null
                        // so we need to guard both scenarios
                        if (editContext?._def) {
                            const isSubflow = (editContext._def.type === 'subflow' || /subflow:.*/.test(editContext._def.type))
                            if (editContext && !isSubflow && typeof editContext._def.defaults?.[configProperty]?.filter === 'function') {
                                filter = function(n) {
                                    return editContext._def.defaults[configProperty].filter.call(editContext,n);
                                }
                            }
                        }
                        updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter);
                    });
                }
            }
        ];

        if (!adding) {
            trayOptions.buttons.unshift({
                class: 'leftButton',
                text: RED._("editor.configDelete"), //'<i class="fa fa-trash"></i>',
                click: function() {
                    var configProperty = name;
                    var configId = editing_config_node.id;
                    var configType = type;
                    var configTypeDef = RED.nodes.getType(configType);

                    try {

                        if (configTypeDef.ondelete) {
                            // Deprecated: never documented but used by some early nodes
                            console.log("Deprecated API warning: config node type ",configType," has an ondelete function - should be oneditdelete");
                            configTypeDef.ondelete.call(editing_config_node);
                        }
                        if (configTypeDef.oneditdelete) {
                            configTypeDef.oneditdelete.call(editing_config_node);
                        }
                    } catch(err) {
                        console.log("oneditdelete",editing_config_node.id,editing_config_node.type,err.toString());
                    }

                    var historyEvent = {
                        t:'delete',
                        nodes:[editing_config_node],
                        changes: {},
                        dirty: RED.nodes.dirty()
                    }
                    for (var i=0;i<editing_config_node.users.length;i++) {
                        var user = editing_config_node.users[i];
                        historyEvent.changes[user.id] = {
                            changed: user.changed,
                            valid: user.valid
                        };
                        for (var d in user._def.defaults) {
                            if (user._def.defaults.hasOwnProperty(d) && user[d] == configId) {
                                historyEvent.changes[user.id][d] = configId
                                user[d] = "";
                                user.changed = true;
                                user.dirty = true;
                            }
                        }
                        validateNode(user);
                    }
                    RED.nodes.remove(configId);
                    RED.nodes.dirty(true);
                    RED.view.redraw(true);
                    RED.history.push(historyEvent);
                    RED.tray.close(function() {
                        var filter = null;
                        if (editContext && typeof editContext._def.defaults[configProperty]?.filter === 'function') {
                            filter = function(n) {
                                return editContext._def.defaults[configProperty].filter.call(editContext,n);
                            }
                        }
                        updateConfigNodeSelect(configProperty,configType,"",prefix,filter);
                    });
                }
            });
        }

        RED.tray.show(trayOptions);
    }
name - name of the property that holds this config node
type - type of config node
id - id of config node to edit. _ADD_ for a new one
prefix - the input prefix of the parent property
editContext - the node that was being edited that triggered editing this node