-
Notifications
You must be signed in to change notification settings - Fork 0
/
join-plus.html
307 lines (295 loc) · 15.5 KB
/
join-plus.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
<script type="text/x-red" data-template-name="assemble">
<div class="form-row">
<label data-i18n="node-red:join.mode.mode"></label>
<select id="node-input-mode" style="width:200px;">
<option value="auto" data-i18n="node-red:join.mode.auto"></option>
<option value="custom" data-i18n="node-red:join.mode.custom"></option>
<option value="reduce" data-i18n="node-red:join.mode.reduce"></option>
</select>
</div>
<div class="node-row-custom">
<div class="form-row node-row-property">
<label data-i18n="node-red:join.combine"> </label>
<input type="text" id="node-input-property" style="width:70%;">
<input type="hidden" id="node-input-propertyType">
</div>
<div class="form-row">
<label data-i18n="node-red:join.create"></label>
<select id="node-input-build" style="width:70%;">
<option value="string" data-i18n="node-red:join.type.string"></option>
<option value="buffer" data-i18n="node-red:join.type.buffer"></option>
<option value="array" data-i18n="node-red:join.type.array"></option>
<option value="object" data-i18n="node-red:join.type.object"></option>
<option value="merged" data-i18n="node-red:join.type.merged"></option>
</select>
</div>
<div class="form-row node-row-key">
<label style="vertical-align:top; margin-top:7px; width:auto; margin-right: 5px;" data-i18n="node-red:join.using"></label>
<div style="display:inline-block">
<input type="text" id="node-input-key" style="width:220px;"> <span data-i18n="node-red:join.key"></span>
</div>
</div>
<div class="form-row node-row-joiner">
<label for="node-input-joiner" data-i18n="node-red:join.joinedUsing"></label>
<input type="text" id="node-input-joiner" style="width:70%">
<input type="hidden" id="node-input-joinerType">
</div>
<div class="form-row node-row-trigger" id="trigger-row">
<label style="width:auto;" data-i18n="node-red:join.send"></label>
<ul>
<li>
<label style="width:280px;" for="node-input-count" data-i18n="node-red:join.afterCount"></label> <input id="node-input-count" data-i18n="[placeholder]node-red:join.count" type="text" style="width:75px;">
</li>
<li class="node-row-accumulate" style="list-style-type:none;">
<input type="checkbox" id="node-input-accumulate" style="display:inline-block; width:20px; margin-left:20px; vertical-align:top;"> <label style="width: auto" for="node-input-accumulate" data-i18n="node-red:join.subsequent"></label>
</li>
<li>
<label style="width:280px;" for="node-input-timeout" data-i18n="node-red:join.afterTimeout"></label> <input id="node-input-timeout" data-i18n="[placeholder]node-red:join.seconds" type="text" style="width:75px;">
</li>
<li>
<label style="width:280px;" for="node-input-unique">Ignore messages sent after timeout?</label> <input id="node-input-unique" type="checkbox" style="width:75px;">
</li>
<li>
<label style="width:auto; padding-top:6px;" data-i18n="[html]node-red:join.complete"></label>
</li>
</ul>
</div>
</div>
<div class="node-row-reduce">
<div class="form-row">
<label for="node-input-reduceExp" data-i18n="node-red:join.reduce.exp" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceExp" data-i18n="[placeholder]node-red:join.reduce.exp-value" style="width:65%">
</div>
<div class="form-row">
<label for="node-input-reduceInit" data-i18n="node-red:join.reduce.init" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceInit" data-i18n="[placeholder]node-red:join.reduce.init" style="width:65%">
<input type="hidden" id="node-input-reduceInitType">
</div>
<div class="form-row">
<label for="node-input-reduceFixup" data-i18n="node-red:join.reduce.fixup" style="margin-left:10px;"></label>
<input type="text" id="node-input-reduceFixup" data-i18n="[placeholder]node-red:join.reduce.exp-value" style="width:65%">
</div>
<div class="form-row">
<label> </label>
<input type="checkbox" id="node-input-reduceRight" style="display:inline-block; width:auto; vertical-align:top; margin-left:10px;">
<label for="node-input-reduceRight" style="width:70%;" data-i18n="node-red:join.reduce.right" style="margin-left:10px;"/>
</div>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips form-tips-auto hide" data-i18n="[html]node-red:join.tip"></div>
</script>
<script type="text/x-red" data-help-name="assemble">
<p>Joins sequences of messages into a single message.</p>
<p>There are three modes available:</p>
<dl>
<dt>automatic</dt>
<dd>When paired with the <b>split</b> node, it will automatically join the messages to reverse the split that was performed.</dd>
<dt>manual</dt>
<dd>Join sequences of messages in a variety of ways.</dd>
<dt>reduce sequence</dt>
<dd>Apply an expression against all messages in a sequence to reduce it to a single message.</dd>
</dl>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">parts<span class="property-type">object</span></dt>
<dd>To automatically join a sequence of messages, they should all have
this property set. The <b>split</b> node generates this property but it
can be manually created. It has the following properties:
<ul>
<li><code>id</code> - an identifier for the group of messages</li>
<li><code>index</code> - the position within the group</li>
<li><code>count</code> - the total number of messages in the group</li>
<li><code>type</code> - the type of message - string/array/object/buffer</li>
<li><code>ch</code> - for a string or buffer, the data used to the split the message as either the string or an array of bytes</li>
<li><code>key</code> - for an object, the key of the property this message was created from</li>
<li><code>len</code> - the length of each message when split using a fixed length value</li>
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>If set, the node will send its output message in its current state.</dd>
</dl>
<h3>Details</h3>
<h4>Automatic mode</h4>
<p>Automatic mode uses the <code>parts</code> property of incoming messages to
determine how the sequence should be joined. This allows it to automatically
reverse the action of a <b>split</b> node.
<h4>Manual mode</h4>
<p>When configured to join in manual mode, the node is able to join sequences
of messages into a number of different results:</p>
<ul>
<li>a <b>string</b> or <b>buffer</b> - created by joining the selected property of each message with the specified join characters or buffer.</li>
<li>an <b>array</b> - created by adding each selected property, or entire message, to the output array.</li>
<li>a <b>key/value object</b> - created by using a property of each message to determine the key under which
the required value is stored.</li>
<li>a <b>merged object</b> - created by merging the property of each message under a single object.</li>
</ul>
<p>The other properties of the output message are taken from the last message received before the result is sent.</p>
<p>A <i>count</i> can be set for how many messages should be received before generating the output message.
For object outputs, once this count has been reached, the node can be configured to send a message for each subsequent message
received.</p>
<p>A <i>timeout</i> can be set to trigger sending the new message using whatever has been received so far.</p>
<p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
<h4>Reduce Sequence mode</h4>
<p>When configured to join in reduce mode, an expression is applied to each
message in a sequence and the result accumulated to produce a single message.</p>
<dl class="message-properties">
<dt>Initial value</dt>
<dd>The initial value of the accumulated value (<code>$A</code>).</dd>
<dt>Reduce expression</dt>
<dd>A JSONata expression that is called for each message in the sequence.
The result is passed to the next call of the expression as the accumulated value.
In the expression, the following special variables can be used:
<ul>
<li><code>$A</code>: the accumulated value, </li>
<li><code>$I</code>: index of the message in the sequence, </li>
<li><code>$N</code>: number of messages in the sequence.</li>
</ul>
</dd>
<dt>Fix-up expression</dt>
<dd>An optional JSONata expression that is applied after the reduce expression
has been applied to all messages in the sequence.
In the expression, following special variables can be used:
<ul>
<li><code>$A</code>: the accumulated value, </li>
<li><code>$N</code>: number of messages in the sequence.</li>
</ul>
</dd>
<p>By default, the reduce expression is applied in order, from the first
to the last message of the sequence. It can optionally be applied in
reverse order.</p>
</dl>
<p><b>Example:</b> the following settings, given a sequence of numeric values,
calculates the average value:
<ul>
<li><b>Reduce expression</b>: <code>$A+payload</code></li>
<li><b>Initial value</b>: <code>0</code></li>
<li><b>Fix-up expression</b>: <code>$A/$N</code></li>
</ul>
</p>
<h4>Storing messages</h4>
<p>This node will buffer messages internally in order to work across sequences. The
runtime setting <code>nodeMessageBufferMaxLength</code> can be used to limit how many messages nodes
will buffer.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('assemble',{
category: 'function',
color:"#E2D96E",
defaults: {
name: {value:""},
mode: {value:"auto"},
build: { value:"string"},
property: { value:"payload", validate:RED.validators.typedInput("propertyType")},
propertyType: { value:"msg"},
key: {value:"topic"},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },
timeout: {value:""},
unique: { value: false },
count: {value:""},
reduceRight: {value:false},
reduceExp: {value:undefined},
reduceInit: {value:undefined},
reduceInitType: {value:undefined},
reduceFixup: {value:undefined}
},
inputs:1,
outputs:1,
icon: "join.png",
label: function() {
return this.name||this._("assemble");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var node = this;
$("#node-input-mode").change(function(e) {
var val = $(this).val();
$(".node-row-custom").toggle(val==='custom');
$(".node-row-reduce").toggle(val==='reduce');
$(".form-tips-auto").toggle((val==='auto') || (val==='reduce'));
if (val === "auto") {
$("#node-input-accumulate").attr('checked', false);
}
else if (val === "custom") {
$("#node-input-build").change();
}
else if (val === "reduce") {
var jsonata_or_empty = {
value: "jsonata",
label: "expression",
icon: "red/images/typedInput/expr.png",
validate: function(v) {
try{
if(v !== "") {
jsonata(v);
}
return true;
}
catch(e){
return false;
}
},
expand:function() {
var that = this;
RED.editor.editExpression({
value: this.value().replace(/\t/g,"\n"),
complete: function(v) {
that.value(v.replace(/\n/g,"\t"));
}
})
}
};
$("#node-input-reduceExp").typedInput({types:[jsonata_or_empty]});
$("#node-input-reduceInit").typedInput({
default: 'num',
types:['flow','global','str','num','bool','json','bin','date','jsonata'],
typeField: $("#node-input-reduceInitType")
});
$("#node-input-reduceFixup").typedInput({types:[jsonata_or_empty]});
}
});
$("#node-input-build").change(function(e) {
var val = $(this).val();
$(".node-row-key").toggle(val==='object');
$(".node-row-accumulate").toggle(val==='object' || val==='merged');
$(".node-row-joiner").toggle(val==='string' || val==='buffer');
$(".node-row-trigger").toggle(val!=='auto');
if (val === 'string' || val==='buffer') {
$("#node-input-property").typedInput('types',['msg']);
$("#node-input-joiner").typedInput("show");
} else {
$("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]);
}
});
$("#node-input-joiner").typedInput({
default: 'str',
typeField: $("#node-input-joinerType"),
types:[
'str',
'bin'
]
});
$("#node-input-property").typedInput({
typeField: $("#node-input-propertyType"),
types:['msg', {value:"full", label:"complete message", hasValue:false}]
});
$("#node-input-key").typedInput({
types:['msg', {value:"merge", label:"", hasValue:false}]
});
$("#node-input-build").change();
$("#node-input-mode").change();
},
oneditsave: function() {
var build = $("#node-input-build").val();
if (build !== 'object' && build !== 'merged') {
$("#node-input-accumulate").prop("checked",false);
}
}
});
</script>