Compare commits

...

220 Commits

Author SHA1 Message Date
Nick O'Leary
b1a706f811 Add comment on RED.nodes.getNodeLinkCount api 2022-01-12 18:04:53 +00:00
Nick O'Leary
c27dd336d9 Move ports behind node body and increase touch zone for each port 2022-01-12 18:01:11 +00:00
Nick O'Leary
5e9ff98c49 Add arrow-heads to links 2022-01-12 18:01:06 +00:00
Nick O'Leary
50cb074172 Visually distinguish ports with connected wires 2022-01-12 18:01:01 +00:00
Nick O'Leary
510a09ecba Add RED.nodes.getNodeLinkCount to provide O(1) lookup of link count on node port 2022-01-12 18:00:53 +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
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
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
Nick O'Leary
21304a695c Fix tcprequest deprecation warnings 2021-10-21 09:40:25 +01:00
Nick O'Leary
fa51b06c46 Merge pull request #3205 from node-red/dev
Node-RED 2.1.0
2021-10-21 09:13:09 +01:00
Nick O'Leary
7560bb8d7b Bump for 2.1.0 2021-10-21 09:08:35 +01:00
Nick O'Leary
fc9d65abcc Merge pull request #3204 from node-red/tcp-req-text-output
Add string option to TCP request node output
2021-10-20 09:40:44 +01:00
Dave Conway-Jones
a7413cccd0 reuse existing labels for tcp request buffer/string option 2021-10-20 09:36:08 +01:00
Nick O'Leary
7610353f07 Position popover properly on a scrolled page
Not needed in the core editor, but needed in the docs pages that can scroll
2021-10-19 22:22:53 +01:00
Dave Conway-Jones
d3f978c90c Add optional string type output to tcp request node
to be similar to tcp in. node
2021-10-19 21:34:23 +01:00
Kazuhito Yokoi
b55a8ef62a Merge branch 'dev' into dev-addjpn 2021-10-19 19:20:32 +09:00
Nick O'Leary
8d79deffb5 Merge pull request #3202 from node-red/tour-guide-fix
Fixes from 2.1.0-beta.2
2021-10-19 10:20:24 +01:00
Nick O'Leary
8158487c3e Reduce churn in setting Switch rule typedInput type 2021-10-19 10:12:13 +01:00
Nick O'Leary
0cc061196d Handle changing value before type without reseting TypedInput 2021-10-19 10:11:37 +01:00
Nick O'Leary
d0ec055222 Fix Inject/Change node restoring typedInput options 2021-10-18 21:23:42 +01:00
Nick O'Leary
ae12ddd32b Change node: Fix deepCopy checkbox spacing on Safari 2021-10-18 20:38:46 +01:00
Nick O'Leary
31da3adaa9 Fix hide/show of palette in tour and add MQTT nodes 2021-10-18 20:29:37 +01:00
Nick O'Leary
9fd5213f13 Merge pull request #3201 from node-red/link-fixes
Fix saving link out node links
2021-10-18 09:43:30 +01:00
Nick O'Leary
de882f5849 Fix saving link out node links 2021-10-18 09:32:00 +01:00
Nick O'Leary
fded1e0021 Refix #3170 - copy switch rule type when adding new rule 2021-10-14 21:47:49 +01:00
Nick O'Leary
6cb06c146d Update for 2.1.0-beta.2 2021-10-14 13:07:58 +01:00
Nick O'Leary
b8f1386ad0 Dynamic MQTT connections (#3189)
* add mqtt-control
- adds auto-connect option to broker
- add new node mqtt-control
- adds i18n messages
- adds documentation

* documentation tweaks

* built in documentation improvements

* fix tip layout causing oversized editor

* remove unused requires

* add missing `unsubscribe` dropdown option
- oddly forgotten - now added

* ensure clientid is updated dynamically

* [rewrite] move mqtt-control login into mqtt-in

* Remove dynamic label

* remove redundant mqtt-control code left overs

* Callback for brokerConn.connect (improve done())
- done is now called on connect callback

* fix race condition if connect/disconnect too fast
- node.connected and node.client.connected getting out of sync

* fix connection fail when switching protocol 3 ~ 5
- ensure protocolId is correct for protocolVersion

* change msg.subscribe prop to `msg.topic`

* unsub all topics if msg.topic is `true`

* delete temprary debugger statements

* Final rework of dynamic mqtt connections

Co-authored-by: Steve-Mcl <sdmclaughlin@gmail.com>
2021-10-14 12:05:06 +01:00
Nick O'Leary
2b38b5ea50 Merge pull request #3200 from node-red/fix-link-call-label
Fix link call label
2021-10-14 12:04:25 +01:00
Nick O'Leary
2f707a6b16 Merge pull request #3199 from node-red/switch-project-fix
Fix switching projects
2021-10-14 12:04:07 +01:00
Nick O'Leary
d4c2fcd559 Merge pull request #3198 from node-red/locale-fix
Use locale setting when installing/enabling node
2021-10-14 12:03:54 +01:00
Nick O'Leary
082970cdb7 Merge pull request #3197 from node-red/tour-projects-fix
Do not show projects-wecome dialog until welcome tour completes
2021-10-14 12:03:41 +01:00
Nick O'Leary
fe97c78977 Merge pull request #3196 from node-red/link-fixes
Fix converting selection to subflow
2021-10-14 12:03:28 +01:00
Nick O'Leary
79394aa69f Merge pull request #3195 from node-red/keyboard-fix
Avoid conflicts with native browser cmd-ctrl type shortcuts
2021-10-14 12:03:17 +01:00
Nick O'Leary
21fd6e3c21 Ensure link call node label updates when link-in changes 2021-10-14 12:00:27 +01:00
Nick O'Leary
de4944cd83 Ensure node is resized if its label changes 2021-10-14 11:53:55 +01:00
Nick O'Leary
3fde5c27ed Fix switching projects 2021-10-14 11:07:49 +01:00
Nick O'Leary
e1d492813e Use locale setting when installing/enabling node
Fixes #3194
2021-10-14 10:57:11 +01:00
Nick O'Leary
48d0ee3b6d Do not show projects-wecome dialog until welcome tour completes
Fixes #3193
2021-10-14 10:17:27 +01:00
Nick O'Leary
eebb64901c Fix converting selection to subflow
Fixes #3191
2021-10-14 09:48:35 +01:00
Nick O'Leary
60e0ed2af6 Avoid conflicts with native browser cmd-ctrl type shortcuts
Fixes #3190
2021-10-14 09:34:46 +01:00
Nick O'Leary
f030694ef4 Merge pull request #3183 from node-red/debug-filter-opts
Redesign debug filter options and make them persistant
2021-10-13 14:47:08 +01:00
Nick O'Leary
e9ed13459a Merge pull request #3184 from node-red/inject-spinner-css
Widen Inject interval box for >1 digit
2021-10-13 14:41:52 +01:00
Nick O'Leary
af1e38fdf7 Merge pull request #3185 from node-red/switch-focus-fix
Fix rule focus when switch 'otherwise' rule is used
2021-10-13 14:41:42 +01:00
Nick O'Leary
b12900e680 Merge pull request #3186 from node-red/context-tools-fix
Ensure message tools stay attached to top-level entry in Debug/Context
2021-10-13 14:41:29 +01:00
Nick O'Leary
44aa1f4a5e Filter out Link Out Return nodes in Link In edit dialog
Fixes #3187
2021-10-13 14:23:43 +01:00
Nick O'Leary
9425548a85 Ensure message tools stay attached to top-level entry in Debug/Context
Fixes #3180
2021-10-12 21:11:20 +01:00
Nick O'Leary
bfd4fc81fe Widen Inject interval box for >1 digit 2021-10-12 17:25:30 +01:00
Nick O'Leary
439af2a325 Fix rule focus when switch 'otherwise' rule is used
Fixes #3182
2021-10-12 17:25:13 +01:00
Nick O'Leary
3204b04455 Overhaul Debug sidebar filter and clear button options 2021-10-12 17:24:51 +01:00
Nick O'Leary
bed1be14ba Allow toolip action/content to be updated 2021-10-12 17:24:46 +01:00
Nick O'Leary
7cd92faf0d Separate 'focus' from 'selected' state in treeList 2021-10-12 17:24:41 +01:00
Nick O'Leary
be7e28af5d Update package dependencies 2021-10-12 17:24:10 +01:00
Nick O'Leary
8eaa762ec5 Merge branch 'dev' of github.com:node-red/node-red into dev 2021-10-11 10:01:46 +01:00
Nick O'Leary
953a9f7cd4 Update to latest node-red-admin 2021-10-11 09:58:55 +01:00
Kazuhito Yokoi
36f099d68b Add Japanese translations for Node-RED v2.1.0-beta.1 2021-10-11 16:02:18 +09:00
Nick O'Leary
c8fd5090bd Merge pull request #3178 from node-red/fix-md-help
Fix table tokenizer for node help markdown renderer
2021-10-08 18:14:10 +01:00
Nick O'Leary
155e1be494 Fix table tokenizer for node help markdown renderer 2021-10-08 17:56:24 +01:00
Nick O'Leary
cf5e125cb3 Merge pull request #3175 from node-red/tab-disabled-fix
Ensure tab state updates properly when toggling enable state
2021-10-08 15:52:16 +01:00
Nick O'Leary
764fc8477d Merge pull request #3176 from node-red/long-module-names
Improve handling of long labels in TreeList
2021-10-08 15:52:05 +01:00
Nick O'Leary
d35e62f8cf Merge pull request #3177 from node-red/scroll-tab-bar
Shift-click tab scroll arrows to jump to start/end
2021-10-08 15:51:56 +01:00
Nick O'Leary
904babdd13 Shift-click tab scroll arrows to jump to start/end 2021-10-08 15:45:17 +01:00
Nick O'Leary
154d3842a8 Improve handling of long labels in TreeList 2021-10-08 15:30:43 +01:00
Nick O'Leary
edb8a120bd Ensure tab state updates properly when toggling enable state 2021-10-08 15:09:51 +01:00
138 changed files with 4170 additions and 1706 deletions

View File

@@ -1,3 +1,123 @@
#### 2.2.0-beta.1: Beta Release
#### 2.1.4: Maintenance Release
Runtime
- fix env var access using $parent for groups (#3278) @HiroyasuNishiyama
- Add proper error handling for 404 errors when serving debug files (#3277) @knolleary
- Add Japanese translations for Node-RED v2.1.0-beta.1 (#3179) @kazuhitoyokoi
- Include full user object on login audit events (#3269) @knolleary
- Remove styling from de locale files (#3237) @knolleary
Editor
- Change tab hide button icon to an eye and add search option (#3282) @knolleary
- Fix i18n handling of namespaces with spaces in (#3281) @knolleary
- Trigger change event when autoComplete fills in input (#3280) @knolleary
- Apply CN i18n fix (#3279) @knolleary
- fix select menu label of config node to use paletteLabel (#3273) @HiroyasuNishiyama
- fix removed tab not to cause node conflict (#3275) @HiroyasuNishiyama
- Group diff fix (#3239) @knolleary
- Only toggle disabled workspace flag if on activeWorkspace (#3252) @knolleary
- Do not show status for disabled nodes (#3253) @knolleary
- Set dimension value for tour guide (#3265) @kazuhitoyokoi
- Avoid redundant initialisation of TypedInput type (#3263) @knolleary
- Don't let themes change flow port label color (#3270) @bonanitech
- Fix treeList gutter calculation to handle floating gutters (#3238) @knolleary
Nodes
- Debug: Handle RegExp types in Debug sidebar (#3251) @knolleary
- Delay: fix 2nd output when in rate limit per topic modes (#3261) @dceejay
- Link: fix to show link target when selected (#3267) @HiroyasuNishiyama
- Inject: Do not modify inject node props in oneditprepare (#3242) @knolleary
- HTTP Request: HTTP Basic Auth should always add : between username and password even if empty (#3236) @hardillb
#### 2.1.3: Maintenance Release
Runtime
- Update gen-publish script to update 'next' tag for main releases
- Add environment variable to enable/disable tours (#3221) @hardillb
- Fix loading non-default language files leaving runtime in wrong locale (#3225) @knolleary
Editor
- Refresh editor settings whenever a node is added or enabled (#3227) @knolleary
- Revert spinner css change that made it shrink in some cases (#3229) @knolleary
- Fix import notification message when importing config nodes (#3224) @knolleary
- Handle changing types of TypedInput repeatedly (#3223) @knolleary
#### 2.1.2: Maintenance Release
Runtime
- node-red-pi: Remove bash dependency (#3216) @a16bitsysop
Editor
- Improved regex for markdown renderer (#3213) @GerwinvBeek
- Fix TypedInput initialisation (#3220) @knolleary
Nodes
- MQTT: fix datatype in node config not used. fixes #3215 (#3219) @Steve-Mcl
#### 2.1.1: Maintenance Release
Editor
- Ensure tourGuide popover doesn't fall offscreen (#3212) @knolleary
- Fix issue with old inject nodes that migrated topic to 'string' type (#3210) @knolleary
- Add cache-busting query params to index.mst (#3211) @knolleary
- Fix TypedInput validation of type without options (#3207) @knolleary
#### 2.1.0: Milestone Release
Editor
- Position popover properly on a scrolled page
- Fixes from 2.1.0-beta.2 (#3202) @knolleary
Nodes
- Link Out: Fix saving link out node links (#3201) @knolleary
- Switch: Refix #3170 - copy switch rule type when adding new rule
- TCP Request: Add string option to TCP request node output (#3204) @dceejay
#### 2.1.0-beta.2: Beta Release
Editor
- Fix switching projects (#3199) @knolleary
- Use locale setting when installing/enabling node (#3198) @knolleary
- Do not show projects-wecome dialog until welcome tour completes (#3197) @knolleary
- Fix converting selection to subflow (#3196) @knolleary
- Avoid conflicts with native browser cmd-ctrl type shortcuts (#3195) @knolleary
- Ensure message tools stay attached to top-level entry in Debug/Context (#3186) @knolleary
- Ensure tab state updates properly when toggling enable state (#3175) @knolleary
- Improve handling of long labels in TreeList (#3176) @knolleary
- Shift-click tab scroll arrows to jump to start/end (#3177) @knolleary
Runtime
- Update package dependencies
- Update to latest node-red-admin
Nodes
- Dynamic MQTT connections (#3189)
- Link: Filter out Link Out Return nodes in Link In edit dialog Fixes #3187
- Link: Fix link call label (#3200) @knolleary
- Debug: Redesign debug filter options and make them persistant (#3183) @knolleary
- Inject: Widen Inject interval box for >1 digit (#3184) @knolleary
- Switch: Fix rule focus when switch 'otherwise' rule is used (#3185) @knolleary
#### 2.1.0-beta.1: Beta Release
Editor
@@ -46,7 +166,7 @@ Nodes
- Switch: Copy previous rule type when adding rule to switch node (#3170) @knolleary
- Delay node: add option to send intermediate messages on separate output (#3166) @knolleary
- Typo in http request set method translation (#3173) @mailsvb
#### 2.0.6: Maintenance Release
Editor

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

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "2.1.0-beta.1",
"version": "2.2.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,9 +26,9 @@
}
],
"dependencies": {
"acorn": "8.5.0",
"acorn": "8.6.0",
"acorn-walk": "8.2.0",
"ajv": "8.6.3",
"ajv": "8.8.2",
"async-mutex": "0.3.2",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
@@ -37,7 +37,7 @@
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.4.1",
"cookie-parser": "1.4.5",
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cronosjs": "1.7.1",
"denque": "2.0.1",
@@ -46,11 +46,11 @@
"form-data": "4.0.0",
"fs-extra": "10.0.0",
"fs.notify": "0.0.4",
"got": "11.8.2",
"got": "11.8.3",
"hash-sum": "2.0.0",
"hpagent": "0.1.2",
"https-proxy-agent": "5.0.0",
"i18next": "21.2.4",
"i18next": "21.5.4",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "3.14.1",
@@ -60,22 +60,22 @@
"media-typer": "1.1.0",
"memorystore": "1.6.6",
"mime": "2.5.2",
"moment-timezone": "0.5.33",
"moment-timezone": "0.5.34",
"mqtt": "4.2.8",
"multer": "1.4.3",
"mustache": "4.2.0",
"node-red-admin": "^2.2.0",
"node-red-admin": "^2.2.1",
"nopt": "5.0.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"on-headers": "1.0.2",
"passport": "0.5.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.4.1",
"raw-body": "2.4.2",
"semver": "7.3.5",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.14.2",
"uglify-js": "3.14.4",
"uuid": "8.3.2",
"ws": "7.5.1",
"xml2js": "0.4.23"
@@ -107,13 +107,13 @@
"i18next-http-backend": "1.3.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "3.0.4",
"marked": "3.0.7",
"minami": "1.2.3",
"mocha": "9.1.2",
"mocha": "9.1.3",
"node-red-node-test-helper": "^0.2.7",
"nodemon": "2.0.13",
"nodemon": "2.0.15",
"proxy": "^1.0.2",
"sass": "1.42.1",
"sass": "1.44.0",
"should": "13.2.3",
"sinon": "11.1.2",
"stoppable": "^1.1.0",

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

@@ -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-beta.1",
"version": "2.2.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "2.1.0-beta.1",
"@node-red/editor-client": "2.1.0-beta.1",
"@node-red/util": "2.2.0-beta.1",
"@node-red/editor-client": "2.2.0-beta.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"clone": "2.1.2",
@@ -28,7 +28,7 @@
"mime": "2.5.2",
"multer": "1.4.3",
"mustache": "4.2.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.5.0",

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",
@@ -90,6 +92,7 @@
"palette": {
"show": "Show palette"
},
"edit": "Edit",
"settings": "Settings",
"userSettings": "User Settings",
"nodes": "Nodes",
@@ -668,7 +671,8 @@
"unusedConfigNodes": "Unused configuration nodes",
"invalidNodes": "Invalid nodes",
"uknownNodes": "Unknown nodes",
"unusedSubflows": "Unused subflows"
"unusedSubflows": "Unused subflows",
"hiddenFlows": "Hidden flows"
}
},
"help": {
@@ -890,6 +894,8 @@
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "No matches found",
"addNode": "add a node..."
},
@@ -1135,6 +1141,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": "無効",
@@ -83,6 +92,7 @@
"palette": {
"show": "パレットを表示"
},
"edit": "編集",
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
@@ -105,6 +115,7 @@
"editPalette": "パレットの管理",
"other": "その他",
"showTips": "ヒントを表示",
"showWelcomeTours": "新バージョンのガイドツアーを表示",
"help": "Node-REDウェブサイト",
"projects": "プロジェクト",
"projects-new": "新規",
@@ -116,7 +127,20 @@
"groupSelection": "選択部分をグループ化",
"ungroupSelection": "選択部分をグループ解除",
"groupMergeSelection": "選択部分をマージ",
"groupRemoveSelection": "グループから削除"
"groupRemoveSelection": "グループから削除",
"arrange": "配置",
"alignLeft": "左揃え",
"alignCenter": "左右中央揃え",
"alignRight": "右揃え",
"alignTop": "上揃え",
"alignMiddle": "上下中央揃え",
"alignBottom": "下揃え",
"distributeHorizontally": "左右に整列",
"distributeVertically": "上下に整列",
"moveToBack": "最背面へ移動",
"moveToFront": "最前面へ移動",
"moveBackwards": "背面へ移動",
"moveForwards": "前面へ移動"
}
},
"actions": {
@@ -451,7 +475,8 @@
"global": "グローバル",
"workspace": "ワークスペース",
"selectAll": "全てのノードを選択",
"selectAllConnected": "接続された全てのノードを選択",
"selectNone": "選択を外す",
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
@@ -461,10 +486,13 @@
"moveNode": "選択したノードを移動(移動量大)",
"toggleSidebar": "サイドバーの表示/非表示",
"togglePalette": "パレットの表示/非表示",
"copyNode": "選択したノードをコピー",
"cutNode": "選択したノードを切り取り",
"copyNode": "ノードをコピー",
"cutNode": "ノードを切り取り",
"pasteNode": "ノードを貼り付け",
"copyGroupStyle": "グループ様式をコピー",
"pasteGroupStyle": "グループ様式を貼り付け",
"undoChange": "変更操作を戻す",
"redoChange": "変更操作をやり直し",
"searchBox": "ノードを検索",
"managePalette": "パレットの管理",
"actionList": "動作一覧"
@@ -519,7 +547,8 @@
"nodeEnabled_plural": "ノードを有効化しました:",
"nodeDisabled": "ノードを無効化しました:",
"nodeDisabled_plural": "ノードを無効化しました:",
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました"
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました",
"unknownNodeRegistered": "ノードの読み込みエラー: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "パレットの管理",
@@ -642,7 +671,8 @@
"unusedConfigNodes": "未使用の設定ノード",
"invalidNodes": "不正なノード",
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー"
"unusedSubflows": "未使用のサブフロー",
"hiddenFlows": "非表示のフロー"
}
},
"help": {
@@ -1108,6 +1138,11 @@
"preview": "UIプレビュー",
"defaultValue": "デフォルト値"
},
"tourGuide": {
"takeATour": "ツアーを開始",
"start": "開始",
"next": "次へ"
},
"languages": {
"de": "ドイツ語",
"en-US": "英語",

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-beta.1",
"version": "2.2.0-beta.1",
"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 = {};
@@ -266,8 +269,8 @@ RED.nodes = (function() {
},
moveNode: function(n, newZ) {
api.removeNode(n);
tabMap[newZ] = tabMap[newZ] || [];
tabMap[newZ].push(n);
n.z = newZ;
api.addNode(n)
},
moveNodesForwards: function(nodes) {
var result = [];
@@ -591,7 +594,9 @@ RED.nodes = (function() {
}
allNodes.addNode(n);
if (!nodeLinks[n.id]) {
nodeLinks[n.id] = {in:[],out:[]};
nodeLinks[n.id] = {
inCount:[],outCount:[],in:[],out:[]
};
}
}
RED.events.emit('nodes:add',n);
@@ -601,15 +606,19 @@ RED.nodes = (function() {
if (l.source) {
// Possible the node hasn't been added yet
if (!nodeLinks[l.source.id]) {
nodeLinks[l.source.id] = {in:[],out:[]};
nodeLinks[l.source.id] = {inCount:[],outCount:[],in:[],out:[]};
}
nodeLinks[l.source.id].out.push(l);
nodeLinks[l.source.id].outCount[l.sourcePort] = (nodeLinks[l.source.id].outCount[l.sourcePort] || 0) + 1
l.source.dirty = true;
}
if (l.target) {
if (!nodeLinks[l.target.id]) {
nodeLinks[l.target.id] = {in:[],out:[]};
nodeLinks[l.target.id] = {inCount:[],outCount:[],in:[],out:[]};
}
nodeLinks[l.target.id].in.push(l);
nodeLinks[l.target.id].inCount[0] = (nodeLinks[l.target.id].inCount[0] || 0) + 1
l.target.dirty = true;
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
linkTabMap[l.source.z].push(l);
@@ -719,29 +728,29 @@ RED.nodes = (function() {
moveGroupToTab(node,z);
return;
}
var oldZ = node.z;
allNodes.moveNode(node,z);
var nl = nodeLinks[node.id];
if (nl) {
nl.in.forEach(function(l) {
var idx = linkTabMap[node.z].indexOf(l);
var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) {
linkTabMap[node.z].splice(idx, 1);
linkTabMap[oldZ].splice(idx, 1);
}
if ((l.source.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l);
}
});
nl.out.forEach(function(l) {
var idx = linkTabMap[node.z].indexOf(l);
var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) {
linkTabMap[node.z].splice(idx, 1);
linkTabMap[oldZ].splice(idx, 1);
}
if ((l.target.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l);
}
});
}
node.z = z;
RED.events.emit("nodes:change",node);
}
function moveGroupToTab(group, z) {
@@ -758,15 +767,19 @@ RED.nodes = (function() {
if (index != -1) {
links.splice(index,1);
if (l.source && nodeLinks[l.source.id]) {
l.source.dirty = true;
var sIndex = nodeLinks[l.source.id].out.indexOf(l)
if (sIndex !== -1) {
nodeLinks[l.source.id].out.splice(sIndex,1)
nodeLinks[l.source.id].outCount[l.sourcePort]--
}
}
if (l.target && nodeLinks[l.target.id]) {
l.target.dirty = true;
var tIndex = nodeLinks[l.target.id].in.indexOf(l)
if (tIndex !== -1) {
nodeLinks[l.target.id].in.splice(tIndex,1)
nodeLinks[l.target.id].inCount[0]--
}
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
@@ -805,7 +818,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 +855,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 +1110,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;
@@ -2384,7 +2402,6 @@ RED.nodes = (function() {
}
function clear() {
allNodes.clear();
links = [];
linkTabMap = {};
nodeLinks = {};
@@ -2405,6 +2422,8 @@ RED.nodes = (function() {
initialLoad = null;
workspaces = {};
allNodes.clear();
RED.nodes.dirty(false);
RED.view.redraw(true, true);
RED.palette.refresh();
@@ -2452,6 +2471,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) {
@@ -2533,7 +2690,7 @@ RED.nodes = (function() {
add: addNode,
remove: removeNode,
clear: clear,
detachNodes: detachNodes,
moveNodesForwards: moveNodesForwards,
moveNodesBackwards: moveNodesBackwards,
moveNodesToFront: moveNodesToFront,
@@ -2545,7 +2702,34 @@ 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 [];
},
getNodeLinkCount: function(id,portType,index) {
// We *could* just let callers use `getNodeLinks` and get the
// the length for themselves. However, that function creates
// a clone of the array - which is needless work if all you
// want is the length
if (nodeLinks[id]) {
if (portType === 1) {
return nodeLinks[id].inCount[index] || 0
} else {
return nodeLinks[id].outCount[index] || 0
}
}
return 0;
},
addWorkspace: addWorkspace,
removeWorkspace: removeWorkspace,
getWorkspaceOrder: function() { return workspacesOrder },
@@ -2619,6 +2803,7 @@ RED.nodes = (function() {
getAllFlowNodes: getAllFlowNodes,
getAllUpstreamNodes: getAllUpstreamNodes,
getAllDownstreamNodes: getAllDownstreamNodes,
getNodeIslands: getNodeIslands,
createExportableNodeSet: createExportableNodeSet,
createCompleteNodeSet: createCompleteNodeSet,
updateConfigNodeUsers: updateConfigNodeUsers,

View File

@@ -201,6 +201,7 @@ var RED = (function() {
RED.projects.refresh(function(activeProject) {
loadFlows(function() {
RED.sidebar.info.refresh()
var showProjectWelcome = false;
if (!activeProject) {
// Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true);
@@ -208,10 +209,10 @@ var RED = (function() {
if (activeProject === false) {
// User previously decline the migration to projects.
} else { // null/undefined
RED.projects.showStartup();
showProjectWelcome = true;
}
}
completeLoad();
completeLoad(showProjectWelcome);
});
});
} else {
@@ -251,6 +252,9 @@ var RED = (function() {
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6),true);
}
if (RED.workspaces.active() === 0 && RED.workspaces.count() > 0) {
RED.workspaces.show(RED.nodes.getWorkspaceOrder()[0])
}
} catch(err) {
console.warn(err);
RED.notify(
@@ -267,7 +271,7 @@ var RED = (function() {
});
}
function completeLoad() {
function completeLoad(showProjectWelcome) {
var persistentNotifications = {};
RED.comms.subscribe("notification/#",function(topic,msg) {
var parts = topic.split("/");
@@ -471,22 +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() {
$.get('nodes/'+id, 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];
@@ -499,18 +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 {
$.get('nodes/'+msg.id, function(data) {
appendNodeConfig(data);
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.nodeAdded", {count:msg.types.length})+typeList,"success");
});
}
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) {
@@ -535,18 +561,24 @@ var RED = (function() {
setTimeout(function() {
loader.end();
checkFirstRun();
checkFirstRun(function() {
if (showProjectWelcome) {
RED.projects.showStartup();
}
});
},100);
}
function checkFirstRun() {
function checkFirstRun(done) {
if (RED.settings.theme("tours") === false) {
done();
return;
}
if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) {
done();
return;
}
RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"));
RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"), done);
}
function buildMainMenu() {
@@ -558,7 +590,7 @@ var RED = (function() {
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
]});
}
menuOptions.push({id:"menu-item-edit-menu", label:"Edit", options: [
menuOptions.push({id:"menu-item-edit-menu", label:RED._("menu.label.edit"), options: [
{id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"},
{id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"},
null,

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

@@ -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();
}
}
],

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

@@ -436,18 +436,17 @@ RED.popover = (function() {
return {
create: createPopover,
tooltip: function(target,content, action) {
var label = content;
if (action) {
label = function() {
var label = content;
var label = function() {
var label = content;
if (action) {
var shortcut = RED.keyboard.getShortcut(action);
if (shortcut && shortcut.key) {
label = $('<span>'+content+' <span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span></span>');
}
return label;
}
return label;
}
return RED.popover.create({
var popover = RED.popover.create({
tooltip: true,
target:target,
trigger: "hover",
@@ -456,6 +455,14 @@ RED.popover = (function() {
content: label,
delay: { show: 750, hide: 50 }
});
popover.setContent = function(newContent) {
content = newContent;
}
popover.setAction = function(newAction) {
action = newAction;
}
return popover;
},
menu: function(options) {
var list = $('<ul class="red-ui-menu"></ul>');
@@ -596,7 +603,7 @@ RED.popover = (function() {
var panelWidth = panel.width();
var top = (targetHeight+pos.top) + offset[1];
if (top+panelHeight > $(window).height()) {
if (top+panelHeight-$(document).scrollTop() > $(window).height()) {
top -= (top+panelHeight)-$(window).height() + 5;
}
if (top < 0) {

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");
@@ -164,9 +166,9 @@ RED.tabs = (function() {
}
})
scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a");
scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();});
scrollLeft.on('mousedown',function(evt) {scrollEventHandler(evt, evt.shiftKey?('-='+scrollContainer.scrollLeft()):'-=150') }).on('click',function(evt){ evt.preventDefault();});
scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a");
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();});
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,evt.shiftKey?('+='+(scrollContainer[0].scrollWidth - scrollContainer.width()-scrollContainer.scrollLeft())):'+=150') }).on('click',function(evt){ evt.preventDefault();});
}
if (options.collapsible) {
@@ -576,7 +578,7 @@ RED.tabs = (function() {
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 +588,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 +811,18 @@ RED.tabs = (function() {
event.preventDefault();
removeTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
}
if (tab.hideable) {
li.addClass("red-ui-tabs-closeable")
var closeLink = $("<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 +831,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 +951,6 @@ RED.tabs = (function() {
renameTab: function(id,label) {
tabs[id].label = label;
var tab = ul.find("a[href='#"+id+"']");
tab.attr("title",label);
tab.find("span.red-ui-text-bidi-aware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label));
updateTabWidths();
},

View File

@@ -24,6 +24,9 @@
* - rootSortable: boolean - if 'sortable' is set, then setting this to
* false, prevents items being sorted to the
* top level of the tree
* - autoSelect: boolean - default true - triggers item selection when navigating
* list by keyboard. If the list has checkboxed items
* you probably want to set this to false
*
* methods:
* - data(items) - clears existing items and replaces with new data
@@ -50,6 +53,7 @@
* deferBuild: true/false, // don't build any ui elements for the item's children
* until it is expanded by the user.
* element: // custom dom element to use for the item - ignored if `label` is set
* collapsible: true/false, // prevent a parent item from being collapsed. default true.
* }
* ]
*
@@ -90,77 +94,99 @@
$.widget( "nodered.treeList", {
_create: function() {
var that = this;
var autoSelect = true;
if (that.options.autoSelect === false) {
autoSelect = false;
}
this.element.addClass('red-ui-treeList');
this.element.attr("tabIndex",0);
var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element);
this.element.on('keydown', function(evt) {
var selected = that._topList.find(".selected").parent().data('data');
if (!selected && (evt.keyCode === 40 || evt.keyCode === 38)) {
that.select(that._data[0]);
var focussed = that._topList.find(".focus").parent().data('data');
if (!focussed && (evt.keyCode === 40 || evt.keyCode === 38)) {
if (that._data[0]) {
if (autoSelect) {
that.select(that._data[0]);
} else {
that._topList.find(".focus").removeClass("focus")
}
that._data[0].treeList.label.addClass('focus')
}
return;
}
var target;
switch(evt.keyCode) {
case 32: // SPACE
case 13: // ENTER
if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
return
}
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
if (focussed.checkbox) {
focussed.treeList.checkbox.trigger("click");
} else if (focussed.radio) {
focussed.treeList.radio.trigger("click");
} else if (focussed.children) {
if (focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.collapse()
} else {
selected.treeList.expand()
focussed.treeList.expand()
}
} else {
that._trigger("confirm",null,selected)
that._trigger("confirm",null,focussed)
}
break;
case 37: // LEFT
evt.preventDefault();
evt.stopPropagation();
if (selected.children&& selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
} else if (selected.parent) {
target = selected.parent;
if (focussed.children&& focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.collapse()
} else if (focussed.parent) {
target = focussed.parent;
}
break;
case 38: // UP
evt.preventDefault();
evt.stopPropagation();
target = that._getPreviousSibling(selected);
target = that._getPreviousSibling(focussed);
if (target) {
target = that._getLastDescendant(target);
}
if (!target && selected.parent) {
target = selected.parent;
if (!target && focussed.parent) {
target = focussed.parent;
}
break;
case 39: // RIGHT
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (!selected.treeList.container.hasClass("expanded")) {
selected.treeList.expand()
if (focussed.children) {
if (!focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.expand()
}
}
break
case 40: //DOWN
evt.preventDefault();
evt.stopPropagation();
if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) {
target = selected.children[0];
if (focussed.children && Array.isArray(focussed.children) && focussed.children.length > 0 && focussed.treeList.container.hasClass("expanded")) {
target = focussed.children[0];
} else {
target = that._getNextSibling(selected);
while (!target && selected.parent) {
selected = selected.parent;
target = that._getNextSibling(selected);
target = that._getNextSibling(focussed);
while (!target && focussed.parent) {
focussed = focussed.parent;
target = that._getNextSibling(focussed);
}
}
break
}
if (target) {
that.select(target);
if (autoSelect) {
that.select(target);
} else {
that._topList.find(".focus").removeClass("focus")
}
target.treeList.label.addClass('focus')
}
});
this._data = [];
@@ -314,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({
@@ -463,6 +489,9 @@
container.addClass("expanded");
}
item.treeList.collapse = function() {
if (item.collapsible === false) {
return
}
if (!item.children) {
return;
}
@@ -533,10 +562,12 @@
}).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,
width: labelPaddingWidth+'px'
}).appendTo(label);
@@ -582,7 +613,7 @@
// Already a parent because we've got the angle-right icon
return;
}
$('<i class="fa fa-angle-right" />').appendTo(treeListIcon);
$('<i class="fa fa-angle-right" />').toggleClass("hide",item.collapsible === false).appendTo(treeListIcon);
treeListIcon.on("click.red-ui-treeList-expand", function(e) {
e.stopPropagation();
e.preventDefault();
@@ -633,6 +664,8 @@
label.on("click", function(e) {
e.stopPropagation();
cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
})
}
item.treeList.select = function(v) {
@@ -640,6 +673,7 @@
cb.trigger("click");
}
}
item.treeList.checkbox = cb;
selectWrapper.appendTo(label)
} else if (item.radio) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>');
@@ -668,6 +702,8 @@
label.on("click", function(e) {
e.stopPropagation();
cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
})
}
item.treeList.select = function(v) {
@@ -676,6 +712,7 @@
}
}
selectWrapper.appendTo(label)
item.treeList.radio = cb;
} else {
label.on("click", function(e) {
if (!that.options.multi) {
@@ -683,10 +720,14 @@
}
label.addClass("selected");
that._selected.add(item);
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
that._trigger("select",e,item)
})
label.on("dblclick", function(e) {
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
if (!item.children) {
that._trigger("confirm",e,item);
}
@@ -834,6 +875,9 @@
if (item.treeList.label) {
item.treeList.label.addClass("selected");
}
that._topList.find(".focus").removeClass("focus");
if (triggerEvent !== false) {
this._trigger("select",null,item)
}
@@ -841,6 +885,9 @@
clearSelection: function() {
this._selected.forEach(function(item) {
item.selected = false;
if (item.treeList.checkbox) {
item.treeList.checkbox.prop('checked',false)
}
if (item.treeList.label) {
item.treeList.label.removeClass("selected")
}

View File

@@ -345,6 +345,47 @@
}
}
};
// For a type with options, check value is a valid selection
// If !opt.multiple, returns the valid option object
// if opt.multiple, returns an array of valid option objects
// If not valid, returns null;
function isOptionValueValid(opt, currentVal) {
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
if (typeof op === "string" && op === currentVal) {
return {value:currentVal}
} else if (op.value === currentVal) {
return op;
}
}
} else {
// Check to see if value is a valid csv of
// options.
var currentValues = {};
var selected = [];
currentVal.split(",").forEach(function(v) {
if (v) {
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
var val = typeof op === "string" ? op : op.value;
if (currentValues.hasOwnProperty(val)) {
delete currentValues[val];
selected.push(typeof op === "string" ? {value:op} : op.value)
}
}
if (!$.isEmptyObject(currentValues)) {
return null;
}
return selected
}
}
var nlsd = false;
$.widget( "nodered.typedInput", {
@@ -378,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);
@@ -408,6 +450,8 @@
});
this.defaultInputType = this.input.attr('type');
// Used to remember selections per-type to restore them when switching between types
this.oldValues = {};
this.uiSelect.addClass("red-ui-typedInput-container");
@@ -490,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();
@@ -512,10 +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);
// Used to remember selections per-type to restore them when switching between types
this.oldValues = {};
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);
}
@@ -763,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') {
@@ -787,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()
@@ -806,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) {
@@ -814,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) {
@@ -859,23 +920,56 @@
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] = this.input.val();
this.oldValues[previousType.value] = previousValue;
} else if (previousType.hasValue === false) {
this.oldValues[previousType.value] = this.input.val();
this.oldValues[previousType.value] = previousValue;
} else {
this.oldValues["_"] = this.input.val();
this.oldValues["_"] = previousValue;
}
if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) {
this.input.val(this.oldValues.hasOwnProperty(opt.value)?this.oldValues[opt.value]:(opt.default||[]).join(","))
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 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 (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) {
@@ -883,6 +977,7 @@
}
}
this.propertyType = type;
this.typeChanged = true;
if (this.typeField) {
this.typeField.val(type);
}
@@ -951,22 +1046,12 @@
var op;
if (!opt.hasValue) {
var validValue = false;
var currentVal = this.input.val();
// Check the value is valid for the available options
var validValues = isOptionValueValid(opt,this.input.val());
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
if (typeof op === "string" && op === currentVal) {
that._updateOptionSelectLabel({value:currentVal});
validValue = true;
break;
} else if (op.value === currentVal) {
that._updateOptionSelectLabel(op);
validValue = true;
break;
}
}
if (!validValue) {
if (validValues) {
that._updateOptionSelectLabel(validValues)
} else {
op = opt.options[0];
if (typeof op === "string") {
this.value(op);
@@ -977,31 +1062,19 @@
}
}
} else {
// Check to see if value is a valid csv of
// options.
var currentValues = {};
var selected = [];
currentVal.split(",").forEach(function(v) {
if (v) {
selected.push(v);
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
delete currentValues[op.value||op];
if (!validValues) {
validValues = (opt.default || []).map(function(v) {
return typeof v === "string"?v:v.value
});
this.value(validValues.join(","));
}
if (!$.isEmptyObject(currentValues)) {
selected = opt.default || [];
// Invalid, set to default/empty
this.value(selected.join(","));
}
that._updateOptionSelectLabel(selected);
that._updateOptionSelectLabel(validValues);
}
} 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)) {
@@ -1025,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,16 @@ RED.deploy = (function() {
var unknownNodes = [];
var invalidNodes = [];
RED.nodes.eachConfig(function(node) {
if (!node.valid && !node.d) {
invalidNodes.push(getNodeInfo(node));
}
if (node.type === "unknown") {
if (unknownNodes.indexOf(node.name) == -1) {
unknownNodes.push(node.name);
}
}
});
RED.nodes.eachNode(function(node) {
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);
}
}
@@ -1755,8 +1764,12 @@ RED.editor = (function() {
editState.changes.disabled = workspace.disabled;
editState.changed = true;
workspace.disabled = disabled;
}
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
if (workspace.id === RED.workspaces.active()) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
}
if (editState.changed) {
var historyEvent = {

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

@@ -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

@@ -243,7 +243,13 @@ RED.keyboard = (function() {
function resolveKeyEvent(evt) {
var slot = partialState||handlers;
if (evt.ctrlKey || evt.metaKey) {
// We cheat with MacOS CMD key and consider it the same as Ctrl.
// That means we don't have to have separate keymaps for different OS.
// It mostly works.
// One exception is shortcuts that include both Cmd and Ctrl. We don't
// support them - but we need to make sure we don't block browser-specific
// shortcuts (such as Cmd-Ctrl-F for fullscreen).
if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) {
slot = slot.ctrl;
}
if (slot && evt.shiftKey) {

View File

@@ -2387,6 +2387,7 @@ RED.projects = (function() {
return {
init: init,
showStartup: function() {
console.warn("showStartup")
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;

View File

@@ -22,6 +22,7 @@ RED.search = (function() {
var selected = -1;
var visible = false;
var searchHistory = [];
var index = {};
var currentResults = [];
var previousActiveElement;
@@ -105,6 +106,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 +152,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 +206,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 +228,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 +305,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 +326,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 +406,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 +430,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

@@ -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

@@ -379,7 +379,7 @@ RED.sidebar.help = (function() {
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) {
tourHeader = '<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>'
@@ -393,10 +393,12 @@ RED.sidebar.help = (function() {
treeList.treeList("select","changelog");
show();
}
function showWelcomeTour(lastSeenVersion) {
function showWelcomeTour(lastSeenVersion, done) {
done = done || function() {};
RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
if (err) {
console.warn("Failed to load welcome tour",err);
done()
return;
}
var currentVersionParts = RED.settings.version.split(".");
@@ -405,6 +407,7 @@ RED.sidebar.help = (function() {
// Only display the tour if its MAJ.MIN versions the current version
// This means if we update MAJ/MIN without updating the tour, the old tour won't get shown
if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) {
done()
return;
}
@@ -412,26 +415,31 @@ RED.sidebar.help = (function() {
// Previously displayed a welcome tour.
if (lastSeenVersion === RED.settings.version) {
// Exact match - don't show the tour
done()
return;
}
var lastSeenParts = lastSeenVersion.split(".");
if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) {
// Running an *older* version than last displayed tour.
done()
return;
}
if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) {
if (lastSeenParts.length === 3 && currentVersionParts.length === 3) {
// Matching non-beta MAJ.MIN - don't repeat tour
done()
return;
}
if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) {
// Running an *older* beta than last displayed tour.
done()
return
}
}
}
RED.tourGuide.run("./tours/welcome.js", function(err) {
RED.settings.set("editor.tours.welcome", RED.settings.version)
done()
})
})

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"},
]
});
@@ -287,11 +290,11 @@ RED.sidebar.info.outliner = (function() {
var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
if (node) {
if (node.type === 'group' || node._def.category !== "config") {
RED.view.select({nodes:[node]})
// RED.view.select({nodes:[node]})
} else if (node._def.category === "config") {
RED.sidebar.info.refresh(node);
} else {
RED.view.select({nodes:[]})
// RED.view.select({nodes:[]})
}
}
})
@@ -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

@@ -348,7 +348,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 +364,8 @@ RED.tourGuide = (function() {
if (step.fallback) {
focus.one("mouseenter", function(evt) {
setTimeout(function() {
var pos = targetElement[0].getBoundingClientRect();
var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5);
focus.css({
width: (4*dimension)+"px",
height: (4*dimension)+"px"

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) {
@@ -39,12 +39,12 @@ RED.utils = (function() {
type: 'descriptionList', // Should match "name" above
raw: match[0], // Text to consume from the source
text: match[0].trim(), // Additional custom properties
tokens: this.inlineTokens(match[0].trim()) // inlineTokens to process **bold**, *italics*, etc.
tokens: this.lexer.inlineTokens(match[0].trim()) // inlineTokens to process **bold**, *italics*, etc.
};
}
},
renderer(token) {
return `<dl class="message-properties">${this.parseInline(token.tokens)}\n</dl>`; // parseInline to turn child tokens into HTML
return `<dl class="message-properties">${this.parser.parseInline(token.tokens)}\n</dl>`; // parseInline to turn child tokens into HTML
}
};
@@ -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) {
@@ -64,14 +64,14 @@ RED.utils = (function() {
return { // Token to generate
type: 'description', // Should match "name" above
raw: match[0], // Text to consume from the source
dt: this.inlineTokens(match[1].trim()), // Additional custom properties
types: this.inlineTokens(match[2].trim()),
dd: this.inlineTokens(match[3].trim()),
dt: this.lexer.inlineTokens(match[1].trim()), // Additional custom properties
types: this.lexer.inlineTokens(match[2].trim()),
dd: this.lexer.inlineTokens(match[3].trim()),
};
}
},
renderer(token) {
return `\n<dt>${this.parseInline(token.dt)}<span class="property-type">${this.parseInline(token.types)}</span></dt><dd>${this.parseInline(token.dd)}</dd>`;
return `\n<dt>${this.parser.parseInline(token.dt)}<span class="property-type">${this.parser.parseInline(token.types)}</span></dt><dd>${this.parser.parseInline(token.dd)}</dd>`;
},
childTokens: ['dt', 'dd'], // Any child tokens to be visited by walkTokens
walkTokens(token) { // Post-processing on the completed token tree
@@ -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')) {
@@ -563,7 +567,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -592,7 +597,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -647,7 +653,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}

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

@@ -208,65 +208,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 +425,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 +471,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 +552,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,9 +579,6 @@ 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) {

View File

@@ -196,14 +196,26 @@ $view-background: $secondary-background;
$view-select-mode-background: $secondary-background-selected;
$view-grid-color: #eee;
$link-color: #999;
$link-link-color: #aaa;
$link-disabled-color: #ccc;
$link-link-active-color: #ff7f0e;
$link-unknown-color: #f00;
$node-label-color: #333;
$node-port-label-color: #888;
$node-border: #999;
$node-border-unknown: #f33;
$node-border-placeholder: #aaa;
$node-background-placeholder: #eee;
$node-port-border: $node-border;
$node-port-border-connected: $link-color;
$node-port-background: #d9d9d9;
$node-port-background-hover: #eee;
$node-port-background-connected: $link-color;
$node-icon-color: #fff;
$node-icon-background-color: rgba(0,0,0,0.05);
$node-icon-background-color-fill: #000;
@@ -231,12 +243,6 @@ $node-status-colors: (
$node-selected-color: #ff7f0e;
$port-selected-color: #ff7f0e;
$link-color: #999;
$link-link-color: #aaa;
$link-disabled-color: #ccc;
$link-link-active-color: #ff7f0e;
$link-unknown-color: #f00;
$clipboard-textarea-background: #F3E7E7;

View File

@@ -43,12 +43,24 @@
border-bottom: 1px solid $secondary-border-color;
box-shadow: 0 2px 6px $shadow;
}
.red-ui-debug-filter-row {
.red-ui-nodeList {
margin: 10px 0;
#red-ui-sidebar-debug-filter-node-list-row {
.red-ui-treeList-label.disabled {
font-style: italic;
color: $secondary-text-color-disabled;
}
.red-ui-treeList-label {
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: inherit;
}
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: inherit;
}
&.focus, &.focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover !important;
}
}
}
.red-ui-debug-msg {
position: relative;
border-bottom: 1px solid $debug-message-border;

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;
@@ -178,12 +185,29 @@
}
.red-ui-flow-port {
stroke: $node-border;
stroke: $node-port-border;
stroke-width: 1;
fill: $node-port-background;
cursor: crosshair;
}
.red-ui-flow-port-background {
opacity: 0;
stroke: none;
fill: #f00;
cursor: crosshair;
}
.red-ui-flow-port-shape {
pointer-events: none;
cursor: crosshair;
}
.red-ui-flow-port-connected .red-ui-flow-port {
fill: $node-port-background-connected;
stroke: $node-port-border-connected;
}
.red-ui-flow-node-error {
fill: $node-status-error-background;
stroke: $node-status-error-border;
@@ -273,9 +297,10 @@ g.red-ui-flow-node-selected {
text-anchor:start;
}
.red-ui-flow-port-hovered {
stroke: $port-selected-color;
fill: $port-selected-color;
.red-ui-flow-port-hovered:not(.red-ui-flow-port-background),
.red-ui-flow-port-background.red-ui-flow-port-hovered + .red-ui-flow-port-shape {
stroke: $port-selected-color !important;
fill: $port-selected-color !important;
}
.red-ui-flow-subflow-port {

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

@@ -73,13 +73,13 @@
.red-ui-projects-dialog-screen-start {
.red-ui-projects-dialog-screen-start-hero {
text-align: center;
font-size: 2em;
font-size: 1.4em;
padding: 10px;
min-height: 60px;
min-height: 40px;
color: $primary-text-color;
}
.red-ui-projects-dialog-screen-start-body {
min-height: 400px;
min-height: 300px;
line-height: 1.6em;
p {
font-size: 1.1em;
@@ -92,7 +92,7 @@
}
button.red-ui-button.red-ui-projects-dialog-button {
width: calc(50% - 80px);
margin: 20px;
margin: 10px 20px;
height: auto;
line-height: 2em;
padding: 10px;

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

@@ -16,6 +16,7 @@
.red-ui-sidebar-info {
height: 100%;
overflow: hidden;
}
.red-ui-sidebar-info hr {
margin: 10px 0;
@@ -433,16 +434,19 @@ div.red-ui-info-table {
}
.red-ui-info-outline-item-controls {
position: absolute;
top:0;
bottom: 0;
right: 0px;
padding: 2px 3px 0 1px;
top:1px;
bottom: 1px;
right: 1px;
padding: 1px 2px 0 1px;
text-align: right;
background: $list-item-background;
.red-ui-treeList-label:hover & {
background: $list-item-background-hover;
}
.red-ui-treeList-label.focus & {
background: $list-item-background-hover;
}
.red-ui-treeList-label.selected & {
background: $list-item-background-selected;
}

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

@@ -89,6 +89,12 @@
color: $list-item-color;
text-decoration: none;
}
&.focus, &.focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover;
outline: 1px solid $form-input-focus-color !important;
outline-offset: -1px;
color: $list-item-color;
}
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: $list-item-background-selected;
outline: none;
@@ -102,6 +108,8 @@
}
.red-ui-treeList-label-text {
margin-left: 4px;
overflow: hidden;
text-overflow: ellipsis;
&:empty {
min-height: 20px;
}
@@ -121,6 +129,7 @@
.red-ui-treeList-icon {
display: inline-block;
flex-shrink: 0;
width: 20px;
text-align: center;
}

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",

View File

@@ -3,48 +3,79 @@ export default {
steps: [
{
titleIcon: "fa fa-map-o",
title: { "en-US": "Welcome to Node-RED 2.1!" },
description: { "en-US": "Let's take a moment to discover the new features in this release." }
title: {
"en-US": "Welcome to Node-RED 2.1!",
"ja": "Node-RED 2.1へようこそ!"
},
description: {
"en-US": "Let's take a moment to discover the new features in this release.",
"ja": "本リリースの新機能を見つけてみましょう。"
}
},
{
title: { "en-US": "A new Tour Guide" },
description: { "en-US": "<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": "A new Tour Guide",
"ja": "新しいツアーガイド"
},
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>",
"ja": "<p>最初に、既に見つけている様に、新機能の本ツアーがあります。本ツアーは、新バージョンのNode-REDフローエディタを初めて開いた時のみ表示されます。</p>" +
"<p>ユーザ設定の表示タブの中で、この機能を無効化することで、本ツアーを表示しないようにすることもできます。</p>"
}
},
{
title: { "en-US": "New Edit menu" },
title: {
"en-US": "New Edit menu",
"ja": "新しい編集メニュー"
},
prepare() {
$("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-edit-menu").parent().addClass("open")
$("#menu-item-edit-menu").parent().addClass("open");
},
complete() {
$("#menu-item-edit-menu").parent().removeClass("open")
$("#menu-item-edit-menu").parent().removeClass("open");
},
element: "#menu-item-edit-menu-submenu",
interactive: false,
direction: "left",
description: { "en-US": "<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>" }
description: {
"en-US": "<p>The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.</p>" +
"<p>The menu now displays keyboard shortcuts for the options.</p>",
"ja": "<p>メインメニューに「編集」セクションが追加されました。本セクションには、切り取り/貼り付けや、変更操作を戻す/やり直しの様な使い慣れたオプションが含まれています。</p>" +
"<p>本メニューには、オプションのためのキーボードショートカットも表示されるようになりました。</p>"
}
},
{
title: { "en-US": "Arranging nodes" },
title: {
"en-US": "Arranging nodes",
"ja": "ノードの配置"
},
prepare() {
$("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-arrange-menu").parent().addClass("open")
$("#menu-item-arrange-menu").parent().addClass("open");
},
complete() {
$("#menu-item-arrange-menu").parent().removeClass("open")
$("#menu-item-arrange-menu").parent().removeClass("open");
},
element: "#menu-item-arrange-menu-submenu",
interactive: false,
direction: "left",
description: { "en-US": "<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>" },
description: {
"en-US": "<p>The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.</p>",
"ja": "<p>メニューの新しい「配置」セクションには、ノードの配置を助ける新しいオプションが提供されています。ノードの端を揃えたり、均等に配置したり、表示順序を変更したりできます。</p>"
}
},
{
title: { "en-US": "Hiding tabs" },
title: {
"en-US": "Hiding tabs",
"ja": "タブの非表示"
},
element: "#red-ui-workspace-tabs > li.active",
description: { "en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-times"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.' },
description: {
"en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-eye-slash"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.',
"ja": '<p><i class="fa fa-eye-slash"></i> アイコンをクリックすることで、タブを非表示にできます。</p><p>情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。'
},
interactive: false,
prepare() {
$("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","block");
@@ -54,9 +85,15 @@ export default {
}
},
{
title: { "en-US": "Tab menu" },
title: {
"en-US": "Tab menu",
"ja": "タブメニュー"
},
element: "#red-ui-workspace-tabs-menu",
description: { "en-US": '<p>The new tab menu also provides lots of new options for your tabs.</p>' },
description: {
"en-US": "<p>The new tab menu also provides lots of new options for your tabs.</p>",
"ja": "<p>新しいタブメニューには、タブに関する沢山の新しいオプションが提供されています。</p>"
},
interactive: false,
direction: "left",
prepare() {
@@ -67,10 +104,16 @@ export default {
}
},
{
title: { "en-US": "Flow and Group level environment variables" },
title: {
"en-US": "Flow and Group level environment variables",
"ja": "フローとグループの環境変数"
},
element: "#red-ui-workspace-tabs > li.active",
interactive: false,
description: { "en-US": "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>" },
description: {
"en-US": "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>",
"ja": "<p>フローとグループには、内部のノードから参照できる環境変数を設定できるようになりました。</p>"
}
},
{
prepare(done) {
@@ -78,44 +121,86 @@ export default {
setTimeout(done,700);
},
element: "#red-ui-tab-editor-tab-envProperties-link-button",
description: { "en-US": "<p>Their edit dialogs have a new Environment Variables section.</p>" },
description: {
"en-US": "<p>Their edit dialogs have a new Environment Variables section.</p>",
"ja": "<p>編集ダイアログに環境変数セクションが追加されました。</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>' },
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>',
"ja": '<p>この表に環境変数が一覧表示されており、<i class="fa fa-plus"></i>ボタンをクリックすることで新しい変数を追加できます。</p>'
},
complete(done) {
$("#node-dialog-cancel").trigger("click");
setTimeout(done,500);
}
},
{
title: {"en-US":"Link Call node added"},
title: {
"en-US": "Link Call node added",
"ja": "Link Callードを追加"
},
prepare(done) {
this.paletteWasClosed = $("#red-ui-main-container").hasClass("red-ui-palette-closed");
RED.actions.invoke("core:toggle-palette",true)
$('[data-palette-type="link call"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
},
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>' },
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>",
"ja": "<p><code>Link Call</code>ノードを用いることで、<code>Link In</code>ノードから始まるフローを呼び出し、<code>Link Out</code>ノードに到達した時に、結果を取得できます。</p>"
}
},
{
title: {"en-US":"File nodes renamed"},
title: {
"en-US": "MQTT nodes support dynamic connections",
"ja": "MQTTードが動的接続をサポート"
},
prepare(done) {
$('[data-palette-type="file"]')[0].scrollIntoView({block:"center"})
$('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
},
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>',
"ja": '<p><code>MQTT</code>ノードは、動的な接続や購読ができるようになりました。</p>'
},
},
{
title: {
"en-US": "File nodes renamed",
"ja": "ファイルノードの名前変更"
},
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>' },
description: {
"en-US": "<p>The file nodes have been renamed to make it clearer which node does what.</p>",
"ja": "<p>fileードの名前が変更され、どのードが何を行うかが明確になりました。</p>"
}
},
{
title: {"en-US":"Deep copy option on Change node"},
title: {
"en-US": "Deep copy option on Change node",
"ja": "Changeードのディープコピーオプション"
},
prepare(done) {
var def = RED.nodes.getType('change')
RED.editor.edit({id:"test",type:"change",rules:[{t:'set',p:'payload',pt:'msg', tot:'msg',to:"anotherProperty"}],_def:def, _:def._})
var def = RED.nodes.getType('change');
RED.editor.edit({id:"test",type:"change",rules:[{t:"set",p:"payload",pt:"msg", tot:"msg",to:"anotherProperty"}],_def:def, _:def._});
setTimeout(done,700);
},
complete(done) {
@@ -123,13 +208,22 @@ export default {
setTimeout(done,500);
},
element: function() {
return $(".node-input-rule-property-deepCopy").next()
return $(".node-input-rule-property-deepCopy").next();
},
description: { "en-US": '<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>' },
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>",
"ja": "<p>値を代入に、値のディープコピーを作成するオプションが追加されました。これによって参照ではなく、完全なコピーが作成されます。</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": "And that's not all...",
"ja": "これが全てではありません..."
},
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>",
"ja": "<p>以下の様な小さな変更が沢山あります:</p><ul><li><code>msg</code> TypedInputの自動補完提案</li><li><code>Join</code>ノードで<code>msg.resetTimeout</code>のサポート</li><li><code>Delay</code>ノードの流量制御モードにおいて先頭メッセージをキューに追加</li><li><code>Delay</code>ードで流量制限されたメッセージ向けの任意の2つ目の出力</li></ul>"
}
}
]
}

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

@@ -143,7 +143,8 @@
margin-bottom: 8px;
}
.inject-time-count {
width: 40px !important;
padding-left: 3px !important;
width: 80px !important;
}
</style>
@@ -352,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() {
@@ -538,12 +541,10 @@
var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"})
.css("width","calc(70% - 30px)")
.appendTo(row)
.typedInput({default:'str',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);
propertyValue.typedInput('type',prop.vt);
},
removable: true,
sortable: true
@@ -558,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];
}
@@ -574,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);
}
@@ -684,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

@@ -292,6 +292,7 @@
};
RED.events.on("project:change", this.clearMessageList);
RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) });
RED.actions.add("core:clear-filtered-debug-messages", function() { RED.debug.clearMessageList(true, true) });
RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); });
RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); });

View File

@@ -280,6 +280,18 @@ module.exports = function(RED) {
root: path.join(__dirname,"lib","debug"),
dotfiles: 'deny'
};
res.sendFile(req.params[0], options);
try {
res.sendFile(
req.params[0],
options,
err => {
if (err) {
res.sendStatus(404);
}
}
)
} catch(err) {
res.sendStatus(404);
}
});
};

View File

@@ -52,7 +52,7 @@
treeList = $("<div>")
.css({width: "100%", height: "100%"})
.appendTo(".node-input-link-row")
.treeList({})
.treeList({autoSelect:false})
.on('treelistitemmouseover',function(e,item) {
if (item.node) {
item.node.highlighted = true;
@@ -68,6 +68,8 @@
}
});
var candidateNodes = RED.nodes.filterNodes({type:targetType});
var candidateNodesCount = 0;
var search = $("#node-input-link-target-filter").searchBox({
style: "compact",
delay: 300,
@@ -80,7 +82,7 @@
var count = treeList.treeList("filter", function(item) {
return item.label.toLowerCase().indexOf(val) > -1 || (item.node && item.node.type.toLowerCase().indexOf(val) > -1)
});
search.searchBox("count",count+" / "+candidateNodes.length);
search.searchBox("count",count+" / "+candidateNodesCount);
}
}
});
@@ -113,6 +115,11 @@
candidateNodes.forEach(function(n) {
if (flowMap[n.z]) {
if (targetType === "link out" && n.mode === 'return') {
// Link In nodes looking for Link Out nodes should not
// include return-mode nodes.
return
}
var isChecked = false;
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
if (isChecked) {
@@ -126,6 +133,7 @@
checkbox: node.type !== "link call",
radio: node.type === "link call"
})
candidateNodesCount++;
}
});
flows = flows.filter(function(f) { return f.children.length > 0 })
@@ -149,7 +157,7 @@
function onEditSave(node) {
var flows = treeList.treeList('data');
node.links = [];
if (node.type !== "link out" || $("node-input-mode").val() === 'link') {
if (node.type !== "link out" || $("#node-input-mode").val() === 'link') {
flows.forEach(function(f) {
f.children.forEach(function(n) {
if (n.selected) {
@@ -234,6 +242,12 @@
},
oneditsave: function() {
onEditSave(this);
// In case the name has changed, ensure any link call nodes on this
// tab are redrawn with the updated name
var localCallNodes = RED.nodes.filterNodes({z:RED.workspaces.active(), type:"link call"});
localCallNodes.forEach(function(node) {
node.dirty = true;
});
},
onadd: onAdd,
oneditresize: resizeNodeList
@@ -259,12 +273,12 @@
}
if (this.links.length > 0) {
var targetNode = RED.nodes.node(this.links[0]);
return targetNode && (targetNode.name || targetNode.id);
return targetNode && (targetNode.name || this._("link.linkCall"));
}
return this._("link.linkCall");
return this._("inject.none");
},
labelStyle: function() {
return (this.name || this.links.length > 0)?"node_label_italic":"";
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
onEditPrepare(this,"link in");
@@ -275,7 +289,6 @@
oneditresize: resizeNodeList
});
RED.nodes.registerType('link out',{
category: 'common',
color:"#ddd",//"#87D8CF",

View File

@@ -31,24 +31,22 @@ RED.debug = (function() {
var activeWorkspace;
var numMessages = 100; // Hardcoded number of message to show in debug window scrollback
var filterVisible = false;
var debugNodeList;
var debugNodeListExpandedFlows = {};
var debugNodeTreeList;
function init(_config) {
config = _config;
var content = $("<div>").css({"position":"relative","height":"100%"});
var toolbar = $('<div class="red-ui-sidebar-header">'+
'<span class="button-group"><a id="red-ui-sidebar-debug-filter" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span></a></span>'+
'<span class="button-group"><a id="red-ui-sidebar-debug-clear" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-trash"></i></a></span></div>').appendTo(content);
'<span class="button-group">'+
'<a id="red-ui-sidebar-debug-filter" style="padding-right: 5px" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span> <i style="padding-left: 5px;" class="fa fa-caret-down"></i></a>'+
'</span>'+
'<span class="button-group">'+
'<a id="red-ui-sidebar-debug-clear" style="border-right: none; padding-right: 6px" class="red-ui-sidebar-header-button" href="#" data-clear-type="all"><i class="fa fa-trash"></i> <span>all</span></a>' +
'<a id="red-ui-sidebar-debug-clear-opts" style="padding: 5px; border-left: none;" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-caret-down"></i></a>'+
'</span></div>').appendTo(content);
var footerToolbar = $('<div>'+
// '<span class="button-group">'+
// '<a class="red-ui-footer-button-toggle text-button selected" id="red-ui-sidebar-debug-view-list" href="#"><span data-i18n="">list</span></a>'+
// '<a class="red-ui-footer-button-toggle text-button" id="red-ui-sidebar-debug-view-table" href="#"><span data-i18n="">table</span></a> '+
// '</span>'+
'<span class="button-group"><a id="red-ui-sidebar-debug-open" class="red-ui-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' +
'</div>');
@@ -56,85 +54,100 @@ RED.debug = (function() {
sbc = messageList[0];
messageTable = $('<div class="red-ui-debug-content red-ui-debug-content-table hide"/>').appendTo(content);
var filterDialog = $('<div class="red-ui-debug-filter-box hide">'+
'<div class="red-ui-debug-filter-row">'+
'<span class="button-group">'+
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option selected" id="red-ui-sidebar-debug-filterAll" href="#"><span data-i18n="node-red:debug.sidebar.filterAll"></span></a>'+
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option" id="red-ui-sidebar-debug-filterSelected" href="#"><span data-i18n="node-red:debug.sidebar.filterSelected"></span></a>'+
'<a class="red-ui-sidebar-header-button-toggle red-ui-sidebar-debug-filter-option" id="red-ui-sidebar-debug-filterCurrent" href="#"><span data-i18n="node-red:debug.sidebar.filterCurrent"></span></a> '+
var filterDialogCloseTimeout;
var filterDialogShown = false;
var filterDialog = $('<div class="red-ui-debug-filter-box hide"></div>').appendTo(toolbar);//content);
filterDialog.on('mouseleave' ,function(evt) {
if (filterDialogShown) {
filterDialogCloseTimeout = setTimeout(function() {
filterDialog.slideUp(200);
filterDialogShown = false;
},500)
}
})
filterDialog.on('mouseenter' ,function(evt) {
clearTimeout(filterDialogCloseTimeout)
})
var filterToolbar = $('<div style="margin-bottom: 3px; display: flex;">'+
'<span style="flex-grow:1; text-align: left;">'+
'<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-all" class="red-ui-sidebar-header-button red-ui-button-small" data-i18n="node-red:debug.sidebar.selectAll"></button></span>' +
'<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-none" class="red-ui-sidebar-header-button red-ui-button-small" data-i18n="node-red:debug.sidebar.selectNone"></button></span>' +
'</span>'+
'</div>'+
'</div>').appendTo(toolbar);//content);
'<span class="button-group"><button type="button" id="red-ui-sidebar-filter-select-close" class="red-ui-sidebar-header-button red-ui-button-small"><i class="fa fa-times"></i></button></span>'+
'</div>').appendTo(filterDialog);
// var filterTypeRow = $('<div class="red-ui-debug-filter-row"></div>').appendTo(filterDialog);
// $('<select><option>Show all debug nodes</option><option>Show selected debug nodes</option><option>Show current flow only</option></select>').appendTo(filterTypeRow);
filterToolbar.find("#red-ui-sidebar-filter-select-close").on('click', function(evt) {
clearTimeout(filterDialogCloseTimeout)
filterDialogShown = false;
filterDialog.slideUp(200);
})
var debugNodeListRow = $('<div class="red-ui-debug-filter-row hide" id="red-ui-sidebar-debug-filter-node-list-row"></div>').appendTo(filterDialog);
var flowCheckboxes = {};
var debugNodeListHeader = $('<div><span data-i18n="node-red:debug.sidebar.debugNodes"></span><span></span></div>');
var headerCheckbox = $('<input type="checkbox">').appendTo(debugNodeListHeader.find("span")[1]).checkboxSet();
debugNodeList = $('<ol>',{style:"text-align: left; min-height: 250px; max-height: 250px"}).appendTo(debugNodeListRow).editableList({
header: debugNodeListHeader,
class: 'red-ui-nodeList',
addItem: function(container,i,node) {
var row = $("<div>").appendTo(container);
row.attr('id','debug-filter-node-list-node-'+node.id.replace(/\./g,"_"));
if (node.type === 'tab') {
container.parent().addClass('red-ui-editableList-section-header');
if (!debugNodeListExpandedFlows.hasOwnProperty(node.id)) {
debugNodeListExpandedFlows[node.id] = true;
filterToolbar.find("#red-ui-sidebar-filter-select-all").on('click', function(evt) {
evt.preventDefault();
var data = debugNodeTreeList.treeList('data');
data.forEach(function(flow) {
if (!flow.selected) {
if (flow.treeList.checkbox) {
flow.treeList.checkbox.trigger('click')
}
var chevron = $('<i class="fa fa-angle-right"></i>').appendTo(row);
$('<span>').text(RED.utils.getNodeLabel(node,node.id)).appendTo(row);
var muteControl = $('<input type="checkbox">').appendTo($('<span class="meta">').appendTo(row));
muteControl.checkboxSet({
parent: headerCheckbox
});
flowCheckboxes[node.id] = muteControl;
row.on("click", function(e) {
e.stopPropagation();
debugNodeListExpandedFlows[node.id] = !debugNodeListExpandedFlows[node.id];
row.toggleClass('expanded',debugNodeListExpandedFlows[node.id]);
debugNodeList.editableList('filter');
})
row.addClass("expandable");
if (node.disabled) {
container.addClass('disabled');
muteControl.checkboxSet('disable');
debugNodeListExpandedFlows[node.id] = false;
}
row.toggleClass('expanded',debugNodeListExpandedFlows[node.id]);
} else {
$('<span>',{style: "margin-left: 20px"}).text(RED.utils.getNodeLabel(node,node.id)).appendTo(row);
row.on("mouseenter",function() {
config.messageMouseEnter(node.id);
});
row.on("mouseleave",function() {
config.messageMouseLeave(node.id);
});
var muteControl = $('<input type="checkbox">').prop('checked',!filteredNodes[node.id]).appendTo($('<span class="meta">').appendTo(row));
muteControl.checkboxSet({
parent: flowCheckboxes[node.z]
}).on("change", function(e) {
filteredNodes[node.id] = !$(this).prop('checked');
$(".red-ui-debug-msg-node-"+node.id.replace(/\./g,"_")).toggleClass('hide',filteredNodes[node.id]);
});
if ((node.hasOwnProperty("active") && !node.active) || RED.nodes.workspace(node.z).disabled) {
container.addClass('disabled');
muteControl.checkboxSet('disable');
flow.children.forEach(function(item) {
if (!item.selected) {
item.treeList.select();
}
})
}
});
refreshMessageList();
})
filterToolbar.find("#red-ui-sidebar-filter-select-none").on('click', function(evt) {
evt.preventDefault();
debugNodeTreeList.treeList('clearSelection');
var data = debugNodeTreeList.treeList('data');
data.forEach(function(flow) {
if (flow.children) {
flow.children.forEach(function(item) {
filteredNodes[item.node.id] = true;
})
}
});
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList();
})
var debugNodeListRow = $('<div class="red-ui-debug-filter-row" id="red-ui-sidebar-debug-filter-node-list-row"></div>').appendTo(filterDialog);
debugNodeTreeList = $("<div></div>").appendTo(debugNodeListRow).css({width: "100%", height: "300px"})
.treeList({autoSelect: false}).on("treelistitemmouseover", function(e, item) {
if (item.node) {
item.node.highlighted = true;
item.node.dirty = true;
RED.view.redraw();
}
}).on("treelistitemmouseout", function(e, item) {
if (item.node) {
item.node.highlighted = false;
item.node.dirty = true;
RED.view.redraw();
}
}).on("treelistselect", function(e, item) {
if (item.children) {
item.children.forEach(function(child) {
if (child.checkbox) {
child.treeList.select(item.selected)
}
})
} else {
if (item.node) {
if (item.selected) {
delete filteredNodes[item.node.id]
} else {
filteredNodes[item.node.id] = true;
}
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList();
}
}
},
addButton: false,
scrollOnAdd: false,
filter: function(node) {
return (node.type === 'tab' || debugNodeListExpandedFlows[node.z] )
},
sort: function(A,B) {
}
});
})
try {
content.i18n();
@@ -144,85 +157,95 @@ RED.debug = (function() {
toolbar.find('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll'));
var filterButtonHandler = function(type) {
return function(e) {
e.preventDefault();
if (filterType !== type) {
$('.red-ui-sidebar-debug-filter-option').removeClass('selected');
$(this).addClass('selected');
if (filterType === 'filterSelected') {
debugNodeListRow.slideUp();
}
filterType = type;
if (filterType === 'filterSelected') {
debugNodeListRow.slideDown();
}
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
}
}
}
filterDialog.find('#red-ui-sidebar-debug-filterAll').on("click",filterButtonHandler('filterAll'));
filterDialog.find('#red-ui-sidebar-debug-filterSelected').on("click",filterButtonHandler('filterSelected'));
filterDialog.find('#red-ui-sidebar-debug-filterCurrent').on("click",filterButtonHandler('filterCurrent'));
// $('#red-ui-sidebar-debug-view-list').on("click",function(e) {
// e.preventDefault();
// if (!$(this).hasClass('selected')) {
// $(this).addClass('selected');
// $('#red-ui-sidebar-debug-view-table').removeClass('selected');
// showMessageList();
// }
// });
// $('#red-ui-sidebar-debug-view-table').on("click",function(e) {
// e.preventDefault();
// if (!$(this).hasClass('selected')) {
// $(this).addClass('selected');
// $('#red-ui-sidebar-debug-view-list').removeClass('selected');
// showMessageTable();
// }
// });
var hideFilterTimeout;
toolbar.on('mouseleave',function() {
if ($('#red-ui-sidebar-debug-filter').hasClass('selected')) {
clearTimeout(hideFilterTimeout);
hideFilterTimeout = setTimeout(function() {
filterVisible = false;
$('#red-ui-sidebar-debug-filter').removeClass('selected');
filterDialog.slideUp(200);
},300);
}
});
toolbar.on('mouseenter',function() {
if ($('#red-ui-sidebar-debug-filter').hasClass('selected')) {
clearTimeout(hideFilterTimeout);
}
})
toolbar.find('#red-ui-sidebar-debug-filter').on("click",function(e) {
e.preventDefault();
if ($(this).hasClass('selected')) {
filterVisible = false;
$(this).removeClass('selected');
clearTimeout(hideFilterTimeout);
filterDialog.slideUp(200);
} else {
$(this).addClass('selected');
filterVisible = true;
refreshDebugNodeList();
filterDialog.slideDown(200);
}
var options = [
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.filterAll"><input type="radio" value="filterAll" name="filter-type" style="margin-top:0"> </span>').i18n() , value: "filterAll" },
{ label: $('<span><span data-i18n="[append]node-red:debug.sidebar.filterSelected"><input type="radio" value="filterSelected" name="filter-type" style="margin-top:0"> </span>...</span>').i18n(), value: "filterSelected" },
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.filterCurrent"><input type="radio" value="filterCurrent" name="filter-type" style="margin-top:0"> </span>').i18n(), value: "filterCurrent" }
]
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value !== filterType) {
filterType = item.value;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
if (filterType === 'filterSelected') {
refreshDebugNodeList();
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
target: $("#red-ui-sidebar-debug-filter"),
align: "left",
offset: [$("#red-ui-sidebar-debug-filter").outerWidth()-2, -1]
})
$('input[name="filter-type"][value="'+RED.settings.get("debug.filter","filterAll")+'"]').prop("checked", true)
});
RED.popover.tooltip(toolbar.find('#red-ui-sidebar-debug-filter'),RED._('node-red:debug.sidebar.filterLog'));
toolbar.find("#red-ui-sidebar-debug-clear").on("click", function(e) {
e.preventDefault();
clearMessageList(false);
var action = RED.settings.get("debug.clearType","all")
clearMessageList(false, action === 'filtered');
});
RED.popover.tooltip(toolbar.find("#red-ui-sidebar-debug-clear"),RED._('node-red:debug.sidebar.clearLog'),"core:clear-debug-messages");
var clearTooltip = RED.popover.tooltip(toolbar.find("#red-ui-sidebar-debug-clear"),RED._('node-red:debug.sidebar.clearLog'),"core:clear-debug-messages");
toolbar.find("#red-ui-sidebar-debug-clear-opts").on("click", function(e) {
e.preventDefault();
var options = [
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.clearLog"><input type="radio" value="all" name="clear-type" style="margin-top:0"> </span>').i18n() , value: "all" },
{ label: $('<span data-i18n="[append]node-red:debug.sidebar.clearFilteredLog"><input type="radio" value="filtered" name="clear-type" style="margin-top:0"> </span>').i18n(), value: "filtered" }
]
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value === "all") {
$("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.all'));
clearTooltip.setAction("core:clear-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearLog'))
RED.settings.set("debug.clearType","all")
} else {
$("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.filtered'));
clearTooltip.setAction("core:clear-filtered-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearFilteredLog'))
RED.settings.set("debug.clearType","filtered")
}
}
});
menu.show({
target: $("#red-ui-sidebar-debug-clear-opts"),
align: "left",
offset: [$("#red-ui-sidebar-debug-clear-opts").outerWidth()-2, -1]
})
$('input[name="clear-type"][value="'+RED.settings.get("debug.clearType","all")+'"]').prop("checked", true)
})
var clearType = RED.settings.get("debug.clearType","all");
if (clearType === "all") {
toolbar.find("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.all'));
clearTooltip.setAction("core:clear-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearLog'))
} else {
toolbar.find("#red-ui-sidebar-debug-clear > span").text(RED._('node-red:debug.sidebar.filtered'));
clearTooltip.setAction("core:clear-filtered-debug-messages");
clearTooltip.setContent(RED._('node-red:debug.sidebar.clearFilteredLog'))
}
filterType = RED.settings.get("debug.filter","filterAll")
var filteredNodeList = RED.settings.get("debug.filteredNodes",[]);
filteredNodes = {}
filteredNodeList.forEach(function(id) {
filteredNodes[id] = true
})
toolbar.find('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
return {
content: content,
@@ -254,8 +277,6 @@ RED.debug = (function() {
function refreshDebugNodeList() {
debugNodeList.editableList('empty');
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
@@ -320,15 +341,45 @@ RED.debug = (function() {
return labelA.localeCompare(labelB);
});
var currentWs = null;
var nodeList = [];
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
nodeList.push(RED.nodes.workspace(node.z));
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: node,
checkbox: true,
selected: !filteredNodes[node.id]
});
}
nodeList.push(node);
});
debugNodeList.editableList('addItems',nodeList);
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
debugNodeTreeList.treeList("data", data);
}
function getTimestamp() {
@@ -340,7 +391,16 @@ RED.debug = (function() {
return m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
}
var refreshTimeout;
function refreshMessageList(_activeWorkspace) {
if (refreshTimeout) {
clearTimeout(refreshTimeout);
}
refreshTimeout = setTimeout(function() {
_refreshMessageList(_activeWorkspace);
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) {
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
}
@@ -415,6 +475,7 @@ RED.debug = (function() {
});
delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
refreshMessageList();
}},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
@@ -601,8 +662,12 @@ RED.debug = (function() {
}
}
function clearMessageList(clearFilter) {
$(".red-ui-debug-msg").remove();
function clearMessageList(clearFilter, filteredOnly) {
if (!filteredOnly) {
$(".red-ui-debug-msg").remove();
} else {
$(".red-ui-debug-msg:not(.hide)").remove();
}
config.clear();
if (!!clearFilter) {
clearFilterSettings();
@@ -613,10 +678,9 @@ RED.debug = (function() {
function clearFilterSettings() {
filteredNodes = {};
filterType = 'filterAll';
$('.red-ui-sidebar-debug-filter-option').removeClass('selected');
$('#red-ui-sidebar-debug-filterAll').addClass('selected');
RED.settings.set("debug.filter",filterType);
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.filterAll'));
$('#red-ui-sidebar-debug-filter-node-list-row').slideUp();
}
return {

View File

@@ -15,18 +15,22 @@ $(function() {
}
}
var uiComponents = RED.debug.init(options);
try {
var uiComponents = RED.debug.init(options);
$(".red-ui-debug-window").append(uiComponents.content);
$(".red-ui-debug-window").append(uiComponents.content);
window.addEventListener('message',function(evt) {
if (evt.data.event === "message") {
RED.debug.handleDebugMessage(evt.data.msg);
} else if (evt.data.event === "workspaceChange") {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
}
},false);
} catch(err) {
console.error(err)
}
window.addEventListener('message',function(evt) {
if (evt.data.event === "message") {
RED.debug.handleDebugMessage(evt.data.msg);
} else if (evt.data.event === "workspaceChange") {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
}
},false);
})
});

View File

@@ -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

@@ -234,8 +234,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 () {

View File

@@ -117,30 +117,35 @@
return r;
}
function createValueField(row){
return $('<input/>',{class:"node-input-rule-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
function createValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
}
function createNumValueField(row){
return $('<input/>',{class:"node-input-rule-num-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'num',types:['flow','global','num','jsonata','env']});
function createNumValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-num-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'num',types:['flow','global','num','jsonata','env']});
}
function createExpValueField(row){
return $('<input/>',{class:"node-input-rule-exp-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'jsonata',types:['jsonata']});
return $('<input/>',{class:"node-input-rule-exp-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:'jsonata',types:['jsonata']});
}
function createBtwnValueField(row){
return $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
function createBtwnValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"width: 100%;"}).appendTo(row)
.typedInput({default:defaultType||'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
}
function createBtwnValue2Field(row3, andLabel){
function createBtwnValue2Field(row3, andLabel, defaultType){
$('<div/>',{class:"node-input-rule-btwn-label", style:"width: 120px; text-align: right;"}).text(" "+andLabel+" ").appendTo(row3);
var row3InputCell = $('<div/>',{style:"flex-grow:1; margin-left: 5px;"}).appendTo(row3);
return $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 100%"}).appendTo(row3InputCell).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
return $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 100%"}).appendTo(row3InputCell)
.typedInput({default:defaultType||'num',types:['msg','flow','global','str','num','jsonata','env',previousValueType]});
}
function createTypeValueField(){
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:'string',types:[
function createTypeValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:defaultType || 'string',types:[
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.png"},
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.png"},
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.png"},
@@ -211,6 +216,7 @@
var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1);
var exportedRule = exportRule(lastRule.element);
opt.r.vt = exportedRule.vt;
opt.r.v = "";
// We could copy the value over as well and preselect it (see the 'activeElement' code below)
// But not sure that feels right. Is copying over the last value 'expected' behaviour?
// It would make sense for an explicit 'copy' action, but not sure where the copy button would
@@ -278,24 +284,12 @@
selectField.on("change", function() {
var fieldToFocus;
var type = selectField.val();
if (valueField){
valueField.typedInput('hide');
}
if (expValueField){
expValueField.typedInput('hide');
}
if (numValueField){
numValueField.typedInput('hide');
}
if (typeValueField){
typeValueField.typedInput('hide');
}
if (btwnValueField){
btwnValueField.typedInput('hide');
}
if (btwnValue2Field){
btwnValue2Field.typedInput('hide');
}
if (valueField) { valueField.typedInput('hide'); }
if (expValueField) { expValueField.typedInput('hide'); }
if (numValueField) { numValueField.typedInput('hide'); }
if (typeValueField) { typeValueField.typedInput('hide'); }
if (btwnValueField) { btwnValueField.typedInput('hide'); }
if (btwnValue2Field) { btwnValue2Field.typedInput('hide'); }
if ((type === "btwn") || (type === "index")) {
if (!btwnValueField){
@@ -318,7 +312,7 @@
} else if (type === "istype") {
if (!typeValueField){
typeValueField = createTypeValueField();
typeValueField = createTypeValueField(rowInputCell);
}
typeValueField.typedInput('show');
fieldToFocus = typeValueField;
@@ -351,7 +345,9 @@
} else {
selectField.width("auto")
}
fieldToFocus.typedInput("focus");
if (fieldToFocus) {
fieldToFocus.typedInput("focus");
}
// Preselect the contents of the element
// if (focusValueField && document.activeElement) {
// document.activeElement.selectionStart = 0;
@@ -359,48 +355,26 @@
// }
});
selectField.val(rule.t);
if ((rule.t == "btwn") || (rule.t == "index")) {
if (!btwnValueField){
btwnValueField = createBtwnValueField(rowInputCell);
}
btwnValueField.typedInput('value',rule.v);
btwnValueField.typedInput('type',rule.vt||'num');
if (!btwnValue2Field){
btwnValue2Field = createBtwnValue2Field(row3, andLabel);
}
if ((rule.t == "btwn") || (rule.t == "index")) {
btwnValueField = createBtwnValueField(rowInputCell,rule.vt||'num');
btwnValueField.typedInput('value',rule.v);
btwnValue2Field = createBtwnValue2Field(row3, andLabel,rule.v2t||'num');
btwnValue2Field.typedInput('value',rule.v2);
btwnValue2Field.typedInput('type',rule.v2t||'num');
} else if ((rule.t === "head") || (rule.t === "tail")) {
if (!numValueField){
numValueField = createNumValueField(row);
}
numValueField = createNumValueField(rowInputCell,rule.vt||'num');
numValueField.typedInput('value',rule.v);
numValueField.typedInput('type',rule.vt||'num');
} else if (rule.t === "istype") {
if (!typeValueField){
typeValueField =createTypeValueField();
}
typeValueField = createTypeValueField(rowInputCell,rule.vt);
typeValueField.typedInput('value',rule.vt);
typeValueField.typedInput('type',rule.vt);
} else if (rule.t === "jsonata_exp") {
if (!expValueField){
expValueField = createExpValueField(row);
}
expValueField = createExpValueField(rowInputCell,rule.vt||'jsonata');
expValueField.typedInput('value',rule.v);
expValueField.typedInput('type',rule.vt||'jsonata');
} else if (typeof rule.v != "undefined") {
if (!valueField){
valueField = createValueField(rowInputCell);
}
valueField = createValueField(rowInputCell,rule.vt||'str');
valueField.typedInput('value',rule.v);
valueField.typedInput('type',rule.vt||'str');
}
if (rule.case) {
caseSensitive.prop('checked',true);
} else {
caseSensitive.prop('checked',false);
}
caseSensitive.prop('checked',!!rule.case);
selectField.change();
var currentOutputs = JSON.parse(outputCount.val()||"{}");

View File

@@ -115,12 +115,12 @@
var regex = this._("change.label.regex");
var deepCopyLabel = this._("change.label.deepCopy");
function createPropertyValue(row2_1,row2_2) {
function createPropertyValue(row2_1, row2_2, defaultType) {
var propValInput = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2_1)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
.typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
var dcLabel = $('<label style="padding-left: 130px; display: flex; align-items: center "></label>').appendTo(row2_2);
var dcLabel = $('<label style="padding-left: 130px;"></label>').appendTo(row2_2);
var deepCopy = $('<input type="checkbox" class="node-input-rule-property-deepCopy" style="width: auto; margin: 0 6px 0 0">').appendTo(dcLabel)
$('<span>').text(deepCopyLabel).appendTo(dcLabel)
@@ -129,20 +129,20 @@
})
return [propValInput, deepCopy];
}
function createFromValue(row3_1) {
function createFromValue(row3_1, defaultType) {
return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
.appendTo(row3_1)
.typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
.typedInput({default:defaultType||'str',types:['msg','flow','global','str','re','num','bool','env']});
}
function createToValue(row3_2) {
function createToValue(row3_2, defaultType) {
return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
.typedInput({default:defaultType||'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
}
function createMoveValue(row4) {
function createMoveValue(row4, defaultType) {
return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
.appendTo(row4)
.typedInput({default:'msg',types:['msg','flow','global']});
.typedInput({default:defaultType||'msg',types:['msg','flow','global']});
}
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
@@ -268,33 +268,22 @@
propertyName.typedInput('value',rule.p);
propertyName.typedInput('type',rule.pt);
if (rule.t == "set") {
if(!propertyValue) {
var parts = createPropertyValue(row2_1, row2_2);
propertyValue = parts[0];
deepCopy = parts[1];
}
var parts = createPropertyValue(row2_1, row2_2, rule.tot);
propertyValue = parts[0];
deepCopy = parts[1];
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
deepCopy.prop("checked", !!rule.dc);
}
if (rule.t == "move") {
if(!moveValue) {
moveValue = createMoveValue(row4);
}
moveValue = createMoveValue(row4,rule.tot);
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
}
if (rule.t == "change") {
if(!fromValue) {
fromValue = createFromValue(row3_1);
}
fromValue = createFromValue(row3_1, rule.fromt);
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
if (!toValue) {
toValue = createToValue(row3_2);
}
toValue = createToValue(row3_2,rule.tot);
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
}
selectField.change();
container[0].appendChild(fragment);

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

@@ -54,6 +54,18 @@
width: 15px;
height: 15px;
}
.form-row-mqtt5 {
display: none;
}
.form-row-mqtt5.form-row-mqtt5-active:not(.form-row-mqtt-static-disabled) {
display: block
}
.form-row-mqtt-static-disabled {
display: none;
/* opacity: 0.3;
pointer-events: none; */
}
</style>
<script type="text/html" data-template-name="mqtt in">
@@ -62,10 +74,18 @@
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topicType" data-i18n="mqtt.label.action"></label>
<select id="node-input-topicType" style="width: 70%">
<option value="topic" data-i18n="mqtt.label.staticTopic"></option>
<option value="dynamic" data-i18n="mqtt.label.dynamicTopic"></option>
</select>
<input type="hidden" id="node-input-inputs">
</div>
<div class="form-row form-row-mqtt-static">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topic">
</div>
<div class="form-row">
<div class="form-row form-row-mqtt-static">
<label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
<select id="node-input-qos" style="width:125px !important">
<option value="0">0</option>
@@ -73,17 +93,7 @@
<option value="2">2</option>
</select>
</div>
<div class="form-row">
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="mqtt.label.output"></span></label>
<select id="node-input-datatype" style="width:70%;">
<option value="auto" data-i18n="mqtt.output.auto"></option>
<option value="buffer" data-i18n="mqtt.output.buffer"></option>
<option value="utf8" data-i18n="mqtt.output.string"></option>
<option value="json" data-i18n="mqtt.output.json"></option>
<option value="base64" data-i18n="mqtt.output.base64"></option>
</select>
</div>
<div class="form-row mqtt-flags-row mqtt5">
<div class="form-row mqtt-flags-row form-row-mqtt5 form-row-mqtt-static">
<label for="node-input-nl" ><i class="fa fa-flag"></i> <span data-i18n="mqtt.label.flags">Flags</span></label>
<div class="mqtt-flags">
<div class="mqtt-flag">
@@ -100,7 +110,7 @@
</div>
</div>
</div>
<div class="form-row mqtt5">
<div class="form-row form-row-mqtt5 form-row-mqtt-static">
<label for="node-input-rh" style="width:100%"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.rh"></span></label>
<select id="node-input-rh" style="margin-left: 104px; width: 70%">
<option value="0" data-i18n="mqtt.label.rh0"></option>
@@ -108,6 +118,16 @@
<option value="2" data-i18n="mqtt.label.rh2"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="mqtt.label.output"></span></label>
<select id="node-input-datatype" style="width:70%;">
<option value="auto" data-i18n="mqtt.output.auto"></option>
<option value="buffer" data-i18n="mqtt.output.buffer"></option>
<option value="utf8" data-i18n="mqtt.output.string"></option>
<option value="json" data-i18n="mqtt.output.json"></option>
<option value="base64" data-i18n="mqtt.output.base64"></option>
</select>
</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">
@@ -185,6 +205,10 @@
<label for="node-config-input-port" style="margin-left:20px; width:43px; "> <span data-i18n="mqtt.label.port"></span></label>
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:55px">
</div>
<div class="form-row" style="margin-bottom:0">
<input type="checkbox" id="node-config-input-autoConnect" style="margin: 0 5px 0 104px; display: inline-block; width: auto;">
<label for="node-config-input-autoConnect" style="width: auto"><span data-i18n="mqtt.label.auto-connect"></span></label>
</div>
<div class="form-row" style="height: 34px;">
<input type="checkbox" id="node-config-input-usetls" style="height: 34px; margin: 0 5px 0 104px; display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-usetls" style="width: 100px; line-height: 34px;"><span data-i18n="mqtt.label.use-tls"></span></label>
@@ -434,6 +458,7 @@
return (this.cleansession===undefined || this.cleansession) || (v||"").length > 0;
}
}},
autoConnect: {value: true},
usetls: {value: false},
verifyservercert: { value: false},
compatmode: { value: false},
@@ -558,6 +583,10 @@
this.usetls = false;
$("#node-config-input-usetls").prop("checked",false);
}
if (typeof this.autoConnect === 'undefined') {
this.autoConnect = true;
$("#node-config-input-autoConnect").prop("checked",true);
}
if (this.compatmode === 'true' || this.compatmode === true) {
delete this.compatmode;
this.protocolVersion = 4;
@@ -704,11 +733,22 @@
}
});
RED.nodes.registerType('mqtt in',{
category: 'network',
defaults: {
name: {value:""},
topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)},
topic: {
value:"",
validate: function(v) {
var isDynamic = this.inputs === 1;
var topicTypeSelect = $("#node-input-topicType");
if (topicTypeSelect.length) {
isDynamic = topicTypeSelect.val()==='dynamic'
}
return isDynamic || ((!!v) && RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)(v));
}
},
qos: {value: "2"},
datatype: {value:"auto",required:true},
broker: {type:"mqtt-broker", required:true},
@@ -716,33 +756,64 @@
nl: {value:false},
rap: {value:true},
rh: {value:0},
inputs: {value:0},
},
color:"#d8bfd8",
inputs:0,
outputs:1,
icon: "bridge.svg",
label: function() {
return this.name||this.topic||"mqtt";
var label = "mqtt";
if(this.topicType !== "dynamic" && this.topic) {
label = this.topic;
}
return this.name || label;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
$("#node-input-broker").on("change",function(d){
const node = this;
const isV5Broker = function() {
var confNode = RED.nodes.node($("#node-input-broker").val());
var v5 = confNode && confNode.protocolVersion == "5";
if(v5) {
$("div.form-row.mqtt5").show();
} else {
$("div.form-row.mqtt5").hide();
}
return confNode && confNode.protocolVersion === "5";
}
const isDynamic = function() {
return $('#node-input-topicType').val() === "dynamic";
}
const updateVisibility = function() {
var v5 = isV5Broker();
var dynamic = isDynamic();
$("div.form-row-mqtt5").toggleClass("form-row-mqtt5-active",!!v5);
$("div.form-row.form-row-mqtt-static").toggleClass("form-row-mqtt-static-disabled", !!dynamic)
}
$("#node-input-broker").on("change",function(d){
updateVisibility();
});
$('#node-input-topicType').on("change", function () {
$("#node-input-inputs").val(isDynamic() ? 1 : 0);
updateVisibility();
});
if (this.inputs === 1) {
$('#node-input-topicType').val('dynamic')
} else {
$('#node-input-topicType').val('topic')
}
$('#node-input-topicType').trigger("change");
if (this.qos === undefined) {
$("#node-input-qos").val("2");
}
if (this.datatype === undefined) {
$("#node-input-datatype").val("auto");
}
},
oneditsave: function() {
if ($('#node-input-topicType').val() === "dynamic") {
$('#node-input-topic').val("");
}
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -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);

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,8 +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"><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>
@@ -206,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">
@@ -215,13 +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},
splitc: {value:"0",required:true},
name: {value:""}
port: {value:"", validate:RED.validators.regex(/^(\d*|)$/)},
out: {value:"time", required:true},
ret: {value:"buffer"},
splitc: {value:"0", required:true},
newline: {value:""},
tls: {type:"tls-config", value:'', required:false}
},
inputs:1,
outputs:1,
@@ -234,6 +319,18 @@
},
oneditprepare: function() {
var previous = null;
if ($("#node-input-ret").val() == undefined) {
$("#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(); }
@@ -260,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()}));
@@ -311,7 +377,7 @@ module.exports = function(RED) {
}
setupTcpClient();
node.on("input", function(msg,nodeSend,nodeDone) {
node.on("input", function(msg, nodeSend, nodeDone) {
if (node.connected && msg.payload != null) {
if (Buffer.isBuffer(msg.payload)) {
client.write(msg.payload);
@@ -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}));
@@ -444,7 +516,12 @@ module.exports = function(RED) {
this.server = n.server;
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); }
@@ -488,7 +565,7 @@ module.exports = function(RED) {
connected: false,
connecting: false
};
enqueue(clients[connection_id].msgQueue, {msg:msg,nodeSend:nodeSend, nodeDone: nodeDone});
enqueue(clients[connection_id].msgQueue, {msg:msg, nodeSend:nodeSend, nodeDone:nodeDone});
clients[connection_id].lastMsg = msg;
if (!clients[connection_id].connecting && !clients[connection_id].connected) {
@@ -499,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) {
@@ -527,13 +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 = data;
nodeSend(RED.util.cloneMessage(msg));
msg.payload = RED.util.cloneMessage(data);
if (node.ret === "string") {
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); }
}
else { nodeSend(msg); }
}
}
// else if (node.splitc === 0) {
@@ -556,6 +688,10 @@ module.exports = function(RED) {
const msg = clients[connection_id].lastMsg || {};
msg.payload = Buffer.alloc(i+1);
buf.copy(msg.payload,0,0,i+1);
if (node.ret === "string") {
try { msg.payload = msg.payload.toString(); }
catch(e) { node.error("Failed to create string", msg); }
}
nodeSend(msg);
if (clients[connection_id].client) {
node.status({});
@@ -578,6 +714,10 @@ module.exports = function(RED) {
const msg = clients[connection_id].lastMsg || {};
msg.payload = Buffer.alloc(i);
buf.copy(msg.payload,0,0,i);
if (node.ret === "string") {
try { msg.payload = msg.payload.toString(); }
catch(e) { node.error("Failed to create string", msg); }
}
nodeSend(msg);
if (clients[connection_id].client) {
node.status({});
@@ -597,6 +737,10 @@ module.exports = function(RED) {
const msg = clients[connection_id].lastMsg || {};
msg.payload = Buffer.alloc(i);
buf.copy(msg.payload,0,0,i);
if (node.ret === "string") {
try { msg.payload = msg.payload.toString(); }
catch(e) { node.error("Failed to create string", msg); }
}
nodeSend(msg);
if (clients[connection_id].client) {
node.status({});
@@ -658,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

@@ -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>

View File

@@ -29,13 +29,13 @@
</dl>
<h3>Details</h3>
<p>Wenn ein Node bei der Verarbeitung einer Nachricht einen Fehler verursacht, wird der Flow in der Regel angehalten.
Der <span style="background-color:Gainsboro">catch</span>-Node kann verwendet werden, um diese Fehler abzufangen und sie mit einem dedizierten Flow zu bearbeiten.</p>
Der catch-Node kann verwendet werden, um diese Fehler abzufangen und sie mit einem dedizierten Flow zu bearbeiten.</p>
<p>Der Node fängt standardmäßig die Fehler aller Nodes im selben Flow ab.
Alternativ kann er auch an bestimmte Nodes gebunden werden.</p>
<p>Wenn ein Fehler ausgelöst wird, empfangen alle angebundenen <span style="background-color:Gainsboro">catch</span>-Nodes die Fehlermeldung.</p>
<p>Wenn ein Fehler in einem Subflow ausgelöst wird, wird der Fehler von einem <span style="background-color:Gainsboro">catch</span>-Node
<p>Wenn ein Fehler ausgelöst wird, empfangen alle angebundenen catch-Nodes die Fehlermeldung.</p>
<p>Wenn ein Fehler in einem Subflow ausgelöst wird, wird der Fehler von einem catch-Node
innerhalb des Subflows abgefangen.
Wenn im Subflow keine <span style="background-color:Gainsboro">catch</span>-Nodes vorhanden sind, wird die Fehlermeldung eine Ebene höher zum Flow weitergereicht,
Wenn im Subflow keine catch-Nodes vorhanden sind, wird die Fehlermeldung eine Ebene höher zum Flow weitergereicht,
in der sich die Subflow-Instanz befindet.</p>
<p>Wenn die Nachricht bereits über eine <code>error</code>-Eigenschaft verfügt, wird sie nach <code>_error</code> kopiert.</p>
</script>

View File

@@ -17,7 +17,7 @@
<script type="text/html" data-help-name="link in">
<p>Erstellung virtueller Verbindungen (Links) zwischen Flows.</p>
<h3>Details</h3>
<p>Der Node kann mit jedem beliebigen <span style="background-color:Gainsboro">link&nbsp;out</span>-Node in einen beliebigen Flow-Tab verlinkt werden.
<p>Der Node kann mit jedem beliebigen link&nbsp;out-Node in einen beliebigen Flow-Tab verlinkt werden.
Sobald sie verlinkt sind, verhalten sie sich so, als wären sie direkt miteinander verbunden.</p>
<p>Die Links zwischen Link-Nodes werden nur angezeigt, wenn ein Link-Node ausgewählt ist.
Wenn Links zu anderen Flow-Tabs vorhanden sind, werden virtuelle Link-Nodes als Gegenparts angezeigt,
@@ -28,7 +28,7 @@
<script type="text/html" data-help-name="link out">
<p>Erstellung virtueller Verbindungen (Links) zwischen Flows.</p>
<h3>Details</h3>
<p>Der Node kann mit jedem beliebigen <span style="background-color:Gainsboro">link&nbsp;in</span>-Node in einen beliebigen Flow-Tab verlinkt werden.
<p>Der Node kann mit jedem beliebigen link&nbsp;in-Node in einen beliebigen Flow-Tab verlinkt werden.
Sobald sie verlinkt sind, verhalten sie sich so, als wären sie direkt miteinander verbunden.</p>
<p>Die Links zwischen Link-Nodes werden nur angezeigt, wenn ein Link-Node ausgewählt ist.
Wenn Links zu anderen Flow-Tabs vorhanden sind, werden virtuelle Link-Nodes als Gegenparts angezeigt,

View File

@@ -47,7 +47,7 @@
<li><code>node.error("Fehlermeldungstext")</code></li>
</ul>
</p>
<p>Der <span style="background-color:Gainsboro">catch</span>-Node kann auch zur Bearbeitung von Fehlern verwendet werden.
<p>Der catch-Node kann auch zur Bearbeitung von Fehlern verwendet werden.
Er wird aufgerufen, indem <code>msg</code> als zweites Argument an <code>node.error</code> übergeben wird:</p>
<pre>node.error("Fehlermeldungstext" ,msg);</pre>
<h4><b>Zugriff auf Node-Informationen</b></h4>

View File

@@ -27,7 +27,7 @@
<ol>
<li><b>value rules</b>: Regeln werden hinsichtlich einer eingestellten Eigenschaft ausgewertet</li>
<li><b>sequence rules</b>: Regeln beziehen sich auf Nachrichtensequenzen,
wie sie beispielsweise durch den <span style="background-color:Gainsboro">split</span>-Node erzeugt werden</li>
wie sie beispielsweise durch den split-Node erzeugt werden</li>
<li>Ein <b>JSONata-Ausdruck</b> kann die gesamte Eingangsnachricht auswerten und einen <code>true</code>-Wert zurückliefern,
um eine Regelerfüllung zu signalisieren</li>
<li>Die <b>ansonsten</b>-Regel wird angewendet, wenn keine vorhergehende Regel übereinstimmt</li>

View File

@@ -25,9 +25,9 @@
<dt class="optional">payload <span class="property-type">string</span></dt>
<dd>Wird an auszuführenden Befehl angehängt, sofern im Node aktiviert.</dd>
<dt class="optional">kill <span class="property-type">string</span></dt>
<dd>Typ des Kill-Signals, das an den zu beendenden <span style="background-color:Gainsboro">exec</span>-Node-Prozess gesendet wird.</dd>
<dd>Typ des Kill-Signals, das an den zu beendenden exec-Node-Prozess gesendet wird.</dd>
<dt class="optional">pid <span class="property-type">number | string</span></dt>
<dd>Prozess-ID des zu beendenden <span style="background-color:Gainsboro">exec</span>-Node-Prozesses.</dd>
<dd>Prozess-ID des zu beendenden exec-Node-Prozesses.</dd>
</dl>
<h3>Ausgangsdaten</h3>
<ol class="node-ports">
@@ -75,7 +75,7 @@
<p>Die zurückgegebenen Daten (Payload) sind in der Regel eine <i>Zeichenfolge (string)</i>,
außer es werden nicht UTF-8-Zeichen wie bei einem <i>binären Puffer (buffer)</i> erkannt.</p>
<p>Bei einem aktiven Node werden Status und die PID angezeigt.
Änderungen können mittels <span style="background-color:Gainsboro">status</span>-Node gelesen werden.</p>
Änderungen können mittels status-Node gelesen werden.</p>
<h4><b>Prozesse beenden</b></h4>
<p>Durch Senden von <code>msg.kill</code> wird ein einzelner aktiver Prozess beendet.
<code>msg.kill</code> sollte als Zeichenfolge (string) den Signaltyp enthalten,

View File

@@ -94,7 +94,7 @@
"label": {
"source": "Fehler abfangen von",
"selectAll": "Alles auswählen",
"uncaught": "Fehler ignorieren, die von anderen <span style=\"background-color:Gainsboro\">catch</span>-Nodes behandelt wurden"
"uncaught": "Fehler ignorieren, die von anderen catch-Nodes behandelt wurden"
},
"scope": {
"all": "allen Nodes",
@@ -475,12 +475,12 @@
"json": "Ein parsed JSON-Objekt",
"tip": {
"in": "Die URL ist relativ zu ",
"res": "Die an diesen Node gesendeten Nachrichten <b>müssen</b> von einem <span style=\"background-color:Gainsboro\">http&nbsp;in</span>-Node stammen",
"res": "Die an diesen Node gesendeten Nachrichten <b>müssen</b> von einem http&nbsp;in-Node stammen",
"req": "Tipp: Wenn die JSON-Syntax-Analyse fehlschlägt, wird die abgerufene Zeichenfolge zurückgegeben, wie sie ist."
},
"httpreq": "http request",
"errors": {
"not-created": "<span style=\"background-color:Gainsboro\">http in</span>-Node kann nicht erstellt werden, wenn httpNodeRoot auf 'false' gesetzt ist.",
"not-created": "http in-Node kann nicht erstellt werden, wenn httpNodeRoot auf 'false' gesetzt ist.",
"missing-path": "Fehlender Pfad",
"no-response": "Kein Antwort-Objekt",
"json-error": "JSON-Parse-Fehler",
@@ -499,7 +499,8 @@
"label": {
"type": "Typ",
"path": "Pfad",
"url": "URL"
"url": "URL",
"subprotocol": "Subprotokoll"
},
"listenon": "Lauschen (listen on)",
"connectto": "Verbinden mit",
@@ -684,7 +685,7 @@
},
"errors": {
"invalid-expr": "Ungültiger JSONata-Ausdruck: __error__",
"too-many": "Zu viele anstehende Nachrichten im <span style=\"background-color:Gainsboro\">switch</span>-Node"
"too-many": "Zu viele anstehende Nachrichten im switch-Node"
}
},
"change": {
@@ -851,7 +852,6 @@
"outputas": "Ausgabe",
"breakchunks": "In Chunks aufteilen",
"breaklines": "In Linien aufteilen",
"filelabel": "file",
"sendError": "Nachricht bei Fehler senden (herkömmlicher Modus)",
"encoding": "Kodierung",
"deletelabel": "lösche __file__",
@@ -940,8 +940,8 @@
"afterTimeout": "Bei Zeitablauf nach erster Nachricht von",
"seconds": "Sekunden",
"complete": "Nach Nachricht mit <code>msg.complete</code>-Eigenschaft",
"tip": "Dieser Modus setzt voraus, dass dieser Node entweder mit einem <span style=\"background-color:Gainsboro\">split</span>-Node kombiniert ist oder dass die empfangenen Nachrichten über eine ordnungsgemäß konfigurierte <code>msg.parts</code>-Eigenschaft verfügen.",
"too-many": "Zu viele anstehende Nachrichten im <span style=\"background-color:Gainsboro\">join</span>-Node",
"tip": "Dieser Modus setzt voraus, dass dieser Node entweder mit einem split-Node kombiniert ist oder dass die empfangenen Nachrichten über eine ordnungsgemäß konfigurierte <code>msg.parts</code>-Eigenschaft verfügen.",
"too-many": "Zu viele anstehende Nachrichten im join-Node",
"merge": {
"topics-label": "Zusammengeführte Topics",
"topics": "Topics",
@@ -970,9 +970,9 @@
"ascending": "aufsteigend",
"descending": "absteigend",
"as-number": "als Zahlenwert",
"invalid-exp": "Ungültiger JSONata-Ausdruck in <span style=\"background-color:Gainsboro\">sort</span>-Node: __message__",
"too-many": "Zu viele anstehende Nachrichten in <span style=\"background-color:Gainsboro\">sort</span>-Node",
"clear": "Anstehende Nachricht in <span style=\"background-color:Gainsboro\">sort</span>-Node löschen"
"invalid-exp": "Ungültiger JSONata-Ausdruck in sort-Node: __message__",
"too-many": "Zu viele anstehende Nachrichten in sort-Node",
"clear": "Anstehende Nachricht in sort-Node löschen"
},
"batch": {
"batch": "batch",
@@ -997,7 +997,7 @@
"topics-label": "Topics",
"topic": "Topic"
},
"too-many": "Zu viele anstehende Nachrichten im <span style=\"background-color:Gainsboro\">batch</span>-Node",
"too-many": "Zu viele anstehende Nachrichten im batch-Node",
"unexpected": "Unerwarteter Modus",
"no-parts": "Keine parts-Eigenschaft in Nachricht"
},

View File

@@ -94,8 +94,8 @@
<script type="text/html" data-help-name="mqtt-broker">
<p>Konfiguration der Verbindung zu einem MQTT-Broker.</p>
<p>Diese Konfiguration erstellt eine einzelne Verbindung zu einem Broker,
welche anschließend von den <span style="background-color:Gainsboro">mqtt&nbsp;in</span>- und
<span style="background-color:Gainsboro">mqtt out</span>-Nodes verwendet werden.</p>
welche anschließend von den mqtt&nbsp;in- und
mqtt out-Nodes verwendet werden.</p>
<p>Der Node generiert eine beliebige Client-ID, falls sie nicht vorgegeben ist und der
Node eine bereinigte Sitzung (clean session) verwenden soll.
Wenn eine Client-ID vorgegeben wird, muss sie für den Broker eindeutig sein, zu dem die Verbindung hergestellt werden soll.</p>

View File

@@ -36,7 +36,7 @@
<dt>res <span class="property-type">object</span></dt>
<dd>HTTP-Antwortobjekt.<br/>
Diese Eigenschaft sollte nicht direkt verwendet werden.
Im <span style="background-color:Gainsboro">http&nbsp;response</span>-Node ist dokumentiert, wie auf eine Anforderung reagiert wird.
Im http&nbsp;response-Node ist dokumentiert, wie auf eine Anforderung reagiert wird.
Diese Eigenschaft muss an der Nachricht angehängt bleiben, die an den Antwort-Node übergeben wird.</dd>
</dl>
<h3>Details</h3>
@@ -50,12 +50,12 @@
<p>Wenn der Inhaltstyp der Anforderung ermittelt werden kann, wird der Hauptteil als passender Typ analysiert.
Z.B. <code>application/json</code> wird zu einem JavaScript-Objekt analysiert.</p>
<p><b>Hinweis</b>: Dieser Node sendet keine Antwort an die Anforderung.
Der Flow muss einen <span style="background-color:Gainsboro">http&nbsp;response</span>-Node enthalten,
Der Flow muss einen http&nbsp;response-Node enthalten,
um die Anforderung zu vervollständigen.</p>
</script>
<script type="text/html" data-help-name="http response">
<p>Senden von Antworten auf Anforderungen, die von einem <span style="background-color:Gainsboro">http&nbsp;in</span>-Node empfangen wurden.</p>
<p>Senden von Antworten auf Anforderungen, die von einem http&nbsp;in-Node empfangen wurden.</p>
<h3>Eingangsdaten</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">string</span></dt>

View File

@@ -34,17 +34,17 @@
</ul>
</dd>
<dt>schemaError <span class="property-type">array</span></dt>
<dd>Wenn die JSON-Schema-Validierung fehlschlägt, wird für den <span style="background-color:Gainsboro">catch</span>-Node eine <code>schemaError</code>-Eigenschaft erstellt,
<dd>Wenn die JSON-Schema-Validierung fehlschlägt, wird für den catch-Node eine <code>schemaError</code>-Eigenschaft erstellt,
die ein Array von Fehlern enthält.</dd>
</dl>
<h3>Details</h3>
<p>Standardmäßig verarbeitet der Node <code>msg.payload</code>,
kann aber auch eine beliebige Nachrichteneigenschaft konvertieren.</p>
<p>Die Konvertierungsrichtung kann im Node auch vorgegeben werden, um eine bestimmte Ziel-Kodierung sicherzustellen.
Dies kann z.B. zusammen mit dem <span style="background-color:Gainsboro">http&nbsp;in</span>-Node benutzt werden, um sicherzustellen,
Dies kann z.B. zusammen mit dem http&nbsp;in-Node benutzt werden, um sicherzustellen,
dass die Nutzdaten (Payload) ein analysiertes (parsed) Objekt ist,
auch wenn eine eingehende Anfrage seinen Inhaltstyp nicht korrekt eingestellt hat,
damit der <span style="background-color:Gainsboro">http&nbsp;in</span>-Node die Konvertierung durchführen kann.</p>
damit der http&nbsp;in-Node die Konvertierung durchführen kann.</p>
<p>Wenn der Node auf Zeichenfolgen-Eingang (string) eingestellt ist und es einen String empfängt,
werden keine weiteren Prüfungen der Eigenschaft durchgeführt.
Der Node prüft weder, ob die Zeichenfolge (string) ein gültiges JSON enthält, noch wird er ihn neu formatieren,

View File

@@ -32,7 +32,7 @@
<dl class="message-properties">
<dt>parts <span class="property-type">object</span></dt>
<dd>Diese Eigenschaft enthält Informationen darüber, wie die Nachricht von der ursprünglichen Nachricht abgeteilt wurde.
Bei Übergabe an ein <span style="background-color:Gainsboro">join</span>-Node kann die Sequenz wieder zu einer einzigen Nachricht zusammengeführt werden.
Bei Übergabe an ein join-Node kann die Sequenz wieder zu einer einzigen Nachricht zusammengeführt werden.
Diese Eigenschaft hat die folgenden Eigenschaften:
<ul>
<li><code>id</code>: Identifikator der Nachrichten-Gruppe</li>
@@ -48,7 +48,7 @@
</dl>
<h3>Details</h3>
<p>Dieser Node macht es einfach, einen Flow zu erstellen, der gemeinsame Aktionen über eine Sequenz von Nachrichten ausführt,
bevor die Sequenz mittels <span style="background-color:Gainsboro">join</span>-Node wieder zu einer einzigen Nachricht neu kombiniert wird.</p>
bevor die Sequenz mittels join-Node wieder zu einer einzigen Nachricht neu kombiniert wird.</p>
<p>Der Node verwendet die <code>msg.parts</code>-Eigenschaft, um die einzelnen Sequenzteile nachzuverfolgen.</p>
<h4><b>Streaming-Modus</b></h4>
<p>Der Node kann auch zum Aufbereiten eines Nachrichtenstroms verwendet werden.
@@ -59,7 +59,7 @@
so wird es im Node aufbewahrt und der nächsten empfangenen Nachricht vorangestellt.</p>
<p>In diesem Modus wird die <code>msg.parts.count</code>-Eigenschaft nicht gesetzt,
da die Anzahl der zu erwartenden Nachrichten im Stream unbekannt ist.
Das bedeutet, dass ein nachfolgender <span style="background-color:Gainsboro">join</span>-Node nicht im Automatikmodus verwendet werden kann.</p>
Das bedeutet, dass ein nachfolgender join-Node nicht im Automatikmodus verwendet werden kann.</p>
</script>
<script type="text/html" data-help-name="join">
@@ -67,7 +67,7 @@
<p>Es sind drei Modi verfügbar:</p>
<dl>
<dt>Automatisch</dt>
<dd>In Kombination mit dem <span style="background-color:Gainsboro">split</span>-Node verbindet es automatisch die Nachrichten, um die zuvor durchgeführte Aufteilung rückgängig zu machen.</dd>
<dd>In Kombination mit dem split-Node verbindet es automatisch die Nachrichten, um die zuvor durchgeführte Aufteilung rückgängig zu machen.</dd>
<dt>Manuell</dt>
<dd>Die Nachrichtensequenzen können auf verschiedene Weisen verbunden werden.</dd>
<dt>Sequenz reduzieren</dt>
@@ -77,7 +77,7 @@
<dl class="message-properties">
<dt class="optional">parts <span class="property-type">object</span></dt>
<dd>Zur automatischen Verbindung einer Nachrichtensequenz sollten alle über diese Eigenschaft verfügen.
Der <span style="background-color:Gainsboro">split</span>-Node erzeugt diese Eigenschaft, sie kann aber auch manuell erstellt werden.
Der split-Node erzeugt diese Eigenschaft, sie kann aber auch manuell erstellt werden.
Sie hat die folgenden Eigenschaften:
<ul>
<li><code>id</code>: Identifikator der Nachrichten-Gruppe</li>
@@ -98,7 +98,7 @@
<h4><b>Automatischer Modus</b></h4>
<p>Der automatische Modus verwendet die <code>parts</code>-Eigenschaften der eingehenden Nachrichten,
um die Sequenz in richtiger Reihenfolge zu verknüpften.
Dies ermöglicht die Aufteilung des <span style="background-color:Gainsboro">split</span>-Nodes automatisch rückgängig zu machen.</p>
Dies ermöglicht die Aufteilung des split-Nodes automatisch rückgängig zu machen.</p>
<h4><b>Manueller Modus</b></h4>
<p>Im manuellen Modus werden Nachrichtensequenzen auf verschiedenen Arten zusammengefügt:</p>

View File

@@ -26,8 +26,8 @@
<p>Für Zahlenwerte kann die numerische Sortierreihenfolge festgelegt werden.</p>
<p>Der Sortierschlüssel kann ein Elementwert oder ein JSONata-Ausdruck beim Sortieren einer Nachrichteneigenschaft
bzw. eine Nachrichteneigenschaft oder ein JSONata-Ausdruck beim Sortieren einer Nachrichtensequenz sein.<p>
<p>Zum Sortieren einer Nachrichtensequenz benötigt der <span style="background-color:Gainsboro">sort</span>-Node die gesetzte <code>msg.parts</code>-Eigenschaft bei den empfangenen Nachrichten.
Diese Eigenschaft wird vom <span style="background-color:Gainsboro">split</span>-Node erzeugt und kann aber auch manuell erzeugt werden.
<p>Zum Sortieren einer Nachrichtensequenz benötigt der sort-Node die gesetzte <code>msg.parts</code>-Eigenschaft bei den empfangenen Nachrichten.
Diese Eigenschaft wird vom split-Node erzeugt und kann aber auch manuell erzeugt werden.
Sie hat die folgenden Eigenschaften:</p>
<p>
<ul>

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