Compare commits

..

296 Commits
2.1.0 ... 2.2.2

Author SHA1 Message Date
Nick O'Leary
5f0ea85f47 Bump for 2.2.2 2022-02-18 14:46:01 +00:00
Nick O'Leary
6c7c1202ed Merge pull request #3451 from Steve-Mcl/fix-mqtt-close-timeout
Fix "close timed out" error when performing full deploy or modifying broker node.
2022-02-18 14:43:34 +00:00
Steve-Mcl
669aa769c2 Fix close timeout on MQTT nodes
fixes #2934
2022-02-17 10:18:46 +00:00
Nick O'Leary
fcf2994015 Bump for 2.2.1 2022-02-15 13:41:53 +00:00
Nick O'Leary
bee21ddc9e Update dependencies 2022-02-15 13:36:05 +00:00
Nick O'Leary
263e68e677 Merge pull request #3447 from Steve-Mcl/fix-hook-module-location
Improve module location parsing (of stack info) when adding hook
2022-02-15 13:18:25 +00:00
Steve-Mcl
2b958f5724 Improve module location parsing when adding hook
- fixes #3401
- handles brackets and no brackets in stack line
- handles short stack
2022-02-15 12:40:34 +00:00
Nick O'Leary
95f7177ef4 Merge pull request #3445 from node-red-hitachi/fix-path-substitution
Fix substitution of NR_NODE_PATH
2022-02-15 11:26:19 +00:00
Nick O'Leary
006324b78e Merge pull request #3446 from Steve-Mcl/better-bad-node-err-log
improve "Unexpected Node Error" logging
2022-02-15 11:26:08 +00:00
Steve-Mcl
efd8c1229d improve "Unexpected Node Error" logging
- fixes #3389
- adds additional info to aid sourcing the issue
- removes lots of undefined info when node type is incorrect
- stores and reports original stack before internal try/catch exception
- ensure this._flow is something before attempting to call `handleError`
2022-02-15 10:51:18 +00:00
Hiroyasu Nishiyama
0f1aea3e0d add test case 2022-02-15 14:41:03 +09:00
Hiroyasu Nishiyama
6a41cbebc9 evaluate env var after path initialization 2022-02-15 14:23:01 +09:00
Nick O'Leary
bffb91f196 Merge pull request #3443 from node-red/delay-field-validation
Allow nbRateUnits to be undefined when validating
2022-02-14 23:18:41 +00:00
Nick O'Leary
4573b65639 Merge pull request #3444 from node-red/keyboard-filter-case
Handle mixed-cased filter terms in keyboard shortcut dialog
2022-02-14 23:18:16 +00:00
Nick O'Leary
3d8505385a Merge pull request #3442 from node-red/dupe-links
Prevent duplicate links being added between nodes
2022-02-14 23:18:01 +00:00
Nick O'Leary
99c053f86b Merge pull request #3440 from Steve-Mcl/monaco-env-suggestions
Coding help for recently added node-red Predefined Environment Variables
2022-02-14 23:02:55 +00:00
Nick O'Leary
f2dde705ef Handle mixed-cased filter terms in keyboard shortcut dialog
Fixes #3400
2022-02-14 20:59:43 +00:00
Nick O'Leary
be11fda814 Allow nbRateUnits to be undefined when validating
Fixes #3407
2022-02-14 20:44:55 +00:00
Nick O'Leary
0261105c52 Prevent duplicate links being added between nodes 2022-02-14 20:35:47 +00:00
Nick O'Leary
264047dc0c Merge pull request #3437 from Steve-Mcl/fix-multiple-wires
bug fix: Dont add wires to undo buffer twice
2022-02-14 20:12:46 +00:00
Steve-Mcl
4d84926ed2 Update monaco for Predefined Environment Variables
- add Predefined Environment Variables to snippet suggestions
- update jsdoc for `env.get`
2022-02-14 13:38:37 +00:00
Nick O'Leary
da3211fee6 Merge pull request #3439 from node-red/remove-debug
Remove console.log when ignoring disabled module
2022-02-14 10:48:33 +00:00
Nick O'Leary
1388b03cf2 Remove console.log when ignoring disabled module 2022-02-14 10:46:08 +00:00
Steve-Mcl
b3f1401ab4 Dont add wires to undo buffer twice
Fixes #3433
2022-02-13 16:56:26 +00:00
Hiroyasu Nishiyama
e5e3832809 fix Japanese message of delay node (#3434) 2022-02-12 16:13:59 +00:00
Nick O'Leary
2eff7da171 Merge pull request #3397 from node-red-hitachi/fix-config-node-validation
fix node validation to be applied to config node
2022-02-09 19:47:23 +00:00
Nick O'Leary
e9622bcfe8 Merge pull request #3391 from node-red-hitachi/fix-hide-flow-tooltip
fix to hide tooltip after removing subflow tab
2022-02-09 19:44:05 +00:00
Hiroyasu Nishiyama
dc73997be3 fix not to use hasOwnProperty 2022-02-08 09:21:52 +09:00
Hiroyasu Nishiyama
fb81121bd3 fix node validation to be applied to config node 2022-02-04 17:45:00 +09:00
Nick O'Leary
5293563a6a Merge pull request #3394 from Alkarex/fix-debug-node-hasOwnProperty
Fix no-prototype-builtins bug in debug node and utils
2022-02-03 16:31:46 +00:00
Alexandre Alapetite
2e1e61dabe Remove part of JSDoc 2022-02-03 16:32:51 +01:00
Alexandre Alapetite
280d63fde7 Fix util.encodeObject 2022-02-03 15:59:25 +01:00
Alexandre Alapetite
e55cbb3e3d Fix bug in debug node due to msg.hasOwnProperty construct
`msg.hasOwnProperty("status")` might make the debug node crash/produce an error if the payload was created with `Object.create(null)`.
This is the case e.g. for `ini` (to parse INI files), an official NPM node:
4f289946b3/lib/ini.js (L63)

My Node-RED node `node-red-contrib-parser-ini`, which is using that library, was hit by this bug and I had to ship a workaround
fe6b1eb4b1/parser-ini.js (L14)

The `msg.hasOwnProperty("xxx")` construct should not be used since ECMAScript 5.1.

ESLint advises in the same direction https://eslint.org/docs/rules/no-prototype-builtins

This patch was produced using the following regex:
Search: `\b([\w.]+).hasOwnProperty\(`
Replace: `Object.prototype.hasOwnProperty.call($1, `

This could be applied more gobally if desired.
2022-02-03 02:01:22 +01:00
Hiroyasu Nishiyama
7959d18248 hide tooltip after removing subflow tab 2022-02-02 11:34:58 +09:00
Nick O'Leary
b7bae18849 Update inner package dependency 2022-01-27 12:58:43 +00:00
Nick O'Leary
fbde247c72 Update node-red-admin dependency 2022-01-27 12:57:32 +00:00
Nick O'Leary
3a69af9034 Merge pull request #3383 from node-red/dev
Merge dev to master for 2.2.0
2022-01-27 12:50:45 +00:00
Nick O'Leary
5c87a6cb76 Update package readmes and licenses 2022-01-27 12:49:48 +00:00
Nick O'Leary
9c6bb434e8 Update contributing guide for new cla 2022-01-27 12:44:22 +00:00
Nick O'Leary
d1bd303dfa Update version for 2.2.0 2022-01-27 10:13:28 +00:00
Nick O'Leary
3813c32454 Update marked to latest 2022-01-27 10:12:55 +00:00
Nick O'Leary
3b00a692ee Update changelog 2022-01-27 10:12:06 +00:00
Nick O'Leary
3304ebe9d3 Merge pull request #3382 from node-red-hitachi/fix-subflow-update
fix opening/closing subflow template not to make subflow changed
2022-01-27 09:30:26 +00:00
Nick O'Leary
513120cbfe Merge pull request #3381 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for v2.2.0
2022-01-27 09:28:29 +00:00
Hiroyasu Nishiyama
b06049d5a3 fix opening/closing subflow template not to make subflow changed 2022-01-27 12:57:37 +09:00
Kazuhito Yokoi
0f50355deb Add Japanese translations for v2.2.0 2022-01-27 11:14:24 +09:00
Nick O'Leary
bd6e35fea2 Merge branch 'dev' of github.com:node-red/node-red into dev 2022-01-26 17:46:47 +00:00
Nick O'Leary
a4fd63cd44 Merge pull request #3380 from Steve-Mcl/search-find-filenode
Pallet Label (if different to type) should be indexed for search
2022-01-26 17:00:47 +00:00
Steve-Mcl
fdc4219b68 palletLabel (if different) should be indexed
for search - fixes #3297
2022-01-26 16:14:34 +00:00
Nick O'Leary
5b428bb8e6 Merge pull request #3379 from node-red-hitachi/nr_node_path-support
add NR_NODE_PATH path support
2022-01-26 14:29:03 +00:00
Hiroyasu Nishiyama
c948ff88a5 add support of NR_NODE_PATH 2022-01-26 21:24:48 +09:00
Nick O'Leary
9b9a0d7060 Update dependencies 2022-01-26 12:20:45 +00:00
Nick O'Leary
16578e3677 Update changelog 2022-01-26 12:07:04 +00:00
Nick O'Leary
be7f84bc67 Merge pull request #3377 from Steve-Mcl/function-node-decs
Update `node` declarations in func.d.ts
2022-01-26 12:03:56 +00:00
Nick O'Leary
033d26f2cb Merge pull request #3378 from node-red/tour-update
Update tour for 2.2.0
2022-01-26 12:03:40 +00:00
Nick O'Leary
4c0826b1c4 Update tour for 2.2.0 2022-01-26 12:00:30 +00:00
Steve-Mcl
c4cc204c94 Add static declerations to func ts. fixes #3376 2022-01-26 11:40:05 +00:00
Nick O'Leary
3747db18b1 Update changelog 2022-01-26 11:34:32 +00:00
Nick O'Leary
4173625fca Merge pull request #3243 from node-red/delete-global-context
Clear context contents when switching projects
2022-01-26 11:28:34 +00:00
Nick O'Leary
0e7863a6fb Merge pull request #3375 from node-red/settings-update
Add editorTheme.tours property to default settings file
2022-01-26 11:21:55 +00:00
Nick O'Leary
207ba00ad2 Add editorTheme.tours property to default settings file 2022-01-26 11:21:11 +00:00
Nick O'Leary
87c89586a5 Merge pull request #3356 from node-red-hitachi/node-prop-env-var
Add support for accessing node id & name as environment variable
2022-01-26 11:11:02 +00:00
Nick O'Leary
1cea1ced82 Merge pull request #3371 from node-red/node-alias
Add _path property to nodes and expose as node.path in Function node
2022-01-26 11:10:43 +00:00
Nick O'Leary
08732bac0f Merge pull request #3374 from Steve-Mcl/mqtt-reconnect-fix
Mqtt reconnect fix
2022-01-26 11:03:20 +00:00
Nick O'Leary
283e8d3c08 Merge pull request #3373 from Steve-Mcl/merge-fix
bug: merge issue. fixes #3364
2022-01-26 11:01:35 +00:00
Nick O'Leary
42a7165596 Merge pull request #3372 from Steve-Mcl/use-paletteLabel-in-help
Use a nodes palette label in help tree
2022-01-26 11:00:53 +00:00
Hiroyasu Nishiyama
aa3f5001d5 additional update of test for NR_ prefix addition 2022-01-26 10:52:41 +09:00
Hiroyasu Nishiyama
56580c4005 add test for NR_ prefix addition 2022-01-26 09:26:05 +09:00
Hiroyasu Nishiyama
ba304c9651 add NR_ prefix to special environment variables 2022-01-26 09:25:30 +09:00
Nick O'Leary
703c5adba7 Add _path property to nodes and expose as node.path in Function node 2022-01-25 21:32:28 +00:00
Steve-Mcl
8b85f6e0a6 ensure help search is case insensitive 2022-01-25 18:27:47 +00:00
Nick O'Leary
c136d22382 Merge pull request #3370 from node-red/fix-vertical-align-subflow-port
Fix vertical align subflow port
2022-01-25 17:09:05 +00:00
Nick O'Leary
42358419ad Fix vertical align of subflow port labels 2022-01-25 17:08:04 +00:00
Nick O'Leary
ab2ced5c37 Merge pull request #3352 from ralphwetzel/dev
Subflow: Add labels to OUTPUT nodes
2022-01-25 17:07:12 +00:00
Steve-Mcl
faf31be0dc Ensure label is shown in help tree 2022-01-25 16:30:30 +00:00
Nick O'Leary
ff4c67d068 Merge pull request #3353 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for v2.2.0-beta.1
2022-01-25 16:02:29 +00:00
Steve-Mcl
10b133db02 bug: merge issue. fixes #3364 2022-01-25 14:47:36 +00:00
Nick O'Leary
f0bf607b43 Merge pull request #3359 from node-red-hitachi/fix-action-list-i18n
fix to make actions list i18n ready and Japanese translation
2022-01-25 14:47:18 +00:00
Steve-Mcl
8948ca5323 Merge branch 'dev' into mqtt-reconnect-fix 2022-01-25 12:32:49 +00:00
Steve-Mcl
9c3be51fe9 Improve MQTT broker disconnect situation
* Ensure topic is sane to prevent error 129
* Ensure a log entry is made
* Update node status
2022-01-25 12:29:51 +00:00
Hiroyasu Nishiyama
2da9161f29 Update packages/node_modules/@node-red/editor-client/src/js/ui/actions.js
Oh, I'm sorry about this.

Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-01-25 21:21:39 +09:00
Hiroyasu Nishiyama
983dad5b53 add test for env var access of GROUP_ID & GROUP_NAME 2022-01-25 21:07:38 +09:00
Hiroyasu Nishiyama
8b23d341b4 add support for env var access of GROUP_ID & GROUP_NAME 2022-01-25 21:04:43 +09:00
Hiroyasu Nishiyama
0ad60013aa add test for env var access of FLOW_ID & FLOW_NAME 2022-01-25 20:52:45 +09:00
Hiroyasu Nishiyama
289815e128 add support of FLOW_ID & FLOW_NAME 2022-01-25 20:46:01 +09:00
Kazuhito Yokoi
f7ee83f1b9 Add translations for restoring zoom level and scroll position 2022-01-25 11:18:35 +09:00
Nick O'Leary
f67aafa8d3 Merge pull request #3361 from node-red/zoom-level
Remember Zoom level and Sidebar tab selection between sessions
2022-01-24 23:21:31 +00:00
Nick O'Leary
4e5ddd57bf Merge branch 'dev' of github.com:node-red/node-red into dev 2022-01-24 23:18:43 +00:00
Nick O'Leary
e0d4ecf835 Merge branch 'master' into dev 2022-01-24 23:18:25 +00:00
Nick O'Leary
81a461115b Merge pull request #3365 from node-red-hitachi/fix-http-request-set-cookie
Fix http request set cookie to use sync API
2022-01-24 21:26:29 +00:00
Nick O'Leary
d679b02658 Add option to remember zoom and scroll position in localStorage 2022-01-24 21:04:27 +00:00
Hiroyasu Nishiyama
5effcdb024 use setCookieSync instead of setCookie 2022-01-19 23:45:13 +09:00
Nick O'Leary
211a5eb2bb Bump for 2.1.6 2022-01-18 09:46:57 +00:00
Nick O'Leary
c4465ba58d Merge pull request #3351 from node-red-hitachi/fix-delay-rate-unit-validation
fix unit value validation of delay node
2022-01-18 09:45:56 +00:00
Nick O'Leary
7903c53876 Merge pull request #3363 from node-red/revert-copy-fix
Revert copy-text change and apply alternative fix
2022-01-18 09:28:37 +00:00
Nick O'Leary
dbefe6a560 Revert copy-text change and apply alternative fix
Reverts PR #3331 and applies the alternative fix originally proposed
in PR #3329
2022-01-18 09:27:15 +00:00
Nick O'Leary
8b1f412255 Merge pull request #3362 from node-red/update-marked
Update marked to latest
2022-01-18 09:22:28 +00:00
Nick O'Leary
a2e0074061 Update marked to latest 2022-01-18 09:20:52 +00:00
Nick O'Leary
5fc920087b Store user zoom level in localStorage 2022-01-17 21:27:48 +00:00
Nick O'Leary
085233ab9b Remember last sidebar tab between editor sessions 2022-01-17 21:27:21 +00:00
Hiroyasu Nishiyama
6657b2629f move i18n code to action list according to PR comment 2022-01-16 12:10:19 +09:00
Nick O'Leary
310a279aaf Merge pull request #3358 from node-red-hitachi/fix-property-error-tooltip
fix to make start of property error tooltip messages aligned
2022-01-15 19:44:18 +00:00
Hiroyasu Nishiyama
58f3a76da7 fix to make actions list i18n ready and Japanese translation 2022-01-15 20:39:05 +09:00
Hiroyasu Nishiyama
a2c9458b1b aligin start of property error tooltip messages 2022-01-15 13:11:56 +09:00
Hiroyasu Nishiyama
75bcd9e8d5 add support for accessing node id & name as env var 2022-01-14 23:40:12 +09:00
Kazuhito Yokoi
977e7ef395 Add Japanese translations for v2.2.0-beta.1 2022-01-14 14:43:28 +09:00
ralphwetzel
eb1b8b577f Merge branch 'node-red:dev' into dev 2022-01-13 23:36:23 +01:00
ralphwetzel
28f91685ce Subflow: Add labels to OUTPUT nodes 2022-01-13 23:33:06 +01:00
Nick O'Leary
81a4fe59d9 Update changelog/welcome tour for 2.2 2022-01-13 15:16:16 +00:00
Hiroyasu Nishiyama
e7189ab81f fix unit value validation of delay node 2022-01-13 23:52:06 +09:00
Nick O'Leary
346db89e66 Merge pull request #3349 from node-red-hitachi/fix-inject-property-validation
fix JSON propety validation of inject node
2022-01-13 12:50:17 +00:00
Hiroyasu Nishiyama
f786c7f144 fix JSON propety validation of inject node 2022-01-13 21:42:50 +09:00
Nick O'Leary
51f45293b8 Merge branch 'master' into dev 2022-01-13 10:52:36 +00:00
Nick O'Leary
943b103001 Update changelog 2022-01-13 10:29:20 +00:00
Nick O'Leary
f055d42277 Update package version for 2.1.5 2022-01-13 10:22:41 +00:00
Nick O'Leary
fb7a2a8d5d Update dependencies 2022-01-13 10:19:56 +00:00
Nick O'Leary
1e5ed2a2e3 Merge pull request #3331 from kazuhitoyokoi/master-fixcopybutton2
Fix copy buttons on the debug window (another method)
2022-01-13 09:50:27 +00:00
Nick O'Leary
e1467dfe23 Merge pull request #3348 from node-red/scrollbar-expand
Prevent function scrollbar from obscuring expand button
2022-01-13 09:49:15 +00:00
Nick O'Leary
c480f96d30 Prevent function scrollbar from obscuring expand button
Fixes #2955
2022-01-13 09:46:37 +00:00
Kazuhito Yokoi
cf613aafb2 Update packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-01-13 10:35:12 +09:00
Nick O'Leary
20dbf7c5f4 Merge pull request #3346 from node-red/fix-stack-capture
Handle reporting error location when stack is truncated
2022-01-12 22:54:25 +00:00
Nick O'Leary
47c912c25b Merge pull request #3345 from node-red/jsonata-compat-mode-fix
Improve jsonata legacy mode detection regex
2022-01-12 22:54:05 +00:00
Nick O'Leary
48fb1a8127 Merge pull request #3344 from node-red/fix-ws-hb
Only setup ws client heartbeat once it is connected
2022-01-12 22:53:51 +00:00
Nick O'Leary
6c1b55db16 Merge pull request #3347 from node-red/flow-name-increment
Fix generating flow name with incrementing number
2022-01-12 22:53:33 +00:00
Nick O'Leary
df70c8a800 Fix generating flow name with incrementing number
Fixes #3295
2022-01-12 22:45:18 +00:00
Nick O'Leary
82ae2e7118 Handle reporting error location when stack is truncated
Fixes #3292
2022-01-12 22:07:44 +00:00
Nick O'Leary
0cf9b5f3df Improve jsonata legacy mode detection regex
Fixes #3290
2022-01-12 21:56:38 +00:00
Nick O'Leary
036a825892 Only setup ws client heartbeat once it is connected
Fixes #3264
2022-01-12 21:46:56 +00:00
Nick O'Leary
8b43b31c64 Add checkbox on project-open screen to clear context 2022-01-12 21:05:47 +00:00
Nick O'Leary
3abef972a7 Clear context contents when switching projects
Fixes #3240
2022-01-12 18:59:29 +00:00
Nick O'Leary
30b00741b5 Merge pull request #3296 from node-red/Json-try-parse-buffer
Let JSON node attempt to parse buffer if it contains a valid string
2022-01-12 18:58:02 +00:00
Nick O'Leary
f86e743cce Merge branch 'master' into dev 2022-01-12 17:59:26 +00:00
Nick O'Leary
cb96fb735e Merge pull request #3289 from node-red/snap-grid
Snap nodes on grid using either edge as reference
2022-01-12 17:57:26 +00:00
Nick O'Leary
459a52d31d Merge pull request #3340 from node-red/cut-wires
Add wire-slice mode to delete wires with Ctrl-RHClick-Drag
2022-01-12 17:52:54 +00:00
Nick O'Leary
44ef9a13d6 Merge pull request #3338 from node-red/detach-action
Detach action
2022-01-12 17:52:32 +00:00
Nick O'Leary
0bb3652a63 Merge pull request #3323 from node-red/highlight-links
Highlight links when selecting nodes
2022-01-12 17:52:03 +00:00
Nick O'Leary
6a82d683a9 Merge pull request #3262 from node-red/search-history
Add search history to main search box
2022-01-12 17:51:46 +00:00
Nick O'Leary
fad708e8de Merge pull request #3294 from node-red/multi-select-links
Allow multiple links to be selected by ctrl-click
2022-01-12 17:51:15 +00:00
Nick O'Leary
c6a38b8355 Merge pull request #3300 from node-red/remove-use-of-verbose-flag-in-core-nodes
Remove use of verbose flag in core nodes - and use node.debug level instead
2022-01-12 17:38:34 +00:00
Nick O'Leary
ee84eb666b Merge pull request #3307 from node-red/add-tls-option-to-tcp-node
Add TLS option to tcp client nodes
2022-01-12 17:37:43 +00:00
Nick O'Leary
6d2793cac6 Merge pull request #3336 from node-red-hitachi/add-link-call-example
add link call example
2022-01-12 17:31:57 +00:00
Nick O'Leary
86d518fc2e Merge pull request #3343 from node-red/adminAuth-logging
Initialize passport when only adminAuth.tokens is set
2022-01-12 17:31:44 +00:00
Nick O'Leary
25dba1a6d5 Merge pull request #3342 from node-red/add-log-logging
Add log logging
2022-01-12 17:29:29 +00:00
Nick O'Leary
af949c62c2 Allow 'escape' to cancel wire-slicing 2022-01-12 17:24:25 +00:00
Nick O'Leary
ea20342d76 Handle non-error responses from token function 2022-01-12 13:48:06 +00:00
Nick O'Leary
7732d52583 Initialize passport when only adminAuth.tokens is set
Fixes #3341
2022-01-12 13:27:01 +00:00
Nick O'Leary
c801bc5e6b Add error logging if Node.log throws an error
Part of #3327
2022-01-12 11:13:29 +00:00
Nick O'Leary
ea43729063 Add _module to Node object to provide module info 2022-01-12 11:07:25 +00:00
Nick O'Leary
e26bae8027 Merge pull request #3333 from tobiasoort/dev
Implemented support for Websocket Subprotocols in WS Client Node.
2022-01-12 09:31:55 +00:00
Tobias Oort
555f155cad Added support for commaseparated subprotocols
Removed placeholder from html
2022-01-12 09:10:35 +01:00
Nick O'Leary
f8c47f59bc Merge pull request #3339 from node-red/fix-filter-node-start-condition
Fix for filter node narrrowbandEq mode start condition failure
2022-01-11 22:39:14 +00:00
Nick O'Leary
f3997128b9 Add wire-slice mode to delete wires with Ctrl-RHClick-Drag 2022-01-11 19:49:40 +00:00
Nick O'Leary
154a4e23dd Add delete-selection-and-reconnect action 2022-01-11 14:11:55 +00:00
Dave Conway-Jones
52e4e0e569 Fix for narrrowbandEq mode start condition failure
And add test
As discussed https://discourse.nodered.org/t/the-filter-node-or-the-old-rbe-node/56371
2022-01-11 13:56:44 +00:00
Nick O'Leary
30f2b96c68 Pressing escape whilst DETACH_DRAGGING should revert change 2022-01-11 00:24:34 +00:00
Nick O'Leary
58c94b7773 Add core:detach-selected-nodes action 2022-01-11 00:16:24 +00:00
Tobias Oort
83203d5f5d Fixed property name in unit-test 2022-01-10 22:23:45 +01:00
Dave Conway-Jones
f77d161643 remove debug msg from inject close 2022-01-10 20:22:20 +00:00
Nick O'Leary
6580b139c0 Merge pull request #3288 from node-red/wiring-shortcut
Wiring shortcut
2022-01-10 18:34:06 +00:00
Nick O'Leary
2743c7c6ac Merge pull request #3304 from node-red-hitachi/dev-unknownconfig
Check availability of type of config node on deploy
2022-01-10 18:33:36 +00:00
Nick O'Leary
062f76214e Merge pull request #3325 from hardillb/http-basic-username-only
Fix basic auth with empty username or password
2022-01-10 18:27:55 +00:00
Nick O'Leary
ce98ed98a2 Merge pull request #3334 from Steve-Mcl/fix-monaco-expanded
load extralibs when expanding monaco. fixes #3319
2022-01-10 18:23:07 +00:00
Nick O'Leary
dce9d93f6c Merge pull request #3335 from node-red-hitachi/fix-file-node-example
Update file node examples according to node name change
2022-01-10 18:22:39 +00:00
Nick O'Leary
f7e35a6cbe Merge pull request #3337 from node-red-hitachi/fix-selection
resume focus after import/export dialog close
2022-01-10 18:21:55 +00:00
Hiroyasu Nishiyama
931335220f resume focus after import/export dialog close 2022-01-09 23:34:08 +09:00
Hiroyasu Nishiyama
3ce35a8a4b add link call example 2022-01-09 20:24:15 +09:00
Hiroyasu Nishiyama
9ac4e5cf6a update file node examples according to node name change 2022-01-09 19:49:06 +09:00
Tobias Oort
bd77d7eec3 Implemented support for Websocket Subprotocols in WS Client Node. 2022-01-08 22:18:05 +01:00
Steve-Mcl
b5e48aa509 load extralibs when expanding monaco. fixes #3319 2022-01-08 19:29:49 +00:00
Kazuhito Yokoi
b14c42b6a4 Update Japanese translations in node help (#3332)
* Update Japanese translations in node help

* Fix typos
2022-01-07 17:43:53 +00:00
Kazuhito Yokoi
ba794ba58c Use clipboard.writeText() instead of execCommand("copy") 2022-01-07 11:49:26 +09:00
Kazuhito Yokoi
b00282590d Fix copy buttons on the debug window 2022-01-07 09:27:01 +09:00
Ben Hardill
44616c6872 Fix basic auth with empty username or password
fix for #3324
2022-01-05 20:56:46 +00:00
Nick O'Leary
aaa2b4c3db Merge pull request #3322 from node-red/inject-props-fix
Fix incorrect clearing of blank payload property in Inject node
2022-01-04 18:59:05 +00:00
Nick O'Leary
8974d8e4df Merge pull request #3321 from node-red/fix-prev-tab-action
Fix findPreviousVisibleTab action
2022-01-04 18:58:45 +00:00
Nick O'Leary
699063cbb0 Merge pull request #3312 from node-red/hide-tabs-fix
Fix storing hidden tab state when not hidden via action
2022-01-04 18:58:32 +00:00
Nick O'Leary
e76000b713 Merge pull request #3311 from node-red/strip-empty-env
Avoid adding empty env properties to tabs/groups
2022-01-04 18:57:53 +00:00
Nick O'Leary
332b372e31 Merge pull request #3310 from node-red/group-env-fix
Update Function to use correct api to access env vars
2022-01-04 18:57:33 +00:00
Nick O'Leary
cb3fcb7bfa Ctrl-Shift-Click on node should add to existing selection 2022-01-03 23:29:42 +00:00
Nick O'Leary
bef641609e Select links when nodes on either side selected 2022-01-03 23:29:12 +00:00
Nick O'Leary
24b52f09df Add RED.nodes.getNodeLinks to help node-link lookup 2022-01-03 23:27:25 +00:00
Nick O'Leary
942b17b807 Fix incorrect clearing of blank payload property in Inject node
Fixes #3316
2022-01-03 21:51:49 +00:00
Nick O'Leary
cf19d7f3ad Fix findPreviousVisibleTab action
Fixes #3320
2022-01-03 21:20:23 +00:00
Nick O'Leary
ebd62a4112 Fix storing hidden tab state when not hidden via action
Fixes #3305
2021-12-28 10:29:42 +00:00
Nick O'Leary
9af7357ca4 Avoid adding empty env properties to tabs/groups
Fixes #3306
2021-12-27 12:03:40 +00:00
Nick O'Leary
0dbc35c252 Update Function to use correct api to access env vars
Fixes #3299
2021-12-27 11:55:10 +00:00
Dave Conway-Jones
c9f03f1ac5 better tests
(and a small fix as a result)
2021-12-27 09:51:50 +00:00
Dave Conway-Jones
02bd292b8c fix and test 2021-12-26 16:12:47 +00:00
Dave Conway-Jones
e5f1029d0c fix variable names for test 2021-12-26 15:37:41 +00:00
Dave Conway-Jones
cae247160f Let tcprequest split incoming strings on delimiter (as per tcpin node)
and fixup i18n messages
2021-12-26 15:28:16 +00:00
Dave Conway-Jones
6692b1992c TCP add tls option to inbound nodes 2021-12-26 12:12:31 +00:00
Dave Conway-Jones
0937837b7f Add TLS config option to TCP client nodes
(not yet when in server mode)
2021-12-24 16:18:00 +00:00
Kunihiko Toumura
828888490a check availability of config node on deploy 2021-12-21 18:10:42 +09:00
Kazuhito Yokoi
91cb6ba73b Add Japanese translations for hidden flow (#3302) 2021-12-20 09:34:41 +00:00
Nick O'Leary
4ee4d32b2e Merge pull request #3301 from kazuhitoyokoi/master-fixwelcometour
Fix hide icon in tour guide
2021-12-17 11:19:27 +00:00
Kazuhito Yokoi
8df630a2f5 Fix hide button icon in tour guide 2021-12-17 19:37:29 +09:00
Dave Conway-Jones
2cad42870e chaneg exec, file and inject node to use node.debug rather than -v flag 2021-12-17 09:58:10 +00:00
Nick O'Leary
43651135f3 Replace selectedLinks array with wrapped Set object 2021-12-13 21:02:55 +00:00
Dave Conway-Jones
6ae42eb787 Let JSON parser attempt to parse buffers if they contain strings
and add/fix test
2021-12-10 15:21:43 +00:00
Nick O'Leary
ecaf866613 Fix serialisation of selection in view 2021-12-07 09:29:11 +00:00
Nick O'Leary
5ea3329b36 Allow multiple links to be selected by ctrl-click 2021-12-07 09:19:27 +00:00
Nick O'Leary
7bb7149f4c Snap nodes on grid using either edge as reference 2021-12-05 23:23:57 +00:00
Nick O'Leary
5856d043ca Add node wiring actions 2021-12-05 19:52:47 +00:00
Nick O'Leary
7cd3e49f04 Merge branch 'master' into dev 2021-12-05 19:32:13 +00:00
Nick O'Leary
173e75175e update changelog 2021-12-02 09:30:41 +00:00
Nick O'Leary
800006dd76 Merge pull request #3282 from node-red/tab-buttons
Change tab hide button icon to an eye and add search option
2021-12-02 09:29:28 +00:00
Nick O'Leary
a824b6910a Add tooltips to tabs and close/hide buttons 2021-12-02 09:23:13 +00:00
Nick O'Leary
dcea382b38 Change tab hide button icon to an eye and add search option 2021-12-01 17:54:17 +00:00
Nick O'Leary
d9f976baea Update package for 2.1.4 2021-12-01 16:06:03 +00:00
Nick O'Leary
1fa13efe19 Merge pull request #3281 from node-red/i18n-space-ns-fix
Fix i18n handling of namespaces with spaces in
2021-12-01 15:15:38 +00:00
Nick O'Leary
33af5cd7c6 Apply i18n namespace fix to runtime component 2021-12-01 15:15:08 +00:00
Nick O'Leary
7cb8f97ef1 Fix i18n handling of namespaces with spaces in 2021-12-01 15:08:04 +00:00
Nick O'Leary
bf965a9cde Merge pull request #3280 from node-red/auto-complete-fix
Trigger change event when autoComplete fills in input
2021-12-01 14:38:10 +00:00
Nick O'Leary
17ffff685a Trigger change event when autoComplete fills in input
Fixes #3271
2021-12-01 14:35:45 +00:00
Nick O'Leary
ae76271cff Merge pull request #3279 from node-red/cn-fix
Apply CN i18n fix
2021-12-01 14:26:37 +00:00
Nick O'Leary
8f3a96d615 Apply CN i18n fix
Fixes #3272

Verified with Google Translate this is a better translation for
"Export to library"
2021-12-01 14:25:35 +00:00
Nick O'Leary
30e750dfe5 Merge pull request #3278 from node-red-hitachi/fix-env-var-access
fix env var access using $parent for groups
2021-12-01 14:23:44 +00:00
Nick O'Leary
682dff7c6f Merge pull request #3273 from node-red-hitachi/fix-config-select-name
fix select menu label of config node to use paletteLabel
2021-12-01 14:02:55 +00:00
Nick O'Leary
85415eb8a8 Merge pull request #3275 from node-red-hitachi/fix-node-conflict
fix removed tab not to cause node conflict
2021-12-01 14:01:50 +00:00
Nick O'Leary
68a80b9244 Merge pull request #3277 from node-red/debug-path-fix
Add proper error handling for 404 errors when serving debug files
2021-12-01 13:48:25 +00:00
Hiroyasu Nishiyama
c331da7323 fix env var access using $env for groups 2021-11-27 19:29:57 +09:00
Nick O'Leary
04ffa06221 Add proper error handling for 404 errors when serving debug files 2021-11-26 11:30:51 +00:00
Hiroyasu Nishiyama
1f0690c6ec fix removed tab not to cause node conflict 2021-11-25 13:26:52 +09:00
Hiroyasu Nishiyama
711467abcd fix select menu label of config node to use paletteLabel 2021-11-24 14:06:45 +09:00
Nick O'Leary
9439cd0e3d Merge pull request #3179 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for Node-RED v2.1.0-beta.1
2021-11-22 23:25:33 +00:00
Nick O'Leary
314c19650d Merge pull request #3239 from node-red/group-diff-fix
Group diff fix
2021-11-22 23:25:19 +00:00
Nick O'Leary
ed6afcd802 Merge pull request #3251 from node-red/regex-debug
Handle RegExp types in Debug sidebar
2021-11-22 23:24:55 +00:00
Nick O'Leary
082d4fe8e1 Merge pull request #3252 from node-red/disable-hidden-flow
Only toggle disabled workspace flag if on activeWorkspace
2021-11-22 23:24:39 +00:00
Nick O'Leary
cd23b44506 Merge pull request #3253 from node-red/clear-disabled-status
Do not show status for disabled nodes
2021-11-22 23:24:18 +00:00
Nick O'Leary
46b6b024b9 Merge pull request #3269 from node-red/auth-id
Include full user object on login audit events
2021-11-22 23:24:00 +00:00
Nick O'Leary
cb88cc35e5 Merge pull request #3261 from node-red/fix-delay-2nd-output-when-in-per-topic-mode
fix 2nd output when in rate limit per topic modes
2021-11-22 23:23:35 +00:00
Nick O'Leary
75c0c44809 Merge pull request #3265 from kazuhitoyokoi/master-fixtourguide
Set dimension value for tour guide
2021-11-22 23:23:03 +00:00
Nick O'Leary
a091b82ba9 Merge pull request #3263 from node-red/ti-fix
Avoid redundant initialisation of TypedInput type
2021-11-22 23:20:40 +00:00
Nick O'Leary
a3b8f022e6 Merge pull request #3270 from bonanitech/node-port-label-color
Don't let themes change flow port label color
2021-11-22 23:20:30 +00:00
Nick O'Leary
279fcb7c51 Merge pull request #3267 from node-red-hitachi/fix-link-target-select
fix to show link target when selected
2021-11-22 23:20:04 +00:00
Mauricio Bonani
49a9376073 Change flow port label CSS variable 2021-11-22 16:24:14 -05:00
Nick O'Leary
96840ede56 Include full user object on login audit events 2021-11-22 19:58:48 +00:00
Hiroyasu Nishiyama
7e7f481f99 fix to show link target when selected 2021-11-19 17:14:22 +09:00
Kazuhito Yokoi
3edbf52bc6 Set dimension value for tour guide 2021-11-18 15:38:49 +09:00
Nick O'Leary
fba6e801fc Avoid redundant initialisation of TypedInput type 2021-11-16 14:32:09 +00:00
Nick O'Leary
720a163273 Bump dev version 2021-11-15 23:38:01 +00:00
Nick O'Leary
a9b12e5172 Add search history to main search box 2021-11-15 23:24:57 +00:00
Dave Conway-Jones
6ac0c0a367 fix 2nd output when in rate limit per topic modes
to fix issue found in #3260
2021-11-15 14:05:16 +00:00
Kazuhito Yokoi
300402d253 Add Japanese translation for tour button 2021-11-12 18:15:57 +09:00
Kazuhito Yokoi
0d9bfae503 Add Japanese translations to tour guide for first flow 2021-11-11 13:02:52 +09:00
Nick O'Leary
bfe0d3b8a3 Do not show status for disabled nodes
Fixes #3249
2021-11-08 21:12:13 +00:00
Nick O'Leary
5fdd9c0546 Only toggle disabled workspace flag if on activeWorkspace
Fixes #3244
2021-11-08 20:38:34 +00:00
Nick O'Leary
b6570a16b8 Handle RegExp types in Debug sidebar
Fixes #3245
2021-11-08 20:24:10 +00:00
Kazuhito Yokoi
8e2d3ea16f Remove unused messages in language catalog 2021-11-08 13:46:24 +09:00
Kazuhito Yokoi
bc2c81f058 Add Japanese translations for link call, mqtt and file nodes 2021-11-08 13:38:17 +09:00
Nick O'Leary
3e0f080ea7 Merge pull request #3242 from node-red/inject-fix
Do not modify inject node props in oneditprepare
2021-11-01 21:40:47 +00:00
Nick O'Leary
679e07189d Do not modify inject node props in oneditprepare
Fixes #3241
2021-11-01 20:10:52 +00:00
Nick O'Leary
a38ebef100 Display group changes property in diff view 2021-10-28 12:14:56 +01:00
Nick O'Leary
b8ad6475e1 Strip off env property if it is empty - avoid inflated flow files 2021-10-28 12:14:34 +01:00
Nick O'Leary
0f0cb3ac6d Fix setting object env var properties so they can be undone 2021-10-28 12:14:03 +01:00
Nick O'Leary
d3efb9d7cc Merge pull request #3238 from node-red/treeLister-gutter-fix
Fix treeList gutter calculation to handle floating gutters
2021-10-28 10:46:04 +01:00
Nick O'Leary
84a237d3f5 Fix treeList gutter calculation to handle floating gutters
Fixes #3234
2021-10-28 10:44:50 +01:00
Nick O'Leary
e6de52eede Merge pull request #3236 from hardillb/http-basic-username-only
HTTP Basic Auth should always add : between username and password even if empty
2021-10-28 10:14:02 +01:00
Nick O'Leary
98aee964d7 Merge pull request #3237 from node-red/remove-de-style
Remove styling from de locale files
2021-10-28 10:11:54 +01:00
Nick O'Leary
570e5442e0 Remove styling from de locale files
Fixes #3230
2021-10-28 10:10:22 +01:00
Ben Hardill
b77a2dc353 Better fix 2021-10-28 10:08:28 +01:00
Ben Hardill
87af31de20 HTTP Basic Auth should always add : to username
fix for #3235
2021-10-28 09:18:17 +01:00
Nick O'Leary
cfe201dbe1 Bump for 2.1.3 2021-10-26 10:42:53 +01:00
Nick O'Leary
6ccdab35e0 Merge pull request #3227 from node-red/reload-node-settings
Refresh editor settings whenever a node is added or enabled
2021-10-26 09:51:41 +01:00
Nick O'Leary
55b9f36b45 Merge pull request #3229 from node-red/spinner-css
Revert spinner css change that made it shrink in some cases
2021-10-26 09:51:31 +01:00
Nick O'Leary
fbcb1130c9 Merge pull request #3221 from hardillb/disable-tours
Add environment variable to enable/disable tours
2021-10-26 09:47:17 +01:00
Nick O'Leary
d4f7a6d2bc Revert spinner css change that made it shrink in some cases 2021-10-26 09:44:32 +01:00
Nick O'Leary
8a19f71abe Refresh editor settings whenever a node is added or enabled
Fixes #3217

This ensures any node-provided settings are loaded and ready
for use by the nodes
2021-10-25 20:48:01 +01:00
Nick O'Leary
fb153757b5 Merge pull request #3225 from node-red/i18n-fix
Fix loading non-default language files leaving runtime in wrong locale
2021-10-25 16:06:12 +01:00
Nick O'Leary
4f175fc93e Fix loading non-default language files leaving runtime in wrong locale 2021-10-25 15:58:07 +01:00
Nick O'Leary
2d4ca7cec0 Merge pull request #3224 from node-red/import-msg-fix
Fix import notification message when importing config nodes
2021-10-25 15:12:46 +01:00
Nick O'Leary
bf0ea89969 Fix import notification message when importing config nodes 2021-10-25 15:08:30 +01:00
Nick O'Leary
073f0c2a20 Merge pull request #3223 from node-red/ti-fix
Handle changing types of TypedInput repeatedly
2021-10-25 13:55:38 +01:00
Nick O'Leary
ba83be9062 Handle changing types of TypedInput repeatedly
Fixes #3222
2021-10-25 13:54:42 +01:00
Ben Hardill
2e7188ea4f Add environment variable to enable/disable tours
NODE_RED_ENABLE_TOURS

Also had to patch editor-api/lib/editor/themes.js to pass
`editorTheme.tours` to the editor.
2021-10-25 11:25:31 +01:00
Nick O'Leary
5a012182d9 Update gen-publish script to update 'next' tag for main releases 2021-10-25 10:05:23 +01:00
Nick O'Leary
b855438af6 Fix changelog 2021-10-25 09:42:44 +01:00
Nick O'Leary
2ffea143e7 Bump for 2.1.2 2021-10-25 09:38:32 +01:00
Duncan Bellamy
61d85b49e6 Remove bash dependency (#3216)
Change backticks to dollar sign and parentheses
2021-10-25 08:44:49 +01:00
Nick O'Leary
35f617e96c Merge pull request #3213 from GerwinvBeek/markdown-regex
Improved regex for markdown renderer
2021-10-24 23:04:20 +01:00
Nick O'Leary
6b6ad47c35 Merge pull request #3220 from node-red/ti-fixes
Fix TypedInput initialisation
2021-10-24 22:58:55 +01:00
Nick O'Leary
e57183ed0e Merge pull request #3219 from Steve-Mcl/mqtt-use-datatype
fix datatype in node config not used. fixes #3215
2021-10-24 22:58:36 +01:00
Nick O'Leary
ecfd61a822 Fix TypedInput initialisation handle 2021-10-24 22:53:22 +01:00
Steve-Mcl
153f87704b fix datatype in node config not used. fixes #3215 2021-10-24 22:21:44 +01:00
Gerwin van Beek
836f7d2163 Improved regex for markdown renderer 2021-10-22 17:05:37 +02:00
Nick O'Leary
d4d6f71cf4 Bump for 2.1.1 2021-10-22 09:27:52 +01:00
Nick O'Leary
42a9da006e Merge pull request #3212 from node-red/fix-tour-guide-width
Ensure tourGuide popover doesn't fall offscreen
2021-10-22 09:26:31 +01:00
Nick O'Leary
2bd5c4f527 Ensure tourGuide popover doesn't fall offscreen
Only handles the left hand edge - will need expanding to cover
the other edges as needed
2021-10-22 09:24:40 +01:00
Nick O'Leary
6a49b5c106 Merge pull request #3210 from node-red/fix-old-inject-migration
Fix issue with old inject nodes that migrated topic to 'string' type
2021-10-22 09:16:54 +01:00
Nick O'Leary
23e14d1b72 Merge pull request #3211 from node-red/cache-bust
Add cache-busting query params to index.mst
2021-10-22 09:16:45 +01:00
Nick O'Leary
f4f11c8884 Add cache-busting query params to index.mst 2021-10-22 09:14:01 +01:00
Nick O'Leary
2b220abdb7 Fix issue with old inject nodes that migrated topic to 'string' type 2021-10-22 09:01:24 +01:00
Nick O'Leary
c1d947ebe3 Merge pull request #3207 from node-red/fix-debug-all
Fix TypedInput validation of type without options
2021-10-21 14:45:09 +01:00
Nick O'Leary
d695cf392e Fix TypedInput validation of type without options
Fixes #3206
2021-10-21 14:44:15 +01:00
Kazuhito Yokoi
b55a8ef62a Merge branch 'dev' into dev-addjpn 2021-10-19 19:20:32 +09:00
Kazuhito Yokoi
36f099d68b Add Japanese translations for Node-RED v2.1.0-beta.1 2021-10-11 16:02:18 +09:00
169 changed files with 4248 additions and 4047 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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
the [forum](https://discourse.nodered.org) first.
All contributors need to sign the JS Foundation's Contributor License Agreement.
It is an online process and quick to do. You can read the details of the agreement
here: https://cla.js.foundation/node-red/node-red.
If you raise a pull-request without having signed the CLA, you will be prompted
to do so automatically.
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
It is an online process and quick to do. If you raise a pull-request without
having signed the CLA, you will be prompted to do so automatically.
### Code Branches

View File

@@ -583,7 +583,7 @@ module.exports = function(grunt) {
grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src;
var copyright = "/**\n"+
" * Copyright JS Foundation and other contributors, http://js.foundation\n"+
" * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+
" *\n"+
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+
" * you may not use this file except in compliance with the License.\n"+

View File

@@ -56,13 +56,13 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena
## 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/).
* 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

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "2.1.0",
"version": "2.2.2",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,65 +26,65 @@
}
],
"dependencies": {
"acorn": "8.5.0",
"acorn": "8.7.0",
"acorn-walk": "8.2.0",
"ajv": "8.6.3",
"ajv": "8.10.0",
"async-mutex": "0.3.2",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"body-parser": "1.19.1",
"cheerio": "1.0.0-rc.10",
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.4.1",
"cookie-parser": "1.4.5",
"cookie": "0.4.2",
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cronosjs": "1.7.1",
"denque": "2.0.1",
"express": "4.17.1",
"express": "4.17.2",
"express-session": "1.17.2",
"form-data": "4.0.0",
"fs-extra": "10.0.0",
"fs.notify": "0.0.4",
"got": "11.8.2",
"got": "11.8.3",
"hash-sum": "2.0.0",
"hpagent": "0.1.2",
"https-proxy-agent": "5.0.0",
"i18next": "21.3.1",
"i18next": "21.6.11",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "3.14.1",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.5",
"jsonata": "1.8.6",
"lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0",
"memorystore": "1.6.6",
"mime": "2.5.2",
"moment-timezone": "0.5.33",
"mqtt": "4.2.8",
"multer": "1.4.3",
"memorystore": "1.6.7",
"mime": "3.0.0",
"moment-timezone": "0.5.34",
"mqtt": "4.3.5",
"multer": "1.4.4",
"mustache": "4.2.0",
"node-red-admin": "^2.2.1",
"node-red-admin": "^2.2.3",
"nopt": "5.0.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"on-headers": "1.0.2",
"passport": "0.5.0",
"passport": "0.5.2",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.4.1",
"raw-body": "2.4.3",
"semver": "7.3.5",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.14.2",
"uglify-js": "3.15.1",
"uuid": "8.3.2",
"ws": "7.5.1",
"ws": "7.5.6",
"xml2js": "0.4.23"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
},
"devDependencies": {
"dompurify": "2.3.3",
"dompurify": "2.3.5",
"grunt": "1.4.1",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
@@ -104,20 +104,20 @@
"grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1",
"i18next-http-backend": "1.3.1",
"i18next-http-backend": "1.3.2",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "3.0.7",
"marked": "4.0.12",
"minami": "1.2.3",
"mocha": "9.1.2",
"mocha": "9.2.0",
"node-red-node-test-helper": "^0.2.7",
"nodemon": "2.0.13",
"nodemon": "2.0.15",
"proxy": "^1.0.2",
"sass": "1.43.2",
"sass": "1.49.7",
"should": "13.2.3",
"sinon": "11.1.2",
"stoppable": "^1.1.0",
"supertest": "6.1.6"
"supertest": "6.2.2"
},
"engines": {
"node": ">=12"

View File

@@ -1,4 +1,4 @@
Copyright JS Foundation and other contributors, http://js.foundation
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Apache License
Version 2.0, January 2004

View File

@@ -141,7 +141,7 @@ function completeVerify(profile,done) {
Users.authenticate(profile).then(function(user) {
if (user) {
Tokens.create(user.username,"node-red-editor",user.permissions).then(function(tokens) {
log.audit({event: "auth.login",username:user.username,scope:user.permissions});
log.audit({event: "auth.login",user,username:user.username,scope:user.permissions});
user.tokens = tokens;
done(null,user);
});

View File

@@ -93,7 +93,7 @@ var passwordTokenExchange = function(client, username, password, scope, done) {
return logEntry.user !== username;
});
Tokens.create(username,client.id,scope).then(function(tokens) {
log.audit({event: "auth.login",username:username,client:client.id,scope:scope});
log.audit({event: "auth.login",user,username:username,client:client.id,scope:scope});
done(null,tokens.accessToken,null,{expires_in:tokens.expires_in});
});
} else {
@@ -146,7 +146,7 @@ function authenticateUserToken(req) {
} else {
reject();
}
});
}).catch(reject);
} else {
reject();
}
@@ -163,6 +163,9 @@ TokensStrategy.prototype.authenticate = function(req) {
authenticateUserToken(req).then(user => {
this.success(user,{scope:user.permissions});
}).catch(err => {
if (err) {
log.trace("token authentication failure: "+err.stack?err.stack:err)
}
this.fail(401);
});
}

View File

@@ -48,9 +48,10 @@ module.exports = {
var prevLang = i18n.i.language;
// Trigger a load from disk of the language if it is not the default
i18n.i.changeLanguage(lang, function(){
var catalog = loadResource(lang, namespace);
res.json(catalog||{});
i18n.i.changeLanguage(prevLang, function() {
var catalog = loadResource(lang, namespace);
res.json(catalog||{});
});
});
i18n.i.changeLanguage(prevLang);
}
}

View File

@@ -122,6 +122,7 @@ module.exports = {
}
if (req.body.active) {
opts.clearContext = req.body.hasOwnProperty('clearContext')?req.body.clearContext:true
runtimeAPI.projects.setActiveProject(opts).then(function() {
listProjects(req,res);
}).catch(function(err) {

View File

@@ -27,7 +27,8 @@ var defaultContext = {
tabicon: {
icon: "red/images/node-red-icon-black.svg",
colour: "#8f0000"
}
},
version: require(path.join(__dirname,"../../package.json")).version
},
header: {
title: "Node-RED",
@@ -227,6 +228,11 @@ module.exports = {
if (theme.theme) {
themeSettings.theme = theme.theme;
}
if (theme.hasOwnProperty("tours")) {
themeSettings.tours = theme.tours;
}
return themeApp;
},
context: async function() {

View File

@@ -90,6 +90,8 @@ function init(settings,_server,storage,runtimeAPI) {
auth.getToken,
auth.errorHandler
);
} else if (settings.adminAuth.tokens) {
adminApp.use(passport.initialize());
}
adminApp.post("/auth/revoke",auth.needsPermission(""),auth.revoke,apiUtil.errorHandler);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "2.1.0",
"version": "2.2.2",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,23 +16,23 @@
}
],
"dependencies": {
"@node-red/util": "2.1.0",
"@node-red/editor-client": "2.1.0",
"@node-red/util": "2.2.2",
"@node-red/editor-client": "2.2.2",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"body-parser": "1.19.1",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.2",
"express": "4.17.1",
"memorystore": "1.6.6",
"mime": "2.5.2",
"multer": "1.4.3",
"express": "4.17.2",
"memorystore": "1.6.7",
"mime": "3.0.0",
"multer": "1.4.4",
"mustache": "4.2.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.5.0",
"ws": "7.5.1"
"passport": "0.5.2",
"ws": "7.5.6"
},
"optionalDependencies": {
"bcrypt": "5.0.1"

View File

@@ -1,4 +1,4 @@
Copyright JS Foundation and other contributors, http://js.foundation
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Apache License
Version 2.0, January 2004

View File

@@ -59,6 +59,8 @@
"hideOtherFlows": "Hide other flows",
"showAllFlows": "Show all flows",
"hideAllFlows": "Hide all flows",
"hiddenFlows": "List __count__ hidden flow",
"hiddenFlows_plural": "List __count__ hidden flows",
"showLastHiddenFlow": "Show last hidden flow",
"listFlows": "List flows",
"listSubflows": "List subflows",
@@ -73,6 +75,8 @@
"view": {
"view": "View",
"grid": "Grid",
"storeZoom": "Restore zoom level on load",
"storePosition": "Restore scroll position on load",
"showGrid": "Show grid",
"snapGrid": "Snap to grid",
"gridSize": "Grid size",
@@ -90,6 +94,7 @@
"palette": {
"show": "Show palette"
},
"edit": "Edit",
"settings": "Settings",
"userSettings": "User Settings",
"nodes": "Nodes",
@@ -668,7 +673,8 @@
"unusedConfigNodes": "Unused configuration nodes",
"invalidNodes": "Invalid nodes",
"uknownNodes": "Unknown nodes",
"unusedSubflows": "Unused subflows"
"unusedSubflows": "Unused subflows",
"hiddenFlows": "Hidden flows"
}
},
"help": {
@@ -890,6 +896,8 @@
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "No matches found",
"addNode": "add a node..."
},
@@ -1087,7 +1095,8 @@
"not-git": "Not a git repository",
"no-resource": "Repository not found",
"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": {
"confirm": "Are you sure you want to delete this project?"
@@ -1135,6 +1144,7 @@
"defaultValue": "Default value"
},
"tourGuide": {
"takeATour": "Take a tour",
"start": "Start",
"next": "Next"
},

View File

@@ -54,7 +54,16 @@
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
"addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加",
"hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
"hideAllFlows": "全てのフローを非表示",
"hiddenFlows": "__count__ 個の非表示のフロー一覧",
"hiddenFlows_plural": "__count__ 個の非表示のフロー一覧",
"showLastHiddenFlow": "最後に非表示にしたフローを表示",
"listFlows": "フロー一覧",
"listSubflows": "サブフロー一覧",
"status": "状態",
"enabled": "有効",
"disabled": "無効",
@@ -66,6 +75,8 @@
"view": {
"view": "表示",
"grid": "グリッド",
"storeZoom": "読み込み時に拡大/縮小のレベルを復元",
"storePosition": "読み込み時にスクロール位置を復元",
"showGrid": "グリッドを表示",
"snapGrid": "ノードの配置を補助",
"gridSize": "グリッドの大きさ",
@@ -83,6 +94,7 @@
"palette": {
"show": "パレットを表示"
},
"edit": "編集",
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
@@ -105,6 +117,7 @@
"editPalette": "パレットの管理",
"other": "その他",
"showTips": "ヒントを表示",
"showWelcomeTours": "新バージョンのガイドツアーを表示",
"help": "Node-REDウェブサイト",
"projects": "プロジェクト",
"projects-new": "新規",
@@ -116,7 +129,20 @@
"groupSelection": "選択部分をグループ化",
"ungroupSelection": "選択部分をグループ解除",
"groupMergeSelection": "選択部分をマージ",
"groupRemoveSelection": "グループから削除"
"groupRemoveSelection": "グループから削除",
"arrange": "配置",
"alignLeft": "左揃え",
"alignCenter": "左右中央揃え",
"alignRight": "右揃え",
"alignTop": "上揃え",
"alignMiddle": "上下中央揃え",
"alignBottom": "下揃え",
"distributeHorizontally": "左右に整列",
"distributeVertically": "上下に整列",
"moveToBack": "最背面へ移動",
"moveToFront": "最前面へ移動",
"moveBackwards": "背面へ移動",
"moveForwards": "前面へ移動"
}
},
"actions": {
@@ -451,7 +477,8 @@
"global": "グローバル",
"workspace": "ワークスペース",
"selectAll": "全てのノードを選択",
"selectAllConnected": "接続された全てのノードを選択",
"selectNone": "選択を外す",
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
@@ -461,10 +488,13 @@
"moveNode": "選択したノードを移動(移動量大)",
"toggleSidebar": "サイドバーの表示/非表示",
"togglePalette": "パレットの表示/非表示",
"copyNode": "選択したノードをコピー",
"cutNode": "選択したノードを切り取り",
"copyNode": "ノードをコピー",
"cutNode": "ノードを切り取り",
"pasteNode": "ノードを貼り付け",
"copyGroupStyle": "グループ様式をコピー",
"pasteGroupStyle": "グループ様式を貼り付け",
"undoChange": "変更操作を戻す",
"redoChange": "変更操作をやり直し",
"searchBox": "ノードを検索",
"managePalette": "パレットの管理",
"actionList": "動作一覧"
@@ -519,7 +549,8 @@
"nodeEnabled_plural": "ノードを有効化しました:",
"nodeDisabled": "ノードを無効化しました:",
"nodeDisabled_plural": "ノードを無効化しました:",
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました"
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました",
"unknownNodeRegistered": "ノードの読み込みエラー: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "パレットの管理",
@@ -642,7 +673,8 @@
"unusedConfigNodes": "未使用の設定ノード",
"invalidNodes": "不正なノード",
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー"
"unusedSubflows": "未使用のサブフロー",
"hiddenFlows": "非表示のフロー"
}
},
"help": {
@@ -864,6 +896,8 @@
"addTitle": "要素を追加"
},
"search": {
"history": "検索履歴",
"clear": "全て削除",
"empty": "一致したものが見つかりませんでした",
"addNode": "ノードを追加..."
},
@@ -1061,7 +1095,8 @@
"not-git": "Gitリポジトリではありません",
"no-resource": "リポジトリが見つかりません",
"cant-get-ssh-key-path": "エラー! 選択したSSHキーのパスを取得できません。",
"unexpected_error": "予期しないエラー"
"unexpected_error": "予期しないエラー",
"clearContext": "プロジェクトを切り替る際にコンテキストを初期化"
},
"delete": {
"confirm": "プロジェクトを削除しても良いですか?"
@@ -1108,6 +1143,11 @@
"preview": "UIプレビュー",
"defaultValue": "デフォルト値"
},
"tourGuide": {
"takeATour": "ツアーを開始",
"start": "開始",
"next": "次へ"
},
"languages": {
"de": "ドイツ語",
"en-US": "英語",
@@ -1116,5 +1156,137 @@
"ru": "ロシア語",
"zh-CN": "中国語(簡体)",
"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": "ナビゲータ表示切替"
}
}

