mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Update Join node runtime to match UI changes
This commit is contained in:
parent
9f8c32ce8f
commit
e594ffe0f8
@ -124,21 +124,11 @@
|
||||
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
|
||||
var hasSubOptions = false;
|
||||
this.typeMap = {};
|
||||
this.types = this.options.types.map(function(opt) {
|
||||
var result;
|
||||
if (typeof opt === 'string') {
|
||||
result = allOptions[opt];
|
||||
} else {
|
||||
result = opt;
|
||||
}
|
||||
that.typeMap[result.value] = result;
|
||||
if (result.options) {
|
||||
hasSubOptions = true;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
this.selectTrigger = $('<a href="#"></a>').prependTo(this.uiSelect);
|
||||
$('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
|
||||
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
|
||||
|
||||
this.types(this.options.types);
|
||||
|
||||
if (this.options.typeField) {
|
||||
this.typeField = $(this.options.typeField).hide();
|
||||
@ -150,13 +140,6 @@
|
||||
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
|
||||
}
|
||||
|
||||
this.selectTrigger = $('<a href="#"></a>').prependTo(this.uiSelect);
|
||||
$('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
|
||||
if (this.types.length === 1) {
|
||||
this.selectTrigger.addClass("disabled");
|
||||
}
|
||||
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
|
||||
|
||||
this.element.on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
});
|
||||
@ -166,36 +149,29 @@
|
||||
this.element.on('change', function() {
|
||||
that.validate();
|
||||
})
|
||||
if (this.types.length > 1) {
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
if (that.typeList.length > 1) {
|
||||
that._showMenu(that.menu,that.selectTrigger);
|
||||
});
|
||||
} else {
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
} else {
|
||||
that.element.focus();
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
|
||||
this.optionSelectTrigger = $('<a href="#" class="red-ui-typedInput-option-trigger" style="display:inline-block"><i class="fa fa-sort-desc"></i></a>').appendTo(this.uiSelect);
|
||||
this.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger);
|
||||
this.optionSelectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
if (that.optionMenu) {
|
||||
that.optionMenu.css({
|
||||
minWidth:that.optionSelectLabel.width()
|
||||
});
|
||||
|
||||
if (hasSubOptions) {
|
||||
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
|
||||
this.optionSelectTrigger = $('<a href="#" class="red-ui-typedInput-option-trigger" style="display:inline-block"><i class="fa fa-sort-desc"></i></a>').appendTo(this.uiSelect);
|
||||
this.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger);
|
||||
this.optionSelectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
if (that.optionMenu) {
|
||||
that.optionMenu.css({
|
||||
minWidth:that.optionSelectLabel.width()
|
||||
});
|
||||
|
||||
that._showMenu(that.optionMenu,that.optionSelectLabel)
|
||||
}
|
||||
});
|
||||
}
|
||||
this.menu = this._createMenu(this.types, function(v) { that.type(v) });
|
||||
this.type(this.options.default||this.types[0].value);
|
||||
that._showMenu(that.optionMenu,that.optionSelectLabel)
|
||||
}
|
||||
});
|
||||
this.type(this.options.default||this.typeList[0].value);
|
||||
},
|
||||
_hideMenu: function(menu) {
|
||||
$(document).off("mousedown.close-property-select");
|
||||
@ -278,7 +254,6 @@
|
||||
return labelWidth;
|
||||
},
|
||||
_resize: function() {
|
||||
|
||||
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
|
||||
this.selectTrigger.width(this.uiWidth+5);
|
||||
} else {
|
||||
@ -298,6 +273,29 @@
|
||||
_destroy: function() {
|
||||
this.menu.remove();
|
||||
},
|
||||
types: function(types) {
|
||||
var that = this;
|
||||
var currentType = this.type();
|
||||
this.typeMap = {};
|
||||
this.typeList = types.map(function(opt) {
|
||||
var result;
|
||||
if (typeof opt === 'string') {
|
||||
result = allOptions[opt];
|
||||
} else {
|
||||
result = opt;
|
||||
}
|
||||
that.typeMap[result.value] = result;
|
||||
return result;
|
||||
});
|
||||
this.selectTrigger.toggleClass("disabled", this.typeList.length === 1);
|
||||
if (this.menu) {
|
||||
this.menu.remove();
|
||||
}
|
||||
this.menu = this._createMenu(this.typeList, function(v) { that.type(v) });
|
||||
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
||||
this.type(this.typeList[0].value);
|
||||
}
|
||||
},
|
||||
width: function(desiredWidth) {
|
||||
this.uiWidth = desiredWidth;
|
||||
this._resize();
|
||||
@ -359,9 +357,14 @@
|
||||
this.optionSelectTrigger.hide();
|
||||
}
|
||||
if (opt.hasValue === false) {
|
||||
this.oldValue = this.element.val();
|
||||
this.element.val("");
|
||||
this.element.hide();
|
||||
} else {
|
||||
if (this.oldValue !== undefined) {
|
||||
this.element.val(this.oldValue);
|
||||
delete this.oldValue;
|
||||
}
|
||||
this.element.show();
|
||||
}
|
||||
this.element.trigger('change');
|
||||
|
@ -44,7 +44,7 @@
|
||||
color:"#E2D96E",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
splt: {value:""}
|
||||
splt: {value:"\\n"}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
@ -61,12 +61,12 @@
|
||||
|
||||
<script type="text/x-red" data-template-name="join">
|
||||
<div class="form-row">
|
||||
<label for="node-input-out"><i class="fa fa-long-arrow-right"></i> Output</label>
|
||||
<select id="node-input-out" style="width:70%; margin-right:5px;">
|
||||
<label for="node-input-build"><i class="fa fa-long-arrow-right"></i> Output</label>
|
||||
<select id="node-input-build" style="width:70%; margin-right:5px;">
|
||||
<option value="auto">automatic</option>
|
||||
<option value="str">a String of joined properties</option>
|
||||
<option value="arr">an Array of properties</option>
|
||||
<option value="obj">an Object of key/property pairs</option>
|
||||
<option value="string">a String of joined properties</option>
|
||||
<option value="array">an Array of properties</option>
|
||||
<option value="object">an Object of key/property pairs</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-row-key">
|
||||
@ -78,22 +78,24 @@
|
||||
<input type="text" id="node-input-property">
|
||||
<input type="hidden" id="node-input-propertyType">
|
||||
</div>
|
||||
<div class="form-row node-row-join">
|
||||
<label for="node-input-join" style="padding-right: 10px; box-sizing: border-box; text-align: right;">joined using</label>
|
||||
<input type="text" id="node-input-join" style="width: 40px">
|
||||
<div class="form-row node-row-joiner">
|
||||
<label for="node-input-joiner" style="padding-right: 10px; box-sizing: border-box; text-align: right;">joined using</label>
|
||||
<input type="text" id="node-input-joiner" style="width: 40px">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-row-trigger">
|
||||
<label style="width: auto;">Send the message:</label>
|
||||
<div style="height: 40px;">
|
||||
<label style="margin-left: 40px; width: auto;"><input type="checkbox" style="width: auto;margin-bottom: 5px;"> after a fixed number of messages: <input type="text" style="width: 75px;"></label>
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
<label style="margin-left: 40px; width: auto;"><input type="checkbox" style="width: auto;margin-bottom: 5px;"> after a timeout following the first message: <input placeholder="seconds" type="text" style="width: 75px;"></label>
|
||||
</div>
|
||||
<div style="height: 40px; padding-top: 6px;" class="node-row-accumulate">
|
||||
<label style="margin-left: 40px; width: auto;"><input type="checkbox" style="width: auto;margin-bottom: 5px;"> whenever a property is updated</label>
|
||||
</div>
|
||||
<ul>
|
||||
<li style="height: 40px;">
|
||||
<label style="width: 280px;" for="node-input-count">After a fixed number of messages:</label> <input id="node-input-count" placeholder="count" type="text" style="width: 75px;">
|
||||
</li>
|
||||
<li style="height: 40px;">
|
||||
<label style="width: 280px;" for="node-input-timeout">After a timeout following the first message:</label> <input id="node-input-timeout" placeholder="seconds" type="text" style="width: 75px;">
|
||||
</li>
|
||||
<li style="height: 40px; padding-top: 6px;" class="node-row-accumulate">
|
||||
<label style="width: auto;"><input type="checkbox" id="node-input-accumulate" style="width: auto;margin-bottom: 5px;margin-right: 5px;"> whenever a property is updated</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-tips form-tips-auto hide">This mode assumes this node is either
|
||||
@ -122,13 +124,14 @@
|
||||
color:"#E2D96E",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
out: { value:"auto"},
|
||||
build: { value:"auto"},
|
||||
property: { value: "payload"},
|
||||
propertyType: { value:"msg"},
|
||||
key: {value:"topic"},
|
||||
joiner: { value:"\\n"},
|
||||
timeout: {value:""},
|
||||
timerr: {value:"send"},
|
||||
count: {value:""},
|
||||
joiner: {value:""},
|
||||
build: {value:"array"}
|
||||
accumulate: {value: false}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
@ -140,14 +143,19 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$("#node-input-out").change(function(e) {
|
||||
$("#node-input-build").change(function(e) {
|
||||
var val = $(this).val();
|
||||
$(".node-row-key").toggle(val==='obj');
|
||||
$(".node-row-property").toggle(val!=='auto');
|
||||
$(".node-row-join").toggle(val==='str');
|
||||
$(".node-row-joiner").toggle(val==='str');
|
||||
$(".node-row-trigger").toggle(val!=='auto');
|
||||
$(".node-row-accumulate").toggle(val==='obj');
|
||||
$(".form-tips-auto").toggle(val==='auto');
|
||||
if (val === 'str') {
|
||||
$("#node-input-property").typedInput('types',['msg']);
|
||||
} else if (val !== 'auto') {
|
||||
$("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]);
|
||||
}
|
||||
})
|
||||
|
||||
$("#node-input-property").typedInput({
|
||||
@ -158,7 +166,7 @@
|
||||
types:['msg']
|
||||
})
|
||||
|
||||
$("#node-input-out").change();
|
||||
$("#node-input-build").change();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -68,6 +68,9 @@ module.exports = function(RED) {
|
||||
|
||||
function JoinNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.property = n.property||"payload";
|
||||
this.propertyType = n.propertyType||"msg";
|
||||
this.key = n.key||"topic";
|
||||
this.timer = Number(n.timeout || 0);
|
||||
this.timerr = n.timerr || "send";
|
||||
this.count = Number(n.count || 0);
|
||||
@ -78,10 +81,6 @@ module.exports = function(RED) {
|
||||
var misc = (this.build === "array") ? [] : {};
|
||||
var tout;
|
||||
|
||||
function isObject (item) {
|
||||
return (typeof item === "object" && !Array.isArray(item)&& ! Buffer.isBuffer(item) && item !== null);
|
||||
}
|
||||
|
||||
// if array came from a string then reassemble it and send it
|
||||
var sendIt = function(m) {
|
||||
if (inflight[m.parts.id].ch !== undefined) { // if it was a string - rejoin it using the split char
|
||||
@ -116,80 +115,93 @@ module.exports = function(RED) {
|
||||
m.payload = misc.join(node.joiner.replace(/\\n/,"\n").replace(/\\r/,"\r").replace(/\\t/,"\t").replace(/\\e/,"\e").replace(/\\f/,"\c").replace(/\\0/,"\0"));
|
||||
}
|
||||
if (node.build === "array") { misc = []; }
|
||||
if (node.build === "object") { misc = {}; }
|
||||
else if (node.build === "object") { misc = {}; }
|
||||
node.send(m);
|
||||
}
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if (msg.hasOwnProperty("payload")) {
|
||||
if (msg.hasOwnProperty("parts")) { // only act if it has parts
|
||||
var count = node.count || msg.parts.count || 1;
|
||||
if (msg.parts.hasOwnProperty("index")) { // it's a numbered part (from a split node)
|
||||
if (!inflight[msg.parts.id]) { // New message - create new empty array of correct size
|
||||
if (msg.parts.type === "object") {
|
||||
inflight[msg.parts.id] = {i:0, a:{}, c:msg.parts.count, ch:msg.parts.ch, t:msg.parts.type};
|
||||
} else { // it's an array or string
|
||||
inflight[msg.parts.id] = {i:0, a:new Array(msg.parts.count), ch:msg.parts.ch, t:msg.parts.type};
|
||||
}
|
||||
if (node.timer !== 0) { // If there is a timer to set start it now
|
||||
inflight[msg.parts.id].timeout = setTimeout(function() {
|
||||
if (node.timerr === "send") { sendIt(msg); }
|
||||
if (node.timerr === "error") { node.error("Incomplete",msg); }
|
||||
delete inflight[msg.parts.id];
|
||||
}, node.timer);
|
||||
}
|
||||
}
|
||||
var property;
|
||||
if (node.propertyType == "full") {
|
||||
property = msg;
|
||||
} else {
|
||||
try {
|
||||
property = RED.util.getMessageProperty(msg,node.property);
|
||||
} catch(err) {
|
||||
node.warn("Message property "+node.property+" not found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (msg.hasOwnProperty("parts")) {
|
||||
// only act if it has parts
|
||||
var count = node.count || msg.parts.count || 1;
|
||||
if (msg.parts.hasOwnProperty("index")) {
|
||||
// it's a numbered part (from a split node)
|
||||
if (!inflight[msg.parts.id]) {
|
||||
// New message - create new empty array of correct size
|
||||
if (msg.parts.type === "object") {
|
||||
inflight[msg.parts.id].a[msg.parts.key] = msg.payload; // Add to the tracking array
|
||||
inflight[msg.parts.id].i = Object.keys(inflight[msg.parts.id].a).length;
|
||||
} else { // it's an array or string
|
||||
inflight[msg.parts.id].a[msg.parts.index] = msg.payload; // Add to the tracking array
|
||||
inflight[msg.parts.id].i += 1; // Increment the count
|
||||
inflight[msg.parts.id] = {i:0, a:{}, c:msg.parts.count, ch:msg.parts.ch, t:msg.parts.type};
|
||||
} else {
|
||||
// it's an array or string
|
||||
inflight[msg.parts.id] = {i:0, a:new Array(msg.parts.count), ch:msg.parts.ch, t:msg.parts.type};
|
||||
}
|
||||
if (inflight[msg.parts.id].i >= count) { sendIt(msg); } // All arrived - send
|
||||
} // otherwise ignore it
|
||||
if (msg.hasOwnProperty("complete")) { // if set then send right away anyway...
|
||||
delete(msg.complete);
|
||||
if (node.timer !== 0) { // If there is a timer to set start it now
|
||||
inflight[msg.parts.id].timeout = setTimeout(function() {
|
||||
if (node.timerr === "send") { sendIt(msg); }
|
||||
else if (node.timerr === "error") { node.error("Incomplete",msg); }
|
||||
delete inflight[msg.parts.id];
|
||||
}, node.timer);
|
||||
}
|
||||
}
|
||||
if (msg.parts.type === "object") {
|
||||
// Add to the tracking array
|
||||
inflight[msg.parts.id].a[msg.parts.key] = property;
|
||||
inflight[msg.parts.id].i = Object.keys(inflight[msg.parts.id].a).length;
|
||||
} else {
|
||||
// it's an array or string
|
||||
// Add to the tracking array
|
||||
inflight[msg.parts.id].a[msg.parts.index] = property;
|
||||
// Increment the count
|
||||
inflight[msg.parts.id].i += 1;
|
||||
}
|
||||
if (inflight[msg.parts.id].i >= count) {
|
||||
// All arrived - send
|
||||
sendIt(msg);
|
||||
}
|
||||
} // otherwise ignore it
|
||||
if (msg.hasOwnProperty("complete")) { // if set then send right away anyway...
|
||||
delete(msg.complete);
|
||||
sendIt(msg);
|
||||
}
|
||||
|
||||
} else {
|
||||
// The case for any messages arriving without parts - ie random messages you want to join.
|
||||
else {
|
||||
var l;
|
||||
if (node.build === "array") { // simple case of build the array
|
||||
misc.push(msg.payload); // Add the payload to an array
|
||||
l = misc.length;
|
||||
} else { // OK so let's build an object
|
||||
if ((msg.key === undefined) && ((msg.topic === undefined) || (msg.topic === ''))) {
|
||||
if (isObject(msg.payload)) { // if it's already an object (and no topic or key) just append it
|
||||
misc = Object.assign(misc,msg.payload);
|
||||
l = Object.keys(misc).length;
|
||||
}
|
||||
else { // if no topic or key and not an object then warn and drop it.
|
||||
node.warn("key or topic not defined");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else { // if it's got a msg.key or msg.topic then use key||topic as the property name
|
||||
misc[ msg.key || msg.topic ] = msg.payload;
|
||||
//if (msg.topic) { msg.topic = (msg.topic.split('/')).slice(0,-1).join('/'); }
|
||||
l = Object.keys(misc).length;
|
||||
}
|
||||
}
|
||||
if (l >= node.count) { sendMisc(msg); } // if it's long enough send it
|
||||
else if (msg.hasOwnProperty("complete")) { // if set then send right away anyway...
|
||||
delete(msg.complete);
|
||||
sendMisc(msg);
|
||||
}
|
||||
else if ((node.timer !== 0) && !tout) { // if not start the timer if there is one.
|
||||
tout = setTimeout(function() {
|
||||
if (node.timerr === "send") { sendMisc(msg); }
|
||||
if (node.timerr === "error") { node.error("Timeout",msg); }
|
||||
if (node.build === "array") { misc = []; }
|
||||
if (node.build === "object") { misc = {}; }
|
||||
}, node.timer);
|
||||
var l;
|
||||
if (node.build === "array" || node.build === "string") {
|
||||
// simple case of build the array
|
||||
// Add the payload to an array
|
||||
misc.push(property);
|
||||
l = misc.length;
|
||||
} else {
|
||||
// OK so let's build an object
|
||||
if (msg.hasOwnProperty(node.key) && msg[node.key] !== "") {
|
||||
misc[msg[node.key]] = property;
|
||||
}
|
||||
l = Object.keys(misc).length;
|
||||
}
|
||||
if (l >= node.count) {
|
||||
// if it's long enough send it
|
||||
sendMisc(msg);
|
||||
} else if (msg.hasOwnProperty("complete")) {
|
||||
// if set then send right away anyway...
|
||||
delete(msg.complete);
|
||||
sendMisc(msg);
|
||||
} else if ((node.timer !== 0) && !tout) {
|
||||
// if not start the timer if there is one.
|
||||
tout = setTimeout(function() {
|
||||
if (node.timerr === "send") { sendMisc(msg); }
|
||||
else if (node.timerr === "error") { node.error("Timeout",msg); }
|
||||
if (node.build === "array") { misc = []; }
|
||||
else if (node.build === "object") { misc = {}; }
|
||||
}, node.timer);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -181,8 +181,9 @@ describe('JOIN node', function() {
|
||||
msg.payload.should.have.property("b","2");
|
||||
msg.payload.should.have.property("c",true);
|
||||
msg.payload.should.have.property("d");
|
||||
msg.payload.d.should.have.property("e",5);
|
||||
msg.payload.should.have.property("f",6);
|
||||
msg.payload.d.should.have.property("e",7);
|
||||
msg.payload.should.have.property("g");
|
||||
msg.payload.g.should.have.property("f",6);
|
||||
done();
|
||||
}
|
||||
catch(e) { }//console.log(e); }
|
||||
@ -191,7 +192,8 @@ describe('JOIN node', function() {
|
||||
n1.receive({payload:"2", topic:"b"});
|
||||
n1.receive({payload:true, topic:"c"});
|
||||
n1.receive({payload:{e:5}, topic:"d"});
|
||||
n1.receive({payload:{f:6}});
|
||||
n1.receive({payload:{e:7}, topic:"d"});
|
||||
n1.receive({payload:{f:6}, topic:"g"});
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user