1
0
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:
Nick O'Leary 2016-06-05 23:32:03 +01:00
parent 9f8c32ce8f
commit e594ffe0f8
4 changed files with 169 additions and 144 deletions

View File

@ -124,21 +124,11 @@
this.options.types = this.options.types||Object.keys(allOptions); this.options.types = this.options.types||Object.keys(allOptions);
var hasSubOptions = false; this.selectTrigger = $('<a href="#"></a>').prependTo(this.uiSelect);
this.typeMap = {}; $('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
this.types = this.options.types.map(function(opt) { this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
var result;
if (typeof opt === 'string') { this.types(this.options.types);
result = allOptions[opt];
} else {
result = opt;
}
that.typeMap[result.value] = result;
if (result.options) {
hasSubOptions = true;
}
return result;
});
if (this.options.typeField) { if (this.options.typeField) {
this.typeField = $(this.options.typeField).hide(); this.typeField = $(this.options.typeField).hide();
@ -150,13 +140,6 @@
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect); 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() { this.element.on('focus', function() {
that.uiSelect.addClass('red-ui-typedInput-focus'); that.uiSelect.addClass('red-ui-typedInput-focus');
}); });
@ -166,20 +149,15 @@
this.element.on('change', function() { this.element.on('change', function() {
that.validate(); that.validate();
}) })
if (this.types.length > 1) {
this.selectTrigger.click(function(event) { this.selectTrigger.click(function(event) {
event.preventDefault(); event.preventDefault();
if (that.typeList.length > 1) {
that._showMenu(that.menu,that.selectTrigger); that._showMenu(that.menu,that.selectTrigger);
});
} else { } else {
this.selectTrigger.click(function(event) {
event.preventDefault();
that.element.focus(); that.element.focus();
})
} }
});
if (hasSubOptions) {
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline' // 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.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.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger);
@ -193,9 +171,7 @@
that._showMenu(that.optionMenu,that.optionSelectLabel) that._showMenu(that.optionMenu,that.optionSelectLabel)
} }
}); });
} this.type(this.options.default||this.typeList[0].value);
this.menu = this._createMenu(this.types, function(v) { that.type(v) });
this.type(this.options.default||this.types[0].value);
}, },
_hideMenu: function(menu) { _hideMenu: function(menu) {
$(document).off("mousedown.close-property-select"); $(document).off("mousedown.close-property-select");
@ -278,7 +254,6 @@
return labelWidth; return labelWidth;
}, },
_resize: function() { _resize: function() {
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) { if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
this.selectTrigger.width(this.uiWidth+5); this.selectTrigger.width(this.uiWidth+5);
} else { } else {
@ -298,6 +273,29 @@
_destroy: function() { _destroy: function() {
this.menu.remove(); 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) { width: function(desiredWidth) {
this.uiWidth = desiredWidth; this.uiWidth = desiredWidth;
this._resize(); this._resize();
@ -359,9 +357,14 @@
this.optionSelectTrigger.hide(); this.optionSelectTrigger.hide();
} }
if (opt.hasValue === false) { if (opt.hasValue === false) {
this.oldValue = this.element.val();
this.element.val(""); this.element.val("");
this.element.hide(); this.element.hide();
} else { } else {
if (this.oldValue !== undefined) {
this.element.val(this.oldValue);
delete this.oldValue;
}
this.element.show(); this.element.show();
} }
this.element.trigger('change'); this.element.trigger('change');

View File

@ -44,7 +44,7 @@
color:"#E2D96E", color:"#E2D96E",
defaults: { defaults: {
name: {value:""}, name: {value:""},
splt: {value:""} splt: {value:"\\n"}
}, },
inputs:1, inputs:1,
outputs:1, outputs:1,
@ -61,12 +61,12 @@
<script type="text/x-red" data-template-name="join"> <script type="text/x-red" data-template-name="join">
<div class="form-row"> <div class="form-row">
<label for="node-input-out"><i class="fa fa-long-arrow-right"></i> Output</label> <label for="node-input-build"><i class="fa fa-long-arrow-right"></i> Output</label>
<select id="node-input-out" style="width:70%; margin-right:5px;"> <select id="node-input-build" style="width:70%; margin-right:5px;">
<option value="auto">automatic</option> <option value="auto">automatic</option>
<option value="str">a String of joined properties</option> <option value="string">a String of joined properties</option>
<option value="arr">an Array of properties</option> <option value="array">an Array of properties</option>
<option value="obj">an Object of key/property pairs</option> <option value="object">an Object of key/property pairs</option>
</select> </select>
</div> </div>
<div class="form-row node-row-key"> <div class="form-row node-row-key">
@ -78,22 +78,24 @@
<input type="text" id="node-input-property"> <input type="text" id="node-input-property">
<input type="hidden" id="node-input-propertyType"> <input type="hidden" id="node-input-propertyType">
</div> </div>
<div class="form-row node-row-join"> <div class="form-row node-row-joiner">
<label for="node-input-join" style="padding-right: 10px; box-sizing: border-box; text-align: right;">joined using</label> <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-join" style="width: 40px"> <input type="text" id="node-input-joiner" style="width: 40px">
</div> </div>
<div class="form-row node-row-trigger"> <div class="form-row node-row-trigger">
<label style="width: auto;">Send the message:</label> <label style="width: auto;">Send the message:</label>
<div style="height: 40px;"> <ul>
<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> <li style="height: 40px;">
</div> <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;">
<div style="height: 40px;"> </li>
<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> <li style="height: 40px;">
</div> <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;">
<div style="height: 40px; padding-top: 6px;" class="node-row-accumulate"> </li>
<label style="margin-left: 40px; width: auto;"><input type="checkbox" style="width: auto;margin-bottom: 5px;"> whenever a property is updated</label> <li style="height: 40px; padding-top: 6px;" class="node-row-accumulate">
</div> <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>
<div class="form-tips form-tips-auto hide">This mode assumes this node is either <div class="form-tips form-tips-auto hide">This mode assumes this node is either
@ -122,13 +124,14 @@
color:"#E2D96E", color:"#E2D96E",
defaults: { defaults: {
name: {value:""}, name: {value:""},
out: { value:"auto"}, build: { value:"auto"},
property: { value: "payload"}, property: { value: "payload"},
propertyType: { value:"msg"},
key: {value:"topic"},
joiner: { value:"\\n"},
timeout: {value:""}, timeout: {value:""},
timerr: {value:"send"},
count: {value:""}, count: {value:""},
joiner: {value:""}, accumulate: {value: false}
build: {value:"array"}
}, },
inputs:1, inputs:1,
outputs:1, outputs:1,
@ -140,14 +143,19 @@
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
oneditprepare: function() { oneditprepare: function() {
$("#node-input-out").change(function(e) { $("#node-input-build").change(function(e) {
var val = $(this).val(); var val = $(this).val();
$(".node-row-key").toggle(val==='obj'); $(".node-row-key").toggle(val==='obj');
$(".node-row-property").toggle(val!=='auto'); $(".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-trigger").toggle(val!=='auto');
$(".node-row-accumulate").toggle(val==='obj'); $(".node-row-accumulate").toggle(val==='obj');
$(".form-tips-auto").toggle(val==='auto'); $(".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({ $("#node-input-property").typedInput({
@ -158,7 +166,7 @@
types:['msg'] types:['msg']
}) })
$("#node-input-out").change(); $("#node-input-build").change();
} }
}); });
</script> </script>

