@node-red/editor-client 4.0.5 → 4.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/red/about +33 -0
- package/public/red/red.js +559 -241
- package/public/red/red.min.js +3 -3
package/public/red/red.js
CHANGED
|
@@ -4495,7 +4495,13 @@ RED.nodes = (function() {
|
|
|
4495
4495
|
|
|
4496
4496
|
var exports = {
|
|
4497
4497
|
setModulePendingUpdated: function(module,version) {
|
|
4498
|
-
|
|
4498
|
+
if (!!RED.plugins.getModule(module)) {
|
|
4499
|
+
// The module updated is a plugin
|
|
4500
|
+
RED.plugins.getModule(module).pending_version = version;
|
|
4501
|
+
} else {
|
|
4502
|
+
moduleList[module].pending_version = version;
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4499
4505
|
RED.events.emit("registry:module-updated",{module:module,version:version});
|
|
4500
4506
|
},
|
|
4501
4507
|
getModule: function(module) {
|
|
@@ -5124,11 +5130,11 @@ RED.nodes = (function() {
|
|
|
5124
5130
|
n["_"] = RED._;
|
|
5125
5131
|
}
|
|
5126
5132
|
if (n._def.category == "config") {
|
|
5127
|
-
configNodes[n.id] =
|
|
5133
|
+
configNodes[n.id] = newNode;
|
|
5128
5134
|
} else {
|
|
5129
5135
|
if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; }
|
|
5130
5136
|
n.dirty = true;
|
|
5131
|
-
updateConfigNodeUsers(
|
|
5137
|
+
updateConfigNodeUsers(newNode, { action: "add" });
|
|
5132
5138
|
if (n._def.category == "subflows" && typeof n.i === "undefined") {
|
|
5133
5139
|
var nextId = 0;
|
|
5134
5140
|
RED.nodes.eachNode(function(node) {
|
|
@@ -5201,6 +5207,7 @@ RED.nodes = (function() {
|
|
|
5201
5207
|
delete nodeLinks[id];
|
|
5202
5208
|
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
|
5203
5209
|
removedLinks.forEach(removeLink);
|
|
5210
|
+
updateConfigNodeUsers(node, { action: "remove" });
|
|
5204
5211
|
var updatedConfigNode = false;
|
|
5205
5212
|
for (var d in node._def.defaults) {
|
|
5206
5213
|
if (node._def.defaults.hasOwnProperty(d)) {
|
|
@@ -5214,10 +5221,6 @@ RED.nodes = (function() {
|
|
|
5214
5221
|
if (configNode._def.exclusive) {
|
|
5215
5222
|
removeNode(node[d]);
|
|
5216
5223
|
removedNodes.push(configNode);
|
|
5217
|
-
} else {
|
|
5218
|
-
var users = configNode.users;
|
|
5219
|
-
users.splice(users.indexOf(node),1);
|
|
5220
|
-
RED.events.emit('nodes:change',configNode)
|
|
5221
5224
|
}
|
|
5222
5225
|
}
|
|
5223
5226
|
}
|
|
@@ -5454,23 +5457,34 @@ RED.nodes = (function() {
|
|
|
5454
5457
|
return {nodes:removedNodes,links:removedLinks, groups: removedGroups, junctions: removedJunctions};
|
|
5455
5458
|
}
|
|
5456
5459
|
|
|
5460
|
+
/**
|
|
5461
|
+
* Add a Subflow to the Workspace
|
|
5462
|
+
*
|
|
5463
|
+
* @param {object} sf The Subflow to add.
|
|
5464
|
+
* @param {boolean|undefined} createNewIds Whether to update the name.
|
|
5465
|
+
*/
|
|
5457
5466
|
function addSubflow(sf, createNewIds) {
|
|
5458
5467
|
if (createNewIds) {
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5468
|
+
// Update the Subflow name to highlight that this is a copy
|
|
5469
|
+
const subflowNames = Object.keys(subflows).map(function (sfid) {
|
|
5470
|
+
return subflows[sfid].name || "";
|
|
5471
|
+
})
|
|
5472
|
+
subflowNames.sort()
|
|
5462
5473
|
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
var subflowName = sf.name;
|
|
5474
|
+
let copyNumber = 1;
|
|
5475
|
+
let subflowName = sf.name;
|
|
5466
5476
|
subflowNames.forEach(function(name) {
|
|
5467
5477
|
if (subflowName == name) {
|
|
5478
|
+
subflowName = sf.name + " (" + copyNumber + ")";
|
|
5468
5479
|
copyNumber++;
|
|
5469
|
-
subflowName = sf.name+" ("+copyNumber+")";
|
|
5470
5480
|
}
|
|
5471
5481
|
});
|
|
5482
|
+
|
|
5472
5483
|
sf.name = subflowName;
|
|
5473
5484
|
}
|
|
5485
|
+
|
|
5486
|
+
sf.instances = [];
|
|
5487
|
+
|
|
5474
5488
|
subflows[sf.id] = sf;
|
|
5475
5489
|
allNodes.addTab(sf.id);
|
|
5476
5490
|
linkTabMap[sf.id] = [];
|
|
@@ -5523,7 +5537,7 @@ RED.nodes = (function() {
|
|
|
5523
5537
|
module: "node-red"
|
|
5524
5538
|
}
|
|
5525
5539
|
});
|
|
5526
|
-
|
|
5540
|
+
|
|
5527
5541
|
sf._def = RED.nodes.getType("subflow:"+sf.id);
|
|
5528
5542
|
RED.events.emit("subflows:add",sf);
|
|
5529
5543
|
}
|
|
@@ -6165,7 +6179,8 @@ RED.nodes = (function() {
|
|
|
6165
6179
|
// Remove the old subflow definition - but leave the instances in place
|
|
6166
6180
|
var removalResult = RED.subflow.removeSubflow(n.id, true);
|
|
6167
6181
|
// Create the list of nodes for the new subflow def
|
|
6168
|
-
|
|
6182
|
+
// Need to sort the list in order to remove missing nodes
|
|
6183
|
+
var subflowNodes = [n].concat(zMap[n.id]).filter((s) => !!s);
|
|
6169
6184
|
// Import the new subflow - no clashes should occur as we've removed
|
|
6170
6185
|
// the old version
|
|
6171
6186
|
var result = importNodes(subflowNodes);
|
|
@@ -6202,9 +6217,20 @@ RED.nodes = (function() {
|
|
|
6202
6217
|
// Replace config nodes
|
|
6203
6218
|
//
|
|
6204
6219
|
configNodeIds.forEach(function(id) {
|
|
6205
|
-
|
|
6220
|
+
const configNode = getNode(id);
|
|
6221
|
+
const currentUserCount = configNode.users;
|
|
6222
|
+
|
|
6223
|
+
// Add a snapshot of the Config Node
|
|
6224
|
+
removedNodes = removedNodes.concat(convertNode(configNode));
|
|
6225
|
+
|
|
6226
|
+
// Remove the Config Node instance
|
|
6206
6227
|
removeNode(id);
|
|
6207
|
-
|
|
6228
|
+
|
|
6229
|
+
// Import the new one
|
|
6230
|
+
importNodes([newConfigNodes[id]]);
|
|
6231
|
+
|
|
6232
|
+
// Re-attributes the user count
|
|
6233
|
+
getNode(id).users = currentUserCount;
|
|
6208
6234
|
});
|
|
6209
6235
|
|
|
6210
6236
|
return {
|
|
@@ -6445,6 +6471,8 @@ RED.nodes = (function() {
|
|
|
6445
6471
|
if (matchingSubflow) {
|
|
6446
6472
|
subflow_denylist[n.id] = matchingSubflow;
|
|
6447
6473
|
} else {
|
|
6474
|
+
const oldId = n.id;
|
|
6475
|
+
|
|
6448
6476
|
subflow_map[n.id] = n;
|
|
6449
6477
|
if (createNewIds || options.importMap[n.id] === "copy") {
|
|
6450
6478
|
nid = getID();
|
|
@@ -6472,7 +6500,7 @@ RED.nodes = (function() {
|
|
|
6472
6500
|
n.status.id = getID();
|
|
6473
6501
|
}
|
|
6474
6502
|
new_subflows.push(n);
|
|
6475
|
-
addSubflow(n,createNewIds || options.importMap[
|
|
6503
|
+
addSubflow(n,createNewIds || options.importMap[oldId] === "copy");
|
|
6476
6504
|
}
|
|
6477
6505
|
}
|
|
6478
6506
|
}
|
|
@@ -6592,7 +6620,7 @@ RED.nodes = (function() {
|
|
|
6592
6620
|
x:parseFloat(n.x || 0),
|
|
6593
6621
|
y:parseFloat(n.y || 0),
|
|
6594
6622
|
z:n.z,
|
|
6595
|
-
type:
|
|
6623
|
+
type: n.type,
|
|
6596
6624
|
info: n.info,
|
|
6597
6625
|
changed:false,
|
|
6598
6626
|
_config:{}
|
|
@@ -6653,7 +6681,6 @@ RED.nodes = (function() {
|
|
|
6653
6681
|
}
|
|
6654
6682
|
}
|
|
6655
6683
|
}
|
|
6656
|
-
node.type = n.type;
|
|
6657
6684
|
node._def = def;
|
|
6658
6685
|
if (node.type === "group") {
|
|
6659
6686
|
node._def = RED.group.def;
|
|
@@ -6683,6 +6710,15 @@ RED.nodes = (function() {
|
|
|
6683
6710
|
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
|
|
6684
6711
|
set: registry.getNodeSet("node-red/unknown")
|
|
6685
6712
|
}
|
|
6713
|
+
var orig = {};
|
|
6714
|
+
for (var p in n) {
|
|
6715
|
+
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
|
|
6716
|
+
orig[p] = n[p];
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6719
|
+
node._orig = orig;
|
|
6720
|
+
node.name = n.type;
|
|
6721
|
+
node.type = "unknown";
|
|
6686
6722
|
} else {
|
|
6687
6723
|
if (subflow_denylist[parentId] || createNewIds || options.importMap[n.id] === "copy") {
|
|
6688
6724
|
parentId = subflow.id;
|
|
@@ -6743,29 +6779,31 @@ RED.nodes = (function() {
|
|
|
6743
6779
|
node.type = "unknown";
|
|
6744
6780
|
}
|
|
6745
6781
|
if (node._def.category != "config") {
|
|
6746
|
-
if (n.hasOwnProperty('inputs')) {
|
|
6747
|
-
node.inputs = n.inputs;
|
|
6782
|
+
if (n.hasOwnProperty('inputs') && def.defaults.hasOwnProperty("inputs")) {
|
|
6783
|
+
node.inputs = parseInt(n.inputs, 10);
|
|
6748
6784
|
node._config.inputs = JSON.stringify(n.inputs);
|
|
6749
6785
|
} else {
|
|
6750
6786
|
node.inputs = node._def.inputs;
|
|
6751
6787
|
}
|
|
6752
|
-
if (n.hasOwnProperty('outputs')) {
|
|
6753
|
-
node.outputs = n.outputs;
|
|
6788
|
+
if (n.hasOwnProperty('outputs') && def.defaults.hasOwnProperty("outputs")) {
|
|
6789
|
+
node.outputs = parseInt(n.outputs, 10);
|
|
6754
6790
|
node._config.outputs = JSON.stringify(n.outputs);
|
|
6755
6791
|
} else {
|
|
6756
6792
|
node.outputs = node._def.outputs;
|
|
6757
6793
|
}
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
} else {
|
|
6764
|
-
// The node declares outputs in its defaults, but has not got a valid value
|
|
6765
|
-
// Defer to the length of the wires array
|
|
6794
|
+
|
|
6795
|
+
// The node declares outputs in its defaults, but has not got a valid value
|
|
6796
|
+
// Defer to the length of the wires array
|
|
6797
|
+
if (node.hasOwnProperty('wires')) {
|
|
6798
|
+
if (isNaN(node.outputs)) {
|
|
6766
6799
|
node.outputs = node.wires.length;
|
|
6800
|
+
} else if (node.wires.length > node.outputs) {
|
|
6801
|
+
// If 'wires' is longer than outputs, clip wires
|
|
6802
|
+
console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs);
|
|
6803
|
+
node.wires = node.wires.slice(0, node.outputs);
|
|
6767
6804
|
}
|
|
6768
6805
|
}
|
|
6806
|
+
|
|
6769
6807
|
for (d in node._def.defaults) {
|
|
6770
6808
|
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
|
|
6771
6809
|
node[d] = n[d];
|
|
@@ -6862,11 +6900,6 @@ RED.nodes = (function() {
|
|
|
6862
6900
|
nodeList = nodeList.map(function(id) {
|
|
6863
6901
|
var node = node_map[id];
|
|
6864
6902
|
if (node) {
|
|
6865
|
-
if (node._def.category === 'config') {
|
|
6866
|
-
if (node.users.indexOf(n) === -1) {
|
|
6867
|
-
node.users.push(n);
|
|
6868
|
-
}
|
|
6869
|
-
}
|
|
6870
6903
|
return node.id;
|
|
6871
6904
|
}
|
|
6872
6905
|
return id;
|
|
@@ -6880,9 +6913,11 @@ RED.nodes = (function() {
|
|
|
6880
6913
|
n = new_subflows[i];
|
|
6881
6914
|
n.in.forEach(function(input) {
|
|
6882
6915
|
input.wires.forEach(function(wire) {
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6916
|
+
if (node_map.hasOwnProperty(wire.id)) {
|
|
6917
|
+
var link = {source:input, sourcePort:0, target:node_map[wire.id]};
|
|
6918
|
+
addLink(link);
|
|
6919
|
+
new_links.push(link);
|
|
6920
|
+
}
|
|
6886
6921
|
});
|
|
6887
6922
|
delete input.wires;
|
|
6888
6923
|
});
|
|
@@ -6891,11 +6926,13 @@ RED.nodes = (function() {
|
|
|
6891
6926
|
var link;
|
|
6892
6927
|
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
|
|
6893
6928
|
link = {source:n.in[wire.port], sourcePort:wire.port,target:output};
|
|
6894
|
-
} else {
|
|
6929
|
+
} else if (node_map.hasOwnProperty(wire.id) || subflow_map.hasOwnProperty(wire.id)) {
|
|
6895
6930
|
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:output};
|
|
6896
6931
|
}
|
|
6897
|
-
|
|
6898
|
-
|
|
6932
|
+
if (link) {
|
|
6933
|
+
addLink(link);
|
|
6934
|
+
new_links.push(link);
|
|
6935
|
+
}
|
|
6899
6936
|
});
|
|
6900
6937
|
delete output.wires;
|
|
6901
6938
|
});
|
|
@@ -6904,11 +6941,13 @@ RED.nodes = (function() {
|
|
|
6904
6941
|
var link;
|
|
6905
6942
|
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
|
|
6906
6943
|
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
|
|
6907
|
-
} else {
|
|
6944
|
+
} else if (node_map.hasOwnProperty(wire.id) || subflow_map.hasOwnProperty(wire.id)) {
|
|
6908
6945
|
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
|
|
6909
6946
|
}
|
|
6910
|
-
|
|
6911
|
-
|
|
6947
|
+
if (link) {
|
|
6948
|
+
addLink(link);
|
|
6949
|
+
new_links.push(link);
|
|
6950
|
+
}
|
|
6912
6951
|
});
|
|
6913
6952
|
delete n.status.wires;
|
|
6914
6953
|
}
|
|
@@ -7087,25 +7126,78 @@ RED.nodes = (function() {
|
|
|
7087
7126
|
return result;
|
|
7088
7127
|
}
|
|
7089
7128
|
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7129
|
+
/**
|
|
7130
|
+
* Update any config nodes referenced by the provided node to ensure
|
|
7131
|
+
* their 'users' list is correct.
|
|
7132
|
+
*
|
|
7133
|
+
* @param {object} node The node in which to check if it contains references
|
|
7134
|
+
* @param {object} options Options to apply.
|
|
7135
|
+
* @param {"add" | "remove"} [options.action] Add or remove the node from
|
|
7136
|
+
* the Config Node users list. Default `add`.
|
|
7137
|
+
* @param {boolean} [options.emitEvent] Emit the `nodes:changes` event.
|
|
7138
|
+
* Default true.
|
|
7139
|
+
*/
|
|
7140
|
+
function updateConfigNodeUsers(node, options) {
|
|
7141
|
+
const defaultOptions = { action: "add", emitEvent: true };
|
|
7142
|
+
options = Object.assign({}, defaultOptions, options);
|
|
7143
|
+
|
|
7144
|
+
for (var d in node._def.defaults) {
|
|
7145
|
+
if (node._def.defaults.hasOwnProperty(d)) {
|
|
7146
|
+
var property = node._def.defaults[d];
|
|
7095
7147
|
if (property.type) {
|
|
7096
7148
|
var type = registry.getNodeType(property.type);
|
|
7097
7149
|
if (type && type.category == "config") {
|
|
7098
|
-
var configNode = configNodes[
|
|
7150
|
+
var configNode = configNodes[node[d]];
|
|
7099
7151
|
if (configNode) {
|
|
7100
|
-
if (
|
|
7101
|
-
configNode.users.
|
|
7102
|
-
|
|
7152
|
+
if (options.action === "add") {
|
|
7153
|
+
if (configNode.users.indexOf(node) === -1) {
|
|
7154
|
+
configNode.users.push(node);
|
|
7155
|
+
if (options.emitEvent) {
|
|
7156
|
+
RED.events.emit('nodes:change', configNode);
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
} else if (options.action === "remove") {
|
|
7160
|
+
if (configNode.users.indexOf(node) !== -1) {
|
|
7161
|
+
const users = configNode.users;
|
|
7162
|
+
users.splice(users.indexOf(node), 1);
|
|
7163
|
+
if (options.emitEvent) {
|
|
7164
|
+
RED.events.emit('nodes:change', configNode);
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7103
7167
|
}
|
|
7104
7168
|
}
|
|
7105
7169
|
}
|
|
7106
7170
|
}
|
|
7107
7171
|
}
|
|
7108
7172
|
}
|
|
7173
|
+
|
|
7174
|
+
// Subflows can have config node env
|
|
7175
|
+
if (node.type.indexOf("subflow:") === 0) {
|
|
7176
|
+
node.env?.forEach((prop) => {
|
|
7177
|
+
if (prop.type === "conf-type" && prop.value) {
|
|
7178
|
+
// Add the node to the config node users
|
|
7179
|
+
const configNode = getNode(prop.value);
|
|
7180
|
+
if (configNode) {
|
|
7181
|
+
if (options.action === "add") {
|
|
7182
|
+
if (configNode.users.indexOf(node) === -1) {
|
|
7183
|
+
configNode.users.push(node);
|
|
7184
|
+
if (options.emitEvent) {
|
|
7185
|
+
RED.events.emit('nodes:change', configNode);
|
|
7186
|
+
}
|
|
7187
|
+
}
|
|
7188
|
+
} else if (options.action === "remove") {
|
|
7189
|
+
if (configNode.users.indexOf(node) !== -1) {
|
|
7190
|
+
const users = configNode.users;
|
|
7191
|
+
users.splice(users.indexOf(node), 1);
|
|
7192
|
+
if (options.emitEvent) {
|
|
7193
|
+
RED.events.emit('nodes:change', configNode);
|
|
7194
|
+
}
|
|
7195
|
+
}
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
});
|
|
7200
|
+
}
|
|
7109
7201
|
}
|
|
7110
7202
|
|
|
7111
7203
|
function flowVersion(version) {
|
|
@@ -8882,10 +8974,61 @@ RED.history = (function() {
|
|
|
8882
8974
|
RED.events.emit("nodes:change",newConfigNode);
|
|
8883
8975
|
}
|
|
8884
8976
|
});
|
|
8977
|
+
} else if (i === "env" && ev.node.type.indexOf("subflow:") === 0) {
|
|
8978
|
+
// Subflow can have config node in node.env
|
|
8979
|
+
let nodeList = ev.node.env || [];
|
|
8980
|
+
nodeList = nodeList.reduce((list, prop) => {
|
|
8981
|
+
if (prop.type === "conf-type" && prop.value) {
|
|
8982
|
+
list.push(prop.value);
|
|
8983
|
+
}
|
|
8984
|
+
return list;
|
|
8985
|
+
}, []);
|
|
8986
|
+
|
|
8987
|
+
nodeList.forEach(function(id) {
|
|
8988
|
+
const configNode = RED.nodes.node(id);
|
|
8989
|
+
if (configNode) {
|
|
8990
|
+
if (configNode.users.indexOf(ev.node) !== -1) {
|
|
8991
|
+
configNode.users.splice(configNode.users.indexOf(ev.node), 1);
|
|
8992
|
+
RED.events.emit("nodes:change", configNode);
|
|
8993
|
+
}
|
|
8994
|
+
}
|
|
8995
|
+
});
|
|
8996
|
+
|
|
8997
|
+
nodeList = ev.changes.env || [];
|
|
8998
|
+
nodeList = nodeList.reduce((list, prop) => {
|
|
8999
|
+
if (prop.type === "conf-type" && prop.value) {
|
|
9000
|
+
list.push(prop.value);
|
|
9001
|
+
}
|
|
9002
|
+
return list;
|
|
9003
|
+
}, []);
|
|
9004
|
+
|
|
9005
|
+
nodeList.forEach(function(id) {
|
|
9006
|
+
const configNode = RED.nodes.node(id);
|
|
9007
|
+
if (configNode) {
|
|
9008
|
+
if (configNode.users.indexOf(ev.node) === -1) {
|
|
9009
|
+
configNode.users.push(ev.node);
|
|
9010
|
+
RED.events.emit("nodes:change", configNode);
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
9013
|
+
});
|
|
9014
|
+
}
|
|
9015
|
+
if (i === "credentials" && ev.changes[i]) {
|
|
9016
|
+
// Reset - Only want to keep the changes
|
|
9017
|
+
inverseEv.changes[i] = {};
|
|
9018
|
+
for (const [key, value] of Object.entries(ev.changes[i])) {
|
|
9019
|
+
// Edge case: node.credentials is cleared after a deploy, so we can't
|
|
9020
|
+
// capture values for the inverse event when undoing past a deploy
|
|
9021
|
+
if (ev.node.credentials) {
|
|
9022
|
+
inverseEv.changes[i][key] = ev.node.credentials[key];
|
|
9023
|
+
}
|
|
9024
|
+
ev.node.credentials[key] = value;
|
|
9025
|
+
}
|
|
9026
|
+
} else {
|
|
9027
|
+
ev.node[i] = ev.changes[i];
|
|
8885
9028
|
}
|
|
8886
|
-
ev.node[i] = ev.changes[i];
|
|
8887
9029
|
}
|
|
8888
9030
|
}
|
|
9031
|
+
|
|
8889
9032
|
ev.node.dirty = true;
|
|
8890
9033
|
ev.node.changed = ev.changed;
|
|
8891
9034
|
|
|
@@ -8965,6 +9108,24 @@ RED.history = (function() {
|
|
|
8965
9108
|
RED.editor.updateNodeProperties(ev.node,outputMap);
|
|
8966
9109
|
RED.editor.validateNode(ev.node);
|
|
8967
9110
|
}
|
|
9111
|
+
// If it's a Config Node, validate user nodes too.
|
|
9112
|
+
// NOTE: The Config Node must be validated before validating users.
|
|
9113
|
+
if (ev.node.users) {
|
|
9114
|
+
const validatedNodes = new Set();
|
|
9115
|
+
const userStack = ev.node.users.slice();
|
|
9116
|
+
|
|
9117
|
+
validatedNodes.add(ev.node.id);
|
|
9118
|
+
while (userStack.length) {
|
|
9119
|
+
const node = userStack.pop();
|
|
9120
|
+
if (!validatedNodes.has(node.id)) {
|
|
9121
|
+
validatedNodes.add(node.id);
|
|
9122
|
+
if (node.users) {
|
|
9123
|
+
userStack.push(...node.users);
|
|
9124
|
+
}
|
|
9125
|
+
RED.editor.validateNode(node);
|
|
9126
|
+
}
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
8968
9129
|
if (ev.links) {
|
|
8969
9130
|
inverseEv.createdLinks = [];
|
|
8970
9131
|
for (i=0;i<ev.links.length;i++) {
|
|
@@ -22218,7 +22379,7 @@ RED.view = (function() {
|
|
|
22218
22379
|
}
|
|
22219
22380
|
selectedLinks.clearUnselected()
|
|
22220
22381
|
},
|
|
22221
|
-
length: () => groups.
|
|
22382
|
+
length: () => groups.size,
|
|
22222
22383
|
forEach: (func) => { groups.forEach(func) },
|
|
22223
22384
|
toArray: () => [...groups],
|
|
22224
22385
|
clear: function () {
|
|
@@ -22251,8 +22412,8 @@ RED.view = (function() {
|
|
|
22251
22412
|
evt.stopPropagation()
|
|
22252
22413
|
RED.contextMenu.show({
|
|
22253
22414
|
type: 'workspace',
|
|
22254
|
-
x:evt.clientX
|
|
22255
|
-
y:evt.clientY
|
|
22415
|
+
x: evt.clientX,
|
|
22416
|
+
y: evt.clientY
|
|
22256
22417
|
})
|
|
22257
22418
|
return false
|
|
22258
22419
|
})
|
|
@@ -24619,22 +24780,21 @@ RED.view = (function() {
|
|
|
24619
24780
|
addToRemovedLinks(reconnectResult.removedLinks)
|
|
24620
24781
|
}
|
|
24621
24782
|
|
|
24622
|
-
|
|
24623
|
-
|
|
24624
|
-
var selectedGroups = [];
|
|
24783
|
+
const startDirty = RED.nodes.dirty();
|
|
24784
|
+
let movingSelectedGroups = [];
|
|
24625
24785
|
if (movingSet.length() > 0) {
|
|
24626
24786
|
|
|
24627
24787
|
for (var i=0;i<movingSet.length();i++) {
|
|
24628
24788
|
node = movingSet.get(i).n;
|
|
24629
24789
|
if (node.type === "group") {
|
|
24630
|
-
|
|
24790
|
+
movingSelectedGroups.push(node);
|
|
24631
24791
|
}
|
|
24632
24792
|
}
|
|
24633
24793
|
// Make sure we have identified all groups about to be deleted
|
|
24634
|
-
for (i=0;i<
|
|
24635
|
-
|
|
24636
|
-
if (n.type === "group" &&
|
|
24637
|
-
|
|
24794
|
+
for (i=0;i<movingSelectedGroups.length;i++) {
|
|
24795
|
+
movingSelectedGroups[i].nodes.forEach(function(n) {
|
|
24796
|
+
if (n.type === "group" && movingSelectedGroups.indexOf(n) === -1) {
|
|
24797
|
+
movingSelectedGroups.push(n);
|
|
24638
24798
|
}
|
|
24639
24799
|
})
|
|
24640
24800
|
}
|
|
@@ -24651,7 +24811,7 @@ RED.view = (function() {
|
|
|
24651
24811
|
addToRemovedLinks(removedEntities.links);
|
|
24652
24812
|
if (node.g) {
|
|
24653
24813
|
var group = RED.nodes.group(node.g);
|
|
24654
|
-
if (
|
|
24814
|
+
if (movingSelectedGroups.indexOf(group) === -1) {
|
|
24655
24815
|
// Don't use RED.group.removeFromGroup as that emits
|
|
24656
24816
|
// a change event on the node - but we're deleting it
|
|
24657
24817
|
var index = group.nodes.indexOf(node);
|
|
@@ -24665,7 +24825,7 @@ RED.view = (function() {
|
|
|
24665
24825
|
removedLinks = removedLinks.concat(result.links);
|
|
24666
24826
|
if (node.g) {
|
|
24667
24827
|
var group = RED.nodes.group(node.g);
|
|
24668
|
-
if (
|
|
24828
|
+
if (movingSelectedGroups.indexOf(group) === -1) {
|
|
24669
24829
|
// Don't use RED.group.removeFromGroup as that emits
|
|
24670
24830
|
// a change event on the node - but we're deleting it
|
|
24671
24831
|
var index = group.nodes.indexOf(node);
|
|
@@ -24687,8 +24847,8 @@ RED.view = (function() {
|
|
|
24687
24847
|
|
|
24688
24848
|
// Groups must be removed in the right order - from inner-most
|
|
24689
24849
|
// to outermost.
|
|
24690
|
-
for (i =
|
|
24691
|
-
var g =
|
|
24850
|
+
for (i = movingSelectedGroups.length-1; i>=0; i--) {
|
|
24851
|
+
var g = movingSelectedGroups[i];
|
|
24692
24852
|
removedGroups.push(g);
|
|
24693
24853
|
RED.nodes.removeGroup(g);
|
|
24694
24854
|
}
|
|
@@ -27105,8 +27265,8 @@ RED.view = (function() {
|
|
|
27105
27265
|
var delta = Infinity;
|
|
27106
27266
|
for (var i = 0; i < lineLength; i++) {
|
|
27107
27267
|
var linePos = pathLine.getPointAtLength(i);
|
|
27108
|
-
var posDeltaX = Math.abs(linePos.x-d3.event.offsetX)
|
|
27109
|
-
var posDeltaY = Math.abs(linePos.y-d3.event.offsetY)
|
|
27268
|
+
var posDeltaX = Math.abs(linePos.x-(d3.event.offsetX / scaleFactor))
|
|
27269
|
+
var posDeltaY = Math.abs(linePos.y-(d3.event.offsetY / scaleFactor))
|
|
27110
27270
|
var posDelta = posDeltaX*posDeltaX + posDeltaY*posDeltaY
|
|
27111
27271
|
if (posDelta < delta) {
|
|
27112
27272
|
pos = linePos
|
|
@@ -28440,7 +28600,7 @@ RED.view = (function() {
|
|
|
28440
28600
|
}
|
|
28441
28601
|
let badgeRDX = 0;
|
|
28442
28602
|
let badgeLDX = 0;
|
|
28443
|
-
|
|
28603
|
+
const scale = RED.view.scale()
|
|
28444
28604
|
for (let i=0,l=evt.el.__annotations__.length;i<l;i++) {
|
|
28445
28605
|
const annotation = evt.el.__annotations__[i];
|
|
28446
28606
|
if (annotations.hasOwnProperty(annotation.id)) {
|
|
@@ -28471,15 +28631,17 @@ RED.view = (function() {
|
|
|
28471
28631
|
}
|
|
28472
28632
|
if (isBadge) {
|
|
28473
28633
|
if (showAnnotation) {
|
|
28474
|
-
|
|
28634
|
+
// getBoundingClientRect is in real-world scale so needs to be adjusted according to
|
|
28635
|
+
// the current scale factor
|
|
28636
|
+
const rectWidth = annotation.element.getBoundingClientRect().width / scale;
|
|
28475
28637
|
let annotationX
|
|
28476
28638
|
if (!opts.align || opts.align === 'right') {
|
|
28477
|
-
annotationX = evt.node.w - 3 - badgeRDX -
|
|
28478
|
-
badgeRDX +=
|
|
28639
|
+
annotationX = evt.node.w - 3 - badgeRDX - rectWidth
|
|
28640
|
+
badgeRDX += rectWidth + 4;
|
|
28479
28641
|
|
|
28480
28642
|
} else if (opts.align === 'left') {
|
|
28481
28643
|
annotationX = 3 + badgeLDX
|
|
28482
|
-
badgeLDX +=
|
|
28644
|
+
badgeLDX += rectWidth + 4;
|
|
28483
28645
|
}
|
|
28484
28646
|
annotation.element.setAttribute("transform", "translate("+annotationX+", -8)");
|
|
28485
28647
|
}
|
|
@@ -29870,18 +30032,27 @@ RED.view.tools = (function() {
|
|
|
29870
30032
|
const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef)
|
|
29871
30033
|
const defaultNodeNameRE = new RegExp('^'+paletteLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')+' (\\d+)$')
|
|
29872
30034
|
if (!typeIndex.hasOwnProperty(n.type)) {
|
|
29873
|
-
const existingNodes = RED.nodes.filterNodes({type: n.type})
|
|
29874
|
-
|
|
29875
|
-
|
|
29876
|
-
let match = defaultNodeNameRE.exec(n.name)
|
|
30035
|
+
const existingNodes = RED.nodes.filterNodes({ type: n.type });
|
|
30036
|
+
const existingIds = existingNodes.reduce((ids, node) => {
|
|
30037
|
+
let match = defaultNodeNameRE.exec(node.name);
|
|
29877
30038
|
if (match) {
|
|
29878
|
-
|
|
29879
|
-
if (nodeNumber
|
|
29880
|
-
|
|
30039
|
+
const nodeNumber = parseInt(match[1], 10);
|
|
30040
|
+
if (!ids.includes(nodeNumber)) {
|
|
30041
|
+
ids.push(nodeNumber);
|
|
29881
30042
|
}
|
|
29882
30043
|
}
|
|
29883
|
-
|
|
29884
|
-
|
|
30044
|
+
return ids;
|
|
30045
|
+
}, []).sort((a, b) => a - b);
|
|
30046
|
+
|
|
30047
|
+
let availableNameNumber = 1;
|
|
30048
|
+
for (let i = 0; i < existingIds.length; i++) {
|
|
30049
|
+
if (existingIds[i] !== availableNameNumber) {
|
|
30050
|
+
break;
|
|
30051
|
+
}
|
|
30052
|
+
availableNameNumber++;
|
|
30053
|
+
}
|
|
30054
|
+
|
|
30055
|
+
typeIndex[n.type] = availableNameNumber;
|
|
29885
30056
|
}
|
|
29886
30057
|
if ((options.renameBlank && n.name === '') || (options.renameClash && defaultNodeNameRE.test(n.name))) {
|
|
29887
30058
|
if (generateHistory) {
|
|
@@ -29913,11 +30084,11 @@ RED.view.tools = (function() {
|
|
|
29913
30084
|
}
|
|
29914
30085
|
}
|
|
29915
30086
|
|
|
29916
|
-
function addJunctionsToWires(
|
|
30087
|
+
function addJunctionsToWires(options = {}) {
|
|
29917
30088
|
if (RED.workspaces.isLocked()) {
|
|
29918
30089
|
return
|
|
29919
30090
|
}
|
|
29920
|
-
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
|
|
30091
|
+
let wiresToSplit = options.wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
|
|
29921
30092
|
if (!wiresToSplit) {
|
|
29922
30093
|
return
|
|
29923
30094
|
}
|
|
@@ -29965,21 +30136,26 @@ RED.view.tools = (function() {
|
|
|
29965
30136
|
if (links.length === 0) {
|
|
29966
30137
|
return
|
|
29967
30138
|
}
|
|
29968
|
-
|
|
29969
|
-
|
|
29970
|
-
|
|
29971
|
-
|
|
29972
|
-
|
|
29973
|
-
|
|
29974
|
-
|
|
29975
|
-
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
30139
|
+
if (addedJunctions.length === 0 && Object.hasOwn(options, 'x') && Object.hasOwn(options, 'y')) {
|
|
30140
|
+
junction.x = options.x
|
|
30141
|
+
junction.y = options.y
|
|
30142
|
+
} else {
|
|
30143
|
+
let pointCount = 0
|
|
30144
|
+
links.forEach(function(l) {
|
|
30145
|
+
if (l._sliceLocation) {
|
|
30146
|
+
junction.x += l._sliceLocation.x
|
|
30147
|
+
junction.y += l._sliceLocation.y
|
|
30148
|
+
delete l._sliceLocation
|
|
30149
|
+
pointCount++
|
|
30150
|
+
} else {
|
|
30151
|
+
junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2
|
|
30152
|
+
junction.y += l.source.y + l.target.y
|
|
30153
|
+
pointCount += 2
|
|
30154
|
+
}
|
|
30155
|
+
})
|
|
30156
|
+
junction.x = Math.round(junction.x/pointCount)
|
|
30157
|
+
junction.y = Math.round(junction.y/pointCount)
|
|
30158
|
+
}
|
|
29983
30159
|
if (RED.view.snapGrid) {
|
|
29984
30160
|
let gridSize = RED.view.gridSize()
|
|
29985
30161
|
junction.x = (gridSize*Math.round(junction.x/gridSize));
|
|
@@ -30169,7 +30345,7 @@ RED.view.tools = (function() {
|
|
|
30169
30345
|
RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() })
|
|
30170
30346
|
|
|
30171
30347
|
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
|
|
30172
|
-
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
|
|
30348
|
+
RED.actions.add("core:split-wires-with-junctions", function (options) { addJunctionsToWires(options) });
|
|
30173
30349
|
|
|
30174
30350
|
RED.actions.add("core:generate-node-names", generateNodeNames )
|
|
30175
30351
|
|
|
@@ -36156,6 +36332,20 @@ RED.editor = (function() {
|
|
|
36156
36332
|
}
|
|
36157
36333
|
}
|
|
36158
36334
|
|
|
36335
|
+
const oldCreds = {};
|
|
36336
|
+
if (editing_node._def.credentials) {
|
|
36337
|
+
for (const prop in editing_node._def.credentials) {
|
|
36338
|
+
if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) {
|
|
36339
|
+
if (editing_node._def.credentials[prop].type === 'password') {
|
|
36340
|
+
oldCreds['has_' + prop] = editing_node.credentials['has_' + prop];
|
|
36341
|
+
}
|
|
36342
|
+
if (prop in editing_node.credentials) {
|
|
36343
|
+
oldCreds[prop] = editing_node.credentials[prop];
|
|
36344
|
+
}
|
|
36345
|
+
}
|
|
36346
|
+
}
|
|
36347
|
+
}
|
|
36348
|
+
|
|
36159
36349
|
try {
|
|
36160
36350
|
const rc = editing_node._def.oneditsave.call(editing_node);
|
|
36161
36351
|
if (rc === true) {
|
|
@@ -36187,6 +36377,25 @@ RED.editor = (function() {
|
|
|
36187
36377
|
}
|
|
36188
36378
|
}
|
|
36189
36379
|
}
|
|
36380
|
+
|
|
36381
|
+
if (editing_node._def.credentials) {
|
|
36382
|
+
for (const prop in editing_node._def.credentials) {
|
|
36383
|
+
if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) {
|
|
36384
|
+
if (oldCreds[prop] !== editing_node.credentials[prop]) {
|
|
36385
|
+
if (editing_node.credentials[prop] === '__PWRD__') {
|
|
36386
|
+
// The password may not exist in oldCreds
|
|
36387
|
+
// The value '__PWRD__' means the password exists,
|
|
36388
|
+
// so ignore this change
|
|
36389
|
+
continue;
|
|
36390
|
+
}
|
|
36391
|
+
editState.changes.credentials = editState.changes.credentials || {};
|
|
36392
|
+
editState.changes.credentials['has_' + prop] = oldCreds['has_' + prop];
|
|
36393
|
+
editState.changes.credentials[prop] = oldCreds[prop];
|
|
36394
|
+
editState.changed = true;
|
|
36395
|
+
}
|
|
36396
|
+
}
|
|
36397
|
+
}
|
|
36398
|
+
}
|
|
36190
36399
|
}
|
|
36191
36400
|
}
|
|
36192
36401
|
|
|
@@ -36829,134 +37038,181 @@ RED.editor = (function() {
|
|
|
36829
37038
|
},
|
|
36830
37039
|
{
|
|
36831
37040
|
id: "node-config-dialog-ok",
|
|
36832
|
-
text: adding?RED._("editor.configAdd"):RED._("editor.configUpdate"),
|
|
37041
|
+
text: adding ? RED._("editor.configAdd") : RED._("editor.configUpdate"),
|
|
36833
37042
|
class: "primary",
|
|
36834
37043
|
click: function() {
|
|
36835
|
-
|
|
37044
|
+
// TODO: Already defined
|
|
37045
|
+
const configProperty = name;
|
|
37046
|
+
const configType = type;
|
|
37047
|
+
const configTypeDef = RED.nodes.getType(configType);
|
|
37048
|
+
|
|
37049
|
+
const wasChanged = editing_config_node.changed;
|
|
37050
|
+
const editState = {
|
|
36836
37051
|
changes: {},
|
|
36837
37052
|
changed: false,
|
|
36838
37053
|
outputMap: null
|
|
36839
37054
|
};
|
|
36840
|
-
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
var configAdding = adding;
|
|
36844
|
-
var configTypeDef = RED.nodes.getType(configType);
|
|
36845
|
-
var d;
|
|
36846
|
-
var input;
|
|
36847
|
-
|
|
36848
|
-
if (configTypeDef.oneditsave) {
|
|
36849
|
-
try {
|
|
36850
|
-
configTypeDef.oneditsave.call(editing_config_node);
|
|
36851
|
-
} catch(err) {
|
|
36852
|
-
console.warn("oneditsave",editing_config_node.id,editing_config_node.type,err.toString());
|
|
36853
|
-
}
|
|
36854
|
-
}
|
|
36855
|
-
|
|
36856
|
-
for (d in configTypeDef.defaults) {
|
|
36857
|
-
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
|
36858
|
-
var newValue;
|
|
36859
|
-
input = $("#node-config-input-"+d);
|
|
36860
|
-
if (input.attr('type') === "checkbox") {
|
|
36861
|
-
newValue = input.prop('checked');
|
|
36862
|
-
} else if ("format" in configTypeDef.defaults[d] && configTypeDef.defaults[d].format !== "" && input[0].nodeName === "DIV") {
|
|
36863
|
-
newValue = input.text();
|
|
36864
|
-
} else {
|
|
36865
|
-
newValue = input.val();
|
|
36866
|
-
}
|
|
36867
|
-
if (newValue != null && newValue !== editing_config_node[d]) {
|
|
36868
|
-
if (editing_config_node._def.defaults[d].type) {
|
|
36869
|
-
if (newValue == "_ADD_") {
|
|
36870
|
-
newValue = "";
|
|
36871
|
-
}
|
|
36872
|
-
// Change to a related config node
|
|
36873
|
-
var configNode = RED.nodes.node(editing_config_node[d]);
|
|
36874
|
-
if (configNode) {
|
|
36875
|
-
var users = configNode.users;
|
|
36876
|
-
users.splice(users.indexOf(editing_config_node),1);
|
|
36877
|
-
RED.events.emit("nodes:change",configNode);
|
|
36878
|
-
}
|
|
36879
|
-
configNode = RED.nodes.node(newValue);
|
|
36880
|
-
if (configNode) {
|
|
36881
|
-
configNode.users.push(editing_config_node);
|
|
36882
|
-
RED.events.emit("nodes:change",configNode);
|
|
36883
|
-
}
|
|
36884
|
-
}
|
|
36885
|
-
editing_config_node[d] = newValue;
|
|
36886
|
-
}
|
|
36887
|
-
}
|
|
36888
|
-
}
|
|
37055
|
+
|
|
37056
|
+
// Call `oneditsave` and search for changes
|
|
37057
|
+
handleEditSave(editing_config_node, editState);
|
|
36889
37058
|
|
|
36890
|
-
|
|
37059
|
+
// Search for changes in the edit box (panes)
|
|
37060
|
+
activeEditPanes.forEach(function (pane) {
|
|
36891
37061
|
if (pane.apply) {
|
|
36892
37062
|
pane.apply.call(pane, editState);
|
|
36893
37063
|
}
|
|
36894
|
-
})
|
|
36895
|
-
|
|
36896
|
-
editing_config_node.label = configTypeDef.label;
|
|
37064
|
+
});
|
|
36897
37065
|
|
|
36898
|
-
|
|
36899
|
-
editing_config_node.
|
|
37066
|
+
// TODO: Why?
|
|
37067
|
+
editing_config_node.label = configTypeDef.label
|
|
36900
37068
|
|
|
37069
|
+
// Check if disabled has changed
|
|
36901
37070
|
if ($("#node-config-input-node-disabled").prop('checked')) {
|
|
36902
37071
|
if (editing_config_node.d !== true) {
|
|
37072
|
+
editState.changes.d = editing_config_node.d;
|
|
37073
|
+
editState.changed = true;
|
|
36903
37074
|
editing_config_node.d = true;
|
|
36904
37075
|
}
|
|
36905
37076
|
} else {
|
|
36906
37077
|
if (editing_config_node.d === true) {
|
|
37078
|
+
editState.changes.d = editing_config_node.d;
|
|
37079
|
+
editState.changed = true;
|
|
36907
37080
|
delete editing_config_node.d;
|
|
36908
37081
|
}
|
|
36909
37082
|
}
|
|
36910
37083
|
|
|
37084
|
+
// NOTE: must be undefined if no scope used
|
|
37085
|
+
const scope = $("#red-ui-editor-config-scope").val() || undefined;
|
|
37086
|
+
|
|
37087
|
+
// Check if the scope has changed
|
|
37088
|
+
if (editing_config_node.z !== scope) {
|
|
37089
|
+
editState.changes.z = editing_config_node.z;
|
|
37090
|
+
editState.changed = true;
|
|
37091
|
+
editing_config_node.z = scope;
|
|
37092
|
+
}
|
|
37093
|
+
|
|
37094
|
+
// Search for nodes that use this config node that are no longer
|
|
37095
|
+
// in scope, so must be removed
|
|
37096
|
+
const historyEvents = [];
|
|
36911
37097
|
if (scope) {
|
|
36912
|
-
|
|
36913
|
-
|
|
36914
|
-
|
|
36915
|
-
|
|
36916
|
-
for (
|
|
36917
|
-
if (
|
|
36918
|
-
if (
|
|
36919
|
-
|
|
36920
|
-
|
|
36921
|
-
|
|
36922
|
-
|
|
36923
|
-
|
|
36924
|
-
|
|
36925
|
-
|
|
36926
|
-
|
|
36927
|
-
|
|
37098
|
+
const newUsers = editing_config_node.users.filter(function (node) {
|
|
37099
|
+
let keepNode = false;
|
|
37100
|
+
let nodeModified = null;
|
|
37101
|
+
|
|
37102
|
+
for (const d in node._def.defaults) {
|
|
37103
|
+
if (node._def.defaults.hasOwnProperty(d)) {
|
|
37104
|
+
if (node._def.defaults[d].type === editing_config_node.type) {
|
|
37105
|
+
if (node[d] === editing_config_node.id) {
|
|
37106
|
+
if (node.z === editing_config_node.z) {
|
|
37107
|
+
// The node is kept only if at least one property uses
|
|
37108
|
+
// this config node in the correct scope.
|
|
37109
|
+
keepNode = true;
|
|
37110
|
+
} else {
|
|
37111
|
+
if (!nodeModified) {
|
|
37112
|
+
nodeModified = {
|
|
37113
|
+
t: "edit",
|
|
37114
|
+
node: node,
|
|
37115
|
+
changes: { [d]: node[d] },
|
|
37116
|
+
changed: node.changed,
|
|
37117
|
+
dirty: node.dirty
|
|
37118
|
+
};
|
|
37119
|
+
} else {
|
|
37120
|
+
nodeModified.changes[d] = node[d];
|
|
37121
|
+
}
|
|
37122
|
+
|
|
37123
|
+
// Remove the reference to the config node
|
|
37124
|
+
node[d] = "";
|
|
37125
|
+
}
|
|
37126
|
+
}
|
|
36928
37127
|
}
|
|
36929
37128
|
}
|
|
36930
37129
|
}
|
|
36931
|
-
|
|
37130
|
+
|
|
37131
|
+
// Add the node modified to the history
|
|
37132
|
+
if (nodeModified) {
|
|
37133
|
+
historyEvents.push(nodeModified);
|
|
37134
|
+
}
|
|
37135
|
+
|
|
37136
|
+
// Mark as changed and revalidate this node
|
|
37137
|
+
if (!keepNode) {
|
|
37138
|
+
node.changed = true;
|
|
37139
|
+
node.dirty = true;
|
|
37140
|
+
validateNode(node);
|
|
37141
|
+
RED.events.emit("nodes:change", node);
|
|
37142
|
+
}
|
|
37143
|
+
|
|
37144
|
+
return keepNode;
|
|
36932
37145
|
});
|
|
37146
|
+
|
|
37147
|
+
// Check if users are changed
|
|
37148
|
+
if (editing_config_node.users.length !== newUsers.length) {
|
|
37149
|
+
editState.changes.users = editing_config_node.users;
|
|
37150
|
+
editState.changed = true;
|
|
37151
|
+
editing_config_node.users = newUsers;
|
|
37152
|
+
}
|
|
36933
37153
|
}
|
|
36934
37154
|
|
|
36935
|
-
if (
|
|
36936
|
-
|
|
37155
|
+
if (editState.changed) {
|
|
37156
|
+
// Set the congig node as changed
|
|
37157
|
+
editing_config_node.changed = true;
|
|
36937
37158
|
}
|
|
36938
37159
|
|
|
37160
|
+
// Now, validate the config node
|
|
36939
37161
|
validateNode(editing_config_node);
|
|
36940
|
-
var validatedNodes = {};
|
|
36941
|
-
validatedNodes[editing_config_node.id] = true;
|
|
36942
37162
|
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
36949
|
-
|
|
37163
|
+
// And validate nodes using this config node too
|
|
37164
|
+
const validatedNodes = new Set();
|
|
37165
|
+
const userStack = editing_config_node.users.slice();
|
|
37166
|
+
|
|
37167
|
+
validatedNodes.add(editing_config_node.id);
|
|
37168
|
+
while (userStack.length) {
|
|
37169
|
+
const node = userStack.pop();
|
|
37170
|
+
if (!validatedNodes.has(node.id)) {
|
|
37171
|
+
validatedNodes.add(node.id);
|
|
37172
|
+
if (node.users) {
|
|
37173
|
+
userStack.push(...node.users);
|
|
36950
37174
|
}
|
|
36951
|
-
validateNode(
|
|
37175
|
+
validateNode(node);
|
|
36952
37176
|
}
|
|
36953
37177
|
}
|
|
36954
|
-
|
|
36955
|
-
|
|
36956
|
-
|
|
36957
|
-
|
|
36958
|
-
|
|
37178
|
+
|
|
37179
|
+
let historyEvent = {
|
|
37180
|
+
t: "edit",
|
|
37181
|
+
node: editing_config_node,
|
|
37182
|
+
changes: editState.changes,
|
|
37183
|
+
changed: wasChanged,
|
|
37184
|
+
dirty: RED.nodes.dirty()
|
|
37185
|
+
};
|
|
37186
|
+
|
|
37187
|
+
if (historyEvents.length) {
|
|
37188
|
+
// Need a multi events
|
|
37189
|
+
historyEvent = {
|
|
37190
|
+
t: "multi",
|
|
37191
|
+
events: [historyEvent].concat(historyEvents),
|
|
37192
|
+
dirty: historyEvent.dirty
|
|
37193
|
+
};
|
|
37194
|
+
}
|
|
37195
|
+
|
|
37196
|
+
if (!adding) {
|
|
37197
|
+
// This event is triggered when the edit box is saved,
|
|
37198
|
+
// regardless of whether there are any modifications.
|
|
37199
|
+
RED.events.emit("editor:save", editing_config_node);
|
|
36959
37200
|
}
|
|
37201
|
+
|
|
37202
|
+
if (editState.changed) {
|
|
37203
|
+
if (adding) {
|
|
37204
|
+
RED.history.push({ t: "add", nodes: [editing_config_node.id], dirty: RED.nodes.dirty() });
|
|
37205
|
+
// Add the new config node and trigger the `nodes:add` event
|
|
37206
|
+
RED.nodes.add(editing_config_node);
|
|
37207
|
+
} else {
|
|
37208
|
+
RED.history.push(historyEvent);
|
|
37209
|
+
RED.events.emit("nodes:change", editing_config_node);
|
|
37210
|
+
}
|
|
37211
|
+
|
|
37212
|
+
RED.nodes.dirty(true);
|
|
37213
|
+
RED.view.redraw(true);
|
|
37214
|
+
}
|
|
37215
|
+
|
|
36960
37216
|
RED.tray.close(function() {
|
|
36961
37217
|
var filter = null;
|
|
36962
37218
|
// when editing a config via subflow edit panel, the `configProperty` will not
|
|
@@ -38238,10 +38494,31 @@ RED.editor = (function() {
|
|
|
38238
38494
|
apply: function(editState) {
|
|
38239
38495
|
var old_env = node.env;
|
|
38240
38496
|
var new_env = [];
|
|
38497
|
+
|
|
38241
38498
|
if (/^subflow:/.test(node.type)) {
|
|
38499
|
+
// Get the list of environment variables from the node properties
|
|
38242
38500
|
new_env = RED.subflow.exportSubflowInstanceEnv(node);
|
|
38243
38501
|
}
|
|
38244
38502
|
|
|
38503
|
+
if (old_env && old_env.length) {
|
|
38504
|
+
old_env.forEach(function (prop) {
|
|
38505
|
+
if (prop.type === "conf-type" && prop.value) {
|
|
38506
|
+
const stillInUse = new_env?.some((p) => p.type === "conf-type" && p.name === prop.name && p.value === prop.value);
|
|
38507
|
+
if (!stillInUse) {
|
|
38508
|
+
// Remove the node from the config node users
|
|
38509
|
+
// Only for empty value or modified
|
|
38510
|
+
const configNode = RED.nodes.node(prop.value);
|
|
38511
|
+
if (configNode) {
|
|
38512
|
+
if (configNode.users.indexOf(node) !== -1) {
|
|
38513
|
+
configNode.users.splice(configNode.users.indexOf(node), 1);
|
|
38514
|
+
RED.events.emit('nodes:change', configNode)
|
|
38515
|
+
}
|
|
38516
|
+
}
|
|
38517
|
+
}
|
|
38518
|
+
}
|
|
38519
|
+
});
|
|
38520
|
+
}
|
|
38521
|
+
|
|
38245
38522
|
// Get the values from the Properties table tab
|
|
38246
38523
|
var items = this.list.editableList('items');
|
|
38247
38524
|
items.each(function (i,el) {
|
|
@@ -38259,7 +38536,6 @@ RED.editor = (function() {
|
|
|
38259
38536
|
}
|
|
38260
38537
|
});
|
|
38261
38538
|
|
|
38262
|
-
|
|
38263
38539
|
if (new_env && new_env.length > 0) {
|
|
38264
38540
|
new_env.forEach(function(prop) {
|
|
38265
38541
|
if (prop.type === "cred") {
|
|
@@ -38270,6 +38546,15 @@ RED.editor = (function() {
|
|
|
38270
38546
|
editState.changed = true;
|
|
38271
38547
|
}
|
|
38272
38548
|
delete prop.value;
|
|
38549
|
+
} else if (prop.type === "conf-type" && prop.value) {
|
|
38550
|
+
const configNode = RED.nodes.node(prop.value);
|
|
38551
|
+
if (configNode) {
|
|
38552
|
+
if (configNode.users.indexOf(node) === -1) {
|
|
38553
|
+
// Add the node to the config node users
|
|
38554
|
+
configNode.users.push(node);
|
|
38555
|
+
RED.events.emit('nodes:change', configNode);
|
|
38556
|
+
}
|
|
38557
|
+
}
|
|
38273
38558
|
}
|
|
38274
38559
|
});
|
|
38275
38560
|
}
|
|
@@ -38395,6 +38680,7 @@ RED.editor = (function() {
|
|
|
38395
38680
|
apply: function(editState) {
|
|
38396
38681
|
var newValue;
|
|
38397
38682
|
var d;
|
|
38683
|
+
// If the node is a subflow, the node's properties (exepts name) are saved by `envProperties`
|
|
38398
38684
|
if (node._def.defaults) {
|
|
38399
38685
|
for (d in node._def.defaults) {
|
|
38400
38686
|
if (node._def.defaults.hasOwnProperty(d)) {
|
|
@@ -38482,9 +38768,16 @@ RED.editor = (function() {
|
|
|
38482
38768
|
}
|
|
38483
38769
|
}
|
|
38484
38770
|
if (node._def.credentials) {
|
|
38485
|
-
|
|
38486
|
-
|
|
38487
|
-
|
|
38771
|
+
const credDefinition = node._def.credentials;
|
|
38772
|
+
const credChanges = updateNodeCredentials(node, credDefinition, this.inputClass);
|
|
38773
|
+
|
|
38774
|
+
if (Object.keys(credChanges).length) {
|
|
38775
|
+
editState.changed = true;
|
|
38776
|
+
editState.changes.credentials = {
|
|
38777
|
+
...(editState.changes.credentials || {}),
|
|
38778
|
+
...credChanges
|
|
38779
|
+
};
|
|
38780
|
+
}
|
|
38488
38781
|
}
|
|
38489
38782
|
}
|
|
38490
38783
|
}
|
|
@@ -38512,10 +38805,11 @@ RED.editor = (function() {
|
|
|
38512
38805
|
* @param node - the node containing the credentials
|
|
38513
38806
|
* @param credDefinition - definition of the credentials
|
|
38514
38807
|
* @param prefix - prefix of the input fields
|
|
38515
|
-
* @return {
|
|
38808
|
+
* @return {object} an object containing the modified properties
|
|
38516
38809
|
*/
|
|
38517
38810
|
function updateNodeCredentials(node, credDefinition, prefix) {
|
|
38518
|
-
|
|
38811
|
+
const changes = {};
|
|
38812
|
+
|
|
38519
38813
|
if (!node.credentials) {
|
|
38520
38814
|
node.credentials = {_:{}};
|
|
38521
38815
|
} else if (!node.credentials._) {
|
|
@@ -38528,24 +38822,35 @@ RED.editor = (function() {
|
|
|
38528
38822
|
if (input.length > 0) {
|
|
38529
38823
|
var value = input.val();
|
|
38530
38824
|
if (credDefinition[cred].type == 'password') {
|
|
38531
|
-
|
|
38532
|
-
|
|
38533
|
-
|
|
38825
|
+
if (value === '__PWRD__') {
|
|
38826
|
+
// A cred value exists - no changes
|
|
38827
|
+
} else if (value === '' && node.credentials['has_' + cred] === false) {
|
|
38828
|
+
// Empty cred value exists - no changes
|
|
38829
|
+
} else if (value === node.credentials[cred]) {
|
|
38830
|
+
// A cred value exists locally in the editor - no changes
|
|
38831
|
+
// Like the user sets a value, saves the config,
|
|
38832
|
+
// reopens the config and save the config again
|
|
38833
|
+
} else {
|
|
38834
|
+
changes['has_' + cred] = node.credentials['has_' + cred];
|
|
38835
|
+
changes[cred] = node.credentials[cred];
|
|
38836
|
+
node.credentials[cred] = value;
|
|
38534
38837
|
}
|
|
38535
|
-
changed = true;
|
|
38536
38838
|
|
|
38537
|
-
|
|
38538
|
-
|
|
38539
|
-
|
|
38540
|
-
|
|
38839
|
+
node.credentials['has_' + cred] = (value !== '');
|
|
38840
|
+
} else {
|
|
38841
|
+
// Since these creds are loaded by the editor,
|
|
38842
|
+
// values can be directly compared
|
|
38843
|
+
if (value !== node.credentials[cred]) {
|
|
38844
|
+
changes[cred] = node.credentials[cred];
|
|
38845
|
+
node.credentials[cred] = value;
|
|
38846
|
+
}
|
|
38541
38847
|
}
|
|
38542
38848
|
}
|
|
38543
38849
|
}
|
|
38544
38850
|
}
|
|
38545
|
-
return changed;
|
|
38546
|
-
}
|
|
38547
|
-
|
|
38548
38851
|
|
|
38852
|
+
return changes;
|
|
38853
|
+
}
|
|
38549
38854
|
})();
|
|
38550
38855
|
;(function() {
|
|
38551
38856
|
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
|
|
@@ -39400,7 +39705,7 @@ RED.editor = (function() {
|
|
|
39400
39705
|
nameField.trigger('change');
|
|
39401
39706
|
}
|
|
39402
39707
|
},
|
|
39403
|
-
sortable:
|
|
39708
|
+
sortable: true,
|
|
39404
39709
|
removable: false
|
|
39405
39710
|
});
|
|
39406
39711
|
var parentEnv = {};
|
|
@@ -44109,6 +44414,30 @@ RED.clipboard = (function() {
|
|
|
44109
44414
|
},100);
|
|
44110
44415
|
}
|
|
44111
44416
|
|
|
44417
|
+
/**
|
|
44418
|
+
* Validates if the provided string looks like valid flow json
|
|
44419
|
+
* @param {string} flowString the string to validate
|
|
44420
|
+
* @returns If valid, returns the node array
|
|
44421
|
+
*/
|
|
44422
|
+
function validateFlowString(flowString) {
|
|
44423
|
+
const res = JSON.parse(flowString)
|
|
44424
|
+
if (!Array.isArray(res)) {
|
|
44425
|
+
throw new Error(RED._("clipboard.import.errors.notArray"));
|
|
44426
|
+
}
|
|
44427
|
+
for (let i = 0; i < res.length; i++) {
|
|
44428
|
+
if (typeof res[i] !== "object") {
|
|
44429
|
+
throw new Error(RED._("clipboard.import.errors.itemNotObject",{index:i}));
|
|
44430
|
+
}
|
|
44431
|
+
if (!Object.hasOwn(res[i], 'id')) {
|
|
44432
|
+
throw new Error(RED._("clipboard.import.errors.missingId",{index:i}));
|
|
44433
|
+
}
|
|
44434
|
+
if (!Object.hasOwn(res[i], 'type')) {
|
|
44435
|
+
throw new Error(RED._("clipboard.import.errors.missingType",{index:i}));
|
|
44436
|
+
}
|
|
44437
|
+
}
|
|
44438
|
+
return res
|
|
44439
|
+
}
|
|
44440
|
+
|
|
44112
44441
|
var validateImportTimeout;
|
|
44113
44442
|
function validateImport() {
|
|
44114
44443
|
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
|
|
@@ -44126,21 +44455,7 @@ RED.clipboard = (function() {
|
|
|
44126
44455
|
return;
|
|
44127
44456
|
}
|
|
44128
44457
|
try {
|
|
44129
|
-
|
|
44130
|
-
throw new Error(RED._("clipboard.import.errors.notArray"));
|
|
44131
|
-
}
|
|
44132
|
-
var res = JSON.parse(v);
|
|
44133
|
-
for (var i=0;i<res.length;i++) {
|
|
44134
|
-
if (typeof res[i] !== "object") {
|
|
44135
|
-
throw new Error(RED._("clipboard.import.errors.itemNotObject",{index:i}));
|
|
44136
|
-
}
|
|
44137
|
-
if (!res[i].hasOwnProperty('id')) {
|
|
44138
|
-
throw new Error(RED._("clipboard.import.errors.missingId",{index:i}));
|
|
44139
|
-
}
|
|
44140
|
-
if (!res[i].hasOwnProperty('type')) {
|
|
44141
|
-
throw new Error(RED._("clipboard.import.errors.missingType",{index:i}));
|
|
44142
|
-
}
|
|
44143
|
-
}
|
|
44458
|
+
validateFlowString(v)
|
|
44144
44459
|
currentPopoverError = null;
|
|
44145
44460
|
popover.close(true);
|
|
44146
44461
|
importInput.removeClass("input-error");
|
|
@@ -44773,16 +45088,16 @@ RED.clipboard = (function() {
|
|
|
44773
45088
|
}
|
|
44774
45089
|
|
|
44775
45090
|
function importNodes(nodesStr,addFlow) {
|
|
44776
|
-
|
|
45091
|
+
let newNodes = nodesStr;
|
|
44777
45092
|
if (typeof nodesStr === 'string') {
|
|
44778
45093
|
try {
|
|
44779
45094
|
nodesStr = nodesStr.trim();
|
|
44780
45095
|
if (nodesStr.length === 0) {
|
|
44781
45096
|
return;
|
|
44782
45097
|
}
|
|
44783
|
-
newNodes =
|
|
45098
|
+
newNodes = validateFlowString(nodesStr)
|
|
44784
45099
|
} catch(err) {
|
|
44785
|
-
|
|
45100
|
+
const e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
|
|
44786
45101
|
e.code = "NODE_RED";
|
|
44787
45102
|
throw e;
|
|
44788
45103
|
}
|
|
@@ -45117,6 +45432,7 @@ RED.clipboard = (function() {
|
|
|
45117
45432
|
}
|
|
45118
45433
|
}
|
|
45119
45434
|
} catch(err) {
|
|
45435
|
+
console.warn('Import failed: ', err)
|
|
45120
45436
|
// Ensure any errors throw above doesn't stop the drop target from
|
|
45121
45437
|
// being hidden.
|
|
45122
45438
|
}
|
|
@@ -47081,15 +47397,15 @@ RED.search = (function() {
|
|
|
47081
47397
|
}
|
|
47082
47398
|
}
|
|
47083
47399
|
|
|
47400
|
+
const scale = RED.view.scale()
|
|
47084
47401
|
const offset = $("#red-ui-workspace-chart").offset()
|
|
47085
|
-
|
|
47086
|
-
let
|
|
47087
|
-
let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
|
|
47402
|
+
let addX = (options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()) / scale
|
|
47403
|
+
let addY = (options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()) / scale
|
|
47088
47404
|
|
|
47089
47405
|
if (RED.view.snapGrid) {
|
|
47090
47406
|
const gridSize = RED.view.gridSize()
|
|
47091
|
-
addX = gridSize * Math.
|
|
47092
|
-
addY = gridSize * Math.
|
|
47407
|
+
addX = gridSize * Math.round(addX / gridSize)
|
|
47408
|
+
addY = gridSize * Math.round(addY / gridSize)
|
|
47093
47409
|
}
|
|
47094
47410
|
|
|
47095
47411
|
if (RED.settings.theme("menu.menu-item-action-list", true)) {
|
|
@@ -47114,7 +47430,9 @@ RED.search = (function() {
|
|
|
47114
47430
|
},
|
|
47115
47431
|
(hasLinks) ? { // has least 1 wire selected
|
|
47116
47432
|
label: RED._("contextMenu.junction"),
|
|
47117
|
-
onselect:
|
|
47433
|
+
onselect: function () {
|
|
47434
|
+
RED.actions.invoke('core:split-wires-with-junctions', { x: addX, y: addY })
|
|
47435
|
+
},
|
|
47118
47436
|
disabled: !canEdit || !hasLinks
|
|
47119
47437
|
} : {
|
|
47120
47438
|
label: RED._("contextMenu.junction"),
|
|
@@ -47924,7 +48242,7 @@ RED.actionList = (function() {
|
|
|
47924
48242
|
var items = [];
|
|
47925
48243
|
RED.nodes.registry.getNodeTypes().forEach(function(t) {
|
|
47926
48244
|
var def = RED.nodes.getType(t);
|
|
47927
|
-
if (def.category !== 'config' && t !== 'unknown' && t !== 'tab') {
|
|
48245
|
+
if (def.set?.enabled !== false && def.category !== 'config' && t !== 'unknown' && t !== 'tab') {
|
|
47928
48246
|
items.push({type:t,def: def, label:getTypeLabel(t,def)});
|
|
47929
48247
|
}
|
|
47930
48248
|
});
|
|
@@ -49351,7 +49669,7 @@ RED.subflow = (function() {
|
|
|
49351
49669
|
item.value = ""+input.prop("checked");
|
|
49352
49670
|
break;
|
|
49353
49671
|
case "conf-types":
|
|
49354
|
-
item.value = input.val()
|
|
49672
|
+
item.value = input.val() === "_ADD_" ? "" : input.val();
|
|
49355
49673
|
item.type = "conf-type"
|
|
49356
49674
|
}
|
|
49357
49675
|
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
|