Compare commits

..

145 Commits
3.1.0 ... 4508

Author SHA1 Message Date
Nick O'Leary
58e2fcbeee Ensure global-config credential env vars are merged on deploy
Fixes #4508
2024-01-15 16:44:43 +00:00
Nick O'Leary
282bb6c414 Merge pull request #4512 from node-red/4503-fix-cache-busting
Restore caching busting functionality without using explict version number
2024-01-15 15:54:40 +00:00
Nick O'Leary
ce5b6a8024 Merge pull request #4487 from GogoVega/fix-validation-nls
Add missing validation messages
2024-01-08 23:42:58 +00:00
Nick O'Leary
0773edcaff Merge pull request #4498 from kazuhitoyokoi/master-jpn3.1.3
Add Japanese translations for v3.1.3
2024-01-08 23:42:35 +00:00
Nick O'Leary
68dcf7bceb Merge pull request #4500 from kazuhitoyokoi/master-disablemenuitems
Add handling to disable items on context menu
2024-01-08 23:42:19 +00:00
Nick O'Leary
d876146ea5 Guard settings access 2024-01-08 23:37:44 +00:00
Nick O'Leary
50627cd697 Generate instanceId and include in hash for cache busting 2024-01-08 23:27:14 +00:00
Nick O'Leary
6a4d293352 Merge pull request #4516 from kazuhitoyokoi/master-fixfocus4contextmenu
Focus Quick Add dialog from context menu
2024-01-08 17:04:00 +00:00
Nick O'Leary
1ad4fe44e2 Merge pull request #4518 from kazuhitoyokoi/master-fixquickadddialog4sf
Fix subflow ports in Quick Add dialog
2024-01-08 17:03:13 +00:00
Kazuhito Yokoi
59ea7a4f70 Fix subflow ports in Quick Add dialog 2024-01-08 03:12:36 +09:00
Kazuhito Yokoi
6c64ba45c2 Focus Quick Add dialog from context menu 2024-01-07 20:46:50 +09:00
Kazuhito Yokoi
c68cc4ac19 Merge branch 'master' into master-disablemenuitems 2024-01-07 18:47:12 +09:00
Kazuhito Yokoi
84ed88c8dd Use single forEach instead of multiple filter 2024-01-07 18:41:08 +09:00
Nick O'Leary
d7345d5bc6 Restore caching busting functionality without using explict version number
Fixes #4503
2024-01-05 23:14:00 +00:00
Nick O'Leary
f0a9b0cf69 Merge pull request #4506 from GogoVega/fix-4505-menu-flow-edit-label
Replace `rename` by `edit` for the menu flow label
2024-01-05 21:00:29 +00:00
Nick O'Leary
26ddb5c1b7 Merge pull request #4502 from kazuhitoyokoi/master-fixsubflowports
Fix location of subflow ports in palette
2024-01-05 20:59:45 +00:00
Nick O'Leary
82f8b64599 Merge pull request #4484 from gorenje/patch-2
Client/Editor Events: fix off-in-on pattern emulating once
2024-01-05 20:56:43 +00:00
GogoVega
7f24de442f Replace 'rename' with 'edit' for the flow label 2024-01-01 15:33:39 +01:00
Kazuhito Yokoi
8365310ca7 Put the changed code on one line to avoid jshint error 2023-12-29 20:32:14 +09:00
Kazuhito Yokoi
aaed9882b8 Add handling to disable items on context menu for node labels 2023-12-29 17:51:03 +09:00
Kazuhito Yokoi
8f5ebfcede Update Japanese translation for v3.1.3 2023-12-29 15:41:40 +09:00
Kazuhito Yokoi
74ff0599d1 Fix location of subflow ports in palette 2023-12-23 19:51:57 +09:00
GogoVega
1828d8a279 Add missing validation messages 2023-12-17 19:59:16 +01:00
Kazuhito Yokoi
70ea5c839a Add handling to disable items on context menu 2023-12-16 17:02:18 +09:00
Gerrit Riessen
e1f2e0656b Client Events: fix off-in-on pattern emulating once
This fixes an issue when RED.events.off(..) is called in a RED.events.on(..) callback:

```
let cb = () => {
  RED.events.off("event-name", cb)
  ....
}
RED.events.on("event-name", cb)
```

This pattern emulates a once(..), i.e., execute a callback once-only for an event.

