diff --git a/CHANGELOG.md b/CHANGELOG.md index b297e93ab..9b8eff388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ +#### 2.1.4: Maintenance Release + +Runtime + + - fix env var access using $parent for groups (#3278) @HiroyasuNishiyama + - Add proper error handling for 404 errors when serving debug files (#3277) @knolleary + - Add Japanese translations for Node-RED v2.1.0-beta.1 (#3179) @kazuhitoyokoi + - Include full user object on login audit events (#3269) @knolleary + - Remove styling from de locale files (#3237) @knolleary + +Editor + + - Change tab hide button icon to an eye and add search option (#3282) @knolleary + - Fix i18n handling of namespaces with spaces in (#3281) @knolleary + - Trigger change event when autoComplete fills in input (#3280) @knolleary + - Apply CN i18n fix (#3279) @knolleary + - fix select menu label of config node to use paletteLabel (#3273) @HiroyasuNishiyama + - fix removed tab not to cause node conflict (#3275) @HiroyasuNishiyama + - Group diff fix (#3239) @knolleary + - Only toggle disabled workspace flag if on activeWorkspace (#3252) @knolleary + - Do not show status for disabled nodes (#3253) @knolleary + - Set dimension value for tour guide (#3265) @kazuhitoyokoi + - Avoid redundant initialisation of TypedInput type (#3263) @knolleary + - Don't let themes change flow port label color (#3270) @bonanitech + - Fix treeList gutter calculation to handle floating gutters (#3238) @knolleary + +Nodes + +- Debug: Handle RegExp types in Debug sidebar (#3251) @knolleary +- Delay: fix 2nd output when in rate limit per topic modes (#3261) @dceejay +- Link: fix to show link target when selected (#3267) @HiroyasuNishiyama +- Inject: Do not modify inject node props in oneditprepare (#3242) @knolleary +- HTTP Request: HTTP Basic Auth should always add : between username and password even if empty (#3236) @hardillb #### 2.1.3: Maintenance Release diff --git a/package.json b/package.json index b23e52876..15ce9148c 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ } ], "dependencies": { - "acorn": "8.5.0", + "acorn": "8.6.0", "acorn-walk": "8.2.0", - "ajv": "8.6.3", + "ajv": "8.8.2", "async-mutex": "0.3.2", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", @@ -37,7 +37,7 @@ "clone": "2.1.2", "content-type": "1.0.4", "cookie": "0.4.1", - "cookie-parser": "1.4.5", + "cookie-parser": "1.4.6", "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.0.1", @@ -46,11 +46,11 @@ "form-data": "4.0.0", "fs-extra": "10.0.0", "fs.notify": "0.0.4", - "got": "11.8.2", + "got": "11.8.3", "hash-sum": "2.0.0", "hpagent": "0.1.2", "https-proxy-agent": "5.0.0", - "i18next": "21.3.1", + "i18next": "21.5.4", "iconv-lite": "0.6.3", "is-utf8": "0.2.1", "js-yaml": "3.14.1", @@ -60,22 +60,22 @@ "media-typer": "1.1.0", "memorystore": "1.6.6", "mime": "2.5.2", - "moment-timezone": "0.5.33", + "moment-timezone": "0.5.34", "mqtt": "4.2.8", "multer": "1.4.3", "mustache": "4.2.0", "node-red-admin": "^2.2.1", "nopt": "5.0.0", - "oauth2orize": "1.11.0", + "oauth2orize": "1.11.1", "on-headers": "1.0.2", "passport": "0.5.0", "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", - "raw-body": "2.4.1", + "raw-body": "2.4.2", "semver": "7.3.5", "tar": "6.1.11", "tough-cookie": "4.0.0", - "uglify-js": "3.14.2", + "uglify-js": "3.14.4", "uuid": "8.3.2", "ws": "7.5.1", "xml2js": "0.4.23" @@ -109,11 +109,11 @@ "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "marked": "3.0.7", "minami": "1.2.3", - "mocha": "9.1.2", + "mocha": "9.1.3", "node-red-node-test-helper": "^0.2.7", - "nodemon": "2.0.13", + "nodemon": "2.0.15", "proxy": "^1.0.2", - "sass": "1.43.2", + "sass": "1.44.0", "should": "13.2.3", "sinon": "11.1.2", "stoppable": "^1.1.0", diff --git a/packages/node_modules/@node-red/editor-api/lib/auth/index.js b/packages/node_modules/@node-red/editor-api/lib/auth/index.js index f32f6d0d6..41d96b3f6 100644 --- a/packages/node_modules/@node-red/editor-api/lib/auth/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/auth/index.js @@ -141,7 +141,7 @@ function completeVerify(profile,done) { Users.authenticate(profile).then(function(user) { if (user) { Tokens.create(user.username,"node-red-editor",user.permissions).then(function(tokens) { - log.audit({event: "auth.login",username:user.username,scope:user.permissions}); + log.audit({event: "auth.login",user,username:user.username,scope:user.permissions}); user.tokens = tokens; done(null,user); }); diff --git a/packages/node_modules/@node-red/editor-api/lib/auth/strategies.js b/packages/node_modules/@node-red/editor-api/lib/auth/strategies.js index bae4df5c3..7a77354fa 100644 --- a/packages/node_modules/@node-red/editor-api/lib/auth/strategies.js +++ b/packages/node_modules/@node-red/editor-api/lib/auth/strategies.js @@ -93,7 +93,7 @@ var passwordTokenExchange = function(client, username, password, scope, done) { return logEntry.user !== username; }); Tokens.create(username,client.id,scope).then(function(tokens) { - log.audit({event: "auth.login",username:username,client:client.id,scope:scope}); + log.audit({event: "auth.login",user,username:username,client:client.id,scope:scope}); done(null,tokens.accessToken,null,{expires_in:tokens.expires_in}); }); } else { diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 19d488b52..aebb7454e 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -28,7 +28,7 @@ "mime": "2.5.2", "multer": "1.4.3", "mustache": "4.2.0", - "oauth2orize": "1.11.0", + "oauth2orize": "1.11.1", "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", "passport": "0.5.0", diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 0e60d17e3..cac66dd7a 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -59,6 +59,8 @@ "hideOtherFlows": "Hide other flows", "showAllFlows": "Show all flows", "hideAllFlows": "Hide all flows", + "hiddenFlows": "List __count__ hidden flow", + "hiddenFlows_plural": "List __count__ hidden flows", "showLastHiddenFlow": "Show last hidden flow", "listFlows": "List flows", "listSubflows": "List subflows", @@ -90,6 +92,7 @@ "palette": { "show": "Show palette" }, + "edit": "Edit", "settings": "Settings", "userSettings": "User Settings", "nodes": "Nodes", @@ -668,7 +671,8 @@ "unusedConfigNodes": "Unused configuration nodes", "invalidNodes": "Invalid nodes", "uknownNodes": "Unknown nodes", - "unusedSubflows": "Unused subflows" + "unusedSubflows": "Unused subflows", + "hiddenFlows": "Hidden flows" } }, "help": { @@ -1135,6 +1139,7 @@ "defaultValue": "Default value" }, "tourGuide": { + "takeATour": "Take a tour", "start": "Start", "next": "Next" }, diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 83a6468a4..6436c0518 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -54,7 +54,14 @@ "delete": "本当に '__label__' を削除しますか?", "dropFlowHere": "ここにフローをドロップしてください", "addFlow": "フローの追加", + "addFlowToRight": "右側にフローを追加", + "hideFlow": "フローを非表示", + "hideOtherFlows": "他のフローを非表示", + "showAllFlows": "全てのフローを表示", + "hideAllFlows": "全てのフローを非表示", + "showLastHiddenFlow": "最後に非表示にしたフローを表示", "listFlows": "フロー一覧", + "listSubflows": "サブフロー一覧", "status": "状態", "enabled": "有効", "disabled": "無効", @@ -83,6 +90,7 @@ "palette": { "show": "パレットを表示" }, + "edit": "編集", "settings": "設定", "userSettings": "ユーザ設定", "nodes": "ノード", @@ -105,6 +113,7 @@ "editPalette": "パレットの管理", "other": "その他", "showTips": "ヒントを表示", + "showWelcomeTours": "新バージョンのガイドツアーを表示", "help": "Node-REDウェブサイト", "projects": "プロジェクト", "projects-new": "新規", @@ -116,7 +125,20 @@ "groupSelection": "選択部分をグループ化", "ungroupSelection": "選択部分をグループ解除", "groupMergeSelection": "選択部分をマージ", - "groupRemoveSelection": "グループから削除" + "groupRemoveSelection": "グループから削除", + "arrange": "配置", + "alignLeft": "左揃え", + "alignCenter": "左右中央揃え", + "alignRight": "右揃え", + "alignTop": "上揃え", + "alignMiddle": "上下中央揃え", + "alignBottom": "下揃え", + "distributeHorizontally": "左右に整列", + "distributeVertically": "上下に整列", + "moveToBack": "最背面へ移動", + "moveToFront": "最前面へ移動", + "moveBackwards": "背面へ移動", + "moveForwards": "前面へ移動" } }, "actions": { @@ -451,7 +473,8 @@ "global": "グローバル", "workspace": "ワークスペース", "selectAll": "全てのノードを選択", - "selectAllConnected": "接続された全てのノードを選択", + "selectNone": "選択を外す", + "selectAllConnected": "接続されたノードを選択", "addRemoveNode": "ノードの選択、選択解除", "editSelected": "選択したノードを編集", "deleteSelected": "選択したノードや接続を削除", @@ -461,10 +484,13 @@ "moveNode": "選択したノードを移動(移動量大)", "toggleSidebar": "サイドバーの表示/非表示", "togglePalette": "パレットの表示/非表示", - "copyNode": "選択したノードをコピー", - "cutNode": "選択したノードを切り取り", + "copyNode": "ノードをコピー", + "cutNode": "ノードを切り取り", "pasteNode": "ノードを貼り付け", + "copyGroupStyle": "グループ様式をコピー", + "pasteGroupStyle": "グループ様式を貼り付け", "undoChange": "変更操作を戻す", + "redoChange": "変更操作をやり直し", "searchBox": "ノードを検索", "managePalette": "パレットの管理", "actionList": "動作一覧" @@ -519,7 +545,8 @@ "nodeEnabled_plural": "ノードを有効化しました:", "nodeDisabled": "ノードを無効化しました:", "nodeDisabled_plural": "ノードを無効化しました:", - "nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました" + "nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました", + "unknownNodeRegistered": "ノードの読み込みエラー: " }, "editor": { "title": "パレットの管理", @@ -1108,6 +1135,11 @@ "preview": "UIプレビュー", "defaultValue": "デフォルト値" }, + "tourGuide": { + "takeATour": "ツアーを開始", + "start": "開始", + "next": "次へ" + }, "languages": { "de": "ドイツ語", "en-US": "英語", diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json index 792f6471a..3252563b7 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json @@ -225,7 +225,7 @@ "compact": "紧凑", "formatted": "已格式化", "copy": "导出到剪贴板", - "export": "到处到库", + "export": "导出到库", "exportAs": "导出为", "overwrite": "替换", "exists": "