View File

@@ -225,7 +225,7 @@
"compact": "紧凑",
"formatted": "已格式化",
"copy": "导出到剪贴板",
"export": "到处到库",
"export": "导出到库",
"exportAs": "导出为",
"overwrite": "替换",
"exists": "<p><b>\"__file__\"</b>已存在</p><p>是否要替换它?</p>"

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "2.1.0",
"version": "2.2.2",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -684,6 +684,13 @@ RED.history = (function() {
peek: function() {
return undoHistory[undoHistory.length-1];
},
replace: function(ev) {
if (undoHistory.length === 0) {
RED.history.push(ev);
} else {
undoHistory[undoHistory.length-1] = ev;
}
},
clear: function() {
undoHistory = [];
redoHistory = [];

View File

@@ -38,6 +38,8 @@ RED.i18n = (function() {
defaultNS: "editor",
fallbackLng: ['en-US'],
returnObjects: true,
keySeparator: ".",
nsSeparator: ":",
interpolation: {
unescapeSuffix: 'HTML',
escapeValue: false,

View File

@@ -38,7 +38,9 @@
},
"red-ui-workspace": {
"backspace": "core:delete-selection",
"ctrl-backspace": "core:delete-selection-and-reconnect",
"delete": "core:delete-selection",
"ctrl-delete": "core:delete-selection-and-reconnect",
"enter": "core:edit-selected-node",
"ctrl-enter": "core:go-to-selection",
"ctrl-c": "core:copy-selection-to-internal-clipboard",

View File

@@ -15,6 +15,9 @@
**/
RED.nodes = (function() {
var PORT_TYPE_INPUT = 1;
var PORT_TYPE_OUTPUT = 0;
var node_defs = {};
var linkTabMap = {};
@@ -597,6 +600,14 @@ RED.nodes = (function() {
RED.events.emit('nodes:add',n);
}
function addLink(l) {
if (nodeLinks[l.source.id]) {
const isUnique = nodeLinks[l.source.id].out.every(function(link) {
return link.sourcePort !== l.sourcePort || link.target.id !== l.target.id
})
if (!isUnique) {
return
}
}
links.push(l);
if (l.source) {
// Possible the node hasn't been added yet
@@ -805,7 +816,6 @@ RED.nodes = (function() {
var removedGroups = [];
if (ws) {
delete workspaces[id];
allNodes.removeTab(id);
delete linkTabMap[id];
workspacesOrder.splice(workspacesOrder.indexOf(id),1);
var i;
@@ -843,6 +853,7 @@ RED.nodes = (function() {
for (i=removedGroups.length-1; i>=0; i--) {
removeGroup(removedGroups[i]);
}
allNodes.removeTab(id);
RED.events.emit('flows:remove',ws);
}
return {nodes:removedNodes,links:removedLinks, groups: removedGroups};
@@ -1097,6 +1108,11 @@ RED.nodes = (function() {
// Until we know how that can happen, add a filter here to remove them
node.nodes = node.nodes.filter(function(n) { return !!n }).map(function(n) { return n.id });
}
if (n.type === "tab" || n.type === "group") {
if (node.env && node.env.length === 0) {
delete node.env;
}
}
if (n._def.category != "config") {
node.x = n.x;
node.y = n.y;
@@ -2453,6 +2469,144 @@ RED.nodes = (function() {
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 {
init: function() {
RED.events.on("registry:node-type-added",function(type) {
@@ -2534,7 +2688,7 @@ RED.nodes = (function() {
add: addNode,
remove: removeNode,
clear: clear,
detachNodes: detachNodes,
moveNodesForwards: moveNodesForwards,
moveNodesBackwards: moveNodesBackwards,
moveNodesToFront: moveNodesToFront,
@@ -2546,7 +2700,20 @@ RED.nodes = (function() {
addLink: addLink,
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,
removeWorkspace: removeWorkspace,
getWorkspaceOrder: function() { return workspacesOrder },
@@ -2620,6 +2787,7 @@ RED.nodes = (function() {
getAllFlowNodes: getAllFlowNodes,
getAllUpstreamNodes: getAllUpstreamNodes,
getAllDownstreamNodes: getAllDownstreamNodes,
getNodeIslands: getNodeIslands,
createExportableNodeSet: createExportableNodeSet,
createCompleteNodeSet: createCompleteNodeSet,
updateConfigNodeUsers: updateConfigNodeUsers,

View File

@@ -475,31 +475,33 @@ var RED = (function() {
var typeList;
var info;
if (topic == "notification/node/added") {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+id,
success: function(data) {
appendNodeConfig(data);
}
RED.settings.refreshSettings(function(err, data) {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+id,
success: function(data) {
appendNodeConfig(data);
}
});
});
});
});
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
loadIconList();
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
loadIconList();
})
} else if (topic == "notification/node/removed") {
for (i=0;i<msg.length;i++) {
m = msg[i];
@@ -512,27 +514,29 @@ var RED = (function() {
loadIconList();
} else if (topic == "notification/node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+msg.id,
success: function(data) {
appendNodeConfig(data);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
}
});
}
RED.settings.refreshSettings(function(err, data) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+msg.id,
success: function(data) {
appendNodeConfig(data);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
}
});
}
});
}
} else if (topic == "notification/node/disabled") {
if (msg.types) {
@@ -552,8 +556,7 @@ var RED = (function() {
$(".red-ui-header-toolbar").show();
RED.sidebar.show(":first");
RED.sidebar.show(":first", true);
setTimeout(function() {
loader.end();
@@ -586,7 +589,7 @@ var RED = (function() {
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
]});
}
menuOptions.push({id:"menu-item-edit-menu", label:"Edit", options: [
menuOptions.push({id:"menu-item-edit-menu", label:RED._("menu.label.edit"), options: [
{id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"},
{id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"},
null,

View File

@@ -125,7 +125,7 @@ RED.settings = (function () {
load(done);
}
var load = function(done) {
var refreshSettings = function(done) {
$.ajax({
headers: {
"Accept": "application/json"
@@ -135,6 +135,23 @@ RED.settings = (function () {
url: 'settings',
success: function (data) {
setProperties(data);
done(null, data);
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = "";
}
RED.user.login(function() { refreshSettings(done); });
} else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
}
}
});
}
var load = function(done) {
refreshSettings(function(err, data) {
if (!err) {
if (!RED.settings.user || RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
@@ -147,18 +164,8 @@ RED.settings = (function () {
console.log("D3",d3.version);
console.groupEnd();
loadUserSettings(done);
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = "";
}
RED.user.login(function() { load(done); });
} else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
}
}
});
})
};
function loadUserSettings(done) {
@@ -234,6 +241,7 @@ RED.settings = (function () {
init: init,
load: load,
loadUserSettings: loadUserSettings,
refreshSettings: refreshSettings,
set: set,
get: get,
remove: remove,

View File

@@ -160,18 +160,19 @@ RED.actionList = (function() {
createDialog();
}
dialog.slideDown(300);
searchInput.searchBox('value',v)
searchInput.searchBox('value',v);
searchResults.editableList('empty');
results = [];
var actions = RED.actions.list();
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) {
action.label = action.id.replace(/:/,": ").replace(/-/g," ").replace(/(^| )./g,function() { return arguments[0].toUpperCase()});
action._label = action.label.toLowerCase();
searchResults.editableList('addItem',action)
})
searchResults.editableList('addItem',action);
});
RED.events.emit("actionList:open");
visible = true;
}

View File

@@ -1,33 +1,39 @@
RED.actions = (function() {
var actions = {
}
};
function addAction(name,handler) {
function addAction(name,handler,options) {
if (typeof handler !== 'function') {
throw new Error("Action handler not a function");
}
if (actions[name]) {
throw new Error("Cannot override existing action");
}
actions[name] = handler;
actions[name] = {
handler: handler,
options: options,
};
}
function removeAction(name) {
delete actions[name];
}
function getAction(name) {
return actions[name];
return actions[name].handler;
}
function invokeAction() {
var args = Array.prototype.slice.call(arguments);
var name = args.shift();
if (actions.hasOwnProperty(name)) {
actions[name].apply(null, args);
var handler = actions[name].handler;
handler.apply(null, args);
}
}
function listActions() {
var result = [];
var missing = [];
Object.keys(actions).forEach(function(action) {
var def = actions[action];
var shortcut = RED.keyboard.getShortcut(action);
var isUser = false;
if (shortcut) {
@@ -35,13 +41,38 @@ RED.actions = (function() {
} else {
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({
id:action,
scope:shortcut?shortcut.scope:undefined,
key:shortcut?shortcut.key:undefined,
user:isUser
})
})
user:isUser,
label: def.label,
options: def.options,
});
});
return result;
}
return {

View File

@@ -71,6 +71,7 @@ RED.clipboard = (function() {
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-download
@@ -81,6 +82,7 @@ RED.clipboard = (function() {
var data = $("#red-ui-clipboard-dialog-export-text").val();
downloadData("flows.json", data);
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-export
@@ -95,6 +97,7 @@ RED.clipboard = (function() {
$( this ).dialog( "close" );
copyText(flowData);
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
RED.view.focus();
} else {
var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
var selectedPath = activeLibraries[activeTab].getSelected();
@@ -110,6 +113,7 @@ RED.clipboard = (function() {
contentType: "application/json; charset=utf-8"
}).done(function() {
$(dialog).dialog( "close" );
RED.view.focus();
RED.notify(RED._("library.exportedToLibrary"),"success");
}).fail(function(xhr,textStatus,err) {
if (xhr.status === 401) {
@@ -171,6 +175,7 @@ RED.clipboard = (function() {
}
}
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-import-conflict
@@ -203,6 +208,7 @@ RED.clipboard = (function() {
// console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
$( this ).dialog( "close" );
RED.view.focus();
}
}
],
@@ -940,7 +946,8 @@ RED.clipboard = (function() {
if (truncated) {
msg += "_truncated";
}
$("#red-ui-clipboard-hidden").val(value).focus().select();
var clipboardHidden = $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo(document.body);
clipboardHidden.val(value).focus().select();
var result = document.execCommand("copy");
if (result && element) {
var popover = RED.popover.create({
@@ -954,14 +961,13 @@ RED.clipboard = (function() {
},1000);
popover.open();
}
$("#red-ui-clipboard-hidden").val("");
clipboardHidden.remove();
if (currentFocus) {
$(currentFocus).focus();
}
return result;
}
function importNodes(nodesStr,addFlow) {
var newNodes = nodesStr;
if (typeof nodesStr === 'string') {
@@ -1236,8 +1242,6 @@ RED.clipboard = (function() {
init: function() {
setupDialogs();
$('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
RED.actions.add("core:show-export-dialog",showExportNodes);
RED.actions.add("core:show-import-dialog",showImportNodes);

View File

@@ -21,7 +21,7 @@
* value: String : the value to insert if selected
* label: String|DOM Element : the label to display in the dropdown.
* }
*
*
*/
$.widget( "nodered.autoComplete", {
@@ -62,7 +62,7 @@
maxHeight: 200,
class: "red-ui-autoComplete-container",
options: completions,
onselect: (opt) => { this.element.val(opt.value); this.element.focus() },
onselect: (opt) => { this.element.val(opt.value); this.element.focus(); this.element.trigger("change") },
onclose: () => { this.completionMenuShown = false; delete this.menu; this.element.focus()}
});
this.menu.show({

View File

@@ -350,6 +350,15 @@ RED.popover = (function() {
}
}
target.on("remove", function (ev) {
if (timer) {
clearTimeout(timer);
}
if (active) {
active = false;
setTimeout(closePopup,delay.hide);
}
});
if (trigger === 'hover') {
target.on('mouseenter',function(e) {
clearTimeout(timer);

View File

@@ -117,6 +117,8 @@ RED.tabs = (function() {
menuOptions = options.menu()
} else if (Array.isArray(options.menu)) {
menuOptions = options.menu;
} else if (typeof options.menu === 'function') {
menuOptions = options.menu();
}
menu = RED.menu.init({options: menuOptions});
menu.attr("id",options.id+"-menu");
@@ -555,28 +557,24 @@ RED.tabs = (function() {
}
}
li.one("transitionend", function(evt) {
li.remove();
if (tabs[id].pinned) {
pinnedTabsCount--;
}
if (options.onremove) {
options.onremove(tabs[id]);
}
delete tabs[id];
updateTabWidths();
if (collapsibleMenu) {
collapsibleMenu.remove();
collapsibleMenu = null;
}
})
li.addClass("hide-tab");
li.width(0);
li.remove();
if (tabs[id].pinned) {
pinnedTabsCount--;
}
if (options.onremove) {
options.onremove(tabs[id]);
}
delete tabs[id];
updateTabWidths();
if (collapsibleMenu) {
collapsibleMenu.remove();
collapsibleMenu = null;
}
}
function findPreviousVisibleTab(li) {
if (!li) {
li = ul.find("li.active").parent();
li = ul.find("li.active");
}
var previous = li.prev();
while(previous.length > 0 && previous.hasClass("hide-tab")) {
@@ -586,9 +584,9 @@ RED.tabs = (function() {
}
function findNextVisibleTab(li) {
if (!li) {
li = ul.find("li.active").parent();
li = ul.find("li.active");
}
var next = ul.find("li.active").next();
var next = li.next();
while(next.length > 0 && next.hasClass("hide-tab")) {
next = next.next();
}
@@ -809,15 +807,18 @@ RED.tabs = (function() {
event.preventDefault();
removeTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
}
if (tab.hideable) {
li.addClass("red-ui-tabs-closeable")
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
closeLink.append('<i class="fa fa-times" />');
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
closeLink.append('<i class="fa fa-eye" />');
closeLink.append('<i class="fa fa-eye-slash" />');
closeLink.on("click",function(event) {
event.preventDefault();
hideTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
}
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
@@ -826,7 +827,8 @@ RED.tabs = (function() {
$('<i class="red-ui-tabs-badge-selected fa fa-check-circle"></i>').appendTo(badges);
}
link.attr("title",tab.label);
// link.attr("title",tab.label);
RED.popover.tooltip(link,function() { return tab.label})
if (options.onadd) {
options.onadd(tab);
@@ -945,7 +947,6 @@ RED.tabs = (function() {
renameTab: function(id,label) {
tabs[id].label = label;
var tab = ul.find("a[href='#"+id+"']");
tab.attr("title",label);
tab.find("span.red-ui-text-bidi-aware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label));
updateTabWidths();
},

View File

@@ -340,7 +340,7 @@
if (child.depth !== parent.depth+1) {
child.depth = parent.depth+1;
// var labelPaddingWidth = ((child.gutter ? child.gutter[0].offsetWidth + 2 : 0) + (child.depth * 20));
var labelPaddingWidth = ((child.gutter?child.gutter.width()+2:0)+(child.depth*20));
var labelPaddingWidth = (((child.gutter&&!child.gutter.hasClass("red-ui-treeList-gutter-float"))?child.gutter.width()+2:0)+(child.depth*20));
child.treeList.labelPadding.width(labelPaddingWidth+'px');
if (child.element) {
$(child.element).css({
@@ -562,8 +562,9 @@
}).appendTo(label)
}
// var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(depth*20);
var labelPaddingWidth = (item.gutter ? item.gutter[0].offsetWidth + 2 : 0) + (depth * 20)
var labelPaddingWidth = ((item.gutter&&!item.gutter.hasClass("red-ui-treeList-gutter-float"))?item.gutter.width()+2:0)+(depth*20);
item.treeList.labelPadding = $('<span>').css({
display: "inline-block",
"flex-shrink": 0,

View File

@@ -419,7 +419,8 @@
}
nlsd = true;
var that = this;
this.identifier = this.element.attr('id') || "TypedInput-"+Math.floor(Math.random()*100);
if (this.options.debug) { console.log(this.identifier,"Create",{defaultType:this.options.default, value:this.element.val()}) }
this.disarmClick = false;
this.input = $('<input class="red-ui-typedInput-input" type="text"></input>');
this.input.insertAfter(this.element);
@@ -533,9 +534,9 @@
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="red-ui-typedInput-icon fa fa-caret-down"></i></span></button>').appendTo(this.uiSelect);
this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
RED.popover.tooltip(this.optionSelectLabel,function() {
return that.optionValue;
});
// RED.popover.tooltip(this.optionSelectLabel,function() {
// return that.optionValue;
// });
this.optionSelectTrigger.on("click", function(event) {
event.preventDefault();
event.stopPropagation();
@@ -555,7 +556,8 @@
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect);
this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton);
this.type(this.options.default||this.typeList[0].value);
this.type(this.typeField.val() || this.options.default||this.typeList[0].value);
this.typeChanged = !!this.options.default;
}catch(err) {
console.log(err.stack);
}
@@ -803,6 +805,7 @@
var that = this;
var currentType = this.type();
this.typeMap = {};
var firstCall = (this.typeList === undefined);
this.typeList = types.map(function(opt) {
var result;
if (typeof opt === 'string') {
@@ -827,10 +830,14 @@
}
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
this.type(this.typeList[0].value);
if (!firstCall) {
this.type(this.typeList[0].value);
}
} else {
this.propertyType = null;
this.type(currentType);
if (!firstCall) {
this.type(currentType);
}
}
if (this.typeList.length === 1 && !this.typeList[0].icon && (!this.typeList[0].label || this.typeList[0].showLabel === false)) {
this.selectTrigger.hide()
@@ -846,7 +853,10 @@
},
value: function(value) {
var that = this;
var opt = this.typeMap[this.propertyType];
// If the default type has been set to an invalid type, then on first
// creation, the current propertyType will not exist. Default to an
// empty object on the assumption the corrent type will be set shortly
var opt = this.typeMap[this.propertyType] || {};
if (!arguments.length) {
var v = this.input.val();
if (opt.export) {
@@ -854,27 +864,38 @@
}
return v;
} else {
if (this.options.debug) { console.log(this.identifier,"----- SET VALUE ------",value) }
var selectedOption = [];
var valueToCheck = value;
if (opt.options) {
var checkValues = [value];
if (opt.hasValue && opt.parse) {
var parts = opt.parse(value);
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
value = parts.value;
valueToCheck = parts.option || parts.value;
}
var checkValues = [valueToCheck];
if (opt.multiple) {
selectedOption = [];
checkValues = value.split(",");
checkValues = valueToCheck.split(",");
}
checkValues.forEach(function(value) {
checkValues.forEach(function(valueToCheck) {
for (var i=0;i<opt.options.length;i++) {
var op = opt.options[i];
if (typeof op === "string") {
if (op === value || op === ""+value) {
if (op === valueToCheck || op === ""+valueToCheck) {
selectedOption.push(that.activeOptions[op]);
break;
}
} else if (op.value === value) {
} else if (op.value === valueToCheck) {
selectedOption.push(op);
break;
}
}
})
if (this.options.debug) { console.log(this.identifier,"set value to",value) }
this.input.val(value);
if (!opt.multiple) {
if (selectedOption.length === 0) {
@@ -899,15 +920,16 @@
return this.propertyType;
} else {
var that = this;
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
var previousValue = null;
var opt = this.typeMap[type];
if (opt && this.propertyType !== type) {
// If previousType is !null, then this is a change of the type, rather than the initialisation
var previousType = this.typeMap[this.propertyType];
var typeChanged = !!previousType;
previousValue = this.input.val();
if (typeChanged) {
if (previousType && this.typeChanged) {
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
if (previousType.options && opt.hasValue !== true) {
this.oldValues[previousType.value] = previousValue;
} else if (previousType.hasValue === false) {
@@ -917,27 +939,37 @@
}
if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) {
if (this.oldValues.hasOwnProperty(opt.value)) {
if (this.options.debug) { console.log(this.identifier,"restored previous (1)",this.oldValues[opt.value]) }
this.input.val(this.oldValues[opt.value]);
} else {
} else if (opt.options) {
// No old value for the option type.
// It is possible code has called 'value' then 'type'
// to set the selected option. This is what the Inject/Switch/Change
// nodes did before 2.1.
// So we need to be careful to not reset the value if it is a valid option.
var validOptions = isOptionValueValid(opt,previousValue);
if (previousValue && validOptions) {
if (this.options.debug) { console.log(this.identifier,{previousValue,opt,validOptions}) }
if ((previousValue || previousValue === '') && validOptions) {
if (this.options.debug) { console.log(this.identifier,"restored previous (2)") }
this.input.val(previousValue);
} else {
if (typeof opt.default === "string") {
if (this.options.debug) { console.log(this.identifier,"restored previous (3)",opt.default) }
this.input.val(opt.default);
} else if (Array.isArray(opt.default)) {
if (this.options.debug) { console.log(this.identifier,"restored previous (4)",opt.default.join(",")) }
this.input.val(opt.default.join(","))
} else {
if (this.options.debug) { console.log(this.identifier,"restored previous (5)") }
this.input.val("");
}
}
} else {
if (this.options.debug) { console.log(this.identifier,"restored default/blank",opt.default||"") }
this.input.val(opt.default||"")
}
} else {
if (this.options.debug) { console.log(this.identifier,"restored old/default/blank") }
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
}
if (previousType.autoComplete) {
@@ -945,6 +977,7 @@
}
}
this.propertyType = type;
this.typeChanged = true;
if (this.typeField) {
this.typeField.val(type);
}
@@ -1040,7 +1073,8 @@
} else {
var selectedOption = this.optionValue||opt.options[0];
if (opt.parse) {
var parts = opt.parse(this.input.val(),selectedOption);
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
var parts = opt.parse(this.input.val(),selectedOptionObj);
if (parts.option) {
selectedOption = parts.option;
if (!this.activeOptions.hasOwnProperty(selectedOption)) {
@@ -1064,6 +1098,7 @@
this._updateOptionSelectLabel(this.activeOptions[selectedOption]);
}
} else if (selectedOption) {
if (this.options.debug) { console.log(this.identifier,"HERE",{optionValue:selectedOption.value}) }
this.optionValue = selectedOption.value;
this._updateOptionSelectLabel(selectedOption);
} else {

View File

@@ -333,6 +333,19 @@ RED.deploy = (function() {
var unknownNodes = [];
var invalidNodes = [];
RED.nodes.eachConfig(function(node) {
if (node.valid === undefined) {
RED.editor.validateNode(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) {
if (!node.valid && !node.d) {
invalidNodes.push(getNodeInfo(node));

View File

@@ -554,6 +554,8 @@ RED.diff = (function() {
color: "#DDAA99",
defaults:{name:{value:""}}
}
} else if (node.type === "group") {
def = RED.group.def;
} else {
def = {};
}
@@ -763,16 +765,15 @@ RED.diff = (function() {
}
}
if (node.hasOwnProperty('x')) {
if (localNode) {
if (localNode.x !== node.x || localNode.y !== node.y) {
if (localNode.x !== node.x || localNode.y !== node.y || localNode.w !== node.w || localNode.h !== node.h ) {
localChanged = true;
localChanges++;
}
}
if (remoteNode) {
if (remoteNode.x !== node.x || remoteNode.y !== node.y) {
if (remoteNode.x !== node.x || remoteNode.y !== node.y|| remoteNode.w !== node.w || remoteNode.h !== node.h) {
remoteChanged = true;
remoteChanges++;
}
@@ -790,7 +791,12 @@ RED.diff = (function() {
localCell.addClass("red-ui-diff-status-"+(localChanged?"changed":"unchanged"));
$('<span class="red-ui-diff-status">'+(localChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(localCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(localCell);
propertyElements['local.position'] = RED.utils.createObjectElement({x:localNode.x,y:localNode.y},
var localPosition = {x:localNode.x,y:localNode.y};
if (localNode.hasOwnProperty('w')) {
localPosition.w = localNode.w;
localPosition.h = localNode.h;
}
propertyElements['local.position'] = RED.utils.createObjectElement(localPosition,
{
path: "position",
exposeApi: true,
@@ -811,7 +817,12 @@ RED.diff = (function() {
if (remoteNode) {
$('<span class="red-ui-diff-status">'+(remoteChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(remoteCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(remoteCell);
propertyElements['remote.position'] = RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y},
var remotePosition = {x:remoteNode.x,y:remoteNode.y};
if (remoteNode.hasOwnProperty('w')) {
remotePosition.w = remoteNode.w;
remotePosition.h = remoteNode.h;
}
propertyElements['remote.position'] = RED.utils.createObjectElement(remotePosition,
{
path: "position",
exposeApi: true,
@@ -883,11 +894,11 @@ RED.diff = (function() {
}
}
}
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='w'&&p!=='h'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
if (def.defaults) {
properties = properties.concat(Object.keys(def.defaults));
}
if (node.type !== 'tab') {
if (node.type !== 'tab' && node.type !== "group") {
properties = properties.concat(['inputLabels','outputLabels']);
}
if ( ((localNode && localNode.hasOwnProperty('icon')) || (remoteNode && remoteNode.hasOwnProperty('icon'))) &&

View File

@@ -744,7 +744,16 @@ RED.editor = (function() {
delete cn.__label__;
});
select.append('<option value="_ADD_"'+(value===""?" selected":"")+'>'+RED._("editor.addNewType", {type:type})+'</option>');
var label = type;
if (typeof node_def.paletteLabel !== "undefined") {
try {
label = RED.utils.sanitize((typeof node_def.paletteLabel === "function" ? node_def.paletteLabel.call(node_def) : node_def.paletteLabel)||type);
} catch(err) {
console.log("Definition error: "+type+".paletteLabel",err);
}
}
select.append('<option value="_ADD_"'+(value===""?" selected":"")+'>'+RED._("editor.addNewType", {type:label})+'</option>');
window.setTimeout(function() { select.trigger("change");},50);
}
}

View File

@@ -577,7 +577,7 @@ RED.editor.codeEditor.monaco = (function() {
createMonacoCompletionItem("set (flow context)", 'flow.set("${1:name}", ${1:value});','Set a value in flow context',range),
createMonacoCompletionItem("get (global context)", 'global.get("${1:name}");','Get a value from global context',range),
createMonacoCompletionItem("set (global context)", 'global.set("${1:name}", ${1:value});','Set a value in global context',range),
createMonacoCompletionItem("get (env)", 'env.get("${1:name}");','Get env variable value',range),
createMonacoCompletionItem("get (env)", 'env.get("${1|NR_NODE_ID,NR_NODE_NAME,NR_NODE_PATH,NR_GROUP_ID,NR_GROUP_NAME,NR_FLOW_ID,NR_FLOW_NAME|}");','Get env variable value',range),
createMonacoCompletionItem("cloneMessage (RED.util)", 'RED.util.cloneMessage(${1:msg});',
["```typescript",
"RED.util.cloneMessage<T extends registry.NodeMessage>(msg: T): T",

View File

@@ -247,7 +247,7 @@
var currentExpression = expressionEditor.getValue();
var expr;
var usesContext = false;
var legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
$(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
try {
expr = jsonata(currentExpression);

View File

@@ -81,7 +81,8 @@
clearTimeout: true,
setInterval: true,
clearInterval: true
}
},
extraLibs: options.extraLibs
});
if (options.cursor) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);

View File

@@ -35,9 +35,10 @@
editState.changed = true;
}
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 (icon !== node.icon) {
if ((icon !== node.icon) &&
(icon !== "")) {
editState.changes.icon = node.icon;
node.icon = icon;
editState.changed = true;
@@ -101,14 +102,14 @@
if (showLabel) {
// Default to show label
if (node.l !== false) {
editState.changes.l = node.l
editState.changes.l = node.l;
editState.changed = true;
}
node.l = false;
} else {
// Node has showLabel:false (eg link nodes)
if (node.hasOwnProperty('l') && node.l) {
editState.changes.l = node.l
editState.changes.l = node.l;
editState.changed = true;
}
delete node.l;
@@ -118,20 +119,20 @@
if (showLabel) {
// Default to show label
if (node.hasOwnProperty('l') && !node.l) {
editState.changes.l = node.l
editState.changes.l = node.l;
editState.changed = true;
}
delete node.l;
} else {
if (!node.l) {
editState.changes.l = node.l
editState.changes.l = node.l;
editState.changed = true;
}
node.l = true;
}
}
}
}
};
});
function buildAppearanceForm(container,node) {
@@ -164,10 +165,10 @@
var categories = RED.palette.getCategories();
categories.sort(function(A,B) {
return A.label.localeCompare(B.label);
})
});
categories.forEach(function(cat) {
categorySelector.append($("<option/>").val(cat.id).text(cat.label));
})
});
categorySelector.append($("<option/>").attr('disabled',true).text("---"));
categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory")));
@@ -180,7 +181,7 @@
$("#subflow-appearance-input-category").width(250);
$("#subflow-appearance-input-custom-category").hide();
}
})
});
$("#subflow-appearance-input-category").val(node.category||"subflows");
var userCount = 0;
@@ -204,7 +205,7 @@
$("#node-input-show-label").toggleButton({
enabledLabel: RED._("editor.show"),
disabledLabel: RED._("editor.hide")
})
});
if (!node.hasOwnProperty("l")) {
// Show label unless def.showLabel set to false
@@ -230,7 +231,7 @@
"#E9967A", "#F3B567", "#FDD0A2",
"#FDF0C2", "#FFAAAA", "#FFCC66",
"#FFF0F0", "#FFFFFF"
]
];
RED.editor.colorPicker.create({
id: "red-ui-editor-node-color",
@@ -245,9 +246,9 @@
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
nodeDiv.css('border-color',borderColor);
}
})
});
}
@@ -264,7 +265,7 @@
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(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);
@@ -292,7 +293,7 @@
RED.popover.tooltip(iconButton, function() {
return $("#red-ui-editor-node-icon").val() || RED._("editor.default");
})
});
$('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow);
}
@@ -417,11 +418,11 @@
});
rows.sort(function(A,B) {
return A.i-B.i;
})
});
rows.forEach(function(r,i) {
r.r.find("label").text((i+1)+".");
r.r.appendTo(outputsDiv);
})
});
if (rows.length === 0) {
buildLabelRow("output",i,"").appendTo(outputsDiv);
} else {
@@ -467,7 +468,7 @@
clear.on("click", function(evt) {
evt.preventDefault();
input.val("");
})
});
}
return result;
}
@@ -501,6 +502,12 @@
}
var v = $(this).val();
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;
});
@@ -509,6 +516,12 @@
changes.outputLabels = node.outputLabels;
node.outputLabels = newValue;
changed = true;
// trigger redraw of dirty port labels
if (node.type === "subflow") {
RED.view.redraw();
}
}
return changed;
}

View File

@@ -55,9 +55,15 @@
}
});
}
if (!isSameObj(old_env, new_env)) {
node.env = new_env;
if (!old_env && new_env.length === 0) {
delete node.env;
} else if (!isSameObj(old_env, new_env)) {
editState.changes.env = node.env;
if (new_env.length === 0) {
delete node.env;
} else {
node.env = new_env;
}
editState.changed = true;
}
}

View File

@@ -590,12 +590,14 @@ RED.group = (function() {
markDirty(group);
}
function getNodes(group,recursive) {
function getNodes(group,recursive,excludeGroup) {
var nodes = [];
group.nodes.forEach(function(n) {
nodes.push(n);
if (n.type !== 'group' || !excludeGroup) {
nodes.push(n);
}
if (recursive && n.type === 'group') {
nodes = nodes.concat(getNodes(n,recursive))
nodes = nodes.concat(getNodes(n,recursive,excludeGroup))
}
})
return nodes;

View File

@@ -49,15 +49,15 @@ RED.keyboard = (function() {
"]": 221,
"{": 219,// <- QWERTY specific
"}": 221 // <- QWERTY specific
}
};
var metaKeyCodes = {
16: true,
17: true,
18: true,
91: true,
93: true
}
var actionToKeyMap = {}
};
var actionToKeyMap = {};
var defaultKeyMap = {};
// FF generates some different keycodes because reasons.
@@ -65,7 +65,7 @@ RED.keyboard = (function() {
59:186,
61:187,
173:189
}
};
function migrateOldKeymap() {
// pre-0.18
@@ -80,7 +80,7 @@ RED.keyboard = (function() {
}
function getUserKey(action) {
return RED.settings.get('editor.keymap',{})[action]
return RED.settings.get('editor.keymap',{})[action];
}
function mergeKeymaps(defaultKeymap, themeKeymap) {
@@ -105,7 +105,7 @@ RED.keyboard = (function() {
scope:scope,
key:key,
user:false
})
});
}
}
}
@@ -115,13 +115,13 @@ RED.keyboard = (function() {
if (themeKeymap.hasOwnProperty(action)) {
if (!themeKeymap[action].key) {
// No key for this action - default is no keybinding
delete mergedKeymap[action]
delete mergedKeymap[action];
} else {
mergedKeymap[action] = [{
scope: themeKeymap[action].scope || "*",
key: themeKeymap[action].key,
user: false
}]
}];
if (mergedKeymap[action][0].scope === "workspace") {
mergedKeymap[action][0].scope = "red-ui-workspace";
}
@@ -179,7 +179,7 @@ RED.keyboard = (function() {
close: function() {
RED.menu.refreshShortcuts();
}
})
});
}
function revertToDefault(action) {
@@ -327,7 +327,7 @@ RED.keyboard = (function() {
scope:scope,
key:key,
user:false
}
};
}
if (!ondown) {
var userAction = getUserKey(cbdown);
@@ -350,7 +350,7 @@ RED.keyboard = (function() {
}
}
} else {
keys.push([key,mod])
keys.push([key,mod]);
}
var slot = handlers;
for (i=0;i<keys.length;i++) {
@@ -373,7 +373,7 @@ RED.keyboard = (function() {
//slot[key] = {scope: scope, ondown:cbdown};
}
slot.handlers = slot.handlers || [];
slot.handlers.push({scope:scope,ondown:cbdown})
slot.handlers.push({scope:scope,ondown:cbdown});
slot.scope = scope;
slot.ondown = cbdown;
}
@@ -390,12 +390,12 @@ RED.keyboard = (function() {
if (parsedKey) {
keys.push(parsedKey);
} else {
console.log("Unrecognised key specifier:",key)
console.log("Unrecognised key specifier:",key);
return;
}
}
} else {
keys.push([key,mod])
keys.push([key,mod]);
}
var slot = handlers;
for (i=0;i<keys.length;i++) {
@@ -417,7 +417,7 @@ RED.keyboard = (function() {
}
if (typeof slot.ondown === "string") {
if (typeof modifiers === 'boolean' && modifiers) {
actionToKeyMap[slot.ondown] = {user: modifiers}
actionToKeyMap[slot.ondown] = {user: modifiers};
} else {
delete actionToKeyMap[slot.ondown];
}
@@ -433,11 +433,11 @@ RED.keyboard = (function() {
function formatKey(key,plain) {
var formattedKey = isMac?key.replace(/ctrl-?/,"&#8984;"):key;
formattedKey = isMac?formattedKey.replace(/alt-?/,"&#8997;"):key;
formattedKey = formattedKey.replace(/shift-?/,"&#8679;")
formattedKey = formattedKey.replace(/left/,"&#x2190;")
formattedKey = formattedKey.replace(/up/,"&#x2191;")
formattedKey = formattedKey.replace(/right/,"&#x2192;")
formattedKey = formattedKey.replace(/down/,"&#x2193;")
formattedKey = formattedKey.replace(/shift-?/,"&#8679;");
formattedKey = formattedKey.replace(/left/,"&#x2190;");
formattedKey = formattedKey.replace(/up/,"&#x2191;");
formattedKey = formattedKey.replace(/right/,"&#x2192;");
formattedKey = formattedKey.replace(/down/,"&#x2193;");
if (plain) {
return formattedKey;
}
@@ -461,7 +461,6 @@ RED.keyboard = (function() {
var container = $(this);
var object = container.data('data');
if (!container.hasClass('keyboard-shortcut-entry-expanded')) {
endEditShortcut();
@@ -485,7 +484,7 @@ RED.keyboard = (function() {
}
$(this).toggleClass("input-error",!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);
scopeSelect.i18n();
@@ -495,7 +494,7 @@ RED.keyboard = (function() {
scopeSelect.val(object.scope||'*');
scopeSelect.on("change", function() {
keyInput.trigger("change");
})
});
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);
@@ -521,10 +520,13 @@ RED.keyboard = (function() {
id:object.id,
scope:shortcut?shortcut.scope: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);
})
});
keyInput.trigger("focus");
}
@@ -559,7 +561,7 @@ RED.keyboard = (function() {
delete object.scope;
} else {
keyDiv.parent().removeClass("keyboard-shortcut-entry-unassigned");
keyDiv.append(RED.keyboard.formatKey(key))
keyDiv.append(RED.keyboard.formatKey(key));
$("<span>").text(scope).appendTo(scopeDiv);
object.key = key;
object.scope = scope;
@@ -572,7 +574,7 @@ RED.keyboard = (function() {
userKeymap[object.id] = {
scope:shortcut.scope,
key:shortcut.key
}
};
RED.settings.set('editor.keymap',userKeymap);
}
}
@@ -588,13 +590,7 @@ RED.keyboard = (function() {
var item = $('<div class="keyboard-shortcut-entry">').appendTo(container);
container.data('data',object);
var text = object.id.replace(/(^.+:([a-z]))|(-([a-z]))/g,function() {
if (arguments[5] === 0) {
return arguments[2].toUpperCase();
} else {
return " "+arguments[4].toUpperCase();
}
});
var text = object.label;
var label = $('<div>').addClass("keyboard-shortcut-entry-text").text(text).appendTo(item);
var user = $('<i class="fa fa-user"></i>').prependTo(label);
@@ -629,14 +625,15 @@ RED.keyboard = (function() {
pane.find("#red-ui-settings-tab-keyboard-filter").searchBox({
delay: 100,
change: function() {
var filterValue = $(this).val().trim();
var filterValue = $(this).val().trim().toLowerCase();
if (filterValue === "") {
shortcutList.editableList('filter', null);
} else {
filterValue = filterValue.replace(/\s/g,"");
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();
shortcuts.sort(function(A,B) {
var Aid = A.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
var Bid = B.id.replace(/^.*:/,"").replace(/[ -]/g,"").toLowerCase();
return Aid.localeCompare(Bid);
var Akey = A.label;
var Bkey = B.label;
return Akey.localeCompare(Bkey);
});
knownShortcuts = new Set();
shortcuts.forEach(function(s) {

View File

@@ -1212,6 +1212,9 @@ RED.projects = (function() {
}
}).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);
$('<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') {
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.code !== 'credentials_load_failed') {
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.projects.settings.switchProject(name);
sendRequest({
@@ -1614,7 +1618,7 @@ RED.projects = (function() {
'*': done
},
}
},{active:true}).then(function() {
},{active:true, clearContext:clearContext}).then(function() {
dialog.dialog( "close" );
RED.events.emit("project:change", {name:name});
}).always(function() {
@@ -1687,7 +1691,7 @@ RED.projects = (function() {
dialogHeight = 590 - (750 - winHeight);
}
$(".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("open");
}

View File

@@ -22,6 +22,7 @@ RED.search = (function() {
var selected = -1;
var visible = false;
var searchHistory = [];
var index = {};
var currentResults = [];
var previousActiveElement;
@@ -52,10 +53,22 @@ RED.search = (function() {
}
l = l||n.label||n.name||n.id||"";
var properties = ['id','type','name','label','info'];
if (n._def && n._def.defaults) {
properties = properties.concat(Object.keys(n._def.defaults));
const node_def = n && n._def;
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++) {
if (n.hasOwnProperty(properties[i])) {
@@ -105,6 +118,7 @@ RED.search = (function() {
val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags);
val = extractFlag(val,"hidden",flags);
// uses:<node-id>
val = extractValue(val,"uses",flags);
@@ -150,7 +164,15 @@ RED.search = (function() {
continue;
}
}
if (flags.hasOwnProperty("hidden")) {
// Only tabs can be hidden
if (node.node.type !== 'tab') {
continue
}
if (!RED.workspaces.isHidden(node.node.id)) {
continue
}
}
if (flags.hasOwnProperty("unused")) {
var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
(isConfigNode && node.node.users.length === 0)
@@ -196,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() {
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);
@@ -204,7 +240,12 @@ RED.search = (function() {
change: function() {
searchResults.editableList('empty');
selected = -1;
currentResults = search($(this).val());
var value = $(this).val();
if (value === "") {
populateSearchHistory();
return;
}
currentResults = search(value);
if (currentResults.length > 0) {
for (i=0;i<Math.min(currentResults.length,25);i++) {
searchResults.editableList('addItem',currentResults[i])
@@ -276,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) {
reveal(currentResults[Math.max(0,selected)].node);
}
@@ -292,7 +338,32 @@ RED.search = (function() {
addItem: function(container,i,object) {
var node = object.node;
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")
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}));
@@ -347,6 +418,12 @@ RED.search = (function() {
}
function reveal(node) {
var searchVal = searchInput.val();
var existingIndex = searchHistory.indexOf(searchVal);
if (existingIndex > -1) {
searchHistory.splice(existingIndex,1);
}
searchHistory.unshift(searchInput.val());
hide();
RED.view.reveal(node.id);
}
@@ -365,9 +442,14 @@ RED.search = (function() {
if (dialog === null) {
createDialog();
} else {
searchResults.editableList('empty');
}
dialog.slideDown(300);
searchInput.searchBox('value',v)
if (!v || v === "") {
populateSearchHistory();
}
RED.events.emit("search:open");
visible = true;
}

View File

@@ -19,6 +19,15 @@ RED.sidebar = (function() {
var sidebar_tabs;
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) {
var options;
if (typeof title === "string") {
@@ -194,16 +203,16 @@ RED.sidebar = (function() {
RED.events.emit("sidebar:resize");
}
function showSidebar(id) {
function showSidebar(id, skipShowSidebar) {
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 (!containsTab(id) && knownTabs[id]) {
sidebar_tabs.addTab(knownTabs[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);
}
}
@@ -227,6 +236,7 @@ RED.sidebar = (function() {
if (tab.toolbar) {
$(tab.toolbar).show();
}
RED.settings.setLocal("last-sidebar-tab", tab.id)
},
onremove: function(tab) {
$(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");
showSidebar();
lastSessionSelectedTab = RED.settings.getLocal("last-sidebar-tab")
RED.sidebar.info.init();
RED.sidebar.help.init();
RED.sidebar.config.init();

View File

@@ -27,5 +27,7 @@ RED.state = {
PANNING: 10,
SELECTING_NODE: 11,
GROUP_DRAGGING: 12,
GROUP_RESIZE: 13
GROUP_RESIZE: 13,
DETACHED_DRAGGING: 14,
SLICING: 15
}

View File

@@ -64,15 +64,17 @@ RED.sidebar.help = (function() {
style: "compact",
delay: 100,
change: function() {
var val = $(this).val().toLowerCase();
if (val) {
const searchFor = $(this).val().toLowerCase();
if (searchFor) {
showTOC();
var c = treeList.treeList('filter',function(item) {
treeList.treeList('filter',function(item) {
if (item.depth === 0) {
return true;
}
return (item.nodeType && item.nodeType.indexOf(val) > -1) ||
(item.subflowLabel && item.subflowLabel.indexOf(val) > -1)
let found = item.nodeType && item.nodeType.toLowerCase().indexOf(searchFor) > -1;
found = found || item.subflowLabel && item.subflowLabel.toLowerCase().indexOf(searchFor) > -1;
found = found || item.palleteLabel && item.palleteLabel.toLowerCase().indexOf(searchFor) > -1;
return found;
},true)
} else {
treeList.treeList('filter',null);
@@ -224,17 +226,21 @@ RED.sidebar.help = (function() {
moduleNames.forEach(function(moduleName) {
var module = modules[moduleName];
var nodeTypes = [];
var setNames = Object.keys(module.sets);
const module = modules[moduleName];
const nodeTypes = [];
const moduleSets = module.sets;
const setNames = Object.keys(moduleSets);
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) {
const n = {_def:RED.nodes.getType(nodeType),type:nodeType}
n.name = getNodePaletteLabel(n);
nodeTypes.push({
id: "node-type:"+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);
}
function getNodeLabel(n) {
var div = $('<div>',{class:"red-ui-node-list-item"});
var icon = RED.utils.createNodeIcon(n).appendTo(div);
var label = n.name;
function getNodePaletteLabel(n) {
let label = n.name;
if (!label && n._def && n._def.paletteLabel) {
try {
label = (typeof n._def.paletteLabel === "function" ? n._def.paletteLabel.call(n._def) : n._def.paletteLabel)||"";
} catch (err) {
}
}
label = label || n.type;
$('<div>',{class:"red-ui-node-label"}).text(n.name||n.type).appendTo(icon);
return label || n.type;
}
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;
}
@@ -379,7 +388,7 @@ RED.sidebar.help = (function() {
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) {
tourHeader = '<div><button type="button" onclick="RED.actions.invoke(\'core:show-welcome-tour\')" class="red-ui-button">Take a tour</button></div>'
tourHeader = '<div><button type="button" onclick="RED.actions.invoke(\'core:show-welcome-tour\')" class="red-ui-button">' + RED._("tourGuide.takeATour") + '</button></div>';
}
}
var aboutHeader = '<div style="text-align:center;">'+tourHeader+'</div>'

View File

@@ -175,6 +175,7 @@ RED.sidebar.info.outliner = (function() {
n.d = true;
}
n.dirty = true;
n.dirtyStatus = true;
n.changed = true;
RED.events.emit("nodes:change",n);
groupHistoryEvent.events.push(historyEvent);
@@ -203,6 +204,7 @@ RED.sidebar.info.outliner = (function() {
n.d = true;
}
n.dirty = true;
n.dirtyStatus = true;
n.changed = true;
RED.events.emit("nodes:change",n);
RED.history.push(historyEvent);
@@ -272,6 +274,7 @@ RED.sidebar.info.outliner = (function() {
{label:RED._("sidebar.info.search.invalidNodes"), value: "is:invalid"},
{label:RED._("sidebar.info.search.uknownNodes"), value: "type:unknown"},
{label:RED._("sidebar.info.search.unusedSubflows"), value:"is:subflow is:unused"},
{label:RED._("sidebar.info.search.hiddenFlows"), value:"is:hidden"},
]
});
@@ -563,7 +566,7 @@ RED.sidebar.info.outliner = (function() {
}
}
function getGutter(n) {
var span = $("<span>",{class:"red-ui-info-outline-gutter"});
var span = $("<span>",{class:"red-ui-info-outline-gutter red-ui-treeList-gutter-float"});
var revealButton = $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-search"></i></button>').appendTo(span).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();

View File

@@ -256,6 +256,10 @@ RED.tourGuide = (function() {
}
$('<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 breadcrumbs = $('<div>',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
@@ -348,7 +352,12 @@ RED.tourGuide = (function() {
maxWidth: maxWidth+"px",
direction: direction,
})
setTimeout(function() {
var pos = popover.element.position()
if (pos.left < 0) {
popover.element.css({left: 0});
}
},100);
if (nextButton) {
setTimeout(function() {
nextButton.focus();
@@ -359,6 +368,8 @@ RED.tourGuide = (function() {
if (step.fallback) {
focus.one("mouseenter", function(evt) {
setTimeout(function() {
var pos = targetElement[0].getBoundingClientRect();
var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5);
focus.css({
width: (4*dimension)+"px",
height: (4*dimension)+"px"

View File

@@ -121,6 +121,13 @@ RED.userSettings = (function() {
// {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",
options: [

View File

@@ -27,7 +27,7 @@ RED.utils = (function() {
level: 'block', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:[^:\n]/);
let m = src.match(/:[^:\n]/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
@@ -53,7 +53,7 @@ RED.utils = (function() {
level: 'inline', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:/);
let m = src.match(/:/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
@@ -109,7 +109,7 @@ RED.utils = (function() {
window._marked.use({extensions: [descriptionList, description] } );
function renderMarkdown(txt) {
var rendered = _marked(txt);
var rendered = _marked.parse(txt);
var cleaned = DOMPurify.sanitize(rendered, {SAFE_FOR_JQUERY: true})
return cleaned;
}
@@ -142,6 +142,8 @@ RED.utils = (function() {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
} else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
} else if (value.hasOwnProperty('type') && value.type === 'regexp') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-string"></span>').text(value.data);
} else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
}
@@ -440,6 +442,8 @@ RED.utils = (function() {
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
} else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
} else if (typeHint === "regexp" || (obj.__enc__ && obj.type === 'regexp')) {
e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').text((typeof obj === "string")?obj:obj.data).appendTo(entryObj);
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);
} else if (typeHint === "internal" || (obj.__enc__ && obj.type === 'internal')) {

View File

@@ -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 {
init: function() {
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-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() })
},

File diff suppressed because it is too large Load Diff

View File

@@ -66,7 +66,7 @@ RED.workspaces = (function() {
var tabId = RED.nodes.id();
do {
workspaceIndex += 1;
} while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
} while ($("#red-ui-workspace-tabs li[flowname='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
ws = {
type: "tab",
@@ -79,12 +79,15 @@ RED.workspaces = (function() {
};
RED.nodes.addWorkspace(ws,targetIndex);
workspace_tabs.addTab(ws,targetIndex);
workspace_tabs.activateTab(tabId);
if (!skipHistoryEntry) {
RED.history.push({t:'add',workspaces:[ws],dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
}
}
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
RED.view.focus();
return ws;
}
@@ -208,65 +211,84 @@ RED.workspaces = (function() {
},
onhide: function(tab) {
hideStack.push(tab.id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
hiddenTabs[tab.id] = true;
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:hide",{workspace: tab.id})
},
onshow: function(tab) {
removeFromHideStack(tab.id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
delete hiddenTabs[tab.id];
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:show",{workspace: tab.id})
},
minimumActiveTabWidth: 150,
scrollable: true,
addButton: "core:add-flow",
addButtonCaption: RED._("workspace.addFlow"),
menu: [
{
id:"red-ui-tabs-menu-option-search-flows",
label: RED._("workspace.listFlows"),
onselect: "core:list-flows"
},
{
id:"red-ui-tabs-menu-option-search-subflows",
label: RED._("workspace.listSubflows"),
onselect: "core:list-subflows"
},
null,
{
id:"red-ui-tabs-menu-option-add-flow",
label: RED._("workspace.addFlow"),
onselect: "core:add-flow"
},
{
id:"red-ui-tabs-menu-option-add-flow-right",
label: RED._("workspace.addFlowToRight"),
onselect: "core:add-flow-to-right"
},
null,
{
id:"red-ui-tabs-menu-option-add-hide-flows",
label: RED._("workspace.hideFlow"),
onselect: "core:hide-flow"
},
{
id:"red-ui-tabs-menu-option-add-hide-other-flows",
label: RED._("workspace.hideOtherFlows"),
onselect: "core:hide-other-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
label: RED._("workspace.showAllFlows"),
onselect: "core:show-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-last-flow",
label: RED._("workspace.showLastHiddenFlow"),
onselect: "core:show-last-hidden-flow"
menu: function() {
var menuItems = [
{
id:"red-ui-tabs-menu-option-search-flows",
label: RED._("workspace.listFlows"),
onselect: "core:list-flows"
},
{
id:"red-ui-tabs-menu-option-search-subflows",
label: RED._("workspace.listSubflows"),
onselect: "core:list-subflows"
},
null,
{
id:"red-ui-tabs-menu-option-add-flow",
label: RED._("workspace.addFlow"),
onselect: "core:add-flow"
},
{
id:"red-ui-tabs-menu-option-add-flow-right",
label: RED._("workspace.addFlowToRight"),
onselect: "core:add-flow-to-right"
},
null,
{
id:"red-ui-tabs-menu-option-add-hide-flows",
label: RED._("workspace.hideFlow"),
onselect: "core:hide-flow"
},
{
id:"red-ui-tabs-menu-option-add-hide-other-flows",
label: RED._("workspace.hideOtherFlows"),
onselect: "core:hide-other-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
label: RED._("workspace.showAllFlows"),
onselect: "core:show-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-last-flow",
label: RED._("workspace.showLastHiddenFlow"),
onselect: "core:show-last-hidden-flow"
}
]
if (hideStack.length > 0) {
menuItems.unshift({
label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
onselect: "core:list-hidden-flows"
})
}
]
return menuItems;
}
});
workspaceTabCount = 0;
}
@@ -406,7 +428,9 @@ RED.workspaces = (function() {
}
}
})
RED.actions.add("core:list-hidden-flows",function() {
RED.actions.invoke("core:search","is:hidden ");
})
RED.actions.add("core:list-flows",function() {
RED.actions.invoke("core:search","type:tab ");
})
@@ -450,7 +474,7 @@ RED.workspaces = (function() {
var changes = { disabled: workspace.disabled };
workspace.disabled = disabled;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
if (id || activeWorkspace) {
if (!id || (id === activeWorkspace)) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
var historyEvent = {
@@ -531,11 +555,11 @@ RED.workspaces = (function() {
}
if (workspace_tabs.contains(id)) {
workspace_tabs.hideTab(id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
hiddenTabs[id] = true;
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
}
},
isHidden: function(id) {
return hideStack.includes(id)
},
show: function(id,skipStack,unhideOnly) {
if (!workspace_tabs.contains(id)) {
var sf = RED.nodes.subflow(id);
@@ -558,14 +582,11 @@ RED.workspaces = (function() {
}
workspace_tabs.activateTab(id);
}
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
delete hiddenTabs[id];
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
},
refresh: function() {
RED.nodes.eachWorkspace(function(ws) {
workspace_tabs.renameTab(ws.id,ws.label);
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
})
RED.nodes.eachSubflow(function(sf) {
if (workspace_tabs.contains(sf.id)) {

View File

@@ -197,6 +197,7 @@ $view-select-mode-background: $secondary-background-selected;
$view-grid-color: #eee;
$node-label-color: #333;
$node-port-label-color: #888;
$node-border: #999;
$node-border-unknown: #f33;
$node-border-placeholder: #aaa;

View File

@@ -360,6 +360,7 @@ button.red-ui-button-small
position: absolute;
top: -3000px;
}
.form-row .red-ui-editor-node-label-form-row {
margin: 5px 0 0 50px;
label {

View File

@@ -21,6 +21,13 @@
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
.red-ui-flow-node-label-italic {
font-style: italic;
@@ -47,7 +54,7 @@
.red-ui-flow-port-label {
stroke-width: 0;
fill: $secondary-text-color;
fill: $node-port-label-color;
font-size: 16px;
dominant-baseline: middle;
text-anchor: middle;

View File

@@ -187,7 +187,7 @@
ul.red-ui-menu-dropdown {
background: $header-menu-background;
border: 1px solid $header-menu-background;
width: 250px !important;
width: 260px !important;
margin-top: 0;
li a {
color: $header-menu-color;

View File

@@ -137,10 +137,10 @@
padding: 0;
border: 1px solid $form-input-border-color;
}
.ui-spinner input[type=text] {
.ui-spinner input {
background: $form-input-background;
margin: 0 17px 0 0;
padding: 8px;
padding: 6px;
border: none;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;

View File

@@ -204,6 +204,28 @@
font-style: italic;
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 {
color: $primary-text-color;

View File

@@ -389,7 +389,19 @@ i.red-ui-tab-icon {
vertical-align: top;
}
.red-ui-tab-hide {
.fa-eye-slash {
display: none;
}
&:hover {
.fa-eye-slash {
display: inline
}
.fa-eye {
display: none
}
}
}
.red-ui-tab-close {
display: none;
background: $tab-background-inactive;

View File

@@ -78,6 +78,12 @@
}
.red-ui-tourGuide-popover-description {
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-description {

View File

@@ -1,14 +1,23 @@
export default {
steps: [
{
title: "Create your first flow",
title: {
'en-US': 'Create your first flow',
'ja': 'はじめてのフローを作成'
},
width: 400,
description: 'This tutorial will guide you through creating your first flow',
description: {
'en-US': 'This tutorial will guide you through creating your first flow',
'ja': '本チュートリアルでは、はじめてのフローを作成する方法について説明します。'
},
nextButton: 'start'
},
{
element: "#red-ui-workspace .red-ui-tab-button.red-ui-tabs-add",
description: 'To add a new tab, click the <i class="fa fa-plus"></i> button',
description: {
'en-US': 'To add a new tab, click the <i class="fa fa-plus"></i> button',
'ja': '新しいタブを追加するため、 <i class="fa fa-plus"></i> ボタンをクリックします。'
},
wait: {
type: "dom-event",
event: "click",
@@ -18,7 +27,10 @@ export default {
{
element: '.red-ui-palette-node[data-palette-type="inject"]',
direction: 'right',
description: 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.',
description: {
'en-US': 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.',
'ja': 'パレットには、利用できる全てのードが一覧表示されます。injectードをワークスペースにドラッグします。'
},
fallback: 'inset-bottom-right',
wait: {
type: "nr-event",
@@ -38,7 +50,10 @@ export default {
{
element: '.red-ui-palette-node[data-palette-type="debug"]',
direction: 'right',
description: 'Next, drag a new Debug node into the workspace.',
description: {
'en-US': 'Next, drag a new Debug node into the workspace.',
'ja': '次に、debugードをワークスペースにドラッグします。'
},
fallback: 'inset-bottom-right',
wait: {
type: "nr-event",
@@ -57,7 +72,10 @@ export default {
},
{
element: function() { return $("#"+this.injectNode.id+" .red-ui-flow-port") },
description: 'Add a wire from the output of the Inject node to the input of the Debug node',
description: {
'en-US': 'Add a wire from the output of the Inject node to the input of the Debug node',
'ja': 'injectードの出力から、debugードの入力へワイヤーで接続します。'
},
fallback: 'inset-bottom-right',
wait: {
type: "nr-event",
@@ -69,7 +87,10 @@ export default {
},
{
element: "#red-ui-header-button-deploy",
description: 'Deploy your changes so the flow is active in the runtime',
description: {
'en-US': 'Deploy your changes so the flow is active in the runtime',
'ja': 'フローをランタイムで実行させるため、変更をデプロイします。'
},
width: 200,
wait: {
type: "dom-event",

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,151 +1,156 @@
export default {
version: "2.1.0",
version: "2.2.0",
steps: [
{
titleIcon: "fa fa-map-o",
title: { "en-US": "Welcome to Node-RED 2.1!" },
description: { "en-US": "Let's take a moment to discover the new features in this release." }
},
{
title: { "en-US": "A new Tour Guide" },
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>"+
"<p>You can choose not to see this tour in the future by disabling it under the View tab of User Settings.</p>" }
},
{
title: { "en-US": "New Edit menu" },
prepare() {
$("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-edit-menu").parent().addClass("open")
title: {
"en-US": "Welcome to Node-RED 2.2!",
"ja": "Node-RED 2.2へようこそ!"
},
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>" }
},
{
title: { "en-US": "Arranging nodes" },
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>" },
},
{
title: { "en-US": "Hiding tabs" },
element: "#red-ui-workspace-tabs > li.active",
description: { "en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-times"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.' },
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","");
description: {
"en-US": "Let's take a moment to discover the new features in this release.",
"ja": "本リリースの新機能を見つけてみましょう。"
}
},
{
title: { "en-US": "Tab menu" },
element: "#red-ui-workspace-tabs-menu",
description: { "en-US": '<p>The new tab menu also provides lots of new options for your tabs.</p>' },
interactive: false,
direction: "left",
prepare() {
$("#red-ui-workspace > .red-ui-tabs > .red-ui-tabs-menu a").trigger("click");
title: {
"en-US": "Search history",
"ja": "検索履歴"
},
description: {
"en-US": "<p>The Search dialog now keeps a history of your searches, making it easier to go back to a previous search.</p>",
"ja": "<p>検索ダイアログが検索履歴を保持するようになりました。これによって、過去の検索に戻りやすくなりました。</p>"
},
element: "#red-ui-search .red-ui-searchBox-form",
prepare(done) {
RED.search.show();
setTimeout(done,400);
},
complete() {
$(document).trigger("click");
}
},
{
title: { "en-US": "Flow and Group level environment variables" },
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>" },
},
{
prepare(done) {
RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties");
setTimeout(done,700);
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>" },
},
{
element: ".node-input-env-container-row",
direction: "left",
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>' },
complete(done) {
$("#node-dialog-cancel").trigger("click");
setTimeout(done,500);
}
},
{
title: {"en-US":"Link Call node added"},
prepare(done) {
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);
title: {
"en-US": "Remembering Zoom & Position",
"ja": "拡大/縮小のレベルや位置を記憶"
},
element: '[data-palette-type="link call"]',
direction: "right",
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>' },
},
{
title: {"en-US":"MQTT nodes support dynamic connections"},
prepare(done) {
$('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
description: {
"en-US": "<p>The editor has new options to restore the zoom level and scroll position when reloading the editor.</p>",
"ja": "<p>エディタを再読み込みした時に、拡大/縮小のレベルやスクロール位置を復元するための新しいオプションを利用できます。</p>"
},
element: '[data-palette-type="mqtt out"]',
direction: "right",
description: { "en-US": '<p>The <code>MQTT</code> nodes now support creating their connections and subscriptions dynamically.</p>' },
},
{
title: {"en-US":"File nodes renamed"},
element: function() { return $("#user-settings-view-store-position").parent()},
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>' },
},
{
title: {"en-US":"Deep copy option on Change node"},
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);
RED.actions.invoke("core:show-user-settings")
setTimeout(done,400);
},
complete(done) {
$("#node-dialog-cancel").trigger("click");
setTimeout(done,500);
$("#node-dialog-ok").trigger("click");
setTimeout(done,400);
},
element: function() {
return $(".node-input-rule-property-deepCopy").next()
},
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>' },
},
{
title: { "en-US": "And that's not all..." },
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>" }
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: {
"en-US": "Detaching nodes from a flow",
"ja": "フローからノードの切り離し"
},
image: "images/detach-repair.gif",
description: {
"en-US": `<p>If you want to remove a node from a flow without deleting it,
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: {
"en-US": "More wiring tricks",
"ja": "その他のワイヤー操作"
},
image: "images/slice.gif",
description: {
"en-US": `<p>A couple more wiring tricks to share.</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>`,
"ja": `<p>その他のいくつかのワイヤー操作</p>
<p>Ctrl (またはCmd)キーを押しながらワイヤーをクリックすることで、複数のワイヤーを選択できるようになりました。これによって、複数のワイヤーを一度に削除することが簡単になりました。</p>
<p>Ctrl (またはCmd)キーを押しながら、マウスの右ボタンを用いてドラッグすると、ワイヤーを切って削除できます。</p>`
}
},
{
title: {
"en-US": "Subflow Output Labels",
"ja": "サブフローの出力ラベル"
},
image: "images/subflow-labels.png",
description: {
"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>"
},
},
{
title: {
"en-US": "Node Updates",
"ja": "ノードの更新"
},
// image: "images/",
description: {
"en-US": `<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>`
}
}
]
}

View File

@@ -31,12 +31,12 @@ interface NodeStatus {
}
declare class node {
/**
* Send 1 or more messages asynchronously
* @param {object | object[]} msg The msg object
* @param {Boolean} [clone=true] Flag to indicate the `msg` should be cloned. Default = `true`
* @see node-red documentation [writing-functions: sending messages asynchronously](https://nodered.org/docs/user-guide/writing-functions#sending-messages-asynchronously)
*/
/**
* Send 1 or more messages asynchronously
* @param {object | object[]} msg The msg object
* @param {Boolean} [clone=true] Flag to indicate the `msg` should be cloned. Default = `true`
* @see node-red documentation [writing-functions: sending messages asynchronously](https://nodered.org/docs/user-guide/writing-functions#sending-messages-asynchronously)
*/
static send(msg:object|object[], clone?:Boolean): void;
/** Inform runtime this instance has completed its operation */
static done();
@@ -57,11 +57,13 @@ declare class node {
*/
static status(status:string|boolean|number);
/** the id of this node */
public readonly id:string;
public static readonly id:string;
/** 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 */
public readonly outputCount:number;
public static readonly outputCount:number;
}
declare class context {
/**
@@ -93,27 +95,27 @@ declare class context {
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
*/
static set(name: string | string[], value?: any | any[]);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param callback - (optional) Callback function (`(err) => {}`)
*/
static set(name: string | string[], value?: any | any[], callback?: Function);
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
*/
static set(name: string | string[], value?: any | any[], store?: string);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
* @param callback - (optional) Callback function (`(err) => {}`)
*/
@@ -158,27 +160,27 @@ declare class flow {
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
*/
static set(name: string | string[], value?: any | any[]);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param callback - (optional) Callback function (`(err) => {}`)
*/
static set(name: string | string[], value?: any | any[], callback?: Function);
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
*/
static set(name: string | string[], value?: any | any[], store?: string);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
* @param callback - (optional) Callback function (`(err) => {}`)
*/
@@ -225,32 +227,32 @@ declare class global {
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
*/
static set(name: string | string[], value?: any | any[]);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param callback - (optional) Callback function (`(err) => {}`)
*/
static set(name: string | string[], value?: any | any[], callback?: Function);
/**
* Set one or multiple values in context (synchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
*/
static set(name: string | string[], value?: any | any[], store?: string);
/**
* Set one or multiple values in context (asynchronous).
* @param name - Name (or array of names) to set in context
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param value - The value (or array of values) to store in context. If the value(s) are null/undefined, the context item(s) will be removed.
* @param store - (optional) Name of context store
* @param callback - (optional) Callback function (`(err) => {}`)
*/
static set(name: string | string[], value?: any | any[], store?: string, callback?: Function);
/** Get an array of the keys in the context store */
static keys(): Array<string>;
/** Get an array of the keys in the context store */
@@ -261,6 +263,20 @@ declare class global {
static keys(store: string, callback: Function);
}
declare class env {
/** Get an environment variable value */
static get(name:string);
/**
* Get an environment variable value
*
* Predefined node-red variables...
* * `NR_NODE_ID` - the ID of the node
* * `NR_NODE_NAME` - the Name of the node
* * `NR_NODE_PATH` - the Path of the node
* * `NR_GROUP_ID` - the ID of the containing group
* * `NR_GROUP_NAME` - the Name of the containing group
* * `NR_FLOW_ID` - the ID of the flow the node is on
* * `NR_FLOW_NAME` - the Name of the flow the node is on
* @param name Name of the environment variable to get
* @example
* ```const flowName = env.get("NR_FLOW_NAME");```
*/
static get(name:string) :string;
}

View File

@@ -7,7 +7,7 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<!--
Copyright JS Foundation and other contributors, http://js.foundation
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,24 +24,24 @@
<title>{{ page.title }}</title>
<link rel="icon" type="image/png" href="{{ page.favicon }}">
<link rel="mask-icon" href="{{ page.tabicon.icon }}" color="{{ page.tabicon.colour }}">
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css">
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="red/style.min.css">
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css?v={{ page.version }}">
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css?v={{ page.version }}">
<link rel="stylesheet" href="red/style.min.css?v={{ page.version }}">
{{#page.css}}
<link rel="stylesheet" href="{{.}}">
{{/page.css}}
{{#asset.vendorMonaco}}
<link rel="stylesheet" href="vendor/monaco/style.css">
<link rel="stylesheet" href="vendor/monaco/style.css?v={{ page.version }}">
{{/asset.vendorMonaco}}
</head>
<body spellcheck="false">
<div id="red-ui-editor"></div>
<script src="vendor/vendor.js"></script>
<script src="vendor/vendor.js?v={{ page.version }}"></script>
{{#asset.vendorMonaco}}
<script src="{{ asset.vendorMonaco }}"></script>
<script src="{{ asset.vendorMonaco }}?v={{ page.version }}"></script>
{{/asset.vendorMonaco}}
<script src="{{ asset.red }}"></script>
<script src="{{ asset.main }}"></script>
<script src="{{ asset.red }}?v={{ page.version }}"></script>
<script src="{{ asset.main }}?v={{ page.version }}"></script>
{{# page.scripts }}
<script src="{{.}}"></script>
{{/ page.scripts }}

View File

@@ -1,4 +1,4 @@
Copyright JS Foundation and other contributors, http://js.foundation
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Apache License
Version 2.0, January 2004

View File

@@ -234,7 +234,7 @@
}
} else if (v[i].vt === "jsonata") {
try{jsonata(v[i].v);}catch(e){return false;}
} else if ([i].vt === "json") {
} else if (v[i].vt === "json") {
try{JSON.parse(v[i].v);}catch(e){return false;}
}
}
@@ -353,14 +353,16 @@
},
oneditprepare: function() {
var node = this;
var payloadType = node.payloadType;
if (node.payloadType == null) {
if (node.payload == "") {
node.payloadType = "date";
payloadType = "date";
} else {
node.payloadType = "str";
payloadType = "str";
}
} else if (node.payloadType === 'string' || node.payloadType === 'none') {
node.payloadType = "str";
payloadType = "str";
}
$("#inject-time-type-select").on("change", function() {
@@ -539,7 +541,7 @@
var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"})
.css("width","calc(70% - 30px)")
.appendTo(row)
.typedInput({default:prop.vt,types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']});
.typedInput({default:prop.vt || 'str',types:['flow','global','str','num','bool','json','bin','date','jsonata','env','msg']});
propertyName.typedInput('value',prop.p);
propertyValue.typedInput('value',prop.v);
@@ -557,12 +559,12 @@
var payload = {
p:'payload',
v: node.payload ? node.payload : '',
vt:node.payloadType ? node.payloadType : 'date'
vt:payloadType ? payloadType : 'date'
};
var topic = {
p:'topic',
v: node.topic ? node.topic : '',
vt:'string'
vt:'str'
}
node.props = [payload,topic];
}
@@ -573,11 +575,16 @@
if (newProp.v === undefined) {
if (prop.p === 'payload') {
newProp.v = node.payload ? node.payload : '';
newProp.vt = node.payloadType ? node.payloadType : 'date';
newProp.vt = payloadType ? payloadType : 'date';
} else if (prop.p === 'topic' && prop.vt === "str") {
newProp.v = node.topic ? node.topic : '';
}
}
if (newProp.vt === "string") {
// Fix bug in pre 2.1 where an old Inject node might have
// a migrated rule with type 'string' not 'str'
newProp.vt = "str";
}
eList.editableList('addItem',newProp);
}
@@ -683,9 +690,9 @@
this.topic = "";
var result = getProps(items, true);
this.props = result.props;
if(result.payloadType) { this.payloadType = result.payloadType; };
if(result.payload) { this.payload = result.payload; };
if(result.topic) { this.topic = result.topic; };
if(result.hasOwnProperty('payloadType')) { this.payloadType = result.payloadType; };
if(result.hasOwnProperty('payload')) { this.payload = result.payload; };
if(result.hasOwnProperty('topic')) { this.topic = result.topic; };
},
button: {
enabled: function() {

View File

@@ -75,16 +75,12 @@ module.exports = function(RED) {
node.repeaterSetup = function () {
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.repeat = this.repeat * 1000;
if (RED.settings.verbose) {
this.log(RED._("inject.repeat", this));
}
this.debug(RED._("inject.repeat", this));
this.interval_id = setInterval(function() {
node.emit("input", {});
}, this.repeat);
} else if (this.crontab) {
if (RED.settings.verbose) {
this.log(RED._("inject.crontab", this));
}
this.debug(RED._("inject.crontab", this));
this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})});
}
};
@@ -148,10 +144,8 @@ module.exports = function(RED) {
}
if (this.interval_id != null) {
clearInterval(this.interval_id);
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
} else if (this.cronjob != null) {
this.cronjob.stop();
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
delete this.cronjob;
}
};

View File

@@ -7,6 +7,7 @@ module.exports = function(RED) {
var debuglength = RED.settings.debugMaxLength || 1000;
var useColors = RED.settings.debugUseColors || false;
util.inspect.styles.boolean = "red";
const { hasOwnProperty } = Object.prototype;
function DebugNode(n) {
var hasEditExpression = (n.targetType === "jsonata");
@@ -107,7 +108,7 @@ module.exports = function(RED) {
}
})
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) {
if (hasOwnProperty.call(msg, "status") && hasOwnProperty.call(msg.status, "source") && hasOwnProperty.call(msg.status.source, "id") && (msg.status.source.id === node.id)) {
done();
return;
}
@@ -118,17 +119,17 @@ module.exports = function(RED) {
var st = (typeof output === 'string') ? output : util.inspect(output);
var fill = "grey";
var shape = "dot";
if (typeof output === 'object' && output.hasOwnProperty("fill") && output.hasOwnProperty("shape") && output.hasOwnProperty("text")) {
if (typeof output === 'object' && hasOwnProperty.call(output, "fill") && hasOwnProperty.call(output, "shape") && hasOwnProperty.call(output, "text")) {
fill = output.fill;
shape = output.shape;
st = output.text;
}
if (node.statusType === "auto") {
if (msg.hasOwnProperty("error")) {
if (hasOwnProperty.call(msg, "error")) {
fill = "red";
st = msg.error.message;
}
if (msg.hasOwnProperty("status")) {
if (hasOwnProperty.call(msg, "status")) {
fill = msg.status.fill || "grey";
shape = msg.status.shape || "ring";
st = msg.status.text || "";
@@ -194,7 +195,7 @@ module.exports = function(RED) {
function sendDebug(msg) {
// don't put blank errors in sidebar (but do add to logs)
//if ((msg.msg === "") && (msg.hasOwnProperty("level")) && (msg.level === 20)) { return; }
//if ((msg.msg === "") && (hasOwnProperty.call(msg, "level")) && (msg.level === 20)) { return; }
msg = RED.util.encodeObject(msg,{maxLength:debuglength});
RED.comms.publish("debug",msg);
}
@@ -280,6 +281,18 @@ module.exports = function(RED) {
root: path.join(__dirname,"lib","debug"),
dotfiles: 'deny'
};
res.sendFile(req.params[0], options);
try {
res.sendFile(
req.params[0],
options,
err => {
if (err) {
res.sendStatus(404);
}
}
)
} catch(err) {
res.sendStatus(404);
}
});
};

View File

@@ -91,21 +91,21 @@
<div id="func-tab-init" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 5;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-body" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 5;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-finalize" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 5;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
@@ -512,6 +512,7 @@
return function(e) {
e.preventDefault();
var value = editor.getValue();
var extraLibs = that.libs || [];
RED.editor.editJavaScript({
value: value,
width: "Infinity",
@@ -523,7 +524,8 @@
setTimeout(function() {
editor.focus();
},300);
}
},
extraLibs: extraLibs
})
}
}

View File

@@ -112,6 +112,7 @@ module.exports = function(RED) {
"var node = {"+
"id:__node__.id,"+
"name:__node__.name,"+
"path:__node__.path,"+
"outputCount:__node__.outputCount,"+
"log:__node__.log,"+
"error:__node__.error,"+
@@ -163,6 +164,7 @@ module.exports = function(RED) {
__node__: {
id: node.id,
name: node.name,
path: node._path,
outputCount: node.outputs,
log: function() {
node.log.apply(node, arguments);
@@ -234,8 +236,7 @@ module.exports = function(RED) {
},
env: {
get: function(envVar) {
var flow = node._flow;
return flow.getSetting(envVar);
return RED.util.getSetting(node, envVar);
}
},
setTimeout: function () {
@@ -345,6 +346,7 @@ module.exports = function(RED) {
var node = {
id:__node__.id,
name:__node__.name,
path:__node__.path,
outputCount:__node__.outputCount,
log:__node__.log,
error:__node__.error,
@@ -367,6 +369,7 @@ module.exports = function(RED) {
var node = {
id:__node__.id,
name:__node__.name,
path:__node__.path,
outputCount:__node__.outputCount,
log:__node__.log,
error:__node__.error,

View File

@@ -114,7 +114,8 @@
timeout: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
timeoutUnits: {value:"seconds"},
rate: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
nbRateUnits: {value:"1", required:false, validate:RED.validators.regex(/\d+|/)},
nbRateUnits: {value:"1", required:false,
validate:function(v) { return v === undefined || (RED.validators.number(v) && (v >= 0)); }},
rateUnits: {value: "second"},
randomFirst: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
randomLast: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},

View File

@@ -372,6 +372,7 @@ module.exports = function(RED) {
hit = false;
for (var b in node.buffer) { // check if already in queue
if (msg.topic === node.buffer[b].msg.topic) {
if (node.outputs === 2) { send([null,node.buffer[b].msg]) }
node.buffer[b].done();
node.buffer[b] = {msg, send, done}; // if so - replace existing entry
hit = true;

View File

@@ -86,7 +86,7 @@ module.exports = function(RED) {
});
var cmd = arg.shift();
/* istanbul ignore else */
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
node.debug(cmd+" ["+arg+"]");
child = spawn(cmd,arg,node.spawnOpt);
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
var unknownCommand = (child.pid === undefined);
@@ -136,7 +136,7 @@ module.exports = function(RED) {
}
else {
/* istanbul ignore else */
if (RED.settings.verbose) { node.log(arg); }
node.debug(arg);
child = exec(arg, node.execOpt, function (error, stdout, stderr) {
var msg2, msg3;
delete msg.payload;
@@ -155,7 +155,7 @@ module.exports = function(RED) {
if (error.signal) { msg3.payload.signal = error.signal; }
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
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") {
msg3 = RED.util.cloneMessage(msg);

View File

@@ -58,7 +58,7 @@ module.exports = function(RED) {
else {
var n = parseFloat(value);
if (!isNaN(n)) {
if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband")) {
if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband" || this.func === "narrowbandEq")) {
if (node.start === '') { node.previous[t] = n; }
else { node.previous[t] = node.start; }
}

View File

@@ -288,7 +288,7 @@ module.exports = function(RED) {
//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) {
node.brokerConn.publish(msg, done); // send the message
@@ -637,24 +637,8 @@ module.exports = function(RED) {
node.deregister = function(mqttNode,done) {
delete node.users[mqttNode.id];
if (node.closing) {
return done();
}
if (Object.keys(node.users).length === 0) {
if (node.client && node.client.connected) {
// Send close message
if (node.closeMessage) {
node.publish(node.closeMessage,function(err) {
node.client.end(done);
});
} else {
node.client.end(done);
}
return;
} else {
if (node.client) { node.client.end(); }
return done();
}
if (!node.closing && node.connected && Object.keys(node.users).length === 0) {
node.disconnect();
}
done();
};
@@ -663,6 +647,7 @@ module.exports = function(RED) {
}
node.connect = function (callback) {
if (node.canConnect()) {
node.closing = false;
node.connecting = true;
setStatusConnecting(node, true);
try {
@@ -672,6 +657,7 @@ module.exports = function(RED) {
let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times
// Register successful connect or reconnect handler
node.client.on('connect', function (connack) {
node.closing = false;
node.connecting = false;
node.connected = true;
if(!callbackDone && typeof callback == "function") {
@@ -730,12 +716,19 @@ module.exports = function(RED) {
node.client.on("reconnect", function() {
setStatusConnecting(node, true);
});
//TODO: what to do with this event? Anything? Necessary?
//Broker Disconnect - V5 event
node.client.on("disconnect", function(packet) {
//Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
var rc = packet && packet.properties && packet.properties.reasonString;
var rc = packet && packet.properties && packet.reasonCode;
//TODO: If keeping this event, do we use these? log these?
const rc = (packet && packet.properties && packet.reasonCode) || packet.reasonCode;
const rs = packet && packet.properties && packet.properties.reasonString || "";
const details = {
broker: (node.clientid?node.clientid+"@":"")+node.brokerurl,
reasonCode: rc,
reasonString: rs
}
node.connected = false;
node.log(RED._("mqtt.state.broker-disconnected", details));
setStatusDisconnected(node, true);
});
// Register disconnect handlers
node.client.on('close', function () {
@@ -758,25 +751,31 @@ module.exports = function(RED) {
}
};
node.disconnect = function (callback) {
const _callback = function () {
const _callback = function (resetNodeConnectedState) {
setStatusDisconnected(node, true);
node.connecting = false;
node.connected = false;
if(resetNodeConnectedState) {
node.closing = true;
node.connecting = false;
node.connected = false;
}
callback && typeof callback == "function" && callback();
};
if(node.client) {
if(node.client.connected && node.closeMessage) {
node.publish(node.closeMessage, function (err) {
node.client.end(_callback);
});
} else if(node.client.connected || node.client.reconnecting) {
node.client.end(_callback);
} else if(node.client.disconnecting || node.client.connected === false) {
_callback();
}
if(node.closing) {
return _callback(false);
}
var endCallBack = function endCallBack() {
}
if(node.connected && node.closeMessage) {
node.publish(node.closeMessage, function (err) {
node.client.end(endCallBack);
_callback(true);
});
} else if(node.connected) {
node.client.end(endCallBack);
_callback(true);
} else {
_callback();
_callback(false);
}
}
node.subscriptionIds = {};
@@ -1040,7 +1039,7 @@ module.exports = function(RED) {
//subscribe to sub.topic & hook up subscriptionHandler
node.brokerConn.subscribe(sub.topic, options, function (topic, payload, packet) {
subscriptionHandler(node, sub.datatype, topic, payload, packet);
subscriptionHandler(node, sub.datatype || node.datatype, topic, payload, packet);
}, node.id);
node.dynamicSubs[sub.topic] = sub; //save for later unsubscription & 'list' action
})
@@ -1068,6 +1067,8 @@ module.exports = function(RED) {
node.brokerConn.unsubscribe(node.topic,node.id, removed);
}
node.brokerConn.deregister(node, done);
} else {
done();
}
});
} else {
@@ -1128,7 +1129,11 @@ module.exports = function(RED) {
}
node.brokerConn.register(node);
node.on('close', function(done) {
node.brokerConn.deregister(node,done);
if (node.brokerConn) {
node.brokerConn.deregister(node,done);
} else {
done();
}
});
} else {
node.error(RED._("mqtt.errors.missing-config"));

View File

@@ -264,7 +264,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
if (opts.headers.hasOwnProperty('cookie')) {
var cookies = cookie.parse(opts.headers.cookie, {decode:String});
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;
}
@@ -277,13 +277,13 @@ in your Node-RED user directory (${RED.settings.userDir}).
} else if (typeof msg.cookies[name] === 'object') {
if(msg.cookies[name].encode === false){
// 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 {
// 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 {
opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true});
opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true});
}
}
}
@@ -298,18 +298,16 @@ in your Node-RED user directory (${RED.settings.userDir}).
}
if (Object.keys(this.credentials).length != 0) {
if (this.authType === "basic") {
// Workaround for https://github.com/sindresorhus/got/issues/1169
var cred = ""
if (this.credentials.user) {
// opts.username = this.credentials.user;
cred = this.credentials.user
}
if (this.credentials.password) {
// opts.password = this.credentials.password;
cred += ":" + this.credentials.password
// Workaround for https://github.com/sindresorhus/got/issues/1169 (fixed in got v12)
// var cred = ""
if (this.credentials.user || this.credentials.password) {
// cred = `${this.credentials.user}:${this.credentials.password}`;
if (this.credentials.user === undefined) { this.credentials.user = ""}
if (this.credentials.password === undefined) { this.credentials.password = ""}
opts.headers.Authorization = "Basic " + Buffer.from(`${this.credentials.user}:${this.credentials.password}`).toString("base64");
}
// build own basic auth header
opts.headers.Authorization = "Basic " + Buffer.from(cred).toString("base64");
// opts.headers.Authorization = "Basic " + Buffer.from(cred).toString("base64");
} else if (this.authType === "digest") {
let digestCreds = this.credentials;
let sentCreds = false;

View File

@@ -177,7 +177,8 @@
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
tls: {type:"tls-config",required: 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,
outputs:0,
@@ -265,7 +266,10 @@
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
<input type="text" id="node-config-input-tls">
</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">
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">

View File

@@ -46,6 +46,12 @@ module.exports = function(RED) {
// Store local copies of the node configuration (as defined in the .html)
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._inputNodes = []; // collection of nodes that want to receive events
@@ -92,7 +98,7 @@ module.exports = function(RED) {
tlsNode.addTLSOptions(options);
}
}
var socket = new ws(node.path,options);
var socket = new ws(node.path,node.subprotocol,options);
socket.setMaxListeners(0);
node.server = socket; // keep for closing
handleConnection(socket);
@@ -105,22 +111,24 @@ module.exports = function(RED) {
if (node.isServer) {
node._clients[id] = socket;
node.emit('opened',{count:Object.keys(node._clients).length,id:id});
} else {
if (node.heartbeat) {
node.heartbeatInterval = setInterval(function() {
if (socket.nrPendingHeartbeat) {
// No pong received
socket.terminate();
socket.nrErrorHandler(new Error("timeout"));
return;
}
socket.nrPendingHeartbeat = true;
socket.ping();
},node.heartbeat);
}
}
socket.on('open',function() {
if (!node.isServer) {
if (node.heartbeat) {
clearInterval(node.heartbeatInterval);
node.heartbeatInterval = setInterval(function() {
if (socket.nrPendingHeartbeat) {
// No pong received
socket.terminate();
socket.nrErrorHandler(new Error("timeout"));
return;
}
socket.nrPendingHeartbeat = true;
try {
socket.ping();
} catch(err) {}
},node.heartbeat);
}
node.emit('opened',{count:'',id:id});
}
});

View File

@@ -23,9 +23,17 @@
</select>
<span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width:65px">
</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%;">
</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">
<label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
@@ -42,7 +50,7 @@
</div>
<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 class="form-row">
@@ -58,17 +66,18 @@
<script type="text/javascript">
RED.nodes.registerType('tcp in',{
category: 'network',
color:"Silver",
color: "Silver",
defaults: {
name: {value:""},
server: {value:"server",required:true},
host: {value:"",validate:function(v) { return (this.server == "server")||v.length > 0;} },
port: {value:"",required:true,validate:RED.validators.number()},
server: {value:"server", required:true},
host: {value:"", validate:function(v) { return (this.server == "server")||v.length > 0;} },
port: {value:"", required:true, validate:RED.validators.number()},
datamode:{value:"stream"},
datatype:{value:"buffer"},
newline:{value:""},
topic: {value:""},
base64: {/*deprecated*/ value:false,required:true}
base64: {/*deprecated*/ value:false, required:true},
tls: {type:"tls-config", value:'', required:false}
},
inputs:0,
outputs:1,
@@ -77,7 +86,7 @@
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
var updateOptions = function() {
@@ -103,6 +112,27 @@
$("#node-input-server").change(updateOptions);
$("#node-input-datatype").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>
@@ -123,6 +153,15 @@
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" style="width: 60%;">
</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">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;">
@@ -144,14 +183,15 @@
<script type="text/javascript">
RED.nodes.registerType('tcp out',{
category: 'network',
color:"Silver",
color: "Silver",
defaults: {
name: {value:""},
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); } },
beserver: {value:"client",required:true},
base64: {value:false,required:true},
end: {value:false,required:true},
name: {value:""}
beserver: {value:"client", required:true},
base64: {value:false, required:true},
end: {value:false, required:true},
tls: {type:"tls-config", value:'', required:false}
},
inputs:1,
outputs:0,
@@ -170,18 +210,42 @@
$("#node-input-port-row").hide();
$("#node-input-host-row").hide();
$("#node-input-end-row").hide();
$("#node-input-tls-enable").hide();
} else if (sockettype == "client"){
$("#node-input-port-row").show();
$("#node-input-host-row").show();
$("#node-input-end-row").show();
$("#node-input-tls-enable").show();
} else {
$("#node-input-port-row").show();
$("#node-input-host-row").hide();
$("#node-input-end-row").show();
$("#node-input-tls-enable").show();
}
};
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>
@@ -194,15 +258,23 @@
<span data-i18n="tcpin.label.port"></span>
<input type="text" id="node-input-port" style="width:60px">
</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">
<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%;">
<option value="buffer" data-i18n="tcpin.output.buffer"></option>
<option value="string" data-i18n="tcpin.output.string"></option>
</select>
</div>
<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%;">
<option value="time" data-i18n="tcpin.return.timeout"></option>
<option value="char" data-i18n="tcpin.return.character"></option>
@@ -213,6 +285,9 @@
<input type="text" id="node-input-splitc" style="width:50px;">
<span id="node-units"></span>
</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">
<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">
@@ -222,14 +297,16 @@
<script type="text/javascript">
RED.nodes.registerType('tcp request',{
category: 'network',
color:"Silver",
color: "Silver",
defaults: {
name: {value:""},
server: {value:""},
port: {value:"",validate:RED.validators.regex(/^(\d*|)$/)},
out: {value:"time",required:true},
port: {value:"", validate:RED.validators.regex(/^(\d*|)$/)},
out: {value:"time", required:true},
ret: {value:"buffer"},
splitc: {value:"0",required:true},
name: {value:""}
splitc: {value:"0", required:true},
newline: {value:""},
tls: {type:"tls-config", value:'', required:false}
},
inputs:1,
outputs:1,
@@ -246,6 +323,14 @@
$("#node-input-ret").val("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-splitc").show();
if (previous === null) { previous = $("#node-input-out").val(); }
@@ -272,6 +357,27 @@
$("#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>

View File

@@ -16,13 +16,46 @@
module.exports = function(RED) {
"use strict";
var reconnectTime = RED.settings.socketReconnectTime||10000;
var socketTimeout = RED.settings.socketTimeout||null;
let reconnectTime = RED.settings.socketReconnectTime || 10000;
let socketTimeout = RED.settings.socketTimeout || null;
const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000;
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`
@@ -53,13 +86,14 @@ module.exports = function(RED) {
this.topic = n.topic;
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
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.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
this.closing = false;
this.connected = false;
var node = this;
var count = 0;
if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); }
if (!node.server) {
var buffer = null;
@@ -70,13 +104,25 @@ module.exports = function(RED) {
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
var id = RED.util.generateId();
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);
var connOpts = {host: node.host};
if (n.tls) {
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",_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;
client.on('data', function (data) {
@@ -89,7 +135,7 @@ module.exports = function(RED) {
buffer = buffer+data;
var parts = buffer.split(node.newline);
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};
node.send(msg);
}
@@ -150,7 +196,13 @@ module.exports = function(RED) {
});
}
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);
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
var id = RED.util.generateId();
@@ -177,7 +229,7 @@ module.exports = function(RED) {
buffer = buffer+data;
var parts = buffer.split(node.newline);
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};
node.send(msg);
}
@@ -269,8 +321,9 @@ module.exports = function(RED) {
this.closing = false;
this.connected = false;
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 client = null;
var end = false;
@@ -278,11 +331,24 @@ module.exports = function(RED) {
var setupTcpClient = function() {
node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
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"});
});
if (n.tls) {
// connOpts = tlsNode.addTLSOptions(connOpts);
// client = tls.connect(connOpts, function() {
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.on('error', function (err) {
node.log(RED._("tcpin.errors.error",{error:err.toString()}));
@@ -368,7 +434,13 @@ module.exports = function(RED) {
else {
var connectedSockets = [];
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);
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
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.out = n.out;
this.ret = n.ret || "buffer";
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
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 !== "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
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 (host && port) {
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.status({fill:"green",shape:"dot",text:"common.status.connected"});
if (clients[connection_id] && clients[connection_id].client) {
@@ -528,17 +640,32 @@ module.exports = function(RED) {
else {
node.warn(RED._("tcpin.errors.no-host"));
}
var chunk = "";
clients[connection_id].client.on('data', function(data) {
if (node.out === "sit") { // if we are staying connected just send the buffer
if (clients[connection_id]) {
const msg = clients[connection_id].lastMsg || {};
msg.payload = RED.util.cloneMessage(data);
if (node.ret === "string") {
try { msg.payload = msg.payload.toString(); }
catch(e) { node.error("Failed to create string", msg); }
try {
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) {
@@ -675,7 +802,13 @@ module.exports = function(RED) {
//node.warn(RED._("tcpin.errors.connect-timeout"));
if (clients[connection_id].client) {
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].connecting = false;
node.status({fill:"green",shape:"dot",text:"common.status.connected"});

View File

@@ -49,7 +49,11 @@ module.exports = function(RED) {
}
var value = RED.util.getMessageProperty(msg,node.property);
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") {
try {
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));

View File

@@ -71,9 +71,7 @@ module.exports = function(RED) {
node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg);
}
else {
if (RED.settings.verbose) {
node.log(RED._("file.status.deletedfile",{file:filename}));
}
node.debug(RED._("file.status.deletedfile",{file:filename}));
nodeSend(msg);
}
done();

View File

@@ -0,0 +1,156 @@
[
{
"id": "62ea32aa.d73aac",
"type": "comment",
"z": "6312c0588348b2d4",
"name": "Example: Link Call Node",
"info": "Link call node can call link in node then get result from link out node.",
"x": 230,
"y": 180,
"wires": []
},
{
"id": "c588bc36.87fec",
"type": "comment",
"z": "6312c0588348b2d4",
"name": "↓ call link in node",
"info": "",
"x": 440,
"y": 220,
"wires": []
},
{
"id": "cd31efb4d2c6967e",
"type": "link call",
"z": "6312c0588348b2d4",
"name": "",
"links": [
"dbc46892c8d14c37"
],
"timeout": "30",
"x": 420,
"y": 260,
"wires": [
[
"c3db64d1d2260340"
]
]
},
{
"id": "dbc46892c8d14c37",
"type": "link in",
"z": "6312c0588348b2d4",
"name": "",
"links": [],
"x": 315,
"y": 340,
"wires": [
[
"e10575d73f2e5352"
]
]
},
{
"id": "6b61792143b3b0a3",
"type": "inject",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"y": 260,
"wires": [
[
"cd31efb4d2c6967e"
]
]
},
{
"id": "e10575d73f2e5352",
"type": "change",
"z": "6312c0588348b2d4",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "Hello, World!",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 340,
"wires": [
[
"cf8438e7137bc0f0"
]
]
},
{
"id": "cf8438e7137bc0f0",
"type": "link out",
"z": "6312c0588348b2d4",
"name": "",
"mode": "return",
"links": [],
"x": 595,
"y": 340,
"wires": []
},
{
"id": "c3db64d1d2260340",
"type": "debug",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 600,
"y": 260,
"wires": []
},
{
"id": "6d077dfa0987febb",
"type": "comment",
"z": "6312c0588348b2d4",
"name": "↑called from link call node",
"info": "",
"x": 410,
"y": 380,
"wires": []
},
{
"id": "53b9a0adfd8c4217",
"type": "comment",
"z": "6312c0588348b2d4",
"name": "↑return to link call node",
"info": "",
"x": 680,
"y": 380,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 220,
"x": 190,
"y": 180,
"wires": [
[
"b4b9f603.739598"
@@ -31,25 +31,25 @@
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "Write string to a file, then read from the file",
"info": "File-in node can read string from a file.",
"x": 260,
"y": 140,
"info": "Read file node can read string from a file.",
"x": 220,
"y": 100,
"wires": []
},
{
"id": "b4b9f603.739598",
"type": "file",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 420,
"y": 220,
"x": 380,
"y": 180,
"wires": [
[
"6dc01cac.5c4bf4"
@@ -59,7 +59,7 @@
{
"id": "2587adb9.7e60f2",
"type": "debug",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 220,
"x": 770,
"y": 180,
"wires": []
},
{
"id": "6dc01cac.5c4bf4",
"type": "file in",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 220,
"x": 580,
"y": 180,
"wires": [
[
"2587adb9.7e60f2"
@@ -93,21 +93,21 @@
{
"id": "f4b4309a.3b78a",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↑read result from file",
"info": "",
"x": 630,
"y": 260,
"x": 590,
"y": 220,
"wires": []
},
{
"id": "672d3693.3cabd8",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 440,
"y": 180,
"x": 400,
"y": 140,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "8997398f.c5d628",
"type": "inject",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "😀",
"payloadType": "str",
"x": 210,
"y": 480,
"x": 170,
"y": 260,
"wires": [
[
"56e32d23.050f44"
@@ -31,25 +31,25 @@
{
"id": "4e598e65.1799d",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "Read data in specified encoding",
"info": "File-in node can specify encoding of data read from a file.",
"x": 230,
"y": 400,
"info": "Read file node can specify encoding of data read from a file.",
"x": 190,
"y": 180,
"wires": []
},
{
"id": "56e32d23.050f44",
"type": "file",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 380,
"y": 480,
"x": 340,
"y": 260,
"wires": [
[
"38fa0579.f2cd8a"
@@ -59,7 +59,7 @@
{
"id": "d28c8994.99c0a8",
"type": "debug",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,23 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 770,
"y": 480,
"x": 730,
"y": 260,
"wires": []
},
{
"id": "38fa0579.f2cd8a",
"type": "file in",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "base64",
"x": 580,
"y": 480,
"allProps": false,
"x": 540,
"y": 260,
"wires": [
[
"d28c8994.99c0a8"
@@ -93,21 +94,21 @@
{
"id": "fa22ca20.ae4528",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↑read data from file as base64 string",
"info": "",
"x": 640,
"y": 520,
"x": 600,
"y": 300,
"wires": []
},
{
"id": "148e25ad.98891a",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 400,
"y": 440,
"x": 360,
"y": 220,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "6a0b1d03.d4cee4",
"type": "inject",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 740,
"x": 160,
"y": 220,
"wires": [
[
"d4b00cb7.a5a23"
@@ -31,25 +31,25 @@
{
"id": "f17ea1d1.8ecc3",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "Read data breaking lines into individual messages",
"info": "File-in node can break read text into messages with individual lines",
"x": 290,
"y": 660,
"info": "Read file node can break read text into messages with individual lines",
"x": 230,
"y": 140,
"wires": []
},
{
"id": "99ae7806.1d6428",
"type": "file",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 540,
"y": 740,
"x": 480,
"y": 220,
"wires": [
[
"70d7892f.d27db8"
@@ -59,7 +59,7 @@
{
"id": "7ed8282c.92b338",
"type": "debug",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 800,
"x": 750,
"y": 280,
"wires": []
},
{
"id": "70d7892f.d27db8",
"type": "file in",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "lines",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 800,
"x": 560,
"y": 280,
"wires": [
[
"7ed8282c.92b338"
@@ -93,27 +93,27 @@
{
"id": "c1b7e05.1d94b2",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↑read data from file breaking lines into messages",
"info": "",
"x": 720,
"y": 840,
"x": 660,
"y": 320,
"wires": []
},
{
"id": "a5f647b2.cf27a8",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 560,
"y": 700,
"x": 500,
"y": 180,
"wires": []
},
{
"id": "d4b00cb7.a5a23",
"type": "template",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "data",
"field": "payload",
"fieldType": "msg",
@@ -121,8 +121,8 @@
"syntax": "plain",
"template": "one\ntwo\nthree!",
"output": "str",
"x": 370,
"y": 740,
"x": 310,
"y": 220,
"wires": [
[
"99ae7806.1d6428"

View File

@@ -2,7 +2,7 @@
{
"id": "bdd57acc.2edc48",
"type": "inject",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 1040,
"x": 180,
"y": 220,
"wires": [
[
"7a069b01.0c2324"
@@ -31,25 +31,25 @@
{
"id": "1fd12220.33953e",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "Creating a message stream from lines of data",
"info": "File-in node can break read text into messages with individual lines. The messages creates a stream of messages.",
"x": 270,
"y": 960,
"info": "Read file node can break read text into messages with individual lines. The messages creates a stream of messages.",
"x": 230,
"y": 140,
"wires": []
},
{
"id": "ab6eb213.2a08d",
"type": "file",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 540,
"y": 1040,
"x": 500,
"y": 220,
"wires": [
[
"b7ed49b0.649fb8"
@@ -59,7 +59,7 @@
{
"id": "c48d8ae0.9ff3a8",
"type": "debug",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 1140,
"x": 770,
"y": 320,
"wires": []
},
{
"id": "b7ed49b0.649fb8",
"type": "file in",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "lines",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 280,
"y": 1140,
"x": 240,
"y": 320,
"wires": [
[
"83073ebe.fcce4"
@@ -93,27 +93,27 @@
{
"id": "3c33e69f.6a04ba",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↑read data from file breaking lines into messages",
"info": "",
"x": 380,
"y": 1180,
"x": 340,
"y": 360,
"wires": []
},
{
"id": "3598bf7d.5712a",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 560,
"y": 1000,
"x": 520,
"y": 180,
"wires": []
},
{
"id": "7a069b01.0c2324",
"type": "template",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "data",
"field": "payload",
"fieldType": "msg",
@@ -121,8 +121,8 @@
"syntax": "plain",
"template": "Apple\nBanana\nGrape\nOrange",
"output": "str",
"x": 370,
"y": 1040,
"x": 330,
"y": 220,
"wires": [
[
"ab6eb213.2a08d"
@@ -132,7 +132,7 @@
{
"id": "8d4ed1d0.821fe",
"type": "join",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "",
"mode": "auto",
"build": "string",
@@ -145,8 +145,8 @@
"timeout": "",
"count": "",
"reduceRight": false,
"x": 630,
"y": 1140,
"x": 590,
"y": 320,
"wires": [
[
"c48d8ae0.9ff3a8"
@@ -156,7 +156,7 @@
{
"id": "83073ebe.fcce4",
"type": "switch",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "< D",
"property": "payload",
"propertyType": "msg",
@@ -170,8 +170,8 @@
"checkall": "true",
"repair": true,
"outputs": 1,
"x": 470,
"y": 1140,
"x": 430,
"y": 320,
"wires": [
[
"8d4ed1d0.821fe"
@@ -181,21 +181,21 @@
{
"id": "2088e195.f7aebe",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↓filter data before \"D\"",
"info": "",
"x": 520,
"y": 1100,
"x": 480,
"y": 280,
"wires": []
},
{
"id": "b848cdc7.61e06",
"type": "comment",
"z": "194a3e4f.a92772",
"z": "6312c0588348b2d4",
"name": "↑join to single string",
"info": "",
"x": 670,
"y": 1180,
"x": 630,
"y": 360,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 200,
"x": 150,
"y": 220,
"wires": [
[
"b4b9f603.739598"
@@ -31,25 +31,25 @@
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "Write string to a file, then read from the file",
"info": "File node can write string to a file.",
"x": 260,
"y": 120,
"info": "Write file node can write string from a file.",
"x": 180,
"y": 140,
"wires": []
},
{
"id": "b4b9f603.739598",
"type": "file",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 420,
"y": 200,
"x": 340,
"y": 220,
"wires": [
[
"6dc01cac.5c4bf4"
@@ -59,7 +59,7 @@
{
"id": "2587adb9.7e60f2",
"type": "debug",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 200,
"x": 730,
"y": 220,
"wires": []
},
{
"id": "6dc01cac.5c4bf4",
"type": "file in",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 200,
"x": 540,
"y": 220,
"wires": [
[
"2587adb9.7e60f2"
@@ -93,21 +93,21 @@
{
"id": "f4b4309a.3b78a",
"type": "comment",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "↑read result from file",
"info": "",
"x": 630,
"y": 240,
"x": 550,
"y": 260,
"wires": []
},
{
"id": "672d3693.3cabd8",
"type": "comment",
"z": "4b63452d.672afc",
"z": "5132b95f037524f9",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 440,
"y": 160,
"x": 360,
"y": 180,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "704479e1.399388",
"type": "inject",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -25,8 +25,8 @@
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 400,
"x": 190,
"y": 260,
"wires": [
[
"402f3b7e.988014"
@@ -36,25 +36,25 @@
{
"id": "8e876a75.e9beb8",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "Write string to a file specied by filename property, the read from the file",
"info": "File node can target file using `filename` property.",
"x": 350,
"y": 320,
"info": "Write file node can target file using `filename` property.",
"x": 310,
"y": 180,
"wires": []
},
{
"id": "402f3b7e.988014",
"type": "file",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"filename": "",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 390,
"y": 400,
"x": 350,
"y": 260,
"wires": [
[
"26e077d6.bbcd98"
@@ -64,7 +64,7 @@
{
"id": "97b6b6b2.a54b38",
"type": "debug",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -73,22 +73,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 770,
"y": 400,
"x": 730,
"y": 260,
"wires": []
},
{
"id": "26e077d6.bbcd98",
"type": "file in",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 580,
"y": 400,
"x": 540,
"y": 260,
"wires": [
[
"97b6b6b2.a54b38"
@@ -98,21 +98,21 @@
{
"id": "85062297.da79",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "↑read result from file",
"info": "",
"x": 590,
"y": 440,
"x": 550,
"y": 300,
"wires": []
},
{
"id": "7316c4fc.b1dcdc",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "↓write to file specified by filename property",
"info": "",
"x": 500,
"y": 360,
"x": 460,
"y": 220,
"wires": []
}
]

View File

@@ -2,7 +2,7 @@
{
"id": "4ac00fb0.d5f52",
"type": "inject",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"props": [
{
@@ -20,8 +20,8 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 600,
"x": 180,
"y": 220,
"wires": [
[
"542cc2f4.92857c"
@@ -31,25 +31,25 @@
{
"id": "671f8295.0e6f6c",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "Delete a file",
"info": "File node can delete a file.",
"x": 170,
"y": 540,
"info": "Write file node can delete a file.",
"x": 130,
"y": 160,
"wires": []
},
{
"id": "542cc2f4.92857c",
"type": "file",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "delete",
"encoding": "none",
"x": 420,
"y": 600,
"x": 380,
"y": 220,
"wires": [
[
"a24da523.5babe8"
@@ -59,7 +59,7 @@
{
"id": "a24da523.5babe8",
"type": "debug",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,18 +68,18 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 630,
"y": 600,
"x": 590,
"y": 220,
"wires": []
},
{
"id": "51157051.2f62",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "↓delete a file",
"info": "",
"x": 390,
"y": 560,
"x": 350,
"y": 180,
"wires": []
}
]

View File

@@ -2,8 +2,8 @@
{
"id": "e4ef1f5e.7cd82",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"z": "6312c0588348b2d4",
"name": "Base64 encoded string",
"props": [
{
"p": "payload"
@@ -20,8 +20,8 @@
"topic": "",
"payload": "8J+YgA==",
"payloadType": "str",
"x": 220,
"y": 820,
"x": 200,
"y": 220,
"wires": [
[
"72b37cc8.177054"
@@ -31,25 +31,25 @@
{
"id": "f5997af4.5a9298",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "Specify encoding of written data",
"info": "File node can specify encoding of data.",
"x": 230,
"y": 740,
"info": "Write file node can specify encoding of data.",
"x": 170,
"y": 140,
"wires": []
},
{
"id": "72b37cc8.177054",
"type": "file",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "base64",
"x": 400,
"y": 820,
"x": 420,
"y": 220,
"wires": [
[
"2da33ec.f45cac2"
@@ -59,7 +59,7 @@
{
"id": "2e814354.278c8c",
"type": "debug",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"active": true,
"tosidebar": true,
@@ -68,22 +68,22 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 820,
"x": 810,
"y": 220,
"wires": []
},
{
"id": "2da33ec.f45cac2",
"type": "file in",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 600,
"y": 820,
"x": 620,
"y": 220,
"wires": [
[
"2e814354.278c8c"
@@ -93,21 +93,21 @@
{
"id": "ec754c99.84bfd",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "↓write string with base64 encoding",
"info": "",
"x": 460,
"y": 780,
"x": 480,
"y": 180,
"wires": []
},
{
"id": "3e6704ff.4ce25c",
"type": "comment",
"z": "4b63452d.672afc",
"z": "6312c0588348b2d4",
"name": "↑read result from file",
"info": "",
"x": 610,
"y": 860,
"x": 630,
"y": 260,
"wires": []
}
]

View File

@@ -25,7 +25,7 @@
<dd>Optional nutzbare Nachrichten-Eigenschaft.</dd>
</dl>
<h3>Details</h3>
<p>Der <span style="background-color:Gainsboro">inject</span>-Node kann einen Flow mit einstellbaren Nutzdaten (Payload) starten.
<p>Der inject-Node kann einen Flow mit einstellbaren Nutzdaten (Payload) starten.
Der voreingestellte Payload ist die aktuelle Zeit als Zeitstempel in Millisekunden
seit Beginn der Unix-Zeitrechnung (1. Januar 1970 UTC).</p>
<p>Der Node unterstützt auch die Injektion von Zeichenfolgen, Zahlenwerten, Booleschen Werten,
@@ -34,7 +34,7 @@
Er kann auch in regelmäßigen Intervallen oder nach einem Zeitplan injizieren.</p>
<p>Er kann auch so eingestellt werden, dass er jedes Mal einen Wert injiziert, wenn der Flow gestartet wird.</p>
<p>Das maximal einstellbare Intervall beträgt etwa 596 Stunden bzw. 24 Tage.
Wenn jedoch Intervalle größer als 24h benötigt werden, sollte ein <span style="background-color:Gainsboro">scheduler</span>-Node verwendet werden,
Wenn jedoch Intervalle größer als 24h benötigt werden, sollte ein scheduler-Node verwendet werden,
der mit Stromausfällen und Neustarts besser umgehen kann.</p>
<p><b>Hinweis</b>: Die Optionen <i>"Intervall zwischen Uhrzeiten"</i> und <i>"Täglicher Zeitpunkt"</i>
verwenden das Standard-Cron-System.</p>

View File

@@ -23,10 +23,10 @@
<p>JavaScript-Objekte und -Arrays können nach Bedarf ein- und ausgeblendet werden.
Binäre Puffer-Objekte (buffer objects) können nach Möglichkeit als Rohdaten oder als Zeichenfolge (string) angezeigt werden.</p>
<p>Neben der eigentlichen Nachricht werden im Debug-Tab auch der Empfangszeitpunkt,
der empfangende <span style="background-color:Gainsboro">debug</span>-Node, sowie Name und Typ der Nachricht protokolliert.
Durch Klicken auf die Node-ID wird der entsprechende <span style="background-color:Gainsboro">debug</span>-Node im Arbeitsbereich angezeigt.</p>
<p>Die Schaltfläche des <span style="background-color:Gainsboro">debug</span>-Nodes kann verwendet werden, um die Debug-Ausgabe ein- und auszuschalten.
Es ist empfehlenswert, alle nicht verwendeten <span style="background-color:Gainsboro">debug</span>-Nodes zu deaktivieren oder gleich zu entfernen.</p>
<p>Der <span style="background-color:Gainsboro">debug</span>-Node kann auch so eingestellt werden, dass außerdem alle Nachrichten in der Systemkonsole ausgegeben und/oder
als kurze Statustexte (max. 32 Zeichen) unter dem <span style="background-color:Gainsboro">debug</span>-Node angezeigt werden.</p>
der empfangende debug-Node, sowie Name und Typ der Nachricht protokolliert.
Durch Klicken auf die Node-ID wird der entsprechende debug-Node im Arbeitsbereich angezeigt.</p>
<p>Die Schaltfläche des debug-Nodes kann verwendet werden, um die Debug-Ausgabe ein- und auszuschalten.
Es ist empfehlenswert, alle nicht verwendeten debug-Nodes zu deaktivieren oder gleich zu entfernen.</p>
<p>Der debug-Node kann auch so eingestellt werden, dass außerdem alle Nachrichten in der Systemkonsole ausgegeben und/oder
als kurze Statustexte (max. 32 Zeichen) unter dem debug-Node angezeigt werden.</p>
</script>

View File

@@ -18,11 +18,11 @@
<p>Anstoß eines weiteren Flows, wenn ein anderer Node seine Nachrichtenbearbeitung abgeschlossen hat.</p>
<h3>Details</h3>
<p>Wenn ein Node die Bearbeitung seiner Nachrichten abgeschlossen hat,
kann der <span style="background-color:Gainsboro">complete</span>-Node dazu benutzt werden, einen weiteren Flow anzustoßen.</p>
kann der complete-Node dazu benutzt werden, einen weiteren Flow anzustoßen.</p>
<p>Der Node kann z.B. mit einem anderen Node ohne Ausgang verknüpft werden
(z.B. E-Mail-Sende-Node), um den Flow fortzusetzen.</p>
<p>Im Node werden dazu die zu überwachenden Nodes des selben Flows ausgewählt.
Im Gegensatz zum <span style="background-color:Gainsboro">catch</span>-Node besteht hier jedoch nicht die Auswahlmöglichkeit aller Nodes des Flows.</p>
Im Gegensatz zum catch-Node besteht hier jedoch nicht die Auswahlmöglichkeit aller Nodes des Flows.</p>
<p>Nicht alle Nodes können diesen Node anstoßen.
Es hängt davon ab, ob die auslösenden Knoten diese Funktion unterstützen,
welche erst mit Node-RED 1.0 eingeführt wurde.</p>

Some files were not shown because too many files have changed in this diff Show More