Discussed in [Forum](https://discourse.nodered.org/t/event-offing-an-on-event-to-perform-only-once/83726)
2023-12-15 10:54:11 +01:00
Kazuhito Yokoi
d287b8867b Add Japanese translations for v3.1.3 2023-12-08 15:28:49 +09:00
Nick O'Leary
0e8d312794 Merge pull request #4476 from node-red/rel313
Bump for 3.1.3 release
2023-12-07 20:30:08 +00:00
Nick O'Leary
c584d51432 Bump for 3.1.3 release 2023-12-07 18:27:33 +00:00
Nick O'Leary
2366b4508f Merge pull request #4475 from node-red/fix-nls
Add missing en-us messages
2023-12-07 18:25:37 +00:00
Nick O'Leary
2f1565fbc9 Add missing en-us messages 2023-12-07 18:19:37 +00:00
Nick O'Leary
7fd0ecf721 Merge pull request #4474 from node-red/rel312
Bump for 3.1.2 release
2023-12-07 14:23:56 +00:00
Nick O'Leary
37d1539fda Bump for 3.1.2 release 2023-12-07 14:12:53 +00:00
Nick O'Leary
1e518396d6 Merge pull request #4470 from wangyiyi2056/Add-action-list-Chinese-translation
Added action list Chinese (Simplified and Traditional) translation + v3.1.1 changes
2023-12-07 13:59:54 +00:00
Nick O'Leary
617b98ed49 Merge pull request #4466 from GogoVega/add-action-list-translation
Add French translation of `action-list` + v3.1.1 changes
2023-12-07 13:56:25 +00:00
Nick O'Leary
6d2a870812 Merge pull request #4472 from node-red/fix-nested-groups-in-subflow
Ensure nested groups inside subflows have their g props remapped
2023-12-07 13:42:14 +00:00
Nick O'Leary
2963f3f1b8 Ensure nested groups inside subflows have their g props remapped 2023-12-07 11:47:02 +00:00
wangyiyi2056
17e4bdbff1 Merge branch 'master' into Add-action-list-Chinese-translation 2023-12-07 09:19:46 +08:00
Nick O'Leary
f3dd5770d9 Merge pull request #4471 from node-red/relax-validation
Relax some node validators to allow undefined value
2023-12-06 12:11:30 +00:00
Nick O'Leary
eebab4a921 Relax some node validators to allow undefined value 2023-12-06 10:30:49 +00:00
Nick O'Leary
b06494c5be Merge pull request #4465 from node-red/4464-fix-switch-typeof-validation
Fix switch validation of typeof field
2023-12-06 10:09:39 +00:00
wangyiyi2056
a0562bef81 Added action list Chinese translation + v3.1.1 2023-12-05 22:50:03 +08:00
Nick O'Leary
eff063a748 Merge pull request #4467 from node-red/fix-group-mouse-pointer
Use move cursor when hovering on group border
2023-12-04 15:55:15 +00:00
Nick O'Leary
94abaaff1e Use move cursor when hovering on group border 2023-12-04 11:49:29 +00:00
GogoVega
03732869e4 Fix that little guy hiding 2023-12-02 18:53:06 +01:00
GogoVega
41868e2652 Apply v3.1.1 changes 2023-12-02 16:16:03 +01:00
GogoVega
81bfba3cea Add actions list translation 2023-12-02 16:10:52 +01:00
Nick O'Leary
8a04eb2e29 Update packages/node_modules/@node-red/nodes/core/function/10-switch.html 2023-12-01 19:40:49 +00:00
Nick O'Leary
1777fc749d Fix switch validation of typeof field
Fixes #4464
2023-12-01 13:11:07 +00:00
Nick O'Leary
fd32ee09ff Merge pull request #4462 from node-red/rel311
Bump for 3.1.1 release
2023-11-30 16:14:40 +00:00
Nick O'Leary
4bb2157cab Bump for 3.1.1 release 2023-11-30 15:49:57 +00:00
Nick O'Leary
47f20cc86a Merge pull request #4404 from node-red/dependabot/github_actions/github-actions-62712cefbd
Bump the github-actions group with 2 updates
2023-11-30 15:15:42 +00:00
Nick O'Leary
5fc4526c70 Merge pull request #4461 from node-red/fix-debug-filter
Fix debug filter
2023-11-30 15:15:04 +00:00
Nick O'Leary
9fe653f821 Merge branch 'master' into fix-debug-filter 2023-11-30 15:08:14 +00:00
Nick O'Leary
55da21ed15 Merge pull request #4460 from node-red/4369-improve-error-handling-in-subflowmodule-parsing
Handle unknown node reference inside subflow module
2023-11-30 15:07:58 +00:00
Nick O'Leary
7ebf84f38c Fix debug filter 2023-11-30 14:50:28 +00:00
Nick O'Leary
d42e75ebd0 Handle unknown node reference inside subflow module 2023-11-30 14:46:43 +00:00
Nick O'Leary
28825049fe Merge pull request #4459 from node-red/4401-fix-debug-window
Fix various issues with debug pop-out window
2023-11-30 11:36:16 +00:00
Nick O'Leary
1c3644e338 Fix various issues with debug pop-out window 2023-11-30 11:23:35 +00:00
Nick O'Leary
2dfabb523b Merge pull request #4457 from node-red/4456-fix-group-in-subflow-lookup
Ensure subflow instances keep track of their groups
2023-11-29 16:41:52 +00:00
Nick O'Leary
3e2d20e536 Merge pull request #4455 from GogoVega/4429-fix-validateNodeProperty
Fix `validateNodeProperty` without validator provided
2023-11-29 16:19:41 +00:00
Nick O'Leary
ee7ee083b0 Merge pull request #4440 from node-red/4429-add-typed-validators
Add validators to any fields using msg-typed Input
2023-11-29 16:18:53 +00:00
Nick O'Leary
fb54c05d9f Ensure subflow instances keep track of their groups 2023-11-29 16:12:12 +00:00
Nick O'Leary
21f807aa66 Merge pull request #4453 from node-red/4413-debounce-uninstall-notifications
Debounce node-removed notifications
2023-11-29 16:11:18 +00:00
Nick O'Leary
6b088bda12 Merge pull request #4452 from node-red/4443-add-modules-install-audit-event
Add modules.install audit event when external module installed
2023-11-29 16:11:04 +00:00
Nick O'Leary
a32ee869ae Merge pull request #4448 from bonanitech/first-commit-has-no-parents
Don't try to load the parents of the first commit
2023-11-29 16:10:42 +00:00
GogoVega
a2d7772958 Fix validateNodeProperty if no validator provided 2023-11-28 20:13:25 +01:00
Stephen McLaughlin
6ec052be18 Merge pull request #4454 from node-red/4380-mqtt-undefined-value
Guard against node.broker being undefined
2023-11-27 17:48:02 +00:00
Nick O'Leary
9c71d52d69 Check node.broker is a string before trying to use it
Fixes #4380
2023-11-27 17:27:32 +00:00
Nick O'Leary
171c146ec5 Debounce node-removed notifications
Fixes #4413
2023-11-27 17:14:15 +00:00
Nick O'Leary
bc6afa2164 Add modules.install audit event when external module installed 2023-11-27 16:58:14 +00:00
Stephen McLaughlin
3c036257ef Merge pull request #4451 from node-red/4446-allow-import-of-subpath-modules
Allow import of modules with subpath in specifier
2023-11-27 16:57:32 +00:00
Nick O'Leary
6633730bf1 Allow import of modules with subpath in specifier
Fixes #4446
2023-11-27 16:44:56 +00:00
Mauricio Bonani
2964a4da5e Don't try to load the parents of the first commit 2023-11-24 16:22:29 -05:00
Stephen McLaughlin
a55554193b Merge pull request #4441 from node-red/4274-allow-theme-to-set-mermaid-theme
Allow a theme to specifiy which theme mermaid should use
2023-11-20 21:43:03 +00:00
Nick O'Leary
d2a8338d4a Rename mermaidOptions to mermaid for contrib themes 2023-11-20 17:50:59 +00:00
Nick O'Leary
e945deeab6 Allow mermaid theme to be set via editorTheme and custom themes
Fixes #4274
2023-11-20 17:47:39 +00:00
Nick O'Leary
722fe02933 Add validators to any fields using msg-typed Input
Fixes #4429
2023-11-20 17:17:52 +00:00
Nick O'Leary
3dec609459 Merge pull request #4438 from node-red/update-deps-2
Update node-red-admin version
2023-11-20 10:00:44 +01:00
Nick O'Leary
e73b9f646d Update nr package to match 2023-11-17 10:44:34 +00:00
Nick O'Leary
3cea6400d1 Update node-red-admin version 2023-11-17 10:42:06 +00:00
Nick O'Leary
c6a8eee73d Merge pull request #4416 from node-red/mqtt-check-topic-length
check topic length > 0 before publish
2023-11-07 17:46:33 +00:00
Nick O'Leary
74d431ea36 Merge pull request #4427 from node-red/4394-update-page-title-on-flow-nav
Update browser title with flow name if set
2023-11-07 17:40:36 +00:00
Nick O'Leary
409a559a13 Merge pull request #4411 from node-red/4376-handle-falsy-env-vars
Handle false-like env vars properly
2023-11-07 17:40:18 +00:00
Nick O'Leary
6829535350 Merge pull request #4409 from node-red/4381-avoid-multiple-settings-saves
Only save settings once during node load process
2023-11-07 17:40:03 +00:00
Nick O'Leary
6488111f79 Merge pull request #4423 from node-red/4415-handle-excluded-core-nodes
Ensure typeSearch handles undefined node definitions
2023-11-07 17:39:46 +00:00
Nick O'Leary
923339c1d8 Merge pull request #4426 from node-red/4044-group-bb-import
Ensure group w/h are imported if present
2023-11-07 17:39:30 +00:00
Nick O'Leary
3f4d96f4cd Merge pull request #4425 from node-red/4418-fix-empty-status-background
Hide node status background when there is no status to show
2023-11-07 17:26:00 +00:00
Nick O'Leary
c52985d245 Update browser title with flow name if set
Fixes #4394
2023-11-07 17:24:04 +00:00
Nick O'Leary
88e6c71aa0 Ensure group w/h are imported if present
Fixes #4044
2023-11-07 17:09:22 +00:00
Nick O'Leary
4d08e297c4 Hide node status background when there is no status to show
Fixes #4418
2023-11-07 16:34:36 +00:00
Nick O'Leary
ad2b30691f Ensure typeSearch handles undefined node definitions
Fixes #4415
2023-11-03 11:28:04 +01:00
Dave Conway-Jones
369bad01b8 check topic length > 0 before publish
to close #4414
2023-11-03 08:55:38 +00:00
Nick O'Leary
b6ecc6d9ea Handle false-like env vars properly 2023-11-02 00:40:55 +01:00
Nick O'Leary
0117df0960 Only save settings once during node load process 2023-11-01 16:18:03 +01:00
Nick O'Leary
6ac905f264 Merge pull request #4407 from node-red/4392-add-close-button-to-restart-notification
Add a close button to the restart-required notification
2023-11-01 15:20:48 +01:00
Nick O'Leary
e5307f6604 Add a close button to the restart-required notification
Fixes #4392
2023-11-01 14:39:51 +01:00
Nick O'Leary
8d9b6dd859 Merge pull request #4405 from node-red/4396-fix-global-config-cred-lookup
Ensure global-config nodes lookup cred values properly
2023-11-01 14:20:56 +01:00
Nick O'Leary
01821ead0f Merge pull request #4371 from ralphwetzel/master
Extend typedInput "num" type validity check to NaN, binary, octal & hex
2023-11-01 14:20:31 +01:00
Nick O'Leary
1451fb9e2f Merge pull request #4399 from kazuhitoyokoi/master-fixrepeat4inject
Fix unintended new line in node name
2023-11-01 14:17:21 +01:00
Nick O'Leary
245751bb23 Merge pull request #4382 from hazymat/master
Ctrl-Enter does not close tray (Monaco) #4377
2023-11-01 14:15:08 +01:00
Nick O'Leary
f07519c705 Merge pull request #4393 from node-red/Fix-buffer-viewer-to-handle-0b-style-
fix buffer viewer to handle 0b style binary
2023-11-01 14:14:20 +01:00
Nick O'Leary
33a978a246 Ensure credentials.get stub is restored after tests 2023-11-01 14:12:20 +01:00
Nick O'Leary
861dc0c383 Ensure global-config nodes lookup cred values properly
Fixes #4396
2023-11-01 11:07:48 +01:00
dependabot[bot]
60593fed4a Bump the github-actions group with 2 updates
Bumps the github-actions group with 2 updates: [actions/setup-node](https://github.com/actions/setup-node) and [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).


Updates `actions/setup-node` from 3 to 4
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

Updates `peter-evans/create-pull-request` from 2 to 5
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v2...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 07:42:43 +00:00
Kazuhito Yokoi
fae3a5c26a Fix unintended new line in node name 2023-10-29 14:58:03 +09:00
Dave Conway-Jones
1a52c0adfc fix buffer viewer to handle 0b style binary 2023-10-24 23:15:34 +01:00
Mat Smith
44c0bbc61e Ctrl-Enter does not close tray (Monaco) #4377 2023-10-12 18:53:34 +01:00
ralphwetzel
5ac50fae3a Extend typedInput "num" type validity check to NaN, binary, octal & hex 2023-09-26 20:00:48 +02:00
Nick O'Leary
ee48a2f2bf Merge pull request #4362 from node-red/4342-subflow-err
Handle credential env var evaluation when no value set
2023-09-25 18:20:28 +01:00
Nick O'Leary
680d5b8216 Merge pull request #4364 from node-red/4323-redo-mermaid-integration
Rework mermaid integration to support off-DOM rendering
2023-09-25 18:08:48 +01:00
Nick O'Leary
c9320c190d Ensure creds object is not undefined when evaling env vars 2023-09-25 18:08:02 +01:00
Nick O'Leary
566c667c5d Merge pull request #4354 from bonanitech/ignore-package-lock
Don't commit package-lock.json
2023-09-25 18:05:46 +01:00
Nick O'Leary
ec6e42e655 Merge pull request #4361 from node-red/4342-fix-subflow-env-self-reference
Fix env evaluation when one env references another in the same object
2023-09-25 18:04:58 +01:00
Nick O'Leary
bba6b6f71d Merge pull request #4365 from node-red/4334-context-labels
Add missing nls labels to context menu
2023-09-25 18:04:35 +01:00
Nick O'Leary
c261f6625a Merge pull request #4368 from node-red/4340-switch-validation
Improve validation of switch/change node rules
2023-09-25 18:04:19 +01:00
Nick O'Leary
a489b270d1 Remove extra debug 2023-09-25 17:38:16 +01:00
Nick O'Leary
51cb61940d Improve validation of switch/change node rules
Fixes #4340
2023-09-25 17:33:59 +01:00
Nick O'Leary
ce2f896b45 Add missing nls labels to context menu
Fixes #4334
2023-09-22 16:29:33 +01:00
Nick O'Leary
6635ff9a69 Rework mermaid integration to support off-DOM rendering 2023-09-22 15:23:01 +01:00
Nick O'Leary
41797f8cef Handle credential env var evaluation when no value set 2023-09-22 13:56:54 +01:00
Nick O'Leary
797cea5394 Fix env evaluation when one env references another in the same object
Fixes #4342
2023-09-22 13:49:54 +01:00
Mauricio Bonani
2880d4120e Don't commit package-lock.json 2023-09-19 14:58:44 -04:00
Nick O'Leary
58dec7b9b0 Merge pull request #4312 from Rotzbua/add_dependabot
Add dependabot for Github Actions
2023-09-19 09:28:50 +01:00
Nick O'Leary
b0f900e25d Merge pull request #4311 from Rotzbua/update_gh_actions
Update outdated Github Actions
2023-09-19 09:28:19 +01:00
Nick O'Leary
fc54848318 Merge pull request #4349 from braincube-io/feat/fmo/makeCsvExportFast
Performance : make CSV export way faster by not re-allocating and handling huge string
2023-09-19 09:22:54 +01:00
Nick O'Leary
2f9f8cda81 Merge pull request #4350 from node-red/Fix-delay-node-passing-on-reset-regression
Fix regression in delay node to not pass on msg.reset
2023-09-19 09:21:33 +01:00
Nick O'Leary
667e7ab8dc Merge pull request #4348 from ZJvandeWeg/patch-2
github: Request `npm run test` in PR template
2023-09-19 09:21:06 +01:00
Nick O'Leary
4e55408fed Merge pull request #4346 from kazuhitoyokoi/master-fixheight4templete
Fix height of description editor in template node
2023-09-19 09:20:09 +01:00
Nick O'Leary
88aa61ea77 Merge pull request #4329 from GogoVega/french-translation-v3.1-beta.4-changes
Add French translation of v3.1.0-beta.4 changes + slight improvements
2023-09-19 09:19:49 +01:00
Nick O'Leary
2ccdeb968c Merge pull request #4332 from node-red/4330-fix-multiple-input-handlers
Handle nodes with multiple input handlers properly
2023-09-19 09:19:22 +01:00
Nick O'Leary
90045683c9 Merge pull request #4331 from node-red/fix-unexpected-link-validation-err
Handle undefined linkType value for existing link-call nodes
2023-09-19 09:18:50 +01:00
Nick O'Leary
eb49b01cbc Merge pull request #4351 from node-red/knolleary-patch-1
Soften the language around unrequited PRs
2023-09-19 09:18:26 +01:00
Nick O'Leary
3a6062775e Update CONTRIBUTING.md
Soften the language around unrequited PRs
2023-09-18 21:03:46 +01:00
Dave Conway-Jones
718a7bfc26 Update 89-delay.js 2023-09-18 19:32:01 +01:00
Franck
27ca30aa82 PERF : make csv way faster by not allocating and handling huge string 2023-09-18 15:16:15 +02:00
Zeger-Jan van de Weg
01b0bf1a36 github: Request npm run test in PR template
Due to the way grunt is installed, `grunt` might not work for the user locally.
However, `npm run test` will succeed for them, as NPM knows where `grunt` is located to execute.
2023-09-17 08:38:10 +02:00
Kazuhito Yokoi
e1cecc9601 Fix height of description editor in template node 2023-09-16 15:46:07 +09:00
GogoVega
4c13f5a0af Adapting the translation depending on the context 2023-09-09 14:34:33 +02:00
GogoVega
f5a9942d5e Fix translation mistakenly erased 2023-09-09 10:59:59 +02:00
Nick O'Leary
ee2d91fb4a Handle nodes with multiple input handlers properly
Fixes #4330
2023-09-08 16:26:10 +01:00
Nick O'Leary
6ca41ba69d Handle undefined linkType value for existing link-call nodes 2023-09-08 16:03:08 +01:00
GogoVega
7f9d142038 Add translation of v3.1-beta.4 changes 2023-09-08 15:45:00 +02:00
GogoVega
44a1f83b26 Add some capital letters 2023-09-08 15:39:46 +02:00
Rotzbua
25120e44ce Update outdated GH Actions 2023-09-05 14:56:14 +02:00
Rotzbua
12a1c5c2f4 Add dependabot for GH Action workflows 2023-09-05 14:11:08 +02:00
91 changed files with 1436 additions and 750 deletions

View File

@@ -30,5 +30,5 @@ the [forum](https://discourse.nodered.org) or
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
- [ ] I have run `grunt` to verify the unit tests pass
- [ ] I have run `npm run test` to verify the unit tests pass
- [ ] I have added suitable unit tests to cover the new/changed functionality

15
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "monthly"
groups:
github-actions:
patterns:
- "*"

View File

@@ -14,25 +14,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out node-red repository
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: 'node-red'
- name: Check out node-red-docker repository
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
repository: 'node-red/node-red-docker'
path: 'node-red-docker'
- name: Check out node-red.github.io repository
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
repository: 'node-red/node-red.github.io'
path: 'node-red.github.io'
- uses: actions/setup-node@v1
- uses: actions/setup-node@v4
with:
node-version: '16'
- run: node ./node-red/.github/scripts/update-node-red-docker.js
- name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
@@ -48,7 +48,7 @@ jobs:
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
- run: node ./node-red/.github/scripts/update-node-red-website.js
- name: Create Website Pull Request
uses: peter-evans/create-pull-request@v2
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>

View File

@@ -19,9 +19,9 @@ jobs:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ docs
.vscode
.nyc_output
sync.ffs_db
package-lock.json

View File

@@ -1,3 +1,77 @@
#### 3.1.3: Maintenance Release
Editor
- Add missing en-us messages (#4475) @knolleary
#### 3.1.2: Maintenance Release
Editor
- Relax some node validators to allow undefined value (#4471) @knolleary
- Fix switch validation of typeof field (#4465) @knolleary
- Use move cursor when hovering on group border (#4467) @knolleary
- Added action list Chinese (Simplified and Traditional) translation + v3.1.1 changes (#4470) @wangyiyi2056
- Add French translation of `action-list` + v3.1.1 changes (#4466) @GogoVega
Runtime
- Ensure nested groups inside subflows have their g props remapped (#4472) @knolleary
#### 3.1.1: Maintenance Release
Editor
- Fix debug filter (#4461) @knolleary
- Fix various issues with debug pop-out window (#4459) @knolleary
- Ensure subflow instances keep track of their groups (#4457) @knolleary
- Fix `validateNodeProperty` without validator provided (#4455) @GogoVega
- Debounce node-removed notifications (#4453) @knolleary
- Don't try to load the parents of the first commit (#4448) @bonanitech
- Allow a theme to specifiy which theme mermaid should use (#4441) @knolleary
- Update browser title with flow name if set (#4427) @knolleary
- Ensure typeSearch handles undefined node definitions (#4423) @knolleary
- Ensure group w/h are imported if present (#4426) @knolleary
- Hide node status background when there is no status to show (#4425) @knolleary
- Add a close button to the restart-required notification (#4407) @knolleary
- Extend typedInput "num" type validity check to NaN, binary, octal & hex (#4371) @ralphwetzel
- Fix unintended new line in node name (#4399) @kazuhitoyokoi
- Ctrl-Enter does not close tray (Monaco) #4377 (#4382) @hazymat
- fix buffer viewer to handle 0b style binary (#4393) @dceejay
- Rework mermaid integration to support off-DOM rendering (#4364) @knolleary
- Add missing nls labels to context menu (#4365) @knolleary
Runtime
- Bump the github-actions group with 2 updates (#4404) @app/dependabot
- Handle unknown node reference inside subflow module (#4460) @knolleary
- Add modules.install audit event when external module installed (#4452) @knolleary
- Allow import of modules with subpath in specifier (#4451) @knolleary
- Update node-red-admin version (#4438) @knolleary
- Handle false-like env vars properly (#4411) @knolleary
- Only save settings once during node load process (#4409) @knolleary
- Ensure global-config nodes lookup cred values properly (#4405) @knolleary
- Handle credential env var evaluation when no value set (#4362) @knolleary
- Don't commit package-lock.json (#4354) @bonanitech
- Fix env evaluation when one env references another in the same object (#4361) @knolleary
- Add dependabot for Github Actions (#4312) @Rotzbua
- Update outdated Github Actions (#4311) @Rotzbua
- github: Request `npm run test` in PR template (#4348) @ZJvandeWeg
- Add French translation of v3.1.0-beta.4 changes + slight improvements (#4329) @GogoVega
- Handle nodes with multiple input handlers properly (#4332) @knolleary
- Soften the language around unrequited PRs (#4351) @knolleary
Nodes
- CSV: make CSV export way faster by not re-allocating and handling huge string (#4349) @Fadoli
- Delay: Fix regression in delay node to not pass on msg.reset (#4350) @dceejay
- Link Call: Handle undefined linkType value for existing link-call nodes (#4331) @knolleary
- MQTT: Guard against node.broker being undefined (#4454) @knolleary
- MQTT: check topic length > 0 before publish (#4416) @dceejay
- Switch/Change: Improve validation of switch/change node rules (#4368) @knolleary
- Template: Fix height of description editor in template node (#4346) @kazuhitoyokoi
- Various: Add validators to any fields using msg-typed Input (#4440) @knolleary
#### 3.1.0: Milestone Release
Editor

View File

@@ -16,6 +16,9 @@ behavior to the project's core team at team@nodered.org.
Please raise any bug reports on the relevant project's issue tracker. Be sure to
search the list to see if your issue has already been raised.
If your issue is more of a question on how to do something with Node-RED, please
consider using the [community forum](https://discourse.nodered.org/).
A good bug report is one that make it easy for us to understand what you were
trying to do and what went wrong.
@@ -35,14 +38,18 @@ For feature requests, please raise them on the [forum](https://discourse.nodered
## Pull-Requests
If you want to raise a pull-request with a new feature, or a refactoring
of existing code, it may well get rejected if you haven't discussed it on
the [forum](https://discourse.nodered.org) first.
of existing code, please come and discuss it with us first. We prefer to
do it that way to make sure your time and effort is well spent on something
that fits with our goals.
If you've got a bug-fix or similar for us, then you are most welcome to
get it raised - just make sure you link back to the issue it's fixing and
try to include some tests!
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
It is an online process and quick to do. If you raise a pull-request without
having signed the CLA, you will be prompted to do so automatically.
### Code Branches
When raising a PR for a fix or a new feature, it is important to target the right branch.

View File

@@ -151,7 +151,6 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0",
"version": "3.1.3",
"description": "Low-code programming for event-driven applications",
"homepage": "https://nodered.org",
"license": "Apache-2.0",
@@ -64,7 +64,7 @@
"mqtt": "4.3.7",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"node-red-admin": "^3.1.0",
"node-red-admin": "^3.1.1",
"node-watch": "0.7.4",
"nopt": "5.0.0",
"oauth2orize": "1.11.1",
@@ -109,7 +109,7 @@
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "4.3.0",
"mermaid": "^9.4.3",
"mermaid": "^10.4.0",
"minami": "1.2.3",
"mocha": "9.2.2",
"node-red-node-test-helper": "^0.3.2",

View File

@@ -51,7 +51,7 @@ module.exports = {
var ui = require("./ui");
ui.init(runtimeAPI);
ui.init(settings, runtimeAPI);
const editorApp = apiUtil.createExpressApp(settings)

View File

@@ -339,6 +339,8 @@ module.exports = {
}
theme.codeEditor = theme.codeEditor || {}
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
}
activeThemeInitialised = true;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
const crypto = require('crypto')
var express = require('express');
var fs = require("fs");
var path = require("path");
@@ -24,13 +25,16 @@ var apiUtils = require("../util");
var theme = require("./theme");
var runtimeAPI;
let settings;
var editorClientDir = path.dirname(require.resolve("@node-red/editor-client"));
var defaultNodeIcon = path.join(editorClientDir,"public","red","images","icons","arrow-in.svg");
var editorTemplatePath = path.join(editorClientDir,"templates","index.mst");
var editorTemplate;
let cacheBuster
module.exports = {
init: function(_runtimeAPI) {
init: function(_settings, _runtimeAPI) {
settings = _settings;
runtimeAPI = _runtimeAPI;
editorTemplate = fs.readFileSync(editorTemplatePath,"utf8");
Mustache.parse(editorTemplate);
@@ -91,6 +95,12 @@ module.exports = {
},
editor: async function(req,res) {
if (!cacheBuster) {
// settings.instanceId is set asynchronously to the editor-api
// being initiaised. So we defer calculating the cacheBuster hash
// until the first load of the editor
cacheBuster = crypto.createHash('md5').update(`${settings.version || 'version'}-${settings.instanceId || 'instanceId'}`).digest("hex").substring(0,12)
}
let sessionMessages;
if (req.session && req.session.messages) {
@@ -99,6 +109,7 @@ module.exports = {
}
res.send(Mustache.render(editorTemplate,{
sessionMessages,
cacheBuster,
...await theme.context()
}));
},

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.1.0",
"version": "3.1.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0",
"@node-red/editor-client": "3.1.0",
"@node-red/util": "3.1.3",
"@node-red/editor-client": "3.1.3",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"clone": "2.1.2",

View File

@@ -109,7 +109,6 @@
"selectionToSubflow": "Auswahl in Subflow umwandeln",
"flows": "Flow",
"add": "Hinzufügen",
"rename": "Umbenennen",
"delete": "Löschen",
"keyboardShortcuts": "Tastenkürzel",
"login": "Anmelden",

View File

@@ -113,7 +113,7 @@
"displayStatus": "Show node status",
"displayConfig": "Configuration nodes",
"import": "Import",
"importExample": "Import Example Flow",
"importExample": "Import example flow",
"export": "Export",
"search": "Search flows",
"searchInput": "search your flows",
@@ -122,7 +122,6 @@
"selectionToSubflow": "Selection to Subflow",
"flows": "Flows",
"add": "Add",
"rename": "Rename",
"delete": "Delete",
"keyboardShortcuts": "Keyboard shortcuts",
"login": "Login",
@@ -130,6 +129,11 @@
"editPalette": "Manage palette",
"other": "Other",
"showTips": "Show tips",
"showNodeHelp": "Show node help",
"enableSelectedNodes": "Enable selected nodes",
"disableSelectedNodes": "Disable selected nodes",
"showSelectedNodeLabels": "Show selected node labels",
"hideSelectedNodeLabels": "Hide selected node labels",
"showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website",
"projects": "Projects",
@@ -511,8 +515,8 @@
"selectAllConnected": "Select connected",
"addRemoveNode": "Add/remove node from selection",
"editSelected": "Edit selected node",
"deleteSelected": "Delete selected nodes or link",
"deleteReconnect": "Delete and Reconnect",
"deleteSelected": "Delete selection",
"deleteReconnect": "Delete and reconnect",
"importNode": "Import nodes",
"exportNode": "Export nodes",
"nudgeNode": "Move selected nodes (1px)",
@@ -1215,9 +1219,8 @@
"validator": {
"errors": {
"invalid-json": "Invalid JSON data: __error__",
"invalid-json-prop": "__prop__: invalid JSON data: __error__",
"invalid-expr": "Invalid JSONata expression: __error__",
"invalid-prop": "Invalid property expression",
"invalid-prop-prop": "__prop__: invalid property expression",
"invalid-num": "Invalid number",
"invalid-num-prop": "__prop__: invalid number",
"invalid-regexp": "Invalid input pattern",
@@ -1229,6 +1232,7 @@
}
},
"contextMenu": {
"showActionList": "Show action list",
"insert": "Insert",
"node": "Node",
"junction": "Junction",

View File

@@ -119,10 +119,9 @@
"searchInput": "Rechercher vos flux",
"subflows": "Sous-flux",
"createSubflow": "Créer un sous-flux",
"selectionToSubflow": "Selection d'un sous-flux",
"selectionToSubflow": "Convertir en sous-flux",
"flows": "Flux",
"add": "Ajouter",
"rename": "Renommer",
"delete": "Supprimer",
"keyboardShortcuts": "Raccourcis clavier",
"login": "Se connecter",
@@ -274,23 +273,23 @@
"recoveredNodesInfo": "Les noeuds importés sur ce flux contiennent un mauvais identifiant de flux. Ces noeuds ont été ajoutés à ce flux afin que vous puissiez les restaurer ou les supprimer.",
"recoveredNodesNotification": "<p>Noeuds importés sans identifiant de flux valide</p><p>Ils ont été ajoutés à un nouveau flux appelé '__flowName__'.</p>",
"export": {
"selected": "noeuds sélectionnés",
"current": "flux actuel",
"selected": "les noeuds sélectionnés",
"current": "le flux actuel",
"all": "tous les flux",
"compact": "condensé",
"formatted": "formaté",
"compact": "Condensé",
"formatted": "Formaté",
"copy": "Copier dans le presse-papier",
"export": "Exporter vers la bibliothèque",
"exportAs": "Exporter en tant que",
"exportAs": "Exporter comme",
"overwrite": "Remplacer",
"exists": "<p><b>\"__file__\"</b> existe déjà.</p><p>Voulez-vous le remplacer ?</p>"
},
"import": {
"import": "Importer vers",
"importSelected": "Importation sélectionnée",
"importSelected": "Importer la sélection",
"importCopy": "Importer une copie",
"viewNodes": "Afficher les noeuds...",
"newFlow": "Nouveau flux",
"viewNodes": "Vérifier ces noeuds",
"newFlow": "un nouveau flux",
"replace": "Remplacer",
"errors": {
"notArray": "L'entrée n'est pas un tableau JSON",
@@ -299,7 +298,7 @@
"missingType": "L'entrée n'est pas un flux valide - l'élément '__index__' n'a pas de propriété 'type'"
},
"conflictNotification1": "Certains des noeuds que vous avez importés existent déjà dans votre espace de travail.",
"conflictNotification2": "Sélectionner les noeuds à importer et choisir s'il faut remplacer les noeuds existants ou en importer une copie."
"conflictNotification2": "Sélectionnez les noeuds à importer et choisissez s'il faut remplacer les noeuds existants ou en importer une copie."
},
"copyMessagePath": "Chemin copié",
"copyMessageValue": "Valeur copiée",
@@ -391,10 +390,10 @@
"subflowInstances": "Il existe __count__ instance de ce modèle de sous-flux",
"subflowInstances_plural": "Il existe __count__ instances de ce modèle de sous-flux",
"editSubflowProperties": "modifier les propriétés",
"input": "entrées:",
"output": "sorties:",
"status": "statut du noeud",
"deleteSubflow": "supprimer le sous-flux",
"input": "Entrées:",
"output": "Sorties:",
"status": "Statut du noeud",
"deleteSubflow": "Supprimer le sous-flux",
"confirmDelete": "Voulez-vous vraiment supprimer ce sous-flux ?",
"info": "Description",
"category": "Catégorie",
@@ -416,6 +415,7 @@
},
"errors": {
"noNodesSelected": "<strong>Impossible de créer un sous-flux</strong> : aucun noeud sélectionné",
"acrossMultipleGroups": "Impossible de créer un sous-flux sur plusieurs groupes",
"multipleInputsToSelection": "<strong>Impossible de créer un sous-flux</strong> : plusieurs entrées pour la sélection"
}
},
@@ -447,8 +447,8 @@
"default": "Par défaut",
"noDefaultLabel": "Aucune",
"defaultLabel": "Utiliser l'étiquette par défaut",
"searchIcons": "Icônes de recherche",
"useDefault": "Utilisation par défaut",
"searchIcons": "Rechercher une icône",
"useDefault": "Icône par défaut",
"description": "Description",
"show": "Afficher",
"hide": "Masquer",
@@ -498,13 +498,13 @@
"keyboard": {
"title": "Raccourcis clavier",
"keyboard": "Clavier",
"filterActions": "Actions de filtrage",
"shortcut": "raccourci",
"scope": "portée",
"filterActions": "Rechercher l'action",
"shortcut": "Raccourci",
"scope": "Portée",
"unassigned": "Non attribué",
"global": "global",
"workspace": "espace de travail",
"editor": "boîte de dialogue d'édition",
"global": "Global",
"workspace": "Espace de travail",
"editor": "Boîte de dialogue d'édition",
"selectAll": "Tout sélectionner",
"selectNone": "Ne rien sélectionner",
"selectAllConnected": "Sélectionner tous les éléments connectés",
@@ -550,22 +550,22 @@
},
"palette": {
"noInfo": "Pas d'information disponible",
"filter": "Filtrer les noeuds",
"filter": "Rechercher le noeud",
"search": "Rechercher les modules",
"addCategory": "Ajouter un nouveau...",
"label": {
"subflows": "sous-flux",
"network": "réseau",
"common": "commun",
"input": "entrée",
"output": "sortie",
"function": "fonction",
"sequence": "séquence",
"parser": "analyseur",
"social": "social",
"storage": "stockage",
"analysis": "analyse",
"advanced": "avancé"
"subflows": "Sous-flux",
"network": "Réseau",
"common": "Commun",
"input": "Entrée",
"output": "Sortie",
"function": "Fonction",
"sequence": "Séquence",
"parser": "Analyseur",
"social": "Social",
"storage": "Stockage",
"analysis": "Analyse",
"advanced": "Avancé"
},
"actions": {
"collapse-all": "Réduire toutes les catégories",
@@ -586,6 +586,7 @@
"editor": {
"title": "Gérer la palette",
"palette": "Palette",
"allCatalogs": "Tous les catalogues",
"times": {
"seconds": "il y a quelques secondes",
"minutes": "il y a quelques minutes",
@@ -609,24 +610,25 @@
"nodeCount_plural": "__label__ noeuds",
"moduleCount": "__count__ module disponible",
"moduleCount_plural": "__count__ modules disponibles",
"inuse": "en cours d'utilisation",
"enableall": "activer tout",
"disableall": "désactiver tout",
"enable": "activer",
"disable": "désactiver",
"remove": "supprimer",
"update": "mettre à jour vers __version__",
"updated": "mis à jour",
"install": "installer",
"installed": "installé",
"conflict": "conflit",
"inuse": "En cours d'utilisation",
"enableall": "Activer tout",
"disableall": "Désactiver tout",
"enable": "Activer",
"disable": "Désactiver",
"remove": "Supprimer",
"update": "Mettre à jour vers __version__",
"updated": "Mis à jour",
"install": "Installer",
"installed": "Installé",
"conflict": "Conflit",
"conflictTip": "<p>Ce module ne peut pas être installé car il inclut un<br/>type de noeud qui a déjà été installé</p><p>Conflits avec <code>__module__</code></p>",
"loading": "Chargement des catalogues...",
"tab-nodes": "Noeuds",
"tab-install": "Installer",
"sort": "trier:",
"sortAZ": "a-z",
"sortRecent": "récent",
"sort": "Trier:",
"sortRelevance": "Pertinence",
"sortAZ": "A-Z",
"sortRecent": "Récent",
"more": "+ __count__ en plus",
"upload": "Charger le fichier tgz du module",
"refresh": "Actualiser la liste des modules",
@@ -667,7 +669,7 @@
"info": {
"name": "Information",
"tabName": "Nom",
"label": "info",
"label": "Info",
"node": "Noeud",
"type": "Type",
"group": "Groupe",
@@ -681,10 +683,10 @@
"properties": "Propriétés",
"info": "Information",
"desc": "Description",
"blank": "vide",
"null": "nul",
"showMore": "afficher en plus",
"showLess": "afficher en moins",
"blank": "Vide",
"null": "Nul",
"showMore": "Afficher en plus",
"showLess": "Afficher en moins",
"flow": "Flux",
"selection": "Sélection",
"nodes": "__count__ noeuds",
@@ -695,7 +697,7 @@
"arrayItems": "__count__ éléments",
"showTips": "Vous pouvez ouvrir les astuces à partir du panneau des paramètres",
"outline": "Plan",
"empty": "vide",
"empty": "Vide",
"globalConfig": "Noeuds de configuration globale",
"triggerAction": "Déclencher une action",
"find": "Rechercher dans l'espace de travail",
@@ -706,7 +708,7 @@
},
"help": {
"name": "Aide",
"label": "aide",
"label": "Aide",
"search": "Aide à la recherche",
"nodeHelp": "Aide sur les noeuds",
"showHelp": "Afficher l'aide",
@@ -717,23 +719,23 @@
},
"config": {
"name": "Noeuds de configuration",
"label": "configuration",
"label": "Configuration",
"global": "Tous les flux",
"none": "aucun",
"subflows": "sous-flux",
"flows": "flux",
"filterAll": "tout",
"none": "Aucun",
"subflows": "Sous-flux",
"flows": "Flux",
"filterAll": "Tout",
"showAllConfigNodes": "Afficher tous les noeuds de configuration",
"filterUnused": "inutilisé",
"filterUnused": "Inutilisé",
"showAllUnusedConfigNodes": "Afficher tous les noeuds de configuration inutilisés",
"filtered": "__count__ caché(s)"
},
"context": {
"name": "Données contextuelles",
"label": "contexte",
"none": "aucune sélection",
"refresh": "actualiser pour charger",
"empty": "vide",
"label": "Contexte",
"none": "Aucune sélection",
"refresh": "Actualiser pour charger",
"empty": "Vide",
"node": "Noeud",
"flow": "Flux",
"global": "Global",
@@ -744,10 +746,10 @@
},
"palette": {
"name": "Gestion des palettes",
"label": "palette"
"label": "Palette"
},
"project": {
"label": "projet",
"label": "Projet",
"name": "Projet",
"description": "Description",
"dependencies": "Dépendances",
@@ -760,11 +762,11 @@
"showProjectSettings": "Afficher les paramètres du projet",
"projectSettings": {
"title": "Paramètres du projet",
"edit": "modifier",
"edit": "Modifier",
"none": "Vide",
"install": "installer",
"removeFromProject": "supprimer du projet",
"addToProject": "ajouter au projet",
"install": "Installer",
"removeFromProject": "Supprimer du projet",
"addToProject": "Ajouter au projet",
"files": "Fichiers",
"flow": "Flux",
"credentials": "Identifiants",
@@ -812,7 +814,7 @@
"workflowAutoTip": "Les modifications sont validées automatiquement à chaque déploiement",
"sshKeys": "Clés SSH",
"sshKeysTip": "Vous permet de créer des connexions sécurisées aux référentiels Git distants.",
"add": "ajouter une clé",
"add": "Ajouter une clé",
"addSshKey": "Ajouter une clé SSH",
"addSshKeyTip": "Générer une nouvelle paire de clés publique/privée",
"name": "Nom",
@@ -848,7 +850,7 @@
"none": "Vide",
"conflictResolve": "Tous les conflits ont été résolus. Valider les modifications pour terminer la fusion.",
"localFiles": "Fichiers locaux",
"all": "tout",
"all": "Tout",
"unmergedChanges": "Modifications non fusionnées",
"abortMerge": "Abandonner la fusion",
"commit": "Valider",
@@ -1097,9 +1099,9 @@
"desc8": "Le fichier contenant les identifiants ne sera pas crypté et son contenu sera facilement lisible",
"create-project-files": "Créer des fichiers de projet",
"create-project": "Créer un projet",
"already-exists": "existe déjà",
"already-exists": "Existe déjà",
"git-error": "Erreur Git",
"git-auth-error": "erreur d'authentification Git"
"git-auth-error": "Erreur d'authentification Git"
},
"create-success": {
"success": "Vous avez créé avec succès votre premier projet !",
@@ -1135,8 +1137,8 @@
"desc2": "Avant de pouvoir cloner un référentiel sur ssh, vous devez ajouter une clé SSH pour y accéder.",
"add-ssh-key": "Ajouter une clé ssh",
"credentials-encryption-key": "Clé de chiffrement des identifiants",
"already-exists-2": "existe déjà",
"git-error": "erreur git",
"already-exists-2": "Existe déjà",
"git-error": "Erreur git",
"con-failed": "La connexion a échoué",
"not-git": "Ce n'est pas un dépôt git",
"no-resource": "Référentiel introuvable",
@@ -1148,8 +1150,8 @@
"confirm": "Voulez-vous vraiment supprimer ce projet ?"
},
"create-project-list": {
"search": "rechercher vos projets",
"current": "actuel"
"search": "Rechercher vos projets",
"current": "Actuel"
},
"require-clean": {
"confirm": "<p>Vous avez des modifications non déployées qui seront perdues.</p><p>Voulez-vous continuer ?</p>"
@@ -1212,9 +1214,8 @@
"validator": {
"errors": {
"invalid-json": "Données JSON invalides : __error__",
"invalid-json-prop": "__prop__: données JSON invalides : __error__",
"invalid-prop": "Expression de propriété non valide",
"invalid-prop-prop": "__prop__: expression de propriété invalide",
"invalid-expr": "Expression JSONata invalide : __error__",
"invalid-prop": "Expression de propriété invalide",
"invalid-num": "Numéro invalide",
"invalid-num-prop": "__prop__: numéro invalide",
"invalid-regexp": "Modèle d'entrée non valide",
@@ -1235,5 +1236,159 @@
"environment": "Environment",
"header": "Variables d'environnement globales",
"revert": "Rétablir"
},
"action-list": {
"toggle-show-tips": "Basculer l'affichage des astuces",
"show-about": "Afficher la description de Node-RED",
"show-welcome-tour": "Afficher la visite de bienvenue",
"show-next-tab": "Afficher l'onglet suivant",
"show-previous-tab": "Afficher l'onglet précédent",
"add-flow": "Ajouter un flux",
"add-flow-to-right": "Ajouter un flux à droite",
"edit-flow": "Modifier le flux",
"remove-flow": "Supprimer le flux",
"enable-flow": "Activer le flux",
"disable-flow": "Désactiver le flux",
"hide-flow": "Masquer le flux",
"hide-other-flows": "Masquer les autres flux",
"hide-all-flows": "Masquer tous les flux",
"show-all-flows": "Afficher tous les flux",
"show-last-hidden-flow": "Afficher le dernier flux masqué",
"list-modified-nodes": "Afficher les flux modifiés",
"list-hidden-flows": "Afficher les flux cachés",
"list-flows": "Lister les flux",
"list-subflows": "Liste les sous-flux",
"go-to-previous-location": "Aller à l'emplacement précédent",
"go-to-next-location": "Aller à l'emplacement suivant",
"copy-selection-to-internal-clipboard": "Copier la sélection dans le presse-papiers",
"cut-selection-to-internal-clipboard": "Couper la sélection dans le presse-papiers",
"paste-from-internal-clipboard": "Coller depuis le presse-papiers",
"detach-selected-nodes": "Détacher les noeuds sélectionnés",
"delete-selection": "Supprimer la sélection",
"delete-selection-and-reconnect": "Supprimer la sélection et reconnecter",
"edit-selected-node": "Modifier le noeud sélectionné",
"go-to-selection": "Aller à la sélection",
"undo": "Annuler les modifications",
"redo": "Rétablir les modifications",
"select-all-nodes": "Sélectionner tous les noeuds",
"select-none": "Sélectionner un noeud",
"enable-selected-nodes": "Activer les noeuds sélectionnés",
"disable-selected-nodes": "Désactiver les noeuds sélectionnés",
"toggle-show-grid": "Basculer l'affichage de la grille",
"toggle-snap-grid": "Basculer l'aide au placement des noeuds",
"toggle-status": "Commuter l'état",
"show-selected-node-labels": "Afficher les étiquettes des noeuds sélectionnés",
"hide-selected-node-labels": "Masquer les étiquettes des noeuds sélectionnés",
"scroll-view-up": "Faire défiler vers le haut",
"scroll-view-right": "Faire défiler vers la droite",
"scroll-view-down": "Faire défiler vers le bas",
"scroll-view-left": "Faire défiler vers la gauche",
"step-view-up": "Faire défiler d'une unité vers le haut",
"step-view-right": "Faire défiler d'une unité vers la droite",
"step-view-down": "Faire défiler d'une unité vers le bas",
"step-view-left": "Faire défiler d'une unité vers la gauche",
"move-selection-up": "Déplacer la sélection vers le haut",
"move-selection-right": "Déplacer la sélection vers la droite",
"move-selection-down": "Déplacer la sélection vers le bas",
"move-selection-left": "Déplacer la sélection vers la gauche",
"move-selection-forwards": "Avancer la sélection",
"move-selection-backwards": "Reculer la sélection",
"move-selection-to-front": "Déplacer la sélection vers l'avant",
"move-selection-to-back": "Déplacer la sélection vers l'arrière",
"step-selection-up": "Déplacer la sélection d'une unité vers le haut",
"step-selection-right": "Déplacer la sélection d'une unité vers la droite",
"step-selection-down": "Déplacer la sélection d'une unité vers le bas",
"step-selection-left": "Déplacer la sélection d'une unité vers la gauche",
"select-connected-nodes": "Sélectionner les noeuds connectés",
"select-downstream-nodes": "Sélectionner les noeuds connectés en aval",
"select-upstream-nodes": "Sélectionner les noeuds connectés en amont",
"go-to-next-node": "Aller au noeud suivant",
"go-to-previous-node": "Aller au noeud précédent",
"go-to-next-sibling": "Aller au noeud frère suivant",
"go-to-previous-sibling": "Aller au noeud frère précédent",
"go-to-nearest-node-on-left": "Aller au noeud gauche le plus proche",
"go-to-nearest-node-on-right": "Aller au noeud droit le plus proche",
"go-to-nearest-node-above": "Aller au noeud supérieur le plus proche",
"go-to-nearest-node-below": "Aller au noeud le plus proche ci-dessous",
"align-selection-to-grid": "Aligner la sélection",
"align-selection-to-left": "Aligner la sélection à gauche",
"align-selection-to-right": "Aligner la sélection à droite",
"align-selection-to-top": "Aligner la sélection en haut",
"align-selection-to-bottom": "Aligner la sélection vers le bas",
"align-selection-to-middle": "Aligner la sélection au centre verticalement",
"align-selection-to-center": "Aligner la sélection au centre horizontalement",
"distribute-selection-horizontally": "Distribuer la sélection horizontalement",
"distribute-selection-vertical": "Distribuer la sélection verticalement",
"wire-series-of-nodes": "Connecter les noeuds en série",
"wire-node-to-multiple": "Connecter les noeuds à plusieurs",
"wire-multiple-to-node": "Connecter plusieurs au noeud",
"split-wire-with-link-nodes": "Diviser le fil avec des noeuds de liaison",
"generate-node-names": "Générer les noms de noeuds",
"show-user-settings": "Afficher les paramètres utilisateur",
"show-help": "Afficher l'aide",
"toggle-palette": "Basculer l'affichage de la palette",
"show-event-log": "Afficher le journal des événements",
"manage-palette": "Gérer la palette",
"toggle-sidebar": "Basculer l'affichage de la barre latérale",
"show-info-tab": "Afficher l'onglet d'informations sur le noeud",
"show-help-tab": "Afficher l'onglet d'aide du noeud",
"show-config-tab": "Afficher l'onglet du noeud de configuration",
"select-all-config-nodes": "Sélectionner tous les noeuds de configuration",
"delete-config-selection": "Supprimer le noeud de configuration sélectionné",
"show-context-tab": "Afficher l'onglet des données contextuelles",
"create-subflow": "Créer un sous-flux",
"convert-to-subflow": "Convertir la sélection en sous-flux",
"group-selection": "Grouper la sélection",
"ungroup-selection": "Dissocier la sélection",
"merge-selection-to-group": "Fusionner la sélection dans le groupe",
"remove-selection-from-group": "Supprimer la sélection du groupe",
"copy-group-style": "Copier le style du groupe",
"paste-group-style": "Coller le style du groupe",
"show-export-dialog": "Afficher la boîte de dialogue d'exportation",
"show-import-dialog": "Afficher la boîte de dialogue d'importation",
"show-library-export-dialog": "Afficher la boîte de dialogue d'exportation de la bibliothèque",
"show-library-import-dialog": "Afficher la boîte de dialogue d'importation de bibliothèque",
"show-examples-import-dialog": "Afficher la boîte de dialogue d'importation d'exemples",
"search": "Rechercher",
"search-previous": "Recherche précédente",
"search-next": "Recherche suivante",
"show-action-list": "Afficher la liste d'actions",
"confirm-edit-tray": "Confirmer la modification",
"cancel-edit-tray": "Annuler la modification",
"show-remote-diff": "Afficher les différences avec les modifications distantes",
"deploy-flows": "Déployer des flux",
"restart-flows": "Redémarrer les flux",
"set-deploy-type-to-full": "Définir le déploiement sur 'tout'",
"set-deploy-type-to-modified-flows": "Définir le déploiement sur 'flux modifiés'",
"set-deploy-type-to-modified-nodes": "Définir le déploiement sur 'noeuds modifiés'",
"show-debug-tab": "Afficher l'onglet de débogage",
"clear-debug-messages": "Supprimer les messages de débogage",
"clear-filtered-debug-messages": "Supprimer les messages de débogage filtrés",
"activate-selected-debug-nodes": "Activer les noeuds de débogage sélectionnés",
"activate-all-debug-nodes": "Activer tous les noeuds de débogage",
"activate-all-flow-debug-nodes": "Activer tous les noeuds de débogage dans un flux",
"deactivate-selected-debug-nodes": "Désactiver les noeuds de débogage sélectionnés",
"deactivate-all-debug-nodes": "Désactiver tous les noeuds de débogage",
"deactivate-all-flow-debug-nodes": "Désactiver tous les noeuds de débogage dans un flux",
"zoom-in": "Zoomer",
"zoom-out": "Dézoomer",
"zoom-reset": "Réinitialiser le zoom",
"toggle-navigator": "Basculer l'affichage du navigateur",
"show-system-info": "Afficher les informations système",
"split-wires-with-junctions": "Diviser les fils avec des jonctions",
"new-project": "Nouveau projet",
"open-project": "Ouvrir le projet",
"show-project-settings": "Afficher les paramètres du projet",
"show-version-control-tab": "Afficher l'onglet de contrôle de version",
"start-flows": "Démarrer les flux",
"stop-flows": "Arrêter les flux",
"copy-item-url": "Copier l'URL de l'élément",
"copy-item-edit-url": "Copier l'URL de modification de l'élément",
"move-flow-to-start": "Déplacer le flux jusqu'au début",
"move-flow-to-end": "Déplacer le flux jusqu'à la fin",
"show-global-env": "Afficher les variables d'environnement globales",
"lock-flow": "Verrouiller le flux",
"unlock-flow": "Déverrouiller le flux",
"show-node-help": "Afficher l'aide du noeud"
}
}

View File

@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "Obtient un objet de date à l'aide de la bibliothèque Moment."
},
"$clone": {
"args": "valeur",
"desc": "Cloner un objet en toute sécurité."
}
}

View File

@@ -122,7 +122,6 @@
"selectionToSubflow": "選択部分をサブフロー化",
"flows": "フロー",
"add": "フローを新規追加",
"rename": "フロー名を変更",
"delete": "フローを削除",
"keyboardShortcuts": "ショートカットキーの説明",
"login": "ログイン",
@@ -130,6 +129,11 @@
"editPalette": "パレットの管理",
"other": "その他",
"showTips": "ヒントを表示",
"showNodeHelp": "ノードのヘルプを表示",
"enableSelectedNodes": "選択したノードを有効化",
"disableSelectedNodes": "選択したノードを無効化",
"showSelectedNodeLabels": "選択したノードのラベル表示",
"hideSelectedNodeLabels": "選択したノードのラベル非表示",
"showWelcomeTours": "新バージョンのガイドツアーを表示",
"help": "Node-REDウェブサイト",
"projects": "プロジェクト",
@@ -511,7 +515,7 @@
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
"deleteSelected": "選択部分を削除",
"deleteReconnect": "削除と再接続",
"importNode": "フローの読み込み",
"exportNode": "フローの書き出し",
@@ -1215,9 +1219,8 @@
"validator": {
"errors": {
"invalid-json": "JSONデータが不正: __error__",
"invalid-json-prop": "__prop__: JSONデータが不正: __error__",
"invalid-expr": "不正なJSONata式: __error__",
"invalid-prop": "プロパティ式が不正",
"invalid-prop-prop": "__prop__: プロパティ式が不正",
"invalid-num": "数値が不正",
"invalid-num-prop": "__prop__: 数値が不正",
"invalid-regexp": "入力パターンが不正",
@@ -1229,6 +1232,7 @@
}
},
"contextMenu": {
"showActionList": "動作一覧を表示",
"insert": "挿入",
"node": "ノード",
"junction": "分岐点",

View File

@@ -79,7 +79,6 @@
"selectionToSubflow": "서브 플로우 선택",
"flows": "플로우",
"add": "추가",
"rename": "이름변경",
"delete": "삭제",
"keyboardShortcuts": "단축키",
"login": "로그인",

View File

@@ -109,7 +109,6 @@
"selectionToSubflow": "Seleção para subfluxo",
"flows": "Fluxos",
"add": "Adicionar",
"rename": "Renomear",
"delete": "Apagar",
"keyboardShortcuts": "Atalhos do teclado",
"login": "Ingressar",
@@ -1186,9 +1185,7 @@
"validator": {
"errors": {
"invalid-json": "Dados JSON inválidos: __error__",
"invalid-json-prop": "__prop__: dados JSON inválidos: __error__",
"invalid-prop": "Expressão de propriedade inválida",
"invalid-prop-prop": "__prop__: expressão de propriedade inválida",
"invalid-num": "Número inválido",
"invalid-num-prop": "__prop__: número inválido",
"invalid-regexp": "Padrão de entrada inválido",

View File

@@ -95,7 +95,6 @@
"selectionToSubflow": "Выделение в подпоток",
"flows": "Потоки",
"add": "Добавить",
"rename": "Переименовать",
"delete": "Удалить",
"keyboardShortcuts": "Сочетания клавиш",
"login": "Войти",

View File

@@ -23,7 +23,11 @@
"position": "位置",
"enable": "启用",
"disable": "禁用",
"upload": "上传"
"upload": "上传",
"lock": "锁定",
"unlock": "解锁",
"locked": "锁定",
"unlocked": "解锁"
},
"type": {
"string": "字符串",
@@ -68,7 +72,13 @@
"enabled": "有效",
"disabled": "无效",
"info": "详细描述",
"selectNodes": "点击节点来选择"
"selectNodes": "点击节点来选择",
"enableFlow": "启用流程",
"disableFlow": "禁用流程",
"lockFlow": "锁定流程",
"unlockFlow": "解除锁定",
"moveToStart": "移动到起始",
"moveToEnd": "移动到末尾"
},
"menu": {
"label": {
@@ -101,6 +111,7 @@
"displayStatus": "显示节点状态",
"displayConfig": "修改节点配置",
"import": "导入",
"importExample": "导入示例流程",
"export": "导出",
"search": "查找流程",
"searchInput": "查找流程",
@@ -109,7 +120,6 @@
"selectionToSubflow": "将选择部分更改为子流程",
"flows": "流程",
"add": "增加",
"rename": "重命名",
"delete": "删除",
"keyboardShortcuts": "键盘快捷方式",
"login": "登录",
@@ -142,7 +152,12 @@
"moveToBack": "置于底层",
"moveToFront": "置于顶层",
"moveBackwards": "向后移动",
"moveForwards": "向前移动"
"moveForwards": "向前移动",
"showNodeHelp":"显示节点帮助",
"enableSelectedNodes":"启用当前选中节点",
"disableSelectedNodes":"禁用当前选中节点",
"showSelectedNodeLabels":"显示选中的节点标签",
"hideSelectedNodeLabels":"隐藏选中的节点标签"
}
},
"actions": {
@@ -403,6 +418,7 @@
},
"errors": {
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
"acrossMultipleGroups": "无法跨多个组创建子流",
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
}
},
@@ -491,12 +507,14 @@
"unassigned": "未分配",
"global": "全局",
"workspace": "工作区",
"editor": "编辑对话框",
"selectAll": "选择所有节点",
"selectNone": "取消所有选择",
"selectAllConnected": "选择所有连接的节点",
"addRemoveNode": "从选择中添加/删除节点",
"editSelected": "编辑选定节点",
"deleteSelected": "删除选定节点或链接",
"deleteReconnect": "删除并重新连接",
"importNode": "导入节点",
"exportNode": "导出节点",
"nudgeNode": "移动所选节点(1px)",
@@ -571,6 +589,7 @@
"editor": {
"title": "面板管理",
"palette": "控制板",
"allCatalogs": "所有目录",
"times": {
"seconds": "秒前",
"minutes": "分前",
@@ -610,6 +629,7 @@
"tab-nodes": "节点",
"tab-install": "安装",
"sort": "排序:",
"sortRelevance": "关联",
"sortAZ": "a-z顺序",
"sortRecent": "日期顺序",
"more": "增加 __count__ 个",
@@ -683,7 +703,11 @@
"empty": "空的",
"globalConfig": "全局配置节点",
"triggerAction": "触发动作",
"find": "在工作区中查找"
"find": "在工作区中查找",
"copyItemUrl": "复制地址",
"copyURL2Clipboard": "复制地址到剪贴板",
"showFlow": "显示流程",
"hideFlow": "隐藏流程"
},
"help": {
"name": "帮助",
@@ -984,7 +1008,10 @@
"quote": "引用",
"link": "链接",
"horizontal-rule": "水平线",
"toggle-preview": "切换预览"
"toggle-preview": "切换预览",
"mermaid": {
"summary": "美人鱼图"
}
},
"bufferEditor": {
"title": "Buffer 编辑器",
@@ -1147,17 +1174,6 @@
"create": "创建分支",
"current": "当前的"
},
"languages": {
"de": "德语",
"en-US": "英文",
"fr": "法语",
"ja": "日语",
"ko": "韩文",
"pt-BR":"葡萄牙语",
"ru":"俄語",
"zh-CN": "简体中文",
"zh-TW": "繁体中文"
},
"create-default-file-set": {
"no-active": "没有活动项目就无法创建默认文件集",
"no-empty": "无法在非空项目上创建默认文件集",
@@ -1188,20 +1204,21 @@
"title": "系统信息"
},
"languages": {
"de": "德语-Deutsch",
"en-US": "英文-English",
"ja": "日语-日本",
"ko": "韩文-한국인",
"ru": "俄语-Русский",
"de": "德语",
"en-US": "英文",
"fr": "法语",
"ja": "日语",
"ko": "韩文",
"pt-BR":"葡萄牙语",
"ru":"俄語",
"zh-CN": "简体中文",
"zh-TW": "繁中文"
"zh-TW": "繁中文"
},
"validator": {
"errors": {
"invalid-json": "无效的 JSON 数据: __error__",
"invalid-json-prop": "__prop__: 无效的 JSON 数据: __error__",
"invalid-expr": "无效的 JSONata 表达式: __error__",
"invalid-prop": "无效的属性表达式",
"invalid-prop-prop": "__prop__: 无效的属性表达式",
"invalid-num": "无效的数字",
"invalid-num-prop": "__prop__: 无效的数字",
"invalid-regexp": "输入格式无效",
@@ -1213,9 +1230,15 @@
}
},
"contextMenu": {
"showActionList":"显示动作列表",
"insert": "插入",
"node": "节点",
"junction": "连接点",
"linkNodes": "链接节点"
},
"env-var": {
"environment": "环境配置",
"header": "全局环境变量",
"revert": "重置"
}
}

View File

@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "使用Moment库获取日期对象。"
},
"$clone": {
"args": "value",
"desc": "安全克隆对象."
}
}

View File

@@ -23,7 +23,11 @@
"position": "位置",
"enable": "啟用",
"disable": "禁用",
"upload": "上傳"
"upload": "上傳",
"lock": "鎖定",
"unlock": "解鎖",
"locked": "鎖定",
"unlocked": "解鎖"
},
"type": {
"string": "字符串",
@@ -38,11 +42,14 @@
}
},
"event": {
"loadPlugins": "加載插件",
"loadPalette": "加載控制板",
"loadNodeCatalogs": "加載節點目錄",
"loadNodes": "加載 __count__ 個節點",
"loadFlows": "加載流程",
"importFlows": "往工作區中加載流程"
"importFlows": "往工作區中加載流程",
"importError": "<p>加載流程錯誤</p><p>__message__</p>",
"loadingProject": "加載項目"
},
"workspace": {
"defaultName": "流程__number__",
@@ -51,18 +58,35 @@
"delete": "確定想要刪除 '__label__'?",
"dropFlowHere": "把流程放到這裡",
"addFlow": "新增流程",
"listFlows": "流程列表",
"addFlowToRight": "在右側新增流程",
"hideFlow": "隱藏流程",
"hideOtherFlows": "隱藏其它流程",
"showAllFlows": "顯示所有流程",
"hideAllFlows": "隱藏所有流程",
"hiddenFlows": "列出 __count__ 個隱藏流程",
"hiddenFlows_plural": "列出 __count__ 個隱藏流程",
"showLastHiddenFlow": "顯示最後一個隱藏流程",
" ": "流程列表",
"listSubflows": "列出子流程",
"status": "狀態",
"enabled": "有效",
"disabled": "無效",
"info": "詳細描述",
"selectNodes": "點擊節點用於選擇"
"selectNodes": "點擊節點用於選擇",
"enableFlow": "啟用流程",
"disableFlow": "禁用流程",
"lockFlow": "鎖定流程",
"unlockFlow": "解除鎖定",
"moveToStart": "移動到起始",
"moveToEnd": "移動到末尾"
},
"menu": {
"label": {
"view": {
"view": "顯示",
"grid": "格線",
"storeZoom": "加載時還原縮放尺寸",
"storePosition": "加載時還原滾動位置",
"showGrid": "顯示格線",
"snapGrid": "對齊格線",
"gridSize": "格線尺寸",
@@ -80,12 +104,14 @@
"palette": {
"show": "顯示控制板"
},
"edit": "編輯",
"settings": "設置",
"userSettings": "使用者設置",
"nodes": "節點",
"displayStatus": "顯示節點狀態",
"displayConfig": "修改節點配置",
"import": "匯入",
"importExample": "導入示例流程",
"export": "匯出",
"search": "搜尋流程",
"searchInput": "搜尋流程",
@@ -94,7 +120,6 @@
"selectionToSubflow": "將選擇部分更改為子流程",
"flows": "流程",
"add": "增加",
"rename": "重新命名",
"delete": "刪除",
"keyboardShortcuts": "鍵盤快速鍵",
"login": "登入",
@@ -102,24 +127,48 @@
"editPalette": "節點管理",
"other": "其他",
"showTips": "顯示小提示",
"help": "Node-RED website",
"showWelcomeTours": "顯示新版本向導",
"help": "Node-RED 文檔主頁",
"projects": "專案",
"projects-new": "新專案",
"projects-open": "開啟專案",
"projects-settings": "專案設定",
"showNodeLabelDefault": "顯示新添加節點的標籤",
"codeEditor": "代碼編輯器",
"groups": "組",
"groupSelection": "選擇組",
"ungroupSelection": "取消選擇組",
"groupMergeSelection": "合并選擇",
"groupRemoveSelection": "從組中移除"
"groupRemoveSelection": "從組中移除",
"arrange": "布局",
"alignLeft": "左對齊",
"alignCenter": "居中對齊",
"alignRight": "右對齊",
"alignTop": "頂部對齊",
"alignMiddle": "垂直居中對齊",
"alignBottom": "底部對齊",
"distributeHorizontally": "横向分布",
"distributeVertically": "垂直分布",
"moveToBack": "置於底層",
"moveToFront": "置於頂層",
"moveBackwards": "向後移動",
"moveForwards": "向前移動",
"showNodeHelp":"顯示節點幫助",
"enableSelectedNodes":"啟用當前選中節點",
"disableSelectedNodes":"禁用當前選中節點",
"showSelectedNodeLabels":"顯示選中的節點標簽",
"hideSelectedNodeLabels":"隱藏選中的節點標簽"
}
},
"actions": {
"toggle-navigator": "切換導航器",
"zoom-out": "縮小",
"zoom-reset": "重置縮放",
"zoom-in": "放大"
"zoom-in": "放大",
"search-flows": "搜索流程",
"search-prev": "上一個",
"search-next": "下一個",
"search-counter": "\"__term__\" __result__ of __count__"
},
"user": {
"loggedInAs": "作為 __name__ 登入",
@@ -135,12 +184,17 @@
}
},
"notification": {
"state": {
"flowsStopped": "流程已停止",
"flowsStarted": "流程已啟動"
},
"warning": "<strong>警告</strong>: __message__",
"warnings": {
"undeployedChanges": "節點中存在未部署的更改",
"nodeActionDisabled": "節點動作在子流程中被禁用",
"nodeActionDisabledSubflow": "子流程中禁用了節點操作",
"missing-types": "流程由於缺少節點類型而停止。請檢查日誌的詳細資訊",
"missing-modules": "<p>流程因缺少模塊而停止。</p>",
"safe-mode": "<p>流程在安全模式下停止。</p><p>您可以修改流程並部署更改以重新啟動。</p>",
"restartRequired": "Node-RED必須重新啟動以啟用升級的模組",
"credentials_load_failed": "<p>流程由於無法解密證書而停止。</p> <p>流程證書文件已加密,但是項目的加密密鑰丟失或無效。</p>",
@@ -151,7 +205,7 @@
"project_not_found": "<p>找不到項目的'__project__'</p>",
"git_merge_conflict": "<p>自動合併更改失敗。</p><p>修復未合併的衝突,然後提交結果。</p>"
},
"error": "<strong>Error</strong>: __message__",
"error": "<strong>錯誤</strong>: __message__",
"errors": {
"lostConnection": "丟失與伺服器的連接,重新連接...",
"lostConnectionReconnect": "丟失與伺服器的連接__time__ 秒後重新連接",
@@ -208,6 +262,8 @@
"download": "下載",
"importUnrecognised": "匯入了無法識別的類型:",
"importUnrecognised_plural": "匯入了無法識別的類型:",
"importDuplicate": "導入了重復節點:",
"importDuplicate_plural": "導入了重復節點:",
"nodesExported": "節點匯出到了剪貼簿",
"nodesImported": "已匯入:",
"nodeCopied": "已複製 __count__ 個節點",
@@ -259,6 +315,10 @@
"modifiedFlowsDesc": "只部署包含已更改節點的流程",
"modifiedNodes": "已更改的節點",
"modifiedNodesDesc": "只部署已經更改的節點",
"startFlows": "啟動",
"startFlowsDesc": "啟動流程",
"stopFlows": "停止",
"stopFlowsDesc": "停止流程",
"restartFlows": "重新啟動流程",
"restartFlowsDesc": "重新啟動當前部署的流程",
"successfulDeploy": "部署成功",
@@ -337,14 +397,28 @@
"output": "輸出:",
"status": "狀態節點",
"deleteSubflow": "刪除子流程",
"confirmDelete": "您確定要刪除此子流程?",
"info": "詳細描述",
"category": "類別",
"module": "模塊",
"license": "許可",
"licenseNone": "無",
"licenseOther": "其它",
"type": "節點類型",
"version": "版本",
"versionPlaceholder": "x.y.z",
"keys": "關鍵字",
"keysPlaceholder": "使用英文逗號分隔關鍵字",
"author": "作者",
"authorPlaceholder": "名字 <email@example.com>",
"desc": "描述",
"env": {
"restore": "恢復為默認子流程",
"remove": "類別刪除環境變量"
},
"errors": {
"noNodesSelected": "<strong>無法創建子流程</strong>: 未選擇節點",
"acrossMultipleGroups": "無法跨多個組創建子流",
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
}
},
@@ -367,12 +441,12 @@
"editConfig": "編輯 __type__ 配置",
"addNewType": "添加新的 __type__ 節點",
"nodeProperties": "節點屬性",
"label": "Label",
"label": "標簽",
"color": "顏色",
"portLabels": "埠標籤",
"labelInputs": "輸入",
"labelOutputs": "輸出",
"settingIcon": "Icon",
"settingIcon": "圖標",
"default": "默認",
"noDefaultLabel": "無",
"defaultLabel": "使用默認標籤",
@@ -385,6 +459,7 @@
"icon": "圖標",
"inputType": "輸入類型",
"selectType": "選擇類型...",
"loadCredentials": "加載節點憑證",
"inputs": {
"input": "輸入",
"select": "選擇",
@@ -419,7 +494,8 @@
},
"errors": {
"scopeChange": "更改範圍將使其他流程中的節點無法使用",
"invalidProperties": "無效的屬性:"
"invalidProperties": "無效的屬性:",
"credentialLoadFailed": "無法加載節點憑據"
}
},
"keyboard": {
@@ -431,11 +507,14 @@
"unassigned": "未分配",
"global": "全局",
"workspace": "工作區",
"editor": "編輯對話框",
"selectAll": "選擇所有節點",
"selectNone": "取消所有選擇",
"selectAllConnected": "選擇所有連接的節點",
"addRemoveNode": "從選擇中添加/刪除節點",
"editSelected": "編輯選定節點",
"deleteSelected": "刪除選定節點或連結",
"deleteReconnect": "刪除並重新連接",
"importNode": "匯入節點",
"exportNode": "匯出節點",
"nudgeNode": "移動所選節點(1px)",
@@ -445,10 +524,14 @@
"copyNode": "複製所選節點",
"cutNode": "剪切所選節點",
"pasteNode": "粘貼節點",
"copyGroupStyle": "復製組樣式",
"pasteGroupStyle": "粘貼組樣式",
"undoChange": "撤銷上次執行的更改",
"redoChange": "重做",
"searchBox": "打開搜尋框",
"managePalette": "管理面板",
"actionList": "動作列表"
"actionList": "動作列表",
"splitWireWithLinks": "使用Link節點拆分已選項"
},
"library": {
"library": "庫",
@@ -466,12 +549,11 @@
"types": {
"local": "本地",
"examples": "例子"
},
"exportToLibrary": "將節點匯出到庫"
}
},
"palette": {
"noInfo": "無可用資訊",
"filter": "過濾節點",
"filter": "過濾已安裝模組",
"search": "搜尋模組",
"addCategory": "添加新的...",
"label": {
@@ -501,11 +583,13 @@
"nodeEnabled_plural": "啟用多個節點:",
"nodeDisabled": "禁用節點:",
"nodeDisabled_plural": "禁用多個節點:",
"nodeUpgraded": "節點模組__module__升級到__version__版本"
"nodeUpgraded": "節點模組__module__升級到__version__版本",
"unknownNodeRegistered": "加載節點錯誤: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "面板管理",
"palette": "Palette",
"palette": "控製板",
"allCatalogs": "所有目錄",
"times": {
"seconds": "秒前",
"minutes": "分前",
@@ -545,10 +629,12 @@
"tab-nodes": "節點",
"tab-install": "安裝",
"sort": "排序:",
"sortRelevance": "關聯",
"sortAZ": "a-z順序",
"sortRecent": "日期順序",
"more": "增加 __count__ 個",
"upload": "上傳模塊tgz文件",
"refresh": "更新模塊列表",
"errors": {
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
@@ -617,7 +703,11 @@
"empty": "空的",
"globalConfig": "全局配置節點",
"triggerAction": "觸發動作",
"find": "在工作區中查找"
"find": "在工作區中查找",
"copyItemUrl": "復製地址",
"copyURL2Clipboard": "復製地址到剪貼板",
"showFlow": "顯示流程",
"hideFlow": "隱藏流程"
},
"help": {
"name": "幫助",
@@ -627,7 +717,8 @@
"showHelp": "顯示幫助",
"showInOutline": "在大綱中顯示",
"showTopics": "顯示主題",
"noHelp": "未選擇幫助主題"
"noHelp": "未選擇幫助主題",
"changeLog": "更新日誌"
},
"config": {
"name": "配置節點",
@@ -828,31 +919,37 @@
"json": "JSON",
"bin": "二進位流",
"date": "時間戳記",
"jsonata": "expression",
"env": "env variable",
"jsonata": "表達式",
"env": "環境變量",
"cred": "證書"
}
},
"editableList": {
"add": "添加"
"add": "添加",
"addTitle": "添加項"
},
"search": {
"empty": "找不到匹配",
"history": "搜索歷史",
"clear": "清除所有",
"empty": "找不到匹配項",
"addNode": "添加一個節點...",
"options": {
"configNodes": "配置節點",
"unusedConfigNodes": "未使用的配置節點",
"invalidNodes": "無效的節點",
"uknownNodes": "未知的節點",
"unusedSubflows": "未使用的子流程"
"unusedSubflows": "未使用的子流程",
"hiddenFlows": "隱藏的流程",
"modifiedNodes": "已修改的節點或流程",
"thisFlow": "當前流程"
}
},
"expressionEditor": {
"functions": "功能",
"functionReference": "Function reference",
"functionReference": "功能參考",
"insert": "插入",
"title": "JSONata運算式編輯器",
"test": "Test",
"test": "測試",
"data": "示例消息",
"result": "結果",
"format": "格式表達方法",
@@ -863,20 +960,28 @@
"invalid-expr": "無效的JSONata運算式:\n __message__",
"invalid-msg": "無效的示例JSON消息:\n __message__",
"context-unsupported": "無法測試上下文函數\n $flowContext 或 $globalContext",
"env-unsupported": "無法測試 $env 函數",
"moment-unsupported": "無法測試 $moment 函數",
"clone-unsupported": "無法測試 $clone 函數",
"eval": "評估運算式錯誤:\n __message__"
}
},
"monaco": {
"setTheme": "設置主題"
},
"jsEditor": {
"title": "JavaScript 編輯器"
},
"textEditor": {
"title": "Text 編輯器"
"title": "文本編輯器"
},
"jsonEditor": {
"title": "JSON編輯器",
"format": "格式化JSON",
"rawMode": "編輯 JSON",
"uiMode": "Visual編輯器",
"uiMode": "可視化編輯器",
"rawMode-readonly": "原始JSON",
"uiMode-readonly": "可視化",
"insertAbove": "在上方插入",
"insertBelow": "在下方插入",
"addItem": "添加項目",
@@ -892,9 +997,9 @@
"title": "Markdown 編輯器",
"expand": "展開",
"format": "F使用markdown格式化",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"heading1": "標題 1",
"heading2": "標題 2",
"heading3": "標題 3",
"bold": "粗體",
"italic": "斜體",
"code": "程式碼",
@@ -903,7 +1008,10 @@
"quote": "引用",
"link": "連結",
"horizontal-rule": "分隔線",
"toggle-preview": "預覽"
"toggle-preview": "切換預覽",
"mermaid": {
"summary": "美人魚圖"
}
},
"bufferEditor": {
"title": "緩衝區編輯器",
@@ -1038,7 +1146,8 @@
"not-git": "不是git倉庫",
"no-resource": "找不到存儲庫",
"cant-get-ssh-key-path": "錯誤! 無法獲取所選的SSH密鑰路徑。",
"unexpected_error": "意外的錯誤"
"unexpected_error": "意外的錯誤",
"clearContext": "更改項目時清除上下文"
},
"delete": {
"confirm": "您確定要刪除此項目嗎?"
@@ -1068,7 +1177,7 @@
"create-default-file-set": {
"no-active": "沒有活動項目就無法創建默認文件集",
"no-empty": "無法在非空項目上創建默認文件集",
"git-error": "git error"
"git-error": "git錯誤"
},
"errors": {
"no-username-email": "您的Git客戶端未配置用戶名/電子郵件。",
@@ -1079,11 +1188,20 @@
"editor-tab": {
"properties": "屬性",
"envProperties": "環境變量",
"module": "模塊屬性",
"description": "描述",
"appearance": "外觀",
"preview": "UI預覽",
"defaultValue": "默認值",
"env": "環境變量"
"defaultValue": "默認值"
},
"tourGuide": {
"takeATour": "查看更新內容",
"start": "開始",
"next": "下一個",
"welcomeTours": "歡迎使用 Node-RED"
},
"diagnostics": {
"title": "系统信息"
},
"languages": {
"de": "德語",
@@ -1095,5 +1213,31 @@
"ru":"俄語",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文"
},
"validator": {
"errors": {
"invalid-json": "無效的 JSON 數據: __error__",
"invalid-expr": "無效的 JSONata 表達式: __error__",
"invalid-prop": "無效的屬性表達式",
"invalid-num": "無效的數字",
"invalid-regexp": "輸入格式無效",
"invalid-regex-prop": "__prop__: 輸入格式無效",
"missing-required-prop": "__prop__: 缺少屬性值",
"invalid-config": "__prop__: 無效的配置節點",
"missing-config": "__prop__: 缺少配置節點",
"validation-error": "__prop__: 驗證錯誤: __node__, __id__: __error__"
}
},
"contextMenu": {
"showActionList":"顯示動作列表",
"insert": "插入",
"node": "節點",
"junction": "連接點",
"linkNodes": "鏈接節點"
},
"env-var": {
"environment": "環境配置",
"header": "全局環境變量",
"revert": "重置"
}
}

View File

@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "使用Moment庫獲取日期對象。"
},
"$clone": {
"args": "value",
"desc": "安全克隆對象."
}
}

View File

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

View File

@@ -39,15 +39,16 @@
console.warn(evt,args);
}
if (handlers[evt]) {
for (var i=0;i<handlers[evt].length;i++) {
let cpyHandlers = [...handlers[evt]];
for (var i=0;i<cpyHandlers.length;i++) {
try {
handlers[evt][i].apply(null, args);
cpyHandlers[i].apply(null, args);
} catch(err) {
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
console.warn(err);
}
}
}
}
return {

View File

@@ -2198,6 +2198,12 @@ RED.nodes = (function() {
}
node._config.x = node.x;
node._config.y = node.y;
if (n.hasOwnProperty('w')) {
node.w = n.w
}
if (n.hasOwnProperty('h')) {
node.h = n.h
}
} else if (n.type.substring(0,7) === "subflow") {
var parentId = n.type.split(":")[1];
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);

View File

@@ -498,6 +498,15 @@ var RED = (function() {
]
}
}
} else if (notificationId === 'restart-required') {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
}
if (!persistentNotifications.hasOwnProperty(notificationId)) {
persistentNotifications[notificationId] = RED.notify(text,options);
@@ -525,6 +534,10 @@ var RED = (function() {
RED.view.redrawStatus(node);
}
});
let pendingNodeRemovedNotifications = []
let pendingNodeRemovedTimeout
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
@@ -562,8 +575,15 @@ var RED = (function() {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
typeList = "<ul><li>"+m.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
pendingNodeRemovedNotifications = pendingNodeRemovedNotifications.concat(m.types.map(RED.utils.sanitize))
if (pendingNodeRemovedTimeout) {
clearTimeout(pendingNodeRemovedTimeout)
}
pendingNodeRemovedTimeout = setTimeout(function () {
typeList = "<ul><li>"+pendingNodeRemovedNotifications.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:pendingNodeRemovedNotifications.length})+typeList,"success");
pendingNodeRemovedNotifications = []
}, 200)
}
}
loadIconList();
@@ -702,7 +722,7 @@ var RED = (function() {
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
{id:"menu-item-workspace-edit",label:RED._("menu.label.edit"),onselect:"core:edit-flow"},
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
]});
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [

View File

@@ -182,7 +182,9 @@
valueLabel: contextLabel
},
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
return (true === RED.utils.validateTypedProperty(v, "num"));
} },
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
json: {
value:"json",

View File

@@ -30,8 +30,26 @@ RED.contextMenu = (function () {
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
const canEdit = !RED.workspaces.isLocked()
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
if (hasSelection) {
selection.nodes.forEach(n => {
if (n.type === 'group') {
hasGroup = true;
} else {
isAllGroups = false;
}
if (n.d) {
hasDisabledNode = true;
} else {
hasEnabledNode = true;
}
if (n.l === undefined || n.l) {
hasLabeledNode = true;
} else {
hasUnlabeledNode = true;
}
});
}
const offset = $("#red-ui-workspace-chart").offset()
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
@@ -44,7 +62,7 @@ RED.contextMenu = (function () {
}
menuItems.push(
{ onselect: 'core:show-action-list', onpostselect: function () { } }
{ onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
)
const insertOptions = []
@@ -55,7 +73,7 @@ RED.contextMenu = (function () {
onselect: function () {
RED.view.showQuickAddDialog({
position: [addX, addY],
touchTrigger: true,
touchTrigger: 'ontouchstart' in window,
splice: isSingleLink ? selection.links[0] : undefined,
// spliceMultiple: isMultipleLinks
})
@@ -108,16 +126,16 @@ RED.contextMenu = (function () {
const nodeOptions = []
if (!hasMultipleSelection && !isGroup) {
nodeOptions.push(
{ onselect: 'core:show-node-help' },
{ onselect: 'core:show-node-help', label: RED._('menu.label.showNodeHelp') },
null
)
}
nodeOptions.push(
{ onselect: 'core:enable-selected-nodes' },
{ onselect: 'core:disable-selected-nodes' },
{ onselect: 'core:enable-selected-nodes', label: RED._('menu.label.enableSelectedNodes'), disabled: !hasDisabledNode },
{ onselect: 'core:disable-selected-nodes', label: RED._('menu.label.disableSelectedNodes'), disabled: !hasEnabledNode },
null,
{ onselect: 'core:show-selected-node-labels' },
{ onselect: 'core:hide-selected-node-labels' }
{ onselect: 'core:show-selected-node-labels', label: RED._('menu.label.showSelectedNodeLabels'), disabled: !hasUnlabeledNode },
{ onselect: 'core:hide-selected-node-labels', label: RED._('menu.label.hideSelectedNodeLabels'), disabled: !hasLabeledNode }
)
menuItems.push({
label: RED._('sidebar.info.node'),
@@ -126,8 +144,8 @@ RED.contextMenu = (function () {
menuItems.push({
label: RED._('sidebar.info.group'),
options: [
{ onselect: 'core:group-selection' },
{ onselect: 'core:ungroup-selection', disabled: !hasGroup },
{ onselect: 'core:group-selection', label: RED._("menu.label.groupSelection") },
{ onselect: 'core:ungroup-selection', label: RED._("menu.label.ungroupSelection"), disabled: !hasGroup },
]
})
if (hasGroup) {
@@ -143,8 +161,8 @@ RED.contextMenu = (function () {
}
menuItems[menuItems.length - 1].options.push(
null,
{ onselect: 'core:copy-group-style', disabled: !hasGroup },
{ onselect: 'core:paste-group-style', disabled: !hasGroup}
{ onselect: 'core:copy-group-style', label: RED._("keyboard.copyGroupStyle"), disabled: !hasGroup },
{ onselect: 'core:paste-group-style', label: RED._("keyboard.pasteGroupStyle"), disabled: !hasGroup}
)
}
if (canEdit && hasMultipleSelection) {
@@ -168,16 +186,16 @@ RED.contextMenu = (function () {
menuItems.push(
null,
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
{ onselect: 'core:undo', label: RED._("keyboard.undoChange"), disabled: RED.history.list().length === 0 },
{ onselect: 'core:redo', label: RED._("keyboard.redoChange"), disabled: RED.history.listRedo().length === 0 },
null,
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
{ onselect: 'core:select-all-nodes' },
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") },
)
}

View File

@@ -115,8 +115,9 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, definition, prop, properties[prop]);
if ((typeof valid) === "string") {
result.push(valid);
}
else if(!valid) {
} else if (Array.isArray(valid)) {
result = result.concat(valid)
} else if(!valid) {
result.push(prop);
}
}
@@ -165,7 +166,7 @@ RED.editor = (function() {
// If the validator takes two arguments, it is a 3.x validator that
// can return a String to mean 'invalid' and provide a reason
if ((definition[property].validate.length === 2) &&
((typeof valid) === "string")) {
((typeof valid) === "string") || Array.isArray(valid)) {
return valid;
} else {
// Otherwise, a 2.x returns a truth-like/false-like value that
@@ -181,6 +182,17 @@ RED.editor = (function() {
error: err.message
});
}
} else if (valid) {
// If the validator is not provided in node property => Check if the input has a validator
if ("category" in node._def) {
const isConfig = node._def.category === "config";
const prefix = isConfig ? "node-config-input" : "node-input";
const input = $("#"+prefix+"-"+property);
const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
if (isTypedInput) {
valid = input.typedInput("validate");
}
}
}
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
if (!value || value == "_ADD_") {

View File

@@ -121,7 +121,7 @@
var i=0,l=bufferBinValue.length;
var c = 0;
for(i=0;i<l;i++) {
var d = parseInt(bufferBinValue[i]);
var d = parseInt(Number(bufferBinValue[i]));
if (!isString && (isNaN(d) || d < 0 || d > 255)) {
valid = false;
break;

View File

@@ -966,12 +966,10 @@ RED.editor.codeEditor.monaco = (function() {
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
try {
ed._standaloneKeybindingService.addDynamicKeybinding(
'-editor.action.insertLineAfter', // command ID prefixed by '-'
null, // keybinding
() => {} // need to pass an empty handler
);
} catch (error) { }
monaco.editor.addKeybindingRule({keybinding: 0, command: "-editor.action.insertLineAfter"});
} catch (error) {
console.warn(error)
}
ed.nodered = {
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh

View File

@@ -169,7 +169,7 @@
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
mermaid.init();
RED.editor.mermaid.render()
},200);
})
if (options.header) {
@@ -178,7 +178,7 @@
if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
mermaid.init();
RED.editor.mermaid.render()
}
panels = RED.panels.create({
id:"red-ui-editor-type-markdown-panels",

View File

@@ -0,0 +1,54 @@
RED.editor.mermaid = (function () {
let initializing = false
let loaded = false
let pendingEvals = []
let diagramIds = 0
function render(selector = '.mermaid') {
// $(selector).hide()
if (!loaded) {
pendingEvals.push(selector)
if (!initializing) {
initializing = true
$.getScript(
'vendor/mermaid/mermaid.min.js',
function (data, stat, jqxhr) {
mermaid.initialize({
startOnLoad: false,
theme: RED.settings.get('mermaid', {}).theme
})
loaded = true
while(pendingEvals.length > 0) {
const pending = pendingEvals.shift()
render(pending)
}
}
)
}
} else {
const nodes = document.querySelectorAll(selector)
nodes.forEach(async node => {
if (!node.getAttribute('mermaid-processed')) {
const mermaidContent = node.innerText
node.setAttribute('mermaid-processed', true)
try {
const { svg } = await mermaid.render('mermaid-render-'+Date.now()+'-'+(diagramIds++), mermaidContent);
node.innerHTML = svg
} catch (err) {
$('<div>').css({
fontSize: '0.8em',
border: '1px solid var(--red-ui-border-color-error)',
padding: '5px',
marginBottom: '10px',
}).text(err.toString()).prependTo(node)
}
}
})
}
}
return {
render: render,
};
})();

View File

@@ -71,7 +71,7 @@ RED.envVar = (function() {
};
if (item.name.trim() !== "") {
new_env.push(item);
if ((item.type === "cred") && (item.value !== "__PWRD__")) {
if (item.type === "cred") {
credentials.map[item.name] = item.value;
credentials.map["has_"+item.name] = (item.value !== "");
item.value = "__PWRD__";

View File

@@ -1,46 +0,0 @@
// Mermaid diagram stub library for on-demand dynamic loading
// Will be overwritten after script loading by $.getScript
var mermaid = (function () {
var enabled /* = undefined */;
var initializing = false;
var initCalled = false;
function initialize(opt) {
if (enabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
enabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
enabled = true;
}
}
if (enabled) {
initializing = true;
$.getScript("vendor/mermaid/mermaid.min.js",
function (data, stat, jqxhr) {
$(".mermaid").show();
// invoke loaded mermaid API
initializing = false;
mermaid.initialize(opt);
if (initCalled) {
mermaid.init();
initCalled = false;
}
});
}
}
function init() {
if (initializing) {
$(".mermaid").hide();
initCalled = true;
}
}
return {
initialize: initialize,
init: init,
};
})();

View File

@@ -484,7 +484,7 @@ RED.palette = (function() {
var currentLabel = paletteNode.attr("data-palette-label");
var currentInfo = paletteNode.attr("data-palette-info");
if (currentLabel !== sf.name || currentInfo !== sf.info) {
if (currentLabel !== sf.name || currentInfo !== sf.info || sf.in.length > 0 || sf.out.length > 0) {
paletteNode.attr("data-palette-info",sf.info);
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||""));
}

View File

@@ -166,7 +166,7 @@ RED.projects.settings = (function() {
var description = addTargetToExternalLinks($('<span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span>')).appendTo(container);
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
setTimeout(function () {
mermaid.init();
RED.editor.mermaid.render()
}, 200);
}

View File

@@ -647,9 +647,9 @@ RED.sidebar.versionControl = (function() {
$.getJSON("projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
result.project = activeProject;
result.parents = entry.parents;
result.oldRev = entry.sha+"~1";
result.oldRev = entry.parents[0].length !== 0 ? entry.sha+"~1" : entry.sha;
result.newRev = entry.sha;
result.oldRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1";
result.oldRevTitle = entry.parents[0].length !== 0 ? RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1" : " ";
result.newRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7);
result.date = humanizeSinceDate(parseInt(entry.date));
RED.diff.showCommitDiff(result);

View File

@@ -383,6 +383,7 @@ RED.sidebar.help = (function() {
$(this).toggleClass('expanded',!isExpanded);
})
helpSection.parent().scrollTop(0);
RED.editor.mermaid.render()
}
function set(html,title) {

View File

@@ -464,7 +464,7 @@ RED.sidebar.info = (function() {
}
$(this).toggleClass('expanded',!isExpanded);
});
mermaid.init();
RED.editor.mermaid.render()
}
var tips = (function() {

View File

@@ -186,8 +186,15 @@ RED.typeSearch = (function() {
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, false);
if (!/^_action_:/.test(object.type) && object.type !== "junction") {
if (/^subflow:/.test(object.type)) {
var sf = RED.nodes.subflow(object.type.substring(8));
if (sf.in.length > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
}
if (sf.out.length > 0) {
$('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
}
} else if (!/^_action_:/.test(object.type) && object.type !== "junction") {
if (def.inputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
}
@@ -323,7 +330,7 @@ RED.typeSearch = (function() {
}
}
function applyFilter(filter,type,def) {
return !filter ||
return !def || !filter ||
(
(!filter.spliceMultiple) &&
(!filter.type || type === filter.type) &&

View File

@@ -101,28 +101,8 @@ RED.utils = (function() {
renderer.code = function (code, lang) {
if(lang === "mermaid") {
// mermaid diagram rendering
if (mermaidIsEnabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
mermaidIsEnabled = true;
}
}
if (mermaidIsEnabled) {
if (!mermaidIsInitialized) {
mermaidIsInitialized = true;
mermaid.initialize({startOnLoad:false});
}
return `<pre class='mermaid'>${code}</pre>`;
}
else {
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
}
}
else {
return `<pre class='mermaid'>${code}</pre>`;
} else {
return "<pre><code>" +code +"</code></pre>";
}
};
@@ -917,6 +897,51 @@ RED.utils = (function() {
}
}
/**
* Checks a typed property is valid according to the type.
* Returns true if valid.
* Return String error message if invalid
* @param {*} propertyType
* @param {*} propertyValue
* @returns true if valid, String if invalid
*/
function validateTypedProperty(propertyValue, propertyType, opt) {
let error
if (propertyType === 'json') {
try {
JSON.parse(propertyValue);
} catch(err) {
error = RED._("validator.errors.invalid-json", {
error: err.message
})
}
} else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) {
if (!RED.utils.validatePropertyExpression(propertyValue)) {
error = RED._("validator.errors.invalid-prop")
}
} else if (propertyType === 'num') {
if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) {
error = RED._("validator.errors.invalid-num")
}
} else if (propertyType === 'jsonata') {
try {
jsonata(propertyValue)
} catch(err) {
error = RED._("validator.errors.invalid-expr", {
error: err.message
})
}
}
if (error) {
if (opt && opt.label) {
return opt.label+': '+error
}
return error
}
return true
}
function getMessageProperty(msg,expr) {
var result = null;
var msgPropParts;
@@ -1451,6 +1476,7 @@ RED.utils = (function() {
getDarkerColor: getDarkerColor,
parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed,
getBrowserInfo: getBrowserInfo
getBrowserInfo: getBrowserInfo,
validateTypedProperty: validateTypedProperty
}
})();

View File

@@ -4187,7 +4187,7 @@ RED.view = (function() {
nodeEl.__statusGroup__.style.display = "none";
} else {
nodeEl.__statusGroup__.style.display = "inline";
let backgroundWidth = 12
let backgroundWidth = 15
var fill = status_colours[d.status.fill]; // Only allow our colours for now
if (d.status.shape == null && fill == null) {
backgroundWidth = 0
@@ -4207,7 +4207,11 @@ RED.view = (function() {
nodeEl.__statusLabel__.textContent = "";
}
const textSize = nodeEl.__statusLabel__.getBBox()
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth + textSize.width + 6)
backgroundWidth += textSize.width
if (backgroundWidth > 0 && textSize.width > 0) {
backgroundWidth += 6
}
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth)
}
delete d.dirtyStatus;
}
@@ -4619,8 +4623,8 @@ RED.view = (function() {
statusBackground.setAttribute("y",-1);
statusBackground.setAttribute("width",200);
statusBackground.setAttribute("height",13);
statusBackground.setAttribute("rx",1);
statusBackground.setAttribute("ry",1);
statusBackground.setAttribute("rx",2);
statusBackground.setAttribute("ry",2);
statusEl.appendChild(statusBackground);
node[0][0].__statusBackground__ = statusBackground;

View File

@@ -17,6 +17,8 @@
RED.workspaces = (function() {
const documentTitle = document.title;
var activeWorkspace = 0;
var workspaceIndex = 0;
@@ -339,12 +341,18 @@ RED.workspaces = (function() {
$("#red-ui-workspace-chart").show();
activeWorkspace = tab.id;
window.location.hash = 'flow/'+tab.id;
if (tab.label) {
document.title = `${documentTitle} : ${tab.label}`
} else {
document.title = documentTitle
}
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
} else {
$("#red-ui-workspace-chart").hide();
activeWorkspace = 0;
window.location.hash = '';
document.title = documentTitle
}
event.workspace = activeWorkspace;
RED.events.emit("workspace:change",event);

View File

@@ -40,46 +40,32 @@ RED.validators = {
return opt ? RED._("validator.errors.invalid-regexp") : false;
};
},
typedInput: function(ptypeName,isConfig,mopt) {
typedInput: function(ptypeName, isConfig, mopt) {
let options = ptypeName
if (typeof ptypeName === 'string' ) {
options = {}
options.typeField = ptypeName
options.isConfig = isConfig
options.allowBlank = false
}
return function(v, opt) {
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
if (ptype === 'json') {
try {
JSON.parse(v);
return true;
} catch(err) {
if (opt && opt.label) {
return RED._("validator.errors.invalid-json-prop", {
error: err.message,
prop: opt.label,
});
}
return opt ? RED._("validator.errors.invalid-json", {
error: err.message
}) : false;
}
} else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) {
if (RED.utils.validatePropertyExpression(v)) {
return true;
}
if (opt && opt.label) {
return RED._("validator.errors.invalid-prop-prop", {
prop: opt.label
});
}
return opt ? RED._("validator.errors.invalid-prop") : false;
} else if (ptype === 'num') {
if (/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v)) {
return true;
}
if (opt && opt.label) {
return RED._("validator.errors.invalid-num-prop", {
prop: opt.label
});
}
return opt ? RED._("validator.errors.invalid-num") : false;
let ptype = options.type
if (!ptype && options.typeField) {
ptype = $("#node-"+(options.isConfig?"config-":"")+"input-"+options.typeField).val() || this[options.typeField];
}
return true;
};
if (options.allowBlank && v === '') {
return true
}
if (options.allowUndefined && v === undefined) {
return true
}
const result = RED.utils.validateTypedProperty(v, ptype, opt)
if (result === true || opt) {
// Valid, or opt provided - return result as-is
return result
}
// No opt - need to return false for backwards compatibilty
return false
}
}
};
};

View File

@@ -114,6 +114,7 @@
pointer-events: stroke;
}
.red-ui-flow-group-outline-select {
cursor: move;
fill: none;
stroke: var(--red-ui-node-selected-color);
pointer-events: none;

View File

@@ -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?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 }}">
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css?v={{ cacheBuster }}">
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css?v={{ cacheBuster }}">
<link rel="stylesheet" href="red/style.min.css?v={{ cacheBuster }}">
{{#page.css}}
<link rel="stylesheet" href="{{.}}">
{{/page.css}}
{{#asset.vendorMonaco}}
<link rel="stylesheet" href="vendor/monaco/style.css?v={{ page.version }}">
<link rel="stylesheet" href="vendor/monaco/style.css?v={{ cacheBuster }}">
{{/asset.vendorMonaco}}
</head>
<body spellcheck="false">
<div id="red-ui-editor"></div>
<script src="vendor/vendor.js?v={{ page.version }}"></script>
<script src="vendor/vendor.js?v={{ cacheBuster }}"></script>
{{#asset.vendorMonaco}}
<script src="{{ asset.vendorMonaco }}?v={{ page.version }}"></script>
<script src="{{ asset.vendorMonaco }}?v={{ cacheBuster }}"></script>
{{/asset.vendorMonaco}}
<script src="{{ asset.red }}?v={{ page.version }}"></script>
<script src="{{ asset.main }}?v={{ page.version }}"></script>
<script src="{{ asset.red }}?v={{ cacheBuster }}"></script>
<script src="{{ asset.main }}?v={{ cacheBuster }}"></script>
{{# page.scripts }}
<script src="{{.}}"></script>
{{/ page.scripts }}

View File

@@ -320,7 +320,7 @@
}
// but replace with repeat one if set to repeat
if ((this.repeat && this.repeat != 0) || this.crontab) {
suffix = " ↻";
suffix = "\t↻";
}
if (this.name) {
return this.name+suffix;

View File

@@ -109,9 +109,8 @@ module.exports = function(RED) {
}
const p = props.shift()
const property = p.p;
const value = p.v ? p.v : '';
const valueType = p.vt ? p.vt : 'str';
const value = p.v !== undefined ? p.v : '';
const valueType = p.vt !== undefined ? p.vt : 'str';
if (property) {
if (valueType === "jsonata") {
if (p.v) {

View File

@@ -86,7 +86,7 @@
},
label: function() {
var suffix = "";
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
if (this.console === true || this.console === "true") { suffix = "\t⇲"; }
if (this.targetType === "jsonata") {
return (this.name || "JSONata") + suffix;
}
@@ -195,6 +195,119 @@
node.dirty = true;
});
RED.view.redraw();
},
requestDebugNodeList: function(filteredNodes) {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
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;
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: {
id: node.id
},
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
if (subWindow) {
try {
subWindow.postMessage({event:"refreshDebugNodeList", nodes:data},"*");
} catch(err) {
console.log(err);
}
}
RED.debug.refreshDebugNodeList(data)
}
};
@@ -396,6 +509,26 @@
}
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
$("#red-ui-sidebar-debug-open").on("click", function(e) {
e.preventDefault();
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
@@ -427,6 +560,8 @@
options.messageSourceClick(msg.id,msg._alias,msg.path);
} else if (msg.event === "clear") {
options.clear();
} else if (msg.event === "requestDebugNodeList") {
options.requestDebugNodeList(msg.filteredNodes)
}
};
window.addEventListener('message',this.handleWindowMessage);

View File

@@ -275,7 +275,7 @@
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
if (((this.linkType || "static") === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}

View File

@@ -167,19 +167,13 @@ RED.debug = (function() {
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)
}
setFilterType(item.value)
if (filterType === 'filterSelected') {
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
@@ -254,131 +248,7 @@ RED.debug = (function() {
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
function refreshDebugNodeList() {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
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;
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]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
function refreshDebugNodeList(data) {
debugNodeTreeList.treeList("data", data);
}
@@ -401,7 +271,7 @@ RED.debug = (function() {
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) {
if (typeof _activeWorkspace === 'string') {
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
}
if (filterType === "filterAll") {
@@ -479,12 +349,12 @@ RED.debug = (function() {
filteredNodes[n.id] = true;
});
delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
setFilterType('filterSelected')
refreshMessageList();
}},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
$("#red-ui-sidebar-debug-filterAll").trigger("click");
clearFilterSettings()
refreshMessageList();
}}
);
@@ -713,9 +583,17 @@ RED.debug = (function() {
if (!!clearFilter) {
clearFilterSettings();
}
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
}
function setFilterType(type) {
if (type !== filterType) {
filterType = type;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
}
function clearFilterSettings() {
filteredNodes = {};
filterType = 'filterAll';
@@ -728,6 +606,7 @@ RED.debug = (function() {
init: init,
refreshMessageList:refreshMessageList,
handleDebugMessage: handleDebugMessage,
clearMessageList: clearMessageList
clearMessageList: clearMessageList,
refreshDebugNodeList: refreshDebugNodeList
}
})();

View File

@@ -12,6 +12,9 @@ $(function() {
},
clear: function() {
window.opener.postMessage({event:"clear"},'*');
},
requestDebugNodeList: function(filteredNodes) {
window.opener.postMessage({event: 'requestDebugNodeList', filteredNodes},'*')
}
}
@@ -26,6 +29,8 @@ $(function() {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
} else if (evt.data.event === "refreshDebugNodeList") {
RED.debug.refreshDebugNodeList(evt.data.nodes)
}
},false);
} catch(err) {

View File

@@ -167,7 +167,35 @@
label:RED._("node-red:common.label.payload"),
validate: RED.validators.typedInput("propertyType", false)},
propertyType: { value:"msg" },
rules: {value:[{t:"eq", v:"", vt:"str"}]},
rules: {
value:[{t:"eq", v:"", vt:"str"}],
validate: function (rules, opt) {
let msg;
const errors = []
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
const opt = { label: RED._('node-red:switch.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.t !== 'istype') {
if (r.hasOwnProperty('v')) {
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
errors.push(msg)
}
}
if (r.hasOwnProperty('v2')) {
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
errors.push(msg)
}
}
}
}
if (errors.length) {
console.log(errors)
return errors
}
return true;
}
},
checkall: {value:"true", required:true},
repair: {value:false},
outputs: {value:1}

View File

@@ -19,71 +19,42 @@
<script type="text/javascript">
(function() {
function isInvalidProperty(v,vt) {
if (/msg|flow|global/.test(vt)) {
if (!RED.utils.validatePropertyExpression(v)) {
return RED._("node-red:change.errors.invalid-prop", {
property: v
});
}
} else if (vt === "jsonata") {
try{ jsonata(v); } catch(e) {
return RED._("node-red:change.errors.invalid-expr", {
error: e.message
});
}
} else if (vt === "json") {
try{ JSON.parse(v); } catch(e) {
return RED._("node-red:change.errors.invalid-json-data", {
error: e.message
});
}
}
return false;
}
RED.nodes.registerType('change', {
color: "#E2D96E",
category: 'function',
defaults: {
name: {value:""},
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) {
var msg;
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
var r = rules[i];
if (r.t === 'set') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
rules:{
value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],
validate: function(rules, opt) {
let msg;
const errors = []
if (!rules || rules.length === 0) { return true }
for (var i=0;i<rules.length;i++) {
const opt = { label: RED._('node-red:change.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.t === 'set' || r.t === 'change' || r.t === 'delete' || r.t === 'move') {
if ((msg = RED.utils.validateTypedProperty(r.p,r.pt,opt)) !== true) {
errors.push(msg)
}
}
if (msg = isInvalidProperty(r.to,r.tot)) {
return msg;
if (r.t === 'set' || r.t === 'change' || r.t === 'move') {
if ((msg = RED.utils.validateTypedProperty(r.to,r.tot,opt)) !== true) {
errors.push(msg)
}
}
} else if (r.t === 'change') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
if(msg = isInvalidProperty(r.from,r.fromt)) {
return msg;
}
if(msg = isInvalidProperty(r.to,r.tot)) {
return msg;
}
} else if (r.t === 'delete') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
} else if (r.t === 'move') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
if (msg = isInvalidProperty(r.to,r.tot)) {
return msg;
if (r.t === 'change') {
if ((msg = RED.utils.validateTypedProperty(r.from,r.fromt,opt)) !== true) {
errors.push(msg)
}
}
}
if (errors.length) {
return errors
}
return true;
}
return true;
}},
},
// legacy
action: {value:""},
property: {value:""},

View File

@@ -57,7 +57,9 @@
action: {value:"scale"},
round: {value:false},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })
},
name: {value:""}
},
inputs: 1,

View File

@@ -153,7 +153,7 @@
}
var editorRow = $("#dialog-form>div.node-text-editor-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$(".node-text-editor").css("height",height+"px");
$("#dialog-form .node-text-editor").css("height",height+"px");
this.editor.resize();
}
});

View File

@@ -284,7 +284,7 @@ module.exports = function(RED) {
done();
}
}
else {
else if (!msg.hasOwnProperty("reset")) {
if (maxKeptMsgsCount(node) > 0) {
if (node.intervalID === -1) {
node.send(msg);

View File

@@ -56,7 +56,7 @@
color:"darksalmon",
defaults: {
command: {value:""},
addpay: {value:""},
addpay: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
append: {value:""},
useSpawn: {value:"false"},
timer: {value:""},

View File

@@ -56,9 +56,11 @@
inout: {value:"out"},
septopics: {value:true},
property: {value:"payload", required:true,
label:RED._("node-red:rbe.label.property")},
label:RED._("node-red:rbe.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
topi: {value:"topic", required:true,
label:RED._("node-red:rbe.label.topic")}
label:RED._("node-red:rbe.label.topic"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })}
},
inputs:1,
outputs:1,

View File

@@ -104,6 +104,7 @@ module.exports = function(RED) {
* @returns `true` if it is a valid topic
*/
function isValidPublishTopic(topic) {
if (topic.length === 0) return false;
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
}
@@ -219,8 +220,8 @@ module.exports = function(RED) {
*/
function subscriptionHandler(node, datatype ,topic, payload, packet) {
const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
: Object.prototype.hasOwnProperty.call(packet, "properties");
if(v5 && packet.properties) {
setStrProp(packet.properties, msg, "responseTopic");
@@ -451,7 +452,7 @@ module.exports = function(RED) {
/**
* Perform the disconnect action
* @param {MQTTInNode|MQTTOutNode} node
* @param {MQTTInNode|MQTTOutNode} node
* @param {Function} done
*/
function handleDisconnectAction(node, done) {
@@ -611,7 +612,7 @@ module.exports = function(RED) {
node.brokerurl = node.url;
} else {
// if the broker is ws:// or wss:// or tcp://
if (node.broker.indexOf("://") > -1) {
if ((typeof node.broker === 'string') && node.broker.indexOf("://") > -1) {
node.brokerurl = node.broker;
// Only for ws or wss, check if proxy env var for additional configuration
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
@@ -865,7 +866,7 @@ module.exports = function(RED) {
* Call end and wait for the client to end (or timeout)
* @param {mqtt.MqttClient} client The broker client
* @param {number} ms The time to wait for the client to end
* @returns
* @returns
*/
let waitEnd = (client, ms) => {
return new Promise( (resolve, reject) => {
@@ -905,7 +906,7 @@ module.exports = function(RED) {
node.subid = 1;
//typedef for subscription object:
/**
/**
* @typedef {Object} Subscription
* @property {String} topic - topic to subscribe to
* @property {Object} [options] - options object
@@ -933,7 +934,7 @@ module.exports = function(RED) {
const ref = _ref || 0;
let options
let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2)
// if options is an object, then clone it
if (typeof _options == "object") {
options = RED.util.cloneMessage(_options || {})
@@ -947,7 +948,7 @@ module.exports = function(RED) {
if (typeof qos === "number" && qos >= 0 && qos <= 2) {
options.qos = qos;
}
subscription.topic = _topic;
subscription.qos = qos;
subscription.options = RED.util.cloneMessage(options);
@@ -957,16 +958,16 @@ module.exports = function(RED) {
}
/**
* If topic is a subscription object, then use that, otherwise look up the topic in
* If topic is a subscription object, then use that, otherwise look up the topic in
* the subscriptions object. If the topic is not found, then create a new subscription
* object and add it to the subscriptions object.
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
*/
node.subscribe = function (topic, options, callback, ref) {
/** @type {Subscription} */
/** @type {Subscription} */
let subscription
let doCompare = false
let changesFound = false
@@ -1004,7 +1005,7 @@ module.exports = function(RED) {
_brokerConn.unsubscribe(sub.topic, sub.ref, true)
}
})
// if subscription is found (or sent in as a parameter), then check for changes.
// if there are any changes requested, tidy up the old subscription
if (subscription) {
@@ -1091,7 +1092,7 @@ module.exports = function(RED) {
delete sub[ref]
}
}
// if instructed to remove the actual MQTT client subscription
// if instructed to remove the actual MQTT client subscription
if (unsub) {
// if there are no more subscriptions for the topic, then remove the topic
if (Object.keys(sub).length === 0) {

View File

@@ -63,7 +63,7 @@ module.exports = function(RED) {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
}
var ou = "";
const ou = [];
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((template.length === 1) && (template[0] === '')) {
@@ -74,7 +74,7 @@ module.exports = function(RED) {
template = Object.keys(msg.payload[0]);
}
}
ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret;
ou.push(template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep));
if (node.hdrout === "once") { node.hdrSent = true; }
}
for (var s = 0; s < msg.payload.length; s++) {
@@ -93,7 +93,7 @@ module.exports = function(RED) {
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
}
}
ou += msg.payload[s].join(node.sep) + node.ret;
ou.push(msg.payload[s].join(node.sep));
}
else {
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
@@ -105,6 +105,7 @@ module.exports = function(RED) {
node.warn(RED._("csv.errors.obj_csv"));
tmpwarn = false;
}
const row = [];
for (var p in msg.payload[0]) {
/* istanbul ignore else */
if (msg.payload[s].hasOwnProperty(p)) {
@@ -118,21 +119,22 @@ module.exports = function(RED) {
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""');
ou += node.quo + q + node.quo + node.sep;
row.push(node.quo + q + node.quo);
}
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
ou += node.quo + q + node.quo + node.sep;
row.push(node.quo + q + node.quo);
}
else { ou += q + node.sep; } // otherwise just add
else { row.push(q); } // otherwise just add
}
}
}
ou = ou.slice(0,-1) + node.ret;
ou.push(row.join(node.sep)); // add separator
}
else {
const row = [];
for (var t=0; t < template.length; t++) {
if (template[t] === '') {
ou += node.sep;
row.push('');
}
else {
var tt = template[t];
@@ -146,19 +148,20 @@ module.exports = function(RED) {
p = RED.util.ensureString(p);
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
p = p.replace(/"/g, '""');
ou += node.quo + p + node.quo + node.sep;
row.push(node.quo + p + node.quo);
}
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
ou += node.quo + p + node.quo + node.sep;
row.push(node.quo + p + node.quo);
}
else { ou += p + node.sep; } // otherwise just add
else { row.push(p); } // otherwise just add
}
}
ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
ou.push(row.join(node.sep)); // add separator
}
}
}
msg.payload = ou;
// join lines, don't forget to add the last new line
msg.payload = ou.join(node.ret) + node.ret;
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
if (msg.payload !== '') { send(msg); }
done();

View File

@@ -41,8 +41,8 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
property: {value:"payload"},
outproperty: {value:"payload"},
property: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
outproperty: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
tag: {value:""},
ret: {value:"html"},
as: {value:"single"}

View File

@@ -32,6 +32,7 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true}),
label:RED._("node-red:json.label.property")},
action: {value:""},
pretty: {value:false}

View File

@@ -27,7 +27,8 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
attr: {value:""},
chr: {value:""}
},

View File

@@ -16,6 +16,7 @@
color:"#DEBD5C",
defaults: {
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }),
label:RED._("node-red:common.label.property")},
name: {value:""}
},

View File

@@ -57,7 +57,7 @@
arraySplt: {value:1},
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:""}
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })}
},
inputs:1,
outputs:1,
@@ -208,7 +208,22 @@
validate:RED.validators.typedInput("propertyType", false)
},
propertyType: { value:"msg"},
key: {value:"topic"},
key: {value:"topic", validate: (function () {
const typeValidator = RED.validators.typedInput({ type: 'msg' })
return function(v, opt) {
const joinMode = $("#node-input-mode").val() || this.mode
if (joinMode !== 'custom') {
return true
}
const buildType = $("#node-input-build").val() || this.build
if (buildType !== 'object') {
return true
} else {
return typeValidator(v, opt)
}
}
})()
},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },

View File

@@ -198,7 +198,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' })},
filenameType: {value:"str"},
appendNewline: {value:true},
createDir: {value:false},
@@ -297,7 +297,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' }) },
filenameType: {value:"str"},
format: {value:"utf8"},
chunk: {value:false},

View File

@@ -36,5 +36,5 @@
<p><b>Remarque</b> : Les options <i>"Intervalle entre les heures"</i> et <i>"à une heure précise"</i> utilisent le système cron standard.
Cela signifie que pour la première option, vous pouvez envoyer un message à intervalle régulier entre les heures voulues.
Si vous voulez envoyer un message toutes les minutes à partir de maintenant, utiliser l'option <i>"intervalle"</i>.</p>
<p><b>Remarque</b> : Pour inclure une nouvelle ligne dans une chaîne, vous devez utiliser un noeud de fonction pour créer la charge utile.</p>
<p><b>Remarque</b> : Pour inclure une nouvelle ligne dans une chaîne, vous devez utiliser soit un noeud de fonction soit le noeud template pour créer la charge utile.</p>
</script>

View File

@@ -11,11 +11,11 @@
"expand": "Développer"
},
"status": {
"connected": "connecté",
"not-connected": "pas connecté",
"disconnected": "déconnecté",
"connecting": "connexion",
"error": "erreur",
"connected": "Connecté",
"not-connected": "Pas connecté",
"disconnected": "Déconnecté",
"connecting": "Connexion",
"error": "Erreur",
"ok": "OK"
},
"notification": {
@@ -32,7 +32,7 @@
},
"inject": {
"inject": "Injecter",
"injectNow": "injecter maintenant",
"injectNow": "Injecter maintenant",
"repeat": "répéter = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "arrêté",
@@ -98,7 +98,7 @@
"catchUncaught": "catch : non capturé",
"label": {
"source": "Détecter les erreurs de",
"selectAll": "tout sélectionner",
"selectAll": "Tout sélectionner",
"uncaught": "Ignorer les erreurs gérées par les autres noeuds Catch"
},
"scope": {
@@ -112,7 +112,7 @@
"statusNodes": "statut : __number__",
"label": {
"source": "Signaler l'état de",
"sortByType": "trier par type"
"sortByType": "Trier par type"
},
"scope": {
"all": "tous les noeuds",
@@ -148,23 +148,23 @@
"deactivated": "Désactivé avec succès : __label__"
},
"sidebar": {
"label": "débogage",
"label": "Débogage",
"name": "Messages de débogage",
"filterAll": "tous les noeuds",
"filterSelected": "noeuds sélectionnés",
"filterCurrent": "flux actuel",
"filterAll": "Tous les noeuds",
"filterSelected": "Noeuds sélectionnés",
"filterCurrent": "Flux actuel",
"debugNodes": "noeuds de débogage",
"clearLog": "Effacer les messages",
"clearFilteredLog": "Effacer les messages filtrés",
"clearLog": "Tous les messages",
"clearFilteredLog": "Les messages filtrés",
"filterLog": "Filtrer les messages",
"openWindow": "Ouvrir dans une nouvelle fenêtre",
"copyPath": "Copier le chemin",
"copyPayload": "Copier la valeur",
"pinPath": "Épingler le chemin",
"selectAll": "tout sélectionner",
"selectNone": "ne rien sélectionner",
"all": "tout",
"filtered": "filtré"
"selectAll": "Tout sélectionner",
"selectNone": "Ne rien sélectionner",
"all": "Tout",
"filtered": "Filtrés"
},
"messageMenu": {
"collapseAll": "Réduire tous les chemins",
@@ -177,11 +177,11 @@
"linkIn": "Lien entrant",
"linkOut": "Lien sortant",
"linkCall": "Appel de lien",
"linkOutReturn": "retour de lien",
"linkOutReturn": "Retour de lien",
"outMode": "Mode",
"sendToAll": "Envoyer à tous les noeuds de liaison connectés",
"returnToCaller": "Retour au noeud de liaison appelant",
"timeout": "temps mort",
"timeout": "Temps mort",
"linkCallType": "Type de liaison",
"staticLinkCall": "Lien fixe",
"dynamicLinkCall": "Lien dynamique (msg.target)",
@@ -225,7 +225,7 @@
"command": "Commande",
"append": "Joindre",
"timeout": "Temps mort",
"timeoutplace": "facultatif",
"timeoutplace": "Facultatif",
"return": "Sortie",
"seconds": "secondes",
"stdout": "stdout",
@@ -234,7 +234,7 @@
"winHide": "Masquer la console"
},
"placeholder": {
"extraparams": "paramètres d'entrée supplémentaires"
"extraparams": "Paramètres d'entrée supplémentaires"
},
"opt": {
"exec": "lorsque la commande est terminée - mode exec",
@@ -319,7 +319,7 @@
"queuemsg": "Mettre en file d'attente les messages intermédiaires",
"dropmsg": "Supprimer les messages intermédiaires",
"sendmsg": "Envoyer les messages intermédiaires sur la 2ème sortie",
"allowrate": "autoriser msg.rate (en ms) à remplacer le débit",
"allowrate": "Autoriser msg.rate (en ms) à remplacer le débit",
"label": {
"delay": "retard",
"variable": "variable",
@@ -349,7 +349,7 @@
}
},
"errors": {
"too-many": "trop de messages en attente dans le noeud 'Delay'",
"too-many": "Trop de messages en attente dans le noeud 'Delay'",
"invalid-timeout": "Valeur de délai invalide",
"invalid-rate": "Valeur de taux invalide",
"invalid-rate-unit": "Valeur de débit invalide",
@@ -359,8 +359,8 @@
},
"trigger": {
"send": "Envoyer",
"then": "puis",
"then-send": "puis envoyer",
"then": "Puis",
"then-send": "Puis envoyer",
"output": {
"string": "la chaîne",
"number": "le nombre",
@@ -381,9 +381,9 @@
"m": "Minutes",
"h": "Heures"
},
"extend": " prolonger le délai si un nouveau message arrive",
"override": "remplacer le délai avec msg.delay",
"second": " envoyer un deuxième message à une sortie séparée",
"extend": " Prolonger le délai si un nouveau message arrive",
"override": "Remplacer le délai avec msg.delay",
"second": " Envoyer un deuxième message à une sortie séparée",
"label": {
"trigger": "déclencher",
"trigger-block": "déclencher et bloquer",
@@ -408,7 +408,7 @@
"mqtt": {
"label": {
"broker": "Serveur",
"example": "par exemple. localhost",
"example": "expl. localhost",
"output": "Sortie",
"qos": "QoS",
"retain": "Conserver",
@@ -438,7 +438,7 @@
"sessionExpiry": "Expiration de la session (secondes)",
"topicAlias": "Alias",
"payloadFormatIndicator": "Formater",
"payloadFormatIndicatorFalse": "octets non spécifiés (par défaut)",
"payloadFormatIndicatorFalse": "Octets non spécifiés (par défaut)",
"payloadFormatIndicatorTrue": "Charge utile encodée en UTF-8",
"protocolVersion": "Protocole",
"protocolVersion3": "MQTT V3.1 (hérité)",
@@ -493,8 +493,8 @@
"false": "faux",
"tip": "Conseil : laisser le sujet, le qos ou le contenu vide si vous souhaitez les définir via les propriétés du msg.",
"errors": {
"not-defined": "sujet non défini",
"missing-config": "configuration du courtier manquante",
"not-defined": "Sujet non défini",
"missing-config": "Configuration du courtier manquante",
"invalid-topic": "Sujet invalide spécifié",
"nonclean-missingclientid": "Aucun ID client défini, utilisation d'une session propre",
"invalid-json-string": "Chaîne JSON invalide",
@@ -514,7 +514,7 @@
"upload": "Accepter les téléchargements de fichiers ?",
"status": "Code d'état",
"headers": "En-têtes",
"other": "autre",
"other": "Autre",
"paytoqs": {
"ignore": "Ignorer",
"query": "Joindre aux paramètres de chaîne de requête",
@@ -625,7 +625,7 @@
"chars": "caractères",
"close": "Fermer",
"optional": "(facultatif)",
"reattach": "rattacher le délimiteur"
"reattach": "Rattacher le délimiteur"
},
"type": {
"listen": "Écoute sur",
@@ -633,8 +633,8 @@
"reply": "Répondre sur TCP"
},
"output": {
"stream": "flux de",
"single": "unique",
"stream": "Flux de",
"single": "Unique",
"buffer": "Tampon",
"string": "Chaîne",
"base64": "Chaîne en Base64"
@@ -657,15 +657,15 @@
"connections_plural": "__count__ connexions"
},
"errors": {
"connection-lost": "connexion perdue avec __host__:__port__",
"timeout": "délai d'expiration du port __port__ du socket fermé",
"cannot-listen": "impossible d'écouter sur le port __port__, erreur : __error__",
"error": "erreur : __error__",
"socket-error": "erreur de courtier depuis __host__:__port__",
"connection-lost": "Connexion perdue avec __host__:__port__",
"timeout": "Délai d'expiration du port __port__ du socket fermé",
"cannot-listen": "Impossible d'écouter sur le port __port__, erreur : __error__",
"error": "Erreur : __error__",
"socket-error": "Erreur de courtier depuis __host__:__port__",
"no-host": "Hôte et/ou port non défini",
"connect-timeout": "délai de connexion",
"connect-fail": "la connexion a échoué",
"bad-string": "échec de la conversion en chaîne",
"connect-timeout": "Délai de connexion",
"connect-fail": "La connexion a échoué",
"bad-string": "Échec de la conversion en chaîne",
"invalid-host": "Hôte invalide",
"invalid-port": "Port invalide"
}
@@ -722,7 +722,7 @@
},
"errors": {
"access-error": "Erreur d'accès UDP, vous aurez peut-être besoin d'un accès root pour les ports inférieurs à 1024",
"error": "erreur : __erreur__",
"error": "Erreur : __erreur__",
"bad-mcaddress": "Mauvaise adresse de multidiffusion",
"interface": "Doit être l'adresse IP de l'interface requise",
"ip-notset": "udp : adresse IP non définie",
@@ -730,7 +730,7 @@
"port-invalid": "udp : numéro de port non valide",
"alreadyused": "udp : port __port__ déjà utilisé",
"ifnotfound": "udp : interface __iface__ introuvable",
"invalid-group": "groupe de multidiffusion invalide"
"invalid-group": "Groupe de multidiffusion invalide"
}
},
"switch": {
@@ -738,15 +738,15 @@
"label": {
"property": "Propriété",
"rule": "règle",
"repair": "recréer des séquences du messages",
"value-rules": "règles de valeur",
"sequence-rules": "règles de séquence"
"repair": "Recréer des séquences du messages",
"value-rules": "Règles de valeur",
"sequence-rules": "Règles de séquence"
},
"previous": "valeur précédente",
"and": "et",
"checkall": "vérifier toutes les règles",
"stopfirst": "arrêter après la première concordance",
"ignorecase": "ignorer la casse",
"checkall": "Vérifier toutes les règles",
"stopfirst": "Arrêter après la première concordance",
"ignorecase": "Ignorer la casse",
"rules": {
"btwn": "est entre",
"cont": "contient",
@@ -767,7 +767,7 @@
},
"errors": {
"invalid-expr": "Expression JSONata non valide : __error__",
"too-many": "trop de messages en attente dans le noeud de commutation"
"too-many": "Trop de messages en attente dans le noeud de commutation"
}
},
"change": {
@@ -840,13 +840,13 @@
"entrée": "Entrée",
"skip-s": "Passer en premier",
"skip-e": "lignes",
"firstrow": "la première ligne contient les noms des colonnes",
"firstrow": "La première ligne contient les noms des colonnes",
"output": "Sortie",
"includerow": "inclure la ligne du nom de la colonne",
"includerow": "Inclure la ligne du nom de la colonne",
"newline": "Nouvelle ligne",
"usestrings": "analyser les valeurs numériques",
"include_empty_strings": "inclure les chaînes vides",
"include_null_values": "inclure les valeurs nulles"
"usestrings": "Analyser les valeurs numériques",
"include_empty_strings": "Inclure les chaînes vides",
"include_null_values": "Inclure les valeurs nulles"
},
"placeholder": {
"columns": "noms de colonnes séparés par des virgules"
@@ -936,8 +936,8 @@
},
"file": {
"label": {
"write": "écrire le fichier",
"read": "lire le fichier",
"write": "Écrire le fichier",
"read": "Lire le fichier",
"filename": "Nom du fichier",
"path": "chemin",
"action": "Action",
@@ -972,8 +972,8 @@
"appendedfile": "ajouté au fichier : __file__"
},
"encoding": {
"none": "par défaut",
"setbymsg": "défini par msg.encoding",
"none": "Par défaut",
"setbymsg": "Défini par msg.encoding",
"native": "Natif",
"unicode": "Unicode",
"japanese": "Japonais",
@@ -990,10 +990,10 @@
"errors": {
"nofilename": "Aucun nom de fichier spécifié",
"invaliddelete": "Attention : suppression non valide. Veuiller utiliser une option de suppression spécifique dans la boîte de dialogue de configuration.",
"deletefail": "échec de la suppression du fichier : __error__",
"writefail": "échec de l'écriture dans le fichier : __error__",
"appendfail": "échec de l'ajout au fichier : __error__",
"createfail": "échec de la création du fichier : __error__"
"deletefail": "Échec de la suppression du fichier : __error__",
"writefail": "Échec de l'écriture dans le fichier : __error__",
"appendfail": "Échec de l'ajout au fichier : __error__",
"createfail": "Échec de la création du fichier : __error__"
},
"tip": "Astuce : Le nom du fichier doit être un chemin absolu, sinon il sera relatif au répertoire de travail du processus Node-RED."
},
@@ -1018,9 +1018,9 @@
"reduce": "réduire la séquence",
"custom": "manuel"
},
"combine": "Combine each",
"combine": "Combiner chaque",
"completeMessage": "message complet",
"create": "créer",
"create": "Créer",
"type": {
"string": "une Chaîne",
"array": "un Tableau",
@@ -1028,13 +1028,13 @@
"object": "un Objet clé/valeur",
"merged": "un Objet fusionné"
},
"using": "en utilisant la valeur de",
"key": "comme la clé",
"using": "En utilisant la valeur du",
"key": "comme clé",
"joinedUsing": "joint en utilisant",
"send": "Envoyer le message :",
"afterCount": "Après un certain nombre de parties du message",
"count": "compter",
"subsequent": "et tous les messages suivants.",
"afterCount": "Après un nombre de parties du message",
"count": "nombre",
"subsequent": "Et tous les messages suivants.",
"afterTimeout": "Après un délai d'attente après le premier message",
"seconds": "secondes",
"complete": "Après un message avec la propriété <code>msg.complete</code> définie",
@@ -1068,10 +1068,10 @@
"order": "Sens",
"ascending": "croissant",
"descending": "descendant",
"as-number": "comme nombre",
"as-number": "Comme nombre",
"invalid-exp": "Expression JSONata invalide dans le noeud sort: __message__",
"too-many": "Trop de messages en attente dans le noeud sort",
"clear": "effacer le message en attente dans le noeud sort"
"clear": "Effacer le message en attente dans le noeud sort"
},
"batch": {
"batch": "Regrouper",
@@ -1084,21 +1084,21 @@
"count": {
"label": "Nombre de messages",
"overlap": "Chevauchement",
"count": "compter",
"count": "nombre",
"invalid": "Comptage et chevauchement invalides"
},
"interval": {
"label": "Intervalle",
"seconds": "secondes",
"empty": "envoyer un message vide lorsqu'aucun message n'arrive"
"empty": "Envoyer un message vide lorsqu'aucun message n'arrive"
},
"concat": {
"topics-label": "Sujets",
"topic": "sujet"
},
"too-many": "trop de messages en attente dans le noeud batch",
"unexpected": "mode inattendu",
"no-parts": "aucune propriété de pièces dans le message",
"too-many": "Trop de messages en attente dans le noeud batch",
"unexpected": "Mode inattendu",
"no-parts": "Aucune propriété de pièces dans le message",
"error": {
"invalid-count": "Compte invalide",
"invalid-overlap": "Recouvrement invalide",
@@ -1132,7 +1132,7 @@
"out": "par rapport à la dernière valeur de sortie valide"
},
"warn": {
"nonumber": "aucun numéro trouvé dans la charge utile"
"nonumber": "Aucun numéro trouvé dans la charge utile"
}
},
"global-config": {

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "3.1.0",
"version": "3.1.3",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -98,7 +98,7 @@ function requireModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return require(parsedModule.module);
return require(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -131,7 +131,7 @@ function importModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return import(parsedModule.module);
return import(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -152,12 +152,13 @@ function importModule(module) {
}
function parseModuleName(module) {
var match = /((?:@[^/]+\/)?[^/@]+)(?:@([\s\S]+))?/.exec(module);
var match = /((?:@[^/]+\/)?[^/@]+)(\/[^/@]+)?(?:@([\s\S]+))?/.exec(module);
if (match) {
return {
spec: module,
module: match[1],
version: match[2],
subpath: match[2] || '',
version: match[3],
builtin: BUILTIN_MODULES.indexOf(match[1]) !== -1,
known: !!knownExternalModules[match[1]]
}
@@ -283,6 +284,7 @@ async function installModule(moduleDetails) {
const runtimeInstalledModules = settings.get("modules") || {};
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
settings.set("modules",runtimeInstalledModules)
log.audit({event: "modules.install",module:moduleDetails.module, version:moduleDetails.version});
}).catch(result => {
var output = result.stderr || result.toString();
var e;

View File

@@ -143,6 +143,12 @@ function loadModuleFiles(modules) {
return loadNodeSetList(pluginList);
}).then(function() {
return loadNodeSetList(nodeList);
}).then(function () {
if (settings.available()) {
return registry.saveNodeList();
} else {
return
}
})
}
@@ -436,7 +442,7 @@ async function loadPlugin(plugin) {
return plugin;
}
}
let invocation = 0
function loadNodeSetList(nodes) {
var promises = [];
nodes.forEach(function(node) {
@@ -451,13 +457,7 @@ function loadNodeSetList(nodes) {
}
});
return Promise.all(promises).then(function() {
if (settings.available()) {
return registry.saveNodeList();
} else {
return;
}
});
return Promise.all(promises)
}
function addModule(module) {

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "3.1.0",
"version": "3.1.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,7 +16,7 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0",
"@node-red/util": "3.1.3",
"clone": "2.1.2",
"fs-extra": "11.1.1",
"semver": "7.5.4",

View File

@@ -99,6 +99,9 @@ var api = module.exports = {
safeSettings.markdownEditor = runtime.settings.editorTheme.markdownEditor || {};
safeSettings.markdownEditor.mermaid = safeSettings.markdownEditor.mermaid || { enabled: true };
}
if (runtime.settings.editorTheme.mermaid) {
safeSettings.mermaid = runtime.settings.editorTheme.mermaid
}
}
safeSettings.libraries = runtime.library.getLibraries();
if (util.isArray(runtime.settings.paletteCategories)) {

View File

@@ -161,7 +161,8 @@ class Flow {
for (let i = 0; i < configNodes.length; i++) {
const node = this.flow.configs[configNodes[i]]
if (node.type === 'global-config' && node.env) {
const nodeEnv = await flowUtil.evaluateEnvProperties(this, node.env, credentials.get(node.id))
const globalCreds = credentials.get(node.id)?.map || {}
const nodeEnv = await flowUtil.evaluateEnvProperties(this, node.env, globalCreds)
this._env = { ...this._env, ...nodeEnv }
}
}

View File

@@ -73,9 +73,20 @@ class Subflow extends Flow {
id: subflowInstance.id,
configs: {},
nodes: {},
groups: {},
subflows: {}
}
if (subflowDef.groups) {
// Clone all of the subflow group definitions and give them new IDs
for (i in subflowDef.groups) {
if (subflowDef.groups.hasOwnProperty(i)) {
node = createNodeInSubflow(subflowInstance.id,subflowDef.groups[i]);
node_map[node._alias] = node;
subflowInternalFlowConfig.groups[node.id] = node;
}
}
}
if (subflowDef.configs) {
// Clone all of the subflow config node definitions and give them new IDs
for (i in subflowDef.configs) {
@@ -101,6 +112,7 @@ class Subflow extends Flow {
remapSubflowNodes(subflowInternalFlowConfig.configs,node_map);
remapSubflowNodes(subflowInternalFlowConfig.nodes,node_map);
remapSubflowNodes(subflowInternalFlowConfig.groups,node_map);
// console.log("Instance config\n",JSON.stringify(subflowInternalFlowConfig,"",2));
@@ -237,7 +249,7 @@ class Subflow extends Flow {
for (j=0;j<wires.length;j++) {
if (wires[j].id != self.subflowDef.id) {
node = self.node_map[wires[j].id];
if (node._originalWires) {
if (node && node._originalWires) {
node.wires = clone(node._originalWires);
}
}
@@ -254,8 +266,10 @@ class Subflow extends Flow {
subflowInstanceModified = true;
} else {
node = self.node_map[wires[j].id];
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
modifiedNodes[node.id] = node;
if (node) {
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
modifiedNodes[node.id] = node;
}
}
}
}
@@ -283,10 +297,14 @@ class Subflow extends Flow {
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
if (!node._originalWires) {
node._originalWires = clone(node.wires);
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
} else {
this.error("Unknown node referenced inside subflow: " + wires[j].id)
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
}
}
}
@@ -302,11 +320,15 @@ class Subflow extends Flow {
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
if (!node._originalWires) {
node._originalWires = clone(node.wires);
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
} else {
this.error("Unknown node referenced inside subflow: " + wires[j].id)
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
}
}
}

View File

@@ -57,18 +57,20 @@ var EnvVarPropertyRE = /^\${(\S+)}$/;
function mapEnvVarProperties(obj,prop,flow,config) {
var v = obj[prop];
const v = obj[prop];
if (Buffer.isBuffer(v)) {
return;
} else if (Array.isArray(v)) {
for (var i=0;i<v.length;i++) {
for (let i=0;i<v.length;i++) {
mapEnvVarProperties(v,i,flow,config);
}
} else if (typeof obj[prop] === 'string') {
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
var envVar = v.substring(2,v.length-1);
var r = redUtil.getSetting(config, envVar, flow);
obj[prop] = r ? r : obj[prop];
const envVar = v.substring(2,v.length-1);
const r = redUtil.getSetting(config, envVar, flow);
if (r !== undefined && r !== '') {
obj[prop] = r
}
}
} else {
for (var p in v) {
@@ -80,6 +82,7 @@ function mapEnvVarProperties(obj,prop,flow,config) {
}
async function evaluateEnvProperties(flow, env, credentials) {
credentials = credentials || {}
const pendingEvaluations = []
const evaluatedEnv = {}
const envTypes = []
@@ -112,6 +115,7 @@ async function evaluateEnvProperties(flow, env, credentials) {
if (pendingEvaluations.length > 0) {
await Promise.all(pendingEvaluations)
}
// Now loop over the env types and evaluate them properly
for (let i = 0; i < envTypes.length; i++) {
let { name, value, type } = envTypes[i]
// If an env-var wants to lookup itself, delegate straight to the parent
@@ -122,7 +126,17 @@ async function evaluateEnvProperties(flow, env, credentials) {
if (evaluatedEnv.hasOwnProperty(value)) {
value = evaluatedEnv[value]
} else {
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
value = redUtil.evaluateNodeProperty(value, type, {_flow: {
// Provide a hook so when it tries to look up a flow setting,
// we can insert the just-evaluated value which hasn't yet
// been set on the flow object - otherwise delegate up to the flow
getSetting: function(name) {
if (evaluatedEnv.hasOwnProperty(name)){
return evaluatedEnv[name]
}
return flow.getSetting(name)
}
}}, null, null);
}
evaluatedEnv[name] = value
}

View File

@@ -27,6 +27,7 @@ var express = require("express");
var path = require('path');
var fs = require("fs");
var os = require("os");
const crypto = require("crypto")
const {log,i18n,events,exec,util,hooks} = require("@node-red/util");
@@ -51,7 +52,7 @@ var adminApi = {
var nodeApp;
var adminApp;
var server;
let userSettings;
/**
* Initialise the runtime module.
@@ -61,8 +62,9 @@ var server;
* better abstracted.
* @memberof @node-red/runtime
*/
function init(userSettings,httpServer,_adminApi) {
function init(_userSettings,httpServer,_adminApi) {
server = httpServer;
userSettings = _userSettings
if (server && server.on) {
// Add a listener to the upgrade event so that we can properly timeout connection
@@ -134,7 +136,12 @@ function start() {
.then(function() { return settings.load(storage)})
.then(function() { return library.init(runtime)})
.then(function() {
if (settings.available()) {
if (settings.get('instanceId') === undefined) {
settings.set('instanceId', crypto.randomBytes(8).toString('hex'))
}
userSettings.instanceId = settings.get('instanceId') || ''
}
if (log.metric()) {
runtimeMetricInterval = setInterval(function() {
reportMetrics();

View File

@@ -42,6 +42,7 @@ function Node(n) {
this._closeCallbacks = [];
this._inputCallback = null;
this._inputCallbacks = null;
this._expectedDoneCount = 0;
if (n.name) {
this.name = n.name;
@@ -159,6 +160,9 @@ Node.prototype.on = function(event, callback) {
if (event == "close") {
this._closeCallbacks.push(callback);
} else if (event === "input") {
if (callback.length === 3) {
this._expectedDoneCount++
}
if (this._inputCallback) {
this._inputCallbacks = [this._inputCallback, callback];
this._inputCallback = null;
@@ -218,19 +222,17 @@ Node.prototype._emitInput = function(arg) {
} else if (node._inputCallbacks) {
// Multiple callbacks registered. Call each one, tracking eventual completion
var c = node._inputCallbacks.length;
let doneCount = 0
for (var i=0;i<c;i++) {
var cb = node._inputCallbacks[i];
if (cb.length === 2) {
c++;
}
try {
cb.call(
node,
arg,
function() { node.send.apply(node,arguments) },
function(err) {
c--;
if (c === 0) {
doneCount++;
if (doneCount === node._expectedDoneCount) {
node._complete(arg,err);
}
}
@@ -257,6 +259,9 @@ Node.prototype._removeListener = Node.prototype.removeListener;
Node.prototype.removeListener = function(name, listener) {
var index;
if (name === "input") {
if (listener.length === 3) {
this._expectedDoneCount--
}
if (this._inputCallback && this._inputCallback === listener) {
// Removing the only callback
this._inputCallback = null;

View File

@@ -384,10 +384,27 @@ var api = module.exports = {
}
}
} else if (nodeType === "global-config") {
if (JSON.stringify(savedCredentials.map) !== JSON.stringify(newCreds.map)) {
savedCredentials.map = newCreds.map;
dirty = true;
}
const existingCredentialKeys = Object.keys(savedCredentials?.map || [])
const newCredentialKeys = Object.keys(newCreds?.map || [])
existingCredentialKeys.forEach(key => {
if (!newCreds.map?.[key]) {
// This key doesn't exist in the new credentials list - remove
delete savedCredentials.map[key]
delete savedCredentials.map[`has_${key}`]
dirty = true
}
})
newCredentialKeys.forEach(key => {
if (!/^has_/.test(key)) {
if (!savedCredentials.map?.[key] || newCreds.map[key] !== '__PWRD__') {
// This key either doesn't exist in current saved, or the
// value has been changed
savedCredentials.map[key] = newCreds.map[key]
savedCredentials.map[`has_${key}`] = newCreds.map[`has_${key}`]
dirty = true
}
}
})
} else {
var dashedType = nodeType.replace(/\s+/g, '-');
var definition = credentialsDef[dashedType];

View File

@@ -48,7 +48,7 @@
"port-in-use": "Erreur : port utilisé",
"uncaught-exception": "Exception non reconnue :",
"admin-ui-disabled": "Interface d'administration désactivée",
"now-running": "Le serveur tourne maintenant sur __listenpath__",
"now-running": "Le serveur est disponible à l'adresse __listenpath__",
"failed-to-start": "Échec lors du démarrage du serveur :",
"headless-mode": "Fonctionne en mode sans interface graphique (headless)",
"httpadminauth-deprecated": "L'utilisation de httpAdminAuth est DÉCONSEILLÉE. Utiliser adminAuth à la place",
@@ -100,7 +100,7 @@
"error": "Erreur lors du chargement des identifiants : __message__",
"error-saving": "Erreur lors de l'enregistrement des identifiants : __message__",
"not-registered": "Le type d'identifiant '__type__' n'a pas été enregistré",
"system-key-warning": "\n\n---------------------------------------------------------------------\nVotre fichier contenant les identifiants de flux est chiffré à l'aide d'une clé générée par le système.\n\nSi la clé générée par le système est perdue pour une raison quelconque, votre fichier contenant\nles identifiants ne sera pas récupérable, vous devrez le supprimer et ressaisir vos identifiants.\n\nVous pouvez définir votre propre clé en utilisant l'option 'credentialSecret' dans\nvotre fichier de paramètres. Node-RED rechiffrera alors votre fichier contenant les identifiants\nà l'aide de la clé que vous avez choisie la prochaine fois que vous déploierez une modification.\n---------------------------------------------------------------------\n",
"system-key-warning": "\n\n--------------------------------------------------------------------------------------------------------\nVotre fichier contenant les identifiants de flux est chiffré à l'aide d'une clé générée par le système.\n\nSi la clé générée par le système est perdue pour une raison quelconque, votre fichier contenant\nles identifiants ne sera pas récupérable, vous devrez le supprimer et ressaisir vos identifiants.\n\nVous pouvez définir votre propre clé en utilisant l'option 'credentialSecret' dans\nvotre fichier de paramètres. Node-RED rechiffrera alors votre fichier contenant les identifiants\nà l'aide de la clé que vous avez choisie la prochaine fois que vous déploierez une modification.\n--------------------------------------------------------------------------------------------------------\n",
"unencrypted": "Utilisation d'identifiants non chiffrés",
"encryptedNotFound": "Identifiants chiffrés introuvables"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "3.1.0",
"version": "3.1.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/registry": "3.1.0",
"@node-red/util": "3.1.0",
"@node-red/registry": "3.1.3",
"@node-red/util": "3.1.3",
"async-mutex": "0.4.0",
"clone": "2.1.2",
"express": "4.18.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
"version": "3.1.0",
"version": "3.1.3",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0",
"version": "3.1.3",
"description": "Low-code programming for event-driven applications",
"homepage": "https://nodered.org",
"license": "Apache-2.0",
@@ -31,15 +31,15 @@
"flow"
],
"dependencies": {
"@node-red/editor-api": "3.1.0",
"@node-red/runtime": "3.1.0",
"@node-red/util": "3.1.0",
"@node-red/nodes": "3.1.0",
"@node-red/editor-api": "3.1.3",
"@node-red/runtime": "3.1.3",
"@node-red/util": "3.1.3",
"@node-red/nodes": "3.1.3",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"express": "4.18.2",
"fs-extra": "11.1.1",
"node-red-admin": "^3.1.0",
"node-red-admin": "^3.1.1",
"nopt": "5.0.0",
"semver": "7.5.4"
},

View File

@@ -29,7 +29,7 @@ describe("api/editor/ui", function() {
var app;
before(function() {
ui.init({
ui.init({}, {
nodes: {
getIcon: function(opts) {
return new Promise(function(resolve,reject) {

View File

@@ -26,6 +26,7 @@ var flowUtils = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util");
var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow");
var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows");
var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node");
var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials");
var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks");
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry");
@@ -61,6 +62,7 @@ describe('Flow', function() {
this.scope = n.scope;
var node = this;
this.foo = n.foo;
this.bar = n.bar;
this.handled = 0;
this.stopped = false;
currentNodes[node.id] = node;
@@ -1235,11 +1237,12 @@ describe('Flow', function() {
})
describe("#env", function () {
afterEach(() => {
delete process.env.V0;
delete process.env.V1;
credentials.get.restore?.()
})
it("can instantiate a node with environment variable property values of group and tab", async function () {
after(function() {
delete process.env.V0;
delete process.env.V1;
})
process.env.V0 = "gv0";
process.env.V1 = "gv1";
process.env.V3 = "gv3";
@@ -1283,10 +1286,6 @@ describe('Flow', function() {
});
it("can access environment variable property using $parent", async function () {
after(function() {
delete process.env.V0;
delete process.env.V1;
})
process.env.V0 = "gv0";
process.env.V1 = "gv1";
var config = flowUtils.parseConfig([
@@ -1321,9 +1320,6 @@ describe('Flow', function() {
});
it("can define environment variable using JSONata", async function () {
after(function() {
delete process.env.V0;
})
var config = flowUtils.parseConfig([
{id:"t1",type:"tab",env:[
{"name": "V0", value: "1+2", type: "jsonata"}
@@ -1346,9 +1342,6 @@ describe('Flow', function() {
});
it("can access global environment variables defined as JSONata values", async function () {
after(function() {
delete process.env.V0;
})
var config = flowUtils.parseConfig([
{id:"t1",type:"tab",env:[
{"name": "V0", value: "1+2", type: "jsonata"}
@@ -1370,15 +1363,21 @@ describe('Flow', function() {
await flow.stop()
});
it("global flow can access global-config defined environment variables", async function () {
after(function() {
delete process.env.V0;
sinon.stub(credentials,"get").callsFake(function(id) {
if (id === 'gc') {
return { map: { GC_CRED: 'gc_cred' }}
}
return null
})
const config = flowUtils.parseConfig([
{id:"gc", type:"global-config", env:[
{"name": "GC0", value: "3+4", type: "jsonata"}
{"name": "GC0", value: "3+4", type: "jsonata"},
{"name": "GC_CRED", type: "cred"},
]},
{id:"t1",type:"tab" },
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"${GC0}",wires:[]},
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"${GC0}", bar:"${GC_CRED}", wires:[]},
]);
// Two-arg call - makes this the global flow that handles global-config nodes
const globalFlow = Flow.create({getSetting:v=>process.env[v]},config);
@@ -1390,6 +1389,7 @@ describe('Flow', function() {
var activeNodes = flow.getActiveNodes();
activeNodes["1"].foo.should.equal(7);
activeNodes["1"].bar.should.equal('gc_cred');
await flow.stop()
await globalFlow.stop()

View File

@@ -183,6 +183,35 @@ describe('Node', function() {
n.receive(message);
});
it('calls parent flow handleComplete when multiple callbacks provided', function(done) {
var n = new RedNode({id:'123',type:'abc', _flow: {
handleComplete: function(node,msg) {
try {
doneCount.should.equal(2)
msg.should.deepEqual(message);
done();
} catch(err) {
done(err);
}
}
}});
var message = {payload:"hello world"};
let doneCount = 0
n.on('input',function(msg, nodeSend, nodeDone) {
doneCount++
nodeDone();
});
// Include a callback without explicit done signature
n.on('input',function(msg) { });
n.on('input',function(msg, nodeSend, nodeDone) {
doneCount++
nodeDone();
});
n.receive(message);
});
it('triggers onComplete hook when done callback provided', function(done) {
var handleCompleteCalled = false;
var hookCalled = false;