\"__file__\"已存在

是否要替换它?

" diff --git a/packages/node_modules/@node-red/editor-client/src/js/i18n.js b/packages/node_modules/@node-red/editor-client/src/js/i18n.js index 028321ce9..b8c03b85b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/i18n.js +++ b/packages/node_modules/@node-red/editor-client/src/js/i18n.js @@ -38,6 +38,8 @@ RED.i18n = (function() { defaultNS: "editor", fallbackLng: ['en-US'], returnObjects: true, + keySeparator: ".", + nsSeparator: ":", interpolation: { unescapeSuffix: 'HTML', escapeValue: false, diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 76f544396..b17387a52 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -805,7 +805,6 @@ RED.nodes = (function() { var removedGroups = []; if (ws) { delete workspaces[id]; - allNodes.removeTab(id); delete linkTabMap[id]; workspacesOrder.splice(workspacesOrder.indexOf(id),1); var i; @@ -843,6 +842,7 @@ RED.nodes = (function() { for (i=removedGroups.length-1; i>=0; i--) { removeGroup(removedGroups[i]); } + allNodes.removeTab(id); RED.events.emit('flows:remove',ws); } return {nodes:removedNodes,links:removedLinks, groups: removedGroups}; @@ -1097,6 +1097,11 @@ RED.nodes = (function() { // Until we know how that can happen, add a filter here to remove them node.nodes = node.nodes.filter(function(n) { return !!n }).map(function(n) { return n.id }); } + if (n.type === "tab" || n.type === "group") { + if (node.env && node.env.length === 0) { + delete node.env; + } + } if (n._def.category != "config") { node.x = n.x; node.y = n.y; diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index 517cbf316..36e45e570 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -590,7 +590,7 @@ var RED = (function() { {id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"} ]}); } - menuOptions.push({id:"menu-item-edit-menu", label:"Edit", options: [ + menuOptions.push({id:"menu-item-edit-menu", label:RED._("menu.label.edit"), options: [ {id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"}, {id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"}, null, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js index 6f1ac1aa1..192ccda7a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js @@ -21,7 +21,7 @@ * value: String : the value to insert if selected * label: String|DOM Element : the label to display in the dropdown. * } - * + * */ $.widget( "nodered.autoComplete", { @@ -62,7 +62,7 @@ maxHeight: 200, class: "red-ui-autoComplete-container", options: completions, - onselect: (opt) => { this.element.val(opt.value); this.element.focus() }, + onselect: (opt) => { this.element.val(opt.value); this.element.focus(); this.element.trigger("change") }, onclose: () => { this.completionMenuShown = false; delete this.menu; this.element.focus()} }); this.menu.show({ diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js index f3327c2e6..d1e6a39d2 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js @@ -117,6 +117,8 @@ RED.tabs = (function() { menuOptions = options.menu() } else if (Array.isArray(options.menu)) { menuOptions = options.menu; + } else if (typeof options.menu === 'function') { + menuOptions = options.menu(); } menu = RED.menu.init({options: menuOptions}); menu.attr("id",options.id+"-menu"); @@ -809,15 +811,18 @@ RED.tabs = (function() { event.preventDefault(); removeTab(tab.id); }); + RED.popover.tooltip(closeLink,RED._("workspace.hideFlow")); } if (tab.hideable) { li.addClass("red-ui-tabs-closeable") - var closeLink = $("",{href:"#",class:"red-ui-tab-close"}).appendTo(li); - closeLink.append(''); + var closeLink = $("",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li); + closeLink.append(''); + closeLink.append(''); closeLink.on("click",function(event) { event.preventDefault(); hideTab(tab.id); }); + RED.popover.tooltip(closeLink,RED._("workspace.hideFlow")); } var badges = $('').appendTo(li); @@ -826,7 +831,8 @@ RED.tabs = (function() { $('').appendTo(badges); } - link.attr("title",tab.label); + // link.attr("title",tab.label); + RED.popover.tooltip(link,function() { return tab.label}) if (options.onadd) { options.onadd(tab); @@ -945,7 +951,6 @@ RED.tabs = (function() { renameTab: function(id,label) { tabs[id].label = label; var tab = ul.find("a[href='#"+id+"']"); - tab.attr("title",label); tab.find("span.red-ui-text-bidi-aware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label)); updateTabWidths(); }, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 9092d4dcf..2d628769e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -556,7 +556,7 @@ this.optionExpandButton = $('').appendTo(this.uiSelect); this.optionExpandButtonIcon = $('').appendTo(this.optionExpandButton); - this.type(this.options.default||this.typeList[0].value); + this.type(this.typeField.val() || this.options.default||this.typeList[0].value); this.typeChanged = !!this.options.default; }catch(err) { console.log(err.stack); @@ -805,6 +805,7 @@ var that = this; var currentType = this.type(); this.typeMap = {}; + var firstCall = (this.typeList === undefined); this.typeList = types.map(function(opt) { var result; if (typeof opt === 'string') { @@ -829,10 +830,14 @@ } this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) }); if (currentType && !this.typeMap.hasOwnProperty(currentType)) { - this.type(this.typeList[0].value); + if (!firstCall) { + this.type(this.typeList[0].value); + } } else { this.propertyType = null; - this.type(currentType); + if (!firstCall) { + this.type(currentType); + } } if (this.typeList.length === 1 && !this.typeList[0].icon && (!this.typeList[0].label || this.typeList[0].showLabel === false)) { this.selectTrigger.hide() diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js b/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js index 646fe326e..b6a069ab5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js @@ -554,6 +554,8 @@ RED.diff = (function() { color: "#DDAA99", defaults:{name:{value:""}} } + } else if (node.type === "group") { + def = RED.group.def; } else { def = {}; } @@ -763,16 +765,15 @@ RED.diff = (function() { } } - if (node.hasOwnProperty('x')) { if (localNode) { - if (localNode.x !== node.x || localNode.y !== node.y) { + if (localNode.x !== node.x || localNode.y !== node.y || localNode.w !== node.w || localNode.h !== node.h ) { localChanged = true; localChanges++; } } if (remoteNode) { - if (remoteNode.x !== node.x || remoteNode.y !== node.y) { + if (remoteNode.x !== node.x || remoteNode.y !== node.y|| remoteNode.w !== node.w || remoteNode.h !== node.h) { remoteChanged = true; remoteChanges++; } @@ -790,7 +791,12 @@ RED.diff = (function() { localCell.addClass("red-ui-diff-status-"+(localChanged?"changed":"unchanged")); $(''+(localChanged?'':'')+'').appendTo(localCell); element = $('').appendTo(localCell); - propertyElements['local.position'] = RED.utils.createObjectElement({x:localNode.x,y:localNode.y}, + var localPosition = {x:localNode.x,y:localNode.y}; + if (localNode.hasOwnProperty('w')) { + localPosition.w = localNode.w; + localPosition.h = localNode.h; + } + propertyElements['local.position'] = RED.utils.createObjectElement(localPosition, { path: "position", exposeApi: true, @@ -811,7 +817,12 @@ RED.diff = (function() { if (remoteNode) { $(''+(remoteChanged?'':'')+'').appendTo(remoteCell); element = $('').appendTo(remoteCell); - propertyElements['remote.position'] = RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y}, + var remotePosition = {x:remoteNode.x,y:remoteNode.y}; + if (remoteNode.hasOwnProperty('w')) { + remotePosition.w = remoteNode.w; + remotePosition.h = remoteNode.h; + } + propertyElements['remote.position'] = RED.utils.createObjectElement(remotePosition, { path: "position", exposeApi: true, @@ -883,11 +894,11 @@ RED.diff = (function() { } } } - var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); + var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='w'&&p!=='h'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); if (def.defaults) { properties = properties.concat(Object.keys(def.defaults)); } - if (node.type !== 'tab') { + if (node.type !== 'tab' && node.type !== "group") { properties = properties.concat(['inputLabels','outputLabels']); } if ( ((localNode && localNode.hasOwnProperty('icon')) || (remoteNode && remoteNode.hasOwnProperty('icon'))) && diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 643110d10..6017fb687 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -744,7 +744,16 @@ RED.editor = (function() { delete cn.__label__; }); - select.append(''); + var label = type; + if (typeof node_def.paletteLabel !== "undefined") { + try { + label = RED.utils.sanitize((typeof node_def.paletteLabel === "function" ? node_def.paletteLabel.call(node_def) : node_def.paletteLabel)||type); + } catch(err) { + console.log("Definition error: "+type+".paletteLabel",err); + } + } + + select.append(''); window.setTimeout(function() { select.trigger("change");},50); } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js index 10425f18a..fb1d89b3f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js @@ -56,8 +56,12 @@ }); } if (!isSameObj(old_env, new_env)) { - node.env = new_env; editState.changes.env = node.env; + if (new_env.length === 0) { + delete node.env; + } else { + node.env = new_env; + } editState.changed = true; } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js index 02c7735a7..045588310 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js @@ -105,6 +105,7 @@ RED.search = (function() { val = extractFlag(val,"unused",flags); val = extractFlag(val,"config",flags); val = extractFlag(val,"subflow",flags); + val = extractFlag(val,"hidden",flags); // uses: val = extractValue(val,"uses",flags); @@ -150,7 +151,15 @@ RED.search = (function() { continue; } } - + if (flags.hasOwnProperty("hidden")) { + // Only tabs can be hidden + if (node.node.type !== 'tab') { + continue + } + if (!RED.workspaces.isHidden(node.node.id)) { + continue + } + } if (flags.hasOwnProperty("unused")) { var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) || (isConfigNode && node.node.users.length === 0) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js index 66fdbeb2d..77eb905f4 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js @@ -379,7 +379,7 @@ RED.sidebar.help = (function() { var currentVersionParts = RED.settings.version.split("."); var tourVersionParts = tour.version.split("."); if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) { - tourHeader = '
' + tourHeader = '
'; } } var aboutHeader = '
'+tourHeader+'
' diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js index e6eff18ae..fb393156e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js @@ -175,6 +175,7 @@ RED.sidebar.info.outliner = (function() { n.d = true; } n.dirty = true; + n.dirtyStatus = true; n.changed = true; RED.events.emit("nodes:change",n); groupHistoryEvent.events.push(historyEvent); @@ -203,6 +204,7 @@ RED.sidebar.info.outliner = (function() { n.d = true; } n.dirty = true; + n.dirtyStatus = true; n.changed = true; RED.events.emit("nodes:change",n); RED.history.push(historyEvent); @@ -272,6 +274,7 @@ RED.sidebar.info.outliner = (function() { {label:RED._("sidebar.info.search.invalidNodes"), value: "is:invalid"}, {label:RED._("sidebar.info.search.uknownNodes"), value: "type:unknown"}, {label:RED._("sidebar.info.search.unusedSubflows"), value:"is:subflow is:unused"}, + {label:RED._("sidebar.info.search.hiddenFlows"), value:"is:hidden"}, ] }); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js index 651020d21..3f81f1d68 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js @@ -364,6 +364,8 @@ RED.tourGuide = (function() { if (step.fallback) { focus.one("mouseenter", function(evt) { setTimeout(function() { + var pos = targetElement[0].getBoundingClientRect(); + var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5); focus.css({ width: (4*dimension)+"px", height: (4*dimension)+"px" diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 642aff6b2..2f5f7063e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -142,6 +142,8 @@ RED.utils = (function() { result = $('').text('function'); } else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) { result = $('').text(value.data); + } else if (value.hasOwnProperty('type') && value.type === 'regexp') { + result = $('').text(value.data); } else { result = $('object'); } @@ -440,6 +442,8 @@ RED.utils = (function() { $('undefined').appendTo(entryObj); } else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) { e = $('').text(obj.data).appendTo(entryObj); + } else if (typeHint === "regexp" || (obj.__enc__ && obj.type === 'regexp')) { + e = $('').text((typeof obj === "string")?obj:obj.data).appendTo(entryObj); } else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) { e = $('').text("function").appendTo(entryObj); } else if (typeHint === "internal" || (obj.__enc__ && obj.type === 'internal')) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 014ebdcfb..166cb2901 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -3675,7 +3675,11 @@ RED.view = (function() { nodeEl = document.getElementById(d.id); } if (nodeEl) { - if (!showStatus || !d.status) { + // Do not show node status if: + // - global flag set + // - node has no status + // - node is disabled + if (!showStatus || !d.status || d.d === true) { nodeEl.__statusGroup__.style.display = "none"; } else { nodeEl.__statusGroup__.style.display = "inline"; @@ -4421,6 +4425,9 @@ RED.view = (function() { n.selected = true; n.dirty = true; movingSet.add(n); + if (targets.length === 1) { + RED.view.reveal(n.id); + } }); updateSelection(); redraw(); @@ -5029,6 +5036,7 @@ RED.view = (function() { delete node.d; } node.dirty = true; + node.dirtyStatus = true; node.changed = true; RED.events.emit("nodes:change",node); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index e73544f06..0082b7aed 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -218,55 +218,64 @@ RED.workspaces = (function() { scrollable: true, addButton: "core:add-flow", addButtonCaption: RED._("workspace.addFlow"), - menu: [ - { - id:"red-ui-tabs-menu-option-search-flows", - label: RED._("workspace.listFlows"), - onselect: "core:list-flows" - }, - { - id:"red-ui-tabs-menu-option-search-subflows", - label: RED._("workspace.listSubflows"), - onselect: "core:list-subflows" - }, - null, - { - id:"red-ui-tabs-menu-option-add-flow", - label: RED._("workspace.addFlow"), - onselect: "core:add-flow" - }, - { - id:"red-ui-tabs-menu-option-add-flow-right", - label: RED._("workspace.addFlowToRight"), - onselect: "core:add-flow-to-right" - }, - null, - { - id:"red-ui-tabs-menu-option-add-hide-flows", - label: RED._("workspace.hideFlow"), - onselect: "core:hide-flow" - }, - { - id:"red-ui-tabs-menu-option-add-hide-other-flows", - label: RED._("workspace.hideOtherFlows"), - onselect: "core:hide-other-flows" - }, - { - id:"red-ui-tabs-menu-option-add-show-all-flows", - label: RED._("workspace.showAllFlows"), - onselect: "core:show-all-flows" - }, - { - id:"red-ui-tabs-menu-option-add-hide-all-flows", - label: RED._("workspace.hideAllFlows"), - onselect: "core:hide-all-flows" - }, - { - id:"red-ui-tabs-menu-option-add-show-last-flow", - label: RED._("workspace.showLastHiddenFlow"), - onselect: "core:show-last-hidden-flow" + menu: function() { + var menuItems = [ + { + id:"red-ui-tabs-menu-option-search-flows", + label: RED._("workspace.listFlows"), + onselect: "core:list-flows" + }, + { + id:"red-ui-tabs-menu-option-search-subflows", + label: RED._("workspace.listSubflows"), + onselect: "core:list-subflows" + }, + null, + { + id:"red-ui-tabs-menu-option-add-flow", + label: RED._("workspace.addFlow"), + onselect: "core:add-flow" + }, + { + id:"red-ui-tabs-menu-option-add-flow-right", + label: RED._("workspace.addFlowToRight"), + onselect: "core:add-flow-to-right" + }, + null, + { + id:"red-ui-tabs-menu-option-add-hide-flows", + label: RED._("workspace.hideFlow"), + onselect: "core:hide-flow" + }, + { + id:"red-ui-tabs-menu-option-add-hide-other-flows", + label: RED._("workspace.hideOtherFlows"), + onselect: "core:hide-other-flows" + }, + { + id:"red-ui-tabs-menu-option-add-show-all-flows", + label: RED._("workspace.showAllFlows"), + onselect: "core:show-all-flows" + }, + { + id:"red-ui-tabs-menu-option-add-hide-all-flows", + label: RED._("workspace.hideAllFlows"), + onselect: "core:hide-all-flows" + }, + { + id:"red-ui-tabs-menu-option-add-show-last-flow", + label: RED._("workspace.showLastHiddenFlow"), + onselect: "core:show-last-hidden-flow" + } + ] + if (hideStack.length > 0) { + menuItems.unshift({ + label: RED._("workspace.hiddenFlows",{count: hideStack.length}), + onselect: "core:list-hidden-flows" + }) } - ] + return menuItems; + } }); workspaceTabCount = 0; } @@ -406,7 +415,9 @@ RED.workspaces = (function() { } } }) - + RED.actions.add("core:list-hidden-flows",function() { + RED.actions.invoke("core:search","is:hidden "); + }) RED.actions.add("core:list-flows",function() { RED.actions.invoke("core:search","type:tab "); }) @@ -450,7 +461,7 @@ RED.workspaces = (function() { var changes = { disabled: workspace.disabled }; workspace.disabled = disabled; $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled); - if (id || activeWorkspace) { + if (!id || (id === activeWorkspace)) { $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled); } var historyEvent = { @@ -536,6 +547,9 @@ RED.workspaces = (function() { RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs)); } }, + isHidden: function(id) { + return hideStack.includes(id) + }, show: function(id,skipStack,unhideOnly) { if (!workspace_tabs.contains(id)) { var sf = RED.nodes.subflow(id); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss index 930b2d2d4..40dbeea06 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss @@ -197,6 +197,7 @@ $view-select-mode-background: $secondary-background-selected; $view-grid-color: #eee; $node-label-color: #333; +$node-port-label-color: #888; $node-border: #999; $node-border-unknown: #f33; $node-border-placeholder: #aaa; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss index 503bdf5bd..860ec1095 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/flow.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/flow.scss @@ -47,7 +47,7 @@ .red-ui-flow-port-label { stroke-width: 0; - fill: $secondary-text-color; + fill: $node-port-label-color; font-size: 16px; dominant-baseline: middle; text-anchor: middle; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/header.scss b/packages/node_modules/@node-red/editor-client/src/sass/header.scss index bd1e78a81..697a90729 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/header.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/header.scss @@ -187,7 +187,7 @@ ul.red-ui-menu-dropdown { background: $header-menu-background; border: 1px solid $header-menu-background; - width: 250px !important; + width: 260px !important; margin-top: 0; li a { color: $header-menu-color; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss b/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss index e83996a5e..b05a3575f 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss @@ -389,7 +389,19 @@ i.red-ui-tab-icon { vertical-align: top; } - +.red-ui-tab-hide { + .fa-eye-slash { + display: none; + } + &:hover { + .fa-eye-slash { + display: inline + } + .fa-eye { + display: none + } + } +} .red-ui-tab-close { display: none; background: $tab-background-inactive; diff --git a/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js b/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js index e7eed1d0f..b26ee7d8f 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js @@ -1,14 +1,23 @@ export default { steps: [ { - title: "Create your first flow", + title: { + 'en-US': 'Create your first flow', + 'ja': 'はじめてのフローを作成' + }, width: 400, - description: 'This tutorial will guide you through creating your first flow', + description: { + 'en-US': 'This tutorial will guide you through creating your first flow', + 'ja': '本チュートリアルでは、はじめてのフローを作成する方法について説明します。' + }, nextButton: 'start' }, { element: "#red-ui-workspace .red-ui-tab-button.red-ui-tabs-add", - description: 'To add a new tab, click the button', + description: { + 'en-US': 'To add a new tab, click the button', + 'ja': '新しいタブを追加するため、 ボタンをクリックします。' + }, wait: { type: "dom-event", event: "click", @@ -18,7 +27,10 @@ export default { { element: '.red-ui-palette-node[data-palette-type="inject"]', direction: 'right', - description: 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.', + description: { + 'en-US': 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.', + 'ja': 'パレットには、利用できる全てのノードが一覧表示されます。injectノードをワークスペースにドラッグします。' + }, fallback: 'inset-bottom-right', wait: { type: "nr-event", @@ -38,7 +50,10 @@ export default { { element: '.red-ui-palette-node[data-palette-type="debug"]', direction: 'right', - description: 'Next, drag a new Debug node into the workspace.', + description: { + 'en-US': 'Next, drag a new Debug node into the workspace.', + 'ja': '次に、debugノードをワークスペースにドラッグします。' + }, fallback: 'inset-bottom-right', wait: { type: "nr-event", @@ -57,7 +72,10 @@ export default { }, { element: function() { return $("#"+this.injectNode.id+" .red-ui-flow-port") }, - description: 'Add a wire from the output of the Inject node to the input of the Debug node', + description: { + 'en-US': 'Add a wire from the output of the Inject node to the input of the Debug node', + 'ja': 'injectノードの出力から、debugノードの入力へワイヤーで接続します。' + }, fallback: 'inset-bottom-right', wait: { type: "nr-event", @@ -69,7 +87,10 @@ export default { }, { element: "#red-ui-header-button-deploy", - description: 'Deploy your changes so the flow is active in the runtime', + description: { + 'en-US': 'Deploy your changes so the flow is active in the runtime', + 'ja': 'フローをランタイムで実行させるため、変更をデプロイします。' + }, width: 200, wait: { type: "dom-event", diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index cb1266b89..367a75899 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -3,48 +3,79 @@ export default { steps: [ { titleIcon: "fa fa-map-o", - title: { "en-US": "Welcome to Node-RED 2.1!" }, - description: { "en-US": "Let's take a moment to discover the new features in this release." } + title: { + "en-US": "Welcome to Node-RED 2.1!", + "ja": "Node-RED 2.1へようこそ!" + }, + description: { + "en-US": "Let's take a moment to discover the new features in this release.", + "ja": "本リリースの新機能を見つけてみましょう。" + } }, { - title: { "en-US": "A new Tour Guide" }, - description: { "en-US": "

First, as you've already found, we now have this tour of new features. We'll only show the tour the first time you open the editor for each new version of Node-RED.

"+ - "

You can choose not to see this tour in the future by disabling it under the View tab of User Settings.

" } + title: { + "en-US": "A new Tour Guide", + "ja": "新しいツアーガイド" + }, + description: { + "en-US": "

First, as you've already found, we now have this tour of new features. We'll only show the tour the first time you open the editor for each new version of Node-RED.

" + + "

You can choose not to see this tour in the future by disabling it under the View tab of User Settings.

", + "ja": "

最初に、既に見つけている様に、新機能の本ツアーがあります。本ツアーは、新バージョンのNode-REDフローエディタを初めて開いた時のみ表示されます。

" + + "

ユーザ設定の表示タブの中で、この機能を無効化することで、本ツアーを表示しないようにすることもできます。

" + } }, { - title: { "en-US": "New Edit menu" }, + title: { + "en-US": "New Edit menu", + "ja": "新しい編集メニュー" + }, prepare() { $("#red-ui-header-button-sidemenu").trigger("click"); - $("#menu-item-edit-menu").parent().addClass("open") + $("#menu-item-edit-menu").parent().addClass("open"); }, complete() { - $("#menu-item-edit-menu").parent().removeClass("open") + $("#menu-item-edit-menu").parent().removeClass("open"); }, element: "#menu-item-edit-menu-submenu", interactive: false, direction: "left", - description: { "en-US": "

The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.

"+ - "

The menu now displays keyboard shortcuts for the options.

" } - + description: { + "en-US": "

The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.

" + + "

The menu now displays keyboard shortcuts for the options.

", + "ja": "

メインメニューに「編集」セクションが追加されました。本セクションには、切り取り/貼り付けや、変更操作を戻す/やり直しの様な使い慣れたオプションが含まれています。

" + + "

本メニューには、オプションのためのキーボードショートカットも表示されるようになりました。

" + } }, { - title: { "en-US": "Arranging nodes" }, + title: { + "en-US": "Arranging nodes", + "ja": "ノードの配置" + }, prepare() { $("#red-ui-header-button-sidemenu").trigger("click"); - $("#menu-item-arrange-menu").parent().addClass("open") + $("#menu-item-arrange-menu").parent().addClass("open"); }, complete() { - $("#menu-item-arrange-menu").parent().removeClass("open") + $("#menu-item-arrange-menu").parent().removeClass("open"); }, element: "#menu-item-arrange-menu-submenu", interactive: false, direction: "left", - description: { "en-US": "

The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.

" }, + description: { + "en-US": "

The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.

", + "ja": "

メニューの新しい「配置」セクションには、ノードの配置を助ける新しいオプションが提供されています。ノードの端を揃えたり、均等に配置したり、表示順序を変更したりできます。

" + } }, { - title: { "en-US": "Hiding tabs" }, + title: { + "en-US": "Hiding tabs", + "ja": "タブの非表示" + }, element: "#red-ui-workspace-tabs > li.active", - description: { "en-US": '

Tabs can now be hidden by clicking their icon.

The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.' }, + description: { + "en-US": '

Tabs can now be hidden by clicking their icon.

The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.', + "ja": '

アイコンをクリックすることで、タブを非表示にできます。

情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。' + }, interactive: false, prepare() { $("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","block"); @@ -54,9 +85,15 @@ export default { } }, { - title: { "en-US": "Tab menu" }, + title: { + "en-US": "Tab menu", + "ja": "タブメニュー" + }, element: "#red-ui-workspace-tabs-menu", - description: { "en-US": '

The new tab menu also provides lots of new options for your tabs.

' }, + description: { + "en-US": "

The new tab menu also provides lots of new options for your tabs.

", + "ja": "

新しいタブメニューには、タブに関する沢山の新しいオプションが提供されています。

" + }, interactive: false, direction: "left", prepare() { @@ -67,10 +104,16 @@ export default { } }, { - title: { "en-US": "Flow and Group level environment variables" }, + title: { + "en-US": "Flow and Group level environment variables", + "ja": "フローとグループの環境変数" + }, element: "#red-ui-workspace-tabs > li.active", interactive: false, - description: { "en-US": "

Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.

" }, + description: { + "en-US": "

Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.

", + "ja": "

フローとグループには、内部のノードから参照できる環境変数を設定できるようになりました。

" + } }, { prepare(done) { @@ -78,20 +121,28 @@ export default { setTimeout(done,700); }, element: "#red-ui-tab-editor-tab-envProperties-link-button", - description: { "en-US": "

Their edit dialogs have a new Environment Variables section.

" }, + description: { + "en-US": "

Their edit dialogs have a new Environment Variables section.

", + "ja": "

編集ダイアログに環境変数セクションが追加されました。

" + } }, { element: ".node-input-env-container-row", direction: "left", - description: { "en-US": '

The environment variables are listed in this table and new ones can be added by clicking the button.

' }, + description: { + "en-US": '

The environment variables are listed in this table and new ones can be added by clicking the button.

', + "ja": '

この表に環境変数が一覧表示されており、ボタンをクリックすることで新しい変数を追加できます。

' + }, complete(done) { $("#node-dialog-cancel").trigger("click"); setTimeout(done,500); } }, - { - title: {"en-US":"Link Call node added"}, + title: { + "en-US": "Link Call node added", + "ja": "Link Callノードを追加" + }, prepare(done) { this.paletteWasClosed = $("#red-ui-main-container").hasClass("red-ui-palette-closed"); RED.actions.invoke("core:toggle-palette",true) @@ -100,22 +151,34 @@ export default { }, element: '[data-palette-type="link call"]', direction: "right", - description: { "en-US": '

The Link Call node lets you call another flow that begins with a Link In node and get the result back when the message reaches a Link Out node.

' }, + description: { + "en-US": "

The Link Call node lets you call another flow that begins with a Link In node and get the result back when the message reaches a Link Out node.

", + "ja": "

Link Callノードを用いることで、Link Inノードから始まるフローを呼び出し、Link Outノードに到達した時に、結果を取得できます。

" + } }, { - title: {"en-US":"MQTT nodes support dynamic connections"}, + title: { + "en-US": "MQTT nodes support dynamic connections", + "ja": "MQTTノードが動的接続をサポート" + }, prepare(done) { $('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"}) setTimeout(done,100); }, element: '[data-palette-type="mqtt out"]', direction: "right", - description: { "en-US": '

The MQTT nodes now support creating their connections and subscriptions dynamically.

' }, + description: { + "en-US": '

The MQTT nodes now support creating their connections and subscriptions dynamically.

', + "ja": '

MQTTノードは、動的な接続や購読ができるようになりました。

' + }, }, { - title: {"en-US":"File nodes renamed"}, + title: { + "en-US": "File nodes renamed", + "ja": "ファイルノードの名前変更" + }, prepare(done) { - $('[data-palette-type="file"]')[0].scrollIntoView({block:"center"}) + $('[data-palette-type="file"]')[0].scrollIntoView({block:"center"}); setTimeout(done,100); }, complete() { @@ -125,13 +188,19 @@ export default { }, element: '[data-palette-type="file"]', direction: "right", - description: { "en-US": '

The file nodes have been renamed to make it clearer which node does what.

' }, + description: { + "en-US": "

The file nodes have been renamed to make it clearer which node does what.

", + "ja": "

fileノードの名前が変更され、どのノードが何を行うかが明確になりました。

" + } }, { - title: {"en-US":"Deep copy option on Change node"}, + title: { + "en-US": "Deep copy option on Change node", + "ja": "Changeノードのディープコピーオプション" + }, prepare(done) { - var def = RED.nodes.getType('change') - RED.editor.edit({id:"test",type:"change",rules:[{t:'set',p:'payload',pt:'msg', tot:'msg',to:"anotherProperty"}],_def:def, _:def._}) + var def = RED.nodes.getType('change'); + RED.editor.edit({id:"test",type:"change",rules:[{t:"set",p:"payload",pt:"msg", tot:"msg",to:"anotherProperty"}],_def:def, _:def._}); setTimeout(done,700); }, complete(done) { @@ -139,13 +208,22 @@ export default { setTimeout(done,500); }, element: function() { - return $(".node-input-rule-property-deepCopy").next() + return $(".node-input-rule-property-deepCopy").next(); }, - description: { "en-US": '

The Set rule has a new option to create a deep copy of the value. This ensures a complete copy is made, rather than using a reference.

' }, + description: { + "en-US": "

The Set rule has a new option to create a deep copy of the value. This ensures a complete copy is made, rather than using a reference.

", + "ja": "

値を代入に、値のディープコピーを作成するオプションが追加されました。これによって参照ではなく、完全なコピーが作成されます。

" + } }, { - title: { "en-US": "And that's not all..." }, - description: { "en-US": "

There are many more smaller changes, including:

  • Auto-complete suggestions in the msg TypedInput.
  • Support for msg.resetTimeout in the Join node.
  • Pushing messages to the front of the queue in the Delay node's rate limiting mode.
  • An optional second output on the Delay node for rate limited messages.
" } + title: { + "en-US": "And that's not all...", + "ja": "これが全てではありません..." + }, + description: { + "en-US": "

There are many more smaller changes, including:

  • Auto-complete suggestions in the msg TypedInput.
  • Support for msg.resetTimeout in the Join node.
  • Pushing messages to the front of the queue in the Delay node's rate limiting mode.
  • An optional second output on the Delay node for rate limited messages.
", + "ja": "

以下の様な小さな変更が沢山あります:

  • msg TypedInputの自動補完提案
  • Joinノードでmsg.resetTimeoutのサポート
  • Delayノードの流量制御モードにおいて先頭メッセージをキューに追加
  • Delayノードで流量制限されたメッセージ向けの任意の2つ目の出力
" + } } ] } diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js index 8d2121580..73d364e43 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.js @@ -280,6 +280,18 @@ module.exports = function(RED) { root: path.join(__dirname,"lib","debug"), dotfiles: 'deny' }; - res.sendFile(req.params[0], options); + try { + res.sendFile( + req.params[0], + options, + err => { + if (err) { + res.sendStatus(404); + } + } + ) + } catch(err) { + res.sendStatus(404); + } }); }; diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js index c4895e0e4..5205a5b18 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-delay.js @@ -372,6 +372,7 @@ module.exports = function(RED) { hit = false; for (var b in node.buffer) { // check if already in queue if (msg.topic === node.buffer[b].msg.topic) { + if (node.outputs === 2) { send([null,node.buffer[b].msg]) } node.buffer[b].done(); node.buffer[b] = {msg, send, done}; // if so - replace existing entry hit = true; diff --git a/packages/node_modules/@node-red/nodes/locales/de/messages.json b/packages/node_modules/@node-red/nodes/locales/de/messages.json index 1a5862f07..0b4c9ea1c 100755 --- a/packages/node_modules/@node-red/nodes/locales/de/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/de/messages.json @@ -851,7 +851,6 @@ "outputas": "Ausgabe", "breakchunks": "In Chunks aufteilen", "breaklines": "In Linien aufteilen", - "filelabel": "file", "sendError": "Nachricht bei Fehler senden (herkömmlicher Modus)", "encoding": "Kodierung", "deletelabel": "lösche __file__", diff --git a/packages/node_modules/@node-red/nodes/locales/ja/common/60-link.html b/packages/node_modules/@node-red/nodes/locales/ja/common/60-link.html index 3ac3a4491..286878dae 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/common/60-link.html +++ b/packages/node_modules/@node-red/nodes/locales/ja/common/60-link.html @@ -29,3 +29,12 @@

linkノード間のリンクはlinkノードを選択した場合にのみ表示されます。他のタブへのリンクがある場合には、仮想的なノードを表示します。この仮想的ノードをクリックすると、対応するタブに移動できます。

注: サブフローの外から中、もしくは、中から外へのリンクを作成することはできません。

+ + 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 bcbf2e8d4..4fdd6bb09 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -144,11 +144,16 @@ "filterCurrent": "現在のフロー", "debugNodes": "debugノード", "clearLog": "ログを削除", + "clearFilteredLog": "選択したメッセージを削除", "filterLog": "ログのフィルタリング", "openWindow": "新しいウィンドウで開く", "copyPath": "パスをコピー", "copyPayload": "値をコピー", - "pinPath": "展開を固定" + "pinPath": "展開を固定", + "selectAll": "全てを選択", + "selectNone": "選択を外す", + "all": "全て", + "filtered": "選択したメッセージ" }, "messageMenu": { "collapseAll": "全パスを折りたたむ", @@ -159,7 +164,15 @@ }, "link": { "linkIn": "link in", - "linkOut": "link out" + "linkOut": "link out", + "linkCall": "link call", + "linkOutReturn": "link return", + "outMode": "モード", + "sendToAll": "接続された全てのlinkノードへ送信", + "returnToCaller": "link callノードへ返却", + "error": { + "missingReturn": "返却するノードの情報が存在しません" + } }, "tls": { "tls": "TLS設定", @@ -282,7 +295,9 @@ "and": "回/", "rate": "流量", "msgper": "メッセージ/", + "queuemsg": "中間メッセージをキューに追加", "dropmsg": "中間メッセージを削除", + "sendmsg": "2番目の出力で中間メッセージを送信", "allowrate": "msg.rate(ミリ秒単位)で流量値を上書き", "label": { "delay": "delay", @@ -401,7 +416,11 @@ "maximumPacketSize": "最大パケット長", "receiveMaximum": "最大受信数", "session": "セッション", - "delay": "遅延" + "delay": "遅延", + "action": "動作", + "staticTopic": "1つのトピックを購読", + "dynamicTopic": "動的な購読", + "auto-connect": "自動接続" }, "sections-label": { "birth-message": "接続時の送信メッセージ(Birthメッセージ)", @@ -442,7 +461,10 @@ "invalid-topic": "不正なトピックが設定されています", "nonclean-missingclientid": "「セッションの初期化」使用時に、クライアントIDが設定されていません", "invalid-json-string": "不正なJSON文字列", - "invalid-json-parse": "JSON文字列のパースに失敗しました" + "invalid-json-parse": "JSON文字列のパースに失敗しました", + "invalid-action-action": "指定された動作が不正です", + "invalid-action-alreadyconnected": "接続する前にブローカから切断してください", + "invalid-action-badsubscription": "msg.topicが存在しないか不正です" } }, "httpin": { @@ -478,6 +500,7 @@ "proxy-config": "プロキシ設定", "use-proxyauth": "プロキシ認証を使用", "noproxy-hosts": "例外ホスト", + "senderr": "2xx以外の応答をcatchノードへ送信", "utf8": "UTF8文字列", "binary": "バイナリバッファ", "json": "JSONオブジェクト", @@ -703,13 +726,15 @@ "delete": "delete __property__", "move": "move __property__", "changeCount": "change: __count__ rules", - "regex": "正規表現を使用" + "regex": "正規表現を使用", + "deepCopy": "値のディープコピー" }, "action": { "set": "値の代入", "change": "値の置換", "delete": "値の削除", "move": "値の移動", + "toValue": "対象の値", "to": "対象の値", "search": "検索する文字列", "replace": "置換後の文字列" @@ -851,6 +876,8 @@ }, "file": { "label": { + "write": "write file", + "read": "read file", "filename": "ファイル名", "action": "動作", "addnewline": "メッセージの入力のたびに改行を追加", @@ -858,7 +885,6 @@ "outputas": "出力形式", "breakchunks": "チャンクへ分割", "breaklines": "行へ分割", - "filelabel": "file", "sendError": "エラーメッセージを送信(互換モード)", "encoding": "文字コード", "deletelabel": "delete __file__", diff --git a/packages/node_modules/@node-red/nodes/locales/ko/messages.json b/packages/node_modules/@node-red/nodes/locales/ko/messages.json index 442e0ef65..328edb2c5 100755 --- a/packages/node_modules/@node-red/nodes/locales/ko/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ko/messages.json @@ -771,7 +771,6 @@ "outputas": "출력형식", "breakchunks": "청크로 분할", "breaklines": "행으로 분할", - "filelabel": "file", "sendError": "에러메세지를 송신(호환모드)", "deletelabel": "delete __file__", "utf8String": "UTF8문자열", diff --git a/packages/node_modules/@node-red/nodes/locales/ru/messages.json b/packages/node_modules/@node-red/nodes/locales/ru/messages.json index f32160bf1..075efe5b2 100755 --- a/packages/node_modules/@node-red/nodes/locales/ru/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ru/messages.json @@ -816,7 +816,6 @@ "outputas": "Выход", "breakchunks": "Разбить файл на части", "breaklines": "Разбить на строки", - "filelabel": "файл", "sendError": "Отправлять сообщение при ошибке (устаревший режим)", "encoding": "Кодировка", "deletelabel": "удалить __file__", diff --git a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json index 7f928f733..090af502f 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json @@ -804,7 +804,6 @@ "outputas": "输出", "breakchunks": "分拆成块", "breaklines": "分拆成行", - "filelabel": "文件", "sendError": "发生错误时发送消息(传统模式)", "encoding": "编码", "deletelabel": "删除 __file__", diff --git a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json index 1335e9ff2..15b45892e 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json @@ -809,7 +809,6 @@ "outputas": "輸出", "breakchunks": "分拆成塊", "breaklines": "分拆成行", - "filelabel": "文件", "sendError": "發生錯誤時發送消息(傳統模式)", "encoding": "編碼", "deletelabel": "刪除 __file__", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 03b101969..e11c27a9b 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -15,13 +15,13 @@ } ], "dependencies": { - "acorn": "8.5.0", + "acorn": "8.6.0", "acorn-walk": "8.2.0", - "ajv": "8.6.3", + "ajv": "8.8.2", "body-parser": "1.19.0", "cheerio": "1.0.0-rc.10", "content-type": "1.0.4", - "cookie-parser": "1.4.5", + "cookie-parser": "1.4.6", "cookie": "0.4.1", "cors": "2.8.5", "cronosjs": "1.7.1", @@ -29,7 +29,7 @@ "form-data": "4.0.0", "fs-extra": "10.0.0", "fs.notify": "0.0.4", - "got": "11.8.2", + "got": "11.8.3", "hash-sum": "2.0.0", "hpagent": "0.1.2", "https-proxy-agent": "5.0.0", @@ -40,7 +40,7 @@ "multer": "1.4.3", "mustache": "4.2.0", "on-headers": "1.0.2", - "raw-body": "2.4.1", + "raw-body": "2.4.2", "tough-cookie": "4.0.0", "uuid": "8.3.2", "ws": "7.5.1", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index c40176645..4bba38fab 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -21,6 +21,6 @@ "fs-extra": "10.0.0", "semver": "7.3.5", "tar": "6.1.11", - "uglify-js": "3.14.2" + "uglify-js": "3.14.4" } } diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index 23f41e0ed..5fb21157e 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -439,8 +439,6 @@ class Flow { } } return [env.name, env]; - - return [env.name, env]; }); group._env = Object.fromEntries(entries); } @@ -457,24 +455,24 @@ class Flow { const val = ((value === "true") || (value === true)); - return { + return [{ val: val - }; + }, null]; } if (type === "cred") { - return { + return [{ val: value - }; + }, null]; } try { var val = redUtil.evaluateNodeProperty(value, type, node, null, null); - return { + return [{ val: val - }; + }, null]; } catch (e) { this.error(e); - return null; + return [null, null]; } } } @@ -488,7 +486,7 @@ class Flow { return this.getGroupEnvSetting(node, parent, name); } } - return null; + return [null, name]; } @@ -545,6 +543,9 @@ class Flow { } } } + else { + key = key.substring(8); + } } return this.parent.getSetting(key); } diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js index 0f3aefe26..3d2bc3afe 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js @@ -373,10 +373,11 @@ class Subflow extends Flow { const node = this.subflowInstance; if (node.g) { const group = this.getGroupNode(node.g); - const result = this.getGroupEnvSetting(node, group, name); + const [result, newName] = this.getGroupEnvSetting(node, group, name); if (result) { return result.val; } + name = newName; } diff --git a/packages/node_modules/@node-red/util/lib/i18n.js b/packages/node_modules/@node-red/util/lib/i18n.js index cddcb764f..4f1cf110f 100644 --- a/packages/node_modules/@node-red/util/lib/i18n.js +++ b/packages/node_modules/@node-red/util/lib/i18n.js @@ -136,8 +136,6 @@ function getCurrentLocale() { function init(settings) { if (!initPromise) { - // Keep this as a 'when' promise as top-level red.js uses 'otherwise' - // and embedded users of NR may have copied that. initPromise = new Promise((resolve,reject) => { i18n.use(MessageFileLoader); var opt = { @@ -146,6 +144,8 @@ function init(settings) { defaultNS: "runtime", ns: [], fallbackLng: defaultLang, + keySeparator: ".", + nsSeparator: ":", interpolation: { unescapeSuffix: 'HTML', escapeValue: false, diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js index 8f123cf25..bfea58f0f 100644 --- a/packages/node_modules/@node-red/util/lib/util.js +++ b/packages/node_modules/@node-red/util/lib/util.js @@ -526,10 +526,11 @@ function getSetting(node, name, flow_) { if (flow) { if (node && node.g) { const group = flow.getGroupNode(node.g); - const result = flow.getGroupEnvSetting(node, group, name); + const [result, newName] = flow.getGroupEnvSetting(node, group, name); if (result) { return result.val; } + name = newName; } return flow.getSetting(name); } @@ -842,6 +843,9 @@ function encodeObject(msg,opts) { length: msg.msg.size } needsStringify = true; + } else if (msg.msg && msg.msg.constructor.name === "RegExp") { + msg.format = 'regexp'; + msg.msg = msg.msg.toString(); } if (needsStringify || (msg.format === "Object")) { msg.msg = safeJSONStringify(msg.msg, function(key, value) { @@ -907,6 +911,12 @@ function encodeObject(msg,opts) { data: Object.fromEntries(Array.from(value.entries()).slice(0,debuglength)), length: value.size } + } else if (value.constructor.name === "RegExp") { + value = { + __enc__: true, + type: "regexp", + data: value.toString() + } } } else if (value === undefined) { value = { diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 0005ed292..ef65c8c67 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -16,10 +16,10 @@ ], "dependencies": { "fs-extra": "10.0.0", - "i18next": "21.3.1", + "i18next": "21.5.4", "json-stringify-safe": "5.0.1", "jsonata": "1.8.5", "lodash.clonedeep": "^4.5.0", - "moment-timezone": "0.5.33" + "moment-timezone": "0.5.34" } } diff --git a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js b/test/unit/@node-red/runtime/lib/flows/Flow_spec.js index 95086c296..eed4810c4 100644 --- a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js +++ b/test/unit/@node-red/runtime/lib/flows/Flow_spec.js @@ -1275,6 +1275,52 @@ describe('Flow', function() { } }); + it("can access environment variable property using $parent", function (done) { + try { + after(function() { + delete process.env.V0; + delete process.env.V1; + }) + process.env.V0 = "gv0"; + process.env.V1 = "gv1"; + var config = flowUtils.parseConfig([ + {id:"t1",type:"tab",env:[ + {"name": "V0", value: "v0", type: "str"} + ]}, + {id:"g1",type:"group",z:"t1",env:[ + {"name": "V0", value: "v1", type: "str"}, + {"name": "V1", value: "v2", type: "str"} + ]}, + {id:"g2",type:"group",z:"t1",g:"g1",env:[ + {"name": "V1", value: "v3", type: "str"} + ]}, + {id:"1",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V0}",wires:[]}, + {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V0}",wires:[]}, + {id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V1}",wires:[]}, + {id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"${$parent.V1}",wires:[]}, + {id:"5",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V1}",wires:[]}, + ]); + var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); + flow.start(); + + var activeNodes = flow.getActiveNodes(); + + activeNodes["1"].foo.should.equal("gv0"); + activeNodes["2"].foo.should.equal("v0"); + activeNodes["3"].foo.should.equal("gv1"); + activeNodes["4"].foo.should.equal("v2"); + activeNodes["5"].foo.should.equal("gv1"); + + flow.stop().then(function() { + done(); + }); + } + catch (e) { + console.log(e.stack); + done(e); + } + + }); });