diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
index 00666e5b4..ea1938e5c 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
@@ -18,7 +18,7 @@
/**
* options:
* - addButton : boolean|string - text for add label, default 'add'
- * - buttons : array - list of custom buttons (objects with fields 'label', 'icon', 'title', 'click')
+ * - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click')
* - height : number|'auto'
* - resize : function - called when list as a whole is resized
* - resizeItem : function(item) - called to resize individual item
@@ -94,7 +94,7 @@
}
buttons.forEach(function(button) {
- var element = $('')
+ var element = $('')
.appendTo(that.topContainer)
.on("click", function(evt) {
evt.preventDefault();
@@ -103,6 +103,9 @@
}
});
+ if (button.id) {
+ element.attr("id", button.id);
+ }
if (button.title) {
element.attr("title", button.title);
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js
index 2e7cd8f03..b7e2325c5 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tray.js
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
-RED.tray = (function() {
+ RED.tray = (function() {
var stack = [];
var editorStack;
@@ -288,9 +288,9 @@ RED.tray = (function() {
right: -(tray.tray.width()+10)+"px"
});
setTimeout(function() {
- if (tray.options.close) {
- tray.options.close();
- }
+ try {
+ if (tray.options.close) { tray.options.close(); }
+ } catch (ex) { }
tray.tray.remove();
if (stack.length > 0) {
var oldTray = stack[stack.length-1];
diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.html b/packages/node_modules/@node-red/nodes/core/common/20-inject.html
index 9bf91ad1e..8e40b8cd8 100644
--- a/packages/node_modules/@node-red/nodes/core/common/20-inject.html
+++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.html
@@ -162,7 +162,62 @@
height += 16;
$("#node-input-property-container").editableList('height',height);
}
-
+ /** Retrieve editableList items (refactored for re-use in the form inject button)*/
+ function getProps(el, legacy) {
+ var result = {
+ props: []
+ }
+ el.each(function(i) {
+ var prop = $(this);
+ var p = {
+ p:prop.find(".node-input-prop-property-name").typedInput('value')
+ };
+ if (p.p) {
+ p.v = prop.find(".node-input-prop-property-value").typedInput('value');
+ p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
+ if(legacy) {
+ if (p.p === "payload") { // save payload to old "legacy" property
+ result.payloadType = p.vt;
+ result.payload = p.v;
+ delete p.v;
+ delete p.vt;
+ } else if (p.p === "topic" && p.vt === "str") {
+ result.topic = p.v;
+ delete p.v;
+ }
+ }
+ result.props.push(p);
+ }
+ });
+ return result;
+ }
+ /** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
+ function doInject(node, customMsg) {
+ var label = node._def.label.call(node);
+ if (label.length > 30) {
+ label = label.substring(0, 50) + "...";
+ }
+ label = label.replace(/&/g, "&").replace(//g, ">");
+ $.ajax({
+ url: "inject/" + node.id,
+ type: "POST",
+ data: customMsg,
+ success: function (resp) {
+ RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
+ },
+ error: function (jqXHR, textStatus, errorThrown) {
+ if (jqXHR.status == 404) {
+ RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.not-deployed") }), "error");
+ } else if (jqXHR.status == 500) {
+ RED.notify(node._("common.notification.error", { message: node._("inject.errors.failed") }), "error");
+ } else if (jqXHR.status == 0) {
+ RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.no-response") }), "error");
+ } else {
+ RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.unexpected", { status: jqXHR.status, message: textStatus }) }), "error");
+ }
+ }
+ });
+ }
RED.nodes.registerType('inject',{
category: 'common',
color:"#a6bbcf",
@@ -283,14 +338,15 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
- if (this.payloadType == null) {
- if (this.payload == "") {
- this.payloadType = "date";
+ var node = this;
+ if (node.payloadType == null) {
+ if (node.payload == "") {
+ node.payloadType = "date";
} else {
- this.payloadType = "str";
+ node.payloadType = "str";
}
- } else if (this.payloadType === 'string' || this.payloadType === 'none') {
- this.payloadType = "str";
+ } else if (node.payloadType === 'string' || node.payloadType === 'none') {
+ node.payloadType = "str";
}
$("#inject-time-type-select").on("change", function() {
@@ -345,17 +401,17 @@
});
var repeattype = "none";
- if (this.repeat != "" && this.repeat != 0) {
+ if (node.repeat != "" && node.repeat != 0) {
repeattype = "interval";
var r = "s";
- var c = this.repeat;
- if (this.repeat % 60 === 0) { r = "m"; c = c/60; }
- if (this.repeat % 1440 === 0) { r = "h"; c = c/60; }
+ var c = node.repeat;
+ if (node.repeat % 60 === 0) { r = "m"; c = c/60; }
+ if (node.repeat % 1440 === 0) { r = "h"; c = c/60; }
$("#inject-time-interval-count").val(c);
$("#inject-time-interval-units").val(r);
$("#inject-time-interval-days").prop("disabled","disabled");
- } else if (this.crontab) {
- var cronparts = this.crontab.split(" ");
+ } else if (node.crontab) {
+ var cronparts = node.crontab.split(" ");
var days = cronparts[4];
if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
repeattype = "time";
@@ -431,7 +487,24 @@
/* */
- $('#node-input-property-container').css('min-height','120px').css('min-width','450px').editableList({
+ var eList = $('#node-input-property-container').css('min-height','120px').css('min-width','450px');
+
+ eList.editableList({
+ buttons: [
+ {
+ id: "node-inject-test-inject-button",
+ label: node._("inject.injectNow"),
+ click: function(e) {
+ var items = eList.editableList('items');
+ var result = getProps(items);
+ var m = {__user_inject_props__: []};
+ if (result && result.props && result.props.length) {
+ m.__user_inject_props__ = result.props;
+ }
+ doInject(node, m);
+ }
+ }
+ ],
addItem: function(container,i,opt) {
var prop = opt;
if (!prop.hasOwnProperty('p')) {
@@ -465,33 +538,38 @@
removable: true,
sortable: true
});
+ $('#node-inject-test-inject-button').css("float", "right").css("margin-right", "unset");
- if (!this.props) {
+ if (RED.nodes.subflow(node.z)) {
+ $('#node-inject-test-inject-button').attr("disabled",true);
+ }
+
+ if (!node.props) {
var payload = {
p:'payload',
- v: this.payload ? this.payload : '',
- vt:this.payloadType ? this.payloadType : 'date'
+ v: node.payload ? node.payload : '',
+ vt:node.payloadType ? node.payloadType : 'date'
};
var topic = {
p:'topic',
- v: this.topic ? this.topic : '',
+ v: node.topic ? node.topic : '',
vt:'string'
}
- this.props = [payload,topic];
+ node.props = [payload,topic];
}
- for (var i=0; i 30) {
- label = label.substring(0,50)+"...";
- }
- label = label.replace(/&/g,"&").replace(//g,">");
- var node = this;
- $.ajax({
- url: "inject/"+this.id,
- type:"POST",
- success: function(resp) {
- RED.notify(node._("inject.success",{label:label}),{type:"success",id:"inject", timeout: 2000});
- },
- error: function(jqXHR,textStatus,errorThrown) {
- if (jqXHR.status == 404) {
- RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.not-deployed")}),"error");
- } else if (jqXHR.status == 500) {
- RED.notify(node._("common.notification.error",{message:node._("inject.errors.failed")}),"error");
- } else if (jqXHR.status == 0) {
- RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.no-response")}),"error");
- } else {
- RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:jqXHR.status,message:textStatus})}),"error");
- }
- }
- });
+ doInject(this);
}
},
oneditresize: resizeDialog
diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.js b/packages/node_modules/@node-red/nodes/core/common/20-inject.js
index 772e238a3..26be74bfa 100644
--- a/packages/node_modules/@node-red/nodes/core/common/20-inject.js
+++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.js
@@ -100,8 +100,12 @@ module.exports = function(RED) {
this.on("input", function(msg, send, done) {
var errors = [];
-
- this.props.forEach(p => {
+ var props = this.props;
+ if(msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
+ props = msg.__user_inject_props__;
+ }
+ delete msg.__user_inject_props__;
+ props.forEach(p => {
var property = p.p;
var value = p.v ? p.v : '';
var valueType = p.vt ? p.vt : 'str';
@@ -156,7 +160,11 @@ module.exports = function(RED) {
var node = RED.nodes.getNode(req.params.id);
if (node != null) {
try {
- node.receive();
+ if (req.body && req.body.__user_inject_props__) {
+ node.receive(req.body);
+ } else {
+ node.receive();
+ }
res.sendStatus(200);
} catch(err) {
res.sendStatus(500);
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index 6945ee8ae..e5108710b 100755
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -32,6 +32,7 @@
},
"inject": {
"inject": "inject",
+ "injectNow": "inject now",
"repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "stopped",
diff --git a/test/nodes/core/common/20-inject_spec.js b/test/nodes/core/common/20-inject_spec.js
index ff5eb7f73..f760bb646 100644
--- a/test/nodes/core/common/20-inject_spec.js
+++ b/test/nodes/core/common/20-inject_spec.js
@@ -510,6 +510,36 @@ describe('inject node', function() {
});
});
+
+ it('should inject custom properties in message', function (done) {
+ //n1: inject node with { topic:"static", payload:"static", bool1:true, str1:"1" }
+ var flow = [{id: "n1", type: "inject", props: [{p:"payload", v:"static", vt:"str"}, {p:"topic", v:"static", vt:"str"}, {p:"bool1", v:"true", vt:"bool"}, {p:"str1", v:"1", vt:"str"}], wires: [["n2"]], z: "flow"},
+ {id: "n2", type: "helper"}];
+ helper.load(injectNode, flow, function () {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on("input", function (msg) {
+ try {
+ msg.should.not.have.property("payload"); //payload removed
+ msg.should.have.property("topic", "t_override"); //changed value to t_override
+ msg.should.have.property("str1", 1);//changed type from str to num
+ msg.should.have.property("num1", 1);//new prop
+ msg.should.have.property("bool1", false);//changed value to false
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ n1.receive({ __user_inject_props__: [
+ {p:"topic", v:"t_override", vt:"str"}, //change value to t_override
+ {p:"str1", v:"1", vt:"num"}, //change type
+ {p:"num1", v:"1", vt:"num"}, //new prop
+ {p:"bool1", v:"false", vt:"bool"}, //change value to false
+ ]});
+ });
+ });
+
+
it('should inject multiple properties using legacy props if needed', function (done) {
var flow = [{id: "n1", type: "inject", payload:"123", payloadType:"num", topic:"foo", props: [{p:"topic", vt:"str"}, {p:"payload"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
@@ -592,6 +622,46 @@ describe('inject node', function() {
});
});
+ it('should inject custom properties in posted message', function(done) {
+ var flow = [{id:"n1", type:"inject", payloadType:"str", topic: "t4",payload:"hello", wires:[["n4"]] },
+ { id:"n4", type:"helper"}];
+ helper.load(injectNode, flow, function() {
+ var n4 = helper.getNode("n4");
+ n4.on("input", function(msg) {
+ msg.should.not.have.property("payload"); //payload removed
+ msg.should.have.property("topic", "t_override"); //changed value to t_override
+ msg.should.have.property("str1", "1"); //injected prop
+ msg.should.have.property("num1", 1); //injected prop
+ msg.should.have.property("bool1", true); //injected prop
+
+ helper.clearFlows().then(function() {
+ done();
+ });
+ });
+ try {
+ helper.request()
+ .post('/inject/n1')
+ .send({ __user_inject_props__: [
+ {p:"topic", v:"t_override", vt:"str"}, //change value to t_override
+ {p:"str1", v:"1", vt:"str"}, //new prop
+ {p:"num1", v:"1", vt:"num"}, //new prop
+ {p:"bool1", v:"true", vt:"bool"}, //new prop
+ ]})
+ .expect(200).end(function(err) {
+ if (err) {
+ console.log(err);
+ return helper.clearFlows()
+ .then(function () {
+ done(err);
+ });
+ }
+ });
+ } catch(err) {
+ done(err);
+ }
+ });
+ });
+
it('should fail for invalid node', function(done) {
helper.request().post('/inject/invalid').expect(404).end(done);
});