mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge pull request #4320 from node-red/dev
Sync `dev` to `master` for 3.1.0 release
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
* @namespace RED.nodes
|
||||
*/
|
||||
RED.nodes = (function() {
|
||||
|
||||
var PORT_TYPE_INPUT = 1;
|
||||
var PORT_TYPE_OUTPUT = 0;
|
||||
|
||||
@@ -47,6 +46,9 @@ RED.nodes = (function() {
|
||||
|
||||
function setDirty(d) {
|
||||
dirty = d;
|
||||
if (!d) {
|
||||
allNodes.clearState()
|
||||
}
|
||||
RED.events.emit("workspace:dirty",{dirty:dirty});
|
||||
}
|
||||
|
||||
@@ -63,12 +65,12 @@ RED.nodes = (function() {
|
||||
defaults: {
|
||||
label: {value:""},
|
||||
disabled: {value: false},
|
||||
locked: {value: false},
|
||||
info: {value: ""},
|
||||
env: {value: []}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var exports = {
|
||||
setModulePendingUpdated: function(module,version) {
|
||||
moduleList[module].pending_version = version;
|
||||
@@ -238,22 +240,72 @@ RED.nodes = (function() {
|
||||
|
||||
// allNodes holds information about the Flow nodes.
|
||||
var allNodes = (function() {
|
||||
// Map node.id -> node
|
||||
var nodes = {};
|
||||
// Map tab.id -> Array of nodes on that tab
|
||||
var tabMap = {};
|
||||
// Map tab.id -> Set of dirty object ids on that tab
|
||||
var tabDirtyMap = {};
|
||||
// Map tab.id -> Set of object ids of things deleted from the tab that weren't otherwise dirty
|
||||
var tabDeletedNodesMap = {};
|
||||
// Set of object ids of things added to a tab after initial import
|
||||
var addedDirtyObjects = new Set()
|
||||
|
||||
function changeCollectionDepth(tabNodes, toMove, direction, singleStep) {
|
||||
const result = []
|
||||
const moved = new Set();
|
||||
const startIndex = direction ? tabNodes.length - 1 : 0
|
||||
const endIndex = direction ? -1 : tabNodes.length
|
||||
const step = direction ? -1 : 1
|
||||
let target = startIndex // Only used for all-the-way moves
|
||||
for (let i = startIndex; i != endIndex; i += step) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
const n = tabNodes[i]
|
||||
if (toMove.has(n)) {
|
||||
if (singleStep) {
|
||||
if (i !== startIndex && !moved.has(tabNodes[i - step])) {
|
||||
tabNodes.splice(i, 1)
|
||||
tabNodes.splice(i - step, 0, n)
|
||||
n._reordered = true
|
||||
result.push(n)
|
||||
}
|
||||
} else {
|
||||
if (i !== target) {
|
||||
tabNodes.splice(i, 1)
|
||||
tabNodes.splice(target, 0, n)
|
||||
n._reordered = true
|
||||
result.push(n)
|
||||
}
|
||||
target += step
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var api = {
|
||||
addTab: function(id) {
|
||||
tabMap[id] = [];
|
||||
tabDirtyMap[id] = new Set();
|
||||
tabDeletedNodesMap[id] = new Set();
|
||||
},
|
||||
hasTab: function(z) {
|
||||
return tabMap.hasOwnProperty(z)
|
||||
},
|
||||
removeTab: function(id) {
|
||||
delete tabMap[id];
|
||||
delete tabDirtyMap[id];
|
||||
delete tabDeletedNodesMap[id];
|
||||
},
|
||||
addNode: function(n) {
|
||||
nodes[n.id] = n;
|
||||
if (tabMap.hasOwnProperty(n.z)) {
|
||||
tabMap[n.z].push(n);
|
||||
api.addObjectToWorkspace(n.z, n.id, n.changed || n.moved)
|
||||
} else {
|
||||
console.warn("Node added to unknown tab/subflow:",n);
|
||||
tabMap["_"] = tabMap["_"] || [];
|
||||
@@ -267,8 +319,37 @@ RED.nodes = (function() {
|
||||
if (i > -1) {
|
||||
tabMap[n.z].splice(i,1);
|
||||
}
|
||||
api.removeObjectFromWorkspace(n.z, n.id)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add an object to our dirty/clean tracking state
|
||||
* @param {String} z
|
||||
* @param {String} id
|
||||
* @param {Boolean} isDirty
|
||||
*/
|
||||
addObjectToWorkspace: function (z, id, isDirty) {
|
||||
if (isDirty) {
|
||||
addedDirtyObjects.add(id)
|
||||
}
|
||||
if (tabDeletedNodesMap[z].has(id)) {
|
||||
tabDeletedNodesMap[z].delete(id)
|
||||
}
|
||||
api.markNodeDirty(z, id, isDirty)
|
||||
},
|
||||
/**
|
||||
* Remove an object from our dirty/clean tracking state
|
||||
* @param {String} z
|
||||
* @param {String} id
|
||||
*/
|
||||
removeObjectFromWorkspace: function (z, id) {
|
||||
if (!addedDirtyObjects.has(id)) {
|
||||
tabDeletedNodesMap[z].add(id)
|
||||
} else {
|
||||
addedDirtyObjects.delete(id)
|
||||
}
|
||||
api.markNodeDirty(z, id, false)
|
||||
},
|
||||
hasNode: function(id) {
|
||||
return nodes.hasOwnProperty(id);
|
||||
},
|
||||
@@ -280,152 +361,54 @@ RED.nodes = (function() {
|
||||
n.z = newZ;
|
||||
api.addNode(n)
|
||||
},
|
||||
moveNodesForwards: function(nodes) {
|
||||
var result = [];
|
||||
/**
|
||||
* @param {array} nodes
|
||||
* @param {boolean} direction true:forwards false:back
|
||||
* @param {boolean} singleStep true:single-step false:all-the-way
|
||||
*/
|
||||
changeDepth: function(nodes, direction, singleStep) {
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var moved = new Set();
|
||||
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i < tabNodes.length-1 && !moved.has(tabNodes[i+1])) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position higher
|
||||
tabNodes.splice(i+1,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
let result = []
|
||||
const tabNodes = tabMap[nodes[0].z];
|
||||
const toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
if (toMove.size > 0) {
|
||||
result = result.concat(changeCollectionDepth(tabNodes, toMove, direction, singleStep))
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
|
||||
const groupNodes = groupsByZ[nodes[0].z] || []
|
||||
const groupsToMove = new Set(nodes.filter(function(n) { return n.type === 'group'}))
|
||||
if (groupsToMove.size > 0) {
|
||||
const groupResult = changeCollectionDepth(groupNodes, groupsToMove, direction, singleStep)
|
||||
if (groupResult.length > 0) {
|
||||
result = result.concat(groupResult)
|
||||
RED.events.emit('groups:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: groupResult
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
RED.view.redraw(true)
|
||||
return result
|
||||
},
|
||||
moveNodesForwards: function(nodes) {
|
||||
return api.changeDepth(nodes, true, true)
|
||||
},
|
||||
moveNodesBackwards: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var moved = new Set();
|
||||
for (var i = 0; i < tabNodes.length; i++) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i > 0 && !moved.has(tabNodes[i-1])) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position lower
|
||||
tabNodes.splice(i-1,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, false, true)
|
||||
},
|
||||
moveNodesToFront: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var target = tabNodes.length-1;
|
||||
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i < target) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
tabNodes.splice(target,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
target--;
|
||||
toMove.delete(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, true, false)
|
||||
},
|
||||
moveNodesToBack: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var target = 0;
|
||||
for (var i = 0; i < tabNodes.length; i++) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i > target) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position lower
|
||||
tabNodes.splice(target,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
target++;
|
||||
toMove.delete(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, false, false)
|
||||
},
|
||||
getNodes: function(z) {
|
||||
return tabMap[z];
|
||||
@@ -433,6 +416,33 @@ RED.nodes = (function() {
|
||||
clear: function() {
|
||||
nodes = {};
|
||||
tabMap = {};
|
||||
tabDirtyMap = {};
|
||||
tabDeletedNodesMap = {};
|
||||
addedDirtyObjects = new Set();
|
||||
},
|
||||
/**
|
||||
* Clear all internal state on what is dirty.
|
||||
*/
|
||||
clearState: function () {
|
||||
// Called when a deploy happens, we can forget about added/remove
|
||||
// items as they have now been deployed.
|
||||
addedDirtyObjects = new Set()
|
||||
const flowsToCheck = new Set()
|
||||
for (const [z, set] of Object.entries(tabDeletedNodesMap)) {
|
||||
if (set.size > 0) {
|
||||
set.clear()
|
||||
flowsToCheck.add(z)
|
||||
}
|
||||
}
|
||||
for (const [z, set] of Object.entries(tabDirtyMap)) {
|
||||
if (set.size > 0) {
|
||||
set.clear()
|
||||
flowsToCheck.add(z)
|
||||
}
|
||||
}
|
||||
for (const z of flowsToCheck) {
|
||||
api.checkTabState(z)
|
||||
}
|
||||
},
|
||||
eachNode: function(cb) {
|
||||
var nodeList,i,j;
|
||||
@@ -498,7 +508,7 @@ RED.nodes = (function() {
|
||||
return result;
|
||||
},
|
||||
getNodeOrder: function(z) {
|
||||
return tabMap[z].map(function(n) { return n.id })
|
||||
return (groupsByZ[z] || []).concat(tabMap[z]).map(n => n.id)
|
||||
},
|
||||
setNodeOrder: function(z, order) {
|
||||
var orderMap = {};
|
||||
@@ -510,6 +520,41 @@ RED.nodes = (function() {
|
||||
B._reordered = true;
|
||||
return orderMap[A.id] - orderMap[B.id];
|
||||
})
|
||||
if (groupsByZ[z]) {
|
||||
groupsByZ[z].sort(function(A,B) {
|
||||
return orderMap[A.id] - orderMap[B.id];
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update our records if an object is dirty or not
|
||||
* @param {String} z tab id
|
||||
* @param {String} id object id
|
||||
* @param {Boolean} dirty whether the object is dirty or not
|
||||
*/
|
||||
markNodeDirty: function(z, id, dirty) {
|
||||
if (tabDirtyMap[z]) {
|
||||
if (dirty) {
|
||||
tabDirtyMap[z].add(id)
|
||||
} else {
|
||||
tabDirtyMap[z].delete(id)
|
||||
}
|
||||
api.checkTabState(z)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Check if a tab should update its contentsChange flag
|
||||
* @param {String} z tab id
|
||||
*/
|
||||
checkTabState: function (z) {
|
||||
const ws = workspaces[z]
|
||||
if (ws) {
|
||||
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
|
||||
if (Boolean(ws.contentsChanged) !== contentsChanged) {
|
||||
ws.contentsChanged = contentsChanged
|
||||
RED.events.emit("flows:change", ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return api;
|
||||
@@ -575,15 +620,53 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
const nodeProxyHandler = {
|
||||
get(node, prop) {
|
||||
if (prop === '__isProxy__') {
|
||||
return true
|
||||
} else if (prop == '__node__') {
|
||||
return node
|
||||
}
|
||||
return node[prop]
|
||||
},
|
||||
set(node, prop, value) {
|
||||
if (node.z && (RED.nodes.workspace(node.z)?.locked || RED.nodes.subflow(node.z)?.locked)) {
|
||||
if (
|
||||
node._def.defaults[prop] ||
|
||||
prop === 'z' ||
|
||||
prop === 'l' ||
|
||||
prop === 'd' ||
|
||||
(prop === 'changed' && (!!node.changed) !== (!!value)) || // jshint ignore:line
|
||||
((prop === 'x' || prop === 'y') && !node.resize && node.type !== 'group')
|
||||
) {
|
||||
throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`)
|
||||
}
|
||||
}
|
||||
if (node.z && (prop === 'changed' || prop === 'moved')) {
|
||||
setTimeout(() => {
|
||||
allNodes.markNodeDirty(node.z, node.id, node.changed || node.moved)
|
||||
}, 0)
|
||||
}
|
||||
node[prop] = value;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function addNode(n) {
|
||||
let newNode
|
||||
if (!n.__isProxy__) {
|
||||
newNode = new Proxy(n, nodeProxyHandler)
|
||||
} else {
|
||||
newNode = n
|
||||
}
|
||||
|
||||
if (n.type.indexOf("subflow") !== 0) {
|
||||
n["_"] = n._def._;
|
||||
} else {
|
||||
var subflowId = n.type.substring(8);
|
||||
var sf = RED.nodes.subflow(subflowId);
|
||||
if (sf) {
|
||||
sf.instances.push(sf);
|
||||
sf.instances.push(newNode);
|
||||
}
|
||||
n["_"] = RED._;
|
||||
}
|
||||
@@ -600,12 +683,13 @@ RED.nodes = (function() {
|
||||
});
|
||||
n.i = nextId+1;
|
||||
}
|
||||
allNodes.addNode(n);
|
||||
allNodes.addNode(newNode);
|
||||
if (!nodeLinks[n.id]) {
|
||||
nodeLinks[n.id] = {in:[],out:[]};
|
||||
}
|
||||
}
|
||||
RED.events.emit('nodes:add',n);
|
||||
RED.events.emit('nodes:add',newNode);
|
||||
return newNode
|
||||
}
|
||||
function addLink(l) {
|
||||
if (nodeLinks[l.source.id]) {
|
||||
@@ -632,10 +716,16 @@ RED.nodes = (function() {
|
||||
}
|
||||
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
|
||||
linkTabMap[l.source.z].push(l);
|
||||
allNodes.addObjectToWorkspace(l.source.z, getLinkId(l), true)
|
||||
}
|
||||
RED.events.emit("links:add",l);
|
||||
}
|
||||
|
||||
function getLinkId(link) {
|
||||
return link.source.id + ':' + link.sourcePort + ':' + link.target.id
|
||||
}
|
||||
|
||||
|
||||
function getNode(id) {
|
||||
if (id in configNodes) {
|
||||
return configNodes[id];
|
||||
@@ -830,6 +920,7 @@ RED.nodes = (function() {
|
||||
if (index !== -1) {
|
||||
linkTabMap[l.source.z].splice(index,1)
|
||||
}
|
||||
allNodes.removeObjectFromWorkspace(l.source.z, getLinkId(l))
|
||||
}
|
||||
}
|
||||
RED.events.emit("links:remove",l);
|
||||
@@ -999,6 +1090,11 @@ RED.nodes = (function() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDownstreamNodes(node) {
|
||||
const downstreamLinks = nodeLinks[node.id].out
|
||||
const downstreamNodes = new Set(downstreamLinks.map(l => l.target))
|
||||
return Array.from(downstreamNodes)
|
||||
}
|
||||
function getAllDownstreamNodes(node) {
|
||||
return getAllFlowNodes(node,'down').filter(function(n) { return n !== node });
|
||||
}
|
||||
@@ -1046,6 +1142,9 @@ RED.nodes = (function() {
|
||||
node.type = n.type;
|
||||
for (var d in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d)) {
|
||||
if (d === 'locked' && !n.locked) {
|
||||
continue
|
||||
}
|
||||
node[d] = n[d];
|
||||
}
|
||||
}
|
||||
@@ -1369,7 +1468,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (node.type !== "subflow") {
|
||||
var convertedNode = RED.nodes.convertNode(node);
|
||||
var convertedNode = RED.nodes.convertNode(node, { credentials: false });
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults[d].type) {
|
||||
var nodeList = node[d];
|
||||
@@ -1402,7 +1501,7 @@ RED.nodes = (function() {
|
||||
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
|
||||
}
|
||||
} else {
|
||||
var convertedSubflow = convertSubflow(node);
|
||||
var convertedSubflow = convertSubflow(node, { credentials: false });
|
||||
nns.push(convertedSubflow);
|
||||
}
|
||||
}
|
||||
@@ -1651,6 +1750,7 @@ RED.nodes = (function() {
|
||||
* Options:
|
||||
* - generateIds - whether to replace all node ids
|
||||
* - addFlow - whether to import nodes to a new tab
|
||||
* - markChanged - whether to set changed=true on all newly imported objects
|
||||
* - reimport - if node has a .z property, dont overwrite it
|
||||
* Only applicible when `generateIds` is false
|
||||
* - importMap - how to resolve any conflicts.
|
||||
@@ -1659,7 +1759,7 @@ RED.nodes = (function() {
|
||||
* - id:replace - import over the top of existing
|
||||
*/
|
||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
||||
const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} }
|
||||
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
|
||||
options = Object.assign({}, defOpts, options)
|
||||
options.importMap = options.importMap || {}
|
||||
const createNewIds = options.generateIds;
|
||||
@@ -1685,7 +1785,7 @@ RED.nodes = (function() {
|
||||
newNodes = newNodesObj;
|
||||
}
|
||||
|
||||
if (!$.isArray(newNodes)) {
|
||||
if (!Array.isArray(newNodes)) {
|
||||
newNodes = [newNodes];
|
||||
}
|
||||
|
||||
@@ -1983,6 +2083,9 @@ RED.nodes = (function() {
|
||||
if (!n.z) {
|
||||
delete configNode.z;
|
||||
}
|
||||
if (options.markChanged) {
|
||||
configNode.changed = true
|
||||
}
|
||||
if (n.hasOwnProperty('d')) {
|
||||
configNode.d = n.d;
|
||||
}
|
||||
@@ -2045,6 +2148,9 @@ RED.nodes = (function() {
|
||||
if (n.hasOwnProperty('g')) {
|
||||
node.g = n.g;
|
||||
}
|
||||
if (options.markChanged) {
|
||||
node.changed = true
|
||||
}
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
if (subflow_denylist[n.z]) {
|
||||
continue;
|
||||
@@ -2276,7 +2382,7 @@ RED.nodes = (function() {
|
||||
// get added
|
||||
if (activeSubflow && /^link /.test(n.type) && n.links) {
|
||||
n.links = n.links.filter(function(id) {
|
||||
var otherNode = RED.nodes.node(id);
|
||||
const otherNode = node_map[id] || RED.nodes.node(id);
|
||||
return (otherNode && otherNode.z === activeWorkspace)
|
||||
});
|
||||
}
|
||||
@@ -2326,19 +2432,6 @@ RED.nodes = (function() {
|
||||
if (n.g && !new_group_set.has(n.g)) {
|
||||
delete n.g;
|
||||
}
|
||||
n.nodes = n.nodes.map(function(id) {
|
||||
return node_map[id];
|
||||
})
|
||||
// Just in case the group references a node that doesn't exist for some reason
|
||||
n.nodes = n.nodes.filter(function(v) {
|
||||
if (v) {
|
||||
// Repair any nodes that have forgotten they are in this group
|
||||
if (v.g !== n.id) {
|
||||
v.g = n.id;
|
||||
}
|
||||
}
|
||||
return !!v
|
||||
});
|
||||
if (!n.g) {
|
||||
groupDepthMap[n.id] = 0;
|
||||
}
|
||||
@@ -2361,21 +2454,22 @@ RED.nodes = (function() {
|
||||
return groupDepthMap[A.id] - groupDepthMap[B.id];
|
||||
});
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
addGroup(n);
|
||||
new_groups[i] = addGroup(new_groups[i]);
|
||||
node_map[new_groups[i].id] = new_groups[i]
|
||||
}
|
||||
|
||||
for (i=0;i<new_junctions.length;i++) {
|
||||
var junction = new_junctions[i];
|
||||
addJunction(junction);
|
||||
new_junctions[i] = addJunction(new_junctions[i]);
|
||||
node_map[new_junctions[i].id] = new_junctions[i]
|
||||
}
|
||||
|
||||
|
||||
// Now the nodes have been fully updated, add them.
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
var node = new_nodes[i];
|
||||
addNode(node);
|
||||
new_nodes[i] = addNode(new_nodes[i])
|
||||
node_map[new_nodes[i].id] = new_nodes[i]
|
||||
}
|
||||
|
||||
// Finally validate them all.
|
||||
// This has to be done after everything is added so that any checks for
|
||||
// dependent config nodes will pass
|
||||
@@ -2383,6 +2477,39 @@ RED.nodes = (function() {
|
||||
var node = new_nodes[i];
|
||||
RED.editor.validateNode(node);
|
||||
}
|
||||
const lookupNode = (id) => {
|
||||
const mappedNode = node_map[id]
|
||||
if (!mappedNode) {
|
||||
return null
|
||||
}
|
||||
if (mappedNode.__isProxy__) {
|
||||
return mappedNode
|
||||
} else {
|
||||
return node_map[mappedNode.id]
|
||||
}
|
||||
}
|
||||
// Update groups to reference proxy node objects
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
// bypass the proxy in case the flow is locked
|
||||
n.__node__.nodes = n.nodes.map(lookupNode)
|
||||
// Just in case the group references a node that doesn't exist for some reason
|
||||
n.__node__.nodes = n.nodes.filter(function(v) {
|
||||
if (v) {
|
||||
// Repair any nodes that have forgotten they are in this group
|
||||
if (v.g !== n.id) {
|
||||
v.g = n.id;
|
||||
}
|
||||
}
|
||||
return !!v
|
||||
});
|
||||
}
|
||||
|
||||
// Update links to use proxy node objects
|
||||
for (i=0;i<new_links.length;i++) {
|
||||
new_links[i].source = lookupNode(new_links[i].source.id) || new_links[i].source
|
||||
new_links[i].target = lookupNode(new_links[i].target.id) || new_links[i].target
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
|
||||
@@ -2511,11 +2638,17 @@ RED.nodes = (function() {
|
||||
junctions = {};
|
||||
junctionsByZ = {};
|
||||
|
||||
var workspaceIds = Object.keys(workspaces);
|
||||
// Ensure all workspaces are unlocked so we don't get any edit-protection
|
||||
// preventing removal
|
||||
workspaceIds.forEach(function(id) {
|
||||
workspaces[id].locked = false
|
||||
});
|
||||
|
||||
var subflowIds = Object.keys(subflows);
|
||||
subflowIds.forEach(function(id) {
|
||||
RED.subflow.removeSubflow(id)
|
||||
});
|
||||
var workspaceIds = Object.keys(workspaces);
|
||||
workspaceIds.forEach(function(id) {
|
||||
RED.workspaces.remove(workspaces[id]);
|
||||
});
|
||||
@@ -2536,10 +2669,15 @@ RED.nodes = (function() {
|
||||
}
|
||||
|
||||
function addGroup(group) {
|
||||
if (!group.__isProxy__) {
|
||||
group = new Proxy(group, nodeProxyHandler)
|
||||
}
|
||||
groupsByZ[group.z] = groupsByZ[group.z] || [];
|
||||
groupsByZ[group.z].push(group);
|
||||
groups[group.id] = group;
|
||||
allNodes.addObjectToWorkspace(group.z, group.id, group.changed || group.moved)
|
||||
RED.events.emit("groups:add",group);
|
||||
return group
|
||||
}
|
||||
function removeGroup(group) {
|
||||
var i = groupsByZ[group.z].indexOf(group);
|
||||
@@ -2554,19 +2692,28 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
RED.group.markDirty(group);
|
||||
|
||||
allNodes.removeObjectFromWorkspace(group.z, group.id)
|
||||
delete groups[group.id];
|
||||
RED.events.emit("groups:remove",group);
|
||||
}
|
||||
function getGroupOrder(z) {
|
||||
const groups = groupsByZ[z]
|
||||
return groups.map(g => g.id)
|
||||
}
|
||||
|
||||
function addJunction(junction) {
|
||||
if (!junction.__isProxy__) {
|
||||
junction = new Proxy(junction, nodeProxyHandler)
|
||||
}
|
||||
junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
|
||||
junctionsByZ[junction.z].push(junction)
|
||||
junctions[junction.id] = junction;
|
||||
if (!nodeLinks[junction.id]) {
|
||||
nodeLinks[junction.id] = {in:[],out:[]};
|
||||
}
|
||||
allNodes.addObjectToWorkspace(junction.z, junction.id, junction.changed || junction.moved)
|
||||
RED.events.emit("junctions:add", junction)
|
||||
return junction
|
||||
}
|
||||
function removeJunction(junction) {
|
||||
var i = junctionsByZ[junction.z].indexOf(junction)
|
||||
@@ -2576,6 +2723,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
delete junctions[junction.id]
|
||||
delete nodeLinks[junction.id];
|
||||
allNodes.removeObjectFromWorkspace(junction.z, junction.id)
|
||||
RED.events.emit("junctions:remove", junction)
|
||||
|
||||
var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
|
||||
@@ -2813,6 +2961,9 @@ RED.nodes = (function() {
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
RED.events.on('deploy', function () {
|
||||
allNodes.clearState()
|
||||
})
|
||||
},
|
||||
registry:registry,
|
||||
setNodeList: registry.setNodeList,
|
||||
@@ -2861,7 +3012,7 @@ RED.nodes = (function() {
|
||||
},
|
||||
addWorkspace: addWorkspace,
|
||||
removeWorkspace: removeWorkspace,
|
||||
getWorkspaceOrder: function() { return workspacesOrder },
|
||||
getWorkspaceOrder: function() { return [...workspacesOrder] },
|
||||
setWorkspaceOrder: function(order) { workspacesOrder = order; },
|
||||
workspace: getWorkspace,
|
||||
|
||||
@@ -2915,6 +3066,20 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
eachGroup: function(cb) {
|
||||
for (var group of Object.values(groups)) {
|
||||
if (cb(group) === false) {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
eachJunction: function(cb) {
|
||||
for (var junction of Object.values(junctions)) {
|
||||
if (cb(junction) === false) {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
node: getNode,
|
||||
|
||||
@@ -2937,6 +3102,7 @@ RED.nodes = (function() {
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
getAllUpstreamNodes: getAllUpstreamNodes,
|
||||
getAllDownstreamNodes: getAllDownstreamNodes,
|
||||
getDownstreamNodes: getDownstreamNodes,
|
||||
getNodeIslands: getNodeIslands,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
|
||||
Reference in New Issue
Block a user