1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge branch 'dev' into dev-addjpn

This commit is contained in:
Kazuhito Yokoi 2021-10-19 19:20:32 +09:00 committed by GitHub
commit b55a8ef62a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1546 additions and 892 deletions

View File

@ -1,3 +1,33 @@
#### 2.1.0-beta.2: Beta Release
Editor
- Fix switching projects (#3199) @knolleary
- Use locale setting when installing/enabling node (#3198) @knolleary
- Do not show projects-wecome dialog until welcome tour completes (#3197) @knolleary
- Fix converting selection to subflow (#3196) @knolleary
- Avoid conflicts with native browser cmd-ctrl type shortcuts (#3195) @knolleary
- Ensure message tools stay attached to top-level entry in Debug/Context (#3186) @knolleary
- Ensure tab state updates properly when toggling enable state (#3175) @knolleary
- Improve handling of long labels in TreeList (#3176) @knolleary
- Shift-click tab scroll arrows to jump to start/end (#3177) @knolleary
Runtime
- Update package dependencies
- Update to latest node-red-admin
Nodes
- Dynamic MQTT connections (#3189)
- Link: Filter out Link Out Return nodes in Link In edit dialog Fixes #3187
- Link: Fix link call label (#3200) @knolleary
- Debug: Redesign debug filter options and make them persistant (#3183) @knolleary
- Inject: Widen Inject interval box for >1 digit (#3184) @knolleary
- Switch: Fix rule focus when switch 'otherwise' rule is used (#3185) @knolleary
#### 2.1.0-beta.1: Beta Release #### 2.1.0-beta.1: Beta Release
Editor Editor

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red", "name": "node-red",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"description": "Low-code programming for event-driven applications", "description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org", "homepage": "http://nodered.org",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -50,7 +50,7 @@
"hash-sum": "2.0.0", "hash-sum": "2.0.0",
"hpagent": "0.1.2", "hpagent": "0.1.2",
"https-proxy-agent": "5.0.0", "https-proxy-agent": "5.0.0",
"i18next": "21.2.4", "i18next": "21.3.1",
"iconv-lite": "0.6.3", "iconv-lite": "0.6.3",
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "3.14.1", "js-yaml": "3.14.1",
@ -64,7 +64,7 @@
"mqtt": "4.2.8", "mqtt": "4.2.8",
"multer": "1.4.3", "multer": "1.4.3",
"mustache": "4.2.0", "mustache": "4.2.0",
"node-red-admin": "^2.2.0", "node-red-admin": "^2.2.1",
"nopt": "5.0.0", "nopt": "5.0.0",
"oauth2orize": "1.11.0", "oauth2orize": "1.11.0",
"on-headers": "1.0.2", "on-headers": "1.0.2",
@ -107,13 +107,13 @@
"i18next-http-backend": "1.3.1", "i18next-http-backend": "1.3.1",
"jquery-i18next": "1.2.1", "jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "3.0.4", "marked": "3.0.7",
"minami": "1.2.3", "minami": "1.2.3",
"mocha": "9.1.2", "mocha": "9.1.2",
"node-red-node-test-helper": "^0.2.7", "node-red-node-test-helper": "^0.2.7",
"nodemon": "2.0.13", "nodemon": "2.0.13",
"proxy": "^1.0.2", "proxy": "^1.0.2",
"sass": "1.42.1", "sass": "1.43.2",
"should": "13.2.3", "should": "13.2.3",
"sinon": "11.1.2", "sinon": "11.1.2",
"stoppable": "^1.1.0", "stoppable": "^1.1.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/editor-api", "name": "@node-red/editor-api",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,8 +16,8 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/util": "2.1.0-beta.1", "@node-red/util": "2.1.0-beta.2",
"@node-red/editor-client": "2.1.0-beta.1", "@node-red/editor-client": "2.1.0-beta.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.19.0", "body-parser": "1.19.0",
"clone": "2.1.2", "clone": "2.1.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/editor-client", "name": "@node-red/editor-client",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -266,8 +266,8 @@ RED.nodes = (function() {
}, },
moveNode: function(n, newZ) { moveNode: function(n, newZ) {
api.removeNode(n); api.removeNode(n);
tabMap[newZ] = tabMap[newZ] || []; n.z = newZ;
tabMap[newZ].push(n); api.addNode(n)
}, },
moveNodesForwards: function(nodes) { moveNodesForwards: function(nodes) {
var result = []; var result = [];
@ -719,29 +719,29 @@ RED.nodes = (function() {
moveGroupToTab(node,z); moveGroupToTab(node,z);
return; return;
} }
var oldZ = node.z;
allNodes.moveNode(node,z); allNodes.moveNode(node,z);
var nl = nodeLinks[node.id]; var nl = nodeLinks[node.id];
if (nl) { if (nl) {
nl.in.forEach(function(l) { nl.in.forEach(function(l) {
var idx = linkTabMap[node.z].indexOf(l); var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) { if (idx != -1) {
linkTabMap[node.z].splice(idx, 1); linkTabMap[oldZ].splice(idx, 1);
} }
if ((l.source.z === z) && linkTabMap[z]) { if ((l.source.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l); linkTabMap[z].push(l);
} }
}); });
nl.out.forEach(function(l) { nl.out.forEach(function(l) {
var idx = linkTabMap[node.z].indexOf(l); var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) { if (idx != -1) {
linkTabMap[node.z].splice(idx, 1); linkTabMap[oldZ].splice(idx, 1);
} }
if ((l.target.z === z) && linkTabMap[z]) { if ((l.target.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l); linkTabMap[z].push(l);
} }
}); });
} }
node.z = z;
RED.events.emit("nodes:change",node); RED.events.emit("nodes:change",node);
} }
function moveGroupToTab(group, z) { function moveGroupToTab(group, z) {
@ -2384,7 +2384,6 @@ RED.nodes = (function() {
} }
function clear() { function clear() {
allNodes.clear();
links = []; links = [];
linkTabMap = {}; linkTabMap = {};
nodeLinks = {}; nodeLinks = {};
@ -2405,6 +2404,8 @@ RED.nodes = (function() {
initialLoad = null; initialLoad = null;
workspaces = {}; workspaces = {};
allNodes.clear();
RED.nodes.dirty(false); RED.nodes.dirty(false);
RED.view.redraw(true, true); RED.view.redraw(true, true);
RED.palette.refresh(); RED.palette.refresh();

View File

@ -201,6 +201,7 @@ var RED = (function() {
RED.projects.refresh(function(activeProject) { RED.projects.refresh(function(activeProject) {
loadFlows(function() { loadFlows(function() {
RED.sidebar.info.refresh() RED.sidebar.info.refresh()
var showProjectWelcome = false;
if (!activeProject) { if (!activeProject) {
// Projects enabled but no active project // Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true); RED.menu.setDisabled('menu-item-projects-open',true);
@ -208,10 +209,10 @@ var RED = (function() {
if (activeProject === false) { if (activeProject === false) {
// User previously decline the migration to projects. // User previously decline the migration to projects.
} else { // null/undefined } else { // null/undefined
RED.projects.showStartup(); showProjectWelcome = true;
} }
} }
completeLoad(); completeLoad(showProjectWelcome);
}); });
}); });
} else { } else {
@ -251,6 +252,9 @@ var RED = (function() {
if (/^#flow\/.+$/.test(currentHash)) { if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6),true); RED.workspaces.show(currentHash.substring(6),true);
} }
if (RED.workspaces.active() === 0 && RED.workspaces.count() > 0) {
RED.workspaces.show(RED.nodes.getWorkspaceOrder()[0])
}
} catch(err) { } catch(err) {
console.warn(err); console.warn(err);
RED.notify( RED.notify(
@ -267,7 +271,7 @@ var RED = (function() {
}); });
} }
function completeLoad() { function completeLoad(showProjectWelcome) {
var persistentNotifications = {}; var persistentNotifications = {};
RED.comms.subscribe("notification/#",function(topic,msg) { RED.comms.subscribe("notification/#",function(topic,msg) {
var parts = topic.split("/"); var parts = topic.split("/");
@ -477,8 +481,17 @@ var RED = (function() {
RED.nodes.addNodeSet(m); RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types); addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() { RED.i18n.loadNodeCatalog(id, function() {
$.get('nodes/'+id, function(data) { var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
appendNodeConfig(data); $.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+id,
success: function(data) {
appendNodeConfig(data);
}
}); });
}); });
}); });
@ -505,10 +518,19 @@ var RED = (function() {
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success"); RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else { } else {
$.get('nodes/'+msg.id, function(data) { var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
appendNodeConfig(data); $.ajax({
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; headers: {
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success"); "Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+msg.id,
success: function(data) {
appendNodeConfig(data);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
}
}); });
} }
} }
@ -535,18 +557,24 @@ var RED = (function() {
setTimeout(function() { setTimeout(function() {
loader.end(); loader.end();
checkFirstRun(); checkFirstRun(function() {
if (showProjectWelcome) {
RED.projects.showStartup();
}
});
},100); },100);
} }
function checkFirstRun() { function checkFirstRun(done) {
if (RED.settings.theme("tours") === false) { if (RED.settings.theme("tours") === false) {
done();
return; return;
} }
if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) { if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) {
done();
return; return;
} }
RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome")); RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"), done);
} }
function buildMainMenu() { function buildMainMenu() {

View File

@ -436,18 +436,17 @@ RED.popover = (function() {
return { return {
create: createPopover, create: createPopover,
tooltip: function(target,content, action) { tooltip: function(target,content, action) {
var label = content; var label = function() {
if (action) { var label = content;
label = function() { if (action) {
var label = content;
var shortcut = RED.keyboard.getShortcut(action); var shortcut = RED.keyboard.getShortcut(action);
if (shortcut && shortcut.key) { if (shortcut && shortcut.key) {
label = $('<span>'+content+' <span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span></span>'); label = $('<span>'+content+' <span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span></span>');
} }
return label;
} }
return label;
} }
return RED.popover.create({ var popover = RED.popover.create({
tooltip: true, tooltip: true,
target:target, target:target,
trigger: "hover", trigger: "hover",
@ -456,6 +455,14 @@ RED.popover = (function() {
content: label, content: label,
delay: { show: 750, hide: 50 } delay: { show: 750, hide: 50 }
}); });
popover.setContent = function(newContent) {
content = newContent;
}
popover.setAction = function(newAction) {
action = newAction;
}
return popover;
}, },
menu: function(options) { menu: function(options) {
var list = $('<ul class="red-ui-menu"></ul>'); var list = $('<ul class="red-ui-menu"></ul>');

View File

@ -24,6 +24,9 @@
* - rootSortable: boolean - if 'sortable' is set, then setting this to * - rootSortable: boolean - if 'sortable' is set, then setting this to
* false, prevents items being sorted to the * false, prevents items being sorted to the
* top level of the tree * top level of the tree
* - autoSelect: boolean - default true - triggers item selection when navigating
* list by keyboard. If the list has checkboxed items
* you probably want to set this to false
* *
* methods: * methods:
* - data(items) - clears existing items and replaces with new data * - data(items) - clears existing items and replaces with new data
@ -50,6 +53,7 @@
* deferBuild: true/false, // don't build any ui elements for the item's children * deferBuild: true/false, // don't build any ui elements for the item's children
* until it is expanded by the user. * until it is expanded by the user.
* element: // custom dom element to use for the item - ignored if `label` is set * element: // custom dom element to use for the item - ignored if `label` is set
* collapsible: true/false, // prevent a parent item from being collapsed. default true.
* } * }
* ] * ]
* *
@ -90,77 +94,99 @@
$.widget( "nodered.treeList", { $.widget( "nodered.treeList", {
_create: function() { _create: function() {
var that = this; var that = this;
var autoSelect = true;
if (that.options.autoSelect === false) {
autoSelect = false;
}
this.element.addClass('red-ui-treeList'); this.element.addClass('red-ui-treeList');
this.element.attr("tabIndex",0); this.element.attr("tabIndex",0);
var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element); var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element);
this.element.on('keydown', function(evt) { this.element.on('keydown', function(evt) {
var selected = that._topList.find(".selected").parent().data('data'); var focussed = that._topList.find(".focus").parent().data('data');
if (!selected && (evt.keyCode === 40 || evt.keyCode === 38)) { if (!focussed && (evt.keyCode === 40 || evt.keyCode === 38)) {
that.select(that._data[0]); if (that._data[0]) {
if (autoSelect) {
that.select(that._data[0]);
} else {
that._topList.find(".focus").removeClass("focus")
}
that._data[0].treeList.label.addClass('focus')
}
return; return;
} }
var target; var target;
switch(evt.keyCode) { switch(evt.keyCode) {
case 32: // SPACE
case 13: // ENTER case 13: // ENTER
if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
return
}
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
if (selected.children) { if (focussed.checkbox) {
if (selected.treeList.container.hasClass("expanded")) { focussed.treeList.checkbox.trigger("click");
selected.treeList.collapse() } else if (focussed.radio) {
focussed.treeList.radio.trigger("click");
} else if (focussed.children) {
if (focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.collapse()
} else { } else {
selected.treeList.expand() focussed.treeList.expand()
} }
} else { } else {
that._trigger("confirm",null,selected) that._trigger("confirm",null,focussed)
} }
break; break;
case 37: // LEFT case 37: // LEFT
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
if (selected.children&& selected.treeList.container.hasClass("expanded")) { if (focussed.children&& focussed.treeList.container.hasClass("expanded")) {
selected.treeList.collapse() focussed.treeList.collapse()
} else if (selected.parent) { } else if (focussed.parent) {
target = selected.parent; target = focussed.parent;
} }
break; break;
case 38: // UP case 38: // UP
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
target = that._getPreviousSibling(selected); target = that._getPreviousSibling(focussed);
if (target) { if (target) {
target = that._getLastDescendant(target); target = that._getLastDescendant(target);
} }
if (!target && selected.parent) { if (!target && focussed.parent) {
target = selected.parent; target = focussed.parent;
} }
break; break;
case 39: // RIGHT case 39: // RIGHT
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
if (selected.children) { if (focussed.children) {
if (!selected.treeList.container.hasClass("expanded")) { if (!focussed.treeList.container.hasClass("expanded")) {
selected.treeList.expand() focussed.treeList.expand()
} }
} }
break break
case 40: //DOWN case 40: //DOWN
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) { if (focussed.children && Array.isArray(focussed.children) && focussed.children.length > 0 && focussed.treeList.container.hasClass("expanded")) {
target = selected.children[0]; target = focussed.children[0];
} else { } else {
target = that._getNextSibling(selected); target = that._getNextSibling(focussed);
while (!target && selected.parent) { while (!target && focussed.parent) {
selected = selected.parent; focussed = focussed.parent;
target = that._getNextSibling(selected); target = that._getNextSibling(focussed);
} }
} }
break break
} }
if (target) { if (target) {
that.select(target); if (autoSelect) {
that.select(target);
} else {
that._topList.find(".focus").removeClass("focus")
}
target.treeList.label.addClass('focus')
} }
}); });
this._data = []; this._data = [];
@ -463,6 +489,9 @@
container.addClass("expanded"); container.addClass("expanded");
} }
item.treeList.collapse = function() { item.treeList.collapse = function() {
if (item.collapsible === false) {
return
}
if (!item.children) { if (!item.children) {
return; return;
} }
@ -583,7 +612,7 @@
// Already a parent because we've got the angle-right icon // Already a parent because we've got the angle-right icon
return; return;
} }
$('<i class="fa fa-angle-right" />').appendTo(treeListIcon); $('<i class="fa fa-angle-right" />').toggleClass("hide",item.collapsible === false).appendTo(treeListIcon);
treeListIcon.on("click.red-ui-treeList-expand", function(e) { treeListIcon.on("click.red-ui-treeList-expand", function(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -634,6 +663,8 @@
label.on("click", function(e) { label.on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
cb.trigger("click"); cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
}) })
} }
item.treeList.select = function(v) { item.treeList.select = function(v) {
@ -641,6 +672,7 @@
cb.trigger("click"); cb.trigger("click");
} }
} }
item.treeList.checkbox = cb;
selectWrapper.appendTo(label) selectWrapper.appendTo(label)
} else if (item.radio) { } else if (item.radio) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>'); var selectWrapper = $('<span class="red-ui-treeList-icon"></span>');
@ -669,6 +701,8 @@
label.on("click", function(e) { label.on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
cb.trigger("click"); cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
}) })
} }
item.treeList.select = function(v) { item.treeList.select = function(v) {
@ -677,6 +711,7 @@
} }
} }
selectWrapper.appendTo(label) selectWrapper.appendTo(label)
item.treeList.radio = cb;
} else { } else {
label.on("click", function(e) { label.on("click", function(e) {
if (!that.options.multi) { if (!that.options.multi) {
@ -684,10 +719,14 @@
} }
label.addClass("selected"); label.addClass("selected");
that._selected.add(item); that._selected.add(item);
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
that._trigger("select",e,item) that._trigger("select",e,item)
}) })
label.on("dblclick", function(e) { label.on("dblclick", function(e) {
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
if (!item.children) { if (!item.children) {
that._trigger("confirm",e,item); that._trigger("confirm",e,item);
} }
@ -835,6 +874,9 @@
if (item.treeList.label) { if (item.treeList.label) {
item.treeList.label.addClass("selected"); item.treeList.label.addClass("selected");
} }
that._topList.find(".focus").removeClass("focus");
if (triggerEvent !== false) { if (triggerEvent !== false) {
this._trigger("select",null,item) this._trigger("select",null,item)
} }
@ -842,6 +884,9 @@
clearSelection: function() { clearSelection: function() {
this._selected.forEach(function(item) { this._selected.forEach(function(item) {
item.selected = false; item.selected = false;
if (item.treeList.checkbox) {
item.treeList.checkbox.prop('checked',false)
}
if (item.treeList.label) { if (item.treeList.label) {
item.treeList.label.removeClass("selected") item.treeList.label.removeClass("selected")
} }

View File

@ -345,6 +345,47 @@
} }
} }
}; };
// For a type with options, check value is a valid selection
// If !opt.multiple, returns the valid option object
// if opt.multiple, returns an array of valid option objects
// If not valid, returns null;
function isOptionValueValid(opt, currentVal) {
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
if (typeof op === "string" && op === currentVal) {
return {value:currentVal}
} else if (op.value === currentVal) {
return op;
}
}
} else {
// Check to see if value is a valid csv of
// options.
var currentValues = {};
var selected = [];
currentVal.split(",").forEach(function(v) {
if (v) {
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
var val = typeof op === "string" ? op : op.value;
if (currentValues.hasOwnProperty(val)) {
delete currentValues[val];
selected.push(typeof op === "string" ? {value:op} : op.value)
}
}
if (!$.isEmptyObject(currentValues)) {
return null;
}
return selected
}
}
var nlsd = false; var nlsd = false;
$.widget( "nodered.typedInput", { $.widget( "nodered.typedInput", {
@ -408,6 +449,8 @@
}); });
this.defaultInputType = this.input.attr('type'); this.defaultInputType = this.input.attr('type');
// Used to remember selections per-type to restore them when switching between types
this.oldValues = {};
this.uiSelect.addClass("red-ui-typedInput-container"); this.uiSelect.addClass("red-ui-typedInput-container");
@ -512,9 +555,6 @@
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect); this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect);
this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton); this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton);
// Used to remember selections per-type to restore them when switching between types
this.oldValues = {};
this.type(this.options.default||this.typeList[0].value); this.type(this.options.default||this.typeList[0].value);
}catch(err) { }catch(err) {
console.log(err.stack); console.log(err.stack);
@ -859,22 +899,44 @@
return this.propertyType; return this.propertyType;
} else { } else {
var that = this; var that = this;
var previousValue = null;
var opt = this.typeMap[type]; var opt = this.typeMap[type];
if (opt && this.propertyType !== type) { if (opt && this.propertyType !== type) {
// If previousType is !null, then this is a change of the type, rather than the initialisation // If previousType is !null, then this is a change of the type, rather than the initialisation
var previousType = this.typeMap[this.propertyType]; var previousType = this.typeMap[this.propertyType];
var typeChanged = !!previousType; var typeChanged = !!previousType;
previousValue = this.input.val();
if (typeChanged) { if (typeChanged) {
if (previousType.options && opt.hasValue !== true) { if (previousType.options && opt.hasValue !== true) {
this.oldValues[previousType.value] = this.input.val(); this.oldValues[previousType.value] = previousValue;
} else if (previousType.hasValue === false) { } else if (previousType.hasValue === false) {
this.oldValues[previousType.value] = this.input.val(); this.oldValues[previousType.value] = previousValue;
} else { } else {
this.oldValues["_"] = this.input.val(); this.oldValues["_"] = previousValue;
} }
if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) { if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) {
this.input.val(this.oldValues.hasOwnProperty(opt.value)?this.oldValues[opt.value]:(opt.default||[]).join(",")) if (this.oldValues.hasOwnProperty(opt.value)) {
this.input.val(this.oldValues[opt.value]);
} else {
// No old value for the option type.
// It is possible code has called 'value' then 'type'
// to set the selected option. This is what the Inject/Switch/Change
// nodes did before 2.1.
// So we need to be careful to not reset the value if it is a valid option.
var validOptions = isOptionValueValid(opt,previousValue);
if (previousValue && validOptions) {
this.input.val(previousValue);
} else {
if (typeof opt.default === "string") {
this.input.val(opt.default);
} else if (Array.isArray(opt.default)) {
this.input.val(opt.default.join(","))
} else {
this.input.val("");
}
}
}
} else { } else {
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||"")) this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
} }
@ -951,22 +1013,12 @@
var op; var op;
if (!opt.hasValue) { if (!opt.hasValue) {
var validValue = false; // Check the value is valid for the available options
var currentVal = this.input.val(); var validValues = isOptionValueValid(opt,this.input.val());
if (!opt.multiple) { if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) { if (validValues) {
op = opt.options[i]; that._updateOptionSelectLabel(validValues)
if (typeof op === "string" && op === currentVal) { } else {
that._updateOptionSelectLabel({value:currentVal});
validValue = true;
break;
} else if (op.value === currentVal) {
that._updateOptionSelectLabel(op);
validValue = true;
break;
}
}
if (!validValue) {
op = opt.options[0]; op = opt.options[0];
if (typeof op === "string") { if (typeof op === "string") {
this.value(op); this.value(op);
@ -977,26 +1029,13 @@
} }
} }
} else { } else {
// Check to see if value is a valid csv of if (!validValues) {
// options. validValues = (opt.default || []).map(function(v) {
var currentValues = {}; return typeof v === "string"?v:v.value
var selected = []; });
currentVal.split(",").forEach(function(v) { this.value(validValues.join(","));
if (v) {
selected.push(v);
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
delete currentValues[op.value||op];
} }
if (!$.isEmptyObject(currentValues)) { that._updateOptionSelectLabel(validValues);
selected = opt.default || [];
// Invalid, set to default/empty
this.value(selected.join(","));
}
that._updateOptionSelectLabel(selected);
} }
} else { } else {
var selectedOption = this.optionValue||opt.options[0]; var selectedOption = this.optionValue||opt.options[0];

View File

@ -243,7 +243,13 @@ RED.keyboard = (function() {
function resolveKeyEvent(evt) { function resolveKeyEvent(evt) {
var slot = partialState||handlers; var slot = partialState||handlers;
if (evt.ctrlKey || evt.metaKey) { // We cheat with MacOS CMD key and consider it the same as Ctrl.
// That means we don't have to have separate keymaps for different OS.
// It mostly works.
// One exception is shortcuts that include both Cmd and Ctrl. We don't
// support them - but we need to make sure we don't block browser-specific
// shortcuts (such as Cmd-Ctrl-F for fullscreen).
if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) {
slot = slot.ctrl; slot = slot.ctrl;
} }
if (slot && evt.shiftKey) { if (slot && evt.shiftKey) {

View File

@ -2387,6 +2387,7 @@ RED.projects = (function() {
return { return {
init: init, init: init,
showStartup: function() { showStartup: function() {
console.warn("showStartup")
if (!RED.user.hasPermission("projects.write")) { if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error"); RED.notify(RED._("user.errors.notAuthorized"),"error");
return; return;

View File

@ -393,10 +393,12 @@ RED.sidebar.help = (function() {
treeList.treeList("select","changelog"); treeList.treeList("select","changelog");
show(); show();
} }
function showWelcomeTour(lastSeenVersion) { function showWelcomeTour(lastSeenVersion, done) {
done = done || function() {};
RED.tourGuide.load("./tours/welcome.js", function(err, tour) { RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
if (err) { if (err) {
console.warn("Failed to load welcome tour",err); console.warn("Failed to load welcome tour",err);
done()
return; return;
} }
var currentVersionParts = RED.settings.version.split("."); var currentVersionParts = RED.settings.version.split(".");
@ -405,6 +407,7 @@ RED.sidebar.help = (function() {
// Only display the tour if its MAJ.MIN versions the current version // Only display the tour if its MAJ.MIN versions the current version
// This means if we update MAJ/MIN without updating the tour, the old tour won't get shown // This means if we update MAJ/MIN without updating the tour, the old tour won't get shown
if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) { if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) {
done()
return; return;
} }
@ -412,26 +415,31 @@ RED.sidebar.help = (function() {
// Previously displayed a welcome tour. // Previously displayed a welcome tour.
if (lastSeenVersion === RED.settings.version) { if (lastSeenVersion === RED.settings.version) {
// Exact match - don't show the tour // Exact match - don't show the tour
done()
return; return;
} }
var lastSeenParts = lastSeenVersion.split("."); var lastSeenParts = lastSeenVersion.split(".");
if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) { if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) {
// Running an *older* version than last displayed tour. // Running an *older* version than last displayed tour.
done()
return; return;
} }
if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) { if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) {
if (lastSeenParts.length === 3 && currentVersionParts.length === 3) { if (lastSeenParts.length === 3 && currentVersionParts.length === 3) {
// Matching non-beta MAJ.MIN - don't repeat tour // Matching non-beta MAJ.MIN - don't repeat tour
done()
return; return;
} }
if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) { if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) {
// Running an *older* beta than last displayed tour. // Running an *older* beta than last displayed tour.
done()
return return
} }
} }
} }
RED.tourGuide.run("./tours/welcome.js", function(err) { RED.tourGuide.run("./tours/welcome.js", function(err) {
RED.settings.set("editor.tours.welcome", RED.settings.version) RED.settings.set("editor.tours.welcome", RED.settings.version)
done()
}) })
}) })

View File

@ -287,11 +287,11 @@ RED.sidebar.info.outliner = (function() {
var node = RED.nodes.node(item.id) || RED.nodes.group(item.id); var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
if (node) { if (node) {
if (node.type === 'group' || node._def.category !== "config") { if (node.type === 'group' || node._def.category !== "config") {
RED.view.select({nodes:[node]}) // RED.view.select({nodes:[node]})
} else if (node._def.category === "config") { } else if (node._def.category === "config") {
RED.sidebar.info.refresh(node); RED.sidebar.info.refresh(node);
} else { } else {
RED.view.select({nodes:[]}) // RED.view.select({nodes:[]})
} }
} }
}) })

View File

@ -563,7 +563,8 @@ RED.utils = (function() {
expandPaths: expandPaths, expandPaths: expandPaths,
ontoggle: ontoggle, ontoggle: ontoggle,
exposeApi: exposeApi, exposeApi: exposeApi,
tools: tools // tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
} }
).appendTo(row); ).appendTo(row);
} }
@ -592,7 +593,8 @@ RED.utils = (function() {
expandPaths: expandPaths, expandPaths: expandPaths,
ontoggle: ontoggle, ontoggle: ontoggle,
exposeApi: exposeApi, exposeApi: exposeApi,
tools: tools // tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
} }
).appendTo(row); ).appendTo(row);
} }
@ -647,7 +649,8 @@ RED.utils = (function() {
expandPaths: expandPaths, expandPaths: expandPaths,
ontoggle: ontoggle, ontoggle: ontoggle,
exposeApi: exposeApi, exposeApi: exposeApi,
tools: tools // tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
} }
).appendTo(row); ).appendTo(row);
} }

View File

@ -4006,10 +4006,10 @@ RED.view = (function() {
var labelParts; var labelParts;
if (d.resize || this.__hideLabel__ !== hideLabel || this.__label__ !== label || this.__outputs__.length !== d.outputs) { if (d.resize || this.__hideLabel__ !== hideLabel || this.__label__ !== label || this.__outputs__.length !== d.outputs) {
labelParts = getLabelParts(label, "red-ui-flow-node-label"); labelParts = getLabelParts(label, "red-ui-flow-node-label");
this.__label__ = label; if (labelParts.lines.length !== this.__labelLineCount__ || this.__label__ !== label) {
if (labelParts.lines.length !== this.__labelLineCount__) {
d.resize = true; d.resize = true;
} }
this.__label__ = label;
this.__labelLineCount__ = labelParts.lines.length; this.__labelLineCount__ = labelParts.lines.length;
if (hideLabel) { if (hideLabel) {

View File

@ -43,12 +43,24 @@
border-bottom: 1px solid $secondary-border-color; border-bottom: 1px solid $secondary-border-color;
box-shadow: 0 2px 6px $shadow; box-shadow: 0 2px 6px $shadow;
} }
.red-ui-debug-filter-row { #red-ui-sidebar-debug-filter-node-list-row {
.red-ui-nodeList { .red-ui-treeList-label.disabled {
margin: 10px 0; font-style: italic;
color: $secondary-text-color-disabled;
}
.red-ui-treeList-label {
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: inherit;
}
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: inherit;
}
&.focus, &.focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover !important;
}
} }
} }
.red-ui-debug-msg { .red-ui-debug-msg {
position: relative; position: relative;
border-bottom: 1px solid $debug-message-border; border-bottom: 1px solid $debug-message-border;

View File

@ -73,13 +73,13 @@
.red-ui-projects-dialog-screen-start { .red-ui-projects-dialog-screen-start {
.red-ui-projects-dialog-screen-start-hero { .red-ui-projects-dialog-screen-start-hero {
text-align: center; text-align: center;
font-size: 2em; font-size: 1.4em;
padding: 10px; padding: 10px;
min-height: 60px; min-height: 40px;
color: $primary-text-color; color: $primary-text-color;
} }
.red-ui-projects-dialog-screen-start-body { .red-ui-projects-dialog-screen-start-body {
min-height: 400px; min-height: 300px;
line-height: 1.6em; line-height: 1.6em;
p { p {
font-size: 1.1em; font-size: 1.1em;
@ -92,7 +92,7 @@
} }
button.red-ui-button.red-ui-projects-dialog-button { button.red-ui-button.red-ui-projects-dialog-button {
width: calc(50% - 80px); width: calc(50% - 80px);
margin: 20px; margin: 10px 20px;
height: auto; height: auto;
line-height: 2em; line-height: 2em;
padding: 10px; padding: 10px;

View File

@ -434,16 +434,19 @@ div.red-ui-info-table {
} }
.red-ui-info-outline-item-controls { .red-ui-info-outline-item-controls {
position: absolute; position: absolute;
top:0; top:1px;
bottom: 0; bottom: 1px;
right: 0px; right: 1px;
padding: 2px 3px 0 1px; padding: 1px 2px 0 1px;
text-align: right; text-align: right;
background: $list-item-background; background: $list-item-background;
.red-ui-treeList-label:hover & { .red-ui-treeList-label:hover & {
background: $list-item-background-hover; background: $list-item-background-hover;
} }
.red-ui-treeList-label.focus & {
background: $list-item-background-hover;
}
.red-ui-treeList-label.selected & { .red-ui-treeList-label.selected & {
background: $list-item-background-selected; background: $list-item-background-selected;
} }

View File

@ -89,6 +89,12 @@
color: $list-item-color; color: $list-item-color;
text-decoration: none; text-decoration: none;
} }
&.focus, &.focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover;
outline: 1px solid $form-input-focus-color !important;
outline-offset: -1px;
color: $list-item-color;
}
&.selected, &.selected .red-ui-treeList-sublabel-text { &.selected, &.selected .red-ui-treeList-sublabel-text {
background: $list-item-background-selected; background: $list-item-background-selected;
outline: none; outline: none;

View File

@ -144,7 +144,9 @@ export default {
"ja": "Link Callードを追加" "ja": "Link Callードを追加"
}, },
prepare(done) { prepare(done) {
$('[data-palette-type="link call"]')[0].scrollIntoView({block:"center"}); this.paletteWasClosed = $("#red-ui-main-container").hasClass("red-ui-palette-closed");
RED.actions.invoke("core:toggle-palette",true)
$('[data-palette-type="link call"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100); setTimeout(done,100);
}, },
element: '[data-palette-type="link call"]', element: '[data-palette-type="link call"]',
@ -154,6 +156,22 @@ export default {
"ja": "<p><code>Link Call</code>ノードを用いることで、<code>Link In</code>ノードから始まるフローを呼び出し、<code>Link Out</code>ノードに到達した時に、結果を取得できます。</p>" "ja": "<p><code>Link Call</code>ノードを用いることで、<code>Link In</code>ノードから始まるフローを呼び出し、<code>Link Out</code>ノードに到達した時に、結果を取得できます。</p>"
} }
}, },
{
title: {
"en-US": "MQTT nodes support dynamic connections",
"ja": "MQTTードが動的接続をサポート"
},
prepare(done) {
$('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
},
element: '[data-palette-type="mqtt out"]',
direction: "right",
description: {
"en-US": '<p>The <code>MQTT</code> nodes now support creating their connections and subscriptions dynamically.</p>',
"ja": '<p><code>MQTT</code>ノードは、動的な接続や購読ができるようになりました。</p>'
},
},
{ {
title: { title: {
"en-US": "File nodes renamed", "en-US": "File nodes renamed",
@ -163,6 +181,11 @@ export default {
$('[data-palette-type="file"]')[0].scrollIntoView({block:"center"}); $('[data-palette-type="file"]')[0].scrollIntoView({block:"center"});
setTimeout(done,100); setTimeout(done,100);
}, },
complete() {
if (this.paletteWasClosed) {
RED.actions.invoke("core:toggle-palette",false)
}
},
element: '[data-palette-type="file"]', element: '[data-palette-type="file"]',
direction: "right", direction: "right",
description: { description: {

View File

@ -143,7 +143,8 @@
margin-bottom: 8px; margin-bottom: 8px;
} }
.inject-time-count { .inject-time-count {
width: 40px !important; padding-left: 3px !important;
width: 80px !important;
} }
</style> </style>
@ -538,12 +539,10 @@
var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"}) var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"})
.css("width","calc(70% - 30px)") .css("width","calc(70% - 30px)")
.appendTo(row) .appendTo(row)
.typedInput({default:'str',types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']}); .typedInput({default:prop.vt,types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']});
propertyName.typedInput('value',prop.p); propertyName.typedInput('value',prop.p);
propertyValue.typedInput('value',prop.v); propertyValue.typedInput('value',prop.v);
propertyValue.typedInput('type',prop.vt);
}, },
removable: true, removable: true,
sortable: true sortable: true

View File

@ -292,6 +292,7 @@
}; };
RED.events.on("project:change", this.clearMessageList); RED.events.on("project:change", this.clearMessageList);
RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) }); RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) });
RED.actions.add("core:clear-filtered-debug-messages", function() { RED.debug.clearMessageList(true, true) });
RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); }); RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); });
RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); }); RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); });

