mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
add support of initialization & finalization to function node
This commit is contained in:
parent
c9ad5bea93
commit
84d2b8ad6d
@ -50,6 +50,18 @@ RED.library = (function() {
|
||||
'</form>'+
|
||||
'</div>'
|
||||
|
||||
function toSingleLine(text) {
|
||||
var result = text.replace(/\\/g, "\\\\").replace(/\n/g, "\\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
function fromSingleLine(text) {
|
||||
var result = text.replace(/\\[\\n]/g, function(s) {
|
||||
return ((s === "\\\\") ? "\\" : "\n");
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function saveToLibrary() {
|
||||
var elementPrefix = activeLibrary.elementPrefix || "node-input-";
|
||||
var name = $("#"+elementPrefix+"name").val().trim();
|
||||
@ -68,6 +80,10 @@ RED.library = (function() {
|
||||
var field = activeLibrary.fields[i];
|
||||
if (field == "name") {
|
||||
data.name = name;
|
||||
} else if(field == "initialize") {
|
||||
data.initialize = toSingleLine(activeLibrary.initEditor.getValue());
|
||||
} else if(field == "finalize") {
|
||||
data.finalize = toSingleLine(activeLibrary.finalizeEditor.getValue());
|
||||
} else {
|
||||
data[field] = $("#"+elementPrefix+field).val();
|
||||
}
|
||||
@ -523,7 +539,17 @@ RED.library = (function() {
|
||||
var elementPrefix = activeLibrary.elementPrefix || "node-input-";
|
||||
for (var i=0; i<activeLibrary.fields.length; i++) {
|
||||
var field = activeLibrary.fields[i];
|
||||
$("#"+elementPrefix+field).val(selectedLibraryItem[field]);
|
||||
if (field === "initialize") {
|
||||
var text = fromSingleLine(selectedLibraryItem.initialize);
|
||||
activeLibrary.initEditor.setValue(text, -1);
|
||||
}
|
||||
else if (field === "finalize") {
|
||||
var text = fromSingleLine(selectedLibraryItem.finalize);
|
||||
activeLibrary.finalizeEditor.setValue(text, -1);
|
||||
}
|
||||
else {
|
||||
$("#"+elementPrefix+field).val(selectedLibraryItem[field]);
|
||||
}
|
||||
}
|
||||
activeLibrary.editor.setValue(libraryEditor.getValue(),-1);
|
||||
}
|
||||
|
@ -4,19 +4,54 @@
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="function.label.function"></span></label>
|
||||
<input type="hidden" id="node-input-func" autofocus="autofocus">
|
||||
<input type="hidden" id="node-input-noerr">
|
||||
|
||||
<div class="form-row">
|
||||
<ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul>
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="position: absolute; right:0; bottom:calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom: 0px">
|
||||
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
|
||||
<input id="node-input-outputs" style="width: 60px;" value="1">
|
||||
|
||||
<div id="func-tabs-content" style="min-height: calc(100% - 80px);">
|
||||
|
||||
<div id="func-tab-init" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-initialize" autofocus="autofocus">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
|
||||
<div style="position: absolute; right:0; margin-top:5px;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="func-tab-code" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-func" autofocus="autofocus">
|
||||
<input type="hidden" id="node-input-noerr">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
|
||||
<div style="position: absolute; right:0; margin-top:5px;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" style="margin-bottom: 0px">
|
||||
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
|
||||
<input id="node-input-outputs" style="width: 60px;" value="1">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="func-tab-finalize" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-finalize" autofocus="autofocus">
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
|
||||
<div style="position: absolute; right:0; margin-top:5px;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -27,7 +62,9 @@
|
||||
name: {value:""},
|
||||
func: {value:"\nreturn msg;"},
|
||||
outputs: {value:1},
|
||||
noerr: {value:0,required:true,validate:function(v) { return !v; }}
|
||||
noerr: {value:0,required:true,validate:function(v) { return !v; }},
|
||||
initialize: {value:""},
|
||||
finalize: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
@ -40,6 +77,28 @@
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var that = this;
|
||||
|
||||
var tabs = RED.tabs.create({
|
||||
id: "func-tabs",
|
||||
onchange: function(tab) {
|
||||
$("#func-tabs-content").children().hide();
|
||||
$("#" + tab.id).show();
|
||||
}
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "func-tab-init",
|
||||
label: that._("function.label.initialize")
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "func-tab-code",
|
||||
label: that._("function.label.function")
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "func-tab-finalize",
|
||||
label: that._("function.label.finalize")
|
||||
});
|
||||
tabs.activateTab("func-tab-code");
|
||||
|
||||
$( "#node-input-outputs" ).spinner({
|
||||
min:0,
|
||||
change: function(event, ui) {
|
||||
@ -70,12 +129,54 @@
|
||||
}
|
||||
});
|
||||
|
||||
this.initEditor = RED.editor.createEditor({
|
||||
id: 'node-input-init-editor',
|
||||
mode: 'ace/mode/nrjavascript',
|
||||
value: $("#node-input-initialize").val(),
|
||||
globals: {
|
||||
msg:true,
|
||||
context:true,
|
||||
RED: true,
|
||||
util: true,
|
||||
flow: true,
|
||||
global: true,
|
||||
console: true,
|
||||
Buffer: true,
|
||||
setTimeout: true,
|
||||
clearTimeout: true,
|
||||
setInterval: true,
|
||||
clearInterval: true
|
||||
}
|
||||
});
|
||||
|
||||
this.finalizeEditor = RED.editor.createEditor({
|
||||
id: 'node-input-finalize-editor',
|
||||
mode: 'ace/mode/nrjavascript',
|
||||
value: $("#node-input-finalize").val(),
|
||||
globals: {
|
||||
msg:true,
|
||||
context:true,
|
||||
RED: true,
|
||||
util: true,
|
||||
flow: true,
|
||||
global: true,
|
||||
console: true,
|
||||
Buffer: true,
|
||||
setTimeout: true,
|
||||
clearTimeout: true,
|
||||
setInterval: true,
|
||||
clearInterval: true
|
||||
}
|
||||
});
|
||||
|
||||
RED.library.create({
|
||||
url:"functions", // where to get the data from
|
||||
type:"function", // the type of object the library is for
|
||||
editor:this.editor, // the field name the main text body goes to
|
||||
initEditor: this.initEditor, // editor for initializer
|
||||
finalizeEditor: this.finalizeEditor, // editor for finalizer
|
||||
mode:"ace/mode/nrjavascript",
|
||||
fields:['name','outputs'],
|
||||
fields:['name','outputs', 'initialize', 'finalize'],
|
||||
ext:"js"
|
||||
});
|
||||
this.editor.focus();
|
||||
@ -98,26 +199,100 @@
|
||||
},300);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
$("#node-init-expand-js").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
var editor = that.initEditor;
|
||||
var value = editor.getValue();
|
||||
RED.editor.editJavaScript({
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: editor.getCursorPosition(),
|
||||
mode: "ace/mode/nrjavascript",
|
||||
complete: function(v,cursor) {
|
||||
editor.setValue(v, -1);
|
||||
editor.gotoLine(cursor.row+1,cursor.column,false);
|
||||
setTimeout(function() {
|
||||
editor.focus();
|
||||
},300);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$("#node-finalize-expand-js").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
var editor = that.finalizeEditor;
|
||||
var value = editor.getValue();
|
||||
RED.editor.editJavaScript({
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: editor.getCursorPosition(),
|
||||
mode: "ace/mode/nrjavascript",
|
||||
complete: function(v,cursor) {
|
||||
editor.setValue(v, -1);
|
||||
editor.gotoLine(cursor.row+1,cursor.column,false);
|
||||
setTimeout(function() {
|
||||
editor.focus();
|
||||
},300);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
oneditsave: function() {
|
||||
var node = this;
|
||||
var noerr = 0;
|
||||
var annot = this.editor.getSession().getAnnotations();
|
||||
this.noerr = 0;
|
||||
$("#node-input-noerr").val(0);
|
||||
for (var k=0; k < annot.length; k++) {
|
||||
//console.log(annot[k].type,":",annot[k].text, "on line", annot[k].row);
|
||||
if (annot[k].type === "error") {
|
||||
$("#node-input-noerr").val(annot.length);
|
||||
this.noerr = annot.length;
|
||||
noerr += annot.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$("#node-input-func").val(this.editor.getValue());
|
||||
this.editor.destroy();
|
||||
delete this.editor;
|
||||
var annotIni = this.initEditor.getSession().getAnnotations();
|
||||
for (var k=0; k < annotIni.length; k++) {
|
||||
if (annotIni[k].type === "error") {
|
||||
noerr += annotIni.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var annotFin = this.finalizeEditor.getSession().getAnnotations();
|
||||
for (var k=0; k < annotFin.length; k++) {
|
||||
if (annotFin[k].type === "error") {
|
||||
noerr += annotFin.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$("#node-input-noerr").val(noerr);
|
||||
this.noerr = noerr;
|
||||
|
||||
$("#node-input-func").val(node.editor.getValue());
|
||||
node.editor.destroy();
|
||||
delete node.editor;
|
||||
|
||||
$("#node-input-initialize").val(node.initEditor.getValue());
|
||||
node.initEditor.destroy();
|
||||
delete node.initEditor;
|
||||
|
||||
$("#node-input-finalize").val(node.finalizeEditor.getValue());
|
||||
node.finalizeEditor.destroy();
|
||||
delete node.finalizeEditor;
|
||||
},
|
||||
oneditcancel: function() {
|
||||
this.editor.destroy();
|
||||
delete this.editor;
|
||||
var node = this;
|
||||
|
||||
node.editor.destroy();
|
||||
delete node.editor;
|
||||
|
||||
node.initEditor.destroy();
|
||||
delete node.initEditor;
|
||||
|
||||
node.finalizeEditor.destroy();
|
||||
delete node.finalizeEditor;
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
@ -129,6 +304,11 @@
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
|
||||
var height = size.height;
|
||||
$("#node-input-init-editor").css("height", (height -100)+"px");
|
||||
$("#node-input-func-editor").css("height", (height -120)+"px");
|
||||
$("#node-input-finalize-editor").css("height", (height -100)+"px");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -62,6 +62,8 @@ module.exports = function(RED) {
|
||||
var node = this;
|
||||
this.name = n.name;
|
||||
this.func = n.func;
|
||||
this.ini = n.initialize;
|
||||
this.fin = n.finalize;
|
||||
|
||||
var handleNodeDoneCall = true;
|
||||
// Check to see if the Function appears to call `node.done()`. If so,
|
||||
@ -89,6 +91,8 @@ module.exports = function(RED) {
|
||||
"};\n"+
|
||||
this.func+"\n"+
|
||||
"})(msg,send,done);";
|
||||
var iniText = "(function () {\n"+this.ini +"\n})();";
|
||||
var finText = "(function () {\n"+this.fin +"\n})();";
|
||||
this.topic = n.topic;
|
||||
this.outstandingTimers = [];
|
||||
this.outstandingIntervals = [];
|
||||
@ -229,6 +233,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
var context = vm.createContext(sandbox);
|
||||
try {
|
||||
vm.runInContext(iniText, context);
|
||||
this.script = vm.createScript(functionText, {
|
||||
filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces
|
||||
displayErrors: true
|
||||
@ -297,6 +302,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
});
|
||||
this.on("close", function() {
|
||||
vm.runInContext(finText, context);
|
||||
while (node.outstandingTimers.length > 0) {
|
||||
clearTimeout(node.outstandingTimers.pop());
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
-->
|
||||
|
||||
<script type="text/html" data-help-name="function">
|
||||
<p>A JavaScript function block to run against the messages being received by the node.</p>
|
||||
<p>A JavaScript function block written in <b>Function</b> tab to run against the messages being received by the node.</p>
|
||||
<p>The messages are passed in as a JavaScript object called <code>msg</code>.</p>
|
||||
<p>By convention it will have a <code>msg.payload</code> property containing
|
||||
the body of the message.</p>
|
||||
<p>The function is expected to return a message object (or multiple message objects), but can choose
|
||||
to return nothing in order to halt a flow.</p>
|
||||
<p>Setup code executed before deploy can be specified in <b>Setup</b> tab. Also, cleanup code executed before flow shutdown can be specified in <b>Cleanup</b> tab.</p>
|
||||
<h3>Details</h3>
|
||||
<p>See the <a target="_blank" href="http://nodered.org/docs/writing-functions.html">online documentation</a>
|
||||
for more information on writing functions.</p>
|
||||
|
@ -207,6 +207,8 @@
|
||||
"function": "",
|
||||
"label": {
|
||||
"function": "Function",
|
||||
"initialize": "Setup",
|
||||
"finalize": "Cleanup",
|
||||
"outputs": "Outputs"
|
||||
},
|
||||
"error": {
|
||||
|
@ -15,10 +15,11 @@
|
||||
-->
|
||||
|
||||
<script type="text/html" data-help-name="function">
|
||||
<p>受信メッセージに対して処理を行うJavaScriptコード(関数の本体)を定義します。</p>
|
||||
<p>受信メッセージに対して処理を行うJavaScriptコード(関数の本体)を<b>Function</b>タブに定義します。</p>
|
||||
<p>入力メッセージは<code>msg</code>という名称のJavaScriptオブジェクトで受け渡されます。</p>
|
||||
<p><code>msg</code>オブジェクトは<code>msg.payload</code>プロパティにメッセージ本体を保持するのが慣例です。</p>
|
||||
<p>通常、コードはメッセージオブジェクト(もしくは複数のメッセージオブジェクト)を返却します。後続フローの実行を停止したい場合は、オブジェクトを返却しなくてもかまいません。</p>
|
||||
<p>デプロイ前に実行すべき初期化コードを<b>初期化処理</b>タブに、フローの停止時に実行すべき終了処理コードを<b>終了処理</b>タブに指定できます。</p>
|
||||
<h3>詳細</h3>
|
||||
<p>コードの書き方の詳細については、<a target="_blank" href="http://nodered.org/docs/writing-functions.html">オンラインドキュメント</a>を参照してください。</p>
|
||||
<h4>メッセージの送信</h4>
|
||||
|
@ -207,6 +207,8 @@
|
||||
"function": "",
|
||||
"label": {
|
||||
"function": "コード",
|
||||
"initialize": "初期化処理",
|
||||
"finalize": "終了処理",
|
||||
"outputs": "出力数"
|
||||
},
|
||||
"error": {
|
||||
|
@ -53,7 +53,6 @@ describe('function node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should be loaded', function(done) {
|
||||
var flow = [{id:"n1", type:"function", name: "function" }];
|
||||
helper.load(functionNode, flow, function() {
|
||||
@ -1336,6 +1335,32 @@ describe('function node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute initialization', function(done) {
|
||||
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X','bar');"},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(functionNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
msg.should.have.property("payload", "bar");
|
||||
done();
|
||||
});
|
||||
n1.receive({payload: "foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute finalization', function(done) {
|
||||
var flow = [{id:"n1",type:"function",wires:[],func:"return msg;",finalize:"global.set('X','bar');"}];
|
||||
helper.load(functionNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var ctx = n1.context().global;
|
||||
helper.unload().then(function () {
|
||||
ctx.get('X').should.equal("bar");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Logger', function () {
|
||||
it('should log an Info Message', function (done) {
|
||||
var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "node.log('test');"}];
|
||||
|
Loading…
Reference in New Issue
Block a user