mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2feb290ae3 | ||
|
45bcb74dae | ||
|
6da890bf88 | ||
|
b8eeef182c | ||
|
54e0de64a7 | ||
|
3bd1b58217 | ||
|
71f8de94b0 | ||
|
4723378f2f | ||
|
8837597ff5 | ||
|
473b93f497 | ||
|
d9c5144fe2 | ||
|
cbc91a9ac8 | ||
|
88c946d401 | ||
|
ff565bacb4 | ||
|
e55301c073 | ||
|
dc69226944 | ||
|
bf6b18b8a6 | ||
|
e1b591d761 | ||
|
27463197cd | ||
|
854460db56 | ||
|
4984af48f1 | ||
|
e9dab46de8 | ||
|
4605f01c5d | ||
|
a0ddf96e03 | ||
|
16d25b9d41 | ||
|
77c4ccf8fb | ||
|
7d9e09f5a7 | ||
|
f4c184af4d | ||
|
9694c8bdfa | ||
|
ca61efc986 | ||
|
ffdbd94927 | ||
|
43df2318d4 | ||
|
21612a5215 | ||
|
756485e308 | ||
|
efbe38f509 | ||
|
daa76e6e5f | ||
|
48d2d269a5 | ||
|
13cac1b5ef | ||
|
479b7e756d | ||
|
503ef62cf5 | ||
|
b355a37378 | ||
|
1acc16c9ef | ||
|
4cbf672b26 | ||
|
272355a48e | ||
|
e2981f2970 | ||
|
804551000a | ||
|
254b6a1e23 | ||
|
3838e4e605 | ||
|
953b7584a3 | ||
|
3da22882e9 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ docs
|
||||
.nyc_output
|
||||
sync.ffs_db
|
||||
package-lock.json
|
||||
.editorconfig
|
||||
|
@ -1,4 +0,0 @@
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
*.backup
|
||||
/public/*
|
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,4 +1,30 @@
|
||||
#### 4.0.7: Maintenance Release
|
||||
#### 4.0.9: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Add details for the dynamic subscription to match the English docs (#5050) @aikitori
|
||||
- Fix tooltip snapping based on `typedInput` type (#5051) @GogoVega
|
||||
- Prevent symbol usage warning in monaco (#5049) @Steve-Mcl
|
||||
- Show subflow flow context under node section of sidebar (#5025) @knolleary
|
||||
- feat: Add custom label for default deploy button in settings.editorTheme (#5030) @matiseni51
|
||||
- Handle long auto-complete suggests (#5042) @knolleary
|
||||
- Handle undefined username when generating user icon (#5043) @knolleary
|
||||
- Handle dragging node into group and splicing link at same time (#5027) @knolleary
|
||||
- Remember context sidebar tree state when refreshing (#5021) @knolleary
|
||||
- Update sf instance env vars when removed from template (#5023) @knolleary
|
||||
- Do not select group when triggering quick-add within it (#5022) @knolleary
|
||||
- Fix library icon handling within library browser component (#5017) @knolleary
|
||||
|
||||
Runtime
|
||||
- Allow env var access to context (#5016) @knolleary
|
||||
- fix debug status reporting if null (#5018) @dceejay
|
||||
- Fix grunt dev via better ndoemon ignore rules (#5015) @knolleary
|
||||
- Fix typo in CHANGELOG (4.0.7-->4.0.8) (#5007) @natcl
|
||||
|
||||
Nodes
|
||||
- Switch: Avoid exceeding call stack when draining message group in Switch (#5014) @knolleary
|
||||
|
||||
#### 4.0.8: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
|
16
nodemon.json
Normal file
16
nodemon.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"ignoreRoot": [
|
||||
".git",
|
||||
".nyc_output",
|
||||
".sass-cache",
|
||||
"bower-components",
|
||||
"coverage"
|
||||
],
|
||||
"ignore": [
|
||||
"/Gruntfile.js",
|
||||
"/.git/*",
|
||||
"*.backup",
|
||||
"/public/*"
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -185,13 +185,12 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (theme.deployButton) {
|
||||
if (theme.deployButton.type == "simple") {
|
||||
themeSettings.deployButton = {
|
||||
type: "simple"
|
||||
}
|
||||
themeSettings.deployButton = {};
|
||||
if (theme.deployButton.label) {
|
||||
themeSettings.deployButton.label = theme.deployButton.label;
|
||||
}
|
||||
if (theme.deployButton.type == "simple") {
|
||||
themeSettings.deployButton.type = theme.deployButton.type;
|
||||
if (theme.deployButton.icon) {
|
||||
url = serveFile(themeApp,"/deploy/",theme.deployButton.icon);
|
||||
if (url) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "4.0.8",
|
||||
"@node-red/editor-client": "4.0.8",
|
||||
"@node-red/util": "4.0.9",
|
||||
"@node-red/editor-client": "4.0.9",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.20.3",
|
||||
"clone": "2.1.2",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -398,14 +398,13 @@ RED.multiplayer = (function () {
|
||||
anonIconBody.setAttribute("d",`M ${radius/2} ${radius/2 + 5} h -2.5 c -2 1 -2 -5 0.5 -4.5 c 2 1 2 1 4 0 c 2.5 -0.5 2.5 5.5 0 4.5 z`);
|
||||
group.appendChild(anonIconBody)
|
||||
} else {
|
||||
const labelText = user.username ? user.username.substring(0,2) : user
|
||||
const label = document.createElementNS("http://www.w3.org/2000/svg","text");
|
||||
if (user.username) {
|
||||
if (user.username || user.email) {
|
||||
label.setAttribute("class","red-ui-multiplayer-annotation-label");
|
||||
label.textContent = user.username.substring(0,2)
|
||||
label.textContent = (user.username || user.email).substring(0,2)
|
||||
} else {
|
||||
label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count")
|
||||
label.textContent = user
|
||||
label.textContent = 'nr'
|
||||
}
|
||||
label.setAttribute("text-anchor", "middle")
|
||||
label.setAttribute("x",radius/2);
|
||||
|
@ -61,7 +61,7 @@
|
||||
}
|
||||
this.menu = RED.popover.menu({
|
||||
tabSelect: true,
|
||||
width: 300,
|
||||
width: Math.max(300, this.element.width()),
|
||||
maxHeight: 200,
|
||||
class: "red-ui-autoComplete-container",
|
||||
options: completions,
|
||||
|
@ -63,6 +63,7 @@
|
||||
pre: value.substring(0,idx),
|
||||
match: value.substring(idx,idx+len),
|
||||
post: value.substring(idx+len),
|
||||
exact: idx === 0 && value.length === searchValue.length
|
||||
}
|
||||
}
|
||||
function generateSpans(match) {
|
||||
@ -83,7 +84,7 @@
|
||||
const srcMatch = getMatch(optSrc, val);
|
||||
if (valMatch.found || srcMatch.found) {
|
||||
const element = $('<div>',{style: "display: flex"});
|
||||
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
||||
const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
|
||||
valEl.append(generateSpans(valMatch));
|
||||
valEl.appendTo(element);
|
||||
if (optSrc) {
|
||||
@ -159,7 +160,7 @@
|
||||
if (valMatch.found) {
|
||||
const optSrc = envVarsMap[v]
|
||||
const element = $('<div>',{style: "display: flex"});
|
||||
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
||||
const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
|
||||
valEl.append(generateSpans(valMatch))
|
||||
valEl.appendTo(element)
|
||||
|
||||
@ -201,7 +202,7 @@
|
||||
const that = this
|
||||
const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
|
||||
contextKnownKeys[scope] = contextKnownKeys[scope] || {}
|
||||
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
|
||||
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Map()
|
||||
if (searchKey.length > 0) {
|
||||
try {
|
||||
RED.utils.normalisePropertyExpression(searchKey)
|
||||
@ -223,11 +224,12 @@
|
||||
const result = data[store] || {}
|
||||
const keys = result.keys || []
|
||||
const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
|
||||
keys.forEach(key => {
|
||||
keys.forEach(keyInfo => {
|
||||
const key = keyInfo.key
|
||||
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
|
||||
contextKnownKeys[scope][store].add(keyPrefix + key)
|
||||
contextKnownKeys[scope][store].set(keyPrefix + key, keyInfo)
|
||||
} else {
|
||||
contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
|
||||
contextKnownKeys[scope][store].set(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]", keyInfo)
|
||||
}
|
||||
})
|
||||
done()
|
||||
@ -242,14 +244,14 @@
|
||||
// Get the flow id of the node we're editing
|
||||
const editStack = RED.editor.getEditStack()
|
||||
if (editStack.length === 0) {
|
||||
done([])
|
||||
done(new Map())
|
||||
return
|
||||
}
|
||||
const editingNode = editStack.pop()
|
||||
if (editingNode.z) {
|
||||
scope = `${scope}/${editingNode.z}`
|
||||
} else {
|
||||
done([])
|
||||
done(new Map())
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -269,17 +271,29 @@
|
||||
return function(val, done) {
|
||||
getContextKeys(val, function (keys) {
|
||||
const matches = []
|
||||
keys.forEach(v => {
|
||||
keys.forEach((keyInfo, v) => {
|
||||
let optVal = v
|
||||
let valMatch = getMatch(optVal, val);
|
||||
if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
|
||||
if (!valMatch.found && val.length > 0) {
|
||||
if (val.endsWith('.')) {
|
||||
// Search key ends in '.' - but doesn't match. Check again
|
||||
// with [" at the end instead so we match bracket notation
|
||||
valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
|
||||
// } else if (val.endsWith('[') && /^array/.test(keyInfo.format)) {
|
||||
// console.log('this case')
|
||||
}
|
||||
}
|
||||
if (valMatch.found) {
|
||||
const element = $('<div>',{style: "display: flex"});
|
||||
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
||||
const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
|
||||
// if (keyInfo.format) {
|
||||
// valMatch.post += ' ' + keyInfo.format
|
||||
// }
|
||||
if (valMatch.exact && /^array/.test(keyInfo.format)) {
|
||||
valMatch.post += `[0-${keyInfo.length}]`
|
||||
optVal += '['
|
||||
|
||||
}
|
||||
valEl.append(generateSpans(valMatch))
|
||||
valEl.appendTo(element)
|
||||
matches.push({
|
||||
@ -1567,7 +1581,8 @@
|
||||
if (tooltip) {
|
||||
tooltip.setContent(valid);
|
||||
} else {
|
||||
tooltip = RED.popover.tooltip(this.elementDiv, valid);
|
||||
const target = this.typeMap[type]?.options ? this.optionSelectLabel : this.elementDiv;
|
||||
tooltip = RED.popover.tooltip(target, valid);
|
||||
this.element.data("tooltip", tooltip);
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ RED.deploy = (function() {
|
||||
/**
|
||||
* options:
|
||||
* type: "default" - Button with drop-down options - no further customisation available
|
||||
* label: the text to display - default: "Deploy"
|
||||
* type: "simple" - Button without dropdown. Customisations:
|
||||
* label: the text to display - default: "Deploy"
|
||||
* icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.svg"
|
||||
@ -51,13 +52,14 @@ RED.deploy = (function() {
|
||||
function init(options) {
|
||||
options = options || {};
|
||||
var type = options.type || "default";
|
||||
var label = options.label || RED._("deploy.deploy");
|
||||
|
||||
if (type == "default") {
|
||||
$('<li><span class="red-ui-deploy-button-group button-group">'+
|
||||
'<a id="red-ui-header-button-deploy" class="red-ui-deploy-button disabled" href="#">'+
|
||||
'<span class="red-ui-deploy-button-content">'+
|
||||
'<img id="red-ui-header-button-deploy-icon" src="red/images/deploy-full-o.svg"> '+
|
||||
'<span>'+RED._("deploy.deploy")+'</span>'+
|
||||
'<span>'+label+'</span>'+
|
||||
'</span>'+
|
||||
'<span class="red-ui-deploy-button-spinner hide">'+
|
||||
'<img src="red/images/spin.svg"/>'+
|
||||
@ -78,7 +80,6 @@ RED.deploy = (function() {
|
||||
mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"})
|
||||
RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems });
|
||||
} else if (type == "simple") {
|
||||
var label = options.label || RED._("deploy.deploy");
|
||||
var icon = 'red/images/deploy-full-o.svg';
|
||||
if (options.hasOwnProperty('icon')) {
|
||||
icon = options.icon;
|
||||
@ -424,11 +425,15 @@ RED.deploy = (function() {
|
||||
const unknownNodes = [];
|
||||
const invalidNodes = [];
|
||||
|
||||
const isDisabled = function (node) {
|
||||
return (node.d || RED.nodes.workspace(node.z)?.disabled);
|
||||
};
|
||||
|
||||
RED.nodes.eachConfig(function (node) {
|
||||
if (node.valid === undefined) {
|
||||
RED.editor.validateNode(node);
|
||||
}
|
||||
if (!node.valid && !node.d) {
|
||||
if (!node.valid && !isDisabled(node)) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
@ -438,7 +443,7 @@ RED.deploy = (function() {
|
||||
}
|
||||
});
|
||||
RED.nodes.eachNode(function (node) {
|
||||
if (!node.valid && !node.d) {
|
||||
if (!node.valid && !isDisabled(node)) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
@ -452,7 +457,7 @@ RED.deploy = (function() {
|
||||
|
||||
const unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function (node) {
|
||||
if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
|
||||
if ((node._def.hasUsers !== false) && (node.users.length === 0) && !isDisabled(node)) {
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
|
@ -1836,8 +1836,18 @@ RED.editor = (function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let envToRemove = new Set()
|
||||
if (!isSameObj(old_env, new_env)) {
|
||||
// Get a list of env properties that have been removed
|
||||
// by comparing old_env and new_env
|
||||
if (old_env) {
|
||||
old_env.forEach(env => { envToRemove.add(env.name) })
|
||||
}
|
||||
if (new_env) {
|
||||
new_env.forEach(env => {
|
||||
envToRemove.delete(env.name)
|
||||
})
|
||||
}
|
||||
editState.changes.env = editing_node.env;
|
||||
editing_node.env = new_env;
|
||||
editState.changed = true;
|
||||
@ -1846,10 +1856,11 @@ RED.editor = (function() {
|
||||
|
||||
|
||||
if (editState.changed) {
|
||||
var wasChanged = editing_node.changed;
|
||||
let wasChanged = editing_node.changed;
|
||||
editing_node.changed = true;
|
||||
validateNode(editing_node);
|
||||
var subflowInstances = [];
|
||||
let subflowInstances = [];
|
||||
let instanceHistoryEvents = []
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+editing_node.id) {
|
||||
subflowInstances.push({
|
||||
@ -1859,13 +1870,35 @@ RED.editor = (function() {
|
||||
n._def.color = editing_node.color;
|
||||
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);
|
||||
}
|
||||
});
|
||||
RED.events.emit("subflows:change",editing_node);
|
||||
RED.nodes.dirty(true);
|
||||
var historyEvent = {
|
||||
let historyEvent = {
|
||||
t:'edit',
|
||||
node:editing_node,
|
||||
changes:editState.changes,
|
||||
@ -1875,7 +1908,13 @@ RED.editor = (function() {
|
||||
instances:subflowInstances
|
||||
}
|
||||
};
|
||||
|
||||
if (instanceHistoryEvents.length > 0) {
|
||||
historyEvent = {
|
||||
t: 'multi',
|
||||
events: [ historyEvent, ...instanceHistoryEvents ],
|
||||
dirty: wasDirty
|
||||
}
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
editing_node.dirty = true;
|
||||
|
@ -691,6 +691,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
2322, //Type 'unknown' is not assignable to type 'string'
|
||||
2339, //property does not exist on
|
||||
2345, //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions'
|
||||
2538, //Ignore symbols as index property error.
|
||||
7043, //i forget what this one is,
|
||||
80001, //Convert to ES6 module
|
||||
80004, //JSDoc types may be moved to TypeScript types.
|
||||
|
@ -27,6 +27,12 @@
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function file2Text(file,cb) {
|
||||
file.arrayBuffer().then(d => {
|
||||
cb( new TextDecoder().decode(d) )
|
||||
}).catch(ex => { cb(`error: ${ex}`) })
|
||||
}
|
||||
|
||||
var initialized = false;
|
||||
var currentEditor = null;
|
||||
/**
|
||||
@ -52,6 +58,7 @@
|
||||
if (files.length === 1) {
|
||||
var file = files[0];
|
||||
var name = file.name.toLowerCase();
|
||||
var fileType = file.type.toLowerCase();
|
||||
|
||||
if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) {
|
||||
file2base64Image(file, function (image) {
|
||||
@ -63,6 +70,29 @@
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ( fileType.startsWith("text/") ) {
|
||||
file2Text(file, function (txt) {
|
||||
var session = currentEditor.getSession();
|
||||
var pos = session.getCursorPosition();
|
||||
session.insert(pos, txt);
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
} else if ($.inArray("text/plain", ev.originalEvent.dataTransfer.types) != -1) {
|
||||
let item = Object.values(ev.originalEvent.dataTransfer.items).filter(d => d.type == "text/plain")[0]
|
||||
|
||||
if (item) {
|
||||
item.getAsString(txt => {
|
||||
var session = currentEditor.getSession();
|
||||
var pos = session.getCursorPosition();
|
||||
session.insert(pos, txt);
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
|
@ -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,
|
||||
|
@ -56,7 +56,16 @@ RED.sidebar.config = (function() {
|
||||
} else {
|
||||
$('<span class="red-ui-palette-node-config-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
|
||||
}
|
||||
|
||||
$('<span class="red-ui-sidebar-node-config-filter-info"></span>').appendTo(header);
|
||||
|
||||
const changeBadgeContainer = $('<svg class="red-ui-sidebar-config-category-changed red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo(header);
|
||||
const changeBadge = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
changeBadge.setAttribute("cx", "5");
|
||||
changeBadge.setAttribute("cy", "5");
|
||||
changeBadge.setAttribute("r", "5");
|
||||
changeBadgeContainer.append(changeBadge);
|
||||
|
||||
category = $('<ul class="red-ui-palette-content red-ui-sidebar-node-config-list"></ul>').appendTo(container);
|
||||
category.on("click", function(e) {
|
||||
$(content).find(".red-ui-palette-node").removeClass("selected");
|
||||
@ -150,9 +159,6 @@ RED.sidebar.config = (function() {
|
||||
$('<li class="red-ui-palette-node-config-type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
}
|
||||
if (node.changed) {
|
||||
labelText += "!!"
|
||||
}
|
||||
var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
|
||||
var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
|
||||
entry.data('node',node.id);
|
||||
@ -181,15 +187,29 @@ RED.sidebar.config = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
if (node.changed) {
|
||||
const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo(nodeDiv);
|
||||
const changeBadge = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||||
changeBadge.setAttribute("cx", "5");
|
||||
changeBadge.setAttribute("cy", "5");
|
||||
changeBadge.setAttribute("r", "5");
|
||||
nodeDivAnnotations.append($(changeBadge));
|
||||
|
||||
const categoryHeader = list.parent().find(".red-ui-sidebar-config-tray-header.red-ui-palette-header");
|
||||
categoryHeader.addClass("red-ui-sidebar-config-changed");
|
||||
nodeDiv.addClass("red-ui-palette-node-config-changed");
|
||||
}
|
||||
|
||||
if (!node.valid) {
|
||||
nodeDiv.addClass("red-ui-palette-node-config-invalid")
|
||||
const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv)
|
||||
const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv);
|
||||
const errorBadge = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
errorBadge.setAttribute("d", "M 0,9 l 10,0 -5,-8 z");
|
||||
nodeDivAnnotations.append($(errorBadge))
|
||||
nodeDivAnnotations.append($(errorBadge));
|
||||
|
||||
nodeDiv.addClass("red-ui-palette-node-config-invalid");
|
||||
RED.popover.tooltip(nodeDivAnnotations, function () {
|
||||
if (node.validationErrors && node.validationErrors.length > 0) {
|
||||
return RED._("editor.errors.invalidProperties")+"<br> - "+node.validationErrors.join("<br> - ")
|
||||
return RED._("editor.errors.invalidProperties") + "<br> - " + node.validationErrors.join("<br> - ");
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -252,6 +272,10 @@ RED.sidebar.config = (function() {
|
||||
$(this).remove();
|
||||
delete categories[id];
|
||||
}
|
||||
|
||||
// Remove the `changed` badge from the category header
|
||||
const categoryHeader = $(this).find(".red-ui-sidebar-config-tray-header.red-ui-palette-header");
|
||||
categoryHeader.removeClass("red-ui-sidebar-config-changed");
|
||||
})
|
||||
var globalConfigNodes = [];
|
||||
var configList = {};
|
||||
|
@ -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]))
|
||||
}
|
||||
|
@ -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+" … "+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);
|
||||
|
@ -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();
|
||||
@ -2178,17 +2170,22 @@ RED.view = (function() {
|
||||
n.n.moved = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if we need to splice a link
|
||||
// If a node has moved and ends up being spliced into a link, keep
|
||||
// track of which historyEvent to add the splice info to
|
||||
let targetSpliceEvent = null
|
||||
if (moveEvent.nodes.length > 0) {
|
||||
historyEvent.events.push(moveEvent)
|
||||
if (activeSpliceLink) {
|
||||
var linkToSplice = d3.select(activeSpliceLink).data()[0];
|
||||
spliceLink(linkToSplice, movingSet.get(0).n, moveEvent)
|
||||
}
|
||||
targetSpliceEvent = moveEvent
|
||||
}
|
||||
if (moveAndChangedGroupEvent.nodes.length > 0) {
|
||||
historyEvent.events.push(moveAndChangedGroupEvent)
|
||||
targetSpliceEvent = moveAndChangedGroupEvent
|
||||
}
|
||||
// activeSpliceLink will only be set if the movingSet has a single
|
||||
// node that is able to splice.
|
||||
if (targetSpliceEvent && activeSpliceLink) {
|
||||
var linkToSplice = d3.select(activeSpliceLink).data()[0];
|
||||
spliceLink(linkToSplice, movingSet.get(0).n, targetSpliceEvent)
|
||||
}
|
||||
|
||||
// Only continue if something has moved
|
||||
|
@ -351,10 +351,10 @@ RED.user = (function() {
|
||||
userIcon.css({
|
||||
backgroundImage: "url("+user.image+")",
|
||||
})
|
||||
} else if (user.anonymous) {
|
||||
} else if (user.anonymous || (!user.username && !user.email)) {
|
||||
$('<i class="fa fa-user"></i>').appendTo(userIcon);
|
||||
} else {
|
||||
$('<span>').text(user.username.substring(0,2)).appendTo(userIcon);
|
||||
$('<span>').text((user.username || user.email).substring(0,2)).appendTo(userIcon);
|
||||
}
|
||||
if (user.profileColor !== undefined) {
|
||||
userIcon.addClass('red-ui-user-profile-color-' + user.profileColor)
|
||||
|
@ -84,6 +84,11 @@ ul.red-ui-sidebar-node-config-list {
|
||||
background: var(--red-ui-node-config-background);
|
||||
color: var(--red-ui-primary-text-color);
|
||||
cursor: pointer;
|
||||
&.red-ui-palette-node-config-invalid.red-ui-palette-node-config-changed {
|
||||
.red-ui-palette-node-annotations.red-ui-flow-node-error {
|
||||
left: calc(100% - 28px);
|
||||
}
|
||||
}
|
||||
}
|
||||
ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type {
|
||||
color: var(--red-ui-secondary-text-color);
|
||||
@ -115,6 +120,15 @@ ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type {
|
||||
.red-ui-palette-node-config-invalid {
|
||||
border-color: var(--red-ui-form-input-border-error-color)
|
||||
}
|
||||
.red-ui-sidebar-config-tray-header.red-ui-palette-header:not(.red-ui-sidebar-config-changed) .red-ui-flow-node-changed {
|
||||
display: none;
|
||||
}
|
||||
.red-ui-sidebar-config-tray-header.red-ui-palette-header.red-ui-sidebar-config-changed .red-ui-flow-node-changed {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
}
|
||||
.red-ui-palette-node-annotations {
|
||||
position: absolute;
|
||||
left: calc(100% - 15px);
|
||||
|
@ -2,4 +2,15 @@
|
||||
&.red-ui-popover-panel {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.red-ui-autoComplete-completion {
|
||||
font-family: var(--red-ui-monospace-font);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
text-overflow: ellipsis;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -514,6 +514,7 @@ RED.debug = (function() {
|
||||
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);
|
||||
|
@ -352,7 +352,9 @@ module.exports = function(RED) {
|
||||
if (msgs.length === 0) {
|
||||
done()
|
||||
} else {
|
||||
setImmediate(() => {
|
||||
drainMessageGroup(msgs,count,done);
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -505,7 +507,9 @@ module.exports = function(RED) {
|
||||
if (err) {
|
||||
node.error(err,nextMsg);
|
||||
}
|
||||
setImmediate(() => {
|
||||
processMessageQueue()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,35 @@
|
||||
</dl>
|
||||
<h3>Details</h3>
|
||||
<p>Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).</p>
|
||||
<p>Dieser Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann.
|
||||
Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.</p>
|
||||
<p>Diese Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.</p>
|
||||
<p>Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Broker-Verbindung nutzen.</p>
|
||||
<h4>Dynamische Steuerung</h4>
|
||||
Die von der Node genutzte Verbindung kann dynamisch gesteuert werden, wenn die MQTT-Node eine der folgenden Nachrichten erhält. Die Payload dieser Nachrichten werden nicht veröffentlicht.
|
||||
<h4>Eingangsdaten</h4>
|
||||
<p>Nur Verfügbar, wenn die Node für dynamische Abonnements konfiguriert wurde.</p>
|
||||
<dl class="message-properties">
|
||||
<dt>action <span class="property-type">string</span></dt>
|
||||
<dd>Der Name der Aktion, die die MQTT-Node ausführen soll. Verfügbare Aktionen sind: <code>"connect"</code>, <code>"disconnect"</code>, <code>"getSubscriptions"</code>, <code>"subscribe"</code> und <code>"unsubscribe"</code>.</dd>
|
||||
<dt class="optional">topic <span class="property-type">string|object|array</span></dt>
|
||||
<dd>Bei den Aktionen <code>"subscribe"</code> und <code>"unsubscribe"</code> gibt diese Eigenschaft die MQTT-Topic an. Dabei kann es sich um Folgendes handeln:
|
||||
<ul>
|
||||
<li>eine Zeichenfolge, die den Topic-Filter enthält</li>
|
||||
<li>ein Objekt mit den Eigenschaften <code>topic</code> und <code>qos</code></li>
|
||||
<li>ein Array aus Zeichenfolgen oder Objekten, um mehrere Topics gleichzeitig zu verwalten</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt class="optional">broker <span class="property-type">broker</span> </dt>
|
||||
<dd>Für die Aktion <code>"connect"</code> kann diese Eigenschaft jede der einzelnen Broker-Konfigurationseinstellungen überschreiben, einschließlich: <ul>
|
||||
<li><code>broker</code></li>
|
||||
<li><code>port</code></li>
|
||||
<li><code>url</code> - überschreibt Broker/Port, um eine vollständige Verbindungs-URL bereitzustellen</li>
|
||||
<li><code>username</code></li>
|
||||
<li><code>password</code></li>
|
||||
</ul>
|
||||
<p>Wenn diese Eigenschaft gesetzt ist und der Broker bereits verbunden ist, wird ein Fehler protokolliert, es sei denn, die Eigenschaft <code>force</code> gesetzt - in diesem Fall wird die Verbindung zum Broker getrennt, die neuen Einstellungen angewendet und erneut verbunden.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="mqtt out">
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@ -16,7 +16,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "4.0.8",
|
||||
"@node-red/util": "4.0.9",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"semver": "7.6.3",
|
||||
|
@ -96,21 +96,37 @@ var api = module.exports = {
|
||||
} else if (scope === 'node') {
|
||||
var node = runtime.nodes.getNode(id);
|
||||
if (node) {
|
||||
if (/^subflow:/.test(node.type)) {
|
||||
ctx = runtime.nodes.getContext(node.id);
|
||||
} else {
|
||||
ctx = node.context();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx) {
|
||||
if (key) {
|
||||
store = store || availableStores.default;
|
||||
ctx.get(key,store,function(err, v) {
|
||||
if (opts.keysOnly) {
|
||||
const result = {}
|
||||
if (Array.isArray(v)) {
|
||||
resolve({ [store]: { format: `array[${v.length}]`}})
|
||||
result.format = `array[${v.length}]`
|
||||
} else if (typeof v === 'object') {
|
||||
resolve({ [store]: { keys: Object.keys(v), format: 'Object' } })
|
||||
result.keys = Object.keys(v).map(k => {
|
||||
if (Array.isArray(v[k])) {
|
||||
return { key: k, format: `array[${v[k].length}]`, length: v[k].length }
|
||||
} else if (typeof v[k] === 'object') {
|
||||
return { key: k, format: 'object' }
|
||||
} else {
|
||||
resolve({ [store]: { keys: [] }})
|
||||
return { key: k }
|
||||
}
|
||||
})
|
||||
result.format = 'object'
|
||||
} else {
|
||||
result.keys = []
|
||||
}
|
||||
resolve({ [store]: result })
|
||||
return
|
||||
}
|
||||
var encoded = util.encodeObject({msg:v});
|
||||
if (store !== availableStores.default) {
|
||||
@ -147,7 +163,7 @@ var api = module.exports = {
|
||||
}
|
||||
return
|
||||
}
|
||||
result[store] = { keys }
|
||||
result[store] = { keys: keys.map(key => { return { key }}) }
|
||||
c--;
|
||||
if (c === 0) {
|
||||
if (!errorReported) {
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "4.0.8",
|
||||
"@node-red/util": "4.0.8",
|
||||
"@node-red/registry": "4.0.9",
|
||||
"@node-red/util": "4.0.9",
|
||||
"async-mutex": "0.5.0",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.21.2",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "4.0.8",
|
||||
"version": "4.0.9",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@ -31,10 +31,10 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "4.0.8",
|
||||
"@node-red/runtime": "4.0.8",
|
||||
"@node-red/util": "4.0.8",
|
||||
"@node-red/nodes": "4.0.8",
|
||||
"@node-red/editor-api": "4.0.9",
|
||||
"@node-red/runtime": "4.0.9",
|
||||
"@node-red/util": "4.0.9",
|
||||
"@node-red/nodes": "4.0.9",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"cors": "2.8.5",
|
||||
|
Loading…
x
Reference in New Issue
Block a user