Merge branch 'master' into fix-subflow-dialog

pull/5012/head
GogoVega 2025-01-31 15:20:00 +01:00
commit dd702d4020
No known key found for this signature in database
GPG Key ID: E1E048B63AC5AC2B
14 changed files with 195 additions and 72 deletions

View File

@ -1,4 +0,0 @@
/Gruntfile.js
/.git/*
*.backup
/public/*

View File

@ -1,4 +1,4 @@
#### 4.0.7: Maintenance Release
#### 4.0.8: Maintenance Release
Editor

16
nodemon.json Normal file
View File

@ -0,0 +1,16 @@
{
"ignoreRoot": [
".git",
".nyc_output",
".sass-cache",
"bower-components",
"coverage"
],
"ignore": [
"/Gruntfile.js",
"/.git/*",
"*.backup",
"/public/*"
]
}

View File

@ -1832,7 +1832,18 @@ RED.editor = (function() {
});
}
const envToRemove = new Set();
if (!isSameObj(oldEnv, newEnv)) {
// Get a list of env properties that have been removed
// by comparing oldEnv and newEnv
if (oldEnv) {
oldEnv.forEach((env) => { envToRemove.add(env.name) });
}
if (newEnv) {
newEnv.forEach((env) => {
envToRemove.delete(env.name)
});
}
editState.changes.env = oldEnv;
editingNode.env = newEnv;
editState.changed = true;
@ -1841,6 +1852,7 @@ RED.editor = (function() {
if (editState.changed) {
const wasChanged = editingNode.changed;
const subflowInstances = [];
const instanceHistoryEvents = [];
// Marks the Subflow has changed and validate it
editingNode.changed = true;
@ -1854,19 +1866,42 @@ RED.editor = (function() {
changed: n.changed
});
n.changed = true;
n.dirty = true;
if (editState.changes.hasOwnProperty("color")) {
// Redraw the node color
n._colorChanged = true;
}
n.changed = true;
n.dirty = true;
if (n.env) {
const oldEnv = n.env;
const newEnv = [];
let envChanged = false;
n.env.forEach((env, index) => {
if (envToRemove.has(env.name)) {
envChanged = true;
} else {
newEnv.push(env);
}
});
if (envChanged) {
instanceHistoryEvents.push({
t: 'edit',
node: n,
changes: { env: oldEnv },
dirty: n.dirty,
changed: n.changed
});
n.env = newEnv;
}
}
updateNodeProperties(n);
validateNode(n);
}
});
const historyEvent = {
let historyEvent = {
t: 'edit',
node: editingNode,
changes: editState.changes,
@ -1877,6 +1912,14 @@ RED.editor = (function() {
}
};
if (instanceHistoryEvents.length > 0) {
historyEvent = {
t: 'multi',
events: [ historyEvent, ...instanceHistoryEvents ],
dirty: wasDirty
};
}
RED.events.emit("subflows:change", editingNode);
RED.history.push(historyEvent);
RED.nodes.dirty(true);

View File

@ -245,10 +245,15 @@ RED.library = (function() {
if (lib.types && lib.types.indexOf(options.url) === -1) {
return;
}
let icon = 'fa fa-hdd-o';
if (lib.icon) {
const fullIcon = RED.utils.separateIconPath(lib.icon);
icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
}
listing.push({
library: lib.id,
type: options.url,
icon: lib.icon || 'fa fa-hdd-o',
icon,
label: RED._(lib.label||lib.id),
path: "",
expanded: true,
@ -303,10 +308,15 @@ RED.library = (function() {
if (lib.types && lib.types.indexOf(options.url) === -1) {
return;
}
let icon = 'fa fa-hdd-o';
if (lib.icon) {
const fullIcon = RED.utils.separateIconPath(lib.icon);
icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
}
listing.push({
library: lib.id,
type: options.url,
icon: lib.icon || 'fa fa-hdd-o',
icon,
label: RED._(lib.label||lib.id),
path: "",
expanded: true,

View File

@ -18,8 +18,6 @@ RED.sidebar.context = (function() {
var content;
var sections;
var localCache = {};
var flowAutoRefresh;
var nodeAutoRefresh;
var nodeSection;
@ -27,6 +25,8 @@ RED.sidebar.context = (function() {
var flowSection;
var globalSection;
const expandedPaths = {}
var currentNode;
var currentFlow;
@ -212,14 +212,41 @@ RED.sidebar.context = (function() {
var l = keys.length;
for (var i = 0; i < l; i++) {
sortedData[keys[i]].forEach(function(v) {
var k = keys[i];
var l2 = sortedData[k].length;
var propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container);
var obj = $(propRow.children()[0]);
const k = keys[i];
let payload = v.msg;
let format = v.format;
const tools = $('<span class="button-group"></span>');
expandedPaths[id + "." + k] = expandedPaths[id + "." + k] || new Set()
const objectElementOptions = {
typeHint: format,
sourceId: id + "." + k,
tools,
path: k,
rootPath: k,
exposeApi: true,
ontoggle: function(path,state) {
path = path.substring(k.length+1)
if (state) {
expandedPaths[id+"."+k].add(path)
} else {
// if 'a' has been collapsed, we want to remove 'a.b' and 'a[0]...' from the set
// of collapsed paths
for (let expandedPath of expandedPaths[id+"."+k]) {
if (expandedPath.startsWith(path+".") || expandedPath.startsWith(path+"[")) {
expandedPaths[id+"."+k].delete(expandedPath)
}
}
expandedPaths[id+"."+k].delete(path)
}
},
expandPaths: [ ...expandedPaths[id+"."+k] ].sort(),
expandLeafNodes: true
}
const propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container);
const obj = $(propRow.children()[0]);
obj.text(k);
var tools = $('<span class="button-group"></span>');
const urlSafeK = encodeURIComponent(k)
var refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
const refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
$.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
@ -229,16 +256,14 @@ RED.sidebar.context = (function() {
tools.detach();
$(propRow.children()[1]).empty();
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
...objectElementOptions,
typeHint: data.format,
sourceId: id+"."+k,
tools: tools,
path: k
}).appendTo(propRow.children()[1]);
}
})
});
RED.popover.tooltip(refreshItem,RED._("sidebar.context.refrsh"));
var deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) {
const deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
var popover = RED.popover.create({
@ -246,7 +271,7 @@ RED.sidebar.context = (function() {
target: propRow,
direction: "left",
content: function() {
var content = $('<div>');
const content = $('<div>');
$('<p data-i18n="sidebar.context.deleteConfirm"></p>').appendTo(content);
var row = $('<p>').appendTo(content);
var bg = $('<span class="button-group"></span>').appendTo(row);
@ -269,16 +294,15 @@ RED.sidebar.context = (function() {
if (container.children().length === 0) {
$('<tr class="red-ui-help-info-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.empty"></td></tr>').appendTo(container).i18n();
}
delete expandedPaths[id + "." + k]
} else {
payload = data.msg;
format = data.format;
tools.detach();
$(propRow.children()[1]).empty();
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: data.format,
sourceId: id+"."+k,
tools: tools,
path: k
...objectElementOptions,
typeHint: data.format
}).appendTo(propRow.children()[1]);
}
});
@ -293,14 +317,7 @@ RED.sidebar.context = (function() {
});
RED.popover.tooltip(deleteItem,RED._("sidebar.context.delete"));
var payload = v.msg;
var format = v.format;
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: v.format,
sourceId: id+"."+k,
tools: tools,
path: k
}).appendTo(propRow.children()[1]);
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), objectElementOptions).appendTo(propRow.children()[1]);
if (contextStores.length > 1) {
$("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))
}

View File

@ -230,7 +230,7 @@ RED.utils = (function() {
var pinnedPaths = {};
var formattedPaths = {};
function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) {
function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools,enablePinning) {
if (!pinnedPaths.hasOwnProperty(sourceId)) {
pinnedPaths[sourceId] = {}
}
@ -250,7 +250,7 @@ RED.utils = (function() {
RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue");
})
RED.popover.tooltip(copyPayload,RED._("node-red:debug.sidebar.copyPayload"));
if (strippedKey !== undefined && strippedKey !== '') {
if (enablePinning && strippedKey !== undefined && strippedKey !== '') {
var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey);
var pinPath = $('<button class="red-ui-button red-ui-button-small red-ui-debug-msg-tools-pin"><i class="fa fa-map-pin"></i></button>').appendTo(tools).on("click", function(e) {
@ -281,13 +281,16 @@ RED.utils = (function() {
}
}
}
function checkExpanded(strippedKey,expandPaths,minRange,maxRange) {
function checkExpanded(strippedKey, expandPaths, { minRange, maxRange, expandLeafNodes }) {
if (expandPaths && expandPaths.length > 0) {
if (strippedKey === '' && minRange === undefined) {
return true;
}
for (var i=0;i<expandPaths.length;i++) {
var p = expandPaths[i];
if (expandLeafNodes && p === strippedKey) {
return true
}
if (p.indexOf(strippedKey) === 0 && (p[strippedKey.length] === "." || p[strippedKey.length] === "[") ) {
if (minRange !== undefined && p[strippedKey.length] === "[") {
@ -394,6 +397,8 @@ RED.utils = (function() {
var sourceId = options.sourceId;
var rootPath = options.rootPath;
var expandPaths = options.expandPaths;
const enablePinning = options.enablePinning
const expandLeafNodes = options.expandLeafNodes;
var ontoggle = options.ontoggle;
var exposeApi = options.exposeApi;
var tools = options.tools;
@ -416,11 +421,11 @@ RED.utils = (function() {
}
header = $('<span class="red-ui-debug-msg-row"></span>').appendTo(element);
if (sourceId) {
addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools);
addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools, enablePinning);
}
if (!key) {
element.addClass("red-ui-debug-msg-top-level");
if (sourceId) {
if (sourceId && !expandPaths) {
var pinned = pinnedPaths[sourceId];
expandPaths = [];
if (pinned) {
@ -476,7 +481,7 @@ RED.utils = (function() {
$('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(typeHint||'string').appendTo(header);
var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element);
$('<pre class="red-ui-debug-msg-type-string"></pre>').text(obj).appendTo(row);
},function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
},function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
}
e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
if (/^#[0-9a-f]{6}$/i.test(obj)) {
@ -592,14 +597,16 @@ RED.utils = (function() {
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
sourceId,
rootPath,
expandPaths,
expandLeafNodes,
ontoggle,
exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
nodeSelector: options.nodeSelector,
enablePinning
}
).appendTo(row);
}
@ -623,21 +630,23 @@ RED.utils = (function() {
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
sourceId,
rootPath,
expandPaths,
expandLeafNodes,
ontoggle,
exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
nodeSelector: options.nodeSelector,
enablePinning
}
).appendTo(row);
}
}
})(),
(function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
checkExpanded(strippedKey,expandPaths,{ minRange, maxRange: Math.min(fullLength-1,(minRange+9)), expandLeafNodes}));
$('<span class="red-ui-debug-msg-object-key"></span>').html("["+minRange+" &hellip; "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
}
if (fullLength < originalLength) {
@ -646,7 +655,7 @@ RED.utils = (function() {
}
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
}
} else if (typeof obj === 'object') {
element.addClass('collapsed');
@ -680,14 +689,16 @@ RED.utils = (function() {
typeHint: false,
hideKey: false,
path: newPath,
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
sourceId,
rootPath,
expandPaths,
expandLeafNodes,
ontoggle,
exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
nodeSelector: options.nodeSelector,
enablePinning
}
).appendTo(row);
}
@ -696,7 +707,7 @@ RED.utils = (function() {
}
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
}
if (key) {
$('<span class="red-ui-debug-msg-type-meta"></span>').text(type).appendTo(entryObj);

View File

@ -1265,11 +1265,6 @@ RED.view = (function() {
var targetGroup = options.group;
var touchTrigger = options.touchTrigger;
if (targetGroup) {
selectedGroups.add(targetGroup,false);
RED.view.redraw();
}
// `point` is the place in the workspace the mouse has clicked.
// This takes into account scrolling and scaling of the workspace.
var ox = point[0];
@ -1591,9 +1586,6 @@ RED.view = (function() {
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
if (targetGroup) {
selectedGroups.add(targetGroup,false);
}
movingSet.add(nn);
updateActiveNodes();
updateSelection();

View File

@ -148,7 +148,7 @@ module.exports = function(RED) {
var st = (typeof output === 'string') ? output : util.inspect(output);
var fill = "grey";
var shape = "dot";
if (typeof output === 'object' && hasOwnProperty.call(output, "fill") && hasOwnProperty.call(output, "shape") && hasOwnProperty.call(output, "text")) {
if (typeof output === 'object' && output?.fill && output?.shape && output?.text) {
fill = output.fill;
shape = output.shape;
st = output.text;

View File

@ -511,9 +511,10 @@ RED.debug = (function() {
typeHint: format,
hideKey: false,
path: path,
sourceId: sourceNode&&sourceNode.id,
sourceId: sourceNode && sourceNode.id,
rootPath: path,
nodeSelector: config.messageSourceClick,
enablePinning: true
});
// Do this in a separate step so the element functions aren't stripped
debugMessage.appendTo(el);

View File

@ -352,7 +352,9 @@ module.exports = function(RED) {
if (msgs.length === 0) {
done()
} else {
drainMessageGroup(msgs,count,done);
setImmediate(() => {
drainMessageGroup(msgs,count,done);
})
}
}
})
@ -505,7 +507,9 @@ module.exports = function(RED) {
if (err) {
node.error(err,nextMsg);
}
processMessageQueue()
setImmediate(() => {
processMessageQueue()
})
});
}

View File

@ -719,6 +719,14 @@ class Flow {
});
}
getContext(scope) {
if (scope === 'flow') {
return this.context
} else if (scope === 'global') {
return context.get('global')
}
}
dump() {
console.log("==================")
console.log(this.TYPE, this.id);

View File

@ -49,6 +49,14 @@ class Group {
}
return this.parent.getSetting(key);
}
error(msg) {
this.parent.error(msg);
}
getContext(scope) {
return this.parent.getContext(scope);
}
}
module.exports = {

View File

@ -100,7 +100,24 @@ async function evaluateEnvProperties(flow, env, credentials) {
}
} else if (type ==='jsonata') {
pendingEvaluations.push(new Promise((resolve, _) => {
redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => {
redUtil.evaluateNodeProperty(value, 'jsonata',{
// Fake a node object to provide access to _flow and context
_flow: flow,
context: () => {
return {
flow: {
get: (value, store, callback) => {
return flow.getContext('flow').get(value, store, callback)
}
},
global: {
get: (value, store, callback) => {
return flow.getContext('global').get(value, store, callback)
}
}
}
}
}, null, (err, result) => {
if (!err) {
if (typeof result === 'object') {
result = { value: result, __clone__: true}