mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge 15c2faffdec7b709826fdba8061a346e7f7c3e27 into 2feb290ae3c6cd88c16e4c27c2006a569e0146e2
This commit is contained in:
commit
cc5651c0c9
@ -1715,6 +1715,30 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyzes the array of nodes passed as an argument to find unknown node types.
|
||||||
|
*
|
||||||
|
* @param {Array<object>} nodes An array of nodes to analyse
|
||||||
|
* @returns {Array<string>} An array with unknown types
|
||||||
|
*/
|
||||||
|
function identifyUnknowTypes(nodes) {
|
||||||
|
const unknownTypes = [];
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
// TODO: remove workspace
|
||||||
|
const knowTypes = ["workspace", "tab", "subflow", "group", "junction"];
|
||||||
|
|
||||||
|
if (!knowTypes.includes(node.type) &&
|
||||||
|
node.type.substring(0, 8) != "subflow:" &&
|
||||||
|
!registry.getNodeType(node.type) &&
|
||||||
|
!unknownTypes.includes(node.type)) {
|
||||||
|
unknownTypes.push(node.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknownTypes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the provided nodes.
|
* Replace the provided nodes.
|
||||||
* This must contain complete Subflow defs or complete Flow Tabs.
|
* This must contain complete Subflow defs or complete Flow Tabs.
|
||||||
@ -1824,6 +1848,205 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the Config Node received as parameter.
|
||||||
|
*
|
||||||
|
* @remarks The id is not modified
|
||||||
|
*
|
||||||
|
* @param {object} configNode The Config Node to copy.
|
||||||
|
* @param {object} def The Config Node definition.
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {boolean} [options.markChanged = false] Whether the Config Node
|
||||||
|
* should have changed. Default `false`.
|
||||||
|
* @returns {object} The new Config Node copied.
|
||||||
|
*/
|
||||||
|
function copyConfigNode(configNode, def, options = {}) {
|
||||||
|
const newNode = {
|
||||||
|
_config: {},
|
||||||
|
_configNodeReferences: new Set(),
|
||||||
|
_def: def,
|
||||||
|
id: configNode.id,
|
||||||
|
type: configNode.type,
|
||||||
|
changed: false,
|
||||||
|
icon: configNode.icon,
|
||||||
|
info: configNode.info,
|
||||||
|
label: def.label,
|
||||||
|
users: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (configNode.z) {
|
||||||
|
newNode.z = configNode.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.markChanged) {
|
||||||
|
newNode.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the config node is disabled
|
||||||
|
if (configNode.hasOwnProperty("d")) {
|
||||||
|
newNode.d = configNode.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy editable properties
|
||||||
|
for (const d in def.defaults) {
|
||||||
|
if (def.defaults.hasOwnProperty(d)) {
|
||||||
|
newNode._config[d] = JSON.stringify(configNode[d]);
|
||||||
|
newNode[d] = configNode[d];
|
||||||
|
|
||||||
|
if (def.defaults[d].type) {
|
||||||
|
// Add the config node used by this config node to the list
|
||||||
|
newNode._configNodeReferences.add(configNode[d])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy credentials - ONLY if the node contains it to avoid erase it
|
||||||
|
if (def.hasOwnProperty("credentials") && configNode.hasOwnProperty("credentials")) {
|
||||||
|
newNode.credentials = {};
|
||||||
|
for (const c in def.credentials) {
|
||||||
|
if (def.credentials.hasOwnProperty(c) && configNode.credentials.hasOwnProperty(c)) {
|
||||||
|
newNode.credentials[c] = configNode.credentials[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the Node received as parameter.
|
||||||
|
*
|
||||||
|
* @remarks The id is not modified
|
||||||
|
*
|
||||||
|
* @param {object} node The Node to copy.
|
||||||
|
* @param {object} def The Node definition.
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {boolean} [options.markChanged = false] Whether the Node
|
||||||
|
* should have changed. Default `false`.
|
||||||
|
* @returns {object} The new Config Node copied.
|
||||||
|
*/
|
||||||
|
function copyNode(node, def, options = {}) {
|
||||||
|
const newNode = {
|
||||||
|
_config: {},
|
||||||
|
_def: def,
|
||||||
|
id: node.id,
|
||||||
|
type: node.type,
|
||||||
|
changed: false,
|
||||||
|
dirty: true,
|
||||||
|
info: node.info,
|
||||||
|
// TODO: parseFloat(node.x) || 0
|
||||||
|
x: parseFloat(node.x || 0),
|
||||||
|
y: parseFloat(node.y || 0),
|
||||||
|
z: node.z,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Whether the node shown its label
|
||||||
|
if (node.hasOwnProperty("l")) {
|
||||||
|
newNode.l = node.l;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the node is disabled
|
||||||
|
if (node.hasOwnProperty("d")) {
|
||||||
|
newNode.d = node.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the node is into a group
|
||||||
|
if (node.hasOwnProperty("g")) {
|
||||||
|
newNode.g = node.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.markChanged) {
|
||||||
|
newNode.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type !== "group" && node.type !== "junction") {
|
||||||
|
newNode.wires = node.wires || [];
|
||||||
|
newNode.inputLabels = node.inputLabels;
|
||||||
|
newNode.outputLabels = node.outputLabels;
|
||||||
|
newNode.icon = node.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === "group") {
|
||||||
|
for (const d in newNode._def.defaults) {
|
||||||
|
if (newNode._def.defaults.hasOwnProperty(d) && d !== "inputs" && d !== "outputs") {
|
||||||
|
newNode[d] = node[d];
|
||||||
|
newNode._config[d] = JSON.stringify(node[d]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newNode._config.x = node.x;
|
||||||
|
newNode._config.y = node.y;
|
||||||
|
if (node.hasOwnProperty("w")) { // Weight
|
||||||
|
newNode.w = node.w;
|
||||||
|
}
|
||||||
|
if (node.hasOwnProperty("h")) { // Height
|
||||||
|
newNode.h = node.h;
|
||||||
|
}
|
||||||
|
} else if (node.type === "junction") {
|
||||||
|
newNode._config.x = node.x;
|
||||||
|
newNode._config.y = node.y;
|
||||||
|
newNode.wires = node.wires || [];
|
||||||
|
newNode.inputs = 1;
|
||||||
|
newNode.outputs = 1;
|
||||||
|
newNode.w = 0;
|
||||||
|
newNode.h = 0;
|
||||||
|
} else if (node.type.substring(0, 7) === "subflow") {
|
||||||
|
newNode.name = node.name;
|
||||||
|
newNode.inputs = node.inputs ?? 0;
|
||||||
|
newNode.outputs = node.outputs ?? 0;
|
||||||
|
newNode.env = node.env;
|
||||||
|
} else {
|
||||||
|
newNode._config.x = node.x;
|
||||||
|
newNode._config.y = node.y;
|
||||||
|
|
||||||
|
if (node.hasOwnProperty("inputs") && def.defaults.hasOwnProperty("inputs")) {
|
||||||
|
newNode._config.inputs = JSON.stringify(node.inputs);
|
||||||
|
newNode.inputs = parseInt(node.inputs, 10);
|
||||||
|
} else {
|
||||||
|
newNode.inputs = def.inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.hasOwnProperty("outputs") && def.defaults.hasOwnProperty("outputs")) {
|
||||||
|
newNode._config.outputs = JSON.stringify(node.outputs);
|
||||||
|
newNode.outputs = parseInt(node.outputs, 10);
|
||||||
|
} else {
|
||||||
|
newNode.outputs = def.outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The node declares outputs in its defaults, but has not got a valid value
|
||||||
|
// Defer to the length of the wires array
|
||||||
|
if (node.hasOwnProperty('wires')) {
|
||||||
|
if (isNaN(newNode.outputs)) {
|
||||||
|
newNode.outputs = newNode.wires.length;
|
||||||
|
} else if (newNode.wires.length > newNode.outputs) {
|
||||||
|
// If 'wires' is longer than outputs, clip wires
|
||||||
|
console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs);
|
||||||
|
// TODO: Pas dans l'autre sens ?
|
||||||
|
newNode.wires = newNode.wires.slice(0, newNode.outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy editable properties
|
||||||
|
for (const d in def.defaults) {
|
||||||
|
if (def.defaults.hasOwnProperty(d) && d !== "inputs" && d !== "outputs") {
|
||||||
|
newNode._config[d] = JSON.stringify(node[d]);
|
||||||
|
newNode[d] = node[d];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy credentials - ONLY if the node contains it to avoid erase it
|
||||||
|
if (def.hasOwnProperty("credentials") && node.hasOwnProperty("credentials")) {
|
||||||
|
newNode.credentials = {};
|
||||||
|
for (const c in def.credentials) {
|
||||||
|
if (def.credentials.hasOwnProperty(c) && node.credentials.hasOwnProperty(c)) {
|
||||||
|
newNode.credentials[c] = node.credentials[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options:
|
* Options:
|
||||||
* - generateIds - whether to replace all node ids
|
* - generateIds - whether to replace all node ids
|
||||||
@ -1930,21 +2153,26 @@ RED.nodes = (function() {
|
|||||||
isInitialLoad = true;
|
isInitialLoad = true;
|
||||||
initialLoad = JSON.parse(JSON.stringify(newNodes));
|
initialLoad = JSON.parse(JSON.stringify(newNodes));
|
||||||
}
|
}
|
||||||
var unknownTypes = [];
|
|
||||||
|
const unknownTypes = identifyUnknowTypes(newNodes);
|
||||||
|
if (!isInitialLoad && unknownTypes.length) {
|
||||||
|
const typeList = $("<ul>");
|
||||||
|
|
||||||
|
unknownTypes.forEach(function (type) {
|
||||||
|
$("<li>").text(type).appendTo(typeList);
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = "<p>" + RED._("clipboard.importUnrecognised", { count: unknownTypes.length }) + "</p>";
|
||||||
|
RED.notify(title + typeList[0].outerHTML, {
|
||||||
|
type: "error",
|
||||||
|
fixed: false,
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (i=0;i<newNodes.length;i++) {
|
for (i=0;i<newNodes.length;i++) {
|
||||||
n = newNodes[i];
|
n = newNodes[i];
|
||||||
var id = n.id;
|
var id = n.id;
|
||||||
// TODO: remove workspace in next release+1
|
|
||||||
if (n.type != "workspace" &&
|
|
||||||
n.type != "tab" &&
|
|
||||||
n.type != "subflow" &&
|
|
||||||
n.type != "group" &&
|
|
||||||
n.type != 'junction' &&
|
|
||||||
!registry.getNodeType(n.type) &&
|
|
||||||
n.type.substring(0,8) != "subflow:" &&
|
|
||||||
unknownTypes.indexOf(n.type)==-1) {
|
|
||||||
unknownTypes.push(n.type);
|
|
||||||
}
|
|
||||||
if (n.z) {
|
if (n.z) {
|
||||||
nodeZmap[n.z] = nodeZmap[n.z] || [];
|
nodeZmap[n.z] = nodeZmap[n.z] || [];
|
||||||
nodeZmap[n.z].push(n);
|
nodeZmap[n.z].push(n);
|
||||||
@ -1967,15 +2195,6 @@ RED.nodes = (function() {
|
|||||||
n.z = recoveryWorkspace.id;
|
n.z = recoveryWorkspace.id;
|
||||||
nodeZmap[recoveryWorkspace.id].push(n);
|
nodeZmap[recoveryWorkspace.id].push(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
if (!isInitialLoad && unknownTypes.length > 0) {
|
|
||||||
var typeList = $("<ul>");
|
|
||||||
unknownTypes.forEach(function(t) {
|
|
||||||
$("<li>").text(t).appendTo(typeList);
|
|
||||||
})
|
|
||||||
typeList = typeList[0].outerHTML;
|
|
||||||
RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeWorkspace = RED.workspaces.active();
|
var activeWorkspace = RED.workspaces.active();
|
||||||
@ -2154,43 +2373,7 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
|
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
|
||||||
configNode = {
|
configNode = copyConfigNode(n, def, options);
|
||||||
id:n.id,
|
|
||||||
z:n.z,
|
|
||||||
type:n.type,
|
|
||||||
info: n.info,
|
|
||||||
users:[],
|
|
||||||
_config:{},
|
|
||||||
_configNodeReferences: new Set()
|
|
||||||
};
|
|
||||||
if (!n.z) {
|
|
||||||
delete configNode.z;
|
|
||||||
}
|
|
||||||
if (options.markChanged) {
|
|
||||||
configNode.changed = true
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('d')) {
|
|
||||||
configNode.d = n.d;
|
|
||||||
}
|
|
||||||
for (d in def.defaults) {
|
|
||||||
if (def.defaults.hasOwnProperty(d)) {
|
|
||||||
configNode[d] = n[d];
|
|
||||||
configNode._config[d] = JSON.stringify(n[d]);
|
|
||||||
if (def.defaults[d].type) {
|
|
||||||
configNode._configNodeReferences.add(n[d])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (def.hasOwnProperty('credentials') && n.hasOwnProperty('credentials')) {
|
|
||||||
configNode.credentials = {};
|
|
||||||
for (d in def.credentials) {
|
|
||||||
if (def.credentials.hasOwnProperty(d) && n.credentials.hasOwnProperty(d)) {
|
|
||||||
configNode.credentials[d] = n.credentials[d];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configNode.label = def.label;
|
|
||||||
configNode._def = def;
|
|
||||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||||
configNode.id = getID();
|
configNode.id = getID();
|
||||||
}
|
}
|
||||||
@ -2251,222 +2434,133 @@ RED.nodes = (function() {
|
|||||||
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
|
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
|
||||||
def = registry.getNodeType(n.type);
|
def = registry.getNodeType(n.type);
|
||||||
if (!def || def.category != "config") {
|
if (!def || def.category != "config") {
|
||||||
var node = {
|
|
||||||
x:parseFloat(n.x || 0),
|
|
||||||
y:parseFloat(n.y || 0),
|
|
||||||
z:n.z,
|
|
||||||
type: n.type,
|
|
||||||
info: n.info,
|
|
||||||
changed:false,
|
|
||||||
_config:{}
|
|
||||||
}
|
|
||||||
if (n.type !== "group" && n.type !== 'junction') {
|
|
||||||
node.wires = n.wires||[];
|
|
||||||
node.inputLabels = n.inputLabels;
|
|
||||||
node.outputLabels = n.outputLabels;
|
|
||||||
node.icon = n.icon;
|
|
||||||
}
|
|
||||||
if (n.type === 'junction') {
|
|
||||||
node.wires = n.wires||[];
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('l')) {
|
|
||||||
node.l = n.l;
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('d')) {
|
|
||||||
node.d = n.d;
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('g')) {
|
|
||||||
node.g = n.g;
|
|
||||||
}
|
|
||||||
if (options.markChanged) {
|
|
||||||
node.changed = true
|
|
||||||
}
|
|
||||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||||
if (subflow_denylist[n.z]) {
|
if (subflow_denylist[n.z]) {
|
||||||
continue;
|
continue;
|
||||||
} else if (subflow_map[node.z]) {
|
} else if (subflow_map[n.z]) {
|
||||||
node.z = subflow_map[node.z].id;
|
n.z = subflow_map[n.z].id;
|
||||||
} else {
|
} else {
|
||||||
node.z = workspace_map[node.z];
|
n.z = workspace_map[n.z];
|
||||||
if (!workspaces[node.z]) {
|
if (!workspaces[n.z]) {
|
||||||
if (createMissingWorkspace) {
|
if (createMissingWorkspace) {
|
||||||
if (missingWorkspace === null) {
|
if (missingWorkspace === null) {
|
||||||
missingWorkspace = RED.workspaces.add(null,true);
|
missingWorkspace = RED.workspaces.add(null,true);
|
||||||
new_workspaces.push(missingWorkspace);
|
new_workspaces.push(missingWorkspace);
|
||||||
}
|
}
|
||||||
node.z = missingWorkspace.id;
|
n.z = missingWorkspace.id;
|
||||||
} else {
|
} else {
|
||||||
node.z = activeWorkspace;
|
n.z = activeWorkspace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.id = getID();
|
// TODO: If updated here, conflict with importMap in L2634
|
||||||
|
//n.id = getID();
|
||||||
} else {
|
} else {
|
||||||
node.id = n.id;
|
const keepNodesCurrentZ = reimport && n.z && (RED.workspaces.contains(n.z) || RED.nodes.subflow(n.z))
|
||||||
const keepNodesCurrentZ = reimport && node.z && (RED.workspaces.contains(node.z) || RED.nodes.subflow(node.z))
|
if (!keepNodesCurrentZ && (n.z == null || (!workspace_map[n.z] && !subflow_map[n.z]))) {
|
||||||
if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
|
|
||||||
if (createMissingWorkspace) {
|
if (createMissingWorkspace) {
|
||||||
if (missingWorkspace === null) {
|
if (missingWorkspace === null) {
|
||||||
missingWorkspace = RED.workspaces.add(null,true);
|
missingWorkspace = RED.workspaces.add(null,true);
|
||||||
new_workspaces.push(missingWorkspace);
|
new_workspaces.push(missingWorkspace);
|
||||||
}
|
}
|
||||||
node.z = missingWorkspace.id;
|
n.z = missingWorkspace.id;
|
||||||
} else {
|
} else {
|
||||||
node.z = activeWorkspace;
|
n.z = activeWorkspace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node._def = def;
|
|
||||||
if (node.type === "group") {
|
// Update Subflow instance properties from Subflow definition
|
||||||
node._def = RED.group.def;
|
if (n.type.substring(0, 7) === "subflow") {
|
||||||
for (d in node._def.defaults) {
|
const parentId = n.type.split(":")[1];
|
||||||
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
|
const subflow = subflow_denylist[parentId] || subflow_map[parentId] || getSubflow(parentId);
|
||||||
node[d] = n[d];
|
|
||||||
node._config[d] = JSON.stringify(n[d]);
|
if (subflow) {
|
||||||
}
|
// If the parent Subflow is found, update Subflow instance properties
|
||||||
}
|
|
||||||
node._config.x = node.x;
|
|
||||||
node._config.y = node.y;
|
|
||||||
if (n.hasOwnProperty('w')) {
|
|
||||||
node.w = n.w
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('h')) {
|
|
||||||
node.h = n.h
|
|
||||||
}
|
|
||||||
} else if (n.type.substring(0,7) === "subflow") {
|
|
||||||
var parentId = n.type.split(":")[1];
|
|
||||||
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
|
||||||
if (!subflow){
|
|
||||||
node._def = {
|
|
||||||
color:"#fee",
|
|
||||||
defaults: {},
|
|
||||||
label: "unknown: "+n.type,
|
|
||||||
labelStyle: "red-ui-flow-node-label-italic",
|
|
||||||
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
|
|
||||||
set: registry.getNodeSet("node-red/unknown")
|
|
||||||
}
|
|
||||||
var orig = {};
|
|
||||||
for (var p in n) {
|
|
||||||
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
|
|
||||||
orig[p] = n[p];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node._orig = orig;
|
|
||||||
node.name = n.type;
|
|
||||||
node.type = "unknown";
|
|
||||||
} else {
|
|
||||||
if (subflow_denylist[parentId] || createNewIds || options.importMap[n.id] === "copy") {
|
if (subflow_denylist[parentId] || createNewIds || options.importMap[n.id] === "copy") {
|
||||||
parentId = subflow.id;
|
n.type = "subflow:" + subflow.id;
|
||||||
node.type = "subflow:"+parentId;
|
def = registry.getNodeType(n.type);
|
||||||
node._def = registry.getNodeType(node.type);
|
// TODO: Why delete `node.i`
|
||||||
delete node.i;
|
delete n.i;
|
||||||
}
|
|
||||||
node.name = n.name;
|
|
||||||
node.outputs = subflow.out.length;
|
|
||||||
node.inputs = subflow.in.length;
|
|
||||||
node.env = n.env;
|
|
||||||
}
|
|
||||||
} else if (n.type === 'junction') {
|
|
||||||
node._def = {defaults:{}}
|
|
||||||
node._config.x = node.x
|
|
||||||
node._config.y = node.y
|
|
||||||
node.inputs = 1
|
|
||||||
node.outputs = 1
|
|
||||||
node.w = 0;
|
|
||||||
node.h = 0;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (!node._def) {
|
|
||||||
if (node.x && node.y) {
|
|
||||||
node._def = {
|
|
||||||
color:"#fee",
|
|
||||||
defaults: {},
|
|
||||||
label: "unknown: "+n.type,
|
|
||||||
labelStyle: "red-ui-flow-node-label-italic",
|
|
||||||
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
|
|
||||||
set: registry.getNodeSet("node-red/unknown")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node._def = {
|
|
||||||
category:"config",
|
|
||||||
set: registry.getNodeSet("node-red/unknown")
|
|
||||||
};
|
|
||||||
node.users = [];
|
|
||||||
// This is a config node, so delete the default
|
|
||||||
// non-config node properties
|
|
||||||
delete node.x;
|
|
||||||
delete node.y;
|
|
||||||
delete node.wires;
|
|
||||||
delete node.inputLabels;
|
|
||||||
delete node.outputLabels;
|
|
||||||
if (!n.z) {
|
|
||||||
delete node.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var orig = {};
|
|
||||||
for (var p in n) {
|
|
||||||
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
|
|
||||||
orig[p] = n[p];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node._orig = orig;
|
|
||||||
node.name = n.type;
|
|
||||||
node.type = "unknown";
|
|
||||||
}
|
|
||||||
if (node._def.category != "config") {
|
|
||||||
if (n.hasOwnProperty('inputs') && node._def.defaults.hasOwnProperty("inputs")) {
|
|
||||||
node.inputs = parseInt(n.inputs, 10);
|
|
||||||
node._config.inputs = JSON.stringify(n.inputs);
|
|
||||||
} else {
|
|
||||||
node.inputs = node._def.inputs;
|
|
||||||
}
|
|
||||||
if (n.hasOwnProperty('outputs') && node._def.defaults.hasOwnProperty("outputs")) {
|
|
||||||
node.outputs = parseInt(n.outputs, 10);
|
|
||||||
node._config.outputs = JSON.stringify(n.outputs);
|
|
||||||
} else {
|
|
||||||
node.outputs = node._def.outputs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The node declares outputs in its defaults, but has not got a valid value
|
// Get inputs/outputs count from parent
|
||||||
// Defer to the length of the wires array
|
n.inputs = subflow.in.length;
|
||||||
if (node.hasOwnProperty('wires')) {
|
n.outputs = subflow.out.length;
|
||||||
if (isNaN(node.outputs)) {
|
} else {
|
||||||
node.outputs = node.wires.length;
|
// If the parent Subflow is not found, this Subflow instance will be marked as unknown
|
||||||
} else if (node.wires.length > node.outputs) {
|
|
||||||
// If 'wires' is longer than outputs, clip wires
|
|
||||||
console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs);
|
|
||||||
node.wires = node.wires.slice(0, node.outputs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (d in node._def.defaults) {
|
|
||||||
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
|
|
||||||
node[d] = n[d];
|
|
||||||
node._config[d] = JSON.stringify(n[d]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node._config.x = node.x;
|
|
||||||
node._config.y = node.y;
|
|
||||||
if (node._def.hasOwnProperty('credentials') && n.hasOwnProperty('credentials')) {
|
|
||||||
node.credentials = {};
|
|
||||||
for (d in node._def.credentials) {
|
|
||||||
if (node._def.credentials.hasOwnProperty(d) && n.credentials.hasOwnProperty(d)) {
|
|
||||||
node.credentials[d] = n.credentials[d];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node_map[n.id] = node;
|
|
||||||
// If an 'unknown' config node, it will not have been caught by the
|
// Try to fix the node definition
|
||||||
// proper config node handling, so needs adding to new_nodes here
|
let isUnknownNode = false;
|
||||||
if (node.type === 'junction') {
|
if (!def) {
|
||||||
new_junctions.push(node)
|
if (n.type === "group") {
|
||||||
} else if (node.type === "unknown" || node._def.category !== "config") {
|
// Group Node Definition
|
||||||
new_nodes.push(node);
|
def = RED.group.def;
|
||||||
} else if (node.type === "group") {
|
} else if (n.type === "junction") {
|
||||||
new_groups.push(node);
|
// Junction Node Definition
|
||||||
new_group_set.add(node.id);
|
def = { defaults: {} };
|
||||||
|
} else if (!n.hasOwnProperty("x") && !n.hasOwnProperty("y")) {
|
||||||
|
// Assumes its an unknown Config Node
|
||||||
|
isUnknownNode = true;
|
||||||
|
def = {
|
||||||
|
category: "config",
|
||||||
|
defaults: {},
|
||||||
|
set: registry.getNodeSet("node-red/unknown")
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Unknown Node
|
||||||
|
isUnknownNode = true;
|
||||||
|
def = {
|
||||||
|
color: "#fee",
|
||||||
|
defaults: {},
|
||||||
|
label: "unknown: " + n.type,
|
||||||
|
labelStyle: "red-ui-flow-node-label-italic",
|
||||||
|
// TODO: ?? instead of ||
|
||||||
|
outputs: n.outputs || (n.wires && n.wires.length) || 0,
|
||||||
|
set: registry.getNodeSet("node-red/unknown")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now all properties have been fixed, copy the node
|
||||||
|
// NOTE: If def is unknown node, editable properties are not copied
|
||||||
|
// TODO: Group Node has config as category - why?
|
||||||
|
const isConfigNode = def?.category === "config" && n.type !== "group";
|
||||||
|
if (isConfigNode) {
|
||||||
|
node_map[n.id] = copyConfigNode(n, def, options);
|
||||||
|
} else {
|
||||||
|
// Node, Group, Junction or Subflow
|
||||||
|
node_map[n.id] = copyNode(n, def, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unknown Node - Copy editable properties so that the node is always exportable
|
||||||
|
if (isUnknownNode) {
|
||||||
|
const propertiesNotCopyable = ["x", "y", "z", "id", "wires"];
|
||||||
|
node_map[n.id]._orig = Object.entries(n).reduce(function (orig, [prop, value]) {
|
||||||
|
if (n.hasOwnProperty(prop) && !propertiesNotCopyable.includes(prop)) {
|
||||||
|
orig[prop] = value;
|
||||||
|
}
|
||||||
|
return orig;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
node_map[n.id].name = n.type;
|
||||||
|
node_map[n.id].type = "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the node has been copied, change the `id` if it's a copy
|
||||||
|
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||||
|
node_map[n.id].id = getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n.type === 'junction') {
|
||||||
|
new_junctions.push(node_map[n.id])
|
||||||
|
} else if (n.type === "group") {
|
||||||
|
new_groups.push(node_map[n.id]);
|
||||||
|
new_group_set.add(node_map[n.id].id);
|
||||||
|
} else {
|
||||||
|
new_nodes.push(node_map[n.id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user