/** * Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ var clone = require("clone"); var redUtil = require("@node-red/util").util; var Log = require("@node-red/util").log; var subflowInstanceRE = /^subflow:(.+)$/; var typeRegistry = require("@node-red/registry"); function diffNodes(oldNode,newNode) { if (oldNode == null) { return true; } var oldKeys = Object.keys(oldNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); var newKeys = Object.keys(newNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" }); if (oldKeys.length != newKeys.length) { return true; } for (var i=0;i 0) { var subflowId = changedSubflowStack.pop(); for (id in newConfig.allNodes) { if (newConfig.allNodes.hasOwnProperty(id)) { node = newConfig.allNodes[id]; if (node.type === 'subflow:'+subflowId) { if (!changed[node.id]) { changed[node.id] = node; if (!changed[changed[node.id].z] && newConfig.allNodes[changed[node.id].z]) { changed[changed[node.id].z] = newConfig.allNodes[changed[node.id].z]; if (newConfig.allNodes[changed[node.id].z].type === "subflow") { // This subflow instance is inside a subflow. Add the // containing subflow to the stack to mark changedSubflowStack.push(changed[node.id].z); delete changed[node.id]; } } } } } } } var diff = { added:Object.keys(added), changed:Object.keys(changed), removed:Object.keys(removed), rewired:Object.keys(wiringChanged), linked:[] } // Traverse the links of all modified nodes to mark the connected nodes var modifiedNodes = diff.added.concat(diff.changed).concat(diff.removed).concat(diff.rewired); var visited = {}; while (modifiedNodes.length > 0) { node = modifiedNodes.pop(); if (!visited[node]) { visited[node] = true; if (linkMap[node]) { if (!changed[node] && !added[node] && !removed[node] && !wiringChanged[node]) { diff.linked.push(node); } modifiedNodes = modifiedNodes.concat(linkMap[node]); } } } // console.log(diff); // for (id in newConfig.allNodes) { // console.log( // (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "), // newConfig.allNodes[id].type.padEnd(10), // id.padEnd(16), // (newConfig.allNodes[id].z||"").padEnd(16), // newConfig.allNodes[id].name||newConfig.allNodes[id].label||"" // ); // } // for (id in removed) { // console.log( // "- "+(diff.linked.indexOf(id)!==-1?"~":" "), // id, // oldConfig.allNodes[id].type, // oldConfig.allNodes[id].name||oldConfig.allNodes[id].label||"" // ); // } return diff; }, /** * Create a new instance of a node * @param {Flow} flow The containing flow * @param {object} config The node configuration object * @return {Node} The instance of the node */ createNode: function(flow,config) { var newNode = null; var type = config.type; try { var nodeTypeConstructor = typeRegistry.get(type); if (nodeTypeConstructor) { var conf = clone(config); delete conf.credentials; for (var p in conf) { if (conf.hasOwnProperty(p)) { mapEnvVarProperties(conf,p); } } try { conf._flow = flow; newNode = new nodeTypeConstructor(conf); } catch (err) { Log.log({ level: Log.ERROR, id:conf.id, type: type, msg: err }); } } else { Log.error(Log._("nodes.flow.unknown-type", {type:type})); } } catch(err) { Log.error(err); } return newNode; } }