View File

@ -52,7 +52,7 @@
treeList = $("<div>") treeList = $("<div>")
.css({width: "100%", height: "100%"}) .css({width: "100%", height: "100%"})
.appendTo(".node-input-link-row") .appendTo(".node-input-link-row")
.treeList({}) .treeList({autoSelect:false})
.on('treelistitemmouseover',function(e,item) { .on('treelistitemmouseover',function(e,item) {
if (item.node) { if (item.node) {
item.node.highlighted = true; item.node.highlighted = true;
@ -68,6 +68,8 @@
} }
}); });
var candidateNodes = RED.nodes.filterNodes({type:targetType}); var candidateNodes = RED.nodes.filterNodes({type:targetType});
var candidateNodesCount = 0;
var search = $("#node-input-link-target-filter").searchBox({ var search = $("#node-input-link-target-filter").searchBox({
style: "compact", style: "compact",
delay: 300, delay: 300,
@ -80,7 +82,7 @@
var count = treeList.treeList("filter", function(item) { var count = treeList.treeList("filter", function(item) {
return item.label.toLowerCase().indexOf(val) > -1 || (item.node && item.node.type.toLowerCase().indexOf(val) > -1) return item.label.toLowerCase().indexOf(val) > -1 || (item.node && item.node.type.toLowerCase().indexOf(val) > -1)
}); });
search.searchBox("count",count+" / "+candidateNodes.length); search.searchBox("count",count+" / "+candidateNodesCount);
} }
} }
}); });
@ -113,6 +115,11 @@
candidateNodes.forEach(function(n) { candidateNodes.forEach(function(n) {
if (flowMap[n.z]) { if (flowMap[n.z]) {
if (targetType === "link out" && n.mode === 'return') {
// Link In nodes looking for Link Out nodes should not
// include return-mode nodes.
return
}
var isChecked = false; var isChecked = false;
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1; isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
if (isChecked) { if (isChecked) {
@ -126,6 +133,7 @@
checkbox: node.type !== "link call", checkbox: node.type !== "link call",
radio: node.type === "link call" radio: node.type === "link call"
}) })
candidateNodesCount++;
} }
}); });
flows = flows.filter(function(f) { return f.children.length > 0 }) flows = flows.filter(function(f) { return f.children.length > 0 })
@ -149,7 +157,7 @@
function onEditSave(node) { function onEditSave(node) {
var flows = treeList.treeList('data'); var flows = treeList.treeList('data');
node.links = []; node.links = [];
if (node.type !== "link out" || $("node-input-mode").val() === 'link') { if (node.type !== "link out" || $("#node-input-mode").val() === 'link') {
flows.forEach(function(f) { flows.forEach(function(f) {
f.children.forEach(function(n) { f.children.forEach(function(n) {
if (n.selected) { if (n.selected) {
@ -234,6 +242,12 @@
}, },
oneditsave: function() { oneditsave: function() {
onEditSave(this); onEditSave(this);
// In case the name has changed, ensure any link call nodes on this
// tab are redrawn with the updated name
var localCallNodes = RED.nodes.filterNodes({z:RED.workspaces.active(), type:"link call"});
localCallNodes.forEach(function(node) {
node.dirty = true;
});
}, },
onadd: onAdd, onadd: onAdd,
oneditresize: resizeNodeList oneditresize: resizeNodeList
@ -259,12 +273,12 @@
} }
if (this.links.length > 0) { if (this.links.length > 0) {
var targetNode = RED.nodes.node(this.links[0]); var targetNode = RED.nodes.node(this.links[0]);
return targetNode && (targetNode.name || targetNode.id); return targetNode && (targetNode.name || this._("link.linkCall"));
} }
return this._("link.linkCall"); return this._("inject.none");
}, },
labelStyle: function() { labelStyle: function() {
return (this.name || this.links.length > 0)?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
oneditprepare: function() { oneditprepare: function() {
onEditPrepare(this,"link in"); onEditPrepare(this,"link in");
@ -275,7 +289,6 @@
oneditresize: resizeNodeList oneditresize: resizeNodeList
}); });
RED.nodes.registerType('link out',{ RED.nodes.registerType('link out',{
category: 'common', category: 'common',
color:"#ddd",//"#87D8CF", color:"#ddd",//"#87D8CF",

View File

@ -31,24 +31,22 @@ RED.debug = (function() {
var activeWorkspace; var activeWorkspace;
var numMessages = 100; // Hardcoded number of message to show in debug window scrollback var numMessages = 100; // Hardcoded number of message to show in debug window scrollback
var filterVisible = false; var debugNodeTreeList;
var debugNodeList;
var debugNodeListExpandedFlows = {};
function init(_config) { function init(_config) {
config = _config; config = _config;
var content = $("<div>").css({"position":"relative","height":"100%"}); var content = $("<div>").css({"position":"relative","height":"100%"});
var toolbar = $('<div class="red-ui-sidebar-header">'+ var toolbar = $('<div class="red-ui-sidebar-header">'+
'<span class="button-group"><a id="red-ui-sidebar-debug-filter" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span></a></span>'+ '<span class="button-group">'+
'<span class="button-group"><a id="red-ui-sidebar-debug-clear" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-trash"></i></a></span></div>').appendTo(content); '<a id="red-ui-sidebar-debug-filter" style="padding-right: 5px" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span> <i style="padding-left: 5px;" class="fa fa-caret-down"></i></a>'+
'</span>'+
'<span class="button-group">'+
'<a id="red-ui-sidebar-debug-clear" style="border-right: none; padding-right: 6px" class="red-ui-sidebar-header-button" href="#" data-clear-type="all"><i class="fa fa-trash"></i> <span>all</span></a>' +
'<a id="red-ui-sidebar-debug-clear-opts" style="padding: 5px; border-left: none;" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-caret-down"></i></a>'+
'</span></div>').appendTo(content);
var footerToolbar = $('<div>'+ var footerToolbar = $('<div>'+
// '<span class="button-group">'+
// '<a class="red-ui-footer-button-toggle text-button selected" id="red-ui-sidebar-debug-view-list" href="#"><span data-i18n="">list</span></a>'+
// '<a class="red-ui-footer-button-toggle text-button" id="red-ui-sidebar-debug-view-table" href="#"><span data-i18n="">table</span></a> '+
// '</span>'+
'<span class="button-group"><a id="red-ui-sidebar-debug-open" class="red-ui-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' + '<span class="button-group"><a id="red-ui-sidebar-debug-open" class="red-ui-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' +
'</div>'); '</div>');
@ -56,85 +54,100 @@ RED.debug = (function() {
sbc = messageList[0]; sbc = messageList[0];
messageTable = $('<div class="red-ui-debug-content red-ui-debug-content-table hide"/>').appendTo(content); messageTable = $('<div class="red-ui-debug-content red-ui-debug-content-table hide"/>').appendTo(content);
var filterDialog = $('<div class="red-ui-debug-filter-box hide">'+ var filterDialogCloseTimeout;
'<div class="red-ui-debug-filter-row">'+ var filterDialogShown = false;
'<span class="button-group">'+ var filterDialog = $('<div class="red-ui-debug-filter-box hide"></div>').appendTo(toolbar);//content);
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option selected" id="red-ui-sidebar-debug-filterAll" href="#"><span data-i18n="node-red:debug.sidebar.filterAll"></span></a>'+ filterDialog.on('mouseleave' ,function(evt) {
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option" id="red-ui-sidebar-debug-filterSelected" href="#"><span data-i18n="node-red:debug.sidebar.filterSelected"></span></a>'+ if (filterDialogShown) {
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option" id="red-ui-sidebar-debug-filterCurrent" href="#"><span data-i18n="node-red:debug.sidebar.filterCurrent"></span></a> '+ filterDialogCloseTimeout = setTimeout(function() {
filterDialog.slideUp(200);
filterDialogShown = false;
},500)
}
})
filterDialog.on('mouseenter' ,function(evt) {
clearTimeout(filterDialogCloseTimeout)
})
var filterToolbar = $('<div style="margin-bottom: 3px; display: flex;">'+
'<span style="flex-grow:1; text-align: left;">'+
'<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-all" class="red-ui-sidebar-header-button red-ui-button-small" data-i18n="node-red:debug.sidebar.selectAll"></button></span>' +
'<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-none" class="red-ui-sidebar-header-button red-ui-button-small" data-i18n="node-red:debug.sidebar.selectNone"></button></span>' +
'</span>'+ '</span>'+
'</div>'+ '<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-close" class="red-ui-sidebar-header-button red-ui-button-small"><i class="fa fa-times"></i></button></span>'+
'</div>').appendTo(toolbar);//content); '</div>').appendTo(filterDialog);
// var filterTypeRow = $('<div class="red-ui-debug-filter-row"></div>').appendTo(filterDialog); filterToolbar.find("#red-ui-sidebar-filter-select-close").on('click', function(evt) {
// $('<select><option>Show all debug nodes</option><option>Show selected debug nodes</option><option>Show current flow only</option></select>').appendTo(filterTypeRow); clearTimeout(filterDialogCloseTimeout)
filterDialogShown = false;
filterDialog.slideUp(200);
})
var debugNodeListRow = $('<div class="red-ui-debug-filter-row hide" id="red-ui-sidebar-debug-filter-node-list-row"></div>').appendTo(filterDialog); filterToolbar.find("#red-ui-sidebar-filter-select-all").on('click', function(evt) {
var flowCheckboxes = {}; evt.preventDefault();
var debugNodeListHeader = $('<div><span data-i18n="node-red:debug.sidebar.debugNodes"></span><span></span></div>'); var data = debugNodeTreeList.treeList('data');
var headerCheckbox = $('<input type="checkbox">').appendTo(debugNodeListHeader.find("span")[1]).checkboxSet(); data.forEach(function(flow) {
if (!flow.selected) {
debugNodeList = $('<ol>',{style:"text-align: left; min-height: 250px; max-height: 250px"}).appendTo(debugNodeListRow).editableList({ if (flow.treeList.checkbox) {
header: debugNodeListHeader, flow.treeList.checkbox.trigger('click')
class: 'red-ui-nodeList',
addItem: function(container,i,node) {
var row = $("<div>").appendTo(container);
row.attr('id','debug-filter-node-list-node-'+node.id.replace(/\./g,"_"));
if (node.type === 'tab') {
container.parent().addClass('red-ui-editableList-section-header');
if (!debugNodeListExpandedFlows.hasOwnProperty(node.id)) {
debugNodeListExpandedFlows[node.id] = true;
} }
var chevron = $('<i class="fa fa-angle-right"></i>').appendTo(row);
$('<span>').text(RED.utils.getNodeLabel(node,node.id)).appendTo(row);
var muteControl = $('<input type="checkbox">').appendTo($('<span class="meta">').appendTo(row));
muteControl.checkboxSet({
parent: headerCheckbox
});
flowCheckboxes[node.id] = muteControl;
row.on("click", function(e) {
e.stopPropagation();
debugNodeListExpandedFlows[node.id] = !debugNodeListExpandedFlows[node.id];
row.toggleClass('expanded',debugNodeListExpandedFlows[node.id]);
debugNodeList.editableList('filter');
})
row.addClass("expandable");
if (node.disabled) {
container.addClass('disabled');
muteControl.checkboxSet('disable');
debugNodeListExpandedFlows[node.id] = false;
}
row.toggleClass('expanded',debugNodeListExpandedFlows[node.id]);
} else { } else {
$('<span>',{style: "margin-left: 20px"}).text(RED.utils.getNodeLabel(node,node.id)).appendTo(row); flow.children.forEach(function(item) {
row.on("mouseenter",function() { if (!item.selected) {
config.messageMouseEnter(node.id); item.treeList.select();
}); }
row.on("mouseleave",function() { })
config.messageMouseLeave(node.id); }
}); });
var muteControl = $('<input type="checkbox">').prop('checked',!filteredNodes[node.id]).appendTo($('<span class="meta">').appendTo(row)); refreshMessageList();
muteControl.checkboxSet({ })
parent: flowCheckboxes[node.z]
}).on("change", function(e) { filterToolbar.find("#red-ui-sidebar-filter-select-none").on('click', function(evt) {
filteredNodes[node.id] = !$(this).prop('checked'); evt.preventDefault();
$(".red-ui-debug-msg-node-"+node.id.replace(/\./g,"_")).toggleClass('hide',filteredNodes[node.id]); debugNodeTreeList.treeList('clearSelection');
}); var data = debugNodeTreeList.treeList('data');
if ((node.hasOwnProperty("active") && !node.active) || RED.nodes.workspace(node.z).disabled) { data.forEach(function(flow) {
container.addClass('disabled'); if (flow.children) {
muteControl.checkboxSet('disable'); flow.children.forEach(function(item) {
filteredNodes[item.node.id] = true;
})
}
});
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList();
})
var debugNodeListRow = $('<div class="red-ui-debug-filter-row" id="red-ui-sidebar-debug-filter-node-list-row"></div>').appendTo(filterDialog);
debugNodeTreeList = $("<div></div>").appendTo(debugNodeListRow).css({width: "100%", height: "300px"})
.treeList({autoSelect: false}).on("treelistitemmouseover", function(e, item) {
if (item.node) {
item.node.highlighted = true;
item.node.dirty = true;
RED.view.redraw();
}
}).on("treelistitemmouseout", function(e, item) {
if (item.node) {
item.node.highlighted = false;
item.node.dirty = true;
RED.view.redraw();
}
}).on("treelistselect", function(e, item) {
if (item.children) {
item.children.forEach(function(child) {
if (child.checkbox) {
child.treeList.select(item.selected)
}
})
} else {
if (item.node) {
if (item.selected) {
delete filteredNodes[item.node.id]
} else {
filteredNodes[item.node.id] = true;
}
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList();
} }
} }
}, })
addButton: false,
scrollOnAdd: false,
filter: function(node) {
return (node.type === 'tab' || debugNodeListExpandedFlows[node.z] )
},
sort: function(A,B) {
}
});
try { try {
content.i18n(); content.i18n();
@ -144,85 +157,95 @@ RED.debug = (function() {
toolbar.find('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll')); toolbar.find('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll'));
var filterButtonHandler = function(type) {
return function(e) {
e.preventDefault();
if (filterType !== type) {
$('.red-ui-sidebar-debug-filter-option').removeClass('selected');
$(this).addClass('selected');
if (filterType === 'filterSelected') {
debugNodeListRow.slideUp();
}
filterType = type;
if (filterType === 'filterSelected') {
debugNodeListRow.slideDown();
}
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
}
}
}
filterDialog.find('#red-ui-sidebar-debug-filterAll').on("click",filterButtonHandler('filterAll'));
filterDialog.find('#red-ui-sidebar-debug-filterSelected').on("click",filterButtonHandler('filterSelected'));
filterDialog.find('#red-ui-sidebar-debug-filterCurrent').on("click",filterButtonHandler('filterCurrent'));
// $('#red-ui-sidebar-debug-view-list').on("click",function(e) {
// e.preventDefault();
// if (!$(this).hasClass('selected')) {
// $(this).addClass('selected');
// $('#red-ui-sidebar-debug-view-table').removeClass('selected');
// showMessageList();
// }
// });
// $('#red-ui-sidebar-debug-view-table').on("click",function(e) {
// e.preventDefault();
// if (!$(this).hasClass('selected')) {
// $(this).addClass('selected');
// $('#red-ui-sidebar-debug-view-list').removeClass('selected');
// showMessageTable();
// }
// });
var hideFilterTimeout;
toolbar.on('mouseleave',function() {
if ($('#red-ui-sidebar-debug-filter').hasClass('selected')) {
clearTimeout(hideFilterTimeout);
hideFilterTimeout = setTimeout(function() {
filterVisible = false;
$('#red-ui-sidebar-debug-filter').removeClass('selected');
filterDialog.slideUp(200);
},300);
}
});
toolbar.on('mouseenter',function() {
if ($('#red-ui-sidebar-debug-filter').hasClass('selected')) {
clearTimeout(hideFilterTimeout);
}
})
toolbar.find('#red-ui-sidebar-debug-filter').on("click",function(e) { toolbar.find('#red-ui-sidebar-debug-filter').on("click",function(e) {
e.preventDefault(); e.preventDefault();
if ($(this).hasClass('selected')) { var options = [
filterVisible = false; { label: $('<span data-i18n="[append]node-red:debug.sidebar.filterAll"><input type="radio" value="filterAll" name="filter-type" style="margin-top:0"> </span>').i18n() , value: "filterAll" },
$(this).removeClass('selected'); { label: $('<span><span data-i18n="[append]node-red:debug.sidebar.filterSelected"><input type="radio" value="filterSelected" name="filter-type" style="margin-top:0"> </span>...</span>').i18n(), value: "filterSelected" },
clearTimeout(hideFilterTimeout); { label: $('<span data-i18n="[append]node-red:debug.sidebar.filterCurrent"><input type="radio" value="filterCurrent" name="filter-type" style="margin-top:0"> </span>').i18n(), value: "filterCurrent" }
filterDialog.slideUp(200); ]
} else { var menu = RED.popover.menu({
$(this).addClass('selected'); options: options,
filterVisible = true; onselect: function(item) {
refreshDebugNodeList(); if (item.value !== filterType) {
filterDialog.slideDown(200); filterType = item.value;
} $('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
if (filterType === 'filterSelected') {
refreshDebugNodeList();
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
target: $("#red-ui-sidebar-debug-filter"),
align: "left",
offset: [$("#red-ui-sidebar-debug-filter").outerWidth()-2, -1]
})
$('input[name="filter-type"][value="'+RED.settings.get("debug.filter","filterAll")+'"]').prop("checked", true)
}); });
RED.popover.tooltip(toolbar.find('#red-ui-sidebar-debug-filter'),RED._('node-red:debug.sidebar.filterLog')); RED.popover.tooltip(toolbar.find('#red-ui-sidebar-debug-filter'),RED._('node-red:debug.sidebar.filterLog'));
toolbar.find("#red-ui-sidebar-debug-clear").on("click", function(e) { toolbar.find("#red-ui-sidebar-debug-clear").on("click", function(e) {
e.preventDefault(); e.preventDefault();
clearMessageList(false); var action = RED.settings.get("debug.clearType","all")
clearMessageList(false, action === 'filtered');
}); });
RED.popover.tooltip(toolbar.find("#red-ui-sidebar-debug-clear"),RED._('node-red:debug.sidebar.clearLog'),"core:clear-debug-messages"); var clearTooltip = RED.popover.tooltip(toolbar.find("#red-ui-sidebar-debug-clear"),RED._('node-red:debug.sidebar.clearLog'),"core:clear-debug-messages");
toolbar.find("#red-ui-sidebar-debug-clear-opts").on("click", function(e) {
e.preventDefault();
var options = [
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.clearLog"><input type="radio" value="all" name="clear-type" style="margin-top:0"> </span>').i18n() , value: "all" },
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.clearFilteredLog"><input type="radio" value="filtered" name="clear-type" style="margin-top:0"> </span>').i18n(), value: "filtered" }
]
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value === "all") {
$("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.all'));
clearTooltip.setAction("core:clear-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearLog'))
RED.settings.set("debug.clearType","all")
} else {
$("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.filtered'));
clearTooltip.setAction("core:clear-filtered-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearFilteredLog'))
RED.settings.set("debug.clearType","filtered")
}
}
});
menu.show({
target: $("#red-ui-sidebar-debug-clear-opts"),
align: "left",
offset: [$("#red-ui-sidebar-debug-clear-opts").outerWidth()-2, -1]
})
$('input[name="clear-type"][value="'+RED.settings.get("debug.clearType","all")+'"]').prop("checked", true)
})
var clearType = RED.settings.get("debug.clearType","all");
if (clearType === "all") {
toolbar.find("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.all'));
clearTooltip.setAction("core:clear-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearLog'))
} else {
toolbar.find("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.filtered'));
clearTooltip.setAction("core:clear-filtered-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearFilteredLog'))
}
filterType = RED.settings.get("debug.filter","filterAll")
var filteredNodeList = RED.settings.get("debug.filteredNodes",[]);
filteredNodes = {}
filteredNodeList.forEach(function(id) {
filteredNodes[id] = true
})
toolbar.find('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
return { return {
content: content, content: content,
@ -254,8 +277,6 @@ RED.debug = (function() {
function refreshDebugNodeList() { function refreshDebugNodeList() {
debugNodeList.editableList('empty');
var workspaceOrder = RED.nodes.getWorkspaceOrder(); var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {}; var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) { workspaceOrder.forEach(function(ws,i) {
@ -320,15 +341,45 @@ RED.debug = (function() {
return labelA.localeCompare(labelB); return labelA.localeCompare(labelB);
}); });
var currentWs = null; var currentWs = null;
var nodeList = []; var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) { candidateNodes.forEach(function(node) {
if (currentWs !== node.z) { if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z; currentWs = node.z;
nodeList.push(RED.nodes.workspace(node.z)); var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: node,
checkbox: true,
selected: !filteredNodes[node.id]
});
} }
nodeList.push(node);
}); });
debugNodeList.editableList('addItems',nodeList); if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
debugNodeTreeList.treeList("data", data);
} }
function getTimestamp() { function getTimestamp() {
@ -340,7 +391,16 @@ RED.debug = (function() {
return m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"); return m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
} }
var refreshTimeout;
function refreshMessageList(_activeWorkspace) { function refreshMessageList(_activeWorkspace) {
if (refreshTimeout) {
clearTimeout(refreshTimeout);
}
refreshTimeout = setTimeout(function() {
_refreshMessageList(_activeWorkspace);
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) { if (_activeWorkspace) {
activeWorkspace = _activeWorkspace.replace(/\./g,"_"); activeWorkspace = _activeWorkspace.replace(/\./g,"_");
} }
@ -415,6 +475,7 @@ RED.debug = (function() {
}); });
delete filteredNodes[sourceId]; delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click"); $("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList(); refreshMessageList();
}}, }},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){ {id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
@ -601,8 +662,12 @@ RED.debug = (function() {
} }
} }
function clearMessageList(clearFilter) { function clearMessageList(clearFilter, filteredOnly) {
$(".red-ui-debug-msg").remove(); if (!filteredOnly) {
$(".red-ui-debug-msg").remove();
} else {
$(".red-ui-debug-msg:not(.hide)").remove();
}
config.clear(); config.clear();
if (!!clearFilter) { if (!!clearFilter) {
clearFilterSettings(); clearFilterSettings();
@ -613,10 +678,9 @@ RED.debug = (function() {
function clearFilterSettings() { function clearFilterSettings() {
filteredNodes = {}; filteredNodes = {};
filterType = 'filterAll'; filterType = 'filterAll';
$('.red-ui-sidebar-debug-filter-option').removeClass('selected'); RED.settings.set("debug.filter",filterType);
$('#red-ui-sidebar-debug-filterAll').addClass('selected'); RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll')); $('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll'));
$('#red-ui-sidebar-debug-filter-node-list-row').slideUp();
} }
return { return {

View File

@ -15,18 +15,22 @@ $(function() {
} }
} }
var uiComponents = RED.debug.init(options); try {
var uiComponents = RED.debug.init(options);
$(".red-ui-debug-window").append(uiComponents.content);
$(".red-ui-debug-window").append(uiComponents.content); window.addEventListener('message',function(evt) {
if (evt.data.event === "message") {
RED.debug.handleDebugMessage(evt.data.msg);
} else if (evt.data.event === "workspaceChange") {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
}
},false);
} catch(err) {
console.error(err)
}
window.addEventListener('message',function(evt) {
if (evt.data.event === "message") {
RED.debug.handleDebugMessage(evt.data.msg);
} else if (evt.data.event === "workspaceChange") {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
}
},false);
}) })
}); });

View File

@ -117,30 +117,35 @@
return r; return r;
} }
function createValueField(row){ function createValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata','env',previousValueType]}); return $('<input/>',{class:"node-input-rule-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
} }
function createNumValueField(row){ function createNumValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-num-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'num',types:['flow','global','num','jsonata','env']}); return $('<input/>',{class:"node-input-rule-num-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'num',types:['flow','global','num','jsonata','env']});
} }
function createExpValueField(row){ function createExpValueField(row){
return $('<input/>',{class:"node-input-rule-exp-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'jsonata',types:['jsonata']}); return $('<input/>',{class:"node-input-rule-exp-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:'jsonata',types:['jsonata']});
} }
function createBtwnValueField(row){ function createBtwnValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]}); return $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
} }
function createBtwnValue2Field(row3, andLabel){ function createBtwnValue2Field(row3, andLabel, defaultType){
$('<div/>',{class:"node-input-rule-btwn-label", style:"width: 120px; text-align: right;"}).text(" "+andLabel+" ").appendTo(row3); $('<div/>',{class:"node-input-rule-btwn-label", style:"width: 120px; text-align: right;"}).text(" "+andLabel+" ").appendTo(row3);
var row3InputCell = $('<div/>',{style:"flex-grow:1; margin-left: 5px;"}).appendTo(row3); var row3InputCell = $('<div/>',{style:"flex-grow:1; margin-left: 5px;"}).appendTo(row3);
return $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 100%"}).appendTo(row3InputCell).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]}); return $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 100%"}).appendTo(row3InputCell)
.typedInput({default:defaultType||'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
} }
function createTypeValueField(){ function createTypeValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'string',types:[ return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:defaultType || 'string',types:[
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.png"}, {value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.png"},
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.png"}, {value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.png"},
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.png"}, {value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.png"},
@ -211,6 +216,7 @@
var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1); var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1);
var exportedRule = exportRule(lastRule.element); var exportedRule = exportRule(lastRule.element);
opt.r.vt = exportedRule.vt; opt.r.vt = exportedRule.vt;
opt.r.v = "";
// We could copy the value over as well and preselect it (see the 'activeElement' code below) // We could copy the value over as well and preselect it (see the 'activeElement' code below)
// But not sure that feels right. Is copying over the last value 'expected' behaviour? // But not sure that feels right. Is copying over the last value 'expected' behaviour?
// It would make sense for an explicit 'copy' action, but not sure where the copy button would // It would make sense for an explicit 'copy' action, but not sure where the copy button would
@ -278,24 +284,12 @@
selectField.on("change", function() { selectField.on("change", function() {
var fieldToFocus; var fieldToFocus;
var type = selectField.val(); var type = selectField.val();
if (valueField){ if (valueField) { valueField.typedInput('hide'); }
valueField.typedInput('hide'); if (expValueField) { expValueField.typedInput('hide'); }
} if (numValueField) { numValueField.typedInput('hide'); }
if (expValueField){ if (typeValueField) { typeValueField.typedInput('hide'); }
expValueField.typedInput('hide'); if (btwnValueField) { btwnValueField.typedInput('hide'); }
} if (btwnValue2Field) { btwnValue2Field.typedInput('hide'); }
if (numValueField){
numValueField.typedInput('hide');
}
if (typeValueField){
typeValueField.typedInput('hide');
}
if (btwnValueField){
btwnValueField.typedInput('hide');
}
if (btwnValue2Field){
btwnValue2Field.typedInput('hide');
}
if ((type === "btwn") || (type === "index")) { if ((type === "btwn") || (type === "index")) {
if (!btwnValueField){ if (!btwnValueField){
@ -318,7 +312,7 @@
} else if (type === "istype") { } else if (type === "istype") {
if (!typeValueField){ if (!typeValueField){
typeValueField = createTypeValueField(); typeValueField = createTypeValueField(rowInputCell);
} }
typeValueField.typedInput('show'); typeValueField.typedInput('show');
fieldToFocus = typeValueField; fieldToFocus = typeValueField;
@ -351,7 +345,9 @@
} else { } else {
selectField.width("auto") selectField.width("auto")
} }
fieldToFocus.typedInput("focus"); if (fieldToFocus) {
fieldToFocus.typedInput("focus");
}
// Preselect the contents of the element // Preselect the contents of the element
// if (focusValueField && document.activeElement) { // if (focusValueField && document.activeElement) {
// document.activeElement.selectionStart = 0; // document.activeElement.selectionStart = 0;
@ -359,48 +355,26 @@
// } // }
}); });
selectField.val(rule.t); selectField.val(rule.t);
if ((rule.t == "btwn") || (rule.t == "index")) {
if (!btwnValueField){
btwnValueField = createBtwnValueField(rowInputCell);
}
btwnValueField.typedInput('value',rule.v);
btwnValueField.typedInput('type',rule.vt||'num');
if (!btwnValue2Field){ if ((rule.t == "btwn") || (rule.t == "index")) {
btwnValue2Field = createBtwnValue2Field(row3, andLabel); btwnValueField = createBtwnValueField(rowInputCell,rule.vt||'num');
} btwnValueField.typedInput('value',rule.v);
btwnValue2Field = createBtwnValue2Field(row3, andLabel,rule.v2t||'num');
btwnValue2Field.typedInput('value',rule.v2); btwnValue2Field.typedInput('value',rule.v2);
btwnValue2Field.typedInput('type',rule.v2t||'num');
} else if ((rule.t === "head") || (rule.t === "tail")) { } else if ((rule.t === "head") || (rule.t === "tail")) {
if (!numValueField){ numValueField = createNumValueField(rowInputCell,rule.vt||'num');
numValueField = createNumValueField(row);
}
numValueField.typedInput('value',rule.v); numValueField.typedInput('value',rule.v);
numValueField.typedInput('type',rule.vt||'num');
} else if (rule.t === "istype") { } else if (rule.t === "istype") {
if (!typeValueField){ typeValueField = createTypeValueField(rowInputCell,rule.vt);
typeValueField =createTypeValueField();
}
typeValueField.typedInput('value',rule.vt); typeValueField.typedInput('value',rule.vt);
typeValueField.typedInput('type',rule.vt);
} else if (rule.t === "jsonata_exp") { } else if (rule.t === "jsonata_exp") {
if (!expValueField){ expValueField = createExpValueField(rowInputCell,rule.vt||'jsonata');
expValueField = createExpValueField(row);
}
expValueField.typedInput('value',rule.v); expValueField.typedInput('value',rule.v);
expValueField.typedInput('type',rule.vt||'jsonata');
} else if (typeof rule.v != "undefined") { } else if (typeof rule.v != "undefined") {
if (!valueField){ valueField = createValueField(rowInputCell,rule.vt||'str');
valueField = createValueField(rowInputCell);
}
valueField.typedInput('value',rule.v); valueField.typedInput('value',rule.v);
valueField.typedInput('type',rule.vt||'str');
}
if (rule.case) {
caseSensitive.prop('checked',true);
} else {
caseSensitive.prop('checked',false);
} }
caseSensitive.prop('checked',!!rule.case);
selectField.change(); selectField.change();
var currentOutputs = JSON.parse(outputCount.val()||"{}"); var currentOutputs = JSON.parse(outputCount.val()||"{}");

View File

@ -115,12 +115,12 @@
var regex = this._("change.label.regex"); var regex = this._("change.label.regex");
var deepCopyLabel = this._("change.label.deepCopy"); var deepCopyLabel = this._("change.label.deepCopy");
function createPropertyValue(row2_1,row2_2) { function createPropertyValue(row2_1, row2_2, defaultType) {
var propValInput = $('<input/>',{class:"node-input-rule-property-value",type:"text"}) var propValInput = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2_1) .appendTo(row2_1)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']}); .typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
var dcLabel = $('<label style="padding-left: 130px; display: flex; align-items: center "></label>').appendTo(row2_2); var dcLabel = $('<label style="padding-left: 130px;"></label>').appendTo(row2_2);
var deepCopy = $('<input type="checkbox" class="node-input-rule-property-deepCopy" style="width: auto; margin: 0 6px 0 0">').appendTo(dcLabel) var deepCopy = $('<input type="checkbox" class="node-input-rule-property-deepCopy" style="width: auto; margin: 0 6px 0 0">').appendTo(dcLabel)
$('<span>').text(deepCopyLabel).appendTo(dcLabel) $('<span>').text(deepCopyLabel).appendTo(dcLabel)
@ -129,20 +129,20 @@
}) })
return [propValInput, deepCopy]; return [propValInput, deepCopy];
} }
function createFromValue(row3_1) { function createFromValue(row3_1, defaultType) {
return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"}) return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
.appendTo(row3_1) .appendTo(row3_1)
.typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']}); .typedInput({default:defaultType||'str',types:['msg','flow','global','str','re','num','bool','env']});
} }
function createToValue(row3_2) { function createToValue(row3_2, defaultType) {
return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"}) return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2) .appendTo(row3_2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']}); .typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
} }
function createMoveValue(row4) { function createMoveValue(row4, defaultType) {
return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"}) return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
.appendTo(row4) .appendTo(row4)
.typedInput({default:'msg',types:['msg','flow','global']}); .typedInput({default:defaultType||'msg',types:['msg','flow','global']});
} }
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({ $('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
@ -268,33 +268,22 @@
propertyName.typedInput('value',rule.p); propertyName.typedInput('value',rule.p);
propertyName.typedInput('type',rule.pt); propertyName.typedInput('type',rule.pt);
if (rule.t == "set") { if (rule.t == "set") {
if(!propertyValue) { var parts = createPropertyValue(row2_1, row2_2, rule.tot);
var parts = createPropertyValue(row2_1, row2_2); propertyValue = parts[0];
propertyValue = parts[0]; deepCopy = parts[1];
deepCopy = parts[1];
}
propertyValue.typedInput('value',rule.to); propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
deepCopy.prop("checked", !!rule.dc); deepCopy.prop("checked", !!rule.dc);
} }
if (rule.t == "move") { if (rule.t == "move") {
if(!moveValue) { moveValue = createMoveValue(row4,rule.tot);
moveValue = createMoveValue(row4);
}
moveValue.typedInput('value',rule.to); moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
} }
if (rule.t == "change") { if (rule.t == "change") {
if(!fromValue) { fromValue = createFromValue(row3_1, rule.fromt);
fromValue = createFromValue(row3_1);
}
fromValue.typedInput('value',rule.from); fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
if (!toValue) { toValue = createToValue(row3_2,rule.tot);
toValue = createToValue(row3_2);
}
toValue.typedInput('value',rule.to); toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
} }
selectField.change(); selectField.change();
container[0].appendChild(fragment); container[0].appendChild(fragment);

View File

@ -54,6 +54,18 @@
width: 15px; width: 15px;
height: 15px; height: 15px;
} }
.form-row-mqtt5 {
display: none;
}
.form-row-mqtt5.form-row-mqtt5-active:not(.form-row-mqtt-static-disabled) {
display: block
}
.form-row-mqtt-static-disabled {
display: none;
/* opacity: 0.3;
pointer-events: none; */
}
</style> </style>
<script type="text/html" data-template-name="mqtt in"> <script type="text/html" data-template-name="mqtt in">
@ -62,10 +74,18 @@
<input type="text" id="node-input-broker"> <input type="text" id="node-input-broker">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-topicType" data-i18n="mqtt.label.action"></label>
<select id="node-input-topicType" style="width: 70%">
<option value="topic" data-i18n="mqtt.label.staticTopic"></option>
<option value="dynamic" data-i18n="mqtt.label.dynamicTopic"></option>
</select>
<input type="hidden" id="node-input-inputs">
</div>
<div class="form-row form-row-mqtt-static">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label> <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic"> <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
</div> </div>
<div class="form-row"> <div class="form-row form-row-mqtt-static">
<label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label> <label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
<select id="node-input-qos" style="width:125px !important"> <select id="node-input-qos" style="width:125px !important">
<option value="0">0</option> <option value="0">0</option>
@ -73,17 +93,7 @@
<option value="2">2</option> <option value="2">2</option>
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row mqtt-flags-row form-row-mqtt5 form-row-mqtt-static">
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="mqtt.label.output"></span></label>
<select id="node-input-datatype" style="width:70%;">
<option value="auto" data-i18n="mqtt.output.auto"></option>
<option value="buffer" data-i18n="mqtt.output.buffer"></option>
<option value="utf8" data-i18n="mqtt.output.string"></option>
<option value="json" data-i18n="mqtt.output.json"></option>
<option value="base64" data-i18n="mqtt.output.base64"></option>
</select>
</div>
<div class="form-row mqtt-flags-row mqtt5">
<label for="node-input-nl" ><i class="fa fa-flag"></i> <span data-i18n="mqtt.label.flags">Flags</span></label> <label for="node-input-nl" ><i class="fa fa-flag"></i> <span data-i18n="mqtt.label.flags">Flags</span></label>
<div class="mqtt-flags"> <div class="mqtt-flags">
<div class="mqtt-flag"> <div class="mqtt-flag">
@ -100,7 +110,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-row mqtt5"> <div class="form-row form-row-mqtt5 form-row-mqtt-static">
<label for="node-input-rh" style="width:100%"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.rh"></span></label> <label for="node-input-rh" style="width:100%"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.rh"></span></label>
<select id="node-input-rh" style="margin-left: 104px; width: 70%"> <select id="node-input-rh" style="margin-left: 104px; width: 70%">
<option value="0" data-i18n="mqtt.label.rh0"></option> <option value="0" data-i18n="mqtt.label.rh0"></option>
@ -108,6 +118,16 @@
<option value="2" data-i18n="mqtt.label.rh2"></option> <option value="2" data-i18n="mqtt.label.rh2"></option>
</select> </select>
</div> </div>
<div class="form-row">
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="mqtt.label.output"></span></label>
<select id="node-input-datatype" style="width:70%;">
<option value="auto" data-i18n="mqtt.output.auto"></option>
<option value="buffer" data-i18n="mqtt.output.buffer"></option>
<option value="utf8" data-i18n="mqtt.output.string"></option>
<option value="json" data-i18n="mqtt.output.json"></option>
<option value="base64" data-i18n="mqtt.output.base64"></option>
</select>
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
@ -185,6 +205,10 @@
<label for="node-config-input-port" style="margin-left:20px; width:43px; "> <span data-i18n="mqtt.label.port"></span></label> <label for="node-config-input-port" style="margin-left:20px; width:43px; "> <span data-i18n="mqtt.label.port"></span></label>
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:55px"> <input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:55px">
</div> </div>
<div class="form-row" style="margin-bottom:0">
<input type="checkbox" id="node-config-input-autoConnect" style="margin: 0 5px 0 104px; display: inline-block; width: auto;">
<label for="node-config-input-autoConnect" style="width: auto"><span data-i18n="mqtt.label.auto-connect"></span></label>
</div>
<div class="form-row" style="height: 34px;"> <div class="form-row" style="height: 34px;">
<input type="checkbox" id="node-config-input-usetls" style="height: 34px; margin: 0 5px 0 104px; display: inline-block; width: auto; vertical-align: top;"> <input type="checkbox" id="node-config-input-usetls" style="height: 34px; margin: 0 5px 0 104px; display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-usetls" style="width: 100px; line-height: 34px;"><span data-i18n="mqtt.label.use-tls"></span></label> <label for="node-config-input-usetls" style="width: 100px; line-height: 34px;"><span data-i18n="mqtt.label.use-tls"></span></label>
@ -434,6 +458,7 @@
return (this.cleansession===undefined || this.cleansession) || (v||"").length > 0; return (this.cleansession===undefined || this.cleansession) || (v||"").length > 0;
} }
}}, }},
autoConnect: {value: true},
usetls: {value: false}, usetls: {value: false},
verifyservercert: { value: false}, verifyservercert: { value: false},
compatmode: { value: false}, compatmode: { value: false},
@ -558,6 +583,10 @@
this.usetls = false; this.usetls = false;
$("#node-config-input-usetls").prop("checked",false); $("#node-config-input-usetls").prop("checked",false);
} }
if (typeof this.autoConnect === 'undefined') {
this.autoConnect = true;
$("#node-config-input-autoConnect").prop("checked",true);
}
if (this.compatmode === 'true' || this.compatmode === true) { if (this.compatmode === 'true' || this.compatmode === true) {
delete this.compatmode; delete this.compatmode;
this.protocolVersion = 4; this.protocolVersion = 4;
@ -704,11 +733,22 @@
} }
}); });
RED.nodes.registerType('mqtt in',{ RED.nodes.registerType('mqtt in',{
category: 'network', category: 'network',
defaults: { defaults: {
name: {value:""}, name: {value:""},
topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)}, topic: {
value:"",
validate: function(v) {
var isDynamic = this.inputs === 1;
var topicTypeSelect = $("#node-input-topicType");
if (topicTypeSelect.length) {
isDynamic = topicTypeSelect.val()==='dynamic'
}
return isDynamic || ((!!v) && RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)(v));
}
},
qos: {value: "2"}, qos: {value: "2"},
datatype: {value:"auto",required:true}, datatype: {value:"auto",required:true},
broker: {type:"mqtt-broker", required:true}, broker: {type:"mqtt-broker", required:true},
@ -716,33 +756,64 @@
nl: {value:false}, nl: {value:false},
rap: {value:true}, rap: {value:true},
rh: {value:0}, rh: {value:0},
inputs: {value:0},
}, },
color:"#d8bfd8", color:"#d8bfd8",
inputs:0, inputs:0,
outputs:1, outputs:1,
icon: "bridge.svg", icon: "bridge.svg",
label: function() { label: function() {
return this.name||this.topic||"mqtt"; var label = "mqtt";
if(this.topicType !== "dynamic" && this.topic) {
label = this.topic;
}
return this.name || label;
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
oneditprepare: function() { oneditprepare: function() {
$("#node-input-broker").on("change",function(d){ const node = this;
const isV5Broker = function() {
var confNode = RED.nodes.node($("#node-input-broker").val()); var confNode = RED.nodes.node($("#node-input-broker").val());
var v5 = confNode && confNode.protocolVersion == "5"; return confNode && confNode.protocolVersion === "5";
if(v5) { }
$("div.form-row.mqtt5").show(); const isDynamic = function() {
} else { return $('#node-input-topicType').val() === "dynamic";
$("div.form-row.mqtt5").hide(); }
} const updateVisibility = function() {
var v5 = isV5Broker();
var dynamic = isDynamic();
$("div.form-row-mqtt5").toggleClass("form-row-mqtt5-active",!!v5);
$("div.form-row.form-row-mqtt-static").toggleClass("form-row-mqtt-static-disabled", !!dynamic)
}
$("#node-input-broker").on("change",function(d){
updateVisibility();
}); });
$('#node-input-topicType').on("change", function () {
$("#node-input-inputs").val(isDynamic() ? 1 : 0);
updateVisibility();
});
if (this.inputs === 1) {
$('#node-input-topicType').val('dynamic')
} else {
$('#node-input-topicType').val('topic')
}
$('#node-input-topicType').trigger("change");
if (this.qos === undefined) { if (this.qos === undefined) {
$("#node-input-qos").val("2"); $("#node-input-qos").val("2");
} }
if (this.datatype === undefined) { if (this.datatype === undefined) {
$("#node-input-datatype").val("auto"); $("#node-input-datatype").val("auto");
} }
},
oneditsave: function() {
if ($('#node-input-topicType').val() === "dynamic") {
$('#node-input-topic').val("");
}
} }
}); });

