mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #3383 from node-red/dev
Merge dev to master for 2.2.0
This commit is contained in:
commit
3a69af9034
2802
CHANGELOG.md
2802
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -38,12 +38,9 @@ If you want to raise a pull-request with a new feature, or a refactoring
|
|||||||
of existing code, it may well get rejected if you haven't discussed it on
|
of existing code, it may well get rejected if you haven't discussed it on
|
||||||
the [forum](https://discourse.nodered.org) first.
|
the [forum](https://discourse.nodered.org) first.
|
||||||
|
|
||||||
All contributors need to sign the JS Foundation's Contributor License Agreement.
|
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
|
||||||
It is an online process and quick to do. You can read the details of the agreement
|
It is an online process and quick to do. If you raise a pull-request without
|
||||||
here: https://cla.js.foundation/node-red/node-red.
|
having signed the CLA, you will be prompted to do so automatically.
|
||||||
|
|
||||||
If you raise a pull-request without having signed the CLA, you will be prompted
|
|
||||||
to do so automatically.
|
|
||||||
|
|
||||||
|
|
||||||
### Code Branches
|
### Code Branches
|
||||||
|
10
README.md
10
README.md
@ -56,13 +56,13 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena
|
|||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
Node-RED is a project of the [OpenJS Foundation](https://openjsf.org).
|
Node-RED is a project of the [OpenJS Foundation](http://openjsf.org).
|
||||||
|
|
||||||
It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
|
It is maintained by:
|
||||||
|
|
||||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
|
||||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
|
||||||
|
|
||||||
|
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||||
|
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||||
|
* And many others...
|
||||||
|
|
||||||
|
|
||||||
## Copyright and license
|
## Copyright and license
|
||||||
|
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "8.7.0",
|
"acorn": "8.7.0",
|
||||||
"acorn-walk": "8.2.0",
|
"acorn-walk": "8.2.0",
|
||||||
"ajv": "8.8.2",
|
"ajv": "8.9.0",
|
||||||
"async-mutex": "0.3.2",
|
"async-mutex": "0.3.2",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"hash-sum": "2.0.0",
|
"hash-sum": "2.0.0",
|
||||||
"hpagent": "0.1.2",
|
"hpagent": "0.1.2",
|
||||||
"https-proxy-agent": "5.0.0",
|
"https-proxy-agent": "5.0.0",
|
||||||
"i18next": "21.6.6",
|
"i18next": "21.6.10",
|
||||||
"iconv-lite": "0.6.3",
|
"iconv-lite": "0.6.3",
|
||||||
"is-utf8": "0.2.1",
|
"is-utf8": "0.2.1",
|
||||||
"js-yaml": "3.14.1",
|
"js-yaml": "3.14.1",
|
||||||
@ -59,10 +59,10 @@
|
|||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"media-typer": "1.1.0",
|
"media-typer": "1.1.0",
|
||||||
"memorystore": "1.6.6",
|
"memorystore": "1.6.6",
|
||||||
"mime": "2.5.2",
|
"mime": "3.0.0",
|
||||||
"moment-timezone": "0.5.34",
|
"moment-timezone": "0.5.34",
|
||||||
"mqtt": "4.3.4",
|
"mqtt": "4.3.4",
|
||||||
"multer": "1.4.3",
|
"multer": "1.4.4",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"node-red-admin": "^2.2.1",
|
"node-red-admin": "^2.2.1",
|
||||||
"nopt": "5.0.0",
|
"nopt": "5.0.0",
|
||||||
@ -75,9 +75,9 @@
|
|||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"tar": "6.1.11",
|
"tar": "6.1.11",
|
||||||
"tough-cookie": "4.0.0",
|
"tough-cookie": "4.0.0",
|
||||||
"uglify-js": "3.14.5",
|
"uglify-js": "3.15.0",
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
"ws": "7.5.1",
|
"ws": "7.5.6",
|
||||||
"xml2js": "0.4.23"
|
"xml2js": "0.4.23"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
@ -104,16 +104,16 @@
|
|||||||
"grunt-sass": "~3.1.0",
|
"grunt-sass": "~3.1.0",
|
||||||
"grunt-simple-mocha": "~0.4.1",
|
"grunt-simple-mocha": "~0.4.1",
|
||||||
"grunt-simple-nyc": "^3.0.1",
|
"grunt-simple-nyc": "^3.0.1",
|
||||||
"i18next-http-backend": "1.3.1",
|
"i18next-http-backend": "1.3.2",
|
||||||
"jquery-i18next": "1.2.1",
|
"jquery-i18next": "1.2.1",
|
||||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||||
"marked": "4.0.10",
|
"marked": "4.0.12",
|
||||||
"minami": "1.2.3",
|
"minami": "1.2.3",
|
||||||
"mocha": "9.1.3",
|
"mocha": "9.2.0",
|
||||||
"node-red-node-test-helper": "^0.2.7",
|
"node-red-node-test-helper": "^0.2.7",
|
||||||
"nodemon": "2.0.15",
|
"nodemon": "2.0.15",
|
||||||
"proxy": "^1.0.2",
|
"proxy": "^1.0.2",
|
||||||
"sass": "1.48.0",
|
"sass": "1.49.0",
|
||||||
"should": "13.2.3",
|
"should": "13.2.3",
|
||||||
"sinon": "11.1.2",
|
"sinon": "11.1.2",
|
||||||
"stoppable": "^1.1.0",
|
"stoppable": "^1.1.0",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
@ -122,6 +122,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.active) {
|
if (req.body.active) {
|
||||||
|
opts.clearContext = req.body.hasOwnProperty('clearContext')?req.body.clearContext:true
|
||||||
runtimeAPI.projects.setActiveProject(opts).then(function() {
|
runtimeAPI.projects.setActiveProject(opts).then(function() {
|
||||||
listProjects(req,res);
|
listProjects(req,res);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-api",
|
"name": "@node-red/editor-api",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "2.1.6",
|
"@node-red/util": "2.2.0",
|
||||||
"@node-red/editor-client": "2.1.6",
|
"@node-red/editor-client": "2.2.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.19.1",
|
"body-parser": "1.19.1",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
@ -25,14 +25,14 @@
|
|||||||
"express-session": "1.17.2",
|
"express-session": "1.17.2",
|
||||||
"express": "4.17.2",
|
"express": "4.17.2",
|
||||||
"memorystore": "1.6.6",
|
"memorystore": "1.6.6",
|
||||||
"mime": "2.5.2",
|
"mime": "3.0.0",
|
||||||
"multer": "1.4.3",
|
"multer": "1.4.4",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"oauth2orize": "1.11.1",
|
"oauth2orize": "1.11.1",
|
||||||
"passport-http-bearer": "1.0.1",
|
"passport-http-bearer": "1.0.1",
|
||||||
"passport-oauth2-client-password": "0.1.2",
|
"passport-oauth2-client-password": "0.1.2",
|
||||||
"passport": "0.5.2",
|
"passport": "0.5.2",
|
||||||
"ws": "7.5.1"
|
"ws": "7.5.6"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"bcrypt": "5.0.1"
|
"bcrypt": "5.0.1"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
@ -75,6 +75,8 @@
|
|||||||
"view": {
|
"view": {
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"grid": "Grid",
|
"grid": "Grid",
|
||||||
|
"storeZoom": "Restore zoom level on load",
|
||||||
|
"storePosition": "Restore scroll position on load",
|
||||||
"showGrid": "Show grid",
|
"showGrid": "Show grid",
|
||||||
"snapGrid": "Snap to grid",
|
"snapGrid": "Snap to grid",
|
||||||
"gridSize": "Grid size",
|
"gridSize": "Grid size",
|
||||||
@ -894,6 +896,8 @@
|
|||||||
"addTitle": "add an item"
|
"addTitle": "add an item"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"history": "Search history",
|
||||||
|
"clear": "clear all",
|
||||||
"empty": "No matches found",
|
"empty": "No matches found",
|
||||||
"addNode": "add a node..."
|
"addNode": "add a node..."
|
||||||
},
|
},
|
||||||
@ -1091,7 +1095,8 @@
|
|||||||
"not-git": "Not a git repository",
|
"not-git": "Not a git repository",
|
||||||
"no-resource": "Repository not found",
|
"no-resource": "Repository not found",
|
||||||
"cant-get-ssh-key-path": "Error! Can't get selected SSH key path.",
|
"cant-get-ssh-key-path": "Error! Can't get selected SSH key path.",
|
||||||
"unexpected_error": "unexpected_error"
|
"unexpected_error": "unexpected_error",
|
||||||
|
"clearContext": "Clear context when switching projects"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"confirm": "Are you sure you want to delete this project?"
|
"confirm": "Are you sure you want to delete this project?"
|
||||||
|
@ -75,6 +75,8 @@
|
|||||||
"view": {
|
"view": {
|
||||||
"view": "表示",
|
"view": "表示",
|
||||||
"grid": "グリッド",
|
"grid": "グリッド",
|
||||||
|
"storeZoom": "読み込み時に拡大/縮小のレベルを復元",
|
||||||
|
"storePosition": "読み込み時にスクロール位置を復元",
|
||||||
"showGrid": "グリッドを表示",
|
"showGrid": "グリッドを表示",
|
||||||
"snapGrid": "ノードの配置を補助",
|
"snapGrid": "ノードの配置を補助",
|
||||||
"gridSize": "グリッドの大きさ",
|
"gridSize": "グリッドの大きさ",
|
||||||
@ -894,6 +896,8 @@
|
|||||||
"addTitle": "要素を追加"
|
"addTitle": "要素を追加"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
"history": "検索履歴",
|
||||||
|
"clear": "全て削除",
|
||||||
"empty": "一致したものが見つかりませんでした",
|
"empty": "一致したものが見つかりませんでした",
|
||||||
"addNode": "ノードを追加..."
|
"addNode": "ノードを追加..."
|
||||||
},
|
},
|
||||||
@ -1091,7 +1095,8 @@
|
|||||||
"not-git": "Gitリポジトリではありません",
|
"not-git": "Gitリポジトリではありません",
|
||||||
"no-resource": "リポジトリが見つかりません",
|
"no-resource": "リポジトリが見つかりません",
|
||||||
"cant-get-ssh-key-path": "エラー! 選択したSSHキーのパスを取得できません。",
|
"cant-get-ssh-key-path": "エラー! 選択したSSHキーのパスを取得できません。",
|
||||||
"unexpected_error": "予期しないエラー"
|
"unexpected_error": "予期しないエラー",
|
||||||
|
"clearContext": "プロジェクトを切り替る際にコンテキストを初期化"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"confirm": "プロジェクトを削除しても良いですか?"
|
"confirm": "プロジェクトを削除しても良いですか?"
|
||||||
@ -1151,5 +1156,137 @@
|
|||||||
"ru": "ロシア語",
|
"ru": "ロシア語",
|
||||||
"zh-CN": "中国語(簡体)",
|
"zh-CN": "中国語(簡体)",
|
||||||
"zh-TW": "中国語(繁体)"
|
"zh-TW": "中国語(繁体)"
|
||||||
|
},
|
||||||
|
"action-list": {
|
||||||
|
"toggle-show-tips": "ヒント表示切替",
|
||||||
|
"show-about": "Node-REDの説明を表示",
|
||||||
|
"show-welcome-tour": "ウェルカムツアー表示",
|
||||||
|
"show-next-tab": "次のタブを表示",
|
||||||
|
"show-previous-tab": "前のタブを表示",
|
||||||
|
"add-flow": "フローを追加",
|
||||||
|
"add-flow-to-right": "フローを右に追加",
|
||||||
|
"edit-flow": "フローを編集",
|
||||||
|
"remove-flow": "フローを削除",
|
||||||
|
"enable-flow": "フローを有効化",
|
||||||
|
"disable-flow": "フローを無効化",
|
||||||
|
"hide-flow": "フローを隠す",
|
||||||
|
"hide-other-flows": "他のフローを非表示",
|
||||||
|
"hide-all-flows": "全てのフローを非表示",
|
||||||
|
"show-all-flows": "全てのフローを表示",
|
||||||
|
"show-last-hidden-flow": "最後に非表示にしたフローを表示",
|
||||||
|
"list-hidden-flows": "非表示フローを表示",
|
||||||
|
"list-flows": "フロー一覧",
|
||||||
|
"list-subflows": "サブフロー一覧",
|
||||||
|
"go-to-previous-location": "前の位置に移動",
|
||||||
|
"go-to-next-location": "次の位置に移動",
|
||||||
|
"copy-selection-to-internal-clipboard": "選択をクリップボードにコピー",
|
||||||
|
"cut-selection-to-internal-clipboard": "選択をクリップボードに切り取り",
|
||||||
|
"paste-from-internal-clipboard": "クリップボードから貼り付け",
|
||||||
|
"detach-selected-nodes": "選択ノードを接続から外す",
|
||||||
|
"delete-selection": "選択を削除",
|
||||||
|
"delete-selection-and-reconnect": "選択を削除し再接続",
|
||||||
|
"edit-selected-node": "選択したノードを編集",
|
||||||
|
"go-to-selection": "選択に移動",
|
||||||
|
"undo": "変更操作を戻す",
|
||||||
|
"redo": "変更操作をやり直し",
|
||||||
|
"select-all-nodes": "全てのノードを選択",
|
||||||
|
"select-none": "ノードを選択",
|
||||||
|
"enable-selected-nodes": "選択ノードを有効化",
|
||||||
|
"disable-selected-nodes": "選択ノードを無効化",
|
||||||
|
"toggle-show-grid": "グリッド表示切替",
|
||||||
|
"toggle-snap-grid": "ノードの配置補助切替",
|
||||||
|
"toggle-status": "ステータス表示切替",
|
||||||
|
"show-selected-node-labels": "選択したノードのラベルを表示",
|
||||||
|
"hide-selected-node-labels": "選択したノードのラベルを非表示",
|
||||||
|
"scroll-view-up": "上スクロール",
|
||||||
|
"scroll-view-right": "右スクロール",
|
||||||
|
"scroll-view-down": "下スクロール",
|
||||||
|
"scroll-view-left": "左スクロール",
|
||||||
|
"step-view-up": "一単位上スクロール",
|
||||||
|
"step-view-right": "一単位右スクロール",
|
||||||
|
"step-view-down": "一単位下スクロール",
|
||||||
|
"step-view-left": "一単位左スクロール",
|
||||||
|
"move-selection-up": "選択を上移動",
|
||||||
|
"move-selection-right": "選択を右移動",
|
||||||
|
"move-selection-down": "選択を下移動",
|
||||||
|
"move-selection-left": "選択を左移動",
|
||||||
|
"move-selection-forwards": "選択を前面に移動",
|
||||||
|
"move-selection-backwards": "選択を背面に移動",
|
||||||
|
"move-selection-to-front": "選択を最前面に移動",
|
||||||
|
"move-selection-to-back": "選択を最背面に移動",
|
||||||
|
"step-selection-up": "選択を一単位上移動",
|
||||||
|
"step-selection-right": "選択を一単位右移動",
|
||||||
|
"step-selection-down": "選択を一単位下移動",
|
||||||
|
"step-selection-left": "選択を一単位左移動",
|
||||||
|
"select-connected-nodes": "接続されたノードを選択",
|
||||||
|
"select-downstream-nodes": "後方に接続されたノードを選択",
|
||||||
|
"select-upstream-nodes": "前方に接続されたノードを選択",
|
||||||
|
"go-to-next-node": "次のノードに移動",
|
||||||
|
"go-to-previous-node": "前のノードに移動",
|
||||||
|
"go-to-next-sibling": "次の兄弟ノードに移動",
|
||||||
|
"go-to-previous-sibling": "前の兄弟ノードに移動",
|
||||||
|
"go-to-nearest-node-on-left": "最も近い左側ノードに移動",
|
||||||
|
"go-to-nearest-node-on-right": "最も近い右側ノードに移動",
|
||||||
|
"go-to-nearest-node-above": "最も近い上側ノードに移動",
|
||||||
|
"go-to-nearest-node-below": "最も近い下側ノードに移動",
|
||||||
|
"align-selection-to-grid": "選択を整列",
|
||||||
|
"align-selection-to-left": "選択を左揃え",
|
||||||
|
"align-selection-to-right": "選択を右揃え",
|
||||||
|
"align-selection-to-top": "選択を上揃え",
|
||||||
|
"align-selection-to-bottom": "選択を下揃え",
|
||||||
|
"align-selection-to-middle": "選択を上下中央揃え",
|
||||||
|
"align-selection-to-center": "選択を左右中央揃え",
|
||||||
|
"distribute-selection-horizontally": "選択を左右に整列",
|
||||||
|
"distribute-selection-vertically": "選択を上下に整列",
|
||||||
|
"wire-series-of-nodes": "ノードを一続きに接続",
|
||||||
|
"wire-node-to-multiple": "ノードを複数に接続",
|
||||||
|
"show-user-settings": "ユーザ設定を表示",
|
||||||
|
"show-help": "ヘルプを表示",
|
||||||
|
"toggle-palette": "パレットの表示切替",
|
||||||
|
"show-event-log": "イベントログを表示",
|
||||||
|
"manage-palette": "パレットの管理",
|
||||||
|
"toggle-sidebar": "サイドバーの表示切替",
|
||||||
|
"show-info-tab": "ノード情報タブの表示",
|
||||||
|
"show-help-tab": "ノードヘルプタブの表示",
|
||||||
|
"show-config-tab": "設定ノードタブの表示",
|
||||||
|
"select-all-config-nodes": "全ての設定ノードを選択",
|
||||||
|
"delete-config-selection": "選択した設定ノードを削除",
|
||||||
|
"show-context-tab": "コンテキストデータタブを表示",
|
||||||
|
"create-subflow": "サブフローを作成",
|
||||||
|
"convert-to-subflow": "選択をサブフローに変換",
|
||||||
|
"group-selection": "選択をグループ化",
|
||||||
|
"ungroup-selection": "選択をグループ解除",
|
||||||
|
"merge-selection-to-group": "選択をグループにマージ",
|
||||||
|
"remove-selection-from-group": "選択をグループから削除",
|
||||||
|
"copy-group-style": "グループのスタイルをコピー",
|
||||||
|
"paste-group-style": "グループのスタイルを貼り付け",
|
||||||
|
"show-export-dialog": "書き出しダイアログを表示",
|
||||||
|
"show-import-dialog": "読み込みダイアログを表示",
|
||||||
|
"show-library-export-dialog": "ライブラリ書き出しダイアログを表示",
|
||||||
|
"show-library-import-dialog": "ライブラリ読み込みダイアログを表示",
|
||||||
|
"show-examples-import-dialog": "サンプル読み込みダイアログを表示",
|
||||||
|
"search": "検索",
|
||||||
|
"show-action-list": "アクション一覧を表示",
|
||||||
|
"confirm-edit-tray": "編集を完了",
|
||||||
|
"cancel-edit-tray": "編集をキャンセル",
|
||||||
|
"show-remote-diff": "リモートとの変更差分を表示",
|
||||||
|
"deploy-flows": "フローをデプロイ",
|
||||||
|
"restart-flows": "フローを再起動",
|
||||||
|
"set-deploy-type-to-full": "デプロイを「全て」に設定",
|
||||||
|
"set-deploy-type-to-modified-flows": "デプロイを「変更したフロー」に設定",
|
||||||
|
"set-deploy-type-to-modified-nodes": "デプロイを「変更したノード」に設定",
|
||||||
|
"show-debug-tab": "デバッグタブを表示",
|
||||||
|
"clear-debug-messages": "デバッグメッセージをクリア",
|
||||||
|
"clear-filtered-debug-messages": "フィルタしたデバッグメッセージをクリア",
|
||||||
|
"activate-selected-debug-nodes": "選択したデバッグノードを有効化",
|
||||||
|
"activate-all-debug-nodes": "全てのデバッグノードを有効化",
|
||||||
|
"activate-all-flow-debug-nodes": "フロー内の全デバッグノードを有効化",
|
||||||
|
"deactivate-selected-debug-nodes": "選択したデバッグノードを無効化",
|
||||||
|
"deactivate-all-debug-nodes": "全てのデバッグノードを無効化",
|
||||||
|
"deactivate-all-flow-debug-nodes": "フロー内の全デバッグノードを無効化",
|
||||||
|
"zoom-in": "ズームイン",
|
||||||
|
"zoom-out": "ズームアウト",
|
||||||
|
"zoom-reset": "ズームリセット",
|
||||||
|
"toggle-navigator": "ナビゲータ表示切替"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-client",
|
"name": "@node-red/editor-client",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -684,6 +684,13 @@ RED.history = (function() {
|
|||||||
peek: function() {
|
peek: function() {
|
||||||
return undoHistory[undoHistory.length-1];
|
return undoHistory[undoHistory.length-1];
|
||||||
},
|
},
|
||||||
|
replace: function(ev) {
|
||||||
|
if (undoHistory.length === 0) {
|
||||||
|
RED.history.push(ev);
|
||||||
|
} else {
|
||||||
|
undoHistory[undoHistory.length-1] = ev;
|
||||||
|
}
|
||||||
|
},
|
||||||
clear: function() {
|
clear: function() {
|
||||||
undoHistory = [];
|
undoHistory = [];
|
||||||
redoHistory = [];
|
redoHistory = [];
|
||||||
|
@ -38,7 +38,9 @@
|
|||||||
},
|
},
|
||||||
"red-ui-workspace": {
|
"red-ui-workspace": {
|
||||||
"backspace": "core:delete-selection",
|
"backspace": "core:delete-selection",
|
||||||
|
"ctrl-backspace": "core:delete-selection-and-reconnect",
|
||||||
"delete": "core:delete-selection",
|
"delete": "core:delete-selection",
|
||||||
|
"ctrl-delete": "core:delete-selection-and-reconnect",
|
||||||
"enter": "core:edit-selected-node",
|
"enter": "core:edit-selected-node",
|
||||||
"ctrl-enter": "core:go-to-selection",
|
"ctrl-enter": "core:go-to-selection",
|
||||||
"ctrl-c": "core:copy-selection-to-internal-clipboard",
|
"ctrl-c": "core:copy-selection-to-internal-clipboard",
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
**/
|
**/
|
||||||
RED.nodes = (function() {
|
RED.nodes = (function() {
|
||||||
|
|
||||||
|
var PORT_TYPE_INPUT = 1;
|
||||||
|
var PORT_TYPE_OUTPUT = 0;
|
||||||
|
|
||||||
var node_defs = {};
|
var node_defs = {};
|
||||||
var linkTabMap = {};
|
var linkTabMap = {};
|
||||||
|
|
||||||
@ -2458,6 +2461,144 @@ RED.nodes = (function() {
|
|||||||
return helpContent;
|
return helpContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNodeIslands(nodes) {
|
||||||
|
var selectedNodes = new Set(nodes);
|
||||||
|
// Maps node => island index
|
||||||
|
var nodeToIslandIndex = new Map();
|
||||||
|
// Maps island index => [nodes in island]
|
||||||
|
var islandIndexToNodes = new Map();
|
||||||
|
var internalLinks = new Set();
|
||||||
|
nodes.forEach((node, index) => {
|
||||||
|
nodeToIslandIndex.set(node,index);
|
||||||
|
islandIndexToNodes.set(index, [node]);
|
||||||
|
var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
|
||||||
|
var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||||
|
inboundLinks.forEach(l => {
|
||||||
|
if (selectedNodes.has(l.source)) {
|
||||||
|
internalLinks.add(l)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
outboundLinks.forEach(l => {
|
||||||
|
if (selectedNodes.has(l.target)) {
|
||||||
|
internalLinks.add(l)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
internalLinks.forEach(l => {
|
||||||
|
let source = l.source;
|
||||||
|
let target = l.target;
|
||||||
|
if (nodeToIslandIndex.get(source) !== nodeToIslandIndex.get(target)) {
|
||||||
|
let sourceIsland = nodeToIslandIndex.get(source);
|
||||||
|
let islandToMove = nodeToIslandIndex.get(target);
|
||||||
|
let nodesToMove = islandIndexToNodes.get(islandToMove);
|
||||||
|
nodesToMove.forEach(n => {
|
||||||
|
nodeToIslandIndex.set(n,sourceIsland);
|
||||||
|
islandIndexToNodes.get(sourceIsland).push(n);
|
||||||
|
})
|
||||||
|
islandIndexToNodes.delete(islandToMove);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const result = [];
|
||||||
|
islandIndexToNodes.forEach((nodes,index) => {
|
||||||
|
result.push(nodes);
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function detachNodes(nodes) {
|
||||||
|
let allSelectedNodes = [];
|
||||||
|
nodes.forEach(node => {
|
||||||
|
if (node.type === 'group') {
|
||||||
|
let groupNodes = RED.group.getNodes(node,true,true);
|
||||||
|
allSelectedNodes = allSelectedNodes.concat(groupNodes);
|
||||||
|
} else {
|
||||||
|
allSelectedNodes.push(node);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (allSelectedNodes.length > 0 ) {
|
||||||
|
const nodeIslands = RED.nodes.getNodeIslands(allSelectedNodes);
|
||||||
|
let removedLinks = [];
|
||||||
|
let newLinks = [];
|
||||||
|
let createdLinkIds = new Set();
|
||||||
|
|
||||||
|
nodeIslands.forEach(nodes => {
|
||||||
|
let selectedNodes = new Set(nodes);
|
||||||
|
let allInboundLinks = [];
|
||||||
|
let allOutboundLinks = [];
|
||||||
|
// Identify links that enter or exit this island of nodes
|
||||||
|
nodes.forEach(node => {
|
||||||
|
var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
|
||||||
|
var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||||
|
inboundLinks.forEach(l => {
|
||||||
|
if (!selectedNodes.has(l.source)) {
|
||||||
|
allInboundLinks.push(l)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
outboundLinks.forEach(l => {
|
||||||
|
if (!selectedNodes.has(l.target)) {
|
||||||
|
allOutboundLinks.push(l)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Identify the links to restore
|
||||||
|
allInboundLinks.forEach(inLink => {
|
||||||
|
// For Each inbound link,
|
||||||
|
// - get source node.
|
||||||
|
// - trace through to all outbound links
|
||||||
|
let sourceNode = inLink.source;
|
||||||
|
let targetNodes = new Set();
|
||||||
|
let visited = new Set();
|
||||||
|
let stack = [inLink.target];
|
||||||
|
while (stack.length > 0) {
|
||||||
|
let node = stack.pop(stack);
|
||||||
|
visited.add(node)
|
||||||
|
let links = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||||
|
links.forEach(l => {
|
||||||
|
if (visited.has(l.target)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
visited.add(l.target);
|
||||||
|
if (selectedNodes.has(l.target)) {
|
||||||
|
// internal link
|
||||||
|
stack.push(l.target)
|
||||||
|
} else {
|
||||||
|
targetNodes.add(l.target)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
targetNodes.forEach(target => {
|
||||||
|
let linkId = `${sourceNode.id}[${inLink.sourcePort}] -> ${target.id}`
|
||||||
|
if (!createdLinkIds.has(linkId)) {
|
||||||
|
createdLinkIds.add(linkId);
|
||||||
|
let link = {
|
||||||
|
source: sourceNode,
|
||||||
|
sourcePort: inLink.sourcePort,
|
||||||
|
target: target
|
||||||
|
}
|
||||||
|
let existingLinks = RED.nodes.filterLinks(link)
|
||||||
|
if (existingLinks.length === 0) {
|
||||||
|
newLinks.push(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2. delete all those links
|
||||||
|
allInboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
|
||||||
|
allOutboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
|
||||||
|
})
|
||||||
|
|
||||||
|
newLinks.forEach(l => RED.nodes.addLink(l));
|
||||||
|
return {
|
||||||
|
newLinks,
|
||||||
|
removedLinks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: function() {
|
init: function() {
|
||||||
RED.events.on("registry:node-type-added",function(type) {
|
RED.events.on("registry:node-type-added",function(type) {
|
||||||
@ -2539,7 +2680,7 @@ RED.nodes = (function() {
|
|||||||
add: addNode,
|
add: addNode,
|
||||||
remove: removeNode,
|
remove: removeNode,
|
||||||
clear: clear,
|
clear: clear,
|
||||||
|
detachNodes: detachNodes,
|
||||||
moveNodesForwards: moveNodesForwards,
|
moveNodesForwards: moveNodesForwards,
|
||||||
moveNodesBackwards: moveNodesBackwards,
|
moveNodesBackwards: moveNodesBackwards,
|
||||||
moveNodesToFront: moveNodesToFront,
|
moveNodesToFront: moveNodesToFront,
|
||||||
@ -2551,7 +2692,20 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
addLink: addLink,
|
addLink: addLink,
|
||||||
removeLink: removeLink,
|
removeLink: removeLink,
|
||||||
|
getNodeLinks: function(id, portType) {
|
||||||
|
if (typeof id !== 'string') {
|
||||||
|
id = id.id;
|
||||||
|
}
|
||||||
|
if (nodeLinks[id]) {
|
||||||
|
if (portType === 1) {
|
||||||
|
// Return cloned arrays so they can be safely modified by caller
|
||||||
|
return [].concat(nodeLinks[id].in)
|
||||||
|
} else {
|
||||||
|
return [].concat(nodeLinks[id].out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
addWorkspace: addWorkspace,
|
addWorkspace: addWorkspace,
|
||||||
removeWorkspace: removeWorkspace,
|
removeWorkspace: removeWorkspace,
|
||||||
getWorkspaceOrder: function() { return workspacesOrder },
|
getWorkspaceOrder: function() { return workspacesOrder },
|
||||||
@ -2625,6 +2779,7 @@ RED.nodes = (function() {
|
|||||||
getAllFlowNodes: getAllFlowNodes,
|
getAllFlowNodes: getAllFlowNodes,
|
||||||
getAllUpstreamNodes: getAllUpstreamNodes,
|
getAllUpstreamNodes: getAllUpstreamNodes,
|
||||||
getAllDownstreamNodes: getAllDownstreamNodes,
|
getAllDownstreamNodes: getAllDownstreamNodes,
|
||||||
|
getNodeIslands: getNodeIslands,
|
||||||
createExportableNodeSet: createExportableNodeSet,
|
createExportableNodeSet: createExportableNodeSet,
|
||||||
createCompleteNodeSet: createCompleteNodeSet,
|
createCompleteNodeSet: createCompleteNodeSet,
|
||||||
updateConfigNodeUsers: updateConfigNodeUsers,
|
updateConfigNodeUsers: updateConfigNodeUsers,
|
||||||
|
@ -556,8 +556,7 @@ var RED = (function() {
|
|||||||
|
|
||||||
$(".red-ui-header-toolbar").show();
|
$(".red-ui-header-toolbar").show();
|
||||||
|
|
||||||
|
RED.sidebar.show(":first", true);
|
||||||
RED.sidebar.show(":first");
|
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
loader.end();
|
loader.end();
|
||||||
|
@ -160,18 +160,19 @@ RED.actionList = (function() {
|
|||||||
createDialog();
|
createDialog();
|
||||||
}
|
}
|
||||||
dialog.slideDown(300);
|
dialog.slideDown(300);
|
||||||
searchInput.searchBox('value',v)
|
searchInput.searchBox('value',v);
|
||||||
searchResults.editableList('empty');
|
searchResults.editableList('empty');
|
||||||
results = [];
|
results = [];
|
||||||
var actions = RED.actions.list();
|
var actions = RED.actions.list();
|
||||||
actions.sort(function(A,B) {
|
actions.sort(function(A,B) {
|
||||||
return A.id.localeCompare(B.id);
|
var Akey = A.label;
|
||||||
|
var Bkey = B.label;
|
||||||
|
return Akey.localeCompare(Bkey);
|
||||||
});
|
});
|
||||||
actions.forEach(function(action) {
|
actions.forEach(function(action) {
|
||||||
action.label = action.id.replace(/:/,": ").replace(/-/g," ").replace(/(^| )./g,function() { return arguments[0].toUpperCase()});
|
|
||||||
action._label = action.label.toLowerCase();
|
action._label = action.label.toLowerCase();
|
||||||
searchResults.editableList('addItem',action)
|
searchResults.editableList('addItem',action);
|
||||||
})
|
});
|
||||||
RED.events.emit("actionList:open");
|
RED.events.emit("actionList:open");
|
||||||
visible = true;
|
visible = true;
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,39 @@
|
|||||||
RED.actions = (function() {
|
RED.actions = (function() {
|
||||||
var actions = {
|
var actions = {
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
function addAction(name,handler) {
|
function addAction(name,handler,options) {
|
||||||
if (typeof handler !== 'function') {
|
if (typeof handler !== 'function') {
|
||||||
throw new Error("Action handler not a function");
|
throw new Error("Action handler not a function");
|
||||||
}
|
}
|
||||||
if (actions[name]) {
|
if (actions[name]) {
|
||||||
throw new Error("Cannot override existing action");
|
throw new Error("Cannot override existing action");
|
||||||
}
|
}
|
||||||
actions[name] = handler;
|
actions[name] = {
|
||||||
|
handler: handler,
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function removeAction(name) {
|
function removeAction(name) {
|
||||||
delete actions[name];
|
delete actions[name];
|
||||||
}
|
}
|
||||||
function getAction(name) {
|
function getAction(name) {
|
||||||
return actions[name];
|
return actions[name].handler;
|
||||||
}
|
}
|
||||||
function invokeAction() {
|
function invokeAction() {
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
var name = args.shift();
|
var name = args.shift();
|
||||||
if (actions.hasOwnProperty(name)) {
|
if (actions.hasOwnProperty(name)) {
|
||||||
actions[name].apply(null, args);
|
var handler = actions[name].handler;
|
||||||
|
handler.apply(null, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function listActions() {
|
function listActions() {
|
||||||
var result = [];
|
var result = [];
|
||||||
|
var missing = [];
|
||||||
Object.keys(actions).forEach(function(action) {
|
Object.keys(actions).forEach(function(action) {
|
||||||
|
var def = actions[action];
|
||||||
var shortcut = RED.keyboard.getShortcut(action);
|
var shortcut = RED.keyboard.getShortcut(action);
|
||||||
var isUser = false;
|
var isUser = false;
|
||||||
if (shortcut) {
|
if (shortcut) {
|
||||||
@ -35,13 +41,38 @@ RED.actions = (function() {
|
|||||||
} else {
|
} else {
|
||||||
isUser = !!RED.keyboard.getUserShortcut(action);
|
isUser = !!RED.keyboard.getUserShortcut(action);
|
||||||
}
|
}
|
||||||
|
if (!def.label) {
|
||||||
|
var name = action;
|
||||||
|
var options = def.options;
|
||||||
|
var key = options ? options.label : undefined;
|
||||||
|
if (!key) {
|
||||||
|
key = "action-list." +name.replace(/^.*:/,"");
|
||||||
|
}
|
||||||
|
var label = RED._(key);
|
||||||
|
if (label === key) {
|
||||||
|
// no translation. convert `name` to description
|
||||||
|
label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
|
||||||
|
if (arguments[5] === 0) {
|
||||||
|
return arguments[2].toUpperCase();
|
||||||
|
} else {
|
||||||
|
return " "+arguments[4].toUpperCase();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
missing.push(key);
|
||||||
|
}
|
||||||
|
def.label = label;
|
||||||
|
}
|
||||||
|
//console.log("; missing:", missing);
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
id:action,
|
id:action,
|
||||||
scope:shortcut?shortcut.scope:undefined,
|
scope:shortcut?shortcut.scope:undefined,
|
||||||
key:shortcut?shortcut.key:undefined,
|
key:shortcut?shortcut.key:undefined,
|
||||||
user:isUser
|
user:isUser,
|
||||||
})
|
label: def.label,
|
||||||
})
|
options: def.options,
|
||||||
|
});
|
||||||
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -557,23 +557,19 @@ RED.tabs = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.one("transitionend", function(evt) {
|
li.remove();
|
||||||
li.remove();
|
if (tabs[id].pinned) {
|
||||||
if (tabs[id].pinned) {
|
pinnedTabsCount--;
|
||||||
pinnedTabsCount--;
|
}
|
||||||
}
|
if (options.onremove) {
|
||||||
if (options.onremove) {
|
options.onremove(tabs[id]);
|
||||||
options.onremove(tabs[id]);
|
}
|
||||||
}
|
delete tabs[id];
|
||||||
delete tabs[id];
|
updateTabWidths();
|
||||||
updateTabWidths();
|
if (collapsibleMenu) {
|
||||||
if (collapsibleMenu) {
|
collapsibleMenu.remove();
|
||||||
collapsibleMenu.remove();
|
collapsibleMenu = null;
|
||||||
collapsibleMenu = null;
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
li.addClass("hide-tab");
|
|
||||||
li.width(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findPreviousVisibleTab(li) {
|
function findPreviousVisibleTab(li) {
|
||||||
|
@ -333,6 +333,16 @@ RED.deploy = (function() {
|
|||||||
var unknownNodes = [];
|
var unknownNodes = [];
|
||||||
var invalidNodes = [];
|
var invalidNodes = [];
|
||||||
|
|
||||||
|
RED.nodes.eachConfig(function(node) {
|
||||||
|
if (!node.valid && !node.d) {
|
||||||
|
invalidNodes.push(getNodeInfo(node));
|
||||||
|
}
|
||||||
|
if (node.type === "unknown") {
|
||||||
|
if (unknownNodes.indexOf(node.name) == -1) {
|
||||||
|
unknownNodes.push(node.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
RED.nodes.eachNode(function(node) {
|
RED.nodes.eachNode(function(node) {
|
||||||
if (!node.valid && !node.d) {
|
if (!node.valid && !node.d) {
|
||||||
invalidNodes.push(getNodeInfo(node));
|
invalidNodes.push(getNodeInfo(node));
|
||||||
|
@ -35,9 +35,10 @@
|
|||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
|
if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
|
||||||
var icon = $("#red-ui-editor-node-icon").val()||""
|
var icon = $("#red-ui-editor-node-icon").val()||"";
|
||||||
if (!this.isDefaultIcon) {
|
if (!this.isDefaultIcon) {
|
||||||
if (icon !== node.icon) {
|
if ((icon !== node.icon) &&
|
||||||
|
(icon !== "")) {
|
||||||
editState.changes.icon = node.icon;
|
editState.changes.icon = node.icon;
|
||||||
node.icon = icon;
|
node.icon = icon;
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
@ -101,14 +102,14 @@
|
|||||||
if (showLabel) {
|
if (showLabel) {
|
||||||
// Default to show label
|
// Default to show label
|
||||||
if (node.l !== false) {
|
if (node.l !== false) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l;
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
node.l = false;
|
node.l = false;
|
||||||
} else {
|
} else {
|
||||||
// Node has showLabel:false (eg link nodes)
|
// Node has showLabel:false (eg link nodes)
|
||||||
if (node.hasOwnProperty('l') && node.l) {
|
if (node.hasOwnProperty('l') && node.l) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l;
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
delete node.l;
|
delete node.l;
|
||||||
@ -118,20 +119,20 @@
|
|||||||
if (showLabel) {
|
if (showLabel) {
|
||||||
// Default to show label
|
// Default to show label
|
||||||
if (node.hasOwnProperty('l') && !node.l) {
|
if (node.hasOwnProperty('l') && !node.l) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l;
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
delete node.l;
|
delete node.l;
|
||||||
} else {
|
} else {
|
||||||
if (!node.l) {
|
if (!node.l) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l;
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
node.l = true;
|
node.l = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function buildAppearanceForm(container,node) {
|
function buildAppearanceForm(container,node) {
|
||||||
@ -164,10 +165,10 @@
|
|||||||
var categories = RED.palette.getCategories();
|
var categories = RED.palette.getCategories();
|
||||||
categories.sort(function(A,B) {
|
categories.sort(function(A,B) {
|
||||||
return A.label.localeCompare(B.label);
|
return A.label.localeCompare(B.label);
|
||||||
})
|
});
|
||||||
categories.forEach(function(cat) {
|
categories.forEach(function(cat) {
|
||||||
categorySelector.append($("<option/>").val(cat.id).text(cat.label));
|
categorySelector.append($("<option/>").val(cat.id).text(cat.label));
|
||||||
})
|
});
|
||||||
categorySelector.append($("<option/>").attr('disabled',true).text("---"));
|
categorySelector.append($("<option/>").attr('disabled',true).text("---"));
|
||||||
categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory")));
|
categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory")));
|
||||||
|
|
||||||
@ -180,7 +181,7 @@
|
|||||||
$("#subflow-appearance-input-category").width(250);
|
$("#subflow-appearance-input-category").width(250);
|
||||||
$("#subflow-appearance-input-custom-category").hide();
|
$("#subflow-appearance-input-custom-category").hide();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
$("#subflow-appearance-input-category").val(node.category||"subflows");
|
$("#subflow-appearance-input-category").val(node.category||"subflows");
|
||||||
var userCount = 0;
|
var userCount = 0;
|
||||||
@ -204,7 +205,7 @@
|
|||||||
$("#node-input-show-label").toggleButton({
|
$("#node-input-show-label").toggleButton({
|
||||||
enabledLabel: RED._("editor.show"),
|
enabledLabel: RED._("editor.show"),
|
||||||
disabledLabel: RED._("editor.hide")
|
disabledLabel: RED._("editor.hide")
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!node.hasOwnProperty("l")) {
|
if (!node.hasOwnProperty("l")) {
|
||||||
// Show label unless def.showLabel set to false
|
// Show label unless def.showLabel set to false
|
||||||
@ -230,7 +231,7 @@
|
|||||||
"#E9967A", "#F3B567", "#FDD0A2",
|
"#E9967A", "#F3B567", "#FDD0A2",
|
||||||
"#FDF0C2", "#FFAAAA", "#FFCC66",
|
"#FDF0C2", "#FFAAAA", "#FFCC66",
|
||||||
"#FFF0F0", "#FFFFFF"
|
"#FFF0F0", "#FFFFFF"
|
||||||
]
|
];
|
||||||
|
|
||||||
RED.editor.colorPicker.create({
|
RED.editor.colorPicker.create({
|
||||||
id: "red-ui-editor-node-color",
|
id: "red-ui-editor-node-color",
|
||||||
@ -245,9 +246,9 @@
|
|||||||
nodeDiv.css('backgroundColor',colour);
|
nodeDiv.css('backgroundColor',colour);
|
||||||
var borderColor = RED.utils.getDarkerColor(colour);
|
var borderColor = RED.utils.getDarkerColor(colour);
|
||||||
if (borderColor !== colour) {
|
if (borderColor !== colour) {
|
||||||
nodeDiv.css('border-color',borderColor)
|
nodeDiv.css('border-color',borderColor);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -264,7 +265,7 @@
|
|||||||
nodeDiv.css('backgroundColor',colour);
|
nodeDiv.css('backgroundColor',colour);
|
||||||
var borderColor = RED.utils.getDarkerColor(colour);
|
var borderColor = RED.utils.getDarkerColor(colour);
|
||||||
if (borderColor !== colour) {
|
if (borderColor !== colour) {
|
||||||
nodeDiv.css('border-color',borderColor)
|
nodeDiv.css('border-color',borderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||||
@ -292,7 +293,7 @@
|
|||||||
|
|
||||||
RED.popover.tooltip(iconButton, function() {
|
RED.popover.tooltip(iconButton, function() {
|
||||||
return $("#red-ui-editor-node-icon").val() || RED._("editor.default");
|
return $("#red-ui-editor-node-icon").val() || RED._("editor.default");
|
||||||
})
|
});
|
||||||
$('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow);
|
$('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,11 +418,11 @@
|
|||||||
});
|
});
|
||||||
rows.sort(function(A,B) {
|
rows.sort(function(A,B) {
|
||||||
return A.i-B.i;
|
return A.i-B.i;
|
||||||
})
|
});
|
||||||
rows.forEach(function(r,i) {
|
rows.forEach(function(r,i) {
|
||||||
r.r.find("label").text((i+1)+".");
|
r.r.find("label").text((i+1)+".");
|
||||||
r.r.appendTo(outputsDiv);
|
r.r.appendTo(outputsDiv);
|
||||||
})
|
});
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
buildLabelRow("output",i,"").appendTo(outputsDiv);
|
buildLabelRow("output",i,"").appendTo(outputsDiv);
|
||||||
} else {
|
} else {
|
||||||
@ -467,7 +468,7 @@
|
|||||||
clear.on("click", function(evt) {
|
clear.on("click", function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
input.val("");
|
input.val("");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -501,6 +502,12 @@
|
|||||||
}
|
}
|
||||||
var v = $(this).val();
|
var v = $(this).val();
|
||||||
hasNonBlankLabel = hasNonBlankLabel || v!== "";
|
hasNonBlankLabel = hasNonBlankLabel || v!== "";
|
||||||
|
|
||||||
|
// mark changed output port labels as dirty
|
||||||
|
if (node.type === "subflow" && (!node.outputLabels || node.outputLabels[index] !== v)) {
|
||||||
|
node.out[index].dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
newValue[index] = v;
|
newValue[index] = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -509,6 +516,12 @@
|
|||||||
changes.outputLabels = node.outputLabels;
|
changes.outputLabels = node.outputLabels;
|
||||||
node.outputLabels = newValue;
|
node.outputLabels = newValue;
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|
||||||
|
// trigger redraw of dirty port labels
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
RED.view.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
@ -590,12 +590,14 @@ RED.group = (function() {
|
|||||||
markDirty(group);
|
markDirty(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodes(group,recursive) {
|
function getNodes(group,recursive,excludeGroup) {
|
||||||
var nodes = [];
|
var nodes = [];
|
||||||
group.nodes.forEach(function(n) {
|
group.nodes.forEach(function(n) {
|
||||||
nodes.push(n);
|
if (n.type !== 'group' || !excludeGroup) {
|
||||||
|
nodes.push(n);
|
||||||
|
}
|
||||||
if (recursive && n.type === 'group') {
|
if (recursive && n.type === 'group') {
|
||||||
nodes = nodes.concat(getNodes(n,recursive))
|
nodes = nodes.concat(getNodes(n,recursive,excludeGroup))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return nodes;
|
return nodes;
|
||||||
|
@ -49,15 +49,15 @@ RED.keyboard = (function() {
|
|||||||
"]": 221,
|
"]": 221,
|
||||||
"{": 219,// <- QWERTY specific
|
"{": 219,// <- QWERTY specific
|
||||||
"}": 221 // <- QWERTY specific
|
"}": 221 // <- QWERTY specific
|
||||||
}
|
};
|
||||||
var metaKeyCodes = {
|
var metaKeyCodes = {
|
||||||
16: true,
|
16: true,
|
||||||
17: true,
|
17: true,
|
||||||
18: true,
|
18: true,
|
||||||
91: true,
|
91: true,
|
||||||
93: true
|
93: true
|
||||||
}
|
};
|
||||||
var actionToKeyMap = {}
|
var actionToKeyMap = {};
|
||||||
var defaultKeyMap = {};
|
var defaultKeyMap = {};
|
||||||
|
|
||||||
// FF generates some different keycodes because reasons.
|
// FF generates some different keycodes because reasons.
|
||||||
@ -65,7 +65,7 @@ RED.keyboard = (function() {
|
|||||||
59:186,
|
59:186,
|
||||||
61:187,
|
61:187,
|
||||||
173:189
|
173:189
|
||||||
}
|
};
|
||||||
|
|
||||||
function migrateOldKeymap() {
|
function migrateOldKeymap() {
|
||||||
// pre-0.18
|
// pre-0.18
|
||||||
@ -80,7 +80,7 @@ RED.keyboard = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUserKey(action) {
|
function getUserKey(action) {
|
||||||
return RED.settings.get('editor.keymap',{})[action]
|
return RED.settings.get('editor.keymap',{})[action];
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeKeymaps(defaultKeymap, themeKeymap) {
|
function mergeKeymaps(defaultKeymap, themeKeymap) {
|
||||||
@ -105,7 +105,7 @@ RED.keyboard = (function() {
|
|||||||
scope:scope,
|
scope:scope,
|
||||||
key:key,
|
key:key,
|
||||||
user:false
|
user:false
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,13 +115,13 @@ RED.keyboard = (function() {
|
|||||||
if (themeKeymap.hasOwnProperty(action)) {
|
if (themeKeymap.hasOwnProperty(action)) {
|
||||||
if (!themeKeymap[action].key) {
|
if (!themeKeymap[action].key) {
|
||||||
// No key for this action - default is no keybinding
|
// No key for this action - default is no keybinding
|
||||||
delete mergedKeymap[action]
|
delete mergedKeymap[action];
|
||||||
} else {
|
} else {
|
||||||
mergedKeymap[action] = [{
|
mergedKeymap[action] = [{
|
||||||
scope: themeKeymap[action].scope || "*",
|
scope: themeKeymap[action].scope || "*",
|
||||||
key: themeKeymap[action].key,
|
key: themeKeymap[action].key,
|
||||||
user: false
|
user: false
|
||||||
}]
|
}];
|
||||||
if (mergedKeymap[action][0].scope === "workspace") {
|
if (mergedKeymap[action][0].scope === "workspace") {
|
||||||
mergedKeymap[action][0].scope = "red-ui-workspace";
|
mergedKeymap[action][0].scope = "red-ui-workspace";
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ RED.keyboard = (function() {
|
|||||||
close: function() {
|
close: function() {
|
||||||
RED.menu.refreshShortcuts();
|
RED.menu.refreshShortcuts();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function revertToDefault(action) {
|
function revertToDefault(action) {
|
||||||
@ -327,7 +327,7 @@ RED.keyboard = (function() {
|
|||||||
scope:scope,
|
scope:scope,
|
||||||
key:key,
|
key:key,
|
||||||
user:false
|
user:false
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (!ondown) {
|
if (!ondown) {
|
||||||
var userAction = getUserKey(cbdown);
|
var userAction = getUserKey(cbdown);
|
||||||
@ -350,7 +350,7 @@ RED.keyboard = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keys.push([key,mod])
|
keys.push([key,mod]);
|
||||||
}
|
}
|
||||||
var slot = handlers;
|
var slot = handlers;
|
||||||
for (i=0;i<keys.length;i++) {
|
for (i=0;i<keys.length;i++) {
|
||||||
@ -373,7 +373,7 @@ RED.keyboard = (function() {
|
|||||||
//slot[key] = {scope: scope, ondown:cbdown};
|
//slot[key] = {scope: scope, ondown:cbdown};
|
||||||
}
|
}
|
||||||
slot.handlers = slot.handlers || [];
|
slot.handlers = slot.handlers || [];
|
||||||
slot.handlers.push({scope:scope,ondown:cbdown})
|
slot.handlers.push({scope:scope,ondown:cbdown});
|
||||||
slot.scope = scope;
|
slot.scope = scope;
|
||||||
slot.ondown = cbdown;
|
slot.ondown = cbdown;
|
||||||
}
|
}
|
||||||
@ -390,12 +390,12 @@ RED.keyboard = (function() {
|
|||||||
if (parsedKey) {
|
if (parsedKey) {
|
||||||
keys.push(parsedKey);
|
keys.push(parsedKey);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unrecognised key specifier:",key)
|
console.log("Unrecognised key specifier:",key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keys.push([key,mod])
|
keys.push([key,mod]);
|
||||||
}
|
}
|
||||||
var slot = handlers;
|
var slot = handlers;
|
||||||
for (i=0;i<keys.length;i++) {
|
for (i=0;i<keys.length;i++) {
|
||||||
@ -417,7 +417,7 @@ RED.keyboard = (function() {
|
|||||||
}
|
}
|
||||||
if (typeof slot.ondown === "string") {
|
if (typeof slot.ondown === "string") {
|
||||||
if (typeof modifiers === 'boolean' && modifiers) {
|
if (typeof modifiers === 'boolean' && modifiers) {
|
||||||
actionToKeyMap[slot.ondown] = {user: modifiers}
|
actionToKeyMap[slot.ondown] = {user: modifiers};
|
||||||
} else {
|
} else {
|
||||||
delete actionToKeyMap[slot.ondown];
|
delete actionToKeyMap[slot.ondown];
|
||||||
}
|
}
|
||||||
@ -433,11 +433,11 @@ RED.keyboard = (function() {
|
|||||||
function formatKey(key,plain) {
|
function formatKey(key,plain) {
|
||||||
var formattedKey = isMac?key.replace(/ctrl-?/,"⌘"):key;
|
var formattedKey = isMac?key.replace(/ctrl-?/,"⌘"):key;
|
||||||
formattedKey = isMac?formattedKey.replace(/alt-?/,"⌥"):key;
|
formattedKey = isMac?formattedKey.replace(/alt-?/,"⌥"):key;
|
||||||
formattedKey = formattedKey.replace(/shift-?/,"⇧")
|
formattedKey = formattedKey.replace(/shift-?/,"⇧");
|
||||||
formattedKey = formattedKey.replace(/left/,"←")
|
formattedKey = formattedKey.replace(/left/,"←");
|
||||||
formattedKey = formattedKey.replace(/up/,"↑")
|
formattedKey = formattedKey.replace(/up/,"↑");
|
||||||
formattedKey = formattedKey.replace(/right/,"→")
|
formattedKey = formattedKey.replace(/right/,"→");
|
||||||
formattedKey = formattedKey.replace(/down/,"↓")
|
formattedKey = formattedKey.replace(/down/,"↓");
|
||||||
if (plain) {
|
if (plain) {
|
||||||
return formattedKey;
|
return formattedKey;
|
||||||
}
|
}
|
||||||
@ -461,7 +461,6 @@ RED.keyboard = (function() {
|
|||||||
var container = $(this);
|
var container = $(this);
|
||||||
var object = container.data('data');
|
var object = container.data('data');
|
||||||
|
|
||||||
|
|
||||||
if (!container.hasClass('keyboard-shortcut-entry-expanded')) {
|
if (!container.hasClass('keyboard-shortcut-entry-expanded')) {
|
||||||
endEditShortcut();
|
endEditShortcut();
|
||||||
|
|
||||||
@ -485,7 +484,7 @@ RED.keyboard = (function() {
|
|||||||
}
|
}
|
||||||
$(this).toggleClass("input-error",!valid);
|
$(this).toggleClass("input-error",!valid);
|
||||||
okButton.attr("disabled",!valid);
|
okButton.attr("disabled",!valid);
|
||||||
})
|
});
|
||||||
|
|
||||||
var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="red-ui-workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
|
var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="red-ui-workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
|
||||||
scopeSelect.i18n();
|
scopeSelect.i18n();
|
||||||
@ -495,7 +494,7 @@ RED.keyboard = (function() {
|
|||||||
scopeSelect.val(object.scope||'*');
|
scopeSelect.val(object.scope||'*');
|
||||||
scopeSelect.on("change", function() {
|
scopeSelect.on("change", function() {
|
||||||
keyInput.trigger("change");
|
keyInput.trigger("change");
|
||||||
})
|
});
|
||||||
|
|
||||||
var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope);
|
var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope);
|
||||||
var okButton = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-check"></i></button>').appendTo(div);
|
var okButton = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-check"></i></button>').appendTo(div);
|
||||||
@ -521,10 +520,13 @@ RED.keyboard = (function() {
|
|||||||
id:object.id,
|
id:object.id,
|
||||||
scope:shortcut?shortcut.scope:undefined,
|
scope:shortcut?shortcut.scope:undefined,
|
||||||
key:shortcut?shortcut.key:undefined,
|
key:shortcut?shortcut.key:undefined,
|
||||||
user:shortcut?shortcut.user:undefined
|
user:shortcut?shortcut.user:undefined,
|
||||||
}
|
|
||||||
|
label: object.label,
|
||||||
|
options: object.options,
|
||||||
|
};
|
||||||
buildShortcutRow(container,obj);
|
buildShortcutRow(container,obj);
|
||||||
})
|
});
|
||||||
|
|
||||||
keyInput.trigger("focus");
|
keyInput.trigger("focus");
|
||||||
}
|
}
|
||||||
@ -559,7 +561,7 @@ RED.keyboard = (function() {
|
|||||||
delete object.scope;
|
delete object.scope;
|
||||||
} else {
|
} else {
|
||||||
keyDiv.parent().removeClass("keyboard-shortcut-entry-unassigned");
|
keyDiv.parent().removeClass("keyboard-shortcut-entry-unassigned");
|
||||||
keyDiv.append(RED.keyboard.formatKey(key))
|
keyDiv.append(RED.keyboard.formatKey(key));
|
||||||
$("<span>").text(scope).appendTo(scopeDiv);
|
$("<span>").text(scope).appendTo(scopeDiv);
|
||||||
object.key = key;
|
object.key = key;
|
||||||
object.scope = scope;
|
object.scope = scope;
|
||||||
@ -572,7 +574,7 @@ RED.keyboard = (function() {
|
|||||||
userKeymap[object.id] = {
|
userKeymap[object.id] = {
|
||||||
scope:shortcut.scope,
|
scope:shortcut.scope,
|
||||||
key:shortcut.key
|
key:shortcut.key
|
||||||
}
|
};
|
||||||
RED.settings.set('editor.keymap',userKeymap);
|
RED.settings.set('editor.keymap',userKeymap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,13 +590,7 @@ RED.keyboard = (function() {
|
|||||||
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
|
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
|
||||||
container.data('data',object);
|
container.data('data',object);
|
||||||
|
|
||||||
var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() {
|
var text = object.label;
|
||||||
if (arguments[5] === 0) {
|
|
||||||
return arguments[2].toUpperCase();
|
|
||||||
} else {
|
|
||||||
return " "+arguments[4].toUpperCase();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var label = $('<div>').addClass("keyboard-shortcut-entry-text").text(text).appendTo(item);
|
var label = $('<div>').addClass("keyboard-shortcut-entry-text").text(text).appendTo(item);
|
||||||
|
|
||||||
var user = $('<i class="fa fa-user"></i>').prependTo(label);
|
var user = $('<i class="fa fa-user"></i>').prependTo(label);
|
||||||
@ -635,8 +631,9 @@ RED.keyboard = (function() {
|
|||||||
} else {
|
} else {
|
||||||
filterValue = filterValue.replace(/\s/g,"");
|
filterValue = filterValue.replace(/\s/g,"");
|
||||||
shortcutList.editableList('filter', function(data) {
|
shortcutList.editableList('filter', function(data) {
|
||||||
return data.id.toLowerCase().replace(/^.*:/,"").replace("-","").indexOf(filterValue) > -1;
|
var label = data.label.toLowerCase();
|
||||||
})
|
return label.indexOf(filterValue) > -1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -657,9 +654,9 @@ RED.keyboard = (function() {
|
|||||||
});
|
});
|
||||||
var shortcuts = RED.actions.list();
|
var shortcuts = RED.actions.list();
|
||||||
shortcuts.sort(function(A,B) {
|
shortcuts.sort(function(A,B) {
|
||||||
var Aid = A.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
|
var Akey = A.label;
|
||||||
var Bid = B.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
|
var Bkey = B.label;
|
||||||
return Aid.localeCompare(Bid);
|
return Akey.localeCompare(Bkey);
|
||||||
});
|
});
|
||||||
knownShortcuts = new Set();
|
knownShortcuts = new Set();
|
||||||
shortcuts.forEach(function(s) {
|
shortcuts.forEach(function(s) {
|
||||||
|
@ -1212,6 +1212,9 @@ RED.projects = (function() {
|
|||||||
}
|
}
|
||||||
}).appendTo(row);
|
}).appendTo(row);
|
||||||
|
|
||||||
|
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-open"></div>').hide().appendTo(container);
|
||||||
|
$('<span style="display: flex; align-items: center;"><input style="padding:0; margin: 0 5px 0 0" checked type="checkbox" id="red-ui-projects-dialog-screen-clear-context"> <label for="red-ui-projects-dialog-screen-clear-context" style="padding:0; margin: 0"> <span data-i18n="projects.create.clearContext"></span></label></span>').appendTo(row).i18n();
|
||||||
|
|
||||||
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
|
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
|
||||||
$('<label for="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.create.project-name")+'</label>').appendTo(row);
|
$('<label for="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.create.project-name")+'</label>').appendTo(row);
|
||||||
|
|
||||||
@ -1501,7 +1504,8 @@ RED.projects = (function() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (projectType === 'open') {
|
} else if (projectType === 'open') {
|
||||||
return switchProject(selectedProject.name,function(err,data) {
|
var clearContext = $("#red-ui-projects-dialog-screen-clear-context").prop("checked")
|
||||||
|
return switchProject(selectedProject.name, clearContext, function(err,data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code !== 'credentials_load_failed') {
|
if (err.code !== 'credentials_load_failed') {
|
||||||
console.log(RED._("projects.create.unexpected_error"),err)
|
console.log(RED._("projects.create.unexpected_error"),err)
|
||||||
@ -1595,7 +1599,7 @@ RED.projects = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchProject(name,done) {
|
function switchProject(name,clearContext,done) {
|
||||||
RED.deploy.setDeployInflight(true);
|
RED.deploy.setDeployInflight(true);
|
||||||
RED.projects.settings.switchProject(name);
|
RED.projects.settings.switchProject(name);
|
||||||
sendRequest({
|
sendRequest({
|
||||||
@ -1614,7 +1618,7 @@ RED.projects = (function() {
|
|||||||
'*': done
|
'*': done
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},{active:true}).then(function() {
|
},{active:true, clearContext:clearContext}).then(function() {
|
||||||
dialog.dialog( "close" );
|
dialog.dialog( "close" );
|
||||||
RED.events.emit("project:change", {name:name});
|
RED.events.emit("project:change", {name:name});
|
||||||
}).always(function() {
|
}).always(function() {
|
||||||
@ -1687,7 +1691,7 @@ RED.projects = (function() {
|
|||||||
dialogHeight = 590 - (750 - winHeight);
|
dialogHeight = 590 - (750 - winHeight);
|
||||||
}
|
}
|
||||||
$(".red-ui-projects-dialog-box").height(dialogHeight);
|
$(".red-ui-projects-dialog-box").height(dialogHeight);
|
||||||
$(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 180);
|
$(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 210);
|
||||||
dialog.dialog('option','title',screen.title||"");
|
dialog.dialog('option','title',screen.title||"");
|
||||||
dialog.dialog("open");
|
dialog.dialog("open");
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ RED.search = (function() {
|
|||||||
var selected = -1;
|
var selected = -1;
|
||||||
var visible = false;
|
var visible = false;
|
||||||
|
|
||||||
|
var searchHistory = [];
|
||||||
var index = {};
|
var index = {};
|
||||||
var currentResults = [];
|
var currentResults = [];
|
||||||
var previousActiveElement;
|
var previousActiveElement;
|
||||||
@ -52,10 +53,22 @@ RED.search = (function() {
|
|||||||
}
|
}
|
||||||
l = l||n.label||n.name||n.id||"";
|
l = l||n.label||n.name||n.id||"";
|
||||||
|
|
||||||
|
|
||||||
var properties = ['id','type','name','label','info'];
|
var properties = ['id','type','name','label','info'];
|
||||||
if (n._def && n._def.defaults) {
|
const node_def = n && n._def;
|
||||||
properties = properties.concat(Object.keys(n._def.defaults));
|
if (node_def) {
|
||||||
|
if (node_def.defaults) {
|
||||||
|
properties = properties.concat(Object.keys(node_def.defaults));
|
||||||
|
}
|
||||||
|
if (n.type !== "group" && node_def.paletteLabel && node_def.paletteLabel !== node_def.type) {
|
||||||
|
try {
|
||||||
|
const label = ("" + (typeof node_def.paletteLabel === "function" ? node_def.paletteLabel.call(node_def) : node_def.paletteLabel)).toLowerCase();
|
||||||
|
if(label && label !== (""+node_def.type).toLowerCase()) {
|
||||||
|
indexProperty(n, l, label);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.warn(`error indexing ${l}`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (var i=0;i<properties.length;i++) {
|
for (var i=0;i<properties.length;i++) {
|
||||||
if (n.hasOwnProperty(properties[i])) {
|
if (n.hasOwnProperty(properties[i])) {
|
||||||
@ -205,6 +218,20 @@ RED.search = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function populateSearchHistory() {
|
||||||
|
if (searchHistory.length > 0) {
|
||||||
|
searchResults.editableList('addItem',{
|
||||||
|
historyHeader: true
|
||||||
|
});
|
||||||
|
searchHistory.forEach(function(entry) {
|
||||||
|
searchResults.editableList('addItem',{
|
||||||
|
history: true,
|
||||||
|
value: entry
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
function createDialog() {
|
function createDialog() {
|
||||||
dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#red-ui-main-container");
|
dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#red-ui-main-container");
|
||||||
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
|
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
|
||||||
@ -213,7 +240,12 @@ RED.search = (function() {
|
|||||||
change: function() {
|
change: function() {
|
||||||
searchResults.editableList('empty');
|
searchResults.editableList('empty');
|
||||||
selected = -1;
|
selected = -1;
|
||||||
currentResults = search($(this).val());
|
var value = $(this).val();
|
||||||
|
if (value === "") {
|
||||||
|
populateSearchHistory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentResults = search(value);
|
||||||
if (currentResults.length > 0) {
|
if (currentResults.length > 0) {
|
||||||
for (i=0;i<Math.min(currentResults.length,25);i++) {
|
for (i=0;i<Math.min(currentResults.length,25);i++) {
|
||||||
searchResults.editableList('addItem',currentResults[i])
|
searchResults.editableList('addItem',currentResults[i])
|
||||||
@ -285,7 +317,12 @@ RED.search = (function() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} if ($(children[selected]).hasClass("red-ui-search-history")) {
|
||||||
|
var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data');
|
||||||
|
if (object) {
|
||||||
|
searchInput.searchBox('value',object.value)
|
||||||
|
}
|
||||||
|
} else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) {
|
||||||
if (currentResults.length > 0) {
|
if (currentResults.length > 0) {
|
||||||
reveal(currentResults[Math.max(0,selected)].node);
|
reveal(currentResults[Math.max(0,selected)].node);
|
||||||
}
|
}
|
||||||
@ -301,7 +338,32 @@ RED.search = (function() {
|
|||||||
addItem: function(container,i,object) {
|
addItem: function(container,i,object) {
|
||||||
var node = object.node;
|
var node = object.node;
|
||||||
var div;
|
var div;
|
||||||
if (object.more) {
|
if (object.historyHeader) {
|
||||||
|
container.parent().addClass("red-ui-search-historyHeader")
|
||||||
|
$('<div>',{class:"red-ui-search-empty"}).text(RED._("search.history")).appendTo(container);
|
||||||
|
$('<button type="button" class="red-ui-button red-ui-button-small"></button>').text(RED._("search.clear")).appendTo(container).on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
searchHistory = [];
|
||||||
|
searchResults.editableList('empty');
|
||||||
|
});
|
||||||
|
} else if (object.history) {
|
||||||
|
container.parent().addClass("red-ui-search-history")
|
||||||
|
div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
|
||||||
|
div.text(object.value);
|
||||||
|
div.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
searchInput.searchBox('value',object.value)
|
||||||
|
searchInput.focus();
|
||||||
|
})
|
||||||
|
$('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></button>').appendTo(container).on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var index = searchHistory.indexOf(object.value);
|
||||||
|
searchHistory.splice(index,1);
|
||||||
|
searchResults.editableList('removeItem', object);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
} else if (object.more) {
|
||||||
container.parent().addClass("red-ui-search-more")
|
container.parent().addClass("red-ui-search-more")
|
||||||
div = $('<a>',{href:'#',class:"red-ui-search-result red-ui-search-empty"}).appendTo(container);
|
div = $('<a>',{href:'#',class:"red-ui-search-result red-ui-search-empty"}).appendTo(container);
|
||||||
div.text(RED._("palette.editor.more",{count:object.more.results.length-object.more.start}));
|
div.text(RED._("palette.editor.more",{count:object.more.results.length-object.more.start}));
|
||||||
@ -356,6 +418,12 @@ RED.search = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reveal(node) {
|
function reveal(node) {
|
||||||
|
var searchVal = searchInput.val();
|
||||||
|
var existingIndex = searchHistory.indexOf(searchVal);
|
||||||
|
if (existingIndex > -1) {
|
||||||
|
searchHistory.splice(existingIndex,1);
|
||||||
|
}
|
||||||
|
searchHistory.unshift(searchInput.val());
|
||||||
hide();
|
hide();
|
||||||
RED.view.reveal(node.id);
|
RED.view.reveal(node.id);
|
||||||
}
|
}
|
||||||
@ -374,9 +442,14 @@ RED.search = (function() {
|
|||||||
|
|
||||||
if (dialog === null) {
|
if (dialog === null) {
|
||||||
createDialog();
|
createDialog();
|
||||||
|
} else {
|
||||||
|
searchResults.editableList('empty');
|
||||||
}
|
}
|
||||||
dialog.slideDown(300);
|
dialog.slideDown(300);
|
||||||
searchInput.searchBox('value',v)
|
searchInput.searchBox('value',v)
|
||||||
|
if (!v || v === "") {
|
||||||
|
populateSearchHistory();
|
||||||
|
}
|
||||||
RED.events.emit("search:open");
|
RED.events.emit("search:open");
|
||||||
visible = true;
|
visible = true;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,15 @@ RED.sidebar = (function() {
|
|||||||
var sidebar_tabs;
|
var sidebar_tabs;
|
||||||
var knownTabs = {};
|
var knownTabs = {};
|
||||||
|
|
||||||
|
// We store the current sidebar tab id in localStorage as 'last-sidebar-tab'
|
||||||
|
// This is restored when the editor is reloaded.
|
||||||
|
// We use sidebar_tabs.onchange to update localStorage. However that will
|
||||||
|
// also get triggered when the first tab gets added to the tabs - typically
|
||||||
|
// the 'info' tab. So we use the following variable to store the retrieved
|
||||||
|
// value from localStorage before we start adding the actual tabs
|
||||||
|
var lastSessionSelectedTab = null;
|
||||||
|
|
||||||
|
|
||||||
function addTab(title,content,closeable,visible) {
|
function addTab(title,content,closeable,visible) {
|
||||||
var options;
|
var options;
|
||||||
if (typeof title === "string") {
|
if (typeof title === "string") {
|
||||||
@ -194,16 +203,16 @@ RED.sidebar = (function() {
|
|||||||
RED.events.emit("sidebar:resize");
|
RED.events.emit("sidebar:resize");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSidebar(id) {
|
function showSidebar(id, skipShowSidebar) {
|
||||||
if (id === ":first") {
|
if (id === ":first") {
|
||||||
id = RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0]
|
id = lastSessionSelectedTab || RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0]
|
||||||
}
|
}
|
||||||
if (id) {
|
if (id) {
|
||||||
if (!containsTab(id) && knownTabs[id]) {
|
if (!containsTab(id) && knownTabs[id]) {
|
||||||
sidebar_tabs.addTab(knownTabs[id]);
|
sidebar_tabs.addTab(knownTabs[id]);
|
||||||
}
|
}
|
||||||
sidebar_tabs.activateTab(id);
|
sidebar_tabs.activateTab(id);
|
||||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
if (!skipShowSidebar && !RED.menu.isSelected("menu-item-sidebar")) {
|
||||||
RED.menu.setSelected("menu-item-sidebar",true);
|
RED.menu.setSelected("menu-item-sidebar",true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,6 +236,7 @@ RED.sidebar = (function() {
|
|||||||
if (tab.toolbar) {
|
if (tab.toolbar) {
|
||||||
$(tab.toolbar).show();
|
$(tab.toolbar).show();
|
||||||
}
|
}
|
||||||
|
RED.settings.setLocal("last-sidebar-tab", tab.id)
|
||||||
},
|
},
|
||||||
onremove: function(tab) {
|
onremove: function(tab) {
|
||||||
$(tab.wrapper).hide();
|
$(tab.wrapper).hide();
|
||||||
@ -255,7 +265,9 @@ RED.sidebar = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar");
|
RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar");
|
||||||
showSidebar();
|
|
||||||
|
lastSessionSelectedTab = RED.settings.getLocal("last-sidebar-tab")
|
||||||
|
|
||||||
RED.sidebar.info.init();
|
RED.sidebar.info.init();
|
||||||
RED.sidebar.help.init();
|
RED.sidebar.help.init();
|
||||||
RED.sidebar.config.init();
|
RED.sidebar.config.init();
|
||||||
|
@ -27,5 +27,7 @@ RED.state = {
|
|||||||
PANNING: 10,
|
PANNING: 10,
|
||||||
SELECTING_NODE: 11,
|
SELECTING_NODE: 11,
|
||||||
GROUP_DRAGGING: 12,
|
GROUP_DRAGGING: 12,
|
||||||
GROUP_RESIZE: 13
|
GROUP_RESIZE: 13,
|
||||||
|
DETACHED_DRAGGING: 14,
|
||||||
|
SLICING: 15
|
||||||
}
|
}
|
||||||
|
@ -64,15 +64,17 @@ RED.sidebar.help = (function() {
|
|||||||
style: "compact",
|
style: "compact",
|
||||||
delay: 100,
|
delay: 100,
|
||||||
change: function() {
|
change: function() {
|
||||||
var val = $(this).val().toLowerCase();
|
const searchFor = $(this).val().toLowerCase();
|
||||||
if (val) {
|
if (searchFor) {
|
||||||
showTOC();
|
showTOC();
|
||||||
var c = treeList.treeList('filter',function(item) {
|
treeList.treeList('filter',function(item) {
|
||||||
if (item.depth === 0) {
|
if (item.depth === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (item.nodeType && item.nodeType.indexOf(val) > -1) ||
|
let found = item.nodeType && item.nodeType.toLowerCase().indexOf(searchFor) > -1;
|
||||||
(item.subflowLabel && item.subflowLabel.indexOf(val) > -1)
|
found = found || item.subflowLabel && item.subflowLabel.toLowerCase().indexOf(searchFor) > -1;
|
||||||
|
found = found || item.palleteLabel && item.palleteLabel.toLowerCase().indexOf(searchFor) > -1;
|
||||||
|
return found;
|
||||||
},true)
|
},true)
|
||||||
} else {
|
} else {
|
||||||
treeList.treeList('filter',null);
|
treeList.treeList('filter',null);
|
||||||
@ -224,17 +226,21 @@ RED.sidebar.help = (function() {
|
|||||||
|
|
||||||
|
|
||||||
moduleNames.forEach(function(moduleName) {
|
moduleNames.forEach(function(moduleName) {
|
||||||
var module = modules[moduleName];
|
const module = modules[moduleName];
|
||||||
var nodeTypes = [];
|
const nodeTypes = [];
|
||||||
|
const moduleSets = module.sets;
|
||||||
var setNames = Object.keys(module.sets);
|
const setNames = Object.keys(moduleSets);
|
||||||
setNames.forEach(function(setName) {
|
setNames.forEach(function(setName) {
|
||||||
module.sets[setName].types.forEach(function(nodeType) {
|
const moduleSet = moduleSets[setName];
|
||||||
|
moduleSet.types.forEach(function(nodeType) {
|
||||||
if ($("script[data-help-name='"+nodeType+"']").length) {
|
if ($("script[data-help-name='"+nodeType+"']").length) {
|
||||||
|
const n = {_def:RED.nodes.getType(nodeType),type:nodeType}
|
||||||
|
n.name = getNodePaletteLabel(n);
|
||||||
nodeTypes.push({
|
nodeTypes.push({
|
||||||
id: "node-type:"+nodeType,
|
id: "node-type:"+nodeType,
|
||||||
nodeType: nodeType,
|
nodeType: nodeType,
|
||||||
element:getNodeLabel({_def:RED.nodes.getType(nodeType),type:nodeType})
|
palleteLabel: n.name,
|
||||||
|
element: getNodeLabel(n)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -254,18 +260,21 @@ RED.sidebar.help = (function() {
|
|||||||
treeList.treeList("data",helpData);
|
treeList.treeList("data",helpData);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodeLabel(n) {
|
function getNodePaletteLabel(n) {
|
||||||
var div = $('<div>',{class:"red-ui-node-list-item"});
|
let label = n.name;
|
||||||
var icon = RED.utils.createNodeIcon(n).appendTo(div);
|
|
||||||
var label = n.name;
|
|
||||||
if (!label && n._def && n._def.paletteLabel) {
|
if (!label && n._def && n._def.paletteLabel) {
|
||||||
try {
|
try {
|
||||||
label = (typeof n._def.paletteLabel === "function" ? n._def.paletteLabel.call(n._def) : n._def.paletteLabel)||"";
|
label = (typeof n._def.paletteLabel === "function" ? n._def.paletteLabel.call(n._def) : n._def.paletteLabel)||"";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
label = label || n.type;
|
return label || n.type;
|
||||||
$('<div>',{class:"red-ui-node-label"}).text(n.name||n.type).appendTo(icon);
|
}
|
||||||
|
|
||||||
|
function getNodeLabel(n) {
|
||||||
|
const div = $('<div>',{class:"red-ui-node-list-item"});
|
||||||
|
const icon = RED.utils.createNodeIcon(n).appendTo(div);
|
||||||
|
$('<div>',{class:"red-ui-node-label"}).text(getNodePaletteLabel(n)).appendTo(icon);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +256,10 @@ RED.tourGuide = (function() {
|
|||||||
}
|
}
|
||||||
$('<div>').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription);
|
$('<div>').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription);
|
||||||
|
|
||||||
|
if (step.image) {
|
||||||
|
$(`<img src="red/tours/${step.image}" />`).appendTo(stepDescription)
|
||||||
|
}
|
||||||
|
|
||||||
var stepToolbar = $('<div>',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent);
|
var stepToolbar = $('<div>',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent);
|
||||||
|
|
||||||
// var breadcrumbs = $('<div>',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
|
// var breadcrumbs = $('<div>',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
|
||||||
|
@ -121,6 +121,13 @@ RED.userSettings = (function() {
|
|||||||
// {setting:"theme", label:"Theme",options:function(done){ done([{val:'',text:'default'}].concat(RED.settings.theme("themes"))) }},
|
// {setting:"theme", label:"Theme",options:function(done){ done([{val:'',text:'default'}].concat(RED.settings.theme("themes"))) }},
|
||||||
// ]
|
// ]
|
||||||
// },
|
// },
|
||||||
|
{
|
||||||
|
title: "menu.label.view.view",
|
||||||
|
options: [
|
||||||
|
{setting:"view-store-zoom",label:"menu.label.view.storeZoom", default: false, toggle:true, onchange: function(val) { if (!val) { RED.settings.removeLocal("zoom-level")}}},
|
||||||
|
{setting:"view-store-position",label:"menu.label.view.storePosition", default: false, toggle:true, onchange: function(val) { if (!val) { RED.settings.removeLocal("scroll-positions")}}},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "menu.label.view.grid",
|
title: "menu.label.view.grid",
|
||||||
options: [
|
options: [
|
||||||
|
@ -725,6 +725,90 @@ RED.view.tools = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function wireSeriesOfNodes() {
|
||||||
|
var selection = RED.view.selection();
|
||||||
|
if (selection.nodes) {
|
||||||
|
if (selection.nodes.length > 1) {
|
||||||
|
var i = 0;
|
||||||
|
var newLinks = [];
|
||||||
|
while (i < selection.nodes.length - 1) {
|
||||||
|
var nodeA = selection.nodes[i];
|
||||||
|
var nodeB = selection.nodes[i+1];
|
||||||
|
if (nodeA.outputs > 0 && nodeB.inputs > 0) {
|
||||||
|
var existingLinks = RED.nodes.filterLinks({
|
||||||
|
source: nodeA,
|
||||||
|
target: nodeB,
|
||||||
|
sourcePort: 0
|
||||||
|
})
|
||||||
|
if (existingLinks.length === 0) {
|
||||||
|
var newLink = {
|
||||||
|
source: nodeA,
|
||||||
|
target: nodeB,
|
||||||
|
sourcePort: 0
|
||||||
|
}
|
||||||
|
RED.nodes.addLink(newLink);
|
||||||
|
newLinks.push(newLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (newLinks.length > 0) {
|
||||||
|
RED.history.push({
|
||||||
|
t: 'add',
|
||||||
|
links: newLinks,
|
||||||
|
dirty: RED.nodes.dirty()
|
||||||
|
})
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wireNodeToMultiple() {
|
||||||
|
var selection = RED.view.selection();
|
||||||
|
if (selection.nodes) {
|
||||||
|
if (selection.nodes.length > 1) {
|
||||||
|
var sourceNode = selection.nodes[0];
|
||||||
|
if (sourceNode.outputs === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var i = 1;
|
||||||
|
var newLinks = [];
|
||||||
|
while (i < selection.nodes.length) {
|
||||||
|
var targetNode = selection.nodes[i];
|
||||||
|
if (targetNode.inputs > 0) {
|
||||||
|
var existingLinks = RED.nodes.filterLinks({
|
||||||
|
source: sourceNode,
|
||||||
|
target: targetNode,
|
||||||
|
sourcePort: Math.min(sourceNode.outputs-1,i-1)
|
||||||
|
})
|
||||||
|
if (existingLinks.length === 0) {
|
||||||
|
var newLink = {
|
||||||
|
source: sourceNode,
|
||||||
|
target: targetNode,
|
||||||
|
sourcePort: Math.min(sourceNode.outputs-1,i-1)
|
||||||
|
}
|
||||||
|
RED.nodes.addLink(newLink);
|
||||||
|
newLinks.push(newLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (newLinks.length > 0) {
|
||||||
|
RED.history.push({
|
||||||
|
t: 'add',
|
||||||
|
links: newLinks,
|
||||||
|
dirty: RED.nodes.dirty()
|
||||||
|
})
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: function() {
|
init: function() {
|
||||||
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
||||||
@ -783,7 +867,8 @@ RED.view.tools = (function() {
|
|||||||
RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') })
|
RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') })
|
||||||
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
|
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
|
||||||
|
|
||||||
|
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
|
||||||
|
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
|
||||||
|
|
||||||
// RED.actions.add("core:add-node", function() { addNode() })
|
// RED.actions.add("core:add-node", function() { addNode() })
|
||||||
},
|
},
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,13 @@
|
|||||||
stroke-dasharray: 10 5;
|
stroke-dasharray: 10 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nr-ui-view-slice {
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke: $view-lasso-stroke;
|
||||||
|
fill: none;
|
||||||
|
stroke-dasharray: 10 5;
|
||||||
|
}
|
||||||
|
|
||||||
.node_label_italic, // deprecated: use red-ui-flow-node-label-italic
|
.node_label_italic, // deprecated: use red-ui-flow-node-label-italic
|
||||||
.red-ui-flow-node-label-italic {
|
.red-ui-flow-node-label-italic {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
@ -204,6 +204,28 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: $form-placeholder-color;
|
color: $form-placeholder-color;
|
||||||
}
|
}
|
||||||
|
.red-ui-search-history {
|
||||||
|
button {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover button {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-search-historyHeader {
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-search-history-result {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.red-ui-search-result-action {
|
.red-ui-search-result-action {
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
|
@ -78,6 +78,12 @@
|
|||||||
}
|
}
|
||||||
.red-ui-tourGuide-popover-description {
|
.red-ui-tourGuide-popover-description {
|
||||||
padding: 10px 20px 5px;
|
padding: 10px 20px 5px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 150px;
|
||||||
|
border: 1px solid var(--red-ui-tourGuide-border);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.red-ui-tourGuide-popover-full {
|
.red-ui-tourGuide-popover-full {
|
||||||
.red-ui-tourGuide-popover-description {
|
.red-ui-tourGuide-popover-description {
|
||||||
|
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/delete-repair.gif
vendored
Normal file
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/delete-repair.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/detach-repair.gif
vendored
Normal file
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/detach-repair.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/slice.gif
vendored
Normal file
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/slice.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 KiB |
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/subflow-labels.png
vendored
Normal file
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/subflow-labels.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@ -1,12 +1,12 @@
|
|||||||
export default {
|
export default {
|
||||||
version: "2.1.0",
|
version: "2.2.0",
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
titleIcon: "fa fa-map-o",
|
titleIcon: "fa fa-map-o",
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Welcome to Node-RED 2.1!",
|
"en-US": "Welcome to Node-RED 2.2!",
|
||||||
"ja": "Node-RED 2.1へようこそ!"
|
"ja": "Node-RED 2.2へようこそ!"
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
"en-US": "Let's take a moment to discover the new features in this release.",
|
"en-US": "Let's take a moment to discover the new features in this release.",
|
||||||
"ja": "本リリースの新機能を見つけてみましょう。"
|
"ja": "本リリースの新機能を見つけてみましょう。"
|
||||||
@ -14,215 +14,142 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "A new Tour Guide",
|
"en-US": "Search history",
|
||||||
"ja": "新しいツアーガイド"
|
"ja": "検索履歴"
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
"en-US": "<p>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.</p>" +
|
"en-US": "<p>The Search dialog now keeps a history of your searches, making it easier to go back to a previous search.</p>",
|
||||||
"<p>You can choose not to see this tour in the future by disabling it under the View tab of User Settings.</p>",
|
"ja": "<p>検索ダイアログが検索履歴を保持するようになりました。これによって、過去の検索に戻りやすくなりました。</p>"
|
||||||
"ja": "<p>最初に、既に見つけている様に、新機能の本ツアーがあります。本ツアーは、新バージョンのNode-REDフローエディタを初めて開いた時のみ表示されます。</p>" +
|
|
||||||
"<p>ユーザ設定の表示タブの中で、この機能を無効化することで、本ツアーを表示しないようにすることもできます。</p>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "New Edit menu",
|
|
||||||
"ja": "新しい編集メニュー"
|
|
||||||
},
|
},
|
||||||
prepare() {
|
element: "#red-ui-search .red-ui-searchBox-form",
|
||||||
$("#red-ui-header-button-sidemenu").trigger("click");
|
|
||||||
$("#menu-item-edit-menu").parent().addClass("open");
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
$("#menu-item-edit-menu").parent().removeClass("open");
|
|
||||||
},
|
|
||||||
element: "#menu-item-edit-menu-submenu",
|
|
||||||
interactive: false,
|
|
||||||
direction: "left",
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.</p>" +
|
|
||||||
"<p>The menu now displays keyboard shortcuts for the options.</p>",
|
|
||||||
"ja": "<p>メインメニューに「編集」セクションが追加されました。本セクションには、切り取り/貼り付けや、変更操作を戻す/やり直しの様な使い慣れたオプションが含まれています。</p>" +
|
|
||||||
"<p>本メニューには、オプションのためのキーボードショートカットも表示されるようになりました。</p>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Arranging nodes",
|
|
||||||
"ja": "ノードの配置"
|
|
||||||
},
|
|
||||||
prepare() {
|
|
||||||
$("#red-ui-header-button-sidemenu").trigger("click");
|
|
||||||
$("#menu-item-arrange-menu").parent().addClass("open");
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
$("#menu-item-arrange-menu").parent().removeClass("open");
|
|
||||||
},
|
|
||||||
element: "#menu-item-arrange-menu-submenu",
|
|
||||||
interactive: false,
|
|
||||||
direction: "left",
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>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.</p>",
|
|
||||||
"ja": "<p>メニューの新しい「配置」セクションには、ノードの配置を助ける新しいオプションが提供されています。ノードの端を揃えたり、均等に配置したり、表示順序を変更したりできます。</p>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Hiding tabs",
|
|
||||||
"ja": "タブの非表示"
|
|
||||||
},
|
|
||||||
element: "#red-ui-workspace-tabs > li.active",
|
|
||||||
description: {
|
|
||||||
"en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-eye-slash"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.',
|
|
||||||
"ja": '<p><i class="fa fa-eye-slash"></i> アイコンをクリックすることで、タブを非表示にできます。</p><p>情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。'
|
|
||||||
},
|
|
||||||
interactive: false,
|
|
||||||
prepare() {
|
|
||||||
$("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","block");
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
$("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Tab menu",
|
|
||||||
"ja": "タブメニュー"
|
|
||||||
},
|
|
||||||
element: "#red-ui-workspace-tabs-menu",
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>The new tab menu also provides lots of new options for your tabs.</p>",
|
|
||||||
"ja": "<p>新しいタブメニューには、タブに関する沢山の新しいオプションが提供されています。</p>"
|
|
||||||
},
|
|
||||||
interactive: false,
|
|
||||||
direction: "left",
|
|
||||||
prepare() {
|
|
||||||
$("#red-ui-workspace > .red-ui-tabs > .red-ui-tabs-menu a").trigger("click");
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
$(document).trigger("click");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Flow and Group level environment variables",
|
|
||||||
"ja": "フローとグループの環境変数"
|
|
||||||
},
|
|
||||||
element: "#red-ui-workspace-tabs > li.active",
|
|
||||||
interactive: false,
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>",
|
|
||||||
"ja": "<p>フローとグループには、内部のノードから参照できる環境変数を設定できるようになりました。</p>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prepare(done) {
|
prepare(done) {
|
||||||
RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties");
|
RED.search.show();
|
||||||
setTimeout(done,700);
|
setTimeout(done,400);
|
||||||
|
},
|
||||||
|
complete() {
|
||||||
|
RED.search.hide();
|
||||||
},
|
},
|
||||||
element: "#red-ui-tab-editor-tab-envProperties-link-button",
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>Their edit dialogs have a new Environment Variables section.</p>",
|
|
||||||
"ja": "<p>編集ダイアログに環境変数セクションが追加されました。</p>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
element: ".node-input-env-container-row",
|
title: {
|
||||||
direction: "left",
|
"en-US": "Remembering Zoom & Position",
|
||||||
|
"ja": "拡大/縮小のレベルや位置を記憶"
|
||||||
|
},
|
||||||
description: {
|
description: {
|
||||||
"en-US": '<p>The environment variables are listed in this table and new ones can be added by clicking the <i class="fa fa-plus"></i> button.</p>',
|
"en-US": "<p>The editor has new options to restore the zoom level and scroll position when reloading the editor.</p>",
|
||||||
"ja": '<p>この表に環境変数が一覧表示されており、<i class="fa fa-plus"></i>ボタンをクリックすることで新しい変数を追加できます。</p>'
|
"ja": "<p>エディタを再読み込みした時に、拡大/縮小のレベルやスクロール位置を復元するための新しいオプションを利用できます。</p>"
|
||||||
|
},
|
||||||
|
element: function() { return $("#user-settings-view-store-position").parent()},
|
||||||
|
prepare(done) {
|
||||||
|
RED.actions.invoke("core:show-user-settings")
|
||||||
|
setTimeout(done,400);
|
||||||
},
|
},
|
||||||
complete(done) {
|
complete(done) {
|
||||||
$("#node-dialog-cancel").trigger("click");
|
$("#node-dialog-ok").trigger("click");
|
||||||
setTimeout(done,500);
|
setTimeout(done,400);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "New wiring actions",
|
||||||
|
"ja": "新しいワイヤー操作"
|
||||||
|
},
|
||||||
|
// image: "images/",
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>A pair of new actions have been added to help with wiring nodes together:</p>
|
||||||
|
<ul>
|
||||||
|
<li><b><code>Wire Series Of Nodes</code></b> - adds a wire (if necessary) between each pair of nodes in the order they were selected.</li>
|
||||||
|
<li><b><code>Wire Node To Multiple</code></b> - wires the first node selected to all of the other selected nodes.</li>
|
||||||
|
</ul>
|
||||||
|
<p>Actions can be accessed from the Action List in the main menu.</p>`,
|
||||||
|
"ja": `<p>ノード接続を支援する2つの新しい操作が追加されました:</p>
|
||||||
|
<ul>
|
||||||
|
<li><b><code>Wire Series Of Nodes</code></b> - ノードを選択した順序で、各ノードのペアの間にワイヤーを(必要に応じて)追加します。</li>
|
||||||
|
<li><b><code>Wire Node To Multiple</code></b> - 最初に選択したノードから、他の選択した全てのノードに対して、ワイヤーを追加します。</li>
|
||||||
|
</ul>
|
||||||
|
<p>メインメニュー内の動作一覧から、これらの操作を利用できます。</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Deleting nodes and reconnecting wires",
|
||||||
|
"ja": "ノードの削除とワイヤーの再接続"
|
||||||
|
},
|
||||||
|
image: "images/delete-repair.gif",
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>It is now possible to delete a selection of nodes and automatically repair the wiring behind them.</p>
|
||||||
|
<p>This is really useful if you want to remove a node from the middle of the flow.</p>
|
||||||
|
<p>Hold the Ctrl (or Cmd) key when you press Delete and the nodes will be gone and the wires repaired.</p>
|
||||||
|
`,
|
||||||
|
"ja": `<p>選択したノードを削除した後、その背後にあるワイヤーを自動的に修復できるようになりました。</p>
|
||||||
|
<p>これは、フローの中からノードを削除する時に、とても便利に使えます。</p>
|
||||||
|
<p>Ctrl (またはCmd)キーを押しながらDeleteキーを押すと、ノードがなくなり、ワイヤーが修復されます。</p>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Link Call node added",
|
"en-US": "Detaching nodes from a flow",
|
||||||
"ja": "Link Callノードを追加"
|
"ja": "フローからノードの切り離し"
|
||||||
},
|
},
|
||||||
prepare(done) {
|
image: "images/detach-repair.gif",
|
||||||
this.paletteWasClosed = $("#red-ui-main-container").hasClass("red-ui-palette-closed");
|
|
||||||
RED.actions.invoke("core:toggle-palette",true)
|
|
||||||
$('[data-palette-type="link call"]')[0].scrollIntoView({block:"center"})
|
|
||||||
setTimeout(done,100);
|
|
||||||
},
|
|
||||||
element: '[data-palette-type="link call"]',
|
|
||||||
direction: "right",
|
|
||||||
description: {
|
description: {
|
||||||
"en-US": "<p>The <code>Link Call</code> node lets you call another flow that begins with a <code>Link In</code> node and get the result back when the message reaches a <code>Link Out</code> node.</p>",
|
"en-US": `<p>If you want to remove a node from a flow without deleting it,
|
||||||
"ja": "<p><code>Link Call</code>ノードを用いることで、<code>Link In</code>ノードから始まるフローを呼び出し、<code>Link Out</code>ノードに到達した時に、結果を取得できます。</p>"
|
you can use the <b><code>Detach Selected Nodes</code></b> action.</p>
|
||||||
|
<p>The nodes will be removed from their flow, the wiring repaired behind them, and then attached to the mouse
|
||||||
|
so you can drop them wherever you want in the workspace.</p>
|
||||||
|
<p>There isn't a default keyboard shortcut assigned for this new action, but
|
||||||
|
you can add your own via the Keyboard pane of the main Settings dialog.</p>`,
|
||||||
|
"ja": `<p>ノードを削除することなく、フローからノードを除きたい場合は、<b><code>Detach Selected Nodes</code></b>操作を利用できます。</p>
|
||||||
|
<p>フローからノードが除かれた後、背後のワイヤーが修復され、ノードはマウスポインタにつながります。そのため、ワークスペースの好きな所にノードを配置できます。</p>
|
||||||
|
<p>この新しい操作に対して、デフォルトのキーボードショートカットは登録されていませんが、メイン設定ダイアログのキーボード設定から追加できます。</p>`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "MQTT nodes support dynamic connections",
|
"en-US": "More wiring tricks",
|
||||||
"ja": "MQTTノードが動的接続をサポート"
|
"ja": "その他のワイヤー操作"
|
||||||
},
|
},
|
||||||
prepare(done) {
|
image: "images/slice.gif",
|
||||||
$('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"})
|
|
||||||
setTimeout(done,100);
|
|
||||||
},
|
|
||||||
element: '[data-palette-type="mqtt out"]',
|
|
||||||
direction: "right",
|
|
||||||
description: {
|
description: {
|
||||||
"en-US": '<p>The <code>MQTT</code> nodes now support creating their connections and subscriptions dynamically.</p>',
|
"en-US": `<p>A couple more wiring tricks to share.</p>
|
||||||
"ja": '<p><code>MQTT</code>ノードは、動的な接続や購読ができるようになりました。</p>'
|
<p>You can now select multiple wires by holding the Ctrl (or Cmd) key
|
||||||
},
|
when clicking on a wire. This makes it easier to delete multiple wires in one go.</p>
|
||||||
},
|
<p>If you hold the Ctrl (or Cmd) key, then click and drag with the right-hand mouse button,
|
||||||
{
|
you can slice through wires to remove them.</p>`,
|
||||||
title: {
|
"ja": `<p>その他のいくつかのワイヤー操作</p>
|
||||||
"en-US": "File nodes renamed",
|
<p>Ctrl (またはCmd)キーを押しながらワイヤーをクリックすることで、複数のワイヤーを選択できるようになりました。これによって、複数のワイヤーを一度に削除することが簡単になりました。</p>
|
||||||
"ja": "ファイルノードの名前変更"
|
<p>Ctrl (またはCmd)キーを押しながら、マウスの右ボタンを用いてドラッグすると、ワイヤーを切って削除できます。</p>`
|
||||||
},
|
|
||||||
prepare(done) {
|
|
||||||
$('[data-palette-type="file"]')[0].scrollIntoView({block:"center"});
|
|
||||||
setTimeout(done,100);
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
if (this.paletteWasClosed) {
|
|
||||||
RED.actions.invoke("core:toggle-palette",false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
element: '[data-palette-type="file"]',
|
|
||||||
direction: "right",
|
|
||||||
description: {
|
|
||||||
"en-US": "<p>The file nodes have been renamed to make it clearer which node does what.</p>",
|
|
||||||
"ja": "<p>fileノードの名前が変更され、どのノードが何を行うかが明確になりました。</p>"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Deep copy option on Change node",
|
"en-US": "Subflow Output Labels",
|
||||||
"ja": "Changeノードのディープコピーオプション"
|
"ja": "サブフローの出力ラベル"
|
||||||
},
|
|
||||||
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._});
|
|
||||||
setTimeout(done,700);
|
|
||||||
},
|
|
||||||
complete(done) {
|
|
||||||
$("#node-dialog-cancel").trigger("click");
|
|
||||||
setTimeout(done,500);
|
|
||||||
},
|
|
||||||
element: function() {
|
|
||||||
return $(".node-input-rule-property-deepCopy").next();
|
|
||||||
},
|
},
|
||||||
|
image: "images/subflow-labels.png",
|
||||||
description: {
|
description: {
|
||||||
"en-US": "<p>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.</p>",
|
"en-US": "<p>If a subflow has labels set for its outputs, they now get shown on the ports within the subflow template view.</p>",
|
||||||
"ja": "<p>値を代入に、値のディープコピーを作成するオプションが追加されました。これによって参照ではなく、完全なコピーが作成されます。</p>"
|
"ja": "<p>サブフローの出力にラベルが設定されている場合、サブフローテンプレート画面内のポートにラベルが表示されるようになりました。</p>"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "And that's not all...",
|
"en-US": "Node Updates",
|
||||||
"ja": "これが全てではありません..."
|
"ja": "ノードの更新"
|
||||||
},
|
},
|
||||||
|
// image: "images/",
|
||||||
description: {
|
description: {
|
||||||
"en-US": "<p>There are many more smaller changes, including:</p><ul><li>Auto-complete suggestions in the <code>msg</code> TypedInput.</li><li>Support for <code>msg.resetTimeout</code> in the <code>Join</code> node.</li><li>Pushing messages to the front of the queue in the <code>Delay</code> node's rate limiting mode.</li><li>An optional second output on the <code>Delay</code> node for rate limited messages.</li></ul>",
|
"en-US": `<ul>
|
||||||
"ja": "<p>以下の様な小さな変更が沢山あります:</p><ul><li><code>msg</code> TypedInputの自動補完提案</li><li><code>Join</code>ノードで<code>msg.resetTimeout</code>のサポート</li><li><code>Delay</code>ノードの流量制御モードにおいて先頭メッセージをキューに追加</li><li><code>Delay</code>ノードで流量制限されたメッセージ向けの任意の2つ目の出力</li></ul>"
|
<li>The JSON node will now handle parsing Buffer payloads</li>
|
||||||
|
<li>The TCP Client nodes support TLS connections</li>
|
||||||
|
<li>The WebSocket node allows you to specify a sub-protocol when connecting</li>
|
||||||
|
</ul>`,
|
||||||
|
"ja": `<ul>
|
||||||
|
<li>JSONノードが、バッファ形式のペイロードを解析できるようになりました。</li>
|
||||||
|
<li>TCPクライアントノードが、TLS接続をサポートしました。</li>
|
||||||
|
<li>WebSocketノードで、接続時にサブプロトコルを指定できるようになりました。</li>
|
||||||
|
</ul>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -57,11 +57,13 @@ declare class node {
|
|||||||
*/
|
*/
|
||||||
static status(status:string|boolean|number);
|
static status(status:string|boolean|number);
|
||||||
/** the id of this node */
|
/** the id of this node */
|
||||||
public readonly id:string;
|
public static readonly id:string;
|
||||||
/** the name of this node */
|
/** the name of this node */
|
||||||
public readonly name:string;
|
public static readonly name:string;
|
||||||
|
/** the path identifier for this node */
|
||||||
|
public static readonly path:string;
|
||||||
/** the number of outputs of this node */
|
/** the number of outputs of this node */
|
||||||
public readonly outputCount:number;
|
public static readonly outputCount:number;
|
||||||
}
|
}
|
||||||
declare class context {
|
declare class context {
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
@ -75,16 +75,12 @@ module.exports = function(RED) {
|
|||||||
node.repeaterSetup = function () {
|
node.repeaterSetup = function () {
|
||||||
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
|
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
|
||||||
this.repeat = this.repeat * 1000;
|
this.repeat = this.repeat * 1000;
|
||||||
if (RED.settings.verbose) {
|
this.debug(RED._("inject.repeat", this));
|
||||||
this.log(RED._("inject.repeat", this));
|
|
||||||
}
|
|
||||||
this.interval_id = setInterval(function() {
|
this.interval_id = setInterval(function() {
|
||||||
node.emit("input", {});
|
node.emit("input", {});
|
||||||
}, this.repeat);
|
}, this.repeat);
|
||||||
} else if (this.crontab) {
|
} else if (this.crontab) {
|
||||||
if (RED.settings.verbose) {
|
this.debug(RED._("inject.crontab", this));
|
||||||
this.log(RED._("inject.crontab", this));
|
|
||||||
}
|
|
||||||
this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
|
this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -148,10 +144,8 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
if (this.interval_id != null) {
|
if (this.interval_id != null) {
|
||||||
clearInterval(this.interval_id);
|
clearInterval(this.interval_id);
|
||||||
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
|
|
||||||
} else if (this.cronjob != null) {
|
} else if (this.cronjob != null) {
|
||||||
this.cronjob.stop();
|
this.cronjob.stop();
|
||||||
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
|
|
||||||
delete this.cronjob;
|
delete this.cronjob;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -112,6 +112,7 @@ module.exports = function(RED) {
|
|||||||
"var node = {"+
|
"var node = {"+
|
||||||
"id:__node__.id,"+
|
"id:__node__.id,"+
|
||||||
"name:__node__.name,"+
|
"name:__node__.name,"+
|
||||||
|
"path:__node__.path,"+
|
||||||
"outputCount:__node__.outputCount,"+
|
"outputCount:__node__.outputCount,"+
|
||||||
"log:__node__.log,"+
|
"log:__node__.log,"+
|
||||||
"error:__node__.error,"+
|
"error:__node__.error,"+
|
||||||
@ -163,6 +164,7 @@ module.exports = function(RED) {
|
|||||||
__node__: {
|
__node__: {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
name: node.name,
|
name: node.name,
|
||||||
|
path: node._path,
|
||||||
outputCount: node.outputs,
|
outputCount: node.outputs,
|
||||||
log: function() {
|
log: function() {
|
||||||
node.log.apply(node, arguments);
|
node.log.apply(node, arguments);
|
||||||
@ -344,6 +346,7 @@ module.exports = function(RED) {
|
|||||||
var node = {
|
var node = {
|
||||||
id:__node__.id,
|
id:__node__.id,
|
||||||
name:__node__.name,
|
name:__node__.name,
|
||||||
|
path:__node__.path,
|
||||||
outputCount:__node__.outputCount,
|
outputCount:__node__.outputCount,
|
||||||
log:__node__.log,
|
log:__node__.log,
|
||||||
error:__node__.error,
|
error:__node__.error,
|
||||||
@ -366,6 +369,7 @@ module.exports = function(RED) {
|
|||||||
var node = {
|
var node = {
|
||||||
id:__node__.id,
|
id:__node__.id,
|
||||||
name:__node__.name,
|
name:__node__.name,
|
||||||
|
path:__node__.path,
|
||||||
outputCount:__node__.outputCount,
|
outputCount:__node__.outputCount,
|
||||||
log:__node__.log,
|
log:__node__.log,
|
||||||
error:__node__.error,
|
error:__node__.error,
|
||||||
|
@ -86,7 +86,7 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
var cmd = arg.shift();
|
var cmd = arg.shift();
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
|
node.debug(cmd+" ["+arg+"]");
|
||||||
child = spawn(cmd,arg,node.spawnOpt);
|
child = spawn(cmd,arg,node.spawnOpt);
|
||||||
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
|
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
|
||||||
var unknownCommand = (child.pid === undefined);
|
var unknownCommand = (child.pid === undefined);
|
||||||
@ -136,7 +136,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (RED.settings.verbose) { node.log(arg); }
|
node.debug(arg);
|
||||||
child = exec(arg, node.execOpt, function (error, stdout, stderr) {
|
child = exec(arg, node.execOpt, function (error, stdout, stderr) {
|
||||||
var msg2, msg3;
|
var msg2, msg3;
|
||||||
delete msg.payload;
|
delete msg.payload;
|
||||||
@ -155,7 +155,7 @@ module.exports = function(RED) {
|
|||||||
if (error.signal) { msg3.payload.signal = error.signal; }
|
if (error.signal) { msg3.payload.signal = error.signal; }
|
||||||
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
|
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
|
||||||
else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
|
else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
|
||||||
if (RED.settings.verbose) { node.log('error:' + error); }
|
node.debug('error:' + error);
|
||||||
}
|
}
|
||||||
else if (node.oldrc === "false") {
|
else if (node.oldrc === "false") {
|
||||||
msg3 = RED.util.cloneMessage(msg);
|
msg3 = RED.util.cloneMessage(msg);
|
||||||
|
@ -288,7 +288,7 @@ module.exports = function(RED) {
|
|||||||
//TODO: delete msg.responseTopic - to prevent it being resent?
|
//TODO: delete msg.responseTopic - to prevent it being resent?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
topicOK = topicOK && !/[\+#]/.test(msg.topic);
|
topicOK = topicOK && !/[\+#\b\f\n\r\t\v\0]/.test(msg.topic);
|
||||||
|
|
||||||
if (topicOK) {
|
if (topicOK) {
|
||||||
node.brokerConn.publish(msg, done); // send the message
|
node.brokerConn.publish(msg, done); // send the message
|
||||||
@ -730,12 +730,18 @@ module.exports = function(RED) {
|
|||||||
node.client.on("reconnect", function() {
|
node.client.on("reconnect", function() {
|
||||||
setStatusConnecting(node, true);
|
setStatusConnecting(node, true);
|
||||||
});
|
});
|
||||||
//TODO: what to do with this event? Anything? Necessary?
|
//Broker Disconnect - V5 event
|
||||||
node.client.on("disconnect", function(packet) {
|
node.client.on("disconnect", function(packet) {
|
||||||
//Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
|
//Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
|
||||||
var rc = packet && packet.properties && packet.properties.reasonString;
|
const rc = (packet && packet.properties && packet.reasonCode) || packet.reasonCode;
|
||||||
var rc = packet && packet.properties && packet.reasonCode;
|
const rs = packet && packet.properties && packet.properties.reasonString || "";
|
||||||
//TODO: If keeping this event, do we use these? log these?
|
const details = {
|
||||||
|
broker: (node.clientid?node.clientid+"@":"")+node.brokerurl,
|
||||||
|
reasonCode: rc,
|
||||||
|
reasonString: rs
|
||||||
|
}
|
||||||
|
node.log(RED._("mqtt.state.broker-disconnected", details));
|
||||||
|
setStatusDisconnected(node, true);
|
||||||
});
|
});
|
||||||
// Register disconnect handlers
|
// Register disconnect handlers
|
||||||
node.client.on('close', function () {
|
node.client.on('close', function () {
|
||||||
|
@ -264,7 +264,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
if (opts.headers.hasOwnProperty('cookie')) {
|
if (opts.headers.hasOwnProperty('cookie')) {
|
||||||
var cookies = cookie.parse(opts.headers.cookie, {decode:String});
|
var cookies = cookie.parse(opts.headers.cookie, {decode:String});
|
||||||
for (var name in cookies) {
|
for (var name in cookies) {
|
||||||
opts.cookieJar.setCookie(cookie.serialize(name, cookies[name], {encode:String}), url, {ignoreError: true});
|
opts.cookieJar.setCookieSync(cookie.serialize(name, cookies[name], {encode:String}), url, {ignoreError: true});
|
||||||
}
|
}
|
||||||
delete opts.headers.cookie;
|
delete opts.headers.cookie;
|
||||||
}
|
}
|
||||||
@ -277,13 +277,13 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
} else if (typeof msg.cookies[name] === 'object') {
|
} else if (typeof msg.cookies[name] === 'object') {
|
||||||
if(msg.cookies[name].encode === false){
|
if(msg.cookies[name].encode === false){
|
||||||
// If the encode option is false, the value is not encoded.
|
// If the encode option is false, the value is not encoded.
|
||||||
opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value, {encode: String}), url, {ignoreError: true});
|
opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name].value, {encode: String}), url, {ignoreError: true});
|
||||||
} else {
|
} else {
|
||||||
// The value is encoded by encodeURIComponent().
|
// The value is encoded by encodeURIComponent().
|
||||||
opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value), url, {ignoreError: true});
|
opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name].value), url, {ignoreError: true});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true});
|
opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,8 @@
|
|||||||
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
||||||
tls: {type:"tls-config",required: false},
|
tls: {type:"tls-config",required: false},
|
||||||
wholemsg: {value:"false"},
|
wholemsg: {value:"false"},
|
||||||
hb: {value: "", validate: RED.validators.number(/*blank allowed*/true) }
|
hb: {value: "", validate: RED.validators.number(/*blank allowed*/true) },
|
||||||
|
subprotocol: {value:"",required: false}
|
||||||
},
|
},
|
||||||
inputs:0,
|
inputs:0,
|
||||||
outputs:0,
|
outputs:0,
|
||||||
@ -265,7 +266,10 @@
|
|||||||
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
|
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
|
||||||
<input type="text" id="node-config-input-tls">
|
<input type="text" id="node-config-input-tls">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-subprotocol"><i class="fa fa-tag"></i> <span data-i18n="websocket.label.subprotocol"></span></label>
|
||||||
|
<input type="text" id="node-config-input-subprotocol">
|
||||||
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
|
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
|
||||||
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
|
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
|
||||||
|
@ -46,6 +46,12 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
// Store local copies of the node configuration (as defined in the .html)
|
// Store local copies of the node configuration (as defined in the .html)
|
||||||
node.path = n.path;
|
node.path = n.path;
|
||||||
|
if (typeof n.subprotocol === "string") {
|
||||||
|
// Split the string on comma and trim each result
|
||||||
|
node.subprotocol = n.subprotocol.split(",").map(v => v.trim())
|
||||||
|
} else {
|
||||||
|
node.subprotocol = [];
|
||||||
|
}
|
||||||
node.wholemsg = (n.wholemsg === "true");
|
node.wholemsg = (n.wholemsg === "true");
|
||||||
|
|
||||||
node._inputNodes = []; // collection of nodes that want to receive events
|
node._inputNodes = []; // collection of nodes that want to receive events
|
||||||
@ -92,7 +98,7 @@ module.exports = function(RED) {
|
|||||||
tlsNode.addTLSOptions(options);
|
tlsNode.addTLSOptions(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var socket = new ws(node.path,options);
|
var socket = new ws(node.path,node.subprotocol,options);
|
||||||
socket.setMaxListeners(0);
|
socket.setMaxListeners(0);
|
||||||
node.server = socket; // keep for closing
|
node.server = socket; // keep for closing
|
||||||
handleConnection(socket);
|
handleConnection(socket);
|
||||||
|
@ -23,9 +23,17 @@
|
|||||||
</select>
|
</select>
|
||||||
<span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width:65px">
|
<span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width:65px">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
|
<div class="form-row hidden" id="node-input-host-row" style="padding-left:110px;">
|
||||||
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
|
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row" id="node-input-tls-enable">
|
||||||
|
<label> </label>
|
||||||
|
<input type="checkbox" id="node-input-usetls" style="display: inline-block; width:auto; vertical-align:top;">
|
||||||
|
<label for="node-input-usetls" style="width:auto" data-i18n="httpin.use-tls"></label>
|
||||||
|
<div id="node-row-tls" class="hide">
|
||||||
|
<label style="width:auto; margin-left:20px; margin-right:10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
|
<label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
|
||||||
@ -42,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="node-row-newline" class="form-row hidden" style="padding-left:110px;">
|
<div id="node-row-newline" class="form-row hidden" style="padding-left:110px;">
|
||||||
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;">
|
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@ -58,17 +66,18 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
RED.nodes.registerType('tcp in',{
|
RED.nodes.registerType('tcp in',{
|
||||||
category: 'network',
|
category: 'network',
|
||||||
color:"Silver",
|
color: "Silver",
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
server: {value:"server",required:true},
|
server: {value:"server", required:true},
|
||||||
host: {value:"",validate:function(v) { return (this.server == "server")||v.length > 0;} },
|
host: {value:"", validate:function(v) { return (this.server == "server")||v.length > 0;} },
|
||||||
port: {value:"",required:true,validate:RED.validators.number()},
|
port: {value:"", required:true, validate:RED.validators.number()},
|
||||||
datamode:{value:"stream"},
|
datamode:{value:"stream"},
|
||||||
datatype:{value:"buffer"},
|
datatype:{value:"buffer"},
|
||||||
newline:{value:""},
|
newline:{value:""},
|
||||||
topic: {value:""},
|
topic: {value:""},
|
||||||
base64: {/*deprecated*/ value:false,required:true}
|
base64: {/*deprecated*/ value:false, required:true},
|
||||||
|
tls: {type:"tls-config", value:'', required:false}
|
||||||
},
|
},
|
||||||
inputs:0,
|
inputs:0,
|
||||||
outputs:1,
|
outputs:1,
|
||||||
@ -77,7 +86,7 @@
|
|||||||
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
|
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name ? "node_label_italic" : "";
|
||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
var updateOptions = function() {
|
var updateOptions = function() {
|
||||||
@ -103,6 +112,27 @@
|
|||||||
$("#node-input-server").change(updateOptions);
|
$("#node-input-server").change(updateOptions);
|
||||||
$("#node-input-datatype").change(updateOptions);
|
$("#node-input-datatype").change(updateOptions);
|
||||||
$("#node-input-datamode").change(updateOptions);
|
$("#node-input-datamode").change(updateOptions);
|
||||||
|
function updateTLSOptions() {
|
||||||
|
if ($("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-row-tls").show();
|
||||||
|
} else {
|
||||||
|
$("#node-row-tls").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.tls) {
|
||||||
|
$('#node-input-usetls').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('#node-input-usetls').prop('checked', false);
|
||||||
|
}
|
||||||
|
updateTLSOptions();
|
||||||
|
$("#node-input-usetls").on("click",function() {
|
||||||
|
updateTLSOptions();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
if (!$("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-input-tls").val("_ADD_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -123,6 +153,15 @@
|
|||||||
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" style="width: 60%;">
|
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" style="width: 60%;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row" id="node-input-tls-enable">
|
||||||
|
<label> </label>
|
||||||
|
<input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
|
<label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label>
|
||||||
|
<div id="node-row-tls" class="hide">
|
||||||
|
<label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row hidden" id="node-input-end-row">
|
<div class="form-row hidden" id="node-input-end-row">
|
||||||
<label> </label>
|
<label> </label>
|
||||||
<input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;">
|
<input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
@ -144,14 +183,15 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
RED.nodes.registerType('tcp out',{
|
RED.nodes.registerType('tcp out',{
|
||||||
category: 'network',
|
category: 'network',
|
||||||
color:"Silver",
|
color: "Silver",
|
||||||
defaults: {
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} },
|
host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} },
|
||||||
port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v); } },
|
port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v); } },
|
||||||
beserver: {value:"client",required:true},
|
beserver: {value:"client", required:true},
|
||||||
base64: {value:false,required:true},
|
base64: {value:false, required:true},
|
||||||
end: {value:false,required:true},
|
end: {value:false, required:true},
|
||||||
name: {value:""}
|
tls: {type:"tls-config", value:'', required:false}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:0,
|
outputs:0,
|
||||||
@ -170,18 +210,42 @@
|
|||||||
$("#node-input-port-row").hide();
|
$("#node-input-port-row").hide();
|
||||||
$("#node-input-host-row").hide();
|
$("#node-input-host-row").hide();
|
||||||
$("#node-input-end-row").hide();
|
$("#node-input-end-row").hide();
|
||||||
|
$("#node-input-tls-enable").hide();
|
||||||
} else if (sockettype == "client"){
|
} else if (sockettype == "client"){
|
||||||
$("#node-input-port-row").show();
|
$("#node-input-port-row").show();
|
||||||
$("#node-input-host-row").show();
|
$("#node-input-host-row").show();
|
||||||
$("#node-input-end-row").show();
|
$("#node-input-end-row").show();
|
||||||
|
$("#node-input-tls-enable").show();
|
||||||
} else {
|
} else {
|
||||||
$("#node-input-port-row").show();
|
$("#node-input-port-row").show();
|
||||||
$("#node-input-host-row").hide();
|
$("#node-input-host-row").hide();
|
||||||
$("#node-input-end-row").show();
|
$("#node-input-end-row").show();
|
||||||
|
$("#node-input-tls-enable").show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
updateOptions();
|
updateOptions();
|
||||||
$("#node-input-beserver").change(updateOptions);
|
$("#node-input-beserver").change(updateOptions);
|
||||||
|
function updateTLSOptions() {
|
||||||
|
if ($("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-row-tls").show();
|
||||||
|
} else {
|
||||||
|
$("#node-row-tls").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.tls) {
|
||||||
|
$('#node-input-usetls').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('#node-input-usetls').prop('checked', false);
|
||||||
|
}
|
||||||
|
updateTLSOptions();
|
||||||
|
$("#node-input-usetls").on("click",function() {
|
||||||
|
updateTLSOptions();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
if (!$("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-input-tls").val("_ADD_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -194,15 +258,23 @@
|
|||||||
<span data-i18n="tcpin.label.port"></span>
|
<span data-i18n="tcpin.label.port"></span>
|
||||||
<input type="text" id="node-input-port" style="width:60px">
|
<input type="text" id="node-input-port" style="width:60px">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row" id="node-input-tls-enable">
|
||||||
|
<label> </label>
|
||||||
|
<input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
|
<label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label>
|
||||||
|
<div id="node-row-tls" class="hide">
|
||||||
|
<label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-out"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label>
|
<label for="node-input-ret"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label>
|
||||||
<select type="text" id="node-input-ret" style="width:54%;">
|
<select type="text" id="node-input-ret" style="width:54%;">
|
||||||
<option value="buffer" data-i18n="tcpin.output.buffer"></option>
|
<option value="buffer" data-i18n="tcpin.output.buffer"></option>
|
||||||
<option value="string" data-i18n="tcpin.output.string"></option>
|
<option value="string" data-i18n="tcpin.output.string"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-out"> </label>
|
<label for="node-input-out"><i class="fa fa-sign-out fa-rotate-90"></i> <span data-i18n="tcpin.label.close"></span></label>
|
||||||
<select type="text" id="node-input-out" style="width:54%;">
|
<select type="text" id="node-input-out" style="width:54%;">
|
||||||
<option value="time" data-i18n="tcpin.return.timeout"></option>
|
<option value="time" data-i18n="tcpin.return.timeout"></option>
|
||||||
<option value="char" data-i18n="tcpin.return.character"></option>
|
<option value="char" data-i18n="tcpin.return.character"></option>
|
||||||
@ -213,6 +285,9 @@
|
|||||||
<input type="text" id="node-input-splitc" style="width:50px;">
|
<input type="text" id="node-input-splitc" style="width:50px;">
|
||||||
<span id="node-units"></span>
|
<span id="node-units"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="node-row-newline" class="form-row hidden" style="padding-left:162px;">
|
||||||
|
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional">
|
||||||
|
</div>
|
||||||
<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" data-i18n="[placeholder]common.label.name">
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||||
@ -222,14 +297,16 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
RED.nodes.registerType('tcp request',{
|
RED.nodes.registerType('tcp request',{
|
||||||
category: 'network',
|
category: 'network',
|
||||||
color:"Silver",
|
color: "Silver",
|
||||||
defaults: {
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
server: {value:""},
|
server: {value:""},
|
||||||
port: {value:"",validate:RED.validators.regex(/^(\d*|)$/)},
|
port: {value:"", validate:RED.validators.regex(/^(\d*|)$/)},
|
||||||
out: {value:"time",required:true},
|
out: {value:"time", required:true},
|
||||||
ret: {value:"buffer"},
|
ret: {value:"buffer"},
|
||||||
splitc: {value:"0",required:true},
|
splitc: {value:"0", required:true},
|
||||||
name: {value:""}
|
newline: {value:""},
|
||||||
|
tls: {type:"tls-config", value:'', required:false}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:1,
|
outputs:1,
|
||||||
@ -246,6 +323,14 @@
|
|||||||
$("#node-input-ret").val("buffer");
|
$("#node-input-ret").val("buffer");
|
||||||
this.ret = "buffer";
|
this.ret = "buffer";
|
||||||
}
|
}
|
||||||
|
$("#node-input-ret").on("change", function() {
|
||||||
|
if ($("#node-input-ret").val() === "string" && $("#node-input-out").val() === "sit") { $("#node-row-newline").show(); }
|
||||||
|
else { $("#node-row-newline").hide(); }
|
||||||
|
});
|
||||||
|
$("#node-input-out").on("change", function() {
|
||||||
|
if ($("#node-input-ret").val() === "string" && $("#node-input-out").val() === "sit") { $("#node-row-newline").show(); }
|
||||||
|
else { $("#node-row-newline").hide(); }
|
||||||
|
});
|
||||||
$("#node-input-out").on('focus', function () { previous = this.value; }).on("change", function() {
|
$("#node-input-out").on('focus', function () { previous = this.value; }).on("change", function() {
|
||||||
$("#node-input-splitc").show();
|
$("#node-input-splitc").show();
|
||||||
if (previous === null) { previous = $("#node-input-out").val(); }
|
if (previous === null) { previous = $("#node-input-out").val(); }
|
||||||
@ -272,6 +357,27 @@
|
|||||||
$("#node-input-splitc").hide();
|
$("#node-input-splitc").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
function updateTLSOptions() {
|
||||||
|
if ($("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-row-tls").show();
|
||||||
|
} else {
|
||||||
|
$("#node-row-tls").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.tls) {
|
||||||
|
$('#node-input-usetls').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('#node-input-usetls').prop('checked', false);
|
||||||
|
}
|
||||||
|
updateTLSOptions();
|
||||||
|
$("#node-input-usetls").on("click",function() {
|
||||||
|
updateTLSOptions();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
if (!$("#node-input-usetls").is(':checked')) {
|
||||||
|
$("#node-input-tls").val("_ADD_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,13 +16,46 @@
|
|||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var reconnectTime = RED.settings.socketReconnectTime||10000;
|
let reconnectTime = RED.settings.socketReconnectTime || 10000;
|
||||||
var socketTimeout = RED.settings.socketTimeout||null;
|
let socketTimeout = RED.settings.socketTimeout || null;
|
||||||
const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000;
|
const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000;
|
||||||
const Denque = require('denque');
|
const Denque = require('denque');
|
||||||
var net = require('net');
|
const net = require('net');
|
||||||
|
const tls = require('tls');
|
||||||
|
|
||||||
var connectionPool = {};
|
let connectionPool = {};
|
||||||
|
|
||||||
|
function normalizeConnectArgs(listArgs) {
|
||||||
|
const args = net._normalizeArgs(listArgs);
|
||||||
|
const options = args[0];
|
||||||
|
const cb = args[1];
|
||||||
|
|
||||||
|
// If args[0] was options, then normalize dealt with it.
|
||||||
|
// If args[0] is port, or args[0], args[1] is host, port, we need to
|
||||||
|
// find the options and merge them in, normalize's options has only
|
||||||
|
// the host/port/path args that it knows about, not the tls options.
|
||||||
|
// This means that options.host overrides a host arg.
|
||||||
|
if (listArgs[1] !== null && typeof listArgs[1] === 'object') {
|
||||||
|
ObjectAssign(options, listArgs[1]);
|
||||||
|
} else if (listArgs[2] !== null && typeof listArgs[2] === 'object') {
|
||||||
|
ObjectAssign(options, listArgs[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb ? [options, cb] : [options];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllowUnauthorized() {
|
||||||
|
const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0';
|
||||||
|
|
||||||
|
if (allowUnauthorized) {
|
||||||
|
process.emitWarning(
|
||||||
|
'Setting the NODE_TLS_REJECT_UNAUTHORIZED ' +
|
||||||
|
'environment variable to \'0\' makes TLS connections ' +
|
||||||
|
'and HTTPS requests insecure by disabling ' +
|
||||||
|
'certificate verification.');
|
||||||
|
}
|
||||||
|
return allowUnauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue `item` in `queue`
|
* Enqueue `item` in `queue`
|
||||||
@ -53,13 +86,14 @@ module.exports = function(RED) {
|
|||||||
this.topic = n.topic;
|
this.topic = n.topic;
|
||||||
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
|
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
|
||||||
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
|
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
|
||||||
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r");
|
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
|
||||||
this.base64 = n.base64;
|
this.base64 = n.base64;
|
||||||
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
|
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
|
||||||
this.closing = false;
|
this.closing = false;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
var node = this;
|
var node = this;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); }
|
||||||
|
|
||||||
if (!node.server) {
|
if (!node.server) {
|
||||||
var buffer = null;
|
var buffer = null;
|
||||||
@ -70,13 +104,25 @@ module.exports = function(RED) {
|
|||||||
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
|
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
|
||||||
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
|
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
|
||||||
var id = RED.util.generateId();
|
var id = RED.util.generateId();
|
||||||
client = net.connect(node.port, node.host, function() {
|
var connOpts = {host: node.host};
|
||||||
buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
|
if (n.tls) {
|
||||||
node.connected = true;
|
var connOpts = tlsNode.addTLSOptions({host: node.host});
|
||||||
node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
|
client = tls.connect(node.port, connOpts, function() {
|
||||||
node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}});
|
buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
|
||||||
});
|
node.connected = true;
|
||||||
client.setKeepAlive(true,120000);
|
node.log(RED._("status.connected", {host: node.host, port: node.port}));
|
||||||
|
node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
client = net.connect(node.port, node.host, function() {
|
||||||
|
buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
|
||||||
|
node.connected = true;
|
||||||
|
node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
|
||||||
|
node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
client.setKeepAlive(true, 120000);
|
||||||
connectionPool[id] = client;
|
connectionPool[id] = client;
|
||||||
|
|
||||||
client.on('data', function (data) {
|
client.on('data', function (data) {
|
||||||
@ -89,7 +135,7 @@ module.exports = function(RED) {
|
|||||||
buffer = buffer+data;
|
buffer = buffer+data;
|
||||||
var parts = buffer.split(node.newline);
|
var parts = buffer.split(node.newline);
|
||||||
for (var i = 0; i<parts.length-1; i+=1) {
|
for (var i = 0; i<parts.length-1; i+=1) {
|
||||||
msg = {topic:node.topic, payload:parts[i]};
|
msg = {topic:node.topic, payload:parts[i] + node.newline.trimEnd()};
|
||||||
msg._session = {type:"tcp",id:id};
|
msg._session = {type:"tcp",id:id};
|
||||||
node.send(msg);
|
node.send(msg);
|
||||||
}
|
}
|
||||||
@ -150,7 +196,13 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var server = net.createServer(function (socket) {
|
let srv = net;
|
||||||
|
let connOpts;
|
||||||
|
if (n.tls) {
|
||||||
|
srv = tls;
|
||||||
|
connOpts = tlsNode.addTLSOptions({});
|
||||||
|
}
|
||||||
|
var server = srv.createServer(connOpts, function (socket) {
|
||||||
socket.setKeepAlive(true,120000);
|
socket.setKeepAlive(true,120000);
|
||||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||||
var id = RED.util.generateId();
|
var id = RED.util.generateId();
|
||||||
@ -177,7 +229,7 @@ module.exports = function(RED) {
|
|||||||
buffer = buffer+data;
|
buffer = buffer+data;
|
||||||
var parts = buffer.split(node.newline);
|
var parts = buffer.split(node.newline);
|
||||||
for (var i = 0; i<parts.length-1; i+=1) {
|
for (var i = 0; i<parts.length-1; i+=1) {
|
||||||
msg = {topic:node.topic, payload:parts[i], ip:socket.remoteAddress, port:socket.remotePort};
|
msg = {topic:node.topic, payload:parts[i] + node.newline.trimEnd(), ip:socket.remoteAddress, port:socket.remotePort};
|
||||||
msg._session = {type:"tcp",id:id};
|
msg._session = {type:"tcp",id:id};
|
||||||
node.send(msg);
|
node.send(msg);
|
||||||
}
|
}
|
||||||
@ -269,8 +321,9 @@ module.exports = function(RED) {
|
|||||||
this.closing = false;
|
this.closing = false;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
var node = this;
|
var node = this;
|
||||||
|
if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); }
|
||||||
|
|
||||||
if (!node.beserver||node.beserver=="client") {
|
if (!node.beserver || node.beserver == "client") {
|
||||||
var reconnectTimeout;
|
var reconnectTimeout;
|
||||||
var client = null;
|
var client = null;
|
||||||
var end = false;
|
var end = false;
|
||||||
@ -278,11 +331,24 @@ module.exports = function(RED) {
|
|||||||
var setupTcpClient = function() {
|
var setupTcpClient = function() {
|
||||||
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
|
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
|
||||||
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
|
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
|
||||||
client = net.connect(node.port, node.host, function() {
|
if (n.tls) {
|
||||||
node.connected = true;
|
// connOpts = tlsNode.addTLSOptions(connOpts);
|
||||||
node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
|
// client = tls.connect(connOpts, function() {
|
||||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
var connOpts = tlsNode.addTLSOptions({host: node.host});
|
||||||
});
|
client = tls.connect(node.port, connOpts, function() {
|
||||||
|
// buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
|
||||||
|
node.connected = true;
|
||||||
|
node.log(RED._("status.connected", {host: node.host, port: node.port}));
|
||||||
|
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
client = net.connect(node.port, node.host, function() {
|
||||||
|
node.connected = true;
|
||||||
|
node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
|
||||||
|
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||||
|
});
|
||||||
|
}
|
||||||
client.setKeepAlive(true,120000);
|
client.setKeepAlive(true,120000);
|
||||||
client.on('error', function (err) {
|
client.on('error', function (err) {
|
||||||
node.log(RED._("tcpin.errors.error",{error:err.toString()}));
|
node.log(RED._("tcpin.errors.error",{error:err.toString()}));
|
||||||
@ -368,7 +434,13 @@ module.exports = function(RED) {
|
|||||||
else {
|
else {
|
||||||
var connectedSockets = [];
|
var connectedSockets = [];
|
||||||
node.status({text:RED._("tcpin.status.connections",{count:0})});
|
node.status({text:RED._("tcpin.status.connections",{count:0})});
|
||||||
var server = net.createServer(function (socket) {
|
let srv = net;
|
||||||
|
let connOpts;
|
||||||
|
if (n.tls) {
|
||||||
|
srv = tls;
|
||||||
|
connOpts = tlsNode.addTLSOptions({});
|
||||||
|
}
|
||||||
|
var server = srv.createServer(connOpts, function (socket) {
|
||||||
socket.setKeepAlive(true,120000);
|
socket.setKeepAlive(true,120000);
|
||||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||||
node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort}));
|
node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||||
@ -445,7 +517,11 @@ module.exports = function(RED) {
|
|||||||
this.port = Number(n.port);
|
this.port = Number(n.port);
|
||||||
this.out = n.out;
|
this.out = n.out;
|
||||||
this.ret = n.ret || "buffer";
|
this.ret = n.ret || "buffer";
|
||||||
|
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
|
||||||
this.splitc = n.splitc;
|
this.splitc = n.splitc;
|
||||||
|
if (n.tls) {
|
||||||
|
var tlsNode = RED.nodes.getNode(n.tls);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.out === "immed") { this.splitc = -1; this.out = "time"; }
|
if (this.out === "immed") { this.splitc = -1; this.out = "time"; }
|
||||||
if (this.out !== "char") { this.splitc = Number(this.splitc); }
|
if (this.out !== "char") { this.splitc = Number(this.splitc); }
|
||||||
@ -500,12 +576,48 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully
|
else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully
|
||||||
|
|
||||||
clients[connection_id].client = net.Socket();
|
var connOpts = {host:host, port:port};
|
||||||
|
if (n.tls) {
|
||||||
|
connOpts = tlsNode.addTLSOptions(connOpts);
|
||||||
|
const allowUnauthorized = getAllowUnauthorized();
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
rejectUnauthorized: !allowUnauthorized,
|
||||||
|
ciphers: tls.DEFAULT_CIPHERS,
|
||||||
|
checkServerIdentity: tls.checkServerIdentity,
|
||||||
|
minDHSize: 1024,
|
||||||
|
...connOpts
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!options.keepAlive) { options.singleUse = true; }
|
||||||
|
|
||||||
|
const context = options.secureContext || tls.createSecureContext(options);
|
||||||
|
|
||||||
|
clients[connection_id].client = new tls.TLSSocket(options.socket, {
|
||||||
|
allowHalfOpen: options.allowHalfOpen,
|
||||||
|
pipe: !!options.path,
|
||||||
|
secureContext: context,
|
||||||
|
isServer: false,
|
||||||
|
requestCert: false, // true,
|
||||||
|
rejectUnauthorized: false, // options.rejectUnauthorized !== false,
|
||||||
|
session: options.session,
|
||||||
|
ALPNProtocols: options.ALPNProtocols,
|
||||||
|
requestOCSP: options.requestOCSP,
|
||||||
|
enableTrace: options.enableTrace,
|
||||||
|
pskCallback: options.pskCallback,
|
||||||
|
highWaterMark: options.highWaterMark,
|
||||||
|
onread: options.onread,
|
||||||
|
signal: options.signal,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
clients[connection_id].client = net.Socket();
|
||||||
|
}
|
||||||
if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);}
|
if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);}
|
||||||
|
|
||||||
if (host && port) {
|
if (host && port) {
|
||||||
clients[connection_id].connecting = true;
|
clients[connection_id].connecting = true;
|
||||||
clients[connection_id].client.connect(port, host, function() {
|
clients[connection_id].client.connect(connOpts, function() {
|
||||||
//node.log(RED._("tcpin.errors.client-connected"));
|
//node.log(RED._("tcpin.errors.client-connected"));
|
||||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||||
if (clients[connection_id] && clients[connection_id].client) {
|
if (clients[connection_id] && clients[connection_id].client) {
|
||||||
@ -528,17 +640,32 @@ module.exports = function(RED) {
|
|||||||
else {
|
else {
|
||||||
node.warn(RED._("tcpin.errors.no-host"));
|
node.warn(RED._("tcpin.errors.no-host"));
|
||||||
}
|
}
|
||||||
|
var chunk = "";
|
||||||
clients[connection_id].client.on('data', function(data) {
|
clients[connection_id].client.on('data', function(data) {
|
||||||
if (node.out === "sit") { // if we are staying connected just send the buffer
|
if (node.out === "sit") { // if we are staying connected just send the buffer
|
||||||
if (clients[connection_id]) {
|
if (clients[connection_id]) {
|
||||||
const msg = clients[connection_id].lastMsg || {};
|
const msg = clients[connection_id].lastMsg || {};
|
||||||
msg.payload = RED.util.cloneMessage(data);
|
msg.payload = RED.util.cloneMessage(data);
|
||||||
if (node.ret === "string") {
|
if (node.ret === "string") {
|
||||||
try { msg.payload = msg.payload.toString(); }
|
try {
|
||||||
catch(e) { node.error("Failed to create string", msg); }
|
if (node.newline && node.newline !== "" ) {
|
||||||
|
chunk += msg.payload.toString();
|
||||||
|
let parts = chunk.split(node.newline);
|
||||||
|
for (var p=0; p<parts.length-1; p+=1) {
|
||||||
|
let m = RED.util.cloneMessage(msg);
|
||||||
|
m.payload = parts[p] + node.newline.trimEnd();
|
||||||
|
nodeSend(m);
|
||||||
|
}
|
||||||
|
chunk = parts[parts.length-1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.payload = msg.payload.toString();
|
||||||
|
nodeSend(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) { node.error(RED._("tcpin.errors.bad-string"), msg); }
|
||||||
}
|
}
|
||||||
nodeSend(msg);
|
else { nodeSend(msg); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else if (node.splitc === 0) {
|
// else if (node.splitc === 0) {
|
||||||
@ -675,7 +802,13 @@ module.exports = function(RED) {
|
|||||||
//node.warn(RED._("tcpin.errors.connect-timeout"));
|
//node.warn(RED._("tcpin.errors.connect-timeout"));
|
||||||
if (clients[connection_id].client) {
|
if (clients[connection_id].client) {
|
||||||
clients[connection_id].connecting = true;
|
clients[connection_id].connecting = true;
|
||||||
clients[connection_id].client.connect(port, host, function() {
|
|
||||||
|
var connOpts = {host:host, port:port};
|
||||||
|
if (n.tls) {
|
||||||
|
connOpts = tlsNode.addTLSOptions(connOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
clients[connection_id].client.connect(connOpts, function() {
|
||||||
clients[connection_id].connected = true;
|
clients[connection_id].connected = true;
|
||||||
clients[connection_id].connecting = false;
|
clients[connection_id].connecting = false;
|
||||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||||
|
@ -49,7 +49,11 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
var value = RED.util.getMessageProperty(msg,node.property);
|
var value = RED.util.getMessageProperty(msg,node.property);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string" || Buffer.isBuffer(value)) {
|
||||||
|
// if (Buffer.isBuffer(value) && node.action !== "obj") {
|
||||||
|
// node.warn(RED._("json.errors.dropped")); done();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
if (node.action === "" || node.action === "obj") {
|
if (node.action === "" || node.action === "obj") {
|
||||||
try {
|
try {
|
||||||
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));
|
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));
|
||||||
|
@ -71,9 +71,7 @@ module.exports = function(RED) {
|
|||||||
node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg);
|
node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (RED.settings.verbose) {
|
node.debug(RED._("file.status.deletedfile",{file:filename}));
|
||||||
node.log(RED._("file.status.deletedfile",{file:filename}));
|
|
||||||
}
|
|
||||||
nodeSend(msg);
|
nodeSend(msg);
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
|
@ -499,7 +499,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"path": "Pfad",
|
"path": "Pfad",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "Subprotokoll"
|
||||||
},
|
},
|
||||||
"listenon": "Lauschen (listen on)",
|
"listenon": "Lauschen (listen on)",
|
||||||
"connectto": "Verbinden mit",
|
"connectto": "Verbinden mit",
|
||||||
|
@ -442,7 +442,8 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"connected": "Connected to broker: __broker__",
|
"connected": "Connected to broker: __broker__",
|
||||||
"disconnected": "Disconnected from broker: __broker__",
|
"disconnected": "Disconnected from broker: __broker__",
|
||||||
"connect-failed": "Connection failed to broker: __broker__"
|
"connect-failed": "Connection failed to broker: __broker__",
|
||||||
|
"broker-disconnected": "Broker __broker__ disconnected client: __reasonCode__ __reasonString__"
|
||||||
},
|
},
|
||||||
"retain": "Retain",
|
"retain": "Retain",
|
||||||
"output": {
|
"output": {
|
||||||
@ -530,7 +531,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "Subprotocol"
|
||||||
},
|
},
|
||||||
"listenon": "Listen on",
|
"listenon": "Listen on",
|
||||||
"connectto": "Connect to",
|
"connectto": "Connect to",
|
||||||
@ -579,7 +581,9 @@
|
|||||||
"server": "Server",
|
"server": "Server",
|
||||||
"return": "Return",
|
"return": "Return",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"chars": "chars"
|
"chars": "chars",
|
||||||
|
"close": "Close",
|
||||||
|
"optional": "(optional)"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"listen": "Listen on",
|
"listen": "Listen on",
|
||||||
@ -596,7 +600,7 @@
|
|||||||
"return": {
|
"return": {
|
||||||
"timeout": "after a fixed timeout of",
|
"timeout": "after a fixed timeout of",
|
||||||
"character": "when character received is",
|
"character": "when character received is",
|
||||||
"number": "a fixed number of chars",
|
"number": "after a fixed number of characters",
|
||||||
"never": "never - keep connection open",
|
"never": "never - keep connection open",
|
||||||
"immed": "immediately - don't wait for reply"
|
"immed": "immediately - don't wait for reply"
|
||||||
},
|
},
|
||||||
@ -616,11 +620,11 @@
|
|||||||
"timeout": "timeout closed socket port __port__",
|
"timeout": "timeout closed socket port __port__",
|
||||||
"cannot-listen": "unable to listen on port __port__, error: __error__",
|
"cannot-listen": "unable to listen on port __port__, error: __error__",
|
||||||
"error": "error: __error__",
|
"error": "error: __error__",
|
||||||
|
|
||||||
"socket-error": "socket error from __host__:__port__",
|
"socket-error": "socket error from __host__:__port__",
|
||||||
"no-host": "Host and/or port not set",
|
"no-host": "Host and/or port not set",
|
||||||
"connect-timeout": "connect timeout",
|
"connect-timeout": "connect timeout",
|
||||||
"connect-fail": "connect failed"
|
"connect-fail": "connect failed",
|
||||||
|
"bad-string": "failed to convert to string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"udp": {
|
"udp": {
|
||||||
|
@ -442,7 +442,8 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"connected": "ブローカへ接続しました: __broker__",
|
"connected": "ブローカへ接続しました: __broker__",
|
||||||
"disconnected": "ブローカから切断されました: __broker__",
|
"disconnected": "ブローカから切断されました: __broker__",
|
||||||
"connect-failed": "ブローカへの接続に失敗しました: __broker__"
|
"connect-failed": "ブローカへの接続に失敗しました: __broker__",
|
||||||
|
"broker-disconnected": "ブローカ __broker__ がクライアントを切断しました: __reasonCode__ __reasonString__"
|
||||||
},
|
},
|
||||||
"retain": "保持",
|
"retain": "保持",
|
||||||
"output": {
|
"output": {
|
||||||
@ -530,7 +531,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "種類",
|
"type": "種類",
|
||||||
"path": "パス",
|
"path": "パス",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "サブプロトコル"
|
||||||
},
|
},
|
||||||
"listenon": "待ち受け",
|
"listenon": "待ち受け",
|
||||||
"connectto": "接続",
|
"connectto": "接続",
|
||||||
@ -579,7 +581,9 @@
|
|||||||
"server": "サーバ",
|
"server": "サーバ",
|
||||||
"return": "戻り値",
|
"return": "戻り値",
|
||||||
"ms": "ミリ秒",
|
"ms": "ミリ秒",
|
||||||
"chars": "文字"
|
"chars": "文字",
|
||||||
|
"close": "終了",
|
||||||
|
"optional": "(任意)"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"listen": "待ち受け",
|
"listen": "待ち受け",
|
||||||
@ -618,7 +622,8 @@
|
|||||||
"socket-error": "__host__:__port__ にてソケットのエラーが生じました",
|
"socket-error": "__host__:__port__ にてソケットのエラーが生じました",
|
||||||
"no-host": "ホスト名またはポートが設定されていません",
|
"no-host": "ホスト名またはポートが設定されていません",
|
||||||
"connect-timeout": "接続がタイムアウトしました",
|
"connect-timeout": "接続がタイムアウトしました",
|
||||||
"connect-fail": "接続に失敗しました"
|
"connect-fail": "接続に失敗しました",
|
||||||
|
"bad-string": "文字列への変換に失敗しました"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"udp": {
|
"udp": {
|
||||||
|
@ -433,7 +433,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "종류",
|
"type": "종류",
|
||||||
"path": "패스",
|
"path": "패스",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "서브 프로토콜"
|
||||||
},
|
},
|
||||||
"listenon": "대기",
|
"listenon": "대기",
|
||||||
"connectto": "접속",
|
"connectto": "접속",
|
||||||
|
@ -461,7 +461,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
"path": "Путь",
|
"path": "Путь",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "Подпротокол"
|
||||||
},
|
},
|
||||||
"listenon": "Слушать на ...",
|
"listenon": "Слушать на ...",
|
||||||
"connectto": "Присоединиться к ...",
|
"connectto": "Присоединиться к ...",
|
||||||
|
@ -454,7 +454,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "类型",
|
"type": "类型",
|
||||||
"path": "路径",
|
"path": "路径",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "子协议"
|
||||||
},
|
},
|
||||||
"listenon": "监听",
|
"listenon": "监听",
|
||||||
"connectto": "连接",
|
"connectto": "连接",
|
||||||
|
@ -458,7 +458,8 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"type": "類型",
|
"type": "類型",
|
||||||
"path": "路徑",
|
"path": "路徑",
|
||||||
"url": "URL"
|
"url": "URL",
|
||||||
|
"subprotocol": "子协议"
|
||||||
},
|
},
|
||||||
"listenon": "監聽",
|
"listenon": "監聽",
|
||||||
"connectto": "連接",
|
"connectto": "連接",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/nodes",
|
"name": "@node-red/nodes",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "8.7.0",
|
"acorn": "8.7.0",
|
||||||
"acorn-walk": "8.2.0",
|
"acorn-walk": "8.2.0",
|
||||||
"ajv": "8.8.2",
|
"ajv": "8.9.0",
|
||||||
"body-parser": "1.19.1",
|
"body-parser": "1.19.1",
|
||||||
"cheerio": "1.0.0-rc.10",
|
"cheerio": "1.0.0-rc.10",
|
||||||
"content-type": "1.0.4",
|
"content-type": "1.0.4",
|
||||||
@ -37,13 +37,13 @@
|
|||||||
"js-yaml": "3.14.1",
|
"js-yaml": "3.14.1",
|
||||||
"media-typer": "1.1.0",
|
"media-typer": "1.1.0",
|
||||||
"mqtt": "4.3.4",
|
"mqtt": "4.3.4",
|
||||||
"multer": "1.4.3",
|
"multer": "1.4.4",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"on-headers": "1.0.2",
|
"on-headers": "1.0.2",
|
||||||
"raw-body": "2.4.2",
|
"raw-body": "2.4.2",
|
||||||
"tough-cookie": "4.0.0",
|
"tough-cookie": "4.0.0",
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
"ws": "7.5.1",
|
"ws": "7.5.6",
|
||||||
"xml2js": "0.4.23",
|
"xml2js": "0.4.23",
|
||||||
"iconv-lite": "0.6.3"
|
"iconv-lite": "0.6.3"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/registry",
|
"name": "@node-red/registry",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,11 +16,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "2.1.6",
|
"@node-red/util": "2.2.0",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"fs-extra": "10.0.0",
|
"fs-extra": "10.0.0",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"tar": "6.1.11",
|
"tar": "6.1.11",
|
||||||
"uglify-js": "3.14.5"
|
"uglify-js": "3.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
@ -99,6 +99,7 @@ var api = module.exports = {
|
|||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {User} opts.user - the user calling the api
|
* @param {User} opts.user - the user calling the api
|
||||||
* @param {String} opts.id - the id of the project to activate
|
* @param {String} opts.id - the id of the project to activate
|
||||||
|
* @param {boolean} opts.clearContext - whether to clear context
|
||||||
* @param {Object} opts.req - the request to log (optional)
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
* @return {Promise<Object>} - resolves when complete
|
* @return {Promise<Object>} - resolves when complete
|
||||||
* @memberof @node-red/runtime_projects
|
* @memberof @node-red/runtime_projects
|
||||||
@ -107,7 +108,7 @@ var api = module.exports = {
|
|||||||
var currentProject = runtime.storage.projects.getActiveProject(opts.user);
|
var currentProject = runtime.storage.projects.getActiveProject(opts.user);
|
||||||
runtime.log.audit({event: "projects.set",id:opts.id}, opts.req);
|
runtime.log.audit({event: "projects.set",id:opts.id}, opts.req);
|
||||||
if (!currentProject || opts.id !== currentProject.name) {
|
if (!currentProject || opts.id !== currentProject.name) {
|
||||||
return runtime.storage.projects.setActiveProject(opts.user, opts.id);
|
return runtime.storage.projects.setActiveProject(opts.user, opts.id, opts.clearContext);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -424,6 +424,17 @@ class Flow {
|
|||||||
*/
|
*/
|
||||||
getGroupEnvSetting(node, group, name) {
|
getGroupEnvSetting(node, group, name) {
|
||||||
if (group) {
|
if (group) {
|
||||||
|
if (name === "NR_GROUP_NAME") {
|
||||||
|
return [{
|
||||||
|
val: group.name
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
if (name === "NR_GROUP_ID") {
|
||||||
|
return [{
|
||||||
|
val: group.id
|
||||||
|
}, null];
|
||||||
|
}
|
||||||
|
|
||||||
if (group.credentials === undefined) {
|
if (group.credentials === undefined) {
|
||||||
group.credentials = credentials.get(group.id) || {};
|
group.credentials = credentials.get(group.id) || {};
|
||||||
}
|
}
|
||||||
@ -499,6 +510,12 @@ class Flow {
|
|||||||
*/
|
*/
|
||||||
getSetting(key) {
|
getSetting(key) {
|
||||||
const flow = this.flow;
|
const flow = this.flow;
|
||||||
|
if (key === "NR_FLOW_NAME") {
|
||||||
|
return flow.label;
|
||||||
|
}
|
||||||
|
if (key === "NR_FLOW_ID") {
|
||||||
|
return flow.id;
|
||||||
|
}
|
||||||
if (flow.credentials === undefined) {
|
if (flow.credentials === undefined) {
|
||||||
flow.credentials = credentials.get(flow.id) || {};
|
flow.credentials = credentials.get(flow.id) || {};
|
||||||
}
|
}
|
||||||
|
@ -371,6 +371,17 @@ class Subflow extends Flow {
|
|||||||
name = name.substring(8);
|
name = name.substring(8);
|
||||||
}
|
}
|
||||||
const node = this.subflowInstance;
|
const node = this.subflowInstance;
|
||||||
|
if (node) {
|
||||||
|
if (name === "NR_NODE_NAME") {
|
||||||
|
return node.name;
|
||||||
|
}
|
||||||
|
if (name === "NR_NODE_ID") {
|
||||||
|
return node.id;
|
||||||
|
}
|
||||||
|
if (name === "NR_NODE_PATH") {
|
||||||
|
return node._path;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (node.g) {
|
if (node.g) {
|
||||||
const group = this.getGroupNode(node.g);
|
const group = this.getGroupNode(node.g);
|
||||||
const [result, newName] = this.getGroupEnvSetting(node, group, name);
|
const [result, newName] = this.getGroupEnvSetting(node, group, name);
|
||||||
|
@ -85,6 +85,7 @@ function createNode(flow,config) {
|
|||||||
try {
|
try {
|
||||||
Object.defineProperty(conf,'_module', {value: typeRegistry.getNodeInfo(type), enumerable: false, writable: true })
|
Object.defineProperty(conf,'_module', {value: typeRegistry.getNodeInfo(type), enumerable: false, writable: true })
|
||||||
Object.defineProperty(conf,'_flow', {value: flow, enumerable: false, writable: true })
|
Object.defineProperty(conf,'_flow', {value: flow, enumerable: false, writable: true })
|
||||||
|
Object.defineProperty(conf,'_path', {value: `${flow.path}/${config._alias||config.id}`, enumerable: false, writable: true })
|
||||||
newNode = new nodeTypeConstructor(conf);
|
newNode = new nodeTypeConstructor(conf);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Log.log({
|
Log.log({
|
||||||
|
@ -62,6 +62,9 @@ function Node(n) {
|
|||||||
if (n._module) {
|
if (n._module) {
|
||||||
Object.defineProperty(this,'_module', {value: n._module, enumerable: false, writable: true })
|
Object.defineProperty(this,'_module', {value: n._module, enumerable: false, writable: true })
|
||||||
}
|
}
|
||||||
|
if (n._path) {
|
||||||
|
Object.defineProperty(this,'_path', {value: n._path, enumerable: false, writable: true })
|
||||||
|
}
|
||||||
this.updateWires(n.wires);
|
this.updateWires(n.wires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,30 +14,30 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
var clone = require("clone");
|
const clone = require("clone");
|
||||||
var log = require("@node-red/util").log;
|
const log = require("@node-red/util").log;
|
||||||
var util = require("@node-red/util").util;
|
const util = require("@node-red/util").util;
|
||||||
var memory = require("./memory");
|
const memory = require("./memory");
|
||||||
|
|
||||||
var settings;
|
let settings;
|
||||||
|
|
||||||
// A map of scope id to context instance
|
// A map of scope id to context instance
|
||||||
var contexts = {};
|
let contexts = {};
|
||||||
|
|
||||||
// A map of store name to instance
|
// A map of store name to instance
|
||||||
var stores = {};
|
let stores = {};
|
||||||
var storeList = [];
|
let storeList = [];
|
||||||
var defaultStore;
|
let defaultStore;
|
||||||
|
|
||||||
// Whether there context storage has been configured or left as default
|
// Whether there context storage has been configured or left as default
|
||||||
var hasConfiguredStore = false;
|
let hasConfiguredStore = false;
|
||||||
|
|
||||||
// Unknown Stores
|
// Unknown Stores
|
||||||
var unknownStores = {};
|
let unknownStores = {};
|
||||||
|
|
||||||
function logUnknownStore(name) {
|
function logUnknownStore(name) {
|
||||||
if (name) {
|
if (name) {
|
||||||
var count = unknownStores[name] || 0;
|
let count = unknownStores[name] || 0;
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
log.warn(log._("context.unknown-store", {name: name}));
|
log.warn(log._("context.unknown-store", {name: name}));
|
||||||
count++;
|
count++;
|
||||||
@ -52,8 +52,8 @@ function init(_settings) {
|
|||||||
stores = {};
|
stores = {};
|
||||||
storeList = [];
|
storeList = [];
|
||||||
hasConfiguredStore = false;
|
hasConfiguredStore = false;
|
||||||
var seed = settings.functionGlobalContext || {};
|
initialiseGlobalContext();
|
||||||
contexts['global'] = createContext("global",seed);
|
|
||||||
// create a default memory store - used by the unit tests that skip the full
|
// create a default memory store - used by the unit tests that skip the full
|
||||||
// `load()` initialisation sequence.
|
// `load()` initialisation sequence.
|
||||||
// If the user has any stores configured, this will be disgarded
|
// If the user has any stores configured, this will be disgarded
|
||||||
@ -61,6 +61,11 @@ function init(_settings) {
|
|||||||
defaultStore = "memory";
|
defaultStore = "memory";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initialiseGlobalContext() {
|
||||||
|
const seed = settings.functionGlobalContext || {};
|
||||||
|
contexts['global'] = createContext("global",seed);
|
||||||
|
}
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
return new Promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
// load & init plugins in settings.contextStorage
|
// load & init plugins in settings.contextStorage
|
||||||
@ -233,12 +238,15 @@ function validateContextKey(key) {
|
|||||||
|
|
||||||
function createContext(id,seed,parent) {
|
function createContext(id,seed,parent) {
|
||||||
// Seed is only set for global context - sourced from functionGlobalContext
|
// Seed is only set for global context - sourced from functionGlobalContext
|
||||||
var scope = id;
|
const scope = id;
|
||||||
var obj = seed || {};
|
const obj = {};
|
||||||
var seedKeys;
|
let seedKeys;
|
||||||
var insertSeedValues;
|
let insertSeedValues;
|
||||||
if (seed) {
|
if (seed) {
|
||||||
seedKeys = Object.keys(seed);
|
seedKeys = Object.keys(seed);
|
||||||
|
seedKeys.forEach(key => {
|
||||||
|
obj[key] = seed[key];
|
||||||
|
})
|
||||||
insertSeedValues = function(keys,values) {
|
insertSeedValues = function(keys,values) {
|
||||||
if (!Array.isArray(keys)) {
|
if (!Array.isArray(keys)) {
|
||||||
if (values[0] === undefined) {
|
if (values[0] === undefined) {
|
||||||
@ -540,8 +548,28 @@ function getContext(nodeId, flowId) {
|
|||||||
return newContext;
|
return newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the context of the given node/flow/global
|
||||||
|
*
|
||||||
|
* If the user has configured a context store, this
|
||||||
|
* will no-op a request to delete node/flow context.
|
||||||
|
*/
|
||||||
function deleteContext(id,flowId) {
|
function deleteContext(id,flowId) {
|
||||||
if(!hasConfiguredStore){
|
if (id === "global") {
|
||||||
|
// 1. delete global from all configured stores
|
||||||
|
var promises = [];
|
||||||
|
for(var plugin in stores){
|
||||||
|
if(stores.hasOwnProperty(plugin)){
|
||||||
|
promises.push(stores[plugin].delete('global'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.all(promises).then(function() {
|
||||||
|
// 2. delete global context
|
||||||
|
delete contexts['global'];
|
||||||
|
// 3. reinitialise global context
|
||||||
|
initialiseGlobalContext();
|
||||||
|
})
|
||||||
|
} else if (!hasConfiguredStore) {
|
||||||
// only delete context if there's no configured storage.
|
// only delete context if there's no configured storage.
|
||||||
var contextId = id;
|
var contextId = id;
|
||||||
if (flowId) {
|
if (flowId) {
|
||||||
@ -549,12 +577,19 @@ function deleteContext(id,flowId) {
|
|||||||
}
|
}
|
||||||
delete contexts[contextId];
|
delete contexts[contextId];
|
||||||
return stores["_"].delete(contextId);
|
return stores["_"].delete(contextId);
|
||||||
}else{
|
} else {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete any contexts that are no longer in use
|
||||||
|
* @param flowConfig object includes allNodes as object of id->node
|
||||||
|
*
|
||||||
|
* If flowConfig is undefined, all flow/node contexts will be removed
|
||||||
|
**/
|
||||||
function clean(flowConfig) {
|
function clean(flowConfig) {
|
||||||
|
flowConfig = flowConfig || { allNodes: {} };
|
||||||
var promises = [];
|
var promises = [];
|
||||||
for(var plugin in stores){
|
for(var plugin in stores){
|
||||||
if(stores.hasOwnProperty(plugin)){
|
if(stores.hasOwnProperty(plugin)){
|
||||||
@ -572,6 +607,16 @@ function clean(flowConfig) {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all contexts, including global and reinitialises global to
|
||||||
|
* initial state.
|
||||||
|
*/
|
||||||
|
function clear() {
|
||||||
|
return clean().then(function() {
|
||||||
|
return deleteContext('global')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
for(var plugin in stores){
|
for(var plugin in stores){
|
||||||
@ -594,5 +639,6 @@ module.exports = {
|
|||||||
getFlowContext:getFlowContext,
|
getFlowContext:getFlowContext,
|
||||||
delete: deleteContext,
|
delete: deleteContext,
|
||||||
clean: clean,
|
clean: clean,
|
||||||
|
clear: clear,
|
||||||
close: close
|
close: close
|
||||||
};
|
};
|
||||||
|
@ -206,6 +206,7 @@ module.exports = {
|
|||||||
eachNode: flows.eachNode,
|
eachNode: flows.eachNode,
|
||||||
getContext: context.get,
|
getContext: context.get,
|
||||||
|
|
||||||
|
clearContext: context.clear,
|
||||||
|
|
||||||
installerEnabled: registry.installerEnabled,
|
installerEnabled: registry.installerEnabled,
|
||||||
installModule: installModule,
|
installModule: installModule,
|
||||||
|
@ -377,8 +377,17 @@ function getActiveProject(user) {
|
|||||||
return activeProject;
|
return activeProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadActiveProject(action) {
|
function reloadActiveProject(action, clearContext) {
|
||||||
|
// Stop the current flows
|
||||||
return runtime.nodes.stopFlows().then(function() {
|
return runtime.nodes.stopFlows().then(function() {
|
||||||
|
if (clearContext) {
|
||||||
|
// Reset context to remove any old values
|
||||||
|
return runtime.nodes.clearContext()
|
||||||
|
} else {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}).then(function() {
|
||||||
|
// Load the new project flows and start them
|
||||||
return runtime.nodes.loadFlows(true).then(function() {
|
return runtime.nodes.loadFlows(true).then(function() {
|
||||||
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
|
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
@ -387,6 +396,9 @@ function reloadActiveProject(action) {
|
|||||||
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
|
events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}});
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function createProject(user, metadata) {
|
function createProject(user, metadata) {
|
||||||
@ -424,7 +436,7 @@ function createProject(user, metadata) {
|
|||||||
return getProject(user, metadata.name);
|
return getProject(user, metadata.name);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function setActiveProject(user, projectName) {
|
function setActiveProject(user, projectName, clearContext) {
|
||||||
return loadProject(projectName).then(function(project) {
|
return loadProject(projectName).then(function(project) {
|
||||||
var globalProjectSettings = settings.get("projects")||{};
|
var globalProjectSettings = settings.get("projects")||{};
|
||||||
globalProjectSettings.activeProject = project.name;
|
globalProjectSettings.activeProject = project.name;
|
||||||
@ -434,7 +446,7 @@ function setActiveProject(user, projectName) {
|
|||||||
// console.log("Updated file targets to");
|
// console.log("Updated file targets to");
|
||||||
// console.log(flowsFullPath)
|
// console.log(flowsFullPath)
|
||||||
// console.log(credentialsFile)
|
// console.log(credentialsFile)
|
||||||
return reloadActiveProject("loaded");
|
return reloadActiveProject("loaded", clearContext);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/runtime",
|
"name": "@node-red/runtime",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/registry": "2.1.6",
|
"@node-red/registry": "2.2.0",
|
||||||
"@node-red/util": "2.1.6",
|
"@node-red/util": "2.2.0",
|
||||||
"async-mutex": "0.3.2",
|
"async-mutex": "0.3.2",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"express": "4.17.2",
|
"express": "4.17.2",
|
||||||
|
2
packages/node_modules/@node-red/util/LICENSE
vendored
2
packages/node_modules/@node-red/util/LICENSE
vendored
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
11
packages/node_modules/@node-red/util/lib/util.js
vendored
11
packages/node_modules/@node-red/util/lib/util.js
vendored
@ -522,6 +522,17 @@ function setObjectProperty(msg,prop,value,createMissing) {
|
|||||||
* @return {String} value of env var
|
* @return {String} value of env var
|
||||||
*/
|
*/
|
||||||
function getSetting(node, name, flow_) {
|
function getSetting(node, name, flow_) {
|
||||||
|
if (node) {
|
||||||
|
if (name === "NR_NODE_NAME") {
|
||||||
|
return node.name;
|
||||||
|
}
|
||||||
|
if (name === "NR_NODE_ID") {
|
||||||
|
return node.id;
|
||||||
|
}
|
||||||
|
if (name === "NR_NODE_PATH") {
|
||||||
|
return node._path;
|
||||||
|
}
|
||||||
|
}
|
||||||
var flow = (flow_ ? flow_ : (node ? node._flow : null));
|
var flow = (flow_ ? flow_ : (node ? node._flow : null));
|
||||||
if (flow) {
|
if (flow) {
|
||||||
if (node && node.g) {
|
if (node && node.g) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/util",
|
"name": "@node-red/util",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -16,7 +16,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs-extra": "10.0.0",
|
"fs-extra": "10.0.0",
|
||||||
"i18next": "21.6.6",
|
"i18next": "21.6.10",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"jsonata": "1.8.5",
|
"jsonata": "1.8.5",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
2
packages/node_modules/node-red/LICENSE
vendored
2
packages/node_modules/node-red/LICENSE
vendored
@ -1,4 +1,4 @@
|
|||||||
Copyright JS Foundation and other contributors, http://js.foundation
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
20
packages/node_modules/node-red/README.md
vendored
20
packages/node_modules/node-red/README.md
vendored
@ -2,12 +2,9 @@
|
|||||||
|
|
||||||
http://nodered.org
|
http://nodered.org
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/node-red/node-red.svg)](https://travis-ci.org/node-red/node-red)
|
Low-code programming for event-driven applications.
|
||||||
[![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master)
|
|
||||||
|
|
||||||
A visual tool for wiring the Internet of Things.
|
![Node-RED: Low-code programming for event-driven applications.](http://nodered.org/images/node-red-screenshot.png)
|
||||||
|
|
||||||
![Node-RED: A visual tool for wiring the Internet of Things](http://nodered.org/images/node-red-screenshot.png)
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -39,15 +36,14 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena
|
|||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
Node-RED is a project of the [JS Foundation](http://js.foundation).
|
Node-RED is a project of the [OpenJS Foundation](http://openjsf.org).
|
||||||
|
|
||||||
It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
|
|
||||||
|
|
||||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
|
||||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
|
||||||
|
|
||||||
|
It is maintained by:
|
||||||
|
|
||||||
|
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||||
|
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||||
|
* And many others...
|
||||||
|
|
||||||
## Copyright and license
|
## Copyright and license
|
||||||
|
|
||||||
Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE).
|
Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).
|
||||||
|
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "2.1.6",
|
"version": "2.2.0",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -31,10 +31,10 @@
|
|||||||
"flow"
|
"flow"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/editor-api": "2.1.6",
|
"@node-red/editor-api": "2.2.0",
|
||||||
"@node-red/runtime": "2.1.6",
|
"@node-red/runtime": "2.2.0",
|
||||||
"@node-red/util": "2.1.6",
|
"@node-red/util": "2.2.0",
|
||||||
"@node-red/nodes": "2.1.6",
|
"@node-red/nodes": "2.2.0",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"express": "4.17.2",
|
"express": "4.17.2",
|
||||||
|
8
packages/node_modules/node-red/settings.js
vendored
8
packages/node_modules/node-red/settings.js
vendored
@ -328,6 +328,12 @@ module.exports = {
|
|||||||
* a collection of themes to chose from.
|
* a collection of themes to chose from.
|
||||||
*/
|
*/
|
||||||
//theme: "",
|
//theme: "",
|
||||||
|
|
||||||
|
/** To disable the 'Welcome to Node-RED' tour that is displayed the first
|
||||||
|
* time you access the editor for each release of Node-RED, set this to false
|
||||||
|
*/
|
||||||
|
//tours: false,
|
||||||
|
|
||||||
palette: {
|
palette: {
|
||||||
/** The following property can be used to order the categories in the editor
|
/** The following property can be used to order the categories in the editor
|
||||||
* palette. If a node's category is not in the list, the category will get
|
* palette. If a node's category is not in the list, the category will get
|
||||||
@ -336,6 +342,7 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
//categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],
|
//categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],
|
||||||
},
|
},
|
||||||
|
|
||||||
projects: {
|
projects: {
|
||||||
/** To enable the Projects feature, set this value to true */
|
/** To enable the Projects feature, set this value to true */
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -349,6 +356,7 @@ module.exports = {
|
|||||||
mode: "manual"
|
mode: "manual"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
codeEditor: {
|
codeEditor: {
|
||||||
/** Select the text editor component used by the editor.
|
/** Select the text editor component used by the editor.
|
||||||
* Defaults to "ace", but can be set to "ace" or "monaco"
|
* Defaults to "ace", but can be set to "ace" or "monaco"
|
||||||
|
@ -106,6 +106,142 @@ describe('inject node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('inject name of node as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_NAME", payloadType: "env", wires: [["n2"]], z: "flow"},
|
||||||
|
{id: "n2", type: "helper"}];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "NAME");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inject id of node as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_ID", payloadType: "env", wires: [["n2"]], z: "flow"},
|
||||||
|
{id: "n2", type: "helper"}];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "n1");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inject path of node as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_NODE_PATH", payloadType: "env", wires: [["n2"]], z: "flow"},
|
||||||
|
{id: "n2", type: "helper"}];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "flow/n1");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('inject name of flow as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_FLOW_NAME", payloadType: "env", wires: [["n2"]], z: "flow"},
|
||||||
|
{id: "n2", type: "helper"},
|
||||||
|
{id: "flow", type: "tab", label: "FLOW" },
|
||||||
|
];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "FLOW");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inject id of flow as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_FLOW_ID", payloadType: "env", wires: [["n2"]], z: "flow"},
|
||||||
|
{id: "n2", type: "helper"},
|
||||||
|
{id: "flow", type: "tab", name: "FLOW" },
|
||||||
|
];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "flow");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inject name of group as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_GROUP_NAME", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
|
||||||
|
{id: "n2", type: "helper"},
|
||||||
|
{id: "g0", type: "group", name: "GROUP" },
|
||||||
|
];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "GROUP");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inject id of group as environment variable ', function (done) {
|
||||||
|
var flow = [{id: "n1", type: "inject", name: "NAME", topnic: "t1", payload: "NR_GROUP_ID", payloadType: "env", wires: [["n2"]], z: "flow", g: "g0"},
|
||||||
|
{id: "n2", type: "helper"},
|
||||||
|
{id: "g0", type: "group", name: "GROUP" },
|
||||||
|
];
|
||||||
|
helper.load(injectNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "g0");
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('sets the value of flow context property', function (done) {
|
it('sets the value of flow context property', function (done) {
|
||||||
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"},
|
var flow = [{id: "n1", type: "inject", topic: "t1", payload: "flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"},
|
||||||
{id: "n2", type: "helper"}];
|
{id: "n2", type: "helper"}];
|
||||||
@ -149,6 +285,7 @@ describe('inject node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('sets the value of two persistable flow context property', function (done) {
|
it('sets the value of two persistable flow context property', function (done) {
|
||||||
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]},
|
var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]},
|
||||||
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "flow", wires: [["n2"]]},
|
{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "flow", wires: [["n2"]]},
|
||||||
|
@ -366,6 +366,18 @@ describe('websocket Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle protocol property', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{ id: "server", type: "websocket-listener", path: "/ws" },
|
||||||
|
{ id: "n1", type: "websocket-client", path: getWsUrl("/ws") },
|
||||||
|
{ id: "n2", type: "websocket-client", path: getWsUrl("/ws"), subprotocol: "testprotocol1, testprotocol2" }];
|
||||||
|
helper.load(websocketNode, flow, function() {
|
||||||
|
helper.getNode("n1").should.have.property("subprotocol", []);
|
||||||
|
helper.getNode("n2").should.have.property("subprotocol", ["testprotocol1","testprotocol2"]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should connect to server', function(done) {
|
it('should connect to server', function(done) {
|
||||||
var flow = [
|
var flow = [
|
||||||
{ id: "server", type: "websocket-listener", path: "/ws" },
|
{ id: "server", type: "websocket-listener", path: "/ws" },
|
||||||
@ -378,6 +390,18 @@ describe('websocket Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should initiate with subprotocol', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{ id: "server", type: "websocket-listener", path: "/ws" },
|
||||||
|
{ id: "n2", type: "websocket-client", path: getWsUrl("/ws"), subprotocol: "testprotocol" }];
|
||||||
|
helper.load(websocketNode, flow, function() {
|
||||||
|
getSocket('server').on('connection', function (sock) {
|
||||||
|
sock.should.have.property("protocol", "testprotocol")
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should close on delete', function(done) {
|
it('should close on delete', function(done) {
|
||||||
var flow = [
|
var flow = [
|
||||||
{ id: "server", type: "websocket-listener", path: "/ws" },
|
{ id: "server", type: "websocket-listener", path: "/ws" },
|
||||||
|
@ -84,9 +84,17 @@ describe('TCP Request Node', function() {
|
|||||||
n2.on("input", msg => {
|
n2.on("input", msg => {
|
||||||
try {
|
try {
|
||||||
if (typeof result === 'object') {
|
if (typeof result === 'object') {
|
||||||
msg.should.have.properties(Object.assign({}, result, {payload: Buffer.from(result.payload)}));
|
if (flow[0].ret === "string") {
|
||||||
|
msg.should.have.properties(Object.assign({}, result, {payload: result.payload}));
|
||||||
|
} else {
|
||||||
|
msg.should.have.properties(Object.assign({}, result, {payload: Buffer.from(result.payload)}));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
msg.should.have.property('payload', Buffer.from(result));
|
if (flow[0].ret === "string") {
|
||||||
|
msg.should.have.property('payload', result);
|
||||||
|
} else {
|
||||||
|
msg.should.have.property('payload', Buffer.from(result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
@ -245,10 +253,41 @@ describe('TCP Request Node', function() {
|
|||||||
}, done);
|
}, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should send & receive, then keep connection, and not split return strings', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", ret:"string", newline:"", wires:[["n2"]] },
|
||||||
|
{id:"n2", type:"helper"}];
|
||||||
|
testTCPMany(flow, [{
|
||||||
|
payload: "foo",
|
||||||
|
topic: 'boo'
|
||||||
|
}, {
|
||||||
|
payload: "bar<A>\nfoo",
|
||||||
|
topic: 'boo'
|
||||||
|
}], {
|
||||||
|
payload: "ACK:foobar<A>\nfoo",
|
||||||
|
topic: 'boo'
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send & receive, then keep connection, and split return strings', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", ret:"string", newline:"<A>\\n", wires:[["n2"]] },
|
||||||
|
{id:"n2", type:"helper"}];
|
||||||
|
testTCPMany(flow, [{
|
||||||
|
payload: "foo",
|
||||||
|
topic: 'boo'
|
||||||
|
}, {
|
||||||
|
payload: "bar<A>\nfoo",
|
||||||
|
topic: 'boo'
|
||||||
|
}], {
|
||||||
|
payload: "ACK:foobar<A>",
|
||||||
|
topic: 'boo'
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
it('should send & recv data to/from server:port from msg', function(done) {
|
it('should send & recv data to/from server:port from msg', function(done) {
|
||||||
var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] },
|
var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] },
|
||||||
{id:"n2", type:"helper"}];
|
{id:"n2", type:"helper"}];
|
||||||
testTCPMany(flow, [{
|
testTCPMany(flow, [
|
||||||
|
{
|
||||||
payload: "f",
|
payload: "f",
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
port: port
|
port: port
|
||||||
|
@ -50,6 +50,24 @@ describe('JSON node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should convert a buffer of a valid json string to a javascript object', function(done) {
|
||||||
|
var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]},
|
||||||
|
{id:"jn2", type:"helper"}];
|
||||||
|
helper.load(jsonNode, flow, function() {
|
||||||
|
var jn1 = helper.getNode("jn1");
|
||||||
|
var jn2 = helper.getNode("jn2");
|
||||||
|
jn2.on("input", function(msg) {
|
||||||
|
msg.should.have.property('topic', 'bar');
|
||||||
|
msg.payload.should.have.property('employees');
|
||||||
|
msg.payload.employees[0].should.have.property('firstName', 'John');
|
||||||
|
msg.payload.employees[0].should.have.property('lastName', 'Smith');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var jsonString = Buffer.from('{"employees":[{"firstName":"John", "lastName":"Smith"}]}');
|
||||||
|
jn1.receive({payload:jsonString,topic: "bar"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should convert a javascript object to a json string', function(done) {
|
it('should convert a javascript object to a json string', function(done) {
|
||||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
{id:"jn2", type:"helper"}];
|
{id:"jn2", type:"helper"}];
|
||||||
@ -166,29 +184,55 @@ describe('JSON node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log an error if asked to parse something thats not json or js', function(done) {
|
it('should log an error if asked to parse an invalid json string in a buffer', function(done) {
|
||||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]},
|
||||||
{id:"jn2", type:"helper"}];
|
{id:"jn2", type:"helper"}];
|
||||||
helper.load(jsonNode, flow, function() {
|
helper.load(jsonNode, flow, function() {
|
||||||
var jn1 = helper.getNode("jn1");
|
try {
|
||||||
var jn2 = helper.getNode("jn2");
|
var jn1 = helper.getNode("jn1");
|
||||||
setTimeout(function() {
|
var jn2 = helper.getNode("jn2");
|
||||||
try {
|
jn1.receive({payload:Buffer.from('{"name":foo}'),topic: "bar"});
|
||||||
var logEvents = helper.log().args.filter(function(evt) {
|
setTimeout(function() {
|
||||||
return evt[0].type == "json";
|
try {
|
||||||
});
|
var logEvents = helper.log().args.filter(function(evt) {
|
||||||
logEvents.should.have.length(1);
|
return evt[0].type == "json";
|
||||||
logEvents[0][0].should.have.a.property('msg');
|
});
|
||||||
logEvents[0][0].msg.toString().should.eql('json.errors.dropped-object');
|
logEvents.should.have.length(1);
|
||||||
done();
|
logEvents[0][0].should.have.a.property('msg');
|
||||||
} catch(err) {
|
logEvents[0][0].msg.should.startWith("Unexpected token o");
|
||||||
done(err);
|
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||||
}
|
done();
|
||||||
},50);
|
} catch(err) { done(err) }
|
||||||
jn1.receive({payload:Buffer.from("a")});
|
},20);
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// it('should log an error if asked to parse something thats not json or js and not in force object mode', function(done) {
|
||||||
|
// var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
|
// {id:"jn2", type:"helper"}];
|
||||||
|
// helper.load(jsonNode, flow, function() {
|
||||||
|
// var jn1 = helper.getNode("jn1");
|
||||||
|
// var jn2 = helper.getNode("jn2");
|
||||||
|
// setTimeout(function() {
|
||||||
|
// try {
|
||||||
|
// var logEvents = helper.log().args.filter(function(evt) {
|
||||||
|
// return evt[0].type == "json";
|
||||||
|
// });
|
||||||
|
// logEvents.should.have.length(1);
|
||||||
|
// logEvents[0][0].should.have.a.property('msg');
|
||||||
|
// logEvents[0][0].msg.toString().should.eql('json.errors.dropped');
|
||||||
|
// done();
|
||||||
|
// } catch(err) {
|
||||||
|
// done(err);
|
||||||
|
// }
|
||||||
|
// },50);
|
||||||
|
// jn1.receive({payload:Buffer.from("abcd")});
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
it('should pass straight through if no payload set', function(done) {
|
it('should pass straight through if no payload set', function(done) {
|
||||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
{id:"jn2", type:"helper"}];
|
{id:"jn2", type:"helper"}];
|
||||||
|
@ -567,4 +567,43 @@ describe('subflow', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should access NR_NODE_PATH env var within subflow instance', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:""},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", type:"subflow:s1",
|
||||||
|
env: [], wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"",
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.payload = env.get('NR_NODE_PATH'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload", "t0/n1/s1-n1");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -201,6 +201,64 @@ describe('Subflow', function() {
|
|||||||
}
|
}
|
||||||
util.inherits(TestEnvNode,Node);
|
util.inherits(TestEnvNode,Node);
|
||||||
|
|
||||||
|
var TestNameEnvNode = function(n) {
|
||||||
|
Node.call(this,n);
|
||||||
|
this._index = createCount++;
|
||||||
|
this.scope = n.scope;
|
||||||
|
this.foo = n.foo;
|
||||||
|
var node = this;
|
||||||
|
this.stopped = false;
|
||||||
|
this.received = null;
|
||||||
|
currentNodes[node.id] = node;
|
||||||
|
this.on('input',function(msg) {
|
||||||
|
var flow = node._flow;
|
||||||
|
var val = flow.getSetting("NR_NODE_NAME");
|
||||||
|
node.received = val;
|
||||||
|
node.send({payload: val});
|
||||||
|
});
|
||||||
|
this.on('close',function() {
|
||||||
|
node.stopped = true;
|
||||||
|
stoppedNodes[node.id] = node;
|
||||||
|
delete currentNodes[node.id];
|
||||||
|
});
|
||||||
|
this.__updateWires = this.updateWires;
|
||||||
|
this.updateWires = function(newWires) {
|
||||||
|
rewiredNodes[node.id] = node;
|
||||||
|
node.newWires = newWires;
|
||||||
|
node.__updateWires(newWires);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
util.inherits(TestNameEnvNode,Node);
|
||||||
|
|
||||||
|
var TestIDEnvNode = function(n) {
|
||||||
|
Node.call(this,n);
|
||||||
|
this._index = createCount++;
|
||||||
|
this.scope = n.scope;
|
||||||
|
this.foo = n.foo;
|
||||||
|
var node = this;
|
||||||
|
this.stopped = false;
|
||||||
|
this.received = null;
|
||||||
|
currentNodes[node.id] = node;
|
||||||
|
this.on('input',function(msg) {
|
||||||
|
var flow = node._flow;
|
||||||
|
var val = flow.getSetting("NR_NODE_ID");
|
||||||
|
node.received = val;
|
||||||
|
node.send({payload: val});
|
||||||
|
});
|
||||||
|
this.on('close',function() {
|
||||||
|
node.stopped = true;
|
||||||
|
stoppedNodes[node.id] = node;
|
||||||
|
delete currentNodes[node.id];
|
||||||
|
});
|
||||||
|
this.__updateWires = this.updateWires;
|
||||||
|
this.updateWires = function(newWires) {
|
||||||
|
rewiredNodes[node.id] = node;
|
||||||
|
node.newWires = newWires;
|
||||||
|
node.__updateWires(newWires);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
util.inherits(TestIDEnvNode,Node);
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
getType = sinon.stub(typeRegistry,"get").callsFake(function(type) {
|
getType = sinon.stub(typeRegistry,"get").callsFake(function(type) {
|
||||||
if (type=="test") {
|
if (type=="test") {
|
||||||
@ -211,6 +269,10 @@ describe('Subflow', function() {
|
|||||||
return TestStatusNode;
|
return TestStatusNode;
|
||||||
} else if (type=="testEnv"){
|
} else if (type=="testEnv"){
|
||||||
return TestEnvNode;
|
return TestEnvNode;
|
||||||
|
} else if (type=="testNameEnv"){
|
||||||
|
return TestNameEnvNode;
|
||||||
|
} else if (type=="testIDEnv"){
|
||||||
|
return TestIDEnvNode;
|
||||||
} else {
|
} else {
|
||||||
return TestAsyncNode;
|
return TestAsyncNode;
|
||||||
}
|
}
|
||||||
@ -880,6 +942,65 @@ describe('Subflow', function() {
|
|||||||
},150);
|
},150);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("can access name of subflow as env var", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",name:"SFN",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]},
|
||||||
|
{id:"sf1",type:"subflow",name:"Subflow 2",info:"",
|
||||||
|
"in":[ {wires:[{id:"sf1-1"}]} ],
|
||||||
|
"out":[ {wires:[{id:"sf1-2",port:0}]} ]},
|
||||||
|
{id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]},
|
||||||
|
{id:"sf1-2",type:"testNameEnv",z:"sf1",foo:"sf1.2",x:166,y:99,wires:[[]]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({
|
||||||
|
getSetting: k=> process.env[k],
|
||||||
|
handleError: (a,b,c) => { console.log(a,b,c); }
|
||||||
|
},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
currentNodes["1"].receive({payload: "test"});
|
||||||
|
setTimeout(function() {
|
||||||
|
currentNodes["3"].should.have.a.property("received", "SFN");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},150);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can access id of subflow as env var", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",name:"SFN",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]},
|
||||||
|
{id:"sf1",type:"subflow",name:"Subflow 2",info:"",
|
||||||
|
"in":[ {wires:[{id:"sf1-1"}]} ],
|
||||||
|
"out":[ {wires:[{id:"sf1-2",port:0}]} ]},
|
||||||
|
{id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]},
|
||||||
|
{id:"sf1-2",type:"testIDEnv",z:"sf1",foo:"sf1.2",x:166,y:99,wires:[[]]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({
|
||||||
|
getSetting: k=> process.env[k],
|
||||||
|
handleError: (a,b,c) => { console.log(a,b,c); }
|
||||||
|
},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
currentNodes["1"].receive({payload: "test"});
|
||||||
|
setTimeout(function() {
|
||||||
|
currentNodes["3"].should.have.a.property("received", "2");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},150);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -156,6 +156,22 @@ describe('context', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('deletes global context',function() {
|
||||||
|
Context.init({functionGlobalContext: {foo:"bar"}});
|
||||||
|
return Context.load().then(function(){
|
||||||
|
var globalContextA = Context.get("global")
|
||||||
|
|
||||||
|
globalContextA.get('foo').should.eql('bar')
|
||||||
|
globalContextA.set("another","value");
|
||||||
|
|
||||||
|
return Context.delete("global").then(function(){
|
||||||
|
var globalContextB = Context.get("global")
|
||||||
|
globalContextB.get('foo').should.eql('bar')
|
||||||
|
should.not.exist(globalContextB.get("another"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('enumerates context keys - sync', function() {
|
it('enumerates context keys - sync', function() {
|
||||||
var flowContextA = Context.getFlowContext("flowA")
|
var flowContextA = Context.getFlowContext("flowA")
|
||||||
var context = Context.get("1","flowA");
|
var context = Context.get("1","flowA");
|
||||||
@ -316,6 +332,31 @@ describe('context', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("clear", function() {
|
||||||
|
it('clears all context',function() {
|
||||||
|
Context.init({functionGlobalContext: {foo:"bar"}});
|
||||||
|
return Context.load().then(function(){
|
||||||
|
var globalContextA = Context.get("global")
|
||||||
|
globalContextA.get('foo').should.eql('bar')
|
||||||
|
globalContextA.set("another","value");
|
||||||
|
|
||||||
|
var flowContextA = Context.getFlowContext("flowA")
|
||||||
|
flowContextA.set("foo","abc");
|
||||||
|
flowContextA.get("foo").should.equal("abc");
|
||||||
|
|
||||||
|
return Context.clear().then(function(){
|
||||||
|
var globalContextB = Context.getFlowContext("global")
|
||||||
|
globalContextB.get('foo').should.eql('bar')
|
||||||
|
should.not.exist(globalContextB.get("another"));
|
||||||
|
|
||||||
|
flowContextA = Context.getFlowContext("flowA")
|
||||||
|
should.not.exist(flowContextA.get("foo"))
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('external context storage',function() {
|
describe('external context storage',function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user