View File

@ -68,6 +68,9 @@ module.exports = function(RED) {
function JoinNode(n) { function JoinNode(n) {
RED.nodes.createNode(this,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.timer = Number(n.timeout || 0);
this.timerr = n.timerr || "send"; this.timerr = n.timerr || "send";
this.count = Number(n.count || 0); this.count = Number(n.count || 0);
@ -78,10 +81,6 @@ module.exports = function(RED) {
var misc = (this.build === "array") ? [] : {}; var misc = (this.build === "array") ? [] : {};
var tout; 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 // if array came from a string then reassemble it and send it
var sendIt = function(m) { var sendIt = function(m) {
if (inflight[m.parts.id].ch !== undefined) { // if it was a string - rejoin it using the split char if (inflight[m.parts.id].ch !== undefined) { // if it was a string - rejoin it using the split char
@ -116,82 +115,95 @@ 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")); 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 === "array") { misc = []; }
if (node.build === "object") { misc = {}; } else if (node.build === "object") { misc = {}; }
node.send(m); node.send(m);
} }
this.on("input", function(msg) { this.on("input", function(msg) {
if (msg.hasOwnProperty("payload")) { var property;
if (msg.hasOwnProperty("parts")) { // only act if it has parts 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; var count = node.count || msg.parts.count || 1;
if (msg.parts.hasOwnProperty("index")) { // it's a numbered part (from a split node) if (msg.parts.hasOwnProperty("index")) {
if (!inflight[msg.parts.id]) { // New message - create new empty array of correct size // 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") { if (msg.parts.type === "object") {
inflight[msg.parts.id] = {i:0, a:{}, c:msg.parts.count, ch:msg.parts.ch, t:msg.parts.type}; 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 } 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}; 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 if (node.timer !== 0) { // If there is a timer to set start it now
inflight[msg.parts.id].timeout = setTimeout(function() { inflight[msg.parts.id].timeout = setTimeout(function() {
if (node.timerr === "send") { sendIt(msg); } if (node.timerr === "send") { sendIt(msg); }
if (node.timerr === "error") { node.error("Incomplete",msg); } else if (node.timerr === "error") { node.error("Incomplete",msg); }
delete inflight[msg.parts.id]; delete inflight[msg.parts.id];
}, node.timer); }, node.timer);
} }
} }
if (msg.parts.type === "object") { if (msg.parts.type === "object") {
inflight[msg.parts.id].a[msg.parts.key] = msg.payload; // Add to the tracking array // 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; inflight[msg.parts.id].i = Object.keys(inflight[msg.parts.id].a).length;
} else { // it's an array or string } else {
inflight[msg.parts.id].a[msg.parts.index] = msg.payload; // Add to the tracking array // it's an array or string
inflight[msg.parts.id].i += 1; // Increment the count // 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);
} }
if (inflight[msg.parts.id].i >= count) { sendIt(msg); } // All arrived - send
} // otherwise ignore it } // otherwise ignore it
if (msg.hasOwnProperty("complete")) { // if set then send right away anyway... if (msg.hasOwnProperty("complete")) { // if set then send right away anyway...
delete(msg.complete); delete(msg.complete);
sendIt(msg); sendIt(msg);
} }
} } else {
// The case for any messages arriving without parts - ie random messages you want to join. // The case for any messages arriving without parts - ie random messages you want to join.
else {
var l; var l;
if (node.build === "array") { // simple case of build the array if (node.build === "array" || node.build === "string") {
misc.push(msg.payload); // Add the payload to an array // simple case of build the array
// Add the payload to an array
misc.push(property);
l = misc.length; l = misc.length;
} else { // OK so let's build an object } else {
if ((msg.key === undefined) && ((msg.topic === undefined) || (msg.topic === ''))) { // OK so let's build an object
if (isObject(msg.payload)) { // if it's already an object (and no topic or key) just append it if (msg.hasOwnProperty(node.key) && msg[node.key] !== "") {
misc = Object.assign(misc,msg.payload); misc[msg[node.key]] = property;
}
l = Object.keys(misc).length; l = Object.keys(misc).length;
} }
else { // if no topic or key and not an object then warn and drop it. if (l >= node.count) {
node.warn("key or topic not defined"); // if it's long enough send it
return; sendMisc(msg);
} } else if (msg.hasOwnProperty("complete")) {
} // if set then send right away anyway...
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); delete(msg.complete);
sendMisc(msg); sendMisc(msg);
} } else if ((node.timer !== 0) && !tout) {
else if ((node.timer !== 0) && !tout) { // if not start the timer if there is one. // if not start the timer if there is one.
tout = setTimeout(function() { tout = setTimeout(function() {
if (node.timerr === "send") { sendMisc(msg); } if (node.timerr === "send") { sendMisc(msg); }
if (node.timerr === "error") { node.error("Timeout",msg); } else if (node.timerr === "error") { node.error("Timeout",msg); }
if (node.build === "array") { misc = []; } if (node.build === "array") { misc = []; }
if (node.build === "object") { misc = {}; } else if (node.build === "object") { misc = {}; }
}, node.timer); }, node.timer);
} }
} }
}
}); });
this.on("close", function() { this.on("close", function() {

View File

@ -181,8 +181,9 @@ describe('JOIN node', function() {
msg.payload.should.have.property("b","2"); msg.payload.should.have.property("b","2");
msg.payload.should.have.property("c",true); msg.payload.should.have.property("c",true);
msg.payload.should.have.property("d"); msg.payload.should.have.property("d");
msg.payload.d.should.have.property("e",5); msg.payload.d.should.have.property("e",7);
msg.payload.should.have.property("f",6); msg.payload.should.have.property("g");
msg.payload.g.should.have.property("f",6);
done(); done();
} }
catch(e) { }//console.log(e); } catch(e) { }//console.log(e); }
@ -191,7 +192,8 @@ describe('JOIN node', function() {
n1.receive({payload:"2", topic:"b"}); n1.receive({payload:"2", topic:"b"});
n1.receive({payload:true, topic:"c"}); n1.receive({payload:true, topic:"c"});
n1.receive({payload:{e:5}, topic:"d"}); 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"});
}); });
}); });