File diff suppressed because it is too large Load Diff

View File

@ -143,12 +143,17 @@
"filterSelected": "selected nodes", "filterSelected": "selected nodes",
"filterCurrent": "current flow", "filterCurrent": "current flow",
"debugNodes": "Debug nodes", "debugNodes": "Debug nodes",
"clearLog": "Clear log", "clearLog": "Clear messages",
"filterLog": "Filter log", "clearFilteredLog": "Clear filtered messages",
"filterLog": "Filter messages",
"openWindow": "Open in new window", "openWindow": "Open in new window",
"copyPath": "Copy path", "copyPath": "Copy path",
"copyPayload": "Copy value", "copyPayload": "Copy value",
"pinPath": "Pin open" "pinPath": "Pin open",
"selectAll": "select all",
"selectNone": "select none",
"all": "all",
"filtered": "filtered"
}, },
"messageMenu": { "messageMenu": {
"collapseAll": "Collapse all paths", "collapseAll": "Collapse all paths",
@ -411,7 +416,11 @@
"maximumPacketSize": "Max Packet Size", "maximumPacketSize": "Max Packet Size",
"receiveMaximum": "Receive Max", "receiveMaximum": "Receive Max",
"session": "Session", "session": "Session",
"delay": "Delay" "delay": "Delay",
"action": "Action",
"staticTopic": "Subscribe to single topic",
"dynamicTopic": "Dynamic subscription",
"auto-connect": "Connect automatically"
}, },
"sections-label":{ "sections-label":{
"birth-message": "Message sent on connection (birth message)", "birth-message": "Message sent on connection (birth message)",
@ -452,7 +461,10 @@
"invalid-topic": "Invalid topic specified", "invalid-topic": "Invalid topic specified",
"nonclean-missingclientid": "No client ID set, using clean session", "nonclean-missingclientid": "No client ID set, using clean session",
"invalid-json-string": "Invalid JSON string", "invalid-json-string": "Invalid JSON string",
"invalid-json-parse": "Failed to parse JSON string" "invalid-json-parse": "Failed to parse JSON string",
"invalid-action-action": "Invalid action specified",
"invalid-action-alreadyconnected": "Disconnect from broker before connecting",
"invalid-action-badsubscription": "msg.topic is missing or invalid"
} }
}, },
"httpin": { "httpin": {

View File

@ -40,6 +40,38 @@
<p>This node requires a connection to a MQTT broker to be configured. This is configured by clicking <p>This node requires a connection to a MQTT broker to be configured. This is configured by clicking
the pencil icon.</p> the pencil icon.</p>
<p>Several MQTT nodes (in or out) can share the same broker connection if required.</p> <p>Several MQTT nodes (in or out) can share the same broker connection if required.</p>
<h4>Dynamic Subscription</h4>
The node can be configured to dynamically control the MQTT connection and its subscriptions. When
enabled, the node will have an input and can be controlled by passing it messages.
<h3>Inputs</h3>
<p>These only apply when the node has been configured for dynamic subscriptions.</p>
<dl class="message-properties">
<dt>action <span class="property-type">string</span></dt>
<dd>the name of the action the node should perform. Available actions are: <code>"connect"</code>,
<code>"disconnect"</code>, <code>"subscribe"</code> and <code>"unsubscribe"</code>.</dd>
<dt class="optional">topic <span class="property-type">string|object|array</span></dt>
<dd>For the <code>"subscribe"</code> and <code>"unsubscribe"</code> actions, this property
provides the topic. It can be set as either:<ul>
<li>a String continaing the topic filter</li>
<li>an Object containing <code>topic</code> and <code>qos</code> properties</li>
<li>an array of either strings or objects to handle multiple topics in one</li>
</ul>
</dd>
<dt class="optional">broker <span class="property-type">broker</span> </dt>
<dd>For the <code>"connect"</code> action, this property can override any
of the individual broker configuration settings, including: <ul>
<li><code>broker</code></li>
<li><code>port</code></li>
<li><code>url</code> - overrides broker/port to provide a complete connection url</li>
<li><code>username</code></li>
<li><code>password</code></li>
</ul>
<p>If this property is set and the broker is already connected an error
will be logged unless it has the <code>force</code> property set - in which case it will
disconnect from the broker, apply the new settings and reconnect.</p>
</dd>
</dl>
</script> </script>
<script type="text/html" data-help-name="mqtt out"> <script type="text/html" data-help-name="mqtt out">
@ -78,6 +110,30 @@
<p>This node requires a connection to a MQTT broker to be configured. This is configured by clicking <p>This node requires a connection to a MQTT broker to be configured. This is configured by clicking
the pencil icon.</p> the pencil icon.</p>
<p>Several MQTT nodes (in or out) can share the same broker connection if required.</p> <p>Several MQTT nodes (in or out) can share the same broker connection if required.</p>
<h4>Dynamic Control</h4>
The connection shared by the node can be controlled dynamically. If the node receives
one of the following control messages, it will not publish the message payload as well.
<h3>Inputs</h3>
<dl class="message-properties">
<dt>action <span class="property-type">string</span></dt>
<dd>the name of the action the node should perform. Available actions are: <code>"connect"</code>,
and <code>"disconnect"</code>.</dd>
<dt class="optional">broker <span class="property-type">broker</span> </dt>
<dd>For the <code>"connect"</code> action, this property can override any
of the individual broker configuration settings, including: <ul>
<li><code>broker</code></li>
<li><code>port</code></li>
<li><code>url</code> - overrides broker/port to provide a complete connection url</li>
<li><code>username</code></li>
<li><code>password</code></li>
</ul>
<p>If this property is set and the broker is already connected an error
will be logged unless it has the <code>force</code> property set - in which case it will
disconnect from the broker, apply the new settings and reconnect.</p>
</dd>
</dl>
</script> </script>
<script type="text/html" data-help-name="mqtt-broker"> <script type="text/html" data-help-name="mqtt-broker">

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/nodes", "name": "@node-red/nodes",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/registry", "name": "@node-red/registry",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,7 +16,7 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/util": "2.1.0-beta.1", "@node-red/util": "2.1.0-beta.2",
"clone": "2.1.2", "clone": "2.1.2",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"semver": "7.3.5", "semver": "7.3.5",

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/runtime", "name": "@node-red/runtime",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,8 +16,8 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/registry": "2.1.0-beta.1", "@node-red/registry": "2.1.0-beta.2",
"@node-red/util": "2.1.0-beta.1", "@node-red/util": "2.1.0-beta.2",
"async-mutex": "0.3.2", "async-mutex": "0.3.2",
"clone": "2.1.2", "clone": "2.1.2",
"express": "4.17.1", "express": "4.17.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/util", "name": "@node-red/util",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
"type": "git", "type": "git",
@ -16,7 +16,7 @@
], ],
"dependencies": { "dependencies": {
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"i18next": "21.2.4", "i18next": "21.3.1",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.8.5", "jsonata": "1.8.5",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red", "name": "node-red",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.2",
"description": "Low-code programming for event-driven applications", "description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org", "homepage": "http://nodered.org",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -31,15 +31,15 @@
"flow" "flow"
], ],
"dependencies": { "dependencies": {
"@node-red/editor-api": "2.1.0-beta.1", "@node-red/editor-api": "2.1.0-beta.2",
"@node-red/runtime": "2.1.0-beta.1", "@node-red/runtime": "2.1.0-beta.2",
"@node-red/util": "2.1.0-beta.1", "@node-red/util": "2.1.0-beta.2",
"@node-red/nodes": "2.1.0-beta.1", "@node-red/nodes": "2.1.0-beta.2",
"basic-auth": "2.0.1", "basic-auth": "2.0.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"express": "4.17.1", "express": "4.17.1",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"node-red-admin": "^2.2.0", "node-red-admin": "^2.2.1",
"nopt": "5.0.0", "nopt": "5.0.0",
"semver": "7.3.5" "semver": "7.3.5"
}, },