1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge branch 'master' into i18n-json-editor

This commit is contained in:
Hiroyasu Nishiyama 2019-11-13 22:42:53 +09:00
commit d5234888b3
22 changed files with 427 additions and 193 deletions

View File

@ -15,6 +15,17 @@
"next": "Next", "next": "Next",
"clone": "Clone project", "clone": "Clone project",
"cont": "Continue" "cont": "Continue"
},
"type": {
"string": "string",
"number": "number",
"boolean": "boolean",
"array": "array",
"buffer": "buffer",
"object": "object",
"jsonString": "JSON string",
"undefined": "undefined",
"null": "null"
} }
}, },
"workspace": { "workspace": {
@ -801,6 +812,7 @@
}, },
"markdownEditor": { "markdownEditor": {
"title": "Markdown editor", "title": "Markdown editor",
"expand": "Expand",
"format": "Formatted with markdown", "format": "Formatted with markdown",
"heading1": "Heading 1", "heading1": "Heading 1",
"heading2": "Heading 2", "heading2": "Heading 2",

View File

@ -15,6 +15,17 @@
"next": "進む", "next": "進む",
"clone": "プロジェクトをクローン", "clone": "プロジェクトをクローン",
"cont": "続ける" "cont": "続ける"
},
"type": {
"string": "文字列",
"number": "数値",
"boolean": "真偽値",
"array": "配列",
"buffer": "バッファ",
"object": "オブジェクト",
"jsonString": "JSON文字列",
"undefined": "undefined",
"null": "null"
} }
}, },
"workspace": { "workspace": {
@ -800,6 +811,7 @@
}, },
"markdownEditor": { "markdownEditor": {
"title": "マークダウンエディタ", "title": "マークダウンエディタ",
"expand": "拡大",
"format": "マークダウン形式で記述", "format": "マークダウン形式で記述",
"heading1": "見出しレベル1", "heading1": "見出しレベル1",
"heading2": "見出しレベル2", "heading2": "見出しレベル2",

View File

@ -1,7 +1,7 @@
{ {
"$string": { "$string": {
"args": "arg", "args": "arg[, prettify]",
"desc": "以下の型変換ルールを用いて、引数 *arg* を文字列へ型変換します。:\n\n - 文字列は変換しません。\n - 関数は空の文字列に変換します。\n - JSONの数値として表現できないため、無限大やNaNはエラーになります。\n - 他の値は `JSON.stringify` 関数を用いて、JSONの文字列へ変換します。" "desc": "以下の型変換ルールを用いて、引数 *arg* を文字列へ型変換します。:\n\n - 文字列は変換しません。\n - 関数は空の文字列に変換します。\n - JSONの数値として表現できないため、無限大やNaNはエラーになります。\n - 他の値は `JSON.stringify` 関数を用いて、JSONの文字列へ変換します。`prettify`が真の場合、JSONを整形出力します。フィールドを1行毎に出力。フィールドのネスト深さによってインデントを行います。"
}, },
"$length": { "$length": {
"args": "str", "args": "str",
@ -185,7 +185,7 @@
}, },
"$reduce": { "$reduce": {
"args": "array, function [, init]", "args": "array, function [, init]",
"desc": "配列の各要素値に関数 `function` を連続的に適用して得られる集約値を返します。 `function` の適用の際には、直前の `function` の適用結果と要素値が引数として与えられます。\n\n関数 `function` は引数を2つ取り、配列の各要素の間に配置する中置演算子のように作用しなくてはなりません。\n\n任意の引数 `init` には、集約時の初期値を設定します。" "desc": "配列の各要素値に関数 `function` を連続的に適用して得られる集約値を返します。 `function` の適用の際には、直前の `function` の適用結果と要素値が引数として与えられます。\n\n関数 `function` は引数を2つ取り、配列の各要素の間に配置する中置演算子のように作用しなくてはなりません。関数`function`のシグネチャは`myfunc($accumulator, $value[, $index[, $array]])`という形式でなければなりません。\n\n任意の引数 `init` には、集約時の初期値を設定します。"
}, },
"$flowContext": { "$flowContext": {
"args": "string", "args": "string",
@ -230,5 +230,37 @@
"$parseInteger": { "$parseInteger": {
"args": "string, picture", "args": "string, picture",
"desc": "`picture`文字列の指定に従って、`string`パラメータを整数(JSON数値)に変換します。`picture`文字列は`$formatInteger`と同じ形式です。" "desc": "`picture`文字列の指定に従って、`string`パラメータを整数(JSON数値)に変換します。`picture`文字列は`$formatInteger`と同じ形式です。"
},
"$error": {
"args": "[str]",
"desc": "メッセージを指定して例外を送出します。メッセージ`str`を省略した場合は`$error() function evaluated`をメッセージとします。"
},
"$assert": {
"args": "arg, str",
"desc": "`arg`が真の場合、undefinedを返します。偽の場合、`str`をメッセージとする例外を送出します。"
},
"$single": {
"args": "array, function",
"desc": "`array`の要素のうち、条件判定関数`function`を満たす(`function`に与えた場合に真偽値`true`を返す)要素が1つのみである場合、それを返します。マッチする要素が1つのみでない場合、例外を送出します。\n\n指定する関数は`function(value [, index [, array]])`というシグネチャでなければなりません。ここで、`value`は`array`の要素値、`index`は要素の添字、第三引数には配列全体を渡します。"
},
"$encodeUrl": {
"args": "str",
"desc": "Uniform Resource Locator (URL)を構成する文字を1、、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Uniform Resource Locator (URL)要素を構成する文字を1、、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "encodeUrlComponentで置換したUniform Resource Locator (URL)をデコードします。\n\n例: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。 \n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "配列`array`から重複要素を削除した配列を返します。"
} }
} }

View File

@ -218,7 +218,7 @@ RED.tabs = (function() {
var thisTab = $(this).parent(); var thisTab = $(this).parent();
var fireSelectionChanged = false; var fireSelectionChanged = false;
if (options.onselect) { if (options.onselect) {
if (evt.metaKey) { if (evt.metaKey || evt.ctrlKey) {
if (thisTab.hasClass("selected")) { if (thisTab.hasClass("selected")) {
thisTab.removeClass("selected"); thisTab.removeClass("selected");
if (thisTab[0] !== currentTab[0]) { if (thisTab[0] !== currentTab[0]) {

View File

@ -2484,7 +2484,7 @@ RED.editor = (function() {
editor.toolbar = customEditTypes['_markdown'].buildToolbar(toolbarRow,editor); editor.toolbar = customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
if (options.expandable !== false) { if (options.expandable !== false) {
var expandButton = $('<button type="button" class="red-ui-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar); var expandButton = $('<button type="button" class="red-ui-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar);
RED.popover.tooltip(expandButton, RED._("markdownEditor.expand"));
expandButton.on("click", function(e) { expandButton.on("click", function(e) {
e.preventDefault(); e.preventDefault();
var value = editor.getValue(); var value = editor.getValue();

View File

@ -207,6 +207,7 @@
} }
expressionEditor.getSession().setValue(v||"",-1); expressionEditor.getSession().setValue(v||"",-1);
}); });
funcSelect.change();
var tabs = RED.tabs.create({ var tabs = RED.tabs.create({
element: $("#red-ui-editor-type-expression-tabs"), element: $("#red-ui-editor-type-expression-tabs"),

View File

@ -19,12 +19,18 @@ RED.subflow = (function() {
var currentLocale = "en-US"; var currentLocale = "en-US";
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+ var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+ '<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>'+
'<div id="subflow-input-ui"></div>'+ '<div id="subflow-input-ui"></div>'+
'</script>'; '</script>';
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+ var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
'<div class="form-row"><label for="subflow-input-name" data-i18n="[append]common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="subflow-input-name"></div>'+ '<div class="form-row">'+
'<label for="subflow-input-name" data-i18n="[append]common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="subflow-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>'+
'<div class="form-row">'+ '<div class="form-row">'+
'<ul style="margin-bottom: 20px;" id="subflow-env-tabs"></ul>'+ '<ul style="margin-bottom: 20px;" id="subflow-env-tabs"></ul>'+
'</div>'+ '</div>'+

View File

@ -164,7 +164,7 @@ RED.workspaces = (function() {
var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody); var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody);
$('<div class="form-row">'+ $('<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+ '<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="node-input-name">'+ '<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>').appendTo(dialogForm); '</div>').appendTo(dialogForm);

View File

@ -70,6 +70,7 @@
border: 1px solid $primary-border-color; border: 1px solid $primary-border-color;
box-sizing: border-box; box-sizing: border-box;
background: $secondary-background; background: $secondary-background;
white-space: nowrap;
z-index: 2000; z-index: 2000;
a { a {
padding: 6px 18px 6px 6px; padding: 6px 18px 6px 6px;

View File

@ -30,16 +30,16 @@
<!-- (with the 'node-input-' prefix). --> <!-- (with the 'node-input-' prefix). -->
<!-- The available icon classes are defined Font Awesome Icons (FA Icons) --> <!-- The available icon classes are defined Font Awesome Icons (FA Icons) -->
<div class="form-row"> <div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label> <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" placeholder="Topic"> <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
</div> </div>
<br/> <br/>
<!-- By convention, most nodes have a 'name' property. The following div --> <!-- By convention, most nodes have a 'name' property. The following div -->
<!-- provides the necessary field. Should always be the last option --> <!-- provides the necessary field. Should always be the last option -->
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div> </div>
</script> </script>

View File

@ -2,7 +2,7 @@
<script type="text/x-red" data-template-name="comment"> <script type="text/x-red" data-template-name="comment">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div> </div>
<div class="form-row node-text-editor-row"> <div class="form-row node-text-editor-row">
<input type="hidden" id="node-input-info" autofocus="autofocus"> <input type="hidden" id="node-input-info" autofocus="autofocus">

View File

@ -127,7 +127,7 @@
}, },
oneditprepare: function() { oneditprepare: function() {
var node = this; var node = this;
var previousValueType = {value:"prev",label:this._("inject.previous"),hasValue:false}; var previousValueType = {value:"prev",label:this._("switch.previous"),hasValue:false};
$("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global','jsonata','env']}); $("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global','jsonata','env']});
var outputCount = $("#node-input-outputs").val("{}"); var outputCount = $("#node-input-outputs").val("{}");
@ -237,15 +237,15 @@
function createTypeValueField(){ function createTypeValueField(){
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'string',types:[ return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'string',types:[
{value:"string",label:"string",hasValue:false}, {value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.png"},
{value:"number",label:"number",hasValue:false}, {value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.png"},
{value:"boolean",label:"boolean",hasValue:false}, {value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.png"},
{value:"array",label:"array",hasValue:false}, {value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"buffer",label:"buffer",hasValue:false}, {value:"buffer",label:RED._("common.type.buffer"),hasValue:false,icon:"red/images/typedInput/bin.png"},
{value:"object",label:"object",hasValue:false}, {value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"json",label:"JSON string",hasValue:false}, {value:"json",label:RED._("common.type.jsonString"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"undefined",label:"undefined",hasValue:false}, {value:"undefined",label:RED._("common.type.undefined"),hasValue:false},
{value:"null",label:"null",hasValue:false} {value:"null",label:RED._("common.type.null"),hasValue:false}
]}); ]});
} }

View File

@ -285,7 +285,11 @@
$("#node-input-property").typedInput('types',['msg']); $("#node-input-property").typedInput('types',['msg']);
$("#node-input-joiner").typedInput("show"); $("#node-input-joiner").typedInput("show");
} else { } else {
$("#node-input-property").typedInput('types',['msg', {value:"full",label:"complete message",hasValue:false}]); $("#node-input-property").typedInput('types', ['msg', {
value: "full",
label: RED._("node-red:join.completeMessage"),
hasValue: false
}]);
} }
}); });
@ -297,7 +301,11 @@
$("#node-input-property").typedInput({ $("#node-input-property").typedInput({
typeField: $("#node-input-propertyType"), typeField: $("#node-input-propertyType"),
types:['msg', {value:"full", label:"complete message", hasValue:false}] types: ['msg', {
value: "full",
label: RED._("node-red:join.completeMessage"),
hasValue: false
}]
}); });
$("#node-input-key").typedInput({ $("#node-input-key").typedInput({

View File

@ -604,33 +604,34 @@
"label": { "label": {
"property": "Property", "property": "Property",
"rule": "rule", "rule": "rule",
"repair" : "recreate message sequences" "repair": "recreate message sequences"
}, },
"previous": "previous value",
"and": "and", "and": "and",
"checkall": "checking all rules", "checkall": "checking all rules",
"stopfirst": "stopping after first match", "stopfirst": "stopping after first match",
"ignorecase": "ignore case", "ignorecase": "ignore case",
"rules": { "rules": {
"btwn":"is between", "btwn": "is between",
"cont":"contains", "cont": "contains",
"regex":"matches regex", "regex": "matches regex",
"true":"is true", "true": "is true",
"false":"is false", "false": "is false",
"null":"is null", "null": "is null",
"nnull":"is not null", "nnull": "is not null",
"istype":"is of type", "istype": "is of type",
"empty":"is empty", "empty": "is empty",
"nempty":"is not empty", "nempty": "is not empty",
"head":"head", "head": "head",
"tail":"tail", "tail": "tail",
"index":"index between", "index": "index between",
"exp":"JSONata exp", "exp": "JSONata exp",
"else":"otherwise", "else": "otherwise",
"hask":"has key" "hask": "has key"
}, },
"errors": { "errors": {
"invalid-expr": "Invalid JSONata expression: __error__", "invalid-expr": "Invalid JSONata expression: __error__",
"too-many" : "too many pending messages in switch node" "too-many": "too many pending messages in switch node"
} }
}, },
"change": { "change": {
@ -848,41 +849,42 @@
"stream":"Handle as a stream of messages", "stream":"Handle as a stream of messages",
"addname":" Copy key to " "addname":" Copy key to "
}, },
"join":{ "join": {
"join": "join", "join": "join",
"mode":{ "mode": {
"mode":"Mode", "mode": "Mode",
"auto":"automatic", "auto": "automatic",
"merge":"merge sequences", "merge": "merge sequences",
"reduce":"reduce sequence", "reduce": "reduce sequence",
"custom":"manual" "custom": "manual"
}, },
"combine":"Combine each", "combine": "Combine each",
"create":"to create", "completeMessage": "complete message",
"type":{ "create": "to create",
"string":"a String", "type": {
"array":"an Array", "string": "a String",
"buffer":"a Buffer", "array": "an Array",
"object":"a key/value Object", "buffer": "a Buffer",
"merged":"a merged Object" "object": "a key/value Object",
"merged": "a merged Object"
}, },
"using":"using the value of", "using": "using the value of",
"key":"as the key", "key": "as the key",
"joinedUsing":"joined using", "joinedUsing": "joined using",
"send":"Send the message:", "send": "Send the message:",
"afterCount":"After a number of message parts", "afterCount": "After a number of message parts",
"count":"count", "count": "count",
"subsequent":"and every subsequent message.", "subsequent": "and every subsequent message.",
"afterTimeout":"After a timeout following the first message", "afterTimeout": "After a timeout following the first message",
"seconds":"seconds", "seconds": "seconds",
"complete":"After a message with the <code>msg.complete</code> property set", "complete": "After a message with the <code>msg.complete</code> property set",
"tip":"This mode assumes this node is either paired with a <i>split</i> node or the received messages will have a properly configured <code>msg.parts</code> property.", "tip": "This mode assumes this node is either paired with a <i>split</i> node or the received messages will have a properly configured <code>msg.parts</code> property.",
"too-many" : "too many pending messages in join node", "too-many": "too many pending messages in join node",
"merge": { "merge": {
"topics-label":"Merged Topics", "topics-label": "Merged Topics",
"topics":"topics", "topics": "topics",
"topic" : "topic", "topic": "topic",
"on-change":"Send merged message on arrival of a new topic" "on-change": "Send merged message on arrival of a new topic"
}, },
"reduce": { "reduce": {
"exp": "Reduce exp", "exp": "Reduce exp",

View File

@ -604,6 +604,7 @@
"rule": "条件", "rule": "条件",
"repair": "メッセージ列の補正" "repair": "メッセージ列の補正"
}, },
"previous": "前回の値",
"and": "", "and": "",
"checkall": "全ての条件を適用", "checkall": "全ての条件を適用",
"stopfirst": "最初に合致した条件で終了", "stopfirst": "最初に合致した条件で終了",
@ -856,6 +857,7 @@
"custom": "手動" "custom": "手動"
}, },
"combine": "結合", "combine": "結合",
"completeMessage": "メッセージ全体",
"create": "出力", "create": "出力",
"type": { "type": {
"string": "文字列", "string": "文字列",

View File

@ -98,7 +98,7 @@
<p><i>合計値</i>」には出力メッセージを送信する前に受信すべきメッセージ数を指定します。オブジェクト出力の場合、この合計値に達すると後続メッセージの到着毎にメッセージを出力するように設定することもできます。</p> <p><i>合計値</i>」には出力メッセージを送信する前に受信すべきメッセージ数を指定します。オブジェクト出力の場合、この合計値に達すると後続メッセージの到着毎にメッセージを出力するように設定することもできます。</p>
<p><i></i>」には新規メッセージを送信するまでの経過時間を設定します。</p> <p><i></i>」には新規メッセージを送信するまでの経過時間を設定します。</p>
<p><code>msg.complete</code>プロパティを設定したメッセージを受信すると、出力メッセージを送信します。この時、メッセージ列の数をリセットします。</p> <p><code>msg.complete</code>プロパティを設定したメッセージを受信すると、出力メッセージを送信します。この時、メッセージ列の数をリセットします。</p>
<p><code>msg.reset</code>プロパティを設定したメッセージを受すると、部分的に受信済みのメッセージを破棄します。これらのメッセージは送信されません。この時、メッセージ列の数をリセットします。</p> <p><code>msg.reset</code>プロパティを設定したメッセージを受すると、部分的に受信済みのメッセージを破棄します。これらのメッセージは送信されません。この時、メッセージ列の数をリセットします。</p>
<h4>列の集約モード</h4> <h4>列の集約モード</h4>
<p>列の集約モードを選択すると、メッセージ列を構成する各々のメッセージに対して式を適用し、集約した値を用いて一つのメッセージを構成します。</p> <p>列の集約モードを選択すると、メッセージ列を構成する各々のメッセージに対して式を適用し、集約した値を用いて一つのメッセージを構成します。</p>

View File

@ -84,9 +84,14 @@ var consoleLogger = function(msg) {
util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+msg.msg.stack); util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+msg.msg.stack);
} else { } else {
var message = msg.msg; var message = msg.msg;
if (typeof message === 'object' && message !== null && message.toString() === '[object Object]' && message.message) { try {
message = message.message; if (typeof message === 'object' && message !== null && message.toString() === '[object Object]' && message.message) {
message = message.message;
}
} catch(e){
message = 'Exception trying to log: '+util.inspect(message);
} }
util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message); util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message);
} }
} }

View File

@ -652,130 +652,148 @@ function normaliseNodeTypeName(name) {
* @memberof @node-red/util_util * @memberof @node-red/util_util
*/ */
function encodeObject(msg,opts) { function encodeObject(msg,opts) {
var debuglength = 1000; try {
if (opts && opts.hasOwnProperty('maxLength')) { var debuglength = 1000;
debuglength = opts.maxLength; if (opts && opts.hasOwnProperty('maxLength')) {
} debuglength = opts.maxLength;
var msgType = typeof msg.msg;
if (msg.msg instanceof Error) {
msg.format = "error";
var errorMsg = {};
if (msg.msg.name) {
errorMsg.name = msg.msg.name;
} }
if (msg.msg.hasOwnProperty('message')) { var msgType = typeof msg.msg;
errorMsg.message = msg.msg.message; if (msg.msg instanceof Error) {
} else { msg.format = "error";
errorMsg.message = msg.msg.toString(); var errorMsg = {};
} if (msg.msg.name) {
msg.msg = JSON.stringify(errorMsg); errorMsg.name = msg.msg.name;
} else if (msg.msg instanceof Buffer) { }
msg.format = "buffer["+msg.msg.length+"]"; if (msg.msg.hasOwnProperty('message')) {
msg.msg = msg.msg.toString('hex'); errorMsg.message = msg.msg.message;
if (msg.msg.length > debuglength) { } else {
msg.msg = msg.msg.substring(0,debuglength); errorMsg.message = msg.msg.toString();
} }
} else if (msg.msg && msgType === 'object') { msg.msg = JSON.stringify(errorMsg);
try { } else if (msg.msg instanceof Buffer) {
msg.format = msg.msg.constructor.name || "Object"; msg.format = "buffer["+msg.msg.length+"]";
// Handle special case of msg.req/res objects from HTTP In node msg.msg = msg.msg.toString('hex');
if (msg.format === "IncomingMessage" || msg.format === "ServerResponse") { if (msg.msg.length > debuglength) {
msg.msg = msg.msg.substring(0,debuglength);
}
} else if (msg.msg && msgType === 'object') {
try {
msg.format = msg.msg.constructor.name || "Object";
// Handle special case of msg.req/res objects from HTTP In node
if (msg.format === "IncomingMessage" || msg.format === "ServerResponse") {
msg.format = "Object";
}
} catch(err) {
msg.format = "Object"; msg.format = "Object";
} }
} catch(err) { if (/error/i.test(msg.format)) {
msg.format = "Object"; msg.msg = JSON.stringify({
} name: msg.msg.name,
if (/error/i.test(msg.format)) { message: msg.msg.message
msg.msg = JSON.stringify({ });
name: msg.msg.name, } else {
message: msg.msg.message var isArray = util.isArray(msg.msg);
}); if (isArray) {
} else { msg.format = "array["+msg.msg.length+"]";
var isArray = util.isArray(msg.msg); if (msg.msg.length > debuglength) {
if (isArray) { // msg.msg = msg.msg.slice(0,debuglength);
msg.format = "array["+msg.msg.length+"]"; msg.msg = {
if (msg.msg.length > debuglength) {
// msg.msg = msg.msg.slice(0,debuglength);
msg.msg = {
__enc__: true,
type: "array",
data: msg.msg.slice(0,debuglength),
length: msg.msg.length
}
}
}
if (isArray || (msg.format === "Object")) {
msg.msg = safeJSONStringify(msg.msg, function(key, value) {
if (key === '_req' || key === '_res') {
value = {
__enc__: true,
type: "internal"
}
} else if (value instanceof Error) {
value = value.toString()
} else if (util.isArray(value) && value.length > debuglength) {
value = {
__enc__: true, __enc__: true,
type: "array", type: "array",
data: value.slice(0,debuglength), data: msg.msg.slice(0,debuglength),
length: value.length length: msg.msg.length
}
} else if (typeof value === 'string') {
if (value.length > debuglength) {
value = value.substring(0,debuglength)+"...";
}
} else if (typeof value === 'function') {
value = {
__enc__: true,
type: "function"
}
} else if (typeof value === 'number') {
if (isNaN(value) || value === Infinity || value === -Infinity) {
value = {
__enc__: true,
type: "number",
data: value.toString()
}
}
} else if (value && value.constructor) {
if (value.type === "Buffer") {
value.__enc__ = true;
value.length = value.data.length;
if (value.length > debuglength) {
value.data = value.data.slice(0,debuglength);
}
} else if (value.constructor.name === "ServerResponse") {
value = "[internal]"
} else if (value.constructor.name === "Socket") {
value = "[internal]"
} }
} }
return value; }
}," "); if (isArray || (msg.format === "Object")) {
} else { msg.msg = safeJSONStringify(msg.msg, function(key, value) {
try { msg.msg = msg.msg.toString(); } if (key === '_req' || key === '_res') {
catch(e) { msg.msg = "[Type not printable]"; } value = {
__enc__: true,
type: "internal"
}
} else if (value instanceof Error) {
value = value.toString()
} else if (util.isArray(value) && value.length > debuglength) {
value = {
__enc__: true,
type: "array",
data: value.slice(0,debuglength),
length: value.length
}
} else if (typeof value === 'string') {
if (value.length > debuglength) {
value = value.substring(0,debuglength)+"...";
}
} else if (typeof value === 'function') {
value = {
__enc__: true,
type: "function"
}
} else if (typeof value === 'number') {
if (isNaN(value) || value === Infinity || value === -Infinity) {
value = {
__enc__: true,
type: "number",
data: value.toString()
}
}
} else if (value && value.constructor) {
if (value.type === "Buffer") {
value.__enc__ = true;
value.length = value.data.length;
if (value.length > debuglength) {
value.data = value.data.slice(0,debuglength);
}
} else if (value.constructor.name === "ServerResponse") {
value = "[internal]"
} else if (value.constructor.name === "Socket") {
value = "[internal]"
}
}
return value;
}," ");
} else {
try { msg.msg = msg.msg.toString(); }
catch(e) { msg.msg = "[Type not printable]" + util.inspect(msg.msg); }
}
}
} else if (msgType === "function") {
msg.format = "function";
msg.msg = "[function]"
} else if (msgType === "boolean") {
msg.format = "boolean";
msg.msg = msg.msg.toString();
} else if (msgType === "number") {
msg.format = "number";
msg.msg = msg.msg.toString();
} else if (msg.msg === null || msgType === "undefined") {
msg.format = (msg.msg === null)?"null":"undefined";
msg.msg = "(undefined)";
} else {
msg.format = "string["+msg.msg.length+"]";
if (msg.msg.length > debuglength) {
msg.msg = msg.msg.substring(0,debuglength)+"...";
} }
} }
} else if (msgType === "function") { return msg;
msg.format = "function"; } catch(e) {
msg.msg = "[function]" msg.format = "error";
} else if (msgType === "boolean") { var errorMsg = {};
msg.format = "boolean"; if (e.name) {
msg.msg = msg.msg.toString(); errorMsg.name = e.name;
} else if (msgType === "number") {
msg.format = "number";
msg.msg = msg.msg.toString();
} else if (msg.msg === null || msgType === "undefined") {
msg.format = (msg.msg === null)?"null":"undefined";
msg.msg = "(undefined)";
} else {
msg.format = "string["+msg.msg.length+"]";
if (msg.msg.length > debuglength) {
msg.msg = msg.msg.substring(0,debuglength)+"...";
} }
if (e.hasOwnProperty('message')) {
errorMsg.message = 'encodeObject Error: ['+e.message + '] Value: '+util.inspect(msg.msg);
} else {
errorMsg.message = 'encodeObject Error: ['+e.toString() + '] Value: '+util.inspect(msg.msg);
}
if (errorMsg.message.length > debuglength) {
errorMsg.message = errorMsg.message.substring(0,debuglength);
}
msg.msg = JSON.stringify(errorMsg);
return msg;
} }
return msg;
} }
module.exports = { module.exports = {

2
scripts/install-ui-test-dependencies.sh Executable file → Normal file
View File

@ -4,4 +4,4 @@ npm install --no-save \
wdio-mocha-framework@^0.6.4 \ wdio-mocha-framework@^0.6.4 \
wdio-spec-reporter@^0.1.5 \ wdio-spec-reporter@^0.1.5 \
webdriverio@^4.14.1 \ webdriverio@^4.14.1 \
chromedriver@2 chromedriver@^78.0.1

View File

@ -61,7 +61,7 @@ exports.config = {
maxInstances: 2, maxInstances: 2,
// //
browserName: 'chrome', browserName: 'chrome',
chromeOptions: { 'goog:chromeOptions': {
args: process.env.NODE_RED_NON_HEADLESS args: process.env.NODE_RED_NON_HEADLESS
// Runs tests with opening a browser. // Runs tests with opening a browser.
? ['--disable-gpu'] ? ['--disable-gpu']
@ -134,7 +134,7 @@ exports.config = {
// Services take over a specific job you don't want to take care of. They enhance // Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new // your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process. // commands. Instead, they hook themselves up into the test process.
port: '9515', port: 9515,
path: '/', path: '/',
services: ['chromedriver'], services: ['chromedriver'],
// //

View File

@ -224,5 +224,29 @@ describe("@node-red/util/log", function() {
}); });
it('it can log without exception', function() {
var msg = {
msg: {
mystrangeobj:"hello",
},
};
msg.msg.toString = function(){
throw new Error('Exception in toString - should have been caught');
}
msg.msg.constructor = { name: "strangeobj" };
var ret = log.info(msg.msg);
});
it('it can log an object but use .message', function() {
var msg = {
msg: {
message: "my special message",
mystrangeobj:"hello",
},
};
var ret = log.info(msg.msg);
sinon.assert.calledWithMatch(util.log,"my special message");
});
}); });

View File

@ -772,6 +772,117 @@ describe("@node-red/util/util", function() {
var resultJson = JSON.parse(result.msg); var resultJson = JSON.parse(result.msg);
resultJson.socket.should.eql('[internal]'); resultJson.socket.should.eql('[internal]');
}); });
it('object which fails to serialise', function(done) {
var msg = {
msg: {
obj:{
cantserialise:{
message:'this will not be displayed',
toJSON: function(val) {
throw 'this exception should have been caught';
return 'should not display because we threw first';
},
},
canserialise:{
message:'this should be displayed',
}
},
}
};
var result = util.encodeObject(msg);
result.format.should.eql("error");
var success = (result.msg.indexOf('cantserialise') > 0);
success &= (result.msg.indexOf('this exception should have been caught') > 0);
success &= (result.msg.indexOf('canserialise') > 0);
success.should.eql(1);
done();
});
it('object which fails to serialise - different error type', function(done) {
var msg = {
msg: {
obj:{
cantserialise:{
message:'this will not be displayed',
toJSON: function(val) {
throw new Error('this exception should have been caught');
return 'should not display because we threw first';
},
},
canserialise:{
message:'this should be displayed',
}
},
}
};
var result = util.encodeObject(msg);
result.format.should.eql("error");
var success = (result.msg.indexOf('cantserialise') > 0);
success &= (result.msg.indexOf('this exception should have been caught') > 0);
success &= (result.msg.indexOf('canserialise') > 0);
success.should.eql(1);
done();
});
it('very large object which fails to serialise should be truncated', function(done) {
var msg = {
msg: {
obj:{
big:"",
cantserialise:{
message:'this will not be displayed',
toJSON: function(val) {
throw new Error('this exception should have been caught');
return 'should not display because we threw first';
},
},
canserialise:{
message:'this should be displayed',
}
},
}
};
for (var i = 0; i < 1000; i++) {
msg.msg.obj.big += 'some more string ';
}
var result = util.encodeObject(msg);
result.format.should.eql("error");
var resultJson = JSON.parse(result.msg);
var success = (resultJson.message.length <= 1000);
success.should.eql(true);
done();
});
it('test bad toString', function(done) {
var msg = {
msg: {
mystrangeobj:"hello",
},
};
msg.msg.toString = function(){
throw new Error('Exception in toString - should have been caught');
}
msg.msg.constructor = { name: "strangeobj" };
var result = util.encodeObject(msg);
var success = (result.msg.indexOf('[Type not printable]') >= 0);
success.should.eql(true);
done();
});
it('test bad object constructor', function(done) {
var msg = {
msg: {
mystrangeobj:"hello",
constructor: {
get name(){
throw new Error('Exception in constructor name');
}
}
},
};
var result = util.encodeObject(msg);
done();
});
}); });
}); });
}); });