From 84d2b8ad6db06c34b678c65b12b856f10548c02f Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 7 Mar 2020 01:55:45 +0900 Subject: [PATCH 1/6] add support of initialization & finalization to function node --- .../editor-client/src/js/ui/library.js | 28 ++- .../nodes/core/function/10-function.html | 224 ++++++++++++++++-- .../nodes/core/function/10-function.js | 6 + .../locales/en-US/function/10-function.html | 3 +- .../nodes/locales/en-US/messages.json | 2 + .../locales/ja/function/10-function.html | 3 +- .../@node-red/nodes/locales/ja/messages.json | 2 + test/nodes/core/function/10-function_spec.js | 27 ++- 8 files changed, 269 insertions(+), 26 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js index e25223f94..2520c16a9 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js @@ -50,6 +50,18 @@ RED.library = (function() { ''+ '' + 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
-
- - - + +
+
    -
    -
    -
    -
    -
    - - + +
    + + + + + + +
    + diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 65a1b4a61..7bdeada65 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -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()); } diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html index 28bc76f3a..c1c768f84 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html @@ -15,12 +15,13 @@ --> diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 4515f425c..14c8e9d49 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -92,8 +92,8 @@ module.exports = function(RED) { var node = this; node.name = n.name; node.func = n.func; - node.ini = n.initialize; - node.fin = n.finalize; + node.ini = n.initialize ? n.initialize : ""; + node.fin = n.finalize ? n.finalize : ""; var handleNodeDoneCall = true; @@ -122,14 +122,11 @@ module.exports = function(RED) { "};\n"+ node.func+"\n"+ "})(msg,send,done);"; - var iniText = "(async function () {\n"+node.ini +"\n})();"; - var finText = "(function () {\n"+node.fin +"\n})();"; var finScript = null; var finOpt = null; node.topic = n.topic; node.outstandingTimers = []; node.outstandingIntervals = []; - var initValue = undefined; var sandbox = { console:console, util:util, @@ -256,9 +253,6 @@ module.exports = function(RED) { if (index > -1) { node.outstandingIntervals.splice(index,1); } - }, - getInitValue: function() { - return initValue; } }; if (util.hasOwnProperty('promisify')) { @@ -273,13 +267,15 @@ module.exports = function(RED) { try { var iniScript = null; var iniOpt = null; - if (iniText || (iniText === "")) { + if (node.ini && (node.ini !== "")) { + var iniText = "(async function () {\n"+node.ini +"\n})();"; iniOpt = createVMOpt(node, " setup"); iniScript = new vm.Script(iniText, iniOpt); } node.script = vm.createScript(functionText, createVMOpt(node, "")); - if (finText || (finText === "")) { - finOpt = createVMOpt(node, "cleanup"); + if (node.fin && (node.fin !== "")) { + var finText = "(function () {\n"+node.fin +"\n})();"; + finOpt = createVMOpt(node, " cleanup"); finScript = new vm.Script(finText, finOpt); } var promise = Promise.resolve(); @@ -380,7 +376,6 @@ module.exports = function(RED) { }); promise.then(function (v) { - initValue = v; var msgs = messages; messages = []; while (msgs.length > 0) { diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html index c1c768f84..160cc5630 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html @@ -21,7 +21,7 @@ the body of the message.

    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.

    -

    Setup code executed before deploy can be specified in Setup tab. Also, cleanup code executed before flow shutdown can be specified in Cleanup tab.

    +

    Setup code executed once whenever Node-RED is started or a new flow configuration is deployed can be specified in Setup tab. Also, cleanup code executed when the node is being stopped or re-deployed can be specified in Close tab.

    Details

    See the online documentation for more information on writing functions.

    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 7e7b73367..b48abb694 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 @@ -208,9 +208,13 @@ "label": { "function": "Function", "initialize": "Setup", - "finalize": "Cleanup", + "finalize": "Close", "outputs": "Outputs" }, + "text": { + "initialize": "// Code added here will be run once whenever Node-RED is started\n// or a new flow configuration is deployed.\n", + "finalize": "// Code added here will be run when the node is being stopped\n// or re-deployed.\n" + }, "error": { "inputListener":"Cannot add listener to 'input' event within Function", "non-message-returned":"Function tried to send a message of type __type__" diff --git a/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html b/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html index ea6372180..b348512df 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/locales/ja/function/10-function.html @@ -19,7 +19,7 @@

    入力メッセージはmsgという名称のJavaScriptオブジェクトで受け渡されます。

    msgオブジェクトはmsg.payloadプロパティにメッセージ本体を保持するのが慣例です。

    通常、コードはメッセージオブジェクト(もしくは複数のメッセージオブジェクト)を返却します。後続フローの実行を停止したい場合は、オブジェクトを返却しなくてもかまいません。

    -

    デプロイ前に実行すべき初期化コードを初期化処理タブに、フローの停止時に実行すべき終了処理コードを終了処理タブに指定できます。

    +

    Node-REDの開始時もしくはフローの設定をデプロイした際実行される初期化コードを初期化処理タブに、ノードの停止もしくは再デプロイ時に実行される終了処理コードを終了処理タブに指定できます。

    詳細

    コードの書き方の詳細については、オンラインドキュメントを参照してください。

    メッセージの送信

    diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index d2bc5e4ee..1272fc8ae 100755 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -211,6 +211,10 @@ "finalize": "終了処理", "outputs": "出力数" }, + "text": { + "initialize": "// ここに記述したコードはNode-REDの実行開始時\n// もしくは新しいフローのデプロイ時に一度だけ実行されます。\n", + "finalize": "// ここに記述したコードはノードの停止時\n// もしくは再デプロイ時に実行されます。\n" + }, "error": { "inputListener": "コード内で'input'イベントのリスナを設定できません", "non-message-returned": "Functionノードが __type__ 型のメッセージ送信を試みました" diff --git a/test/nodes/core/function/10-function_spec.js b/test/nodes/core/function/10-function_spec.js index ec3effc88..ab7e4b486 100644 --- a/test/nodes/core/function/10-function_spec.js +++ b/test/nodes/core/function/10-function_spec.js @@ -1349,6 +1349,20 @@ describe('function node', function() { }); }); + it('should wait completion of initialization', function(done) { + var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X', '-'); return new Promise((resolve, reject) => setTimeout(() => { global.set('X','bar'); resolve(); }, 500));"}, + {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() { From a764a4a44b52b7f5227674cb9609e97af0fc7c49 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 11 May 2020 22:55:07 +0900 Subject: [PATCH 5/6] update initial contents for setup & close code --- .../@node-red/nodes/core/function/10-function.html | 2 -- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 4 ++-- .../node_modules/@node-red/nodes/locales/ja/messages.json | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html index 56c19fc25..5cc12e3b8 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html @@ -156,7 +156,6 @@ var count = finalText.split("\n").length; this.finalizeEditor.moveCursorTo(count -1, 0); } - RED.library.create({ url:"functions", // where to get the data from @@ -245,7 +244,6 @@ } }) }); - }, oneditsave: function() { 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 b48abb694..0b93dddc3 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 @@ -212,8 +212,8 @@ "outputs": "Outputs" }, "text": { - "initialize": "// Code added here will be run once whenever Node-RED is started\n// or a new flow configuration is deployed.\n", - "finalize": "// Code added here will be run when the node is being stopped\n// or re-deployed.\n" + "initialize": "// Code added here will be run once whenever the node is deployed.\n", + "finalize": "// Code added here will be run when the node is being stopped or re-deployed.\n" }, "error": { "inputListener":"Cannot add listener to 'input' event within Function", diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index 1272fc8ae..35e15aae7 100755 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -212,8 +212,8 @@ "outputs": "出力数" }, "text": { - "initialize": "// ここに記述したコードはNode-REDの実行開始時\n// もしくは新しいフローのデプロイ時に一度だけ実行されます。\n", - "finalize": "// ここに記述したコードはノードの停止時\n// もしくは再デプロイ時に実行されます。\n" + "initialize": "// ここのコードはノードのデプロイ時に一度実行されます。\n", + "finalize": "// ここのコードはノードの停止もしくは再デプロイ時に実行されます。\n" }, "error": { "inputListener": "コード内で'input'イベントのリスナを設定できません", From 9512450d7c0fa62031a971a92d0a104346ecb3bc Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 22 May 2020 14:57:28 +0100 Subject: [PATCH 6/6] Reduce duplicated code in Function node html --- .../editor-client/src/js/ui/editors/js.js | 3 + .../nodes/core/function/10-function.html | 222 +++++++----------- .../nodes/locales/en-US/messages.json | 4 +- 3 files changed, 85 insertions(+), 144 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js index 6906adea9..8f00d6d9a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js @@ -87,6 +87,9 @@ expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); } dialogForm.i18n(); + setTimeout(function() { + expressionEditor.focus(); + },300); }, close: function() { expressionEditor.destroy(); diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html index 5cc12e3b8..026c0e574 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html @@ -109,53 +109,34 @@ } }); - var 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.editor = RED.editor.createEditor({ - id: 'node-input-func-editor', - mode: 'ace/mode/nrjavascript', - value: $("#node-input-func").val(), - globals: globals - }); - var initCode = $("#node-input-initialize").val(); - var initText = RED._("node-red:function.text.initialize"); - var isEmptyInitCode = (initCode === ""); - this.initEditor = RED.editor.createEditor({ - id: 'node-input-init-editor', - mode: 'ace/mode/nrjavascript', - value: (isEmptyInitCode ? initText : initCode), - globals: globals - }); - if (isEmptyInitCode) { - var count = initText.split("\n").length; - this.initEditor.moveCursorTo(count -1, 0); - } - - var finalCode = $("#node-input-finalize").val(); - var finalText = RED._("node-red:function.text.finalize"); - var isEmptyFinalCode = (finalCode === ""); - this.finalizeEditor = RED.editor.createEditor({ - id: 'node-input-finalize-editor', - mode: 'ace/mode/nrjavascript', - value: (isEmptyFinalCode ? finalText : finalCode), - globals: globals - }); - if (isEmptyFinalCode) { - var count = finalText.split("\n").length; - this.finalizeEditor.moveCursorTo(count -1, 0); + var buildEditor = function(id, value, defaultValue) { + var editor = RED.editor.createEditor({ + id: id, + mode: 'ace/mode/nrjavascript', + value: value || defaultValue || "", + 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 + } + }); + if (defaultValue && value === "") { + editor.moveCursorTo(defaultValue.split("\n").length - 1, 0); + } + return editor; } + this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize")) + this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val()) + this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize")) RED.library.create({ url:"functions", // where to get the data from @@ -170,7 +151,7 @@ return that.initEditor.getValue(); }, set: function(v) { - that.initEditor.setValue(v, -1); + that.initEditor.setValue(v||RED._("node-red:function.text.initialize"), -1); } }, { @@ -179,7 +160,7 @@ return that.finalizeEditor.getValue(); }, set: function(v) { - that.finalizeEditor.setValue(v, -1); + that.finalizeEditor.setValue(v||RED._("node-red:function.text.finalize"), -1); } } ], @@ -187,113 +168,65 @@ }); this.editor.focus(); + + var expandButtonClickHandler = function(editor) { + return function(e) { + e.preventDefault(); + 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-init-expand-js").on("click", expandButtonClickHandler(this.initEditor)); + $("#node-function-expand-js").on("click", expandButtonClickHandler(this.editor)); + $("#node-finalize-expand-js").on("click", expandButtonClickHandler(this.finalizeEditor)); + + RED.popover.tooltip($("#node-init-expand-js"), RED._("node-red:common.label.expand")); RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand")); + RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand")); - $("#node-function-expand-js").on("click", function(e) { - e.preventDefault(); - var value = that.editor.getValue(); - RED.editor.editJavaScript({ - value: value, - width: "Infinity", - cursor: that.editor.getCursorPosition(), - mode: "ace/mode/nrjavascript", - complete: function(v,cursor) { - that.editor.setValue(v, -1); - that.editor.gotoLine(cursor.row+1,cursor.column,false); - setTimeout(function() { - that.editor.focus(); - },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(); $("#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") { - noerr += annot.length; - break; + + var disposeEditor = function(editorName,targetName,defaultValue) { + var editor = node[editorName]; + var annot = editor.getSession().getAnnotations(); + for (var k=0; k < annot.length; k++) { + if (annot[k].type === "error") { + noerr += annot.length; + break; + } } - } - 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; + var val = editor.getValue(); + if (defaultValue && val == defaultValue) { + val = ""; } + editor.destroy(); + delete node[editorName]; + $("#"+targetName).val(val); } + disposeEditor("editor","node-input-func"); + disposeEditor("initEditor","node-input-initialize", RED._("node-red:function.text.initialize")); + disposeEditor("finalizeEditor","node-input-finalize", RED._("node-red:function.text.finalize")); + $("#node-input-noerr").val(noerr); this.noerr = noerr; - $("#node-input-func").val(node.editor.getValue()); - node.editor.destroy(); - delete node.editor; - - var initCode = node.initEditor.getValue(); - if (initCode === RED._("node-red:function.text.initialize")) { - initCode = ""; - } - $("#node-input-initialize").val(initCode); - node.initEditor.destroy(); - delete node.initEditor; - - var finalCode = node.finalizeEditor.getValue(); - if (finalCode === RED._("node-red:function.text.finalize")) { - finalCode = ""; - } - $("#node-input-finalize").val(finalCode); - node.finalizeEditor.destroy(); - delete node.finalizeEditor; }, oneditcancel: function() { var node = this; @@ -320,8 +253,13 @@ var height = size.height; $("#node-input-init-editor").css("height", (height -105)+"px"); - $("#node-input-func-editor").css("height", (height -140)+"px"); + $("#node-input-func-editor").css("height", (height -145)+"px"); $("#node-input-finalize-editor").css("height", (height -105)+"px"); + + this.initEditor.resize(); + this.editor.resize(); + this.finalizeEditor.resize(); + } }); 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 0b93dddc3..216d0fc85 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 @@ -212,8 +212,8 @@ "outputs": "Outputs" }, "text": { - "initialize": "// Code added here will be run once whenever the node is deployed.\n", - "finalize": "// Code added here will be run when the node is being stopped or re-deployed.\n" + "initialize": "// Code added here will be run once\n// whenever the node is deployed.\n", + "finalize": "// Code added here will be run when the\n// node is being stopped or re-deployed.\n" }, "error": { "inputListener":"Cannot add listener to 'input' event within Function",